Created
April 6, 2010 16:59
-
-
Save hugoware/357819 to your computer and use it in GitHub Desktop.
Uses anonymous methods to wrap impersonation with .NET
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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