mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00

... in aa-teardown (actually everything that uses rc.apparmor.functions) and aa-remove-unknown. Fixes: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2093797
339 lines
9 KiB
Bash
339 lines
9 KiB
Bash
#!/bin/sh
|
|
# ----------------------------------------------------------------------
|
|
# Copyright (c) 1999-2008 NOVELL (All rights reserved)
|
|
# Copyright (c) 2009-2018 Canonical Ltd. (All rights reserved)
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of version 2 of the GNU General Public
|
|
# License published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, contact Novell, Inc.
|
|
# ----------------------------------------------------------------------
|
|
# rc.apparmor.functions by Steve Beattie
|
|
#
|
|
# NOTE: rc.apparmor initscripts that source this file need to implement
|
|
# the following set of functions:
|
|
# aa_action
|
|
# aa_log_action_start
|
|
# aa_log_action_end
|
|
# aa_log_success_msg
|
|
# aa_log_warning_msg
|
|
# aa_log_failure_msg
|
|
# aa_log_skipped_msg
|
|
# aa_log_daemon_msg
|
|
# aa_log_end_msg
|
|
|
|
# Some nice defines that we use
|
|
|
|
PARSER=/sbin/apparmor_parser
|
|
PARSER_OPTS=
|
|
# Suppress warnings when booting in quiet mode
|
|
if [ "${QUIET:-no}" = yes ] || [ "${quiet:-n}" = y ]; then
|
|
PARSER_OPTS="$PARSER_OPTS --quiet"
|
|
fi
|
|
|
|
if [ -d /etc/apparmor.d ] ; then
|
|
PROFILE_DIRS=/etc/apparmor.d
|
|
else
|
|
aa_log_warning_msg "Unable to find profiles directory, installation problem?"
|
|
fi
|
|
|
|
# Eg. snapd policy might need this on some systems if loading policy
|
|
# during early boot if not using the snapd unit file
|
|
ADDITIONAL_PROFILE_DIR=
|
|
if [ -n "$ADDITIONAL_PROFILE_DIR" ] && [ -d "$ADDITIONAL_PROFILE_DIR" ]; then
|
|
PROFILE_DIRS="$PROFILE_DIRS $ADDITIONAL_PROFILE_DIR"
|
|
fi
|
|
AA_STATUS=/usr/sbin/aa-status
|
|
SECURITYFS=/sys/kernel/security
|
|
SFS_MOUNTPOINT="${SECURITYFS}/apparmor"
|
|
|
|
# keep exit status from parser during profile load. 0 is good, 1 is bad
|
|
STATUS=0
|
|
|
|
# Test if the apparmor "module" is present.
|
|
is_apparmor_present() {
|
|
[ -d /sys/module/apparmor ]
|
|
}
|
|
|
|
# Checks to see if the current container is capable of having internal AppArmor
|
|
# profiles that should be loaded. Callers of this function should have already
|
|
# verified that they're running inside of a container environment with
|
|
# something like `systemd-detect-virt --container`.
|
|
#
|
|
# The only known container environments capable of supporting internal policy
|
|
# are LXD and LXC environments, and Windows Subsystem for Linux.
|
|
#
|
|
# Returns 0 if the container environment is capable of having its own internal
|
|
# policy and non-zero otherwise.
|
|
#
|
|
# IMPORTANT: This function will return 0 in the case of a non-LXD/non-LXC
|
|
# system container technology being nested inside of a LXD/LXC container that
|
|
# utilized an AppArmor namespace and profile stacking. The reason 0 will be
|
|
# returned is because .ns_stacked will be "yes" and .ns_name will still match
|
|
# "lx[dc]-*" since the nested system container technology will not have set up
|
|
# a new AppArmor profile namespace. This will result in the nested system
|
|
# container's boot process to experience failed policy loads but the boot
|
|
# process should continue without any loss of functionality. This is an
|
|
# unsupported configuration that cannot be properly handled by this function.
|
|
is_container_with_internal_policy() {
|
|
# this function is sometimes called independently of
|
|
# is_apparmor_loaded(), so also define this here.
|
|
local ns_stacked_path="${SFS_MOUNTPOINT}/.ns_stacked"
|
|
local ns_name_path="${SFS_MOUNTPOINT}/.ns_name"
|
|
local ns_stacked
|
|
local ns_name
|
|
|
|
# WSL needs to be detected explicitly
|
|
if [ -x /usr/bin/systemd-detect-virt ] && \
|
|
[ "$(systemd-detect-virt --container)" = "wsl" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if ! [ -f "$ns_stacked_path" ] || ! [ -f "$ns_name_path" ]; then
|
|
return 1
|
|
fi
|
|
|
|
read -r ns_stacked < "$ns_stacked_path"
|
|
if [ "$ns_stacked" != "yes" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# LXD, Incus and LXC set up AppArmor namespaces starting with "lxd-",
|
|
# "incus-" and "lxc-", respectively. Return non-zero for all other
|
|
# namespace identifiers.
|
|
read -r ns_name < "$ns_name_path"
|
|
if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
|
|
[ "${ns_name#incus-*}" = "$ns_name" ] && \
|
|
[ "${ns_name#lxc-*}" = "$ns_name" ]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
__parse_profiles_dir() {
|
|
local parser_cmd="$1"
|
|
local profile_dir="$2"
|
|
local status=0
|
|
|
|
if [ ! -d "$profile_dir" ]; then
|
|
aa_log_failure_msg "Profile directory not found: $profile_dir"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$(ls "$profile_dir"/)" ]; then
|
|
aa_log_failure_msg "No profiles found in $profile_dir"
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2086
|
|
if ! "$PARSER" $PARSER_OPTS "$parser_cmd" -- "$profile_dir"; then
|
|
status=1
|
|
aa_log_failure_msg "At least one profile failed to load"
|
|
fi
|
|
return "$status"
|
|
}
|
|
|
|
parse_profiles() {
|
|
# get parser arg
|
|
case "$1" in
|
|
load)
|
|
PARSER_CMD="--add"
|
|
PARSER_MSG="Loading AppArmor profiles "
|
|
;;
|
|
reload)
|
|
PARSER_CMD="--replace"
|
|
PARSER_MSG="Reloading AppArmor profiles "
|
|
;;
|
|
*)
|
|
aa_log_failure_msg "required 'load' or 'reload'"
|
|
exit 1
|
|
;;
|
|
esac
|
|
aa_log_action_start "$PARSER_MSG"
|
|
# run the parser on all of the apparmor profiles
|
|
if [ ! -f "$PARSER" ]; then
|
|
aa_log_failure_msg "AppArmor parser not found"
|
|
exit 1
|
|
fi
|
|
|
|
for profile_dir in $PROFILE_DIRS; do
|
|
__parse_profiles_dir "$PARSER_CMD" "$profile_dir" || STATUS=$?
|
|
done
|
|
|
|
aa_log_action_end "$STATUS"
|
|
return "$STATUS"
|
|
}
|
|
|
|
is_apparmor_loaded() {
|
|
if ! is_securityfs_mounted ; then
|
|
mount_securityfs
|
|
fi
|
|
|
|
if [ -f "${SFS_MOUNTPOINT}/profiles" ]; then
|
|
return 0
|
|
fi
|
|
|
|
is_apparmor_present
|
|
|
|
return $?
|
|
}
|
|
|
|
is_securityfs_mounted() {
|
|
test -d "$SECURITYFS" -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
|
|
return $?
|
|
}
|
|
|
|
mount_securityfs() {
|
|
if grep -q securityfs /proc/filesystems ; then
|
|
aa_action "Mounting securityfs on $SECURITYFS" \
|
|
mount -t securityfs securityfs "$SECURITYFS"
|
|
return $?
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
apparmor_start() {
|
|
aa_log_daemon_msg "Starting AppArmor"
|
|
if ! is_apparmor_present ; then
|
|
aa_log_failure_msg "Starting AppArmor - failed, To enable AppArmor, ensure your kernel is configured with CONFIG_SECURITY_APPARMOR=y then add 'security=apparmor apparmor=1' to the kernel command line"
|
|
aa_log_end_msg 1
|
|
return 1
|
|
elif ! is_apparmor_loaded ; then
|
|
aa_log_failure_msg "Starting AppArmor - AppArmor control files aren't available under /sys/kernel/security/, please make sure securityfs is mounted."
|
|
aa_log_end_msg 1
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
|
|
aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
|
|
aa_log_end_msg 1
|
|
return 1
|
|
fi
|
|
|
|
# if there is anything in the profiles file don't load
|
|
if ! read -r _ < "$SFS_MOUNTPOINT/profiles"; then
|
|
parse_profiles load
|
|
else
|
|
aa_log_skipped_msg ": already loaded with profiles."
|
|
return 0
|
|
fi
|
|
aa_log_end_msg 0
|
|
return 0
|
|
}
|
|
|
|
remove_profiles() {
|
|
|
|
# removing profiles as we directly read from apparmorfs
|
|
# doesn't work, since we are removing entries which screws up
|
|
# our position. Lets hope there are never enough profiles to
|
|
# overflow the variable
|
|
if ! is_apparmor_loaded ; then
|
|
aa_log_failure_msg "AppArmor module is not loaded"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -w "$SFS_MOUNTPOINT/.remove" ] ; then
|
|
aa_log_failure_msg "Root privileges not available"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -x "$PARSER" ] ; then
|
|
aa_log_failure_msg "Unable to execute AppArmor parser"
|
|
return 1
|
|
fi
|
|
|
|
retval=0
|
|
# We filter child profiles as removing the parent will remove
|
|
# the children
|
|
sed -e "s/ (\(enforce\|complain\|prompt\|kill\|unconfined\))$//" "$SFS_MOUNTPOINT/profiles" | \
|
|
LC_COLLATE=C sort | grep -v // | {
|
|
while read -r profile ; do
|
|
printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
|
|
rc=$?
|
|
if [ "$rc" -ne 0 ] ; then
|
|
retval=$rc
|
|
aa_log_failure_msg "Unloading profile '$profile' failed"
|
|
fi
|
|
done
|
|
return "$retval"
|
|
}
|
|
}
|
|
|
|
apparmor_stop() {
|
|
aa_log_daemon_msg "Unloading AppArmor profiles"
|
|
remove_profiles
|
|
rc=$?
|
|
aa_log_end_msg "$rc"
|
|
return "$rc"
|
|
}
|
|
|
|
apparmor_kill() {
|
|
if ! is_apparmor_loaded ; then
|
|
aa_log_failure_msg "AppArmor module is not loaded"
|
|
return 1
|
|
fi
|
|
|
|
aa_log_failure_msg "apparmor_kill() is no longer supported because AppArmor can't be built as a module"
|
|
return 1
|
|
}
|
|
|
|
__apparmor_restart() {
|
|
if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
|
|
aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
|
|
return 4
|
|
fi
|
|
|
|
aa_log_daemon_msg "Restarting AppArmor"
|
|
|
|
parse_profiles reload
|
|
|
|
rc=$?
|
|
aa_log_end_msg "$rc"
|
|
return "$rc"
|
|
}
|
|
|
|
apparmor_restart() {
|
|
if ! is_apparmor_loaded ; then
|
|
apparmor_start
|
|
rc=$?
|
|
return "$rc"
|
|
fi
|
|
|
|
__apparmor_restart
|
|
return $?
|
|
}
|
|
|
|
apparmor_try_restart() {
|
|
if ! is_apparmor_loaded ; then
|
|
return 0
|
|
fi
|
|
|
|
__apparmor_restart
|
|
return $?
|
|
}
|
|
|
|
apparmor_status () {
|
|
if test -x "$AA_STATUS" ; then
|
|
"$AA_STATUS" --verbose
|
|
return $?
|
|
fi
|
|
if ! is_apparmor_loaded ; then
|
|
echo "AppArmor is not loaded."
|
|
rc=1
|
|
else
|
|
echo "AppArmor is enabled."
|
|
rc=0
|
|
fi
|
|
echo "Install the apparmor-utils package to receive more detailed"
|
|
echo "status information here (or examine $SFS_MOUNTPOINT directly)."
|
|
|
|
return "$rc"
|
|
}
|