User Impersonation in .NET






4.62/5 (27 votes)
This sample shows how to switch between security contexts within the same process. It also demonstrates how this can be done on a single or multiple threads.
Description
This sample shows how to switch between security contexts within the same process. It also demostrates how this can be done on a single or multiple threads.
Why It's Here
I was looking through the Message Board on The Code Project site and came across a very interesting question. One of the members was asking how to access a file on another computer in a Workgroup without being prompted to specify user credentials.
I started researching the topic and found the solution in the .NET class library reference. I was impressed by the elegance of the solution, so I decided to have it published here for the benefit of other fellow-developers.
Explanation
The main class within the project is the ImpersonateUser
that uses Win32 API function LogonUser
and the .NET class WindowsImpersonationContext
that represents the Windows user before impersonation.
To impersonate a different user, the class follows three distincts steps as listed below.
Step 1
Using LogonUser
, it creates a new security token and obtains a handle to it.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle is a new security token
Step 2
Using this handle, it creates an instance of the WindowsIdentity
class.
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
Step 3
It then uses the WindowsIdentity.Impersonate()
method that returns WindowsImpersonationContext
to assign the new security token to the current WindowsImpersonationContext
.
// Declared in the ImpersonateUser class
private static WindowsImpersonationContext impersonatedUser;
// used within the class Impersonate method
impersonatedUser = newId.Impersonate();
Main Class
Here's the code for the ImpersonateUser
class:
public class ImpersonateUser
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password) {
try {
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue) {
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser call failed with error code : " + ret);
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
impersonatedUser = newId.Impersonate();
}
catch (Exception ex) {
Console.WriteLine("Exception occurred. " + ex.Message);
}
}
// Stops impersonation
public void Undo() {
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
}
}
Example
ImpersonateUser iU = new ImpersonateUser();
// TODO: Replace credentials
iU.Impersonate("remoteMachine", "userID", "password");
// Next lines of code will run in the security context of the user specified above
// ....
// Revert back to the previous user identity
iU.Undo();
Please download the source code and run the demo project - it will give you a better idea how easy it is to use the class.
How To Use ImpersUsr.dll
- Download and extract ImpersUsr.dll
- Create a Console type project
- Add a reference to ImpersUsr.dll
- Instantiate the
ImpersonateUser
class - Call
ImpersonateUser.Impersonate(..)
method - Code whatever you need to run in the new security context
- Call
ImpersonateUser.Undo()
method to stop impersonating
Multithreading
Please download the source to see how to start a separate thread in a different security context.
History
- 6th June, 2006: Initial post