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