6a61a211cf
/etc/security should produce no output (and thus suppress the report) when nothing is wrong. While we're here, use printf instead of two echos, like the rest of the script.
902 lines
23 KiB
Bash
902 lines
23 KiB
Bash
#!/bin/sh -
|
|
#
|
|
# $NetBSD: security,v 1.84 2003/10/01 04:29:03 jhawk Exp $
|
|
# from: @(#)security 8.1 (Berkeley) 6/9/93
|
|
#
|
|
|
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
|
|
|
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=secure1.$$
|
|
TMP1=secure2.$$
|
|
TMP2=secure3.$$
|
|
MPBYUID=secure4.$$
|
|
MPBYPATH=secure5.$$
|
|
LIST=secure6.$$
|
|
OUTPUT=secure7.$$
|
|
LABELS=secure8.$$
|
|
PKGS=secure9.$$
|
|
CHANGEFILES=secure10.$$
|
|
|
|
|
|
# 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
|
|
|
|
|
|
# Check the master password file syntax.
|
|
#
|
|
if checkyesno check_passwd; then
|
|
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
|
|
while read uid homedir; do
|
|
if [ -d ${homedir}/ ] ; then
|
|
file=`ls -ldgT ${homedir}`
|
|
printf -- "$uid $file\n"
|
|
fi
|
|
done < $MPBYPATH |
|
|
awk '$1 != $4 && $4 != "root" \
|
|
{ print "user " $1 " home directory is owned by " $4 }
|
|
$2 ~ /^-....w/ \
|
|
{ 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 '$1 != $5 && $5 != "root" \
|
|
{ print "user " $1 " " $2 " file is owned by " $5 }
|
|
$3 ~ /^-...r/ \
|
|
{ print "user " $1 " " $2 " file is group readable" }
|
|
$3 ~ /^-......r/ \
|
|
{ print "user " $1 " " $2 " file is other readable" }
|
|
$3 ~ /^-....w/ \
|
|
{ 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 '$1 != $5 && $5 != "root" \
|
|
{ print "user " $1 " " $2 " file is owned by " $5 }
|
|
$3 ~ /^-....w/ \
|
|
{ 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 -l /var/mail | \
|
|
awk ' NR == 1 { 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
|
|
for file in $special_files; do
|
|
[ ! -s $file ] && continue
|
|
mtree -e -l -p / $check_mtree_flags -f $file
|
|
done > $OUTPUT
|
|
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
|
|
for file in $special_files; do
|
|
[ ! -s $file ] && continue
|
|
mtree -D -k type -f $file -E exclude |
|
|
sed '/^type=file/!d ; s/type=file \.//'
|
|
done > $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
|
|
for file in $special_files; do
|
|
[ ! -s $file ] && continue
|
|
mtree -D -k type -f $file -I nodiff |
|
|
sed '/^type=file/!d ; s/type=file \.//'
|
|
done >> $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
|
|
if [ -s $OUTPUT ] ; then
|
|
printf "\nRunning /etc/security.local:\n"
|
|
cat $OUTPUT
|
|
fi
|
|
fi
|