[Date Prev][Date Next] [Chronological] [Thread] [Top]

Re: (ITS#4750) libldap initialization of ~/.ldaprc and setuid



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.

-- 
Russ Allbery (rra@debian.org)               <http://www.eyrie.org/~eagle/>