【问题标题】:How to use LogonUser properly to impersonate domain user from workgroup client如何正确使用 LogonUser 从工作组客户端模拟域用户
【发布时间】:2011-06-28 18:13:24
【问题描述】:

ASP.NET: Impersonate against a domain on VMWare

这个问题是我要问的,但答案没有提供有关如何派生 _token 的详细信息。它似乎只使用WindowsIdentity.GetCurrent().Token,所以没有发生冒充。

Can I impersonate a user on a different Active Directory domain in .NET?

下一个问题的答案相互矛盾,接受的问题带有评论“我开始怀疑我的问题出在其他地方”。没有帮助。

LogonUser works only for my domain

下一个问题似乎暗示这是不可能的,但它涉及 2 个域,所以我不确定它是否相关。

我真正的问题是:

  • 有可能吗?如果可以,
  • 怎么做?我哪里做错了?

到目前为止我尝试的是,使用来自http://msdn.microsoft.com/en-us/library/chf6fbt4%28v=VS.80%29.aspx的代码

bool returnValue = LogonUser(user, domain, password,
            LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
            ref tokenHandle);
// after this point, returnValue = false

Win32错误是

登录失败:未知用户名或密码错误

【问题讨论】:

    标签: c# .net winforms c#-2.0 impersonation


    【解决方案1】:

    很少有帖子建议使用LOGON_TYPE_NEW_CREDENTIALS 而不是LOGON_TYPE_NETWORKLOGON_TYPE_INTERACTIVE。我在一台机器连接到域而另一台没有连接到域时遇到了模拟问题,这解决了它。 this post 中的最后一个代码 sn-p 表明在森林中模拟确实有效,但它没有具体说明建立信任的任何内容。所以这可能值得一试:

    const int LOGON_TYPE_NEW_CREDENTIALS = 9;
    const int LOGON32_PROVIDER_WINNT50 = 3;
    bool returnValue = LogonUser(user, domain, password,
                LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,
                ref tokenHandle);
    

    MSDN says LOGON_TYPE_NEW_CREDENTIALS 仅在使用 LOGON32_PROVIDER_WINNT50 时有效。

    【讨论】:

    • 请注意,LOGON_TYPE_NEW_CREDENTIALS 在用于访问网络资源之前似乎不会验证凭据,因此它不能像LOGON_TYPE_NETWORK 那样用于身份验证。
    • 我在模拟与我的应用程序运行的域不同的域时遇到了同样的问题。另一个域也没有信任。我会收到无意义的错误“系统找不到指定的文件”。像您上面建议的那样将 0,2 更改为 9,3 有效!现在我可以从不同域的文件共享中读取文件。谢谢你,你让我免于痛苦!
    • 谢谢,它有效,但整个问题很奇怪。我尝试在同一网络中进行身份验证以模拟,但在使用 LOGON32_LOGON_INTERACTIVE 时总是收到“未知用户名或密码错误”消息。在我的开发机器上一切正常。我很想追根溯源,但我该从哪里开始呢?
    • @Daniel 好问题。我只是通过试验文档中给出的选项才发现这一点,而不是因为我知道它是如何工作的或为什么工作;-)
    【解决方案2】:

    这对我有用,完整的工作示例(我希望更多人会这样做):

    //logon impersonation
    using System.Runtime.InteropServices; // DllImport
    using System.Security.Principal; // WindowsImpersonationContext
    using System.Security.Permissions; // PermissionSetAttribute
    
    ...
    
    class Program {
    
        // 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 void DoWorkUnderImpersonation() {
            //elevate privileges before doing file copy to handle domain security
            WindowsImpersonationContext impersonationContext = null;
            IntPtr userHandle = IntPtr.Zero;
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;
            string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
            string user = ConfigurationManager.AppSettings["ImpersonationUser"];
            string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
    
            try {
                Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);
    
                // if domain name was blank, assume local machine
                if (domain == "")
                    domain = System.Environment.MachineName;
    
                // Call LogonUser to get a token for the user
                bool loggedOn = LogonUser(user,
                                            domain,
                                            password,
                                            LOGON32_LOGON_INTERACTIVE,
                                            LOGON32_PROVIDER_DEFAULT,
                                            ref userHandle);
    
                if (!loggedOn) {
                    Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
                    return;
                }
    
                // Begin impersonating the user
                impersonationContext = WindowsIdentity.Impersonate(userHandle);
    
                Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);
    
                //run the program with elevated privileges (like file copying from a domain server)
                DoWork();
    
            } catch (Exception ex) {
                Console.WriteLine("Exception impersonating user: " + ex.Message);
            } finally {
                // Clean up
                if (impersonationContext != null) {
                    impersonationContext.Undo();
                }
    
                if (userHandle != IntPtr.Zero) {
                    CloseHandle(userHandle);
                }
            }
        }
    
    
        private void DoWork() {
            //everything in here has elevated privileges
    
            //example access files on a network share through e$ 
            string[] files = System.IO.Directory.GetFiles(@"\\domainserver\e$\images", "*.jpg");
        }
    }
    

    【讨论】:

    • 如何防止password 的内容通过内存转储或附加的调试器泄漏到受感染的系统上?我注意到你把它留在了记忆中。
    • 这些 API 太挑剔了。我得到了几乎相同的解决方案,但由于 logonprovider 和 logontype 的错误组合而失败。与默认值相关的交互很好,而 newcredentials 仅适用于 winnt50...使用这种方法,Environment.Username 真正返回模拟帐户,如果这是您需要的,请使用此解决方案。
    【解决方案3】:

    我遇到了同样的问题。不知道你是否解决了这个问题,但我真正想做的是使用 AD 凭据访问网络共享。在这种情况下,您需要使用 WNetAddConnection2()

    【讨论】:

      【解决方案4】:

      我已经成功地模拟了另一个域中的用户,但在两个域之间建立了信任。

      var token = IntPtr.Zero;
      var result = LogonUser(userID, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
      if (result)
      {
          return WindowsIdentity.Impersonate(token);
      }
      

      【讨论】:

        【解决方案5】:

        最好使用 SecureString:

        var password = new SecureString();
        var phPassword phPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
        IntPtr phUserToken;
        LogonUser(username, domain, phPassword, LOGON32_LOGON_INTERACTIVE,  LOGON32_PROVIDER_DEFAULT, out phUserToken);
        

        还有:

        Marshal.ZeroFreeGlobalAllocUnicode(phPassword);
        password.Dispose();
        

        函数定义:

        private static extern bool LogonUser(
          string pszUserName,
          string pszDomain,
          IntPtr pszPassword,
          int dwLogonType,
          int dwLogonProvider,
          out IntPtr phToken);
        

        【讨论】:

        • 是的,底层 winapi 的文档对此非常清楚。引用“使用完密码后,通过调用 SecureZeroMemory 函数从内存中清除密码。有关保护密码的更多信息,请参阅处理密码。”
        【解决方案6】:

        无效的登录名/密码也可能与您的 DNS 服务器中的问题有关 - 这就是发生在我身上的事情,让我损失了 5 个小时的生命。看看能不能在域名上指定ip地址。

        【讨论】:

        • 那么 DNS 问题是什么。我想我在我们的一个网站上遇到了这个问题
        猜你喜欢
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多