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: 12/04/2000 01:28:12
[ On Sunday, December 3, 2000 at 23:39:39 ( -0500), Matthew Orgass wrote: ]
> Subject: Re: Addition to force open to open only regular files
>
> On Thu, 30 Nov 2000, Greg A. Woods wrote:
> 
> > On the other hand I've shown at least circumstantially that the
> > restriction POSIX.1 puts on never allowing a privileged process to
> > regain its privileges after dropping them with setuid() has a very
> > positive benefit in that set-ID programs designed explicitly for POSIX.1
> > are less likely to suffer from the types of exploits which allow a
> > cracker to insert unauthorised code into a process since by the time
> > this is likely the process will have forever given up its privileges
> [...]
> 
>   This ability is just as easily available when you can switch effective
> IDs.  The only difference is that if you can temporarily switch IDs you
> can run less code at the higher privilege when you still need the
> privileged ID later.

I'm not sure what you're getting at here.

Within the confines of user-level code it makes absolutely no difference
what ID you run your code as if you can at any time, temporarily, switch
IDs.  If such code is vulnerable to a code-insertion attack then it is
*always* exploitable -- the attacker need only insert the seteuid(0)
call and continue to open his back door.  What you say appears to
perpetuate the fallacy that there's some benefit to temporarily running
a bunch of code in a given process as an unprivileged user.  It is in
fact a danger, not a benefit, because it gives a false sense of security.

(Obviously it makes a great deal of difference which ID you are running
as when you call certain system calls! :-)

>   It was pointed out to me in private mail that the logical end to the
> isolation of privileged code is to remove setuid completely and use IPC.
> This has convinced me that any major changes to improve setuid would not
> be worth the effort, which could be better spent making it possible to run
> a system without setuid (NetBSD certainly has made progress towards this
> goal).

Well, you can't remove setuid() completely, at least not without
inventing a whole lot of privileged daemons and incorporating a whole
lot of authentication goo in your IPC (eg. kerberise everything in
sight, including the kernel!).  I think there are already operating
systems that don't make use of the setuid concept, and they're certainly
not unix-like (though some claim POSIX.[12?] compliance! :-).

I think I'll stick to multi-user systems that use setuid, at least for
the time being.

However using IPC with carefully designed single-shot privileged set-ID
programs is clearly the most secure way to design subsystems that have
to make use of resources that require privileges to access.  This is
exactly what I was talking about in the example of how ftpd could still
open socket bound to the privileged port #20 after having authenticated
and authorised the logged in user and called setuid(login_user).

I.e. the best systems security will come from *never* running big
monolithic daemons as root, and from always permanently dropping
privilege as soon as possible in any prgram which performs
authentication and authorisation.

Of course once you've done all of this neither seteuid() nor open_as()
would be necessary and we'd be back to just plain old setuid()!  :-)

>   If HOSTALIASES support is really desired for setuid programs, I think it
> should be done by keeping track of the true real id in the kernel while
> maintaining current behavior.  Instead of a O_REG_FILE open flag, use
> O_TRUE_ID or such that opens as the true real id in all cases.  This would
> solve the immediate problem in a more complete way than O_REG_FILE without
> breaking compatibility for setre[ug]id programs.

First off, please let us forget about setre*id() -- they're as good as
dead and gone, especially for the purposes of this discussion since
there are no programs using them left in the base system, and as yet
I've not encountered any add-on programs that need them either.

Even for this particular point the issue is moot -- the current real ID,
as returned by getuid(), is always going to be the correct one to use
for the purposes of something like $HOSTALIASES or $TZ (with a tzfile).
If a program has changed its real-ID then it must, by design, fully
intend to do everything as if it were originally invoked by that user.
To assume anything else is pointless and would be catering to broken
code.

There is no such thing as a "true" ID.  Any privileged process is free
to change its real ID at any time.  Remembering the real-ID even after a
setreuid() or setuid() is only going to lead you into trouble with
changing the paradigm too much without clearly thinking the full
implications through (hmmm, unless maybe if you clear it on exec()).

So, if you're not going to restrict the ID swapping capabilities of
seteuid() then there's absolutely no point to in making any changes to
any kernel API whatsoever.  The kernel already does an admirable job of
keeping track of the current real ID, and will supply it on demand of
getuid(), and library functions like those which use $HOSTALIASES can
easily make use of getuid() and seteuid() [with ID swapping] to always
do "The Right Thing(tm)."

Also note that an O_REAL_ID flag is just the same as the most minimal
open_as() API.

You can even implement the minimal open_as() in a library routine.  If
you don't do away with ID swapping then you can do it quite simply with
seteuid().

If you do eliminate ID swapping, or you wish to avoid seteuid(), then
you can still implemente it in a user-land function and you'll end up
with something like my fopen_as_user() function from smail-3 (which
actually isn't really safe for $HOSTALIASES or $TZ).  This will make you
want to have open_as() [or at least O_REAL_ID] in the kernel.

However if you don't eliminate ID swapping you'll eventually find out
how dangerous it can be and you'll want to move open_as() into the
kernel again and finally eliminate ID swapping.

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