views:

238

answers:

3

There is a perl script that needs to run as root but we must make sure the user who runs the script did not log-in originally as user 'foo' as it will be removed during the script.

So how can I find out if the user, who might have su-ed several times since she logged in has not impersonated 'foo' at any time in that chain?

I found an interesting perl script that was calling the following two shell scripts, but I think that would only work on Solaris.

my $shell_parent = 
`ps -ef | grep -v grep | awk \'{print \$2\" \"\$3}\' | egrep \"^@_\" | awk \'{print \$2}'`;

my $parent_owner = 
`ps -ef | grep -v grep | awk \'{print \$1\" \"\$2}\' | grep @_ | awk \'{print \$1}\'`;

This needs to work on both Linux and Solaris and I'd rather eliminate the repeated calls to he the shell and keep the whole thing in Perl.

A: 

Here's a Perl program that checks for direct setuid change:

#! /usr/bin/perl

sub callingUser() {
    my ($login, $pass, $uid, $gid) = getpwuid($<);
    return $login;
}

sub effectiveUser() {
    my ($login, $pass, $uid, $gid) = getpwuid($>);
    return $login;
}

printf("Real user name: %s\n", effectiveUser());
printf("Calling user name: %s\n", callingUser());

But since you mentioned that the setuid change may have occured anytime before, you probably have to parse the output of ps: I would do it using the following command. This command only uses features defined in POSIX, so I hope it is portable to all kinds of systems:

ps -e -o pid,ppid,user,ruser
Roland Illig
A: 

Maybe the following is what you want. The function hasBeenUser reads the process table and then follows the process chain from the current process down the parent process. If any of the processes on the way has a user or real user field equal to the username in question, the function returns a nonzero value.

#! /usr/bin/perl

sub hasBeenUser($) {
        my ($username) = @_;

        my $procs = {};
        open(PS, "ps -e -o pid,ppid,user,ruser |") or die;
        while (defined(my $line = <PS>)) {
                next unless $line =~ m"^(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+$";
                my ($pid, $ppid, $user, $ruser) = (int($1), int($2), $3, $4);
                $procs->{$pid} = [$pid, $ppid, $user, $ruser];
        }
        close(PS) or die;

        my $pid = $$;
        while (exists($procs->{$pid})) {
                my $proc = $procs->{$pid};
                delete $procs->{$pid}; # don't risk ending in an endless loop.
                warn "D: checking process $pid\n";
                if ($proc->[2] eq $username || $proc[3] eq $username) {
                        warn "E: process $pid was called by $username.\n";
                        return 1;
                }
                last if $pid < 2;
                $pid = $proc->[1];
        }
        return 0;
}

hasBeenUser("del"); # should return 0
hasBeenUser("root"); # should return nonzero
Roland Illig
+1  A: 

Quick and dirty and (UNIX only):

my $user = (split /\s/,`who am i`)[0];

The who am i command returns the owner of the TTY - i.e. who you were when you logged in.

If you want to do this in pure perl:

use POSIX;
my $tty = POSIX::ttyname(1); # The tty we are running in
my $uid = (stat $tty)[4];    # The owner uid of that tty
my $user = getpwuid($uid);   # The user with that uid

This will return the correct user, even after multiple su's. This usually freaks out your (less experienced) sysadmins.

Gavin Brock