Subject: Re: Addition to force open to open only regular files
To: Matthew Orgass <darkstar@pgh.net>
From: Greg A. Woods <woods@weird.com>
List: tech-kern
Date: 12/06/2000 03:32:39
[ On Monday, December 4, 2000 at 22:28:34 (-0500), Matthew Orgass wrote: ]
> Subject: Re: Addition to force open to open only regular files
>
>   Right, this is the issue.  When you call library routines, you have no
> control over what they do.  If you limit privilege to the exact calls that
> need it, then you limit the extent of privileged code.  While it is still
> possible that libc open and such will do unintended things, it is much
> less likely.  As you mentioned, this does nothing against bugs, but
> something like HOSTALIAS would not have the tape rewind problem if run as
> the real user.  It might result in incorrect behavior later, but it at
> least avoids the immediate privileged use of tainted data.

Remember that "privileged use of tainted data", and "accidentally
opening a rewind tape device" are two completely separate and unrelated
problems.  Implementers of $HOSTALIASES and $TZ with tzfile support are
always going to have to worry about the former, but need not worry about
the latter if they employ one of the techniques discussed such that the
open is always done with the credentials of the real user.

>   It may never go away completely for compatibility, but it should not be
> especially difficult (on a large change scale) to remove its use from the
> base system and provide a means to disable it.

Unless you make setuid go away sompletely you've not likely gained very
much additional security, at least not in a general sense....

>  Many things that use
> setuid (and all that are truely necessary for basic system operation)
> should be done by appropriate kernel protection (new syscalls if
> necessary).

Examples of such things would be???

>  The rest can easily be done with IPC.  An inetd type daemon
> could be used to run the daemons only when necessary.

Hmm...  "easily" is not a word I'd use to describe what it would
take...  While AF_LOCAL sockets can pass credentials, there are still
some messy details to worry about.
 
> > I think I'll stick to multi-user systems that use setuid, at least for
> > the time being.
> 
>   It certainly needs work to get there, but it is not exceptionally
> difficult.

Anything that changes the paradigm by which systems security is
implemented is, by definition, exceptionally difficult.  Often the
implications are not fully understood for years after the fact.

> > 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.
> 
>   These should be started by an inetd type daemon, not by set-id.  This
> way the user has no influence over the environment, cwd, or anything else
> that might affect its operation.

So all you've really done is move setuid into user-land then, and
potentially introduced just as many new caveats as you may have
eliminated old ones....

>   Ftpd is a sepecial case due to the broken protocol.  This could be fixed
> by allowing access to just that port to that process after setuid.  It
> should also be possible to reserve ports to any user, so it may also make
> sense to just always run as user ftp as you would with a long running ftpd
> (perhaps with a mechanism to allow the kernel to apply permissions for
> both ftp and the user when accessing files).

No, that's completely inside-out.  You can't be accessing an arbitrary
user's private files without first being the superuser.  If you're the
superuser at the start then you can either use setuid() or seteuid() to
take on the credentials of the authenticated user, or you could use a
version of open_as() to safely access the user's files.  If you're not
the superuser then ftpd can't provide access to an arbitrary user's
private files no matter whether or not it could authenticate that user.

I.e. ftpd must always start as the superuser, one way or another (even
if you've got an authentication daemon such as the Cyrus pwcheck
program).  It's got to do almost exactly the same things from a security
perspective as "login" does -- but instead of forking the user's normal
shell it provides a little "command language" and makes/accepts network
connections and copies data to/from files.

The problem with the silly port-20 thing is that you need to bind a
socket to it potentially many times *after* you've authenticated the
user, which means that if you use setuid() to permanently drop down to
the user's privileges the only way to "reserve" the port is to reserve
it to a specific process-ID and have some kind of unreserve-on-exit
feature such that a new process with the same ID can't exploit the port
too.

While such a mechanism could be implemented, it's probably no easier to
do than it would be to simply use a restricted unprivileged group-ID to
invoke a little setuid-root helper process that would hand back a socket
descriptor that's already bound to port 20, just as I described some
time ago, or perhaps with a broker daemon that accepts AF_LOCAL requests
with credential passing so that it can verify that the requesting
process is running with the restricted effective group-ID for ftpd, and
which then passes back a file descriptor for the already-bound socket.
Whether one chooses the setuid or daemon approach is really only a
matter of performance concerns, which are non-existant in this case, so
simplicity and elegance should rule the decision.  The setuid approach
is simpler because you don't have to pass your credentials through the
AF_LOCAL socket and the helper program can assume that the ftpd process
wouldn't have been able to start it if ftpd wasn't already running with
the correct group-ID.  Having one less daemon to keep running is also a
good thing, so I think the protected set-group-ID helper program is
clearly the best way to go.

> > 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.
> 
>   Until the libc major number is bumped they need to be supported for
> compatibility.  It could be argued that it would be better to bump the
> major and remove setre[ug]id, however until that happens compatibility is
> required.

No, that's not true at all.  "Compatability" and keeping the function in
libc have nothing to do with each other whatsoever.  I've already
removed "compatability" from my kernel as an experiment, and other than
the fact that I can't find a probram that still uses setreuid(), all is
well.  I.e. there's no reason why the kernel can't simply dishonour such
calls and thus the library can still to contain the function and the
system call can remain in the syscall table -- it just doesn't do
anything any more but return an error (and perhaps log its use).

(Of course there must be a half dozen or more other reasons to bump the
libc major number already, so the combined incentive seems more than
enough to "just do it"!  :-)

> > 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).
> 
>   This is not true when setre[ug]id is used to swap the real and effective
> ID.  In other cases, the real id would always be used.

but no base programs (and probably no pkgsrc programs) do that any more,
and thus the kernel need not honour any setreuid() request any more, and
so this issue does not exist.  It's easy to err on the side of safety,
especially if you've de-flowered the dangerous call beforehand.

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