Process.Start() throws “Access Denied” after Imper

2020-03-31 04:52发布

I'm having a problem executing a file from a file server using Process.Start() + Impersonation. Please help me to solve my problem. Is there another way to do this?

This is the button click event.

private void btnOpen_Click(object sender, EventArgs e)
    {
        try
        {
            newUser = cls.ImpersonateUser("username", "domain", "password");

            string fileName = @"\\network_computer\Test\Test.doc";

            System.Diagnostics.Process.Start(fileName);
        }
        catch (Exception ex) { throw ex; }
        finally
        {
            if (newUser != null)
                newUser.Undo();
        }
    }

This is the impersonation class.

public class clsImpersonate
    {
        #region 'Impersonation'

        // group type enum
        public enum SECURITY_IMPERSONATION_LEVEL : int
        {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3
        }

        // obtains user token
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        // closes open handes returned by LogonUser
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        // creates duplicate token handle
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
            int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

        private System.Security.Principal.WindowsImpersonationContext newUser;

        public WindowsImpersonationContext ImpersonateUser(string sUsername, string sDomain, string sPassword)
        {
            // initialize tokens
            IntPtr pExistingTokenHandle = new IntPtr(0);
            IntPtr pDuplicateTokenHandle = new IntPtr(0);
            pExistingTokenHandle = IntPtr.Zero;
            pDuplicateTokenHandle = IntPtr.Zero;

            // if domain name was blank, assume local machine
            if (sDomain == "")
                sDomain = System.Environment.MachineName;

            try
            {
                string sResult = null;

                const int LOGON32_PROVIDER_DEFAULT = 0;

                // create token
                const int LOGON32_LOGON_INTERACTIVE = 2;
                //const int SecurityImpersonation = 2;

                // get handle to token
                bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);

                // did impersonation fail?
                if (false == bImpersonated)
                {
                    int nErrorCode = Marshal.GetLastWin32Error();
                    sResult = "LogonUser() failed with error code: " + nErrorCode + "\r\n";

                    // show the reason why LogonUser failed
                    //MessageBox.Show(sResult, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                // Get identity before impersonation
                sResult += "Before impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";

                bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);

                // did DuplicateToken fail?
                if (false == bRetVal)
                {
                    int nErrorCode = Marshal.GetLastWin32Error();
                    CloseHandle(pExistingTokenHandle); // close existing handle
                    sResult += "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";

                    // show the reason why DuplicateToken failed
                    //MessageBox.Show(sResult, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return null;
                }
                else
                {
                    // create new identity using new primary token
                    WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
                    WindowsImpersonationContext impersonatedUser = newId.Impersonate();

                    // check the identity after impersonation
                    sResult += "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";

                    //MessageBox.Show(sResult, "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return impersonatedUser;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                // close handle(s)
                if (pExistingTokenHandle != IntPtr.Zero)
                    CloseHandle(pExistingTokenHandle);
                if (pDuplicateTokenHandle != IntPtr.Zero)
                    CloseHandle(pDuplicateTokenHandle);
            }
        }

        #endregion
    }

1条回答
Emotional °昔
2楼-- · 2020-03-31 05:51

If you debug do you see any error in

bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);

or in

bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);

?

Inside your button click event, try add a Using statement:

private void btnOpen_Click(object sender, EventArgs e)
{
    try
    {
        using (newUser = cls.ImpersonateUser("username", "domain", "password"))
        {
            string fileName = @"\\network_computer\Test\Test.doc";
            System.Diagnostics.Process.Start(fileName);
        }
    }
    catch (Exception ex) { throw ex; }
    finally
    {
        if (newUser != null)
            newUser.Undo();
    }
}

ANSWER UPDATE (#01):

Try the following code:

public class clsImpersonate
{
    #region 'Impersonation'

    // obtains user token
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    // closes open handes returned by LogonUser
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

    public IntPtr ImpersonateUser(string sUsername, string sDomain, string sPassword)
    {
        // initialize token
        IntPtr pExistingTokenHandle = new IntPtr(0);

        // if domain name was blank, assume local machine
        if (sDomain == "")
            sDomain = System.Environment.MachineName;

        try
        {
            string sResult = null;

            const int LOGON32_PROVIDER_DEFAULT = 0;

            // create token
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // get handle to token
            bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);

            // did impersonation fail?
            if (false == bImpersonated)
            {
                int nErrorCode = Marshal.GetLastWin32Error();
                sResult = "LogonUser() failed with error code: " + nErrorCode + "\r\n";

                // show the reason why LogonUser failed
                //MessageBox.Show(sResult, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Get identity before impersonation
            sResult += "Before impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";

            return pExistingTokenHandle;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public bool FreeImpersonationResource(IntPtr _token)
    {
        // close handle(s)
        if (_token != IntPtr.Zero)
            return CloseHandle(_token);
        else return true;
    }

    #endregion
}

And then, the button click event:

private void btnOpen_Click(object sender, EventArgs e)
    {
        try
        {
            IntPtr token = cls.ImpersonateUser("username", "domain", "password");

            using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(token))
            {
                string fileName = @"\\network_computer\Test\Test.doc";
                System.Diagnostics.Process.Start(fileName);
            }

            cls.FreeImpersonationResource(token);
        }
        catch (Exception ex) { throw ex; }
    }

Note:
- If you use LOGON32_LOGON_INTERACTIVE inside LogonUser function, you don't need to duplicate the token, because the handle you get is a Primary Token that you can immediately use. (Take a look at this MSDN page).
- If you use a using statement for your impersonation context you don't need to Undo, because it "automatically undo" at the end of the using.
- If it still doesn't work, try using the IP Address of the server instead of its domain name.
- If it still doesn't work, the last thing I can suggest is to take your original code and move the entire method ImpersonateUser inside the button click event, and then execute the original instruction System.Diagnostics.Process.Start(fileName); immediately after the instruction System.Diagnostics.Process.Start(fileName);.

I hope at least some part of this answer will help :)

UPDATE (#02):

I've tried this myself and it's working, using the code in my previous edit with the following modification:

  • inside the class clsImpersonate you have to change the type for the LogonUser function to LOGON32_LOGON_NEW_CREDENTIALS:

    // create token
    const int LOGON32_LOGON_INTERACTIVE = 2;
    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
    
    // get handle to token
    bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
        LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
    
  • inside the button click event you have to change the way you use Process.Start(). Also check this link for some sample on Process.Start functionality

    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            IntPtr token = cls.ImpersonateUser("username", "domain", "password");
    
            using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(token))
            {
                string fileName = @"Winword.exe"; // the name of the sw you will use to open your file, I suppose MS Word
                string argument = @"\\network_computer\Test\Test.doc"; // the path pf the file you want to open
    
                Process process = new Process();
                ProcessStartInfo info = new ProcessStartInfo();
                info.FileName = fileName;
                info.Arguments = argument;
    
                process.StartInfo = info;
                process.Start();
            }
    
            cls.FreeImpersonationResource(token);
        }
        catch (Exception ex) { throw ex; }
    }
    
查看更多
登录 后发表回答