123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- #!/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
- MODULE=apparmor
- PARSER=/sbin/apparmor_parser
- PARSER_OPTS=--write-cache
- # 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
- 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
- # 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 environment.
- #
- # 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 define this here.
- SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}"
- local ns_stacked_path="${SFS_MOUNTPOINT}/.ns_stacked"
- local ns_name_path="${SFS_MOUNTPOINT}/.ns_name"
- local ns_stacked
- local ns_name
- 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
- }
- # This set of patterns to skip needs to be kept in sync with
- # AppArmor.pm::isSkippableFile()
- # returns 0 if profile should NOT be skipped
- # returns 1 on verbose skip
- # returns 2 on silent skip
- skip_profile() {
- local profile=$1
- if [ "${profile%.rpmnew}" != "${profile}" -o \
- "${profile%.rpmsave}" != "${profile}" -o \
- "${profile%.orig}" != "${profile}" -o \
- "${profile%.rej}" != "${profile}" -o \
- "${profile%\~}" != "${profile}" ] ; then
- return 1
- fi
- # Silently ignore the dpkg, pacman, and xbps files
- if [ "${profile%.dpkg-new}" != "${profile}" -o \
- "${profile%.dpkg-old}" != "${profile}" -o \
- "${profile%.dpkg-dist}" != "${profile}" -o \
- "${profile%.dpkg-bak}" != "${profile}" -o \
- "${profile%.dpkg-remove}" != "${profile}" -o \
- "${profile%.pacsave}" != "${profile}" -o \
- "${profile%.pacnew}" != "${profile}" ] ; then
- return 2
- fi
- if echo "${profile}" | egrep -q '^.+\.new-[0-9\.]+_[0-9]+$'; then
- return 2
- 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
- # Note: the parser automatically skips files that match skip_profile()
- # when we pass it a directory, but not when we pass it an individual
- # profile. So we need to use skip_profile only in the latter case,
- # as long as the parser is in sync' with skip_profile().
- "$PARSER" $PARSER_OPTS $parser_cmd -- "$profile_dir" || {
- # FIXME: once the parser properly handles broken profiles
- # (LP: #1377338), remove the following code and the
- # skip_profile() function. For now, if the parser returns
- # an error, just run it again separately on each profile.
- for profile in $profile_dir/*; do
- skip_profile "${profile}"
- skip=$?
- if [ "$skip" -eq 2 ]; then
- # Ignore skip status == 2 (silent skip)
- continue
- elif [ "$skip" -ne 0 ] ; then
- aa_log_skipped_msg "$profile"
- logger -t "AppArmor(init)" -p daemon.warn \
- "Skipping profile $profile"
- continue
- fi
- if [ ! -f "${profile}" ] ; then
- continue
- fi
- echo "$profile"
- done | \
- # Use xargs to parallelize calls to the parser over all CPUs
- xargs -n1 -d"\n" --max-procs=$(getconf _NPROCESSORS_ONLN) \
- "$PARSER" $PARSER_OPTS $parser_cmd --
- if [ $? -ne 0 ]; 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"
- aa_log_action_end 1
- 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
- }
- profiles_names_list() {
- # 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
- if [ ! -d "$profile_dir" ]; then
- aa_log_warning_msg "- Profile directory not found: $profile_dir"
- continue
- fi
- for profile in $profile_dir/*; do
- if skip_profile "${profile}" && [ -f "${profile}" ] ; then
- LIST_ADD=$($PARSER -N "$profile" )
- if [ $? -eq 0 ]; then
- echo "$LIST_ADD"
- fi
- fi
- done
- done
- }
- failstop_system() {
- level=$(runlevel | cut -d" " -f2)
- if [ $level -ne "1" ] ; then
- aa_log_failure_msg "- could not start AppArmor. Changing to runlevel 1"
- telinit 1;
- return -1;
- fi
- aa_log_failure_msg "- could not start AppArmor."
- return -1
- }
- is_apparmor_loaded() {
- if ! is_securityfs_mounted ; then
- mount_securityfs
- fi
- if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then
- SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}"
- 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 line < "$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\))$//" "$SFS_MOUNTPOINT/profiles" | \
- LC_COLLATE=C sort | grep -v // | {
- while read profile ; do
- echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
- rc=$?
- if [ ${rc} -ne 0 ] ; then
- retval=${rc}
- 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() {
- aa_log_daemon_msg "Unloading AppArmor modules "
- if ! is_apparmor_loaded ; then
- aa_log_failure_msg "AppArmor module is not loaded"
- return 1
- fi
- if is_apparmor_present ; then
- MODULE=apparmor
- else
- aa_log_failure_msg "AppArmor is builtin"
- return 1
- fi
- /sbin/modprobe -qr $MODULE
- rc=$?
- aa_log_end_msg $rc
- return $rc
- }
- __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
- }
|