Quanah Gibson-Mount quanah@stanford.edu writes:
Russ Allbery rra@debian.org wrote:
So, what does it do, then? How doesn't it work? What would work instead given the above constraint?
I'll note I opened ITS#4750 upstream on this issue. Howard has said that if a good security argument can be made, it could be committed into the current (2.3+ releases).
The security argument is made in the Debian bug:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=387467
specifically:
| > I'm curious about this, are you sure a libldap would read an "ldaprc" | > file when run from a setuid program? | | Yep: | | # ls -l /usr/bin/passwd | -rwsr-xr-x 1 root root 32296 2006-08-25 19:49 /usr/bin/passwd | # mv /etc/ldap/ldap.conf /etc/ldap/ldap.conf.away | | $ cd /tmp | $ passwd testuser | passwd: unknown user testuser | $ echo 'TLS_CACERT /etc/ssl/certs/ldapca.pem' > ldaprc | $ passwd testuser | passwd: You may not view or modify password information for testuser. | | > Or that it'd read the | > current-directory ldaprc in that situation? Can you provide an strace | > showing this happening? | | The interesting fragments: | | execve("/usr/bin/passwd", ["passwd", "testuser"], [/* 16 vars */]) = 0 | [...] | access("/etc/suid-debug", F_OK) = -1 ENOENT (No such file or directory) | [...] | getuid32() = 1000 | [...] | geteuid32() = 0 | [...] | open("/etc/libnss-ldap.conf", O_RDONLY) = 3 | [...] | open("/etc/ldap/ldap.conf", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory) | open("ldaprc", O_RDONLY|O_LARGEFILE) = 3 | | There is no path component in the last open(), so ldaprc is always read | from the current directory. | | > Also, the user would have to have access to more than the ldaprc file, | > no? Since the user couldn't control what server is being connected to | > without more control on the system, or control over the DNS, etc. | | True, but DNS poisoning is trivial (especially in a large local network | with lots of not-so-trusted machines). If I'd trust the network I'd have | no need for TLS at all...
Clearly the user should have an /etc/ldap/ldap.conf under any normal circumstance, and a configuration that does not is most likely broken. However, my argument is that while there may be cases where a user creating a security vulnerability through a broken configuration is simply the user's problem, I believe this is a case where the action taken has no intuitive connection to security. This was initially noticed because a user was putting all their LDAP options in /etc/libnss-ldap.conf rather than in /etc/ldap/ldap.conf, which may be broken but which is not something that is obviously broken from a *security* standpoint.
Having a library attempt to open files in the current directory and read them for configuration is highly non-intuitive and unexpected. Having it read configuration out of the user's home directory is less so, but in the case of setuid programs is still rather disturbing, particularly since the setuid program has no direct control over this behavior.
So, I see two potential problems here:
* A user can override the LDAP configuration of a setuid binary and cause it to, for instance, trust LDAP servers that it wouldn't otherwise trust, which is a potential attack (albeit a difficult one to exploit). If the setuid binary knows that it's using LDAP, it can avoid this via an environment variable, but frequently given such things as NSS plugins and PAM modules the binary isn't going to know that LDAP is involved. The primary problem here is that said setuid binary will read the configuration file based on HOME from the enviroment (it appears to me from the code) or the current directory (the above example), which violates one of the standard secure programming practices for setuid binaries (never trust the process environment including working directory that you're handed by the user).
* The library may fall back to reading a file out of the current directory. I don't know if this behavior persists in 2.3; the above example is from 2.1. If it does persist, it opens other potential problems that don't involve setuid but instead involve creating files in the current working directory of a process one wishes to attack. Again, due to the plugin nature of current operating systems, the process being attacked may not even be aware that it's using LDAP. I think this exploit is unlikely to occur in practice since it requires some attacker control over the home directory of the process being attacked, but it's theoretically present.
Steve's patch addresses the first issue but doesn't do much about the second, so it's an improvement but I'm not sure it's a complete solution. Unfortunately, this support is also important for the current functionality of the command-line tools and doubtless other programs that use the LDAP libraries.
What we ideally need is some way for a user of the LDAP libraries to say, through the API, that user configuration files should not be loaded but that isn't a process-wide flag that interferes with the expected behavior of other uses of LDAP elsewhere in the same process. And as mentioned, I don't see an obvious way to do that without changing the API, which is, of course, a pain.
In the meantime, the patch isn't exactly something I'd want to take upstream either, but it at least addresses the most obvious problem, and more problematically I don't see a better way of addressing it other than saying "well, anyone without a system-wide ldap.conf loses." And I wouldn't be comfortable trying to defend that position.
I suppose another possible workaround specific to the NSS module (and probably the PAM module as well) would be to proactively check whether there's a system-wide ldap.conf file and fail immediately if there isn't. That leaves the problem open for other setuid uses of the LDAP libraries, but I don't expect there are a lot of those and what ones there may be are more likely to be able to use the LDAPNOINIT flag.