How might one set up a secure, efficient message-passing facility?

How might one set up a secure, efficient message-passing facility?

Post by Dan Bernste » Tue, 12 Feb 1991 12:17:56



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

 
 
 

How might one set up a secure, efficient message-passing facility?

Post by The Grey Wo » Fri, 15 Feb 1991 06:50:19



Quote:>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?

If you're mucking about with uids, then no, because once you change
real and effective uids to something other than zero, you're stuck.
Most programs with any sense of integrity whatsoever will not allow
the real uid and the effective uid to remain different.

If you're fiddling with descriptors, then no, unless you have a way
to restore them to their "original" state if something goes Wrong.

Quote:

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

Compare the uid of the user with the uid of the descriptor with the effective
uid of the user -- if something is off, that's pretty obvious.  (Were
you looking for something more?)

It seems to me that your concern of setuid programs using the library
are only worth worrying about if you're implementing shared libs (which,
so far, have proven to be more trouble than they're worth -- at least
for me, and for the moment).

Quote:

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

If the program is "established", that is, if the program is not just some
hack that a gullible sysadmin gave setuid privileges to (so the user
could re-create it with other calls, using other libes, etc. (in which
case this is a moot point anyway!)), then why the worry about a secure
descriptor being passed out of a setuid program?  I don't see how it
could be unless the program had some serious design flaws.  Or unless the
library is rewritten completely to subvert your efforts in which case,
again, the point is moot.

Quote:

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

In general, this doesn't happen anyway; I believe pains are taken to
ensure that descriptors 0, 1, and 2 are all that's passed to the user's
shell.  What the shell does after that is its own business (and quite
messy, too, if I read the shell code right).

Quote:>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()?

4a:  I don't think there is.  Closing the descriptor is probably the
        best way that you're gonna insure that nobody else gets the thing.
4b:  Well, you could check to see who inherited the descriptor somehow
        (probably in the lib routines...).
4c:  I rather hope not.  I can see *not* setting FCLEX, but I can't
        see setting FNCLEX on every fd.

Quote:

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

It would be useful, but I think there's a mechanism which would be
nice to have which might help your cause.  How about a FCSUID operation
in ioctl() or an added flag in the word returned by a fcntl(fd, F_GETFL, arg)
call which would cause the file descriptors to be closed on setre[ug]id
calls?

(Tangent:  Why don't they just implement all the setuid/setgid stuff
 into one setreugid() system call instead of two?)

Quote:

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

Ack.  That last part does make a bit of a pinhole doesn't it?  Kind of a
hole you could fly a planet through.

Quote:

>---Dan

--
thought:  I ain't so damb dumn! | Your brand new kernel just dump core on you
war: Invalid argument           | And fsck can't find root inode 2
                                | Don't worry -- be happy...
...!{ucbvax,acad,uunet,amdahl,pyramid}!unisoft!greywolf

 
 
 

1. Q: efficient way for short message passing

Hi, netters:

I have some network programing questions:
1: Now I need to write a code handling communication between two machines
   on the top of tcp-ip. I know the normal way to do it. However, the facing
   problem is the system needs to pass back and forth short message (say one
   or two bytes) in realtime. Basiclly, one machine (A) needs to send one signal
   to the other one (B) to tell what to do next, and after B finishes the job,
   it needs to feedback one signal indicating either done or error or something
   else. So A has to send only one or two bytes once at a time and has to wait
   to read one or two bytes feedback from B. This kind of read and write is
   really cost and turns out tremendously reduce the performance. Basiclly,
   using larger buffer size in read and write should be more efficient. However,
   in my case, I have to use only one or two bytes as the buffer size. Even the
   overall bytes transmitted are lot many, it is too time comsuming in this way.
   Does anyone have any related experience? If do, please give me some hints.
   Also, please point out of some nice books about efficient and advanced I/O.  

2: As I know, for the write call, it will actually write the data into the
   system buffer when it returns and later the system will physically write
   the data in the buffer into the disk. What is the working mechanism when
   the write is writing data into a socket? Is it synchronized? After the
   write, can the other side read the data at once even the size of the data
   might be small? Do I need to set it with synchronized option (too slow) to
   make the data is actually be readable by the other side?

That is my questions. Email reply preferred, post is also fine.
Many thanks in advance!

--dong

2. Netwinder forsale?

3. ld: Principles of one-pass vs. two-pass

4. Alpha release of NT support for the Apache web server

5. Shape Ups,Men's Shape Ups,Men's Skechers Shape Ups - new styles!

6. how to setup a terminal server?

7. problems configuring shared memory facility and semaphores facility

8. fsck reports a bad superblock. How do I fix it?

9. Routing "user" facility system messages to xterm

10. IPC message facility on Sparc

11. How to determine the priority and facility of a specific syslog message?

12. Message queue facility not in system...

13. "Message Queue facility inactive" -- HELP!