Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 12 Oct 2002 07:20:20 +1000
From:      Peter Jeremy <peterjeremy@optushome.com.au>
To:        Chris BeHanna <behanna@zbzoom.net>
Cc:        FreeBSD Security <security@FreeBSD.ORG>
Subject:   Re: access() is a security hole?
Message-ID:  <20021011212020.GA209@server.c18609.belrs1.nsw.optusnet.com.au>
In-Reply-To: <20021011094935.I86274-100000@topperwein.pennasoft.com>
References:  <20021011185423.B12227-100000@gamplex.bde.org> <20021011094935.I86274-100000@topperwein.pennasoft.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, Oct 11, 2002 at 10:01:30AM -0400, Chris BeHanna wrote:
>    Perhaps the way to avoid the race is to open the file, lock it,
>and *then* call access(), then close the file or proceed based upon
>the result.
>
>    Yes, I know--there's a possible race between open() and fcntl().
...
>    Once a process has the lock, it can call fstat() to determine if
>the real [ug]id (which it already knows) has permission to access the
>file in the desired manner.

I can see two more critical problems:

Firstly, open() options like O_TRUNC and O_CREAT will have already
modified the file based on the e[gu]id before the later checks
discover that the r[gu]id shouldn't have access to the file.  You
can delay the O_TRUNC by calling ftruncate() later, but there's no
way to postpone O_CREAT.

Secondly (and more seriously), open() only returns a file descriptor
referring to the leaf file in the pathname - and file locks only
affect that specific file.  Using fstat() on the returned descriptor
can only tell you the access permissions on that particular file, it
can't tell you that access should have been blocked due to permissions
on intervening directories - and the file lock won't prevent an
attacker changing the intervening directory structure.

The sequence open(),fstat(),access(),stat() and verifying that the
inode returned by the fstat() and stat() are the same makes the race
harder to win, but there's still a race:  Someone could manage to
swap the file back to the original between the access() and stat().

It's not at all clear how to solve this in userland.  In the absence
of symlinks, you can parse the pathname, using open(),fstat(),fchdir()
to securely get to the final pathname component.  Unfortunately,
there's no way to securely do this and handle symlinks (because you
have to use lstat() to detect a symlink and there is a gap between
the lstat() and subsequent open().

The only solution I can see is a new "open_as_real_user()" system call
which is identical to open(2) except that it performs all the access
checks using the processes real credentials instead of the effective
credentials.  (This could potentially be done using a new O_REALUSER
flag on the existing open()).

Peter 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20021011212020.GA209>