PowerShell Remoting Project Home

Thursday, February 16, 2006

Groups Command in MSH: Get-WindowsIdentity cmdlet

(Added on Mar 6th) There is a follow up post on this topic here.

In Linux Bash shell, there is a handy  groups command, which takes a username as input and return user associated groups. To get similar functionality,  I wrote the Get-WindowsIdentity cmdlet, which take  a MshCredential as input, call LogonUser Win32 API and return a System.Security. Principal.WindowsIdentity object. The bad news, you got to have a username and password. Example:
>get-WindowsIdentity (get-credential)

AuthenticationType : MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
ImpersonationLevel : None
IsAuthenticated    : True
IsGuest            : False
IsSystem           : False
IsAnonymous        : False
Name               : Domain\Administrator
Owner              : S-1-5-21-XXXXXXXXX-XXXXXXX-XXXX-XX
User               : S-1-5-21-XXXXXXXXX-XXXXXXX-XXXX-XX
Groups             : {S-1-5-21-XXXXXXXXX-XXXXXXX-XXXX-XX, , , , , , , }
Token              : XXXX
This cmdlet is designed for WindowsXP SP2. In Windows 2000, user required to have SeTcbPrivilege,a.k.a Act as part of the operating system, to call LogonUser. The reference (at the end of the post), provide a workaround to not call LongonUser: Use NegotiateStream. But it did not work for me.

If you are using windows server 2003 and want to check a domain account, don't use this cmdlet. Just do something like this:
>$id = new-object System.Security.Principal.WindowsIdentity("Domain\User")
This cmdlet could be useful if you are trying
1. To find out a specific user (you known his/her password) belongs to which groups

2. To check a user's password without login as that user or using runas/su.msh. If you input wrong password, you will get an error:
get Get-WindowsIdentity : Win32 API (LogonUser) Error: 1326


C# code:
using System;
using System.Security.Principal;
using System.Management.Automation;
using System.Net;
using System.Runtime.InteropServices;

namespace MSHForFun.Security
{
    public class LogonUserHelper
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool LogonUser(
          string principal,
          string authority,
          string password,
          LogonTypes logonType,
          LogonProviders logonProvider,
          out IntPtr token);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr handle);
        enum LogonTypes : uint
        {
            Interactive = 2,
            Network,
            Batch,
            Service,
            NetworkCleartext = 8,
            NewCredentials
        }
        enum LogonProviders : uint
        {
            Default = 0, // default for platform (use this!)
            WinNT35,     // sends smoke signals to authority
            WinNT40,     // uses NTLM
            WinNT50      // negotiates Kerb or NTLM
        }
        public static WindowsIdentity LogonUser(MshCredential UserCre)
        {
            IntPtr token = IntPtr.Zero;
            NetworkCredential Cre = UserCre.GetNetworkCredential();
            bool result = LogonUser(
                Cre.UserName, "", Cre.Password,
                LogonTypes.Network,
                LogonProviders.Default,
                out token);
            WindowsIdentity id;
            if (result)
            {
                id = new WindowsIdentity(token);
                CloseHandle(token);
            }
            else
            {
                throw new Exception("Win32 API (LogonUser) Error: " + Marshal.GetLastWin32Error().ToString());
            }
            return id;
        }

    }

    /// <summary>
    /// Get User WindowsIdentity
    /// </summary>
    [Cmdlet("Get", "WindowsIdentity", SupportsShouldProcess = true)]
    public class GetWindowsIdentityCmd : Cmdlet
    {
        #region Parameters
        private MshCredential userCre = null;
        /// <summary>Process Arrary to work with</summary>
        [Parameter(Mandatory = true, Position = 0)]
        public MshCredential UserCredential
        {
            get { return userCre; }
            set { userCre = value; }
        }
        #endregion

        /// <summary>
        /// Call LogonUser Return a WindowsIdentity Object
        /// </summary>
        protected override void ProcessRecord()
        {
            if (ShouldProcess("Get WindowsIdentity of User: " + userCre.UserName))
            {
                WriteObject(LogonUserHelper.LogonUser(userCre));
            }
        }
    }
}
Reference: http://pluralsight.com/wiki/default.aspx/Keith.GuideBook/HowToGetATokenForAUser.html

Have fun!

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

Tags:       


Comments:

Post a Comment





<< Home