diff --git a/utils/Makefile b/utils/Makefile index d00b8f787..cfea5a238 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -30,7 +30,7 @@ PERLTOOLS = aa-exec aa-notify PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \ aa-autodep aa-audit aa-complain aa-enforce aa-disable \ aa-status aa-unconfined -TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode +TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode aa-remove-unknown PYSETUP = python-tools-setup.py PYMODULES = $(wildcard apparmor/*.py) diff --git a/utils/aa-remove-unknown b/utils/aa-remove-unknown new file mode 100644 index 000000000..c0c7b5f97 --- /dev/null +++ b/utils/aa-remove-unknown @@ -0,0 +1,108 @@ +#!/bin/sh +# ---------------------------------------------------------------------- +# Copyright (c) 2017 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, see . +# ---------------------------------------------------------------------- + +APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions +APPARMORFS=/sys/kernel/security/apparmor +PROFILES="${APPARMORFS}/profiles" +REMOVE="${APPARMORFS}/.remove" + +DRY_RUN=0 + +. $APPARMOR_FUNCTIONS + +usage() { + local progname="$1" + local rc="$2" + local msg="usage: ${progname} [options]\n +Remove profiles unknown to the system + +Options: + -h, --help Show this help message and exit + -n Dry run; don't remove profiles" + + if [ "$rc" -ne 0 ] ; then + echo "$msg" 1>&2 + else + echo "$msg" + fi + + exit "$rc" +} + +if [ "$#" -gt 1 ] ; then + usage "$0" 1 +elif [ "$#" -eq 1 ] ; then + if [ "$1" = "-h" -o "$1" = "--help" ] ; then + usage "$0" 0 + elif [ "$1" = "-n" ] ; then + DRY_RUN=1 + else + usage "$0" 1 + fi +fi + + +# We can't use a -r test here because while $PROFILES is world-readable, +# apparmorfs may still return EACCES from open() +# +# We have to do this check because error checking awk's getline() below is +# tricky and, as is, results in an infinite loop when apparmorfs returns an +# error from open(). +if ! IFS= read line < "$PROFILES" ; then + echo "ERROR: Unable to read apparmorfs profiles file" 1>&2 + exit 1 +elif [ ! -w "$REMOVE" ] ; then + echo "ERROR: Unable to write to apparmorfs remove file" 1>&2 + exit 1 +fi + +# Clean out running profiles not associated with the current profile +# set, excluding the libvirt dynamically generated profiles. +# Note that we reverse sort the list of profiles to remove to +# ensure that child profiles (e.g. hats) are removed before the +# parent. We *do* need to remove the child profile and not rely +# on removing the parent profile when the profile has had its +# child profile names changed. +profiles_names_list | awk ' +BEGIN { + while (getline < "'${PROFILES}'" ) { + str = sub(/ \((enforce|complain)\)$/, "", $0); + if (match($0, /^libvirt-[0-9a-f\-]+$/) == 0) + arr[$str] = $str + } +} + +{ if (length(arr[$0]) > 0) { delete arr[$0] } } + +END { + for (key in arr) + if (length(arr[key]) > 0) { + printf("%s\n", arr[key]) + } +} +' | LC_COLLATE=C sort -r | \ + while IFS= read profile ; do + if [ "$DRY_RUN" -ne 0 ]; then + echo "Would remove '${profile}'" + else + echo "Removing '${profile}'" + echo -n "$profile" > "${REMOVE}" + fi + done + +# will not catch all errors, but still better than nothing +exit $? diff --git a/utils/aa-remove-unknown.pod b/utils/aa-remove-unknown.pod new file mode 100644 index 000000000..b16646579 --- /dev/null +++ b/utils/aa-remove-unknown.pod @@ -0,0 +1,51 @@ +=pod + +=head1 NAME + +aa-remove-unknown - remove unknown AppArmor profiles + +=head1 SYNOPSIS + +B [option] + +=head1 DESCRIPTION + +B will inventory all profiles in /etc/apparmor.d/, compare +that list to the profiles currently loaded into the kernel, and then remove all +of the loaded profiles that were not found in /etc/apparmor.d/. It will also +report the name of each profile that it removes on standard out. + +=head1 OPTIONS + +=over 4 + +=item -h, --help + +displays a short usage statement. + +=item -n + +dry run; only prints the names of profiles that would be removed + +=back + +=head1 EXAMPLES + + $ sudo ./aa-remove-unknown -n + Would remove 'test//null-/usr/bin/whoami' + Would remove 'test' + + $ sudo ./aa-remove-unknown + Removing 'test//null-/usr/bin/whoami' + Removing 'test' + +=head1 BUGS + +None. Please report any you find to Launchpad at +L. + +=head1 SEE ALSO + +apparmor(7) + +=cut