Tuesday, September 13, 2016

AD : Attempt to compromise security (HRESULT: 0x800704F1)

The full error is:

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
Only the former was an option for me.

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
In other words, much the same as an IIS SSL certificate.

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;

try
{     
      ldapConnection = new DirectoryEntry(LDAP fqdn, user, password);               
}
with:
ldapConnection = new DirectoryEntry(LDAP fqdn, user, password, 
    Password,AuthenticationTypes.SecureSocketsLayer); 
Coming at this from another angle, this code also worked:

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!

Monday, September 12, 2016

OAuth2 : Verifying the ADFS JWT signature

I wrote up this post recently: OAuth2 : Verifying the Azure AD JWT signature.

So how do you do this with ADFS? You need ADFS 4.0 - Server 2016.

This has the following ADFS OAuth information ( ~ metadata) endpoints:



If we go to the "keys" endpoint, we see:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "hHk-...A6k",
      "x5t": "hHk-...A6k",
      "n": "tGy...w9Q",
      "e": "AQAB",
      "x5c": [
        "MIIC7..Log=="
      ]
    }
  ]
}

The information under "x5c" is the certificate that you need.

As  per the linked post, you need to wrap this with "---BEGIN--- ---END---" and copy / paste and you'll see that the signing key is now verified.

Enjoy!



Monday, September 05, 2016

B2C : The Process ID cannot be found for .NET Core exe

The .NET Core B2C sample is here.

I was helping to get it working on another Windows 10 box with VS 2015 when we got the error:

"The Process ID cannot be found for .NET Core exe".

Works fine on my Windows 8.1 box.

If we run .NET Core from the command line and then ran the project, it loaded but then localhost refused the connection.

So Mr. Google to the rescue and the vast majority of the suggestions were along the lines of:

"For me the problem was solved by closing down Visual Studio, deleting project.lock.json
and starting Visual Studio again"

That didn't help and neither did a whole lot of others.

Then I came across a reference along the lines of localhost certificates that aren't trusted for SSL.

And some bells started ringing because I hate IIS Express and don't use it but the Windows 10 machine did not have IIS installed so was running IIS Express by default.

(I always battle with IIS Express and SSL).

And the B2C sample uses https://localhost.

You need to trust the IIS Express self-signed certificate as per this post.

That fixed the problem.

How a trusted certificate issue caused an error message regarding a process ID is another issue entirely :-)

Enjoy!


Friday, September 02, 2016

Postman : Using Postman for Confidential Grant on ADFS

Continuing the series for ADFS 4.0 on Server 2016.

The confidential flow relies on a client_id and a secret_key to authenticate the user.

The gist for the Postman collection is here.

You need to update your ADFS FQDN and the client_id and secret_key.

ADFS returns:

{
  "access_token": "eyJ...ErQ",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "openid"
}

You can plug the access token into a JWT viewer.

Enjoy!