home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3358 < prev    next >
Encoding:
Internet Message Format  |  1991-05-17  |  59.4 KB

  1. From: df@sei.cmu.edu (Dan Farmer)
  2. Newsgroups: alt.sources
  3. Subject: alpha perl-cops, 3 of 3
  4. Message-ID: <25552@as0c.sei.cmu.edu>
  5. Date: 17 May 91 04:47:32 GMT
  6.  
  7. Submitted-by: df@death.cert.sei.cmu.edu
  8. Archive-name: alpha p-cops/part03
  9.  
  10. #!/bin/sh
  11. # this is apcops.03 (part 3 of alpha p-cops)
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file p-cops.alpha/user.chk continued
  14. #
  15. if test ! -r _shar_seq_.tmp; then
  16.     echo 'Please unpack part 1 first!'
  17.     exit 1
  18. fi
  19. (read Scheck
  20.  if test "$Scheck" != 3; then
  21.     echo Please unpack part "$Scheck" next!
  22.     exit 1
  23.  else
  24.     exit 0
  25.  fi
  26. ) < _shar_seq_.tmp || exit 1
  27. if test ! -f _shar_wnt_.tmp; then
  28.     echo 'x - still skipping p-cops.alpha/user.chk'
  29. else
  30. echo 'x - continuing file p-cops.alpha/user.chk'
  31. sed 's/^X//' << 'SHAR_EOF' >> 'p-cops.alpha/user.chk' &&
  32. # even if invoked as an sh or csh or foosh script.
  33. # notice we don't use full path cause we don't
  34. # know where the user has perl on their system.
  35. #
  36. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  37. & eval 'exec perl -S $0 $argv:q'
  38. X    if $running_under_some_stupid_shell_instead_of_perl;
  39. X
  40. #
  41. # check for writable files in all user's homes
  42. #
  43. require "pass.cache.pl";
  44. require "is_able.pl";
  45. X
  46. # files checked for:
  47. @ftable = ("rhosts", "profile", "login", "logout", "cshrc",
  48. X       "bashrc", "kshrc", "tcshrc", "netrc", "forward", "dbxinit",
  49. X       "distfile", "exrc", "emacsrc", "remote", "mh_profile",
  50. X       "xinitrc", "xsession", "Xdefaults", "Xresources", "rninit");
  51. X
  52. @readables = ("netrc", "rhosts");
  53. X
  54. local(%done);
  55. X
  56. for $i (keys %uname2dir) {
  57. X    $dir = $uname2dir{$i};
  58. X    #   I don't want to hear about every file in their home dir, if 
  59. X    # is WW, but still need to check the .netrc file for readability...
  60. X    next if $done{$dir}++;
  61. X    if (-e $dir) { 
  62. X        if (&is_able($dir, "w", "w")) {
  63. X            for $r (@readables) {
  64. X                if (-s "$dir/.$r") {
  65. X                    &is_able("$dir/.$r", "w", "r");
  66. X        }
  67. X        }
  68. X            next;
  69. X    }
  70. X        for $file (@ftable) {
  71. X            $foo_file = $dir . "/.$file";
  72. X            if (-e $foo_file) {
  73. X                &is_able($foo_file, "w", "w");
  74. X                for $r (@readables) {
  75. X                    if ($file eq $r && -s $foo_file) {
  76. X                        &is_able($foo_file, "w", "r");
  77. X            }
  78. X        }
  79. X        }
  80. X    }
  81. X    }
  82. }
  83. X
  84. 1;
  85. SHAR_EOF
  86. echo 'File p-cops.alpha/user.chk is complete' &&
  87. chmod 0700 p-cops.alpha/user.chk ||
  88. echo 'restore of p-cops.alpha/user.chk failed'
  89. Wc_c="`wc -c < 'p-cops.alpha/user.chk'`"
  90. test 2048 -eq "$Wc_c" ||
  91.     echo 'p-cops.alpha/user.chk: original size 2048, current size' "$Wc_c"
  92. rm -f _shar_wnt_.tmp
  93. fi
  94. # ============= p-cops.alpha/stat.pl ==============
  95. if test -f 'p-cops.alpha/stat.pl' -a X"$1" != X"-c"; then
  96.     echo 'x - skipping p-cops.alpha/stat.pl (File already exists)'
  97.     rm -f _shar_wnt_.tmp
  98. else
  99. > _shar_wnt_.tmp
  100. echo 'x - extracting p-cops.alpha/stat.pl (Text)'
  101. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/stat.pl' &&
  102. ;# $Header: stat.pl,v 3.0.1.1 90/08/09 04:01:34 lwall Locked $
  103. ;# Usage:
  104. ;#    require 'stat.pl';
  105. ;#    @ary = stat(foo);
  106. ;#    $st_dev = @ary[$ST_DEV];
  107. ;#
  108. $ST_DEV =    0 + $[;
  109. $ST_INO =    1 + $[;
  110. $ST_MODE =    2 + $[;
  111. $ST_NLINK =    3 + $[;
  112. $ST_UID =    4 + $[;
  113. $ST_GID =    5 + $[;
  114. $ST_RDEV =    6 + $[;
  115. $ST_SIZE =    7 + $[;
  116. $ST_ATIME =    8 + $[;
  117. $ST_MTIME =    9 + $[;
  118. $ST_CTIME =    10 + $[;
  119. $ST_BLKSIZE =    11 + $[;
  120. $ST_BLOCKS =    12 + $[;
  121. X
  122. ;# Usage:
  123. ;#    require 'stat.pl';
  124. ;#    do Stat('foo');        # sets st_* as a side effect
  125. ;#
  126. sub Stat {
  127. X    ($st_dev,$st_ino,$st_mode,$st_nlink,$st_uid,$st_gid,$st_rdev,$st_size,
  128. X    $st_atime,$st_mtime,$st_ctime,$st_blksize,$st_blocks) = stat(shift(@_));
  129. }
  130. X
  131. 1;
  132. SHAR_EOF
  133. chmod 0700 p-cops.alpha/stat.pl ||
  134. echo 'restore of p-cops.alpha/stat.pl failed'
  135. Wc_c="`wc -c < 'p-cops.alpha/stat.pl'`"
  136. test 653 -eq "$Wc_c" ||
  137.     echo 'p-cops.alpha/stat.pl: original size 653, current size' "$Wc_c"
  138. rm -f _shar_wnt_.tmp
  139. fi
  140. # ============= p-cops.alpha/suckline.pl ==============
  141. if test -f 'p-cops.alpha/suckline.pl' -a X"$1" != X"-c"; then
  142.     echo 'x - skipping p-cops.alpha/suckline.pl (File already exists)'
  143.     rm -f _shar_wnt_.tmp
  144. else
  145. > _shar_wnt_.tmp
  146. echo 'x - extracting p-cops.alpha/suckline.pl (Text)'
  147. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/suckline.pl' &&
  148. #
  149. #  As title implies... :-)
  150. #
  151. sub main'suckline {
  152. X    local($file, $_) = @_;
  153. #   local($package) = caller;
  154. X
  155. #   $file =~ s/^([^']+)$/$package'$1/; 
  156. X    {
  157. X    if (s/\\\n?$//) {
  158. X        $_ .= <$file>;
  159. X        redo;
  160. X    }
  161. X    } 
  162. X    $_;
  163. }
  164. X
  165. 1;
  166. SHAR_EOF
  167. chmod 0700 p-cops.alpha/suckline.pl ||
  168. echo 'restore of p-cops.alpha/suckline.pl failed'
  169. Wc_c="`wc -c < 'p-cops.alpha/suckline.pl'`"
  170. test 229 -eq "$Wc_c" ||
  171.     echo 'p-cops.alpha/suckline.pl: original size 229, current size' "$Wc_c"
  172. rm -f _shar_wnt_.tmp
  173. fi
  174. # ============= p-cops.alpha/suid.chk ==============
  175. if test -f 'p-cops.alpha/suid.chk' -a X"$1" != X"-c"; then
  176.     echo 'x - skipping p-cops.alpha/suid.chk (File already exists)'
  177.     rm -f _shar_wnt_.tmp
  178. else
  179. > _shar_wnt_.tmp
  180. echo 'x - extracting p-cops.alpha/suid.chk (Text)'
  181. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/suid.chk' &&
  182. #!/bin/sh  # need to mention perl here to avoid recursion
  183. #
  184. #  Usage: suid.chk [-n] [-s secure_dir] [search_starting_directory]
  185. #
  186. #   Shell script intended to be run periodically by cron in order
  187. #   to spot changes in files with the suid or sgid bits set.
  188. #
  189. #    suid.chk    840919        Prentiss Riddle
  190. #
  191. #     This changes into the $SECURE directory first, then 
  192. #   uses find(1) to search the directories in $SEARCH for all
  193. #   files with the 4000 or 2000 permission bits set.  $STOP is a file
  194. #   containing "ls -gildsa" output for known setuid or setgid programs.
  195. #   Any additions or changes to this list represent potential security
  196. #   problems, so they are reported.
  197. #
  198. #  Modified 8/15/89, Dan Farmer:
  199. #    Just changed the program/doc names and some of the temp
  200. #  files to make it fit in with the rest of the programs....
  201. #  Modified 12/26/90, df
  202. #       Now flags SUID shell scripts and world writeable SUID files, too.
  203. #
  204. #  Rewritten in perl, 1/17/91, df
  205. #  Major hacks by tchrist 5/14/91
  206. #
  207. # NOTE:
  208. #   If you know where perl is and your system groks #!, put its
  209. # pathname at the top to make this a tad faster.
  210. #
  211. # the following magic is from the perl man page
  212. # and should work to get us to run with perl 
  213. # even if invoked as an sh or csh or foosh script.
  214. # notice we don't use full path cause we don't
  215. # know where the user has perl on their system.
  216. #
  217. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  218. & eval 'exec perl -S $0 $argv:q'
  219. X    if $running_under_some_stupid_shell_instead_of_perl;
  220. X
  221. require "hostname.pl";
  222. require "is_able.pl";
  223. require "file_owner.pl";
  224. require "pathconf.pl";
  225. require "chk_strings.pl";
  226. require "pass.cache.pl";
  227. package suid_chk; # name space protection
  228. $debug=0;
  229. X
  230. #
  231. # Getopts stuff
  232. $usage = "Usage: $0 [-n] [-s secure_dir] [starting_directory]\n";
  233. require 'getopts.pl';
  234. # Process the command args; Either specify verbose or an alternate config file:
  235. die $usage unless &Getopts('ns:');
  236. X
  237. $suid_dir = $'SECURE || '.';
  238. if (defined($opt_s)) { $suid_dir = $opt_s; }
  239. X
  240. # Do NFS stuff?  Yes unless opt:
  241. if (defined($opt_n)) { $skip_nfs = $opt_n; }
  242. else { $skip_nfs = 0; }
  243. X
  244. $STOP="$suid_dir/suid.stop";
  245. $TEMPOLD="$suid_dir/fsold$$";
  246. $TEMPCUR="$suid_dir/fscur$$";
  247. $TEMPNEW="$suid_dir/fsnew$$";
  248. $TEMPGON="$suid_dir/fsgon$$";
  249. $TEMPM="$suid_dir/fsm$$";
  250. X
  251. if (@ARGV > 1) { die $usage; }
  252. elsif (@ARGV == 1) { $start_dir = shift; }
  253. X
  254. # these may be terribly rash assumptions....
  255. $start_dir="/" unless defined $start_dir;
  256. $find_can_ls = 1 unless defined $find_can_ls;
  257. X
  258. $NONFS = '\( -type f -fstype nfs -prune \) -o' if $skip_nfs;  
  259. $find_ls = $find_can_ls ? '-ls' : "-exec $'LS -gilds {} \\;";
  260. X
  261. die "Error -- Security directory $suid_dir doesn't exist\n" unless -d $suid_dir;
  262. unless (-d $suid_dir) {
  263. X    mkdir($suid_dir, 0700) || die "can't mkdir $suid_dir: $!";
  264. X    } 
  265. chdir  $suid_dir || die "can't chdir $suid_dir: $!";
  266. X
  267. # find the setuid programs and sort
  268. &run("$'FIND $start_dir $NONFS -type f \\( -perm -4000 -o -perm -2000 \\) $find_ls | $'SORT > $TEMPCUR");
  269. X
  270. # compare with the sorted stop list
  271. # create stop file if needed
  272. if (! -f $STOP) { open(S,">$STOP"); close(S); }
  273. X
  274. &run("$'SORT <$STOP >$TEMPOLD");
  275. &run("$'COMM -13 $TEMPOLD $TEMPCUR | $'SORT +8 >$TEMPNEW");
  276. &run("$'COMM -23 $TEMPOLD $TEMPCUR | $'SORT +8 >$TEMPGON");
  277. X
  278. local($is_able'silent) = 1;
  279. local($chk_strings'recurse) = 0 unless defined $chk_strings'recurse;
  280. X
  281. # report changes
  282. if (-s $TEMPNEW || -s $TEMPGON) {
  283. X    if (-s $TEMPNEW) {
  284. X    die "Can't open $TEMPNEW: $!" unless open TEMPNEW;
  285. X    while (<TEMPNEW>) {
  286. X        ($file) = /(\S+)$/;
  287. X
  288. X        # don't want SUID files to be world writable!
  289. X        # although *reasonable* systems clear the bit on write
  290. X        print "Warning!  SUID file $file is _World_ writable!\n" 
  291. X        if &'is_able ($file, "w", "w"); 
  292. X            
  293. X        if (-r $file && -f _ && -T $file) {
  294. X        print "Warning!  ", &'Owner($file) ? '' : 'ROOT-owned ', 
  295. X            "SUID file $file is a non-binary, executable file!\n";
  296. X        }
  297. X
  298. X        &'chk_strings($file) if -r _;
  299. X    }
  300. X    close TEMPNEW;
  301. X    }
  302. X
  303. X    if (-s $TEMPNEW) {
  304. X    die "Can't reopen $TEMPNEW: $!" unless open TEMPNEW;
  305. X    print "\nThese files are newly setuid/setgid:\n\n";
  306. X    print while <TEMPNEW>;
  307. X    }
  308. X
  309. X    if (-s $TEMPGON) {
  310. X    die "Can't reopen $TEMPGON: $!" unless open TEMPGON;
  311. X    print "\nThese files are no longer setuid/setgid:\n\n";
  312. X    print while <TEMPGON>;
  313. X    }
  314. X
  315. }
  316. X
  317. unlink $TEMPOLD, $TEMPCUR, $TEMPNEW, $TEMPGON;
  318. X
  319. sub run {
  320. X    print "running: $_[0]\n" if $debug;
  321. X    system $_[0];
  322. X    warn "command $_[0] returned $?" if $?;
  323. X
  324. #  end it all....
  325. X
  326. 1;
  327. SHAR_EOF
  328. chmod 0700 p-cops.alpha/suid.chk ||
  329. echo 'restore of p-cops.alpha/suid.chk failed'
  330. Wc_c="`wc -c < 'p-cops.alpha/suid.chk'`"
  331. test 4477 -eq "$Wc_c" ||
  332.     echo 'p-cops.alpha/suid.chk: original size 4477, current size' "$Wc_c"
  333. rm -f _shar_wnt_.tmp
  334. fi
  335. # ============= p-cops.alpha/root.chk ==============
  336. if test -f 'p-cops.alpha/root.chk' -a X"$1" != X"-c"; then
  337.     echo 'x - skipping p-cops.alpha/root.chk (File already exists)'
  338.     rm -f _shar_wnt_.tmp
  339. else
  340. > _shar_wnt_.tmp
  341. echo 'x - extracting p-cops.alpha/root.chk (Text)'
  342. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/root.chk' &&
  343. #!/bin/sh  # need to mention perl here to avoid recursion
  344. #
  345. #  Usage: root.chk
  346. #
  347. #  This script checks pathnames inside root's startup files for 
  348. # writability, improper umask settings (world writable), non-root
  349. # entries in /.rhosts, writable binaries in root's path,
  350. # and to ensure that root is in /etc/ftpuser.
  351. #
  352. # Also check for a single "+" in /etc/hosts.equiv (world is trusted),
  353. # and that /bin, /etc and certain key files are root owned, so that you
  354. # can't, say, rcp from a host.equived machine and blow over the password
  355. # file... this may or may not be bad, decide for yourself.
  356. # Startup files are /.login /.cshrc /.profile
  357. #
  358. #  Mechanism:  These files contain paths and filenames that are stripped
  359. # out using "grep".  These strings are then processed by the "is_able"
  360. # program to see if they are world writable.  Strings of the form:
  361. #
  362. #    path=(/bin /usr/bin .)
  363. #        and
  364. #    PATH=/bin:/usr/bin:.:
  365. #
  366. # are checked  to ensure that "." is not in the path.  All
  367. # results are echoed to standard output.  In addition, some effort was
  368. # put into parsing out paths with multiple lines; e.g. ending in "\",
  369. # and continuing on the next line.  Also, all executable files and 
  370. # directories in there are checked for writability as well.
  371. #
  372. #  For umask stuff, simply grep for umask in startup files, and check
  373. # umask value.  For /etc/ftpuser, simple grep to check if root is in
  374. # the file.  For /etc/hosts.equiv, just check to see if "+" is alone
  375. # on a line by awking it.
  376. #
  377. #  NOTE:
  378. # if you know where perl is and your system groks #!, put its
  379. # pathname at the top to make this a tad faster.
  380. #
  381. # the following magic is from the perl man page
  382. # and should work to get us to run with perl 
  383. # even if invoked as an sh or csh or foosh script.
  384. # notice we don't use full path cause we don't
  385. # know where the user has perl on their system.
  386. #
  387. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  388. & eval 'exec perl -S $0 $argv:q'
  389. X    if $running_under_some_stupid_shell_instead_of_perl;
  390. X
  391. # rewritten in perl by tchrist@convex.com
  392. X
  393. # root startup/important files
  394. X
  395. require 'file_owner.pl';
  396. require 'fgrep.pl';
  397. require 'suckline.pl';
  398. require 'is_able.pl';
  399. require 'chk_strings.pl';
  400. require 'glob.pl';
  401. X
  402. package root_chk;
  403. X
  404. # use -a true if you care about non-executables
  405. # in root's path
  406. X
  407. $ARGV[0] eq '-a' && ($all_files++, shift);
  408. X
  409. die "usage: root.chk [-a]\n" if @ARGV;
  410. X
  411. $W = 'Warning! ';
  412. X
  413. $cshrc    = '/.cshrc';
  414. $profile= '/.profile';
  415. $rhosts = '/.rhosts';
  416. X
  417. $| = 1;
  418. X
  419. @big_files= ('/.login', '/.cshrc', '/.profile', '/.logout' );
  420. X
  421. # root should own *at least* these, + $big_files; you can check for all files
  422. # in /bin & /etc, or just the directories (the default.)
  423. # root_files="/bin /bin/* /etc /etc/* $big_files $rhosts"
  424. @root_files= ('/bin','/etc',@big_files,$rhosts,'/etc/passwd','/etc/group');
  425. X
  426. # misc important stuff
  427. $ftp='/etc/ftpusers';
  428. $equiv='/etc/hosts.equiv';
  429. X
  430. #   should't have anyone but root owning /bin or /etc files/directories
  431. # In case some of the critical files don't exist (/.rhost), toss away error
  432. # messages
  433. X
  434. if (@bad_files = grep (-e && &'Owner($_), @root_files)) {
  435. X    print "$W  Root does not own the following file(s):\n";
  436. X    print "\t@bad_files\n";
  437. X
  438. local($chk_strings'recurse) = 1 unless defined $chk_strings'recurse;
  439. X
  440. for $file (@big_files) {
  441. X    open file || next;
  442. X
  443. X    &'chk_strings($file);
  444. X
  445. X    # check for group or other writable umask
  446. X    while (<file>) {
  447. X    next if /^\s*#/;
  448. X    next unless /umask\s*(\d+)/;
  449. X    next unless ~oct($1) & 022;
  450. X    print "$W root's umask set to $1 in $file\n";
  451. X    } 
  452. X
  453. print "$W $ftp exists and root is not in it\n" 
  454. X    if -e $ftp && !&'fgrep($ftp,'root');
  455. X
  456. print "$W A \"+\" entry exists in $equiv!\n" if &'fgrep($equiv, '^\+$');
  457. X
  458. if (open rhosts) {
  459. X    while (<rhosts>) {
  460. X    next unless /\S+\s+(\S+)/ && $1 ne 'root';
  461. X    print "$W Non-root entry in $rhosts! $1\n";
  462. X    }
  463. close(rhosts);
  464. X
  465. undef @rootpath;
  466. X
  467. # checking paths...
  468. #
  469. # Get the root paths from $csh.
  470. X
  471. if (open(CSHRC, $cshrc)) {
  472. X    $path = '';
  473. X    while (<CSHRC>) {
  474. X    next if /^\s*#/;
  475. X    chop unless /\\$/;
  476. X    if (/set\s+path\s*=/) {
  477. X        $_ = &'suckline($cshrc, $_);
  478. X        s/.*set\s+path\s*=\s*//;
  479. X        s/\((.*)\)/$1/;
  480. X        s/#.*/./;
  481. X        @tmppath = grep($_ ne '', split(' '));
  482. X        for (@tmppath) { $whence{$_} .= " " . $cshrc; } 
  483. X        push(@rootpath, @tmppath);
  484. X    } 
  485. X    } 
  486. X    close(CSHRC);
  487. X
  488. if (open login) {
  489. X    $path = '';
  490. X    while (<cshrc>) {
  491. X    next if /^\s*#/;
  492. X    chop unless /\\$/;
  493. X    if (/set\s+path\s*=/) {
  494. X        $_ = &'suckline('login', $_);
  495. X        s/.*set\s+path\s*=\s*//;
  496. X        s/\((.*)\)/$1/;
  497. X        s/#.*/./;
  498. X        @tmppath = grep($_ ne '', split(' '));
  499. X        for (@tmppath) { $whence{$_} .= " " . $login; } 
  500. X        push(@rootpath, @tmppath);
  501. X    } 
  502. X    } 
  503. X    close(login);
  504. }
  505. X
  506. if (open profile) {
  507. X    $path = '';
  508. X    while (<profile>) {
  509. X    next if /^\s*#/;
  510. X    chop unless /\\$/;
  511. X    if (/PATH=/) {
  512. X        $_ = &'suckline('profile', $_);
  513. X        s/.*PATH=//;
  514. X        s/#.*//;
  515. X        @tmppath = split(/:/);
  516. X        for (@tmppath) { $whence{$_} .= " " . $profile; } 
  517. X        push(@rootpath, @tmppath);
  518. X    } 
  519. X    } 
  520. X    close(profile);
  521. X
  522. for (keys %whence) {
  523. X    $whence{$_} =~ s/^ //;
  524. X    $whence{$_} =~ s/ / and /g;
  525. X
  526. undef %seen;
  527. grep($seen{$_}++, @rootpath);
  528. X
  529. $is_able'silent = 1;
  530. for (keys %seen) {
  531. X    if (!-e && $_ ne ".") {
  532. X    print "$W path component $_ in $whence{$_} doesn't exist!\n";
  533. X    next;
  534. X    } 
  535. X
  536. X    if (/^\.?$/) {  # null -> dot
  537. X    print "$W \".\" (or current directory) is in root's path in $whence{$_}!\n";
  538. X    } elsif (&'is_writable($_)) {
  539. X    print "$W Directory $_ is _World_ writable and in root's path in $whence{$_}!\n";
  540. X    next;
  541. X    }
  542. X
  543. X    foreach $file (&'glob("$_/*")) {
  544. X    # can't just check -x here, as that depends on current user
  545. X    $is_executable = -f $file && (&'Mode($file) & 0111);
  546. X    if (($all_files || $is_executable) && 
  547. X            ($how = &'is_writable($file, 'w', 'w'))) {
  548. X        print "$W _World_ $how ",
  549. X            $is_executable ? 'executable' : 'file',
  550. X        " $file in root path component $_ from $whence{$_}!\n";
  551. X    } 
  552. X    }
  553. X
  554. $is_able'silent = 0;
  555. X
  556. 1;
  557. SHAR_EOF
  558. chmod 0700 p-cops.alpha/root.chk ||
  559. echo 'restore of p-cops.alpha/root.chk failed'
  560. Wc_c="`wc -c < 'p-cops.alpha/root.chk'`"
  561. test 5959 -eq "$Wc_c" ||
  562.     echo 'p-cops.alpha/root.chk: original size 5959, current size' "$Wc_c"
  563. rm -f _shar_wnt_.tmp
  564. fi
  565. # ============= p-cops.alpha/README.perl ==============
  566. if test -f 'p-cops.alpha/README.perl' -a X"$1" != X"-c"; then
  567.     echo 'x - skipping p-cops.alpha/README.perl (File already exists)'
  568.     rm -f _shar_wnt_.tmp
  569. else
  570. > _shar_wnt_.tmp
  571. echo 'x - extracting p-cops.alpha/README.perl (Text)'
  572. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/README.perl' &&
  573. X
  574. **ALPHA ALERT**ALPHA ALERT**ALPHA ALERT**ALPHA ALERT**ALPHA ALERT**
  575. X
  576. (Warning!  This is the alpha version of p-cops, or perl cops... beta
  577. will be forthcoming shortly...)
  578. X
  579. X  This will attempt to familiarize you with the perl version of cops.  If
  580. you have never used cops before, you can get the complete documentation,
  581. along with shell/C code, via anon-ftp, at cert.sei.cmu.edu, ~ftp/pub/cops/1.02.
  582. Ok; the main difference with this version is that (besides being written
  583. in perl) there is a config file -- "cops.cf", that is used to control the
  584. cruft inside all of the modules.  No more muss, no more fuss.  I'll try
  585. to go over the main ones here.  The only important thing you have to set in
  586. the "cops" main file (or via the "-s" flag) is the secure directory, which 
  587. by default is "."; this is where cops will look for the config file and all
  588. the programs.  Also, if something is flagged as world-writable, and the file
  589. itself is not writable, but the parent directory is, then there will be an
  590. asterix after the warning (e.g. /usr/foo/bar is World Writable! (*).)  
  591. Finally, the suid.chk program, like all the rest of the programs, is meant 
  592. to be run as a part of "cops"; it's output will go to stdout if run 
  593. standalone, or either get mailed or saved to the result file if run under 
  594. cops.  That's a bit different than the old version.  Depending on comments,
  595. I'll either keep it this way, or put in some more options.
  596. X
  597. X  As said in the config file -- "cops.cf" (a "#" sign denotes comments):
  598. X
  599. # anything beginning with /^\s*[$@%&]/ will be eval'd
  600. X
  601. X  In general, you can put variables and programs that will be run inside
  602. the config file.  Variables look startlingly like they do in normal perl
  603. (look at the "PROGRAMS" section below for more on running programs); e.g.:
  604. X
  605. $NMAIL         = 0;         # send mail instead of generating reports
  606. $ONLY_DIFF     = 0;          # only send diff from last time
  607. $SECURE_USERS   = "root";     # user to receive mailed report
  608. X
  609. X  Setting something to "0" (without quotes is fine) generally means that 
  610. the option is not used.  "1" (or non-zero values, if you feel gutsy) is 
  611. used for a positive/true/whatever value.  The variables in general should
  612. be very similar to their normal cops counterparts; in this case, setting
  613. NMAIL to 1 would mean to mail info to the user listed in SECURE_USERS.
  614. If ONLY_DIFF is 1, it will only mail reports if change has occurred.
  615. X
  616. X  In general, variables in package main are for cops itself, whereas
  617. those with package qualifiers are for a particular chk routine or
  618. for auxiliary routines.  For instance, the following lines:
  619. X
  620. # this one says to ignore warnings about paths matching these regexps
  621. @chk_strings'ignores = ( '^/tmp/?', '^/(var|usr)/tmp/?' );
  622. X
  623. X  "chk_strings" is a routine that checks for writable programs within other
  624. programs, usually executed by root, such as /etc/rc and crontab.  This line
  625. says to ignore any files that start with a "/tmp", "/var/tmp", or "/usr/tmp".
  626. If you have a file or set of files that always are returning writable that
  627. are inside your rc and cron files, then you can put exceptions here.  One
  628. possibility is that you don't care about files created by other programs,
  629. so that anything after a ">" should be ignored.  You might add something
  630. like '>.*' to ignore files like "/usr/bar/snowcone", in a line like
  631. "/foo/bar/command > /usr/bar/snowcone".
  632. X
  633. X  Next, there is a nifty option, that does recursive searching inside the
  634. files chk_strings looks at.  This is neat... get it working by setting this
  635. to 1:
  636. X
  637. $chk_strings'recurse = 1;
  638. X
  639. X  So, if you have a line like this in /etc/rc:
  640. X
  641. /usr/bin/foo > /dev/console
  642. X
  643. X  It will examine "/usr/bin/foo" for programs inside of it -- and it will
  644. keep going until it has exhausted all possibilities.  So you can get warning
  645. messages like:
  646. X
  647. Warning!  File /foo/bar (inside /usr/local/X11R4/bin/X inside /usr/local/X11R4
  648. /bin/xdm inside /etc/rc.local) is _World_ writable!
  649. X
  650. X  Fun stuff.  No one can hide, now...
  651. X
  652. PROGRAMS
  653. =========
  654. X
  655. X  Running a program within cops is easy; you just have the program with
  656. any options by itself on a line.  Semi-colons are not welcome here.
  657. E.g.:
  658. X
  659. # first test the security of the root account
  660. root.chk
  661. X
  662. X  Some variables specific to the various programs are here as well, e.g.:
  663. X
  664. # now of the various devices.  -g means to check group writability, too
  665. $MTAB    = '/etc/fstab';
  666. $EXPORTS = '/etc/exports';
  667. $TAB_STYLE = 'new';
  668. dev.chk -g 
  669. X
  670. X  This is specifying the export files, etc., and saying that you should
  671. use the "new" format style in the exports file.  Ultrix, etc. uses the
  672. old style.  Suid.chk eats up time -- consider the "-n" flag for systems
  673. that have big NFS mounted disks.  And that's it -- the rest should be very 
  674. similar to the old cops, and theoretically, should give you similar or
  675. the same results.
  676. X
  677. X  Good luck!  Send bugs, flames, etc. to df@cert.sei.cmu.edu
  678. X
  679. X -- dan
  680. SHAR_EOF
  681. chmod 0700 p-cops.alpha/README.perl ||
  682. echo 'restore of p-cops.alpha/README.perl failed'
  683. Wc_c="`wc -c < 'p-cops.alpha/README.perl'`"
  684. test 4848 -eq "$Wc_c" ||
  685.     echo 'p-cops.alpha/README.perl: original size 4848, current size' "$Wc_c"
  686. rm -f _shar_wnt_.tmp
  687. fi
  688. # ============= p-cops.alpha/root.chk.old ==============
  689. if test -f 'p-cops.alpha/root.chk.old' -a X"$1" != X"-c"; then
  690.     echo 'x - skipping p-cops.alpha/root.chk.old (File already exists)'
  691.     rm -f _shar_wnt_.tmp
  692. else
  693. > _shar_wnt_.tmp
  694. echo 'x - extracting p-cops.alpha/root.chk.old (Text)'
  695. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/root.chk.old' &&
  696. #!/bin/sh  # need to mention perl here to avoid recursion
  697. #
  698. #  Usage: root.chk
  699. #
  700. #  This script checks pathnames inside root's startup files for 
  701. # writability, improper umask settings (world writable), non-root
  702. # entries in /.rhosts, writable binaries in root's path,
  703. # and to ensure that root is in /etc/ftpuser.
  704. #
  705. # Also check for a single "+" in /etc/hosts.equiv (world is trusted),
  706. # and that /bin, /etc and certain key files are root owned, so that you
  707. # can't, say, rcp from a host.equived machine and blow over the password
  708. # file... this may or may not be bad, decide for yourself.
  709. # Startup files are /.login /.cshrc /.profile
  710. #
  711. #  Mechanism:  These files contain paths and filenames that are stripped
  712. # out using "grep".  These strings are then processed by the "is_able"
  713. # program to see if they are world writable.  Strings of the form:
  714. #
  715. #    path=(/bin /usr/bin .)
  716. #        and
  717. #    PATH=/bin:/usr/bin:.:
  718. #
  719. # are checked  to ensure that "." is not in the path.  All
  720. # results are echoed to standard output.  In addition, some effort was
  721. # put into parsing out paths with multiple lines; e.g. ending in "\",
  722. # and continuing on the next line.  Also, all executable files and 
  723. # directories in there are checked for writability as well.
  724. #
  725. #  For umask stuff, simply grep for umask in startup files, and check
  726. # umask value.  For /etc/ftpuser, simple grep to check if root is in
  727. # the file.  For /etc/hosts.equiv, just check to see if "+" is alone
  728. # on a line by awking it.
  729. #
  730. #  NOTE:
  731. # if you know where perl is and your system groks #!, put its
  732. # pathname at the top to make this a tad faster.
  733. #
  734. # the following magic is from the perl man page
  735. # and should work to get us to run with perl 
  736. # even if invoked as an sh or csh or foosh script.
  737. # notice we don't use full path cause we don't
  738. # know where the user has perl on their system.
  739. #
  740. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  741. & eval 'exec perl -S $0 $argv:q'
  742. X    if $running_under_some_stupid_shell_instead_of_perl;
  743. X
  744. # rewritten in perl by tchrist@convex.com
  745. X
  746. # root startup/important files
  747. X
  748. require 'file_owner.pl';
  749. require 'fgrep.pl';
  750. require 'suckline.pl';
  751. require 'is_able.pl';
  752. require 'chk_strings.pl';
  753. require 'glob.pl';
  754. X
  755. package root_chk;
  756. X
  757. # use -a true if you care about non-executables
  758. # in root's path
  759. X
  760. $ARGV[0] eq '-a' && ($all_files++, shift);
  761. X
  762. die "usage: root.chk [-a]\n" if @ARGV;
  763. X
  764. $W = 'Warning! ';
  765. X
  766. $cshrc    = '/.cshrc';
  767. $profile= '/.profile';
  768. $rhosts = '/.rhosts';
  769. X
  770. $| = 1;
  771. X
  772. @big_files= ('/.login', '/.cshrc', '/.profile', '/.logout' );
  773. X
  774. # root should own *at least* these, + $big_files; you can check for all files
  775. # in /bin & /etc, or just the directories (the default.)
  776. # root_files="/bin /bin/* /etc /etc/* $big_files $rhosts"
  777. @root_files= ('/bin','/etc',@big_files,$rhosts,'/etc/passwd','/etc/group');
  778. X
  779. # misc important stuff
  780. $ftp='/etc/ftpusers';
  781. $equiv='/etc/hosts.equiv';
  782. X
  783. #   should't have anyone but root owning /bin or /etc files/directories
  784. # In case some of the critical files don't exist (/.rhost), toss away error
  785. # messages
  786. X
  787. if (@bad_files = grep (-e && &'Owner($_), @root_files)) {
  788. X    print "$W  Root does not own the following file(s):\n";
  789. X    print "\t@bad_files\n";
  790. X
  791. local($chk_strings'recurse) = 1 unless defined $chk_strings'recurse;
  792. X
  793. for $file (@big_files) {
  794. X    open file || next;
  795. X
  796. X    &'chk_strings($file);
  797. X
  798. X    # check for group or other writable umask
  799. X    while (<file>) {
  800. X    next if /^\s*#/;
  801. X    next unless /umask\s*(\d+)/;
  802. X    next unless ~oct($1) & 022;
  803. X    print "$W root's umask set to $1 in $file\n";
  804. X    } 
  805. X
  806. print "$W $ftp exists and root is not in it\n" 
  807. X    if -e $ftp && !&'fgrep($ftp,'root');
  808. X
  809. print "$W A \"+\" entry exists in $equiv!\n" if &'fgrep($equiv, '^\+$');
  810. X
  811. if (open rhosts) {
  812. X    while (<rhosts>) {
  813. X    next unless /\S+\s+(\S+)/ && $1 ne 'root';
  814. X    print "$W Non-root entry in $rhosts! $1\n";
  815. X    }
  816. close(rhosts);
  817. X
  818. undef @rootpath;
  819. X
  820. # checking paths...
  821. #
  822. # Get the root paths from $csh.
  823. X
  824. if (open $cshrc) {
  825. X    $path = '';
  826. X    while (<cshrc>) {
  827. X    next if /^\s*#/;
  828. X    chop unless /\\$/;
  829. X    if (/set\s+path\s*=/) {
  830. print "FOO-3: @rootpath , $_\n";
  831. X        $_ = &'suckline(cshrc, $_);
  832. print "FOO-2: @rootpath , $_\n";
  833. X        s/.*set\s+path\s*=\s*//;
  834. X        s/\((.*)\)/$1/;
  835. X        s/#.*/./;
  836. X        @tmppath = grep($_ ne '', split(' '));
  837. print "FOO-1: @rootpath , $_\n";
  838. X        for (@tmppath) { $whence{$_} .= " " . $cshrc; } 
  839. X        push(@rootpath, @tmppath);
  840. print "FOO-0: @rootpath , $_\n";
  841. X    } 
  842. X    } 
  843. X    close(cshrc);
  844. X
  845. print "FOO0: @rootpath\n";
  846. X
  847. if (open login) {
  848. X    $path = '';
  849. X    while (<cshrc>) {
  850. X    next if /^\s*#/;
  851. X    chop unless /\\$/;
  852. X    if (/set\s+path\s*=/) {
  853. X        $_ = &'suckline('login', $_);
  854. X        s/.*set\s+path\s*=\s*//;
  855. X        s/\((.*)\)/$1/;
  856. X        s/#.*/./;
  857. X        @tmppath = grep($_ ne '', split(' '));
  858. X        for (@tmppath) { $whence{$_} .= " " . $login; } 
  859. X        push(@rootpath, @tmppath);
  860. X    } 
  861. X    } 
  862. X    close(login);
  863. }
  864. X
  865. print "FOO1: @rootpath\n";
  866. X
  867. if (open profile) {
  868. X    $path = '';
  869. X    while (<profile>) {
  870. X    next if /^\s*#/;
  871. X    chop unless /\\$/;
  872. X    if (/PATH=/) {
  873. X        $_ = &'suckline('profile', $_);
  874. X        s/.*PATH=//;
  875. X        s/#.*//;
  876. X        @tmppath = split(/:/);
  877. X        for (@tmppath) { $whence{$_} .= " " . $profile; } 
  878. X        push(@rootpath, @tmppath);
  879. X    } 
  880. X    } 
  881. X    close(profile);
  882. X
  883. print "FOO2: @rootpath\n";
  884. X
  885. for (keys %whence) {
  886. X    $whence{$_} =~ s/^ //;
  887. X    $whence{$_} =~ s/ / and /g;
  888. X
  889. undef %seen;
  890. grep($seen{$_}++, @rootpath);
  891. X
  892. $is_able'silent = 1;
  893. for (keys %seen) {
  894. X    print "WAK: $_\n";
  895. X    if (!-e && $_ ne ".") {
  896. X    print "$W path component $_ in $whence{$_} doesn't exist!\n";
  897. X    next;
  898. X    } 
  899. X
  900. X    if (/^\.?$/) {  # null -> dot
  901. X    print "$W \".\" (or current directory) is in root's path in $whence{$_}!\n";
  902. X    } elsif (&'is_writable($_)) {
  903. X    print "$W Directory $_ is _World_ writable and in root's path in $whence{$_}!\n";
  904. X    next;
  905. X    }
  906. X
  907. X    foreach $file (&'glob("$_/*")) {
  908. X    print "BAR: $_\n";
  909. X    # can't just check -x here, as that depends on current user
  910. X    $is_executable = -f $file && (&'Mode($file) & 0111);
  911. X    if (($all_files || $is_executable) && 
  912. X            ($how = &'is_writable($file, 'w', 'w'))) {
  913. X        print "$W _World_ $how ",
  914. X            $is_executable ? 'executable' : 'file',
  915. X        " $file in root path component $_ from $whence{$_}!\n";
  916. X    } 
  917. X    }
  918. X
  919. $is_able'silent = 0;
  920. X
  921. 1;
  922. SHAR_EOF
  923. chmod 0700 p-cops.alpha/root.chk.old ||
  924. echo 'restore of p-cops.alpha/root.chk.old failed'
  925. Wc_c="`wc -c < 'p-cops.alpha/root.chk.old'`"
  926. test 6206 -eq "$Wc_c" ||
  927.     echo 'p-cops.alpha/root.chk.old: original size 6206, current size' "$Wc_c"
  928. rm -f _shar_wnt_.tmp
  929. fi
  930. # ============= p-cops.alpha/README.kuang ==============
  931. if test -f 'p-cops.alpha/README.kuang' -a X"$1" != X"-c"; then
  932.     echo 'x - skipping p-cops.alpha/README.kuang (File already exists)'
  933.     rm -f _shar_wnt_.tmp
  934. else
  935. > _shar_wnt_.tmp
  936. echo 'x - extracting p-cops.alpha/README.kuang (Text)'
  937. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/README.kuang' &&
  938. This is a perl version of Dan's version of Bob Baldwin's Kuang program
  939. (originally written as some shell scripts and C programs). 
  940. X
  941. The original intent was to improve the speed of kuang, which is
  942. especially important for installations like ours with several thousand
  943. accounts and NFS things and all that.  The shell version of Kuang used
  944. C programs to add rules, get a groups members, determine the writers
  945. of a file, and so on, which really slowed things down.
  946. X
  947. X        "no" problems    /etc staff writeable
  948. X        -------------    --------------------
  949. shell kuang    2:14 (14)    12:26 (98)    0.1 p/s
  950. perl kuang    1:10 (18)     2:34 (588)    3.8 p/s
  951. X
  952. --- Steve Romig, CIS, Ohio State, October 1990
  953. X
  954. ------------------------------------------------------------------------------
  955. X
  956. Some Features
  957. ---- --------
  958. X
  959. X  Caches passwd/group file entries in an associative array for faster
  960. X  lookups.  This is particularly helpful on insecure systems using YP
  961. X  where password and group lookups are slow and you have to do alot of
  962. X  them...:-)
  963. X
  964. X  Can specify target (uid or gid) on command line.
  965. X
  966. X  Can use -l option to generate PAT for a goal.
  967. X
  968. X  Can use -f to preload file owner, group and mode info, which is
  969. X  helpful in speeding things up and in avoiding file system
  970. X  'shadows'...  See the man page for details.
  971. X
  972. Future plans, things to fix:
  973. ----------------------------
  974. X
  975. - In a large environment (like ours, 260+ machines, 30+ file systems
  976. X  on as many servers, 2000 password file entries served by YP) it
  977. X  would be nice to 'precompute' successful plans that would be common
  978. X  to all systems.  In particular, plans for becoming most of the users
  979. X  with home directories on the NFS file systems would be useful, since
  980. X  we don't really want to recheck these on each host.  You wouldn't
  981. X  want the plan to be too deep - probably shouldn't span more than 2
  982. X  uids (1 on each end: grant u.romig grant g.staff write ~foo/.login
  983. X  grant u.foo).  I'm thinking that you could feed a list of these
  984. X  precomputed plans to kuang and add some code that causes it to
  985. X  splice in relevent plans where it can to short cut the planning
  986. X  steps.  For example, if one of the plans in uids.next is something
  987. X  like "grant u.foo ...", and I have the precomputed plan mentioned
  988. X  above, I could splice the two: "grant u.romig grant g.staff write
  989. X  ~foo/.login grant u.foo ..." and skip all the normal steps that
  990. X  would've been taken to get there.
  991. X
  992. - Hmmm...thinking about it, it seems like some of the steps are a bit
  993. X  too implicit...maybe the rules should be broken out a bit more.
  994. X  That will cost in processing time, though.
  995. X
  996. - Would be really, really nice to be able to deal with PATH variables
  997. X  - location of ., who can write elements of path, etc.  Basic rule is
  998. X  "anyone who can replace anything in any of path directories or the
  999. X  path directories themselves can become that PATH's user..."  This
  1000. X  can be really messy though - in our environment, the path for a user
  1001. X  will depend on the architecture type of the machine that he is
  1002. X  logged into, and to get the path, you'd have to read and interpret
  1003. X  his .login (including variable assignments, source's and
  1004. X  conditionals).  Urf.  One wonders whether it might be better to have
  1005. X  something running as root that su's to each username in turn and
  1006. X  gets the path that way...
  1007. X
  1008. - ignore plans that start with "uid root", unless that's the only element - root
  1009. X  can get to anything, and hopefully nothing can get to root...?
  1010. X
  1011. - remove duplicate names from uid2names and gid2names...
  1012. X
  1013. - with ~/.login world writeable - only follows group path, but not OTHER.
  1014. X
  1015. - add plans to asseccible list.
  1016. X
  1017. Done
  1018. ----
  1019. X
  1020. - Need to find all plans that lead to compromise, not just a plan.
  1021. X
  1022. - An earlier version scanned the password file looking for generally
  1023. X  accesible accounts (no password), which would be added to the
  1024. X  uids.known list (in addition to -1, "other").  I had planned on also
  1025. X  adding a password checker which would allow us to also add accounts
  1026. X  with easily guessed passwords.  Eventually I nuked the code that
  1027. X  scanned the password file to speed things up, and further reflection
  1028. X  reveals that it isn't wise to add the password scanning to kuang
  1029. X  itself.  At some point we should add a comand line option that
  1030. X  allows us to add additional uid's (or gid's?) to the uids.known
  1031. X  list.  That way the user could run some other tool to scan the
  1032. X  password file and generate a list of accessible accounts, which
  1033. X  could then be fed to kuang.  Makes it faster on clients using YP
  1034. X  since most of the password file is the same for all N clients, why
  1035. X  scan it N times.  Means that user can do smarter things to/with the
  1036. X  password file checks (list all accounts with no password or easily
  1037. X  guessed password, filter out "ok" entries (eg, sync) and etc.)
  1038. X
  1039. - We aren't dealing with uid's and gid's correctly.  If there are
  1040. X  several entries that list the same UID, but with different names,
  1041. X  directories and shells, we'll only check plans for becoming one of
  1042. X  them, rather than any of them.  Hmmm...this is easier than I
  1043. X  thought, when we evaluate some plan for granting a particular uid,
  1044. X  we need to evaluate plans for all usernames that can become that
  1045. X  uid.  Just stick a loop in there somewhere...get CF's for each of
  1046. X  username's in turn.  Bah, harder than I thought, since it'd have to
  1047. X  scan the whole password file to figure which username/home directories
  1048. X  can become which uid's.  Similarly with groups.
  1049. X
  1050. X  Current plan: by default, kuang will have to scan the whole password
  1051. X  and group files so it can be sure to get all possible ways to become
  1052. X  some uid or gid.  Internally, really need several lists:
  1053. X
  1054. X    mapping from uid to list of usernames that have that uid
  1055. X    mapping from a username to home directory, shell
  1056. X    mapping from gid to list of uids that have access to that
  1057. X      gid when they login (either member of group with that gid or
  1058. X      given as login group in passwd file)
  1059. X    mapping from gid to list of group names for that gid
  1060. X
  1061. X  Course, this means that we have to read the whole password and group
  1062. X  file, most of which will be common to many machines (like in a YP
  1063. X  environment).  We could preload the tables above from files created
  1064. X  once, containing the brunt of the YP info, and then augment that
  1065. X  withthe local passwd and group info on each host when kuang is
  1066. X  invoked, but then we need to correctly interpret funky YP things
  1067. X  like +@netgroup:::*:..., which means that the uid has a name but no
  1068. X  password here...and similarly with shell substitutions and so on.
  1069. X  Bah. 
  1070. X
  1071. - The kuang described in Baldwin's dissertation is somewhat different
  1072. X  in nature from this one.  The original computes a Privilege Access
  1073. X  Table (PAT) which describes for each uid and gid which uids have
  1074. X  access to that uid.  To assess security, we compare this against the
  1075. X  security policy for the site, which similarly describes which uid's
  1076. X  are supposed to have access to each uid and gid.  A sample SP might
  1077. X  be that each uid should be accessible only by itself and root, and
  1078. X  each gid should be accessible only to the members of that group and
  1079. X  root.  If the PAT listed additional uid's for some priv, that would
  1080. X  constitute a violation of the Security Policy for the site.
  1081. X
  1082. X  The current kuang is different.  It registers Success (a problem was
  1083. X  found) if it determines that some uid in the uids.known list (-1,
  1084. X  "other" by default) can access the target privilege.  It may find
  1085. X  along the way that extra uids can access some uid, but these aren't
  1086. X  reported as specific problems unless they are added to the
  1087. X  uids.known list. 
  1088. X
  1089. X  We could do something similar to the kuang described in the paper by
  1090. X  setting uids.known to be all the uids that aren't in the security
  1091. X  policy table for the target uid, and running kuang against the
  1092. X  target.  This would report success for each uid that could access
  1093. X  the target.  You could do similar things with groups - uids.known
  1094. X  would be all the uids that aren't members of the group...
  1095. X
  1096. X  Alternately, we could simply have kuang record the list of uids that
  1097. X  can access the target priv and print the list when its done.  That
  1098. X  way you could iterate kuang against all uids and gids and compare
  1099. X  the resulting PAT against your security policy and record the
  1100. X  differences.  You'd probably want to record the plan for each uid
  1101. X  reported also.
  1102. X
  1103. X  On our system this would mean running kuang roughly 2500
  1104. X  times to check 1 host, and we have about 300 hosts...urf...assuming
  1105. X  that each kuang invocation has to check 50 plans, that's a total of
  1106. X  125,000 plans per host, or about an hour of real time...not as bad
  1107. X  as it could be, though.
  1108. X
  1109. - It would be nice to add to the list of rules.  It would be especialy
  1110. X  nice to extract the rules from the code so that we can create site
  1111. X  specific rule files (for example, we use X11r4 here, and many users
  1112. X  have a .Xinitrc that contains shell commands that get executed when
  1113. X  they login.)
  1114. X
  1115. X  Easiest way to do this would be to extract the rules as Perl code so
  1116. X  we can take advantage of conditionals and so on, and include them
  1117. X  within the body of kuang somehow.  A sample rule in perl:
  1118. X
  1119. X    if (&shell($uid) eq "/bin/csh") {
  1120. X        &addto("files", &home($uid)."/.login", 
  1121. X            "replace .login $plan");
  1122. X    }
  1123. X
  1124. X  which simply means "if the user's shell is csh, then try to replace
  1125. X  his .login file." 
  1126. SHAR_EOF
  1127. chmod 0600 p-cops.alpha/README.kuang ||
  1128. echo 'restore of p-cops.alpha/README.kuang failed'
  1129. Wc_c="`wc -c < 'p-cops.alpha/README.kuang'`"
  1130. test 9306 -eq "$Wc_c" ||
  1131.     echo 'p-cops.alpha/README.kuang: original size 9306, current size' "$Wc_c"
  1132. rm -f _shar_wnt_.tmp
  1133. fi
  1134. # ============= p-cops.alpha/suid.stop ==============
  1135. if test -f 'p-cops.alpha/suid.stop' -a X"$1" != X"-c"; then
  1136.     echo 'x - skipping p-cops.alpha/suid.stop (File already exists)'
  1137.     rm -f _shar_wnt_.tmp
  1138. else
  1139. > _shar_wnt_.tmp
  1140. echo 'x - extracting p-cops.alpha/suid.stop (Text)'
  1141. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/suid.stop' &&
  1142. SHAR_EOF
  1143. chmod 0700 p-cops.alpha/suid.stop ||
  1144. echo 'restore of p-cops.alpha/suid.stop failed'
  1145. Wc_c="`wc -c < 'p-cops.alpha/suid.stop'`"
  1146. test 0 -eq "$Wc_c" ||
  1147.     echo 'p-cops.alpha/suid.stop: original size 0, current size' "$Wc_c"
  1148. rm -f _shar_wnt_.tmp
  1149. fi
  1150. # ============= p-cops.alpha/pwgrid.pl ==============
  1151. if test -f 'p-cops.alpha/pwgrid.pl' -a X"$1" != X"-c"; then
  1152.     echo 'x - skipping p-cops.alpha/pwgrid.pl (File already exists)'
  1153.     rm -f _shar_wnt_.tmp
  1154. else
  1155. > _shar_wnt_.tmp
  1156. echo 'x - extracting p-cops.alpha/pwgrid.pl (Text)'
  1157. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/pwgrid.pl' &&
  1158. # Routines for reading and caching user and group information.
  1159. #
  1160. # Provides a bunch of routines and a bunch of arrays.  Routines 
  1161. # (and their usage):
  1162. #
  1163. #    load_passwd_info($use_getent, $file_name)
  1164. #
  1165. #    loads user information into the %uname* and %uid* arrays 
  1166. #    (see below).  
  1167. #
  1168. #    If $use_getent is non-zero:
  1169. #        get the info via repeated 'getpwent' calls.  This can be
  1170. #        *slow* on some hosts, especially if they are running as a
  1171. #        YP (NIS) client.
  1172. #    If $use_getent is 0:
  1173. #        if $file_name is "", then get the info from reading the 
  1174. #        results of "ypcat passwd" and from /etc/passwd.  Otherwise, 
  1175. #        read the named file.  The file should be in passwd(5) 
  1176. #        format.
  1177. #
  1178. #    load_group_info($use_gentent, $file_name)
  1179. #
  1180. #    is similar to load_passwd_info.
  1181. #
  1182. # Information is stored in several convenient associative arrays:
  1183. #
  1184. #   %uname2shell    Assoc array, indexed by user name, value is 
  1185. #            shell for that user name.
  1186. #
  1187. #   %uname2dir        Assoc array, indexed by user name, value is
  1188. #            home directory for that user name.
  1189. #
  1190. #   %uname2uid        Assoc array, indexed by name, value is uid for 
  1191. #            that uid.
  1192. #            
  1193. #   %uname2passwd    Assoc array, indexed by name, value is password
  1194. #            for that user name.
  1195. #
  1196. #   %uid2names        Assoc array, indexed by uid, value is list of
  1197. #            user names with that uid, in form "name name
  1198. #            name...". 
  1199. #
  1200. #   %gid2members    Assoc array, indexed by gid, value is list of
  1201. #            group members in form "name name name..."
  1202. #
  1203. #   %gname2gid        Assoc array, indexed by group name, value is
  1204. #            matching gid.
  1205. #
  1206. #   %gid2names        Assoc array, indexed by gid, value is the
  1207. #            list of group names with that gid in form 
  1208. #            "name name name...".
  1209. #
  1210. # You can also use routines named the same as the arrays - pass the index 
  1211. # as the arg, get back the value.  If you use this, get{gr|pw}{uid|gid|nam} 
  1212. # will be used to lookup entries that aren't found in the cache.
  1213. #
  1214. # To be done:
  1215. #    probably ought to add routines to deal with full names.
  1216. #    maybe there ought to be some anal-retentive checking of password 
  1217. #    and group entries.
  1218. #    probably ought to cache get{pw|gr}{nam|uid|gid} lookups also.
  1219. #    probably ought to avoid overwriting existing entries (eg, duplicate 
  1220. #       names in password file would collide in the tables that are 
  1221. #    indexed by name).
  1222. #
  1223. # Disclaimer:
  1224. #    If you use YP and you use netgroup entries such as 
  1225. #    +@servers::::::
  1226. #    +:*:::::/usr/local/utils/messages
  1227. #    then loading the password file in with &load_passwd_info(0) will get 
  1228. #    you mostly correct YP stuff *except* that it won't do the password and 
  1229. #    shell substitutions as you'd expect.  You might want to use 
  1230. #    &load_passwd_info(1) instead to use getpwent calls to do the lookups, 
  1231. #    which would be more correct.
  1232. #
  1233. X
  1234. %uname2shell = ();
  1235. %uname2dir = ();
  1236. %uname2uid = ();
  1237. %uname2passwd = ();
  1238. %uid2names = ();
  1239. %gid2members = ();
  1240. %gname2gid = ();
  1241. %gid2names = ();
  1242. X
  1243. if (! defined($DOMAINNAME)) {
  1244. X    $DOMAINNAME = "/bin/domainname";
  1245. }
  1246. if (! defined($YPCAT)) {
  1247. X    $YPCAT = "/bin/ypcat";
  1248. }
  1249. X
  1250. $passwd_loaded = 0;        # flags to use to avoid reloading everything
  1251. $group_loaded = 0;        # unnecessarily...
  1252. X
  1253. #
  1254. # We provide routines for getting values from the data structures as well.
  1255. # These are named after the data structures they cache their data in.  Note 
  1256. # that they will all generate password and group file lookups via getpw* 
  1257. # and getgr* if they can't find info in the cache, so they will work
  1258. # "right" even if load_passwd_info and load_group_info aren't called to 
  1259. # preload the caches.
  1260. #
  1261. # I should point out, however, that if you don't call load_*_info to preload
  1262. # the cache, uid2names, gid2names and gid2members *will not* be complete, since 
  1263. # you must read the entire password and group files to get a complete picture.
  1264. # This might be acceptable in some cases, so you can skip the load_*_info
  1265. # calls if you know what you are doing...
  1266. #
  1267. sub uname2shell {
  1268. X    local($key) = @_;
  1269. X
  1270. X    if (! defined($uname2shell{$key})) {
  1271. X    &add_pw_info(getpwnam($key));
  1272. X    }
  1273. X
  1274. X    return($uname2shell{$key});
  1275. }
  1276. X
  1277. sub uname2dir {
  1278. X    local($key) = @_;
  1279. X    local(@pw_info);
  1280. X
  1281. X    if (! defined($uname2dir{$key})) {
  1282. X    &add_pw_info(getpwnam($key));
  1283. X    }
  1284. X
  1285. X    return($uname2dir{$key});
  1286. }
  1287. X
  1288. sub uname2uid {
  1289. X    local($key) = @_;
  1290. X    local(@pw_info);
  1291. X
  1292. X    if (! defined($uname2uid{$key})) {
  1293. X    &add_pw_info(getpwnam($key));
  1294. X    }
  1295. X
  1296. X    return($uname2uid{$key});
  1297. }
  1298. X
  1299. sub uname2passwd {
  1300. X    local($key) = @_;
  1301. X    local(@pw_info);
  1302. X
  1303. X    if (! defined($uname2passwd{$key})) {
  1304. X    &add_pw_info(getpwnam($key));
  1305. X    }
  1306. X
  1307. X    return($uname2passwd{$key});
  1308. }
  1309. X
  1310. sub uid2names {
  1311. X    local($key) = @_;
  1312. X    local(@pw_info);
  1313. X
  1314. X    if (! defined($uid2names{$key})) {
  1315. X    &add_pw_info(getpwuid($key));
  1316. X    }
  1317. X
  1318. X    return($uid2names{$key});
  1319. }
  1320. X
  1321. sub gid2members {
  1322. X    local($key) = @_;
  1323. X    local(@gr_info);
  1324. X
  1325. X    if (! defined($gid2members{$key})) {
  1326. X    &add_gr_info(getgrgid($key));
  1327. X    }
  1328. X
  1329. X    return($gid2members{$key});
  1330. }
  1331. X
  1332. sub gname2gid {
  1333. X    local($key) = @_;
  1334. X    local(@gr_info);
  1335. X
  1336. X    if (! defined($gname2gid{$key})) {
  1337. X    &add_gr_info(getgrnam($key));
  1338. X    }
  1339. X
  1340. X    return($gname2gid{$key});
  1341. }
  1342. X
  1343. sub gid2names {
  1344. X    local($key) = @_;
  1345. X    local(@gr_info);
  1346. X
  1347. X    if (! defined($gid2names{$key})) {
  1348. X    &add_gr_info(getgrgid($key));
  1349. X    }
  1350. X
  1351. X    return($gid2names{$key});
  1352. }
  1353. X
  1354. #
  1355. # Update user information for the user named $name.  We cache the password, 
  1356. # uid, login group, home directory and shell.
  1357. #
  1358. X
  1359. sub add_pw_info {
  1360. X    local($name, $passwd, $uid, $gid) = @_;
  1361. X    local($dir, $shell);
  1362. X
  1363. #
  1364. # Ugh!  argh...yech...sigh.  If we use getpwent, we get back 9 elts, 
  1365. # if we parse /etc/passwd directly we get 7.  Pick off the last 2 and 
  1366. # assume that they are the $directory and $shell.  
  1367. #
  1368. X    $dir = $_[$#_ - 1];
  1369. X    $shell = $_[$#_];
  1370. X
  1371. X    if ($name ne "") {
  1372. X    $uname2shell{$name} = $shell;
  1373. X    $uname2dir{$name} = $dir;
  1374. X    $uname2uid{$name} = $uid;
  1375. X    $uname2passwd{$name} = $passwd;
  1376. X
  1377. X    if ($gid ne "") {
  1378. X        # fixme: should probably check for duplicates...sigh
  1379. X
  1380. X        if (defined($gid2members{$gid})) {
  1381. X        $gid2members{$gid} .= " $name";
  1382. X        } else {
  1383. X        $gid2members{$gid} = $name;
  1384. X        }
  1385. X    }
  1386. X
  1387. X    if ($uid ne "") {
  1388. X        if (defined($uid2names{$uid})) {
  1389. X        $uid2names{$uid} .= " $name";
  1390. X        } else {
  1391. X        $uid2names{$uid} = $name;
  1392. X        }
  1393. X    }
  1394. X    }
  1395. }
  1396. X
  1397. #
  1398. # Update group information for the group named $name.  We cache the gid 
  1399. # and the list of group members.
  1400. #
  1401. X
  1402. sub add_gr_info {
  1403. X    local($name, $passwd, $gid, $members) = @_;
  1404. X
  1405. X    if ($name ne "") {
  1406. X    $gname2gid{$name} = $gid;
  1407. X
  1408. X    if ($gid ne "") {
  1409. X        if (defined($gid2names)) {
  1410. X        $gid2names{$gid} .= " $name";
  1411. X        } else {
  1412. X        $gid2names{$gid} = $name;
  1413. X        }
  1414. X
  1415. X        # fixme: should probably check for duplicates
  1416. X
  1417. X        $members = join(' ', split(/[, \t]+/, $members));
  1418. X
  1419. X        if (defined($gid2members{$gid})) {
  1420. X        $gid2members{$gid} .= " " . $members;
  1421. X        } else {
  1422. X        $gid2members{$gid} = $members;
  1423. X        }
  1424. X    }
  1425. X    }
  1426. }
  1427. X
  1428. #
  1429. # We need to suck in the entire group and password files so that we can 
  1430. # make the %uid2names, %gid2members and %gid2names lists complete.  Otherwise,
  1431. # we would just read the entries as needed with getpw* and cache the results.
  1432. # Sigh.
  1433. #
  1434. # There are several ways that we might find the info.  If $use_getent is 1, 
  1435. # then we just use getpwent and getgrent calls to read the info in.
  1436. #
  1437. # That isn't real efficient if you are using YP (especially on a YP client), so
  1438. # if $use_getent is 0, we can use ypcat to get a copy of the passwd and
  1439. # group maps in a fairly efficient manner.  If we do this we have to also read
  1440. # the local /etc/{passwd,group} files to complete our information.  If we aren't 
  1441. # using YP, we just read the local pasword and group files.
  1442. #
  1443. sub load_passwd_info {
  1444. X    local($use_getent, $file_name) = @_;
  1445. X    local(@pw_info);
  1446. X
  1447. X    if ($passwd_loaded) {
  1448. X    return;
  1449. X    }
  1450. X
  1451. X    $passwd_loaded = 1;
  1452. X
  1453. X    if ($use_getent) {
  1454. X    #
  1455. X    # Use getpwent to get the info from the system, and add_pw_info to 
  1456. X    # cache it.
  1457. X    #
  1458. X    while ((@pw_info = getpwent()) != 0) {
  1459. X        &add_pw_info(@pw_info);
  1460. X    }
  1461. X
  1462. X    endpwent();
  1463. X
  1464. X    return();
  1465. X    } elsif ($file_name eq "") {
  1466. X    chop($has_yp = `$DOMAINNAME`);
  1467. X    if ($has_yp) {
  1468. X        #
  1469. X        # If we have YP (NIS), then use ypcat to get the stuff from the 
  1470. X        # map.
  1471. X        #
  1472. X        open(FILE, "$YPCAT passwd|") ||
  1473. X          die "can't 'ypcat passwd'";
  1474. X        while (<FILE>) {
  1475. X        chop;
  1476. X        &add_pw_info(split(/:/));
  1477. X        }
  1478. X        close(FILE);
  1479. X    }
  1480. X
  1481. X    #
  1482. X    # We have to read /etc/passwd no matter what...
  1483. X    #
  1484. X    $file_name = "/etc/passwd";
  1485. X    }
  1486. X
  1487. X    open(FILE, $file_name) ||
  1488. X      die "can't open $file_name";
  1489. X
  1490. X    while (<FILE>) {
  1491. X    chop;
  1492. X        
  1493. X    if ($_ !~ /^\+/) {
  1494. X        &add_pw_info(split(/:/));
  1495. X    }
  1496. X
  1497. X    # fixme: if the name matches +@name, then this is a wierd 
  1498. X    # netgroup thing, and we aren't dealing with it right.  might want
  1499. X    # to warn the poor user...suggest that he use the use_getent 
  1500. X    # method instead.
  1501. X    }
  1502. X
  1503. X    close(FILE);
  1504. }
  1505. X
  1506. sub load_group_info {
  1507. X    local($use_getent, $file_name) = @_;
  1508. X    local(@gr_info);
  1509. X
  1510. X    if ($group_loaded) {
  1511. X    return;
  1512. X    }
  1513. X
  1514. X    $group_loaded = 1;
  1515. X
  1516. X    if ($use_getent) {
  1517. X    #
  1518. X    # Use getgrent to get the info from the system, and add_gr_info to 
  1519. X    # cache it.
  1520. X    #
  1521. X    while ((@gr_info = getgrent()) != 0) {
  1522. X        &add_gr_info(@gr_info);
  1523. X    }
  1524. X
  1525. X    endgrent();
  1526. X
  1527. X    return();
  1528. X    } elsif ($file_name eq "") {
  1529. X    chop($has_yp = `$DOMAINNAME`);
  1530. X    if ($has_yp) {
  1531. X        #
  1532. X        # If we have YP (NIS), then use ypcat to get the stuff from the 
  1533. X        # map.
  1534. X        #
  1535. X        open(FILE, "$YPCAT group|") ||
  1536. X          die "can't 'ypcat group'";
  1537. X        while (<FILE>) {
  1538. X        chop;
  1539. X        &add_gr_info(split(/:/));
  1540. X        }
  1541. X        close(FILE);
  1542. X    }
  1543. X
  1544. X    #
  1545. X    # We have to read /etc/group no matter what...
  1546. X    #
  1547. X    $file_name = "/etc/group";
  1548. X    }
  1549. X
  1550. X    open(FILE, $file_name) ||
  1551. X      die "can't open $file_name";
  1552. X
  1553. X    while (<FILE>) {
  1554. X    chop;
  1555. X    if ($_ !~ /^\+/) {
  1556. X        &add_gr_info(split(/:/));
  1557. X    }
  1558. X
  1559. X    # fixme: if the name matches +@name, then this is a wierd 
  1560. X    # netgroup thing, and we aren't dealing with it right.  might want
  1561. X    # to warn the poor user...suggest that he use the use_getent 
  1562. X    # method instead.
  1563. X    }
  1564. X
  1565. X    close(FILE);
  1566. }
  1567. X
  1568. 1;
  1569. X
  1570. SHAR_EOF
  1571. chmod 0600 p-cops.alpha/pwgrid.pl ||
  1572. echo 'restore of p-cops.alpha/pwgrid.pl failed'
  1573. Wc_c="`wc -c < 'p-cops.alpha/pwgrid.pl'`"
  1574. test 9915 -eq "$Wc_c" ||
  1575.     echo 'p-cops.alpha/pwgrid.pl: original size 9915, current size' "$Wc_c"
  1576. rm -f _shar_wnt_.tmp
  1577. fi
  1578. # ============= p-cops.alpha/root.chk.new ==============
  1579. if test -f 'p-cops.alpha/root.chk.new' -a X"$1" != X"-c"; then
  1580.     echo 'x - skipping p-cops.alpha/root.chk.new (File already exists)'
  1581.     rm -f _shar_wnt_.tmp
  1582. else
  1583. > _shar_wnt_.tmp
  1584. echo 'x - extracting p-cops.alpha/root.chk.new (Text)'
  1585. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/root.chk.new' &&
  1586. #!/bin/sh  # need to mention perl here to avoid recursion
  1587. #
  1588. #  Usage: root.chk
  1589. #
  1590. #  This script checks pathnames inside root's startup files for 
  1591. # writability, improper umask settings (world writable), non-root
  1592. # entries in /.rhosts, writable binaries in root's path,
  1593. # and to ensure that root is in /etc/ftpuser.
  1594. #
  1595. # Also check for a single "+" in /etc/hosts.equiv (world is trusted),
  1596. # and that /bin, /etc and certain key files are root owned, so that you
  1597. # can't, say, rcp from a host.equived machine and blow over the password
  1598. # file... this may or may not be bad, decide for yourself.
  1599. # Startup files are /.login /.cshrc /.profile
  1600. #
  1601. #  Mechanism:  These files contain paths and filenames that are stripped
  1602. # out using "grep".  These strings are then processed by the "is_able"
  1603. # program to see if they are world writable.  Strings of the form:
  1604. #
  1605. #    path=(/bin /usr/bin .)
  1606. #        and
  1607. #    PATH=/bin:/usr/bin:.:
  1608. #
  1609. # are checked  to ensure that "." is not in the path.  All
  1610. # results are echoed to standard output.  In addition, some effort was
  1611. # put into parsing out paths with multiple lines; e.g. ending in "\",
  1612. # and continuing on the next line.  Also, all executable files and 
  1613. # directories in there are checked for writability as well.
  1614. #
  1615. #  For umask stuff, simply grep for umask in startup files, and check
  1616. # umask value.  For /etc/ftpuser, simple grep to check if root is in
  1617. # the file.  For /etc/hosts.equiv, just check to see if "+" is alone
  1618. # on a line by awking it.
  1619. #
  1620. #  NOTE:
  1621. # if you know where perl is and your system groks #!, put its
  1622. # pathname at the top to make this a tad faster.
  1623. #
  1624. # the following magic is from the perl man page
  1625. # and should work to get us to run with perl 
  1626. # even if invoked as an sh or csh or foosh script.
  1627. # notice we don't use full path cause we don't
  1628. # know where the user has perl on their system.
  1629. #
  1630. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  1631. & eval 'exec perl -S $0 $argv:q'
  1632. X    if $running_under_some_stupid_shell_instead_of_perl;
  1633. X
  1634. # rewritten in perl by tchrist@convex.com
  1635. X
  1636. # root startup/important files
  1637. X
  1638. require 'file_owner.pl';
  1639. require 'fgrep.pl';
  1640. require 'suckline.pl';
  1641. require 'is_able.pl';
  1642. require 'chk_strings.pl';
  1643. require 'glob.pl';
  1644. X
  1645. package root_chk;
  1646. X
  1647. # use -a true if you care about non-executables
  1648. # in root's path
  1649. X
  1650. $ARGV[0] eq '-a' && ($all_files++, shift);
  1651. X
  1652. die "usage: root.chk [-a]\n" if @ARGV;
  1653. X
  1654. $W = 'Warning! ';
  1655. X
  1656. $cshrc    = '/.cshrc';
  1657. $profile= '/.profile';
  1658. $rhosts = '/.rhosts';
  1659. X
  1660. $| = 1;
  1661. X
  1662. @big_files= ('/.login', '/.cshrc', '/.profile', '/.logout' );
  1663. X
  1664. # root should own *at least* these, + $big_files; you can check for all files
  1665. # in /bin & /etc, or just the directories (the default.)
  1666. # root_files="/bin /bin/* /etc /etc/* $big_files $rhosts"
  1667. @root_files= ('/bin','/etc',@big_files,$rhosts,'/etc/passwd','/etc/group');
  1668. X
  1669. # misc important stuff
  1670. $ftp='/etc/ftpusers';
  1671. $equiv='/etc/hosts.equiv';
  1672. X
  1673. #   should't have anyone but root owning /bin or /etc files/directories
  1674. # In case some of the critical files don't exist (/.rhost), toss away error
  1675. # messages
  1676. X
  1677. if (@bad_files = grep (-e && &'Owner($_), @root_files)) {
  1678. X    print "$W  Root does not own the following file(s):\n";
  1679. X    print "\t@bad_files\n";
  1680. X
  1681. local($chk_strings'recurse) = 1 unless defined $chk_strings'recurse;
  1682. X
  1683. for $file (@big_files) {
  1684. X    open file || next;
  1685. X
  1686. X    &'chk_strings($file);
  1687. X
  1688. X    # check for group or other writable umask
  1689. X    while (<file>) {
  1690. X    next if /^\s*#/;
  1691. X    next unless /umask\s*(\d+)/;
  1692. X    next unless ~oct($1) & 022;
  1693. X    print "$W root's umask set to $1 in $file\n";
  1694. X    } 
  1695. X
  1696. print "$W $ftp exists and root is not in it\n" 
  1697. X    if -e $ftp && !&'fgrep($ftp,'root');
  1698. X
  1699. print "$W A \"+\" entry exists in $equiv!\n" if &'fgrep($equiv, '^\+$');
  1700. X
  1701. if (open rhosts) {
  1702. X    while (<rhosts>) {
  1703. X    next unless /\S+\s+(\S+)/ && $1 ne 'root';
  1704. X    print "$W Non-root entry in $rhosts! $1\n";
  1705. X    }
  1706. close(rhosts);
  1707. X
  1708. undef @rootpath;
  1709. X
  1710. # checking paths...
  1711. #
  1712. # Get the root paths from $csh.
  1713. X
  1714. print "FOO-4:\n";
  1715. if (open(CSHRC, $cshrc)) {
  1716. X    $path = '';
  1717. X    while (<CSHRC>) {
  1718. X    print "FOO-3: $_\n";
  1719. X    next if /^\s*#/;
  1720. X    chop unless /\\$/;
  1721. X    if (/set\s+path\s*=/) {
  1722. X      print "FOO-2: $_\n";
  1723. X        $_ = &'suckline($cshrc, $_);
  1724. X        s/.*set\s+path\s*=\s*//;
  1725. X        s/\((.*)\)/$1/;
  1726. X        s/#.*/./;
  1727. X        @tmppath = grep($_ ne '', split(' '));
  1728. X        for (@tmppath) { $whence{$_} .= " " . $cshrc; } 
  1729. X        push(@rootpath, @tmppath);
  1730. X    } 
  1731. X    } 
  1732. X    close(CSHRC);
  1733. X
  1734. print "FOO0: @rootpath\n";
  1735. X
  1736. if (open login) {
  1737. X    $path = '';
  1738. X    while (<cshrc>) {
  1739. X    next if /^\s*#/;
  1740. X    chop unless /\\$/;
  1741. X    if (/set\s+path\s*=/) {
  1742. X        $_ = &'suckline('login', $_);
  1743. X        s/.*set\s+path\s*=\s*//;
  1744. X        s/\((.*)\)/$1/;
  1745. X        s/#.*/./;
  1746. X        @tmppath = grep($_ ne '', split(' '));
  1747. X        for (@tmppath) { $whence{$_} .= " " . $login; } 
  1748. X        push(@rootpath, @tmppath);
  1749. X    } 
  1750. X    } 
  1751. X    close(login);
  1752. }
  1753. X
  1754. print "FOO1: @rootpath\n";
  1755. X
  1756. if (open profile) {
  1757. X    $path = '';
  1758. X    while (<profile>) {
  1759. X    next if /^\s*#/;
  1760. X    chop unless /\\$/;
  1761. X    if (/PATH=/) {
  1762. X        $_ = &'suckline('profile', $_);
  1763. X        s/.*PATH=//;
  1764. X        s/#.*//;
  1765. X        @tmppath = split(/:/);
  1766. X        for (@tmppath) { $whence{$_} .= " " . $profile; } 
  1767. X        push(@rootpath, @tmppath);
  1768. X    } 
  1769. X    } 
  1770. X    close(profile);
  1771. X
  1772. print "FOO2: @rootpath\n";
  1773. X
  1774. for (keys %whence) {
  1775. X    $whence{$_} =~ s/^ //;
  1776. X    $whence{$_} =~ s/ / and /g;
  1777. X
  1778. undef %seen;
  1779. grep($seen{$_}++, @rootpath);
  1780. X
  1781. $is_able'silent = 1;
  1782. for (keys %seen) {
  1783. X    print "WAK: $_\n";
  1784. X    if (!-e && $_ ne ".") {
  1785. X    print "$W path component $_ in $whence{$_} doesn't exist!\n";
  1786. X    next;
  1787. X    } 
  1788. X
  1789. X    if (/^\.?$/) {  # null -> dot
  1790. X    print "$W \".\" (or current directory) is in root's path in $whence{$_}!\n";
  1791. X    } elsif (&'is_writable($_)) {
  1792. X    print "$W Directory $_ is _World_ writable and in root's path in $whence{$_}!\n";
  1793. X    next;
  1794. X    }
  1795. X
  1796. X    foreach $file (&'glob("$_/*")) {
  1797. X    print "BAR: $_\n";
  1798. X    # can't just check -x here, as that depends on current user
  1799. X    $is_executable = -f $file && (&'Mode($file) & 0111);
  1800. X    if (($all_files || $is_executable) && 
  1801. X            ($how = &'is_writable($file, 'w', 'w'))) {
  1802. X        print "$W _World_ $how ",
  1803. X            $is_executable ? 'executable' : 'file',
  1804. X        " $file in root path component $_ from $whence{$_}!\n";
  1805. X    } 
  1806. X    }
  1807. X
  1808. $is_able'silent = 0;
  1809. X
  1810. 1;
  1811. SHAR_EOF
  1812. chmod 0700 p-cops.alpha/root.chk.new ||
  1813. echo 'restore of p-cops.alpha/root.chk.new failed'
  1814. Wc_c="`wc -c < 'p-cops.alpha/root.chk.new'`"
  1815. test 6147 -eq "$Wc_c" ||
  1816.     echo 'p-cops.alpha/root.chk.new: original size 6147, current size' "$Wc_c"
  1817. rm -f _shar_wnt_.tmp
  1818. fi
  1819. # ============= p-cops.alpha/rules.pl ==============
  1820. if test -f 'p-cops.alpha/rules.pl' -a X"$1" != X"-c"; then
  1821.     echo 'x - skipping p-cops.alpha/rules.pl (File already exists)'
  1822.     rm -f _shar_wnt_.tmp
  1823. else
  1824. > _shar_wnt_.tmp
  1825. echo 'x - extracting p-cops.alpha/rules.pl (Text)'
  1826. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/rules.pl' &&
  1827. sub apply_rules {
  1828. X    local($op, $value, @plan) = @_;
  1829. X
  1830. X    printf("eval($op $value): %s\n", &ascii_plan(@plan)) if $opt_d;
  1831. X
  1832. X    #
  1833. X    # apply UID attack rules...
  1834. X    #
  1835. X    if ($op eq "u") {
  1836. X    #
  1837. X    # If we can replace /etc/passwd or /usr/lib/aliases, we can grant 
  1838. X    # any uid. 
  1839. X    #
  1840. X    &addto("r", "/etc/passwd", @plan);
  1841. X        &addto("r", "/usr/lib/aliases", @plan);
  1842. X        &addto("r", "/etc/aliases", @plan);
  1843. X
  1844. X    #
  1845. X    # Check CF's for all usernames with this uid.
  1846. X    #
  1847. uname_loop:
  1848. X    foreach $uname (split(/ /, $uid2names{$value})) {
  1849. X        $home = $uname2dir{$uname};
  1850. X
  1851. X        next uname_loop unless $home;
  1852. X
  1853. X        if ($home eq "/") {
  1854. X        $home = "";
  1855. X        }
  1856. X        &addto("r", "$home/.rhosts", @plan);
  1857. X        &addto("r", "$home/.login", @plan);
  1858. X        &addto("r", "$home/.logout", @plan);
  1859. X        &addto("r", "$home/.cshrc", @plan);
  1860. X        &addto("r", "$home/.profile", @plan);
  1861. X    }
  1862. X
  1863. X    #
  1864. X    # Controlling files for root...
  1865. X    #
  1866. X    @rootlist = ( 
  1867. X        "/etc/rc", "/etc/rc.boot", "/etc/rc.single", 
  1868. X        "/etc/rc.config", "/etc/rc.local", "/usr/lib/crontab",
  1869. X        "/usr/spool/cron/crontabs",
  1870. X        );
  1871. X
  1872. X    if ($value eq "0") {
  1873. X        foreach $file (@rootlist) {
  1874. X            &addto("r", $file, @plan);
  1875. X        }
  1876. X        # Experimental!
  1877. X        # you can remove this if desired - tjt
  1878. X        #do "rc.prog";
  1879. X    }
  1880. X
  1881. X    #
  1882. X    # Other CFs for non-root folks...
  1883. X    #
  1884. X    if ($value ne "0") {
  1885. X        &addto("r", "/etc/hosts.equiv", @plan);
  1886. X        if (-s "/etc/hosts.equiv") {
  1887. X        &addto("r", "/etc/hosts", @plan);
  1888. X        }
  1889. X    }
  1890. X
  1891. X    #
  1892. X    # Plans for attacking GIDs...
  1893. X    #
  1894. X    } elsif ($op eq "g") {    # apply gid attack rules
  1895. X
  1896. X    #
  1897. X    # If we can replace /etc/group we can become any group
  1898. X    #                  
  1899. X        &addto("r", "/etc/group", @plan);
  1900. X
  1901. X    #
  1902. X    # If we can grant any member of a group we can grant that group
  1903. X    #
  1904. member_loop:
  1905. X    foreach $uname (split(/ /, $gid2members{$value})) {
  1906. X        if (! defined($uname2uid{$uname})) {
  1907. X        printf(stderr "group '%s' member '%s' doesn't exist.\n",
  1908. X            $value,
  1909. X            $uname);
  1910. X        next member_loop;
  1911. X        }
  1912. X
  1913. X        &addto("u", $uname2uid{$uname}, @plan);
  1914. X    }
  1915. X
  1916. X    #
  1917. X    # Plans for attacking files...
  1918. X    #
  1919. X
  1920. X    } elsif ($op eq "r" || $op eq "w") {
  1921. X
  1922. X        ($owner, $group, $other) = &filewriters($value);
  1923. X
  1924. X    &addto("u", $owner, @plan) if ($owner ne "");
  1925. X    &addto("g", $group, @plan) if ($group ne "");
  1926. X    &addto("u", "-1", @plan) if ($other);
  1927. X
  1928. X    #
  1929. X    # If the goal is to replace the file, check the parent directory...
  1930. X    #
  1931. X    if ($op eq "r") {
  1932. X        $parent = $value;
  1933. X        $parent =~ s#/[^/]*$##;     # strip last / and remaining stuff
  1934. X
  1935. X        if ($parent eq "") {
  1936. X        $parent = "/";
  1937. X        }
  1938. X
  1939. X        if ($parent ne $value) {
  1940. X        &addto("r", $parent, @plan);
  1941. X        }
  1942. X    }
  1943. X
  1944. X    } else {            # wow, bad $type of object!
  1945. X    printf(stderr "kuang: bad op in apply_rules!\n");
  1946. X    printf(stderr "op '%s' value '%s' plan '%s'\n",
  1947. X        $op,
  1948. X        $value,
  1949. X        &ascii_plan(@plan));
  1950. X    exit(1);
  1951. X    }
  1952. }
  1953. X
  1954. 1;
  1955. X
  1956. SHAR_EOF
  1957. chmod 0600 p-cops.alpha/rules.pl ||
  1958. echo 'restore of p-cops.alpha/rules.pl failed'
  1959. Wc_c="`wc -c < 'p-cops.alpha/rules.pl'`"
  1960. test 2768 -eq "$Wc_c" ||
  1961.     echo 'p-cops.alpha/rules.pl: original size 2768, current size' "$Wc_c"
  1962. rm -f _shar_wnt_.tmp
  1963. fi
  1964. rm -f _shar_seq_.tmp
  1965. echo You have unpacked the last part
  1966. exit 0
  1967.