Subject: Re: Addition to force open to open only regular files
To: None <tech-kern@netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: tech-kern
Date: 11/17/2000 13:46:49
[ On Friday, November 17, 2000 at 19:09:47 (+0100), Wolfgang Solfrank wrote: ]
> Subject: Re: Addition to force open to open only regular files
>
> Maybe I'm an idiot, but can anyone please try to explain to me what
> an open_as system call would buy us above the sequence of set*id/open?
> There must be something I don't get...

No, you're not an idiot!  There was a thread a while ago that I think
gave some more detailed discussion, but I'm not sure a proposed
specification and the resulting implications have been clearly stated in
one message.

The idea is to offer a variant of open() that also takes "uid" and
perhaps "gid" and maybe even "groups" parameters.  It would have similar
rules as setuid() in terms of what valid values these parameters could
take on (i.e. if the effective user was 0 then any value would be
permitted, otherwise only values matching the real (or maybe effective)
IDs would be permitted), and it would perform the open() with the
supplied credentials.

The purpose is to eliminate the race condition that would occur if
access() and open() were used separately.

In the case of $HOSTALIASES and rewind tape devices it would in effect
prevent the library routines using $HOSTALIASES from accidentally
opening a tape device when called from a set-ID process where the
current effective-ID was permitted to open that device.  I.e. the
library would do something like:

  fd = open_as(getenv("HOSTALIASES"), O_RDONLY, (mode_t) 0, getuid());

and since the tape device wouldn't normally be accessible by the real
user-ID, the call would fail with EPERM.

If access() had been used to check if the file would have been
accessible before calling open() then a race condition is possible and
the user could fool the library routine into opening an arbitrary file
by changing it, between the execution of access() and open(), from a
normal and accessible file to a symlink that points to the tape device
(for example).

A special set of filesystem-ID credentials in the process structure
would be used in a similar way -- the set-ID program would call
setfuid(getuid()) [and maybe setfgid(getgid()), etc.] before calling
open() [or any other filesystem affecting system call], and as a result
the kernel would do filesystem accesses not as the effective user, but
rather as the real user.  Presumably this would/could be undone by
resetting the fs-IDs to be the same as the set-IDs again.

In smail-3.2.0.x I've done something similar with a function called
fopen_as_user(), and what's unique about my implementation over the one
that preceded it is that I use fork() and setuid() instead of seteuid().
This allows smail to hopefully safely open things like ~/.forward files
because the open() is done as the user.  Of course in a portable
user-land implemtation, such as the one in smail, it's still necessary
to first open the file as the effective user (i.e. root) since portable
implementations have to work where there are no file descriptor passing
facilities (and thus my implementation is not safe against a race attack
of a file like the tape rewind-on-close device).  If FD passing is
possible then the child process can do the sole open and just pass back
the resulting descriptor to the parent, but I've not implemented that
version (yet).  Without FD passing it's necessary to open the file
twice, once in the parent, and then again in the child as the real user,
and then compare the fstat() results to ensure that both opened the same
file (i.e. avoided all race conditions).  With seteuid() you can do all
of this with one open and without forking, but of course seteuid() isn't
portable, and I also claim it isn't safe because there have been
successful exploits against it in the past in other systems.

-- 
							Greg A. Woods

+1 416 218-0098      VE3TCP      <gwoods@acm.org>      <robohack!woods>
Planix, Inc. <woods@planix.com>; Secrets of the Weird <woods@weird.com>