2006-04-11 21:52:54 +00:00
#!/bin/sh
# ----------------------------------------------------------------------
2011-02-22 11:14:34 -06:00
# Copyright (c) 1999-2008 NOVELL (All rights reserved)
2018-10-30 14:34:19 +00:00
# Copyright (c) 2009-2018 Canonical Ltd. (All rights reserved)
2006-04-11 21:52:54 +00:00
#
# 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.
# ----------------------------------------------------------------------
2007-04-04 21:56:08 +00:00
# rc.apparmor.functions by Steve Beattie
2006-04-11 21:52:54 +00:00
#
2007-04-04 21:56:08 +00:00
# NOTE: rc.apparmor initscripts that source this file need to implement
2006-04-11 21:52:54 +00:00
# the following set of functions:
2007-04-04 21:56:08 +00:00
# aa_action
2009-11-11 10:51:05 -08:00
# aa_log_action_start
# aa_log_action_end
2007-04-04 21:56:08 +00:00
# aa_log_success_msg
# aa_log_warning_msg
# aa_log_failure_msg
# aa_log_skipped_msg
2009-11-11 10:51:05 -08:00
# aa_log_daemon_msg
# aa_log_end_msg
2006-04-11 21:52:54 +00:00
# Some nice defines that we use
2009-11-11 10:51:05 -08:00
PARSER=/sbin/apparmor_parser
2018-10-30 14:04:32 +00:00
PARSER_OPTS=
# Suppress warnings when booting in quiet mode
if [ "${QUIET:-no}" = yes ] || [ "${quiet:-n}" = y ]; then
PARSER_OPTS="$PARSER_OPTS --quiet"
fi
2006-04-11 21:52:54 +00:00
2018-12-28 16:30:41 +00:00
if [ -d /etc/apparmor.d ] ; then
2018-10-30 14:31:39 +00:00
PROFILE_DIRS=/etc/apparmor.d
2018-10-30 14:13:44 +00:00
else
aa_log_warning_msg "Unable to find profiles directory, installation problem?"
2006-04-11 21:52:54 +00:00
fi
2020-09-22 12:18:35 -07:00
# Eg. snapd policy might need this on some systems if loading policy
# during early boot if not using the snapd unit file
2020-09-22 12:09:47 -07:00
ADDITIONAL_PROFILE_DIR=
if [ -n "$ADDITIONAL_PROFILE_DIR" ] && [ -d "$ADDITIONAL_PROFILE_DIR" ]; then
2019-04-22 15:02:45 +02:00
PROFILE_DIRS="$PROFILE_DIRS $ADDITIONAL_PROFILE_DIR"
2018-10-30 14:31:39 +00:00
fi
2010-11-03 17:03:52 -07:00
AA_STATUS=/usr/sbin/aa-status
2007-04-04 21:23:42 +00:00
SECURITYFS=/sys/kernel/security
2019-06-21 19:22:15 +02:00
SFS_MOUNTPOINT="${SECURITYFS}/apparmor"
2006-04-11 21:52:54 +00:00
# keep exit status from parser during profile load. 0 is good, 1 is bad
STATUS=0
2008-01-03 22:27:20 +00:00
# Test if the apparmor "module" is present.
is_apparmor_present() {
2019-01-01 17:55:48 +00:00
[ -d /sys/module/apparmor ]
2008-01-03 22:27:20 +00:00
}
2018-10-30 14:34:19 +00:00
# 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
2021-11-08 21:21:09 +00:00
# are LXD and LXC environments, and Windows Subsystem for Linux.
2018-10-30 14:34:19 +00:00
#
# 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() {
set SFS_MOUNTPOINT in is_container_with_internal_policy()
is_container_with_internal_policy() is called independently of
apparmor_*() in the systemd unit and potentially other consumers of
rc.apparmor.functions. When the unit and rc.apparmor.functions functions
were rewritten, they were written so that SFS_MOUNTPOINT was only set in
is_apparmor_loaded(), but this is only called in apparmor_start(),
remove_profiles(), apparmor_kill(), apparmor_restart(), apparmor_try_restart()
and apparmor_status() and not is_container_with_internal_policy().
While it is clear that is_container_with_internal_policy() is meant to
be called before apparmor_start(), is is unclear why SFS_MOUNTPOINT is
only defined in is_apparmor_loaded(). There are several ways to fix
this:
1. update is_container_with_internal_policy() to call is_apparmor_loaded()
2. identify the callers of is_container_with_internal_policy() and have
them call is_apparmor_loaded()
3. reorganize the code to remove duplicate calls and assignments
4. define SFS_MOUNTPOINT along with SECURITYFS and MODULE, at the top
level
5. also define SFS_MOUNTPOINT in is_container_with_internal_policy()
'1' would result in redundant calls in many common cases since the
systemd unit would call is_apparmor_loaded() both in
is_container_with_internal_policy() and prior to other calls.
'2' would like break consumers of rc.apparmor.funcions, like
Debian/Ubuntu's profile-load.
'3' is perhaps ok, but requires more effort and is regression-prone.
'4' seems the simplest, most correct fix
'5' is what this patch implements, which is as simple as '4' but tries
to maintain the original author's intent of when to set SFS_MOUNTPOINT.
PR: https://gitlab.com/apparmor/apparmor/merge_requests/363
Signed-off-by: Jamie Strandboge <jamie@strandboge.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
2019-04-15 16:42:15 -05:00
# 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"
2018-10-30 14:34:19 +00:00
local ns_stacked
local ns_name
2021-11-08 21:21:09 +00:00
# WSL needs to be detected explicitly
2022-02-13 07:15:11 +00:00
if [ "$(systemd-detect-virt --container)" = "wsl" ]; then
2021-11-08 21:21:09 +00:00
return 0
fi
2018-10-30 14:34:19 +00:00
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 and LXC set up AppArmor namespaces starting with "lxd-" 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#lxc-*}" = "$ns_name" ]; then
return 1
fi
return 0
}
2018-10-30 14:31:39 +00:00
__parse_profiles_dir() {
local parser_cmd="$1"
local profile_dir="$2"
local status=0
2006-04-11 21:52:54 +00:00
2018-10-30 14:31:39 +00:00
if [ ! -d "$profile_dir" ]; then
aa_log_failure_msg "Profile directory not found: $profile_dir"
return 1
2006-04-11 21:52:54 +00:00
fi
2019-03-18 19:30:37 +01:00
if [ -z "$(ls "$profile_dir"/)" ]; then
2018-10-30 14:31:39 +00:00
aa_log_failure_msg "No profiles found in $profile_dir"
2006-08-04 17:16:47 +00:00
return 1
2006-04-11 21:52:54 +00:00
fi
2022-02-13 07:17:36 +00:00
# shellcheck disable=SC2086
2022-02-13 07:18:43 +00:00
if ! "$PARSER" $PARSER_OPTS "$parser_cmd" -- "$profile_dir"; then
2022-01-26 19:32:09 -08:00
status=1
aa_log_failure_msg "At least one profile failed to load"
fi
2019-04-22 15:02:45 +02:00
return "$status"
2018-10-30 14:31:39 +00:00
}
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
2009-11-11 10:51:05 -08:00
aa_log_action_end "$STATUS"
2019-04-22 15:02:45 +02:00
return "$STATUS"
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:23:42 +00:00
is_apparmor_loaded() {
if ! is_securityfs_mounted ; then
mount_securityfs
fi
2019-06-21 19:22:15 +02:00
if [ -f "${SFS_MOUNTPOINT}/profiles" ]; then
2007-04-04 21:23:42 +00:00
return 0
fi
2019-01-01 17:55:48 +00:00
is_apparmor_present
2008-01-03 22:27:20 +00:00
2007-04-04 21:23:42 +00:00
return $?
}
is_securityfs_mounted() {
2019-04-22 15:02:45 +02:00
test -d "$SECURITYFS" -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
2007-04-04 21:23:42 +00:00
return $?
}
mount_securityfs() {
if grep -q securityfs /proc/filesystems ; then
2019-04-22 15:02:45 +02:00
aa_action "Mounting securityfs on $SECURITYFS" \
mount -t securityfs securityfs "$SECURITYFS"
2007-04-04 21:23:42 +00:00
return $?
fi
return 0
}
2007-04-04 21:56:08 +00:00
apparmor_start() {
2009-11-11 10:51:05 -08:00
aa_log_daemon_msg "Starting AppArmor"
2018-11-03 07:15:16 -07:00
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
2006-04-11 21:52:54 +00:00
fi
if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
2007-04-04 21:56:08 +00:00
aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
2009-11-11 10:51:05 -08:00
aa_log_end_msg 1
2006-04-11 21:52:54 +00:00
return 1
fi
2008-11-07 12:53:37 +00:00
# if there is anything in the profiles file don't load
2022-02-13 07:20:46 +00:00
if ! read -r _line < "$SFS_MOUNTPOINT/profiles"; then
2006-04-11 21:52:54 +00:00
parse_profiles load
else
2011-08-13 14:13:49 +02:00
aa_log_skipped_msg ": already loaded with profiles."
return 0
2006-04-11 21:52:54 +00:00
fi
2009-11-11 10:51:05 -08:00
aa_log_end_msg 0
return 0
2006-04-11 21:52:54 +00:00
}
2006-12-12 10:54:44 +00:00
remove_profiles() {
2018-11-03 16:39:49 -07:00
# removing profiles as we directly read from apparmorfs
2006-04-11 21:52:54 +00:00
# doesn't work, since we are removing entries which screws up
# our position. Lets hope there are never enough profiles to
# overflow the variable
2007-04-04 21:23:42 +00:00
if ! is_apparmor_loaded ; then
2009-11-11 10:51:05 -08:00
aa_log_failure_msg "AppArmor module is not loaded"
2006-04-11 21:52:54 +00:00
return 1
fi
if [ ! -w "$SFS_MOUNTPOINT/.remove" ] ; then
2009-11-11 10:51:05 -08:00
aa_log_failure_msg "Root privileges not available"
2006-04-11 21:52:54 +00:00
return 1
fi
2019-04-22 15:02:45 +02:00
if [ ! -x "$PARSER" ] ; then
2009-11-11 10:51:05 -08:00
aa_log_failure_msg "Unable to execute AppArmor parser"
2006-04-11 21:52:54 +00:00
return 1
fi
retval=0
Attached is a patch to make the initscript not fail if /tmp is full
by converting the comm(1) usage on temporary files to an embedded
awk script. On both Ubuntu and OpenSUSE, a version of awk (mawk in
Ubuntu, gawk in OpenSUSE) is either a direct or indirect dependency
on the minimal or base package set, and the original reporter also
mentioned that an awk-based solution would be palatable in a way that
converting to bash, or using perl or python here would not be.
In the embedded awk script, I've tried to avoid gawk or mawk specific
behaviors or extensions; e.g. this is the reason for the call to sort
on the output of the awk script, rather than using gawk's asort(). But
please let me know if you see anything that shouldn't be portable
across awk implementations.
An additional issue that is fixed in both scripts is handling child
profiles (e.g. hats) during reload. If child profiles are filtered
out (via grep -v '//') of the list to consider, then on reloading
a profile where a child profile has been removed or renamed, that
child profile will continue to stick around. However, if the profile
containing child profiles is removed entirely, if the initscript
attempts to unload the child profiles after the parent is removed,
this will fail because they were unloaded when the parent was unloaded.
Thus I removed any filtering of child profiles out, but do a post-awk
reverse sort which guarantees that any child profiles will be removed
before their parent is. I also added the LC_COLLATE=C (based on the
Ubuntu version) to the sort call to ensure a consistent sort order.
To restate, the problem with the existing code is that it creates
temporary files in $TMPDIR (by default /tmp) and if that partition
is full, problems with the reload action ensue. Alternate solutions
include switching the initscript to use bash and its <$() extension
or setting TMPDIR to /dev/shm/. The former is unpalatable to some
(particularly for an initscript), and for the latter, /dev/shm is
only guaranteed to exist on GNU libc based systems (glibc apparently
expects /dev/shm to exist for its POSIX shared memory implementation;
see shm_overview(7)). So to me, awk (sans GNU extensions) looks to
be the least bad option here.
Bug: https://launchpad.net/bugs/775785
2011-08-26 15:55:43 -07:00
# We filter child profiles as removing the parent will remove
# the children
2011-10-12 00:45:11 +02:00
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
2019-03-15 23:12:17 +01:00
LC_COLLATE=C sort | grep -v // | {
2019-04-22 15:02:45 +02:00
while read -r profile ; do
printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
2019-03-15 23:12:17 +01:00
rc=$?
2019-04-22 15:02:45 +02:00
if [ "$rc" -ne 0 ] ; then
retval=$rc
2019-03-15 23:12:17 +01:00
fi
done
2019-04-22 15:02:45 +02:00
return "$retval"
2019-03-15 23:12:17 +01:00
}
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
apparmor_stop() {
2019-08-30 13:40:51 +01:00
aa_log_daemon_msg "Unloading AppArmor profiles"
2006-04-11 21:52:54 +00:00
remove_profiles
2009-11-11 10:51:05 -08:00
rc=$?
2019-04-22 15:02:45 +02:00
aa_log_end_msg "$rc"
return "$rc"
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
apparmor_kill() {
2007-04-04 21:23:42 +00:00
if ! is_apparmor_loaded ; then
2009-11-11 10:51:05 -08:00
aa_log_failure_msg "AppArmor module is not loaded"
2007-04-04 21:23:42 +00:00
return 1
fi
2019-03-15 22:55:51 +01:00
aa_log_failure_msg "apparmor_kill() is no longer supported because AppArmor can't be built as a module"
return 1
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
__apparmor_restart() {
2006-04-11 21:52:54 +00:00
if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
2007-04-04 21:56:08 +00:00
aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
2006-04-11 21:52:54 +00:00
return 4
fi
2011-09-15 21:23:25 +02:00
aa_log_daemon_msg "Restarting AppArmor"
2006-04-11 21:52:54 +00:00
parse_profiles reload
Attached is a patch to make the initscript not fail if /tmp is full
by converting the comm(1) usage on temporary files to an embedded
awk script. On both Ubuntu and OpenSUSE, a version of awk (mawk in
Ubuntu, gawk in OpenSUSE) is either a direct or indirect dependency
on the minimal or base package set, and the original reporter also
mentioned that an awk-based solution would be palatable in a way that
converting to bash, or using perl or python here would not be.
In the embedded awk script, I've tried to avoid gawk or mawk specific
behaviors or extensions; e.g. this is the reason for the call to sort
on the output of the awk script, rather than using gawk's asort(). But
please let me know if you see anything that shouldn't be portable
across awk implementations.
An additional issue that is fixed in both scripts is handling child
profiles (e.g. hats) during reload. If child profiles are filtered
out (via grep -v '//') of the list to consider, then on reloading
a profile where a child profile has been removed or renamed, that
child profile will continue to stick around. However, if the profile
containing child profiles is removed entirely, if the initscript
attempts to unload the child profiles after the parent is removed,
this will fail because they were unloaded when the parent was unloaded.
Thus I removed any filtering of child profiles out, but do a post-awk
reverse sort which guarantees that any child profiles will be removed
before their parent is. I also added the LC_COLLATE=C (based on the
Ubuntu version) to the sort call to ensure a consistent sort order.
To restate, the problem with the existing code is that it creates
temporary files in $TMPDIR (by default /tmp) and if that partition
is full, problems with the reload action ensue. Alternate solutions
include switching the initscript to use bash and its <$() extension
or setting TMPDIR to /dev/shm/. The former is unpalatable to some
(particularly for an initscript), and for the latter, /dev/shm is
only guaranteed to exist on GNU libc based systems (glibc apparently
expects /dev/shm to exist for its POSIX shared memory implementation;
see shm_overview(7)). So to me, awk (sans GNU extensions) looks to
be the least bad option here.
Bug: https://launchpad.net/bugs/775785
2011-08-26 15:55:43 -07:00
2011-09-15 21:23:25 +02:00
rc=$?
2019-04-22 15:02:45 +02:00
aa_log_end_msg "$rc"
return "$rc"
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
apparmor_restart() {
2007-04-04 21:23:42 +00:00
if ! is_apparmor_loaded ; then
2007-04-04 21:56:08 +00:00
apparmor_start
2006-04-11 21:52:54 +00:00
rc=$?
2019-04-22 15:02:45 +02:00
return "$rc"
2006-04-11 21:52:54 +00:00
fi
2007-04-04 21:56:08 +00:00
__apparmor_restart
2007-04-04 21:23:42 +00:00
return $?
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
apparmor_try_restart() {
2007-04-04 21:23:42 +00:00
if ! is_apparmor_loaded ; then
2007-05-24 05:00:34 +00:00
return 0
2006-04-11 21:52:54 +00:00
fi
2007-04-04 21:56:08 +00:00
__apparmor_restart
2007-04-04 21:23:42 +00:00
return $?
2006-04-11 21:52:54 +00:00
}
2007-04-04 21:56:08 +00:00
apparmor_status () {
2019-04-22 15:02:45 +02:00
if test -x "$AA_STATUS" ; then
"$AA_STATUS" --verbose
2006-04-11 21:52:54 +00:00
return $?
fi
2011-11-10 09:43:10 -08:00
if ! is_apparmor_loaded ; then
2006-04-11 21:52:54 +00:00
echo "AppArmor is not loaded."
rc=1
else
2011-11-10 09:43:10 -08:00
echo "AppArmor is enabled."
2006-04-11 21:52:54 +00:00
rc=0
fi
echo "Install the apparmor-utils package to receive more detailed"
2019-04-22 15:02:45 +02:00
echo "status information here (or examine $SFS_MOUNTPOINT directly)."
2006-04-11 21:52:54 +00:00
2019-04-22 15:02:45 +02:00
return "$rc"
2006-04-11 21:52:54 +00:00
}