Re: mutilog via fifo

Daryl Tester <[email protected]> Wed, 06 Aug 2008 11:53:16 +0930
Newsgroups gmane.comp.djb.syslog
Message-ID <[email protected]>
Sorry, my free cycles were devoted to another problem, but I've now
had a look into this further.

Paul Jarc wrote:

> Daryl Tester <[email protected]> wrote:

>> tcpdaemon listens on a socket.  When a connection establishes, it
>> forks, sets sets up fd 0 & 1 onto the incoming connection, setuid to
>> the UID supplied, then execs the elhttp process.

> Almost.  tcpserver changes its uid/gid after opening the listening
> socket (since that's the part that requires root access, for
> low-numbered ports), but before entering the loop where it accepts
> incoming connections.

Ah, mea culpa for going from memory.  I keep forgetting it doesn't
improve with age (which should be a reminder, but I forget that too :-).

>> /dev/stderr should return this file descriptor upon opening,

> No, it would be a new descriptor, but connected to the same underlying
> pipe.

Yep, bad description on my part.

> (I would say "as if by dup()", but I don't know if that's
> entirely true - descriptor flags and positioning might be kept
> separate, FAIK.)

I had (still have) that uncertainty as well ...

Later, that same thread, Richard wrote:

> This works like a charm :)

Good.  I did forget to mention that a dummy file argument had to be left
in place in position 4 of the arguments to cause the fdopen to work
(it doesn't matter what argument, just as long as there is one), but
I see you cleverly worked this out.  :-)

I then wrote:

>> I'm still intrigued as to why the open of /dev/stderr is failing
>> though.

It looks to be a kernel problem, which can be replicated simply by
running (as root) (note: there may be transcription errors - the vm
I'm using can't cut 'n' paste at the moment):

# setuidgid nobody sh -c "echo hi; echo there > /dev/stderr"
hi
sh: /dev/stderr: Permission denied

However, stderr (which symlinks to /proc/self/fd/2) appears to have
the correct permissions and ownership (of course, it will be a
different process id, but it appears to be set correctly for self).

# setuidgid nobody ls -l /proc/self/fd
total 0
lrwx------ 1 nobody nobody 64 Aug  6 11:25 0 -> /dev/pts/3
lrwx------ 1 nobody nobody 64 Aug  6 11:25 1 -> /dev/pts/3
lrwx------ 1 nobody nobody 64 Aug  6 11:25 2 -> /dev/pts/3
lr-x------ 1 nobody nobody 64 Aug  6 11:25 3 -> /proc/5482/fd

The attached C program exhibits the same issue, so it doesn't appear
specific to anything setuidgid is doing.  The program should emit
"open(STDERR) as uid 500: Permission denied" if the issue exists, or
silently exit if it works :-).  For the record, I'm seeing the issue
on a Centos 5.2 box running 2.6.18, and an Ubuntu 8.04 running 2.6.24.

So congratulations Richard, it looks like you've unearthed a bug
(or two, if you count elhttp's silent fopen error :-).


Cheers,
  --dt
tst.c (text/x-csrc, 815 B)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int
main(int argc, char *argv[])
{
    int  fd;

    if (getuid() != 0) {
        fprintf(stderr, "I insist I must run as root\n");
        exit(1);
    }

    fd = open("/dev/stderr", O_WRONLY);
    if (fd == -1) {
        perror("open(STDERR) as root");
    } else {
        close(fd);
    }
    
    /* Set ourselves to an arbitrary user id, group first */
    if (setgid(500) == -1) {
        perror("setgid(500)");
        exit(1);
    }
    if (setuid(500) == -1) {
        perror("setuid(500)");
        exit(1);
    }

    fd = open("/dev/stderr", O_WRONLY);
    if (fd == -1) {
        perror("open(STDERR) as uid 500");
    } else {
        close(fd);
    }
    
    return 0;
}