Univention Bugzilla – Bug 36215
Deny posix logins if user password expiration date has been reached
Last modified: 2018-02-20 18:02:43 CET
Currently a LDAP bind is possible if a user password has expired/reached the expiration date. Every application using LDAP bind for authentication has to check on its own if the password has expired. There are some 3rd party applications that do not check the shadowLastChange/shadowMax attributes. There should be an optional cron job that check regulary if a user password has expired and then disables the posix login.
The package univention-ldap now adds the attributes shadowMax and shadowExpire to the LDAP indices: - shadowMax → pres - shadowExpire → eq This way, the object lookup will much faster is large environments. The shadowMax attribute is required for Bug #35088 that has been also fixed. YAML: 2014-10-17-univention-ldap.yaml In univention-server-master (src pkg: univention-server) a new cron job has been added that would call the new script /usr/share/univention-directory-manager-tools/lock_expired_passwords on a daily basis if the cron job is enabled. The cron job is scheduled for execution every night at 03:23 (this may be changed via UCR but is undocumented on purpose; if unset 03:23 is used). The cron job may be enabled via directory/manager/user/lock_expired_passwords/cron/enabled. By default, the cron job is disabled. If enabled, user accounts with expired password will be locked automatically via the cron job and therefore the users are unable to authenticate via e.g. LDAP simple bind. YAML: 2014-10-17-univention-server.yaml The script looks for user accounts that have expired user passwords (shadowLastChange + shadowMax <= today). For all accounts found, the POSIX account will be locked so a simple LDAP bind will be no longer possible. The password expiration date is handled correctly by Windows/kerberos, so no action is required here. Please note that this script may need a lot of resources. It looks for users with shadowMax=* and then calculates the password expiration date via UDM. YAML: 2014-10-15-univention-directory-manager-modules.yaml
The latest Jenkins test failed: http://jenkins.knut.univention.de:8080/job/UCS-3.2/job/UCS-3.2-3/job/Autotest%20MultiEnv/SambaVersion=s3,Systemrolle=master/64/testReport/61_udm-users/25_script_lock_expired_passwords/test/
Changes have been also merged to UCS 4. (In reply to Stefan Gohmann from comment #3) > The latest Jenkins test failed: Disabled some checks due to bug #36210 → FIXED ucs-test
I have a user with an expired password. I executed ./lock_expired_passwords to disable the posix account (ldapsearch with that account is no longer possible). A login with that user on UMC still works and the UMC wants me to change the password. All OK up to now. But during the password change to posix lock is not removed, ldapsearch i still not possible for that user.
please revert the changes, we have decided to move this to 4.0-0-errata
Changes have been reverted: univention-server: 3.2: r55435 + r55436 + r55439 4.0: r55437 + r55438 univention-ldap: 3.2: r55441 + r55442 I've left the 4.0 change and added a changelog: r55445 univention-directory-manager-modules: 3.2: r55443 + r55444 + r55446 I've left the 4.0 change and added a changelog: r55445
(In reply to Stefan Gohmann from comment #6) > Changes have been reverted: > > univention-server: > 3.2: r55435 + r55436 + r55439 > 4.0: r55437 + r55438 > > univention-ldap: > 3.2: r55441 + r55442 > I've left the 4.0 change and added a changelog: r55445 > > univention-directory-manager-modules: > 3.2: r55443 + r55444 + r55446 > I've left the 4.0 change and added a changelog: r55445 ok
I've also removed the test cases in 3.2-3 and 3.2-4: r55493 + r55494
*** Bug 36318 has been marked as a duplicate of this bug. ***
Added a new slapd overlay shadowmax (97_shadowbind_overlay_rules.patch, 97_shadowbind_overlay.quilt). This overlay checks shadowExpire and shadowMax/shadowLastChange (of the bindDN object) during LDAP bind and denies the login if * now > shadowExpire * now > (shadowMax + shadowLastChange) with LDAP_INSUFFICIENT_ACCESS->"password expired"/"account expired". The overlay ignores the following objects: * rootdn/updatedn * objects without the shadowAccount objectClass * all objects that match the LDAP filter from the shadowbind-ignore-filter config parameter (default is (objectClass=univentionDomainController)) univention-ldap-server: added shadowmax overlay config in 40univention-ldap-server_database openldap: cherry picked from 4.1 and rebuilt in 4.2 with new patches, built with special version 2.4.42+dfsg-2.9999.201609051147, otherwise the new 4.2 version (from the build-system) is smaller than in 4.1. -> build-package-ng -P ucs -r 4.2-0-0 --no-pbuilder-update \ -v 2.4.42+dfsg-2.9999.201609051147 -p openldap tests: -> time ucs-test -s ldap -E dangerous without shadowbind fail "Test userPassword with K5KEY in combination with userexpiry." real 34m7.517s user 3m35.632s sys 0m40.872s with shadowbind fail "Test userPassword with K5KEY in combination with userexpiry." real 34m22.515s user 3m33.060s sys 0m39.984s -> python ldap bind dn = 'uid=test1,cn=users,dc=w2k12,dc=test' pw = 'univention1' lo = ldap.open('10.200.7.150') for i in xrange(1, 10000): try: lo.simple_bind_s(dn, pw) except ldap.INSUFFICIENT_ACCESS as err: pass with shadowbind real 0m31.879s user 0m0.416s sys 0m0.272s without shadowbind real 0m29.482s user 0m0.396s sys 0m0.208s for i in xrange(1, 10000): lo, po = univention.admin.uldap.getAdminConnection() lo.lo.lo.unbind() with shadowbind real 1m36.172s user 1m0.172s sys 0m3.480s without shadowbind real 1m42.059s user 1m4.584s sys 0m3.808s
We need to fix Bug #41786 and the test case 60_umc/07_expired_password so that UMC can detect the new error messages and respond with an appropriate error.
debian/univention-ldap-server.postinst:» ldap/shadowbind?true This is by default set to true. Is this correct?
We have various places in the code which expect ldap.INVALID_CREDENTIALS instead of ldap.INSUFFICIENT_ACCESS (e.g. the UMC-Server crashes, the Self-Service doesn't function correctly anymore and raises Tracebacks to the frontend). Why is that error code used? Is ldap.INVALID_CREDENTIALS incorrect? Should we adapt uldap.py to catch this also?
(In reply to Florian Best from comment #12) > debian/univention-ldap-server.postinst:» ldap/shadowbind?true > > This is by default set to true. Is this correct? no, should be deactivated by default (In reply to Florian Best from comment #13) > We have various places in the code which expect ldap.INVALID_CREDENTIALS > instead of ldap.INSUFFICIENT_ACCESS (e.g. the UMC-Server crashes, the > Self-Service doesn't function correctly anymore and raises Tracebacks to the > frontend). > Why is that error code used? I thought it is more appropriate. > Is ldap.INVALID_CREDENTIALS incorrect? I a certain way, yes, the credentials are NOT invalid, they account expired. > Should we adapt uldap.py to catch this also? We can change from ldap.INSUFFICIENT_ACCESS to ldap.INVALID_CREDENTIALS is that make things easier, please ask Stefan.
The description for both these status codes is: === 0x31 49 LDAP_INVALID_CREDENTIALS IESG RFC 4511 DSA Indicates that during a Bind Request operation one of the following occurred: * The client passed either an incorrect DN or password. * The password is incorrect because it has expired, Intruder Detection has locked the account, or some other similar reason. 0x32 50 LDAP_INSUFFICIENT_ACCESS IESG RFC 4511 DSA Indicates that the caller does not have sufficient rights to perform the requested operation. === https://ldapwiki.com/wiki/LDAP%20Result%20Codes Or this one: === 49: Invalid Credentials This indicates that the client attempted to bind as a user that does not exist, attempted to bind as a user that is not allowed to bind (e.g., because it has expired, because it has been locked because of too many failed authentication attempts, etc.), or attempted to bind with credentials that were not correct for the target user. 50: Insufficient Access Rights This indicates that the client does not have permission to perform the requested operation. https://www.ldap.com/ldap-result-code-reference ==== I think 49 is more correct as the password/account is expired and this happens during bind(). 50 looks more like an failing operation due to missing ACLs.
Which of both error codes does the OpenLDAP code return and in which situation?
(In reply to Arvid Requate from comment #16) > Which of both error codes does the OpenLDAP code return and in which > situation? INVALID_CREDENTIALS during bind, INSUFFICIENT_ACCESS during ACL problems: UCS 4.1: >>> import univention.uldap >>> x = univention.uldap.access(host='localhost') >>> x.bind('cn=foo', 'bar') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/pymodules/python2.7/univention/uldap.py", line 165, in bind self.lo.simple_bind_s(self.binddn, self.__encode_pwd(self.bindpw)) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 881, in simple_bind_s res = self._apply_method_s(SimpleLDAPObject.simple_bind_s,*args,**kwargs) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 862, in _apply_method_s return func(self,*args,**kwargs) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 215, in simple_bind_s resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 476, in result3 resp_ctrl_classes=resp_ctrl_classes File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 483, in result4 ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 106, in _ldap_call result = func(*args,**kwargs) ldap.INVALID_CREDENTIALS: {'desc': 'Invalid credentials'} >>> x = univention.uldap.getMachineConnection() >>> x.modify('cn=admin,dc=school,dc=local', [('objectClass', None, 'person')]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/pymodules/python2.7/univention/uldap.py", line 468, in modify self.modify_s(dn, ml) File "/usr/lib/pymodules/python2.7/univention/uldap.py", line 494, in modify_s self.lo.modify_s(dn, ml) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 364, in modify_s return self.result(msgid,all=1,timeout=self.timeout) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 465, in result resp_type, resp_data, resp_msgid = self.result2(msgid,all,timeout) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 469, in result2 resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all,timeout) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 476, in result3 resp_ctrl_classes=resp_ctrl_classes File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 483, in result4 ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop) File "/usr/lib/python2.7/dist-packages/ldap/ldapobject.py", line 106, in _ldap_call result = func(*args,**kwargs) ldap.INSUFFICIENT_ACCESS: {'desc': 'Insufficient access'}
Ok that's a clear point for ldap.INVALID_CREDENTIALS I think.
(In reply to Arvid Requate from comment #18) > Ok that's a clear point for ldap.INVALID_CREDENTIALS I think. yep, changed the overlay to return LDAP_INVALID_CREDENTIALS
(In reply to Felix Botner from comment #14) > (In reply to Florian Best from comment #12) > > debian/univention-ldap-server.postinst:» ldap/shadowbind?true > > > > This is by default set to true. Is this correct? > > no, should be deactivated by default As discussed, it should be enabled for new installed systems by default and deactivated for updated systems. Please add a note to the "Postprocessing of the update" chapter.
(In reply to Stefan Gohmann from comment #20) > (In reply to Felix Botner from comment #14) > > (In reply to Florian Best from comment #12) > > > debian/univention-ldap-server.postinst:» ldap/shadowbind?true > > > > > > This is by default set to true. Is this correct? > > > > no, should be deactivated by default > > As discussed, it should be enabled for new installed systems by default and > deactivated for updated systems. Please add a note to the "Postprocessing of > the update" chapter. changelog/release-notes-4.2-0-de.xml changelog/release-notes-4.2-0-en.xml r76908 univention-ldap: activate/deactivate ldap/shadowbind on installation/update
@arvid i have applied your patch and rebuilt openldap (2.4.42+dfsg-2.A~4.2.0.201703021814)
Looks pretty good, in the normal Samba/AD setup it works: * udm policies/pwhistory modify --dn ... --set expiryInterval=10 * create a user directly in samba via samba-tool -> shadowLastChange is set * artificially modify shadowLastChange to 11 days earlier * => ldapsearch denied * kapsswd on that user => shadowLastChange is updated * => ldapsearch works again Now for the corner case we discussed: we probably need to exclude cases where userPassword: {KINIT} This happens in AD-Connector mode: On a UCS system configured as AD-member I adjusted the default UDM password policy to 10 days max (via UMC). When I create a new user in AD, the S4-Connector uses the UDM users/user module which apparently writes shadowLastChange and shadowMax: ================================================================== dn: uid=wuser3,cn=users,dc=w2k8r2d2,dc=ar uid: wuser3 krb5PrincipalName: wuser3@W2K8R2D2.AR objectClass: krb5KDCEntry objectClass: person objectClass: automount objectClass: top objectClass: inetOrgPerson objectClass: sambaSamAccount objectClass: organizationalPerson objectClass: univentionPWHistory objectClass: univentionMail objectClass: univentionSAMLEnabled objectClass: shadowAccount objectClass: krb5Principal objectClass: posixAccount objectClass: univentionObject uidNumber: 2008 sambaAcctFlags: [U ] shadowMax: 10 krb5MaxLife: 86400 shadowLastChange: 17227 cn: wuser3 wname3 krb5PasswordEnd: 20170312000000Z krb5Key: ... krb5MaxRenew: 604800 krb5KeyVersionNumber: 1 loginShell: /bin/bash univentionObjectType: users/user krb5KDCFlags: 126 sambaPwdLastSet: 1488477369 sambaPasswordHistory: ... displayName: wuser3 wname3 sambaSID: S-1-5-21-584594710-3545396725-2272022506-5016 gecos: wuser3 wname3 sn: wname3 pwhistory: ... homeDirectory: /home/wuser3 givenName: wuser3 gidNumber: 5001 sambaPrimaryGroupSID: S-1-5-21-584594710-3545396725-2272022506-513 userPassword: {KINIT} sambaNTPassword: NO PASSWORD********************* univentionObjectFlag: synced ================================================================== These values never get changed, even when I run kpasswd on that user. As a test I've set shadowLastChange to 17227 - 11 = 17216: ================================================================== root@master110:~# ldapsearch -D 'uid=wuser3,cn=users,dc=w2k8r2d2,dc=ar' \ -w Univention.2 -b "dc=w2k8r2d2,dc=ar" -s base ldap_bind: Invalid credentials (49) additional info: password expired root@master110:~# kpasswd wuser3 wuser3@W2K8R2D2.AR's Password: New password for wuser3@W2K8R2D2.AR: Verify password - New password for wuser3@W2K8R2D2.AR: Success root@master110:~# ldapsearch -D 'uid=wuser3,cn=users,dc=w2k8r2d2,dc=ar' \ -w Univention.3 -b "dc=w2k8r2d2,dc=ar" -s base ldap_bind: Invalid credentials (49) additional info: password expired ==================================================================
Created attachment 8492 [details] patch for 97_shadowbind_overlay.quilt OK, here is a patch to add a test for userPassword={KINIT} and skip the shadow tests. But i wonder if we should just modify to default shadowbind-ignore-filter to "(|(objectClass=univentionDomainController)(userPassword={KINIT}))" to be more flexible. The pwd_scheme_kinit is activated by default, even without member mode. So are we sure {KINIT} is only used in member mode (shadow tests may make sense, if KINIT is actiavted but UCS is used as kerberos server)
Would be great if the checks for password expiration and account expiration could be activated independently. I see UCS@school+CSP scenarios where account expiration is desired but password expiration would lead to problems.
(In reply to Sönke Schwardt-Krummrich from comment #25) > Would be great if the checks for password expiration and account expiration > could be activated independently. I see UCS@school+CSP scenarios where > account expiration is desired but password expiration would lead to problems. Yes, good point, but i think 4.2-0-errata is enough (see Bug # 43725, or? (In reply to Arvid Requate from comment #23) > Now for the corner case we discussed: we probably need to exclude cases where > userPassword: {KINIT} > ... As discusses, i changed the default ignore filter to also ignore userPassword={KINIT} objects.
The whole logic for the password and account expiry parsing is also solved by pam_ldap. This overlay module will conflict with pam_ldap as now the bind() always fails but pam_ldap makes a bind() and can't detect then anymore if e.g. the account is expired, the password is expired, …. pam_ldap is therefore unable to respond with PAM_ACCT_EXPIRED / PAM_PERM_DENIED / PAM_AUTHTOKEN_REQD in PAM acct_mgmt() and chauthtok() is also broken then.
Hmm, maybe we can learn something from the way the ppolicy overlay responds and the way pam_ldap is supposed to handle that? e.g. https://www.linux.com/blog/openldap-ppolicy-overlay-user-authentication If we have to home-brew something then maybe it's possible to return some additional info along with the error code and adjust pam_ldap to evaluate that info. That info should only be returned from OpenLDAP if the authentication generally worked.
OK, there is maybe a problem with clients using only pam_ldap for auth and account. With the shadowbind overlay activated pam auth no longer works and instead of "password expired" or something like that, they get "invalid credentials" (because pam ldap auth fails instead of pam ldap account). But this also depends on the pam_ldap.conf (see below). But, UCS uses pam_krb instead of pam_ldap. So i see no problem there -> univention-ldapsearch -D uid=test1,dc=four,dc=two -x -w Univention.99 ldap_bind: Invalid credentials (49) additional info: password expired -> ssh test1@10.200.7.50 Password: You are required to change your password immediately (password aged) Current Kerberos password: -> UMC password change also works Maybe we have to recheck http://docs.software-univention.de/domain-4.1.html#ext-dom-ubuntu (auth_provider = krb5, id_provider = ldap) Besides, even if i remove pam_krb from the pam stack, i get -> su test1 Passwort: You are required to change your LDAP password immediately. su: Fehler beim Ändern des Authentifizierungstoken from pam_ldap, because (at least in our pam_ldap.conf) we use a rootbindn, which is apparently used for the ldap lookup. ppolicy: There is no special handling im pam_ldap for ppolicy. So is see no problem here.
(In reply to Felix Botner from comment #29) > OK, there is maybe a problem with clients using only pam_ldap for auth and > account. With the shadowbind overlay activated pam auth no longer works and > instead of "password expired" or something like that, they get "invalid > credentials" (because pam ldap auth fails instead of pam ldap account). But > this also depends on the pam_ldap.conf (see below). Yes > But, UCS uses pam_krb instead of pam_ldap. UMC uses pam_ldap (auth and account) for users/user accounts with only the option "Simple authentication account". > So i see no problem there > > -> univention-ldapsearch -D uid=test1,dc=four,dc=two -x -w Univention.99 > ldap_bind: Invalid credentials (49) > additional info: password expired but yes, this is probably not a problem.
Ok, works, also with UCC. Changelog ok.
UCS 4.2 has been released: https://docs.software-univention.de/release-notes-4.2-0-en.html https://docs.software-univention.de/release-notes-4.2-0-de.html If this error occurs again, please use "Clone This Bug".