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

  1. From: df@sei.cmu.edu (Dan Farmer)
  2. Newsgroups: alt.sources
  3. Subject: alpha perl-cops, 2 of 3
  4. Message-ID: <25551@as0c.sei.cmu.edu>
  5. Date: 17 May 91 04:46:54 GMT
  6.  
  7. Submitted-by: df@death.cert.sei.cmu.edu
  8. Archive-name: alpha p-cops/part02
  9.  
  10. #!/bin/sh
  11. # this is apcops.02 (part 2 of alpha p-cops)
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file p-cops.alpha/kuang 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" != 2; 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/kuang'
  29. else
  30. echo 'x - continuing file p-cops.alpha/kuang'
  31. sed 's/^X//' << 'SHAR_EOF' >> 'p-cops.alpha/kuang' &&
  32. #   operation (u for uid, g for gid, r for replace, w for write) and
  33. #   value is a uid, gid or pathname.
  34. #
  35. #   A plan is a chain of CO's that are connected to each other.  If
  36. #   /.login were writeable by uid 216, we might have a plan such as:
  37. #
  38. #    uid 216 => write /.login => uid 0
  39. #
  40. #   which means (in English) "if we can become uid 216, then write 
  41. #   /.login which gives you access to uid 0 (when root next logs in)."
  42. #   Plans are represented in several ways: as arrays:
  43. #
  44. #    ("u 0", "w /.login", "u 216")
  45. #
  46. #   Note that the order is reversed.  As a string:
  47. #
  48. #    "u 0\034w /.login\034u 216"
  49. #
  50. #   The target is the object that we are trying to gain (a uid, gid or
  51. #   file, typically u.root or some other UID).
  52. #
  53. # Data Structures
  54. #
  55. #   %known        An assocc array, indexed by CO.  This lists
  56. #            the COs that we already have access to.  If we
  57. #                       find a plan that leads from a CO in the known
  58. #                       list to the target, we've succeeded in
  59. #                       finding a major security flaw.  
  60. #
  61. #   @new        An array of plans that are to be evaluated in
  62. #            the next cycle. 
  63. #
  64. #   @old        An array of plans that we are currently
  65. #            evaluating. 
  66. #
  67. #   %beendone        An assoc array that lists the plans that have
  68. #            already been tried.  Used to prevent loops.
  69. #
  70. #   @accessible        An array of the uids that can reach the
  71. #            target. 
  72. #
  73. #   %files        An assoc array, indexed by file name, contains
  74. #            cached file info.  value is of form "uid gid
  75. #            mode". 
  76. #
  77. # From pwgrid:
  78. #
  79. #   %uname2shell    Assoc array, indexed by user name, values are
  80. #            shells. 
  81. #
  82. #   %uname2dir        Assoc array, indexed by user name, values are
  83. #            home directories.
  84. #
  85. #   %uname2uid        Assoc array, indexed by name, values are uids.
  86. #            
  87. #   %uid2names        Assoc array, indexed by uid, value is list of
  88. #            user names with that uid, in form "name name
  89. #            name...". 
  90. #
  91. #   %gid2members    Assoc array, indexed by gid, value is list of
  92. #            group members (user names).
  93. #
  94. #   %gname2gid        Assoc array, indexed by group name, values are
  95. #            matching gids.
  96. #
  97. #   %gid2names        Assoc array, indexed by gid, values are
  98. #            matching group names.
  99. #
  100. X
  101. do 'yagrip.pl' ||
  102. X  die "can't do yagrip.pl";
  103. X
  104. do 'pwgrid.pl' ||
  105. X  die "can't do pwgrid.pl";
  106. X
  107. do 'rules.pl' ||
  108. X  die "can't do rules.pl";
  109. X
  110. X
  111. #
  112. # Turns a string of the form "operation value" or "value" into
  113. # standard "CO" form ("operation value").  Converts user or group
  114. # names into corresponding uid and gid values. 
  115. #
  116. # Returns nothing if it isn't parseable.
  117. #
  118. X
  119. sub canonicalize {
  120. X    local($string) = @_;
  121. X    local($op, $value);
  122. X
  123. X    if ($string =~ /^([ugrw]) ([^ \t\n]+)$/) { # of form "op value"
  124. X    $op = $1;
  125. X    $value = $2;
  126. X    } elsif ($string =~ /^[^ \t\n]+$/) {       # of form "value"
  127. X        $value = $string;
  128. X    $op = "u";
  129. X    } else {
  130. X    return();
  131. X    }
  132. X
  133. X    if ($op eq "u" && $value =~ /^[^0-9]+$/) { # user name, not ID
  134. X        if (defined($uname2uid{$value})) {
  135. X        $value = $uname2uid{$value};
  136. X    } else {
  137. X        printf(stderr "There's no user named '%s'.\n", $value);
  138. X        return();
  139. X    }
  140. X    } elsif ($op eq "g" && $value =~/^[^0-9]+$/) {
  141. X    if (defined($gname2gid{$value})) {
  142. X        $value = $gname2gid{$value};
  143. X    } else {
  144. X        printf(stderr "There's no group named '%s'.\n", $value);
  145. X        return();
  146. X    }
  147. X    }
  148. X
  149. X    return($op, $value);
  150. }
  151. X
  152. X
  153. #
  154. # Preload file information from a text file or DBM database.  
  155. # If $opt_f.dir exists, then we just shadow %files from a DBM
  156. # database.  Otherwise, open the file and read the entries into 
  157. # %files.  
  158. #
  159. # $add_files_to_cache is set to 0 if we get the info from 
  160. # DBM since we wouldn't want to pollute update our DBM cache
  161. # with local file info which wouldn't apply to other hosts.
  162. #
  163. X
  164. sub preload_file_info {
  165. X    local($count, $f_type, $f_uid, $f_gid, $f_mode, $f_name);
  166. X
  167. X    if (defined($opt_d)) {
  168. X    printf("loading file info...\n");
  169. X    }
  170. X
  171. X    if (-f "$opt_f.dir") {
  172. X    $add_files_to_cache = 0;
  173. X
  174. X    dbmopen(files, $opt_f, 0644) ||
  175. X      die sprintf("can't open DBM file '%s'", $opt_f);
  176. X    } else {
  177. X    open(FILEDATA, $opt_f) || 
  178. X      die sprintf("kuang: can't open '%s'", $opt_f);
  179. X
  180. X    $count = 0;
  181. X    while (<FILEDATA>) {
  182. X        $count++;
  183. X
  184. X        chop;
  185. X        ($f_type, $f_uid, $f_gid, $f_mode, $f_name) = split;
  186. X        
  187. X        if ($count % 1000 == 0) {
  188. X        printf("line $count, reading entry for $f_name\n");
  189. X        }
  190. X        $files{$f_name} = join(' ', $f_uid, $f_gid, $f_mode);
  191. X    }
  192. X
  193. X    close(FILEDATA);
  194. X    }
  195. }
  196. X
  197. #
  198. # Preload the known information.  Reads data from a file, 1 entry per line,
  199. # each entry is a CO that we "know" can be used.
  200. #
  201. X
  202. sub preload_known_info {
  203. X    local($file_name) = @_;
  204. X    local($op, $value, $co);
  205. X
  206. X    open(FILE, $file_name) ||
  207. X      die sprintf("kuang: can't open '%s'", $file_name);
  208. X
  209. X  known_loop:
  210. X    while (<FILE>) {
  211. X    chop;
  212. X    if ((($op, $value) = &canonicalize($_)) == 2) {
  213. X        $co = sprintf("%s %s", $op, $value);
  214. X        $known{$co} = 1;
  215. X    } else {
  216. X        printf(stderr "kuang: invalid entry in known list: line %d '%s'.\n",
  217. X           $.,
  218. X           $_);
  219. X    }
  220. X    }
  221. X
  222. X    close(FILE);
  223. }
  224. X    
  225. X
  226. #
  227. # Do various initialization type things.
  228. #
  229. X
  230. sub init_kuang {
  231. X    local($which, $name, $uid, $gid);
  232. X    local($op, $value, $co);
  233. X
  234. X    #
  235. X    # Deal with args...
  236. X    #
  237. X
  238. X    &getopt($options) ||
  239. X      die $usage;
  240. X
  241. X    if ($#ARGV == -1) {
  242. X    push(@ARGV, "u root");
  243. X    }
  244. X
  245. X    #
  246. X    # Preload anything...
  247. X    #
  248. X    if (defined($opt_f)) {
  249. X    &preload_file_info();
  250. X    }
  251. X
  252. X    if (defined($opt_d)) {
  253. X    printf("load passwd info...\n");
  254. X    }
  255. X
  256. X    if (defined($opt_p)) {
  257. X    if (defined($opt_P)) {
  258. X        printf(stderr "You can only specify one of -p or -P, not both.\n");
  259. X        exit(1);
  260. X    }
  261. X
  262. X    &load_passwd_info(0, $opt_p);
  263. X    } elsif (defined($opt_P)) {
  264. X    &load_passwd_info(0);
  265. X    } else {
  266. X    &load_passwd_info(1);
  267. X    }
  268. X
  269. X    if (defined($opt_d)) {
  270. X    printf("load group info...\n");
  271. X    }
  272. X
  273. X    if (defined($opt_g)) {
  274. X    if (defined($opt_G)) {
  275. X        printf(stderr "You can only specify one of -g or -G, not both.\n");
  276. X        exit(1);
  277. X    }
  278. X
  279. X    &load_group_info(0, $opt_g);
  280. X    } elsif (defined($opt_G)) {
  281. X    &load_group_info(0);
  282. X    } else {
  283. X    &load_group_info(1);
  284. X    }
  285. X
  286. X    #
  287. X    # Need some of the password and group stuff.  Suck in passwd and 
  288. X    # group info, store by uid and gid in an associative array of strings
  289. X    # which consist of fields corresponding to the passwd and group file 
  290. X    # entries (and what the heck, we'll use : as a delimiter also...:-)
  291. X    #
  292. X    $uname2shell{"OTHER"} = "";
  293. X    $uname2dir{"OTHER"} = "";
  294. X    $uname2uid{"OTHER"} = -1;
  295. X    $uid2names{-1} = "OTHER";
  296. X
  297. X    $known{"u -1"} = 1;        # We can access uid OTHER
  298. X
  299. X    if (defined($opt_k)) {
  300. X    &preload_known_info($opt_k);
  301. X    }
  302. X
  303. X    #
  304. X    # Create the target list from the remaining (non-option) args...
  305. X    #
  306. X    while ($#ARGV >= 0) {
  307. X    $elt = pop(@ARGV);
  308. X    if ((($op, $value) = &canonicalize($elt)) == 2) {
  309. X        $co = sprintf("%s %s", $op, $value);
  310. X        push(@targets, $co);
  311. X    } else {
  312. X        printf(stderr "target '%s' isn't of correct form\n", $elt);
  313. X    }
  314. X    }
  315. }
  316. X
  317. X
  318. #
  319. # Call this to set things up for a new target.  Resets old, new, beendone 
  320. # and accessible.  
  321. #
  322. sub set_target {
  323. X    local($target) = @_;
  324. X
  325. X    @old = ();
  326. X    @new = ();
  327. X    %beendone = ();
  328. X    @accessible = ();
  329. # fixme: reset known?
  330. X
  331. X    if ($target =~ /^([ugrw]) ([^ \t]+)$/) {
  332. X    &addto($1, $2);
  333. X    return(0);
  334. X    } else {
  335. X    printf(stderr "kuang: bad target '%s'\n", $target);
  336. X    return(1);
  337. X    }
  338. }
  339. X
  340. #
  341. # Break a CO into an (operation, value) pair and return it.  If it
  342. # isn't in "operation value" form, return ().
  343. #
  344. sub breakup {
  345. X    local($co) = @_;
  346. X    local($operation, $value);
  347. X
  348. X    if ($co =~ /^([ugrw]) ([^ \t]+)$/) {
  349. X    $operation = $1;
  350. X    $value = $2;
  351. X    } else {
  352. X    printf(stderr "Yowza, breakup failed on '%s'\n",
  353. X        $co);
  354. X    exit(1);
  355. X    }
  356. X
  357. X    return($operation, $value);
  358. }
  359. X
  360. #
  361. # Get the writers of the named file - return as (UID, GID, OTHER)
  362. # triplet.  Owner can always write, since he can chmod the file if he
  363. # wants. 
  364. #
  365. # (fixme) are there any problems in this sort of builtin rule?  should
  366. # we make this knowledge more explicit?
  367. #
  368. sub filewriters {
  369. X    local($name) = @_;
  370. X    local($tmp, $mode, $uid, $gid, $other);
  371. X    
  372. X    #
  373. X    # Check the file cache - avoid disk lookups for performance and 
  374. X    # to avoid shadows...
  375. X    #
  376. X    if (defined($files{$name})) {
  377. X    $cache_hit++;
  378. X    
  379. X    ($uid, $gid, $mode, $tmp) = split(/ /, $files{$name});
  380. X    } else {
  381. X    $cache_miss++;
  382. X
  383. X    unless (-e $name) {
  384. X        if ($add_files_to_cache) {
  385. X        $files{$name} = "";
  386. X        }
  387. X        # ENOTDIR = 20 
  388. X        ($! == 20) && print "Warning: Illegal Path: '$name'\n";
  389. X        # EACCES = 13
  390. X        ($! == 13) && print "Warning: Permission Denied: '$name'\n";
  391. X        # all values are returned "" here.
  392. X        return;
  393. X    }
  394. X
  395. X    ($tmp,$tmp,$mode,$tmp,$uid,$gid) = stat(_);
  396. X    if ($add_files_to_cache) {
  397. X        $files{$name} = join(' ', "$uid", "$gid", "$mode");
  398. X    }
  399. X    }
  400. X
  401. X    if (($mode & 020) != 020) {
  402. X    $gid = "";
  403. X    }
  404. X    
  405. X    if (($mode & 02) == 02) {
  406. X    $other = 1;
  407. X    } else {
  408. X    $other = 0;
  409. X    }
  410. X
  411. X    return($uid, $gid, $other);
  412. }
  413. X
  414. X
  415. sub ascii_plan {
  416. X    local(@plan) = @_;
  417. X    local($op, $value, $result);
  418. X
  419. X    for ($i = $#plan; $i >= 0; $i--) {
  420. X    ($op, $value) = &breakup($plan[$i]);
  421. X
  422. X      case: 
  423. X    {
  424. X        if ($op eq "g") {
  425. X        $op = "grant gid";
  426. X        last case;
  427. X        }
  428. X
  429. X        if ($op eq "u") {
  430. X        $op = "grant uid";
  431. X        last case;
  432. X        }
  433. X
  434. X        if ($op eq "r") {
  435. X        $op = "replace";
  436. X        last case;
  437. X        }
  438. X
  439. X        if ($op eq "w") {
  440. X        $op = "write";
  441. X        last case;
  442. X        }
  443. X
  444. X        printf(stderr "Bad op '%s' in plan '%s'\n",
  445. X           $op,
  446. X           join(';', @plan));
  447. X        last case;
  448. X    }
  449. X
  450. X    $result .= "$op $value ";
  451. X    }
  452. X
  453. X    return($result);
  454. }
  455. X
  456. #
  457. # Add a plan to the list of plans to check out.
  458. #
  459. sub addto {
  460. X    local($op, $value, @plan) = @_;
  461. X    local($co);
  462. X
  463. X    $co = sprintf("%s %s",
  464. X          $op,
  465. X          $value);
  466. X
  467. X    #
  468. X    # See if the op and value is "uid root" - if so, and if the @plan 
  469. X    # isn't empty, then don't bother checking - if the target isn't root, 
  470. X    # its silly to pursue plans that require becoming root since if we can 
  471. X    # become root, we can become anything.  If the target is root, then 
  472. X    # this would be a loop anyway.
  473. X    #
  474. X    if ($op eq "u" && $value eq "0" && $#plan >= 0) {
  475. X    if (defined($opt_d)) {
  476. X        printf("addto: aborted root plan '%s'\n",
  477. X           &ascii_plan(@plan, $co));
  478. X    }
  479. X    return;
  480. X    }
  481. X
  482. X    #
  483. X    # See whether there's an entry for $co in the known list.
  484. X    # If so - success, we've found a suitable breakin plan.
  485. X    #
  486. X    # Yes, we want to check to see whether the whole Controlling Operation 
  487. X    # is one that is known to us, rather than just the object.  I
  488. X    # might have a hole that allows me to "replace /bin/foo" which is
  489. X    # somewhat different than "write /bin/foo"  
  490. X    #
  491. X    if (! defined($opt_l) && defined($known{$co})) {
  492. X    printf("Success! %s\n",
  493. X           &ascii_plan(@plan, $co));
  494. X    }
  495. X
  496. X    #
  497. X    # Check for loops -- if the new CO is part of the plan that we're
  498. X    # adding it to, this is a loop.
  499. X    #
  500. X    foreach $entry (@plan) {
  501. X    if ($entry eq $co) {
  502. X        if (defined($opt_d)) {
  503. X        printf("addto: aborted loop in plan '%s'\n",
  504. X               &ascii_plan(@plan, $co));
  505. X        }
  506. X        return;
  507. X    }
  508. X    }
  509. X
  510. X    #
  511. X    # Add this CO to the plan array...
  512. X    #
  513. X    push(@plan, $co);
  514. X
  515. X    #
  516. X    # Make an ascii version of sorts...
  517. X    #
  518. X    $text_plan = join($;, @plan);
  519. X
  520. X    #
  521. X    # Check to see if the new plan has been done.
  522. X    #
  523. X    if (defined($beendone{$text_plan})) {
  524. X    if (defined($opt_d)) {
  525. X        printf("addto: plan's been done - '%s'\n",
  526. X           &ascii_plan(@plan));
  527. X    }
  528. X    return;
  529. X    }
  530. X
  531. X    #
  532. X    # If we made it this far, its a new plan and isn't a loop.  
  533. X    #
  534. X
  535. X    #
  536. X    # Add to the beendone list...
  537. X    #
  538. X    $beendone{$text_plan} = 1;
  539. X
  540. X    #
  541. X    # Add to new plan list...
  542. X    #
  543. X    push(@new, $text_plan);
  544. X
  545. X    if (defined($opt_v)) {
  546. X    printf("addto: %s\n", 
  547. X           &ascii_plan(@plan));
  548. X    }
  549. X
  550. X    #
  551. X    # If this is a uid goal, then add the plan to the accessible list.
  552. X    #
  553. X    if ($op eq "u" && $value ne "0" && defined($opt_l)) {
  554. X    push(@accessible, $value);
  555. X    }
  556. }
  557. X
  558. #
  559. #----------------------------------------------------------------------
  560. #Main program follows...initialize and loop till we're done.
  561. #
  562. X
  563. &init_kuang();
  564. X
  565. target_loop:
  566. foreach $target (@targets) {
  567. X    if (&set_target($target)) {
  568. X    next target_loop;
  569. X    }
  570. X
  571. X    while ($#new >= 0) {
  572. X    @old = @new;
  573. X    @new = ();
  574. X
  575. X    foreach $t_plan (@old) {
  576. X        @plan = split(/\034/, $t_plan);
  577. X        ($op, $value) = &breakup($plan[$#plan]);
  578. X
  579. X        &apply_rules($op, $value, @plan);
  580. X    }
  581. X    }
  582. X
  583. X    if (defined($opt_l)) {
  584. X    foreach $elt (@accessible) {
  585. X        printf("$elt\n");
  586. X    }
  587. X    }
  588. }
  589. X
  590. if (defined($opt_d)) {
  591. X    printf("File info cache hit/access ratio: %g\n", 
  592. X           ($cache_hit + $cache_miss > 0) 
  593. X            ? $cache_hit / ($cache_hit + $cache_miss)
  594. X            : 0.0);
  595. }
  596. X
  597. X
  598. SHAR_EOF
  599. echo 'File p-cops.alpha/kuang is complete' &&
  600. chmod 0700 p-cops.alpha/kuang ||
  601. echo 'restore of p-cops.alpha/kuang failed'
  602. Wc_c="`wc -c < 'p-cops.alpha/kuang'`"
  603. test 14688 -eq "$Wc_c" ||
  604.     echo 'p-cops.alpha/kuang: original size 14688, current size' "$Wc_c"
  605. rm -f _shar_wnt_.tmp
  606. fi
  607. # ============= p-cops.alpha/misc.chk ==============
  608. if test -f 'p-cops.alpha/misc.chk' -a X"$1" != X"-c"; then
  609.     echo 'x - skipping p-cops.alpha/misc.chk (File already exists)'
  610.     rm -f _shar_wnt_.tmp
  611. else
  612. > _shar_wnt_.tmp
  613. echo 'x - extracting p-cops.alpha/misc.chk (Text)'
  614. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/misc.chk' &&
  615. #!/bin/sh  # need to mention perl here to avoid recursion
  616. #
  617. #  Usage: misc.chk.pl [-d]
  618. #
  619. #  This shell script checks a variety of miscellaneous potential
  620. # security problems that really don't belong anywhere else.
  621. #
  622. #  Right now this looks for to see if tftp & rexd are enabled,
  623. # to check if the uudecode alias is in the mail alias file and
  624. # not commented out, and if uudecode can create a SUID file.
  625. #
  626. #  Mechanism:  tftp.chk will try to get /etc/motd from the localhost.
  627. # Not much too it; just connect and try to get it.  For rexd, just
  628. # look in the /etc/inetd.conf file to see if it's enabled (e.g., not
  629. # commented out).
  630. #
  631. #  Warning:  it may take a minute or so to complete the test, since tftp
  632. # might take a while to get the test file, or it may take a while to time
  633. # out the connection (which is what usually happens if the test fails.)
  634. # NOTE:
  635. #   If you know where perl is and your system groks #!, put its
  636. # pathname at the top to make this a tad faster.
  637. #
  638. # the following magic is from the perl man page
  639. # and should work to get us to run with perl 
  640. # even if invoked as an sh or csh or foosh script.
  641. # notice we don't use full path cause we don't
  642. # know where the user has perl on their system.
  643. #
  644. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  645. & eval 'exec perl -S $0 $argv:q'
  646. X    if $running_under_some_stupid_shell_instead_of_perl;
  647. X
  648. package main;
  649. require 'chk_strings.pl';
  650. require 'fgrep.pl';
  651. X
  652. if ($ARGV[0] eq '-d') {
  653. X    #$chk_strings'debug = 1;  # verbose debugging
  654. X    $misc_chk'debug = 1;
  655. X    shift;
  656. }
  657. X
  658. die "Usage: $0 [-d]\n" if @ARGV > 0;
  659. X
  660. X
  661. $TFTP="/usr/ucb/tftp" unless defined $TFTP;
  662. $UUDECODE="/usr/bin/uudecode" unless defined $UUDECODE; 
  663. X
  664. package misc_chk;
  665. X
  666. # look for uudecode alias in $aliases
  667. $aliases="/usr/lib/aliases" if -f "/usr/lib/aliases";
  668. $aliases = ( -f '/usr/lib/aliases' && '/usr/lib/aliases' )
  669. X    || ( -f '/etc/aliases'       && '/etc/aliases' )
  670. X    || 'BOGUS';
  671. $uu="decode";
  672. X
  673. # look for rexd in $inetd; this file could be "/etc/servers", too!
  674. if (!defined($inetd)) {
  675. X    $inetd = ( -f '/etc/inetd.conf' && '/etc/inetd.conf') ||
  676. X         ( -f '/etc/servers' && '/etc/servers') ||
  677. X         'BOGUS';
  678. X    }
  679. $rexd="rexecd";
  680. X
  681. # tmp and target file (for tftp test)
  682. $target="/etc/motd";
  683. $tmp="./tmp.$$";
  684. X
  685. # should probably generalize routine for chking for pats in file at some point
  686. X
  687. #  Read from $inetd to see if daemons are running.
  688. # Comments are lines starting with a "#", so ignore.
  689. # Checking for rexd:
  690. #
  691. print "Checking for $rexd in $inetd\n" if $debug;
  692. if (@matches = grep(!/\s*#/, &'fgrep($inetd, $rexd))) {
  693. X    print "Warning!  $rexd is enabled in $inetd!\n";
  694. }
  695. X
  696. # Check to see if anything started inetd.conf is writable;
  697. print "Checking for writable dirs in $inetd\n" if $debug;
  698. &'chk_strings($inetd);
  699. X
  700. # Checking for uudecode alias:
  701. print "Checking for $uu alias in $aliases\n" if $debug;
  702. print "Warning!  $uu is enabled in $aliases!\n"
  703. X    if &'fgrep($aliases, "^\s*$uu:");
  704. X
  705. # uucode stuff -- thanks to pete shipley...
  706. print "Checking uudecode out\n" if $debug;
  707. if (-x $'UUDECODE) {
  708. X    open(UU, "| $'UUDECODE");
  709. X    print UU <<EOD_;
  710. begin 4755 ./foobar.$$
  711. end
  712. EOD_
  713. X    close(UU);
  714. }
  715. X
  716. &'is_able($'UUDECODE,'s','s');    # check if uudecode is SUID
  717. $is_able'silent = 1;
  718. print "Warning!  $'UUDECODE creates setuid files!\n"
  719. X   if &'is_able("./foobar.$$",'s','s');
  720. $is_able'silent = 0;
  721. unlink("./foobar.$$");
  722. X
  723. #  The rest is all for tftp stuff:
  724. #
  725. #   Get the local hostname...
  726. $hostname =  ( -x '/bin/hostname'  && `/bin/hostname` ) 
  727. X      || ( -x '/bin/uname'      && `/bin/uname -n` )
  728. X      || ( -x '/usr/bin/uuname' && `/usr/bin/uuname -l`)
  729. X      || 'Amnesiac!';
  730. chop $hostname;
  731. X
  732. #   Do the dirty work -- check tftp for the localhost, if it was found;
  733. # this might take a bit, since tftp might have to time out.
  734. X
  735. print "Checking out tftp on $hostname\n" if $debug;
  736. if (-x $'TFTP) {
  737. X    open(SAVOUT, ">&STDOUT");    # suppress file not found
  738. X    open(SAVERR, ">&STDERR");    # it's not as bad as it looks..
  739. X    open(STDOUT, ">/dev/null") || die "Can't redirect stdout: $!\n";
  740. X    open(STDERR, ">&STDOUT") || die "Can't dup stdout: $!\n";
  741. X    close(STDOUT); close(STDERR);
  742. X    open(TFTP, "| $'TFTP");
  743. print TFTP <<_XXX_;
  744. connect $hostname
  745. get $target $tmp
  746. quit
  747. _XXX_
  748. X    close(TFTP);
  749. X    open(STDERR, ">&SAVERR"); close(SAVERR);
  750. X    open(STDOUT, ">&SAVOUT"); close(SAVOUT);
  751. } # > /dev/null 2> /dev/null
  752. X
  753. print "Warning!  tftp is enabled on $hostname!\n" if -s $tmp;
  754. unlink $tmp;
  755. X
  756. # end of script
  757. X
  758. 1;
  759. SHAR_EOF
  760. chmod 0700 p-cops.alpha/misc.chk ||
  761. echo 'restore of p-cops.alpha/misc.chk failed'
  762. Wc_c="`wc -c < 'p-cops.alpha/misc.chk'`"
  763. test 4413 -eq "$Wc_c" ||
  764.     echo 'p-cops.alpha/misc.chk: original size 4413, current size' "$Wc_c"
  765. rm -f _shar_wnt_.tmp
  766. fi
  767. # ============= p-cops.alpha/kuang.1 ==============
  768. if test -f 'p-cops.alpha/kuang.1' -a X"$1" != X"-c"; then
  769.     echo 'x - skipping p-cops.alpha/kuang.1 (File already exists)'
  770.     rm -f _shar_wnt_.tmp
  771. else
  772. > _shar_wnt_.tmp
  773. echo 'x - extracting p-cops.alpha/kuang.1 (Text)'
  774. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/kuang.1' &&
  775. .TH KUANG 1 "4 October 1990"
  776. .SH NAME
  777. kuang \- find security problems through rule based analysis
  778. .SH SYNOPSIS
  779. .B kuang
  780. .RB "[\|" \-v  "\|]"
  781. .RB "[\|" \-d "\|]"
  782. .RB "[\|" \-l "\|]"
  783. .RB "[\|" \-f filedata "\|]"
  784. .RB "[\|" \-P "\|]"
  785. .RB "[\|" \-G "\|]"
  786. .RB "[\|" \-p passwd "\|]"
  787. .RB "[\|" \-g group "\|]"
  788. .RB "[\|" 
  789. .IR u.username | g.groupname "\|]"
  790. .br
  791. .SH DESCRIPTION
  792. .LP
  793. .B kuang
  794. uses rule based analysis to examine the current security configuration
  795. of a site and determine whether certain security problems exist.
  796. X
  797. .B kuang 
  798. contains embedded rules that describe the projection model and
  799. some of the attacker tricks used on Unix systems.  It uses these rules
  800. to reason backward from a desired goal (such as "grant u.root"),
  801. generating potential "attack" plans from the rules and file system
  802. state and then evaluating them to see whether they are reachable
  803. according to the state recorded in the password and group files and in
  804. the ownership and modes of the file systems.
  805. X
  806. By default, 
  807. .B kuang 
  808. uses "grant u.root" as its initial goal.  You can change that by
  809. specifying a username (u.username) or groupname (g.groupname) on the
  810. command line.  Normally 
  811. .B kuang
  812. determines a plan to be successful if it determines that anyone
  813. (u.other) can become the initial goal.  
  814. X
  815. The 
  816. .B \-v
  817. option causes 
  818. .B kuang
  819. to print a message about every plan added to the evaluation list.
  820. This can help one to understand how 
  821. .B kuang 
  822. works.  The 
  823. .B \-d 
  824. option causes 
  825. .B kuang
  826. to print a message when it evaluates a plan to determine whether to
  827. retain it and add onto it or ignore it.  Beware - these options will often
  828. produce lots of output.
  829. X
  830. Normally 
  831. .B kuang
  832. only registers success when it finds that everyone on the system can
  833. become the target uid or gid.  With the 
  834. .B \-l
  835. option, 
  836. .B kuang
  837. will list every uid that can access the goal.  This provides a more
  838. complete picture of the state of security - you might deem it a
  839. problem if several users can become root, even if u.other cannot.  
  840. X
  841. One might adopt the view that each uid should only be accessible by
  842. itself and root, and that each gid should be accessible only by the
  843. members of that group and root.  One can then compare the expected
  844. access list for a given uid or gid against the 
  845. .B kuang
  846. generated list to find security problems that 
  847. .B kuang
  848. wouldn't ordinarily tell you about.
  849. X
  850. The goals that 
  851. .B kuang
  852. use seem cryptic, but are really pretty straightforward.  Each goal
  853. consists of a list of <action> <object> pairs.  Typical actions are
  854. user, group, write and replace.  Typical objects are user names,
  855. group names and file names.  The goal
  856. "user root" (or u.root) means to have access to the root UID (0), or
  857. in other words, to be able to run any program using that uid.  
  858. Similarly,
  859. "group staff" (or g.staff) means to have access to group staff.
  860. The long goal
  861. "user bill  group graphics replace /n/shoe/0/fred replace
  862. /n/shoe/0/fred/.profile user fred group staff" means become
  863. user bill, get access to the graphics group, replace the file
  864. /n/shoe/0/fred, replace /n/shoe/0/fred/.profile, become fred,
  865. grant access to the staff group.  The problem that allows this to
  866. happen is that the /n/shoe/0 directory is writeable by the graphics
  867. group, meaning that anyone in that group can replace the .profile file
  868. for the fred user and gain access to that account and the groups it
  869. belongs to when fred next logs in.  Ooops.
  870. X
  871. To do a thorough job, 
  872. .B kuang 
  873. really needs to be able to access all of
  874. the controlling files of all users.  In some environments, home
  875. directories are located in NFS mounted file systems where the client
  876. doesn't have root access.  
  877. X
  878. The problem is that some home directories may be
  879. protected so that group foo can read/write them, but OTHER can't.
  880. .B kuang 
  881. running as some user not in group foo won't be able to read or
  882. search the directory, creating a blind spot that may hide security
  883. problems (for example, if group foo can write that user's .login and
  884. gain access to some other important priv...)  Running 
  885. .B kuang
  886. as root
  887. won't help unless we are running on the server that exports that
  888. file system, since root==nobody through NFS here.  Of course, then
  889. you'll find other blind spots on other servers, meaning that you'll
  890. never be able to see a complete picture of how things are from any
  891. spot on the net.  Running 
  892. .B kuang
  893. on every machine might not even
  894. help, since the blind spots might prevent them from seeing viable
  895. paths to Success on any of the machines.  Sigh.
  896. X
  897. Soooo we've added a 
  898. .B -f 
  899. option that causes 
  900. .B kuang 
  901. to preload owner, group and mode information for a list of files.
  902. Each line of the file should be of the form "type uid gid mode name".
  903. .B type
  904. is ignored by 
  905. .B kuang.
  906. .B uid 
  907. and 
  908. .B gid
  909. are the user and group ID numbers, in decimal.
  910. .B mode
  911. is the permissions for the file, in octal.  And 
  912. .B name
  913. is the name of the file.  We've also added a program called
  914. .B get-cf
  915. that can be run as root on a server to create a file of the above form
  916. for the control files for the user's with home directories on that
  917. server.  Then you can run 
  918. .B get-cf 
  919. on every server as root, concatenate all the data together, and
  920. preload it into Perl.  This will fix the shadow problems mentioned
  921. above and should also speed things up since you won't need to do all
  922. the file system references.
  923. .B kuang -f file
  924. will use a DBM database in place of a text file if file.dir exists.
  925. X
  926. .B Kuang
  927. needs to read the entire password and group databases before it
  928. starts, so that it has a complete idea of what users are in what groups
  929. and so on.  This can be somewhat slow on systems using YP, since by
  930. default 
  931. .B kuang
  932. uses the getpwent and getgrent routines to get the information (which
  933. is tedious on a YP client).  
  934. The 
  935. .B -P
  936. and 
  937. .B -G
  938. options cause 
  939. .B kuang
  940. to read /etc/passwd (/etc/group) and to use ypcat to read the rest of
  941. the passwd (group) YP maps, which can be much faster.  In addition, 
  942. the 
  943. .B -p 
  944. and 
  945. .B -g 
  946. options cause 
  947. .B kuang 
  948. to read the named files instead of /etc/passwd.
  949. X
  950. .SH "SEE ALSO"
  951. "Rule Based Analysis of Computer Security", Robert W. Baldwin, MIT,
  952. June 1987.
  953. X
  954. The README file that comes with 
  955. .B kuang
  956. describes many of the design considerations, problems and future
  957. plans.
  958. X
  959. .SH NOTES
  960. .LP
  961. This version of 
  962. .B kuang
  963. is based on the shell script versions that Dan Farmer included with
  964. the 
  965. .B COPS 
  966. security package, which in turn were based on code written by  Robert
  967. Baldwin himself.
  968. X
  969. You should read the other documentation that should come with this
  970. version and modify the rules in 
  971. .B kuang
  972. to suite your site.
  973. X
  974. .SH BUGS
  975. .LP
  976. Probably many.
  977. X
  978. The 
  979. .B -P
  980. and 
  981. .B -G 
  982. options don't work right if you use +@ constructions with YP.  They do
  983. work right if you use a simple "+:" entry, however.
  984. X
  985. SHAR_EOF
  986. chmod 0600 p-cops.alpha/kuang.1 ||
  987. echo 'restore of p-cops.alpha/kuang.1 failed'
  988. Wc_c="`wc -c < 'p-cops.alpha/kuang.1'`"
  989. test 6730 -eq "$Wc_c" ||
  990.     echo 'p-cops.alpha/kuang.1: original size 6730, current size' "$Wc_c"
  991. rm -f _shar_wnt_.tmp
  992. fi
  993. # ============= p-cops.alpha/pass.cache.pl ==============
  994. if test -f 'p-cops.alpha/pass.cache.pl' -a X"$1" != X"-c"; then
  995.     echo 'x - skipping p-cops.alpha/pass.cache.pl (File already exists)'
  996.     rm -f _shar_wnt_.tmp
  997. else
  998. > _shar_wnt_.tmp
  999. echo 'x - extracting p-cops.alpha/pass.cache.pl (Text)'
  1000. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/pass.cache.pl' &&
  1001. #
  1002. #   Routines for reading and caching user and group information.  These
  1003. # are used in multiple programs... it caches the info once, then hopefully
  1004. # won't be used again.
  1005. #
  1006. #  Steve Romig, May 1991.
  1007. #
  1008. # Provides a bunch of routines and a bunch of arrays.  Routines 
  1009. # (and their usage):
  1010. #
  1011. #    load_passwd_info($use_getent, $file_name)
  1012. #
  1013. #    loads user information into the %uname* and %uid* arrays 
  1014. #    (see below).  
  1015. #
  1016. #    If $use_getent is non-zero:
  1017. #        get the info via repeated 'getpwent' calls.  This can be
  1018. #        *slow* on some hosts, especially if they are running as a
  1019. #        YP (NIS) client.
  1020. #    If $use_getent is 0:
  1021. #        if $file_name is "", then get the info from reading the 
  1022. #        results of "ypcat passwd" and from /etc/passwd.  Otherwise, 
  1023. #        read the named file.  The file should be in passwd(5) 
  1024. #        format.
  1025. #
  1026. #    load_group_info($use_gentent, $file_name)
  1027. #
  1028. #    is similar to load_passwd_info.
  1029. #
  1030. # Information is stored in several convenient associative arrays:
  1031. #
  1032. #   %uname2shell    Assoc array, indexed by user name, value is 
  1033. #            shell for that user name.
  1034. #
  1035. #   %uname2dir        Assoc array, indexed by user name, value is
  1036. #            home directory for that user name.
  1037. #
  1038. #   %uname2uid        Assoc array, indexed by name, value is uid for 
  1039. #            that uid.
  1040. #            
  1041. #   %uname2passwd    Assoc array, indexed by name, value is password
  1042. #            for that user name.
  1043. #
  1044. #   %uid2names        Assoc array, indexed by uid, value is list of
  1045. #            user names with that uid, in form "name name
  1046. #            name...". 
  1047. #
  1048. #   %gid2members    Assoc array, indexed by gid, value is list of
  1049. #            group members in form "name name name..."
  1050. #
  1051. #   %gname2gid        Assoc array, indexed by group name, value is
  1052. #            matching gid.
  1053. #
  1054. #   %gid2names        Assoc array, indexed by gid, value is the
  1055. #            list of group names with that gid in form 
  1056. #            "name name name...".
  1057. #
  1058. # You can also use routines named the same as the arrays - pass the index 
  1059. # as the arg, get back the value.  If you use this, get{gr|pw}{uid|gid|nam} 
  1060. # will be used to lookup entries that aren't found in the cache.
  1061. #
  1062. # To be done:
  1063. #    probably ought to add routines to deal with full names.
  1064. #    maybe there ought to be some anal-retentive checking of password 
  1065. #    and group entries.
  1066. #    probably ought to cache get{pw|gr}{nam|uid|gid} lookups also.
  1067. #    probably ought to avoid overwriting existing entries (eg, duplicate 
  1068. #       names in password file would collide in the tables that are 
  1069. #    indexed by name).
  1070. #
  1071. # Disclaimer:
  1072. #    If you use YP and you use netgroup entries such as 
  1073. #    +@servers::::::
  1074. #    +:*:::::/usr/local/utils/messages
  1075. #    then loading the password file in with &load_passwd_info(0) will get 
  1076. #    you mostly correct YP stuff *except* that it won't do the password and 
  1077. #    shell substitutions as you'd expect.  You might want to use 
  1078. #    &load_passwd_info(1) instead to use getpwent calls to do the lookups, 
  1079. #    which would be more correct.
  1080. #
  1081. X
  1082. package main;
  1083. X
  1084. $PASSWD = '/etc/passwd' unless defined $PASSWD;
  1085. X
  1086. require 'pathconf.pl';
  1087. X
  1088. %uname2shell = ();
  1089. %uname2dir = ();
  1090. %uname2uid = ();
  1091. %uname2passwd = ();
  1092. %uid2names = ();
  1093. %gid2members = ();
  1094. %gname2gid = ();
  1095. %gid2names = ();
  1096. X
  1097. $DOMAINNAME = "/bin/domainname" unless defined $DOMAINNAME;
  1098. $YPCAT = "/bin/ypcat" unless defined $YPCAT;
  1099. X
  1100. $yptmp = "./yptmp.$$";
  1101. X
  1102. $passwd_loaded = 0;        # flags to use to avoid reloading everything
  1103. $group_loaded = 0;        # unnecessarily...
  1104. X
  1105. #
  1106. # We provide routines for getting values from the data structures as well.
  1107. # These are named after the data structures they cache their data in.  Note 
  1108. # that they will all generate password and group file lookups via getpw* 
  1109. # and getgr* if they can't find info in the cache, so they will work
  1110. # "right" even if load_passwd_info and load_group_info aren't called to 
  1111. # preload the caches.
  1112. #
  1113. # I should point out, however, that if you don't call load_*_info to preload
  1114. # the cache, uid2names, gid2names and gid2members *will not* be complete, since 
  1115. # you must read the entire password and group files to get a complete picture.
  1116. # This might be acceptable in some cases, so you can skip the load_*_info
  1117. # calls if you know what you are doing...
  1118. #
  1119. sub uname2shell {
  1120. X    local($key) = @_;
  1121. X
  1122. X    if (! defined($uname2shell{$key})) {
  1123. X    &add_pw_info(getpwnam($key));
  1124. X    }
  1125. X
  1126. X    return($uname2shell{$key});
  1127. }
  1128. X
  1129. sub uname2dir {
  1130. X    local($key) = @_;
  1131. X    local(@pw_info);
  1132. X
  1133. X    if (! defined($uname2dir{$key})) {
  1134. X    &add_pw_info(getpwnam($key));
  1135. X    }
  1136. X
  1137. X    return($uname2dir{$key});
  1138. }
  1139. X
  1140. sub uname2uid {
  1141. X    local($key) = @_;
  1142. X    local(@pw_info);
  1143. X
  1144. X    if (! defined($uname2uid{$key})) {
  1145. X    &add_pw_info(getpwnam($key));
  1146. X    }
  1147. X
  1148. X    return($uname2uid{$key});
  1149. }
  1150. X
  1151. sub uname2passwd {
  1152. X    local($key) = @_;
  1153. X    local(@pw_info);
  1154. X
  1155. X    if (! defined($uname2passwd{$key})) {
  1156. X    &add_pw_info(getpwnam($key));
  1157. X    }
  1158. X
  1159. X    return($uname2passwd{$key});
  1160. }
  1161. X
  1162. sub uid2names {
  1163. X    local($key) = @_;
  1164. X    local(@pw_info);
  1165. X
  1166. X    if (! defined($uid2names{$key})) {
  1167. X    &add_pw_info(getpwuid($key));
  1168. X    }
  1169. X
  1170. X    return($uid2names{$key});
  1171. }
  1172. X
  1173. sub gid2members {
  1174. X    local($key) = @_;
  1175. X    local(@gr_info);
  1176. X
  1177. X    if (! defined($gid2members{$key})) {
  1178. X    &add_gr_info(getgrgid($key));
  1179. X    }
  1180. X
  1181. X    return($gid2members{$key});
  1182. }
  1183. X
  1184. sub gname2gid {
  1185. X    local($key) = @_;
  1186. X    local(@gr_info);
  1187. X
  1188. X    if (! defined($gname2gid{$key})) {
  1189. X    &add_gr_info(getgrnam($key));
  1190. X    }
  1191. X
  1192. X    return($gname2gid{$key});
  1193. }
  1194. X
  1195. sub gid2names {
  1196. X    local($key) = @_;
  1197. X    local(@gr_info);
  1198. X
  1199. X    if (! defined($gid2names{$key})) {
  1200. X    &add_gr_info(getgrgid($key));
  1201. X    }
  1202. X
  1203. X    return($gid2names{$key});
  1204. }
  1205. X
  1206. #
  1207. # Update user information for the user named $name.  We cache the password, 
  1208. # uid, login group, home directory and shell.
  1209. #
  1210. X
  1211. sub add_pw_info {
  1212. X    local($name, $passwd, $uid, $gid) = @_;
  1213. X    local($dir, $shell);
  1214. X
  1215. #
  1216. # Ugh!  argh...yech...sigh.  If we use getpwent, we get back 9 elts, 
  1217. # if we parse /etc/passwd directly we get 7.  Pick off the last 2 and 
  1218. # assume that they are the $directory and $shell.  
  1219. #
  1220. X    $dir = $_[$#_ - 1];
  1221. X    $shell = $_[$#_];
  1222. X
  1223. X    if ($name ne "") {
  1224. X    $uname2shell{$name} = $shell;
  1225. X    $uname2dir{$name} = $dir;
  1226. X    $uname2uid{$name} = $uid;
  1227. X    $uname2passwd{$name} = $passwd;
  1228. X
  1229. X    if ($gid ne "") {
  1230. X        # fixme: should probably check for duplicates...sigh
  1231. X
  1232. X        if (defined($gid2members{$gid})) {
  1233. X        $gid2members{$gid} .= " $name";
  1234. X        } else {
  1235. X        $gid2members{$gid} = $name;
  1236. X        }
  1237. X    }
  1238. X
  1239. X    if ($uid ne "") {
  1240. X        if (defined($uid2names{$uid})) {
  1241. X        $uid2names{$uid} .= " $name";
  1242. X        } else {
  1243. X        $uid2names{$uid} = $name;
  1244. X        }
  1245. X    }
  1246. X    }
  1247. }
  1248. X
  1249. #
  1250. # Update group information for the group named $name.  We cache the gid 
  1251. # and the list of group members.
  1252. #
  1253. X
  1254. sub add_gr_info {
  1255. X    local($name, $passwd, $gid, $members) = @_;
  1256. X
  1257. X    if ($name ne "") {
  1258. X    $gname2gid{$name} = $gid;
  1259. X
  1260. X    if ($gid ne "") {
  1261. X        if (defined($gid2names)) {
  1262. X        $gid2names{$gid} .= " $name";
  1263. X        } else {
  1264. X        $gid2names{$gid} = $name;
  1265. X        }
  1266. X
  1267. X        # fixme: should probably check for duplicates
  1268. X
  1269. X        $members = join(' ', split(/[, \t]+/, $members));
  1270. X
  1271. X        if (defined($gid2members{$gid})) {
  1272. X        $gid2members{$gid} .= " " . $members;
  1273. X        } else {
  1274. X        $gid2members{$gid} = $members;
  1275. X        }
  1276. X    }
  1277. X    }
  1278. }
  1279. X
  1280. #
  1281. # We need to suck in the entire group and password files so that we can 
  1282. # make the %uid2names, %gid2members and %gid2names lists complete.  Otherwise,
  1283. # we would just read the entries as needed with getpw* and cache the results.
  1284. # Sigh.
  1285. #
  1286. # There are several ways that we might find the info.  If $use_getent is 1, 
  1287. # then we just use getpwent and getgrent calls to read the info in.
  1288. #
  1289. # That isn't real efficient if you are using YP (especially on a YP client), so
  1290. # if $use_getent is 0, we can use ypcat to get a copy of the passwd and
  1291. # group maps in a fairly efficient manner.  If we do this we have to also read
  1292. # the local /etc/{passwd,group} files to complete our information.  If we aren't 
  1293. # using YP, we just read the local pasword and group files.
  1294. #
  1295. sub load_passwd_info {
  1296. X    local($use_getent, $file_name) = @_;
  1297. X    local(@pw_info);
  1298. X
  1299. X    if ($passwd_loaded) {
  1300. X    return;
  1301. X    }
  1302. X
  1303. X    $passwd_loaded = 1;
  1304. X
  1305. X    if ($use_getent) {
  1306. X    #
  1307. X    # Use getpwent to get the info from the system, and add_pw_info to 
  1308. X    # cache it.
  1309. X    #
  1310. X    while ((@pw_info = getpwent()) != 0) {
  1311. X        &add_pw_info(@pw_info);
  1312. X    }
  1313. X
  1314. X    endpwent();
  1315. X
  1316. X    return();
  1317. X    } elsif ($file_name eq "") {
  1318. X    chop($has_yp = `$DOMAINNAME`);
  1319. X    if ($has_yp) {
  1320. X        #
  1321. X        # If we have YP (NIS), then use ypcat to get the stuff from the 
  1322. X        # map.@
  1323. X        #
  1324. X        system("$YPCAT passwd > $yptmp 2> /dev/null");
  1325. X        if (-s $yptmp) {
  1326. X            open(FILE, "$YPCAT passwd|") ||
  1327. X              die "can't 'ypcat passwd'";
  1328. X            while (<FILE>) {
  1329. X            chop;
  1330. X            &add_pw_info(split(/:/));
  1331. X                }
  1332. X            }
  1333. X        close(FILE);
  1334. X    }
  1335. X
  1336. X    #
  1337. X    # We have to read /etc/passwd no matter what...
  1338. X    #
  1339. X    $file_name = "/etc/passwd";
  1340. X    }
  1341. X
  1342. X    open(FILE, $file_name) ||
  1343. X      die "can't open $file_name";
  1344. X
  1345. X    while (<FILE>) {
  1346. X    chop;
  1347. X        
  1348. X    if ($_ !~ /^\+/) {
  1349. X        &add_pw_info(split(/:/));
  1350. X    }
  1351. X
  1352. X    # fixme: if the name matches +@name, then this is a wierd 
  1353. X    # netgroup thing, and we aren't dealing with it right.  might want
  1354. X    # to warn the poor user...suggest that he use the use_getent 
  1355. X    # method instead.
  1356. X    }
  1357. X
  1358. X    close(FILE);
  1359. }
  1360. X
  1361. sub load_group_info {
  1362. X    local($use_getent, $file_name) = @_;
  1363. X    local(@gr_info);
  1364. X
  1365. X    if ($group_loaded) {
  1366. X    return;
  1367. X    }
  1368. X
  1369. X    $group_loaded = 1;
  1370. X
  1371. X    if ($use_getent) {
  1372. X    #
  1373. X    # Use getgrent to get the info from the system, and add_gr_info to 
  1374. X    # cache it.
  1375. X    #
  1376. X    while ((@gr_info = getgrent()) != 0) {
  1377. X        &add_gr_info(@gr_info);
  1378. X    }
  1379. X
  1380. X    endgrent();
  1381. X
  1382. X    return();
  1383. X    } elsif ($file_name eq "") {
  1384. X    chop($has_yp = `$DOMAINNAME`);
  1385. X    if ($has_yp) {
  1386. X        #
  1387. X        # If we have YP (NIS), then use ypcat to get the stuff from the 
  1388. X        # map.
  1389. X        #
  1390. X        system("$YPCAT passwd > $yptmp 2> /dev/null");
  1391. X        if (-s $yptmp) {
  1392. X            open(FILE, "$YPCAT group|") ||
  1393. X              die "can't 'ypcat group'";
  1394. X            while (<FILE>) {
  1395. X            chop;
  1396. X            &add_gr_info(split(/:/));
  1397. X                }
  1398. X            close(FILE);
  1399. X        }
  1400. X    }
  1401. X
  1402. X    #
  1403. X    # We have to read /etc/group no matter what...
  1404. X    #
  1405. X    $file_name = "/etc/group";
  1406. X    }
  1407. X
  1408. X    open(FILE, $file_name) ||
  1409. X      die "can't open $file_name";
  1410. X
  1411. X    while (<FILE>) {
  1412. X    chop;
  1413. X    if ($_ !~ /^\+/) {
  1414. X        &add_gr_info(split(/:/));
  1415. X    }
  1416. X
  1417. X    # fixme: if the name matches +@name, then this is a wierd 
  1418. X    # netgroup thing, and we aren't dealing with it right.  might want
  1419. X    # to warn the poor user...suggest that he use the use_getent 
  1420. X    # method instead.
  1421. X    }
  1422. X
  1423. X    close(FILE);
  1424. }
  1425. X
  1426. # Load the password stuff -- Do NOT take this out!
  1427. &'load_passwd_info(0,$PASSWD);
  1428. X
  1429. unlink $yptmp;
  1430. X
  1431. 1;
  1432. SHAR_EOF
  1433. chmod 0700 p-cops.alpha/pass.cache.pl ||
  1434. echo 'restore of p-cops.alpha/pass.cache.pl failed'
  1435. Wc_c="`wc -c < 'p-cops.alpha/pass.cache.pl'`"
  1436. test 10420 -eq "$Wc_c" ||
  1437.     echo 'p-cops.alpha/pass.cache.pl: original size 10420, current size' "$Wc_c"
  1438. rm -f _shar_wnt_.tmp
  1439. fi
  1440. # ============= p-cops.alpha/pass.chk ==============
  1441. if test -f 'p-cops.alpha/pass.chk' -a X"$1" != X"-c"; then
  1442.     echo 'x - skipping p-cops.alpha/pass.chk (File already exists)'
  1443.     rm -f _shar_wnt_.tmp
  1444. else
  1445. > _shar_wnt_.tmp
  1446. echo 'x - extracting p-cops.alpha/pass.chk (Text)'
  1447. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/pass.chk' &&
  1448. #
  1449. #  Pass.chk -- a password guesser.  Functionally equivalent to the original
  1450. # cops password guesser.  The -P option doesn't work right now (alpha release,
  1451. # don't you know :-), since we're doing funky things with password caching,
  1452. # but this will change soon.
  1453. #
  1454. #  Usage: $0 [options] dictionary
  1455. #
  1456. #    -P pfile    password file
  1457. #    -p        print found passwords (incompatible with -M)
  1458. #       -d        check prefix/suffix of digits [0-9]
  1459. #       -g        check all words in gcos, plan, project, signature files
  1460. #    -r         reverse the word tests
  1461. #    -s         check all single chars as passwords
  1462. #    -u         output the current user being checked
  1463. #    -U         try uppercase word tests
  1464. #    -v        verbose, print advisory information
  1465. #
  1466. # Originally written by Tim Tessin with lots more features, etc., etc., etc.
  1467. # I ripped out all of his extra functionality that duplicated some of the
  1468. # other cops stuff, and some things that just didn't fit, and added some
  1469. # code to finish the simulation of the old checker. -- dan
  1470. #
  1471. X
  1472. require "pass.cache.pl";
  1473. X
  1474. $Passwd = "/etc/passwd";
  1475. $Adjunct = "/etc/security/passwd.adjunct";
  1476. $Log = "passwd.log";
  1477. $Tmp = "tmpi.$$";
  1478. $Secure_Tmp = "/etc/security/tmpi.$$";
  1479. $Secure_Tmp = "./tmpi.$$";
  1480. $Secure_Log = "./passwd.log";
  1481. $Move = "/bin/mv";
  1482. $Create = "/bin/touch";
  1483. X
  1484. &Getopts("dgiprsuUvwyo:P:l:b:e:") || do {
  1485. X    print STDERR "Illegal arguments\n";
  1486. X    &usage();
  1487. X    exit(1);
  1488. X    };
  1489. X    
  1490. sub usage {
  1491. X    print STDERR "usage: pi -firuwyv -p <pwdfile> -l <logfile>\n          -o <nday> -b <bname> -e <ename>\n";
  1492. X    }
  1493. X
  1494. # sanity checks
  1495. $opt_P = $Passwd unless $opt_P;
  1496. $opt_l = $Log unless $opt_l;
  1497. unless (-r $opt_P) {
  1498. X    print STDERR "Can't read passwd file $opt_P\n";
  1499. X    exit(1);
  1500. X    }
  1501. X
  1502. # unbuffer output
  1503. select (STDOUT); $| = 1;
  1504. X
  1505. $dups = 0;        # duplicate name entries
  1506. $new = 0;        # new entries
  1507. $changed = 0;        # password (and other data) changed
  1508. $deleted = 0;        # deleted entries
  1509. $updated = 0;        # data other than password changed
  1510. $nlog = 0;        # number of log entries, used for print decisions
  1511. $ntest = 0;
  1512. $ndone = 0;
  1513. X
  1514. for $uid (keys %uname2passwd) {
  1515. X    $pass = $uname2passwd{$uid};
  1516. X    if ($try = &dopwd()) {
  1517. X        $pwd = ($opt_p) ? $try : "";
  1518. X        # printf "Username: %-8s  <password guessed>  $pwd\n",$P[0];
  1519. X        printf "Warning!  Password Problem: Guessed: %s\t\t$pwd\n",$P[0];
  1520. X        }
  1521. X    $ndone++;
  1522. X    $time = time();
  1523. X    print LOG "$time$ok:$value\n";
  1524. X    }
  1525. X
  1526. &finish;
  1527. 1;
  1528. # end of program
  1529. X
  1530. X
  1531. ######################## Support Subroutines ###########################
  1532. # dopwd tests each password entry against several simple checks and all
  1533. # the words in the dictionaries specified.  The simple checks consists
  1534. # of trying the username as the password ('joe' accounts), words derived
  1535. # from the gecos fields (usually first and last names).
  1536. X
  1537. sub dopwd {
  1538. $tries = 0;
  1539. X
  1540. if ($opt_u) { print "$uid\n"; }
  1541. X
  1542. # try user name
  1543. ($try = &testpwd($uid,$pass)) && return $try;
  1544. X
  1545. # do gcos field?
  1546. if ($opt_g) {
  1547. X    @gcos = split(/[.,& -]/,$uname2gcos{$uid});
  1548. X    foreach $i (@gcos) {
  1549. X        next unless $i;        # skip null split values
  1550. X        ($try = &testpwd($i,$pass)) && return $try;
  1551. X        }
  1552. X
  1553. X    # Try names from misc files
  1554. X    #
  1555. X    undef %words;
  1556. X    # files to check
  1557. X    @files2chk = ("/.project", "/.plan", "/.signature");
  1558. X    $home = $uname2dir{$uid};
  1559. X    for $i (@files2chk) {
  1560. X        open (FOOFILE, $home . $i);
  1561. X        while (<FOOFILE>) {
  1562. X            chop;
  1563. X            @line = split(/([.,;\s])/);
  1564. X            for $j (@line) {
  1565. X                $words{$j}=$j unless $j=~/[\s]/;
  1566. X                }
  1567. X            }
  1568. X        close FOOFILE;
  1569. X        }
  1570. X    for $k (values %words) {
  1571. X        # print "word $k\n";
  1572. X        ($try = &testpwd($k,$pass)) && return $try;
  1573. X        }
  1574. X    }
  1575. X
  1576. # do dictionaries
  1577. # save state of upper/reverse so individual dicts can temporarily
  1578. # override.
  1579. foreach $i (@ARGV) {
  1580. X    if (open (DICT,$i)) {
  1581. X        while (<DICT>) {
  1582. X            chop;
  1583. X            if ($try = &testpwd($_,$pass)) {
  1584. X                close DICT;
  1585. X                return $try;
  1586. X                }
  1587. X            }
  1588. X        close DICT;
  1589. X        }
  1590. X    }
  1591. return 0;
  1592. }
  1593. X
  1594. X
  1595. # small subroutines to help the main password cracker.  All are labeled
  1596. # p_xxx, where xxx is the identifying name.
  1597. #
  1598. X
  1599. # if leading character is upper-case, also try lower case version
  1600. sub p_lc {
  1601. local($try) = @_;
  1602. local($ntry);
  1603. if ( $try =~ /^[A-Z]/ ) {
  1604. X    ($ntry = $try) =~ y/A-Z/a-z/;
  1605. X    push(@total_guesses, $ntry);
  1606. X    }
  1607. }
  1608. X
  1609. # reverse check
  1610. sub p_rev {
  1611. local($try) = @_;
  1612. local($ntry);
  1613. $ntry = reverse $try;
  1614. if ($ntry ne $try) {
  1615. X    push(@total_guesses, $ntry);
  1616. X    }
  1617. }
  1618. X
  1619. # uppercase check
  1620. sub p_up {
  1621. local($try) = @_;
  1622. local($ntry);
  1623. ($ntry = $try) =~ y/a-z/A-Z/;
  1624. if ($ntry ne $try) { push(@total_guesses, $ntry); }
  1625. }
  1626. X
  1627. # testpwd checks a word to see if it matches the encrpted password
  1628. # if the word is capitalized, the lowercase version is tried as well
  1629. X
  1630. sub testpwd {
  1631. local ($try,$pass) = @_;
  1632. local (@total_guesses);
  1633. X
  1634. push(@total_guesses, $try);
  1635. X
  1636. # free (lower case) check if first letter is uppercase
  1637. &p_lc($try);
  1638. # reverse?
  1639. if ($opt_r) { &p_rev($try); }
  1640. # uppercase?
  1641. if ($opt_U) { &p_up($try); }
  1642. X
  1643. if ($opt_d) {
  1644. X    if (length ($try) < 8) {
  1645. X        foreach $i ('0'..'9') {
  1646. X            $ntry = $i.$try;
  1647. X            push(@total_guesses, $ntry);
  1648. X            if ($opt_r) { &p_rev($ntry); }
  1649. X            if ($opt_U) { &p_up($ntry); }
  1650. X            }
  1651. X        foreach $i ('0'..'9') {
  1652. X            $ntry = $try.$i;
  1653. X            push(@total_guesses, $ntry);
  1654. X            if ($opt_r) { &p_rev($ntry); }
  1655. X            if ($opt_U) { &p_up($ntry); }
  1656. X            }
  1657. X        }
  1658. X    }
  1659. X
  1660. # do single letters, #'s, if needed
  1661. if ($opt_s && $user ne $last_user) {
  1662. X    $last_user = $user;
  1663. X    foreach $i (0..9) { push(@total_guesses, $i); }
  1664. X    foreach $i (A..Z) { push(@total_guesses, $i); }
  1665. X    foreach $i (a..z) { push(@total_guesses, $i); }
  1666. X    }
  1667. X
  1668. foreach $i (@total_guesses) {
  1669. X    # print "trying $i\n";
  1670. X    $epw = &pcrypt($i, $pass);
  1671. X    ($epw eq $pass) && return $i;
  1672. X    }
  1673. undef @total_guesses;
  1674. X
  1675. return 0;
  1676. }
  1677. X
  1678. sub finish {
  1679. unlink "/tmp/pi.log.$$";
  1680. unlink "/tmp/xpi.$$";
  1681. unlink $Tmp;
  1682. }
  1683. X
  1684. # These routines taken from the perl library to provide the functions here
  1685. # so we don't have to depend on perl libraries being properly installed.
  1686. X
  1687. # getopts.pl - a better getopt.pl
  1688. # Usage:
  1689. #      do Getopts('a:bc');  # -a takes arg. -b & -c not. Sets opt_* as a
  1690. #                           #  side effect.
  1691. sub Getopts {
  1692. X    local($argumentative) = @_;
  1693. X    local(@args,$_,$first,$rest,$errs);
  1694. X    local($[) = 0;
  1695. X
  1696. X    @args = split( / */, $argumentative );
  1697. X    while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
  1698. X    ($first,$rest) = ($1,$2);
  1699. X    $pos = index($argumentative,$first);
  1700. X    if($pos >= $[) {
  1701. X        if($args[$pos+1] eq ':') {
  1702. X        shift(@ARGV);
  1703. X        if($rest eq '') {
  1704. X            $rest = shift(@ARGV);
  1705. X        }
  1706. X        eval "\$opt_$first = \$rest;";
  1707. X        }
  1708. X        else {
  1709. X        eval "\$opt_$first = 1";
  1710. X        if($rest eq '') {
  1711. X            shift(@ARGV);
  1712. X        }
  1713. X        else {
  1714. X            $ARGV[0] = "-$rest";
  1715. X        }
  1716. X        }
  1717. X    }
  1718. X    else {
  1719. X        print STDERR "Unknown option: $first\n";
  1720. X        ++$errs;
  1721. X        if($rest ne '') {
  1722. X        $ARGV[0] = "-$rest";
  1723. X        }
  1724. X        else {
  1725. X        shift(@ARGV);
  1726. X        }
  1727. X    }
  1728. X    }
  1729. X    $errs == 0;
  1730. }
  1731. X
  1732. sub pcrypt {
  1733. local($try, $pass) = @_;
  1734. X
  1735. print "Trying \"$try\" on $uid\n" if $opt_v;
  1736. X
  1737. $epw = crypt($try,$pass);
  1738. X
  1739. return $epw;
  1740. }
  1741. X
  1742. SHAR_EOF
  1743. chmod 0700 p-cops.alpha/pass.chk ||
  1744. echo 'restore of p-cops.alpha/pass.chk failed'
  1745. Wc_c="`wc -c < 'p-cops.alpha/pass.chk'`"
  1746. test 6684 -eq "$Wc_c" ||
  1747.     echo 'p-cops.alpha/pass.chk: original size 6684, current size' "$Wc_c"
  1748. rm -f _shar_wnt_.tmp
  1749. fi
  1750. # ============= p-cops.alpha/passwd.chk ==============
  1751. if test -f 'p-cops.alpha/passwd.chk' -a X"$1" != X"-c"; then
  1752.     echo 'x - skipping p-cops.alpha/passwd.chk (File already exists)'
  1753.     rm -f _shar_wnt_.tmp
  1754. else
  1755. > _shar_wnt_.tmp
  1756. echo 'x - extracting p-cops.alpha/passwd.chk (Text)'
  1757. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/passwd.chk' &&
  1758. #
  1759. #   passwd.chk
  1760. #
  1761. # Check password file -- /etc/passwd -- for incorrect number of fields,
  1762. # duplicate uid's, non-alphanumeric uids, and non-numeric group id's.
  1763. # Mechanism:  This script ensures that each line of the passwd file (in
  1764. # $etc, line 47) has 7 fields and is non-blank, as well as examining the
  1765. # file for any duplicate users.  It then checks to ensure that the first
  1766. # character of the login name is alphanumeric, and that all uid and gid
  1767. # numbers are indeed numeric and non-negative.  It also checks the
  1768. # validity of the home directory.
  1769. # For yellow pages passwords, it does the same checking, but in order to
  1770. # get a listing of all members of the password file, it does a "ypcat
  1771. # passwd" and uses the output from that as a passwd file.
  1772. # The /etc/passwd file has a very specific format, making the task fairly
  1773. # simple.  Normally it has lines with 7 fields, each field separated by a
  1774. # colon (:).  The first field is the user id, the second field is the
  1775. # encrypted password (an asterix (*) means the group has no password,
  1776. # otherwise the first two characters are the salt), the third field is the
  1777. # user id number, the fourth field is the group id number, the fifth field
  1778. # is the GECOS field (basically holds miscellaneous information, varying
  1779. # from site to site), the sixth field is the home directory of the user,
  1780. # and lastly the seventh field is the login shell of the user.  No blank
  1781. # lines should be present.  Uid's will be flagged if over 8 chars, unless
  1782. # the $OVER_8 variable (line 45) is set to "YES".
  1783. # If a line begins with a plus sign (+), it is a yellow pages entry.  See
  1784. # passwd(5) for more information, if this applies to your site.
  1785. X
  1786. # If your kernel doesn't understand the #! notation, this script will
  1787. # feed itself to perl
  1788. eval 'exec /usr/local/bin/perl -S $0 $*'
  1789. X    if $running_under_some_shell;
  1790. X
  1791. #   Used for Sun C2 security group file. 'FALSE' (default) will flag
  1792. # valid C2 passwd syntax as an error, 'TRUE' attempts to validate it.
  1793. # Thanks to Pete Troxell for pointing this out.
  1794. $C2='FALSE';
  1795. X
  1796. #  Some systems allow long uids; set this to 'TRUE', if so (thanks
  1797. # to Pete Shipley (lot of petes around here, eh?)):
  1798. $OVER_8='NO';
  1799. X
  1800. #
  1801. # Important files:
  1802. $etc="/etc/passwd";
  1803. $yptmp = "./yptmp.$$";
  1804. X
  1805. # If YPCAT is in the environment, use that, otherwise use the default
  1806. if (defined $ENV{'YPCAT'}) { $YPCAT = $ENV{'YPCAT'}; }
  1807. else { $YPCAT = "/usr/bin/ypcat"; }
  1808. X
  1809. # read in $etc passwd file. This creates an associative array to check
  1810. # duplicate login names.
  1811. open(PASSWD, $etc) || exit(1);
  1812. while(<PASSWD>) {
  1813. X    push(@PW_FILE, $_);
  1814. X    ($log, $pass) = split(/:/);
  1815. X    if (!defined $foo{$log}) {
  1816. X        $foo{$log} = $pass;
  1817. X        }
  1818. X    else {
  1819. X        print "Warning!  Duplicate uid found in $etc: $log\n";
  1820. X        }
  1821. X    }
  1822. close(PASSWD);
  1823. X
  1824. # same for yellow pages.
  1825. if (-s $YPCAT) {
  1826. X    system("$YPCAT passwd > $yptmp 2> /dev/null");
  1827. X        if (-s $yptmp && open(YP, "$YPCAT passwd|") != 0) {
  1828. X        while(<YP>) {
  1829. X            push(@YP_FILE, $_);
  1830. X            ($log, $pass) = split(/:/);
  1831. X            if (!defined $foo{$log}) {
  1832. X                $foo{$log} = $pass;
  1833. X                }
  1834. X             else {
  1835. X                print "Warning!  Duplicate uid found in yellow pages: $log\n";
  1836. X                }
  1837. X            close(YP);
  1838. X            }
  1839. X        }
  1840. X    }
  1841. X
  1842. #   First line is for a yellow pages entry in the password file.
  1843. # It really should check for correct yellow pages syntax....
  1844. $NR = 0;
  1845. foreach $_ (@PW_FILE) {
  1846. X    $NR++;
  1847. X    @ARR = split(/:/);
  1848. X    next if ($ARR[0] =~ /^\+/o);
  1849. X
  1850. X    if (/^\s*$/o) {
  1851. X        print "Warning!  Password file, line $NR, is blank\n";
  1852. X        next;
  1853. X    }
  1854. X    
  1855. X    if ($#ARR != 6) {
  1856. X        print "Warning!  Password file, line $NR, does not have 7 fields: \n\t$_";
  1857. X    }
  1858. X
  1859. X    if ($ARR[0] !~ /^[A-Za-z0-9]/o) {
  1860. X        print "Warning!  Password file, line $NR, nonalphanumeric login: \n\t$_";
  1861. X    }
  1862. X
  1863. X    if ($OVER_8 ne 'NO' && length($ARR[0]) > 8) {
  1864. X        print "Warning!  Password file, line $NR, uid > 8 chars\n\t$_";
  1865. X    }
  1866. X
  1867. X    if ($ARR[1] eq "") {
  1868. X        print "Warning!  Password file, line $NR, no password:\n\t$_";
  1869. X    }
  1870. X
  1871. X     if ($C2 eq "TRUE" && $ARR[1] =~ /^##/o && "##".$ARR[0] ne $ARR[1]) {
  1872. X        print "Warning!  Password file, line $NR, invalid password field for C2: \n\t$_";
  1873. X    }
  1874. X
  1875. X    if ($ARR[2] !~ /^[0-9]/o) {
  1876. X        if ($ARR[2] < 0) {
  1877. X            print "Warning!  Password file, line $NR, negative user id: \n\t$_";
  1878. X        } else {
  1879. X            print "Warning!  Password file, line $NR, nonnumeric user id: \n\t$_";
  1880. X        }
  1881. X    }
  1882. X
  1883. X    if ($ARR[2] == 0 && $ARR[0] ne "root") {
  1884. X        print "Warning!  Password file, line $NR, user $ARR[0] has uid = 0 and is not root\n\t$_";
  1885. X    }
  1886. X
  1887. X    if ($ARR[3] !~ /^[0-9]/o) {
  1888. X        print "Warning!  Password file, line $NR, nonnumeric group id: \n\t$_";
  1889. X    }
  1890. X
  1891. X    if ($ARR[5] !~ /^\//o) {
  1892. X        print "Warning!  Password file, line $NR, invalid login directory: \n\t$_";
  1893. X    }
  1894. X
  1895. }
  1896. X
  1897. #
  1898. # Test yellow pages passwords as well
  1899. # before you complain about me putting the test for YP_FILE first, you get less
  1900. # indentation if you do it this way. Change it if you want.
  1901. if (defined @YP_FILE) {
  1902. X
  1903. $NR = 0;
  1904. foreach $_ (@YP_FILE) {
  1905. X    $NR++;
  1906. X    @ARR = split(/:/);
  1907. X
  1908. X    if (/^\s*$/o) {
  1909. X        print "Warning!  YPassword file, line $NR, is blank\n";
  1910. X        next;
  1911. X    }
  1912. X    
  1913. X    if ($#ARR != 6) {
  1914. X        print "Warning!  YPassword file, line $NR, does not have 7 fields: \n\t$_";
  1915. X    }
  1916. X
  1917. X    if ($ARR[0] !~ /^[A-Za-z0-9]/o) {
  1918. X        print "Warning!  YPassword file, line $NR, nonalphanumeric login: \n\t$_";
  1919. X    }
  1920. X
  1921. X    if ($OVER_8 ne 'NO' && length($ARR[0]) > 8) {
  1922. X        print "Warning!  YPassword file, line $NR, uid > 8 chars\n\t$_";
  1923. X    }
  1924. X
  1925. X    if ($ARR[1] eq "") {
  1926. X        print "Warning!  YPassword file, line $NR, no password:\n\t$_";
  1927. X    }
  1928. X
  1929. X    if ($ARR[2] !~ /^[0-9]/o) {
  1930. X        if ($ARR[2] < 0) {
  1931. X            print "Warning!  YPassword file, line $NR, negative user id: \n\t$_";
  1932. X        } else {
  1933. X            print "Warning!  YPassword file, line $NR, nonnumeric user id: \n\t$_";
  1934. X        }
  1935. X    }
  1936. X
  1937. X    if ($ARR[2] == 0 && $ARR[0] ne "root") {
  1938. X        print "Warning!  YPassword file, line $NR, user $ARR[0] has uid = 0 and is not root\n\t$_";
  1939. X    }
  1940. X
  1941. X    if ($ARR[3] !~ /^[0-9]/o) {
  1942. X        print "Warning!  YPassword file, line $NR, nonnumeric group id: \n\t$_";
  1943. X    }
  1944. X
  1945. X    if ($ARR[5] !~ /^\//o) {
  1946. X        print "Warning!  YPassword file, line $NR, invalid login directory: \n\t$_";
  1947. X    }
  1948. X
  1949. X    }
  1950. }
  1951. X
  1952. unlink $yptmp;
  1953. X
  1954. 1;
  1955. # end
  1956. SHAR_EOF
  1957. chmod 0700 p-cops.alpha/passwd.chk ||
  1958. echo 'restore of p-cops.alpha/passwd.chk failed'
  1959. Wc_c="`wc -c < 'p-cops.alpha/passwd.chk'`"
  1960. test 5989 -eq "$Wc_c" ||
  1961.     echo 'p-cops.alpha/passwd.chk: original size 5989, current size' "$Wc_c"
  1962. rm -f _shar_wnt_.tmp
  1963. fi
  1964. # ============= p-cops.alpha/pathconf.pl ==============
  1965. if test -f 'p-cops.alpha/pathconf.pl' -a X"$1" != X"-c"; then
  1966.     echo 'x - skipping p-cops.alpha/pathconf.pl (File already exists)'
  1967.     rm -f _shar_wnt_.tmp
  1968. else
  1969. > _shar_wnt_.tmp
  1970. echo 'x - extracting p-cops.alpha/pathconf.pl (Text)'
  1971. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/pathconf.pl' &&
  1972. $YPCAT = '/usr/bin/ypcat';
  1973. $STRINGS = '/usr/ucb/strings';
  1974. $TFTP = '/usr/ucb/tftp';
  1975. $UUDECODE = '/usr/bin/uudecode';
  1976. $CMP = '/bin/cmp';
  1977. $LS = '/bin/ls';
  1978. X
  1979. # end of perl needed programs
  1980. X
  1981. $AWK = '/bin/awk';
  1982. $CAT = '/bin/cat';
  1983. $CC = '/bin/cc';
  1984. $CHMOD = '/bin/chmod';
  1985. $COMM = '/usr/bin/comm';
  1986. $CP = '/bin/cp';
  1987. $DATE = '/bin/date';
  1988. $DIFF = '/bin/diff';
  1989. $ECHO = '/bin/echo';
  1990. $EGREP = '/usr/bin/egrep';
  1991. $EXPR = '/bin/expr';
  1992. $FIND = '/usr/bin/find';
  1993. $GREP = '/bin/grep';
  1994. $MAIL = '/bin/mail';
  1995. $MKDIR = '/bin/mkdir';
  1996. $MV = '/bin/mv';
  1997. $RM = '/bin/rm';
  1998. $SED = '/bin/sed';
  1999. $SH = '/bin/sh';
  2000. $SORT = '/usr/bin/sort';
  2001. $TEST = '/bin/test';
  2002. $TOUCH = '/usr/bin/touch';
  2003. $UNIQ = '/usr/bin/uniq';
  2004. X
  2005. 1;
  2006. SHAR_EOF
  2007. chmod 0700 p-cops.alpha/pathconf.pl ||
  2008. echo 'restore of p-cops.alpha/pathconf.pl failed'
  2009. Wc_c="`wc -c < 'p-cops.alpha/pathconf.pl'`"
  2010. test 677 -eq "$Wc_c" ||
  2011.     echo 'p-cops.alpha/pathconf.pl: original size 677, current size' "$Wc_c"
  2012. rm -f _shar_wnt_.tmp
  2013. fi
  2014. # ============= p-cops.alpha/pathconf.sh ==============
  2015. if test -f 'p-cops.alpha/pathconf.sh' -a X"$1" != X"-c"; then
  2016.     echo 'x - skipping p-cops.alpha/pathconf.sh (File already exists)'
  2017.     rm -f _shar_wnt_.tmp
  2018. else
  2019. > _shar_wnt_.tmp
  2020. echo 'x - extracting p-cops.alpha/pathconf.sh (Text)'
  2021. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/pathconf.sh' &&
  2022. YPCAT = '/usr/bin/ypcat';
  2023. STRINGS = '/usr/ucb/strings';
  2024. TFTP = '/usr/ucb/tftp';
  2025. UUDECODE = '/usr/bin/uudecode';
  2026. X
  2027. # end of perl needed programs
  2028. X
  2029. AWK = '/bin/awk';
  2030. CAT = '/bin/cat';
  2031. CC = '/bin/cc';
  2032. CHMOD = '/bin/chmod';
  2033. CMP = '/bin/cmp';
  2034. COMM = '/usr/bin/comm';
  2035. CP = '/bin/cp';
  2036. DATE = '/bin/date';
  2037. DIFF = '/bin/diff';
  2038. ECHO = '/bin/echo';
  2039. EGREP = '/usr/bin/egrep';
  2040. EXPR = '/bin/expr';
  2041. FIND = '/usr/bin/find';
  2042. GREP = '/bin/grep';
  2043. LS = '/bin/ls';
  2044. MAIL = '/bin/mail';
  2045. MKDIR = '/bin/mkdir';
  2046. MV = '/bin/mv';
  2047. RM = '/bin/rm';
  2048. SED = '/bin/sed';
  2049. SH = '/bin/sh';
  2050. SORT = '/usr/bin/sort';
  2051. TEST = '/bin/test';
  2052. TOUCH = '/usr/bin/touch';
  2053. UNIQ = '/usr/bin/uniq';
  2054. SHAR_EOF
  2055. chmod 0700 p-cops.alpha/pathconf.sh ||
  2056. echo 'restore of p-cops.alpha/pathconf.sh failed'
  2057. Wc_c="`wc -c < 'p-cops.alpha/pathconf.sh'`"
  2058. test 644 -eq "$Wc_c" ||
  2059.     echo 'p-cops.alpha/pathconf.sh: original size 644, current size' "$Wc_c"
  2060. rm -f _shar_wnt_.tmp
  2061. fi
  2062. # ============= p-cops.alpha/rc.chk ==============
  2063. if test -f 'p-cops.alpha/rc.chk' -a X"$1" != X"-c"; then
  2064.     echo 'x - skipping p-cops.alpha/rc.chk (File already exists)'
  2065.     rm -f _shar_wnt_.tmp
  2066. else
  2067. > _shar_wnt_.tmp
  2068. echo 'x - extracting p-cops.alpha/rc.chk (Text)'
  2069. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/rc.chk' &&
  2070. #!/bin/sh  # need to mention perl here to avoid recursion
  2071. #
  2072. #  Usage: rc.chk
  2073. #
  2074. #  This checks pathnames and files inside the shell script files /etc/rc*
  2075. # for writability.  The commands inside the files /etc/rc* are executed when
  2076. # the machine is booted, so are of special interest.
  2077. #
  2078. # Made easy by chk_strings :-)
  2079. #
  2080. # Name: Martin Foord    Username: maf  Date: Thu Jan 17 15:11:09 EST 1991 
  2081. # Email: maf%dbsm.oz.au@munnari.oz.au
  2082. #
  2083. # NOTE:
  2084. #   If you know where perl is and your system groks #!, put its
  2085. # pathname at the top to make this a tad faster.
  2086. #
  2087. # the following magic is from the perl man page
  2088. # and should work to get us to run with perl 
  2089. # even if invoked as an sh or csh or foosh script.
  2090. # notice we don't use full path cause we don't
  2091. # know where the user has perl on their system.
  2092. #
  2093. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  2094. & eval 'exec perl -S $0 $argv:q'
  2095. X    if $running_under_some_stupid_shell_instead_of_perl;
  2096. X
  2097. require 'chk_strings.pl';
  2098. X
  2099. while (</etc/rc*>) {
  2100. X    &chk_strings($_);
  2101. }
  2102. X
  2103. 1;
  2104. SHAR_EOF
  2105. chmod 0700 p-cops.alpha/rc.chk ||
  2106. echo 'restore of p-cops.alpha/rc.chk failed'
  2107. Wc_c="`wc -c < 'p-cops.alpha/rc.chk'`"
  2108. test 1014 -eq "$Wc_c" ||
  2109.     echo 'p-cops.alpha/rc.chk: original size 1014, current size' "$Wc_c"
  2110. rm -f _shar_wnt_.tmp
  2111. fi
  2112. # ============= p-cops.alpha/reconfig.pl ==============
  2113. if test -f 'p-cops.alpha/reconfig.pl' -a X"$1" != X"-c"; then
  2114.     echo 'x - skipping p-cops.alpha/reconfig.pl (File already exists)'
  2115.     rm -f _shar_wnt_.tmp
  2116. else
  2117. > _shar_wnt_.tmp
  2118. echo 'x - extracting p-cops.alpha/reconfig.pl (Text)'
  2119. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/reconfig.pl' &&
  2120. #!/bin/sh  # need to mention perl here to avoid recursion
  2121. # NOTE:
  2122. #   If you know where perl is and your system groks #!, put its
  2123. # pathname at the top to make this a tad faster.
  2124. #
  2125. # the following magic is from the perl man page
  2126. # and should work to get us to run with perl 
  2127. # even if invoked as an sh or csh or foosh script.
  2128. # notice we don't use full path cause we don't
  2129. # know where the user has perl on their system.
  2130. #
  2131. eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' 
  2132. & eval 'exec perl -S $0 $argv:q'
  2133. X    if $running_under_some_stupid_shell_instead_of_perl;
  2134. X
  2135. #  Target shell scripts in question:
  2136. $COPS_CONFIG="pathconf.pl";
  2137. X
  2138. #  Potential directories to find commands:
  2139. @all_dirs=("/bin",
  2140. X       "/usr/bin",
  2141. X       "/usr/ucb",
  2142. X       "/usr/local/bin",  # scary
  2143. X       "/usr/bsd");
  2144. X
  2145. # comment out next line if you want your own current path used instead
  2146. #
  2147. # @all_dirs = split(/:/, $ENV{'PATH'});
  2148. X
  2149. #  Target commands in question, sans those checked above:
  2150. @all_commands= ("cc", "awk", "cat",
  2151. X        "chmod", "cmp", "comm", "cp",
  2152. X        "date", "diff", "echo", "egrep", "expr",
  2153. X        "find", "grep", "ls", "mail",
  2154. X        "mkdir", "mv", "rm", "sed",
  2155. X        "sh", "sort", "test", "tftp", "touch",
  2156. X        "uudecode", "uniq", "ypcat");
  2157. X
  2158. @want{@all_commands} = ();
  2159. X
  2160. %exceptions=   ('strings', 'chk_strings',
  2161. X                'tftp', 'misc.chk',
  2162. X        'cmp', 'ftp.chk',
  2163. X                'uudecode', 'misc.chk');
  2164. X
  2165. # grab the current values:
  2166. open COPS_CONFIG || die "Can't open $COPS_CONFIG: $!";
  2167. X
  2168. $new = "$COPS_CONFIG.$$";
  2169. open(NEW_CONFIG, ">$new") || die "Can't open $new: $!";
  2170. X
  2171. while (<COPS_CONFIG>) {
  2172. X    unless (/\$(\w+)\s*=\s*(['"])(\S*)\2/) {
  2173. X    print NEW_CONFIG;
  2174. X    next;
  2175. X    } 
  2176. X    ($cap_command, $path) = ($1, $3);
  2177. X    ($command = $cap_command) =~ tr/A-Z/a-z/;
  2178. X    unless (($newpath = &getpath($command)) || $command =~ /^yp/) {
  2179. X    warn "Warning!  no path for $command!\n";
  2180. X    warn "          $exceptions{$command} will not work as planned!\n"
  2181. X             if $exceptions{$command};
  2182. X    $errors++;
  2183. X    } else {
  2184. X    delete $want{$command};
  2185. X    } 
  2186. X    print "old $path now in $newpath\n" if $newpath ne $path;
  2187. X    print NEW_CONFIG "\$$cap_command = '$newpath';\n";
  2188. X
  2189. }
  2190. X
  2191. for (sort keys %want) {
  2192. X    delete $want{$_} if $path = &getpath($_);
  2193. X    tr/a-z/A-Z/;
  2194. X    print NEW_CONFIG '$', $_, " = '", $path, "';\n";
  2195. X
  2196. close(COPS_CONFIG) || die "can't close $COPS_CONFIG: $!";
  2197. close(NEW_CONFIG) || die "can't close $new: $!";
  2198. X
  2199. if (@missing = keys %want) {
  2200. X     warn "Warning!   missing paths for @missing!\n";
  2201. X     warn "The shell version may not work right!\n";
  2202. X
  2203. X
  2204. if ($errors) {
  2205. X    print STDERR "Not all paths were found: write anyway? ";
  2206. X    exit 1 if <STDIN> !~ /^\s*y/i;
  2207. X    print STDERR "Ok, but this might not be right...\n";
  2208. X
  2209. $old = "$COPS_CONFIG.old";
  2210. X
  2211. rename($COPS_CONFIG, $old)
  2212. X    || die "can't rename $COPS_CONFIG to $old: $!";
  2213. X
  2214. rename($new, $COPS_CONFIG)
  2215. X    || die "can't rename $new to $COPS_CONFIG: $!";
  2216. X
  2217. X
  2218. open COPS_CONFIG || die "can't re-open $COPS_CONFIG: $!";
  2219. ($SH_CONF = $COPS_CONFIG) =~ s/\.pl$/.sh/;
  2220. open (SH_CONF, ">$SH_CONF") || die "can't create $SH_CONF: $!";
  2221. X
  2222. while (<COPS_CONFIG>) {
  2223. X    s/^\$//;
  2224. X    print SH_CONF;
  2225. close SH_CONF || die "can't close $SH_CONF: $!";
  2226. X
  2227. Xexit 0;
  2228. X
  2229. #############
  2230. X
  2231. sub getpath {
  2232. X    local($cmd) = @_;
  2233. X    local($path);
  2234. X
  2235. X    for (@all_dirs) {
  2236. X    return $path if -x ($path = "$_/$cmd");
  2237. X    } 
  2238. X    '';
  2239. SHAR_EOF
  2240. chmod 0700 p-cops.alpha/reconfig.pl ||
  2241. echo 'restore of p-cops.alpha/reconfig.pl failed'
  2242. Wc_c="`wc -c < 'p-cops.alpha/reconfig.pl'`"
  2243. test 3296 -eq "$Wc_c" ||
  2244.     echo 'p-cops.alpha/reconfig.pl: original size 3296, current size' "$Wc_c"
  2245. rm -f _shar_wnt_.tmp
  2246. fi
  2247. # ============= p-cops.alpha/user.chk ==============
  2248. if test -f 'p-cops.alpha/user.chk' -a X"$1" != X"-c"; then
  2249.     echo 'x - skipping p-cops.alpha/user.chk (File already exists)'
  2250.     rm -f _shar_wnt_.tmp
  2251. else
  2252. > _shar_wnt_.tmp
  2253. echo 'x - extracting p-cops.alpha/user.chk (Text)'
  2254. sed 's/^X//' << 'SHAR_EOF' > 'p-cops.alpha/user.chk' &&
  2255. #!/bin/sh  # need to mention perl here to avoid recursion
  2256. #
  2257. #  This combines user.chk and home.chk.  It searches for home directories
  2258. # and various user startup files for world writability, as well as flagging
  2259. # any .rhosts and .netrc files that are readable.  You can change the
  2260. # files checked by changing @ftable and @readables, respectively.
  2261. #  NOTE:
  2262. # if you know where perl is and your system groks #!, put its
  2263. # pathname at the top to make this a tad faster.
  2264. #
  2265. # the following magic is from the perl man page
  2266. # and should work to get us to run with perl 
  2267. SHAR_EOF
  2268. true || echo 'restore of p-cops.alpha/user.chk failed'
  2269. fi
  2270. echo 'End of alpha p-cops part 2'
  2271. echo 'File p-cops.alpha/user.chk is continued in part 3'
  2272. echo 3 > _shar_seq_.tmp
  2273. exit 0
  2274.