【问题标题】:use Process.Start while Impersonating (Window Application)在模拟时使用 Process.Start(窗口应用程序)
【发布时间】:2016-03-23 16:58:44
【问题描述】:

我正在尝试在 Impersonation 下使用 Process.Start(),我已经 google 了几天,我遇到的大多数答案都是在 ASP.net 下,但我正在为 Window Application 开发,所以我遇到了困难找到根本原因。

这是我的模拟代码

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

我正在尝试通过我的程序打开文档(无.exe,例如.txt、.doc)

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

到目前为止,我能够使用模拟用户检测目录/文件,假设我当前的登录用户不可见,因为我没有授予它访问权限。但是每当我尝试打开文档时,都会出错

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

如果我错了,请纠正我,所以在这种情况下,我不应该将 UseShellExecute 设置为 false(以及带有用户名和密码输入的 processStartInfo 吗?),因为它用于可执行文件(?),我确实也遇到了 CreateProcessAsUser 函数,而且这个函数似乎也不适用于我的情况,从我读到的示例中,它也适用于 .exe 文件。

如果有人能启发我,将不胜感激。

更新:模拟类

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
}

【问题讨论】:

  • 你能把你的 Impersonator 类的所有代码贴出来
  • 嗨,谢谢回复,我在问题中更新了我的课程
  • 对不起,我没有看到你的更新,我会尝试比较这两个实现。
  • 对不起,我刚才弄乱了格式,我刚刚更新了问题。我会测试你发布的代码并稍后在这里更新,干杯:-D
  • 它需要您 PInvoke RunProcessAsUser,这要实现这一点要困难得多。尝试 ping Microsoft 以获得咨询服务。

标签: c# impersonation


【解决方案1】:

这是我用来模拟用户的完整类:

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

如何将字符串转换为安全字符串:

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

所以你可以这样使用它:

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

【讨论】:

  • 对于模拟用户,我将其授予管理员角色,并授予对文档的完全控制权,我还需要为用户授予什么其他权限?
  • 用户对文件夹有权限吗?
  • 是的,我授予用户完全控制权,我可以通过模拟添加和删除,但我无法打开它:-/
  • 你看过这个帖子了吗:stackoverflow.com/questions/3936456/…
  • 是的,我看过这个帖子 b4,如果我的理解是正确的,CreateProcessAsUser 仅适用于可执行文件,并且 ProcessStartInfo.UserName + Password 属性迫使我将 UseShellExecute 设置为 false 也仅适用于 .exe .
【解决方案2】:

模拟时不能使用UseShellExecute = true。这与 shell 执行在 Windows 中的工作方式有关。基本上,所有内容都传递给 shell,它查找如何处理动词(在您的情况下为“打开”),然后在拥有 shell 的用户下启动应用程序,该用户 not 是模拟用户 -如果没有会话,模拟用户实际上没有 shell!

尽管您使用不同的机制来模拟用户,但 documentationUseShellExecute 仍然适用于您的情况:

如果UserName属性不为null或为空字符串,UseShellExecute必须为false,否则在调用Process.Start(ProcessStartInfo)方法时会抛出InvalidOperationException

要解决此问题,最简单的方法可能是按照此答案中的说明自行查找已注册的应用程序:Finding the default application for opening a particular file type on Windows。通过关联应用程序的路径,您可以以其他用户身份启动可执行文件。

【讨论】:

    猜你喜欢
    • 2017-02-20
    • 2013-04-06
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 2010-11-03
    相关资源
    最近更新 更多