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