可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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
回答3:
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
回答4:
I recognized a corner case when calling scripts from mc (at least in our RHEL's), which results that the who am i
does not output anything. To circumvent that, I produced the following one-liner in bash:
REALUSERNAME=$(ps uhp `ps -AjH | grep \`ps -u $USER fh | awk '{ print $0; if(index($0, "ps -u $USER fh")) exit 0;}' | tac | awk '{if(!index($0, "\\\\\_")){print $1; exit 0;}}'\` | awk '{print $3}'` | awk '{print $1}')
Essentially, this walks backwards on the tree output of ps -u $USER fh
and then crops on the topmost username column.
Thoughts, better solutions are welcome :-)