in Kerberos
krb5_kuserok and unreadable .k5login files
daemon@ATHENA.MIT.EDU (Geoffrey Thomas)
Sun Nov 7 11:09:54 2010
Date: Sat, 6 Nov 2010 22:06:43 -0400 (EDT)
From: Geoffrey Thomas <email@example.com>
Cc: firstname.lastname@example.org, Alexander Chernyakhovsky <email@example.com>
Content-Type: text/plain; charset="us-ascii"
Alex Chernyakhovsky and I found some edge cases in the .k5login handling
code when the file exists but is for some reason not readable. It appears
the code is entirely designed to assume that if the file exists, it is
readable. There are some specific cases where this is likely to happen,
including when running under SELinux or on AFS.
Specifically, k5login_ok (in lib/krb5/os/kuserok.c) checks whether the
file exists by checking access(filename, F_OK). However, the file can
exist but not be readable, in which case the fopen() later will fail. It's
not clear what the intended behavior here is, and the code seems to be
treating a failure of fopen() more like a system error (compare e.g. the
previous get_k5login_filename call returning ENOMEM) than explicitly
considering the case of a .k5login that exists but isn't readable.
Although the code presumably generally runs as root, there are a fair
number of cases in which this can happen; we've run into the default
SELinux configuration preventing ssh from reading files in users' home
directories (see Red Hat Bugzilla #501107), and in AFS, since you don't
have the user's tokens yet at this point, the obvious approach of making a
.k5login in your non-world-readable AFS home directory will fail.
There are also some cases where the file can exist but access() still
returns nonzero; one in particular is EACCES. It's not clear to me if,
say, POSIX allows that for F_OK, but AFS can actually return EACCESS in
certain cases, specifically when the file hasn't been brought into cache
by a user authenticated to read the file. In this case by a
two-wrongs-make-a-right mechanism, you do in fact get to log in most of
the time because the "if access(filename, F_OK) != 0" check can't
distinguish ENOENT and any other nonzero return like EACCESS, and so the
code behaves as if the file doesn't exist.
Finally, it's not really clear that the correct response to the .k5login
not being owned by the user or by root should cause a "REJECT" return,
i.e., the same as if the .k5login existed and actively did not include the
user. In the case of a malicious user creating the file, this seems to
enable a denial-of-service as opposed to just returning "PASS", the return
code given when the .k5login doesn't exist at all, in which case the user
could at least log in with his/her own principal. On the other hand, in
the non-malicious case (e.g. some AFS setups), this is definitely less
helpful than "PASS".
We're thinking that the best solution may be a fourth return code from
k5login_ok, "UNUSABLE", in addition to ACCEPT/PASS/REJECT, to indicate
that a .k5login exists but is unreadable or otherwise unusable to make
security policy decisions. By default UNUSABLE would work like REJECT but
instead callers of krb5_kuserok could use it to log a much clearer error
asking you to investigate the permissions around the .k5login, but perhaps
an option in krb5.conf could allow UNUSABLE to work like PASS.
Another solution would be simply to explicitly consider the case of a
.k5login being unreadable as the .k5login essentially not existing, and
return PASS instead.
Kerberos mailing list Kerberos@mit.edu