Server 2016 AD和IIS Express无法设置用户密码但可以创建用户

Server 2016 AD and IIS Express Cannot Set User Password but Can Create User

由于我的 IDE 不断崩溃,我不得不创建一个虚拟机较少的新开发环境,因此我在同一台服务器上创建了一个带有 AD 和 IIS 的虚拟机。

我在我的旧环境中使用了以下代码:

      PrincipalContext ctx = new PrincipalContext(ContextType.Domain,
                Environment.GetEnvironmentVariable("DOMAIN"),
                Environment.GetEnvironmentVariable("USER_OU"),
                Environment.GetEnvironmentVariable("SERVICE_USERNAME"),
                Environment.GetEnvironmentVariable("SERVICE_PASSWORD"));

        UserPrincipalEx usr = new UserPrincipalEx(ctx);

        usr.Name = ticket.FirstName + " " + ticket.LastName;
        usr.SamAccountName = ticket.Username;
        usr.GivenName = ticket.FirstName;
        usr.Surname = ticket.LastName;
        usr.DisplayName = ticket.FirstName + " " + ticket.Account.LastName;
        usr.UserPrincipalName = ticket.Username + "@" + Environment.GetEnvironmentVariable("DOMAIN");
        usr.Enabled = enabled;

        try
        {
            usr.Save();
            usr.SetPassword(temppwd);
            usr.ExpirePasswordNow();
        }

我仍然可以保存用户并且它出现在 AD 中,但是 SetPassword 不再有效:

[IIS EXPRESS] Request started: "POST" https://localhost:5001/create
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
   --- End of inner exception stack trace ---
   at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
   at System.DirectoryServices.AccountManagement.SDSUtils.SetPassword(DirectoryEntry de, String newPassword)
   at System.DirectoryServices.AccountManagement.ADStoreCtx.SetPassword(AuthenticablePrincipal p, String newPassword)
   at System.DirectoryServices.AccountManagement.PasswordInfo.SetPassword(String newPassword)
   at System.DirectoryServices.AccountManagement.AuthenticablePrincipal.SetPassword(String newPassword)

我的服务帐户是域管理员,如果我尝试使用自己的 AD 凭据,我会遇到同样的错误。

我已经厌倦了在 Save() 之前调用 SetPassword() 但它在同一点失败了。

唯一的区别是我在同一台服务器上安装了 AD 和 IIS。 Jetbrains Rider 和 VS2019 都玩腻了。我的项目截止日期越来越近了,我真的被困住了。

None 的用户有 'User Cannot Change Password Set' 而新用户在 'Account Options' 下没有任何选项设置。

SetPassword 设置 unicodePwd 属性。这对何时可以更新有一些限制。 documentation for that 表示:

Windows 2000 operating system servers require that the client have a 128-bit (or better) SSL/TLS-encrypted connection to the DC in order to modify this attribute. On Windows Server 2003 operating system and later, the DC also permits modification of the unicodePwd attribute on a connection protected by 128-bit (or better) Simple Authentication and Security Layer (SASL)-layer encryption instead of SSL/TLS.

默认情况下它应该设置一个安全连接(对我来说是这样),但它可能出于某种原因不能在您的设置中。

您可以将构造函数中的 ContextOptions 对象传递给您的 PrincipalContext。默认情况下,它会自动设置为 ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing,这应该是安全的。但是 ContextOptions.Negotiate 使用 "either Kerberos or NTLM",而 ContextOptions.Signing(加密)依赖于 Kerberos。所以也许它正在退回到 NTLM 并且无法加密。

创建帐户后,您可能可以通过检查这些值来确认这一点:

Console.WriteLine(ctx.Options);
Console.WriteLine(((DirectoryEntry) usr.GetUnderlyingObject()).AuthenticationType);

您要寻找的值是:

Negotiate, Signing, Sealing
Secure, Signing, Sealing

这就是我在 SetPassword 工作时所拥有的。但我不确定如果它回退到 NTLM,它是否真的改变了这些值。有时它会很安静地做这件事。

无论如何,如果没有发生 Kerberos,您可以解决该问题,或者尝试通过 LDAPS(LDAP over SSL)进行连接。看起来像这样:

PrincipalContext ctx = new PrincipalContext(ContextType.Domain,
    Environment.GetEnvironmentVariable("DOMAIN") + ":636",
    Environment.GetEnvironmentVariable("USER_OU"),
    ContextOptions.Negotiate | ContextOptions.SecureSocketLayer,
    Environment.GetEnvironmentVariable("SERVICE_USERNAME"),
    Environment.GetEnvironmentVariable("SERVICE_PASSWORD"));

但这可能会导致其他问题,因为您的 DC 需要有您信任的证书。