security revision 1.39
1#!/bin/sh - 2# 3# $NetBSD: security,v 1.39 1999/07/22 00:47:50 hubertf Exp $ 4# from: @(#)security 8.1 (Berkeley) 6/9/93 5# 6 7PATH=/sbin:/usr/sbin:/bin:/usr/bin 8 9if [ -f /etc/rc.subr ]; then 10 . /etc/rc.subr 11else 12 echo "Can't read /etc/rc.subr; aborting." 13 exit 1; 14fi 15 16umask 077 17 18if [ -s /etc/security.conf ]; then 19 . /etc/security.conf 20fi 21 22SECUREDIR=/tmp/_securedir.$$ 23if ! mkdir $SECUREDIR; then 24 echo can not create $SECUREDIR. 25 exit 1 26fi 27 28if ! cd $SECUREDIR; then 29 echo can not chdir to $SECUREDIR. 30 exit 1 31fi 32 33if [ -z "$max_loginlen" ];then 34 max_loginlen=8 35fi 36 37ERR=secure1.$$ 38TMP1=secure2.$$ 39TMP2=secure3.$$ 40MPBYUID=secure4.$$ 41MPBYPATH=secure5.$$ 42LIST=secure6.$$ 43OUTPUT=secure7.$$ 44LABELS=secure8.$$ 45 46trap '/bin/rm -rf $SECUREDIR ; exit 0' 0 2 3 47 48MP=/etc/master.passwd 49 50# these is used several times. 51awk -F: '!/^+/ { print $1 " " $3 }' $MP | sort -k2n > $MPBYUID 52awk -F: '{ print $1 " " $9 }' $MP | sort -k2 > $MPBYPATH 53 54# Check the master password file syntax. 55# 56if checkyesno check_passwd; then 57 awk -v "len=$max_loginlen" ' 58 BEGIN { 59 while ( getline < "/etc/shells" > 0 ) { 60 if ($0 ~ /^\#/ || $0 ~ /^$/ ) 61 continue; 62 shells[$1]++; 63 } 64 FS=":"; 65 } 66 67 { 68 if ($0 ~ /^[ ]*$/) { 69 printf "Line %d is a blank line.\n", NR; 70 next; 71 } 72 if (NF != 10 && ($1 != "+" || NF != 1)) 73 printf "Line %d has the wrong number of fields.\n", NR; 74 if ($1 == "+" ) { 75 if (NF != 1 && $3 == 0) 76 printf "Line %d includes entries with uid 0.\n", NR; 77 next; 78 } 79 if ($1 !~ /^[A-Za-z0-9]*$/) 80 printf "Login %s has non-alphanumeric characters.\n", 81 $1; 82 if (length($1) > len) 83 printf "Login %s has more than "len" characters.\n", $1; 84 if ($2 == "") 85 printf "Login %s has no password.\n", $1; 86 if (length($2) != 13 && length($2) != 20 && $2 != "") { 87 if ($10 == "" || shells[$10]) 88 printf "Login %s is off but still has a valid shell (%s)\n", 89 $1, $10; 90 } else if (! shells[$10]) 91 printf "Login %s does not have a valid shell (%s)\n", 92 $1, $10; 93 if ($3 == 0 && $1 != "root" && $1 != "toor") 94 printf "Login %s has a user id of 0.\n", $1; 95 if ($3 < 0) 96 printf "Login %s has a negative user id.\n", $1; 97 if ($4 < 0) 98 printf "Login %s has a negative group id.\n", $1; 99 }' < $MP > $OUTPUT 100 if [ -s $OUTPUT ] ; then 101 printf "\nChecking the $MP file:\n" 102 cat $OUTPUT 103 fi 104 105 awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT 106 if [ -s $OUTPUT ] ; then 107 printf "\n$MP has duplicate user names.\n" 108 column $OUTPUT 109 fi 110 111# To not exclude 'toor', a standard duplicate root account, from the duplicate 112# account test, uncomment the line below (without egrep in it)and comment 113# out the line (with egrep in it) below it. 114# 115# < $MPBYUID uniq -d -f 1 | awk '{ print $2 }' > $TMP2 116 < $MPBYUID egrep -v '^toor ' | uniq -d -f 1 | awk '{ print $2 }' > $TMP2 117 if [ -s $TMP2 ] ; then 118 printf "\n$MP has duplicate user id's.\n" 119 while read uid; do 120 grep -w $uid $MPBYUID 121 done < $TMP2 | column 122 fi 123fi 124 125# Backup the master password file; a special case, the normal backup 126# mechanisms also print out file differences and we don't want to do 127# that because this file has encrypted passwords in it. 128# 129CUR=/var/backups/`basename $MP`.current 130BACK=/var/backups/`basename $MP`.backup 131if [ -s $CUR ] ; then 132 if cmp -s $CUR $MP; then 133 : 134 else 135 cp -p $CUR $BACK 136 cp -p $MP $CUR 137 chown root:wheel $CUR 138 fi 139else 140 cp -p $MP $CUR 141 chown root:wheel $CUR 142fi 143 144# Check the group file syntax. 145# 146if checkyesno check_group; then 147 GRP=/etc/group 148 awk -F: '{ 149 if ($0 ~ /^[ ]*$/) { 150 printf "Line %d is a blank line.\n", NR; 151 next; 152 } 153 if (NF != 4 && ($1 != "+" || NF != 1)) 154 printf "Line %d has the wrong number of fields.\n", NR; 155 if ($1 == "+" ) { 156 next; 157 } 158 if ($1 !~ /^[A-za-z0-9]*$/) 159 printf "Group %s has non-alphanumeric characters.\n", 160 $1; 161 if (length($1) > 8) 162 printf "Group %s has more than 8 characters.\n", $1; 163 if ($3 !~ /[0-9]*/) 164 printf "Login %s has a negative group id.\n", $1; 165 }' < $GRP > $OUTPUT 166 if [ -s $OUTPUT ] ; then 167 printf "\nChecking the $GRP file:\n" 168 cat $OUTPUT 169 fi 170 171 awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT 172 if [ -s $OUTPUT ] ; then 173 printf "\n$GRP has duplicate group names.\n" 174 column $OUTPUT 175 fi 176fi 177 178# Check for root paths, umask values in startup files. 179# The check for the root paths is problematical -- it's likely to fail 180# in other environments. Once the shells have been modified to warn 181# of '.' in the path, the path tests should go away. 182# 183if checkyesno check_rootdotfiles; then 184 > $OUTPUT 185 rhome=`csh -fc "echo ~root"` 186 umaskset=no 187 list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login" 188 for i in $list ; do 189 if [ -f $i ] ; then 190 if egrep umask $i > /dev/null ; then 191 umaskset=yes 192 fi 193 egrep umask $i | 194 awk '$2 % 100 < 20 \ 195 { print "\tRoot umask is group writeable" } 196 $2 % 10 < 2 \ 197 { print "\tRoot umask is other writeable" }' \ 198 >> $OUTPUT 199 SAVE_PATH=$PATH 200 unset PATH 201 /bin/csh -f -s << end-of-csh > /dev/null 2>&1 202 source $i 203 /bin/ls -ldgT \$path > $TMP1 204end-of-csh 205 PATH=$SAVE_PATH 206 awk '{ 207 if ($10 ~ /^\.$/) { 208 print "\tThe root path includes ."; 209 next; 210 } 211 } 212 $1 ~ /^d....w/ \ 213 { print "\tRoot path directory " $10 " is group writeable." } \ 214 $1 ~ /^d.......w/ \ 215 { print "\tRoot path directory " $10 " is other writeable." }' \ 216 < $TMP1 >> $OUTPUT 217 fi 218 done 219 if [ $umaskset = "no" -o -s $OUTPUT ] ; then 220 printf "\nChecking root csh paths, umask values:\n$list\n\n" 221 if [ -s $OUTPUT ]; then 222 cat $OUTPUT 223 fi 224 if [ $umaskset = "no" ] ; then 225 printf "\tRoot csh startup files do not set the umask.\n" 226 fi 227 fi 228 229 > $OUTPUT 230 rhome=/root 231 umaskset=no 232 list="/etc/profile ${rhome}/.profile" 233 for i in $list; do 234 if [ -f $i ] ; then 235 if egrep umask $i > /dev/null ; then 236 umaskset=yes 237 fi 238 egrep umask $i | 239 awk '$2 % 100 < 20 \ 240 { print "\tRoot umask is group writeable" } \ 241 $2 % 10 < 2 \ 242 { print "\tRoot umask is other writeable" }' \ 243 >> $OUTPUT 244 SAVE_PATH=$PATH 245 unset PATH 246 /bin/sh << end-of-sh > /dev/null 2>&1 247 . $i 248 list=\`echo \$PATH | /usr/bin/sed -e \ 249 's/^:/.:/;s/:$/:./;s/::/:.:/g;s/:/ /g'\` 250 /bin/ls -ldgT \$list > $TMP1 251end-of-sh 252 PATH=$SAVE_PATH 253 awk '{ 254 if ($10 ~ /^\.$/) { 255 print "\tThe root path includes ."; 256 next; 257 } 258 } 259 $1 ~ /^d....w/ \ 260 { print "\tRoot path directory " $10 " is group writeable." } \ 261 $1 ~ /^d.......w/ \ 262 { print "\tRoot path directory " $10 " is other writeable." }' \ 263 < $TMP1 >> $OUTPUT 264 265 fi 266 done 267 if [ $umaskset = "no" -o -s $OUTPUT ] ; then 268 printf "\nChecking root sh paths, umask values:\n$list\n" 269 if [ -s $OUTPUT ]; then 270 cat $OUTPUT 271 fi 272 if [ $umaskset = "no" ] ; then 273 printf "\tRoot sh startup files do not set the umask.\n" 274 fi 275 fi 276fi 277 278# Root and uucp should both be in /etc/ftpusers. 279# 280if checkyesno check_ftpusers; then 281 > $OUTPUT 282 list="uucp "`awk '$2 == 0 { print $1 }' $MPBYUID` 283 for i in $list; do 284 if /usr/libexec/ftpd -C $i ; then 285 printf "\t$i is not denied\n" >> $OUTPUT 286 fi 287 done 288 if [ -s $OUTPUT ]; then 289 printf "\nChecking the /etc/ftpusers configuration:\n" 290 cat $OUTPUT 291 fi 292fi 293 294# Uudecode should not be in the /etc/aliases file. 295# 296if checkyesno check_aliases; then 297 if egrep '^[^#]*(uudecode|decode).*\|' /etc/aliases; then 298 printf "\nEntry for uudecode in /etc/aliases file.\n" 299 fi 300fi 301 302# Files that should not have + signs. 303# 304if checkyesno check_rhosts; then 305 list="/etc/hosts.equiv /etc/hosts.lpd" 306 for f in $list ; do 307 if [ -f $f ] && egrep '\+' $f > /dev/null ; then 308 printf "\nPlus sign in $f file.\n" 309 fi 310 done 311 312 # Check for special users with .rhosts files. Only root and toor should 313 # have .rhosts files. Also, .rhosts files should not have plus signs. 314 awk -F: '$1 != "root" && $1 != "toor" && \ 315 ($3 < 100 || $1 == "ftp" || $1 == "uucp") \ 316 { print $1 " " $9 }' $MP | 317 sort -k2 | 318 while read uid homedir; do 319 if [ -f ${homedir}/.rhosts ] ; then 320 rhost=`ls -ldgT ${homedir}/.rhosts` 321 printf "$uid: $rhost\n" 322 fi 323 done > $OUTPUT 324 if [ -s $OUTPUT ] ; then 325 printf "\nChecking for special users with .rhosts files.\n" 326 cat $OUTPUT 327 fi 328 329 while read uid homedir; do 330 if [ -f ${homedir}/.rhosts -a -r ${homedir}/.rhosts ] && \ 331 egrep '\+' ${homedir}/.rhosts > /dev/null ; then 332 printf "$uid: + in .rhosts file.\n" 333 fi 334 done < $MPBYPATH > $OUTPUT 335 if [ -s $OUTPUT ] ; then 336 printf "\nChecking .rhosts files syntax.\n" 337 cat $OUTPUT 338 fi 339fi 340 341# Check home directories. Directories should not be owned by someone else 342# or writeable. 343# 344if checkyesno check_homes; then 345 while read uid homedir; do 346 if [ -d ${homedir}/ ] ; then 347 file=`ls -ldgT ${homedir}` 348 printf "$uid $file\n" 349 fi 350 done < $MPBYPATH | 351 awk '$1 != $4 && $4 != "root" \ 352 { print "user " $1 " home directory is owned by " $4 } 353 $2 ~ /^-....w/ \ 354 { print "user " $1 " home directory is group writeable" } 355 $2 ~ /^-.......w/ \ 356 { print "user " $1 " home directory is other writeable" }' \ 357 > $OUTPUT 358 if [ -s $OUTPUT ] ; then 359 printf "\nChecking home directories.\n" 360 cat $OUTPUT 361 fi 362 363 # Files that should not be owned by someone else or readable. 364 list=".Xauthority .netrc" 365 while read uid homedir; do 366 for f in $list ; do 367 file=${homedir}/${f} 368 if [ -f $file ] ; then 369 printf "$uid $f `ls -ldgT $file`\n" 370 fi 371 done 372 done < $MPBYPATH | 373 awk '$1 != $5 && $5 != "root" \ 374 { print "user " $1 " " $2 " file is owned by " $5 } 375 $3 ~ /^-...r/ \ 376 { print "user " $1 " " $2 " file is group readable" } 377 $3 ~ /^-......r/ \ 378 { print "user " $1 " " $2 " file is other readable" } 379 $3 ~ /^-....w/ \ 380 { print "user " $1 " " $2 " file is group writeable" } 381 $3 ~ /^-.......w/ \ 382 { print "user " $1 " " $2 " file is other writeable" }' \ 383 > $OUTPUT 384 385 # Files that should not be owned by someone else or writeable. 386 list=".bash_history .bash_login .bash_logout .bash_profile .bashrc \ 387 .cshrc .emacs .exrc .forward .history .klogin .login .logout \ 388 .profile .qmail .rc_history .rhosts .tcshrc .twmrc .xinitrc \ 389 .xsession" 390 while read uid homedir; do 391 for f in $list ; do 392 file=${homedir}/${f} 393 if [ -f $file ] ; then 394 printf "$uid $f `ls -ldgT $file`\n" 395 fi 396 done 397 done < $MPBYPATH | 398 awk '$1 != $5 && $5 != "root" \ 399 { print "user " $1 " " $2 " file is owned by " $5 } 400 $3 ~ /^-....w/ \ 401 { print "user " $1 " " $2 " file is group writeable" } 402 $3 ~ /^-.......w/ \ 403 { print "user " $1 " " $2 " file is other writeable" }' \ 404 >> $OUTPUT 405 if [ -s $OUTPUT ] ; then 406 printf "\nChecking dot files.\n" 407 cat $OUTPUT 408 fi 409fi 410 411# Mailboxes should be owned by user and unreadable. 412# 413if checkyesno check_varmail; then 414 ls -l /var/mail | sed 1d | \ 415 awk '$3 != $9 \ 416 { print "user " $9 " mailbox is owned by " $3 } 417 $1 != "-rw-------" \ 418 { print "user " $9 " mailbox is " $1 ", group " $4 }' > $OUTPUT 419 if [ -s $OUTPUT ] ; then 420 printf "\nChecking mailbox ownership.\n" 421 cat $OUTPUT 422 fi 423fi 424 425# NFS exports shouldn't be globally exported 426# 427if checkyesno check_nfs && [ -f /etc/exports ]; then 428 awk '{ 429 # ignore comments and blank lines 430 if ($0 ~ /^\#/ || $0 ~ /^$/ ) 431 next; 432 433 readonly = 0; 434 for (i = 2; i <= NF; ++i) { 435 if ($i ~ /-ro/) 436 readonly = 1; 437 else if ($i !~ /^-/) 438 next; 439 } 440 if (readonly) 441 print "File system " $1 " globally exported, read-only." 442 else 443 print "File system " $1 " globally exported, read-write." 444 }' < /etc/exports > $OUTPUT 445 if [ -s $OUTPUT ] ; then 446 printf "\nChecking for globally exported file systems.\n" 447 cat $OUTPUT 448 fi 449fi 450 451# Display any changes in setuid files and devices. 452# 453if checkyesno check_devices; then 454 > $ERR 455 (find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \ 456 -o -fstype procfs \) -a -prune -o \ 457 \( \( -perm -u+s -a ! -type d \) -o \ 458 \( -perm -g+s -a ! -type d \) -o \ 459 -type b -o -type c \) -print0 | \ 460 xargs -0 ls -ldgTq | sort +9 > $LIST) 2> $OUTPUT 461 462 # Display any errors that occurred during system file walk. 463 if [ -s $OUTPUT ] ; then 464 printf "Setuid/device find errors:\n" >> $ERR 465 cat $OUTPUT >> $ERR 466 printf "\n" >> $ERR 467 fi 468 469 # Display any changes in the setuid file list. 470 egrep -v '^[bc]' $LIST > $TMP1 471 if [ -s $TMP1 ] ; then 472 # Check to make sure uudecode isn't setuid. 473 if grep -w uudecode $TMP1 > /dev/null ; then 474 printf "\nUudecode is setuid.\n" >> $ERR 475 fi 476 477 CUR=/var/backups/setuid.current 478 BACK=/var/backups/setuid.backup 479 480 if [ -s $CUR ] ; then 481 if cmp -s $CUR $TMP1 ; then 482 : 483 else 484 > $TMP2 485 join -110 -210 -v2 $CUR $TMP1 > $OUTPUT 486 if [ -s $OUTPUT ] ; then 487 printf "Setuid additions:\n" >> $ERR 488 tee -a $TMP2 < $OUTPUT >> $ERR 489 printf "\n" >> $ERR 490 fi 491 492 join -110 -210 -v1 $CUR $TMP1 > $OUTPUT 493 if [ -s $OUTPUT ] ; then 494 printf "Setuid deletions:\n" >> $ERR 495 tee -a $TMP2 < $OUTPUT >> $ERR 496 printf "\n" >> $ERR 497 fi 498 499 sort -k10 $TMP2 $CUR $TMP1 | \ 500 sed -e 's/[ ][ ]*/ /g' | \ 501 uniq -u > $OUTPUT 502 if [ -s $OUTPUT ] ; then 503 printf "Setuid changes:\n" >> $ERR 504 column -t $OUTPUT >> $ERR 505 printf "\n" >> $ERR 506 fi 507 508 cp $CUR $BACK 509 cp $TMP1 $CUR 510 fi 511 else 512 printf "Setuid additions:\n" >> $ERR 513 column -t $TMP1 >> $ERR 514 printf "\n" >> $ERR 515 cp $TMP1 $CUR 516 fi 517 fi 518 519 # Check for block and character disk devices that are readable or 520 # writeable or not owned by root.operator. 521 >$TMP1 522 DISKLIST="acd ccd cd ch fd hk hp mcd md ra rb rd rl rx rz \ 523 sd se ss tz uk up vnd wd xd xy" 524# DISKLIST="$DISKLIST ct mt st wt" 525 for i in $DISKLIST; do 526 egrep "^b.*/${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 527 egrep "^c.*/r${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 528 done 529 530 awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \ 531 { printf "Disk %s is user %s, group %s, permissions %s.\n", \ 532 $11, $3, $4, $1; }' < $TMP1 > $OUTPUT 533 if [ -s $OUTPUT ] ; then 534 printf "\nChecking disk ownership and permissions.\n" >> $ERR 535 cat $OUTPUT >> $ERR 536 printf "\n" >> $ERR 537 fi 538 539 # Display any changes in the device file list. 540 egrep '^[bc]' $LIST | sort -k11 > $TMP1 541 if [ -s $TMP1 ] ; then 542 CUR=/var/backups/device.current 543 BACK=/var/backups/device.backup 544 545 if [ -s $CUR ] ; then 546 if cmp -s $CUR $TMP1 ; then 547 : 548 else 549 > $TMP2 550 join -111 -211 -v2 $CUR $TMP1 > $OUTPUT 551 if [ -s $OUTPUT ] ; then 552 printf "Device additions:\n" >> $ERR 553 tee -a $TMP2 < $OUTPUT >> $ERR 554 printf "\n" >> $ERR 555 fi 556 557 join -111 -211 -v1 $CUR $TMP1 > $OUTPUT 558 if [ -s $OUTPUT ] ; then 559 printf "Device deletions:\n" >> $ERR 560 tee -a $TMP2 < $OUTPUT >> $ERR 561 printf "\n" >> $ERR 562 fi 563 564 # Report any block device change. Ignore 565 # character devices, only the name is 566 # significant. 567 cat $TMP2 $CUR $TMP1 | \ 568 sed -e '/^c/d' | \ 569 sort -k11 | \ 570 sed -e 's/[ ][ ]*/ /g' | \ 571 uniq -u > $OUTPUT 572 if [ -s $OUTPUT ] ; then 573 printf "Block device changes:\n" >> $ERR 574 column -t $OUTPUT >> $ERR 575 printf "\n" >> $ERR 576 fi 577 578 cp $CUR $BACK 579 cp $TMP1 $CUR 580 fi 581 else 582 printf "Device additions:\n" >> $ERR 583 column -t $TMP1 >> $ERR 584 printf "\n" >> $ERR 585 cp $TMP1 $CUR >> $ERR 586 fi 587 fi 588 if [ -s $ERR ] ; then 589 printf "\nChecking setuid files and devices:\n" 590 cat $ERR 591 printf "\n" 592 fi 593fi 594 595# Check special files. 596# Check system binaries. 597# 598# Create the mtree tree specifications using: 599# 600# mtree -cx -pDIR -kcksum,gid,mode,nlink,size,link,time,uid > DIR.secure 601# chown root:wheel DIR.secure 602# chmod 600 DIR.secure 603# 604# Note, this is not complete protection against Trojan horsed binaries, as 605# the hacker can modify the tree specification to match the replaced binary. 606# For details on really protecting yourself against modified binaries, see 607# the mtree(8) manual page. 608# 609if checkyesno check_mtree; then 610 mtree -e -p / -f /etc/mtree/special > $OUTPUT 611 if [ -s $OUTPUT ]; then 612 printf "\nChecking special files and directories.\n" 613 cat $OUTPUT 614 fi 615 616 > $OUTPUT 617 for file in /etc/mtree/*.secure; do 618 [ $file = '/etc/mtree/*.secure' ] && continue 619 tree=`sed -n -e '3s/.* //p' -e 3q $file` 620 mtree -f $file -p $tree > $TMP1 621 if [ -s $TMP1 ]; then 622 printf "\nChecking $tree:\n" >> $OUTPUT 623 cat $TMP1 >> $OUTPUT 624 fi 625 done 626 if [ -s $OUTPUT ]; then 627 printf "\nChecking system binaries:\n" 628 cat $OUTPUT 629 fi 630fi 631 632CHANGELIST="" 633 634# Backup disklabels of available disks 635# 636if checkyesno check_disklabels; then 637 # generate list of old disklabels and remove them 638 ls -1d /var/backups/disklabel.* 2>/dev/null | 639 egrep -v '\.(backup|current)$' > $LABELS 640 xargs rm < $LABELS 641 642 disks=`iostat -x | sed 1d | awk '$1 !~ /^[mf]d/ { print $1; }'` 643 for i in $disks; do 644 dlf="/var/backups/disklabel.$i" 645 disklabel $i > $dlf 2>/dev/null 646 done 647 648 # append list of new disklabels, sort list 649 ls -1d /var/backups/disklabel.* 2>/dev/null | 650 egrep -v '\.(backup|current)$' >> $LABELS 651 sort -u -o $LABELS $LABELS 652 CHANGELIST=$LABELS 653fi 654 655# List of files that get backed up and checked for any modifications. Each 656# file is expected to have two backups, /var/backups/file.{current,backup}. 657# Any changes cause the files to rotate. 658# 659if checkyesno check_changelist && [ -s /etc/changelist ] ; then 660 CHANGELIST="/etc/changelist $CHANGELIST" 661fi 662 663if [ -n "$CHANGELIST" ]; then 664 for file in `egrep -hv "^#|$MP" $CHANGELIST`; do 665 CUR=/var/backups/`basename $file`.current 666 BACK=/var/backups/`basename $file`.backup 667 if [ -f $file ]; then 668 if [ -f $CUR ] ; then 669 diff $CUR $file > $OUTPUT 670 if [ -s $OUTPUT ] ; then 671 printf "\n======\n%s diffs (OLD < > NEW)\n======\n" $file 672 cat $OUTPUT 673 mv -f $CUR $BACK 674 cp -p $file $CUR 675 chown root:wheel $CUR 676 fi 677 else 678 printf "\n======\n%s added\n======\n" $file 679 diff /dev/null $file 680 cp -p $file $CUR 681 chown root:wheel $CUR 682 fi 683 else 684 if [ -f $CUR ]; then 685 printf "\n======\n%s removed\n======\n" $file 686 diff $CUR /dev/null 687 mv -f $CUR $BACK 688 fi 689 fi 690 done 691fi 692