namespace Tools; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using System.Security.Principal; /// /// 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. /// /// /// 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] /// ... /// }); /// ... /// [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(bool active, Func 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 RunImpersonatedAsync(bool active, Func> 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 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); }