Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failing POSIX::getuid/getgid and related ops #6977

Closed
p5pRT opened this issue Dec 11, 2003 · 7 comments
Closed

Failing POSIX::getuid/getgid and related ops #6977

p5pRT opened this issue Dec 11, 2003 · 7 comments

Comments

@p5pRT
Copy link

p5pRT commented Dec 11, 2003

Migrated from rt.perl.org#24641 (status was 'resolved')

Searchable as RT24641$

@p5pRT
Copy link
Author

p5pRT commented Dec 11, 2003

From stas@stason.org

Jörg Walter was helping me over irc to debug some POSIX uid/gid
get/set related problems. Here are the bugs that we have identified​:

All the following tests are run as root. We will attempt to
setuid/setgid into user 'nobody' and then try to check whether that
user is allowed to read/write/execute in that directory. The directory
/root has the perm​:

  drwx------ root root

so it should fail to do that.

Test 1​:

Using perl's $(, $), $<, $> "sudo" to "nobody" and try to check
whether /root is -r, -w, -x by 'nobody'​:

  % perl -le ' \
  my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
  $( = $) = $gid+0; \
  die "failed to change gid to $gid (now​: $(, $))" if $( != $gid; \
  $< = $> = $uid+0; \
  die "failed to change uid to $uid (now​: $<, $>)" if $< != $uid; \
  print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
  NOK

as expected user 'nobody' can't -r, -w, -x '/root'. Notice that set
uid and set gid (and their effective counterparts) process was
successful (as we've got NOK).

Test 2​:

Do the same this time using the equivalent POSIX calls​:

  % perl -le 'require POSIX; \
  my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
  POSIX​::setgid($gid+0); \
  die "failed to change gid to $gid (now​: $(, $))" \
  if POSIX​::getgid() != $gid; \
  POSIX​::setuid($uid+0); \
  die "failed to change uid to $uid (now​: $<, $>)" \
  if POSIX​::getuid() != $uid; \
  print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
  failed to change gid to 65534 (now​: 0 0, 0 0) at -e line 4.

oops, has setgid failed? Not really, see the next test

Test 3​:

Now don't check whether set[ug]id has failed​:

  % perl -le 'require POSIX; \
  my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
  POSIX​::setgid($gid+0); \
  POSIX​::setuid($uid+0); \
  print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
  OK

Something is wrong as user 'nobody' should get 'NOK' (it can't read
that dir). As Jörg has figured out there are two problems here. If you
look at ps(1) (by adding sleep(100 to keep it up), the above script
shows that the program is running under nobody.nobody, so set[ug]id
didn't fail.

The first problem is that POSIX​::getuid and POSIX​::getgid return wrong
values (they return 'root' values) as if the process hasn't called
set[ug]id. Changing POSIX set calls to perl vars seems to affect
POSIX​::get[ug]id​:

  % perl -le 'require POSIX; \
  my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
  $( = $) = $gid+0; \
  die "failed to change gid to $gid (now​: $(, $))" \
  if POSIX​::getgid() != $gid; \
  $< = $> = $uid+0; \
  die "failed to change uid to $uid (now​: $<, $>)" \
  if POSIX​::getuid() != $uid; \
  print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
  NOK

meaning that POSIX​::set[ug]id do affect the program environment, but
not perl variables $(, $), $<, $> and not POSIX​::get[ug]id.

Now look again at the test #3, it has reported OK, even though the
process has successfully migrated to run under 'nobody'. Let's change
the test to do something that is outside of perl's control, e.g. try
to write to that directory, and expect it to fail under nobody​:

  % perl -le 'require POSIX; \
  my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
  POSIX​::setgid($gid+0); \
  POSIX​::setuid($uid+0); \
  qx[each "foo" > /root/nobody]'
  sh​: line 1​: /root/nobody​: Permission denied

which shows what? That directives -r, -w, -x still think that the
process is runnin under 'root' and return OK.

So to summarize there are two bugs​: while doing what it is supposed to do
POSIX​::set[ug]id doesn't affect
1) $(, $), $<, $>, POSIX​::get[ug]id.
2) -r, -w, -x ops
may be it's the same issue.

These aren't glibc bugs, since this C program works correctly (its get

#include <stdio.h>
main() {
  setgid(65534);
  setuid(65534);
  printf("%i %i\n",getuid(),getgid());
}

get calls match the set calls. (65534 is nobody on my machine)

I can see the same behavior with all perls I have 5.005_03 - blead,
here is one of them​:

perl -V
Summary of my perl5 (revision 5.0 version 8 subversion 1) configuration​:
  Platform​:
  osname=linux, osvers=2.4.18-23mdksmp, archname=i386-linux-thread-multi
  uname='linux hp6.mandrakesoft.com 2.4.18-23mdksmp #1 smp fri aug 2
12​:31​:40 cest 2002 i686 unknown unknown gnulinux '
  config_args='-des -Dinc_version_list=5.8.0/i386-linux-thread-multi 5.8.0
5.6.1 5.6.0 -Darchname=i386-linux -Dcc=gcc -Doptimize=-O2 -fomit-frame-pointer
-pipe -march=i586 -mcpu=pentiumpro -Dprefix=/usr -Dvendorprefix=/usr
-Dsiteprefix=/usr -Dman3ext=3pm -Dcf_by=MandrakeSoft -Dmyhostname=localhost
-Dperladmin=root@​localhost -Dd_dosuid -Ud_csh -Duseshrplib -Dusethreads'
  hint=recommended, useposix=true, d_sigaction=define
  usethreads=define use5005threads=undef useithreads=define
usemultiplicity=define
  useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
  use64bitint=undef use64bitall=undef uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS
-fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
  optimize='-O2 -fomit-frame-pointer -pipe -march=i586 -mcpu=pentiumpro ',
  cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS
-fno-strict-aliasing -I/usr/local/include -I/usr/include/gdbm'
  ccversion='', gccversion='3.3.1 (Mandrake Linux 9.2 3.3.1-1mdk)',
gccosandvers=''
  intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
  ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t',
lseeksize=8
  alignbytes=4, prototype=define
  Linker and Libraries​:
  ld='gcc', ldflags =' -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib
  libs=-lnsl -lndbm -lgdbm -ldl -lm -lcrypt -lutil -lpthread -lc
  perllibs=-lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
  libc=/lib/libc-2.3.2.so, so=so, useshrplib=true, libperl=libperl.so
  gnulibc_version='2.3.2'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic
-Wl,-rpath,/usr/lib/perl5/5.8.1/i386-linux-thread-multi/CORE'
  cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib'

Characteristics of this binary (from libperl)​:
  Compile-time options​: MULTIPLICITY USE_ITHREADS USE_LARGE_FILES
PERL_IMPLICIT_CONTEXT
  Locally applied patches​:
  RC4
  Built under linux
  Compiled at Sep 1 2003 17​:29​:01
  %ENV​:
  PERLDOC_PAGER="less -R"
  @​INC​:
  /usr/lib/perl5/5.8.1/i386-linux-thread-multi
  /usr/lib/perl5/5.8.1
  /usr/lib/perl5/site_perl/5.8.1/i386-linux-thread-multi
  /usr/lib/perl5/site_perl/5.8.1
  /usr/lib/perl5/site_perl/5.8.0/i386-linux-thread-multi
  /usr/lib/perl5/site_perl/5.8.0
  /usr/lib/perl5/site_perl
  /usr/lib/perl5/vendor_perl/5.8.1/i386-linux-thread-multi
  /usr/lib/perl5/vendor_perl/5.8.1
  /usr/lib/perl5/vendor_perl/5.8.0/i386-linux-thread-multi
  /usr/lib/perl5/vendor_perl/5.8.0
  /usr/lib/perl5/vendor_perl
  .

__________________________________________________________________
Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
http​://stason.org/ mod_perl Guide ---> http​://perl.apache.org
mailto​:stas@​stason.org http​://use.perl.org http​://apacheweek.com
http​://modperlbook.org http​://apache.org http​://ticketmaster.com

@p5pRT
Copy link
Author

p5pRT commented Dec 20, 2003

From @rgs

Stas Bekman (via RT) wrote​:

The first problem is that POSIX​::getuid and POSIX​::getgid return wrong
values (they return 'root' values) as if the process hasn't called
set[ug]id. Changing POSIX set calls to perl vars seems to affect
POSIX​::get[ug]id​:

% perl -le 'require POSIX; \
my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
$( = $) = $gid+0; \
die "failed to change gid to $gid (now​: $(, $))" \
if POSIX​::getgid() != $gid; \
$&lt; = $&gt; = $uid+0; \
die "failed to change uid to $uid (now​: $&lt;, $>)" \
if POSIX​::getuid() != $uid; \
print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
NOK

meaning that POSIX​::set[ug]id do affect the program environment, but
not perl variables $(, $), $&lt;, $&gt; and not POSIX​::get[ug]id.

Affecting PL_uid et alli from POSIX.xs should be quite easy.
POSIX​::get[ui]gd are only functions that return perl variables so this
should solve this issue as well.

...

which shows what? That directives -r, -w, -x still think that the
process is runnin under 'root' and return OK.

They internally use PL_uid (most probably).

So to summarize there are two bugs​: while doing what it is supposed to do
POSIX​::set[ug]id doesn't affect
1) $(, $), $&lt;, $&gt;, POSIX​::get[ug]id.
2) -r, -w, -x ops
may be it's the same issue.

To summarize : easy to fix, but regression tests would be hard to write.

@p5pRT
Copy link
Author

p5pRT commented Dec 22, 2003

From stas@stason.org

Rafael Garcia-Suarez wrote​:

Stas Bekman (via RT) wrote​:

The first problem is that POSIX​::getuid and POSIX​::getgid return wrong
values (they return 'root' values) as if the process hasn't called
set[ug]id. Changing POSIX set calls to perl vars seems to affect
POSIX​::get[ug]id​:

% perl -le 'require POSIX; \
my($uid, $gid) = (getpwnam("nobody"))[2..3]; \
$( = $) = $gid+0; \
die "failed to change gid to $gid (now​: $(, $))" \
if POSIX​::getgid() != $gid; \
$&lt; = $&gt; = $uid+0; \
die "failed to change uid to $uid (now​: $&lt;, $>)" \
if POSIX​::getuid() != $uid; \
print -r q{/root} && -w _ && -x _ ? q{OK} : q{NOK};'
NOK

meaning that POSIX​::set[ug]id do affect the program environment, but
not perl variables $(, $), $&lt;, $&gt; and not POSIX​::get[ug]id.

Affecting PL_uid et alli from POSIX.xs should be quite easy.
POSIX​::get[ui]gd are only functions that return perl variables so this
should solve this issue as well.

Does it mean that you have a fix already? Cool!

which shows what? That directives -r, -w, -x still think that the
process is runnin under 'root' and return OK.

They internally use PL_uid (most probably).

So to summarize there are two bugs​: while doing what it is supposed to do
POSIX​::set[ug]id doesn't affect
1) $(, $), $&lt;, $&gt;, POSIX​::get[ug]id.
2) -r, -w, -x ops
may be it's the same issue.

To summarize : easy to fix, but regression tests would be hard to write.

Yes, the easiest is to run as root. They could be optional tests, though.

I've also later discovered that​:

  $( = $) = $gid+0;

is not enough, as it doesn't quite change the group completely. Something
about not running setgroups() (which is supposed to happen internally). If the
/root dir was drwxr-x--- running the above will still allow the inferior owner
access the /root dir rx, via the group perms.

The really right way to drop perms completely is to​:

  $( = $) = "$gid $gid";
  $&lt; = $&gt; = $uid+0;

notice that you pass the same group id for "other groups".

also a few months ago I reported on behalf of one of the modperl users is that
.acls/access() aren't quite working via -r, -w, -x tests on some systems. So
one can't really use -r, -w, -x tests reliably.

Therefore I ended up doing the actual attempts to read, write and execute to
be sure that I get things right​:

# this sub is executed from an external process only, since it
# "sudo"'s into a uid/gid of choice
sub run_root_fs_test {
  my($uid, $gid, $dir) = @​_;

  # first must change gid and egid ("$gid $gid" for an empty
  # setgroups() call as explained in perlvar.pod)
  my $groups = "$gid $gid";
  $( = $) = $groups;
  die "failed to change gid to $gid" unless $( eq $groups &amp;&amp; $) eq $groups;

  # only now can change uid and euid
  $&lt; = $&gt; = $uid+0;
  die "failed to change uid to $uid" unless $&lt; == $uid &amp;&amp; $&gt; == $uid;

  my $file = catfile $dir, ".apache-test-file-$$-".time.int(rand);
  eval "END { unlink q[$file] }";

  # unfortunately we can't run the what seems to be an obvious test​:
  # -r $dir && -w _ && -x _
  # since not all perl implementations do it right (e.g. sometimes
  # acls are ignored, at other times setid/gid change is ignored)
  # therefore we test by trying to attempt to read/write/execute

  # -w
  open TEST, ">$file" or die "failed to open $file​: $!";

  # -x
  -f $file or die "$file cannot be looked up";
  close TEST;

  # -r
  opendir DIR, $dir or die "failed to open dir $dir​: $!";
  defined readdir DIR or die "failed to read dir $dir​: $!";
  close DIR;

  # all tests passed
  print "OK";
}

If you are wondering what this function is for, it tests whether Apache will
be able to read/write/execute files under dir $dir, once it switches to
$uid/$gid (since Apache is started as root, but never runs its child processes
as such). So hence the test to detect sure failures before they hit the user.

Thanks again to Jörg Walter for working this one out.

p.s. I'm not 100% sure the example in perlsec is correct, as it changes uids
before gids, which I found doesn't quite work (once uid is changed, you can't
change gid of the process). Normally it should be the other way around. Though
it might be different under setuid processes.

__________________________________________________________________
Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
http​://stason.org/ mod_perl Guide ---> http​://perl.apache.org
mailto​:stas@​stason.org http​://use.perl.org http​://apacheweek.com
http​://modperlbook.org http​://apache.org http​://ticketmaster.com

@p5pRT
Copy link
Author

p5pRT commented Dec 25, 2003

From @rgs

Stas Bekman wrote​:

also a few months ago I reported on behalf of one of the modperl users is that
.acls/access() aren't quite working via -r, -w, -x tests on some systems. So
one can't really use -r, -w, -x tests reliably.

(I may have mentioned this earlier) Does the filetest pragma help?

@p5pRT
Copy link
Author

p5pRT commented Dec 25, 2003

From @rgs

Stas Bekman wrote​:

Affecting PL_uid et alli from POSIX.xs should be quite easy.
POSIX​::get[ui]gd are only functions that return perl variables so this
should solve this issue as well.

Does it mean that you have a fix already? Cool!

I applied the following patch to bleadperl :

(I tried to be the least intrusive possible -- more eyeballs welcome.
Notably this doesn't get around all the mess in mg.c about setting
$&gt;, $&lt; and friends with the forest of system-specific syscalls ; this
isn't the purpose of POSIX.pm after all.)

Change 21958 by rgs@​rgs-home on 2003/12/25 21​:22​:25

  Fix bug [perl #24641] : when POSIX​::set[ug]id() are called,
  update the perl variables PL_uid and PL_euid (resp. PL_gid
  and PL_egid) with the new values.

Affected files ...

... //depot/perl/ext/POSIX/POSIX.xs#121 edit

Differences ...

==== //depot/perl/ext/POSIX/POSIX.xs#121 (text) ====

@​@​ -1827,10 +1827,20 @​@​
SysRet
setgid(gid)
  Gid_t gid
+ CLEANUP​:
+ if (RETVAL >= 0) {
+ PL_gid = getgid();
+ PL_egid = getegid();
+ }

SysRet
setuid(uid)
  Uid_t uid
+ CLEANUP​:
+ if (RETVAL >= 0) {
+ PL_uid = getuid();
+ PL_euid = geteuid();
+ }

SysRetLong
sysconf(name)

@p5pRT p5pRT closed this as completed Dec 25, 2003
@p5pRT
Copy link
Author

p5pRT commented Dec 25, 2003

@rgs - Status changed from 'new' to 'resolved'

@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2003

From stas@stason.org

Rafael Garcia-Suarez wrote​:

Stas Bekman wrote​:

also a few months ago I reported on behalf of one of the modperl users is that
.acls/access() aren't quite working via -r, -w, -x tests on some systems. So
one can't really use -r, -w, -x tests reliably.

(I may have mentioned this earlier) Does the filetest pragma help?

The original poster has never followed up on the proposed solution​:
http​://marc.theaimsgroup.com/?l=apache-modperl&m=105159295409019&w=2
So I don't know.

Let me ping the modperl list, may be someone who has an access to acl based
system can verify that.

__________________________________________________________________
Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
http​://stason.org/ mod_perl Guide ---> http​://perl.apache.org
mailto​:stas@​stason.org http​://use.perl.org http​://apacheweek.com
http​://modperlbook.org http​://apache.org http​://ticketmaster.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant