📜 ⬆️ ⬇️

Execution of a piece of code on behalf of a specific user

Formulation of the problem



Imagine the following situation, there is a directory A, which only userA has access to, there is a directory B, only userB has access to it, you need to copy files from directory A to directory B, given that the program can be started by users userC, userD, userF, ... etc.

Solution outline


')
As one of the solutions, you can use the following scheme:
the program is launched on behalf of any user, then it is authorized under the user userA, reads the file into memory, returns to the initial user, authorizes under the user userB, writes the file to the final directory, returns to the initial user. For large file sizes, you can create blocking, but this no longer applies to this task.


Decision



It will take three simple classes (in fact, the first one is sufficient).

[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] internal class MySafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid { private MySafeTokenHandle() : base(true) { } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] override protected bool ReleaseHandle() { return NativeMethods.CloseHandle(handle); } } [SuppressUnmanagedCodeSecurity()] internal static class NativeMethods { #region P/Invoke internal const int LOGON32_LOGON_INTERACTIVE = unchecked((int)2); internal const int LOGON32_PROVIDER_DEFAULT = unchecked((int)0); [DllImport("advapi32.dll")] internal static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out MySafeTokenHandle phToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] internal static extern int DuplicateToken(MySafeTokenHandle hToken, int impersonationLevel, out MySafeTokenHandle hNewToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool CloseHandle(IntPtr handle); #endregion } /// <summary> ///          /// </summary> [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)] public class Impersonation : IDisposable { WindowsImpersonationContext impersonationContext; /// <summary> ///      /// </summary> /// <param name="userName"> </param> /// <param name="domain"></param> /// <param name="password"></param> /// <returns>false -        </returns> public void ImpersonateUser(String userName, String domain, String password) { WindowsIdentity tempWindowsIdentity; MySafeTokenHandle token; MySafeTokenHandle tokenDuplicate; if (NativeMethods.LogonUserA(userName, domain, password, NativeMethods.LOGON32_LOGON_INTERACTIVE, NativeMethods.LOGON32_PROVIDER_DEFAULT, out token) != 0) { using (token) { if (NativeMethods.DuplicateToken(token, 2, out tokenDuplicate) != 0) { using (tokenDuplicate) { if (!tokenDuplicate.IsInvalid) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate.DangerousGetHandle()); impersonationContext = tempWindowsIdentity.Impersonate(); return; } } } } } else throw new Exception("LogonUser failed: " + Marshal.GetLastWin32Error().ToString()); } /// <summary> ///       /// </summary> public void Dispose() { impersonationContext.Undo(); GC.SuppressFinalize(this); } } 


The second class is responsible for the execution of the code:
 /// <summary> ///         /// </summary> public class ImpersonationExecutor { public string USR { get; set; } public string DOMAIN { get; set; } public string PWD { get; set; } /// <summary> ///  /// </summary> /// <param name="userName"> </param> /// <param name="domainName"></param> /// <param name="password"></param> public ImpersonationExecutor(string userName, string domainName, string password) { USR = userName; DOMAIN = domainName; PWD = password; } /// <summary> ///   /// </summary> /// <typeparam name="T">  </typeparam> /// <param name="action"></param> /// <param name="arg"></param> public void ExecuteCode<T>(Action<T> action, T arg) { using (Impersonation user = new Impersonation()) { user.ImpersonateUser(USR, DOMAIN, PWD); action(arg); } } } 


And finally, a very small factory:
 public class ImpersonationFactory { public static ImpersonationExecutor Create(string UserName, string Domain, string Password) { return new ImpersonationExecutor(UserName, Domain, Password); } } 


Usage example


As an example, we solve the problem posed in the formulation.
 string from = "from"; string to = "to"; byte[] buffer = null; ImpersonationFactory.Create("userA", "domainA", "passwordA").ExecuteCode<string>(delegate(string path) { buffer = File.ReadAllBytes(path); }, from); ImpersonationFactory.Create("userB", "domainB", "passwordB").ExecuteCode<string>(delegate(string path) { File.WriteAllBytes(path, buffer); }, to); 


Total


Problem solved. As an option, it was possible to use different applications (services / services) working under different users, but the flexibility will be lost. I would be glad to see other ways.
Search on Habra did not see this particular solution.

UPD : fixed Impersonation class, according to bobermaniac

Source: https://habr.com/ru/post/135299/


All Articles