【问题标题】:Impersonating a Windows user模拟 Windows 用户
【发布时间】:2012-04-12 04:18:26
【问题描述】:

我正在使用代码来模拟用户帐户以访问文件共享。

public class Impersonator :
    IDisposable
{
    #region Public methods.
    // ------------------------------------------------------------------

    /// <summary>
    /// Constructor. Starts the impersonation with the given credentials.
    /// Please note that the account that instantiates the Impersonator class
    /// needs to have the 'Act as part of operating system' privilege set.
    /// </summary>
    /// <param name="userName">The name of the user to act as.</param>
    /// <param name="domainName">The domain name of the user to act as.</param>
    /// <param name="password">The password of the user to act as.</param>
    public Impersonator(
        string userName,
        string domainName,
        string password )
    {
        ImpersonateValidUser( userName, domainName, password );
    }

    // ------------------------------------------------------------------
    #endregion

    #region IDisposable member.
    // ------------------------------------------------------------------

    public void Dispose()
    {
        UndoImpersonation();
    }

    // ------------------------------------------------------------------
    #endregion

    #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);

    private const int LOGON32_LOGON_INTERACTIVE = 2;
    private const int LOGON32_PROVIDER_DEFAULT = 0;

    // ------------------------------------------------------------------
    #endregion

    #region Private member.
    // ------------------------------------------------------------------

    /// <summary>
    /// Does the actual impersonation.
    /// </summary>
    /// <param name="userName">The name of the user to act as.</param>
    /// <param name="domainName">The domain name of the user to act as.</param>
    /// <param name="password">The password of the user to act as.</param>
    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 );
                        impersonationContext = 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 ( impersonationContext!=null )
        {
            impersonationContext.Undo();
        }   
    }

    private WindowsImpersonationContext impersonationContext = null;

    // ------------------------------------------------------------------
    #endregion
}

然后使用:

using (new Impersonator("username", "domain", "password"))
        {
            Process.Start("explorer.exe", @"/root,\\server01-Prod\abc");
        }

我收到“拒绝访问”错误。

此用户可能有权访问此共享。我可以映射驱动器,使用“网络使用”,但此代码不起作用。现在我认为这是代码。有没有人看到什么?有更好的方法吗?

【问题讨论】:

  • 这是从哪里运行的?如果这是托管在 IIS 上的应用程序,则默认 IIS 用户可能没有模拟权限
  • 我记得我遇到过类似的问题,请尝试使用您的管理员用户在简单的控制台应用程序中运行此代码。我实际上不确定您是否能够通过在 IIS 上运行的 Web 应用程序执行此操作。这与 ASP.NET 用户的权限有关(据我所知)

标签: c# windows impersonation


【解决方案1】:

试试这个:

[DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken);

用法:

IntPtr userToken = IntPtr.Zero;

bool success = External.LogonUser(
  "john.doe", 
  "domain.com", 
  "MyPassword", 
  (int) AdvApi32Utility.LogonType.LOGON32_LOGON_INTERACTIVE, //2
  (int) AdvApi32Utility.LogonProvider.LOGON32_PROVIDER_DEFAULT, //0
  out userToken);

if (!success)
{
  throw new SecurityException("Logon user failed");
}

using (WindowsIdentity.Impersonate(userToken))
{
 Process.Start("explorer.exe", @"/root,\\server01-Prod\abc");
}

【讨论】:

  • 我正在使用同一段代码并看到我似乎无法解释的这种行为:有时 LogonUser 返回退出代码 0 并且 Win32Exceptionis: "System.ComponentModel.Win32Exception: The目录名无效”。但是 10 分钟后重新运行相同的功能就可以了。你碰巧知道为什么吗?如有必要,我可以为您提供更多背景信息。
【解决方案2】:

如果我理解正确,您的意图是在模拟上下文中运行该过程。

来自 CreateProcess 的文档(由 Process.Start 使用)说: 如果调用进程正在模拟另一个用户,则新进程将使用调用进程的令牌,而不是模拟令牌。要在模拟令牌所代表的用户的安全上下文中运行新进程,请使用 CreateProcessAsUser 或 CreateProcessWithLogonW 函数。

所以,您使用了错误的 API 来执行此操作。

【讨论】:

    【解决方案3】:

    当您调用Process.Start 并传入一个包含您要运行该进程的用户名、密码和域的ProcessStartInfo 实例而不是使用您的Impersonator 类时会发生什么?

    也许,如果可行,那么您的 Impersonator 类应该创建一个 ProcessStartInfo 实例并使用它来创建新进程(将其封装在类本身中)。

    var psi = new ProcessStartInfo("explorer.exe", @"/root,\\server01-Prod\abc");
    psi.Domain = domain;
    psi.UserName = username;
    psi.Password = password;
    psi.WorkingDirectory = workingDir;
    
    Process.Start(psi);
    

    另外,根据MSDN docs...

    设置域、用户名和密码属性 ProcessStartInfo 对象是启动一个 使用用户凭据进行处理。

    您还应该在使用不同的用户凭据启动进程时设置工作目录。

    【讨论】:

    • 我没有收到错误,但我期待资源管理器窗口打开。 psi.Domain = "域"; psi.UserName = "用户"; psi.密码=密码; psi.FileName = "explorer.exe"; psi.Arguments = @"/root,\\server01-pv\abc"; psi.ErrorDialog = true; psi.UseShellExecute = 假; Process.Start(psi);
    • 设置UseShellExecute = true时会发生什么?
    • 另外,如果你设置ErrorDialog = true,我认为你必须设置UseShellExecute = true。并在ProcessStartInfo 实例上设置WorkingDirectory
    • 这样做了,我得到了一个错误。 Process 对象必须将 UseShellExecute 属性设置为 false 才能以用户身份启动进程 Code - psi.Domain = "domain"; psi.UserName = "用户"; psi.密码=密码; psi.WorkingDirectory = @"C:\WINDOWS"; psi.FileName = "explorer.exe"; psi.Arguments = @"/root,\\server-01-PV\abc"; psi.ErrorDialog = true; psi.UseShellExecute = true; Process.Start(psi);
    • @KyleJohnson psi.UseShellExecute = false,不是吗?
    猜你喜欢
    • 2014-05-01
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 1970-01-01
    • 2013-01-28
    相关资源
    最近更新 更多