My application had a WindowsIdentity crisis
- by Brian Donahue
The project I have been working on this week to test computer environments needs to do various actions as a user other than the one running the application. For instance, it looks up an installed Windows Service, finds out who the startup user is, and tries to connect to a database as that Windows user.
Later on, it will need to access a file in the context of the currently logged-in user.
With ASP .NET, this is super-easy: just go into Web.Config and set up the "identity impersonate" node, which can either impersonate a named user or the one who had logged into the website if authentication was enabled. With Windows applications, this is not so straightforward.
There may be something I am overlooking, but the limitation seems to be that you can only change the security context on the current thread: any threads spawned by the impersonated thread also inherit the impersonated credentials. Impersonation is easy enough to do, once you figure out how.
Here is my code for impersonating a user on the current thread:
         using System;
        using System.ComponentModel;
        using System.Runtime.InteropServices;
        using System.Security.Principal;
        public class ImpersonateUser
        {
                IntPtr userHandle;
  [DllImport("advapi32.dll", SetLastError = true)]
                static extern bool LogonUser(
                        string lpszUsername,
                        string lpszDomain,
                        string lpszPassword,
                        LogonType dwLogonType,
                        LogonProvider dwLogonProvider,
                        out IntPtr phToken
                        );
 
 
                [DllImport("kernel32.dll", SetLastError = true)]
                static extern bool CloseHandle(IntPtr hHandle);
 
 
                enum LogonType : int
                {
                        Interactive = 2,
                        Network = 3,
                        Batch = 4,
                        Service = 5,
                        NetworkCleartext = 8,
                        NewCredentials = 9,
                }
 
 
                enum LogonProvider : int
                {
                        Default = 0,
                }
                public static WindowsImpersonationContext Impersonate(string user, string domain, string password)
                {
  IntPtr userHandle = IntPtr.Zero;
                        bool loggedOn = LogonUser(
                                user,
                                domain,
                                password,
                                LogonType.Interactive,
                                LogonProvider.Default,
                                out userHandle);
 
 
 
                        if (!loggedOn)
                        throw new Win32Exception(Marshal.GetLastWin32Error());
 
                        WindowsIdentity identity = new WindowsIdentity(userHandle);
                        WindowsPrincipal principal = new WindowsPrincipal(identity);
                        System.Threading.Thread.CurrentPrincipal = principal;
                        return identity.Impersonate();
  }
        }
 
/* Call impersonation */
ImpersonateUser.Impersonate("UserName","DomainName","Password");
/* When you want to go back to the original user */
WindowsIdentity.Impersonate(IntPtr.Zero); 
When you want to stop impersonating, you can call Impersonate() again with a null pointer. This will allow you to simulate a variety of different Windows users from the same applicaiton.