Bugtraq mailing list archives

Re: access(2)--a security hole?


From: mouse () Collatz McRCIM McGill EDU (der Mouse)
Date: Fri, 21 Oct 1994 10:29:20 -0400


the FreeBSD man page for access(2) includes a section titled "CAVEAT"
which says that "Access() is a potential security hole and should
never be used."

Good for them.  I don't know of any other system that's so aboveboard
about it.

[...] i cant see why this is a hole.

can you enlighten me?

access() itself is probably not a hole, in that it does what it's
supposed to do.  Now, I didn't write the remark you quote, and don't
know what whoever did was thinking - but I can perhaps talk a little
about why _I_ think access() is a potential security hole.

First, I should note that this discussion applies to access() as used
by setuid programs.  (In a non-setuid program, access has some valid,
reasonable, safe uses, such as to verify execute bits cheaply before
going to all the expense of a fork and exec attempt.  I've also used
F_OK to avoid pathname collision in programs where robustness in the
face of pathological cases is not an issue.)

The problem is that it is essentially impossible to use access() for
anything without introducing a race condition.  The only real use of
access() is to verify that the real user can perform a given access to
a pathname obtained from an untrustworthy source.  (If the source of
the pathname is trustworthy, there's no need to check.)  The problem is
that there is no way to ensure that the interpretation of the pathname
doesn't change between the access() call and the call that actually
performs whatever the operation is.  Thus, using access() lulls the
programmer into a warm fuzzy sense of security for being such a good
coder and performing proper checks before doing things, while actually
just opening up a race condition.  Perhaps if open(2) took a flag like
O_REALUID, to perform access controls as the real user rather than the
effective user, access() wouldn't have been created and this whole
discussion would be moot.  In the meantime, one is generally better off
switching back to the real UID, performing the operation and fielding
any errors that occur, and then (if desired) switching back to the
setuid UID afterwards.

Unfortunately a few antique systems can't switch back and forth like
this, and among modern systems there is no way that works everywhere,
so you have to either write off a good deal of portability or else
litter your code with #ifdefs (or something equivalent) - which is
perhaps why it's done as seldom as it is.

                                        der Mouse

                            mouse () collatz mcrcim mcgill edu



Current thread: