Subject: Re: Addition to force open to open only regular files
To: NetBSD Kernel Technical Discussion List <tech-kern@netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: tech-kern
Date: 11/16/2000 16:42:58
[ On Thursday, November 16, 2000 at 09:06:49 (+0100), Jaromír Dolecek wrote: ]
> Subject: Re: Addition to force open to open only regular files
>
> Greg A. Woods wrote:
> > This is a good thing.  Allowing a process that has run as an ordinary
> > user to (re)gain superuser privileges is extremely dangerous and opens
> > the system to many different kinds of possible vulnerabilities.  There
> > have been several exploits available in the past to systems which have
> > made this mistake and I have no doubt that there will be more in the
> > future.  (Solaris-2.3 and its related bretheren are one example)
> 
> I think it's not about being able to seteuid() back to root
> after setuid().
> It's more about being able to switch effective id arbitrarily, without
> affecting real id. After call to setuid(), the passed id gets
> used as real id and effective id is droppped, so it's no longer possible
> to switch between original real id and original effective id.

Uh huh, but with sete*id() that doesn't happen, and that can be bad if
the saved-set-ID was zero (i.e. the superuser).

Once you revert from your effective-ID back to your real-ID you should
never be able to return to the effective-ID again.  A process that has
run as the real user should never be able to (re)gain its set-ID or
former priviledges again.  If it can it might find itself unexpectedly
dealing with compromised data, or even compromised code in some past
examples of related vulnerabilities.

In the Unix security model a process should only be able to run with
superuser privileges by virtue of having a superuser parent process, or
by virtue of having been executed through a set-UID-root binary.  When
one extends this model to cover semi-privileged users which control
access to various system subsystems, such as various devices or shared
services, then the same no-swapping rule should really be enforced.

However the compromise in SysV and POSIX is to enforce this no-swap rule
only for processes where the effective-ID is/was the superuser and to
allow swapping IDs when it's just another non-privileged user.

This is generally considered to be an acceptable risk because the
superuser cannot be directly compromised in this way.  However one has
to be extremely careful to not create a scenario where several such
set-ID programs can eventually open the hole for a trojan or other such
compromise that would then make the superuser vulnerable.

For example execve(2) doesn't mention what happens to the saved-set-ID
values if the new process image file is not set-ID but the parent
process was.  If the code makes the mistake of not resetting the
saved-set-ID values to the real-IDs then suddenly it might be possible
for unrelated code to run seteuid(0) and gain superuser status
unexpectedly (which is especially interesting to consider if you find it
possible to ptrace() such a process!).  I don't know if this is possible
in NetBSD today, or not, but it's an example of just how complex the
saved-set-ID and ID swapping model gets in conjunction with other
subsystems.  Past examples of real vulnerabilities have involved new
subsystems, such as /proc, that didn't take approriate precautions to
protect a process when it was running with reduced privileges and thus
allowed it to be compromised and then regain its privileges and suffer a
major vulnerability.

The problem appears to be that programmers continue to think about
privileged processes backwards and will tend to run far more code than
necessary in a privileged state ("Just make it setuid -- that'll make it
work!").  That leads to problems that can only be solved by temporarily
lowering privileges so as to not do nasty things accidentally (eg. open
a rewind-on-close tape device when you only meant to open a simple name
mapping table).

Because some programmers generally percieve fork() to be too "heavy"
(despite the fact that this was only temporarily true in the history of
Unix) to use, this has lead the demand for saved-set-ID swapping
capability.  Even if you're willing to fork(), there are still
portability problems with doing things like file descriptor passing,
which further drive the demand for saved-set-ID swapping ability.  I
have implemented what I believe to be a safe fopen_as() function without
using seteuid() or descriptor passing, but it was not easy to do, and it
is not easy even for me to understand after the fact, and it is not
small or simple code either.

However if you turn things around such that trust is granted in small
increments to ever smaller bits of code then you can do things extremely
safely without ever having to think of using sete*id() and thus without
leaving large and complex set-UID-root programs directly accessable by
rogue users.

-- 
							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>