Tuesday, September 3, 2013

Impersonation in Console/Windows Application in same and cross domain


After Go live of any application, there are issues which occurred for certain users sometimes. To nail down these issues, we need to execute code under that user i.e. impersonate user identities and run application under his user ID.

In Web application, this is quite straightforward and same can be achieved using web.config using location attribute like:


<location path="<Page/Service Path>">
    <system.web>
      <identity impersonate="true" />
    </system.web>
  </location>


<location path="<Page/Service Path>">
    <system.web>
      <identity impersonate="true" userName="<user Name>" password="<Password>"/>
    </system.web>
  </location>

Now, question comes, how to achieve same in console/Windows application if needed. so.. Here you go:

For impersonation in Console/Windows application, best and simple way is to use P/I invoke.  Following sample you can use for same:

Add following method at Class level:

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);  

In your method:

Same Domain

IntPtr tokenHandle = IntPtr.Zero;
            bool returnValue = LogonUser("<User Name>", "<Domain>", "<Password>", 2, 0, ref tokenHandle); 
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
            {
// Add code to be executed under that user
}


Cross Domain:

This is little tricky. Follow given steps: 

1. Create class Impersonation.cs

2. Add following P/I invokes and enums

[DllImport("advapi32.dll", SetLastError = true)]
        private static extern int LogonUser(
        string lpszUserName,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(
        IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(
        IntPtr handle);

        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        enum LogonType
        {
            Interactive = 2,
            Network = 3,
            Batch = 4,
            Service = 5,
            Unlock = 7,
            NetworkClearText = 8,
            NewCredentials = 9
        }
        enum LogonProvider
        {
            Default = 0,
            WinNT35 = 1,
            WinNT40 = 2,
            WinNT50 = 3

        }

3. Use following method

        public void ImpersonateUser(
        string userName,
        string domain,
        string password)
        {


            WindowsIdentity tempWindowsIdentity = null;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                if (RevertToSelf())
                {
                    if (LogonUser(
                    userName,
                    domain,
                    password,
                    (int)LogonType.NewCredentials,
                    (int)LogonProvider.WinNT50,

                    //LOGON32_LOGON_INTERACTIVE,
                        //LOGON32_PROVIDER_DEFAULT,
                    ref token) != 0)
                    {
                        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {
                            tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                            impersonationContext = tempWindowsIdentity.Impersonate();
                        }
                        else
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (token != IntPtr.Zero)
                {
                    CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CloseHandle(tokenDuplicate);
                }
            }
        }


4. Create a instance of class as given below and add your code segment 

using (Impersonator imperso = new Impersonator("<userName>","<domain>","Password"))
{
 //Add your code here

}


That's it! ... You are all set to execute your code section under that user.

Thanks!