LDAPS or StartTLS? That is the question…

Due to the various security issues around the different SSL implementations, I’ve seen an increasing demand for OpenAM’s StartTLS support even though OpenAM perfectly supported LDAPS. In this post I’m going to show you how to set up both StartTLS and LDAPS in a dummy OpenDJ 2.6.2 environment, and then I’ll attempt to compare them from a security point of view.

NOTE: the instructions provided here for setting up secure connections are by no means the most accurate ones, as I only provide them for demonstration purposes. You should always consult the product documentation for much more detailed, and precise information.

Common Steps

Both LDAPS and StartTLS requires a private key, I’m just going to assume you know how to generate/convert (or obtain from a trusted CA) it.
Once you have your JKS file ready first make sure that it contains a PrivateKeyEntry:

$ keytool -list  -keystore server.jks 
Enter keystore password:  

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

1, 2015.04.22., PrivateKeyEntry, 
Certificate fingerprint (SHA1): AA:30:0D:8E:DE:4C:F9:AB:AC:FA:61:E7:B4:F5:56:EF:3E:F4:F6:4A

After verifying the keystore, copy it into the config folder with the name “keystore” and also create a keystore.pin file that contains the keystore’s password.

In order to make this JKS available for OpenDJ you’ll need to run the following dsconfig command (for this demo’s purpose we are going to reuse the existing Key/Trust Manager Providers):

dsconfig set-key-manager-provider-prop 
          --provider-name JKS 
          --set enabled:true 
          --hostname localhost 
          --port 4444 
          --trustStorePath /path/to/opendj/config/admin-truststore 
          --bindDN cn=Directory Manager 
          --bindPassword ****** 
          --no-prompt

Since key management always goes hand-in-hand with trust management, we need to set up the Trust Management Provider as well. For this, you’ll need to create a JKS keystore which only contains the public certificate and place it into the config folder with the name “truststore“, and again, you’ll need to create a truststore.pin file containing the truststore’s password.

dsconfig set-trust-manager-provider-prop 
          --provider-name JKS 
          --set enabled:true 
          --set trust-store-pin-file:config/truststore.pin 
          --hostname localhost 
          --port 4444 
          --trustStorePath /path/to/opendj/config/admin-truststore 
          --bindDN cn=Directory Manager 
          --bindPassword ****** 
          --no-prompt

LDAPS

From protocol point of view LDAPS is not too different from HTTPS actually: in order to establish a connection to the directory, the client MUST perform an SSL/TLS Handshake with the server first, hence all the LDAP protocol messages are transported on a secure, encrypted channel.
Configuring and enabling the LDAPS Connection Handler in OpenDJ doesn’t really take too much effort:

dsconfig set-connection-handler-prop 
          --handler-name LDAPS Connection Handler 
          --set enabled:true 
          --set listen-port:1636 
          --hostname localhost 
          --port 4444 
          --trustStorePath /path/to/opendj/config/admin-truststore 
          --bindDN cn=Directory Manager 
          --bindPassword ****** 
          --no-prompt

After this you will need to restart the directory server, during the startup you should see the following message:

[22/04/2015:20:47:26 +0100] category=PROTOCOL severity=NOTICE msgID=2556180 msg=Started listening for new connections on LDAPS Connection Handler 0.0.0.0 port 1636

To test the connection, you can just run a simple ldapsearch command:

$ bin/ldapsearch -Z -h localhost -p 1636 -D "cn=Directory Manager" -w ****** -b dc=example,dc=com "uid=user.0" "*"
The server is using the following certificate: 
    Subject DN:  EMAILADDRESS=peter.major@forgerock.com, CN=aldaris.sch.bme.hu, OU=Sustaining, O=ForgeRock Ltd, L=Bristol, C=GB
    Issuer DN:  EMAILADDRESS=peter.major@forgerock.com, CN=aldaris.sch.bme.hu, OU=Sustaining, O=ForgeRock Ltd, L=Bristol, C=GB
    Validity:  Wed Apr 22 19:43:22 BST 2015 through Thu Apr 21 19:43:22 BST 2016
Do you wish to trust this certificate and continue connecting to the server?
Please enter "yes" or "no":

As you can see I’ve been prompted to accept my self-signed certificate, and after entering “yes”, I could see the entry I’ve been looking for.

StartTLS

StarTLS for LDAP is slightly different from LDAPS, the main difference being, that first the client needs to establish an unencrypted connection with the directory server. At any point in time after establishing the connection (as long as there are no outstanding LDAP operations on the connection), the StartTLS extended operation shall be sent across to the server. Once a successful extended operation response has been received, the client can initiate the TLS handshake over the existing connection. Once the handshake is done, all future LDAP operations will be transmitted on the now secure, encrypted channel.
Personally my concerns with StartTLS are:

  • You must have a plain LDAP port open on the network.
  • Even after a client connects to the directory there is absolutely nothing preventing the user from sending BIND or any other kind of requests on the unencrypted channel before actually performing the StartTLS extended operation.

Now let’s see how to set up StartTLS:

dsconfig set-connection-handler-prop 
          --handler-name LDAP Connection Handler 
          --set allow-start-tls:true 
          --set key-manager-provider:JKS 
          --set trust-manager-provider:JKS 
          --hostname localhost 
          --port 4444 
          --trustStorePath /path/to/opendj/config/admin-truststore 
          --bindDN cn=Directory Manager 
          --bindPassword ****** 
          --no-prompt

Restart the server, and then verify that the connection works, run:

$ bin/ldapsearch -q -h localhost -p 1389 -D "cn=Directory Manager" -w ****** -b dc=example,dc=com "uid=user.0" "*"
The server is using the following certificate: 
    Subject DN:  EMAILADDRESS=peter.major@forgerock.com, CN=aldaris.sch.bme.hu, OU=Sustaining, O=ForgeRock Ltd, L=Bristol, C=GB
    Issuer DN:  EMAILADDRESS=peter.major@forgerock.com, CN=aldaris.sch.bme.hu, OU=Sustaining, O=ForgeRock Ltd, L=Bristol, C=GB
    Validity:  Wed Apr 22 19:43:22 BST 2015 through Thu Apr 21 19:43:22 BST 2016
Do you wish to trust this certificate and continue connecting to the server?
Please enter "yes" or "no":

Again, you can see that the entry is returned just fine after accepting the server certificate. For the sake of testing you can remove the “-q” (–useStartTLS) parameter from the ldapsearch command and you should still see the entry being returned, but this time around the connection was not encrypted at all.

So how does one prevent clients from using the connection without actually performing the StartTLS extended operation?
There is no real solution for this (based on my limited understanding of ACIs), because I couldn’t really find anything in the available list of permissions that would match BIND operations. Actually I’ve tried to set up an ACI like this:

aci: (target="ldap:///dc=example,dc=com")(version 3.0;acl "Prevent plain LDAP operations"; deny (all)(ssf<="1");)
 

but the BIND operations were still successful over plain LDAP. Whilst it was good that I couldn't really perform other LDAP operations, I think the worst has already happened, the user's password was transferred over an insecure network connection.
For more details on ssf by the way, feel free to check out the documentation. ;)

UPDATE!
Chris Ridd let me know that there is a way to enforce secure connections for BIND operations as well by configuring the password policy. To set up the password policy just run the following command:

dsconfig set-password-policy-prop 
          --policy-name Default Password Policy 
          --set require-secure-authentication:true 
          --hostname localhost 
          --port 4444 
          --trustStorePath /path/to/opendj/config/admin-truststore 
          --bindDN cn=Directory Manager 
          --bindPassword ****** 
          --no-prompt

Future BIND operations on unsecured LDAP connection will result in the following error:

[23/04/2015:10:04:27 +0100] BIND RES conn=2 op=0 msgID=1 result=49 authFailureID=197124 authFailureReason="Rejecting a simple bind request because the password policy requires secure authentication" authDN="uid=user.0,ou=people,dc=example,dc=com" etime=1

The problem is though that again, nothing actually prevented the user from sending the password over the unsecured connection...

Common misconceptions

I think the following misconceptions are causing the most problems around security:

  • StartTLS is more secure, because it has TLS in the name: WRONG! StartTLS allows just as well the usage of SSL(v2/v3) protocols, it is definitely not limited to TLS v1.x protocols by any means! Hopefully my explanation above makes it clearer that StartTLS is probably less secure than LDAPS.
  • LDAPS is less secure, because it has the ugly S (thinking it stands for SSL, but actually it stands for Secure): WRONG! as always, the actual security you can gain by using LDAPS connections is all matter of configuration. A badly configured LDAPS can still result in unsafe communication, yes, but LDAPS can just as well leverage the (currently considered safe) TLSv1.2 protocol and be perfectly safe.

I think I just can't emphasize this enough: use LDAPS if possible.

Understanding the login process

As authentication is pretty much the core functionality of OpenAM, I believe it is helpful to have a good understanding of how it works really. For starters let’s have a look at the different concepts around authentication.

Authentication modules

Authentication modules are just a simple piece of functionality that is meant to identify the user by some means. Depending on your requirements an authentication module could verify user credentials, or perform some kind of two factor verification process. For more complex use-cases you could use the auth module to collect informations about the end-user and with the help of a fraud-detection system determine if the current login attempt is “risky”.

In any case, the authentication modules are performing some (customizable) logic, and at the end of the module processing you can either ignore the current authentication module (neither a success nor a failure), OR succeed with a logged in user, OR just simply fail (invalid credentials, etc).

The authentication module implementations are JAAS based (with some abstraction on top of plain JAAS), so it is all based on callbacks (think of callbacks as input fields) that needs to be “handled” and submitted. When the callbacks are submitted, the AMLoginModule’s #process gets invoked with the callbacks. This is the time when the authentication module can start to process the submitted data and determine if the authentication attempt was successful. Since an authentication process can involve multiple steps (more than one set of callbacks to submit: for example requesting username, and than some verification code), the #process method needs to return a number that represents the next state (there are special numbers like ISAuthConstants.LOGIN_SUCCEED that represent successful authentication result, i.e. no further need to present callbacks), which then will be used to determine the next set of callbacks to display on the UI. Assuming that the authentication finished successfully, we need to return the magic LOGIN_SUCCEED state.

So how does OpenAM really know who the user is?

Once the authentication is successful, the auth framework will call AMLoginModule#getPrincipal which needs to return the authenticated user’s principal. #getPrincipal has a key role in the authentication process, so make sure its implemented correctly (or when using built-in modules, make sure they are configured correctly).

Authentication chains

The next sensible building blocks are the authentication chains. The auth chains can be considered as combinations of various authentication modules to present a single authentication procedure for the end-users. Following the previous example, one could think that checking some verification code after providing a username and password may not be actually the job of a single authentication module, and probably should be implemented separately. In that case one could implement one module for username/password login, and then implement an another module for code verification. To make sure the user logs in using both auth modules, one could create an authentication chain that includes both of them, and then the user will just need to authenticate against that chain.

Since the modules are JAAS-based, it makes sense to set up the chain configurations similarly to JAAS as well, but I’m not going to go into too much details on that front, instead you should just read about JAAS a bit more (especially about the “flags”).

Profile lookup

Once the user has successfully authenticated, there is a thing called “profile lookup”. This bit is all about trying to find the logged in user in one of the configured data stores, and then ensure that the user-specific settings (things like custom idle/max timeout values, or session quota even) are all applied for the just-to-be-created session. There are other additional checks as well, like ensuring that the logged in user actually has an active status in the system (e.g. doesn’t have a locked account).

To make things a bit more clearer let’s talk about User Profile Mode now (Access controlrealmAuthenticationAll Core Settings). The user profile mode tells what should happen at the profile lookup stage of the authentication, and these are the possible modes:

  • Required – this is the default mode, which just means that the user profile MUST exist in the configured data store.
  • Ignored – the user profile does not have to exist, the user profile will not be looked up as part of the authentication process.
  • Dynamic – the profile will be looked up, but if it doesn’t already exist, it will be dynamically created in the data store.
  • Dynamic with User Alias – this is similar to Dynamic, but it also appears to store user alias attributes in the newly created entries (I must admit I don’t fully understand this mode yet).

I believe it is important to stop here a bit and emphasize the following:
The authentication module may interact with arbitrary external components during the authentication phase, however when it comes to profile lookup, that will be always performed against the configured data stores. If you are running into the infamous “User has no profile in this organization” error message, then that means, that the authentication was successful, but the profile lookup failed, since OpenAM was unable to find the user in the configured data stores.

The profile lookup itself is performed based on the return value of #getPrincipal, so this is why it is really important to ensure that the module works correctly. The returned principal can be simply a username like “helloworld”, but it can also have a DN format like “uid=helloworld,ou=people,dc=example,dc=com” (see LDAP module’s Return User DN to DataStore setting). When the returned value is a DN, then the RDN value will be used for the data store search (so helloworld), hence it is important to ensure that the data store has been configured to search users based on the attribute that is expected to have the value of helloworld.

The idea behind all of this is that in #getPrincipal the username returned should be unique across the user base, so even if let’s say you allow someone authenticating with “John Smith”, you should still return a bit more meaningful username (like jsmith123) to the backend. That way you can ensure that when you ask for “John Smith”‘s user details, you will get the right set of values.

Post authentication actions

After a successful profile lookup there are various additional things that OpenAM does, but I’m not really going to go into the nifty details for those. Here’s a small list of things that normally happens:

  • Check if user account is active.
  • Check if the account is locked (using OpenAM’s built-in Account Lockout feature).
  • Check if there are user-specific session settings configured for the user, and apply those values for the newly created session.
  • When the user session is created, check if the session quota has been exhausted and run the corresponding quota exhaustion action if yes.
  • Execute the Post Authentication Processing plugins.
  • Determine the user’s success login URL and also ensure that the goto URL is validated.

Summary

Whilst we only discussed portions of the actual authentication process, I think the main concepts for authentication are laid out, so hopefully when you need to configure OpenAM the next time around, you can reuse the things learned here. :)

Rencontrez ForgeRock à SIdO Lyon, les 7 et 8 Avril

Salon Internet des ObjetsJe serai présent avec notre équipe au SIdO, l’événement 100% dédié à l’Internet des Objets qui aura lieu à Lyon les 7 et 8 Avril 2015.

Outre notre présence dans l’espace coworking pendant les 2 jours, Lasse Andresen, CTO de ForgeRock, animera un workshop avec ARM et Schneider sur la place de l’Identité dans l’Internet des Objets, le Mercredi 8 à 13h30.

N’hésitez pas à venir nous rendre visite dans l’espace coworking.


Filed under: General, InFrench Tagged: conference, ForgeRock, france, identity, internet-of-things, iot, Lyon, privacy, security

Join us for The Identity Summit

Meet the Security and Identity rockstars and thought leaders at The Identity Summit, May 27-29th 2015 !

In addition to the two full days of sessions, this year at The Identity Summit, all ForgeRock customers are invited to participate in a pre-event community day where you will be able to interact with ForgeRock product development and other customers.

Photo by Anthony Quintano - https://www.flickr.com/photos/quintanomedia/

Photo by Anthony Quintano – https://www.flickr.com/photos/quintanomedia/

The event will take place at the Ritz in Half Moon Bay, California.

Register today. Sign-up for the customer user group is part of The Identity Summit registration process. Make sure to add the Customer User Group as an “Additional Item” before submitting your information.

The call for speakers is opened until April 13th.


Filed under: General, Identity Tagged: California, conference, ForgeRock, identity, IRM, summit, user-group