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