Skip to content

Instantly share code, notes, and snippets.

@transistor1
Last active July 18, 2018 15:31
Show Gist options
  • Save transistor1/578f876bb05a90b418e2 to your computer and use it in GitHub Desktop.
Save transistor1/578f876bb05a90b418e2 to your computer and use it in GitHub Desktop.
namespace Zeta.EnterpriseLibrary.Tools
{
#region Using directives.
// ----------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
// ----------------------------------------------------------------------
#endregion
/////////////////////////////////////////////////////////////////////////
/// <summary>
/// Impersonation of a user. Allows to execute code under another
/// user context.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <remarks>
/// This class is based on the information in the Microsoft knowledge base
/// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158
///
/// Encapsulate an instance into a using-directive like e.g.:
///
/// ...
/// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
/// {
/// ...
/// [code that executes under the new context]
/// ...
/// }
/// ...
/// </remarks>
public class Impersonator :
IDisposable
{
#region Public methods.
// ------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="Impersonator"/> class.
/// </summary>
public Impersonator()
{
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public Impersonator(
string userName,
string domainName,
string password,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
profileBehaviour );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public Impersonator(
string userName,
string domainName,
string password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
profileBehaviour );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public Impersonator(
string userName,
string domainName,
SecureString password,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
profileBehaviour );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public Impersonator(
string userName,
string domainName,
SecureString password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
profileBehaviour );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
string password )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
public Impersonator(
string userName,
string domainName,
string password,
LoginType loginType )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public Impersonator(
string userName,
string domainName,
SecureString password )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Constructor. Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
public Impersonator(
string userName,
string domainName,
SecureString password,
LoginType loginType )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public void Impersonate(
string userName,
string domainName,
string password,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
profileBehaviour );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public void Impersonate(
string userName,
string domainName,
string password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
profileBehaviour );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public void Impersonate(
string userName,
string domainName,
SecureString password,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
profileBehaviour );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
public void Impersonate(
string userName,
string domainName,
SecureString password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
profileBehaviour );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public void Impersonate(
string userName,
string domainName,
string password )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
public void Impersonate(
string userName,
string domainName,
string password,
LoginType loginType )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
public void Impersonate(
string userName,
string domainName,
SecureString password )
{
ImpersonateValidUser(
userName,
domainName,
password,
LoginType.Interactive,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Starts the impersonation with the given credentials.
/// The account that instantiates the Impersonator class
/// needs to have the 'Act as part of operating system' privilege set.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">The login type.</param>
public void Impersonate(
string userName,
string domainName,
SecureString password,
LoginType loginType )
{
ImpersonateValidUser(
userName,
domainName,
password,
loginType,
ProfileBehaviour.DontLoad );
}
/// <summary>
/// Undoes the impersonation. Safe to call even if not yet
/// impersonized.
/// </summary>
public void Undo()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region IDisposable member.
// ------------------------------------------------------------------
/// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
UndoImpersonation();
GC.SuppressFinalize( this );
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="Impersonator"/> is reclaimed by garbage collection.
/// </summary>
~Impersonator()
{
UndoImpersonation();
}
// ------------------------------------------------------------------
#endregion
#region P/Invoke.
// ------------------------------------------------------------------
/// <summary>
/// Logons the user.
/// </summary>
/// <param name="lpszUserName">Name of the LPSZ user.</param>
/// <param name="lpszDomain">The LPSZ domain.</param>
/// <param name="lpszPassword">The LPSZ password.</param>
/// <param name="dwLogonType">Type of the dw logon.</param>
/// <param name="dwLogonProvider">The dw logon provider.</param>
/// <param name="phToken">The ph token.</param>
/// <returns></returns>
[DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
private static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken );
/// <summary>
/// Logons the user2.
/// </summary>
/// <param name="lpszUserName">Name of the LPSZ user.</param>
/// <param name="lpszDomain">The LPSZ domain.</param>
/// <param name="Password">The password.</param>
/// <param name="dwLogonType">Type of the dw logon.</param>
/// <param name="dwLogonProvider">The dw logon provider.</param>
/// <param name="phToken">The ph token.</param>
/// <returns></returns>
[DllImport( @"advapi32.dll", EntryPoint = @"LogonUser", CharSet = CharSet.Unicode, SetLastError = true )]
private static extern int LogonUser2(
string lpszUserName,
string lpszDomain,
IntPtr Password,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken );
/// <summary>
/// Duplicates the token.
/// </summary>
/// <param name="hToken">The h token.</param>
/// <param name="impersonationLevel">The impersonation level.</param>
/// <param name="hNewToken">The h new token.</param>
/// <returns></returns>
[DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
private static extern int DuplicateToken(
IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken );
/// <summary>
/// Reverts to self.
/// </summary>
/// <returns></returns>
[DllImport( @"advapi32.dll", CharSet = CharSet.Auto, SetLastError = true )]
private static extern bool RevertToSelf();
/// <summary>
/// Closes the handle.
/// </summary>
/// <param name="handle">The handle.</param>
/// <returns></returns>
[DllImport( @"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true )]
private static extern bool CloseHandle(
IntPtr handle );
/// <summary>
///
/// </summary>
private const int LOGON32_PROVIDER_DEFAULT = 0;
[DllImport( @"userenv.dll", SetLastError = true, CharSet = CharSet.Auto )]
private static extern bool LoadUserProfile(
IntPtr hToken,
ref PROFILEINFO lpProfileInfo );
[DllImport( @"userenv.dll", SetLastError = true, CharSet = CharSet.Auto )]
private static extern bool UnloadUserProfile(
IntPtr hToken,
IntPtr hProfile );
[StructLayout( LayoutKind.Sequential )]
private struct PROFILEINFO
{
public int dwSize;
public int dwFlags;
[MarshalAs( UnmanagedType.LPTStr )]
public String lpUserName;
[MarshalAs( UnmanagedType.LPTStr )]
public String lpProfilePath;
[MarshalAs( UnmanagedType.LPTStr )]
public String lpDefaultPath;
[MarshalAs( UnmanagedType.LPTStr )]
public String lpServerName;
[MarshalAs( UnmanagedType.LPTStr )]
public String lpPolicyPath;
public IntPtr hProfile;
}
// ------------------------------------------------------------------
#endregion
#region Private methods.
// ------------------------------------------------------------------
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">Type of the login.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
private void ImpersonateValidUser(
string userName,
string domainName,
string password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
Trace.TraceInformation(
string.Format(
@"[Impersonation] About to impersonate as domain '{0}', user '{1}'.",
domainName,
userName ) );
try
{
if ( domainName != null && domainName.Length <= 0 )
{
domainName = null;
}
IntPtr token = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser(
userName,
domainName,
password,
(int)loginType,
LOGON32_PROVIDER_DEFAULT,
ref token ) != 0 )
{
if ( DuplicateToken( token, 2, ref _impersonationToken ) != 0 )
{
CheckLoadProfile( profileBehaviour );
WindowsIdentity tempWindowsIdentity =
new WindowsIdentity( _impersonationToken );
_impersonationContext =
tempWindowsIdentity.Impersonate();
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
}
}
catch ( Exception x )
{
Trace.TraceError(
string.Format(
@"[Impersonation] Error impersonating as domain '{0}', user '{1}'.",
domainName,
userName ),
x );
throw;
}
Trace.TraceInformation(
string.Format(
@"[Impersonation] Successfully impersonated as domain '{0}', user '{1}'.",
domainName,
userName ) );
}
/// <summary>
/// Does the actual impersonation.
/// </summary>
/// <param name="userName">The name of the user to act as.</param>
/// <param name="domainName">The domain name of the user to act as.</param>
/// <param name="password">The password of the user to act as.</param>
/// <param name="loginType">Type of the login.</param>
/// <param name="profileBehaviour">The profile behaviour.</param>
private void ImpersonateValidUser(
string userName,
string domainName,
SecureString password,
LoginType loginType,
ProfileBehaviour profileBehaviour )
{
Trace.TraceInformation(
string.Format(
@"[Impersonation] About to impersonate as domain '{0}', user '{1}'.",
domainName,
userName ) );
try
{
if ( domainName != null && domainName.Length <= 0 )
{
domainName = null;
}
IntPtr token = IntPtr.Zero;
IntPtr passwordPtr = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
// Marshal the SecureString to unmanaged memory.
passwordPtr =
Marshal.SecureStringToGlobalAllocUnicode( password );
if ( LogonUser2(
userName,
domainName,
passwordPtr,
(int)loginType,
LOGON32_PROVIDER_DEFAULT,
ref token ) != 0 )
{
if ( DuplicateToken( token, 2, ref _impersonationToken ) != 0 )
{
CheckLoadProfile( profileBehaviour );
WindowsIdentity tempWindowsIdentity =
new WindowsIdentity( _impersonationToken );
_impersonationContext =
tempWindowsIdentity.Impersonate();
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
else
{
int le = Marshal.GetLastWin32Error();
throw new Win32Exception( le );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode( passwordPtr );
}
}
catch ( Exception x )
{
Trace.TraceError(
string.Format(
@"[Impersonation] Error impersonating as domain '{0}', user '{1}'.",
domainName,
userName ),
x );
throw;
}
Trace.TraceInformation(
string.Format(
@"[Impersonation] Successfully impersonated as domain '{0}', user '{1}'.",
domainName,
userName ) );
}
/// <summary>
/// Checks and loads the load profile.
/// </summary>
private void CheckLoadProfile(
ProfileBehaviour profileBehaviour )
{
if ( profileBehaviour == ProfileBehaviour.Load )
{
_profileInfo = new PROFILEINFO();
_profileInfo.dwSize = Marshal.SizeOf( _profileInfo );
_profileInfo.lpUserName = WindowsIdentity.GetCurrent().Name;
if ( LoadUserProfile( _impersonationToken, ref _profileInfo ) )
{
_profileBehaviour = profileBehaviour;
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void UndoImpersonation()
{
if ( _impersonationContext != null )
{
Trace.TraceInformation(
string.Format(
@"[Impersonation] About to undo impersonation." ) );
try
{
_impersonationContext.Undo();
_impersonationContext = null;
}
catch ( Exception x )
{
Trace.TraceError(
string.Format(
@"[Impersonation] Error undoing impersonation." ),
x );
throw;
}
Trace.TraceInformation(
string.Format(
@"[Impersonation] Successfully undone impersonation." ) );
}
// --
if ( _profileBehaviour == ProfileBehaviour.Load )
{
Trace.TraceInformation(
string.Format(
@"[Impersonation] About to unload user profile." ) );
try
{
if ( !UnloadUserProfile( _impersonationToken, _profileInfo.hProfile ) )
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
_profileBehaviour = ProfileBehaviour.DontLoad;
}
catch ( Exception x )
{
Trace.TraceError(
string.Format(
@"[Impersonation] Error unloading user profile." ),
x );
throw;
}
}
if ( _impersonationToken != IntPtr.Zero )
{
CloseHandle( _impersonationToken );
_impersonationToken = IntPtr.Zero;
}
}
// ------------------------------------------------------------------
#endregion
#region Private variables.
// ------------------------------------------------------------------
private WindowsImpersonationContext _impersonationContext;
private ProfileBehaviour _profileBehaviour = ProfileBehaviour.DontLoad;
private PROFILEINFO _profileInfo;
private IntPtr _impersonationToken = IntPtr.Zero;
// ------------------------------------------------------------------
#endregion
}
/////////////////////////////////////////////////////////////////////////
/// <summary>
/// How to log in the user.
/// </summary>
public enum LoginType
{
#region Enum members.
// ------------------------------------------------------------------
/// <summary>
/// Interactive. This is the default.
/// </summary>
Interactive = 2,
/// <summary>
///
/// </summary>
Batch = 4,
/// <summary>
///
/// </summary>
Network = 3,
/// <summary>
///
/// </summary>
NetworkClearText = 0,
/// <summary>
///
/// </summary>
Service = 5,
/// <summary>
///
/// </summary>
Unlock = 7,
/// <summary>
///
/// </summary>
NewCredentials = 9
// ------------------------------------------------------------------
#endregion
}
/////////////////////////////////////////////////////////////////////////
/// <summary>
/// How to deal with the user's profile.
/// </summary>
/// <remarks>
/// 2008-05-21, suggested and implemented by Tim Daplyn
/// ([email protected]).
/// </remarks>
public enum ProfileBehaviour
{
#region Enum members.
// ------------------------------------------------------------------
/// <summary>
/// Do not load the user's profile. This is the default behaviour.
/// </summary>
DontLoad,
/// <summary>
/// Load the user's profile.
/// </summary>
Load
// ------------------------------------------------------------------
#endregion
}
/////////////////////////////////////////////////////////////////////////
}
@UweKeim
Copy link

UweKeim commented Jul 18, 2018

Hey, this is my class. Thanks for using 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment