[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index][Thread Index][Top&Search][Original]

setuid behavior change; documentation reflects old behavior



[This is the same article that I posted to comp.lang.perl.moderated a
few days ago.  Since I got no response, and I'm talking about what
seems to be either a bug in Perl or in the documentation, I'm sending
it here.]

I have discovered a difference between the documentation and the
behavior of seteuid in Perl.  It isn't completely clear to me whether
Perl is to blame, my build of Perl, or the documentation.  

I have also isolated a difference in behavior between 5.005_03 and
previous releases, including 5.004_04 and 5.003_EMBED.  It appears
that the documentation shipped with 5.005_03 matches the behavior from
previous releases, but not 5.005_03.

Before I commit code to production that could prove problematic in the
future, I'd like to understand if this is something that just changed
with 5.005, or whether the correct behavior is still subject to debate.

So my question is, "What is the correct behavior here, and are we to
fix Perl or the documentation?"

And now for what led me to the question.

The perlsec manpage shows the following example code.

         use English;
         die "Can't fork: $!" unless defined $pid = open(KID, "-|");
         if ($pid) {           # parent
             while (<KID>) {
                 # do something
             }
             close KID;
         } else {
             my @temp = ($EUID, $EGID);
             $EUID = $UID;
             $EGID = $GID;    #      initgroups() also called!
             # Make sure privs are really gone
             ($EUID, $EGID) = @temp;
             die "Can't drop privileges"
                     unless $UID == $EUID  && $GID eq $EGID;
             $ENV{PATH} = "/bin:/usr/bin";
             exec 'myprog', 'arg1', 'arg2'
                 or die "can't exec myprog: $!";
         }

Naturally, the $EUID = $UID isn't very useful exactly as-is, so let's
change the $UID to the uid for `nobody' on this system (32767) and
$GID to be the gid for `nogroup' (6403).  My Perl also barfed on the
die, so I fixed it with some parens.  (This doesn't bother me.  I like
Lisp.)

die "Can't fork: $!" unless defined ($pid = open(KID, "-|")); 

Now, when I run the code as root, it die()s, "Can't drop privileges",
which means that the second assignment of $EUID (and probably $EGID)
worked, although the way this code is written, it suggests that it
should have failed if the privileges had, in fact, been dropped.

If my else block looks like this, however,

  my @temp = ($EUID, $EGID);
  $EUID = 32767;
  $EGID = 6403;
  open(FOO, ">/tmp/foo.$$") or die "cannot open FOO";
  print FOO "\n";
  close FOO;
  print STDERR "EUID is $EUID
EGID is $EGID
UID is $UID
GID is $GID\n";

$EUID, $UID, and $GID are all what I would expect.  Furthermore, the
/tmp/foo.$$ file is owned by nobody.  Strangely, EGID seems to be
unaffected by my attempted assignment.  Indeed, truss(1) shows that
the attempt failed!

$ sudo ./setuid.pl
EUID is 32767
EGID is 1 558 11 275 10
UID is 0
GID is 1 558 11 275 10

$ ls -ld foo.29601
-rw-r--r--   1 nobody   other          1 Jan  1 20:58 foo.29601

$ sudo truss ./setuid.pl
[snipped for brevity]
29679:  seteuid(32767)                                  = 0
29679:  getuid()                                        = 0 [32767]
29679:  setegid(6403)                                   Err#1 EPERM
29679:  getgid()                                        = 1 [1]
29679:  open("/tmp/foo.29679", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
29679:  write(3, "\n", 1)                               = 1
29679:  close(3)                                        = 0
29679:  getgroups(32, 0xEFFFF638)                       = 4
29679:  getgroups(32, 0xEFFFF638)                       = 4

The reason that the second assignment of $EUID, which is expected to
fail, works, is because in order to return the EPERM error for a
seteuid call, (this from the setuid(2) man page):

     EPERM     For setuid() and seteuid() the effective  user  of
               the calling process is not super-user, and the uid
               parameter does not match either the real or  saved
               user  IDs.

It would appear that in this case, the saved user ID is 0, so the
reassignment back to euid=0 works.

Just for grins, I decided to try to fork a child from the child after
the seteuid to see what would happen there: we're root!

# in the child:
EUID is 32767 
EGID is 1 558 11 275 10 
UID is 0 
GID is 1 558 11 275 10 

# grandchild:
EUID is 0 
EGID is 1 558 11 275 10 
UID is 0 
GID is 1 558 11 275 10 


My else block for this test is:

  @temp = ($EUID, $EGID);
  $EUID = 32767;
  $EGID = 6403;
  die "Can't fork: $!" unless defined ($newpid = open(GRANDKID, "-|"));
  if($newpid) {
    while(<GRANDKID>) {
      ;# hang out
    }
    close GRANDKID;
  } else {
    print STDERR "EUID is $EUID
EGID is $EGID
UID is $UID
GID is $GID\n";
    # Make sure privs are really gone
    ($EUID, $EGID) = @temp;
    die "Can't drop privileges"
      unless $UID == $EUID;
    open(FOO, ">/tmp/foo.$$") or die "cannot open FOO";
    print FOO "\n";
    close FOO;
    print STDERR "EUID is $EUID
EGID is $EGID
UID is $UID
GID is $GID\n";
    $ENV{PATH} = "/bin:/usr/bin";
    exec 'myprog', 'arg1', 'arg2'
      or die "can't exec myprog: $!";
  }


Here's the system's information about Perl.

Summary of my perl5 (5.0 patchlevel 5 subversion 3) configuration:
  Platform:
    osname=solaris, osvers=2.6, archname=sun4-solaris
    uname='sunos gold 5.6 generic_105181-13 sun4u sparc sunw,ultra-4 '
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef useperlio=undef d_sfio=undef
  Compiler:
    cc='cc', optimize='-O', gccversion=
    cppflags='-I/usr/local/include'
    ccflags ='-I/usr/local/include'
    stdchar='unsigned char', d_stdstdio=define, usevfork=false
    intsize=4, longsize=4, ptrsize=4, doublesize=8
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    alignbytes=8, usemymalloc=y, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib /usr/ccs/lib
    libs=-lsocket -lnsl -lgdbm -ldb -ldl -lm -lc -lcrypt
    libc=/lib/libc.so, so=so, useshrplib=false, libperl=libperl.a
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
    cccdlflags='-KPIC', lddlflags='-G -L/usr/local/lib'


Characteristics of this binary (from libperl): 
  Built under solaris
  Compiled at Jun 21 1999 19:12:48
  @INC:
    /usr/local/lib/perl5/5.00503/sun4-solaris
    /usr/local/lib/perl5/5.00503
    /usr/local/lib/perl5/site_perl/5.005/sun4-solaris
    /usr/local/lib/perl5/site_perl/5.005
    .

It gets more strange.  With Perl 5.004_04 under IRIX 5.3, the
reassignment of $EUID works, but the program doesn't die() and
complain about not being able to drop privileges:

# ./setuid.pl
EUID is 0 
EGID is 0 4 3 2 1 0 
UID is 0 
GID is 0 4 3 2 1 0
can't exec myprog: No such file or directory at ./setuid.pl line 26.

-rw-r--r--    1 root     sys            0 Jan  1 21:15 foo.19431

If I do not attempt the reassignment of $EUID, 
# ./setuid.pl
EUID is 60001 
EGID is 0 4 3 2 1 0 
UID is 0 
GID is 0 4 3 2 1 0

Summary of my perl5 (5.0 patchlevel 4 subversion 4) configuration:
  Platform:
    osname=irix, osvers=5, archname=IP22-irix
    uname='irix goffette 5.3 02091401 ip22 mips '
    hint=recommended, useposix=true, d_sigaction=define
    bincompat3=y useperlio=undef d_sfio=undef
  Compiler:
    cc='gcc', optimize='-O', gccversion=2.7.2
    cppflags='-I/usr/local/include -I/usr/freeware/include'
    ccflags ='-I/usr/local/include -I/usr/freeware/include'
    stdchar='unsigned char', d_stdstdio=define, usevfork=false
    voidflags=15, castflags=0, d_casti32=define, d_castneg=define
    intsize=4, alignbytes=8, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='ld', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib /lib
    libs=-lmalloc -lsun -lm -lc -lcrypt -lbsd -lPW
    libc=/usr/lib/libc.so, so=so
    useshrplib=false, libperl=libperl.a
  Dynamic Linking:
    dlsrc=dl_none.xs, dlext=none, d_dlsymun=undef, ccdlflags=''
    cccdlflags='', lddlflags=''


Characteristics of this binary (from libperl): 
  Built under irix
  Compiled at Nov 27 1997 12:13:13
  @INC:
    /usr/local/lib/perl5/IP22-irix/5.00404
    /usr/local/lib/perl5
    /usr/local/lib/perl5/site_perl/IP22-irix
    /usr/local/lib/perl5/site_perl
    .


On another Solaris machine, 2.5.1, the same behavior as under IRIX 5.3
is exhibited:

# without reassignment attempt:
EUID is 60001 
EGID is 1 1 
UID is 0 
GID is 1 1

# with reassignment attempt (note, no die()):
EUID is 0 
EGID is 1 1 
UID is 0 
GID is 1 1

Summary of my perl5 (5.0 patchlevel 4 subversion 4) configuration:
  Platform:
    osname=solaris, osvers=2.5.1, archname=sun4-solaris
    uname='sunos strangepork.interhack.net 5.5.1 generic_103640-08
sun4u sparc sunw,ultra-1 '
    hint=recommended, useposix=true, d_sigaction=define
    bincompat3=y useperlio=undef d_sfio=undef
  Compiler:
    cc='gcc', optimize='-O', gccversion=2.7.0
    cppflags='-I/usr/local/include -I/usr/gnu/include'
    ccflags ='-I/usr/local/include -I/usr/gnu/include'
    stdchar='unsigned char', d_stdstdio=define, usevfork=false
    voidflags=15, castflags=0, d_casti32=define, d_castneg=define
    intsize=4, alignbytes=8, usemymalloc=y, prototype=define
  Linker and Libraries:
    ld='gcc', ldflags =' -L/usr/local/lib -L/usr/gnu/lib'
    libpth=/usr/local/lib /usr/gnu/lib /lib /usr/lib /usr/ccs/lib
    libs=-lsocket -lnsl -ldl -lm -lc -lcrypt
    libc=/lib/libc.so, so=so
    useshrplib=false, libperl=libperl.a
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
    cccdlflags='-fpic', lddlflags='-G -L/usr/local/lib -L/usr/gnu/lib'


Characteristics of this binary (from libperl): 
  Built under solaris
  Compiled at Jan  8 1998 12:04:07
  @INC:
    /usr/local/lib/perl5/sun4-solaris/5.00404
    /usr/local/lib/perl5
    /usr/local/lib/perl5/site_perl/sun4-solaris
    /usr/local/lib/perl5/site_perl
    .

And the same is true again, on an old FreeBSD machine:

This is perl, version 5.003 with EMBED
        built under freebsd at Sep 25 1996 13:29:54
        + suidperl security patch

-- 
Matt Curtin cmcurtin@interhack.net http://www.interhack.net/people/cmcurtin/


[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index][Thread Index][Top&Search][Original]