Tim's Weblog
Tim Strehle’s links and thoughts on Web apps, software development and Digital Asset Management, since 2002.

Use ppolicy_hash_cleartext to keep OpenLDAP from storing and returning plain text passwords

(Disclosure: I’m no OpenLDAP expert, just a user. Use the commands below at your own risk. Please let me know if I’m recommending something dumb…)

OpenLDAP slapd is popular open source server software that implements the LDAP protocol – you use it to store users, groups and their attributes, and you can use it for authentication (checking whether a given username/password combination is valid).

The experts will know about this, but to a novice it’s counter-intuitive that by default, OpenLDAP stores passwords in plain text on disk, and even returns plain text passwords in search results. Here’s what this looks like:

On my CentOS 7 box with slapd 2.4.39, I created a user named “someone” and set his password to “secret”. This command shows that the password is stored in clear text on disk:

[digicol@dcxcentos7vm ~]$ sudo strings /var/lib/ldap/id2entry.bdb \
    | grep -C 1 secret

When I search the directory, I can ask for the password:

[digicol@dcxcentos7vm ~]$ ldapsearch -x -H ldap://localhost:389 \
    -b dc=example,dc=com -D cn=admin,dc=example,dc=com -w dc \
    -LLL 'cn=someone' userPassword
dn: uid=someone,ou=people,dc=example,dc=com
userPassword:: c2VjcmV0

At first sight, the userPassword value looks like something that might be encrypted. It isn’t, however; the double colon indicates that the value is Base64 encoded. So I can easily read the password in plaintext:

[digicol@dcxcentos7vm ~]$ php -r 'var_dump(base64_decode("c2VjcmV0"));'
string(6) "secret"

While most people would agree that plaintext passwords are bad practice, OpenLDAP doesn’t do this by accident. It’s by design, as their documentation states under Security Considerations – Password Storage:

“RFC4519 specifies that passwords are not stored in encrypted (or hashed) form. This allows a wide range of password-based authentication mechanisms, such as DIGEST-MD5 to be used. This is also the most interoperable storage scheme. However, it may be desirable to store a hash of password instead. slapd(8) supports a variety of storage schemes for the administrator to choose from.”

For some background, see How do you turn on password hashing (SSHA) in openLDAP on Stackoverflow.

How to enable Hash Passwords in OpenLDAP has a solution, explaining how to enable ppolicy_hash_cleartext via the ppolicy (Password Policy) overlay module. But it uses the deprecated slapd.conf configuration file. Here’s how I did the same using the newer slapd-config configuration system, which you’re interacting with using the OpenLDAP command line tools instead of editing a config file (blog posts by David Robillard and Milo Oostergo helped me figure this out).

First, I created an LDIF file with the stuff to add to the configuration, /tmp/config.ldif:

# Load the ppolicy module
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModuleLoad: ppolicy.la

# Enable the overlay and its hash_cleartext policy
dn: olcOverlay={5}ppolicy,olcDatabase={2}bdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: {5}ppolicy
olcPPolicyHashCleartext: TRUE

To find the correct parent entry for the olcOverlay, I had to run this search which showed me I had to use olcDatabase={2}bdb, not the olcDatabase={2}hdb given in the blog post I had read:

sudo ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config dn \
    | grep olcDatabase

Then I imported the file with ldapadd. Note that you gain access to the OpenLDAP config database by running as root and using -Y EXTERNAL instead of -D:

[digicol@dcxcentos7vm openldap]$ sudo ldapadd -Y EXTERNAL \
    -H ldapi:/// -f /tmp/config.ldif 
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
adding new entry "cn=module{0},cn=config"
adding new entry "olcOverlay={5}ppolicy,olcDatabase={2}bdb,cn=config"

Now I changed my example user’s password from “secret” to “hashed”. Re-running the above commands confirms that the password is not stored as plaintext on disk…

[digicol@dcxcentos7vm ~]$ sudo strings /var/lib/ldap/id2entry.bdb \
    | grep -C 1 hashed

… and it isn’t returned in the clear anymore; the value is marked as SSHA-crypted:

[digicol@dcxcentos7vm ~]$ ldapsearch -x -H ldap://localhost:389 \
    -b dc=example,dc=com -D cn=admin,dc=example,dc=com -w dc \
    -LLL 'cn=someone' userPassword
dn: uid=someone,ou=people,dc=example,dc=com
userPassword:: e1NTSEF9eG5rNFNCTTNLK1lzeFBEa3hiM2pDbEVlVU03YTNadGk=

[digicol@dcxcentos7vm ~]$ php -r \
string(38) "{SSHA}xnk4SBM3K+YsxPDkxb3jClEeUM7a3Zti"