Being able to have people log into websites using credentials they already use is a huge advantage. Hosting those websites in DMZ zones away from domain controllers means that you need to do this via LDAP, but this isn’t secure since the query results are sent in plain text. Enter LDAPS, which from the research I did online seems to be the most misunderstood technology I have used to date, with very few documented examples.
It’s a pretty simple idea – wrap LDAP in a TLS session. The LDAP client connects to the server. The server sends over its certificate, which is used to authenticate it as being the actual server the client expects. This is critical since the webserver is delegating authentication to it. A TLS session is established, and the client binds to the directory with credentials sent in plain text, but TLS provides the security against eavesdropping.
What is absolutely baffling is how many big companies are completely oblivious to getting this working correctly. I deal with one of the big players in the email scanning space which offers LDAPS authentication so customers can use their normal Active Directory credentials to check their spam. I had bought a £500 VeriSign certificate expressly for this purpose, but when this certificate expired I discovered that this company still had connectivity to the domain controller. They are clearly not even checking the chain of trust on the certificate, even though they allow it to authenticate users! It would be relatively trivial to exploit this to impersonate a particular domain controller, insert an appropriate dummy user account, and hack any of their customers who use federated authentication. Security by DNS is not security (think man-in-the-middle, or cache-poisoning).
I have also worked with a supposedly major website CMS system which cannot get this right, despite claims by the vendor that it supports LDAPS. It categorically doesn’t – LDAP on a different TCP port is still LDAP.
There seems to be a lot of conflicting information out there on how this works and how the certificates are involved, so this is the overview as I understand it:
- The LDAPS client initiates a connection to the DC on TCP636 specifiying the DC’s FQDN (IP address or hostname only are not sufficient).
- The DC checks through its Local Computer certificate store for a certificate with the same FQDN, then signs using its private key as proof of ID. The certificate is sent to the client, so the client gets the DC’s public key.
- The LDAPS client checks the chain of trust on that certificate. If the CA is not a trusted public one (like VeriSign) the client system will need to have the issuing CA’s own certificate pre-loaded into its Local Computer Trusted Root Certification Authorities certificate store. Remember, the web server is not in the Active Directory domain so it won’t implicity trust that CA.
- Once the client trusts that the DC is genuine, the SSL session is established and the client can bind to the directory using plain text Active Directory account credentials.
Configuring your Domain Controller for LDAPS
This knowledgebase article outlines the steps the DC goes through to select a certificate, and includes details of how to create the CSR for external certification authorites: http://support.microsoft.com/kb/321051
The information there is pretty good, and there aren’t many gotchas. If you’re aiming for third party servers to bind to your AD then you will probably want an SSL certificate from a commercial CA. Be aware that you will need a certificate with a Subject Alternate Name (to have two hostnames registered) if you use a different internal FQDN for that server. The cheap public CAs like GoDaddy could not offer this the last time I enquired (admittedly about two years ago). If the website that will be binding to your directory is your own then you won’t want to buy a certificate. If you don’t already have it, install the Active Directory Certificate Services role to one of your Windows domain servers. That whole process is somewhat outside the scope of this blog post. Heed Microsoft’s warning from their Technet LDAP over SSL wiki:
Warning Before you install a certification authority (CA), you should be aware that you are creating or extending a public key infrastructure (PKI). Be sure to design a PKI that is appropriate for your organization. See PKI Design Brief Overview for additional information.
DCs should auto-enroll for their own certificates once that’s up and running. As per that Microsoft KB article that’s all you need to do. DCs will respond to LDAPS requests providing they can find a valid SSL certificate. I did run into some issues setting up my PKI which I have documented in another post.
If you’re using your own Active Directory domain’s CA, export the CA’s own certificate by running:
certutil -ca.cert c:\temp\domain-SERVERNAME-CA.cer
Configuring the DMZ webserver
Assuming your domain controller isn’t in public DNS, add its FQDN to the hosts file on your DMZ webserver.
Use the Certificates MMC snap-in to import the CA’s certificate you exported earlier (run MMC.EXE, File -> Add/Remove Snap-in -> Certificates -> Computer Account -> Local Computer -> Trusted Root Certification Authorities -> Certificates -> right-click -> All Tasks -> Import).
Example C# code snippet for the website:
NetworkCredential networkCredentials = new System.Net.NetworkCredential(adminUsername, adminPassword);
LdapDirectoryIdentifier ldapDirectoryIdentifier = new LdapDirectoryIdentifier(ldapServer, ldapPort);
LdapConnection ldapConnection = new LdapConnection(ldapDirectoryIdentifier, networkCredentials, AuthType.Basic);
ldapConnection.SessionOptions.SecureSocketLayer = true;
Verifying your LDAPS server using OpenLDAP
Since in my case I was not developing the .Net code myself, I found it useful to use OpenLDAP to demonstrate to our developers that the server and firewall were definitely working as expected. I had been unable to get the normal Windows LDAP troubleshooting tool LDP.EXE to work and it’s not particularly verbose during failures (as it turns out, I hadn’t been using the FQDN). OpenLDAP is extremely easy to install on any Linux distro with a package management system. I ended up using my Synology NAS at home. The format of the issuing CA certificate needs to be slightly different though (base64 encoded). On the CA server, use:
certutil -ca.cert c:\temp\domain-SERVERNAME-CA.cer
certutil -encode c:\temp\domain-SERVERNAME-CA.cer c:\temp\domain-SERVERNAME-CA.pem
Add the following line to OpenLDAP’s ldap.conf (mine was /opt/etc/openldap/ldap.conf):
Make sure of course to copy the .pem file to that location.
The ldapsearch syntax is quite a minefield but my sample query was as follows:
ldapsearch -x -v -H 'ldaps://ldap.domain.com' -b 'dc=domain,dc=com' -s base -D 'email@example.com' -W
which binds to the directory as firstname.lastname@example.org (you are prompted for a password), and requests only the top level directory objects.
It seems to be very difficult to compile OpenLDAP for Windows, in fact it’s actually impossible using MinGW (one of the dependencies CyrusSASL needs Visual Studio to compile).
I tried to use this binary build of OpenLDAP for Windows but it ignores ldap.conf because the people who compiled it forgot to define the variable sysconfdir in the configure script. I was able to use the excellent Process Monitor to track a failed filesystem read to a path featuring the static entry $SYSCONFDIR$ as ldapsearch.exe is invoked. Unfortunately as such you can’t add the CA cert location to the config.