Hacking Linux Exposed

About
Authors
Contents
Reviews
Foreword
Purchase

Articles
Books
Sourcecode
Tools
Errata

Home

 


(view this code in a separate window)

#!/usr/bin/suidperl
#
# Copyright 2001, Bri Hatch
# Released under the GPL
#
# rcspasswd - A program to front-end the passwd command with rcs
#             control
#
# Installation:
#
# Before you run this script the first time, you should run the
# following commands
#
#      prompt# mkdir /etc/RCS
#      prompt# echo "/etc/shadow" | ci -u -i /etc/shadow
#
# This will create the RCS directory and do an initial checkin
# of the shadow file.
#
# Then, to make sure everyone uses this script instead of the
# actual passwd command,
#
#      prompt# chmod 700 /usr/bin/passwd
#
# Install this perl program as '/usr/bin/rcspasswd', etc, and tell
# your users to use it instead of passwd (which won't work anymore...)
# You will then need to give it the setuserid bit s.t. it can write
# to /etc/RCS.
#
#      prompt# chmod u+s /usr/bin/rcspasswd
#
# If you get errors about the Rcs module not being installed, snag
# it from CPAN, ala
#
#   prompt# perl -MCPAN -e 'install Rcs'
#
# Alternate installation:
# (Use carefully, this could break the way your distribution
#  handles /etc/shadow.)
#           chgrp shadow /usr/bin/rcspasswd /etc/RCS /etc/shadow
#           chmod 750 /usr/bin/rcspasswd
#           chmod g+s /usr/bin/rcspasswd
#	    chmod 770 /etc/RCS
#           chmod 440 /etc/shadow
#
# (Note that you could make it merely setgid if you play with the
# permissions of /etc/RCS and make a new group instead, your call.)
#
#
#
# Bugs:     The user can 'killall -9 rcspasswd' to kill this
#           script,leaving the file checked out, affecting a
#           DoS on passwd changes.
#
# Notes:    Failed password changes are logged via Rcs as such.
#           However, a failed password change should result in
#           no changes to /etc/shadow, so no Rcs revision would
#           be created, and thus this log is effectively discarded.
#           But since we're still calling the actual passwd program,
#           all it's logging (and PAM if enabled) is still in effect
#           anyway, so it's no loss.



$PASSWD_CMD='/usr/bin/passwd';		# Hard code appropriately.

$SHADOW='/etc/shadow';			# Folks aren't using passwd still,
					# are they?  Please, god, I hope not.



# Ok, no more changes necessary from here down.

sub bail { print STDERR @_, "\n"; checkin(); exit 1; }


require 5.003;				# Doesn't have the old suidperl bug.
use Rcs;
use File::Basename;

undef %ENV;				# Don't allow user-supplied env,
					# avoid current and future bugs with
					# environment issues.

$ENV{PATH}="/";				# Prevent any PATH issues.
$ENV{LOGNAME}='root';			# for log entry, again, ignore user
					# supplied input.

# Let's not bother with workdir and rcsdir methods for the
# Rcs module, and just cd in and use non fully qualified
# path names.

chdir dirname $SHADOW or bail "Can't cd to ", dirname $SHADOW, "\n";
$SHADOW=basename $SHADOW;


Rcs->quiet(1);  			# Let's not see random output
Rcs->bindir('/usr/bin');		# Set this to the location where
					# you can find 'co', 'ci' and friends.
$shadow = Rcs->new;
$shadow->file($SHADOW);

$SETUID=1 if $> != $<;			# Are we running setuid?

$realuid=$<;
$realuname = getpwuid($realuid);
$msg = "rcspasswd change by $realuname.";


# Check out the shadow file.  This will also prevent this script
# from running more than once simultaneously, which is a bit of
# duplication given that passwd takes care of shadow locking anyway.

$<=$> if $SETUID;		# Needed for Rcs -- it checks real uid,
				# annoyingly

$shadow->co('-l');		# This will automatically die's if failure,
				# no need for error checking.

# Reset to invoking user for passwd command, and die if we fail,
# otherwise normal users will be running passwd as root.

$SETUID and $< = $realuid and $< != $realuid and bail "Can't reset uid";

# Run the passwd command now, using the arguments supplied.
#
# We don't modify @ARGV at all because we assume that, passwd
# being given the exact same uid/euid it would normally receive
# were this script not in the way, that it will act normal and
# handle all the permissions for us, not allowing 'joe' to
# change the pw for 'bob' etc.

system $PASSWD_CMD, @ARGV;

$msg .= " (ERROR: '$PASSWD_CMD @ARGV' command failed)" if $?;

checkin();
exit 0;




sub checkin {
  $<=$>;			# Need for Rcs, again.
  
  $shadow->ci('-u',"-m$msg");	# -u to make sure it's checked out again
				# s.t. it's avalable for authentication
				# like normal.  It would suck for /etc/shadow
				# to be in RCS only, not in /etc ...
}