1#!/bin/sh - 2# 3# $NetBSD: security,v 1.75 2002/05/21 13:50:46 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 null \ 572 -o -fstype procfs \) -a -prune -o \ 573 \( \( -perm -u+s -a ! -type d \) -o \ 574 \( -perm -g+s -a ! -type d \) -o \ 575 -type b -o -type c \) -print0 | \ 576 xargs -0 ls -ldgTq | sort +9 > $LIST) 2> $OUTPUT 577 578 # Display any errors that occurred during system file walk. 579 if [ -s $OUTPUT ] ; then 580 printf "Setuid/device find errors:\n" >> $ERR 581 cat $OUTPUT >> $ERR 582 printf "\n" >> $ERR 583 fi 584 585 # Display any changes in the setuid file list. 586 egrep -v '^[bc]' $LIST > $TMP1 587 if [ -s $TMP1 ] ; then 588 # Check to make sure uudecode isn't setuid. 589 if grep -w uudecode $TMP1 > /dev/null ; then 590 printf "\nUudecode is setuid.\n" >> $ERR 591 fi 592 593 file=$work_dir/setuid 594 migrate_file "$backup_dir/setuid" "$file" 595 CUR=${file}.current 596 BACK=${file}.backup 597 if [ -s $CUR ] ; then 598 if cmp -s $CUR $TMP1 ; then 599 : 600 else 601 > $TMP2 602 join -110 -210 -v2 $CUR $TMP1 > $OUTPUT 603 if [ -s $OUTPUT ] ; then 604 printf "Setuid additions:\n" >> $ERR 605 tee -a $TMP2 < $OUTPUT >> $ERR 606 printf "\n" >> $ERR 607 fi 608 609 join -110 -210 -v1 $CUR $TMP1 > $OUTPUT 610 if [ -s $OUTPUT ] ; then 611 printf "Setuid deletions:\n" >> $ERR 612 tee -a $TMP2 < $OUTPUT >> $ERR 613 printf "\n" >> $ERR 614 fi 615 616 sort -k10 $TMP2 $CUR $TMP1 | \ 617 sed -e 's/[ ][ ]*/ /g' | \ 618 uniq -u > $OUTPUT 619 if [ -s $OUTPUT ] ; then 620 printf "Setuid changes:\n" >> $ERR 621 column -t $OUTPUT >> $ERR 622 printf "\n" >> $ERR 623 fi 624 625 backup_file update $TMP1 $CUR $BACK 626 fi 627 else 628 printf "Setuid additions:\n" >> $ERR 629 column -t $TMP1 >> $ERR 630 printf "\n" >> $ERR 631 backup_file add $TMP1 $CUR $BACK 632 fi 633 fi 634 635 # Check for block and character disk devices that are readable or 636 # writeable or not owned by root.operator. 637 >$TMP1 638 DISKLIST="ccd ch hk hp ld md ra raid rb rd rl rx \ 639 sd se ss uk up vnd wd xd xy" 640# DISKLIST="$DISKLIST ct mt st wt" 641 for i in $DISKLIST; do 642 egrep "^b.*/${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 643 egrep "^c.*/r${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1 644 done 645 646 awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \ 647 { printf "Disk %s is user %s, group %s, permissions %s.\n", \ 648 $11, $3, $4, $1; }' < $TMP1 > $OUTPUT 649 if [ -s $OUTPUT ] ; then 650 printf "\nChecking disk ownership and permissions.\n" >> $ERR 651 cat $OUTPUT >> $ERR 652 printf "\n" >> $ERR 653 fi 654 655 # Display any changes in the device file list. 656 egrep '^[bc]' $LIST | sort -k11 > $TMP1 657 if [ -s $TMP1 ] ; then 658 file=$work_dir/device 659 migrate_file "$backup_dir/device" "$file" 660 CUR=${file}.current 661 BACK=${file}.backup 662 663 if [ -s $CUR ] ; then 664 if cmp -s $CUR $TMP1 ; then 665 : 666 else 667 > $TMP2 668 join -111 -211 -v2 $CUR $TMP1 > $OUTPUT 669 if [ -s $OUTPUT ] ; then 670 printf "Device additions:\n" >> $ERR 671 tee -a $TMP2 < $OUTPUT >> $ERR 672 printf "\n" >> $ERR 673 fi 674 675 join -111 -211 -v1 $CUR $TMP1 > $OUTPUT 676 if [ -s $OUTPUT ] ; then 677 printf "Device deletions:\n" >> $ERR 678 tee -a $TMP2 < $OUTPUT >> $ERR 679 printf "\n" >> $ERR 680 fi 681 682 # Report any block device change. Ignore 683 # character devices, only the name is 684 # significant. 685 cat $TMP2 $CUR $TMP1 | \ 686 sed -e '/^c/d' | \ 687 sort -k11 | \ 688 sed -e 's/[ ][ ]*/ /g' | \ 689 uniq -u > $OUTPUT 690 if [ -s $OUTPUT ] ; then 691 printf "Block device changes:\n" >> $ERR 692 column -t $OUTPUT >> $ERR 693 printf "\n" >> $ERR 694 fi 695 696 backup_file update $TMP1 $CUR $BACK 697 fi 698 else 699 printf "Device additions:\n" >> $ERR 700 column -t $TMP1 >> $ERR 701 printf "\n" >> $ERR 702 backup_file add $TMP1 $CUR $BACK >> $ERR 703 fi 704 fi 705 if [ -s $ERR ] ; then 706 printf "\nChecking setuid files and devices:\n" 707 cat $ERR 708 printf "\n" 709 fi 710fi 711 712# Check special files. 713# Check system binaries. 714# 715# Create the mtree tree specifications using: 716# mtree -cx -pDIR -kmd5,uid,gid,mode,nlink,size,link,time > DIR.secure 717# chown root:wheel DIR.secure 718# chmod u+r,go= DIR.secure 719# 720# Note, this is not complete protection against Trojan horsed binaries, as 721# the hacker can modify the tree specification to match the replaced binary. 722# For details on really protecting yourself against modified binaries, see 723# the mtree(8) manual page. 724# 725if checkyesno check_mtree; then 726 for file in $special_files; do 727 [ ! -s $file ] && continue 728 mtree -e -l -p / -f $file 729 done > $OUTPUT 730 if [ -s $OUTPUT ]; then 731 printf "\nChecking special files and directories.\n" 732 cat $OUTPUT 733 fi 734 735 for file in /etc/mtree/*.secure; do 736 [ $file = '/etc/mtree/*.secure' ] && continue 737 tree=`sed -n -e '3s/.* //p' -e 3q $file` 738 mtree -f $file -p $tree > $TMP1 739 if [ -s $TMP1 ]; then 740 printf "\nChecking $tree:\n" 741 cat $TMP1 742 fi 743 done > $OUTPUT 744 if [ -s $OUTPUT ]; then 745 printf "\nChecking system binaries:\n" 746 cat $OUTPUT 747 fi 748fi 749 750# Backup disklabels of available disks 751# 752if checkyesno check_disklabels; then 753 # migrate old disklabels 754 for file in `ls -1d $backup_dir/$backup_dir/disklabel.* \ 755 $backup_dir/disklabel.* 2>/dev/null`; do 756 migrate_file "$file" "$work_dir/${file##*/}" 757 done 758 759 # generate list of old disklabels & fdisks and remove them 760 ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null | 761 egrep -v '\.(backup|current)(,v)?$' > $LABELS 762 xargs rm < $LABELS 763 764 # generate disklabels of all disks excluding: cd fd md 765 disks=`iostat -x | awk 'NR > 1 && $1 !~ /^[cfm]d/ { print $1; }'` 766 for i in $disks; do 767 disklabel $i > "$work_dir/disklabel.$i" 2>/dev/null 768 done 769 770 # if fdisk is available, generate fdisks for: ed ld sd wd 771 if [ -x /sbin/fdisk ]; then 772 disks=`iostat -x| awk 'NR > 1 && $1 ~ /^[elsw]d/ { print $1; }'` 773 for i in $disks; do 774 /sbin/fdisk $i > "$work_dir/fdisk.$i" 2>/dev/null 775 done 776 fi 777 778 # append list of new disklabels and fdisks 779 ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null | 780 egrep -v '\.(backup|current)(,v)?$' >> $LABELS 781 CHANGELIST="$LABELS $CHANGELIST" 782fi 783 784# Check for changes in the list of installed pkgs 785# 786if checkyesno check_pkgs && [ -d $pkgdb_dir ]; then 787 pkgs=$work_dir/pkgs 788 migrate_file "$backup_dir/pkgs" "$pkgs" 789 ( cd $pkgdb_dir 790 pkg_info | sort 791 echo "" 792 find . \( -name +REQUIRED_BY -o -name +CONTENTS \) -print0 | 793 xargs -0 ls -ldgTq | sort -t. +1 | sed -e 's, \./, ,' 794 ) > $pkgs 795 echo "$pkgs" > $PKGS 796 CHANGELIST="$PKGS $CHANGELIST" 797fi 798 799# List of files that get backed up and checked for any modifications. 800# Any changes cause the files to rotate. 801# 802if checkyesno check_changelist ; then 803 for file in $special_files; do 804 [ ! -s $file ] && continue 805 mtree -D -k type -f $file -E exclude | 806 sed '/^type=file/!d ; s/type=file \.//' 807 done > $CHANGEFILES 808 809 ( 810 # Add other files which might dynamically exist: 811 # /etc/ifconfig.* 812 # /etc/raid*.conf 813 # /etc/rc.d/* 814 # /etc/rc.conf.d/* 815 # 816 echo "/etc/ifconfig.*" 817 echo "/etc/raid*.conf" 818 echo "/etc/rc.d/*" 819 echo "/etc/rc.conf.d/*" 820 821 # Add /etc/changelist 822 # 823 if [ -s /etc/changelist ]; then 824 grep -v '^#' /etc/changelist 825 fi 826 ) | while read file; do 827 case "$file" in 828 *[\*\?\[]*) # If changelist line is a glob ... 829 # ... expand possible backup files 830 # 831 ls -1d $(echo $backup_dir/${file}.current) 2>/dev/null \ 832 | sed "s,^$backup_dir/,, ; s,\.current$,," 833 834 # ... expand possible files 835 # 836 ls -1d $(echo $file) 2>/dev/null 837 ;; 838 *) 839 # Otherwise, just print the filename 840 echo $file 841 ;; 842 esac 843 done >> $CHANGEFILES 844 CHANGELIST="$CHANGEFILES $CHANGELIST" 845fi 846 847# Special case backups, including the master password file and 848# ssh private host keys. The normal backup mechanisms for 849# $check_changelist (see below) also print out the actual file 850# differences and we don't want to do that for these files 851# 852echo $MP > $TMP1 # always add /etc/master.passwd 853for file in $special_files; do 854 [ ! -s $file ] && continue 855 mtree -D -k type -f $file -I nodiff | 856 sed '/^type=file/!d ; s/type=file \.//' 857done >> $TMP1 858grep -v '^$' $TMP1 | sort -u > $TMP2 859 860while read file; do 861 backup_and_diff "$file" no 862done < $TMP2 863 864 865if [ -n "$CHANGELIST" ]; then 866 grep -h -v '^$' $CHANGELIST | sort -u > $TMP1 867 comm -23 $TMP1 $TMP2 | while read file; do 868 backup_and_diff "$file" yes 869 done 870fi 871 872if [ -f /etc/security.local ]; then 873 echo "" 874 echo "Running /etc/security.local:" 875 . /etc/security.local 876fi 877