Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Uses anonymous methods to wrap impersonation with .NET
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace Security {
/// <summary>
/// Allows you to execute code with an alternate set of credentials
/// </summary>
public class ImpersonationContext : IDisposable {
#region Imported Methods
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken(
IntPtr token,
int impersonationLevel,
ref IntPtr newToken
);
[DllImport("advapi32.dll")]
private static extern int LogonUserA(
string username,
string domain,
string password,
int logonType,
int logonProvider,
ref IntPtr token
);
#endregion
#region Constants
private const int INTERACTIVE_LOGON = 2;
private const int DEFAULT_PROVIDER = 0;
private const string REGEX_GROUP_USERNAME = "username";
private const string REGEX_GROUP_DOMAIN = "domain";
private const string REGEX_EXTRACT_USER_INFO =
@"^(?<domain>[^\\]+)\\(?<username>.*)$|^(?<username>[^@]+)@(?<domain>.*)$";
private const string EXCEPTION_COULD_NOT_IMPERSONATE =
"Could not impersonate user '{0}'.";
private const string EXCEPTION_COULD_NOT_PARSE_USERNAME =
"Cannot determine username and domain from '{0}'";
#endregion
#region Constructors
/// <summary>
/// Creates a new Impersonation context
/// </summary>
public ImpersonationContext(string fullUsername, string password) {
this.SetCredentials(fullUsername, password);
}
/// <summary>
/// Creates a new Impersonation context
/// </summary>
public ImpersonationContext(string username, string domain, string password) {
this.SetCredentials(username, domain, password);
}
#endregion
#region Static Creation
/// <summary>
/// Executes a set of code using the credentials provided
/// </summary>
public static void Execute(string fullUsername, string password, Action action) {
using (ImpersonationContext context = new ImpersonationContext(fullUsername, password)) {
context.Execute(action);
}
}
/// <summary>
/// Executes a set of code using the credentials provided
/// </summary>
public static void Execute(string username, string domain, string password, Action action) {
using (ImpersonationContext context = new ImpersonationContext(username, domain, password)) {
context.Execute(action);
}
}
#endregion
#region Properties
/// <summary>
/// The username for this connection
/// </summary>
public string Username { get; private set; }
/// <summary>
/// The domain name for this user
/// </summary>
public string Domain { get; private set; }
/// <summary>
/// The identity of the executing account
/// </summary>
public WindowsIdentity Identity { get; private set; }
//connection details
private string _Password;
private WindowsImpersonationContext _Context;
#endregion
#region Private Methods
/// <summary>
/// Begins to impersonate the provided credentials
/// </summary>
public bool BeginImpersonation() {
//create the token containers
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (ImpersonationContext.RevertToSelf()) {
//attempt the login
int success = ImpersonationContext.LogonUserA(
this.Username,
this.Domain,
this._Password,
INTERACTIVE_LOGON,
DEFAULT_PROVIDER,
ref token
);
//if this worked, perform the impersonation
if (success != 0) {
int duplicate = ImpersonationContext.DuplicateToken(token, 2, ref tokenDuplicate);
if (duplicate != 0) {
//assign the identity to use
//this.Identity = new WindowsIdentity(tokenDuplicate);
this._Context = WindowsIdentity.Impersonate(tokenDuplicate);
if (this._Context != null) {
ImpersonationContext.CloseHandle(token);
ImpersonationContext.CloseHandle(tokenDuplicate);
return true;
}
}
}
}
//close the tokens if required
if (token != IntPtr.Zero) { ImpersonationContext.CloseHandle(token); }
if (tokenDuplicate != IntPtr.Zero) { ImpersonationContext.CloseHandle(tokenDuplicate); }
//return this failed
return false;
}
/// <summary>
/// Ends impersonating the current request
/// </summary>
public void EndImpersonation() {
if (this._Context is WindowsImpersonationContext) { this._Context.Undo(); }
}
#endregion
#region Public Methods
/// <summary>
/// Accepts a full domain and assigns it for this connection
/// </summary>
public void SetCredentials(string fullUsername, string password) {
//extract the user information
Match user = Regex.Match(fullUsername, REGEX_EXTRACT_USER_INFO);
if (!user.Success) {
string message = string.Format(EXCEPTION_COULD_NOT_PARSE_USERNAME, fullUsername);
throw new ArgumentException(message);
}
//extract the values
string username = user.Groups[REGEX_GROUP_USERNAME].Value;
string domain = user.Groups[REGEX_GROUP_DOMAIN].Value;
//update the credentials
this.SetCredentials(username, domain, password);
}
/// <summary>
/// Changes the credentials for this connection to use
/// </summary>
public void SetCredentials(string username, string domain, string password) {
this.Username = username;
this.Domain = domain;
this._Password = password;
}
/// <summary>
/// Executes the action using the credentials provided
/// </summary>
public void Execute(Action action) {
//perform the requested action
if (this.BeginImpersonation()) {
try {
action();
}
finally {
this.EndImpersonation();
}
}
//since this couldn't login, give up
else {
string message = string.Format(EXCEPTION_COULD_NOT_IMPERSONATE, this.Username);
throw new OperationCanceledException(message);
}
}
#endregion
#region IDisposable Members
/// <summary>
/// Performs any cleanup work
/// </summary>
public void Dispose() {
this.EndImpersonation();
if (this._Context is WindowsImpersonationContext) { this._Context.Dispose(); }
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.