Subject: Re: setreuid() and setregid()
To: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
From: Greg A. Woods <woods@kuma.web.net>
List: tech-kern
Date: 05/31/1996 18:26:15
[ On Thu, May 30, 1996 at 14:16:18 (-0400), der Mouse wrote: ]
> Subject: Re: setreuid() and setregid()
>
> > Yes this seems twisted at first glance, esp. since the process is
> > trying to give up its setuid-ness.
> 
> And second glance, and third...the real problem is overloading the
> saved-set-id.

Hmmm....  Yes, I suppose that's true in some sense.

I think it would be possible to add a new call, such as revertuid() that
would be able to safely set the effective uid and the saved uid from the
real-uid.  This limited function should not require privilege of any
sort and should achieve what's desired.

I think trying to do this with the existing interface is what's causing
the the discrepancy between the NetBSD model and the POSIX goals.

> > However, given the unix-like systems base their entire security
> > policy on the set-user-ID-on-exec feature, and the fact that there is
> > but one privileged user (i.e. uid==0),
> 
> This isn't true, actually.  gid kmem is privileged, the uid that owns
> the "system" executables (typically "bin") is privileged, "games" is
> privileged...not by any property of the kernel, but simply because the
> system is set up so that those IDs are empowered to do things that
> ordinary users cannot.

Let's back up again.  Unless the process has an effective uid of 0, it
is *not* ``Privileged'' in the true sense.  ``Privilege,'' in the
security model sense, is a very narrowly defined term.  I don't have an
exact reference at this point, but I can assure you given my experience
in reading security requirements and evaluation documents that this is so.

Giving access to /dev/kmem to user but euid==0 is, strictly speaking,
entering a hole in the security mechanisms, but is not in any way giving
privilege to whatever particular gid "kmem" is associated with, etc.

Privilege, in the unix model, is merely that state when euid==0, and all
system calls that depend on privilege make *only* this one check.
Nothing in user land beyond binaries with setuid=0 have any effect on
privilege.

A setuid-root binary can easily give the rights of euid==0 to any or all
users (just "chown root /bin/sh; chmod u+s /bin/sh" for example), but
this doesn't mean the users who execute this binary are themselves
privileged in the kernel security point of view -- they have merely
become privileged through the existing security mechanism.

Same for users in group kmem if /dev/kmem is made group writable and
such a user can then clobber his euid to 0.  All this has done is
subvert the set-uid-on-exec mechanism and thus broken the policy, *not*
given privilege to these users.

Other operating systems may have multiple levels of privilege, or indeed
multiple privileged users.  Unix and NetBSD, et al do not (UNIX/MLS does).

This is the neat thing about the unix model though -- this one simple
mechanism, when combined with the file ownership and protection
mechanisms, makes it possible to implement many different kinds and
levels of application-level security.  This is also why some people find
unix security difficult -- the onus of the policy enforcement rests
entirely on the administrator and applications programmer.  There's very
very very little the operating system itself can do "wrong" or to get in
the way of the application.

> > The first trivial fix is to just call setuid(getruid()); and remember
> > never to call setuid() again.
> 
> This leaves you wide open to anything that tricks the process into
> executing code you didn't write (stack-smash bugs are the commonest
> example); this is exactly _why_ a security-conscious coder will want to
> completely revoke all privilege, to limit the damage such a bug can do.

Yes I agree this is "better" in many ways and leads to a significant
increase of integrity protection, but without something like the
revertuid() call I proposed, I can't see how to safely add this to the
existing functions.  Perhaps we could take away the long standing
"feature" of setuid() only setting euid from ruid and rely on seteuid()
to do that and instead make an unprivileged setuid() do what revertuid()
would do.  I personally would rather see strict POSIX compliance of the
defined functions, including the proposed seteuid() call, and an
additional "non-standard" call that I would name revertuid().  I think
this would have less impact than changing setuid() [again], and would
certainly make it easier to claim POSIX compliance, and would certainly
make it easier to ensure portability of set-user-id programs (only one
call to #ifdef out, rather than some strange different behaviour to
account for).

-- 
							Greg A. Woods

+1 416 443-1734			VE3TCP			robohack!woods
Planix, Inc. <woods@planix.com>; Secrets Of The Weird <woods@weird.com>