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

0

I had to create a new dev environment with less VMs as my IDE kept crashing, so I created a VM with AD and IIS on the same server.

I was using the following code fine in my old environment:

      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();
        }

I can still save the user and it appears in AD, however SetPassword no longer works:

[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)

My service account is a Domain Admin and I get the same error if I try my own AD creds.

I have tired calling SetPassword() before Save() but it fails at the same point.

The only difference is that I have AD and IIS on the same server. I have tired both Jetbrains Rider and VS2019. I am getting very close to my project deadline and I am really stuck.

None of the users have 'User Cannot Change Password Set' and the new users don't have any options under 'Account Options' set.

asp.net
active-directory
windows-server-2016
asked on Stack Overflow Oct 10, 2019 by Hawke

1 Answer

1

SetPassword sets the unicodePwd attribute. That has some restrictions on when it can be updated. The documentation for that says:

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.

It should setup a secure connection by default (it does for me), but it's possible that it can't in your setup for whatever reason.

You can pass a ContextOptions object in the constructor to your PrincipalContext. By default that is automatically set to ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing, which should be secure. But ContextOptions.Negotiate uses "either Kerberos or NTLM", and ContextOptions.Signing (the encryption) depends on Kerberos. So maybe it's falling back to NTLM and can't encrypt.

You might be able to confirm this by inspecting these values, after you create the account:

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

The values you'd be looking for are:

Negotiate, Signing, Sealing
Secure, Signing, Sealing

That's what I have when SetPassword works. But I'm not sure if it actually changes those values if it falls back to NTLM. Sometimes it does that pretty silently.

In any case, if Kerberos isn't happening, you can either troubleshoot that, or attempt to connect via LDAPS (LDAP over SSL). That would look something like this:

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

But that can cause other issues since your DC needs to have a certificate that you trust.

answered on Stack Overflow Oct 10, 2019 by Gabriel Luci

User contributions licensed under CC BY-SA 3.0