Your Peace of Mind is our Commitment

Contact Us English Recent Articles

Password Expiry Problem

This problem was encountered in a mixed Debian / Ubuntu Linux environment, with Kerberos authentication and an OpenLDAP backend. Some clients are laptops, and are using pam_ccreds for credential caching so that users can login when away from the office.

The problem was that users logging in to a laptop would be told that their password had expired, and forced to change it. Logins on other machines did not report an expired password and did not force the user to change it.

Initial investigation focussed around the PAM configuration on the laptops, and how it differed to the server:

Server PAM

common-auth

auth [success=done new_authtok_reqd=done default=ignore] pam_unix.so try_first_pass audit auth [success=done new_authtok_reqd=done default=ignore] pam_krb5.so minimum_uid=1000 use_first_pass ignore_root forwardable auth [success=ok new_authtok_reqd=ok ignore=ignore default=bad] pam_ldap.so use_first_pass ignore_authinfo_unavail auth [success=done new_authtok_reqd=done ignore=ignore default=bad] pam_ldap.so use_first_pass

common-account

account sufficient pam_ldap.so account required pam_unix.so

Laptop PAM

common-auth

auth [success=5 user_unknown=ignore new_authtok_reqd=ignore default=ignore] pam_unix.so debug nullok_secure try_first_pass auth [success=3 new_authtok_reqd=3 default=ignore] pam_krb5.so minimum_uid=1000 defer_pwchange use_first_pass auth [authinfo_unavail=ignore success=2 new_authtok_reqd=2 default=1] pam_ldap.so debug use_first_pass auth [success=2 default=die] pam_ccreds.so action=validate use_first_pass auth [default=die] pam_ccreds.so action=update auth [default=ignore] pam_ccreds.so action=store auth required pam_permit.so auth optional pam_cap.so

common-account

account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_unix.so debug broken_shadow account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_krb5.so minimum_uid=1000 account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_ldap.so debug account required pam_permit.so

The difficulty is that the laptop needs the pam_unix module before the kerberos and ldap modules, otherwise the whole stack can time out waiting for the network services to respond when the laptop is offline. However, the pam_unix account module is responding that the user's max password age is 0 for users that should be cached. Once a module has flagged the password as expired, the user is forced to change it.

Solution

Contrary to expectation, the solution was not to change the laptop configuration, but to change the access controls on the LDAP server. Reference pages on Kerberos and LDAP recommend LDAP access controls like this:

ldapsearch -x -H ldaps://server.example.com -D cn=config -W -b cn=config "(|(cn=config)(olcDatabase={1}hdb))" olcAccess Enter LDAP Password: # extended LDIF # # LDAPv3 # base with scope subtree # filter: (|(cn=config)(olcDatabase={1}hdb)) # requesting: olcAccess # # config dn: cn=config # {1}hdb, config dn: olcDatabase={1}hdb,cn=config olcAccess: {0}to attrs=userPassword,shadowLastChange,krbPrincipalKey by dn="cn=manager,dc=yuike e,dc=com,dc=hk" write by anonymous auth by self write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=manager,dc=yuikee,dc=com,dc=hk" write by * read # search result search: 2 result: 0 Success # numResponses: 3 # numEntries: 2

But this does not allow the reading of the shadowLastChange attribute by pam_unix. It works when changed to:

ldapsearch -x -H ldaps://bluewhale.yuikee.com.hk -D cn=config -W -b cn=config "(|(cn=config)(olcDatabase={1}hdb))" olcAccess Enter LDAP Password: # extended LDIF # # LDAPv3 # base with scope subtree # filter: (|(cn=config)(olcDatabase={1}hdb)) # requesting: olcAccess # # config dn: cn=config # {1}hdb, config dn: olcDatabase={1}hdb,cn=config olcAccess: {0}to attrs=shadowLastChange by dn="cn=manager,dc=yuikee,dc=com,dc= hk" write by anonymous read by self write by * read olcAccess: {1}to attrs=userPassword,krbPrincipalKey by dn="cn=manager,dc=yuike e,dc=com,dc=hk" write by anonymous auth by self write by * none olcAccess: {2}to dn.base="" by * read olcAccess: {3}to * by dn="cn=manager,dc=yuikee,dc=com,dc=hk" write by * read # search result search: 2 result: 0 Success # numResponses: 3 # numEntries: 2

Here, anonymous is permitted to read shadowLastChange.


More Information