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