System.DirectoryServices.AccountManagement.PrincipalOperationException: The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you. (Exception from HRESULT: 0x800704F1) ---> System.Runtime.InteropServices.COMException: The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you. (Exception from HRESULT: 0x800704F1)
This is described here:
MS16-101: Description of the security update for Windows authentication methods
Essentially, the updates disable the ability of the Negotiate process to fall back to NTLM when Kerberos authentication fails for password change operation.
I found these useful:
Enabling Secure LDAP on Windows Server 2008/2012 Domain Controllers
Enabling Secure LDAP on Windows Server 2008/2012 Domain Controllers: Configuration
There is a good write-up here:
Solution for Problem in Change Password after Windows Security Update
TL;DR
- Enable LDAPS
- Set "Trusted Hosts"
- Revert the update
And so began a journey into the depths of the Internet - or more specifically the huge amount of garbage, rubbish and misinformation out there :-(
One of the problems was the connection string.
I naively assumed that I simply needed to change:
LDAP://fqdn to
LDAPS://fqdn
But from what I can see, there is no actual protocol called LDAPS? It's official name is "Secure LDAP" and the connection string you need is:
LDAP://fqdn:636
The "correct" way to enable it is to install an Enterprise Root CA on a Domain Controller.
That wasn't an option (and a massive overkill) so I started wondering about installing a self-signed certificate on the DC.
Disclaimer: Using self-signed certificates or installing certificates as follows may not be the best solution security wise. Use this at your own risk!
The certificate that you need has to follow certain rules e.g.
- It has to be for "Server Authentication" (OID: 1.3.6.1.5.5.7.3.1)
- The Subject name must match the Fully Qualified Domain Name (FQDN) of the host machine, such as Subject:CN=server.domain.com
- (This is also true for the first name in the Subject Alternative Name (SAN))
- The host machine account needs to have access to the private key
Once you have the .pfx file, and using "mmc" with:
Service Account / Local Computer
Select "Active Directory Domain Services".
and import.
You need this certificate in "NTDS\Personal" and "NTDS\Trusted Root Certification Authorities".
To check this, you need to run "ldp" from the command line.
This should result in:
"Host supports SSL, SSL cipher strength = 256 bits"
If you need to run "ldp" from a non-DC machine, you need Remote Server Administration Tools (RSAT) for Windows Client and Windows Server.
Once "ldp" runs OK, you now have secure LDAP.
Now for the C# .NET side:
Replace the ldapConnection below:
DirectoryEntry ldapConnection = null;with:
try
{
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password);
}
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password,Coming at this from another angle, this code also worked:
Password,AuthenticationTypes.SecureSocketsLayer);
Authenticating a user over LDAP in .Net: LdapConnection vs. PrincipalContext
Footnote: I also found that I could leave out the extra parameter:
"Password,AuthenticationTypes.SecureSocketsLayer"
if the user was "domain\user" rather than "user". Go figure!
Enjoy!