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:
-
/etc/login.defs
ENCRYPT_METHOD SHA512
-
/etc/pam.d/common-auth
1 auth sufficient pam_ldap.so use_first_pass
-
/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
-
/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
/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)
/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: