security revision 1.59
1#!/bin/sh -
2#
3#	$NetBSD: security,v 1.59 2001/09/23 19:10:25 perry Exp $
4#	from: @(#)security	8.1 (Berkeley) 6/9/93
5#
6
7PATH=/sbin:/usr/sbin:/bin:/usr/bin
8
9if [ -f /etc/rc.subr ]; then
10	. /etc/rc.subr
11else
12	echo "Can't read /etc/rc.subr; aborting."
13	exit 1;
14fi
15
16umask 077
17
18if [ -s /etc/security.conf ]; then
19	. /etc/security.conf
20fi
21
22SECUREDIR=`mktemp -d /tmp/_securedir.XXXXXX` || exit 1
23
24trap "/bin/rm -rf $SECUREDIR ; exit 0" EXIT INT QUIT
25
26if ! cd "$SECUREDIR"; then
27	echo "Can not cd to $SECUREDIR".
28	exit 1
29fi
30
31if [ -z "$max_loginlen" ];then
32	max_loginlen=8
33fi
34
35if [ -z "$max_grouplen" ]; then
36	max_grouplen=8
37fi
38
39ERR=secure1.$$
40TMP1=secure2.$$
41TMP2=secure3.$$
42MPBYUID=secure4.$$
43MPBYPATH=secure5.$$
44LIST=secure6.$$
45OUTPUT=secure7.$$
46LABELS=secure8.$$
47
48# Handle backup_dir not being set in .conf file
49backup_dir=${backup_dir:-/var/backups}
50
51MP=/etc/master.passwd
52
53# these is used several times.
54awk -F: '!/^+/ { print $1 " " $3 }' $MP | sort -k2n > $MPBYUID
55awk -F: '{ print $1 " " $9 }' $MP | sort -k2 > $MPBYPATH
56
57# Check the master password file syntax.
58#
59if checkyesno check_passwd; then
60	awk -v "len=$max_loginlen" '
61	BEGIN {
62		while ( getline < "/etc/shells" > 0 ) {
63			if ($0 ~ /^\#/ || $0 ~ /^$/ )
64				continue;
65			shells[$1]++;
66		}
67		FS=":";
68	}
69
70	{
71		if ($0 ~ /^[	 ]*$/) {
72			printf "Line %d is a blank line.\n", NR;
73			next;
74		}
75		if (NF != 10 && ($1 != "+" || NF != 1))
76			printf "Line %d has the wrong number of fields.\n", NR;
77		if ($1 == "+" )  {
78			if (NF != 1 && $3 == 0)
79			    printf "Line %d includes entries with uid 0.\n", NR;
80			next;
81		}
82		if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/)
83			printf "Login %s has non-alphanumeric characters.\n",
84			    $1;
85		if (length($1) > len)
86			printf "Login %s has more than "len" characters.\n", $1;
87		if ($2 == "")
88			printf "Login %s has no password.\n", $1;
89		if (length($2) != 13 && 
90		    length($2) != 20 && 
91		    length($2) != 34 &&
92		    $2 != "" &&
93		    $2 !~ /^\*[A-z-]+$/ &&
94		    $1 != "toor") {
95			if ($10 == "" || shells[$10])
96		    printf "Login %s is off but still has a valid shell (%s)\n",
97				    $1, $10;
98		} else if (! shells[$10])
99			printf "Login %s does not have a valid shell (%s)\n",
100			    $1, $10;
101		if ($3 == 0 && $1 != "root" && $1 != "toor")
102			printf "Login %s has a user id of 0.\n", $1;
103		if ($3 < 0)
104			printf "Login %s has a negative user id.\n", $1;
105		if ($4 < 0)
106			printf "Login %s has a negative group id.\n", $1;
107	}' < $MP > $OUTPUT
108	if [ -s $OUTPUT ] ; then
109		printf "\nChecking the $MP file:\n"
110		cat $OUTPUT
111	fi
112
113	awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT
114	if [ -s $OUTPUT ] ; then
115		printf "\n$MP has duplicate user names.\n"
116		column $OUTPUT
117	fi
118
119# To not exclude 'toor', a standard duplicate root account, from the duplicate
120# account test, uncomment the line below (without egrep in it)and comment
121# out the line (with egrep in it) below it.
122#
123#	< $MPBYUID uniq -d -f 1 | awk '{ print $2 }' > $TMP2
124	< $MPBYUID egrep -v '^toor ' | uniq -d -f 1 | awk '{ print $2 }' > $TMP2
125	if [ -s $TMP2 ] ; then
126		printf "\n$MP has duplicate user id's.\n"
127		while read uid; do
128			grep -w $uid $MPBYUID
129		done < $TMP2 | column
130	fi
131fi
132
133# Backup the master password file; a special case, the normal backup
134# mechanisms also print out file differences and we don't want to do
135# that because this file has encrypted passwords in it.
136#
137CUR=$backup_dir/${MP##*/}.current
138BACK=$backup_dir/${MP##*/}.backup
139if [ -s $CUR ] ; then
140	if cmp -s $CUR $MP; then
141		:
142	else
143		backup_file update $MP $CUR $BACK
144	fi
145else
146	backup_file add $MP $CUR $BACK
147fi
148
149# Check the group file syntax.
150#
151if checkyesno check_group; then
152	GRP=/etc/group
153	awk -F: -v "len=$max_grouplen" '{
154		if ($0 ~ /^[	 ]*$/) {
155			printf "Line %d is a blank line.\n", NR;
156			next;
157		}
158		if (NF != 4 && ($1 != "+" || NF != 1))
159			printf "Line %d has the wrong number of fields.\n", NR;
160		if ($1 == "+" )  {
161			next;
162		}
163		if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/)
164			printf "Group %s has non-alphanumeric characters.\n",
165			    $1;
166		if (length($1) > len)
167			printf "Group %s has more than "len" characters.\n", $1;
168		if ($3 !~ /[0-9]*/)
169			printf "Login %s has a negative group id.\n", $1;
170	}' < $GRP > $OUTPUT
171	if [ -s $OUTPUT ] ; then
172		printf "\nChecking the $GRP file:\n"
173		cat $OUTPUT
174	fi
175
176	awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT
177	if [ -s $OUTPUT ] ; then
178		printf "\n$GRP has duplicate group names.\n"
179		column $OUTPUT
180	fi
181fi
182
183# Check for root paths, umask values in startup files.
184# The check for the root paths is problematical -- it's likely to fail
185# in other environments.  Once the shells have been modified to warn
186# of '.' in the path, the path tests should go away.
187#
188if checkyesno check_rootdotfiles; then
189	> $OUTPUT
190	rhome=`csh -fc "echo ~root"`
191	umaskset=no
192	list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login"
193	for i in $list ; do
194		if [ -f $i ] ; then
195			if egrep '^[ \t]*umask[ \t]+[0-7]+' $i > /dev/null ; then
196				umaskset=yes
197			fi
198			# double check the umask value itself; ensure that both the
199			# 020 and 002 bits are set.
200			# we handle this in decimal initially to extract the digits,
201			# and then extract the `2' bit of each digit.
202			# this is made especially painful because 
203			# bitwise operations were left out of awk.
204			egrep '^[ \t]*umask[ \t]+[0-7]+' $i |
205			awk '{ g= ($2 % 100) - ($2 % 10);
206			       g /= 10;
207			       g = g % 4;
208			       g -= g % 2;
209			       if (g != 2) { print "\tRoot umask is group writeable" }
210			       o = ($2 % 10);
211			       o = o % 4;
212			       o -= o % 2;
213			       if (o != 2) { print "\tRoot umask is other writeable" } }' |
214			    sort -u >> $OUTPUT
215			SAVE_PATH=$PATH
216			unset PATH
217			/bin/csh -f -s << end-of-csh > /dev/null 2>&1
218				source $i
219				/bin/ls -ldgT \$path > $TMP1
220end-of-csh
221			PATH=$SAVE_PATH
222			awk '{
223				if ($10 ~ /^\.$/) {
224					print "\tThe root path includes .";
225					next;
226				}
227			     }
228			     $1 ~ /^d....w/ \
229		{ print "\tRoot path directory " $10 " is group writeable." } \
230			     $1 ~ /^d.......w/ \
231		{ print "\tRoot path directory " $10 " is other writeable." }' \
232			< $TMP1 >> $OUTPUT
233		fi
234	done
235	if [ $umaskset = "no" -o -s $OUTPUT ] ; then
236		printf "\nChecking root csh paths, umask values:\n$list\n\n"
237		if [ -s $OUTPUT ]; then
238			cat $OUTPUT
239		fi
240		if [ $umaskset = "no" ] ; then
241		    printf "\tRoot csh startup files do not set the umask.\n"
242		fi
243	fi
244
245	> $OUTPUT
246	rhome=/root
247	umaskset=no
248	list="/etc/profile ${rhome}/.profile"
249	for i in $list; do
250		if [ -f $i ] ; then
251			if egrep umask $i > /dev/null ; then
252				umaskset=yes
253			fi
254			egrep umask $i |
255			awk '$2 % 100 < 20 \
256				{ print "\tRoot umask is group writeable" } \
257			     $2 % 10 < 2 \
258				{ print "\tRoot umask is other writeable" }' \
259			    >> $OUTPUT
260			SAVE_PATH=$PATH
261			unset PATH
262			/bin/sh << end-of-sh > /dev/null 2>&1
263				. $i
264				list=\`echo \$PATH | /usr/bin/sed -e \
265				    's/^:/.:/;s/:$/:./;s/::/:.:/g;s/:/ /g'\`
266				/bin/ls -ldgT \$list > $TMP1
267end-of-sh
268			PATH=$SAVE_PATH
269			awk '{
270				if ($10 ~ /^\.$/) {
271					print "\tThe root path includes .";
272					next;
273				}
274			     }
275			     $1 ~ /^d....w/ \
276		{ print "\tRoot path directory " $10 " is group writeable." } \
277			     $1 ~ /^d.......w/ \
278		{ print "\tRoot path directory " $10 " is other writeable." }' \
279			< $TMP1 >> $OUTPUT
280
281		fi
282	done
283	if [ $umaskset = "no" -o -s $OUTPUT ] ; then
284		printf "\nChecking root sh paths, umask values:\n$list\n"
285		if [ -s $OUTPUT ]; then
286			cat $OUTPUT
287		fi
288		if [ $umaskset = "no" ] ; then
289			printf "\tRoot sh startup files do not set the umask.\n"
290		fi
291	fi
292fi
293
294# Root and uucp should both be in /etc/ftpusers.
295#
296if checkyesno check_ftpusers; then
297	> $OUTPUT
298	list="uucp "`awk '$2 == 0 { print $1 }' $MPBYUID`
299	for i in $list; do
300		if /usr/libexec/ftpd -C $i ; then
301			printf "\t$i is not denied\n" >> $OUTPUT
302		fi
303	done
304	if [ -s $OUTPUT ]; then
305		printf "\nChecking the /etc/ftpusers configuration:\n"
306		cat $OUTPUT
307	fi
308fi
309
310# Uudecode should not be in the /etc/mail/aliases file.
311#
312if checkyesno check_aliases; then
313	for f in /etc/mail/aliases /etc/aliases; do
314		if [ -f $f ] && egrep '^[^#]*(uudecode|decode).*\|' $f; then
315			printf "\nEntry for uudecode in $f file.\n"
316		fi
317	done
318fi
319
320# Files that should not have + signs.
321#
322if checkyesno check_rhosts; then
323	list="/etc/hosts.equiv /etc/hosts.lpd"
324	for f in $list ; do
325		if [ -f $f ] && egrep '\+' $f > /dev/null ; then
326			printf "\nPlus sign in $f file.\n"
327		fi
328	done
329
330	# Check for special users with .rhosts files.  Only root and toor should
331	# have .rhosts files.  Also, .rhosts files should not have plus signs.
332	awk -F: '$1 != "root" && $1 != "toor" && \
333		($3 < 100 || $1 == "ftp" || $1 == "uucp") \
334			{ print $1 " " $9 }' $MP |
335	sort -k2 |
336	while read uid homedir; do
337		if [ -f ${homedir}/.rhosts ] ; then
338			rhost=`ls -ldgT ${homedir}/.rhosts`
339			printf -- "$uid: $rhost\n"
340		fi
341	done > $OUTPUT
342	if [ -s $OUTPUT ] ; then
343		printf "\nChecking for special users with .rhosts files.\n"
344		cat $OUTPUT
345	fi
346
347	while read uid homedir; do
348		if [ -f ${homedir}/.rhosts -a -r ${homedir}/.rhosts ] && \
349		    cat -f ${homedir}/.rhosts | egrep '\+' > /dev/null ; then
350			printf -- "$uid: + in .rhosts file.\n"
351		fi
352	done < $MPBYPATH > $OUTPUT
353	if [ -s $OUTPUT ] ; then
354		printf "\nChecking .rhosts files syntax.\n"
355		cat $OUTPUT
356	fi
357fi
358
359# Check home directories.  Directories should not be owned by someone else
360# or writeable.
361#
362if checkyesno check_homes; then
363	while read uid homedir; do
364		if [ -d ${homedir}/ ] ; then
365			file=`ls -ldgT ${homedir}`
366			printf -- "$uid $file\n"
367		fi
368	done < $MPBYPATH |
369	awk '$1 != $4 && $4 != "root" \
370		{ print "user " $1 " home directory is owned by " $4 }
371	     $2 ~ /^-....w/ \
372		{ print "user " $1 " home directory is group writeable" }
373	     $2 ~ /^-.......w/ \
374		{ print "user " $1 " home directory is other writeable" }' \
375	    > $OUTPUT
376	if [ -s $OUTPUT ] ; then
377		printf "\nChecking home directories.\n"
378		cat $OUTPUT
379	fi
380
381	# Files that should not be owned by someone else or readable.
382	list=".Xauthority .netrc"
383	while read uid homedir; do
384		for f in $list ; do
385			file=${homedir}/${f}
386			if [ -f $file ] ; then
387				printf -- "$uid $f `ls -ldgT $file`\n"
388			fi
389		done
390	done < $MPBYPATH |
391	awk '$1 != $5 && $5 != "root" \
392		{ print "user " $1 " " $2 " file is owned by " $5 }
393	     $3 ~ /^-...r/ \
394		{ print "user " $1 " " $2 " file is group readable" }
395	     $3 ~ /^-......r/ \
396		{ print "user " $1 " " $2 " file is other readable" }
397	     $3 ~ /^-....w/ \
398		{ print "user " $1 " " $2 " file is group writeable" }
399	     $3 ~ /^-.......w/ \
400		{ print "user " $1 " " $2 " file is other writeable" }' \
401	    > $OUTPUT
402
403	# Files that should not be owned by someone else or writeable.
404	list=".bash_history .bash_login .bash_logout .bash_profile .bashrc \
405	      .cshrc .emacs .exrc .forward .history .klogin .login .logout \
406	      .profile .qmail .rc_history .rhosts .tcshrc .twmrc .xinitrc \
407	      .xsession"
408	while read uid homedir; do
409		for f in $list ; do
410			file=${homedir}/${f}
411			if [ -f $file ] ; then
412				printf -- "$uid $f `ls -ldgT $file`\n"
413			fi
414		done
415	done < $MPBYPATH |
416	awk '$1 != $5 && $5 != "root" \
417		{ print "user " $1 " " $2 " file is owned by " $5 }
418	     $3 ~ /^-....w/ \
419		{ print "user " $1 " " $2 " file is group writeable" }
420	     $3 ~ /^-.......w/ \
421		{ print "user " $1 " " $2 " file is other writeable" }' \
422	    >> $OUTPUT
423	if [ -s $OUTPUT ] ; then
424		printf "\nChecking dot files.\n"
425		cat $OUTPUT
426	fi
427fi
428
429# Mailboxes should be owned by user and unreadable.
430#
431if checkyesno check_varmail; then
432	ls -l /var/mail | sed 1d | \
433	awk '$3 != $9 \
434		{ print "user " $9 " mailbox is owned by " $3 }
435	     $1 != "-rw-------" \
436		{ print "user " $9 " mailbox is " $1 ", group " $4 }' > $OUTPUT
437	if [ -s $OUTPUT ] ; then
438		printf "\nChecking mailbox ownership.\n"
439		cat $OUTPUT
440	fi
441fi
442
443# NFS exports shouldn't be globally exported
444#
445if checkyesno check_nfs && [ -f /etc/exports ]; then
446	awk '{
447		# ignore comments and blank lines
448		if ($0 ~ /^\#/ || $0 ~ /^$/ )
449			next;
450
451		readonly = 0;
452		for (i = 2; i <= NF; ++i) {
453			if ($i ~ /-ro/)
454				readonly = 1;
455			else if ($i !~ /^-/)
456				next;
457		}
458		if (readonly)
459			print "File system " $1 " globally exported, read-only."
460		else
461			print "File system " $1 " globally exported, read-write."
462	}' < /etc/exports > $OUTPUT
463	if [ -s $OUTPUT ] ; then
464		printf "\nChecking for globally exported file systems.\n"
465		cat $OUTPUT
466	fi
467fi
468
469# Display any changes in setuid files and devices.
470#
471if checkyesno check_devices; then
472	> $ERR
473	(find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \
474			-o -fstype procfs \) -a -prune -o \
475	    \( \( -perm -u+s -a ! -type d \) -o \
476	       \( -perm -g+s -a ! -type d \) -o \
477	       -type b -o -type c \) -print0 | \
478	xargs -0 ls -ldgTq | sort +9 > $LIST) 2> $OUTPUT
479
480	# Display any errors that occurred during system file walk.
481	if [ -s $OUTPUT ] ; then
482		printf "Setuid/device find errors:\n" >> $ERR
483		cat $OUTPUT >> $ERR
484		printf "\n" >> $ERR
485	fi
486
487	# Display any changes in the setuid file list.
488	egrep -v '^[bc]' $LIST > $TMP1
489	if [ -s $TMP1 ] ; then
490		# Check to make sure uudecode isn't setuid.
491		if grep -w uudecode $TMP1 > /dev/null ; then
492			printf "\nUudecode is setuid.\n" >> $ERR
493		fi
494
495		CUR=$backup_dir/setuid.current
496		BACK=$backup_dir/setuid.backup
497
498		if [ -s $CUR ] ; then
499			if cmp -s $CUR $TMP1 ; then
500				:
501			else
502				> $TMP2
503				join -110 -210 -v2 $CUR $TMP1 > $OUTPUT
504				if [ -s $OUTPUT ] ; then
505					printf "Setuid additions:\n" >> $ERR
506					tee -a $TMP2 < $OUTPUT >> $ERR
507					printf "\n" >> $ERR
508				fi
509
510				join -110 -210 -v1 $CUR $TMP1 > $OUTPUT
511				if [ -s $OUTPUT ] ; then
512					printf "Setuid deletions:\n" >> $ERR
513					tee -a $TMP2 < $OUTPUT >> $ERR
514					printf "\n" >> $ERR
515				fi
516
517				sort -k10 $TMP2 $CUR $TMP1 | \
518				    sed -e 's/[	 ][	 ]*/ /g' | \
519				    uniq -u > $OUTPUT
520				if [ -s $OUTPUT ] ; then
521					printf "Setuid changes:\n" >> $ERR
522					column -t $OUTPUT >> $ERR
523					printf "\n" >> $ERR
524				fi
525
526				backup_file update $TMP1 $CUR $BACK
527			fi
528		else
529			printf "Setuid additions:\n" >> $ERR
530			column -t $TMP1 >> $ERR
531			printf "\n" >> $ERR
532			backup_file add $TMP1 $CUR $BACK
533		fi
534	fi
535
536	# Check for block and character disk devices that are readable or
537	# writeable or not owned by root.operator.
538	>$TMP1
539	DISKLIST="acd ccd cd ch fd hk hp mcd md ra rb rd rl rx \
540	    sd se ss uk up vnd wd xd xy"
541#	DISKLIST="$DISKLIST ct mt st wt"
542	for i in $DISKLIST; do
543		egrep "^b.*/${i}[0-9][0-9]*[a-p]$"  $LIST >> $TMP1
544		egrep "^c.*/r${i}[0-9][0-9]*[a-p]$"  $LIST >> $TMP1
545	done
546
547	awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \
548		{ printf "Disk %s is user %s, group %s, permissions %s.\n", \
549		    $11, $3, $4, $1; }' < $TMP1 > $OUTPUT
550	if [ -s $OUTPUT ] ; then
551		printf "\nChecking disk ownership and permissions.\n" >> $ERR
552		cat $OUTPUT >> $ERR
553		printf "\n" >> $ERR
554	fi
555
556	# Display any changes in the device file list.
557	egrep '^[bc]' $LIST | sort -k11 > $TMP1
558	if [ -s $TMP1 ] ; then
559		CUR=$backup_dir/device.current
560		BACK=$backup_dir/device.backup
561
562		if [ -s $CUR ] ; then
563			if cmp -s $CUR $TMP1 ; then
564				:
565			else
566				> $TMP2
567				join -111 -211 -v2 $CUR $TMP1 > $OUTPUT
568				if [ -s $OUTPUT ] ; then
569					printf "Device additions:\n" >> $ERR
570					tee -a $TMP2 < $OUTPUT >> $ERR
571					printf "\n" >> $ERR
572				fi
573
574				join -111 -211 -v1 $CUR $TMP1 > $OUTPUT
575				if [ -s $OUTPUT ] ; then
576					printf "Device deletions:\n" >> $ERR
577					tee -a $TMP2 < $OUTPUT >> $ERR
578					printf "\n" >> $ERR
579				fi
580
581				# Report any block device change. Ignore
582				# character devices, only the name is
583				# significant.
584				cat $TMP2 $CUR $TMP1 | \
585				    sed -e '/^c/d' | \
586				    sort -k11 | \
587				    sed -e 's/[	 ][	 ]*/ /g' | \
588				    uniq -u > $OUTPUT
589				if [ -s $OUTPUT ] ; then
590					printf "Block device changes:\n" >> $ERR
591					column -t $OUTPUT >> $ERR
592					printf "\n" >> $ERR
593				fi
594
595				backup_file update $TMP1 $CUR $BACK
596			fi
597		else
598			printf "Device additions:\n" >> $ERR
599			column -t $TMP1 >> $ERR
600			printf "\n" >> $ERR
601			backup_file add $TMP1 $CUR $BACK >> $ERR
602		fi
603	fi
604	if [ -s $ERR ] ; then
605		printf "\nChecking setuid files and devices:\n"
606		cat $ERR
607		printf "\n"
608	fi
609fi
610
611# Check special files.
612# Check system binaries.
613#
614# Create the mtree tree specifications using:
615#
616#	mtree -cx -pDIR -kcksum,gid,mode,nlink,size,link,time,uid > DIR.secure
617#	chown root:wheel DIR.secure
618#	chmod 600 DIR.secure
619#
620# Note, this is not complete protection against Trojan horsed binaries, as
621# the hacker can modify the tree specification to match the replaced binary.
622# For details on really protecting yourself against modified binaries, see
623# the mtree(8) manual page.
624#
625if checkyesno check_mtree; then
626	mtree -e -l -p / -f /etc/mtree/special > $OUTPUT
627	if [ -s $OUTPUT ]; then
628		printf "\nChecking special files and directories.\n"
629		cat $OUTPUT
630	fi
631
632	> $OUTPUT
633	for file in /etc/mtree/*.secure; do
634		[ $file = '/etc/mtree/*.secure' ] && continue
635		tree=`sed -n -e '3s/.* //p' -e 3q $file`
636		mtree -f $file -p $tree > $TMP1
637		if [ -s $TMP1 ]; then
638			printf "\nChecking $tree:\n" >> $OUTPUT
639			cat $TMP1 >> $OUTPUT
640		fi
641	done
642	if [ -s $OUTPUT ]; then
643		printf "\nChecking system binaries:\n"
644		cat $OUTPUT
645	fi
646fi
647
648CHANGELIST=""
649
650# Backup disklabels of available disks
651#
652if checkyesno check_disklabels; then
653		# generate list of old disklabels and remove them
654	ls -1d $backup_dir/disklabel.* 2>/dev/null |
655	    egrep -v '\.(backup|current)(,v)?$' > $LABELS
656	xargs rm < $LABELS
657
658	disks=`iostat -x | sed 1d | awk '$1 !~ /^[cfm]d/ { print $1; }'`
659	for i in $disks; do
660		dlf="$backup_dir/disklabel.$i"
661		disklabel $i > $dlf 2>/dev/null
662	done
663
664		# append list of new disklabels, sort list
665	ls -1d $backup_dir/disklabel.* 2>/dev/null |
666	    egrep -v '\.(backup|current)(,v)?$' >> $LABELS
667	sort -u -o $LABELS $LABELS
668	CHANGELIST=$LABELS
669fi
670
671# List of files that get backed up and checked for any modifications.  Each
672# file is expected to have two backups, $backup_dir/file.{current,backup}.
673# Any changes cause the files to rotate.
674#
675if checkyesno check_changelist && [ -s /etc/changelist ] ; then
676	CHANGELIST="/etc/changelist $CHANGELIST"
677fi
678
679if [ -n "$CHANGELIST" ]; then
680	for file in `egrep -hv "^#|$MP" $CHANGELIST`; do
681		# old changelist backup names
682		OCUR=$backup_dir/${file##*/}.current
683		OBACK=$backup_dir/${file##*/}.backup
684		# new changelist backup names
685		CUR=$backup_dir$file.current
686		BACK=$backup_dir$file.backup
687		# roll over old backups
688		if [ ! -d ${CUR%/*} ]; then
689			mkdir -p ${CUR%/*}
690		fi
691		if [ -f $OCUR -a ! -f $CUR ]; then
692			mv $OCUR $CUR
693		fi
694		if [ -f $OCUR,v -a ! -f $CUR,v ]; then
695			mv $OCUR,v $CUR,v
696		fi
697		if [ -f $OBACK -a ! -f $BACK ]; then
698			mv $OBACK $BACK
699		fi
700		# and down to work
701		if [ -f $file ]; then
702			if [ -f $CUR ] ; then
703				diff $CUR $file > $OUTPUT
704				if [ -s $OUTPUT ] ; then
705		printf "\n======\n%s diffs (OLD < > NEW)\n======\n" $file
706					cat $OUTPUT
707					backup_file update $file $CUR $BACK
708				fi
709			else
710		printf "\n======\n%s added\n======\n" $file
711				diff /dev/null $file
712				backup_file add $file $CUR $BACK
713			fi
714		else
715			if [ -f $CUR ]; then
716		printf "\n======\n%s removed\n======\n" $file
717				diff $CUR /dev/null
718				backup_file remove $file $CUR $BACK
719			fi
720		fi
721	done
722fi
723
724if [ -f /etc/security.local ]; then
725	echo ""
726	echo "Running /etc/security.local:"
727	. /etc/security.local
728fi
729