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.