【问题标题】:Getting the Current username when impersonated模拟时获取当前用户名
【发布时间】:2011-11-28 15:11:35
【问题描述】:

我正在使用类似以下方法在我的代码中模拟用户:

How do you do Impersonation in .NET?

在另一个类中,我需要找出当前用户(例如“mydomain\moose”),但我不知道我当前是否在冒充另一个用户。

如果我冒充他人,如何获取用户名?

System.Environment.UserName 和 System.Security.Principal.WindowsIdentity.GetCurrent().Name 都返回原始用户,而不是当前模拟的用户。

更多详情:

我正在做这种模拟,以便我可以访问用户通常无权访问的网络共享中的某些文件。

如果我使用 LOGON32_LOGON_INTERACTIVE 登录类型,我会看到新用户,但我无法访问网络共享。如果我使用 LOGON32_LOGON_NEW_CREDENTIALS 登录类型(值 9),我可以访问网络共享,但在 Environment.UserName 中看不到新用户。

【问题讨论】:

  • 它应该返回模拟用户。你确定你在查找当前用户的方法调用中被冒充了吗?
  • 我刚刚测试了使用模拟用户访问我的登录用户无权访问的目录。我可以访问该目录,但两种方法仍然返回原始用户。一旦我停止模拟,我就会拒绝访问尝试访问该目录,所以我知道它是正确模拟的。
  • 添加了更多细节。似乎是导致问题的登录类型。
  • msdn.microsoft.com/en-us/library/windows/desktop/…。你可能想试试LOGON32_LOGON_BATCH。对于 NEW_CREDENTIALS 它不起作用,因为“新的登录会话具有相同的本地标识符,但对其他网络连接使用不同的凭据。”

标签: c# .net


【解决方案1】:

首先,我想指出WindowsIdentity.GetCurrent().Name 的属性将返回什么 如果您使用LOGON32_LOGON_NEW_CREDENTIALSLOGON32_LOGON_INTERACTIVE 作为LogonUser(在模拟类中)函数的登录类型:

  1. 使用LOGON32_LOGON_INTERACTIVE

    // Assuming this code runs under USER_B
    
    using (var imp = new Impersonation("treyresearch", "USER_A", "SecurePwd", LOGON32_LOGON_INTERACTIVE ))
    {
      // Now, we run under USER_A
      Console.Out.WriteLine(WindowsIdentity.GetCurrent().Name); // Will return USER_A
    }
    
  2. 使用LOGON32_LOGON_NEW_CREDENTIALS

    // Assuming this codes runs under USER_B
    
    using (var imp = new Impersonation("treyresearch", "USER_A", "SecurePwd", LOGON32_LOGON_NEW_CREDENTIALS ))
    {
      Console.Out.WriteLine(WindowsIdentity.GetCurrent().Name); // Will return USER_B
    }
    

这是您在问题中描述的行为,并且与 MSDN 上对 LogonUser 函数的描述一致。对于LOGON32_LOGON_NEW_CREDENTIALS,创建的用户令牌只是当前用户令牌的克隆。这意味着创建的用户会话与调用线程具有相同的标识符。传递给LogonUser 函数的凭据仅用于出站网络连接。

其次,让我指出两种情况,LOGON32_LOGON_INTERACTIVELOGON32_LOGON_NEW_CREDENTIALS 之间的描述差异变得清晰:

  • 两台加入域的计算机:computer_A、computer_B
  • 两个用户:user_A(计算机_A 上的本地管理员)、user_B(B 上只有标准用户权限)
  • computer_B 上的一个网络共享(mynetworkshare,user_B 确实有权访问共享)。
  • computer_A 上的一个本地文件夹(只有 user_A 有权写入此文件夹)。

您在 computer_A 上运行程序(在 user_A 的帐户下)。您冒充 user_B(使用 LOGON32_LOGON_INTERACTIVE)。然后您连接到计算机_B 上的网络共享并尝试将文件复制到本地文件夹(只有 user_A 有权写入此文件夹)。然后,您会收到拒绝访问错误消息,因为文件操作是使用对本地文件夹没有权限的 user_B 的权限完成的。

情况同上。但是现在,我们使用LOGON32_LOGON_NEW_CREDENTIALS 来模拟user_B。我们连接到网络驱动器并将文件从网络驱动器复制到本地文件夹。在这种情况下操作成功,因为文件操作是在user_A的权限下完成的。

【讨论】:

  • 这确实有帮助。不幸的是,让我访问我需要的网络共享的唯一方法是使用 LOGON32_LOGON_NEW_CREDENTIALS,但是我看不到我是谁。因此,在这种特殊情况下,我将需要尽量不使用模拟,我认为无论如何这都是真正的解决方案。我将此标记为答案,因为它包含有关传递给 LogonUser() 的登录类型差异的最详细信息。
  • @Moose:如果您只需要知道您是否在模仿,请查看以下 stackoverflow 问题/答案:stackoverflow.com/questions/3973982/…
  • 我只需要知道如何获取用户身份,这正是我想要的。太棒了!
  • 这是对 LOGON32_LOGON_NEW_CREDENTIALS 工作原理的出色解释。谢谢!
【解决方案2】:

根据http://msdn.microsoft.com/en-us/library/chf6fbt4.aspx 的示例,当前身份在模拟期间发生变化。您确定您的代码在模拟代码块内吗?

【讨论】:

  • 是的,刚刚测试过。仍然没有运气。我知道我的模拟是正确的,但它仍然恢复了原始用户。
【解决方案3】:

我写了一个辅助类来做到这一点:

public static class ImpersonationUtils
{
    private const int SW_SHOW = 5;
    private const int TOKEN_QUERY = 0x0008;
    private const int TOKEN_DUPLICATE = 0x0002;
    private const int TOKEN_ASSIGN_PRIMARY = 0x0001;
    private const int STARTF_USESHOWWINDOW = 0x00000001;
    private const int STARTF_FORCEONFEEDBACK = 0x00000040;
    private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
    private const int TOKEN_IMPERSONATE = 0x0004;
    private const int TOKEN_QUERY_SOURCE = 0x0010;
    private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
    private const int TOKEN_ADJUST_GROUPS = 0x0040;
    private const int TOKEN_ADJUST_DEFAULT = 0x0080;
    private const int TOKEN_ADJUST_SESSIONID = 0x0100;
    private const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
    private const int TOKEN_ALL_ACCESS =
        STANDARD_RIGHTS_REQUIRED |
        TOKEN_ASSIGN_PRIMARY |
        TOKEN_DUPLICATE |
        TOKEN_IMPERSONATE |
        TOKEN_QUERY |
        TOKEN_QUERY_SOURCE |
        TOKEN_ADJUST_PRIVILEGES |
        TOKEN_ADJUST_GROUPS |
        TOKEN_ADJUST_DEFAULT |
        TOKEN_ADJUST_SESSIONID;

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct STARTUPINFO
    {
        public int cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public int dwX;
        public int dwY;
        public int dwXSize;
        public int dwYSize;
        public int dwXCountChars;
        public int dwYCountChars;
        public int dwFillAttribute;
        public int dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    private enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    private enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool CreateProcessAsUser(
        IntPtr hToken,
        string lpApplicationName,
        string lpCommandLine,
        ref SECURITY_ATTRIBUTES lpProcessAttributes,
        ref SECURITY_ATTRIBUTES lpThreadAttributes,
        bool bInheritHandles,
        int dwCreationFlags,
        IntPtr lpEnvironment,
        string lpCurrentDirectory,
        ref STARTUPINFO lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool DuplicateTokenEx(
        IntPtr hExistingToken,
        int dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpThreadAttributes,
        int ImpersonationLevel,
        int dwTokenType,
        ref IntPtr phNewToken);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool OpenProcessToken(
        IntPtr ProcessHandle,
        int DesiredAccess,
        ref IntPtr TokenHandle);

    [DllImport("userenv.dll", SetLastError = true)]
    private static extern bool CreateEnvironmentBlock(
            ref IntPtr lpEnvironment,
            IntPtr hToken,
            bool bInherit);

    [DllImport("userenv.dll", SetLastError = true)]
    private static extern bool DestroyEnvironmentBlock(
            IntPtr lpEnvironment);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(
        IntPtr hObject);

    private static void LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock, int sessionId)
    {
        var pi = new PROCESS_INFORMATION();
        var saProcess = new SECURITY_ATTRIBUTES();
        var saThread = new SECURITY_ATTRIBUTES();
        saProcess.nLength = Marshal.SizeOf(saProcess);
        saThread.nLength = Marshal.SizeOf(saThread);

        var si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        si.lpDesktop = @"WinSta0\Default";
        si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
        si.wShowWindow = SW_SHOW;

        if (!CreateProcessAsUser(
            token,
            null,
            cmdLine,
            ref saProcess,
            ref saThread,
            false,
            CREATE_UNICODE_ENVIRONMENT,
            envBlock,
            null,
            ref si,
            out pi))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateProcessAsUser failed");
        }
    }

    private static IDisposable Impersonate(IntPtr token)
    {
        var identity = new WindowsIdentity(token);
        return identity.Impersonate();
    }

    private static IntPtr GetPrimaryToken(Process process)
    {
        var token = IntPtr.Zero;
        var primaryToken = IntPtr.Zero;

        if (OpenProcessToken(process.Handle, TOKEN_DUPLICATE, ref token))
        {
            var sa = new SECURITY_ATTRIBUTES();
            sa.nLength = Marshal.SizeOf(sa);

            if (!DuplicateTokenEx(
                token,
                TOKEN_ALL_ACCESS,
                ref sa,
                (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                (int)TOKEN_TYPE.TokenPrimary,
                ref primaryToken))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error(), "DuplicateTokenEx failed");
            }

            CloseHandle(token);
        }
        else
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed");
        }

        return primaryToken;
    }

    private static IntPtr GetEnvironmentBlock(IntPtr token)
    {
        var envBlock = IntPtr.Zero;
        if (!CreateEnvironmentBlock(ref envBlock, token, false))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateEnvironmentBlock failed");
        }
        return envBlock;
    }

    public static void LaunchAsCurrentUser(string cmdLine)
    {
        var process = Process.GetProcessesByName("explorer").FirstOrDefault();
        if (process != null)
        {
            var token = GetPrimaryToken(process);
            if (token != IntPtr.Zero)
            {
                var envBlock = GetEnvironmentBlock(token);
                if (envBlock != IntPtr.Zero)
                {
                    LaunchProcessAsUser(cmdLine, token, envBlock, process.SessionId);
                    if (!DestroyEnvironmentBlock(envBlock))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error(), "DestroyEnvironmentBlock failed");
                    }
                }

                CloseHandle(token);
            }
        }
    }

    public static IDisposable ImpersonateCurrentUser()
    {
        var process = Process.GetProcessesByName("explorer").FirstOrDefault();
        if (process != null)
        {
            var token = GetPrimaryToken(process);
            if (token != IntPtr.Zero)
            {
                return Impersonate(token);
            }
        }

        throw new Exception("Could not find explorer.exe");
    }
}

你可以这样使用它:

ImpersonationUtils.LaunchAsCurrentUser("notepad");

using (ImpersonationUtils.ImpersonateCurrentUser())
{

}

您可以在此处找到更多解释和示例:

Impersonating CurrentUser from SYSTEM

【讨论】:

  • 我希望我能在几个月前找到这个!太晚了,我不得不自己弄清楚。尽管如此,我认为OP在问别的东西。他想确定用户名字符串,即使代码冒充其他人。
【解决方案4】:

【讨论】:

  • 你的意思是GetTokenInformation吗?不确定是否有可能在给定从 LogonUser 返回的模拟令牌 HANDLE 的情况下将 CredHandle 提供给 QueryCredentialAttributes。
猜你喜欢
  • 2013-04-19
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多