{"id":1805,"date":"2015-08-27T00:00:00","date_gmt":"2015-08-26T22:00:00","guid":{"rendered":"https:\/\/wwwneu.strehle.de\/tim\/weblog\/archives\/2015\/08\/27\/1567-2\/"},"modified":"2025-07-31T22:00:24","modified_gmt":"2025-07-31T20:00:24","slug":"1567-2","status":"publish","type":"post","link":"https:\/\/www.strehle.de\/tim\/weblog\/archives\/2015\/08\/27\/1567-2\/","title":{"rendered":"Use ppolicy_hash_cleartext to keep OpenLDAP from storing and returning plain text passwords"},"content":{"rendered":"\n<p><em>(Disclosure: I\u2019m no OpenLDAP expert, just a user. Use the commands below at your own risk. Please let me know if I\u2019m recommending something dumb\u2026)<\/em><\/p>\n\n\n\n<p><a href=\"http:\/\/www.openldap.org\">OpenLDAP<\/a> slapd is popular open source server software that implements the LDAP protocol \u2013 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).<\/p>\n\n\n\n<p>The experts will know about this, but to a novice it\u2019s counter-intuitive that by default, OpenLDAP stores passwords in plain text on disk, and even returns plain text passwords in search results. Here\u2019s what this looks like:<\/p>\n\n\n\n<p>On my CentOS 7 box with slapd 2.4.39, I created a user named \u201csomeone\u201d and set his password to \u201csecret\u201d. This command shows that the password is stored in clear text on disk:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm ~]$ sudo strings \/var\/lib\/ldap\/id2entry.bdb \\\n    | grep -C 1 secret\nuserPassword\nsecret\nsomeone\n<\/code><\/pre>\n\n\n\n<p>When I search the directory, I can ask for the password:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm ~]$ ldapsearch -x -H ldap:\/\/localhost:389 \\\n    -b dc=example,dc=com -D cn=admin,dc=example,dc=com -w dc \\\n    -LLL 'cn=someone' userPassword\ndn: uid=someone,ou=people,dc=example,dc=com\nuserPassword:: c2VjcmV0\n<\/code><\/pre>\n\n\n\n<p>At first sight, the <code>userPassword<\/code> value looks like something that might be encrypted. It isn\u2019t, however; the double colon indicates that the value is Base64 encoded. So I can easily read the password in plaintext:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm ~]$ php -r 'var_dump(base64_decode(\"c2VjcmV0\"));'\nstring(6) \"secret\"\n<\/code><\/pre>\n\n\n\n<p>While most people would agree that plaintext passwords are bad practice, OpenLDAP doesn\u2019t do this by accident. It\u2019s by design, as their documentation states under <a href=\"http:\/\/www.openldap.org\/doc\/admin24\/security.html#Password%20Storage\">Security Considerations \u2013 Password Storage<\/a>:<\/p>\n\n\n\n<p>\u201cRFC4519 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.\u201d<\/p>\n\n\n\n<p>For some background, see <a href=\"http:\/\/stackoverflow.com\/questions\/11731547\/how-do-you-turn-on-password-hashing-ssha-in-openldap\">How do you turn on password hashing (SSHA) in openLDAP<\/a> on Stackoverflow.<\/p>\n\n\n\n<p><a href=\"http:\/\/xacmlinfo.org\/2015\/06\/25\/enable-hash-passwords-in-openldap\/\">How to enable Hash Passwords in OpenLDAP<\/a> has a solution, explaining how to enable <code>ppolicy_hash_cleartext<\/code> via the <a href=\"http:\/\/www.openldap.org\/software\/man.cgi?query=slapo-ppolicy&amp;sektion=5&amp;apropos=0&amp;manpath=OpenLDAP+2.4-Release\">ppolicy (Password Policy)<\/a> <a href=\"http:\/\/www.openldap.org\/doc\/admin24\/overlays.html\">overlay<\/a> module. But it uses the deprecated <code>slapd.conf<\/code> configuration file. Here\u2019s how I did the same using the newer <a href=\"http:\/\/www.openldap.org\/doc\/admin24\/slapdconf2.html\">slapd-config configuration system<\/a>, which you\u2019re interacting with using the OpenLDAP command line tools instead of editing a config file (blog posts by <a href=\"http:\/\/itdavid.blogspot.de\/2012\/05\/howto-openldap-2.html\">David Robillard<\/a> and <a href=\"https:\/\/www.oostergo.net\/node\/85\">Milo Oostergo<\/a> helped me figure this out).<\/p>\n\n\n\n<p>First, I created an LDIF file with the stuff to add to the configuration, <code>\/tmp\/config.ldif<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Load the ppolicy module\ndn: cn=module{0},cn=config\nobjectClass: olcModuleList\ncn: module{0}\nolcModuleLoad: ppolicy.la\n\n# Enable the overlay and its hash_cleartext policy\ndn: olcOverlay={5}ppolicy,olcDatabase={2}bdb,cn=config\nobjectClass: olcOverlayConfig\nobjectClass: olcPPolicyConfig\nolcOverlay: {5}ppolicy\nolcPPolicyHashCleartext: TRUE\n<\/code><\/pre>\n\n\n\n<p>To find the correct parent entry for the olcOverlay, I had to run this search which showed me I had to use <code>olcDatabase={2}bdb<\/code>, not the <code>olcDatabase={2}hdb<\/code> given in the blog post I had read:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ldapsearch -Y EXTERNAL -H ldapi:\/\/\/ -b cn=config dn \\\n    | grep olcDatabase\n<\/code><\/pre>\n\n\n\n<p>Then I imported the file with <code>ldapadd<\/code>. Note that you gain access to the OpenLDAP config database by running as root and using <code>-Y EXTERNAL<\/code> instead of <code>-D<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm openldap]$ sudo ldapadd -Y EXTERNAL \\\n    -H ldapi:\/\/\/ -f \/tmp\/config.ldif \nSASL\/EXTERNAL authentication started\nSASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth\nSASL SSF: 0\nadding new entry \"cn=module{0},cn=config\"\nadding new entry \"olcOverlay={5}ppolicy,olcDatabase={2}bdb,cn=config\"\n<\/code><\/pre>\n\n\n\n<p>Now I changed my example user\u2019s password from \u201csecret\u201d to \u201chashed\u201d. Re-running the above commands confirms that the password is not stored as plaintext on disk\u2026<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm ~]$ sudo strings \/var\/lib\/ldap\/id2entry.bdb \\\n    | grep -C 1 hashed\n<\/code><\/pre>\n\n\n\n<p>\u2026 and it isn\u2019t returned in the clear anymore; the value is marked as SSHA-crypted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;digicol@dcxcentos7vm ~]$ ldapsearch -x -H ldap:\/\/localhost:389 \\\n    -b dc=example,dc=com -D cn=admin,dc=example,dc=com -w dc \\\n    -LLL 'cn=someone' userPassword\ndn: uid=someone,ou=people,dc=example,dc=com\nuserPassword:: e1NTSEF9eG5rNFNCTTNLK1lzeFBEa3hiM2pDbEVlVU03YTNadGk=\n\n&#91;digicol@dcxcentos7vm ~]$ php -r \\\n    'var_dump(base64_decode(\"e1NTSEF9eG5rNFNCTTNLK1lzeFBEa3hiM2pDbEVlVU03YTNadGk=\"));'\nstring(38) \"{SSHA}xnk4SBM3K+YsxPDkxb3jClEeUM7a3Zti\"\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>(Disclosure: I\u2019m no OpenLDAP expert, just a user. Use the commands below at your own risk. Please let me know if I\u2019m recommending something dumb\u2026) OpenLDAP slapd is popular open source server software that implements the LDAP protocol \u2013 you use it to store users, groups and their attributes, and you can use it for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_share_on_mastodon":"0"},"categories":[1],"tags":[],"class_list":["post-1805","post","type-post","status-publish","format-standard","hentry","category-weblog"],"share_on_mastodon":{"url":"","error":""},"_links":{"self":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1805","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/comments?post=1805"}],"version-history":[{"count":1,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1805\/revisions"}],"predecessor-version":[{"id":1921,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/posts\/1805\/revisions\/1921"}],"wp:attachment":[{"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/media?parent=1805"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/categories?post=1805"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.strehle.de\/tim\/wp-json\/wp\/v2\/tags?post=1805"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}