security revision 1.72
1#!/bin/sh - 2# 3# $NetBSD: security,v 1.72 2001/10/18 16:08:24 lukem 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 17TZ=UTC; export TZ 18 19if [ -s /etc/security.conf ]; then 20 . /etc/security.conf 21fi 22 23# Set reasonable defaults (if they're not set in security.conf) 24# 25backup_dir=${backup_dir:-/var/backups} 26pkgdb_dir=${pkgdb_dir:-/var/db/pkg} 27max_loginlen=${max_loginlen:-8} 28max_grouplen=${max_grouplen:-8} 29 30# Other configurable variables 31# 32special_files="/etc/mtree/special /etc/mtree/special.local" 33MP=/etc/master.passwd 34CHANGELIST="" 35work_dir=$backup_dir/work 36 37if [ ! -d "$work_dir" ]; then 38 mkdir -p "$work_dir" 39fi 40 41SECUREDIR=`mktemp -d /tmp/_securedir.XXXXXX` || exit 1 42 43trap "/bin/rm -rf $SECUREDIR ; exit 0" EXIT INT QUIT PIPE 44 45if ! cd "$SECUREDIR"; then 46 echo "Can not cd to $SECUREDIR". 47 exit 1 48fi 49 50ERR=secure1.$$ 51TMP1=secure2.$$ 52TMP2=secure3.$$ 53MPBYUID=secure4.$$ 54MPBYPATH=secure5.$$ 55LIST=secure6.$$ 56OUTPUT=secure7.$$ 57LABELS=secure8.$$ 58PKGS=secure9.$$ 59CHANGEFILES=secure10.$$ 60 61 62# migrate_file old new 63# Determine if the "${old}" path name needs to be migrated to the 64# "${new}" path. Also checks if "${old}.current" needs migrating, 65# and if so, migrate it and possibly "${old}.current,v" and 66# "${old}.backup". 67# 68migrate_file() 69{ 70 _old=$1 71 _new=$2 72 if [ -z "$_old" -o -z "$_new" ]; then 73 err 3 "USAGE: migrate_file old new" 74 fi 75 if [ ! -d "${_new%/*}" ]; then 76 mkdir -p "${_new%/*}" 77 fi 78 if [ -f "${_old}" -a ! -f "${_new}" ]; then 79 echo "==> migrating ${_old}" 80 echo " to ${_new}" 81 mv "${_old}" "${_new}" 82 fi 83 if [ -f "${_old}.current" -a ! -f "${_new}.current" ]; then 84 echo "==> migrating ${_old}.current" 85 echo " to ${_new}.current" 86 mv "${_old}.current" "${_new}.current" 87 if [ -f "${_old}.current,v" -a ! -f "${_new}.current,v" ]; then 88 echo "==> migrating ${_old}.current,v" 89 echo " to ${_new}.current,v" 90 mv "${_old}.current,v" "${_new}.current,v" 91 fi 92 if [ -f "${_old}.backup" -a ! -f "${_new}.backup" ]; then 93 echo "==> migrating ${_old}.backup" 94 echo " to ${_new}.backup" 95 mv "${_old}.backup" "${_new}.backup" 96 fi 97 fi 98} 99 100 101# backup_and_diff file printdiff 102# Determine if file needs backing up, and if so, do it. 103# If printdiff is yes, display the diffs, otherwise 104# just print a message saying "[changes omitted]". 105# 106backup_and_diff() 107{ 108 _file=$1 109 _printdiff=$2 110 if [ -z "$_file" -o -z "$_printdiff" ]; then 111 err 3 "USAGE: backup_and_diff file printdiff" 112 fi 113 ! checkyesno _printdiff 114 _printdiff=$? 115 116 _old=$backup_dir/${_file##*/} 117 case "$_file" in 118 $work_dir/*) 119 _new=$_file 120 migrate_file "$backup_dir/$_old" "$_new" 121 migrate_file "$_old" "$_new" 122 ;; 123 *) 124 _new=$backup_dir/$_file 125 migrate_file "$_old" "$_new" 126 ;; 127 esac 128 CUR=${_new}.current 129 BACK=${_new}.backup 130 if [ -f $_file ]; then 131 if [ -f $CUR ] ; then 132 if [ "$_printdiff" -ne 0 ]; then 133 diff $CUR $_file > $OUTPUT 134 else 135 if ! cmp -s $CUR $_file; then 136 echo "[changes omitted]" 137 fi > $OUTPUT 138 fi 139 if [ -s $OUTPUT ] ; then 140 printf \ 141 "\n======\n%s diffs (OLD < > NEW)\n======\n" $_file 142 cat $OUTPUT 143 backup_file update $_file $CUR $BACK 144 fi 145 else 146 printf "\n======\n%s added\n======\n" $_file 147 if [ "$_printdiff" -ne 0 ]; then 148 diff /dev/null $_file 149 else 150 echo "[changes omitted]" 151 fi 152 backup_file add $_file $CUR $BACK 153 fi 154 else 155 if [ -f $CUR ]; then 156 printf "\n======\n%s removed\n======\n" $_file 157 if [ "$_printdiff" -ne 0 ]; then 158 diff $CUR /dev/null 159 else 160 echo "[changes omitted]" 161 fi 162 backup_file remove $_file $CUR $BACK 163 fi 164 fi 165} 166 167 168# These are used several times. 169# 170awk -F: '!/^+/ { print $1 " " $3 }' $MP | sort -k2n > $MPBYUID 171awk -F: '{ print $1 " " $9 }' $MP | sort -k2 > $MPBYPATH 172 173 174# Check the master password file syntax. 175# 176if checkyesno check_passwd; then 177 awk -v "len=$max_loginlen" ' 178 BEGIN { 179 while ( getline < "/etc/shells" > 0 ) { 180 if ($0 ~ /^\#/ || $0 ~ /^$/ ) 181 continue; 182 shells[$1]++; 183 } 184 FS=":"; 185 } 186 187 { 188 if ($0 ~ /^[ ]*$/) { 189 printf "Line %d is a blank line.\n", NR; 190 next; 191 } 192 if (NF != 10 && ($1 != "+" || NF != 1)) 193 printf "Line %d has the wrong number of fields.\n", NR; 194 if ($1 == "+" ) { 195 if (NF != 1 && $3 == 0) 196 printf "Line %d includes entries with uid 0.\n", NR; 197 next; 198 } 199 if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/) 200 printf "Login %s has non-alphanumeric characters.\n", 201 $1; 202 if (length($1) > len) 203 printf "Login %s has more than "len" characters.\n", $1; 204 if ($2 == "") 205 printf "Login %s has no password.\n", $1; 206 if (length($2) != 13 && 207 length($2) != 20 && 208 length($2) != 34 && 209 $2 != "" && 210 $2 !~ /^\*[A-z-]+$/ && 211 $1 != "toor") { 212 if ($10 == "" || shells[$10]) 213 printf "Login %s is off but still has a valid shell (%s)\n", 214 $1, $10; 215 } else if (! shells[$10]) 216 printf "Login %s does not have a valid shell (%s)\n", 217 $1, $10; 218 if ($3 == 0 && $1 != "root" && $1 != "toor") 219 printf "Login %s has a user id of 0.\n", $1; 220 if ($3 < 0) 221 printf "Login %s has a negative user id.\n", $1; 222 if ($4 < 0) 223 printf "Login %s has a negative group id.\n", $1; 224 }' < $MP > $OUTPUT 225 if [ -s $OUTPUT ] ; then 226 printf "\nChecking the $MP file:\n" 227 cat $OUTPUT 228 fi 229 230 awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT 231 if [ -s $OUTPUT ] ; then 232 printf "\n$MP has duplicate user names.\n" 233 column $OUTPUT 234 fi 235 236# To not exclude 'toor', a standard duplicate root account, from the duplicate 237# account test, uncomment the line below (without egrep in it)and comment 238# out the line (with egrep in it) below it. 239# 240# < $MPBYUID uniq -d -f 1 | awk '{ print $2 }' > $TMP2 241 < $MPBYUID egrep -v '^toor ' | uniq -d -f 1 | awk '{ print $2 }' > $TMP2 242 if [ -s $TMP2 ] ; then 243 printf "\n$MP has duplicate user id's.\n" 244 while read uid; do 245 grep -w $uid $MPBYUID 246 done < $TMP2 | column 247 fi 248fi 249 250# Check the group file syntax. 251# 252if checkyesno check_group; then 253 GRP=/etc/group 254 awk -F: -v "len=$max_grouplen" '{ 255 if ($0 ~ /^[ ]*$/) { 256 printf "Line %d is a blank line.\n", NR; 257 next; 258 } 259 if (NF != 4 && ($1 != "+" || NF != 1)) 260 printf "Line %d has the wrong number of fields.\n", NR; 261 if ($1 == "+" ) { 262 next; 263 } 264 if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/) 265 printf "Group %s has non-alphanumeric characters.\n", 266 $1; 267 if (length($1) > len) 268 printf "Group %s has more than "len" characters.\n", $1; 269 if ($3 !~ /[0-9]*/) 270 printf "Login %s has a negative group id.\n", $1; 271 }' < $GRP > $OUTPUT 272 if [ -s $OUTPUT ] ; then 273 printf "\nChecking the $GRP file:\n" 274 cat $OUTPUT 275 fi 276 277 awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT 278 if [ -s $OUTPUT ] ; then 279 printf "\n$GRP has duplicate group names.\n" 280 column $OUTPUT 281 fi 282fi 283 284# Check for root paths, umask values in startup files. 285# The check for the root paths is problematical -- it's likely to fail 286# in other environments. Once the shells have been modified to warn 287# of '.' in the path, the path tests should go away. 288# 289if checkyesno check_rootdotfiles; then 290 rhome=~root 291 umaskset=no 292 list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login" 293 for i in $list ; do 294 if [ -f $i ] ; then 295 if egrep '^[ \t]*umask[ \t]+[0-7]+' $i > /dev/null ; 296 then 297 umaskset=yes 298 fi 299 # Double check the umask value itself; ensure that 300 # both the group and other write bits are set. 301 # 302 egrep '^[ \t]*umask[ \t]+[0-7]+' $i | 303 awk '{ 304 if ($2 ~ /^.$/ || $2 ~! /[^2367].$/) { 305 print "\tRoot umask is group writeable" 306 } 307 if ($2 ~ /[^2367]$/) { 308 print "\tRoot umask is other writeable" 309 } 310 }' | sort -u 311 SAVE_PATH=$PATH 312 unset PATH 313 /bin/csh -f -s << end-of-csh > /dev/null 2>&1 314 source $i 315 /bin/ls -ldgT \$path > $TMP1 316end-of-csh 317 PATH=$SAVE_PATH 318 awk '{ 319 if ($10 ~ /^\.$/) { 320 print "\tThe root path includes ."; 321 next; 322 } 323 } 324 $1 ~ /^d....w/ \ 325 { print "\tRoot path directory " $10 " is group writeable." } \ 326 $1 ~ /^d.......w/ \ 327 { print "\tRoot path directory " $10 " is other writeable." }' \ 328 < $TMP1 329 fi 330 done > $OUTPUT 331 if [ $umaskset = "no" -o -s $OUTPUT ] ; then 332 printf "\nChecking root csh paths, umask values:\n$list\n\n" 333 if [ -s $OUTPUT ]; then 334 cat $OUTPUT 335 fi 336 if [ $umaskset = "no" ] ; then 337 printf "\tRoot csh startup files do not set the umask.\n" 338 fi 339 fi 340 341 umaskset=no 342 list="/etc/profile ${rhome}/.profile" 343 for i in $list; do 344 if [ -f $i ] ; then 345 if egrep umask $i > /dev/null ; then 346 umaskset=yes 347 fi 348 egrep umask $i | 349 awk '$2 ~ /^.$/ || $2 ~ /[^2367].$/ \ 350 { print "\tRoot umask is group writeable" } \ 351 $2 ~ /[^2367]$/ \ 352 { print "\tRoot umask is other writeable" }' 353 SAVE_PATH=$PATH 354 unset PATH 355 /bin/sh << end-of-sh > /dev/null 2>&1 356 . $i 357 list=\`echo \$PATH | /usr/bin/sed -e \ 358 's/^:/.:/;s/:$/:./;s/::/:.:/g;s/:/ /g'\` 359 /bin/ls -ldgT \$list > $TMP1 360end-of-sh 361 PATH=$SAVE_PATH 362 awk '{ 363 if ($10 ~ /^\.$/) { 364 print "\tThe root path includes ."; 365 next; 366 } 367 } 368 $1 ~ /^d....w/ \ 369 { print "\tRoot path directory " $10 " is group writeable." } \ 370 $1 ~ /^d.......w/ \ 371 { print "\tRoot path directory " $10 " is other writeable." }' \ 372 < $TMP1 373 374 fi 375 done > $OUTPUT 376 if [ $umaskset = "no" -o -s $OUTPUT ] ; then 377 printf "\nChecking root sh paths, umask values:\n$list\n" 378 if [ -s $OUTPUT ]; then 379 cat $OUTPUT 380 fi 381 if [ $umaskset = "no" ] ; then 382 printf "\tRoot sh startup files do not set the umask.\n" 383 fi 384 fi 385fi 386 387# Root and uucp should both be in /etc/ftpusers. 388# 389if checkyesno check_ftpusers; then 390 list="uucp "`awk '$2 == 0 { print $1 }' $MPBYUID` 391 for i in $list; do 392 if /usr/libexec/ftpd -C $i ; then 393 printf "\t$i is not denied\n" 394 fi 395 done > $OUTPUT 396 if [ -s $OUTPUT ]; then 397 printf "\nChecking the /etc/ftpusers configuration:\n" 398 cat $OUTPUT 399 fi 400fi 401 402# Uudecode should not be in the /etc/mail/aliases file. 403# 404if checkyesno check_aliases; then 405 for f in /etc/mail/aliases /etc/aliases; do 406 if [ -f $f ] && egrep '^[^#]*(uudecode|decode).*\|' $f; then 407 printf "\nEntry for uudecode in $f file.\n" 408 fi 409 done 410fi 411 412# Files that should not have + signs. 413# 414if checkyesno check_rhosts; then 415 list="/etc/hosts.equiv /etc/hosts.lpd" 416 for f in $list ; do 417 if [ -f $f ] && egrep '\+' $f > /dev/null ; then 418 printf "\nPlus sign in $f file.\n" 419 fi 420 done 421 422 # Check for special users with .rhosts files. Only root and toor should 423 # have .rhosts files. Also, .rhosts files should not have plus signs. 424 awk -F: '$1 != "root" && $1 != "toor" && \ 425 ($3 < 100 || $1 == "ftp" || $1 == "uucp") \ 426 { print $1 " " $9 }' $MP | 427 sort -k2 | 428 while read uid homedir; do 429 if [ -f ${homedir}/.rhosts ] ; then 430 rhost=`ls -ldgT ${homedir}/.rhosts` 431 printf -- "$uid: $rhost\n" 432 fi 433 done > $OUTPUT 434 if [ -s $OUTPUT ] ; then 435 printf "\nChecking for special users with .rhosts files.\n" 436 cat $OUTPUT 437 fi 438 439 while read uid homedir; do 440 if [ -f ${homedir}/.rhosts -a -r ${homedir}/.rhosts ] && \ 441 cat -f ${homedir}/.rhosts | egrep '\+' > /dev/null ; then 442 printf -- "$uid: + in .rhosts file.\n" 443 fi 444 done < $MPBYPATH > $OUTPUT 445 if [ -s $OUTPUT ] ; then 446 printf "\nChecking .rhosts files syntax.\n" 447 cat $OUTPUT 448 fi 449fi 450 451# Check home directories. Directories should not be owned by someone else 452# or writeable. 453# 454if checkyesno check_homes; then 455 while read uid homedir; do 456 if [ -d ${homedir}/ ] ; then 457 file=`ls -ldgT ${homedir}` 458 printf -- "$uid $file\n" 459 fi 460 done < $MPBYPATH | 461 awk '$1 != $4 && $4 != "root" \ 462 { print "user " $1 " home directory is owned by " $4 } 463 $2 ~ /^-....w/ \ 464 { print "user " $1 " home directory is group writeable" } 465 $2 ~ /^-.......w/ \ 466 { print "user " $1 " home directory is other writeable" }' \ 467 > $OUTPUT 468 if [ -s $OUTPUT ] ; then 469 printf "\nChecking home directories.\n" 470 cat $OUTPUT 471 fi 472 473 # Files that should not be owned by someone else or readable. 474 list=".Xauthority .netrc .ssh/id_dsa .ssh/id_rsa .ssh/identity" 475 while read uid homedir; do 476 for f in $list ; do 477 file=${homedir}/${f} 478 if [ -f $file ] ; then 479 printf -- "$uid $f `ls -ldgT $file`\n" 480 fi 481 done 482 done < $MPBYPATH | 483 awk '$1 != $5 && $5 != "root" \ 484 { print "user " $1 " " $2 " file is owned by " $5 } 485 $3 ~ /^-...r/ \ 486 { print "user " $1 " " $2 " file is group readable" } 487 $3 ~ /^-......r/ \ 488 { print "user " $1 " " $2 " file is other readable" } 489 $3 ~ /^-....w/ \ 490 { print "user " $1 " " $2 " file is group writeable" } 491 $3 ~ /^-.......w/ \ 492 { print "user " $1 " " $2 " file is other writeable" }' \ 493 > $OUTPUT 494 495 # Files that should not be owned by someone else or writeable. 496 list=".bash_history .bash_login .bash_logout .bash_profile .bashrc \ 497 .cshrc .emacs .exrc .forward .history .klogin .login .logout \ 498 .profile .qmail .rc_history .rhosts .shosts ssh .tcshrc .twmrc \ 499 .xinitrc .xsession .ssh/authorized_keys .ssh/authorized_keys2 \ 500 .ssh/config .ssh/id_dsa.pub .ssh/id_rsa.pub .ssh/identity.pub \ 501 .ssh/known_hosts .ssh/known_hosts2" 502 while read uid homedir; do 503 for f in $list ; do 504 file=${homedir}/${f} 505 if [ -f $file ] ; then 506 printf -- "$uid $f `ls -ldgT $file`\n" 507 fi 508 done 509 done < $MPBYPATH | 510 awk '$1 != $5 && $5 != "root" \ 511 { print "user " $1 " " $2 " file is owned by " $5 } 512 $3 ~ /^-....w/ \ 513 { print "user " $1 " " $2 " file is group writeable" } 514 $3 ~ /^-.......w/ \ 515 { print "user " $1 " " $2 " file is other writeable" }' \ 516 >> $OUTPUT 517 if [ -s $OUTPUT ] ; then 518 printf "\nChecking dot files.\n" 519 cat $OUTPUT 520 fi 521fi 522 523# Mailboxes should be owned by user and unreadable. 524# 525if checkyesno check_varmail; then 526 ls -l /var/mail | \ 527 awk ' NR == 1 { next; } 528 $3 != $9 { 529 print "user " $9 " mailbox is owned by " $3 530 } 531 $1 != "-rw-------" { 532 print "user " $9 " mailbox is " $1 ", group " $4 533 }' > $OUTPUT 534 if [ -s $OUTPUT ] ; then 535 printf "\nChecking mailbox ownership.\n" 536 cat $OUTPUT 537 fi 538fi 539 540# NFS exports shouldn't be globally exported 541# 542if checkyesno check_nfs && [ -f /etc/exports ]; then 543 awk '{ 544 # ignore comments and blank lines 545 if ($0 ~ /^\#/ || $0 ~ /^$/ ) 546 next; 547 548 readonly = 0; 549 for (i = 2; i <= NF; ++i) { 550 if ($i ~ /-ro/) 551 readonly = 1; 552 else if ($i !~ /^-/) 553 next; 554 } 555 if (readonly) 556 print "File system " $1 " globally exported, read-only." 557 else 558 print "File system " $1 " globally exported, read-write." 559 }' < /etc/exports > $OUTPUT 560 if [ -s $OUTPUT ] ; then 561 printf "\nChecking for globally exported file systems.\n" 562 cat $OUTPUT 563 fi 564fi 565 566# Display any changes in setuid files and devices. 567# 568if checkyesno check_devices; then 569 > $ERR 570 (find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \ 571 -o -fstype procfs \) -a -prune -o \ 572 \( \( -perm -u+s -a ! -type d \) -o \ 573 \( -perm -g+s -a ! -type d \) -o \ 574 -type b -o -type c \) -print0 | \ 575 xargs -0 ls -ldgTq | sort +9 > $LIST) 2> $OUTPUT 576 577 # Display any errors that occurred during system file walk. 578 if [ -s $OUTPUT ] ; then 579 printf "Setuid/device find errors:\n" >> $ERR 580 cat $OUTPUT >> $ERR 581 printf "\n" >> $ERR 582 fi 583 584 # Display any changes in the setuid file list. 585 egrep -v '^[bc]' $LIST > $TMP1 586 if [ -s $TMP1 ] ; then 587 # Check to make sure uudecode isn't setuid. 588 if grep -w uudecode $TMP1 > /dev/null ; then 589 printf "\nUudecode is setuid.\n" >> $ERR 590 fi 591 592 file=$work_dir/setuid 593 migrate_file "$backup_dir/setuid" "$file" 594 CUR=${file}.current 595 BACK=${file}.backup 596 if [ -s $CUR ] ; then 597 if cmp -s $CUR $TMP1 ; then 598 : 599 else 600 > $TMP2 601 join -110 -210 -v2 $CUR $TMP1 > $OUTPUT 602 if [ -s $OUTPUT ] ; then 603 printf "Setuid additions:\n" >> $ERR 604 tee -a $TMP2 < $OUTPUT >> $ERR 605 printf "\n" >> $ERR 606 fi 607 608 join -110 -210 -v1 $CUR $TMP1 > $OUTPUT 609 if [ -s $OUTPUT ] ; then 610 printf "Setuid deletions:\n" >> $ERR 611 tee -a $TMP2 < $OUTPUT >> $ERR 612 printf "\n" >> $ERR 613 fi 614 615 sort -k10 $TMP2 $CUR $TMP1 | \ 616 sed -e 's/[ ][ ]*/ /g' | \ 617 uniq -u > $OUTPUT 618 if [ -s $OUTPUT ] ; then 619 printf "Setuid changes:\n" >> $ERR 620 column -t $OUTPUT >> $ERR 621 printf "\n" >> $ERR 622 fi 623 624 backup_file update $TMP1 $CUR $BACK 625 fi 626 else 627 printf "Setuid additions:\n" >> $ERR 628 column -t $TMP1 >> $ERR 629 printf "\n" >> $ERR 630 backup_file add $TMP1 $CUR $BACK 631 fi 632 fi 633 634 # Check for block and character disk devices that are readable or 635 # writeable or not owned by root.operator. 636 >$TMP1 637 DISKLIST="ccd ch hk hp ld md ra raid rb rd rl rx \ 638 sd se ss uk up vnd wd xd xy" 639# DISKLIST="$DISKLIST ct mt st wt" 640 for i in $DISKLIST; do 641 egrep "^b.*/${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 642 egrep "^c.*/r${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 643 done 644 645 awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \ 646 { printf "Disk %s is user %s, group %s, permissions %s.\n", \ 647 $11, $3, $4, $1; }' < $TMP1 > $OUTPUT 648 if [ -s $OUTPUT ] ; then 649 printf "\nChecking disk ownership and permissions.\n" >> $ERR 650 cat $OUTPUT >> $ERR 651 printf "\n" >> $ERR 652 fi 653 654 # Display any changes in the device file list. 655 egrep '^[bc]' $LIST | sort -k11 > $TMP1 656 if [ -s $TMP1 ] ; then 657 file=$work_dir/device 658 migrate_file "$backup_dir/device" "$file" 659 CUR=${file}.current 660 BACK=${file}.backup 661 662 if [ -s $CUR ] ; then 663 if cmp -s $CUR $TMP1 ; then 664 : 665 else 666 > $TMP2 667 join -111 -211 -v2 $CUR $TMP1 > $OUTPUT 668 if [ -s $OUTPUT ] ; then 669 printf "Device additions:\n" >> $ERR 670 tee -a $TMP2 < $OUTPUT >> $ERR 671 printf "\n" >> $ERR 672 fi 673 674 join -111 -211 -v1 $CUR $TMP1 > $OUTPUT 675 if [ -s $OUTPUT ] ; then 676 printf "Device deletions:\n" >> $ERR 677 tee -a $TMP2 < $OUTPUT >> $ERR 678 printf "\n" >> $ERR 679 fi 680 681 # Report any block device change. Ignore 682 # character devices, only the name is 683 # significant. 684 cat $TMP2 $CUR $TMP1 | \ 685 sed -e '/^c/d' | \ 686 sort -k11 | \ 687 sed -e 's/[ ][ ]*/ /g' | \ 688 uniq -u > $OUTPUT 689 if [ -s $OUTPUT ] ; then 690 printf "Block device changes:\n" >> $ERR 691 column -t $OUTPUT >> $ERR 692 printf "\n" >> $ERR 693 fi 694 695 backup_file update $TMP1 $CUR $BACK 696 fi 697 else 698 printf "Device additions:\n" >> $ERR 699 column -t $TMP1 >> $ERR 700 printf "\n" >> $ERR 701 backup_file add $TMP1 $CUR $BACK >> $ERR 702 fi 703 fi 704 if [ -s $ERR ] ; then 705 printf "\nChecking setuid files and devices:\n" 706 cat $ERR 707 printf "\n" 708 fi 709fi 710 711# Check special files. 712# Check system binaries. 713# 714# Create the mtree tree specifications using: 715# mtree -cx -pDIR -kmd5,uid,gid,mode,nlink,size,link,time > DIR.secure 716# chown root:wheel DIR.secure 717# chmod u+r,go= DIR.secure 718# 719# Note, this is not complete protection against Trojan horsed binaries, as 720# the hacker can modify the tree specification to match the replaced binary. 721# For details on really protecting yourself against modified binaries, see 722# the mtree(8) manual page. 723# 724if checkyesno check_mtree; then 725 for file in $special_files; do 726 [ ! -s $file ] && continue 727 mtree -e -l -p / -f $file 728 done > $OUTPUT 729 if [ -s $OUTPUT ]; then 730 printf "\nChecking special files and directories.\n" 731 cat $OUTPUT 732 fi 733 734 for file in /etc/mtree/*.secure; do 735 [ $file = '/etc/mtree/*.secure' ] && continue 736 tree=`sed -n -e '3s/.* //p' -e 3q $file` 737 mtree -f $file -p $tree > $TMP1 738 if [ -s $TMP1 ]; then 739 printf "\nChecking $tree:\n" 740 cat $TMP1 741 fi 742 done > $OUTPUT 743 if [ -s $OUTPUT ]; then 744 printf "\nChecking system binaries:\n" 745 cat $OUTPUT 746 fi 747fi 748 749# Backup disklabels of available disks 750# 751if checkyesno check_disklabels; then 752 # migrate old disklabels 753 for file in `ls -1d $backup_dir/$backup_dir/disklabel.* \ 754 $backup_dir/disklabel.* 2>/dev/null`; do 755 migrate_file "$file" "$work_dir/${file##*/}" 756 done 757 758 # generate list of old disklabels & fdisks and remove them 759 ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null | 760 egrep -v '\.(backup|current)(,v)?$' > $LABELS 761 xargs rm < $LABELS 762 763 # generate disklabels of all disks excluding: cd fd md 764 disks=`iostat -x | awk 'NR > 1 && $1 !~ /^[cfm]d/ { print $1; }'` 765 for i in $disks; do 766 disklabel $i > "$work_dir/disklabel.$i" 2>/dev/null 767 done 768 769 # if fdisk is available, generate fdisks for: ed ld sd wd 770 if [ -x /sbin/fdisk ]; then 771 disks=`iostat -x| awk 'NR > 1 && $1 ~ /^[elsw]d/ { print $1; }'` 772 for i in $disks; do 773 /sbin/fdisk $i > "$work_dir/fdisk.$i" 2>/dev/null 774 done 775 fi 776 777 # append list of new disklabels and fdisks 778 ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null | 779 egrep -v '\.(backup|current)(,v)?$' >> $LABELS 780 CHANGELIST="$LABELS $CHANGELIST" 781fi 782 783# Check for changes in the list of installed pkgs 784# 785if checkyesno check_pkgs && [ -d $pkgdb_dir ]; then 786 pkgs=$work_dir/pkgs 787 migrate_file "$backup_dir/pkgs" "$pkgs" 788 ( cd $pkgdb_dir 789 pkg_info | sort 790 echo "" 791 find . \( -name +REQUIRED_BY -o -name +CONTENTS \) -print0 | 792 xargs -0 ls -ldgTq | sort -t. +1 | sed -e 's, \./, ,' 793 ) > $pkgs 794 echo "$pkgs" > $PKGS 795 CHANGELIST="$PKGS $CHANGELIST" 796fi 797 798# List of files that get backed up and checked for any modifications. 799# Any changes cause the files to rotate. 800# 801if checkyesno check_changelist ; then 802 for file in $special_files; do 803 [ ! -s $file ] && continue 804 mtree -D -k type -f $file -E exclude | 805 sed '/^type=file/!d ; s/type=file \.//' 806 done > $CHANGEFILES 807 808 # Add other files which might dynamically exist: 809 # /etc/ifconfig.* 810 # /etc/raid*.conf 811 # /etc/rc.d/* 812 # /etc/rc.conf.d/* 813 # 814 ls -1d $backup_dir/etc/ifconfig.*.current \ 815 $backup_dir/etc/raid*.conf.current \ 816 $backup_dir/etc/rc.d/*.current \ 817 $backup_dir/etc/rc.conf.d/*.current \ 818 2>/dev/null | 819 sed "s,^$backup_dir/,/, ; s,\.current$,," >> $CHANGEFILES 820 ls -1d /etc/ifconfig.* \ 821 /etc/raid*.conf \ 822 /etc/rc.d/* \ 823 /etc/rc.conf.d/* \ 824 2>/dev/null >> $CHANGEFILES 825 826 # Add /etc/changelist 827 # 828 if [ -s /etc/changelist ]; then 829 grep -v '^#' /etc/changelist >> $CHANGEFILES 830 fi 831 832 CHANGELIST="$CHANGEFILES $CHANGELIST" 833fi 834 835# Special case backups, including the master password file and 836# ssh private host keys. The normal backup mechanisms for 837# $check_changelist (see below) also print out the actual file 838# differences and we don't want to do that for these files 839# 840echo $MP > $TMP1 # always add /etc/master.passwd 841for file in $special_files; do 842 [ ! -s $file ] && continue 843 mtree -D -k type -f $file -I nodiff | 844 sed '/^type=file/!d ; s/type=file \.//' 845done >> $TMP1 846sort -u $TMP1 > $TMP2 847 848while read file; do 849 backup_and_diff "$file" no 850done < $TMP2 851 852 853if [ -n "$CHANGELIST" ]; then 854 cat $CHANGELIST | sort -u > $TMP1 855 comm -23 $TMP1 $TMP2 | while read file; do 856 backup_and_diff "$file" yes 857 done 858fi 859 860if [ -f /etc/security.local ]; then 861 echo "" 862 echo "Running /etc/security.local:" 863 . /etc/security.local 864fi 865