ricky about SPNEGO/Kerberos in JavaEE
Fri, 19.12.2014 23:05
Hi dadel, I wrote two more entries about this subject (PAC and standard solution). Please r [...]
dadel about SPNEGO/Kerberos in JavaEE
Tue, 16.12.2014 14:30
thanks a lot for this useful tutorial, but could you give me a recommendation for authorizatio [...]
ricky about Disappointment with the Web Cryptography API
Sat, 15.11.2014 13:07
The final report of the workshop is public: http://www.w3.org/2012/webcrypto/webcrypto-next [...]
Gregor about Disappointment with the Web Cryptography API
Fri, 07.11.2014 11:13
Hi, I've found your article and I can be just more disappointed than you. I can't believe that [...]
Daniel about Kerberos, Apache 2.4 and Solaris 10 (Part I)
Mon, 03.11.2014 18:19
I was able to solve the problem by adding this two lines in httpd-ssl.conf LimitRequestLine 6 [...]
Daniel about Kerberos, Apache 2.4 and Solaris 10 (Part I)
Mon, 03.11.2014 17:34
Hola Ricky I just realized that you're from Spain too I managed to compile successful [...]
ricky about Huge fonts in new Chromium 38
Sun, 02.11.2014 14:47
Another quite disgusting issue with Chromium, in my desktop box (an old system) chromium brows [...]
ricky about Kerberos, Apache 2.4 and Solaris 10 (Part I)
Fri, 31.10.2014 22:41
Hi Daniel, In this entry everything is compiled in 32 bits, but it seems apache in your env [...]
Saturday, September 27. 2014
The main security constraint about the LDAP protocol is that passwords (in the common scenario) travel through the network in clear. If we keep in mind that this protocol is used mainly to authenticate users, the risk is even greater. This entry is going to show briefly the typical solutions for this security problem and exemplify how applications should interact with those solutions (Java examples).
Some time ago a previous entry about LDAP was presented, in that occasion the post talked about password policies. Nevertheless a simple Bind class was presented that checked the password for a user. This class used a tricky reconnect to retrieve the controls returned by the Bind, but now the solution is going to be improved in order to perform all the steps without re-login (you know, learn something new everyday). The class will be slightly modified for each different solution presented throughout the entry. In the server part two different LDAP servers will be used: OpenLDAP (default debian package working in standard ports 389 and 636) and the old OpenDS (installed apart in ports 1389 and 1636).
SASL / DIGEST-MD5
The Simple Authentication Security Layer (SASL) is a kind of wrapper that permit applications to authenticate using different security mechanisms in a generic way. SASL defines several mechanisms like Kerberos (an old friend inside the blog) or DIGEST-MD5. This time, the later is the first technique presented in the entry to avoid clear text passwords.
DIGEST-MD5 is a mechanism to negotiate the credentials invented long ago for web servers (although it can be used in any other server type). The idea is quite simple: the client sends that it wants to login using SASL/DIGEST-MD5 in the BIND; the server responds with a nonce (a random number generated by the server) and the realm (a name for the place we want to login); the client with that information, username and password performs a MD5 hash (using the nonce produces a unique hash), and sends it to the server; the server recomputes the same hash and checks if both are the same. The advantage of this algorithm is clear, the password is never sent in a clear way, but there is also a disadvantage, the server needs to know the real password of the user (it should be stored with a reversible cryptographic method).
OpenLDAP supports DIGEST-MD5 but it has two restrictions: the user password should be in clear text (not reversible but plain) and a mapping is needed between username and DNs. I followed this entry from sbahloul blog in order to configure everything properly. Nevertheless Debian now does not use slapd.conf file and you need to configure the mapping performing ldap commands against the configuration branch. The following command creates a mapping to match the username sent in DIGEST-MD5 against the entry with that username set as uid attribute:
# ldapmodify -Y EXTERNAL -H ldapi:/// dn: cn=config changetype: modify add: olcAuthzRegexp olcAuthzRegexp: uid=([^,]*),cn=digest-md5,cn=auth ldap:///dc=example,dc=com??sub?(uid=$1)
Then the password of a user was assigned in clear text:
$ ldapmodify -h localhost -p 389 -D "cn=admin,dc=example,dc=com" -W dn: uid=ricky,ou=people,dc=example,dc=com changetype: modify replace: userpassword userpassword: XXXXXXXX
Finally the following ldapsearch command performs a SASL/DIGEST-MD5 bind against OpenLDAP:
$ ldapsearch -LLL -Y DIGEST-MD5 -h localhost -p 389 -b "dc=example,dc=com" -Q -U "ricky" -w XXXXXXXX uid=ricky dn dn: uid=ricky,ou=People,dc=example,dc=com
In this command the -Y option forces DIGEST-MD5 and now the username is specified with the -U option (instead of the common -D with the full DN of the user only the username is sent, the previous mapping locates the entry with the password for that uid).
In OpenDS the configuration is very similar and with similar steps. But OpenDS just needs a reversible encryption for the password (it can be AES, 3DES, Blowfish, Clear, Base64,...), for example I setup AES as the default storage mechanism for the default policy.
./dsconfig set-password-policy-prop -h localhost -p 4444 -D "cn=Directory Manager" \ -w XXXXXXXX -X -n --policy-name "Default Password Policy" \ --set default-password-storage-scheme:AES
It is clearly a more secure solution, but the key should be accessible by the server and, in the case of OpenDS, it is stored under a special branch cn=admin data of the server (under this branch the OpenDS software stores all the different keys it uses). If that key is obtained the passwords are equally compromised.
OpenDS also uses a mapping to locate the user password from the username, the default mapping is exactly the one that was implemented in OpenLDAP (the DIGEST-MD5 mechanism uses the mapping defined in the entry cn=Exact Match,cn=Identity Mappers,cn=config which maps the username with the uid). So the same ldapsearch presented before works with OpenDS (attacking to the 1389 port instead default 389).
Finally the file DigestMD5Bind.java is the class to login using DIGEST-MD5 in Java. The main characteristic is that a new property Context.SECURITY_AUTHENTICATION is set to DIGEST-MD5 in the environment to specify that this mechanism will be used. As I commented before, the new class is different to the one presented in the previous entry because now no re-bind is done. The context is created without username and password (the connection is established but no BIND is sent) and then that information is added to the context and the reconnect executed (the real BIND is sent to the server, letting us recover controls). Using DIGEST-MD5 the Context.SECURITY_PRINCIPAL should be the username (uid mapping) and not the complete DN of the user as in a common login. The representative code is presented below.
And it can be executed with the following command (first argument is the LDAP URL, second the username and finally the password).
$ java -cp . DigestMD5Bind ldap://penance:389 ricky XXXXXXXX Using LDAP url ldap://penance:389... OK Connecting as ricky... OK
The previous login mechanism (DIGEST-MD5) avoids showing the clear password over the wire but compromises the solution storing user passwords in clear or with a reversible encryption algorithm. Those mechanisms were thought as a secondary option but never to replace the real and proper solution for this problem: TLS or SSL. The Transport Layer Security is a protocol to cryptographically secure any other TCP/IP protocol based on certificates and asymmetric keys.
In this first example the TLS mechanism will secure a previous non-secure connection. In this scenario the client opens a non-secure ldap connection and then it requests explicitly to start a TLS communication, in that moment the typical steps for securing the protocol are executed (certificate exchange, cipher selection,...). This way the application uses the same non-secure port (389 in OpenLDAP and 1389 in OpenDS) but, when the client explicitly requests it, the same socket (same port) starts a TLS session. This method avoids using a new port but the client should request explicitly the TLS connection.
In order to use TLS/SSL in OpenLDAP and OpenDS a new self-signed certificate was created (inside /etc/ldap directory).
# cd /etc/ldap # openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -subj "/C=ES/O=localhost/CN=penance" -keyout localhost.key -out localhost.crt
And the cn=config branch is modified in OpenLDAP to specify the new key and certificate file (debian instructions were followed) :
# ldapmodify -Y EXTERNAL -H ldapi:/// dn: cn=config changetype: modify add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/ldap/localhost.key - add: olcTLSCertificateFile olcTLSCertificateFile: /etc/ldap/localhost.crt
Besides the startup command should be changed, in Debian the file /etc/default/slapd is modified to include the secure port 636 in the variable SLAPD_SERVICES (that variable is used as the -h option in the slapd daemon startup, this way the process start listening in the secure port too, the new port will be used in the next scenario).
SLAPD_SERVICES="ldap:/// ldaps:/// ldapi:///"
Once the server is restarted the TLS can be tested using the following ldapsearch command in linux:
$ LDAPTLS_CACERT=/etc/ldap/localhost.crt \ ldapsearch -LLL -ZZ -H ldap://penance:389 -s base -b "dc=example,dc=com" \ -D "uid=ricky,ou=people,dc=example,dc=com" -w XXXXXXXX objectclass=
The self-signed certificate is specified in the env variable LDAPTLS_CACERT as a trusted CA (PEM format) and then the option -ZZ is specified to force TLS in the communication.
Configuring OpenDS to use a certificate is even easier, it can be done at startup and this way the TLS/SSL is available at startup. The same ldapsearch presented before should work now for OpenDS in the port 1389.
Using Java the TLS should be started sending the StartTlsRequest as a extended operation (see this wonderful forum entry fro more information). The sample TlsBind.java is used to perform a TLS login, the context is again created without login (just establishing the connection), then the TLS request is executed and, finally, the login/BIND is executed. This way the BIND is performed once the secure connection is in place, so the password is sent in a secure way.
And the previous class can be executed in a similar way to the previous digest command:
$ java -Djavax.net.ssl.trustStore=penance.jks TlsBind ldap://penance:389 uid=ricky,ou=people,dc=example,dc=com XXXXXXXX Using LDAP url ldap://penance:1389... OK Negotiating TLS... OK Connecting as uid=ricky,ou=people,dc=example,dc=com... OK
Only two tips are remarkable. First, the javax.net.ssl.trustStore should be set to add the self-signed certificate as trusted for the Java execution. Second, in all the documentation I have read (for example the previous forum entry or the official Java documentation) it is commented that the previous connection can be re-used once the TLS session is closed. Nevertheless I have checked that the connection is always closed by the server (both, OpenLDAP and OpenDS) as soon as the client closes the TLS request. I suppose that TLS standard admits this behavior, so please take this in mind, once the TLS connection is established you should continue using it until the end, you cannot come back to the non-secure connection.
The final solution is just using the SSL port (default ldaps port 636 in OpenLDAP and 1636 in OpenDS). With this configuration all the communication is directly encrypted and therefore nothing is sent in plain. SSL was already configured in the previous point for both servers. In order to test the secure port a new ldapsearch command can be executed.
$ LDAPTLS_CACERT=/etc/ldap/localhost.crt \ ldapsearch -LLL -H ldaps://penance:636 -s base -b "dc=example,dc=com" \ -D "uid=ricky,ou=people,dc=example,dc=com" -w XXXXXXXX objectclass=
The new Bind.java is very similar to the presented in the previous entry (this solution represents almost zero code change). Now only the Context.SECURITY_PROTOCOL is modified (it should be ssl for ldaps and plain to non-secure ldap), I just guessed the value depending the URL of the connection (it will be ssl if the URL starts with ldaps://, plain if not) and the same code works for secure and non-secure situations.
The main class file is executed this way:
$ java -Djavax.net.ssl.trustStore=penance.jks -cp . Bind ldaps://localhost uid=ricky,ou=people,dc=example,dc=com XXXXXXXX Using LDAP url ldaps://localhost... OK Connecting as user uid=ricky,ou=people,dc=example,dc=com... OK
Again the javax.net.ssl.trustStore system property should be specified in order to trust in the self-signed certificate configured in both servers.
Today's entry is simple and direct, LDAP servers usually authenticate users receiving passwords in clear over the network, the post has commented three common techniques to encrypt them. The first method used DIGEST-MD5 which avoids clear passwords without using TLS/SSL. This algorithm needs the server to know the password of the user (that means passwords should be stored in clear or, at least, in a reversible cryptographic algorithm inside the server). Nevertheless the definitive solution for this problem is using ldaps (ldap over SSL/TLS). The entry has exemplified two situations: starting TLS over non-secure connection and using directly a secure SSL port. Obviously the second option is the recommended cos it is easier to develop inside pre-existing applications but both are equally secure.
Saturday, March 31. 2012
If you remember some time ago a three chapter series about JavaEE certificate login were published in this blog. In all of them an LDAP server was used as the identity store (in those entries OpenLDAP was configured) and, needless to say, that is the common situation. This time the entry is going to extend that subject mixing JavaEE, LDAP and password policies. A password policy is a set of rules governing the use of passwords in the repository, it manages concepts like complexity (minimum length, numbers, capitals, history passwords, dictionary,...), expiration (maximum and minimum age, expiration by inactivity), locking (failed attempts lock the account for a specified time or forever) and so on. Obviously any JavaEE application which supports password policies needs to understand the state of the user password and acts accordingly. That is what this entry talks about.
First of all the general diagram of all the possible user states is commented. More or less all LDAP servers (and, in general, any user repository) manage the following ideas:
With all those cases the following account (password) states can be defined:
So now the question is how to guess the state of the user account when a LDAP server is used. The ldap operation BIND is used to check the credentials of any user and it can return SUCCESS (code 0), INVALID_CREDENTIALS (49) or any other internal error (see LDAP return codes). What servers usually use to display account state at BIND are controls and/or error messages. Response controls are a kind of tags that can be attached to any LDAP response in order to remark something. A response control is defined by an OID (Object IDentifier) like any other object in the LDAP standard and has a value. An error message is a descriptive text that the LDAP server gives to the client in an error response. So any account/password state should be guessed using only controls after a bind success and controls/error messages after a bind error (obviously success responses do not have error messages).
Exemplifying the previous paragraph I am going to explain how Netscape based LDAP servers manage account states. When I say Netscape I mean any LDAP server that comes from Netscape, nowadays there are a lot of them, 389 Directory Server (I do not know if if it supports all the commented policy features), Oracle Unified Directory (OUD), Oracle Directory Server Enterprise Edition (ODSEE), OpenDS, OpenDJ,... With an old OpenDS that I have already installed in my laptop, I configured detailed error bind responses, then a new password policy was created and, finally, the policy was assigned to my user. The result of some testing was the following table, which summarizes how a BIND operation is responded in each account state (good password is always sent).
So it is quite easy to understand, Netscape LDAP servers manage two controls:
And as you see guessing the user state is just a question of checking those two control tags and the returned error messages.
Now a second important part, how Java lets manage error responses and controls. Error responses to a Bind are always in the message of the AuthenticationException. Any error 49 (INVALID_CREDENTIALS) throws that exception and its message is composed using the code and the error response, both are concatenated. Controls are managed by the so-named interface and they need an LdapContext (method getResponseControls) to be collected. A tricky reconnect has to be used in case response controls needed to be retrieved after a failed bind (the bind in Java is hidden inside the creation of the Context, if an error is returned, context is never created, so no method can be called). I present a little Bind.java which just does it, connects using a manager user (like a common Java application would do) and then performs a user bind recollecting controls and error. I used this program to complete the table presented before. The main code is displayed below.
Finally I modified my CertSecurityCustom project (the one I used in custom certificate login entry) to retrieve the account state and show correct messages at login time. Besides if the user is in PASSWORD_MUST_CHANGE state he is forced to change the password, and if he is in WARNING_EXPIRED a message is shown in the index page. The video presents a user whose password is in the warning period (appropriate warning message is shown). Then using a common ldapmodify command the user password is reset, so he must change it immediately. After that the same user fails several times until locked message is shown.
I hope this entry would be good for demythifying LDAP password states. I personally knew the ideas behind this matter but I had never studied it very deeply. It was a nice time doing the little program and filling the table. I suppose that any other LDAP server would work in a very similar way and the ideas presented here could be easily extended to cover it.
First think! Then act!
 By default OpenDS does not give a detailed error response (security reasons). It should be configured like this:
$ ./dsconfig set-global-configuration-prop -h localhost -p 4444 --trustAll \ -D "cn=Directory Manager" -w **** -n --set return-bind-error-messages:true
 I created a sample password policy with some complexity requirements, very short age, locking for 30 minutes after 5 failed attempts and force password changing after admin reset:
$ ./dsconfig create-password-policy -h localhost -p 4444 -D "cn=Directory Manager" \ -w **** --trustAll -n --policy-name "Sample Password Policy" \ --set password-attribute:userPassword --set default-password-storage-scheme:"Salted SHA-1"\ --set lockout-duration:"30 minutes" --set lockout-failure-count:5 \ --set max-password-age:"2 hours" --set password-expiration-warning-interval:"1 hours" \ --set force-change-on-reset:true --set password-history-count:8 \ --set password-validator:"Length-Based Password Validator"
 My user was assigned to this policy, it is just done assigning a special attribute to the user:
$ ldapmodify -D "cn=Directory Manager" -w **** -h localhost -p 1389 dn: uid=ricky,ou=people,dc=example,dc=com changetype: modify add: ds-pwp-password-policy-dn ds-pwp-password-policy-dn: cn=Sample Password Policy,cn=Password Policies,cn=config
(Page 1 of 1, totaling 2 entries)