Given the focus on security breaches leaking account information the last few years, we have taken a fresh look at how secure our LDAP passwords really are, and if we can let OpenLDAP use a modern hash algorithm.

So what is a modern hash? Articles on the net usually mention SHA-512, PBKDF-2, brypt and scrypt. The latter is considered better as it consumes an arbitrarily large amount of memory and can thus be more resistant to a GPU based attack.

Storing passwords

OpenLDAP can store passwords in clear-text, as encrypted strings, or as hashes (one-way algorithms). Usually one stores the password in the userPassword attribute provided by or inherited from the organization, organizationalUnit or person object class (RFC4519).

The userPassword attribute is in most installations by default protected with ACLs in the server configuration, by only giving administrators or the owner of the object containing the attribute access to this attribute.

The LDAP server content is in it self stored in a local database backend, usually BDB or HDB.

In a worst case scenario, a malicious user can either somehow bypass the LDAP server access protection and retrieve passwords or hashes via the LDAP protocol, or somehow access the OS, get root privileges and read the LDAP server’s database file from the file system. In these cases, a strong password hash is imperative.

OpenLDAP built-in security

If the password content is prepended by a `{}' string, the LDAP server will use the given scheme to encrypt or hash the password. Vanilla OpenLDAP 2.4 supports the following encryption schemes:

MD5
hashed password using the MD5 hash algorithm
SMD5
MD5 with salt
SHA
hashed password using the SHA-1 hash algorithm
SSHA
SHA-1 with salt

The SSHA is given as the most secure password scheme supported. Unfortunately attacks against SHA-1 were found back in 2005 and the scheme has been officially frowned upon for a long time. The salt does help, but SHA-1 is getting a bit long in the tooth. To put it mildly.

So is that it?

OpenLDAP pass-through authentication

OpenLDAP can also use external processes to verify and hash passwords. These schemes are:

  • CRYPT - will use the OS’ crypt library as a password handler
  • SASL - will use Cyrus SASL as a password handler

Cyrus SASL was last updated in 2012, but CRYPT is a part of the POSIX API and should be continuously updated. So - can CRYPT give us an up-to-date hash?

Crypt to the rescue

It turns out that Linux based glibc versions of crypt support additional encryption schemes through an additional versioning scheme encoded in the password hash, This scheme is now defined as the PHC String format. The versions currently supported in glibc crypt are:

Scheme ID Schema
1 MD5
2a Blowfish / bcrypt
3 NTHASH
5 SHA-256
6 SHA-512
md5 Solaris MD5
sha1 PBKDF1 with SHA-1

The format used for the additional encryption schemes can be expressed according to the PHC string format:

$<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]

Here the salt and the hash are Base64 encoded strings.

An interesting detail here is the parameter argument in the PHC formatted string - for the sha-512 hash a valid argument is rounds=<N>, with a default value of 5000. This implements a key stretching scheme.

So with SHA-512, a long salt and a high ‘rounds’ parameter, we should be able to generate a reasonably secure hash. As of writing.

OpenLDAP default password variables

You can instruct OpenLDAP to use a strong encryption scheme by default:

password-hash {CRYPT}
password-crypt-salt-format "$6$%.16s"

Or for a cn=config enabled OpenLDAP server:

olcPasswordHash: {CRYPT}
olcPasswordCryptSaltFormat: $6$%.16s

The latter setting’s value is in sprintf(3) format which should only contain one % conversion. The string character count implies the length of the salt, which for crypt can be up to 16 characters long.

In order to gain a longer key stretch, you can use:

password-crypt-salt-format "$6$rounds=50000$%.16s"

I have unfortunately not had the time to test the key-stretching default value, but will update this post with results later.

mkpasswd and LDIF

mkpasswd is a handy utility which can create a PHC formatted hash. In the following example, I read a salt from /dev/random:

$ mkpasswd --rounds 500000 -m sha-512 --salt `head -c 40 /dev/random | base64 | sed -e 's/+/./g' |  cut -b 10-25` 'Try to break this one!'
$6$rounds=500000$KWcfAQjEPsey2aEr$X/E/I9ySD3zO6Z6cfrTo6MrSIMV5oNQuLjxg4kf4nB8f0WZLFuBYlQ86EkHatocuLB0ajd6DsHfmn8Bajoo9u/

Combined with LDIF we get the following hypothetical LDIF string in order to set a secure password for a given account:

dn: uid=lars,ou=Employees,dc=redpill-linpro,dc=com
changetype: modify
replace: userPassword
userPassword: {CRYPT}$6$rounds=500000$KWcfAQjEPsey2aEr$X/E/I9ySD3zO6Z6cfrTo6MrSIMV5oNQuLjxg4kf4nB8f0WZLFuBYlQ86EkHatocuLB0ajd6DsHfmn8Bajoo9u/

Further steps

With the password storage in a better shape, don’t forget to ensure that all communication with the LDAP server are encrypted!

Lars Olafsen

Technical Operations Manager at Redpill Linpro

Lars has been in the IT business for around two decades now, working initially both as a developer and systems administrator. He's been with Redpill Linpro for 9 years, and in addition to being one of our operations managers, he still gets the time to solder together pieces of code.

Containerized Development Environment

Do you spend days or weeks setting up your development environment just the way you like it when you get a new computer? Is your home directory a mess of dotfiles and metadata that you’re reluctant to clean up just in case they do something useful? Do you avoid trying new versions of software because of the effort to roll back software and settings if the new version doesn’t work?

Take control over your local development environment with containerization and Dev-Env-as-Code!

... [continue reading]

Ansible-runner

Published on February 27, 2024

Portable Java shell scripts with Java 21

Published on February 21, 2024