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