|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 55810 (0xda02)
Types: TextFile
Names: »cops.16«
└─⟦4f9d7c866⟧ Bits:30007245 EUUGD6: Sikkerheds distributionen
└─⟦this⟧ »./cops/1.04/shars/cops.16«
#!/bin/sh
# this is p4.shar.16 (part 16 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file cops_104/perl/cron.chk continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 16; 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 cops_104/perl/cron.chk'
else
echo 'x - continuing file cops_104/perl/cron.chk'
sed 's/^X//' << 'SHAR_EOF' >> 'cops_104/perl/cron.chk' &&
Xrequire 'chk_strings.pl';
Xrequire 'pathconf.pl';
X
X# should also add args to override default crontab locations
Xdie "Usage: $0 [-rd]\n" unless &Getopts('rd') && !@ARGV;
X
X$chk_strings'debug = $opt_d;
X$chk_strings'recurse = $opt_r;
X
Xpackage cron_chk;
X
X# Possible location of crontab file:
X$cron = "/usr/lib/crontab";
X# alternate reality locations of crontab file:
X@alt_cron = ("/usr/spool/cron/crontabs");
X
Xif ( ! -s $cron) {
X for (@alt_cron) {
X # are there ever multiple crontab directories?
X (@crons = &'glob("$_/*")), last if -d;
X }
X die "No crontabs?\n" if ! @crons;
X}
X@crons = ($cron) unless @crons;
X
X# ignore /tmp /dev/null and tty stuff
X# &'chk_strings ignores all of above
X# STILL NEED to ignore stuff after `>' ??
X# when we add @ignore stuff to &'chk_strings
X# @ignore stuff is in &'chk_strings now, do we want to ignore filenames
X# being redirected into .. might as well leave them, let the user decide.
X
X# finally, do the checking -- maybe for one, maybe for lots of cron-ites:
Xfor (@crons) {
X if (! -e) {
X warn "$0: $_: $!\n";
X next;
X }
X &'chk_strings($_);
X}
X
X1;
SHAR_EOF
echo 'File cops_104/perl/cron.chk is complete' &&
chmod 0700 cops_104/perl/cron.chk ||
echo 'restore of cops_104/perl/cron.chk failed'
Wc_c="`wc -c < 'cops_104/perl/cron.chk'`"
test 2199 -eq "$Wc_c" ||
echo 'cops_104/perl/cron.chk: original size 2199, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/dev.chk ==============
if test -f 'cops_104/perl/dev.chk' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/dev.chk (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/dev.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/dev.chk' &&
X#!/bin/sh -- need to mention perl here to avoid recursion
X'true' || eval 'exec perl -S $0 $argv:q';
Xeval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
X& eval 'exec /usr/bin/perl -S $0 $argv:q'
X if 0;
X
X#
X# dev.chk [-g]
X#
X# This shell script checks the permissions of all devs listed in the
X# file /etc/fstab (the "mount" command would be a preferable way of
X# getting the file system name, but the syntax of the output is variable
X# from machine to machine), and flags them if they are readable by using
X# the "is_readable" command. It also checks for unrestricted NFS
X# mountings. By default, dev_check will flag devs only if world readable
X# or writable. The -g option tells it to print out devs that are also
X# group readable/writable.
X# As an aside, the fact that NFS mounted dirs are world readable isn't
X# a big deal, but they shouldn't be world writable. So do two checks here,
X# instead of one.
X#
X# Two types of /etc/fstab formats I've seen so far:
X#
X# "old" --
X# spec:file:type:freq:passno:name:options
X# NFS are indicated by an "@"
X#
X# "new" --
X# fsname dir type opts freq passno
X# NFS are indicated by an ":"
X#
X# tchrist@convex.com
X#
X
Xrequire 'is_able.pl';
X
X$MTAB = '/etc/fstab' unless defined $MTAB;
X$EXPORTS = '/etc/exports' unless defined $EXPORTS;
X$TAB_STYLE = 'new' unless defined $TAB_STYLE; # or 'old'
X
X&usage if @ARGV > 1;
X
Xsub usage { die "Usage: $0 [-g]\n"; }
X
Xif (@ARGV == 1) {
X if ($ARGV[0] eq '-g') {
X $group++;
X } else {
X &usage;
X }
X}
X
X
Xopen MTAB || die "can't open $MTAB: $!\n";
X
Xwhile (<MTAB>) {
X next if /^#/;
X chop;
X if ($TAB_STYLE eq 'new') {
X ($dev, $fs) = split;
X next unless $fs;
X if ($dev =~ /:/) {
X push(@nfs_devs, $fs);
X } else {
X push(@local_devs, $dev);
X }
X } else {
X ($dev, $fs) = split(/:/);
X next unless $fs;
X if ($dev =~ /@/) {
X push(@nfs_devs, $fs);
X } else {
X push(@local_devs, $dev);
X }
X }
X
X}
X
Xif (open EXPORTS) {
X while (<EXPORTS>) {
X next if /^\s*#/;
X next if /\S\s+\S/;
X next if /^\s*$/;
X chop;
X print "Warning! NFS file system $_ exported with no restrictions.\n";
X }
X}
X
X# WARNING: we may hang if server down....
X#
Xfor (@nfs_devs, @local_devs) {
X &is_able($_, 'w', 'w');
X next unless $group;
X &is_able($_, 'g', 'w');
X}
X
Xfor (@local_devs) {
X &is_able($_, 'w', 'r');
X next unless $group;
X &is_able($_, 'g', 'r');
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/dev.chk ||
echo 'restore of cops_104/perl/dev.chk failed'
Wc_c="`wc -c < 'cops_104/perl/dev.chk'`"
test 2378 -eq "$Wc_c" ||
echo 'cops_104/perl/dev.chk: original size 2378, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/fgrep.pl ==============
if test -f 'cops_104/perl/fgrep.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/fgrep.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/fgrep.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/fgrep.pl' &&
X#
X# Just a quick perl fgrep...
X#
Xpackage fgrep;
X
Xsub main'fgrep {
X local($file, @exprs) = @_;
X local(@list);
X
X if (open file) {
X $code = "while (<file>) {\n\tchop;\n";
X for (@exprs) {
X $code .= "\tpush(\@list, \$_), next if m\201${_}\201;\n";
X }
X $code .= "}\n";
X warn "fgrep code is $code" if $debug;
X eval $code;
X warn "fgrep @exprs $file: $@\n" if $@;
X } elsif ($debug) {
X warn "main'fgrep: can't open $file: $!\n";
X }
X
X @list;
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/fgrep.pl ||
echo 'restore of cops_104/perl/fgrep.pl failed'
Wc_c="`wc -c < 'cops_104/perl/fgrep.pl'`"
test 463 -eq "$Wc_c" ||
echo 'cops_104/perl/fgrep.pl: original size 463, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/file_mode.pl ==============
if test -f 'cops_104/perl/file_mode.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/file_mode.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/file_mode.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/file_mode.pl' &&
X#
X# This retrieves a possibly cached mode on file.
X# If it returns "BOGUS", it means that the stat failed.
X#
X# tchrist@convex.com
X
Xpackage main;
Xrequire 'stat.pl';
X
Xpackage file_mode;
X
Xsub main'Mode {
X local($file) = @_;
X
X if (!defined $modes{$file}) {
X if (&'Stat($file)) {
X $modes{$file} = $'st_mode;
X } else {
X $modes{$file} = 'BOGUS';
X }
X }
X $modes{$file};
X}
SHAR_EOF
chmod 0700 cops_104/perl/file_mode.pl ||
echo 'restore of cops_104/perl/file_mode.pl failed'
Wc_c="`wc -c < 'cops_104/perl/file_mode.pl'`"
test 414 -eq "$Wc_c" ||
echo 'cops_104/perl/file_mode.pl: original size 414, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/file_owner.pl ==============
if test -f 'cops_104/perl/file_owner.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/file_owner.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/file_owner.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/file_owner.pl' &&
X#
X# This retrieves possibly cached owner of a file.
X# If it returns "BOGUS", it means that the stat failed.
X
Xpackage main;
Xrequire 'stat.pl';
X
Xpackage file_owner;
X
Xsub main'Owner {
X local($file) = @_;
X
X if (!defined $owners{$file}) {
X if (&'Stat($file)) {
X $owners{$file} = $'st_uid;
X } else {
X $owners{$file} = 'BOGUS';
X }
X }
X $owners{$file};
X}
SHAR_EOF
chmod 0700 cops_104/perl/file_owner.pl ||
echo 'restore of cops_104/perl/file_owner.pl failed'
Wc_c="`wc -c < 'cops_104/perl/file_owner.pl'`"
test 398 -eq "$Wc_c" ||
echo 'cops_104/perl/file_owner.pl: original size 398, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/ftp.chk ==============
if test -f 'cops_104/perl/ftp.chk' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/ftp.chk (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/ftp.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/ftp.chk' &&
X#!/bin/sh -- need to mention perl here to avoid recursion
X'true' || eval 'exec perl -S $0 $argv:q';
Xeval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
X& eval 'exec /usr/bin/perl -S $0 $argv:q'
X if 0;
X
X# Usage: ftp.chk
X#
X# This shell script checks to see if you've set up (mainly anonymous)
X# ftp correctly. There seems to be some different types of ftp's
X# around; for instance, some allow "chmod" -- and if the home dir is
X# owned by "ftp", you're toast. So I've tried to err on the side of
X# safety...
X#
X# See the man page for a more detailed description, here's what this
X# checks for:
X#
X# - User ftp exists in the password file.
X# - root (or all root equivalents) are in ftpusers file.
X# - Home directory for ftp should exist, and not be /
X# - The ~ftp/etc/{passwd|group} should not be the same as the real ones.
X# - Various critical files/directories should exist, and have correct
X# permissions and owners; variables "$primary" and "$secondary" can be set
X# to whomever you want owning the files:
X#
X# File/Dir Perms Owner Other
X# ========= ====== ====== ======
X# ~ftp non-w.w. root
X# or
X# ~ftp 555 ftp if no chmod command exists
X#
X# All of these are ftp owned iff no chmod exists...
X#
X# ~ftp/bin non-w.w. root/ftp
X# ~ftp/bin/ls 111 root/ftp
X# ~ftp/etc non-w.w. root/ftp
X# ~ftp/etc/passwd non-w.w. root/ftp 0 size or nonexistant
X# ~ftp/etc/group non-w.w. root/ftp 0 size or nonexistant
X# ~ftp/pub non-w.w. root/ftp
X# ~ftp/incoming world-writable root/ftp This can be set to "pub"
X# ~ftp/.rhosts non-w.w. root 0 size, is optional
X# ~ftp/* non-w.w. other dirs/files in ~ftp
X#
X#
X
Xrequire 'is_able.pl';
Xrequire 'file_mode.pl';
Xrequire 'glob.pl';
Xrequire 'fgrep.pl';
Xrequire 'pass.cache.pl';
Xrequire 'file_owner.pl';
Xrequire 'pathconf.pl';
X
X$CMP="/bin/cmp" unless defined $CMP;
X
Xpackage ftp;
X
X# Primary and secondary owners of the ftp files/dirs; if you *don't* have
X# chmod, you can probably change the secondary owner to "ftp". If you have
X# chmod in your ftp, definitely have secondary to some other account (root
X# is fine for this.)
X$primary = "root" unless defined $primary;
X$secondary = "ftp" unless defined $secondary;
X
X# some might have this as ftpd; is the account in /etc/passwd
X$ftpuid = "ftp";
X
X# system files
X$ftpusers = "/etc/ftpusers";
X$passwd = $'PASSWD || "/etc/passwd";
X$group = $'GROUP || "/etc/group";
X
X# ftp's home:
X$ftproot = &'uname2dir($ftpuid);
X$anonymous = $ftproot ne '';
X
X$ftprhosts = "$ftproot/.rhosts";
X$ftpbin = "$ftproot/bin";
X$ftpls = "$ftpbin/ls";
X$ftpetc = "$ftproot/etc";
X$ftppasswd = "$ftpetc/passwd";
X$ftpgroup = "$ftpetc/group";
X
X$W = 'Warning! ' unless defined $W;
X
X# the pub/incoming stuff; by default, pub is *not* world writable, incoming
X# is; if you want pub to be world writable, just change incoming to "pub"
X$incoming = "pub";
X
X@crit_files=($ftpgroup,
X $ftppasswd,
X $ftpls);
X
Xif (-s $ftpusers) {
X # check to see if root (or root equivalents) is in ftpusers file
X @all_roots = split(" ", $'uid2names{0});
X for $i (@all_roots) {
X if (length($user2passwd{$i}) == 13 && ! &'fgrep($ftpusers, "^$i$")) {
X print "Warning! $i should be in $ftpusers!\n";
X }
X }
X}
X
X# do the anonymous ftp checking stuff now?
Xdie unless $anonymous;
X
X# if the user ftp doesn't exist, no-anon stuff....
X# if $TEST -z $ftproot -a "$anonymous" = "yes" ; then
X
Xdie "${W}Need user $ftpuid for anonymous ftp to work!\n" if ($ftpuid eq "");
X
X# if the user ftp doesn't exist, no-anon stuff....
Xif (! -d $ftproot || $ftproot eq "") {
X die "${W}Home directory for ftp doesn\'t exist!\n";
X}
Xif ($ftproot eq "/") {
X print qq:${W}$ftproot ftp's home directory should not be "/"!\n:;
X}
X
X# want to check all the critical files and directories for correct
X# ownership. Some versions of ftp don't need much of anything... no
X# etc directory or password/group files.
X# others need etc directory & password/group files. Experiment.
X#
Xpush(@crit_files, $ftpbin, $ftpetc);
Xfor $i (@crit_files) {
X $owner = &'Owner($i);
X
X if ($owner eq 'BOGUS') {
X print "${W}Critical anon-ftp file $i is missing!\n";
X next;
X }
X
X $owner = $'uid2names{$owner};
X
X if ($owner !~ /\b$primary\b|\b$secondary\b/) {
X print "${W}$i should be owned by $primary or $secondary, not $owner!\n";
X }
X}
X
X# Don't want the passwd and group files to be the real ones!
Xif (&'Owner($ftppasswd) ne 'BOGUS' &&
X $passwd ne $ftppasswd &&
X ! system "$CMP -s $passwd $ftppasswd")
X{
X print "${W}$ftppasswd and $passwd are the same!\n";
X}
X
Xif (&'Owner($ftpgroup) ne 'BOGUS' &&
X $group ne $ftpgroup &&
X ! system "$CMP -s $passwd $ftpgroup")
X{
X print "${W}$ftpgroup and $group are the same!\n";
X}
X
X# ftproot is special; if owned by root; should be !world writable;
X# if owned by ftp, should be mode 555
X
Xif (&'Owner($ftproot) ne 'BOGUS') {
X $owner = $'uid2names{&'Owner($ftproot)};
X $perms=&'Mode($ftproot);
X if ($owner !~ /\b$primary\b|\b$secondary\b/) {
X print "${W}$ftproot should be owned by $primary or $secondary, not $owner!\n";
X }
X
X # ftp-root should not be world-writable:
X &'is_able($ftproot, "w", "w");
X
X # if ftp owns root-dir, then mode should be 555:
X if ($owner eq $ftpuid && $perms ne 00555) {
X print "${W}$ftproot should be mode 555!\n";
X }
X}
X
X#
X# check the .rhosts file:
Xif (-f $ftprhosts) {
X if (-s $ftprhosts) {
X print "${W}$ftprhosts should be be empty!\n";
X }
X $owner=$'uid2names{&'Owner($ftprhosts)};
X if ($owner ne $primary && $owner ne $secondary) {
X print "${W}$ftprhosts should be owned by $primary or $secondary!\n";
X }
X}
X
X# finally, some permissions of miscellaneous files:
Xif (($perms=&'Mode($ftpls)) & 0666) {
X printf "${W}Incorrect permissions (%04o) on $ftpls!\n", $perms;
X}
X
Xif (($perms=&'Mode($ftppasswd)) & 0333) {
X printf "${W}Incorrect permissions (%04o) on $ftppasswd!\n", $perms;
X}
X
X
Xif (($perms=&'Mode($ftpgroup)) & 0333) {
X printf "${W}Incorrect permissions (%04o) on $ftpgroup!\n", $perms;
X}
X
X# Finally, the ~ftp/{pub|incoming|whatever} stuff:
Xopendir(FTPDIR, $ftproot) || die "can't opendir $ftproot: $!\n";
X
X@all_dirs=grep(-d, readdir(FTPDIR));
X
Xlocal($is_able'silent) = 1;
Xfor $i (@all_dirs) {
X if ($i ne $incoming && &'is_able($ftproot . "/$i", "w", "w")) {
X print "${W}Anon-ftp directory $i is World Writable!\n";
X }
X}
X
X1;
X# end of script
SHAR_EOF
chmod 0700 cops_104/perl/ftp.chk ||
echo 'restore of cops_104/perl/ftp.chk failed'
Wc_c="`wc -c < 'cops_104/perl/ftp.chk'`"
test 6582 -eq "$Wc_c" ||
echo 'cops_104/perl/ftp.chk: original size 6582, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/get-cf ==============
if test -f 'cops_104/perl/get-cf' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/get-cf (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/get-cf (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/get-cf' &&
X#! /usr/local/bin/perl
X
X@dot_files = (
X ".login", ".logout", ".cshrc", # csh, cshe or tcsh
X ".profile", # ksh, sh
X ".env", # ksh
X ".alias", ".aliases", # common for all shells
X "user.ps", ".user.ps", "tools.ps", ".tools.ps",
X "startup.ps", ".startup.ps", # NeWS
X ".mgrc", # MGR
X ".X11init", ".awmrc", ".twmrc", ".xinitrc", # X11
X ".emacs" # emacs
X);
X
X%seen = {};
X
Xopen(HOST, "/bin/hostname |") || die "can't get the hostname";
Xchop($hostname=<HOST>);
Xclose(HOST);
X
Xuser_loop:
X for (($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent();
X $name ne "";
X ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent()) {
X
X #
X # If the user has a home directory on this server, get the info
X # about the directory, his CF's and so on.
X #
X if ($dir =~ m,^/n/$hostname/,) {
X if (! -d $dir) {
X printf(stderr "home directory '%s' for user '%s' doesn't exist.\n",
X $dir,
X $name);
X next user_loop;
X }
X
X ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
X $atime,$mtime,$ctime,$blksize,$blocks)
X = stat(_);
X $mode = $mode & 07777;
X
X &spit_it_out("d", $uid, $gid, $mode, $dir);
X
X foreach $file (@dot_files) {
X $path = "$dir/$file";
X
X if (-f $path) {
X ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
X $atime,$mtime,$ctime,$blksize,$blocks)
X = stat(_);
X $mode = $mode & 07777;
X
X &spit_it_out("f", $uid, $gid, $mode, $dir);
X }
X }
X }
X }
X
X
X
X
Xsub spit_it_out {
X local($type, $uid, $gid, $mode, $name) = @_;
X
X if (defined($seen{$name})) {
X return;
X }
X
X printf("%s %d %d 0%o %s\n", $type, $uid, $gid, $mode, $name);
X $seen{$name} = 1;
X}
X
SHAR_EOF
chmod 0700 cops_104/perl/get-cf ||
echo 'restore of cops_104/perl/get-cf failed'
Wc_c="`wc -c < 'cops_104/perl/get-cf'`"
test 1776 -eq "$Wc_c" ||
echo 'cops_104/perl/get-cf: original size 1776, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/getopts.pl ==============
if test -f 'cops_104/perl/getopts.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/getopts.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/getopts.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/getopts.pl' &&
X;# getopts.pl - a better getopt.pl
X
X;# Usage:
X;# do Getopts('a:bc'); # -a takes arg. -b & -c not. Sets opt_* as a
X;# # side effect.
X
Xsub Getopts {
X local($argumentative) = @_;
X local(@args,$_,$first,$rest,$errs);
X local($[) = 0;
X
X @args = split( / */, $argumentative );
X while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
X ($first,$rest) = ($1,$2);
X $pos = index($argumentative,$first);
X if($pos >= $[) {
X if($args[$pos+1] eq ':') {
X shift(@ARGV);
X if($rest eq '') {
X $rest = shift(@ARGV);
X }
X eval "\$opt_$first = \$rest;";
X }
X else {
X eval "\$opt_$first = 1";
X if($rest eq '') {
X shift(@ARGV);
X }
X else {
X $ARGV[0] = "-$rest";
X }
X }
X }
X else {
X print STDERR "Unknown option: $first\n";
X ++$errs;
X if($rest ne '') {
X $ARGV[0] = "-$rest";
X }
X else {
X shift(@ARGV);
X }
X }
X }
X $errs == 0;
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/getopts.pl ||
echo 'restore of cops_104/perl/getopts.pl failed'
Wc_c="`wc -c < 'cops_104/perl/getopts.pl'`"
test 902 -eq "$Wc_c" ||
echo 'cops_104/perl/getopts.pl: original size 902, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/glob.pl ==============
if test -f 'cops_104/perl/glob.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/glob.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/glob.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/glob.pl' &&
X#
X# This does shell or perl globbing without resorting
X# to the shell -- we were having problems with the shell blowing
X# up with extra long pathnames and lots of file names. set $glob'debug
X# for trace information.
X#
X# tom christiansen <tchrist@convex.com>
X
Xpackage glob;
X
Xsub main'glob {
X local($expr) = @_;
X local(@files);
X
X $? = 0;
X open(SAVERR, ">&STDERR"); close(STDERR); # suppress args too long
X @files = <${expr}>;
X if ($?) {
X print SAVERR "shell glob blew up on $expr\n" if $debug;
X @files = &SHglob($expr);
X }
X open (STDERR, ">&SAVERR");
X # if (@files == 1 && $files[0] eq $expr) { @files = ''; } # sh foo
X @files;
X}
X
Xsub main'SHglob {
X local($expr) = @_;
X local(@retlist) = ();
X local($dir);
X
X print "SHglob: globbing $expr\n" if $debug;
X
X $expr =~ s/([.{+\\])/\\$1/g;
X $expr =~ s/\*/.*/g;
X $expr =~ s/\?/./g;
X
X for $dir (split(' ',$expr)) {
X push(@retlist, &main'REglob($dir));
X }
X
X return sort @retlist;
X}
X
Xsub main'REglob {
X local($path) = @_;
X local($_);
X local(@retlist) = ();
X local($root,$expr,$pos);
X local($relative) = 0;
X local(@dirs);
X local($user);
X
X $haveglobbed = 0;
X
X @dirs = split(/\/+/, $path);
X
X if ($dirs[$[] =~ m!~(.*)!) {
X $dirs[$[] = &homedir($1);
X return @retlist unless $dirs[$[];
X } elsif ($dirs[$[] eq '') {
X $dirs[$[] = '/' unless $dirs[$[] =~ m!^\.{1,2}$!;
X } else {
X unshift(@dirs, '.');
X $relative = 1;
X }
X
X printf "REglob: globbing %s\n", join('/',@dirs) if $debug;
X
X @retlist = &expand(@dirs);
X
X for (@retlist) {
X if ($relative) {
X s!^\./!!o;
X }
X s!/{2,}!/!g;
X }
X
X return sort @retlist;
X}
X
Xsub expand {
X local($dir, $thisdir, @rest) = @_;
X local($nextdir);
X local($_);
X local(@retlist) = ();
X local(*DIR);
X
X unless ($haveglobbed || $thisdir =~ /([^\\]?)[?.*{[+\\]/ && $1 ne '\\') {
X @retlist = ($thisdir);
X } else {
X unless (opendir(DIR,$dir)) {
X warn "glob: can't opendir $dir: $!\n" if $debug;
X } else {
X @retlist = grep(/^$thisdir$/,readdir(DIR));
X @retlist = grep(!/^\./, @retlist) unless $thisdir =~ /^\\\./;
X $haveglobbed++;
X }
X closedir DIR;
X }
X
X for (@retlist) {
X $_ = $dir . '/' . $_;
X }
X
X if ($nextdir = shift @rest) {
X local(@newlist) = ();
X for (@retlist) {
X push(@newlist,&expand($_,$nextdir,@rest));
X }
X @retlist = @newlist;
X }
X
X return @retlist;
X}
X
Xsub homedir {
X local($user) = @_;
X local(@pwent);
X # global %homedir
X
X if (!$user) {
X return $ENV{'HOME'} if $ENV{'HOME'};
X ($user = $ENV{'USER'}) ||
X ($user = getlogin) ||
X (($user) = getpwnam($>));
X warn "glob'homedir: who are you, user #$>?\n" unless $user;
X return '/';
X }
X unless (defined $homedir{$user}) {
X if (@pwent = getpwnam($user)) {
X $homedir{$user} = $pwent[$#pwent - 1];
X } else {
X warn "glob'homedir: who are you, user #$>?\n" unless $user;
X $homedir{$user} = '/';
X }
X }
X return $homedir{$user};
X}
X
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/glob.pl ||
echo 'restore of cops_104/perl/glob.pl failed'
Wc_c="`wc -c < 'cops_104/perl/glob.pl'`"
test 2963 -eq "$Wc_c" ||
echo 'cops_104/perl/glob.pl: original size 2963, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/group.chk ==============
if test -f 'cops_104/perl/group.chk' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/group.chk (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/group.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/group.chk' &&
X#!/bin/sh -- need to mention perl here to avoid recursion
X'true' || eval 'exec perl -S $0 $argv:q';
Xeval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
X& eval 'exec /usr/bin/perl -S $0 $argv:q'
X if 0;
X
X#
X# group.chk.pl
X#
X# Perl version: composer@chem.bu.edu
X# Based on original shell script, group.chk
X#
X# Check group file -- /etc/group -- for incorrect number of fields,
X# duplicate groups, non-alphanumeric group names, and non-numeric group
X# id's.
X#
X# Mechanism: Group.check uses awk to ensure that each line of the group
X# has 4 fields, as well as examining each line for any duplicate groups or
X# any duplicate user id's in a given group by using "sort -u" to ferret
X# out any duplications. It also checks to make sure that the password
X# field (the second one) is a "*", meaning the group has no password (a
X# group password is usually not necessary because each member listed on
X# the line has all the privilages that the group has.) All results are
X# echoed to standard output. Finally it ensures that the group names
X# are alphanumeric, that the group id's are numeric, and that there are
X# no blank lines. For yellow pages groups, it does the same checking,
X# but in order to get a listing of all members of the groups, it does a
X# "ypcat group".
X#
X# The /etc/group file has a very specific format, making the task
X# fairly simple. Normally it has lines with 4 fields, each field
X# separated by a colon (:). The first field is the group name, the second
X# field is the encrypted password (an asterix (*) means the group has no
X# password, otherwise the first two characters are the salt), the third
X# field is the group id number, and the fourth field is a list of user
X# ids in the group. If a line begins with a plus sign (+), it is a yellow
X# pages entry. See group(5) for more information.
X#
X
X# should get below config stuff from cops.cf file
Xpackage main;
X
Xdie "Usage: $0\n" if @ARGV;
X
Xrequire 'pathconf.pl';
X
X# Used for Sun C2 security group file. FALSE (default) will flag
X# valid C2 group syntax as an error, TRUE attempts to validate it.
X# Thanks to Pete Troxell for pointing this out.
X#
X# moved to cops.cf
X
Xpackage group_chk;
X
X$etc_group = $'GROUP || '/etc/group';
X
X# Testing $etc_group for potential problems....
Xopen (Group, "< $etc_group") || warn "$0: Can't open $etc_group: $!\n";
X&chk_group_file_format('Group');
Xclose Group;
X
X# Testing ypcat group for potential problems
X$yp=0;
Xif (-s $'YPCAT && -x _) {
X open(YGroup, "$'YPCAT group 2>/dev/null |")
X || die "$0: Can't popen $'YPCAT: $!\n";
X $yp=1;
X &chk_group_file_format('YGroup');
X close(YGroup);
X}
X
X# usage: &chk_group_file_format('Filehandle-name');
X# skips over lines that begin with "+:"
X# It really should check for correct yellow pages syntax....
X#
X# this routine checks lines read from a filehandle for potential format
X# problems .. should be matching group(5)
X#
X# checks for duplicate users in a group as it reads the lines instead
X# of after (as the original shell script does)
X
Xsub chk_group_file_format {
X local($file) = @_;
X local($W) = "Warning! $file file,";
X undef %groups;
X
X while (<$file>) {
X # should really check for correct YP syntax
X next if /^[-+]:/; # skipping YP lines for now
X print "$W line $., is blank\n" if /^\s*$/;
X ($group,$pass,$gid,$users) = split(?:?);
X $groups{$group}++; # keep track of dups
X print "$W line $., does not have 4 fields:\n\t$_" if (@_ != 4);
X print "$W line $., nonalphanumeric group name:\n\t$_"
X if $group !~ /^[_A-Za-z0-9-]+$/;
X if ($pass && $pass ne '*') {
X if ( ! $C2 || $yp ) {
X print "$W line $., group has password:\n\t$_"
X if length($pass) == 13;
X } else {
X print "$W line $., group has invalid field for C2:\n\t$_"
X if $pass ne "#\$$user";
X }
X }
X # print "$W line $., nonnumeric group id: $_" if $_[2] !~ /^\d+$/;
X if ($gid !~ /^\d+$/) {
X if ($uid < 0) {
X print "$W line $., negative group id:\n\t$_";
X }
X else { print "$W line $., nonnumeric group id:\n\t$_"; }
X }
X
X # look for duplicate users in a group
X # kinda ugly, but it works .. and I have too much other work right
X # now to clean it up. maybe later.. ;-)
X chop($users); # down here, 'cos split gets rid of final null fields
X @users = sort split(/\s*,\s*/, $users);
X # %users = # of times user is in group, $dup_user = duplicate found
X undef %users; $dup_user=0;
X grep(!($users{$_}++) && 0, @users);
X for (keys %users) {
X (print "Warning! Group $group has duplicate user(s):\n"),
X $dup_user=1 if !$dup_user && $users{$_} > 1;
X print "$_ " if $users{$_} > 1;
X }
X print "\n" if $dup_user;
X
X }
X # find duplicate group names
X # not the best way, but it works..
X # boy, this is ugly too .. but, not as bad as above.. :)
X $dup_warned = 0;
X for (sort keys %groups) {
X (print "Warning! Duplicate Group(s) found in $file:\n"), $dup_warned++
X if !$dup_warned && $groups{$_} > 1;
X print "$_ " if $groups{$_} > 1;
X }
X print "\n" if $dup_warned;
X}
X
X1;
X# end
SHAR_EOF
chmod 0700 cops_104/perl/group.chk ||
echo 'restore of cops_104/perl/group.chk failed'
Wc_c="`wc -c < 'cops_104/perl/group.chk'`"
test 4981 -eq "$Wc_c" ||
echo 'cops_104/perl/group.chk: original size 4981, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/hostname.pl ==============
if test -f 'cops_104/perl/hostname.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/hostname.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/hostname.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/hostname.pl' &&
X#
X# file: hostname.pl
X# usage: $hostname = &'hostname;
X#
X# purpose: get hostname -- try method until we get an answer
X# or return "Amnesiac!"
X#
X
Xpackage hostname;
X
Xsub main'hostname {
X if (!defined $hostname) {
X $hostname = ( -x '/bin/hostname' && `/bin/hostname` )
X || ( -x '/bin/uname' && `/bin/uname -n` )
X || ( -x '/usr/bin/uuname' && `/usr/bin/uuname -l`)
X || 'Amnesiac! '; # trailing space is for chop
X chop $hostname;
X }
X $hostname;
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/hostname.pl ||
echo 'restore of cops_104/perl/hostname.pl failed'
Wc_c="`wc -c < 'cops_104/perl/hostname.pl'`"
test 475 -eq "$Wc_c" ||
echo 'cops_104/perl/hostname.pl: original size 475, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/is_able.chk ==============
if test -f 'cops_104/perl/is_able.chk' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/is_able.chk (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/is_able.chk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/is_able.chk' &&
X#!/bin/sh -- need to mention perl here to avoid recursion
X'true' || eval 'exec perl -S $0 $argv:q';
Xeval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
X& eval 'exec /usr/bin/perl -S $0 $argv:q'
X if 0;
X
X#
X# is_able.chk
X#
X# This shell script checks the permissions of all files and directories
X# listed in the configuration file "is_able.lst", and prints warning messages
X# according to the status of files. You can specify world or group readability
X# or writeability. See the config file for the format of the configuration
X# file.
X#
X# Mechanism: This shell script parses each line from the configure file
X# and uses the "is_able.pl" program to check if any of
X# the directories in question are writable by world/group.
X#
X
Xrequire 'is_able.pl';
Xrequire 'file_mode.pl';
Xrequire 'glob.pl';
X
Xif ($ARGV[0] eq '-d') {
X shift;
X $debug = $glob'debug = 1; # maybe should turn off glob'debug afterwards
X}
X
Xunshift (@ARGV, "is_able.lst" ) unless @ARGV;
X
Xwhile (<>) {
X next if /^\s*#/;
X split;
X next unless @_ == 3;
X ($file, $x, $y) = @_;
X @files = $file =~ /[\[?*]/ ? &'glob($file) : ($file);
X for $file (@files) {
X print STDERR "is_able $file $x $y\n" if $debug;
X &'is_able($file, $x, $y);
X }
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/is_able.chk ||
echo 'restore of cops_104/perl/is_able.chk failed'
Wc_c="`wc -c < 'cops_104/perl/is_able.chk'`"
test 1235 -eq "$Wc_c" ||
echo 'cops_104/perl/is_able.chk: original size 1235, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/is_able.lst ==============
if test -f 'cops_104/perl/is_able.lst' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/is_able.lst (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/is_able.lst (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/is_able.lst' &&
X# This lists any/all sensitive files the administration wants to ensure
X# non-read/writability of. Comments are lines starting with a "#".
X#
X# USE FULL PATHNAMES!
X#
X# Lines are of the format:
X#
X# /path/to/{dir|file} World/Group Read/Write/Both
X#
X# as above {w|g} {r|w|b}
X#
X/ w w
X/etc w w
X/usr w w
X/bin w w
X/dev w w
X/usr/bin w w
X/usr/etc w w
X/usr/adm w w
X/usr/lib w w
X/usr/include w w
X/usr/spool w w
X/usr/spool/mail w w
X/usr/spool/news w w
X/usr/spool/uucp w w
X/usr/spool/at w w
X/usr/local w w
X/usr/local/bin w w
X/usr/local/lib w w
X/usr/users w w
X/Mail w w
X
X# some Un*x's put shadowpass stuff here:
X/etc/security w r
X
X# /.login /.profile /.cshrc /.rhosts
X/.* w w
X
X# I think everything in /etc should be !world-writable, as a rule; but
X# if you're selecting individual files, do at *least* these:
X# /etc/passwd /etc/group /etc/inittab /etc/rc /etc/rc.local /etc/rc.boot
X# /etc/hosts.equiv /etc/profile /etc/syslog.conf /etc/export /etc/utmp
X# /etc/wtmp
X/etc/* w w
X
X/bin/* w w
X/usr/bin/* w w
X/usr/etc/* w w
X/usr/adm/* w w
X/usr/lib/* w w
X/usr/include/* w w
X/usr/local/lib/* w w
X/usr/local/bin/* w w
X/usr/etc/yp* w w
X/usr/etc/yp/* w w
X
X# individual files:
X/usr/lib/crontab w b
X/usr/lib/aliases w w
X/usr/lib/sendmail w w
X/usr/spool/uucp/L.sys g b
X
X# NEVER want these writeable/readable!
X/dev/kmem w b
X/dev/mem w b
X
X# Optional List of assorted files that shouldn't be
X# write/readable (mix 'n match; add to the list as desired):
X/usr/adm/sulog w r
X/.netrc w b
X# HP-UX and others:
X/etc/btmp w b
X/etc/securetty w b
X# Sun-fun
X/dev/drum w b
X/dev/nit w b
X/etc/sunlink/dni/rc w w
SHAR_EOF
chmod 0700 cops_104/perl/is_able.lst ||
echo 'restore of cops_104/perl/is_able.lst failed'
Wc_c="`wc -c < 'cops_104/perl/is_able.lst'`"
test 1678 -eq "$Wc_c" ||
echo 'cops_104/perl/is_able.lst: original size 1678, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/kuang ==============
if test -f 'cops_104/perl/kuang' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/kuang (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/kuang (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/kuang' &&
X#!/bin/sh -- need to mention perl here to avoid recursion
X'true' || eval 'exec perl -S $0 $argv:q';
Xeval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
X& eval 'exec /usr/users/df/bin/perl.sun4 -S $0 $argv:q'
X if 0;
X# & eval 'exec /usr/local/bin/perl -S $0 $argv:q'
X#
X# kuang - rule based analysis of Unix security
X#
X# Perl version by Steve Romig of the CIS department, The Ohio State
X# University, October 1990.
X#
X# Based on the shell script version by Dan Farmer from his COPS
X# package, which in turn is based on a shell version by Robert
X# Baldwin.
X#
X#-----------------------------------------------------------------------------
X# Players:
X# romig Steve Romig, romig@cis.ohio-state.edu
X# tjt Tim Tessin, tjt@cirrus.com
X#
X# History:
X# 4/25/91 tjt, romig Various fixes to filewriters (better messages about
X# permission problems) and don't update the DBM cache
X# with local file info.
X# 11/1/90 romig Major rewrite - generic lists, nuking get_entry
X# and put_entry, moved rules to separate file.
X#
X
X#
X# Options
X#
X# -l list uid's that can access the given target, directly
X# or indirectly
X# -d debug
X# -V verbose
X#
X# -k file load the list of known CO's
X# -f file preload file information from the named file.
X# -p file preload passwd info from the named file.
X# -Y preload passwd info from ypcat + /etc/passwd
X# -g group preload group info from the named file.
X# -G preload group info from ypcat + /etc/group
X#
X# NOTE:
X# If you know where perl is and your system groks #!, put its
X# pathname at the top to make this a tad faster.
X#
X# the following magic is from the perl man page
X# and should work to get us to run with perl
X# even if invoked as an sh or csh or foosh script.
X# notice we don't use full path cause we don't
X# know where the user has perl on their system.
X#
X
X$options = "ldVk:p:g:f:YG";
X$usage = "usage: kuang [-l] [-d] [-v] [-k known] [-f file] [-Y] [-G] [-p passwd] [-g group] [u.username|g.groupname]\n";
X
X$add_files_to_cache = 1; # Whether to update the %files cache
X # with local file info or not.
X
X#
X# Terminology:
X#
X# An "op" is an operation, such as uid, gid, write, or replace.
X# 'uid' means to gain access to some uid, 'gid' means to gain access
X# to some gid. 'write' and 'replace' refer to files - replace means
X# that we can delete a file and replace it with a new one somehow
X# (for example, if we could write the directory it is in).
X#
X# An object is a uid, gid or pathname.
X#
X# A Controlling Operation (CO) is a (operation, object) pair
X# represented as "op object": "uid 216" (become uid 216) or "replace
X# /.rhosts" (replace file /.rhosts). These are represented
X# internally as "c value", where "c" is a character representing an
X# operation (u for uid, g for gid, r for replace, w for write) and
X# value is a uid, gid or pathname.
X#
X# A plan is a chain of CO's that are connected to each other. If
X# /.login were writeable by uid 216, we might have a plan such as:
X#
X# uid 216 => write /.login => uid 0
X#
X# which means (in English) "if we can become uid 216, then write
X# /.login which gives you access to uid 0 (when root next logs in)."
X# Plans are represented in several ways: as arrays:
X#
X# ("u 0", "w /.login", "u 216")
X#
X# Note that the order is reversed. As a string:
X#
X# "u 0\034w /.login\034u 216"
X#
X# The target is the object that we are trying to gain (a uid, gid or
X# file, typically u.root or some other UID).
X#
X# Data Structures
X#
X# %known An assocc array, indexed by CO. This lists
X# the COs that we already have access to. If we
X# find a plan that leads from a CO in the known
X# list to the target, we've succeeded in
X# finding a major security flaw.
X#
X# @new An array of plans that are to be evaluated in
X# the next cycle.
X#
X# @old An array of plans that we are currently
X# evaluating.
X#
X# %beendone An assoc array that lists the plans that have
X# already been tried. Used to prevent loops.
X#
X# @accessible An array of the uids that can reach the
X# target.
X#
X# %files An assoc array, indexed by file name, contains
X# cached file info. value is of form "uid gid
X# mode".
X#
X# From pwgrid:
X#
X# %uname2shell Assoc array, indexed by user name, values are
X# shells.
X#
X# %uname2dir Assoc array, indexed by user name, values are
X# home directories.
X#
X# %uname2uid Assoc array, indexed by name, values are uids.
X#
X# %uid2names Assoc array, indexed by uid, value is list of
X# user names with that uid, in form "name name
X# name...".
X#
X# %gid2members Assoc array, indexed by gid, value is list of
X# group members (user names).
X#
X# %gname2gid Assoc array, indexed by group name, values are
X# matching gids.
X#
X# %gid2names Assoc array, indexed by gid, values are
X# matching group names.
X#
X
Xdo 'yagrip.pl' ||
X die "can't do yagrip.pl";
X
X# do 'pwgrid.pl' ||
X# die "can't do pwgrid.pl";
Xdo 'pass.cache.pl' ||
X die "can't do pass.cache.pl";
X
Xdo 'rules.pl' ||
X die "can't do rules.pl";
X
X\f
X#
X# Turns a string of the form "operation value" or "value" into
X# standard "CO" form ("operation value"). Converts user or group
X# names into corresponding uid and gid values.
X#
X# Returns nothing if it isn't parseable.
X#
X
Xsub canonicalize {
X local($string) = @_;
X local($op, $value);
X
X if ($string =~ /^([ugrw]) ([^ \t\n]+)$/) { # of form "op value"
X $op = $1;
X $value = $2;
X } elsif ($string =~ /^[^ \t\n]+$/) { # of form "value"
X $value = $string;
X $op = "u";
X } else {
X return();
X }
X
X if ($op eq "u" && $value =~ /^[^0-9]+$/) { # user name, not ID
X if (defined($uname2uid{$value})) {
X $value = $uname2uid{$value};
X } else {
X printf(stderr "There's no user named '%s'.\n", $value);
X return();
X }
X } elsif ($op eq "g" && $value =~/^[^0-9]+$/) {
X if (defined($gname2gid{$value})) {
X $value = $gname2gid{$value};
X } else {
X printf(stderr "There's no group named '%s'.\n", $value);
X return();
X }
X }
X
X return($op, $value);
X}
X
X\f
X#
X# Preload file information from a text file or DBM database.
X# If $opt_f.dir exists, then we just shadow %files from a DBM
X# database. Otherwise, open the file and read the entries into
X# %files.
X#
X# $add_files_to_cache is set to 0 if we get the info from
X# DBM since we wouldn't want to pollute update our DBM cache
X# with local file info which wouldn't apply to other hosts.
X#
X
Xsub preload_file_info {
X local($count, $f_type, $f_uid, $f_gid, $f_mode, $f_name);
X
X if (defined($opt_d)) {
X printf("loading file info...\n");
X }
X
X if (-f "$opt_f.dir") {
X $add_files_to_cache = 0;
X
X dbmopen(files, $opt_f, 0644) ||
X die sprintf("can't open DBM file '%s'", $opt_f);
X } else {
X open(FILEDATA, $opt_f) ||
X die sprintf("kuang: can't open '%s'", $opt_f);
X
X $count = 0;
X while (<FILEDATA>) {
X $count++;
X
X chop;
X ($f_type, $f_uid, $f_gid, $f_mode, $f_name) = split;
X
X if ($count % 1000 == 0) {
X printf("line $count, reading entry for $f_name\n");
X }
X $files{$f_name} = join(' ', $f_uid, $f_gid, $f_mode);
X }
X
X close(FILEDATA);
X }
X}
X
X#
X# Preload the known information. Reads data from a file, 1 entry per line,
X# each entry is a CO that we "know" can be used.
X#
X
Xsub preload_known_info {
X local($file_name) = @_;
X local($op, $value, $co);
X
X open(FILE, $file_name) ||
X die sprintf("kuang: can't open '%s'", $file_name);
X
X known_loop:
X while (<FILE>) {
X chop;
X if ((($op, $value) = &canonicalize($_)) == 2) {
X $co = sprintf("%s %s", $op, $value);
X $known{$co} = 1;
X } else {
X printf(stderr "kuang: invalid entry in known list: line %d '%s'.\n",
X $.,
X $_);
X }
X }
X
X close(FILE);
X}
X
X\f
X#
X# Do various initialization type things.
X#
X
Xsub init_kuang {
X local($which, $name, $uid, $gid);
X local($op, $value, $co);
X
X #
X # Deal with args...
X #
X
X &getopt($options) ||
X die $usage;
X
X if ($#ARGV == -1) {
X push(@ARGV, "u root");
X }
X
X #
X # Preload anything...
X #
X if (defined($opt_f)) {
X &preload_file_info();
X }
X
X if (defined($opt_d)) {
X printf("load passwd info...\n");
X }
X
X if (defined($opt_p)) {
X if (defined($opt_Y)) {
X printf(stderr "You can only specify one of -p or -P, not both.\n");
X exit(1);
X }
X
X &load_passwd_info(0, $opt_p);
X } elsif (defined($opt_Y)) {
X &load_passwd_info(0);
X } else {
X &load_passwd_info(1);
X }
X
X if (defined($opt_d)) {
X printf("load group info...\n");
X }
X
X if (defined($opt_g)) {
X if (defined($opt_G)) {
X printf(stderr "You can only specify one of -g or -G, not both.\n");
X exit(1);
X }
X
X &load_group_info(0, $opt_g);
X } elsif (defined($opt_G)) {
X &load_group_info(0);
X } else {
X &load_group_info(1);
X }
X
X #
X # Need some of the password and group stuff. Suck in passwd and
X # group info, store by uid and gid in an associative array of strings
X # which consist of fields corresponding to the passwd and group file
X # entries (and what the heck, we'll use : as a delimiter also...:-)
X #
X $uname2shell{"OTHER"} = "";
X $uname2dir{"OTHER"} = "";
X $uname2uid{"OTHER"} = -1;
X $uid2names{-1} = "OTHER";
X
X $known{"u -1"} = 1; # We can access uid OTHER
X
X if (defined($opt_k)) {
X &preload_known_info($opt_k);
X }
X
X #
X # Create the target list from the remaining (non-option) args...
X #
X while ($#ARGV >= 0) {
X $elt = pop(@ARGV);
X if ((($op, $value) = &canonicalize($elt)) == 2) {
X $co = sprintf("%s %s", $op, $value);
X push(@targets, $co);
X } else {
X printf(stderr "target '%s' isn't of correct form\n", $elt);
X }
X }
X}
X
X\f
X#
X# Call this to set things up for a new target. Resets old, new, beendone
X# and accessible.
X#
Xsub set_target {
X local($target) = @_;
X
X @old = ();
X @new = ();
X %beendone = ();
X @accessible = ();
X# fixme: reset known?
X
X if ($target =~ /^([ugrw]) ([^ \t]+)$/) {
X &addto($1, $2);
X return(0);
X } else {
X printf(stderr "kuang: bad target '%s'\n", $target);
X return(1);
X }
X}
X
X#
X# Break a CO into an (operation, value) pair and return it. If it
X# isn't in "operation value" form, return ().
X#
Xsub breakup {
X local($co) = @_;
X local($operation, $value);
X
X if ($co =~ /^([ugrw]) ([^ \t]+)$/) {
X $operation = $1;
X $value = $2;
X } else {
X printf(stderr "Yowza, breakup failed on '%s'\n",
X $co);
X exit(1);
X }
X
X return($operation, $value);
X}
X
X#
X# Get the writers of the named file - return as (UID, GID, OTHER)
X# triplet. Owner can always write, since he can chmod the file if he
X# wants.
X#
X# (fixme) are there any problems in this sort of builtin rule? should
X# we make this knowledge more explicit?
X#
Xsub filewriters {
X local($name) = @_;
X local($tmp, $mode, $uid, $gid, $other);
X
X #
X # Check the file cache - avoid disk lookups for performance and
X # to avoid shadows...
X #
X if (defined($files{$name})) {
X $cache_hit++;
X
X ($uid, $gid, $mode, $tmp) = split(/ /, $files{$name});
X } else {
X $cache_miss++;
X
X unless (-e $name) {
X if ($add_files_to_cache) {
X $files{$name} = "";
X }
X # ENOTDIR = 20
X ($! == 20) && print "Warning: Illegal Path: '$name'\n";
X # EACCES = 13
X ($! == 13) && print "Warning: Permission Denied: '$name'\n";
X # all values are returned "" here.
X return;
X }
X
X ($tmp,$tmp,$mode,$tmp,$uid,$gid) = stat(_);
X if ($add_files_to_cache) {
X $files{$name} = join(' ', "$uid", "$gid", "$mode");
X }
X }
X
X if (($mode & 020) != 020) {
X $gid = "";
X }
X
X if (($mode & 02) == 02) {
X $other = 1;
X } else {
X $other = 0;
X }
X
X return($uid, $gid, $other);
X}
X
X\f
Xsub ascii_plan {
X local(@plan) = @_;
X local($op, $value, $result);
X
X for ($i = $#plan; $i >= 0; $i--) {
X ($op, $value) = &breakup($plan[$i]);
X
X case:
X {
X if ($op eq "g") {
X $op = "grant gid";
X last case;
X }
X
X if ($op eq "u") {
X $op = "grant uid";
X last case;
X }
X
X if ($op eq "r") {
X $op = "replace";
X last case;
X }
X
X if ($op eq "w") {
X $op = "write";
X last case;
X }
X
X printf(stderr "Bad op '%s' in plan '%s'\n",
X $op,
X join(';', @plan));
X last case;
X }
X
X $result .= "$op $value ";
X }
X
X return($result);
X}
X
X#
X# Add a plan to the list of plans to check out.
X#
Xsub addto {
X local($op, $value, @plan) = @_;
X local($co);
X
X $co = sprintf("%s %s",
X $op,
X $value);
X
X #
X # See if the op and value is "uid root" - if so, and if the @plan
X # isn't empty, then don't bother checking - if the target isn't root,
X # its silly to pursue plans that require becoming root since if we can
X # become root, we can become anything. If the target is root, then
X # this would be a loop anyway.
X #
X if ($op eq "u" && $value eq "0" && $#plan >= 0) {
X if (defined($opt_d)) {
X printf("addto: aborted root plan '%s'\n",
X &ascii_plan(@plan, $co));
X }
X return;
X }
X
X #
X # See whether there's an entry for $co in the known list.
X # If so - success, we've found a suitable breakin plan.
X #
X # Yes, we want to check to see whether the whole Controlling Operation
X # is one that is known to us, rather than just the object. I
X # might have a hole that allows me to "replace /bin/foo" which is
X # somewhat different than "write /bin/foo"
X #
X if (! defined($opt_l) && defined($known{$co})) {
X printf("Success! %s\n",
X &ascii_plan(@plan, $co));
X }
X
X #
X # Check for loops -- if the new CO is part of the plan that we're
X # adding it to, this is a loop.
X #
X foreach $entry (@plan) {
X if ($entry eq $co) {
X if (defined($opt_d)) {
X printf("addto: aborted loop in plan '%s'\n",
X &ascii_plan(@plan, $co));
X }
X return;
X }
X }
X
X #
X # Add this CO to the plan array...
X #
X push(@plan, $co);
X
X #
X # Make an ascii version of sorts...
X #
X $text_plan = join($;, @plan);
X
X #
X # Check to see if the new plan has been done.
X #
X if (defined($beendone{$text_plan})) {
X if (defined($opt_d)) {
X printf("addto: plan's been done - '%s'\n",
X &ascii_plan(@plan));
X }
X return;
X }
X
X #
X # If we made it this far, its a new plan and isn't a loop.
X #
X
X #
X # Add to the beendone list...
X #
X $beendone{$text_plan} = 1;
X
X #
X # Add to new plan list...
X #
X push(@new, $text_plan);
X
X if (defined($opt_V)) {
X printf("addto: %s\n",
X &ascii_plan(@plan));
X }
X
X #
X # If this is a uid goal, then add the plan to the accessible list.
X #
X if ($op eq "u" && $value ne "0" && defined($opt_l)) {
X push(@accessible, $value);
X }
X}
X
X#
X#----------------------------------------------------------------------
X#Main program follows...initialize and loop till we're done.
X#
X
X&init_kuang();
X
Xtarget_loop:
Xforeach $target (@targets) {
X if (&set_target($target)) {
X next target_loop;
X }
X
X while ($#new >= 0) {
X @old = @new;
X @new = ();
X
X foreach $t_plan (@old) {
X @plan = split(/\034/, $t_plan);
X ($op, $value) = &breakup($plan[$#plan]);
X
X &apply_rules($op, $value, @plan);
X }
X }
X
X if (defined($opt_l)) {
X foreach $elt (@accessible) {
X printf("$elt\n");
X }
X }
X}
X
Xif (defined($opt_d)) {
X printf("File info cache hit/access ratio: %g\n",
X ($cache_hit + $cache_miss > 0)
X ? $cache_hit / ($cache_hit + $cache_miss)
X : 0.0);
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/kuang ||
echo 'restore of cops_104/perl/kuang failed'
Wc_c="`wc -c < 'cops_104/perl/kuang'`"
test 15363 -eq "$Wc_c" ||
echo 'cops_104/perl/kuang: original size 15363, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/is_able.pl ==============
if test -f 'cops_104/perl/is_able.pl' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/is_able.pl (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/is_able.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/is_able.pl' &&
X#
X# (This takes the place of the C program is_able.c, BTW.)
X#
X# is_able filename {w|g|s|S} {r|w|B|b|s}
X# (world/group/SUID/SGID read/write/{read&write}/{suid&write}/s[ug]id)
X#
X# The second arg of {r|w} determines whether a file is (group or world
X# depending on the first arg of {w|g}) writable/readable, or if it is
X# SUID/SGID (first arg, either s or S, respectively), and prints out a
X# short message to that effect.
X#
X# So:
X# is_able w w # checks if world writable
X# is_able g r # checks if group readable
X# is_able s s # checks if SUID
X# is_able S b # checks if world writable and SGID
X
Xpackage main;
Xrequire 'file_mode.pl';
X
Xpackage is_able;
X
X# package statics
X#
X%wg = (
X 'w', 00006,
X 'g', 00060,
X 's', 04000,
X 'S', 02000,
X );
X
X%rwb= (
X 'r', 00044,
X 'w', 00022,
X 'B', 00066,
X 'b', 04022,
X 's', 06000,
X );
X
X$silent = 0; # for suppressing diagnostic messages
X
X
Xsub main'is_able {
X local($file, $wg, $rwb) = @_;
X
X local (
X $mode, # file mode
X $piece, # 1 directory component
X @pieces, # all the pieces
X @dirs, # all the directories
X $p, # punctuation; (*) mean writable
X # due to writable parent
X $retval, # true if vulnerable
X $[ # paranoia
X );
X
X &usage, return undef if @_ != 3 || $file eq '';
X
X &usage, return undef unless defined $wg{$wg} && defined $rwb{$rwb};
X
X if (&'Mode($file) eq 'BOGUS' && $noisy) {
X warn "is_able: can't stat $file: $!\n";
X return undef;
X }
X
X $retval = 0;
X
X if ($rwb{$rwb} & $rwb{'w'}) {
X @pieces = split(m#/#, $file);
X for ($i = 1; $i <= $#pieces; $i++) {
X push(@dirs, join('/', @pieces[0..$i]));
X }
X } else {
X @dirs = ( $file );
X }
X
X for $piece ( reverse @dirs ) {
X
X next unless $mode = &'Mode($piece);
X next if $mode eq 'BOGUS';
X
X next unless $mode &= 07777 & $wg{$wg} & $rwb{$rwb};
X
X $retval = 1;
X
X $p = $piece eq $file ? '!' : '! (*)';
X
X $parent_is_writable = $p eq '! (*)'; # for later
X
X next if $silent; # for &is_writable
X
X print "Warning! $file is group readable$p\n" if $mode & 00040;
X print "Warning! $file is _World_ readable$p\n" if $mode & 00004;
X print "Warning! $file is group writable$p\n" if $mode & 00020;
X print "Warning! $file is _World_ writable$p\n" if $mode & 00002;
X print "Warning! $file is SUID!\n" if $mode & 04000;
X print "Warning! $file is SGID!\n" if $mode & 02000;
X
X last if $piece ne $file; # only complain on first writable parent
X }
X $retval;
X}
X
Xsub main'is_writable {
X local($silent) = 1;
X &'is_able($_[0], 'w', 'w')
X ? $parent_is_writable
X ? "writable (*)"
X : "writable"
X : 0;
X}
X
Xsub main'is_readable {
X local($silent) = 1;
X &'is_able($_[0], 'w', 'r');
X}
X
Xsub usage {
X warn <<EOF;
XUsage: is_able file {w|g|S|s} {r|w|B|b|s}
X (not: is_able @_)
XEOF
X}
X
X1;
SHAR_EOF
chmod 0700 cops_104/perl/is_able.pl ||
echo 'restore of cops_104/perl/is_able.pl failed'
Wc_c="`wc -c < 'cops_104/perl/is_able.pl'`"
test 2835 -eq "$Wc_c" ||
echo 'cops_104/perl/is_able.pl: original size 2835, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= cops_104/perl/kuang.1 ==============
if test -f 'cops_104/perl/kuang.1' -a X"$1" != X"-c"; then
echo 'x - skipping cops_104/perl/kuang.1 (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting cops_104/perl/kuang.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'cops_104/perl/kuang.1' &&
X.TH KUANG 1 "4 October 1990"
X.SH NAME
Xkuang \- find security problems through rule based analysis
X.SH SYNOPSIS
X.B kuang
X.RB "[\|" \-v "\|]"
X.RB "[\|" \-d "\|]"
X.RB "[\|" \-l "\|]"
X.RB "[\|" \-k known"\|]"
X.RB "[\|" \-f filedata "\|]"
X.RB "[\|" \-P "\|]"
X.RB "[\|" \-G "\|]"
X.RB "[\|" \-p passwd "\|]"
X.RB "[\|" \-g group "\|]"
X.RB "[\|"
X.IR u.username | g.groupname "\|]"
X.br
X.SH DESCRIPTION
X.LP
X.B kuang
Xuses rule based analysis to examine the current security configuration
Xof a site and determine whether certain security problems exist.
X
X.B kuang
Xcontains embedded rules that describe the projection model and
Xsome of the attacker tricks used on Unix systems. It uses these rules
SHAR_EOF
true || echo 'restore of cops_104/perl/kuang.1 failed'
fi
echo 'End of part 16'
echo 'File cops_104/perl/kuang.1 is continued in part 17'
echo 17 > _shar_seq_.tmp
exit 0