简体   繁体   English

模拟在本地工作,在服务器上失败

[英]Impersonation works locally, fails on server

Self service password reset site. 自助服务密码重置站点。 For certain operations, I am impersonating a technical user that has account operator privileges in the domain. 对于某些操作,我模拟了在域中具有帐户操作员特权的技术用户。 It works perfectly on my laptop, I can change a user's password, unlock the account or query the domain for all the locked accounts. 它可以在我的笔记本电脑上完美运行,我可以更改用户密码,解锁帐户或查询域中所有已锁定的帐户。

It even worked on the server until some 2 weeks ago. 它甚至可以在服务器上运行,直到大约两周前。 I tried to investigate for changes in our environment bot no one is aware of any change that could have had an effect on this. 我试图调查环境bot的变化,没有人知道可能对此产生影响的任何变化。

The best part is that I have no error messages at all. 最好的部分是我根本没有错误消息。 Marshal.GetLastWin32Error() is returning zero, which is basically "everything went OK". Marshal.GetLastWin32Error()返回零,这基本上是“一切正常”。

Here's the code where the impersonation happens: 这是模拟发生的代码:

    #region accountManagement

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

    WindowsImpersonationContext impersonationContext;

    private bool impersonateValidUser(String userName, String domain, String password)
    {
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_BATCH = 4;
        const int LOGON32_LOGON_SERVICE = 5;
        const int LOGON32_LOGON_UNLOCK = 7;
        const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
        const int LOGON32_PROVIDER_DEFAULT = 0;

        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            // Int32 result = LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
            // Response.Write(">>> " + result.ToString());
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_UNLOCK, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }

        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return false;
    }

    private void undoImpersonation()
    {
        impersonationContext.Undo();
    }

    #endregion

And here is the piece that calls it (irrelevant parts removed): 这就是它的名称(删除了不相关的部分):

                String iuUser = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.user"];
                String iuPass = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.pass"];
                String iuDomn = System.Configuration.ConfigurationManager.AppSettings["domain.name"];
                if (impersonateValidUser(iuUser, iuDomn, iuPass))
                {
                    try
                    {
                        email = user.Properties["mail"].Value.ToString();
                        user.Invoke("SetPassword", new object[] { pw1 });
                        user.Properties["LockOutTime"].Value = 0; //unlock account
                        user.CommitChanges();
                        user.Close();

                        undoImpersonation();

                        // clear form and redirect
                        pPopupSuccess.Visible = true;
                        hfSuccess.Value = "The account is unlocked now and the password has been reset successfully.";
                    }
                    catch (Exception ex)
                    {
                        Exception innerException = ex.InnerException;
                        DirectoryServicesCOMException exds = (DirectoryServicesCOMException)innerException;
                        String errorMessage = "<p><strong>Your password probably did not meet the requirements.</strong></p>";
                        errorMessage += "<p>" + System.Configuration.ConfigurationManager.AppSettings["domain.pwreqmessage"] + "</p>";
                        errorMessage += "<strong>Detailed error message:</strong><br />";
                        errorMessage += ex.Message;
                        errorMessage += "<br />";
                        errorMessage += ex.StackTrace;

                        if (innerException != null) { 
                            errorMessage = errorMessage + innerException.Message;
                        }
                        pPopupError.Visible = true;
                        hfErrorMessage.Value = errorMessage;
                    }
                }
                else
                {
                    // The impersonation failed. Include a fail-safe mechanism here.
                    pPopupError.Visible = true;
                    hfErrorMessage.Value = "<p>Impersonation error. Failed to elevate the rights to account operator. Please report this error to the Help Desk.</p>";
                    hfErrorMessage.Value += "<p>" + Marshal.GetLastWin32Error().ToString() + "</p>";
                }

What I keep getting is NOT an exception at the middle, but my own message at the very end of the second code piece saying the impersonation was not successful. 我一直得到的不是中间的异常,而是我在第二段代码结尾处的自述,即模拟未成功。 And Marshal.GetLastWin32Error() just returs zero. 而Marshal.GetLastWin32Error()只会归零。

What can go wrong here and how can I get more information on what's happening? 这里可能出什么问题,我如何获得正在发生的事情的更多信息? What can be different on the server that makes this code fail, while it's running OK on my dev PC? 在我的开发PC上运行正常时,导致此代码失败的服务器上可能有什么不同?

Sorry, it was a #PEBKAC. 抱歉,这是#PEBKAC。 Windows security log suggested that Windows安全日志建议

The user has not been granted the requested logon type at this machine. 用户尚未在此计算机上被授予请求的登录类型。

So the answer is "check your windows events!" 因此答案是“检查Windows事件!”

This is strange because I had been using this server with this technical account. 这很奇怪,因为我一直将此服务器与此技术帐户一起使用。 So I double-checked the user and put it back to the Administrators group of the server. 因此,我仔细检查了用户并将其放回服务器的Administrators组。 I think it is more than I really need, also a security risk, but I will do some experiments soon to see what is the minimum amount of rights locally to keep it working. 我认为这超出了我的实际需要,同时也存在安全风险,但是我将很快进行一些实验,以了解保持其正常运行所需的最低本地权限。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM