arthurdejong / nss-pam-ldapd

NSS and PAM modules for lookups using LDAP

Home Page:https://arthurdejong.org/nss-pam-ldapd/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

pam, ppolicy and password expiration

montag451 opened this issue · comments

Hi,

I'm currently working on a project where the NSS part is handled by nss-pam-ldapd and the PAM part is handled by nss-pam-ldap. The reason for this choice is that when the project was started nss-pam-ldapd didn't support ppolicy. Now that it is supported, I would like to switch to a full nss-pam-ldapd solution but I have a hard time figuring out how password expiration is supposed to work. With the old solution, when a user try to log in on our system he is invited to change his password if it has expired but with nss-pam-ldapd he can't log in at all. I understand that an "Invalid credentials" (return code 49) is returned by OpenLDAP when the password is expired but the ppolicy overlay add a message indicating that the password has expired. Looking at the code I saw that the ppolicy message is handled by nslcd but still pam_authenticate() return PAM_AUTH_ERR and so the login attempt is rejected. The behaviour of nss-pam-ldap was to return PAM_SUCCESS in pam_authenticate() and PAM_NEW_AUTHTOK_REQD in pam_acct_mgmt() so the application using PAM was able to ask for the renewal of the password.

Am I missing something or is there no way to implement the behaviour of our old system ?

PS : We are using nss-pam-ldapd 0.9.7 on Debian Stretch.

Thanks in advance for you answer.

Hello Mr de Jong,

I am Mr. Arigita, from Spain. I’m stuck with a problem with nslcd and I need help. Before writing here I spent many days searching the web for a solution to my problem without success:

I have a multimaster Openldap setup with ppolicy overlay. Users are forced to change expired passwords. I managed to make all clients to display and prompt for a new password in a nss-pam-ldap normal configuration (/etc/ldap.conf). However, I was limited in filters and I found your packages (nss-pam-ldapd) which helped me to define better group, passwd and authz filters (in /etc/nslcd.conf). So I shitched and started using your nslcd daemon and tools. But now I do not get any password expired warning and no password change prompt is displayed on the client’s machines while login in with expired passwords through ssh. For testing I removed all filters and left a simple nslcd.conf file:

  • nss-pam-ldapd v.0.9.9

  • /etc/nslcd.conf:

uid nslcd
gid nslcd
uri ldap://temis/
base dc=domain
ldap_version 3
binddn cn=leoldap,dc=domain
bindpw ****
ssl start_tls
tls_reqcert allow
tls_cacertfile /etc/ssl/certs/ca-certificates.crt

The situation is as follows:

  • 2 x multimaster slapd servers
  • 1 x HAproxy load balancer. Tcp Ldap traffic forwarded to the multimasters.
  • Many Ubuntu 16, 18 and Debian 8,9 clients. (At the moment only testing 2 clients with Ubuntu 16/18)

Here are some configs and dumps:

  • objectClass=olcPpolicyConfig:

dn: olcOverlay={4}ppolicy,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: {4}ppolicy
olcPPolicyDefault: cn=PWUsuarios,ou=Politicas,ou=Seguridad,ou=Grupos,dc=domain
olcPPolicyHashCleartext: TRUE
olcPPolicyUseLockout: FALSE
olcPPolicyForwardUpdates: FALSE

  • objectClass=pwdPolicy (pwdMaxAge is set to 2 mins for testing)

dn: cn=PWUsuarios,ou=Politicas,ou=Seguridad,ou=Grupos,dc=domain
cn: PWUsuarios
objectClass: pwdPolicy
objectClass: device
objectClass: top
objectClass: pwdPolicyChecker
pwdAllowUserChange: TRUE
pwdAttribute: userPassword
pwdCheckModule: pqchecker.so
pwdCheckQuality: 2
pwdFailureCountInterval: 0
pwdInHistory: 3
pwdLockoutDuration: 3600
pwdMaxFailure: 3
pwdMinLength: 10
pwdMustChange: TRUE
pwdMaxAge: 120
pwdExpireWarning: 120
pwdGraceAuthNLimit: 1
pwdLockout: TRUE

dn: cn=PWApps,ou=Politicas,ou=Seguridad,ou=Grupos,dc=domain
cn: PWApps
objectClass: pwdPolicy
objectClass: device
objectClass: top
objectClass: pwdPolicyChecker
pwdAllowUserChange: FALSE
pwdAttribute: userPassword
pwdCheckModule: pqchecker.so
pwdCheckQuality: 2
pwdFailureCountInterval: 0
pwdGraceAuthNLimit: 0
pwdLockoutDuration: 0
pwdMaxFailure: 3
pwdMinLength: 8

  • nslcd -d

nslcd: DEBUG: NSS_LDAP nss-pam-ldapd 0.9.9
nslcd: DEBUG: ldap_set_option(LDAP_OPT_X_TLS_REQUIRE_CERT,allow)
nslcd: DEBUG: ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE,"/etc/ssl/certs/ca-certificates.crt")
nslcd: DEBUG: CFG: threads 5
nslcd: DEBUG: CFG: uid nslcd
nslcd: DEBUG: CFG: gid 131
nslcd: DEBUG: CFG: uri ldap://temis/
nslcd: DEBUG: CFG: ldap_version 3
nslcd: DEBUG: CFG: binddn cn=leoldap,dc=domain
nslcd: DEBUG: CFG: bindpw ***
nslcd: DEBUG: CFG: base dc=domain
nslcd: DEBUG: CFG: scope sub
nslcd: DEBUG: CFG: deref never
nslcd: DEBUG: CFG: referrals yes
nslcd: DEBUG: CFG: filter aliases (objectClass=nisMailAlias)
nslcd: DEBUG: CFG: filter ethers (objectClass=ieee802Device)
nslcd: DEBUG: CFG: filter group (objectClass=posixGroup)
nslcd: DEBUG: CFG: filter hosts (objectClass=ipHost)
nslcd: DEBUG: CFG: filter netgroup (objectClass=nisNetgroup)
nslcd: DEBUG: CFG: filter networks (objectClass=ipNetwork)
nslcd: DEBUG: CFG: filter passwd (objectClass=posixAccount)
nslcd: DEBUG: CFG: filter protocols (objectClass=ipProtocol)
nslcd: DEBUG: CFG: filter rpc (objectClass=oncRpc)
nslcd: DEBUG: CFG: filter services (objectClass=ipService)
nslcd: DEBUG: CFG: filter shadow (objectClass=shadowAccount)
nslcd: DEBUG: CFG: map group userPassword ""
nslcd: DEBUG: CFG: map passwd userPassword "
"
nslcd: DEBUG: CFG: map passwd gecos "${gecos:-$cn}"
nslcd: DEBUG: CFG: map shadow userPassword ""
nslcd: DEBUG: CFG: map shadow shadowLastChange "${shadowLastChange:--1}"
nslcd: DEBUG: CFG: map shadow shadowMin "${shadowMin:--1}"
nslcd: DEBUG: CFG: map shadow shadowMax "${shadowMax:--1}"
nslcd: DEBUG: CFG: map shadow shadowWarning "${shadowWarning:--1}"
nslcd: DEBUG: CFG: map shadow shadowInactive "${shadowInactive:--1}"
nslcd: DEBUG: CFG: map shadow shadowExpire "${shadowExpire:--1}"
nslcd: DEBUG: CFG: map shadow shadowFlag "${shadowFlag:-0}"
nslcd: DEBUG: CFG: pam_authc_ppolicy yes
nslcd: DEBUG: CFG: bind_timelimit 10
nslcd: DEBUG: CFG: timelimit 0
nslcd: DEBUG: CFG: idle_timelimit 0
nslcd: DEBUG: CFG: reconnect_sleeptime 1
nslcd: DEBUG: CFG: reconnect_retrytime 10
nslcd: DEBUG: CFG: ssl start_tls
nslcd: DEBUG: CFG: tls_reqcert allow
nslcd: DEBUG: CFG: tls_cacertfile /etc/ssl/certs/ca-certificates.crt
nslcd: DEBUG: CFG: pagesize 0
nslcd: DEBUG: CFG: nss_initgroups_ignoreusers kernoops,bin,whoopsie,systemd-network,nslcd,cups-pk-helper,hplip,pulse,rou,daemon,colord,avahi,messagebus,xrdp,backup,gnome-initial-setup,mysql,irc,man,openldap,new...
nslcd: DEBUG: CFG: nss_min_uid 0
nslcd: DEBUG: CFG: nss_uid_offset 0
nslcd: DEBUG: CFG: nss_gid_offset 0
nslcd: DEBUG: CFG: nss_nested_groups no
nslcd: DEBUG: CFG: nss_getgrent_skipmembers no
nslcd: DEBUG: CFG: nss_disable_enumeration no
nslcd: DEBUG: CFG: validnames /^[a-z0-9.@$()]([a-z0-9.@$() ~-]
[a-z0-9._@$()~-])?$/i
nslcd: DEBUG: CFG: ignorecase no
nslcd: DEBUG: CFG: pam_authc_search BASE
nslcd: DEBUG: CFG: cache dn2uid 15m 15m
nslcd: version 0.9.9 starting
nslcd: DEBUG: unlink() of /var/run/nslcd/socket failed (ignored): No such file or directory
nslcd: DEBUG: initgroups("nslcd",131) done
nslcd: DEBUG: setgid(131) done
nslcd: DEBUG: setuid(127) done
nslcd: accepting connections
nslcd: [8b4567] DEBUG: connection from pid=101098 uid=0 gid=0
nslcd: [8b4567] <passwd="rarigita"> DEBUG: myldap_search(base="dc=domain", filter="(&(objectClass=posixAccount)(uid=rarigita))")
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_simple_bind_s("cn=leoldap,dc=domain","") (uri="ldap://temis/")
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_result(): cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain
nslcd: [8b4567] <passwd="rarigita"> (re)loading /etc/nsswitch.conf
nslcd: [8b4567] <passwd="rarigita"> DEBUG: ldap_result(): end of results (1 total)
nslcd: [7b23c6] DEBUG: connection from pid=101098 uid=0 gid=0
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: myldap_search(base="dc=domain", filter="(&(objectClass=posixAccount)(uid=rarigita))")
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_simple_bind_s("cn=leoldap,dc=domain","
") (uri="ldap://temis/")
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_result(): cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain
nslcd: [7b23c6] <passwd="rarigita"> DEBUG: ldap_result(): end of results (1 total)
nslcd: [3c9869] DEBUG: connection from pid=101098 uid=0 gid=0
nslcd: [3c9869] <shadow="rarigita"> DEBUG: myldap_search(base="dc=domain", filter="(&(objectClass=shadowAccount)(uid=rarigita))")
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_simple_bind_s("cn=leoldap,dc=domain","") (uri="ldap://temis/")
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_result(): cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain
nslcd: [3c9869] <shadow="rarigita"> DEBUG: ldap_result(): end of results (1 total)
nslcd: [334873] DEBUG: connection from pid=101098 uid=0 gid=0
nslcd: [334873] <passwd="rarigita"> DEBUG: myldap_search(base="dc=domain", filter="(&(objectClass=posixAccount)(uid=rarigita))")
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_simple_bind_s("cn=leoldap,dc=domain","
") (uri="ldap://temis/")
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_result(): cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain
nslcd: [334873] <passwd="rarigita"> DEBUG: ldap_result(): end of results (1 total)
nslcd: [b0dc51] DEBUG: connection from pid=101098 uid=0 gid=0
nslcd: [b0dc51] <authc="rarigita"> DEBUG: nslcd_pam_authc("rarigita","sshd","")
nslcd: [b0dc51] <authc="rarigita"> DEBUG: myldap_search(base="dc=domain", filter="(&(objectClass=posixAccount)(uid=rarigita))")
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_simple_bind_s("cn=leoldap,dc=domain","
") (uri="ldap://temis/")
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_result(): cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain
nslcd: [b0dc51] <authc="rarigita"> DEBUG: myldap_search(base="cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain", filter="(objectClass=)")
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_initialize(ldap://temis/)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_rebind_proc()
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_start_tls_s()
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_sasl_bind("cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain","
**") (uri="ldap://temis/") (ppolicy=yes)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (Password expired)
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_parse_result() result: Invalid credentials
nslcd: [b0dc51] <authc="rarigita"> DEBUG: failed to bind to LDAP server ldap://temis/: Invalid credentials
nslcd: [b0dc51] <authc="rarigita"> DEBUG: ldap_unbind()
nslcd: [b0dc51] <authc="rarigita"> cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain: Invalid credentials
nslcd: [b0dc51] <authc="rarigita"> cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain: Password expired
nslcd: [495cff] DEBUG: connection from pid=101160 uid=0 gid=0
nslcd: [495cff] <group/member="root"> DEBUG: ignored group member

  • Tail -f /var/log/syslog | grep slapd

Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 fd=21 ACCEPT from IP=10.6.22.124:44996 (IP=10.6.22.121:389)
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=0 STARTTLS
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=0 RESULT oid= err=0 text=
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 fd=21 TLS established tls_ssf=256 ssf=256
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=1 BIND dn="cn=leoldap,dc=domain" method=128
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=1 BIND dn="cn=leoldap,dc=domain" mech=SIMPLE ssf=0
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=1 RESULT tag=97 err=0 text=
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=2 SRCH base="dc=domain" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=rarigita))"
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=2 SRCH attr=uidNumber cn gecos uid objectClass homeDirectory gidNumber loginShell
Mar 18 13:27:36 CarlosIs99 slapd[1757]: conn=6320 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 fd=22 ACCEPT from IP=10.6.22.124:45032 (IP=10.6.22.121:389)
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=0 STARTTLS
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=0 RESULT oid= err=0 text=
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 fd=22 TLS established tls_ssf=256 ssf=256
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=1 BIND dn="cn=leoldap,dc=domain" method=128
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=1 BIND dn="cn=leoldap,dc=domain" mech=SIMPLE ssf=0
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=1 RESULT tag=97 err=0 text=
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=2 SRCH base="dc=domain" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=rarigita))"
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=2 SRCH attr=uidNumber cn gecos uid objectClass homeDirectory gidNumber loginShell
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6321 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6322 fd=23 ACCEPT from IP=10.6.22.124:45036 (IP=10.6.22.121:389)
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6322 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6322 op=0 STARTTLS
Mar 18 13:27:41 CarlosIs99 slapd[1757]: conn=6322 op=0 RESULT oid= err=0 text=
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 fd=23 TLS established tls_ssf=256 ssf=256
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=1 BIND dn="cn=leoldap,dc=domain" method=128
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=1 BIND dn="cn=leoldap,dc=domain" mech=SIMPLE ssf=0
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=1 RESULT tag=97 err=0 text=
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=2 SRCH base="dc=domain" scope=2 deref=0 filter="(&(objectClass=shadowAccount)(uid=rarigita))"
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=2 SRCH attr=shadowFlag shadowMax shadowMin shadowLastChange uid shadowExpire shadowInactive shadowWarning
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6322 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 fd=24 ACCEPT from IP=10.6.22.124:45038 (IP=10.6.22.121:389)
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=0 STARTTLS
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=0 RESULT oid= err=0 text=
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 fd=24 TLS established tls_ssf=256 ssf=256
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=1 BIND dn="cn=leoldap,dc=domain" method=128
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=1 BIND dn="cn=leoldap,dc=domain" mech=SIMPLE ssf=0
Mar 18 13:27:42 CarlosIs99 slapd[1757]: conn=6323 op=1 RESULT tag=97 err=0 text=
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6323 op=2 SRCH base="dc=domain" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=rarigita))"
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6323 op=2 SRCH attr=uidNumber cn gecos uid objectClass homeDirectory gidNumber loginShell
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6323 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6320 op=3 SRCH base="dc=domain" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=rarigita))"
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6320 op=3 SRCH attr=uid uidNumber
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6320 op=3 SEARCH RESULT tag=101 err=0 nentries=1 text=
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 fd=25 ACCEPT from IP=10.6.22.124:45050 (IP=10.6.22.121:389)
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=0 EXT oid=1.3.6.1.4.1.1466.20037
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=0 STARTTLS
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=0 RESULT oid= err=0 text=
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 fd=25 TLS established tls_ssf=256 ssf=256
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=1 BIND dn="cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain" method=128
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=1 BIND dn="cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain" mech=SIMPLE ssf=0
Mar 18 13:27:43 CarlosIs99 slapd[1757]: ppolicy_bind: Entry cn=Rodrigo Arigita,ou=Usuarios,ou=Bandam,ou=Externos,dc=domain has an expired password: 0 grace logins
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=1 RESULT tag=97 err=49 text=
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 op=2 UNBIND
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6324 fd=25 closed
Mar 18 13:27:43 CarlosIs99 slapd[1757]: conn=6320 op=4 ABANDON msg=4

/etc/pam.d/common-* are defaults as set by the pam-auth-update.

  • /etc/pam.d/common-auth:

auth [success=2 default=ignore] pam_unix.so nullok_secure
auth [success=1 default=ignore] pam_ldap.so minimum_uid=1000 use_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
auth optional pam_cap.so

  • /etc/pam.d/common-account:

account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
account requisite pam_deny.so
account required pam_permit.so
account [success=ok new_authtok_reqd=done ignore=ignore user_unknown=ignore authinfo_unavail=ignore default=bad] pam_ldap.so minimum_uid=1000

  • /etc/pam.d/common-password:

password [success=2 default=ignore] pam_unix.so obscure sha512
password [success=1 default=ignore] pam_ldap.so minimum_uid=1000 try_first_pass
password requisite pam_deny.so
password required pam_permit.so
password optional pam_gnome_keyring.so

Groups are posixGroups, accounts are posixAccount, shadowAccount and inetOrgPerson. But I don’t get any password expired password notification at the ssh login prompt. Is this normal?. What am I missing?. I have read plenty of posts about this and I belive it must be working on a default clean install of the packages...

Password expired and password change prompt is working on nss-pam-ldap config. Can you provide advice or help? Itwould be greatly appreciated. I’m 3 days testing nslcd without success to display expired password message at ssh login prompt. Also, would this be supported on debian 7 and Ubuntu 14? We have few older setups in our network.

Thanks you.

@rarigita Have you tried to apply my patch #17 ? It has fixed this issue for me. The pull request is quite old now and I don't think it will ever be merged so you will have to maintain your own version of nss-pam-ldapd unfortunately.

You wre using v.0.9.7. Do you know if this was solved on newer versions: 0.9.9 or 0.9.10? We have quite a lot of debian/ubuntu clients in our network, and I don't like the idea of installing the patched package. I don't know why this is not working by default.

You wre using v.0.9.7. Do you know if this was solved on newer versions: 0.9.9 or 0.9.10? We have quite a lot of debian/ubuntu clients in our network, and I don't like the idea of installing the patched package. I don't know why this is not working by default.

I don't know if it has been corrected in the following versions but I doubt it

commented

I think I just ran into the same issue? Though I have a hard time following this thread.

My observation of the issue basically boils down to: nslcd binds, and gets the ppolicy response that a password change is in order.
Then it does the search governed by pam_authc_search.

Now: if ppolicy has expired/wants to reset the users password, that search will fail, because it will block any operation that's not changing the users password.
And because that search fails, the original ppolicy response is all but forgotten about, and an auth failure is returned.

I was able to confirm that by setting "pam_authc_search NONE" in my config. And sure enough, with that set, I get greeted with a password change dialog on login, and no longer with an auth failure.

Edit: I should add that the proposed fix in #17 does not work for me. Without setting pam_authc_search to NONE, I still do not get a password change prompt.

I also run into this issue. In my case the proposed fix in #17 worked. Changing the setting pam_authc_search, didn't make any difference.

Sorry for not commenting on this sooner but I have a hard time properly reproducing this and getting a clear grip on the implications of the proposed change (and overall lack of time).

With a normal configuration:

  • nslcd does an authentication attempt to the LDAP server (a BIND operation, requesting extra ppolicy response)
  • the LDAP server includes an overall result (LDAP_SUCCESS if the authentication succeeded, LDAP_INVALID_CREDENTIALS if the password was incorrect) as well as a ppolicy result (these extra messages can contain more data as to why the authentication failed or an indication that the user should change their password)
  • it seems that the LDAP server can respond with PP_passwordExpired both when the overall authentication succeeded or failed (in the former case the user still has a grace period to change the password, in the latter case not)
  • the change in #17 ignores overall authentication failures if the server responds with PP_passwordExpired (or PP_changeAfterReset) ppolicy controls
  • I'm pretty confident nslcd conforms to the behaviour defined in the expired Internet-Draft: https://datatracker.ietf.org/doc/html/draft-behera-ldap-password-policy-11#name-bind-operation

I recommend not applying the patch in #17 because it also ignores authentication errors when the password expired and the user has used up the grace logins (and perhaps more errors. This is why it was also necessary to disable pam_authc_search with the patch because the authentication actually failed. Since the authentication failed, the user also will not be allowed to change their password anyway but, depending on the application that uses PAM, it might let the user into the system with an expired password (e.g. when the application does not support changing the user's password or allows cancelling password change).

The proper solution is to have the LDAP server return a correct ppolicy control message in combination with the authentication result. This might be a ppolicy configuration issue (setting a higher pwdGraceAuthnLimit higher could resolve the issue, especially since nslcd may end up authenticating the user multiple times if an actual password change is needed due to how PAM works; another candidate is pwdExpireWarning). When using OpenLDAP: the behaviour of the ppolicy controls has changed between 2.4 and 2.5 so it might be needed to update the configuration.

It could be that some LDAP servers get confused when ppolicy controls are requested. In that case adding pam_authc_ppolicy no to nslcd.conf might help (nslcd will still check the shadow properties on the account if they are present).

If you do run into this issue please post configuration information, LDAP server versions and configuration and logs (e.g. like rarigita posted).

@arthurdejong Thank you for your insight. Just fyi when I use the patch alone (i.e. without disabling pam_authc_search ) I do not see any implications as described in the fourth bullet item ( ...ignores overall authentication failures if the server responds with PP_passwordExpired (or PP_changeAfterReset) ppolicy controls). Authentication errors are not ignored after the password has expired and the grace logins have been used.

Hi,
To make it work I had to add "pam_authc_search NONE" to nslcd.conf. I kept "pam_authc_ppolicy yes".
Would this lead to any issue?

Thank you

Setting pam_authc_search NONE by itself should not be a problem. The original reason for its implementation was that some LDAP servers returned an authentication success even though authentication actually failed while the following search would fail (this was mostly seen when trying to authenticate with a blank password). Since the PAM module now also has a nullok option blank passwords are not accepted by default any more. I would say combining the two might be a bad idea (depending on your LDAP server).

Thank you for your reply @arthurdejong. It all makes sense.
Best regards

@arthurdejong : If you don't mind, I will close this issue