OpenLDAP pam_ldap crypt SHA-512 passwd HOWTO

Configuring Slapd, debian, and ubuntu to use SHA-512 passwords stored on the LDAP server in crypt format

Also allow users to change their own passwords

Chad S. Matsalla

September 29, 2017

 

I created an Ansible playbook to document the setup of an OpenLDAP server.

I configured pam_ldap on debian and ubuntu clients to use the ldap server.

I discovered that the passwords being stored on the server were being stored in {crypt}passwordhash format. I really wanted it to me stored as described in man 3 crypt:

$id$salt$encrypted

specifically:

{CRYPT}$6$bCFmhgGp8n9T403x$kwTl5QRsRRPHiTsRfPuIbRydXuidEMlvk0QhltoZVVTibNPNcYmbQWMqbD6kXlts5GY8f5n707kExdAbQttNC1

Note that according to crypt 3, "{CRYPT}$6" means "SHA-512 (since glibc 2.7)".

I could not determine the mojo to have the system both allow logins and password changes with SHA-512 passwords being stored in OpenLDAP.

The system had the following moving parts:

  • pam_ldap (on debian testing, 186-4 as of today)
  • pam_unix (from libpam-modules:amd64 1.1.8-3.6 as of today)
  • slapd (slapd 2.4.42+dfsg-2ubuntu3 as of today)
  • Ansible for configuration (v2.X?)

Here’s what’s necessary to have all moving parts mesh together:

  1. /etc/login.defs
    ENCRYPT_METHOD SHA512
  2. /etc/pam.d/common-auth
     1 auth    sufficient      pam_ldap.so use_first_pass
  3. /etc/pam.d/common-password
     1 password    sufficient  pam_ldap.so
     2 password    sufficient  pam_unix.so sha512 obscure min=4 nullok try_first_pass 
  4. /etc/pam.d/common-account
      1 account     sufficient  pam_succeed_if.so uid < 1000 quiet
      2 account     [default=bad success=ok user_unknown=ignore] pam_ldap.so
  5. /etc/ldap.conf
      1 base dc=bioinfo,dc=nrc,dc=ca
      2 uri ldaps://192.168.2.80
      3 ldap_version 3
      4 pam_password exop
      5 pam_filter objectclass=posixAccount
      6 nss_base_passwd ou=People,dc=bioinfo,dc=nrc,dc=ca
      7 nss_base_shadow ou=People,dc=bioinfo,dc=nrc,dc=ca
      8 nss_base_group ou=Groups,dc=bioinfo,dc=nrc,dc=ca
      9 basedn cn=pam-login-user,ou=ldap-security,dc=bioinfo,dc=nrc,dc=ca
    

    NOTICE “exop” (see below)

  6. /etc/pam_ldap.conf
      1 base dc=bioinfo,dc=nrc,dc=ca
      2 uri ldaps://ldap-server
      3 ldap_version 3
      4 pam_password exop
      5 pam_filter objectclass=posixAccount
      6 nss_base_passwd ou=People,dc=bioinfo,dc=nrc,dc=ca
      7 nss_base_shadow ou=People,dc=bioinfo,dc=nrc,dc=ca
      8 nss_base_group ou=Groups,dc=bioinfo,dc=nrc,dc=ca
    

    NOTICE “exop” (see below) and notice the absence of “basedn”

For our facility, pam-login-user is a read-only user as specified by the following acls:

olcAccess: {0}to *       by self write       by * read
olcAccess: {1}to dn.subtree="ou=People,dc=bioinfo,dc=nrc,dc=ca"    by dn.exact
 ="cn=pam-login-user,ou=ldap-security,dc=bioinfo,dc=nrc,dc=ca" read
olcAccess: {2}to dn.subtree="ou=People,dc=bioinfo,dc=nrc,dc=ca"    by dn.exact
 ="cn=pam-login-user,ou=ldap-security,dc=bioinfo,dc=nrc,dc=ca" auth

I don’t use any other ACLs.

I used this ldif to modify olcDatabase={-1}frontend to fit my purposes:

 13 dn: olcDatabase={-1}frontend,cn=config
 14 changetype: modify
 15 replace: olcPasswordHash
 16 olcPasswordHash: {CRYPT}
 17
 18 dn: cn=config
 19 changetype: modify
 20 replace: olcPasswordCryptSaltFormat
 21 olcPasswordCryptSaltFormat: $6$%.16s

The key here is ‘exop’ in ldap.conf and pam_ldap.conf. This sends the new password, unhashed, to the ldap server. The ldap server then hashes the password and stores it in the database. You can see this when you do a search:

root@ldap-server:~# ldapsearch -v -x -LLL -H ldaps://ldap-server -b 'dc=bioinfo,dc=nrc,dc=ca' -D 'cn=pam-login-user,ou=ldap-security,dc=bioinfo,dc=nrc,dc=ca' -w PASSWORD "(uid=chad)” userPassword
ldap_initialize( ldaps://ldap-server:636/??base )
filter: (uid=chad)
requesting: userPassword
dn: uid=chad,ou=People,dc=bioinfo,dc=nrc,dc=ca
userPassword:: e0NSWVBUfSQ2JG5JSkdjVzAxS2JsTzZpcjUkdGE0dTVxQTBzcGhLM3RYcWxSOWh
 SU3U0LjFnaEtMYWk5SWRoNEpyc0d6aThsZUwyM3ZleGtUVXNuelBEVFIySjBxei5FOXc0UkswVm1Y
 a3o4OXdDNTE=
root@ldap-server:~# echo "e0NSWVBUfSQ2JGJDRm1oZ0dwOG45VDQwM3gka3dUbDVRUnNSUlBIaVRzUmZQdUliUnlkWHVpZEVNbHZrMFFobHRvWlZWVGliTlBOY1ltYlFXTXFiRDZrWGx0czVHWThmNW43MDdrRXhkQWJRdHROQzE=" | base64 -d
{CRYPT}$6$bCFmhgGp8n9T403x$kwTl5QRsRRPHiTsRfPuIbRydXuidEMlvk0QhltoZVVTibNPNcYmbQWMqbD6kXlts5GY8f5n707kExdAbQttNC1

That’s what you want to see!

Another key is that pam_ldap.conf does not have the basedn line in it. Notice the ACL that allows a user to change their own password? When you leave the basedn out of pam_ldap.conf, the system binds to the ldap server _as the user_, not as pam-login-user or admin. It first user LDAP auth (that’s why there’s an ACL to allow it), then changes the password.

A full Ansible playbook for my systems is as follows:

  1 - name: Chads standard packages are installed
  2   apt: name={{ item }} state=installed install_recommends=no
  3   with_items:
  4     - libpam-ldap
  5 - name: Set up nsswitch.conf
  6   copy:
  7     dest: "/etc/nsswitch.conf"
  8     backup: yes
  9     content: |
 10       passwd: files ldap
 11       group:  files ldap
 12       shadow: files ldap
 13 - name: Set up common-account
 14   copy:
 15     backup: yes
 16     dest: "/etc/pam.d/common-account"
 17     content: |
 18       account     sufficient  pam_succeed_if.so uid < 1000 quiet
 19       account     [default=bad success=ok user_unknown=ignore] pam_ldap.so
 20       account      required   pam_permit.so
 21 - name: Set up common-auth
 22   copy:
 23     backup: yes
 24     dest: "/etc/pam.d/common-auth"
 25     content: |
 26       auth    sufficient      pam_ldap.so use_first_pass
 27       auth     sufficient     pam_unix.so nullok_secure
 28       auth    requisite       pam_succeed_if.so uid >= 1000 quiet
 29       auth     required  pam_deny.so
 30       auth     required  pam_permit.so
 31       auth     optional  pam_mount.so
 32 - name: Set up common-session
 33   copy:
 34     backup: yes
 35     dest: "/etc/pam.d/common-session"
 36     content: |
 37       session  [default=1]  pam_permit.so
 38       session  requisite    pam_deny.so
 39       session  required     pam_permit.so
 40       session  required     pam_unix.so
 41       session optional     pam_ldap.so
 42       session  optional     pam_mount.so
 43       session required     pam_mkhomedir.so skel=/etc/skel umask=0022
 44 - name: Set up common-password
 45   copy:
 46     backup: yes
 47     dest: "/etc/pam.d/common-password"
 48     owner: "root"
 49     mode: 0644
 50     content: |
 51       password    sufficient  pam_ldap.so
 52       password    sufficient  pam_unix.so sha512 obscure min=4 nullok try_first_pass
 53       password    requisite   pam_deny.so
 54       password    required    pam_permit.so
 55 # - name: Remove pam_ldap.conf
 56 #  file:
 57 #    path: /etc/pam_ldap.conf
 58 #    state: absent
 59 - name: Set up ldap.conf
 60   copy:
 61     backup: yes
 62     dest: "/etc/ldap.conf"
 63     content: |
 64       base dc=bioinfo,dc=nrc,dc=ca
 65       uri ldaps://192.168.2.80
 66       ldap_version 3
 67       pam_password exop
 68       pam_filter objectclass=posixAccount
 69       nss_base_passwd ou=People,dc=bioinfo,dc=nrc,dc=ca
 70       nss_base_shadow ou=People,dc=bioinfo,dc=nrc,dc=ca
 71       nss_base_group ou=Groups,dc=bioinfo,dc=nrc,dc=ca
 72       basedn cn=pam-login-user,ou=ldap-security,dc=bioinfo,dc=nrc,dc=ca
 73 - name: Set up pam_ldap.conf
 74   copy:
 75     backup: yes
 76     dest: "/etc/pam_ldap.conf"
 77     content: |
 78       base dc=bioinfo,dc=nrc,dc=ca
 79       uri ldaps://ldap-server
 80       ldap_version 3
 81       pam_password exop
 82       pam_filter objectclass=posixAccount
 83       nss_base_passwd ou=People,dc=bioinfo,dc=nrc,dc=ca
 84       nss_base_shadow ou=People,dc=bioinfo,dc=nrc,dc=ca
 85       nss_base_group ou=Groups,dc=bioinfo,dc=nrc,dc=ca
 86 - name: Set up /etc/pam_ldap.secret
 87   copy:
 88     backup: yes
 89     dest: "/etc/pam_ldap.secret"
 90     content: |
 91       PASSWORD HERE

I hope this helps someone. At the time I did this (September 2017) I read the first 30 google hits and NONE helped.

Chad Matsalla

Saskatoon, Saskatchewan, Canada

References: