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