#!/bin/sh # ---------------------------------------------------------------------- # Copyright (c) 1999-2008 NOVELL (All rights reserved) # Copyright (c) 2009-2012 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 CONFIG_DIR=/etc/apparmor MODULE=apparmor OLD_MODULE=subdomain if [ -f "${CONFIG_DIR}/${MODULE}.conf" ] ; then APPARMOR_CONF="${CONFIG_DIR}/${MODULE}.conf" elif [ -f "${CONFIG_DIR}/${OLD_MODULE}.conf" ] ; then APPARMOR_CONF="${CONFIG_DIR}/${OLD_MODULE}.conf" elif [ -f "/etc/immunix/subdomain.conf" ] ; then aa_log_warning_msg "/etc/immunix/subdomain.conf is deprecated, use ${CONFIG_DIR}/subdomain.conf instead" APPARMOR_CONF="/etc/immunix/subdomain.conf" elif [ -f "/etc/subdomain.conf" ] ; then aa_log_warning_msg "/etc/subdomain.conf is deprecated, use ${CONFIG_DIR}/subdomain.conf instead" APPARMOR_CONF="/etc/subdomain.conf" else aa_log_warning_msg "Unable to find config file in ${CONFIG_DIR}, installation problem?" fi # Read configuration options from /etc/subdomain.conf, default is to # warn if subdomain won't load. SUBDOMAIN_MODULE_PANIC="warn" SUBDOMAIN_ENABLE_OWLSM="no" APPARMOR_ENABLE_AAEVENTD="no" if [ -f "${APPARMOR_CONF}" ] ; then #parse the conf file to see what we should do . "${APPARMOR_CONF}" fi PARSER=/sbin/apparmor_parser # SUBDOMAIN_DIR and APPARMOR_DIR might be defined in subdomain.conf|apparmor.conf if [ -d "${APPARMOR_DIR}" ] ; then PROFILE_DIR=${APPARMOR_DIR} elif [ -d "${SUBDOMAIN_DIR}" ] ; then PROFILE_DIR=${SUBDOMAIN_DIR} elif [ -d /etc/apparmor.d ] ; then PROFILE_DIR=/etc/apparmor.d elif [ -d /etc/subdomain.d ] ; then PROFILE_DIR=/etc/subdomain.d fi AA_EV_BIN=/usr/sbin/aa-eventd AA_EV_PIDFILE=/var/run/aa-eventd.pid AA_STATUS=/usr/sbin/aa-status SD_EV_BIN=/usr/sbin/sd-event-dispatch.pl SD_EV_PIDFILE=/var/run/sd-event-dispatch.init.pid SD_STATUS=/usr/sbin/subdomain_status SECURITYFS=/sys/kernel/security SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \ sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null) # 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() { local modules=$1 shift while [ $# -gt 0 ] ; do modules="$modules|$1" shift done # check for subdomainfs version of module grep -qE "^($modules)[[:space:]]" /proc/modules [ $? -ne 0 -a -d /sys/module/apparmor ] return $? } # 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 \ -e "${PROFILE_DIR}/disable/`basename ${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() { # get parser arg case "$1" in load) PARSER_ARGS="--add" PARSER_MSG="Loading AppArmor profiles " ;; reload) PARSER_ARGS="--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 if [ ! -d "$PROFILE_DIR" ]; then aa_log_failure_msg "Profile directory not found" aa_log_action_end 1 exit 1 fi if [ -z "$(ls $PROFILE_DIR/)" ]; then aa_log_failure_msg "No profiles found" aa_log_action_end 1 return 1 fi for profile in $PROFILE_DIR/*; do skip_profile "${profile}" skip=$? # Ignore skip status == 2 (silent skip) if [ "$skip" -eq 1 ] ; then aa_log_skipped_msg "$profile" logger -t "AppArmor(init)" -p daemon.warn "Skipping profile $profile" STATUS=2 continue elif [ "$skip" -ne 0 ]; then continue fi if [ -f "${profile}" ] ; then $PARSER $PARSER_ARGS "$profile" > /dev/null if [ $? -ne 0 ]; then aa_log_failure_msg "$profile failed to load" STATUS=1 fi fi done if [ $STATUS -eq 2 ]; then STATUS=0 fi 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 if [ ! -d "$PROFILE_DIR" ]; then aa_log_failure_msg "- Profile directory not found" exit 1 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 } 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 } module_panic() { # the module failed to load, determine what action should be taken case "$SUBDOMAIN_MODULE_PANIC" in "warn"|"WARN") return 1 ;; "panic"|"PANIC") failstop_system rc=$? return $rc ;; *) aa_log_failure_msg "- invalid AppArmor module fail option" return -1 ;; esac } is_apparmor_loaded() { if ! is_securityfs_mounted ; then mount_securityfs fi mount_subdomainfs if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}" return 0 fi if [ -f "${SECURITYFS}/${OLD_MODULE}/profiles" ]; then SFS_MOUNTPOINT="${SECURITYFS}/${OLD_MODULE}" return 0 fi if [ -f "${SUBDOMAINFS_MOUNTPOINT}/profiles" ]; then SFS_MOUNTPOINT=${SUBDOMAINFS_MOUNTPOINT} return 0 fi # check for subdomainfs version of module is_apparmor_present apparmor subdomain 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 } mount_subdomainfs() { # for backwords compatibility if grep -q subdomainfs /proc/filesystems && \ ! grep -q subdomainfs /proc/mounts && \ [ -n "${SUBDOMAINFS_MOUNTPOINT}" ]; then aa_action "Mounting subdomainfs on ${SUBDOMAINFS_MOUNTPOINT}" \ mount "${SUBDOMAINFS_MOUNTPOINT}" return $? fi return 0 } unmount_subdomainfs() { SUBDOMAINFS=$(grep subdomainfs /proc/mounts | cut -d" " -f2 2> /dev/null) if [ -n "${SUBDOMAINFS}" ]; then aa_action "Unmounting subdomainfs" umount ${SUBDOMAINFS} fi } load_module() { local rc=0 if modinfo -F filename apparmor > /dev/null 2>&1 ; then MODULE=apparmor elif modinfo -F filename ${OLD_MODULE} > /dev/null 2>&1 ; then MODULE=${OLD_MODULE} fi if ! is_apparmor_present apparmor subdomain ; then aa_action "Loading AppArmor module" /sbin/modprobe -q $MODULE $1 rc=$? if [ $rc -ne 0 ] ; then module_panic rc=$? if [ $rc -ne 0 ] ; then exit $rc fi fi fi if ! is_apparmor_loaded ; then return 1 fi return $rc } apparmor_start() { aa_log_daemon_msg "Starting AppArmor" if ! is_apparmor_loaded ; then load_module rc=$? if [ $rc -ne 0 ] ; then aa_log_end_msg $rc return $rc fi 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 configure_owlsm # 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 subdomainfs # 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 unmount_subdomainfs if is_apparmor_present apparmor ; then MODULE=apparmor elif is_apparmor_present subdomain ; then MODULE=subdomain 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" configure_owlsm 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 $? } configure_owlsm () { if [ "${SUBDOMAIN_ENABLE_OWLSM}" = "yes" -a -f ${SFS_MOUNTPOINT}/control/owlsm ] ; then # Sigh, the "sh -c" is necessary for the SuSE aa_action # and it can't be abstracted out as a seperate function, as # that breaks under RedHat's action, which needs a # binary to invoke. aa_action "Enabling OWLSM extension" sh -c "echo -n \"1\" > \"${SFS_MOUNTPOINT}/control/owlsm\"" elif [ -f "${SFS_MOUNTPOINT}/control/owlsm" ] ; then aa_action "Disabling OWLSM extension" sh -c "echo -n \"0\" > \"${SFS_MOUNTPOINT}/control/owlsm\"" fi } apparmor_status () { if test -x ${AA_STATUS} ; then ${AA_STATUS} --verbose return $? fi if test -x ${SD_STATUS} ; then ${SD_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 }