|
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: 55517 (0xd8dd) Types: TextFile Names: »cops.04«
└─⟦4f9d7c866⟧ Bits:30007245 EUUGD6: Sikkerheds distributionen └─⟦this⟧ »./cops/1.02/shars/cops.04«
#!/bin/sh # This is part 04 of cops # ============= cops/init_kuang ============== if test ! -d 'cops'; then echo 'x - creating directory cops' mkdir 'cops' fi if test -f 'cops/init_kuang' -a X"$1" != X"-c"; then echo 'x - skipping cops/init_kuang (File already exists)' else echo 'x - extracting cops/init_kuang (Text)' sed 's/^X//' << 'SHAR_EOF' > 'cops/init_kuang' && X# /* Copyright 1985 Robert W. Baldwin */ X# /* Copyright 1986 Robert W. Baldwin */ X############################################### X# Kuang: Rule based computer security checker. X############################################### X XCAT=/bin/cat XECHO=/bin/echo X X# X# Initialization. X# X./clearfiles X# X# First setup what we have access to. X# The uids.k file must include the user 'OTHER' meaning the world access bits. X# Add any other UIDs accessible to the attacker (e.g., ftp, daemon). X# X# Directly accessible user IDs. X$CAT >uids.k <<END XOTHER XEND X# X# Directly accessible group IDs. X# This usually includes a group like 'users', which most users are in. X# X$CAT >gids.k <<END XEND X# X# Setup the primary goal(s). X# X$ECHO Setting up goal #>/dev/tty X./addto uids root DO ANYTHING SHAR_EOF chmod 0700 cops/init_kuang || echo 'restore of cops/init_kuang failed' Wc_c="`wc -c < 'cops/init_kuang'`" test 773 -eq "$Wc_c" || echo 'cops/init_kuang: original size 773, current size' "$Wc_c" fi # ============= cops/is_able.chk ============== if test -f 'cops/is_able.chk' -a X"$1" != X"-c"; then echo 'x - skipping cops/is_able.chk (File already exists)' else echo 'x - extracting cops/is_able.chk (Text)' sed 's/^X//' << 'SHAR_EOF' > 'cops/is_able.chk' && 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_{read|write}able" program to check if any of X# the directories in question are writable by world/group. All results X# are written to standard output. X# XTEST=/bin/test XECHO=/bin/echo XAWK=/bin/awk XSED=/bin/sed X Xconfig_file=is_able.lst X Xif $TEST ! -f "$config_file" ; then X $ECHO "Config file $config_file doesn't exist!" X exit X fi X X# Read from $dir_list (e.g. "is.chk.lst") what files/dirs to check. X# X# Comments are lines starting with a "#". X# X# /path/to/{dir|file} World/Group Read/Write/Both X# as above {W|w|G|g} {R|r|W|w|B|b} X# X{ X$AWK '/^#/ {next;} \ X { world=group=read=write=both=0; \ X # need 3 fields, or format error X if (NF != 3) next; \ X if ($2 != "W" && $2 != "w" && $2 != "G" && $2 != "g") next; \ X if ($3!="R"&&$3!="r"&&$3!="W"&&$3!="w"&&$3!="B"&&$3!="b") next; \ X for (f=1;f < NF; f++) printf("%s ", $f); \ X print $NF; X }' $config_file X} | Xwhile read targets X do X # Use sed, 'cause awk lets me down (line too long) -- then realize X # I should have used sed anyway. Lazy bum. X foo=`echo "$targets" | $SED 's/\(.*\)....$/\1/'` X args=`echo $targets | $SED 's/.*\(...\)$/\1/'` X for f in $foo X do X# echo $f $args X ./is_able $f $args X done X done X X# end of script SHAR_EOF chmod 0700 cops/is_able.chk || echo 'restore of cops/is_able.chk failed' Wc_c="`wc -c < 'cops/is_able.chk'`" test 1637 -eq "$Wc_c" || echo 'cops/is_able.chk: original size 1637, current size' "$Wc_c" fi # ============= cops/is_able.lst ============== if test -f 'cops/is_able.lst' -a X"$1" != X"-c"; then echo 'x - skipping cops/is_able.lst (File already exists)' else echo 'x - extracting cops/is_able.lst (Text)' sed 's/^X//' << 'SHAR_EOF' > 'cops/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# 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/usr/bin w w X/usr/etc w w X/usr/adm w w X/usr/lib 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/local/lib/* w w X/usr/local/bin/* 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 w b X X# NEVER want these 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 SHAR_EOF chmod 0600 cops/is_able.lst || echo 'restore of cops/is_able.lst failed' Wc_c="`wc -c < 'cops/is_able.lst'`" test 1547 -eq "$Wc_c" || echo 'cops/is_able.lst: original size 1547, current size' "$Wc_c" fi # ============= cops/kuang ============== if test -f 'cops/kuang' -a X"$1" != X"-c"; then echo 'x - skipping cops/kuang (File already exists)' else echo 'x - extracting cops/kuang (Text)' sed 's/^X//' << 'SHAR_EOF' > 'cops/kuang' && X: X# /* Copyright 1985 Robert W. Baldwin */ X# /* Copyright 1986 Robert W. Baldwin */ X# X# Jan 1990, Ported to bourne shell from Csh. Dan Farmer X# X# Took out some comments, combined four of Bob's shell X# scripts into one (the target script remains separate for X# easy editing of targets.) More or less a straight line X# for line translation; a rewrite that goes for speed will X# come later. Maybe just rewrite it in C. Yeah, that's it.... X X############################################### X# Kuang: Rule based computer security checker. X############################################### X X# commands used.... XSH=/bin/sh XMV=/bin/mv XTEST=/bin/test XECHO=/bin/echo XAWK=/bin/awk XRM=/bin/rm X X# Initialization. X$SH ./init_kuang X X# Main loop X# X$ECHO Starting main loop #>/dev/tty Xwhile $TEST -f uids.n -o -f gids.n -o -f files.n X do X if $TEST -f uids.n ; then X $MV uids.n uids.x X X# Process a list of uids from stdin. X# Usage: douids username comments X $ECHO Called douids #>/dev/tty X i=1 X while $TEST "1" X do X nextuid=`$AWK '{if (NR=="'$i'") print $0}' uids.x` X i=`expr $i + 1` X X if $TEST -z "$nextuid" ; then X break; X fi X X user=`$ECHO $nextuid | $AWK '{print $1}'` X X $ECHO " " User $user #>/dev/tty X X# Rules mapping uids to files. X# X next=`$ECHO $nextuid | $AWK '{for (i=2;i<=NF;i++) printf("%s ", $i)}'` X ./addto files /etc/passwd replace grant $user $next X ./addto files /usr/lib/aliases replace trojan $user $next X X# hsh = home sweet home = home directory of $user X hsh=`./tilde $user` X X if $TEST -f $hsh/.rhosts ; then X ./addto files $hsh/.rhosts write grant $user $next X fi X X if $TEST -f $hsh/.login ; then X ./addto files $hsh/.login replace trojan $user $next X fi X X if $TEST -f $hsh/.cshrc ; then X ./addto files $hsh/.cshrc replace trojan $user $next X fi X X if $TEST -f $hsh/.profile ; then X ./addto files $hsh/.profile replace trojan $user $next X fi X X if $TEST "$user" = "root" ; then X if $TEST -f /usr/lib/crontab ; then X ./addto files /usr/lib/crontab replace create supershell $next X else X ./addto files /usr/spool/cron/crontabs replace create supershell $next X fi X ./addto files /etc/rc replace trojan $user $next X ./addto files /etc/rc.local replace trojan $user $next X fi X X if $TEST "$user" != "root" ; then X ./addto files /etc/hosts.equiv replace allow rlogin $next X fi X X if $TEST "$user" != "root" -a -f /etc/hosts.equiv -a -s /etc/hosts.equiv X then X ./addto files /etc/hosts replace fake HostAddress $next X fi X X done Xfi X X if $TEST -f gids.n ; then X $MV gids.n gids.x X X $ECHO Called dogids #>/dev/tty X i=1 X while $TEST "1" X do X nextgid=`$AWK '{if (NR=="'$i'") print $0}' gids.x` X i=`expr $i + 1` X X if $TEST -z "$nextgid" ; then X break; X fi X X group=`$ECHO $nextgid | $AWK '{print $1}'` X $ECHO " " Group $group #>/dev/tty X X# Rules mapping gids to uids. X# X next=`$ECHO $nextgid | $AWK '{for (i=2;i<=NF;i++) printf("%s ", $i)}'` X use=`./members $group` X for user in $use X do X ./addto uids $user grant $group $next X done X X# Rules mapping gids to files. X# X ./addto files /etc/group replace grant $group $next X done X fi X X if $TEST -f files.n ; then X $MV files.n files.x X X# A list of file names is read from successive lines of stdin. X# Each file is examined for ways to access it. X# The input format is: X# <filename> <whitespace> <mode> <comments> X# The <mode> is either "write" or "replace". X# X $ECHO Called dofiles. #>/dev/tty X i=1 X while $TEST "1" X do X nextfile=`$AWK '{if (NR=='"$i"') print $0}' files.x` X i=`expr $i + 1` X if $TEST -z "$nextfile" ; then X break; X fi X X file=`$ECHO $nextfile | $AWK '{print $1}'` X mode=`$ECHO $nextfile | $AWK '{print $2}'` X X $ECHO " File $file, mode $mode" #>/dev/tty X X# Rules converting filename goals into UserName or GroupName goals. X# X next=`$ECHO $nextfile | $AWK '{for (i=3;i<=NF;i++) printf("%s ", $i)}'` X X writers=`./filewriters $file` X numwriters=`$ECHO $writers | $AWK '{print NF}'` X if $TEST "$numwriters" = "3" ; then X owner=`$ECHO $writers | $AWK '{print $1}'` X group=`$ECHO $writers | $AWK '{print $2}'` X other=`$ECHO $writers | $AWK '{print $3}'` X X $ECHO " Writers are $owner $group $other" #>/dev/tty X ./addto uids $owner $mode $file $next X if $TEST "$group" != "NONE" ; then X ./addto gids $group $mode $file $next X fi X if $TEST "$other" != "NONE" ; then X ./addto uids $other $mode $file $next X fi X else X $ECHO " $file does not exist" #>/dev/tty X continue X fi X X# Rules converting filename goals into other filename goals. X# X if $TEST "$mode" != "replace" ; then X continue X fi X X parent=`$ECHO $file | $AWK -F/ '{if (NF == 2) { X printf("/%s", $1)} X else if (NF>2) {for (i=2;i<NF;i++) printf("/%s", $i)} X else printf("")'}` X X basename=`$ECHO $file | $AWK -F/ '{print $NF}'` X X $ECHO -n " " Parent directory is $parent #>/dev/tty X $ECHO ", " basename is $basename #>/dev/tty X if $TEST -n "$parent" ; then X ./addto files $parent write replace $basename $next X fi X done X X fi Xdone X X# destroy the evidence.... Need "Success" file for report, though. X$RM files.? gids.? uids.? SHAR_EOF chmod 0700 cops/kuang || echo 'restore of cops/kuang failed' Wc_c="`wc -c < 'cops/kuang'`" test 5969 -eq "$Wc_c" || echo 'cops/kuang: original size 5969, current size' "$Wc_c" fi # ============= cops/kuang.pl.shar ============== if test -f 'cops/kuang.pl.shar' -a X"$1" != X"-c"; then echo 'x - skipping cops/kuang.pl.shar (File already exists)' else echo 'x - extracting cops/kuang.pl.shar (Text)' sed 's/^X//' << 'SHAR_EOF' > 'cops/kuang.pl.shar' && X#!/bin/sh X# This is a shell archive (shar 3.10) X# made 01/01/1991 01:09 UTC by df@death.cert.sei.cmu.edu X# X# existing files WILL be overwritten X# X# This shar contains: X# length mode name X# ------ ---------- ------------------------------------------ X# 9782 -rw------- README X# 1776 -rwx------ get-cf X# 16925 -rwx------ kuang X# 6490 -rw------- kuang.1 X# 284 -rwx------ kuang_all X# 1307 -rw------- put-cf X# 1274 -rw------- yagrip.pl X# Xtouch 2>&1 | fgrep '[-amc]' > /tmp/s3_touch$$ Xif [ -s /tmp/s3_touch$$ ] Xthen X TOUCH=can Xelse X TOUCH=cannot Xfi Xrm -f /tmp/s3_touch$$ X# ============= README ============== Xsed 's/^X//' << 'SHAR_EOF' > README && XXThis is a perl version of Dan's version of Bob Baldwin's Kuang program XX(originally written as some shell scripts and C programs). XX XXThe original intent was to improve the speed of kuang, which is XXespecially important for installations like ours with several thousand XXaccounts and NFS things and all that. The shell version of Kuang used XXC programs to add rules, get a groups members, determine the writers XXof a file, and so on, which really slowed things down. XX XX "no" problems /etc staff writeable XX ------------- -------------------- XXshell kuang 2:14 (14) 12:26 (98) 0.1 p/s XXperl kuang 1:10 (18) 2:34 (588) 3.8 p/s XX XXThe "no" problems column indicates the time taken (and number of plans XXconsidered) for the shell and Perl versions of Kuang on a system with XXno known security problems. The "/etc staff writeable" column gives XXtiming and # of plans for a system with a /etc directory that is XXwriteable by group staff, which contains several dozen users. XX XXAs you can see, the Perl version is a bit faster. Turns out there are XXall sorts of details that need to be considered in real XXimplementations of Kuang type programs, some of which are discussed XXbelow. XX XX --- Steve Romig, CIS, Ohio State, October 1990 XX XX------------------------------------------------------------------------------ XX XXSome Features of the Perl Version XX XX Caches passwd/group file entries in an associative array for faster XX lookups. This is particularly helpful on insecure systems using YP XX where password and group lookups are slow and you have to do a lot of XX them...:-) XX XX Can specify target (uid or gid) on command line. XX XX Can use -l option to generate PAT for a goal. XX XX Can use -f to preload file owner, group and mode info, which is XX helpful in speeding things up and in avoiding file system XX 'shadows'... See the man page for details. XX XXFuture plans, things to fix: XX XX- An earlier version scanned the password file looking for generally XX accessible accounts (no password), which would be added to the XX uids.known list (in addition to -1, "other"). I had planned on also XX adding a password checker which would allow us to also add accounts XX with easily guessed passwords. Eventually I nuked the code that XX scanned the password file to speed things up, and further reflection XX reveals that it isn't wise to add the password scanning to Kuang XX itself (since there are many other things that might be considered XX in determining whether an account is accessible or not, and you XX probably don't want to add them all to Kuang). XX XX At some point we should add a command line option that allows us to XX add additional uid's (or gid's?) to the uids.known list. That way XX the user could run some other tool to scan the password file and XX generate a list of accessible accounts, which could then be fed to XX kuang. Makes it faster on clients using YP since most of the XX password file is the same for all N clients, why scan it N times. XX This would make it easier for the Kuang user to do smarter things XX to/with the password file checks (list all accounts with no password XX or easily guessed password, filter out "ok" entries (eg, sync) and XX etc.) XX XX- This version doesn't deal with uid's and gid's correctly. If there XX are several entries that list the same UID, but with different XX names, directories and shells, we'll only check plans for becoming XX one of them, rather than any of them, so some possible plans aren't XX even examined. XX XX Hmmm...this is easier than I thought - when we evaluate some plan XX for granting a particular uid, we need to evaluate plans for all XX usernames that can become that uid. Just stick a loop in there XX somewhere...get CF's for each of username's in turn. XX XX Bah, harder than I thought, since it'd have to scan the whole XX password file to figure which username/home directories can become XX which uid's. Similarly with groups. XX XX Current plan: by default, kuang will have to scan the whole password XX and group files so it can be sure to get all possible ways to become XX some uid or gid. Internally, really need several lists: XX XX mapping from uid to list of usernames that have that uid XX mapping from a username to home directory, shell XX mapping from gid to list of uids that have access to that XX gid when they login (either member of group with that gid or XX given as login group in passwd file) XX mapping from gid to list of group names for that gid XX XX Course, this means that we have to read the whole password and group XX file, most of which will be common to many machines (like in a YP XX environment). We could preload the tables above from files created XX once, containing the brunt of the YP info, and then augment that XX with the local passwd and group info on each host when kuang is XX invoked, but then we need to correctly interpret funky YP things XX like +@netgroup:::*:..., which means that the uid has a name but no XX password here...and similarly with shell substitutions and so on. XX Bah. XX XX- In a large environment (like ours, 260+ machines, 30+ file systems XX on as many servers, 2000 password file entries served by YP) it XX would be nice to 'precompute' successful plans that would be common XX to all systems. In particular, plans for becoming most of the users XX with home directories on the NFS file systems would be useful, since XX we don't really want to recheck these on each host. You wouldn't XX want the plan to be too deep - probably shouldn't span more than 2 XX uids (1 on each end: grant u.romig grant g.staff write ~foo/.login XX grant u.foo). I'm thinking that you could feed a list of these XX precomputed plans to kuang and add some code that causes it to XX splice in relevant plans where it can to short cut the planning XX steps. For example, if one of the plans in uids.next is something XX like "grant u.foo ...", and I have the precomputed plan mentioned XX above, I could splice the two: "grant u.romig grant g.staff write XX ~foo/.login grant u.foo ..." and skip all the normal steps that XX would've been taken to get there. XX XX I'm not sure this is even feasible or useful. Food for thought. XX XX- Hmmm...thinking about it, it seems like some of the steps are a bit XX too implicit...maybe the rules should be broken out a bit more. XX That will cost in processing time, though. XX XX- Would be really, really nice to be able to deal with PATH variables XX - location of ., who can write elements of path, etc. Basic rule is XX "anyone who can replace anything in any of path directories or the XX path directories themselves can become that PATH's user..." This XX can be really messy though - in our environment, the path for a user XX will depend on the architecture type of the machine that he is XX logged into, and to get the path, you'd have to read and interpret XX his .login (including variable assignments, source's and XX conditionals). Urf. One wonders whether it might be better to have XX something running as root that su's to each username in turn and XX gets the path that way...:-) XX XX- The kuang described in Baldwin's dissertation is somewhat different XX in nature from this one. The original computes a Privilege Access XX Table (PAT) which describes for each uid and gid which uids have XX access to that uid. To assess security, we compare this against the XX security policy for the site, which similarly describes which uid's XX are supposed to have access to each uid and gid. A sample SP might XX be that each uid should be accessible only by itself and root, and XX each gid should be accessible only to the members of that group and XX root. If the PAT listed additional uid's for some priv, that would XX constitute a violation of the Security Policy for the site. XX XX The current kuang is different. It registers Success (a problem was XX found) if it determines that some uid in the uids.known list (-1, XX "other" by default) can access the target privilege. It may find XX along the way that extra uids can access some uid, but these aren't XX reported as specific problems unless they are added to the XX uids.known list. XX XX We could do something similar to the kuang described in the paper by XX setting uids.known to be all the uids that aren't in the security XX policy table for the target uid, and running kuang against the XX target. This would report success for each uid that could access XX the target. You could do similar things with groups - uids.known XX would be all the uids that aren't members of the group... XX XX Alternately, we could simply have kuang record the list of uids that XX can access the target priv and print the list when its done. That XX way you could iterate kuang against all uids and gids and compare XX the resulting PAT against your security policy and record the XX differences. You'd probably want to record the plan for each uid XX reported also. XX XX On our system this would mean running kuang roughly 2500 XX times to check 1 host, and we have about 300 hosts...urf...assuming XX that each kuang invocation has to check 50 plans, that's a total of XX 125,000 plans per host, or about an hour of real time...not as bad XX as it could be, though. XX XX- It would be nice to add to the list of rules. It would be especially XX nice to extract the rules from the code so that we can create site XX specific rule files (for example, we use X11r4 here, and many users XX have a .Xinitrc that contains shell commands that get executed when XX they login.) XX XX Easiest way to do this would be to extract the rules as Perl code so XX we can take advantage of conditionals and so on, and include them XX within the body of kuang somehow. A sample rule in perl: XX XX if (&shell($uid) eq "/bin/csh") { XX &addto("files", &home($uid)."/.login", XX "replace .login $plan"); XX } XX XX which simply means "if the user's shell is csh, then try to replace XX his .login file." XX XSHAR_EOF Xchmod 0600 README || echo "restore of README fails" Xif [ $TOUCH = can ] Xthen X touch -am 1228143490 README Xfi X# ============= get-cf ============== Xsed 's/^X//' << 'SHAR_EOF' > get-cf && XX#! /usr/local/bin/perl XX XX@dot_files = ( XX ".login", ".logout", ".cshrc", # csh, cshe or tcsh XX ".profile", # ksh, sh XX ".env", # ksh XX ".alias", ".aliases", # common for all shells XX "user.ps", ".user.ps", "tools.ps", ".tools.ps", XX "startup.ps", ".startup.ps", # NeWS XX ".mgrc", # MGR XX ".X11init", ".awmrc", ".twmrc", ".xinitrc", # X11 XX ".emacs" # emacs XX); XX XX%seen = {}; XX XXopen(HOST, "/bin/hostname |") || die "can't get the hostname"; XXchop($hostname=<HOST>); XXclose(HOST); XX XXuser_loop: XX for (($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent(); XX $name ne ""; XX ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent()) { XX XX # XX # If the user has a home directory on this server, get the info XX # about the directory, his CF's and so on. XX # XX if ($dir =~ m,^/n/$hostname/,) { XX if (! -d $dir) { XX printf(stderr "home directory '%s' for user '%s' doesn't exist.\n", XX $dir, XX $name); XX next user_loop; XX } XX XX ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, XX $atime,$mtime,$ctime,$blksize,$blocks) XX = stat(_); XX $mode = $mode & 07777; XX XX &spit_it_out("d", $uid, $gid, $mode, $dir); XX XX foreach $file (@dot_files) { XX $path = "$dir/$file"; XX XX if (-f $path) { XX ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, XX $atime,$mtime,$ctime,$blksize,$blocks) XX = stat(_); XX $mode = $mode & 07777; XX XX &spit_it_out("f", $uid, $gid, $mode, $dir); XX } XX } XX } XX } XX XX XX XX XXsub spit_it_out { XX local($type, $uid, $gid, $mode, $name) = @_; XX XX if (defined($seen{$name})) { XX return; XX } XX XX printf("%s %d %d 0%o %s\n", $type, $uid, $gid, $mode, $name); XX $seen{$name} = 1; XX} XX XSHAR_EOF Xchmod 0700 get-cf || echo "restore of get-cf fails" Xif [ $TOUCH = can ] Xthen X touch -am 1220223490 get-cf Xfi X# ============= kuang ============== Xsed 's/^X//' << 'SHAR_EOF' > kuang && XX#! /usr/local/bin/perl XX XX# XX# kuang - rule based analysis of Unix security XX# XX# Perl version by Steve Romig of the CIS department, The Ohio State XX# University, October 1990. XX# XX# Based on the shell script version by Dan Farmer from his COPS XX# package, which in turn is based on a shell version by Robert XX# Baldwin. XX# XX XXdo 'yagrip.pl' || XX die "can't do yagrip.pl"; XX XX$options = "vdlf:D"; XX$usage = "usage: kuang [-v] [-d] [-l] [-D] [-f filedata] [u.username|g.groupname]\n"; XX XX# XX# Simple Unix Kuang, a security checking program. XX# XX# This is a perl version of Dan Farmer's version of Bob Baldwin's XX# shell scripts. XX# XX XX# XX# passwd_byuid lookup a password entry by uid, return as XX# (name, password, directory, shell) list. XX# XXsub passwd_byuid { XX local($uid) = @_; XX local($name, $passwd, $gid, $quota, $comment, $gcos, $dir, $shell); XX XX if (! defined($passwd_byuid_list{$uid})) { XX ($name, $passwd, $t_uid, $gid, $quota, $comment, $gcos, $dir, $shell) = XX getpwuid($uid); XX XX if ($t_uid eq "") { XX return(); XX } XX XX $passwd_byuid_list{$uid} = join(':', $name, $passwd, $dir, $shell); XX $passwd_byname_list{$name} = $uid; XX } XX XX return(split(/:/, $passwd_byuid_list{$uid})); XX} XX XXsub passwd_byname { XX local($name) = @_; XX local($passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell); XX XX if (! defined($passwd_byname_list{$name})) { XX ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = XX getpwnam($name); XX XX if ($uid eq "") { XX return(); XX } XX XX $passwd_byuid_list{$uid} = join(':', $name, $passwd, $dir, $shell); XX $passwd_byname_list{$name} = $uid; XX } XX XX return($passwd_byname_list{$name}); XX} XX XX# XX# group_bygid lookup a group entry by gid, return as XX# (name, members) list. XX# XXsub group_bygid { XX local($gid) = @_; XX local($name, $t_passwd, $t_gid, $members); XX XX if (! defined($group_bygid_list{$gid})) { XX ($name, $t_passwd, $t_gid, $members) = getgrgid($gid); XX XX if ($t_gid eq "") { XX return(); XX } XX XX $group_bygid_list{$gid} = join(':', $name, $members); XX $group_byname_list{$name} = $gid; XX } XX XX return(split(/:/, $group_bygid_list{$gid})); XX} XX XXsub group_byname { XX local($name) = @_; XX local($gname, $passwd, $gid, $members); XX XX if (! defined($group_byname_list{$name})) { XX ($gname, $passwd, $gid, $members) = getgrnam($name); XX XX if ($gid eq "") { XX printf(stderr "A group named '$name' does not exist!\n"); XX exit(1); XX } XX XX $group_bygid_list{$gid} = join(':', $name, $members); XX $group_byname_list{$name} = $gid; XX } XX XX return($group_byname_list{$name}); XX} XX XX# XX# Do various initialization type things. XX# XX XXsub init_kuang { XX local($which, $name, $uid, $gid); XX local($f_type, $f_uid, $f_gid, $f_mode, $f_name); XX local($count); XX XX $which = "u"; XX $name = "root"; XX XX # XX # Deal with args... XX # XX XX &getopt($options) || XX die $usage; XX XX if ($#ARGV == 0) { XX ($which, $name) = split(/\./, $ARGV[0]); XX XX if ($name eq "") { XX $name = $which; XX $which = "u"; XX } XX XX if ($which ne "u" && $which ne "g") { XX printf(stderr "target must be given as u.user or g.group\n"); XX exit(1); XX } XX } elsif ($#ARGV > 0) { XX printf(stderr $usage); XX exit(1); XX } XX XX # XX # Preload the file data... XX # XX if (defined($opt_f)) { XX # XX # If we are dumping the file data to a DBM file, nuke the existing XX # ones and open the dbm file. Otherwise, open the DBM file XX # only if they exist. XX # XX $read_from_file = 1; XX XX if (defined($opt_D)) { XX unlink("$opt_f.dir", "$opt_f.pag"); XX XX dbmopen(files, $opt_f, 0644) || XX die sprintf("can't open DBM file '%s'", $opt_f); XX } elsif (-f "$opt_f.dir") { XX dbmopen(files, $opt_f, 0644) || XX die sprintf("can't open DBM file '%s'", $opt_f); XX XX $read_from_file = 0; XX } XX XX if ($read_from_file) { XX open(FILEDATA, $opt_f) || XX die sprintf("kuang: can't open '%s'", $opt_f); XX XX $count = 0; XX while (<FILEDATA>) { XX $count++; XX XX chop; XX ($f_type, $f_uid, $f_gid, $f_mode, $f_name) = split; XX XX if ($count % 1000 == 0) { XX printf("line $count, reading entry for $f_name\n"); XX } XX $files{$f_name} = join(' ', $f_uid, $f_gid, $f_mode); XX } XX XX close(FILEDATA); XX } XX } XX XX if (defined($opt_D)) { XX dbmclose(files); XX XX exit(0); XX } XXif (defined($opt_v)) { XX printf("done with files\n"); XX } XX # XX # Need some of the password and group stuff. Suck in passwd and XX # group info, store by uid and gid in an associative array of strings XX # which consist of fields corresponding to the passwd and group file XX # entries (and what the heck, we'll use : as a delimiter also...:-) XX # XX XX $passwd_byuid_list{-1} = "OTHER:::"; # add an entry for OTHER XX $passwd_byname_list{"OTHER"} = -1; XX XX $uids_known{-1} = ""; # we can access OTHER XX %uids_new = (); XX XX %gids_known = (); XX %gids_new = (); XX XX %files_new = (); XX XX # XX # Set up initial goal: become target user or group XX # XX if ($which eq "u") { XX $uid = &passwd_byname($name); XX if ($uid ne "") { XX &addto("uids", $uid, "grant u.$name do anything"); XX } else { XX printf(stderr "There is no user with username '$name'.\n"); XX exit(1); XX } XX } else { XX $gid = &group_byname($name); XX if ($gid ne "") { XX &addto("gids", $gid, "grant g.$name"); XX } else { XX printf(stderr "There is no group named '$name'.\n"); XX exit(1); XX } XX } XX} XX XX# XX# Get the home directory for this UID from the passwd file cache. XX# XXsub gethome { XX local($uid) = @_; XX local($tmp, $home); XX XX ($tmp, $tmp, $home, $tmp) = &passwd_byuid($uid); XX return($home); XX} XX XX# XX# Get the writers of the named file - return as (UID, GID, OTHER) XX# triplet. Owner can always write, since he can chmod the file if he XX# wants. XX# XX# (fixme) are there any problems in this sort of builtin rule? should XX# we make this knowledge more explicit? XX# XXsub filewriters { XX local($name) = @_; XX local($tmp, $mode, $uid, $gid, $other); XX XX # XX # Check the file cache - avoid disk lookups for performance and XX # to avoid shadows... XX # XX if (defined($files{$name})) { XX $cache_hit++; XX XX ($uid, $gid, $mode) = split(/ /, $files{$name}); XX $mode = oct($mode); XX } else { XX $cache_miss++; XX XX if (! -e $name && $read_from_file) { XX $files{$name} = ""; XX return; XX } XX XX ($tmp,$tmp,$mode,$tmp,$uid,$gid) = stat(_); XX if ($read_from_file) { XX $files{$name} = join(' ', $uid, $gid, $mode); XX } XX } XX XX if (($mode & 020) != 020) { XX $gid = ""; XX } XX XX if (($mode & 02) == 02) { XX $other = 1; XX } else { XX $other = 0; XX } XX XX return($uid, $gid, $other); XX} XX XX# XX# return # of entries in given associative array. XX# XXsub sizeof { XX local(*which) = @_; XX local(@keywords); XX XX @keywords = keys %which; XX return($#keywords + 1); XX} XX XX# XX# return appropriate entry from named associative array of given type. XX# returns a (key, value) pair - if key is "", there was no entry. XX# XXsub getentry { XX local($which, $type, $key) = @_; XX local($newkey, $value); XX XX $newkey = ""; XX $value = ""; XX XX which: { XX if ($which eq "uids") { XX type0: { XX if ($type eq "known") { XX if (defined($uids_known{$key})) { XX $newkey = $key; $value = $uids_known{$key}; XX } XX last type0; XX } XX XX if ($type eq "new") { XX if (defined($uids_new{$key})) { XX $newkey = $key; $value = $uids_new{$key}; XX } XX last type0; XX } XX XX if ($type eq "pending") { XX if (defined($uids_pending{$key})) { XX $newkey = $key; $value = $uids_pending{$key}; XX } XX last type0; XX } XX XX if ($type eq "old") { XX if (defined($uids_old{$key})) { XX $newkey = $key; $value = $uids_old{$key}; XX } XX last type0; XX } XX XX printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX if ($which eq "gids") { XX type1: { XX if ($type eq "known") { XX if (defined($gids_known{$key})) { XX $newkey = $key; $value = $gids_known{$key}; XX } XX last type1; XX } XX XX if ($type eq "new") { XX if (defined($gids_new{$key})) { XX $newkey = $key; $value = $gids_new{$key}; XX } XX last type1; XX } XX XX if ($type eq "pending") { XX if (defined($gids_pending{$key})) { XX $newkey = $key; $value = $gids_pending{$key}; XX } XX last type1; XX } XX XX if ($type eq "old") { XX if (defined($gids_old{$key})) { XX $newkey = $key; $value = $gids_old{$key}; XX } XX last type1; XX } XX XX printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX if ($which eq "files") { XX type2: { XX if ($type eq "known") { XX if (defined($files_known{$key})) { XX $newkey = $key; $value = $files_known{$key}; XX } XX last type2; XX } XX XX if ($type eq "new") { XX if (defined($files_new{$key})) { XX $newkey = $key; $value = $files_new{$key}; XX } XX last type2; XX } XX XX if ($type eq "pending") { XX if (defined($files_pending{$key})) { XX $newkey = $key; $value = $files_pending{$key}; XX } XX last type2; XX } XX XX if ($type eq "old") { XX if (defined($files_old{$key})) { XX $newkey = $key; $value = $files_old{$key}; XX } XX last type2; XX } XX XX printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX printf(stderr "kuang: fatal error in getentry: which is wrong (%s)\n", XX $which); XX exit(1); XX } XX XX return($newkey, $value); XX} XX XX XX# XX# stores a (key, value) in the associative array of the given type. XX# XXsub putentry { XX local($which, $type, $key, $value) = @_; XX XX which: { XX if ($which eq "uids") { XX type0: { XX if ($type eq "known") { XX $uids_known{$key} = $value; last type0; XX } XX XX if ($type eq "new") { XX $uids_new{$key} = $value; last type0; XX } XX XX if ($type eq "pending") { XX $uids_pending{$key} = $value; last type0; XX } XX XX if ($type eq "old") { XX $uids_old{$key} = $value; last type0; XX } XX XX printf(stderr "kuang: fatal error in putentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX if ($which eq "gids") { XX type1: { XX if ($type eq "known") { XX $gids_known{$key} = $value; last type1; XX } XX XX if ($type eq "new") { XX $gids_new{$key} = $value; last type1; XX } XX XX if ($type eq "pending") { XX $gids_pending{$key} = $value; last type1; XX } XX XX if ($type eq "old") { XX $gids_old{$key} = $value; last type1; XX } XX XX printf(stderr "kuang: fatal error in putentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX if ($which eq "files") { XX type2: { XX if ($type eq "known") { XX $files_known{$key} = $value; last type2; XX } XX XX if ($type eq "new") { XX $files_new{$key} = $value; last type2; XX } XX XX if ($type eq "pending") { XX $files_pending{$key} = $value; last type2; XX } XX XX if ($type eq "old") { XX $files_old{$key} = $value; last type2; XX } XX XX printf(stderr "kuang: fatal error in putentry: type is wrong (%s)\n", XX $type); XX exit(1); XX } XX XX last which; XX } XX XX printf(stderr "kuang: fatal error in putentry: which is wrong (%s)\n", XX $which); XX exit(1); XX } XX} XX XX XXsub addto { XX local($which, $key, $plan) = @_; XX local($tkey, $tvalue); XX XX # XX # See whether there's an entry for $key in the known list for the XX # $which array. If so - success, we've found a suitable breakin XX # path. XX # XX XX ($tkey, $tvalue) = &getentry($which, "known", $key); XX if ($tkey eq $key) { XX printf("Success! $key $plan\n"); XX return; XX } XX XX # XX # Check to see if its a duplicate - if so, don't need to do anything. XX # XX ($tkey, $tvalue) = &getentry($which, "pending", $key); XX if ($tkey eq $key) { XX return; XX } XX XX # XX # Add to pending list for $which... XX # XX &putentry($which, "pending", $key, $plan); XX XX # XX # Add to next goal list for $which... XX # XX &putentry($which, "new", $key, $plan); XX XX if (defined($opt_v)) { XX printf("addto: $which --> $plan\n"); XX } XX XX # XX # If this is a uid goal, then add the plan to the accessible list. XX # XX if ($which eq "uids" && $key ne "0" && defined($opt_l)) { XX $accessible{$plan} = "1"; XX } XX} XX XX# XX#---------------------------------------------------------------------- XX#Main program follows...initialize and loop till we're done. XX# XX XX&init_kuang(); XX XX# XX# While there's still something to pursue... XX# XXwhile (&sizeof(*uids_new) != 0 || XX &sizeof(*gids_new) != 0 || XX &sizeof(*files_new) != 0) { XX XX # XX # Deal with uids first... XX # XX if (&sizeof(*uids_new) != 0) { XX %uids_old = %uids_new; XX %uids_new = (); XX XX foreach $uid (keys %uids_old) { XX $plan = $uids_old{$uid}; XX XX if (defined($opd_d)) { XX printf("uids evel: $uid '$plan'\n"); XX } XX XX &addto("files", "/etc/passwd", "replace /etc/passwd $plan"); XX &addto("files", "/usr/lib/aliases", "replace /usr/lib/aliases $plan"); XX XX # XX # Add controlling files for this user. There are probably XX # others (such as .logout, X tool start things and so on). XX # (fixme) add other CF's... XX # XX $home = &gethome($uid); XX XX if ($home ne "") { XX if ($home eq "/") { XX $home = ""; XX } XX XX if (-e "$home/.rhosts") { XX &addto("files", "$home/.rhosts", "write $home/.rhosts $plan"); XX } XX XX if (-e "$home/.login") { XX &addto("files", "$home/.login", "replace $home/.login $plan"); XX } XX XX if (-e "$home/.logout") { XX &addto("files", "$home/.logout", "replace $home/.logout $plan"); XX } XX XX if (-e "$home/.cshrc") { XX &addto("files", "$home/.cshrc", "replace $home/.cshrc $plan"); XX } XX XX if (-e "$home/.profile") { XX &addto("files", "$home/.profile", "replace $home/.profile $plan"); XX } XX } XX XX # XX # Controlling files for root... XX # XX if ($uid+0 == 0) { XX foreach $file ("/etc/rc", "/etc/rc.boot", "/etc/rc.single", "/etc/rc.config", "/etc/rc.local", "/usr/lib/crontab", "/usr/spool/cron/crontabs") { XX if (-e $file) { XX &addto("files", $file, "replace $file $plan"); XX } XX } XX } XX XX if ($uid+0 != 0) { XX &addto("files", "/etc/hosts.equiv", "replace /etc/hosts.equiv allow rlogin $plan"); XX XX if (-s "/etc/hosts.equiv") { XX &addto("files", "/etc/hosts", "replace /etc/hosts fake hostaddress allow rlogin $plan"); XX } XX } XX } XX } XX XX # XX # Deal with groups... XX # XX if (&sizeof(*gids_new) != 0) { XX %gids_old = %gids_new; XX %gids_new = (); XX XXbar_loop: XX foreach $gid (keys %gids_old) { XX $plan = $gids_old{$gid}; XX if (defined($opt_d)) { XX printf("gids eval: $gid '$plan'\n"); XX } XX XX ($gname, $members) = &group_bygid($gid); XX if ($gname eq "") { XX printf("There is no group with gid $gid.\n"); XX next bar_loop; XX } XX XXfoo_loop: XX foreach $uname (split(/[ \t\n]+/, $members)) { XX $uid = &passwd_byname($uname); XX XX if ($uid eq "") { XX printf(stderr "Group $gname has an unknown user $uname\n"); XX next foo_loop; XX } XX XX &addto("uids", "$uid", "grant u.$uname $plan"); XX } XX XX &addto("files", "/etc/group", "replace /etc/group $plan"); XX } XX } XX XX # XX # Deal with files... XX # XX if (&sizeof(*files_new) != 0) { XX %files_old = %files_new; XX %files_new = (); XX XXfile_loop: XX foreach $file (keys %files_old) { XX $plan = $files_old{$file}; XX ($mode) = split(/[ \t\n]+/, $plan); XX XX if (defined($opt_d)) { XX printf("files eval: $file '$plan'\n"); XX } XX XX ($owner, $group, $other) = &filewriters($file); XX XX if ($owner eq "") { XX printf("%s does not exist\n", $file); XX next file_loop; XX } XX XX ($uname) = &passwd_byuid($owner); XX if ($uname eq "") { XX $uname = $owner; XX } XX XX &addto("uids", $owner, "grant u.$uname $plan"); XX XX if ($group ne "") { XX ($gname, $tmp) = &group_bygid($group); XX XX if ($gname ne "") { XX &addto("gids", $group, "grant g.$gname $plan"); XX } else { XX printf(stderr "There is no group with gid $group.\n"); XX } XX } XX XX if ($other) { XX &addto("uids", -1, "grant u.OTHER $plan"); XX } XX XX if ($mode eq "replace") { XX $parent = $file; XX $parent =~ s|/[^/]*$||; # strip last / and remaining XX XX if ($parent eq "") { # if nothing left, use / XX $parent = "/"; XX } XX XX if ($parent ne $file) { # since $file might've been / XX &addto("files", $parent, "replace $parent $plan"); XX } XX } XX } XX } XX} XX XXif (defined($opt_l)) { XX foreach $key (keys %accessible) { XX printf("$key\n"); XX } XX} XX XXif (defined($opt_v) || $cache_hit) { XX printf("File info cache hit/access ratio: %g\n", XX $cache_hit / ($cache_hit + $cache_miss)); XX } XSHAR_EOF Xchmod 0700 kuang || echo "restore of kuang fails" Xif [ $TOUCH = can ] Xthen X touch -am 1228143890 kuang Xfi X# ============= kuang.1 ============== Xsed 's/^X//' << 'SHAR_EOF' > kuang.1 && XX.TH KUANG 1 "4 October 1990" XX.SH NAME XXkuang \- find security problems through rule based analysis XX.SH SYNOPSIS XX.B kuang XX.RB "[\|" \-v "\|]" XX.RB "[\|" \-d "\|]" XX.RB "[\|" \-l "\|]" XX.RB "[\|" \-D "\|]" XX.RB "[\|" \-f filedata "\|]" XX.RB "[\|" XX.IR u.username "\|]" XX.br XX.B kuang XX.RB "[\|" \-v "\|]" XX.RB "[\|" \-d "\|]" XX.RB "[\|" \-l "\|]" XX.RB "[\|" \-D "\|]" XX.RB "[\|" \-f filedata "\|]" XX.RB "[\|" XX.IR g.groupname "\|]" XX.br XX.SH DESCRIPTION XX.LP XX.B kuang XXuses rule based analysis to examine the current security configuration XXof a site and determine whether certain security problems exist. XX XX.B kuang XXcontains embedded rules that describe the projection model and XXsome of the attacker tricks used on Unix systems. It uses these rules XXto reason backward from a desired goal (such as "grant u.root"), XXgenerating potential "attack" plans from the rules and file system XXstate and then evaluating them to see whether they are reachable XXaccording to the state recorded in the password and group files and in XXthe ownership and modes of the file systems. XX XXBy default, XX.B kuang XXuses "grant u.root" as its initial goal. You can change that by XXspecifying a username (u.username) or groupname (g.groupname) on the XXcommand line. Normally XX.B kuang XXdetermines a plan to be successful if it determines that anyone XX(u.other) can become the initial goal. XX XXThe XX.B \-v XXoption causes XX.B kuang XXto print a message about every plan added to the evaluation list. XXThis can help one to understand how XX.B kuang XXworks. The XX.B \-d XXoption causes XX.B kuang XXto print a message when it evaluates a plan to determine whether to XXretain it and add onto it or ignore it. These options will often XXproduce lots of output, beware. XX XXNormally XX.B kuang XXonly registers success when it finds that everyone on the system can XXbecome the target uid or gid. With the XX.B \-l XXoption, XX.B kuang XXwill list every uid that can become the goal. This provides a more XXcomplete picture of the state of security - you might deem it a XXproblem if several users can become root, even if the rest cannot. XX XXOne might adopt the view that each uid should only be accessible by XXitself and root, and that each gid should be accessible only by the XXmembers of that group and root. One can then compare the expected XXaccess list for a given uid or gid against the XX.B kuang XXgenerated list to find security problems that XX.B kuang XXwouldn't ordinarily tell you about. XX XXThe goals that XX.B kuang XXuse seem cryptic, but are really pretty straightforward. Each goal XXconsists of a list of <action> <object> pairs. Typical actions are XXgrant, write and replace. Typical objects are user names XX(u.username), group names (g.groupname) and files names. The goal XX"grant u.root" means to have access to the root UID (0), in other XXwords, to be able to run any program using that uid. Similarly, XX"grant g.staff" means to have access to group staff. The long goal XX"grant u.bill grant g.graphics replace /n/shoe/0/fred replace XX/n/shoe/0/fred/.profile grant u.fred grant g.staff" means become XXuser bill, get access to the graphics group, replace the file XX/n/shoe/0/fred, replace /n/shoe/0/fred/.profile, become fred, XXgrant access to the staff group. The problem that allows this to XXhappen is that the /n/shoe/0 directory is writeable by the graphics XXgroup, meaning that anyone in that group can replace the .profile file XXfor the fred user and gain access to that account and the groups it XXbelongs to when fred next logs in. Ooops. XX XXTo do a thorough job, XX.B kuang XXreally needs to be able to access all of XXthe controlling files of all users. In some environments, home XXdirectories are located in NFS mounted file systems where the client XXdoesn't have root access. XX XXProblem is that some home directories may be XXprotected so that group foo can read/write them, but OTHER can't. XX.B kuang XXrunning as some user not in group foo won't be able to read or XXsearch the directory, creating a blind spot that may hide security XXproblems (for example, if group foo can write that user's .login and XXgain access to some other important priv...) Running XX.B kuang XXas root XXwon't help unless we are running on the server that exports that XXfile system, since root==nobody through NFS here. Of course, then XXyou'll find other blind spots on other servers, meaning that you'll XXnever be able to see a complete picture of how things are from any XXspot on the net. Running XX.B kuang XXon every machine might not even XXhelp, since the blind spots might prevent them from seeing viable XXpaths to Success on any of the machines. Sigh. XX XXSoooo we've added a XX.B -f XXoption that causes XX.B kuang XXto preload owner, group and mode information for a list of files. XXEach line of the file should be of the form "type uid gid mode name". XX.B type XXis ignored by XX.B kuang. XX.B uid XXand XX.B gid XXare the user and group ID numbers, in decimal. XX.B mode XXis the permissions for the file, in octal. And XX.B name XXis the name of the file. We've also added a program called XX.B get-cf XXthat can be run as root on a server to create a file of the above form XXfor the control files for the user's with home directories on that XXserver. Then you can run XX.B get-cf XXon every server as root, concatenate all the data together, and XXpreload it into Perl. This will fix the shadow problems mentioned XXabove and should also speed things up since you won't need to do all XXthe file system references. XX XX.B kuang -f file XXwill use a DBM database in place of a text file if file.dir exists. XXTo create a DBM database from a text file of the form described above, XXuse XX.B kuang -f file -D. XXThis will suck in the text file and create a DBM database from it and XXquit. This speeds up kuang's initialization somewhat, though it isn't XXclear that its worth doing unless you have a local disk for the DBM XXfile. XX XX.SH "SEE ALSO" XX"Rule Based Analysis of Computer Security", Robert W. Baldwin, MIT, June 1987. XX.SH NOTES XX.LP XXThis version of XX.B kuang XXis based on the shell script versions that Dan Farmer included with XXthe XX.B COPS XXsecurity package, which in turn were based on code written by Robert XXBaldwin himself. XX XXYou should read the other documentation that should come with this XXversion and modify the rules in XX.B kuang XXto suite your site. XX XX.SH BUGS XX.LP XXThe rules should be extracted from the code so that they could be XXaugmented in a site specific fashion more readily. XX XXThe system doesn't work correctly when multiple users in the password XXfile share the same UID. In that event, it only checks plans for the XXfirst. XSHAR_EOF Xchmod 0600 kuang.1 || echo "restore of kuang.1 fails" Xif [ $TOUCH = can ] Xthen X touch -am 1220223490 kuang.1 Xfi X# ============= kuang_all ============== Xsed 's/^X//' << 'SHAR_EOF' > kuang_all && XX#!/bin/sh XX# XX# Quick script to run kuang on all the users on your system. Requires XX# the perl version of kuang, of course. XX# XX# df, 1990 XX# XXetc_passwd=/etc/passwd XXresults=./Success XX XXall_users=`awk -F: '{print $1}' $etc_passwd` XX XXfor i in $all_users XX do XX ./kuang $i >> $results XX done XX XSHAR_EOF Xchmod 0700 kuang_all || echo "restore of kuang_all fails" Xif [ $TOUCH = can ] Xthen X touch -am 1231200990 kuang_all Xfi X# ============= put-cf ============== Xsed 's/^X//' << 'SHAR_EOF' > put-cf && XX#! /usr/local/bin/perl XX XXfor ($i = 0; $i <= $#ARGV; $i++) { XX open(FILE, $ARGV[$i]); XX XX line: XX while (<FILE>) { XX chop; XX ($type, $uid, $gid, $mode, $name) = split; XX $mode = oct($mode); XX XX &create_dirs_as_needed(&basename($name)); XX XX if ($type eq "d") { XX if (mkdir($name, $mode) == 0) { XX printf(stderr "mkdir $name failed: $!\n"); XX if (chmod($mode, $name) != 1) { XX printf(stderr "chmod $mode $name failed\n"); XX } XX } XX XX } else { XX open(TMP, $name) || XX printf(stderr "can't create $name: $!\n"); XX XX close(TMP); XX XX if (chmod($mode, $name) != 1) { XX printf(stderr "chmod $mode $name failed\n"); XX } XX } XX XX if (chown($uid, $gid, $name) != 1) { XX printf(stderr "chown $uid $gid $name failed\n"); XX } XX } XX} XX XXsub basename { XX local($path) = @_; XX local(@elts); XX XX @elts = split(/\//, $path); XX pop(@elts); XX return(join('/', @elts)); XX} XX XX XXsub create_dirs_as_needed { XX local($path) = @_; XX local($base); XX XX if (-f $path) { XX printf(stderr "Yack, encountered a file named '%s' where we expected a directory.\n"); XX return; XX } XX XX if (-d $path) { XX return; XX } XX XX $base = &basename($path); XX XX &create_dirs_as_needed($base); XX XX if (mkdir($path, 0755) == 0) { XX printf(stderr "mkdir failed for '$path' in create_dirs_as_needed: $!\n"); XX } XX} XX XX XX XSHAR_EOF Xchmod 0600 put-cf || echo "restore of put-cf fails" Xif [ $TOUCH = can ] Xthen X touch -am 1220223490 put-cf Xfi X# ============= yagrip.pl ============== Xsed 's/^X//' << 'SHAR_EOF' > yagrip.pl && XX#Yet Another Getopt Routine In Perl XX# jgreely@cis.ohio-state.edu, 89/11/1 XX#usage: XX#&getopt("f:bar") || XX# die &usage("script","f:bar","oo","[files ...]"); XX# XXsub getopt { XX local($_,$flag,$opt,$f,$r,@temp) = @_; XX @temp = split(/(.):/); XX while ($#temp >= $[) { XX $flag .= shift(@temp); XX $opt .= shift(@temp); XX } XX while ($_ = $ARGV[0], /^-(.)(.*)/ && shift(@ARGV)) { XX ($f,$r) = ($1,$2); XX last if $f eq '-'; XX if (index($flag,$f) >= $[) { XX eval "\$opt_$f++;"; XX $r =~ /^(.)(.*)/,redo if $r ne ''; XX }elsif (index($opt,$f) >= $[) { XX $r = $r eq '' ? shift(@ARGV) : $r; XX eval "\$opt_$f = \$r;"; XX }else{ XX print STDERR "Unrecognized switch \"-$f\".\n"; XX return 0; XX } XX } XX return 1; XX} XX XX#usage: usage: XX# &usage(progname,arglist,@names,@last); XX#ex: XX# &usage("script","f:bar","oo","[file ...]"); XX#would return XX# "usage: script [-f oo] [-bar] [file ...]" XX# XXsub usage { XX local($prog,$_,@list) = @_; XX local($string,$flag,@string,@temp,@last) = (); XX @temp = split(/(.):/); XX push(@string,"usage:",$prog); XX while ($#temp >= $[) { XX if (($flag = shift(@temp)) ne '') { XX push(@string,"[-$flag]"); XX } XX if (($flag = shift(@temp)) ne '') { XX push(@string,sprintf("[-%s %s]",$flag,shift(@list))); XX } XX } XX push(@string,@list) if $#list >= $[; XX return join(' ',@string) . "\n"; XX} XX1; XSHAR_EOF Xchmod 0600 yagrip.pl || echo "restore of yagrip.pl fails" Xif [ $TOUCH = can ] Xthen X touch -am 1220223490 yagrip.pl Xfi Xexit 0 SHAR_EOF chmod 0600 cops/kuang.pl.shar || echo 'restore of cops/kuang.pl.shar failed' Wc_c="`wc -c < 'cops/kuang.pl.shar'`" test 41217 -eq "$Wc_c" || echo 'cops/kuang.pl.shar: original size 41217, current size' "$Wc_c" fi true || echo 'restore of cops/makefile failed' echo End of part 4, continue with part 5 exit 0