+++ This bug was initially created as a clone of Bug #643022 +++ <snip> Also when I do not specify the password when creating the user (with -p), some kind of "default" string appears in password: # luseradd tst ldap search listing (see the user pass): # tst, omoris, free, gold.testday dn: uid=tst,ou=omoris,ou=free,dc=gold,dc=testday uidNumber: 503 uid: tst shadowMin: 0 shadowMax: 99999 shadowWarning: 7 shadowExpire: -1 homeDirectory: /home/tst loginShell: /bin/bash shadowInactive: -1 userPassword:: ISE= [snip] --- Additional comment from mitr on 2010-10-14 11:58:39 EDT --- <snip> The "userPassword:: ISE=" value decodes to '!!', which should [not] let the user to log in, per RFC 2307.
The comment above is correct WRT RFC 2307 - i.e. using only nss_ldap and using NSS to read the shadow entry and authenticate against it: in that case the RFC 2307 prohibition of unprefixed values means the shadow password returned is always "*". This is, unfortunately, not general enough - when using pam_ldap on a client (or when connecting to the LDAP server using the LDAP protocol, and perhaps in other cases), the generic definition of "userPassword" (RFC 4519) applies, and the "!!" values is considered a plaintext password that can be used to succesfully log in, at least when the server is openldap (I haven't checked against 389). Impact: (luseradd) without specifying a password creates an user account that can, under some circumstances, be used to log in using a default password. In usual cases, the password would be set either in the initial luseradd invocation, or in a following lpasswd invocation, leaving at most a short window when the "!!" value can be used. It is possible, however, to enter "system" accounts into LDAP for which the password is never set; in such cases the default password could persist for indefinite time. This would argue for a rather high vulnerability severity rating. AFAICS the bug has been in libuser for a very long time, since RHEL 3 at least. I have Cc:ed LDAP server maintainers to this bug - could you please verify my understanding of the situation?
Adding Nalin as the pam_ldap maintainer as he might have some good ideas about where the fault lies.
pam_ldap is not at fault. There are three basic ways to authenticate an user defined in LDAP: - Load the encrypted password from the LDAP server to the client, verify password locally. This is what nss_ldap without pam_ldap does, an it seems to be fine. - Try to authenticate to the LDAP server using the provided password. This is what pam_ldap does (by definition), and "!!" may work. This depends on server capabilities and configuration, but we already know that at least openssl can treat the password as unencrypted. Note that pam_ldap can not filter out "!!" - it really may be a legitimate password. All policy is in the LDAP server in this scenario. - Use an independent authentication mechanism, e.g. Kerberos. In this case the userPassword value is irrelevant. I wasn't able to figure out from sssd-ldap(5) what pam_sss uses. Usually, Kerberos is preferred to pam_ldap with TLS, which is preferred to nss_ldap without pam_ldap. But any of these configurations is "valid" and may be used by our users in production setups. Also note that regardless of the authentication mechanism used to log in users to client computers, "!!" may remain a valid password for the user to log in to the LDAP server directly. The server administrator needs to set up ACLs to restrict what users can change, regardless of the libuser problem - but there are some unauthorized access scenarios, for example an attacker might connect to the LDAP server using the "!!" password and change the user's password to something else. AFAICS the only place where this can be fixed is libuser. Changing userdefaults/lU_USERPASSWORD to "{crypt}!!" is an acceptable workaround if the "ldap" module is used - otherwise this value is not defined, although it would work in practice. A correct fix would be to use the "{crypt}!!" value internally in the LDAP module. There is a theoretical case where libuser is used to create the user record both locally and in LDAP - in that case there is no 100% correct solution in the libuser model because it cannot use a different value for different modules.
Please disregard lpasswd - it is attempting to authenticate using the local PAM configuration as the target user; this does not make much sense, but it is also not really relevant to the primary issue.
(In reply to comment #4) > Adding Nalin as the pam_ldap maintainer as he might have some good ideas about > where the fault lies. The pam_ldap password check is done by attempting an LDAP bind, so the comparison with the user-supplied value is performed at the server. The check for '!!' is done in pam_unix during the auth phase, so other modules aren't going to heed it. If the server supports checking {CRYPT} passwords, then I agree that always using {CRYPT} is the best route. I'm just not certain that all servers do support it. If we can just omit a default 'userPassword' value without breaking anything, that might be an alternative.
If we have libuser set _no_ password by default, what will happen? Will that allow a LDAP-based user to authenticate without a password? What about a local user? If libuser is using shadow/passwd, and we set the default to {crypt}!!, will the local system treat that the same as !! and prevent logins?
(In reply to comment #10) > If we have libuser set _no_ password by default, what will happen? OpenLDAP disallows unauthenticated binds by default. Therefore pam_ldap should fail. ldap_bind: Server is unwilling to perform (53) additional info: unauthenticated bind (DN with no password) disallowed But it can be enabled with 'olcAllows: bind_anon_dn' in cn=config. If somebody needs this configuration, {crypt}!! as a default password will be safe way.
Setting no userPassword attribute seems to be the cleanest LDAP solution, and reading the code, the rest of the system would probably handle it fine (nss_ldap uses "*" if userPassword is not present, nss_sss uses "*" regardless of userPassword value). Internally in libuser, I'm rather worried that removing the default value could break applications - dereferencing an unexpected NULL value. As for LDAP servers that do not support {crypt}, libuser uses {crypt} everywhere, so if using libuser makes any sense at all, {crypt} is supported. The case of libuser modifying both LDAP and passwd/shadow at once is a semantic mess... if the value of pw_passwd from /etc/passwd and userPassword from LDAP are different (as they almost always are), the LU_USERPASSWORD value returned by libuser is always wrong. This is the mirror image of our problem, that there is no single correct default value of the LU_USERPASSWORD attribute. In addition to the above-discussed "!!" value, the shadow module can set userPassword to "x". The value actually set by libuser depends on module ordering in libuser.conf. In general, libuser is at various internal stages of user creation (user_default, user_add) not able to distinguish between "!!"/"x" specified by the user (intended to be a raw userPassword value) and "!!"/"x" set up by default (intended to be a crypt(2) hash matching no input). Considering that both "!!" and "x" are rather ridiculous values for a plaintext password, I think it would make most sense to do an ugly hack inside the LDAP module, and replace both "!!" and "x" by "{crypt}!!", _only_ when _adding_ an user. Any other value would be passed through unchanged: - The default value of LDAP userPassword would not allow logins - Applications that use libuser and set an explicit, LDAP-formatted, userPassword value would continue to work (except for unencrypted "x" and "!!") - All LDAP-formatted userPassword modifications done using the libuser "*_modify" operation would continue to work unchanged - libuser password change operations (*_setpass *) would continue to work. (This - i.e. lu_user_add() + lu_user_setpass() - is the recommended method, and is used by luseradd(8).) In particular, this would continue to work even in the combined LDAP + /etc/passwd + /etc/shadow scenario. - The "{crypt}!!" value won't appear in /etc/passwd and /etc/shadow (and in particular, /etc/passwd will contain the correct "x" marker for shadow passwords). What do you all think?
This sounds like it would work and solve the problem, yes. At this point I suspect we will be calling this a security vulnerability and will need to assign a CVE name to it. Is the fix for this going to be fairly intrusive, or something we can easily backport? We'll need to fix libuser across the board for this. I do think this is a fairly low impact issue as it requires using LDAP with libuser and creating a user without an initial password provided. A preliminary CVSSv2 score for this would be 2.6/AV:L/AC:H/Au:N/C:P/I:P/A:N (based on the creation of the account with '!!' as the password as opposed to being able to log into an account with that password set since the vulnerability is in the creation of such accounts). As such, I think we could safely defer this for some older products, but it might be wise to get this fix into Fedora and RHEL6 where new installations would lead to more creation of user accounts than older/more-established installations where adding new users would not be as common an occurrence. Thoughts?
For the record, since I don't think it was answered clearly above: When 'ldap' is selected as the authentication provider in SSSD, it performs an LDAP bind the same way that pam_ldap.so does. Therefore, if the server would validate that as an acceptable password, SSSD would too. The fact that SSSD didn't in your above example is unexpected. I also note that the secure.log reported (System error) in that case, which is actually not the same as rejecting it. That actually means that something went wrong in SSSD internally and we took the route of most-secure response (to deny). I'll need to do some testing to figure out why that happened.
(In reply to comment #16) > The fact that SSSD didn't in your above example is unexpected. I also note that > the secure.log reported (System error) in that case, which is actually not the > same as rejecting it. That actually means that something went wrong in SSSD > internally and we took the route of most-secure response (to deny). > > I'll need to do some testing to figure out why that happened. Could it not be due to lack of kerberos credentials on that test account?
(In reply to comment #17) > (In reply to comment #16) > > The fact that SSSD didn't in your above example is unexpected. I also note that > > the secure.log reported (System error) in that case, which is actually not the > > same as rejecting it. That actually means that something went wrong in SSSD > > internally and we took the route of most-secure response (to deny). > > > > I'll need to do some testing to figure out why that happened. > > Could it not be due to lack of kerberos credentials on that test account? It could be. Was SSSD configured to use Kerberos for authentication, or LDAP? If it was configured for Kerberos, and that user didn't exist on the KDC, then that might explain it. Though it should still be returning denied rather than error.
This is now public via 0.57 release: https://fedorahosted.org/libuser/browser/NEWS?rev=libuser-0.57
Created libuser tracking bugs for this issue Affects: fedora-all [bug 668534]
A script similar to the following could be used to see if any passwords do not start with {CRYPT} and would need to be updated/changed: #!/usr/bin/python import ldap l = ldap.initialize("ldap://server") #l.simple_bind_s("cn=Manager,dc=server", "password") results = l.search_s("dc=server", ldap.SCOPE_SUBTREE, "(userPassword=*)", ["userPassword"]) for dn, attributes in results: if not attributes["userPassword"][0].startswith("{CRYPT}"): print dn #l.unbind_s()
(In reply to comment #42) > l = ldap.initialize("ldap://server") > #l.simple_bind_s("cn=Manager,dc=server", "password") Just to clarify, it's not uncommon to restrict access to userPassword attribute, so the connection is likely to need to bind using a sufficiently privileged user account. Directory Manager account is such example. Details depend on the ACL settings of each LDAP instance. > if not attributes["userPassword"][0].startswith("{CRYPT}"): > print dn This should also report accounts with userPassword using different hash. {SSHA} is commonly used and default for RHDS, for example. Depending on the goal, this can be modified to search for all accounts with plain text passwords, or only compare to the default password '!!' created by libuser.
This issue has been addressed in following products: Red Hat Enterprise Linux 6 Red Hat Enterprise Linux 5 Red Hat Enterprise Linux 4 Via RHSA-2011:0170 https://rhn.redhat.com/errata/RHSA-2011-0170.html