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