Last active
March 22, 2024 12:21
-
-
Save UweKeim/365d43420236f62cd39e47e8fd2bfaba to your computer and use it in GitHub Desktop.
Impersonation in .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
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