perl COPS, 3/3

Dan Farmer df at sei.cmu.edu
Sat Jun 22 14:29:32 AEST 1991


#!/bin/sh
# this is p-cops.103.03 (part 3 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file beta/passwd continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 3; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
	echo 'x - still skipping beta/passwd'
else
echo 'x - continuing file beta/passwd'
sed 's/^X//' << 'SHAR_EOF' >> 'beta/passwd' &&
stupak:*OdHloLgJnVCgI:131:20:Kenneth Stupak:/usr/users/stupak:/bin/csh
rdp:fZ3tNW/ICf0TI:159:78:Richard Pethia:/usr/users/rdp:/usr/local/bin/tcsh
connelly:u1KPqdq.5rML6:2955:20:John Connelly:/usr/users/connelly:/bin/csh
ecd:T2iMJ3K55qFOA:2993:78:Edward DeHart:/usr/users/ecd:/usr/local/bin/tcsh
tgp:uAONGKl7Smc/U:2999:70:Tod Pike:/usr/users/tgp:/bin/csh
pjg:*UTkWVHoPfhANw:3086:20:Patrick Glasso:/usr/users/pjg:/bin/csh
ph:*xisuhVETJw.Vo:3144:78:Paul Holbrook:/usr/users/ph:/bin/false
krvw:VaYVunaqW4D9.:3145:78:Kenneth R. van Wyk:/usr/users/krvw:/usr/local/bin/tcsh
tjm:s3oXhld2/.ngc:3159:20:Todd Miller:/usr/users/tjm:/bin/csh
cfalkens:yhJpwSRg/b4LI:3202:78:Carolyn Falkenstern:/usr/users/cfalkens:/bin/csh
byf:ZFRZIqr1ijgs2:3217:78:Barbara Fraser:/usr/users/byf:/usr/local/bin/tcsh
df::3271:78:Dan Farmer:/usr/users/df:/usr/local/bin/tcsh
brg:*:3366:78:Barbara Gratz:/usr/users/brg:/usr/local/bin/tcsh
nobody:*:65534:20:Nobody:/usr/nobody:/bin/date
news:*:8000:8000:News Maintainer:/usr/users/news:/bin/date
lizard:jW4Gl.3ncbiZg:8002:78:BYF's Play Thing:/usr/users/lizard:/usr/local/bin/tcsh
SHAR_EOF
echo 'File beta/passwd is complete' &&
chmod 0600 beta/passwd ||
echo 'restore of beta/passwd failed'
Wc_c="`wc -c < 'beta/passwd'`"
test 1555 -eq "$Wc_c" ||
	echo 'beta/passwd: original size 1555, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/passwd.chk ==============
if test -f 'beta/passwd.chk' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/passwd.chk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/passwd.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/passwd.chk' &&
#!/bin/sh -- need to mention perl here to avoid recursion
'true' || eval 'exec perl -S $0 $argv:q';
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X        if 0;
X
#
#   passwd.chk
#
# composer at chem.bu.edu
#
# Check password file -- /etc/passwd -- for incorrect number of fields,
# duplicate uid's, non-alphanumeric uids, and non-numeric group id's.
# 
# Mechanism:  This script ensures that each line of the passwd file (in
# $etc, line 47) has 7 fields and is non-blank, as well as examining the
# file for any duplicate users.  It then checks to ensure that the first
# character of the login name is alphanumeric, and that all uid and gid
# numbers are indeed numeric and non-negative.  It also checks the
# validity of the home directory.
# 
# For yellow pages passwords, it does the same checking, but in order to
# get a listing of all members of the password file, it does a "ypcat
# passwd" and uses the output from that as a passwd file.
# 
# The /etc/passwd file has a very specific format, making the task fairly
# simple.  Normally it has lines with 7 fields, each field separated by a
# colon (:).  The first field is the user id, the second field is the
# encrypted password (an asterix (*) means the group has no password,
# otherwise the first two characters are the salt), the third field is the
# user id number, the fourth field is the group id number, the fifth field
# is the GECOS field (basically holds miscellaneous information, varying
# from site to site), the sixth field is the home directory of the user,
# and lastly the seventh field is the login shell of the user.  No blank
# lines should be present.  Uid's will be flagged if over 8 chars, unless
# the $OVER_8 variable (line 45) is set to "YES".
# 
# If a line begins with a plus sign (+), it is a yellow pages entry.  See
# passwd(5) for more information, if this applies to your site.
# 
X
require 'pathconf.pl';
require 'pass.cache.pl';
#   Used for Sun C2 security group file. 'FALSE' (default) will flag
# valid C2 passwd syntax as an error, 'TRUE' attempts to validate it.
# Thanks to Pete Troxell for pointing this out.
$C2='FALSE' if undef($C2);
X
#  Some systems allow long uids; set this to 'TRUE', if so (thanks
# to Pete Shipley (lot of petes around here, eh?)):
$OVER_8='NO' if undef($OVER_8);
X
package passwd_chk;
X
#
# Important files:
$etc_passwd = $'PASSWD || '/etc/passwd';
X
#   Check $etc_passwd for potential problems, or use the alternate method
# set in cops.cf:
if (!"$'GET_PASSWD") {
X	open(Passwd, $etc_passwd) ||
X		warn "$0: Can't open $etc_passwd: $!\n";
X	}
else {
X	open(Passwd, "$'GET_PASSWD|") ||
X		warn "$0: Can't open $etc_passwd: $!\n";
X	}
&chk_passwd_file_format('Passwd');
close Passwd;
X
# check ypcat passwd for potential problems... (same checks)
if (-s $'YPCAT && -x _) {
X    open(YPasswd, "$'YPCAT passwd 2>/dev/null |")
X	|| die "$0: Can't popen $'YPCAT: $!\n";
X    &chk_passwd_file_format('YPasswd');
X    close YPasswd;
}
X  
sub chk_passwd_file_format {
X    local($file) = @_;
X    local($W) = "Warning!  $file file,";
X    undef %users;
X  
X    while (<$file>) {
X	# should really check for correct YP syntax
X	next if /^[-+]/;    # skipping YP lines for now
X
X	print "$W line $., is blank\n", next if /^\s*$/;
X
X	# make code a little more readable .. use names.. 
X	($user,$pass,$uid,$gid,$gcos,$home,$shell) = split(?:?);
X	$users{$user}++;    # keep track of dups
X	print "$W line $., does not have 7 fields:\n\t$_" if (@_ != 7);
X	print "$W line $., nonalphanumeric username:\n\t$_"
X	    if $user !~ /^[A-Za-z0-9]+$/;
X	print "$W line $., numeric username:\n\t$_"
X	    if $user =~ /^\d+$/;
X	print "$W line $., login name > 8 characters:\n\t$_"
X	    if ( ! $OVER_8 && length($user) > 8);
X	print "$W line $., no password:\n\t$_" unless $pass;
X	print "$W line $., invalid password field for C2:\n\t$_"
X	    if ($C2 && $pass =~ /^##/ && "##$user" ne $pass);
X	if ($uid !~ /^\d+$/) {
X	    if ($uid < 0) {
X		print "$W line $., negative user id (uid):\n\t$_";
X	    } else {
X		print "$W line $., nonnumeric user id (uid):\n\t$_";
X	    }
X	}
X	# what about checks for certain ranges of UIDs .. -composer
X	print "$W line $., user $user has uid == 0 and is not root\n\t$_"
X	    if $uid == 0 && $user ne "root";
X	print "$W line $., nonnumeric group id (gid):\n\t$_"
X	    unless $gid =~ /^\d+$/;
X	print "$W line $., invalid home directory:\n\t$_"
X	    unless $home =~ m:^/:;
X
X    }
X    # find duplicate usernames
X    # not the best way, but it works ...
X    $dup_warned = 0;
X    for (sort keys %users) {
X	(print "Warning!  Duplicate username(s) found in $file:\n"),
X	    $dup_warned++ if !$dup_warned && $users{$_} > 1;
X	print "$_ " if $users{$_} > 1;
X    }
X    print "\n" if $dup_warned;
}
X  
1;
# end of passwd.chk file
SHAR_EOF
chmod 0700 beta/passwd.chk ||
echo 'restore of beta/passwd.chk failed'
Wc_c="`wc -c < 'beta/passwd.chk'`"
test 4773 -eq "$Wc_c" ||
	echo 'beta/passwd.chk: original size 4773, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/pathconf.pl ==============
if test -f 'beta/pathconf.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/pathconf.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/pathconf.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/pathconf.pl' &&
$YPCAT = '/usr/bin/ypcat';
$STRINGS = '/usr/ucb/strings';
$TFTP = '/usr/ucb/tftp';
$UUDECODE = '/usr/bin/uudecode';
$CMP = '/bin/cmp';
$LS = '/bin/ls';
X
# end of perl needed programs
X
$AWK = '/bin/awk';
$CAT = '/bin/cat';
$CC = '/bin/cc';
$CHMOD = '/bin/chmod';
$COMM = '/usr/bin/comm';
$CP = '/bin/cp';
$DATE = '/bin/date';
$DIFF = '/bin/diff';
$ECHO = '/bin/echo';
$EGREP = '/usr/bin/egrep';
$EXPR = '/bin/expr';
$FIND = '/usr/bin/find';
$GREP = '/bin/grep';
$MAIL = '/bin/mail';
$MKDIR = '/bin/mkdir';
$MV = '/bin/mv';
$RM = '/bin/rm';
$SED = '/bin/sed';
$SH = '/bin/sh';
$SORT = '/usr/bin/sort';
$TEST = '/bin/test';
$TOUCH = '/usr/bin/touch';
$UNIQ = '/usr/bin/uniq';
X
1;
SHAR_EOF
chmod 0700 beta/pathconf.pl ||
echo 'restore of beta/pathconf.pl failed'
Wc_c="`wc -c < 'beta/pathconf.pl'`"
test 677 -eq "$Wc_c" ||
	echo 'beta/pathconf.pl: original size 677, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/pathconf.sh ==============
if test -f 'beta/pathconf.sh' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/pathconf.sh (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/pathconf.sh (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/pathconf.sh' &&
YPCAT = '/usr/bin/ypcat';
STRINGS = '/usr/ucb/strings';
TFTP = '/usr/ucb/tftp';
UUDECODE = '/usr/bin/uudecode';
X
# end of perl needed programs
X
AWK = '/bin/awk';
CAT = '/bin/cat';
CC = '/bin/cc';
CHMOD = '/bin/chmod';
CMP = '/bin/cmp';
COMM = '/usr/bin/comm';
CP = '/bin/cp';
DATE = '/bin/date';
DIFF = '/bin/diff';
ECHO = '/bin/echo';
EGREP = '/usr/bin/egrep';
EXPR = '/bin/expr';
FIND = '/usr/bin/find';
GREP = '/bin/grep';
LS = '/bin/ls';
MAIL = '/bin/mail';
MKDIR = '/bin/mkdir';
MV = '/bin/mv';
RM = '/bin/rm';
SED = '/bin/sed';
SH = '/bin/sh';
SORT = '/usr/bin/sort';
TEST = '/bin/test';
TOUCH = '/usr/bin/touch';
UNIQ = '/usr/bin/uniq';
SHAR_EOF
chmod 0700 beta/pathconf.sh ||
echo 'restore of beta/pathconf.sh failed'
Wc_c="`wc -c < 'beta/pathconf.sh'`"
test 644 -eq "$Wc_c" ||
	echo 'beta/pathconf.sh: original size 644, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/rc.chk ==============
if test -f 'beta/rc.chk' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/rc.chk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/rc.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/rc.chk' &&
#!/bin/sh -- need to mention perl here to avoid recursion
'true' || eval 'exec perl -S $0 $argv:q';
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X        if 0;
X
#
#  Usage: rc.chk
#
#  This checks pathnames and files inside the shell script files /etc/rc*
# for writability.  The commands inside the files /etc/rc* are executed when
# the machine is booted, so are of special interest.
#
# Made easy by chk_strings :-)
#
# Name: Martin Foord	Username: maf  Date: Thu Jan 17 15:11:09 EST 1991 
# Email: maf%dbsm.oz.au at munnari.oz.au
#
X
require 'chk_strings.pl';
X
# probably don't need to, but might want to do &'glob("/etc/rc*") instead.. ;-)
@all_rc_files = ("/etc/rc*", "/etc/*rc", "/etc/rc*.d/*",
X		 "/etc/shutdown.d/*", "/etc/inittab");
X
for $file (@all_rc_files) {
X	while (<${file}>) {
X		if (-r $_) {
X			&chk_strings($_);
X			}
X		}
X	}
X
1;
SHAR_EOF
chmod 0700 beta/rc.chk ||
echo 'restore of beta/rc.chk failed'
Wc_c="`wc -c < 'beta/rc.chk'`"
test 897 -eq "$Wc_c" ||
	echo 'beta/rc.chk: original size 897, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/reconfig.pl ==============
if test -f 'beta/reconfig.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/reconfig.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/reconfig.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/reconfig.pl' &&
#!/bin/sh  # need to mention perl here to avoid recursion
# NOTE:
#   If you know where perl is and your system groks #!, put its
# pathname at the top to make this a tad faster.
#
# the following magic is from the perl man page
# and should work to get us to run with perl 
# even if invoked as an sh or csh or foosh script.
# notice we don't use full path cause we don't
# know where the user has perl on their system.
#
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
& eval 'exec perl -S $0 $argv:q'
X    if $running_under_some_stupid_shell_instead_of_perl;
X
#  Target shell scripts in question:
$COPS_CONFIG="pathconf.pl";
X
#  Potential directories to find commands:
@all_dirs=("/bin",
X	   "/usr/bin",
X	   "/usr/ucb",
X	   "/usr/local/bin",  # scary
X	   "/usr/bsd");
X
# uncomment next line if you want your own current path used instead
#
# @all_dirs = split(/:/, $ENV{'PATH'});
X
#  Target commands in question, sans those checked above:
@all_commands= ("cc", "awk", "cat",
X		"chmod", "cmp", "comm", "cp",
X		"date", "diff", "echo", "egrep", "expr",
X		"find", "grep", "ls", "mail",
X		"mkdir", "mv", "rm", "sed",
X		"sh", "sort", "test", "tftp", "touch",
X		"uudecode", "uniq", "ypcat");
X
@want{@all_commands} = ();
X
%exceptions=   ('strings', 'chk_strings',
X                'tftp', 'misc.chk',
X		'cmp', 'ftp.chk',
X                'uudecode', 'misc.chk');
X
# grab the current values:
open COPS_CONFIG || die "Can't open $COPS_CONFIG: $!\n";
X
$new = "$COPS_CONFIG.$$";
open(NEW_CONFIG, ">$new") || die "Can't open $new: $!\n";
X
while (<COPS_CONFIG>) {
X    unless (/\$(\w+)\s*=\s*(['"])(\S*)\2/) {
X	print NEW_CONFIG;
X	next;
X    } 
X    ($cap_command, $path) = ($1, $3);
X    ($command = $cap_command) =~ tr/A-Z/a-z/;
X    unless (($newpath = &getpath($command)) || $command =~ /^yp/) {
X	warn "Warning!  no path for $command!\n";
X	warn "          $exceptions{$command} will not work as planned!\n"
X		     if $exceptions{$command};
X	$errors++;
X    } else {
X	delete $want{$command};
X    } 
X    print "old $path now in $newpath\n" if $newpath ne $path;
X    print NEW_CONFIG "\$$cap_command = '$newpath';\n";
X
}
X
for (sort keys %want) {
X    delete $want{$_} if $path = &getpath($_);
X    tr/a-z/A-Z/;
X    print NEW_CONFIG '$', $_, " = '", $path, "';\n";
} 
X
close(COPS_CONFIG) || die "can't close $COPS_CONFIG: $!\n";
close(NEW_CONFIG) || die "can't close $new: $!\n";
X
if (@missing = keys %want) {
X     warn "Warning!   missing paths for @missing!\n";
X     warn "The shell version may not work right!\n";
} 
X
X
if ($errors) {
X    print STDERR "Not all paths were found: write anyway? ";
X    # what about removing NEW_CONFIG, $new ??
X    exit 1 if <STDIN> !~ /^\s*y/i;
X    print STDERR "Ok, but this might not be right...\n";
} 
X
$old = "$COPS_CONFIG.old";
X
rename($COPS_CONFIG, $old)
X    || die "can't rename $COPS_CONFIG to $old: $!\n";
X
rename($new, $COPS_CONFIG)
X    || die "can't rename $new to $COPS_CONFIG: $!\n";
X
X
open COPS_CONFIG || die "can't re-open $COPS_CONFIG: $!\n";
($SH_CONF = $COPS_CONFIG) =~ s/\.pl$/.sh/;
open (SH_CONF, ">$SH_CONF") || die "can't create $SH_CONF: $!\n";
X
while (<COPS_CONFIG>) {
X    s/^\$//;
X    print SH_CONF;
} 
close SH_CONF || die "can't close $SH_CONF: $!\n";
X
Xexit 0;
X
#############
X
sub getpath {
X    local($cmd) = @_;
X    local($path);
X
X    for (@all_dirs) {
X	return $path if -x ($path = "$_/$cmd");
X    } 
X    '';
} 
SHAR_EOF
chmod 0700 beta/reconfig.pl ||
echo 'restore of beta/reconfig.pl failed'
Wc_c="`wc -c < 'beta/reconfig.pl'`"
test 3358 -eq "$Wc_c" ||
	echo 'beta/reconfig.pl: original size 3358, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/root.chk ==============
if test -f 'beta/root.chk' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/root.chk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/root.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/root.chk' &&
#!/bin/sh -- need to mention perl here to avoid recursion
'true' || eval 'exec perl -S $0 $argv:q';
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X        if 0;
X
#
#  Usage: root.chk
#
#  This script checks pathnames inside root's startup files for 
# writability, improper umask settings (world writable), non-root
# entries in /.rhosts, writable binaries in root's path,
# and to ensure that root is in /etc/ftpuser.
#
# Also check for a single "+" in /etc/hosts.equiv (world is trusted),
# and that /bin, /etc and certain key files are root owned, so that you
# can't, say, rcp from a host.equived machine and blow over the password
# file... this may or may not be bad, decide for yourself.
# Startup files are /.login /.cshrc /.profile
#
#  Mechanism:  These files contain paths and filenames that are stripped
# out using "grep".  These strings are then processed by the "is_able"
# program to see if they are world writable.  Strings of the form:
#
#	path=(/bin /usr/bin .)
#		and
#	PATH=/bin:/usr/bin:.:
#
# are checked  to ensure that "." is not in the path.  All
# results are echoed to standard output.  In addition, some effort was
# put into parsing out paths with multiple lines; e.g. ending in "\",
# and continuing on the next line.  Also, all executable files and 
# directories in there are checked for writability as well.
#
#  For umask stuff, simply grep for umask in startup files, and check
# umask value.  For /etc/ftpuser, simple grep to check if root is in
# the file.  For /etc/hosts.equiv, just check to see if "+" is alone
# on a line by awking it.
#
X
# rewritten in perl by tchrist at convex.com
# 
X
# root startup/important files
X
require 'file_owner.pl';
require 'fgrep.pl';
require 'suckline.pl';
require 'is_able.pl';
require 'chk_strings.pl';
require 'glob.pl';
X
package root_chk;
X
# use -a true if you care about non-executables
# in root's path
X
$ARGV[0] eq '-a' && ($all_files++, shift);
X
die "usage: root.chk [-a]\n" if @ARGV;
X
$W = 'Warning! ';
X
$cshrc	= '/.cshrc';
$profile= '/.profile';
$rhosts = '/.rhosts';
X
$| = 1;
X
@big_files= ('/.login', '/.cshrc', '/.profile', '/.logout' );
X
# root should own *at least* these, + $big_files; you can check for all files
# in /bin & /etc, or just the directories (the default.)
# root_files="/bin /bin/* /etc /etc/* $big_files $rhosts"
@root_files= ('/bin','/etc', at big_files,$rhosts,'/etc/passwd','/etc/group');
X
# misc important stuff
$ftp='/etc/ftpusers';
$equiv='/etc/hosts.equiv';
X
#   should't have anyone but root owning /bin or /etc files/directories
# In case some of the critical files don't exist (/.rhost), toss away error
# messages
X
if (@bad_files = grep (-e && &'Owner($_), @root_files)) {
X    print "$W  Root does not own the following file(s):\n";
X    print "\t at bad_files\n";
} 
X
local($chk_strings'recurse) = 1 unless defined $chk_strings'recurse;
X
for $file (@big_files) {
X    open file || next;
X
X    &'chk_strings($file);
X
X    # check for group or other writable umask
X    while (<file>) {
X	next if /^\s*#/;
X	next unless /umask\s*(\d+)/;
X	next unless ~oct($1) & 022;
X	print "$W root's umask set to $1 in $file\n";
X    } 
} 
X
print "$W $ftp exists and root is not in it\n" 
X    if -e $ftp && !&'fgrep($ftp,'root');
X
print "$W A \"+\" entry exists in $equiv!\n" if &'fgrep($equiv, '^\+$');
X
if (open rhosts) {
X    while (<rhosts>) {
X	next unless /\S+\s+(\S+)/ && $1 ne 'root';
X	print "$W Non-root entry in $rhosts! $1\n";
X    }
} 
close(rhosts);
X
undef @rootpath;
X
# checking paths...
#
# Get the root paths from $csh.
X
if (open(CSHRC, $cshrc)) {
X    $path = '';
X    while (<CSHRC>) {
X	next if /^\s*#/;
X	chop unless /\\$/;
X	if (/set\s+path\s*=/) {
X	    $_ = &'suckline($cshrc, $_);
X	    s/.*set\s+path\s*=\s*//;
X	    s/\((.*)\)/$1/;
X	    s/#.*/./;
X	    @tmppath = grep($_ ne '', split(' '));
X	    for (@tmppath) { $whence{$_} .= " " . $cshrc; } 
X	    push(@rootpath, @tmppath);
X	} 
X    } 
X    close(CSHRC);
} 
X
if (open login) {
X    $path = '';
X    while (<cshrc>) {
X	next if /^\s*#/;
X	chop unless /\\$/;
X	if (/set\s+path\s*=/) {
X	    $_ = &'suckline('login', $_);
X	    s/.*set\s+path\s*=\s*//;
X	    s/\((.*)\)/$1/;
X	    s/#.*/./;
X	    @tmppath = grep($_ ne '', split(' '));
X	    for (@tmppath) { $whence{$_} .= " " . $login; } 
X	    push(@rootpath, @tmppath);
X	} 
X    } 
X    close(login);
}
X
if (open profile) {
X    $path = '';
X    while (<profile>) {
X	next if /^\s*#/;
X	chop unless /\\$/;
X	if (/PATH=/) {
X	    $_ = &'suckline('profile', $_);
X	    s/.*PATH=//;
X	    s/#.*//;
X	    @tmppath = split(/:/);
X	    for (@tmppath) { $whence{$_} .= " " . $profile; } 
X	    push(@rootpath, @tmppath);
X	} 
X    } 
X    close(profile);
} 
X
for (keys %whence) {
X    $whence{$_} =~ s/^ //;
X    $whence{$_} =~ s/ / and /g;
} 
X
undef %seen;
grep($seen{$_}++, @rootpath);
X
$is_able'silent = 1;
for (keys %seen) {
X    if (!-e && $_ ne ".") {
X	print "$W path component $_ in $whence{$_} doesn't exist!\n";
X	next;
X    } 
X
X    if (/^\.?$/) {  # null -> dot
X	print "$W \".\" (or current directory) is in root's path in $whence{$_}!\n";
X    } elsif (&'is_writable($_)) {
X	print "$W Directory $_ is _World_ writable and in root's path in $whence{$_}!\n";
X	next;
X    }
X
X    foreach $file (&'glob("$_/*")) {
X	# can't just check -x here, as that depends on current user
X	$is_executable = -f $file && (&'Mode($file) & 0111);
X	if (($all_files || $is_executable) && 
X		    ($how = &'is_writable($file, 'w', 'w'))) {
X	    print "$W _World_ $how ",
X		    $is_executable ? 'executable' : 'file',
X		" $file in root path component $_ from $whence{$_}!\n";
X	} 
X    }
} 
X
$is_able'silent = 0;
X
1;
SHAR_EOF
chmod 0700 beta/root.chk ||
echo 'restore of beta/root.chk failed'
Wc_c="`wc -c < 'beta/root.chk'`"
test 5609 -eq "$Wc_c" ||
	echo 'beta/root.chk: original size 5609, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/rules.pl ==============
if test -f 'beta/rules.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/rules.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/rules.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/rules.pl' &&
sub apply_rules {
X    local($op, $value, @plan) = @_;
X
X    printf("eval($op $value): %s\n", &ascii_plan(@plan)) if $opt_d;
X
X    #
X    # apply UID attack rules...
X    #
X    if ($op eq "u") {
X	#
X	# If we can replace /etc/passwd or /usr/lib/aliases, we can grant 
X	# any uid. 
X	#
X	&addto("r", "/etc/passwd", @plan);
X        &addto("r", "/usr/lib/aliases", @plan);
X        &addto("r", "/etc/aliases", @plan);
X
X	#
X	# Check CF's for all usernames with this uid.
X	#
uname_loop:
X    foreach $uname (split(/ /, $uid2names{$value})) {
X	    $home = $uname2dir{$uname};
X
X	    next uname_loop unless $home;
X
X	    if ($home eq "/") {
X		$home = "";
X	    }
X	    &addto("r", "$home/.rhosts", @plan);
X	    &addto("r", "$home/.login", @plan);
X	    &addto("r", "$home/.logout", @plan);
X	    &addto("r", "$home/.cshrc", @plan);
X	    &addto("r", "$home/.profile", @plan);
X	}
X
X	#
X	# Controlling files for root...
X	#
X	@rootlist = ( 
X		"/etc/rc", "/etc/rc.boot", "/etc/rc.single", 
X		"/etc/rc.config", "/etc/rc.local", "/usr/lib/crontab",
X		"/usr/spool/cron/crontabs",
X		);
X
X	if ($value eq "0") {
X	    foreach $file (@rootlist) {
X		    &addto("r", $file, @plan);
X	    }
X	    # Experimental!
X	    # you can remove this if desired - tjt
X	    #do "rc.prog";
X	}
X
X	#
X	# Other CFs for non-root folks...
X	#
X	if ($value ne "0") {
X	    &addto("r", "/etc/hosts.equiv", @plan);
X	    if (-s "/etc/hosts.equiv") {
X		&addto("r", "/etc/hosts", @plan);
X	    }
X	}
X
X    #
X    # Plans for attacking GIDs...
X    #
X    } elsif ($op eq "g") {	# apply gid attack rules
X
X	#
X	# If we can replace /etc/group we can become any group
X	#				  
X        &addto("r", "/etc/group", @plan);
X
X	#
X	# If we can grant any member of a group we can grant that group
X	#
member_loop:
X	foreach $uname (split(/ /, $gid2members{$value})) {
X	    if (! defined($uname2uid{$uname})) {
X		printf(stderr "group '%s' member '%s' doesn't exist.\n",
X			$value,
X			$uname);
X		next member_loop;
X	    }
X
X	    &addto("u", $uname2uid{$uname}, @plan);
X	}
X
X    #
X    # Plans for attacking files...
X    #
X
X    } elsif ($op eq "r" || $op eq "w") {
X
X        ($owner, $group, $other) = &filewriters($value);
X
X	&addto("u", $owner, @plan) if ($owner ne "");
X	&addto("g", $group, @plan) if ($group ne "");
X	&addto("u", "-1", @plan) if ($other);
X
X	#
X	# If the goal is to replace the file, check the parent directory...
X	#
X	if ($op eq "r") {
X	    $parent = $value;
X	    $parent =~ s#/[^/]*$##;     # strip last / and remaining stuff
X
X	    if ($parent eq "") {
X		$parent = "/";
X	    }
X
X	    if ($parent ne $value) {
X		&addto("r", $parent, @plan);
X	    }
X	}
X
X    } else {			# wow, bad $type of object!
X	printf(stderr "kuang: bad op in apply_rules!\n");
X	printf(stderr "op '%s' value '%s' plan '%s'\n",
X		$op,
X		$value,
X		&ascii_plan(@plan));
X	exit(1);
X    }
}
X
1;
X
SHAR_EOF
chmod 0600 beta/rules.pl ||
echo 'restore of beta/rules.pl failed'
Wc_c="`wc -c < 'beta/rules.pl'`"
test 2768 -eq "$Wc_c" ||
	echo 'beta/rules.pl: original size 2768, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/stat.pl ==============
if test -f 'beta/stat.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/stat.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/stat.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/stat.pl' &&
;# $Header: stat.pl,v 3.0.1.1 90/08/09 04:01:34 lwall Locked $
;# Usage:
;#	require 'stat.pl';
;#	@ary = stat(foo);
;#	$st_dev = @ary[$ST_DEV];
;#
$ST_DEV =	0 + $[;
$ST_INO =	1 + $[;
$ST_MODE =	2 + $[;
$ST_NLINK =	3 + $[;
$ST_UID =	4 + $[;
$ST_GID =	5 + $[;
$ST_RDEV =	6 + $[;
$ST_SIZE =	7 + $[;
$ST_ATIME =	8 + $[;
$ST_MTIME =	9 + $[;
$ST_CTIME =	10 + $[;
$ST_BLKSIZE =	11 + $[;
$ST_BLOCKS =	12 + $[;
X
;# Usage:
;#	require 'stat.pl';
;#	do Stat('foo');		# sets st_* as a side effect
;#
sub Stat {
X    ($st_dev,$st_ino,$st_mode,$st_nlink,$st_uid,$st_gid,$st_rdev,$st_size,
X	$st_atime,$st_mtime,$st_ctime,$st_blksize,$st_blocks) = stat(shift(@_));
}
X
1;
SHAR_EOF
chmod 0700 beta/stat.pl ||
echo 'restore of beta/stat.pl failed'
Wc_c="`wc -c < 'beta/stat.pl'`"
test 653 -eq "$Wc_c" ||
	echo 'beta/stat.pl: original size 653, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/suckline.pl ==============
if test -f 'beta/suckline.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/suckline.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/suckline.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/suckline.pl' &&
#
#  As title implies... :-)
#
sub main'suckline {
X    local($file, $_) = @_;
#   local($package) = caller;
X
#   $file =~ s/^([^']+)$/$package'$1/; 
X    {
X	if (s/\\\n?$//) {
X	    $_ .= <$file>;
X	    redo;
X	}
X    } 
X    $_;
}
X
1;
SHAR_EOF
chmod 0700 beta/suckline.pl ||
echo 'restore of beta/suckline.pl failed'
Wc_c="`wc -c < 'beta/suckline.pl'`"
test 229 -eq "$Wc_c" ||
	echo 'beta/suckline.pl: original size 229, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/suid.chk ==============
if test -f 'beta/suid.chk' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/suid.chk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/suid.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/suid.chk' &&
#!/bin/sh -- need to mention perl here to avoid recursion
'true' || eval 'exec perl -S $0 $argv:q';
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X        if 0;
X
#
#  Usage: suid.chk [-n] [-s secure_dir] [search_starting_directory]
#
#   Shell script intended to be run periodically by cron in order
#   to spot changes in files with the suid or sgid bits set.
#
#	suid.chk	840919		Prentiss Riddle
#
#     This changes into the $SECURE directory first, then 
#   uses find(1) to search the directories in $SEARCH for all
#   files with the 4000 or 2000 permission bits set.  $STOP is a file
#   containing "ls -gildsa" output for known setuid or setgid programs.
#   Any additions or changes to this list represent potential security
#   problems, so they are reported.
#
#  Modified 8/15/89, Dan Farmer:
#	Just changed the program/doc names and some of the temp
#  files to make it fit in with the rest of the programs....
#  Modified 12/26/90, df
#       Now flags SUID shell scripts and world writeable SUID files, too.
#
#  Rewritten in perl, 1/17/91, df
#  Major hacks by tchrist 5/14/91
#
X
require "hostname.pl";
require "is_able.pl";
require "file_owner.pl";
require "pathconf.pl";
require "chk_strings.pl";
require "pass.cache.pl";
package suid_chk; # name space protection
$debug=0;
X
#
# Getopts stuff
$usage = "Usage: $0 [-n] [-s secure_dir] [starting_directory]\n";
require 'getopts.pl';
# Process the command args; Either specify verbose or an alternate config file:
die $usage unless &`Getopts('ns:');
X
$suid_dir = $'SECURE || '.';
if (defined($opt_s)) { $suid_dir = $opt_s; }
X
# Do NFS stuff?  Yes unless opt:
if (defined($opt_n)) { $skip_nfs = $opt_n; }
else { $skip_nfs = 0; }
X
$STOP="$suid_dir/suid.stop";
$TEMPOLD="$suid_dir/fsold$$";
$TEMPCUR="$suid_dir/fscur$$";
$TEMPNEW="$suid_dir/fsnew$$";
$TEMPGON="$suid_dir/fsgon$$";
$TEMPM="$suid_dir/fsm$$";
X
if (@ARGV > 1) { die $usage; }
elsif (@ARGV == 1) { $start_dir = shift; }
X
# these may be terribly rash assumptions....
$start_dir="/" unless defined $start_dir;
$find_can_ls = 1 unless defined $find_can_ls;
X
$NONFS = '-type d \( -fstype nfs -prune \) -o' if $skip_nfs;  
$find_ls = $find_can_ls ? '-ls' : "-exec $'LS -gilds {} \\;";
X
die "Error -- Security directory $suid_dir doesn't exist\n"
X    unless -d $suid_dir;
unless (-d $suid_dir) {
X    mkdir($suid_dir, 0700) || die "can't mkdir $suid_dir: $!";
} 
chdir $suid_dir || die "can't chdir $suid_dir: $!\n";
X
# find the setuid programs and sort
&run("$'FIND $start_dir $NONFS -type f \\( -perm -4000 -o -perm -2000 \\) $find_ls | $'SORT > $TEMPCUR");
X
# compare with the sorted stop list
# create stop file if needed
if (! -f $STOP) { open(S,">$STOP"); close(S); }
X
&run("$'SORT <$STOP >$TEMPOLD");
&run("$'COMM -13 $TEMPOLD $TEMPCUR | $'SORT +8 >$TEMPNEW");
&run("$'COMM -23 $TEMPOLD $TEMPCUR | $'SORT +8 >$TEMPGON");
X
local($is_able'silent) = 1;
local($chk_strings'recurse) = 0 unless defined $chk_strings'recurse;
X
# report changes
if (-s $TEMPNEW || -s $TEMPGON) {
X    if (-s $TEMPNEW) {
X	open TEMPNEW || die "Can't open $TEMPNEW: $!\n";
X	while (<TEMPNEW>) {
X	    ($file) = /(\S+)$/;
X
X	    # don't want SUID files to be world writable!
X	    # although *reasonable* systems clear the bit on write
X	    print "Warning!  SUID file $file is _World_ writable!\n" 
X		if &'is_able ($file, "w", "w"); 
X		    
X	    if (-r $file && -f _ && -T $file) {
X		print "Warning!  ", &'Owner($file) ? '' : 'ROOT-owned ', 
X		    "SUID file $file is a non-binary, executable file!\n";
X	    }
X
X	    &'chk_strings($file) if -r _;
X	}
X	close TEMPNEW;
X    }
X
X    if (-s $TEMPNEW) {
X	open TEMPNEW || die "Can't reopen $TEMPNEW: $!\n";
X	print "\nThese files are newly setuid/setgid:\n\n";
X	print while <TEMPNEW>;
X    }
X
X    if (-s $TEMPGON) {
X	open TEMPGON || die "Can't reopen $TEMPGON: $!\n";
X	print "\nThese files are no longer setuid/setgid:\n\n";
X	print while <TEMPGON>;
X    }
X
}
X
unlink $TEMPOLD, $TEMPCUR, $TEMPNEW, $TEMPGON;
X
sub run {
X    print "running: $_[0]\n" if $debug;
X    system $_[0];
X    warn "command $_[0] returned $?" if $?;
} 
X
#  end it all....
X
1;
SHAR_EOF
chmod 0700 beta/suid.chk ||
echo 'restore of beta/suid.chk failed'
Wc_c="`wc -c < 'beta/suid.chk'`"
test 4128 -eq "$Wc_c" ||
	echo 'beta/suid.chk: original size 4128, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/suid.stop ==============
if test -f 'beta/suid.stop' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/suid.stop (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/suid.stop (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/suid.stop' &&
SHAR_EOF
chmod 0700 beta/suid.stop ||
echo 'restore of beta/suid.stop failed'
Wc_c="`wc -c < 'beta/suid.stop'`"
test 0 -eq "$Wc_c" ||
	echo 'beta/suid.stop: original size 0, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/user.chk ==============
if test -f 'beta/user.chk' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/user.chk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/user.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/user.chk' &&
#!/bin/sh -- need to mention perl here to avoid recursion
'true' || eval 'exec perl -S $0 $argv:q';
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X        if 0;
X
#
#  This combines user.chk and home.chk.  It searches for home directories
# and various user startup files for world writability, as well as flagging
# any .rhosts and .netrc files that are readable.  You can change the
# files checked by changing @ftable and @readables, respectively.
# 
X
#
# check for writable files in all user's homes
#
require "pass.cache.pl";
require "is_able.pl";
X
# files checked for:
@ftable = ("rhosts", "profile", "login", "logout", "cshrc",
X	   "bashrc", "bash_profile", "inputrc", "screenrc",
X	   "kshrc", "tcshrc", "netrc", "forward", "dbxinit",
X	   "distfile", "exrc", "emacsrc", "remote", "mh_profile",
X	   "xinitrc", "xsession", "Xdefaults", "Xresources", "rninit");
X
@readables = ("netrc", "rhosts");
X
local(%done);
X
# what's the point of doing a keys and using $i ??
# why not just do "for $dir (values %uname2dir) {" ????
for $i (keys %uname2dir) {
X    $dir = $uname2dir{$i};
X    #   I don't want to hear about every file in their home dir, if 
X    # is WW, but still need to check the .netrc file for readability...
X    next unless $dir;
X    next if $done{$dir}++;
X    if (-e $dir) { 
X        if (&is_able($dir, "w", "w")) {
X            for $r (@readables) {
X                if (-s "$dir/.$r") {
X                    &is_able("$dir/.$r", "w", "r");
X		}
X	    }
X            next;
X	}
X        for $file (@ftable) {
X            $foo_file = $dir . "/.$file";
X            if (-e $foo_file) {
X                &is_able($foo_file, "w", "w");
X                for $r (@readables) {
X                    if ($file eq $r && -s $foo_file) {
X                        &is_able($foo_file, "w", "r");
X		    }
X		}
X	    }
X	}
X    }
}
X
1;
SHAR_EOF
chmod 0700 beta/user.chk ||
echo 'restore of beta/user.chk failed'
Wc_c="`wc -c < 'beta/user.chk'`"
test 1870 -eq "$Wc_c" ||
	echo 'beta/user.chk: original size 1870, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= beta/yagrip.pl ==============
if test -f 'beta/yagrip.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping beta/yagrip.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting beta/yagrip.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'beta/yagrip.pl' &&
#Yet Another Getopt Routine In Perl
# jgreely at cis.ohio-state.edu, 89/11/1
#usage:
#&getopt("f:bar") ||
#	die &usage("script","f:bar","oo","[files ...]");
#
sub getopt {
X	local($_,$flag,$opt,$f,$r, at temp) = @_;
X	@temp = split(/(.):/);
X	while ($#temp >= $[) {
X		$flag .= shift(@temp);
X		$opt .= shift(@temp);
X	}
X	while ($_ = $ARGV[0], /^-(.)(.*)/ && shift(@ARGV)) {
X		($f,$r) = ($1,$2);
X		last if $f eq '-';
X		if (index($flag,$f) >= $[) {
X			eval "\$opt_$f++;";
X			$r =~ /^(.)(.*)/,redo if $r ne '';
X		}elsif (index($opt,$f) >= $[) {
X			$r = $r eq '' ? shift(@ARGV) : $r;
X			eval "\$opt_$f = \$r;";
X		}else{
X			print STDERR "Unrecognized switch \"-$f\".\n";
X			return 0;
X		}
X	}
X	return 1;
}
X
#usage: usage:
# &usage(progname,arglist, at names, at last);
#ex:
# &usage("script","f:bar","oo","[file ...]");
#would return
# "usage: script [-f oo] [-bar] [file ...]"
#
sub usage {
X	local($prog,$_, at list) = @_;
X	local($string,$flag, at string, at temp, at last) = ();
X	@temp = split(/(.):/);
X	push(@string,"usage:",$prog);
X	while ($#temp >= $[) {
X		if (($flag = shift(@temp)) ne '') {
X			push(@string,"[-$flag]");
X		}
X		if (($flag = shift(@temp)) ne '') {
X			push(@string,sprintf("[-%s %s]",$flag,shift(@list)));
X		}
X	}
X	push(@string, at list) if $#list >= $[;
X	return join(' ', at string) . "\n";
}
1;
SHAR_EOF
chmod 0600 beta/yagrip.pl ||
echo 'restore of beta/yagrip.pl failed'
Wc_c="`wc -c < 'beta/yagrip.pl'`"
test 1274 -eq "$Wc_c" ||
	echo 'beta/yagrip.pl: original size 1274, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
rm -f _shar_seq_.tmp
echo You have unpacked the last part
exit 0



More information about the Alt.sources mailing list