Use Process.Start while impersonating (Window application)

I am trying to use Process.Start () under impersonation, I have Google for several days, most of the answers that I met were in ASP.net, but I am developing a Window application, so I am having difficulty finding the root cause.

This is my personalized code.

private void impersonateValidUser(string userName, string domain, string password) { WindowsIdentity tempWindowsIdentity = null; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 ) { tempWindowsIdentity = new WindowsIdentity( tokenDuplicate ); mImpersonationContext = tempWindowsIdentity.Impersonate(); } } } 

and I'm trying to open a document through my program (no .exe, e.g. .txt, .doc)

  using (new Impersonator(DomainUserID, Domain, Password)) Process.Start(filePath); 

Until now, I could find a directory / file with an impersonated user that would be invisible to my current login user, since I did not grant him access. But whenever I try to open a document, I get an error

  System.ComponentModel.Win32Exception (0x80004005): Access is denied 

Please correct me if I am wrong, so in this case I should not set UseShellExecute to false (and processStartInfo with username and password too?), Because it is for the executable (?) And I also encounter the CreateProcessAsUser function, and this function does not seem to apply to my case, from the example that I also read for the .exe file.

It would be nice if someone could enlighten me.

update: impersonating a class

 public class Impersonator : IDisposable { #region P/Invoke [DllImport("advapi32.dll", SetLastError = true)] private static extern int LogonUser( string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int DuplicateToken( IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool RevertToSelf(); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern bool CloseHandle(IntPtr handle); #endregion #region Constants private const int LOGON32_LOGON_INTERACTIVE = 2; private const int LOGON32_PROVIDER_DEFAULT = 0; #endregion #region Attributes private WindowsImpersonationContext mImpersonationContext = null; #endregion #region Public methods. public Impersonator( string userName, string domainName, string password) { impersonateValidUser(userName, domainName, password); } #endregion #region IDisposable member. public void Dispose() { undoImpersonation(); } #endregion #region Private member. private void impersonateValidUser(string userName, string domain, string password) { WindowsIdentity tempWindowsIdentity = null; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; try { if ( RevertToSelf() ) { if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 ) { tempWindowsIdentity = new WindowsIdentity( tokenDuplicate ); mImpersonationContext = tempWindowsIdentity.Impersonate(); } else { throw new Win32Exception( Marshal.GetLastWin32Error() ); } } else { throw new Win32Exception( Marshal.GetLastWin32Error() ); } } else { throw new Win32Exception( Marshal.GetLastWin32Error() ); } } finally { if ( token != IntPtr.Zero ) { CloseHandle( token ); } if ( tokenDuplicate != IntPtr.Zero ) { CloseHandle( tokenDuplicate ); } } } /// <summary> /// Reverts the impersonation. /// </summary> private void undoImpersonation() { if ( mImpersonationContext != null ) { mImpersonationContext.Undo(); } } #endregion } 
+5
source share
2 answers

You cannot use UseShellExecute = true when impersonating. This is due to how the shell works in Windows. Basically, everything is transferred to the shell, which looks at how to process the verb (β€œopen” in your case), and then launches the application as a user who owns a shell that is not an impersonated user - an impersonated user does not actually have a shell if there is no session!

Although you use a different mechanism to impersonate the user, the documentation for UseShellExecute still applies in your case:

UseShellExecute must be false if the UserName property is not null or an empty string, or an InvalidOperationException will be InvalidOperationException when the Process.Start(ProcessStartInfo) method is Process.Start(ProcessStartInfo) .

To solve this problem, it may be easiest to find the registered application yourself, as described in this answer: Search for the default application to open a specific file type in Windows . Using the path to the associated application, you can run the executable file as another user.

+2
source

Here is the complete class that I use to impersonate the user:

 /// <summary> /// Provide a context to impersonate operations. /// </summary> [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public class Impersonate : IDisposable { /// <summary> /// Initialize a new instance of the ImpersonateValidUser class with the specified user name, password, and domain. /// </summary> /// <param name="userName">The user name associated with the impersonation.</param> /// <param name="password">The password for the user name associated with the impersonation.</param> /// <param name="domain">The domain associated with the impersonation.</param> /// <exception cref="ArgumentException">If the logon operation failed.</exception> public Impersonate(string userName, SecureString password, string domain) { ValidateParameters(userName, password, domain); WindowsIdentity tempWindowsIdentity; IntPtr userAccountToken = IntPtr.Zero; IntPtr passwordPointer = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; try { if (Kernel32.RevertToSelf()) { // Marshal the SecureString to unmanaged memory. passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password); if (Advapi32.LogonUser(userName, domain, passwordPointer, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref userAccountToken)) { if (Advapi32.DuplicateToken(userAccountToken, 2, ref tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); if (impersonationContext != null) { return; } } } } } finally { // Zero-out and free the unmanaged string reference. Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer); // Close the token handle. if (userAccountToken != IntPtr.Zero) Kernel32.CloseHandle(userAccountToken); if (tokenDuplicate != IntPtr.Zero) Kernel32.CloseHandle(tokenDuplicate); } throw new ArgumentException(string.Format("Logon operation failed for userName {0}.", userName)); } /// <summary> /// Reverts the user context to the Windows user represented by the WindowsImpersonationContext. /// </summary> public void UndoImpersonation() { impersonationContext.Undo(); } /// <summary> /// Releases all resources used by <see cref="Impersonate"/> : /// - Reverts the user context to the Windows user represented by this object : <see cref="System.Security.Principal.WindowsImpersonationContext"/>.Undo(). /// - Dispose the <see cref="System.Security.Principal.WindowsImpersonationContext"/>. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (impersonationContext != null) { //UndoImpersonation(); impersonationContext.Dispose(); impersonationContext = null; } } } private void ValidateParameters(string userName, SecureString password, string domain) { if (string.IsNullOrWhiteSpace(userName)) { throw new ArgumentNullException("userName"); } if (password == null || password.Length == 0) { throw new ArgumentNullException("password"); } if (string.IsNullOrWhiteSpace(userName)) { throw new ArgumentNullException("domain"); } } private WindowsImpersonationContext impersonationContext; private const int LOGON32_LOGON_INTERACTIVE = 2; public const int LOGON32_PROVIDER_DEFAULT = 0; } 

how to convert a string to a protected string:

 var secure = new SecureString(); foreach (char c in "myclearpassword") { secure.AppendChar(c); } 

SO You can use it like this:

 using (var imp = new Impersonate(DomainUserID, SecurePassword, Domain)) { Process.Start(filePath); } 
0
source

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


All Articles