Search code examples
c#impersonation

use Process.Start while Impersonating (Window Application)


I'm trying to use Process.Start() under Impersonation, i have google for few days, most answer i come across was under ASP.net, but I'm developing for Window Application, so I'm having difficulty to find the root cause.

This is my impersonate 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 document through my program (none .exe, such as .txt, .doc)

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

So far I'm able to detect the directory/file with the impersonate user, which suppose to be invisible to my current login user since i did not grant it the access. But whenever i try to open the document, i get error

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

Please correct me if I'm wrong, so in this scenario, I'm not suppose to set the UseShellExecute to false (and processStartInfo with username and password input too?) because it's for executable file(?), and i do come across with CreateProcessAsUser function as well, and this function doesn't seems to be applicable to my case too, from the example i read it's for the .exe file too.

Would be appreciate if anyone could enlightenment me.

update: impersonate 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
}

Solution

  • You can't use UseShellExecute = true when impersonating. This is related to the way how shell execution works in Windows. Basically everything is passed to the shell which looks up how to handle the verb ("open" in your case) and then starts the application under the user owning the shell, which is not the impersonated user - the impersonated user doesn't actually have a shell if there is no session!

    Although you use a different mechanism for impersonating a 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 thrown when the Process.Start(ProcessStartInfo) method is called.

    To solve this issue it might be the easiest to look up the registered application yourself as described in this answer: Finding the default application for opening a particular file type on Windows. With the path to the associated application you can then start the executable as the other user.