0

我在使用 Process.Start() + Impersonation 从文件服务器执行文件时遇到问题。请帮我解决我的问题。还有另一种方法可以做到这一点吗?

这是按钮单击事件。

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();
        }
    }

这是模拟类。

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
    }
4

1 回答 1

2

如果您调试,您是否看到任何错误

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

或在

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

?

在您的按钮单击事件中,尝试添加一条Using语句:

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();
    }
}

答案更新(#01):

试试下面的代码:

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
}

然后,按钮单击事件:

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; }
    }

注意:
- 如果您LOGON32_LOGON_INTERACTIVE在 LogonUser 函数中使用,则不需要复制令牌,因为您获得的句柄是您可以立即使用的 Primary Token。(看看这个 MSDN 页面)。
- 如果您在模拟上下文中使用using语句,则不需要Undo,因为它在 . 结尾处“自动撤消” using
- 如果还是不行,请尝试使用服务器的 IP 地址而不是其域名。
- 如果它仍然不起作用,我建议的最后一件事是获取您的原始代码并将整个方法移动ImpersonateUser到按钮单击事件中,然后在指令System.Diagnostics.Process.Start(fileName);之后立即执行原始指令System.Diagnostics.Process.Start(fileName);

我希望这个答案的至少一部分会有所帮助:)

更新(#02)

我自己试过这个并且它正在工作,使用我之前编辑中的代码并进行以下修改:

  • 在类clsImpersonate中,您必须将LogonUser函数的类型更改为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);
    
  • 在按钮单击事件中,您必须更改使用 Process.Start() 的方式。另请查看此链接以获取有关 Process.Start 功能的一些示例

    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; }
    }
    
于 2013-05-29T06:47:01.317 回答