Skip to content

Instantly share code, notes, and snippets.

@UweKeim
Last active March 22, 2024 12:21
Show Gist options
  • Save UweKeim/365d43420236f62cd39e47e8fd2bfaba to your computer and use it in GitHub Desktop.
Save UweKeim/365d43420236f62cd39e47e8fd2bfaba to your computer and use it in GitHub Desktop.
Impersonation in .NET
namespace Tools;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
/// <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.:
/// ...
/// new Impersonator( "myUsername", "myDomainname", "myPassword" ).RunImpersonated(
/// true,
/// () => {
/// ...
/// [code that executes under the new context]
/// ...
/// });
/// ...
/// </remarks>
[PublicAPI]
public sealed class Impersonator(
string? userName,
string? domainName,
string? password)
{
// https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated
private const int LOGON32_PROVIDER_DEFAULT = 0;
private const int LOGON32_LOGON_INTERACTIVE = 2;
public T RunImpersonated<T>(bool active, Func<T> func)
{
if (active)
{
var r = LogonUser(
userName ?? string.Empty,
domainName ?? string.Empty,
password ?? string.Empty,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out var safeAccessTokenHandle);
if (r && safeAccessTokenHandle != null)
{
try
{
#pragma warning disable CA1416 // Validate platform compatibility
return WindowsIdentity.RunImpersonated(safeAccessTokenHandle, func);
#pragma warning restore CA1416 // Validate platform compatibility
}
finally
{
safeAccessTokenHandle.Dispose();
}
}
else
{
var ret = Marshal.GetLastWin32Error();
throw new Win32Exception(ret);
}
}
else
{
return func();
}
}
public async Task<T> RunImpersonatedAsync<T>(bool active, Func<Task<T>> func)
{
if (active)
{
var r = LogonUser(
userName ?? string.Empty, domainName ?? string.Empty, password ?? string.Empty,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out var safeAccessTokenHandle);
if (r && safeAccessTokenHandle != null)
{
try
{
#pragma warning disable CA1416 // Validate platform compatibility
return await WindowsIdentity.RunImpersonatedAsync(safeAccessTokenHandle, func);
#pragma warning restore CA1416 // Validate platform compatibility
}
finally
{
safeAccessTokenHandle.Dispose();
}
}
else
{
var ret = Marshal.GetLastWin32Error();
throw new Win32Exception(ret);
}
}
else
{
return await func();
}
}
public void RunImpersonated(bool active, Action action)
{
if (active)
{
var r = LogonUser(
userName ?? string.Empty,
domainName ?? string.Empty,
password ?? string.Empty,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out var safeAccessTokenHandle);
if (r && safeAccessTokenHandle != null)
{
try
{
#pragma warning disable CA1416 // Validate platform compatibility
WindowsIdentity.RunImpersonated(safeAccessTokenHandle, action);
#pragma warning restore CA1416 // Validate platform compatibility
}
finally
{
safeAccessTokenHandle.Dispose();
}
}
else
{
var ret = Marshal.GetLastWin32Error();
throw new Win32Exception(ret);
}
}
else
{
action();
}
}
public async Task RunImpersonatedAsync(bool active, Func<Task> action)
{
if (active)
{
var r = LogonUser(
userName ?? string.Empty,
domainName ?? string.Empty,
password ?? string.Empty,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out var safeAccessTokenHandle);
if (r && safeAccessTokenHandle != null)
{
try
{
#pragma warning disable CA1416 // Validate platform compatibility
await WindowsIdentity.RunImpersonatedAsync(safeAccessTokenHandle, action);
#pragma warning restore CA1416 // Validate platform compatibility
}
finally
{
safeAccessTokenHandle.Dispose();
}
}
else
{
var ret = Marshal.GetLastWin32Error();
throw new Win32Exception(ret);
}
}
else
{
await action();
}
}
[DllImport(@"advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out SafeAccessTokenHandle? phToken);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment