6b45f4ed79
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.)
1086 lines
28 KiB
Bash
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
|