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