#!/usr/bin/bash
# vim: cindent:shiftwidth=4:tabstop=4:smarttab:textwidth=100

set -o posix
set -o errexit
set -o pipefail
set -o nounset
#set -o xtrace

#$title$ Application Armor - Linux kernel security module
#$check$ no denied actions
#$ref$ /etc/apparmor.d/, aa-logprof(8), /var/log/audit/
#$author$ Rafal Rzeczkowski
#$version$ 0.7.3

level_check long

#CHANGELOG
#0.50	initial
#0.51	detect profiles in complain mode
#0.5.2	list unconfined processes for reference
#0.5.3	verify that AppArmor is enabled
#0.6.0	detect active profiles left in complain mode
#0.6.1	correct spelling in a help message
#0.6.2	correct ShellCheck suggestions
#0.6.3	suppress aa-logprof output
#0.6.4	cleanup unconfined process list display
#0.7.0	detect loaded profiles without backing configuration
#0.7.1	detect disabled profiles
#0.7.2	detect disabled systemd service
#0.7.3	suppress error messages from aa-logprof

declare -r -a PROBLEMS=(LSM_DISABLED SERVICE_DISABLED UNKNOWN NEW_EVENTS COMPLAIN_CONFIGURED COMPLAIN_LOADED DISABLED_PROFILES)
test -n ${#PROBLEMS[@]}

declare -r PROFILES_DIR='/etc/apparmor.d'
declare -r KERNEL_DIR='/sys/kernel/security/apparmor'

if [[ -d "$KERNEL_DIR" ]]; then
	echodebug 'AppArmor LSM is enabled'
else
	fail caution "AppArmor LSM is disabled: missing $KERNEL_DIR"
	helpmsg 'AppArmor files are installed, but the LSM kernel interface is not activated yet. reboot?'
	problem LSM_DISABLED 0
	exit
fi

if command -v systemctl >/dev/null; then
	apparmor_service_status=$(systemctl is-enabled apparmor)
	if [[ "$apparmor_service_status" = 'enabled' ]]; then
		echodebug 'AppArmor systemd service is enabled'
	else
		fail caution "AppArmor service is $apparmor_service_status"
		helpmsg 'enable and start the service with systemctl'
		problem SERVICE_DISABLED 0
		exit
	fi
fi

unknown_profiles=$(aa-remove-unknown -n)
if [[ -z "$unknown_profiles" ]]; then
	echodebug 'no unknown profiles'
else
	fail caution 'profiles loaded without backing configuration files'
	helpmsg 'profile identifiers have been updated since AppArmor configuration was loaded'
	problem UNKNOWN 0
	exit
fi

if [[ $DEBUG -gt 0 ]]; then
	echodebug 'unconfined process list:'
	ps --deselect --ppid 2 --deselect --pid 2 --context |
		awk '{if (index($0,"(enforce)") || $1=="1" || index($0,"syscheck "))
			next
		else
			print $0
		}'
fi

# scan the log file and check if
# there are new AppArmor events that are not covered by the existing profile set
if aa-logprof >'/dev/null' 2>&1 <'/dev/null'; then
	echodebug 'no new events'
else
	fail warning 'new DENIED events in audit log'
	helpmsg 'run {aa-logprof} and decide if the registered event was a result of a malicious attack or was a part of normal program function'
	problem NEW_EVENTS 0
	exit
fi

if grep --quiet --fixed-strings --recursive --regexp='flags=(complain)' $PROFILES_DIR; then
	fail caution 'some configured profiles are still not enforced'
	helpmsg "use {aa-enforce} to change profiles in $PROFILES_DIR from {complain} to {enforce} mode"
	problem COMPLAIN_CONFIGURED 0
	exit
else
	echodebug 'no configured profiles in complain mode'
fi

# shellcheck disable=SC2009 # full ps is needed to display security labels
if ps --deselect --ppid 2 -o label | grep --quiet --fixed-strings --regexp='(complain)'; then
	fail caution 'some active profiles are still not enforced'
	helpmsg 'use {service apparmor reload} to update the active profile state'
	problem COMPLAIN_LOADED 0
	exit
else
	echodebug 'no active profiles in complain mode'
fi

if [[ -d '/etc/apparmor.d/disable' ]]; then
	fail caution 'some profiles are disabled'
	helpmsg 'remove {/etc/apparmor.d/disable} directory'
	problem DISABLED_PROFILES 0
	exit
else
	echodebug 'no disabled profiles'
fi

ok
