I want to set up a library so that, e.g., a syslog message can be
securely tagged with the userid of the logging process. I imagine that
the library needs some way to create a descriptor with the right uid
pointing to some sort of message-passing daemon. The daemon would then
make sure that each message from a descriptor is tagged with the uid
that created the descriptor. For definiteness, let's say the descriptor
is a UNIX-domain socket (under BSD) or a stream (under System V) so that
processes can shuttle descriptors to each other and talk efficiently
rather than routing every message through the daemon.
So far the library is perfectly straightforward. The problem is that
creating the secure descriptor in the first place probably requires
executing a program and having it shuttle the descriptor back. I want
this library to be more efficient than that: while an extra
fork()/exec() doesn't make a difference to long-running programs, short
programs should be able to pass messages without doubling their runtime.
First question: Is there any (practical) way of creating the descriptor
securely, reliably, and without forking a process?
One solution is to pass the secure descriptor from parent to child. In
most cases, just one extra execution will suffice to give message
passing to an entire user process tree. The library can check whether
the descriptor is indeed an open UNIX-domain socket owned by the current
userid, and create a new descriptor if not. (An environment variable can
show the right descriptor if it's not the default of, say, 13.)
Almost all existing setuid programs will have no problem with this
scheme: they only execute programs as the original userid, and they
don't use the library themselves, so the descriptor will just pass
through unchanged. This rule will begin to break down when setuid
programs start using the library (e.g., if syslog() is rewritten).
A user must not be able to forge a descriptor that looks secure.
Second question: What can the library do to test that a descriptor
passed into a program really does point to the message-passing facility?
Is it enough to test that is it a UNIX-domain socket in some reserved
namespace? What should that namespace be? What can be done with streams?
Third question: What can the library do to make sure that a secure
descriptor isn't accidentally passed out of a setuid program? Should it
set close-on-exec on the descriptor inside any setuid program? Is this
enough?
The other problem case is a setuid program that executes programs as
something other than the original userid. The classic examples of this
are su and login. The library will somehow have to make sure that
there's no chance of login starting with a root descriptor and passing
it on to the user's shell.
Fourth question: Is there any way to make sure of this other than
requiring su and login (and any other programs that change uid) to close
the descriptor? Is there any more efficient solution than setting
close-on-exec for every root message-passing descriptor, so that every
root process has to get a new descriptor? Are there any setuid programs
that do anything like unset close-on-exec on every descriptor before an
exec()?
Whoever gives me answers that I use will get appropriate credit in the
package. Final question: Does anyone think a secure message-passing
system will be useful? I think the inability to write a setuid library
is one of the biggest problems with UNIX security: it convinces people
that security can only come at a huge expense in efficiency, and they'd
rather have the latter.
I don't want to hear that Mach and other UNIX variants do this already.
I'm sure I'll be able to port the library to a Mach machine if I ever
use one. I also don't want to hear that message queues are a solution;
they do not report the remote userid reliably, they do not support basic
operations like file descriptor passing, and some of their BSD ports are
insecure. (Last I checked, any user under Ultrix could remove any other
user's message queues, no matter what the permissions---and then
substitute a message queue under his own control. How useful.)
---Dan