#!/usr/bin/perl #$Id: sudoshell,v 1.14 2002/04/03 23:10:20 howen Exp $ $ENV{PATH}="/bin:/usr/bin:/usr/sbin:/usr/local/bin"; # Give ourselves a safe one. use strict; my $GREP = "grep"; my $SUDO="sudo"; my $PS; my $initscr; # OS dependencies if ($^O eq 'solaris' || $^O eq 'linux'){ $PS="ps -elf"; $initscr="/etc/init.d/sudoscriptd"; } elsif($^O eq 'freebsd' || $^O eq 'openbsd') { $PS="ps -aux"; $initscr="/usr/local/etc/rc.d/sudoscriptd.sh"; } else { print <<'EOM'; Sorry, but your OS is not among the ones I support Currently, that's linux, solaris, freebsd and openbsd That's because those are the ones I have access to. If you'd like support for your OS, either give me a root shell (!) on a representative system running your OS, or port it yourself and send me (hbo@egbok.com) the diffs. If system directory locations (i.e. /var/log and /var/run) don't have to change, and your system has Perl 5 and POSIX, That shouldn't be too hard. See the PORTING document in the distribution for details. EOM exit; } #' # No more failing open. Good rhetoric. Bad practicum. check_sudoscriptd(); my $rundir="/var/run/sudoscript"; my $fifo="$rundir/typescript"; if ($>){ # not run as root. Try sudo exec "$SUDO $0"; } if ($>){ print "Not root! Exiting.\n"; exit; } if (!defined $ENV{SHELL}){ print "Can't determine your shell. Set the SHELL env variable! Exiting.\n"; exit; } # Don't take their word for it. open SHELLS, "/etc/shells" || die $!; my $shellsre=join "|", map {chomp;s~/~\\\/~g;$_} (<SHELLS>); close SHELLS; if ($ENV{SHELL}!~/$shellsre/){ print "Your shell (".$ENV{SHELL}.") isn't in /etc/shells! Exiting.\n"; exit; } my $name; if ($ENV{SUDO_UID}){ ($name) =getpwuid $ENV{SUDO_UID}; } else { $name = "root"; } if (-p $fifo){ # script to the fifo if it exists open FIFO, ">>$fifo" || die $!; print FIFO "sudoshell started by $name\n"; close FIFO; exec "script $fifo"; } else { print "The logging FIFO doesn't exist. Can't run shell!\n"; } sub check_sudoscriptd { if (!checkpid()){ my $ans; print "The sudoscriptd doesn't appear to be running!\n"; print "Would you like me to start it for you? (requires root privilege)? "; $ans=<>; chomp $ans; die "Can't run sudoshell without sudoscriptd" if ($ans!~/^[Y|y]/); print <<'EOM'; This will be a one-off startup of the daemon. You may have to arrange for it to be started when the system starts, if that's what you want. See the INSTALL file in the distribution for details. EOM # bloody emacs sytax highlighting doesn't grok here docs if (! -x $initscr){ print "Hmm.. I can't seem to find the sudoscriptd startup file.\n"; print "Please tell me where it is. or just return for exit "; $ans=<>; chomp $ans; if (-x $ans) { $initscr=$ans; } else { die "Can't run sudoshell without sudoscriptd"; } } system($SUDO, $initscr, "start",'&'); print "waiting for the daemon .."; sleep 3; print "done\n"; if (!checkpid()){ print "Sorry, but I can't seem to start sudoscriptd for you!\n"; print "exiting\n"; exit; } } } sub checkpid { my $dpidf="/var/run/sudoscriptd.pid"; my $dpid; my @ret; my $gotone=0; if (-e $dpidf){ open DPID, $dpidf || die $!; $dpid=<DPID>; chomp $dpid; @ret=`$PS |$GREP $dpid | $GREP -v $GREP`; $gotone= ($#ret >-1 && $ret[0]=~/sudoscriptd/); } return $gotone; } =pod =head1 NAME sudoshell, ss - Run a root shell with logging =head1 SYNOPSIS sudoshell or sudo sudoshell =head1 DESCRIPTION I<sudoshell> runs the I<script> command with a fifo as the typescript. Used in conjunction with I<sudoscriptd> and I<sudo>, it provides a way to maintain the sudo audit trail while running a root shell. =head1 README I<Sudo> is a tool used for running programs with root privilege. Its major benefit is the audit trail it provides, as it logs each invocation with the command name, its arguments and the user who ran it. Because this audit trail is lost if a user runs a shell (e.g. bash or csh) with sudo, many sites restrict sudo to not allow such usage. Since this can cause problems, (see L<SUDO AND SHELLS>) many users prefer to retain the root password, even if it means forgoing support. This outcome also results in a loss of the audit trail, while increasing the chances that an unmanaged system will become a support problem later. Sudoshell is a small Perl script that works in conjunction with a logging daemon (see L<sudoscriptd>) to log all activity within a root shell. It uses the unix I<script> command to create the log. Once invoked, all console commands and output are logged to a fifo. The logging daemon reads from this fifo and manages log files to store the data produced. The logs are rotated to ensure that they do not overflow the disk space on the logging partition. Sudoshell checks to see if the daemon is running and offers to start it if it is not. (It does this with sudo, so you need to have sudo access to perform this step._) Sudoshell then checks to see if it has been run with root privilege, via 'sudo sudoshell' or otherwise. If not, it reinvokes itself using sudo. The script then checks the user's SHELL environment variable. If the value of this variable doesn't match one of the shells listed in /etc/shells, sudoshell refuses to run. Next the logging fifo is checked. If it exists, sudoshell runs the script command using the fifo as the typescript. If it doesn't exist, sudoshell exits. (This shouldn't happen because the daemon's status has already been checked.) =head1 SUDO AND SHELLS Most root tasks can be accomplished with sudo without running a shell. However certain tasks, such as running privileged commands in a pipeline, can be more difficult using sudo. Since sudo sometimes prompts for a password (depending on how long ago the user last authenticated) you can run into quirky situations like this: howen@ivan03|509> sudo ls | sudo more Password:Password:(enter password) (enter password) #sudoshell# CVS sudoscriptd sudoscriptd~ sudoshell sudoshell~ howen@ivan03|510> In this case we get two password prompts, right on top of one another. We enter the password for the first prompt, and sudo waits for the next one. Since the prompt is on the preceding line, this can be very confusing. Another place sudo has difficulty is with I/O redirection: howen@ivan03|504> sudo chown root /tmp/foo howen@ivan03|505> ls -l /tmp/foo -r--r--r-- 1 root other 1464 Mar 25 13:10 /tmp/foo howen@ivan03|506> sudo ls >>/tmp/foo bash: /tmp/foo: Permission denied howen@ivan03|507> sudo ls | sudo cat >>/tmp/foo bash: /tmp/foo: Permission denied But this works: howen@ivan03|508> sudo ls | sudo perl -e 'open OUT, ">>/tmp/foo";while(<>){print OUT;}' It's not something you can generally recommend, however. =head1 BUGS Sudoshell logs the name of the user that successfully invokes it when it starts, but there is no way to uniquely stamp subsequent lines (generated by script(1)) with the user's ID. A solution to this would involve a quite-a-bit more complicated c program. This solution is imperfect, but simple and portable. =head1 SEE ALSO L<sudoscriptd> L<script> =head1 LICENSE L<sudoshell> may be distributed under the same terms as Perl itself. =head1 PREREQUISITES sudoscriptd - L<http://www.egbok.com/sudoscript/sudoscriptd> or on CPAN sudo - L<http://www.courtesan.com/sudo/index.html> =head1 OSNAMES C<Solaris> C<Linux> C<freebsd> C<openbsd> =head1 SCRIPT CATEGORIES UNIX/System_administration =head1 CONTRIBUTORS The following people offered helpful advice: Dan Rich (drich@emplNOoyeeSPAMs.org) Alex Griffiths (dag@unifiedNOcomputingSPAM.com) Bruce Gray (bruce.gray@aNOcSPAMm.org) =head1 AUTHOR Howard Owen (hbo@egbok.com)