Home | History | Annotate | Line # | Download | only in etc
security revision 1.14
      1 #!/bin/sh -
      2 #
      3 #	$NetBSD: security,v 1.14 1996/05/22 00:51:08 mrg Exp $
      4 #	from: @(#)security	8.1 (Berkeley) 6/9/93
      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 && $2 != "" && ($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 [ -f $f ] && 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 if [ -f /etc/exports ]; then
    329     # File systems should not be globally exported.
    330     awk '{
    331 	readonly = 0;
    332 	for (i = 2; i <= NF; ++i) {
    333 		if ($i ~ /-ro/)
    334 			readonly = 1;
    335 		else if ($i !~ /^-/)
    336 			next;
    337 	}
    338 	if (readonly)
    339 		print "File system " $1 " globally exported, read-only."
    340 	else
    341 		print "File system " $1 " globally exported, read-write."
    342     }' < /etc/exports > $OUTPUT
    343     if [ -s $OUTPUT ] ; then
    344 	printf "\nChecking for globally exported file systems.\n"
    345 	cat $OUTPUT
    346     fi
    347 fi
    348 
    349 # Display any changes in setuid files and devices.
    350 printf "\nChecking setuid files and devices:\n"
    351 (find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \
    352 		-o -fstype procfs \) -a -prune -o \
    353     \( -perm -u+s -o \( -perm -g+s -a ! -type d \) -o \
    354        ! -type d -a ! -type f -a ! -type l -a \
    355        ! -type s \) -print | \
    356 sort | sed -e 's/^/ls -ldgT /' | sh > $LIST) 2> $OUTPUT
    357 
    358 # Display any errors that occurred during system file walk.
    359 if [ -s $OUTPUT ] ; then
    360 	printf "Setuid/device find errors:\n"
    361 	cat $OUTPUT
    362 	printf "\n"
    363 fi
    364 
    365 # Display any changes in the setuid file list.
    366 egrep -v '^[bc]' $LIST > $TMP1
    367 if [ -s $TMP1 ] ; then
    368 	# Check to make sure uudecode isn't setuid.
    369 	if grep -w uudecode $TMP1 > /dev/null ; then
    370 		printf "\nUudecode is setuid.\n"
    371 	fi
    372 
    373 	CUR=/var/backups/setuid.current
    374 	BACK=/var/backups/setuid.backup
    375 
    376 	if [ -s $CUR ] ; then
    377 		if cmp -s $CUR $TMP1 ; then
    378 			:
    379 		else
    380 			> $TMP2
    381 			join -110 -210 -v2 $CUR $TMP1 > $OUTPUT
    382 			if [ -s $OUTPUT ] ; then
    383 				printf "Setuid additions:\n"
    384 				tee -a $TMP2 < $OUTPUT
    385 				printf "\n"
    386 			fi
    387 
    388 			join -110 -210 -v1 $CUR $TMP1 > $OUTPUT
    389 			if [ -s $OUTPUT ] ; then
    390 				printf "Setuid deletions:\n"
    391 				tee -a $TMP2 < $OUTPUT
    392 				printf "\n"
    393 			fi
    394 
    395 			sort +9 $TMP2 $CUR $TMP1 | \
    396 			    sed -e 's/[	 ][	 ]*/ /g' | uniq -u > $OUTPUT
    397 			if [ -s $OUTPUT ] ; then
    398 				printf "Setuid changes:\n"
    399 				column -t $OUTPUT
    400 				printf "\n"
    401 			fi
    402 
    403 			cp $CUR $BACK
    404 			cp $TMP1 $CUR
    405 		fi
    406 	else
    407 		printf "Setuid additions:\n"
    408 		column -t $TMP1
    409 		printf "\n"
    410 		cp $TMP1 $CUR
    411 	fi
    412 fi
    413 
    414 # Check for block and character disk devices that are readable or writeable
    415 # or not owned by root.operator.
    416 >$TMP1
    417 DISKLIST="dk fd hd hk hp jb kra ra rb rd rl rx rz sd up wd"
    418 for i in $DISKLIST; do
    419 	egrep "^b.*/${i}[0-9][0-9]*[a-h]$"  $LIST >> $TMP1
    420 	egrep "^c.*/r${i}[0-9][0-9]*[a-h]$"  $LIST >> $TMP1
    421 done
    422 
    423 awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \
    424 	{ printf("Disk %s is user %s, group %s, permissions %s.\n", \
    425 	    $11, $3, $4, $1); }' < $TMP1 > $OUTPUT
    426 if [ -s $OUTPUT ] ; then
    427 	printf "\nChecking disk ownership and permissions.\n"
    428 	cat $OUTPUT
    429 	printf "\n"
    430 fi
    431 
    432 # Display any changes in the device file list.
    433 egrep '^[bc]' $LIST | sort +10 > $TMP1
    434 if [ -s $TMP1 ] ; then
    435 	CUR=/var/backups/device.current
    436 	BACK=/var/backups/device.backup
    437 
    438 	if [ -s $CUR ] ; then
    439 		if cmp -s $CUR $TMP1 ; then
    440 			:
    441 		else
    442 			> $TMP2
    443 			join -111 -211 -v2 $CUR $TMP1 > $OUTPUT
    444 			if [ -s $OUTPUT ] ; then
    445 				printf "Device additions:\n"
    446 				tee -a $TMP2 < $OUTPUT
    447 				printf "\n"
    448 			fi
    449 
    450 			join -111 -211 -v1 $CUR $TMP1 > $OUTPUT
    451 			if [ -s $OUTPUT ] ; then
    452 				printf "Device deletions:\n"
    453 				tee -a $TMP2 < $OUTPUT
    454 				printf "\n"
    455 			fi
    456 
    457 			# Report any block device change.  Ignore character
    458 			# devices, only the name is significant.
    459 			cat $TMP2 $CUR $TMP1 | \
    460 			sed -e '/^c/d' | \
    461 			sort +10 | \
    462 			sed -e 's/[	 ][	 ]*/ /g' | \
    463 			uniq -u > $OUTPUT
    464 			if [ -s $OUTPUT ] ; then
    465 				printf "Block device changes:\n"
    466 				column -t $OUTPUT
    467 				printf "\n"
    468 			fi
    469 
    470 			cp $CUR $BACK
    471 			cp $TMP1 $CUR
    472 		fi
    473 	else
    474 		printf "Device additions:\n"
    475 		column -t $TMP1
    476 		printf "\n"
    477 		cp $TMP1 $CUR
    478 	fi
    479 fi
    480 
    481 # Check special files.
    482 # Check system binaries.
    483 #
    484 # Create the mtree tree specifications using:
    485 #
    486 #	mtree -cx -pDIR -kcksum,gid,mode,nlink,size,link,time,uid > DIR.secure
    487 #	chown root.wheel DIR.SECURE
    488 #	chmod 600 DIR.SECURE
    489 #
    490 # Note, this is not complete protection against Trojan horsed binaries, as
    491 # the hacker can modify the tree specification to match the replaced binary.
    492 # For details on really protecting yourself against modified binaries, see
    493 # the mtree(8) manual page.
    494 if cd /etc/mtree; then
    495 	mtree -e -p / -f /etc/mtree/special > $OUTPUT
    496 	if [ -s $OUTPUT ] ; then
    497 		printf "\nChecking special files and directories.\n"
    498 		cat $OUTPUT
    499 	fi
    500 
    501 	> $OUTPUT
    502 	for file in *.secure; do
    503 		[ $file = '*.secure' ] && continue
    504 		tree=`sed -n -e '3s/.* //p' -e 3q $file`
    505 		mtree -f $file -p $tree > $TMP1
    506 		if [ -s $TMP1 ]; then
    507 			printf "\nChecking $tree:\n" >> $OUTPUT
    508 			cat $TMP1 >> $OUTPUT
    509 		fi
    510 	done
    511 	if [ -s $OUTPUT ] ; then
    512 		printf "\nChecking system binaries:\n"
    513 		cat $OUTPUT
    514 	fi
    515 fi
    516 
    517 # List of files that get backed up and checked for any modifications.  Each
    518 # file is expected to have two backups, /var/backups/file.{current,backup}.
    519 # Any changes cause the files to rotate.
    520 if [ -s /etc/changelist ] ; then
    521 	for file in `cat /etc/changelist`; do
    522 		CUR=/var/backups/`basename $file`.current
    523 		BACK=/var/backups/`basename $file`.backup
    524 		if [ -s $file ]; then
    525 			if [ -s $CUR ] ; then
    526 				diff $CUR $file > $OUTPUT
    527 				if [ -s $OUTPUT ] ; then
    528 		printf "\n======\n%s diffs (OLD < > NEW)\n======\n" $file
    529 					cat $OUTPUT
    530 					cp -p $CUR $BACK
    531 					cp -p $file $CUR
    532 					chown root.wheel $CUR $BACK
    533 				fi
    534 			else
    535 				cp -p $file $CUR
    536 				chown root.wheel $CUR
    537 			fi
    538 		fi
    539 	done
    540 fi
    541