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