[sudo-users] How to know real uid/gid?

Josef Wolf jw at raven.inka.de
Thu Jun 8 16:41:07 EDT 2006

On Fri, May 26, 2006 at 09:34:15AM +0200, Josef Wolf wrote:
> On Fri, May 26, 2006 at 10:16:15AM +1000, Matthew Hannigan wrote:
> > On Thu, May 25, 2006 at 09:27:18PM +0200, Josef Wolf wrote:

> > > > You've got env vars SUDO_UID and SUDO_GID ...
[ ... ]
> > > > So I guess you can set[ug]id to those if you wanted to 'drop'
> > > > privs.  Be careful that they're not tainted though.
> > > 
> > > Isn't this set by sudo?  So how they can be tainted?  How would one try
> > > to exploit that?
> > 
> > Er, with difficulty :-)  unless the thing you're
> > spawning is an interpreter or has some internal
> > language that lets you change env vars.  
> As long as the vars are captured before the user has a chance to change
> them, it should be safe.

So I have come up with the piece of code I included below.  It is meant to
work with sudo as well as with setuid scripts (depends on how the admin
prefers to install the package).

I am pretty confident about it, but when it comes to security issues, I
have always some bad feeling that I might have overseen/missed some
important points.  So I'd like to hear opinions from people who are
experienced with security issues whether I have overseen some important

The sudoers line used to invoke via sudo would be:
   naclt ALL = NOPASSWD: /home/naclt/bin/naclient

The mode when used to invoke via setuid would be:
   -rwsr-x---  1 root  naclt 11232 2006-06-04 11:49 naclient

If anyone have doubts whether this is really secure, please share your
opinion.  I hope I've put enough comments to make clear how it is meant
to work.  If there are portability issues, I'd like to hear about them, too.
Any suggestions are welcome!

Here's the code snippet:

#! /usr/bin/perl -T
use strict;
use warnings;

my $user  = "naclt";
my $group = "naclt";

# Determine which uid/gid is allowed to run the program
my $owner_uid = (getpwnam ($user))[2];
my $owner_gid = (getgrnam ($group))[2];
die "Can't determine uid/gid for $user/$group" if $owner_uid<1 || $owner_gid<1;

# Get real uid/gid of calling user.
my ($ruid, $rgid) = ( $<, $( );

if ($ruid<1 || $rgid<1) {
    # Why are we already privileged?  Setuid/gid programs don't modify
    # real uid/gid.  Thus, at this point, we have two possibilities:
    #  1. sudo is involved.  In this case get real uid/gid from environment.
    #  2. the program was invoked by root. In this case it is safe to assume
    #     real uid/gid are those of the "owner".

    $ruid = exists $ENV{"SUDO_UID"} ? $ENV{"SUDO_UID"} : $owner_uid;
    $rgid = exists $ENV{"SUDO_GID"} ? $ENV{"SUDO_GID"} : $owner_gid;

if (!defined $ruid || $ruid!=$owner_uid ||
    !defined $rgid || $rgid!=$owner_gid) {
    die "You are not allowed to run this program";
%ENV = (PATH=>"/sbin:/usr/sbin/:/bin:/usr/bin");
umask 077 or die "Can't set umask";

# At this point we have root privileges (to get a privileged port, for
# example) and can safely drop privileges before doing any potentially
# dangerous operations.

&do_privileged_work;   # get privileged port, for example
&do_unprivileged_work; # There's hopefully no way to gain privileges back

sub drop_privileges {
    $) = $rgid; $> = $ruid;

    # If either real or effective uid/gid is zero, there's no guarantee
    # that privileges can't be gained again.  So when in doubt, we prefer
    # to die.
    unless ( $( or $) or $< or $> ) { die ("Can't drop privileges!") }

More information about the sudo-users mailing list