Annotation of src/etc/security, Revision 1.1.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