From 3186b0903523cabb58fb9b32fa8c95372f1ea3d4 Mon Sep 17 00:00:00 2001 From: Steve Beattie Date: Fri, 26 Aug 2011 16:03:03 -0700 Subject: [PATCH] Merge from trunk revision 1805: 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. Nominated-By: Steve Beattie Acked-By: John Johansen Bug: https://launchpad.net/bugs/775785 --- parser/rc.apparmor.functions | 54 +++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/parser/rc.apparmor.functions b/parser/rc.apparmor.functions index 21297f0c9..b4a9e2fa3 100644 --- a/parser/rc.apparmor.functions +++ b/parser/rc.apparmor.functions @@ -83,15 +83,6 @@ SECURITYFS=/sys/kernel/security SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \ sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null) -if [ -d "/var/lib/${MODULE}" ] ; then - APPARMOR_TMPDIR="/var/lib/${MODULE}" -elif [ -d "/var/lib/${OLD_MODULE}" ] ; then - APPARMOR_TMPDIR="/var/lib/${OLD_MODULE}" -else - APPARMOR_TMPDIR="/tmp" -fi - - # keep exit status from parser during profile load. 0 is good, 1 is bad STATUS=0 @@ -223,7 +214,6 @@ parse_profiles() { profiles_names_list() { # run the parser on all of the apparmor profiles - TMPFILE=$1 if [ ! -f "$PARSER" ]; then aa_log_failure_msg "- AppArmor parser not found" exit 1 @@ -236,9 +226,9 @@ profiles_names_list() { for profile in $PROFILE_DIR/*; do if skip_profile "${profile}" && [ -f "${profile}" ] ; then - LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" | grep -v '//') + LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" ) if [ $? -eq 0 ]; then - echo "$LIST_ADD" >>$TMPFILE + echo "$LIST_ADD" fi fi done @@ -409,18 +399,16 @@ remove_profiles() { fi retval=0 - #the list of profiles isn't stable once we start adding or removing - #them so store to tmp first (in reverse order so hat profiles are removed first) - MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX) - sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | sort -r > "$MODULE_PLIST" - cat "$MODULE_PLIST" | while read profile ; do + # 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 - rm "$MODULE_PLIST" return ${retval} } @@ -462,17 +450,33 @@ __apparmor_restart() { configure_owlsm parse_profiles reload - PNAMES_LIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX) - profiles_names_list ${PNAMES_LIST} - MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX) # Clean out running profiles not associated with the current profile # set, excluding the libvirt dynamically generated profiles. - sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | egrep -v '^libvirt-[0-9a-f\-]+$' | sort >"$MODULE_PLIST" - sort "$PNAMES_LIST" | comm -2 -3 "$MODULE_PLIST" - | while IFS= read profile ; do + # 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 < "'${SFS_MOUNTPOINT}'/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 echo -n "$profile" > "$SFS_MOUNTPOINT/.remove" done - rm "$MODULE_PLIST" - rm "$PNAMES_LIST" return 0 }