NetBSD/etc/security
lukem 610ee5bd6f Merge /etc/mtree/special & /etc/mtree/special.local using "mtree -M".
This allows users to override mtree/special entries in mtree/special.local,
which is useful if you've replaced a directory with a symlink (for example).
This effectively makes $check_mtree_follow_symlinks=YES pointless, but
I'm retaining that for compatibility reasons.

Fix bug in generation of $MPBYUID (used "/^+/" instead of "/^\+/" as a regex),
which has existed for a long time but only failed with our awk; GNU awk seems
to have permitted this.  (This meant that the duplicate UID check was broken
when using our awk.)

Rename some temp files to more accurately reflect their purpose, to
aid debugging.
2004-07-23 06:12:16 +00:00

908 lines
24 KiB
Bash

#!/bin/sh -
#
# $NetBSD: security,v 1.91 2004/07/23 06:12:16 lukem Exp $
# from: @(#)security 8.1 (Berkeley) 6/9/93
#
PATH=/sbin:/usr/sbin:/bin:/usr/bin
rcvar_manpage='security.conf(5)'
if [ -f /etc/rc.subr ]; then
. /etc/rc.subr
else
echo "Can't read /etc/rc.subr; aborting."
exit 1;
fi
umask 077
TZ=UTC; export TZ
if [ -s /etc/security.conf ]; then
. /etc/security.conf
fi
# Set reasonable defaults (if they're not set in security.conf)
#
backup_dir=${backup_dir:-/var/backups}
pkgdb_dir=${pkgdb_dir:-/var/db/pkg}
max_loginlen=${max_loginlen:-8}
max_grouplen=${max_grouplen:-8}
# Other configurable variables
#
special_files="/etc/mtree/special /etc/mtree/special.local"
MP=/etc/master.passwd
CHANGELIST=""
work_dir=$backup_dir/work
if [ ! -d "$work_dir" ]; then
mkdir -p "$work_dir"
fi
SECUREDIR=`mktemp -d /tmp/_securedir.XXXXXX` || exit 1
trap "/bin/rm -rf $SECUREDIR ; exit 0" EXIT INT QUIT PIPE
if ! cd "$SECUREDIR"; then
echo "Can not cd to $SECUREDIR".
exit 1
fi
ERR=err.$$
TMP1=tmp1.$$
TMP2=tmp2.$$
MPBYUID=mpbyuid.$$
MPBYPATH=mpbypath.$$
LIST=list.$$
OUTPUT=output.$$
LABELS=labels.$$
PKGS=pkgs.$$
CHANGEFILES=changefiles.$$
SPECIALSPEC=specialspec.$$
# migrate_file old new
# Determine if the "${old}" path name needs to be migrated to the
# "${new}" path. Also checks if "${old}.current" needs migrating,
# and if so, migrate it and possibly "${old}.current,v" and
# "${old}.backup".
#
migrate_file()
{
_old=$1
_new=$2
if [ -z "$_old" -o -z "$_new" ]; then
err 3 "USAGE: migrate_file old new"
fi
if [ ! -d "${_new%/*}" ]; then
mkdir -p "${_new%/*}"
fi
if [ -f "${_old}" -a ! -f "${_new}" ]; then
echo "==> migrating ${_old}"
echo " to ${_new}"
mv "${_old}" "${_new}"
fi
if [ -f "${_old}.current" -a ! -f "${_new}.current" ]; then
echo "==> migrating ${_old}.current"
echo " to ${_new}.current"
mv "${_old}.current" "${_new}.current"
if [ -f "${_old}.current,v" -a ! -f "${_new}.current,v" ]; then
echo "==> migrating ${_old}.current,v"
echo " to ${_new}.current,v"
mv "${_old}.current,v" "${_new}.current,v"
fi
if [ -f "${_old}.backup" -a ! -f "${_new}.backup" ]; then
echo "==> migrating ${_old}.backup"
echo " to ${_new}.backup"
mv "${_old}.backup" "${_new}.backup"
fi
fi
}
# backup_and_diff file printdiff
# Determine if file needs backing up, and if so, do it.
# If printdiff is yes, display the diffs, otherwise
# just print a message saying "[changes omitted]".
#
backup_and_diff()
{
_file=$1
_printdiff=$2
if [ -z "$_file" -o -z "$_printdiff" ]; then
err 3 "USAGE: backup_and_diff file printdiff"
fi
! checkyesno _printdiff
_printdiff=$?
_old=$backup_dir/${_file##*/}
case "$_file" in
$work_dir/*)
_new=$_file
migrate_file "$backup_dir/$_old" "$_new"
migrate_file "$_old" "$_new"
;;
*)
_new=$backup_dir/$_file
migrate_file "$_old" "$_new"
;;
esac
CUR=${_new}.current
BACK=${_new}.backup
if [ -f $_file ]; then
if [ -f $CUR ] ; then
if [ "$_printdiff" -ne 0 ]; then
diff ${diff_options} $CUR $_file > $OUTPUT
else
if ! cmp -s $CUR $_file; then
echo "[changes omitted]"
fi > $OUTPUT
fi
if [ -s $OUTPUT ] ; then
printf \
"\n======\n%s diffs (OLD < > NEW)\n======\n" $_file
cat $OUTPUT
backup_file update $_file $CUR $BACK
fi
else
printf "\n======\n%s added\n======\n" $_file
if [ "$_printdiff" -ne 0 ]; then
diff ${diff_options} /dev/null $_file
else
echo "[changes omitted]"
fi
backup_file add $_file $CUR $BACK
fi
else
if [ -f $CUR ]; then
printf "\n======\n%s removed\n======\n" $_file
if [ "$_printdiff" -ne 0 ]; then
diff ${diff_options} $CUR /dev/null
else
echo "[changes omitted]"
fi
backup_file remove $_file $CUR $BACK
fi
fi
}
# These are used several times.
#
awk -F: '!/^\+/ { print $1 " " $3 }' $MP | sort -k2n > $MPBYUID
awk -F: '{ print $1 " " $9 }' $MP | sort -k2 > $MPBYPATH
for file in $special_files; do
[ -s $file ] && cat $file
done | mtree -CM -k all > $SPECIALSPEC || exit 1
# Check the master password file syntax.
#
if checkyesno check_passwd; then
# XXX: the sense of permit_star is reversed; the code works as
# implemented, but usage needs to be negated.
checkyesno check_passwd_permit_star && permit_star=0 || permit_star=1
awk -v "len=$max_loginlen" \
-v "nowarn_shells_list=$check_passwd_nowarn_shells" \
-v "nowarn_users_list=$check_passwd_nowarn_users" \
-v "permit_star=$permit_star" '
BEGIN {
while ( getline < "/etc/shells" > 0 ) {
if ($0 ~ /^\#/ || $0 ~ /^$/ )
continue;
shells[$1]++;
}
split(nowarn_shells_list, a);
for (i in a) nowarn_shells[a[i]]++;
split(nowarn_users_list, a);
for (i in a) nowarn_users[a[i]]++;
uid0_users_list="root toor"
split(uid0_users_list, a);
for (i in a) uid0_users[a[i]]++;
FS=":";
}
{
if ($0 ~ /^[ ]*$/) {
printf "Line %d is a blank line.\n", NR;
next;
}
if (NF != 10 && ($1 != "+" || NF != 1))
printf "Line %d has the wrong number of fields.\n", NR;
if ($1 == "+" ) {
if (NF != 1 && $3 == 0)
printf "Line %d includes entries with uid 0.\n",
NR;
next;
}
if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/)
printf "Login %s has non-alphanumeric characters.\n",
$1;
if (length($1) > len)
printf "Login %s has more than "len" characters.\n",
$1;
if ($2 == "" && !nowarn_users[$1])
printf "Login %s has no password.\n", $1;
if (!nowarn_shells[$10] && !nowarn_users[$1]) {
if (length($2) != 13 &&
length($2) != 20 &&
$2 !~ /^\$1/ &&
$2 !~ /^\$2/ &&
$2 != "" &&
(permit_star || $2 != "*") &&
$2 !~ /^\*[A-z-]+$/ &&
$1 != "toor") {
if ($10 == "" || shells[$10])
printf "Login %s is off but still has "\
"a valid shell (%s)\n", $1, $10;
} else if (! shells[$10])
printf "Login %s does not have a valid "\
"shell (%s)\n", $1, $10;
}
if ($3 == 0 && !uid0_users[$1] && !nowarn_users[$1])
printf "Login %s has a user id of 0.\n", $1;
if ($3 < 0)
printf "Login %s has a negative user id.\n", $1;
if ($4 < 0)
printf "Login %s has a negative group id.\n", $1;
}' < $MP > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking the $MP file:\n"
cat $OUTPUT
fi
awk -F: '{ print $1 }' $MP | sort | uniq -d > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\n$MP has duplicate user names.\n"
column $OUTPUT
fi
# To not exclude 'toor', a standard duplicate root account, from the duplicate
# account test, uncomment the line below (without egrep in it)and comment
# out the line (with egrep in it) below it.
#
# < $MPBYUID uniq -d -f 1 | awk '{ print $2 }' > $TMP2
< $MPBYUID egrep -v '^toor ' | uniq -d -f 1 | awk '{ print $2 }' > $TMP2
if [ -s $TMP2 ] ; then
printf "\n$MP has duplicate user id's.\n"
while read uid; do
grep -w $uid $MPBYUID
done < $TMP2 | column
fi
fi
# Check the group file syntax.
#
if checkyesno check_group; then
GRP=/etc/group
awk -F: -v "len=$max_grouplen" '{
if ($0 ~ /^[ ]*$/) {
printf "Line %d is a blank line.\n", NR;
next;
}
if (NF != 4 && ($1 != "+" || NF != 1))
printf "Line %d has the wrong number of fields.\n", NR;
if ($1 == "+" ) {
next;
}
if ($1 !~ /^[A-Za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])*$/)
printf "Group %s has non-alphanumeric characters.\n",
$1;
if (length($1) > len)
printf "Group %s has more than "len" characters.\n", $1;
if ($3 !~ /[0-9]*/)
printf "Login %s has a negative group id.\n", $1;
}' < $GRP > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking the $GRP file:\n"
cat $OUTPUT
fi
awk -F: '{ print $1 }' $GRP | sort | uniq -d > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\n$GRP has duplicate group names.\n"
column $OUTPUT
fi
fi
# Check for root paths, umask values in startup files.
# The check for the root paths is problematical -- it's likely to fail
# in other environments. Once the shells have been modified to warn
# of '.' in the path, the path tests should go away.
#
if checkyesno check_rootdotfiles; then
rhome=~root
umaskset=no
list="/etc/csh.cshrc /etc/csh.login ${rhome}/.cshrc ${rhome}/.login"
for i in $list ; do
if [ -f $i ] ; then
if egrep '^[ \t]*umask[ \t]+[0-7]+' $i > /dev/null ;
then
umaskset=yes
fi
# Double check the umask value itself; ensure that
# both the group and other write bits are set.
#
egrep '^[ \t]*umask[ \t]+[0-7]+' $i |
awk '{
if ($2 ~ /^.$/ || $2 ~! /[^2367].$/) {
print "\tRoot umask is group writable"
}
if ($2 ~ /[^2367]$/) {
print "\tRoot umask is other writable"
}
}' | sort -u
SAVE_PATH=$PATH
unset PATH
/bin/csh -f -s << end-of-csh > /dev/null 2>&1
source $i
/bin/ls -ldgT \$path > $TMP1
end-of-csh
export PATH=$SAVE_PATH
awk '{
if ($10 ~ /^\.$/) {
print "\tThe root path includes .";
next;
}
}
$1 ~ /^d....w/ \
{ print "\tRoot path directory " $10 " is group writable." } \
$1 ~ /^d.......w/ \
{ print "\tRoot path directory " $10 " is other writable." }' \
< $TMP1
fi
done > $OUTPUT
if [ $umaskset = "no" -o -s $OUTPUT ] ; then
printf "\nChecking root csh paths, umask values:\n$list\n\n"
if [ -s $OUTPUT ]; then
cat $OUTPUT
fi
if [ $umaskset = "no" ] ; then
printf "\tRoot csh startup files do not set the umask.\n"
fi
fi
umaskset=no
list="/etc/profile ${rhome}/.profile"
for i in $list; do
if [ -f $i ] ; then
if egrep umask $i > /dev/null ; then
umaskset=yes
fi
egrep umask $i |
awk '$2 ~ /^.$/ || $2 ~ /[^2367].$/ \
{ print "\tRoot umask is group writable" } \
$2 ~ /[^2367]$/ \
{ print "\tRoot umask is other writable" }'
SAVE_PATH=$PATH
unset PATH
/bin/sh << end-of-sh > /dev/null 2>&1
. $i
list=\`echo \$PATH | /usr/bin/sed -e \
's/^:/.:/;s/:$/:./;s/::/:.:/g;s/:/ /g'\`
/bin/ls -ldgT \$list > $TMP1
end-of-sh
export PATH=$SAVE_PATH
awk '{
if ($10 ~ /^\.$/) {
print "\tThe root path includes .";
next;
}
}
$1 ~ /^d....w/ \
{ print "\tRoot path directory " $10 " is group writable." } \
$1 ~ /^d.......w/ \
{ print "\tRoot path directory " $10 " is other writable." }' \
< $TMP1
fi
done > $OUTPUT
if [ $umaskset = "no" -o -s $OUTPUT ] ; then
printf "\nChecking root sh paths, umask values:\n$list\n"
if [ -s $OUTPUT ]; then
cat $OUTPUT
fi
if [ $umaskset = "no" ] ; then
printf "\tRoot sh startup files do not set the umask.\n"
fi
fi
fi
# Root and uucp should both be in /etc/ftpusers.
#
if checkyesno check_ftpusers; then
list="uucp "`awk '$2 == 0 { print $1 }' $MPBYUID`
for i in $list; do
if /usr/libexec/ftpd -C $i ; then
printf "\t$i is not denied\n"
fi
done > $OUTPUT
if [ -s $OUTPUT ]; then
printf "\nChecking the /etc/ftpusers configuration:\n"
cat $OUTPUT
fi
fi
# Uudecode should not be in the /etc/mail/aliases file.
#
if checkyesno check_aliases; then
for f in /etc/mail/aliases /etc/aliases; do
if [ -f $f ] && egrep '^[^#]*(uudecode|decode).*\|' $f; then
printf "\nEntry for uudecode in $f file.\n"
fi
done
fi
# Files that should not have + signs.
#
if checkyesno check_rhosts; then
list="/etc/hosts.equiv /etc/hosts.lpd"
for f in $list ; do
if [ -f $f ] && egrep '\+' $f > /dev/null ; then
printf "\nPlus sign in $f file.\n"
fi
done
# Check for special users with .rhosts files. Only root and toor should
# have .rhosts files. Also, .rhosts files should not have plus signs.
awk -F: '$1 != "root" && $1 != "toor" && \
($3 < 100 || $1 == "ftp" || $1 == "uucp") \
{ print $1 " " $9 }' $MP |
sort -k2 |
while read uid homedir; do
if [ -f ${homedir}/.rhosts ] ; then
rhost=`ls -ldgT ${homedir}/.rhosts`
printf -- "$uid: $rhost\n"
fi
done > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking for special users with .rhosts files.\n"
cat $OUTPUT
fi
while read uid homedir; do
if [ -f ${homedir}/.rhosts -a -r ${homedir}/.rhosts ] && \
cat -f ${homedir}/.rhosts | egrep '\+' > /dev/null ; then
printf -- "$uid: + in .rhosts file.\n"
fi
done < $MPBYPATH > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking .rhosts files syntax.\n"
cat $OUTPUT
fi
fi
# Check home directories. Directories should not be owned by someone else
# or writable.
#
if checkyesno check_homes; then
checkyesno check_homes_permit_usergroups && \
permit_usergroups=1 || permit_usergroups=0
while read uid homedir; do
if [ -d ${homedir}/ ] ; then
file=`ls -ldgT ${homedir}`
printf -- "$uid $file\n"
fi
done < $MPBYPATH |
awk -v "usergroups=$permit_usergroups" '
$1 != $4 && $4 != "root" \
{ print "user " $1 " home directory is owned by " $4 }
$2 ~ /^-....w/ && (!usergroups || $5 != $1) \
{ print "user " $1 " home directory is group writable" }
$2 ~ /^-.......w/ \
{ print "user " $1 " home directory is other writable" }' \
> $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking home directories.\n"
cat $OUTPUT
fi
# Files that should not be owned by someone else or readable.
list=".Xauthority .netrc .ssh/id_dsa .ssh/id_rsa .ssh/identity"
while read uid homedir; do
for f in $list ; do
file=${homedir}/${f}
if [ -f $file ] ; then
printf -- "$uid $f `ls -ldgT $file`\n"
fi
done
done < $MPBYPATH |
awk -v "usergroups=$permit_usergroups" '
$1 != $5 && $5 != "root" \
{ print "user " $1 " " $2 " file is owned by " $5 }
$3 ~ /^-...r/ && (!usergroups || $6 != $1) \
{ print "user " $1 " " $2 " file is group readable" }
$3 ~ /^-......r/ \
{ print "user " $1 " " $2 " file is other readable" }
$3 ~ /^-....w/ && (!usergroups || $6 != $1) \
{ print "user " $1 " " $2 " file is group writable" }
$3 ~ /^-.......w/ \
{ print "user " $1 " " $2 " file is other writable" }' \
> $OUTPUT
# Files that should not be owned by someone else or writable.
list=".bash_history .bash_login .bash_logout .bash_profile .bashrc \
.cshrc .emacs .exrc .forward .history .k5login .klogin .login \
.logout .profile .qmail .rc_history .rhosts .shosts ssh .tcshrc \
.twmrc .xinitrc .xsession .ssh/authorized_keys \
.ssh/authorized_keys2 .ssh/config .ssh/id_dsa.pub \
.ssh/id_rsa.pub .ssh/identity.pub .ssh/known_hosts \
.ssh/known_hosts2"
while read uid homedir; do
for f in $list ; do
file=${homedir}/${f}
if [ -f $file ] ; then
printf -- "$uid $f `ls -ldgT $file`\n"
fi
done
done < $MPBYPATH |
awk -v "usergroups=$permit_usergroups" '
$1 != $5 && $5 != "root" \
{ print "user " $1 " " $2 " file is owned by " $5 }
$3 ~ /^-....w/ && (!usergroups || $6 != $1) \
{ print "user " $1 " " $2 " file is group writable" }
$3 ~ /^-.......w/ \
{ print "user " $1 " " $2 " file is other writable" }' \
>> $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking dot files.\n"
cat $OUTPUT
fi
fi
# Mailboxes should be owned by user and unreadable.
#
if checkyesno check_varmail; then
ls -lA /var/mail | \
awk ' NR == 1 { next; }
$9 ~ /^\./ {next; }
$3 != $9 {
print "user " $9 " mailbox is owned by " $3
}
$1 != "-rw-------" {
print "user " $9 " mailbox is " $1 ", group " $4
}' > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking mailbox ownership.\n"
cat $OUTPUT
fi
fi
# NFS exports shouldn't be globally exported
#
if checkyesno check_nfs && [ -f /etc/exports ]; then
awk '{
# ignore comments and blank lines
if ($0 ~ /^\#/ || $0 ~ /^$/ )
next;
readonly = 0;
for (i = 2; i <= NF; ++i) {
if ($i ~ /-ro/)
readonly = 1;
else if ($i !~ /^-/)
next;
}
if (readonly)
print "File system " $1 " globally exported, read-only."
else
print "File system " $1 " globally exported, read-write."
}' < /etc/exports > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking for globally exported file systems.\n"
cat $OUTPUT
fi
fi
# Display any changes in setuid files and devices.
#
if checkyesno check_devices; then
> $ERR
(find / \( ! -fstype local -o -fstype fdesc -o -fstype kernfs \
-o -fstype null \
-o -fstype procfs \) -a -prune -o \
\( \( -perm -u+s -a ! -type d \) -o \
\( -perm -g+s -a ! -type d \) -o \
-type b -o -type c \) -print0 | \
xargs -0 ls -ldgTq | sort +9 > $LIST) 2> $OUTPUT
# Display any errors that occurred during system file walk.
if [ -s $OUTPUT ] ; then
printf "Setuid/device find errors:\n" >> $ERR
cat $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
# Display any changes in the setuid file list.
egrep -v '^[bc]' $LIST > $TMP1
if [ -s $TMP1 ] ; then
# Check to make sure uudecode isn't setuid.
if grep -w uudecode $TMP1 > /dev/null ; then
printf "\nUudecode is setuid.\n" >> $ERR
fi
file=$work_dir/setuid
migrate_file "$backup_dir/setuid" "$file"
CUR=${file}.current
BACK=${file}.backup
if [ -s $CUR ] ; then
if cmp -s $CUR $TMP1 ; then
:
else
> $TMP2
join -110 -210 -v2 $CUR $TMP1 > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Setuid additions:\n" >> $ERR
tee -a $TMP2 < $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
join -110 -210 -v1 $CUR $TMP1 > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Setuid deletions:\n" >> $ERR
tee -a $TMP2 < $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
sort -k10 $TMP2 $CUR $TMP1 | \
sed -e 's/[ ][ ]*/ /g' | \
uniq -u > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Setuid changes:\n" >> $ERR
column -t $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
backup_file update $TMP1 $CUR $BACK
fi
else
printf "Setuid additions:\n" >> $ERR
column -t $TMP1 >> $ERR
printf "\n" >> $ERR
backup_file add $TMP1 $CUR $BACK
fi
fi
# Check for block and character disk devices that are readable or
# writable or not owned by root.operator.
>$TMP1
DISKLIST="ccd ch hk hp ld md ra raid rb rd rl rx \
sd se ss uk up vnd wd xd xy"
# DISKLIST="$DISKLIST ct mt st wt"
for i in $DISKLIST; do
egrep "^b.*/${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1
egrep "^c.*/r${i}[0-9][0-9]*[a-p]$" $LIST >> $TMP1
done
awk '$3 != "root" || $4 != "operator" || $1 !~ /.rw-r-----/ \
{ printf "Disk %s is user %s, group %s, permissions %s.\n", \
$11, $3, $4, $1; }' < $TMP1 > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "\nChecking disk ownership and permissions.\n" >> $ERR
cat $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
# Display any changes in the device file list.
egrep '^[bc]' $LIST | sort -k11 > $TMP1
if [ -s $TMP1 ] ; then
file=$work_dir/device
migrate_file "$backup_dir/device" "$file"
CUR=${file}.current
BACK=${file}.backup
if [ -s $CUR ] ; then
if cmp -s $CUR $TMP1 ; then
:
else
> $TMP2
join -111 -211 -v2 $CUR $TMP1 > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Device additions:\n" >> $ERR
tee -a $TMP2 < $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
join -111 -211 -v1 $CUR $TMP1 > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Device deletions:\n" >> $ERR
tee -a $TMP2 < $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
# Report any block device change. Ignore
# character devices, only the name is
# significant.
cat $TMP2 $CUR $TMP1 | \
sed -e '/^c/d' | \
sort -k11 | \
sed -e 's/[ ][ ]*/ /g' | \
uniq -u > $OUTPUT
if [ -s $OUTPUT ] ; then
printf "Block device changes:\n" >> $ERR
column -t $OUTPUT >> $ERR
printf "\n" >> $ERR
fi
backup_file update $TMP1 $CUR $BACK
fi
else
printf "Device additions:\n" >> $ERR
column -t $TMP1 >> $ERR
printf "\n" >> $ERR
backup_file add $TMP1 $CUR $BACK >> $ERR
fi
fi
if [ -s $ERR ] ; then
printf "\nChecking setuid files and devices:\n"
cat $ERR
printf "\n"
fi
fi
# Check special files.
# Check system binaries.
#
# Create the mtree tree specifications using:
# mtree -cx -pDIR -kmd5,uid,gid,mode,nlink,size,link,time > DIR.secure
# chown root:wheel DIR.secure
# chmod u+r,go= DIR.secure
#
# Note, this is not complete protection against Trojan horsed binaries, as
# the hacker can modify the tree specification to match the replaced binary.
# For details on really protecting yourself against modified binaries, see
# the mtree(8) manual page.
#
if checkyesno check_mtree; then
if checkyesno check_mtree_follow_symlinks; then
check_mtree_flags="-L"
else
check_mtree_flags=""
fi
mtree -e -l -p / $check_mtree_flags -f $SPECIALSPEC 3>&1 >$OUTPUT 2>&3 |
grep -v '^mtree: dev/tty: Device not configured$' >&2
if [ -s $OUTPUT ]; then
printf "\nChecking special files and directories.\n"
cat $OUTPUT
fi
for file in /etc/mtree/*.secure; do
[ $file = '/etc/mtree/*.secure' ] && continue
tree=`sed -n -e '3s/.* //p' -e 3q $file`
mtree $check_mtree_flags -f $file -p $tree > $TMP1
if [ -s $TMP1 ]; then
printf "\nChecking $tree:\n"
cat $TMP1
fi
done > $OUTPUT
if [ -s $OUTPUT ]; then
printf "\nChecking system binaries:\n"
cat $OUTPUT
fi
fi
# Backup disklabels of available disks
#
if checkyesno check_disklabels; then
# migrate old disklabels
for file in `ls -1d $backup_dir/$backup_dir/disklabel.* \
$backup_dir/disklabel.* 2>/dev/null`; do
migrate_file "$file" "$work_dir/${file##*/}"
done
# generate list of old disklabels & fdisks and remove them
ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null |
egrep -v '\.(backup|current)(,v)?$' > $LABELS
xargs rm < $LABELS
# generate disklabels of all disks excluding: cd fd md
disks=`iostat -x | awk 'NR > 1 && $1 !~ /^[cfm]d/ { print $1; }'`
for i in $disks; do
disklabel $i > "$work_dir/disklabel.$i" 2>/dev/null
done
# if fdisk is available, generate fdisks for: ed ld sd wd
if [ -x /sbin/fdisk ]; then
disks=`iostat -x| awk 'NR > 1 && $1 ~ /^[elsw]d/ { print $1; }'`
for i in $disks; do
/sbin/fdisk $i > "$work_dir/fdisk.$i" 2>/dev/null
done
fi
# append list of new disklabels and fdisks
ls -1d $work_dir/disklabel.* $work_dir/fdisk.* 2>/dev/null |
egrep -v '\.(backup|current)(,v)?$' >> $LABELS
CHANGELIST="$LABELS $CHANGELIST"
fi
# Check for changes in the list of installed pkgs
#
if checkyesno check_pkgs && [ -d $pkgdb_dir ]; then
pkgs=$work_dir/pkgs
migrate_file "$backup_dir/pkgs" "$pkgs"
( cd $pkgdb_dir
pkg_info | sort
echo ""
find . \( -name +REQUIRED_BY -o -name +CONTENTS \) -print0 |
xargs -0 ls -ldgTq | sort -t. +1 | sed -e 's, \./, ,'
) > $pkgs
echo "$pkgs" > $PKGS
CHANGELIST="$PKGS $CHANGELIST"
fi
# List of files that get backed up and checked for any modifications.
# Any changes cause the files to rotate.
#
if checkyesno check_changelist ; then
mtree -D -k type -f $SPECIALSPEC -E exclude |
sed '/^type=file/!d ; s/type=file \.//' | unvis > $CHANGEFILES
(
# Add other files which might dynamically exist:
# /etc/ifconfig.*
# /etc/raid*.conf
# /etc/rc.d/*
# /etc/rc.conf.d/*
#
echo "/etc/ifconfig.*"
echo "/etc/raid*.conf"
echo "/etc/rc.d/*"
echo "/etc/rc.conf.d/*"
# Add /etc/changelist
#
if [ -s /etc/changelist ]; then
grep -v '^#' /etc/changelist
fi
) | while read file; do
case "$file" in
*[\*\?\[]*) # If changelist line is a glob ...
# ... expand possible backup files
#
ls -1d $(echo $backup_dir/${file}.current) 2>/dev/null \
| sed "s,^$backup_dir/,, ; s,\.current$,,"
# ... expand possible files
#
ls -1d $(echo $file) 2>/dev/null
;;
*)
# Otherwise, just print the filename
echo $file
;;
esac
done >> $CHANGEFILES
CHANGELIST="$CHANGEFILES $CHANGELIST"
fi
# Special case backups, including the master password file and
# ssh private host keys. The normal backup mechanisms for
# $check_changelist (see below) also print out the actual file
# differences and we don't want to do that for these files
#
echo $MP > $TMP1 # always add /etc/master.passwd
mtree -D -k type -f $SPECIALSPEC -I nodiff |
sed '/^type=file/!d ; s/type=file \.//' | unvis >> $TMP1
grep -v '^$' $TMP1 | sort -u > $TMP2
while read file; do
backup_and_diff "$file" no
done < $TMP2
if [ -n "$CHANGELIST" ]; then
grep -h -v '^$' $CHANGELIST | sort -u > $TMP1
comm -23 $TMP1 $TMP2 | while read file; do
backup_and_diff "$file" yes
done
fi
if [ -f /etc/security.local ]; then
. /etc/security.local > $OUTPUT 2>&1
if [ -s $OUTPUT ] ; then
printf "\nRunning /etc/security.local:\n"
cat $OUTPUT
fi
fi