Perl's lack of setgroups

dgl on 2007-05-03T01:12:05

(Hi, I thought I'd start a blog..)

I want a process (started as root) to put itself in some additional groups before it changes to an unprivileged user. perldoc perlvar states this isn't possible via setting $GID or $(:

"However, a value assigned to $( must be a single number used to set the real gid. So the value given by $( should not be assigned back to $( without being forced numeric, such as by adding zero."
Next stop is POSIX, it helpfully contradicts perlvar:
"setgid [..] Similar to assigning a value to the Perl’s builtin $) variable, see "$GID" in perlvar, except that the latter will change only the real user identifier, and that the setgid() uses only a single numeric argument, as opposed to a space-separated list of numbers."
POSIX also provides the function getgroups, but there's no sign of setgroups! Even more helpfully yet another part of the Perl documentation implies this feature was added in perl 5.004.



I gave up and just tried it (with Perl 5.8.8 on Ubuntu):

# perl -MEnglish -e'$EGID = $GID = "1000 10"; $EUID = $UID = 65534; system("id")'
uid=65534(nobody) gid=1000(dgl) groups=0(root)
It doesn't work. Also notice it keeps the additional group root around, which is a bit scary too.


Perl does provide some help though -- through syscall() it's possible to work around the lack of setgroups. Unfortantely syscall() depends on knowing the number of the system call, which varies according to the architecture (even x86-64 is different to x86) and operating system in use.

setgroups is simply (on Linux x86):

sub setgroups {
  syscall 206, scalar @_, pack("L" x @_, @_);
}
This has the desired effect:
# perl -MEnglish -MPOSIX -e'sub setgroups { syscall 206, scalar @_, pack("L" x @_, @_); } setgroups(1000,10); $EGID = $GID = 1000; $EUID = $UID = 65534; system("id")'
uid=65534(nobody) gid=1000(dgl) groups=10(uucp),1000(dgl)
Maybe it would be better written as a module for CPAN although personally I'd rather see this fixed in the Perl core. As it leaves additional groups around with no way to get rid of them, I wouldn't be surprised if it results in a security issue in Perl programs trying to drop privileges.



Note under Linux the syscall number can be found by looking in /usr/include/<arch>/syscall.h (where <arch> is your architecture, e.g. x86). It might be easier to grep /usr/include for __NR_setgroups32 (or __NR_setgroups if it's a newer architecture).

Update: Ignore this post, I realised it's very simple, $EGID is the thing to set first.. i.e. $GID = $EGID = "10 1000"; works fine..


Updating posts

Aristotle on 2007-05-03T12:19:43

Hint: if you put that sort of update at the top of the post, it would spare many of your readers the effort of fully reading the entire post in detail. (I would have switched to skimming mode if I knew you went down a garden path.)

Thanks in advance. :-)

Re:Updating posts

dgl on 2007-05-03T17:30:53

Good point.

BTW, is there a way (or greasemonkey script..) to make this entry box bigger?

Re:Updating posts

Aristotle on 2007-05-03T21:11:29

I use the Stylish extension to impose this site-specific style:

@namespace url(http://www.w3.org/1999/xhtml);
@-moz-document domain("use.perl.org") {
  textarea[name="postercomment"], textarea[name="article"] {
    width: 45em !important;
    height: 40em !important;
  }
}

Another option would be Resizable Textareas, I guess.