#!/bin/sh -
#
# Copyright (c) 2000  The FreeBSD Project
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#	@(#)security	5.3 (Berkeley) 5/28/91
# $FreeBSD$
#

PATH=/sbin:/bin:/usr/bin
LC_ALL=C; export LC_ALL
rc=0
LOG=/var/log
TMP=/var/run/_secure.$$

separator () {
	echo ''
	echo ''
}

catmsgs() {
  [ -f $LOG/messages.0.gz ] && zcat $LOG/messages.0.gz
  [ -f $LOG/messages.0 ] && cat $LOG/messages.0
  [ -f $LOG/messages ] && cat $LOG/messages
}

sflag=FALSE ignore=
while getopts ams c
do
  case "$c" in
    a) ignore="$ignore|^amd:";;
    m) ignore="$ignore|^mfs:";;
    s) sflag=TRUE;;
  esac
done

yesterday=`date -v-1d "+%b %e "`

host=`hostname`
[ $sflag = FALSE ] && echo "Subject: ${host} security check output"

umask 027

echo "checking setuid files and devices:"

# Don't have ncheck, but this does the equivalent of the commented out block.
# Note that one of the original problems, the possibility of overrunning
# the args to ls, is still here...
#
MP=`mount -t ufs | grep -v " nosuid" | awk '{ print $3 }' | sort`
set ${MP}
while [ $# -ge 1 ]; do
	mount=$1
	shift
	find $mount -xdev -type f \
		\( -perm -u+x -or -perm -g+x -or -perm -o+x \) \
		\( -perm -u+s -or -perm -g+s \) -print0
done | xargs -0 -n 20 ls -liTd | sort +10 > ${TMP}

if [ ! -f ${LOG}/setuid.today ]; then
	[ $rc -lt 1 ] && rc=1
	separator
	echo "no ${LOG}/setuid.today"
	cp ${TMP} ${LOG}/setuid.today || rc=3
fi

if ! cmp ${LOG}/setuid.today ${TMP} >/dev/null; then
	[ $rc -lt 1 ] && rc=1
	separator
	echo "${host} setuid diffs:"
	diff -w ${LOG}/setuid.today ${TMP}
	mv ${LOG}/setuid.today ${LOG}/setuid.yesterday || rc=3
	mv ${TMP} ${LOG}/setuid.today || rc=3
fi

# Show changes in the way filesystems are mounted
#
[ -n "$ignore" ] && cmd="egrep -v ${ignore#|}" || cmd=cat
if mount -p | $cmd > $TMP; then
	if [ ! -f $LOG/mount.today ]; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "no $LOG/mount.today"
		cp $TMP $LOG/mount.today || rc=3
	fi
	if ! cmp $LOG/mount.today $TMP >/dev/null 2>&1; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "$host changes in mounted filesystems:"
		diff -b $LOG/mount.today $TMP
		mv $LOG/mount.today $LOG/mount.yesterday || rc=3
		mv $TMP $LOG/mount.today || rc=3
	fi
fi

separator
echo "checking for uids of 0:"
n=$(awk -F: '$3==0 {print $1,$3}' /etc/master.passwd |
    tee /dev/stderr |
    sed -e '/^root 0$/d' -e '/^toor 0$/d' |
    wc -l)
[ $n -gt 0 -a $rc -lt 1 ] && rc=1

separator
echo "checking for passwordless accounts:"
n=$(awk -F: 'NF > 1 && $1 !~ /^[#+-]/ && $2=="" {print $0}' /etc/master.passwd |
    tee /dev/stderr | wc -l)
[ $n -gt 0 -a $rc -lt 1 ] && rc=1

# Show denied packets
#
if ipfw -a l 2>/dev/null | egrep "deny|reset|unreach" > ${TMP}; then
	if [ ! -f ${LOG}/ipfw.today ]; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "no ${LOG}/ipfw.today"
		cp ${TMP} ${LOG}/ipfw.today || rc=3
	fi

	if ! cmp ${LOG}/ipfw.today ${TMP} >/dev/null; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "${host} denied packets:"
		diff -b ${LOG}/ipfw.today ${TMP} | egrep "^>"
		mv ${LOG}/ipfw.today ${LOG}/ipfw.yesterday || rc=3
		mv ${TMP} ${LOG}/ipfw.today || rc=3
	fi
fi

# Show ipfw rules which have reached the log limit
#
IPFW_LOG_LIMIT=`sysctl -n net.inet.ip.fw.verbose_limit 2> /dev/null`
if [ $? -eq 0 -a "${IPFW_LOG_LIMIT}" -ne 0 ]; then
	ipfw -a l | grep " log " | perl -n -e \
		'/^\d+\s+(\d+)/; print if ($1 >= '$IPFW_LOG_LIMIT')' > ${TMP}
	if [ -s "${TMP}" ]; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "ipfw log limit reached:"
		cat ${TMP}
	fi
fi

# Show kernel log messages
#
if dmesg 2>/dev/null > ${TMP}; then
	if [ ! -f ${LOG}/dmesg.today ]; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "no ${LOG}/dmesg.today"
		cp ${TMP} ${LOG}/dmesg.today || rc=3
	fi

	if ! cmp ${LOG}/dmesg.today ${TMP} >/dev/null 2>&1; then
		[ $rc -lt 1 ] && rc=1
		separator
		echo "${host} kernel log messages:"
		diff -b ${LOG}/dmesg.today ${TMP} | egrep "^>"
		mv ${LOG}/dmesg.today ${LOG}/dmesg.yesterday || rc=3
		mv ${TMP} ${LOG}/dmesg.today || rc=3
	fi
fi

# Show login failures
#
separator
echo "${host} login failures:"
n=$(catmsgs | grep -i "^$yesterday.*login failure" | tee /dev/stderr | wc -l)
[ $n -gt 0 -a $rc -lt 1 ] && rc=1

# Show tcp_wrapper warning messages
#
separator
echo "${host} refused connections:"
n=$(catmsgs | grep -i "^$yesterday.*refused connect" | tee /dev/stderr | wc -l)
[ $n -gt 0 -a $rc -lt 1 ] && rc=1

rm -f ${TMP}

exit $rc