NetBSD/etc/security
pgoyette 6b45f4ed79 Set the redirection correctly, so that stderr gets duped to the
already redirected stdout, rather than duping stdout to stderr!

Without this fix, the disklabel output is included in the log file
rather than being discarded as intended.  (The purpose of running
disklabel this first time is only to check for success.)
2015-04-20 22:46:35 +00:00

1086 lines
28 KiB
Bash

#!/bin/sh -
#
# $NetBSD: security,v 1.120 2015/04/20 22:46:35 pgoyette 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
if [ -s /etc/pkgpath.conf ]; then
. /etc/pkgpath.conf
fi
# Set reasonable defaults (if they're not set in security.conf)
#
backup_dir=${backup_dir:-/var/backups}
max_loginlen=${max_loginlen:-8}
max_grouplen=${max_grouplen:-8}
pkg_admin=${pkg_admin:-/usr/sbin/pkg_admin}
pkg_info=${pkg_info:-/usr/sbin/pkg_info}
# 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 -t _securedir) || 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.$$
LVM_LABELS=lvm.$$
PKGS=pkgs.$$
CHANGEFILES=changefiles.$$
SPECIALSPEC=specialspec.$$
if [ -n "${pkgdb_dir}" ]; then
echo "WARNING: Setting pkgdb_dir in security.conf(5) is deprecated"
echo "WARNING: Please define PKG_DBDIR in pkg_install.conf(5) instead"
_compat_K_flag="-K ${pkgdb_dir}"
fi
have_pkgs() {
$pkg_info ${_compat_K_flag} -q -E '*'
}
# 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
checkyesno check_passwd_permit_nonalpha \
&& permit_nonalpha=1 || permit_nonalpha=0
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" \
-v "permit_nonalpha=$permit_nonalpha" \
'
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;
}
# NIS compat entry?
compatline = $1 ~ "^[\\+-]";
if (compatline) {
if ($1 == "+" && NF == 1) {
next;
}
sub("^.", "", $1);
}
if (NF != 10)
printf "Line %d has the wrong number of fields.\n", NR;
if (compatline) {
if ($3 == 0)
printf "Line %d includes entries with uid 0.\n",
NR;
if ($1 == "")
next;
}
if (!permit_nonalpha &&
$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 == "" && !compatline && !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 !~ /^\$sha1/ &&
$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 (compatline && $10 == "") {
# nothing
} 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 != "" && $3 < 0)
printf "Login %s has a negative user id.\n", $1;
if ($4 != "" && $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
awk -v "permit_dups_list=$check_passwd_permit_dups" \
'
BEGIN {
split(permit_dups_list, a);
for (i in a) permit_dups[a[i]]++;
}
{
if (!permit_dups[$1])
print $2;
}' < $MPBYUID | uniq -d > $TMP2
if [ -s $TMP2 ] ; then
printf "\n$MP has duplicate user ids.\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
dupgroups=""
for group in $(cat $OUTPUT) ; do
gcount=$(awk -F: "/$group/ { print \$1,\$3 }" $GRP |
sort -u | wc -l)
if [ $gcount -gt 1 ]; then
dupgroups="$dupgroups $group"
fi
done
if [ ! -z $dupgroups ] ; then
printf "\n$GRP has duplicate group names.\n"
printf "$dupgroups\n"
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" \
-v "permit_owners_list=$check_homes_permit_other_owner" '
BEGIN {
split(permit_owners_list, a);
for (i in a) permit_owners[a[i]]++;
}
$1 != $4 && $4 != "root" && !permit_owners[$1] \
{ print "user " $1 " home directory is owned by " $4 }
$2 ~ /^d....w/ && (!usergroups || $5 != $1) \
{ print "user " $1 " home directory is group writable" }
$2 ~ /^d.......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" \
-v "permit_owners_list=$check_homes_permit_other_owner" '
BEGIN {
split(permit_owners_list, a);
for (i in a) permit_owners[a[i]]++;
}
$1 != $5 && $5 != "root" && !permit_owners[$1] \
{ 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" \
-v "permit_owners_list=$check_homes_permit_other_owner" '
BEGIN {
split(permit_owners_list, a);
for (i in a) permit_owners[a[i]]++;
}
$1 != $5 && $5 != "root" && !permit_owners[$1] \
{ 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;
# manage line continuation
while ($NF ~ /^\\$/) {
$NF = "";
line = $0 "";
getline;
$0 = line $0 "";
}
delete dir;
readonly = ndir = 0;
for (i = 1; i <= NF; ++i) {
if ($i ~ /^\//) dir[ndir++] = $i;
else if ($i ~ /^-/) {
if ($i ~ /^-(ro|o)$/) readonly = 1;
if ($i ~ /^-network/) next;
}
else next;
}
if (readonly)
for (item in dir)
rodir[nrodir++] = dir[item];
else
for (item in dir)
rwdir[nrwdir++] = dir[item];
}
END {
if (nrodir) {
printf("Globally exported file system%s, read-only:\n",
nrodir > 1 ? "s" : "");
for (item in rodir)
printf("\t%s\n", rodir[item]);
}
if (nrwdir) {
printf("Globally exported file system%s, read-write:\n",
nrwdir > 1 ? "s" : "");
for (item in rwdir)
printf("\t%s\n", rwdir[item]);
}
}' < /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
(
# Convert check_devices_ignore_fstypes="foo !bar bax"
# into "-fstype foo -o ! -fstype bar -o -fstype bax"
# and check_devices_ignore_paths="/foo !/bar /bax"
# into " -path /foo -o ! -path /bar -o -path /bax"
#
ignexpr=$(\
echo $check_devices_ignore_fstypes | \
sed -e's/\(!*\)\([^[:space:]]\{1,\}\)/-o \1 -fstype \2/g' ; \
echo $check_devices_ignore_paths | \
sed -e's/\(!*\)\([^[:space:]]\{1,\}\)/-o \1 -path \2/g' \
)
# Massage the expression into ( $ignexpr ) -a -prune -o
if [ -n "${ignexpr}" ]; then
ignexpr=$(\
echo $ignexpr | \
sed -e 's/^-o /( /' \
-e 's/$/ ) -a -prune -o/' \
)
fi
find / $ignexpr \
\( \( -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 & wedges,
# and remove them
ls -1d $work_dir/disklabel.* $work_dir/fdisk.* $work_dir/wedges.* \
2>/dev/null |
egrep -v '\.(backup|current)(,v)?$' > $LABELS
xargs rm < $LABELS
disks="$(iostat -x | cut -f 1 -d ' ' )"
# generate disklabels of all disks excluding: cd fd md dk st
# nfs and "device" (the header of iostat)
for i in $disks; do
case $i in
[cfm]d[0-9]*|dk[0-9]*|st[0-9]*|nfs[0-9]*|device)
;;
*)
if disklabel $i > /dev/null 2>&1; then
disklabel $i > "$work_dir/disklabel.$i"
fi
;;
esac
done
# if fdisk is available, generate fdisks for: ed ld sd wd
if [ -x /sbin/fdisk ]; then
for i in $disks; do
case $i in
[elsw]d[0-9]*)
/sbin/fdisk $i > "$work_dir/fdisk.$i" \
2>/dev/null
;;
esac
done
fi
# if dkctl is available, generate dkctl listwedges
# for: ed ld sd wd cgd ofdisk ra rl raid
if [ -x /sbin/dkctl ]; then
for i in $disks; do
case $i in
[elsw]d[0-9]*|cgd[0-9]*|ofdisk[0-9]*|r[al][0-9]*|raid[0-9]*)
if /sbin/dkctl $i listwedges -qe; then
/sbin/dkctl $i listwedges \
> "$work_dir/wedges.$i" 2>/dev/null
fi
;;
esac
done
fi
# append list of new disklabels, fdisks and wedges
ls -1d $work_dir/disklabel.* $work_dir/fdisk.* $work_dir/wedges.* \
2>/dev/null |
egrep -v '\.(backup|current)(,v)?$' >> $LABELS
CHANGELIST="$LABELS $CHANGELIST"
fi
if checkyesno check_lvm; then
# generate list of existing LVM elements Physical Volumes,
# Volume Groups and Logical Volumes.
if [ -x /sbin/lvm ]; then
lvm pvdisplay -m >"$work_dir/lvm.pv" 2>/dev/null
lvm vgdisplay -m >"$work_dir/lvm.vg" 2>/dev/null
lvm lvdisplay -m >"$work_dir/lvm.lv" 2>/dev/null
fi
ls -1d $work_dir/lvm.* 2>/dev/null |
egrep -v '\.(backup|current)(,v)?$'>> $LVM_LABELS
CHANGELIST="$CHANGELIST $LVM_LABELS"
fi
# Check for changes in the list of installed pkgs
#
if checkyesno check_pkgs && have_pkgs; then
pkgs=$work_dir/pkgs
migrate_file "$backup_dir/pkgs" "$pkgs"
pkg_dbdir=$(${pkg_admin} config-var PKG_DBDIR)
: ${pkg_dbdir:=/var/db/pkg}
( cd $pkg_dbdir
$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/*"
echo "/etc/lvm/backup/*"
echo "/etc/lvm/archive/*"
# 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 have_pkgs; then
if checkyesno check_pkg_vulnerabilities; then
${pkg_admin} ${_compat_K_flag} audit >${OUTPUT} 2>&1
if [ -s ${OUTPUT} ]; then
printf "\nInstalled vulnerable packages:\n"
cat ${OUTPUT}
fi
fi
if checkyesno check_pkg_signatures; then
${pkg_admin} ${_compat_K_flag} check >${OUTPUT} 2>&1
if [ $? -ne 0 ]; then
printf "\nFiles with invalid signatures:\n"
cat ${OUTPUT}
fi
fi
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