1#!/bin/sh - 2# 3# @(#)security 5.28 (Berkeley) 05/08/92 4# 5 6PATH=/sbin:/usr/sbin:/bin:/usr/bin 7 8umask 077 9 10ERR=/tmp/_secure1.$$ 11TMP1=/tmp/_secure2.$$ 12TMP2=/tmp/_secure3.$$ 13TMP3=/tmp/_secure4.$$ 14LIST=/tmp/_secure5.$$ 15OUTPUT=/tmp/_secure6.$$ 16 17trap 'rm -f $ERR $TMP1 $TMP2 $TMP3 $LIST $OUTPUT' 0 18 19# Check the master password file syntax. 20MP=/etc/master.passwd 21awk -F: '{ 22 if ($0 ~ /^[ ]*$/) { 23 printf("Line %d is a blank line.\n", NR); 24 next; 25 } 26 if (NF != 10) 27 printf("Line %d has the wrong number of fields.\n", NR); 28 if ($1 !~ /^[A-Za-z0-9]*$/) 29 printf("Login %s has non-alphanumeric characters.\n", $1); 30 if (length($1) > 8) 31 printf("Login %s has more than 8 characters.\n", $1); 32 if ($2 == "") 33 printf("Login %s has no password.\n", $1); 34 if (length($2) != 13 && ($10 ~ /.*sh$/ || $10 == "")) 35 printf("Login %s is off but still has a valid shell.\n", $1); 36 if ($3 == 0 && $1 != "root" && $1 != "toor") 37 printf("Login %s has a user id of 0.\n", $1); 38 if ($3 < 0) 39 printf("Login %s has a negative user id.\n", $1); 40 if ($4 < 0) 41 printf("Login %s has a negative group id.\n", $1); 42}' < $MP > $OUTPUT 43if [ -s $OUTPUT ] ; then 44 printf "\nChecking the $MP file:\n" 45 cat $OUTPUT 46fi 47 48awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT 49if [ -s $OUTPUT ] ; then 50 printf "\n$MP has duplicate user names.\n" 51 column $OUTPUT 52fi 53 54awk -F: '{ print $1 " " $3 }' $MP | sort -n +1 | tee $TMP1 | 55uniq -d -f 1 | awk '{ print $2 }' > $TMP2 56if [ -s $TMP2 ] ; then 57 printf "\n$MP has duplicate user id's.\n" 58 while read uid; do 59 grep -w $uid $TMP1 60 done < $TMP2 | column 61fi 62 63# Backup the master password file; a special case, the normal backup 64# mechanisms also print out file differences and we don't want to do 65# that because this file has encrypted passwords in it. 66CUR=/var/backups/`basename $MP`.current 67BACK=/var/backups/`basename $MP`.backup 68if [ -s $CUR ] ; then 69 if cmp -s $CUR $MP; then 70 : 71 else 72 cp -p $CUR $BACK 73 cp -p $MP $CUR 74 chown root.wheel $CUR 75 fi 76else 77 cp -p $MP $CUR 78 chown root.wheel $CUR 79fi 80 81# Check the group file syntax. 82GRP=/etc/group 83awk -F: '{ 84 if ($0 ~ /^[ ]*$/) { 85 printf("Line %d is a blank line.\n", NR); 86 next; 87 } 88 if (NF != 4) 89 printf("Line %d has the wrong number of fields.\n", NR); 90 if ($1 !~ /^[A-za-z0-9]*$/) 91 printf("Group %s has non-alphanumeric characters.\n", $1); 92 if (length($1) > 8) 93 printf("Group %s has more than 8 characters.\n", $1); 94 if ($3 !~ /[0-9]*/) 95 printf("Login %s has a negative group id.\n", $1); 96}' < $GRP > $OUTPUT 97if [ -s $OUTPUT ] ; then 98 printf "\nChecking the $GRP file:\n" 99 cat $OUTPUT 100fi 101 102awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT 103if [ -s $OUTPUT ] ; then 104 printf "\n$GRP has duplicate group names.\n" 105 column $OUTPUT 106fi 107 108# Check for root paths, umask values in startup files. 109# The check for the root paths is problematical -- it's likely to fail 110# in other environments. Once the shells have been modified to warn 111# of '.' in the path, the path tests should go away. 112> $OUTPUT 113rhome=/root 114umaskset=no 115list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login" 116for i in $list ; do 117 if [ -f $i ] ; then 118 if egrep umask $i > /dev/null ; then 119 umaskset=yes 120 fi 121 egrep umask $i | 122 awk '$2 % 100 < 20 \ 123 { print "Root umask is group writeable" } 124 $2 % 10 < 2 \ 125 { print "Root umask is other writeable" }' >> $OUTPUT 126 /bin/csh -f -s << end-of-csh > /dev/null 2>&1 127 unset path 128 source $i 129 /bin/ls -ldgT \$path > $TMP1 130end-of-csh 131 awk '{ 132 if ($10 ~ /^\.$/) { 133 print "The root path includes ."; 134 next; 135 } 136 } 137 $1 ~ /^d....w/ \ 138 { print "Root path directory " $10 " is group writeable." } \ 139 $1 ~ /^d.......w/ \ 140 { print "Root path directory " $10 " is other writeable." }' \ 141 < $TMP1 >> $OUTPUT 142 fi 143done 144if [ $umaskset = "no" -o -s $OUTPUT ] ; then 145 printf "\nChecking root csh paths, umask values:\n$list\n" 146 if [ -s $OUTPUT ]; then 147 cat $OUTPUT 148 fi 149 if [ $umaskset = "no" ] ; then 150 printf "\nRoot csh startup files do not set the umask.\n" 151 fi 152fi 153 154> $OUTPUT 155rhome=/root 156umaskset=no 157list="${rhome}/.profile" 158for i in $list; do 159 if [ -f $i ] ; then 160 if egrep umask $i > /dev/null ; then 161 umaskset=yes 162 fi 163 egrep umask $i | 164 awk '$2 % 100 < 20 \ 165 { print "Root umask is group writeable" } \ 166 $2 % 10 < 2 \ 167 { print "Root umask is other writeable" }' >> $OUTPUT 168 /bin/sh << end-of-sh > /dev/null 2>&1 169 PATH= 170 . $i 171 list=\`echo \$PATH | /usr/bin/sed -e 's/:/ /g'\` 172 /bin/ls -ldgT \$list > $TMP1 173end-of-sh 174 awk '{ 175 if ($10 ~ /^\.$/) { 176 print "The root path includes ."; 177 next; 178 } 179 } 180 $1 ~ /^d....w/ \ 181 { print "Root path directory " $10 " is group writeable." } \ 182 $1 ~ /^d.......w/ \ 183 { print "Root path directory " $10 " is other writeable." }' \ 184 < $TMP1 >> $OUTPUT 185 186 fi 187done 188if [ $umaskset = "no" -o -s $OUTPUT ] ; then 189 printf "\nChecking root sh paths, umask values:\n$list\n" 190 if [ -s $OUTPUT ]; then 191 cat $OUTPUT 192 fi 193 if [ $umaskset = "no" ] ; then 194 printf "\nRoot sh startup files do not set the umask.\n" 195 fi 196fi 197 198# Root and uucp should both be in /etc/ftpusers. 199if egrep root /etc/ftpusers > /dev/null ; then 200 : 201else 202 printf "\nRoot not listed in /etc/ftpusers file.\n" 203fi 204if egrep uucp /etc/ftpusers > /dev/null ; then 205 : 206else 207 printf "\nUucp not listed in /etc/ftpusers file.\n" 208fi 209 210# Uudecode should not be in the /etc/aliases file. 211if egrep 'uudecode|decode' /etc/aliases; then 212 printf "\nThere is an entry for uudecode in the /etc/aliases file.\n" 213fi 214 215# Files that should not have + signs. 216list="/etc/hosts.equiv /etc/hosts.lpd" 217for f in $list ; do 218 if egrep '\+' $f > /dev/null ; then 219 printf "\nPlus sign in $f file.\n" 220 fi 221done 222 223# Check for special users with .rhosts files. Only root and toor should 224# have a .rhosts files. Also, .rhosts files should not plus signs. 225awk -F: '$1 != "root" && $1 != "toor" && \ 226 ($3 < 100 || $1 == "ftp" || $1 == "uucp") \ 227 { print $1 " " $6 }' /etc/passwd | 228while read uid homedir; do 229 if [ -f ${homedir}/.rhosts ] ; then 230 rhost=`ls -ldgT ${homedir}/.rhosts` 231 printf "$uid: $rhost\n" 232 fi 233done > $OUTPUT 234if [ -s $OUTPUT ] ; then 235 printf "\nChecking for special users with .rhosts files.\n" 236 cat $OUTPUT 237fi 238 239awk -F: '{ print $1 " " $6 }' /etc/passwd | \ 240while read uid homedir; do 241 if [ -f ${homedir}/.rhosts ] && \ 242 egrep '\+' ${homedir}/.rhosts > /dev/null ; then 243 printf "$uid: + in .rhosts file.\n" 244 fi 245done > $OUTPUT 246if [ -s $OUTPUT ] ; then 247 printf "\nChecking .rhosts files syntax.\n" 248 cat $OUTPUT 249fi 250 251# Check home directories. Directories should not be owned by someone else 252# or writeable. 253awk -F: '{ print $1 " " $6 }' /etc/passwd | \ 254while read uid homedir; do 255 if [ -d ${homedir}/ ] ; then 256 file=`ls -ldgT ${homedir}` 257 printf "$uid $file\n" 258 fi 259done | 260awk '$1 != $4 && $4 != "root" \ 261 { print "user " $1 " home directory is owned by " $4 } 262 $2 ~ /^-....w/ \ 263 { print "user " $1 " home directory is group writeable" } 264 $2 ~ /^-.......w/ \ 265 { print "user " $1 " home directory is other writeable" }' > $OUTPUT 266if [ -s $OUTPUT ] ; then 267 printf "\nChecking home directories.\n" 268 cat $OUTPUT 269fi 270 271# Files that should not be owned by someone else or readable. 272list=".netrc .rhosts" 273awk -F: '{ print $1 " " $6 }' /etc/passwd | \ 274while read uid homedir; do 275 for f in $list ; do 276 file=${homedir}/${f} 277 if [ -f $file ] ; then 278 printf "$uid $f `ls -ldgT $file`\n" 279 fi 280 done 281done | 282awk '$1 != $5 && $5 != "root" \ 283 { print "user " $1 " " $2 " file is owned by " $5 } 284 $3 ~ /^-...r/ \ 285 { print "user " $1 " " $2 " file is group readable" } 286 $3 ~ /^-......r/ \ 287 { print "user " $1 " " $2 " file is other readable" } 288 $3 ~ /^-....w/ \ 289 { print "user " $1 " " $2 " file is group writeable" } 290 $3 ~ /^-.......w/ \ 291 { print "user " $1 " " $2 " file is other writeable" }' > $OUTPUT 292 293# Files that should not be owned by someone else or writeable. 294list=".bashrc .cshrc .emacsrc .exrc .forward .klogin .login .logout \ 295 .profile .tcshrc" 296awk -F: '{ print $1 " " $6 }' /etc/passwd | \ 297while read uid homedir; do 298 for f in $list ; do 299 file=${homedir}/${f} 300 if [ -f $file ] ; then 301 printf "$uid $f `ls -ldgT $file`\n" 302 fi 303 done 304done | 305awk '$1 != $5 && $5 != "root" \ 306 { print "user " $1 " " $2 " file is owned by " $5 } 307 $3 ~ /^-....w/ \ 308 { print "user " $1 " " $2 " file is group writeable" } 309 $3 ~ /^-.......w/ \ 310 { print "user " $1 " " $2 " file is other writeable" }' >> $OUTPUT 311if [ -s $OUTPUT ] ; then 312 printf "\nChecking dot files.\n" 313 cat $OUTPUT 314fi 315 316# Mailboxes should be owned by user and unreadable. 317ls -l /var/mail | sed 1d | \ 318awk '$3 != $9 \ 319 { print "user " $9 " mailbox is owned by " $3 } 320 $1 != "-rw-------" \ 321 { print "user " $9 " mailbox is " $1 ", group " $4 }' > $OUTPUT 322if [ -s $OUTPUT ] ; then 323 printf "\nChecking mailbox ownership.\n" 324 cat $OUTPUT 325fi 326 327# File systems should not be globally exported. 328awk '{ 329 readonly = 0; 330 for (i = 2; i <= NF; ++i) { 331 if ($i ~ /-ro/) 332 readonly = 1; 333 else if ($i !~ /^-/) 334 next; 335 } 336 if (readonly) 337 print "File system " $1 " globally exported, read-only." 338 else 339 print "File system " $1 " globally exported, read-write." 340}' < /etc/exports > $OUTPUT 341if [ -s $OUTPUT ] ; then 342 printf "\nChecking for globally exported file systems.\n" 343 cat $OUTPUT 344fi 345 346# Display any changes in setuid files and devices. 347printf "\nChecking setuid files and devices:\n" 348(find / ! -fstype local -a -prune -o \ 349 \( -perm -u+s -o -perm -g+s -o ! -type d -a ! -type f -a ! -type l -a \ 350 ! -type s \) | \ 351sort | sed -e 's/^/ls -ldgT /' | sh > $LIST) 2> $OUTPUT 352 353# Display any errors that occurred during system file walk. 354if [ -s $OUTPUT ] ; then 355 printf "Setuid/device find errors:\n" 356 cat $OUTPUT 357 printf "\n" 358fi 359 360# Display any changes in the setuid file list. 361egrep -v '^[bc]' $LIST > $TMP1 362if [ -s $TMP1 ] ; then 363 # Check to make sure uudecode isn't setuid. 364 if grep -w uudecode $TMP1 > /dev/null ; then 365 printf "\nUudecode is setuid.\n" 366 fi 367 368 CUR=/var/backups/setuid.current 369 BACK=/var/backups/setuid.backup 370 371 if [ -s $CUR ] ; then 372 if cmp -s $CUR $TMP1 ; then 373 : 374 else 375 > $TMP2 376 join -110 -210 -v2 $CUR $TMP1 > $OUTPUT 377 if [ -s $OUTPUT ] ; then 378 printf "Setuid additions:\n" 379 tee -a $TMP2 < $OUTPUT 380 printf "\n" 381 fi 382 383 join -110 -210 -v1 $CUR $TMP1 > $OUTPUT 384 if [ -s $OUTPUT ] ; then 385 printf "Setuid deletions:\n" 386 tee -a $TMP2 < $OUTPUT 387 printf "\n" 388 fi 389 390 sort +9 $TMP2 $CUR $TMP1 | \ 391 sed -e 's/[ ][ ]*/ /g' | uniq -u > $OUTPUT 392 if [ -s $OUTPUT ] ; then 393 printf "Setuid changes:\n" 394 column -t $OUTPUT 395 printf "\n" 396 fi 397 398 cp $CUR $BACK 399 cp $TMP1 $CUR 400 fi 401 else 402 printf "Setuid additions:\n" 403 column -t $TMP1 404 printf "\n" 405 cp $TMP1 $CUR 406 fi 407fi 408 409# Check for block and character disk devices that are readable or writeable 410# or not owned by root.operator. 411>$TMP1 412DISKLIST="dk fd hd hk hp jb kra ra rb rd rl rx rz sd up wd" 413for i in $DISKLIST; do 414 egrep "^b.*/${i}[0-9][0-9]*[a-h]$" $LIST >> $TMP1 415 egrep "^c.*/r${i}[0-9][0-9]*[a-h]$" $LIST >> $TMP1 416done 417 418awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \ 419 { printf("Disk %s is user %s, group %s, permissions %s.\n", \ 420 $11, $3, $4, $1); }' < $TMP1 > $OUTPUT 421if [ -s $OUTPUT ] ; then 422 printf "\nChecking disk ownership and permissions.\n" 423 cat $OUTPUT 424 printf "\n" 425fi 426 427# Display any changes in the device file list. 428egrep '^[bc]' $LIST | sort +10 > $TMP1 429if [ -s $TMP1 ] ; then 430 CUR=/var/backups/device.current 431 BACK=/var/backups/device.backup 432 433 if [ -s $CUR ] ; then 434 if cmp -s $CUR $TMP1 ; then 435 : 436 else 437 > $TMP2 438 join -111 -211 -v2 $CUR $TMP1 > $OUTPUT 439 if [ -s $OUTPUT ] ; then 440 printf "Device additions:\n" 441 tee -a $TMP2 < $OUTPUT 442 printf "\n" 443 fi 444 445 join -111 -211 -v1 $CUR $TMP1 > $OUTPUT 446 if [ -s $OUTPUT ] ; then 447 printf "Device deletions:\n" 448 tee -a $TMP2 < $OUTPUT 449 printf "\n" 450 fi 451 452 # Report any block device change. Ignore character 453 # devices, only the name is significant. 454 cat $TMP2 $CUR $TMP1 | \ 455 sed -e '/^c/d' | \ 456 sort +10 | \ 457 sed -e 's/[ ][ ]*/ /g' | \ 458 uniq -u > $OUTPUT 459 if [ -s $OUTPUT ] ; then 460 printf "Block device changes:\n" 461 column -t $OUTPUT 462 printf "\n" 463 fi 464 465 cp $CUR $BACK 466 cp $TMP1 $CUR 467 fi 468 else 469 printf "Device additions:\n" 470 column -t $TMP1 471 printf "\n" 472 cp $TMP1 $CUR 473 fi 474fi 475 476# Check special files. 477# Check system binaries. 478# 479# Create the mtree tree specifications using: 480# 481# mtree -cx -pDIR -kcksum,gid,mode,nlink,size,link,time,uid > DIR.secure 482# chown root.wheel DIR.SECURE 483# chmod 600 DIR.SECURE 484# 485# Note, this is not complete protection against Trojan horsed binaries, as 486# the hacker can modify the tree specification to match the replaced binary. 487# For details on really protecting yourself against modified binaries, see 488# the mtree(8) manual page. 489if cd /etc/mtree; then 490 mtree -e -p / -f /etc/mtree/special > $OUTPUT 491 if [ -s $OUTPUT ] ; then 492 printf "\nChecking special files and directories.\n" 493 cat $OUTPUT 494 fi 495 496 > $OUTPUT 497 for file in *.secure; do 498 tree=`sed -n -e '3s/.* //p' -e 3q $file` 499 mtree -f $file -p $tree > $TMP1 500 if [ -s $TMP1 ]; then 501 printf "\nChecking $tree:\n" >> $OUTPUT 502 cat $TMP1 >> $OUTPUT 503 fi 504 done 505 if [ -s $OUTPUT ] ; then 506 printf "\nChecking system binaries:\n" 507 cat $OUTPUT 508 fi 509fi 510 511# List of files that get backed up and checked for any modifications. Each 512# file is expected to have two backups, /var/backups/file.{current,backup}. 513# Any changes cause the files to rotate. 514if [ -s /etc/changelist ] ; then 515 for file in `cat /etc/changelist`; do 516 CUR=/var/backups/`basename $file`.current 517 BACK=/var/backups/`basename $file`.backup 518 if [ -s $file ]; then 519 if [ -s $CUR ] ; then 520 diff $CUR $file > $OUTPUT 521 if [ -s $OUTPUT ] ; then 522 printf "\n======\n%s diffs (OLD < > NEW)\n======\n" $file 523 cat $OUTPUT 524 cp -p $CUR $BACK 525 cp -p $file $CUR 526 chown root.wheel $CUR $BACK 527 fi 528 else 529 cp -p $file $CUR 530 chown root.wheel $CUR 531 fi 532 fi 533 done 534fi 535