#!/bin/sh -ue

## Run chkrootkit daily and email results to the user
# This is run by the systemd timer or cron

# note that all variables can be changed by editing $CONFIG
CONFIG=/etc/chkrootkit/chkrootkit.conf

CHKROOTKIT=/usr/sbin/chkrootkit

LOG_DIR=/var/log/chkrootkit

OUTPUT="$LOG_DIR/log.today"
RAW_OUTPUT="$OUTPUT.raw"
EXPECTED_OUTPUT="$LOG_DIR/log.expected"

# If you do not want to use ionice, set IONICE="" in $CONFIG
if [ -x /usr/bin/ionice ] && /usr/bin/ionice -c3 true 2> /dev/null; then
	IONICE="/usr/bin/ionice -c3"
else
	IONICE=""
fi

TIMEOUT=1h
RUN_DAILY=false # $CONFIG should set this to 'true'
DIFF_MODE=false
RUN_DAILY_OPTS=""
MAILTO="" # CONFIG should set this to an email address/username to have the output sent
SUBJECT="[chkrootkit] alert for $(hostname --fqdn 2> /dev/null || hostname --short 2> /dev/null || echo "localhost")"

## lines matching patterns in here are omitted entirely from the output, if the file exists.
IGNORE_FILE="/etc/chkrootkit/chkrootkit.ignore"

## The output (after any IGNORE_FILE) is passed through $FILTER)
# see comments in /etc/chkrootkit/chkrootkit.conf
NETWORK_MANAGER="(/usr/lib/systemd/systemd-networkd|/usr/sbin/(NetworkManager|wpa_supplicant|dhclient|dhcpc?d[0-9]*))"
REPLACEMENT="<Standard network manager>[PID]"
FILTER_EXPECTED_MANAGERS="s!PACKET SNIFFER\(($NETWORK_MANAGER)\[[0-9]+\]\)!$REPLACEMENT!"

# If you have false positives you can use FILTER_MISC to make it more stable
FILTER_MISC=""

# Remove some lines from the output entirely, (set to "" to not remove anything)
# REMOVE_LINES="/^(lxcbr0|vz-containers): not promisc and no packet sniffer sockets/d"
REMOVE_LINES=""

# To disable all filtering you can set FILTER to "" or "cat"
# For more complicate filtering, set FILTER to a script to execute (stdin is the chkrootkit output)
FILTER="sed -r -e '$FILTER_EXPECTED_MANAGERS;$FILTER_MISC;$REMOVE_LINES'"

unset NETWORK_MANAGER REPLACEMENT FILTER_EXPECTED_MANAGERS REMOVE_LINES FILTER_MISC

if [ -r "$CONFIG" ]; then
	. "$CONFIG"
fi

# support upgrades from bullseye where $OLD_CONFIG was used
OLD_CONFIG=/etc/chkrootkit.conf
if [ -r "$OLD_CONFIG" ]; then
	echo "WARNING: $OLD_CONFIG is deprecated. Please put your settings in $CONFIG instead: $OLD_CONFIG will be ignored in a future release and should be deleted."
	. "$OLD_CONFIG"
fi

if [ "$RUN_DAILY" = "false" ]; then
	exit 0
fi

if [ ! -r "$IGNORE_FILE" ]; then
	IGNORE_FILE=/dev/null
fi

if [ -z "$FILTER" ]; then
	# explicitly disabled
	FILTER="cat"
elif ! echo "test" | eval "$FILTER" > /dev/null 2>&1; then
	echo "Ignoring invalid \$FILTER='$FILTER'"
	FILTER="cat"
fi

if [ -n "$TIMEOUT" ]; then
	TIMEOUT="timeout $TIMEOUT"
fi

# Run, filter, and save output
run() {
	CMD="$TIMEOUT $IONICE $CHKROOTKIT $RUN_DAILY_OPTS"
	{
		STATUS=0
		eval "$CMD" 2>&1 || STATUS=$?
		case "$STATUS" in
			124)
				echo "$CMD timed out ($TIMEOUT)"
				;;
			125 | 126 | 127 | 137)
				echo "Error running: $CMD"
				echo "Exit code: $STATUS: see timeout(1)"
				;;
		esac
	} | tee "$RAW_OUTPUT" 2>&1 \
		| eval "$FILTER" 2>&1 \
		| grep -E -v -f "$IGNORE_FILE" 2>&1 \
			> "$OUTPUT" 2>&1 || :

	# show results
	if [ "$DIFF_MODE" = "false" ]; then
		cat "$OUTPUT" 2>&1 || true
	elif [ ! -f "$EXPECTED_OUTPUT" ]; then
		echo "No file $LOG_DIR/log.expected"
		echo "This file should contain expected output from chkrootkit"
		echo
		echo "Today's run produced the following output:"
		echo "--- [ BEGIN: cat $OUTPUT ] ---"
		cat "$OUTPUT" 2>&1 || true
		echo "--- [ END: cat $OUTPUT ] ---"
		echo
		echo "To create this file containing all output from today's run, do (as root)"
		echo "# cp -a $OUTPUT $EXPECTED_OUTPUT"
		echo "# (note that unedited output is in $RAW_OUTPUT)"
	elif ! diff -q "$EXPECTED_OUTPUT" "$OUTPUT" > /dev/null 2>&1; then
		echo "chkrootkit output was not as expected."
		echo
		echo "The difference is:"
		echo "--- [ BEGIN: diff -u $EXPECTED_OUTPUT $OUTPUT ] ---"
		diff -u "$EXPECTED_OUTPUT" "$OUTPUT" 2>&1 || true
		echo "--- [ END: diff -u $EXPECTED_OUTPUT $OUTPUT ] ---"
		echo
		echo "To update the expected output, run (as root)"
		echo "#  cp -a -f $OUTPUT $EXPECTED_OUTPUT"
		echo "# (note that unedited output is in $RAW_OUTPUT)"
	fi
}

FILE="$LOG_DIR/chkrootkit-daily.log"
run > "$FILE"
if [ -s "$FILE" ]; then
	if [ -n "$MAILTO" ]; then
		if [ -n "${STARTED_BY_SYSTEMD-}" ]; then
			# run by systemd: product a line on stdout for the journal
			echo "sending alert to $MAILTO: $SUBJECT"
		fi
		mail -s "$SUBJECT" "$MAILTO" < "$FILE"
	else
		# output on stdout - this will end up in the journal (systemd)
		# or if running under cron will be emailed
		cat "$FILE"
	fi
fi
