Source code pulled from OpenBSD for OpenNTPD. The place to contribute to this code is via the OpenBSD CVS tree.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

767 lines
20 KiB

28 years ago
19 years ago
  1. #!/bin/sh -
  2. #
  3. # $OpenBSD: security,v 1.81 2008/07/23 16:05:47 sthen Exp $
  4. # from: @(#)security 8.1 (Berkeley) 6/9/93
  5. #
  6. PATH=/bin:/usr/bin:/sbin:/usr/sbin
  7. umask 077
  8. DIR=`mktemp -d /tmp/_secure.XXXXXXXXXX` || exit 1
  9. ERR=$DIR/_secure1
  10. TMP1=$DIR/_secure2
  11. TMP2=$DIR/_secure3
  12. TMP3=$DIR/_secure4
  13. LIST=$DIR/_secure5
  14. OUTPUT=$DIR/_secure6
  15. trap 'rm -rf $DIR; exit 1' 0 1 2 3 13 15
  16. # Check the master password file syntax.
  17. MP=/etc/master.passwd
  18. awk -F: '{
  19. if ($0 ~ /^[ ]*$/) {
  20. printf("Line %d is a blank line.\n", NR);
  21. next;
  22. }
  23. if (NF != 10)
  24. printf("Line %d has the wrong number of fields:\n%s\n", NR, $0);
  25. if ($1 ~ /^[+-]/)
  26. next;
  27. if ($1 == "")
  28. printf("Line %d has an empty login field:\n%s\n", NR, $0);
  29. else if ($1 !~ /^[A-Za-z0-9_][A-Za-z0-9_\-\.]*\$?$/)
  30. printf("Login %s has non-alphanumeric characters.\n", $1);
  31. if (length($1) > 31)
  32. printf("Login %s has more than 31 characters.\n", $1);
  33. if ($2 == "")
  34. printf("Login %s has no password.\n", $1);
  35. if ($2 != "" && length($2) != 13 && ($10 ~ /.*sh$/ || $10 == "") &&
  36. ($2 !~ /^\$[0-9a-f]+\$/) && ($2 != "skey")) {
  37. if (system("test -s /etc/skey/"$1"") == 0)
  38. printf("Login %s is off but still has a valid shell and an entry in /etc/skey.\n", $1);
  39. if (system("test -d "$9" -a ! -r "$9"") == 0)
  40. printf("Login %s is off but still has valid shell and home directory is unreadable\n\t by root; cannot check for existence of alternate access files.\n", $1);
  41. else if (system("for file in .ssh .rhosts .shosts .klogin; do if test -e "$9"/$file; then if ((ls -ld "$9"/$file | cut -b 2-10 | grep -q r) && (test ! -O "$9"/$file)) ; then exit 1; fi; fi; done"))
  42. printf("Login %s is off but still has a valid shell and alternate access files in\n\t home directory are still readable.\n",$1);
  43. }
  44. if ($3 == 0 && $1 != "root")
  45. printf("Login %s has a user ID of 0.\n", $1);
  46. if ($3 < 0)
  47. printf("Login %s has a negative user ID.\n", $1);
  48. if ($4 < 0)
  49. printf("Login %s has a negative group ID.\n", $1);
  50. if (int($7) != 0 && system("test "$7" -lt `date +%s`") == 0)
  51. printf("Login %s has expired.\n", $1);
  52. }' < $MP > $OUTPUT
  53. if [ -s $OUTPUT ] ; then
  54. echo "\nChecking the ${MP} file:"
  55. cat $OUTPUT
  56. fi
  57. awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT
  58. if [ -s $OUTPUT ] ; then
  59. echo "\n${MP} has duplicate user names."
  60. column $OUTPUT
  61. fi
  62. awk -F: '/^[^\+]/ { print $1 " " $3 }' $MP | sort -n +1 | tee $TMP1 |
  63. uniq -d -f 1 | awk '{ print $2 }' > $TMP2
  64. if [ -s $TMP2 ] ; then
  65. echo "\n${MP} has duplicate user IDs."
  66. while read uid; do
  67. grep -w $uid $TMP1
  68. done < $TMP2 | column
  69. fi
  70. # Backup the master password file; a special case, the normal backup
  71. # mechanisms also print out file differences and we don't want to do
  72. # that because this file has encrypted passwords in it.
  73. if [ ! -d /var/backups ] ; then
  74. mkdir /var/backups
  75. chmod 700 /var/backups
  76. fi
  77. CUR=/var/backups/`basename $MP`.current
  78. BACK=/var/backups/`basename $MP`.backup
  79. if [ -s $CUR ] ; then
  80. if cmp -s $CUR $MP; then
  81. :
  82. else
  83. cp -p $CUR $BACK
  84. cp -p $MP $CUR
  85. chown root:wheel $CUR
  86. fi
  87. else
  88. cp -p $MP $CUR
  89. chown root:wheel $CUR
  90. fi
  91. # Check the group file syntax.
  92. GRP=/etc/group
  93. awk -F: '{
  94. if ($0 ~ /^[ ]*$/) {
  95. printf("Line %d is a blank line.\n", NR);
  96. next;
  97. }
  98. if ($1 ~ /^[+-].*$/)
  99. next;
  100. if (NF != 4)
  101. printf("Line %d has the wrong number of fields:\n%s\n", NR, $0);
  102. if ($1 !~ /^[A-Za-z0-9_][A-Za-z0-9_\-\.]*$/)
  103. printf("Group %s has non-alphanumeric characters.\n", $1);
  104. if (length($1) > 31)
  105. printf("Group %s has more than 31 characters.\n", $1);
  106. if ($3 !~ /[0-9]*/)
  107. printf("Login %s has a negative group ID.\n", $1);
  108. }' < $GRP > $OUTPUT
  109. if [ -s $OUTPUT ] ; then
  110. echo "\nChecking the ${GRP} file:"
  111. cat $OUTPUT
  112. fi
  113. awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT
  114. if [ -s $OUTPUT ] ; then
  115. echo "\n${GRP} has duplicate group names."
  116. column $OUTPUT
  117. fi
  118. # Check for root paths, umask values in startup files.
  119. # The check for the root paths is problematical -- it's likely to fail
  120. # in other environments. Once the shells have been modified to warn
  121. # of '.' in the path, the path tests should go away.
  122. > $OUTPUT
  123. rhome=/root
  124. umaskset=no
  125. list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login"
  126. for i in $list ; do
  127. if [ -s $i ] ; then
  128. if egrep -aq '[[:space:]]*umask[[:space:]]' $i ; then
  129. umaskset=yes
  130. fi
  131. awk '{
  132. if ($1 == "umask") {
  133. if ($2 % 100 / 10 ~ /^[0145]/)
  134. print "Root umask is group writable";
  135. if ($2 % 10 ~ /^[0145]/)
  136. print "Root umask is other writable";
  137. }
  138. }' < $i >> $OUTPUT
  139. SAVE_PATH=$PATH
  140. unset PATH
  141. /bin/csh -f -s << end-of-csh > /dev/null 2>&1
  142. source $i
  143. if (\$?path) then
  144. /bin/ls -ldgT \$path > $TMP1
  145. else
  146. cat /dev/null > $TMP1
  147. endif
  148. end-of-csh
  149. PATH=$SAVE_PATH
  150. awk '{
  151. if ($10 ~ /^\.$/) {
  152. print "The root path includes .";
  153. next;
  154. }
  155. }
  156. $1 ~ /^d....w/ \
  157. { print "Root path directory " $10 " is group writable." } \
  158. $1 ~ /^d.......w/ \
  159. { print "Root path directory " $10 " is other writable." }' \
  160. < $TMP1 >> $OUTPUT
  161. fi
  162. done
  163. if [ $umaskset = "no" -o -s $OUTPUT ] ; then
  164. echo "\nChecking root csh paths, umask values:\n${list}"
  165. if [ -s $OUTPUT ] ; then
  166. cat $OUTPUT
  167. fi
  168. if [ $umaskset = "no" ] ; then
  169. echo "\nRoot csh startup files do not set the umask."
  170. fi
  171. fi
  172. > $OUTPUT
  173. > $TMP2
  174. rhome=/root
  175. umaskset=no
  176. list="/etc/profile ${rhome}/.profile"
  177. for i in $list; do
  178. if [ -s $i ] ; then
  179. if egrep -a umask $i > /dev/null ; then
  180. umaskset=yes
  181. fi
  182. egrep -a umask $i |
  183. awk '$2 % 100 < 20 \
  184. { print "Root umask is group writable" } \
  185. $2 % 10 < 2 \
  186. { print "Root umask is other writable" }' >> $OUTPUT
  187. SAVE_PATH=$PATH
  188. SAVE_ENV=$ENV
  189. unset PATH ENV
  190. /bin/sh << end-of-sh > /dev/null 2>&1
  191. . $i
  192. if [ X"\$PATH" != "X" ]; then
  193. list=\`echo \$PATH | /usr/bin/sed -e 's/:/ /g'\`
  194. /bin/ls -ldgT \$list > $TMP1
  195. else
  196. > $TMP1
  197. fi
  198. echo \$ENV >> $TMP2
  199. end-of-sh
  200. PATH=$SAVE_PATH
  201. ENV=$SAVE_ENV
  202. awk '{
  203. if ($10 ~ /^\.$/) {
  204. print "The root path includes .";
  205. next;
  206. }
  207. }
  208. $1 ~ /^d....w/ \
  209. { print "Root path directory " $10 " is group writable." } \
  210. $1 ~ /^d.......w/ \
  211. { print "Root path directory " $10 " is other writable." }' \
  212. < $TMP1 >> $OUTPUT
  213. fi
  214. done
  215. if [ $umaskset = "no" -o -s $OUTPUT ] ; then
  216. echo "\nChecking root sh paths, umask values:\n${list}"
  217. if [ -s $OUTPUT ] ; then
  218. cat $OUTPUT
  219. fi
  220. if [ $umaskset = "no" ] ; then
  221. echo "\nRoot sh startup files do not set the umask."
  222. fi
  223. fi
  224. # A good .kshrc will not have a umask or path, that being set in .profile
  225. # check anyway.
  226. > $OUTPUT
  227. rhome=/root
  228. list="/etc/ksh.kshrc `cat $TMP2`"
  229. (cd $rhome
  230. for i in $list; do
  231. if [ -s $i ] ; then
  232. egrep -a umask $i |
  233. awk '$2 % 100 < 20 \
  234. { print "Root umask is group writable" } \
  235. $2 % 10 < 2 \
  236. { print "Root umask is other writable" }' >> $OUTPUT
  237. if egrep -a PATH= $i > /dev/null ; then
  238. SAVE_PATH=$PATH
  239. unset PATH
  240. /bin/ksh << end-of-sh > /dev/null 2>&1
  241. . $i
  242. if [ X"\$PATH" != "X" ]; then
  243. list=\`echo \$PATH | /usr/bin/sed -e 's/:/ /g'\`
  244. /bin/ls -ldgT \$list > $TMP1
  245. else
  246. > $TMP1
  247. fi
  248. end-of-sh
  249. PATH=$SAVE_PATH
  250. awk '{
  251. if ($10 ~ /^\.$/) {
  252. print "The root path includes .";
  253. next;
  254. }
  255. }
  256. $1 ~ /^d....w/ \
  257. { print "Root path directory " $10 " is group writable." } \
  258. $1 ~ /^d.......w/ \
  259. { print "Root path directory " $10 " is other writable." }' \
  260. < $TMP1 >> $OUTPUT
  261. fi
  262. fi
  263. done
  264. )
  265. if [ -s $OUTPUT ] ; then
  266. echo "\nChecking root ksh paths, umask values:\n${list}"
  267. cat $OUTPUT
  268. fi
  269. # Root and uucp should both be in /etc/ftpusers.
  270. if egrep root /etc/ftpusers > /dev/null ; then
  271. :
  272. else
  273. echo "\nRoot not listed in /etc/ftpusers file."
  274. fi
  275. if egrep uucp /etc/ftpusers > /dev/null ; then
  276. :
  277. else
  278. echo "\nUucp not listed in /etc/ftpusers file."
  279. fi
  280. # Uudecode should not be in the /etc/mail/aliases file.
  281. if egrep 'uudecode|decode' /etc/mail/aliases; then
  282. echo "\nThere is an entry for uudecode in the /etc/mail/aliases file."
  283. fi
  284. # hostname.if files may contain secrets and should not be
  285. # world-readable.
  286. for f in /etc/hostname.* ; do
  287. if [ ! -e $f ]; then
  288. continue
  289. fi
  290. if [ "$(stat -Lf "%SLp" $f)" != "---" ]; then
  291. echo "\n$f is world readable."
  292. fi
  293. done
  294. # Files that should not have + signs.
  295. list="/etc/hosts.equiv /etc/shosts.equiv /etc/hosts.lpd"
  296. for f in $list ; do
  297. if [ -s $f ] ; then
  298. awk '{
  299. if ($0 ~ /^\+@.*$/)
  300. next;
  301. if ($0 ~ /^\+.*$/)
  302. printf("\nPlus sign in %s file.\n", FILENAME);
  303. }' $f
  304. fi
  305. done
  306. # Check for special users with .rhosts/.shosts files. Only root
  307. # should have .rhosts/.shosts files. Also, .rhosts/.shosts
  308. # files should not have plus signs.
  309. awk -F: '$1 != "root" && $1 !~ /^[+-]/ && \
  310. ($3 < 100 || $1 == "ftp" || $1 == "uucp") \
  311. { print $1 " " $6 }' /etc/passwd |
  312. while read uid homedir; do
  313. for j in .rhosts .shosts; do
  314. # Root owned .rhosts/.shosts files are ok.
  315. if [ -s ${homedir}/$j -a ! -O ${homedir}/$j ] ; then
  316. rhost=`ls -ldgT ${homedir}/$j`
  317. echo "${uid}: ${rhost}"
  318. fi
  319. done
  320. done > $OUTPUT
  321. if [ -s $OUTPUT ] ; then
  322. echo "\nChecking for special users with .rhosts/.shosts files."
  323. cat $OUTPUT
  324. fi
  325. awk -F: '/^[^+-]/ { print $1 " " $6 }' /etc/passwd | \
  326. while read uid homedir; do
  327. for j in .rhosts .shosts; do
  328. if [ -s ${homedir}/$j ] ; then
  329. awk '{
  330. if ($0 ~ /^+@.*$/ )
  331. next;
  332. if ($0 ~ /^\+[ ]*$/ )
  333. printf("%s has + sign in it.\n",
  334. FILENAME);
  335. }' ${homedir}/$j
  336. fi
  337. done
  338. done > $OUTPUT
  339. if [ -s $OUTPUT ] ; then
  340. echo "\nChecking .rhosts/.shosts files syntax."
  341. cat $OUTPUT
  342. fi
  343. # Check home directories. Directories should not be owned by someone else
  344. # or writeable.
  345. awk -F: '/^[^+-]/ { print $1 " " $6 }' /etc/passwd | \
  346. while read uid homedir; do
  347. if [ -d ${homedir}/ ] ; then
  348. file=`ls -ldgT ${homedir}`
  349. echo "${uid} ${file}"
  350. fi
  351. done |
  352. awk '$1 != $4 && $4 != "root" \
  353. { print "user " $1 " home directory is owned by " $4 }
  354. $2 ~ /^-....w/ \
  355. { print "user " $1 " home directory is group writable" }
  356. $2 ~ /^-.......w/ \
  357. { print "user " $1 " home directory is other writable" }' > $OUTPUT
  358. if [ -s $OUTPUT ] ; then
  359. echo "\nChecking home directories."
  360. cat $OUTPUT
  361. fi
  362. # Files that should not be owned by someone else or readable.
  363. list=".netrc .rhosts .gnupg/secring.gpg .gnupg/random_seed \
  364. .pgp/secring.pgp .shosts .ssh/identity .ssh/id_dsa .ssh/id_rsa"
  365. awk -F: '/^[^+-]/ { print $1 " " $6 }' /etc/passwd | \
  366. while read uid homedir; do
  367. for f in $list ; do
  368. file=${homedir}/${f}
  369. if [ -f $file ] ; then
  370. echo "${uid} ${f} `ls -ldgT ${file}`"
  371. fi
  372. done
  373. done |
  374. awk '$1 != $5 && $5 != "root" \
  375. { print "user " $1 " " $2 " file is owned by " $5 }
  376. $3 ~ /^-...r/ \
  377. { print "user " $1 " " $2 " file is group readable" }
  378. $3 ~ /^-......r/ \
  379. { print "user " $1 " " $2 " file is other readable" }
  380. $3 ~ /^-....w/ \
  381. { print "user " $1 " " $2 " file is group writable" }
  382. $3 ~ /^-.......w/ \
  383. { print "user " $1 " " $2 " file is other writable" }' > $OUTPUT
  384. # Files that should not be owned by someone else or writeable.
  385. list=".bashrc .bash_profile .bash_login .bash_logout .cshrc \
  386. .emacs .exrc .forward .fvwmrc .inputrc .klogin .kshrc .login \
  387. .logout .nexrc .profile .screenrc .ssh .ssh/config \
  388. .ssh/authorized_keys .ssh/authorized_keys2 .ssh/environment \
  389. .ssh/known_hosts .ssh/rc .tcshrc .twmrc .xsession .xinitrc \
  390. .Xdefaults .Xauthority"
  391. awk -F: '/^[^+-]/ { print $1 " " $6 }' /etc/passwd | \
  392. while read uid homedir; do
  393. for f in $list ; do
  394. file=${homedir}/${f}
  395. if [ -f $file ] ; then
  396. echo "${uid} ${f} `ls -ldgT ${file}`"
  397. fi
  398. done
  399. done |
  400. awk '$1 != $5 && $5 != "root" \
  401. { print "user " $1 " " $2 " file is owned by " $5 }
  402. $3 ~ /^-....w/ \
  403. { print "user " $1 " " $2 " file is group writable" }
  404. $3 ~ /^-.......w/ \
  405. { print "user " $1 " " $2 " file is other writable" }' >> $OUTPUT
  406. if [ -s $OUTPUT ] ; then
  407. echo "\nChecking dot files."
  408. cat $OUTPUT
  409. fi
  410. # Mailboxes should be owned by user and unreadable.
  411. ls -l /var/mail | sed 1d | \
  412. awk '$3 != $9 \
  413. { print "user " $9 " mailbox is owned by " $3 }
  414. $1 != "-rw-------" \
  415. { print "user " $9 " mailbox is " $1 ", group " $4 }' > $OUTPUT
  416. if [ -s $OUTPUT ] ; then
  417. echo "\nChecking mailbox ownership."
  418. cat $OUTPUT
  419. fi
  420. # File systems should not be globally exported.
  421. if [ -s /etc/exports ] ; then
  422. awk '{
  423. if (($1 ~ /^#/) || ($1 ~ /^$/))
  424. next;
  425. readonly = 0;
  426. for (i = 2; i <= NF; ++i) {
  427. if ($i ~ /^-ro$/)
  428. readonly = 1;
  429. else if ($i !~ /^-/ || $i ~ /^-network/)
  430. next;
  431. }
  432. if (readonly)
  433. print "File system " $1 " globally exported, read-only."
  434. else
  435. print "File system " $1 " globally exported, read-write."
  436. }' < /etc/exports > $OUTPUT
  437. if [ -s $OUTPUT ] ; then
  438. echo "\nChecking for globally exported file systems."
  439. cat $OUTPUT
  440. fi
  441. fi
  442. # Display any changes in setuid/setgid files and devices.
  443. pending="\nChecking setuid/setgid files and devices:\n"
  444. (find / \( ! -fstype local \
  445. -o -fstype procfs -o -fstype afs -o -fstype xfs \) -a -prune -o \
  446. -type f -a \( -perm -u+s -o -perm -g+s \) -print0 -o \
  447. ! -type d -a ! -type f -a ! -type l -a ! -type s -a ! -type p \
  448. -print0 | xargs -0 ls -ldgT | sort +9 > $LIST) 2> $OUTPUT
  449. # Display any errors that occurred during system file walk.
  450. if [ -s $OUTPUT ] ; then
  451. echo "${pending}Setuid/device find errors:"
  452. pending=
  453. cat $OUTPUT
  454. echo ""
  455. fi
  456. # Display any changes in the setuid/setgid file list.
  457. FIELDS1=1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,0
  458. FIELDS2=2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,0
  459. egrep -av '^[bc]' $LIST | join -o $FIELDS2 -110 -210 -v2 /dev/null - > $TMP1
  460. if [ -s $TMP1 ] ; then
  461. # Check to make sure uudecode isn't setuid.
  462. if grep -aw uudecode $TMP1 > /dev/null ; then
  463. echo "${pending}\nUudecode is setuid."
  464. pending=
  465. fi
  466. CUR=/var/backups/setuid.current
  467. BACK=/var/backups/setuid.backup
  468. if [ -s $CUR ] ; then
  469. if cmp -s $CUR $TMP1 ; then
  470. :
  471. else
  472. > $TMP2
  473. join -o $FIELDS2 -110 -210 -v2 $CUR $TMP1 > $OUTPUT
  474. if [ -s $OUTPUT ] ; then
  475. echo "${pending}Setuid additions:"
  476. pending=
  477. tee -a $TMP2 < $OUTPUT | column -t
  478. echo ""
  479. fi
  480. join -o $FIELDS1 -110 -210 -v1 $CUR $TMP1 > $OUTPUT
  481. if [ -s $OUTPUT ] ; then
  482. echo "${pending}Setuid deletions:"
  483. pending=
  484. tee -a $TMP2 < $OUTPUT | column -t
  485. echo ""
  486. fi
  487. sort +9 $TMP2 $CUR $TMP1 | \
  488. sed -e 's/[ ][ ]*/ /g' | uniq -u > $OUTPUT
  489. if [ -s $OUTPUT ] ; then
  490. echo "${pending}Setuid changes:"
  491. pending=
  492. column -t $OUTPUT
  493. echo ""
  494. fi
  495. cp $CUR $BACK
  496. cp $TMP1 $CUR
  497. fi
  498. else
  499. echo "${pending}Setuid additions:"
  500. pending=
  501. column -t $TMP1
  502. echo ""
  503. cp $TMP1 $CUR
  504. fi
  505. fi
  506. # Check for block and character disk devices that are readable or writeable
  507. # or not owned by root.operator.
  508. >$TMP1
  509. DISKLIST="ccd dk fd hd hk hp jb kra ra rb rd rl rx rz sd up vnd wd xd"
  510. for i in $DISKLIST; do
  511. egrep "^b.*/${i}[0-9][0-9]*[B-H]?[a-p]$" $LIST >> $TMP1
  512. egrep "^c.*/r${i}[0-9][0-9]*[B-H]?[a-p]$" $LIST >> $TMP1
  513. done
  514. awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \
  515. { printf("Disk %s is user %s, group %s, permissions %s.\n", \
  516. $11, $3, $4, $1); }' < $TMP1 > $OUTPUT
  517. if [ -s $OUTPUT ] ; then
  518. echo "\nChecking disk ownership and permissions."
  519. cat $OUTPUT
  520. echo ""
  521. fi
  522. FIELDS1=1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,0
  523. FIELDS2=2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9,2.10,0
  524. # Display any changes in the device file list.
  525. egrep -a '^[bc]' $LIST | sort +10 | \
  526. join -o $FIELDS2 -111 -211 -v2 /dev/null - > $TMP1
  527. if [ -s $TMP1 ] ; then
  528. CUR=/var/backups/device.current
  529. BACK=/var/backups/device.backup
  530. if [ -s $CUR ] ; then
  531. if cmp -s $CUR $TMP1 ; then
  532. :
  533. else
  534. > $TMP2
  535. join -o $FIELDS2 -111 -211 -v2 $CUR $TMP1 > $OUTPUT
  536. if [ -s $OUTPUT ] ; then
  537. echo "Device additions:"
  538. tee -a $TMP2 < $OUTPUT | column -t
  539. echo ""
  540. fi
  541. join -o $FIELDS1 -111 -211 -v1 $CUR $TMP1 > $OUTPUT
  542. if [ -s $OUTPUT ] ; then
  543. echo "Device deletions:"
  544. tee -a $TMP2 < $OUTPUT | column -t
  545. echo ""
  546. fi
  547. # Report any block device change. Ignore character
  548. # devices, only the name is significant.
  549. cat $TMP2 $CUR $TMP1 | \
  550. sed -e '/^c/d' | \
  551. sort +10 | \
  552. sed -e 's/[ ][ ]*/ /g' | \
  553. uniq -u > $OUTPUT
  554. if [ -s $OUTPUT ] ; then
  555. echo "Block device changes:"
  556. column -t $OUTPUT
  557. echo ""
  558. fi
  559. cp $CUR $BACK
  560. cp $TMP1 $CUR
  561. fi
  562. else
  563. echo "Device additions:"
  564. column -t $TMP1
  565. echo ""
  566. cp $TMP1 $CUR
  567. fi
  568. fi
  569. # Check special files.
  570. # Check system binaries.
  571. #
  572. # Create the mtree tree specifications using:
  573. #
  574. # mtree -cx -p DIR -K md5digest,type >/etc/mtree/DIR.secure
  575. # chown root:wheel /etc/mtree/DIR.secure
  576. # chmod 600 /etc/mtree/DIR.secure
  577. #
  578. # Note, this is not complete protection against Trojan horsed binaries, as
  579. # the hacker can modify the tree specification to match the replaced binary.
  580. # For details on really protecting yourself against modified binaries, see
  581. # the mtree(8) manual page.
  582. if [ -d /etc/mtree ] ; then
  583. cd /etc/mtree
  584. mtree -e -l -p / -f /etc/mtree/special > $OUTPUT
  585. if [ -s $OUTPUT ] ; then
  586. echo "\nChecking special files and directories."
  587. echo "Output format is:\n\tfilename:"
  588. echo "\t\tcriteria (shouldbe, reallyis)"
  589. cat $OUTPUT
  590. fi
  591. > $OUTPUT
  592. for file in *.secure; do
  593. [ $file = '*.secure' ] && continue
  594. tree=`sed -n -e '3s/.* //p' -e 3q $file`
  595. mtree -f $file -p $tree > $TMP1
  596. if [ -s $TMP1 ] ; then
  597. echo "\nChecking ${tree}:" >> $OUTPUT
  598. cat $TMP1 >> $OUTPUT
  599. fi
  600. done
  601. if [ -s $OUTPUT ] ; then
  602. echo "\nChecking system binaries:"
  603. cat $OUTPUT
  604. fi
  605. else
  606. echo /etc/mtree is missing
  607. fi
  608. # List of files that get backed up and checked for any modifications. Each
  609. # file is expected to have two backups, /var/backups/file.{current,backup}.
  610. # Any changes cause the files to rotate.
  611. _fnchg() {
  612. echo "$1" | sed 's/^\///;s/\//_/g'
  613. }
  614. if [ -s /etc/changelist ] ; then
  615. for file in `egrep -v "^(#|\+|$MP)" /etc/changelist`; do
  616. CUR=/var/backups/$(_fnchg "$file").current
  617. BACK=/var/backups/$(_fnchg "$file").backup
  618. if [ -s $file -a ! -d $file ] ; then
  619. if [ -s $CUR ] ; then
  620. diff -ua $CUR $file > $OUTPUT
  621. if [ -s $OUTPUT ] ; then
  622. echo "\n======\n${file} diffs (-OLD +NEW)\n======"
  623. cat $OUTPUT
  624. cp -p $CUR $BACK
  625. cp -p $file $CUR
  626. chown root:wheel $CUR $BACK
  627. fi
  628. else
  629. echo "\n======\n${file} diffs (-OLD +NEW)\n======"
  630. diff -u /dev/null $file
  631. cp -p $file $CUR
  632. chown root:wheel $CUR
  633. fi
  634. fi
  635. if [ ! -s $file -a -s $CUR ]; then
  636. echo "\n======\n${file} diffs (-OLD +NEW)\n======"
  637. diff -u $CUR /dev/null
  638. cp -p $CUR $BACK
  639. rm -f $CUR
  640. chown root:wheel $BACK
  641. fi
  642. done
  643. for file in `egrep "^\+" /etc/changelist`; do
  644. file="${file#+}"
  645. CUR=/var/backups/$(_fnchg "$file").current.md5
  646. BACK=/var/backups/$(_fnchg "$file").backup.md5
  647. if [ -s $file -a ! -d $file ] ; then
  648. MD5_NEW=`md5 $file | sed 's/^.* //'`
  649. if [ -s $CUR ] ; then
  650. MD5_OLD="`cat $CUR`"
  651. if [ "$MD5_NEW" != "$MD5_OLD" ]; then
  652. echo "\n======\n${file} MD5 checksums\n======"
  653. echo "OLD: $MD5_OLD"
  654. echo "NEW: $MD5_NEW"
  655. cp -p $CUR $BACK
  656. echo $MD5_NEW > $CUR
  657. chown root:wheel $CUR $BACK
  658. chmod 600 $CUR
  659. fi
  660. else
  661. echo "\n======\n${file} new MD5 checksum\n======"
  662. echo "NEW: $MD5_NEW"
  663. echo $MD5_NEW > $CUR
  664. chown root:wheel $CUR
  665. chmod 600 $CUR
  666. fi
  667. fi
  668. if [ ! -s $file -a -s $CUR ]; then
  669. MD5_OLD="`cat $CUR`"
  670. echo "\n======\n${file} removed MD5 checksum\n======"
  671. echo "OLD: $MD5_OLD"
  672. cp -p $CUR $BACK
  673. rm $CUR
  674. chown root:wheel $BACK
  675. fi
  676. done
  677. fi
  678. # Make backups of the labels for any mounted disks and produce diffs
  679. # when they change.
  680. for d in `df -ln | sed -n 's:^/dev/\([a-z]*[0-9]*\)[a-p].*$:\1:p' | sort -u`; do
  681. file=/var/backups/disklabel.$d
  682. CUR=$file.current
  683. BACK=$file.backup
  684. if disklabel $d > $file 2>&1 ; then
  685. if [ -s $CUR ] ; then
  686. diff -u $CUR $file > $OUTPUT
  687. if [ -s $OUTPUT ] ; then
  688. echo "\n======\n${d} diffs (-OLD +NEW)\n======"
  689. cat $OUTPUT
  690. cp -p $CUR $BACK
  691. cp -p $file $CUR
  692. chown root:wheel $CUR $BACK
  693. fi
  694. else
  695. cp -p $file $CUR
  696. chown root:wheel $CUR
  697. fi
  698. fi
  699. rm -f $file
  700. done
  701. # Backup the list of installed packages and produce diffs when it changes.
  702. file=/var/backups/pkglist
  703. CUR=$file.current
  704. BACK=$file.backup
  705. if pkg_info > $file 2>&1 ; then
  706. if [ -s $CUR ] ; then
  707. diff -u $CUR $file > $OUTPUT
  708. if [ -s $OUTPUT ] ; then
  709. echo "\n======\nInstalled package changes (-OLD +NEW)\n======"
  710. cat $OUTPUT
  711. cp -p $CUR $BACK
  712. cp -p $file $CUR
  713. chown root:wheel $CUR $BACK
  714. fi
  715. else
  716. cp -p $file $CUR
  717. chown root:wheel $CUR
  718. fi
  719. fi
  720. rm -f $file