mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1048 made it so rules like mount slave /snap/bin/** -> /**, mount /snap/bin/** -> /**, would get passed into change_mount_type rule generation when they shouldn't have been. This would result in two different errors. 1. If kernel mount flags were present on the rule. The error would be caught causing an error to be returned, causing profile compilation to fail. 2. If the rule did not contain explicit flags then rule would generate change_mount_type permissions based on souly the mount point. And the implied set of flags. However this is incorrect as it should not generate change_mount permissions for this type of rule. Not only does it ignore the source/device type condition but it generates permissions that were never intended. When used in combination with a deny prefix this overly broad rule can result in almost all mount rules being denied, as the denial takes priority over the allow mount rules. Fixes: https://bugs.launchpad.net/apparmor/+bug/2023814 Fixes: https://bugzilla.opensuse.org/show_bug.cgi?id=1211989 Fixes:9d3f8c6cc
("parser: fix parsing of source as mount point for propagation type flags") Fixes: MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1048 MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1054 Signed-off-by: John Johansen <john.johansen@canonical.com> (cherry picked from commit86d193e183
) Signed-off-by: John Johansen <john.johansen@canonical.com>
664 lines
27 KiB
Bash
Executable file
664 lines
27 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2013
|
|
# 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 Canonical Ltd.
|
|
#
|
|
|
|
# Tests for post-parser equality among multiple profiles. These tests are
|
|
# useful to verify that keyword aliases, formatting differences, etc., all
|
|
# result in the same parser output.
|
|
|
|
set -o pipefail
|
|
|
|
_SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}" )
|
|
|
|
APPARMOR_PARSER="${APPARMOR_PARSER:-${_SCRIPTDIR}/../apparmor_parser}"
|
|
fails=0
|
|
errors=0
|
|
verbose="${VERBOSE:-}"
|
|
|
|
hash_binary_policy()
|
|
{
|
|
printf %s "$1" | ${APPARMOR_PARSER} --features-file "${_SCRIPTDIR}/features_files/features.all" -qS 2>/dev/null| md5sum | cut -d ' ' -f 1
|
|
return $?
|
|
}
|
|
|
|
# verify_binary - compares the binary policy of multiple profiles
|
|
# $1: Test type (equality or inequality)
|
|
# $2: A short description of the test
|
|
# $3: The known-good profile
|
|
# $4..$n: The profiles to compare against $3
|
|
#
|
|
# Upon failure/error, prints out the test description and profiles that failed
|
|
# and increments $fails or $errors for each failure and error, respectively
|
|
verify_binary()
|
|
{
|
|
local t=$1
|
|
local desc=$2
|
|
local good_profile=$3
|
|
local good_hash
|
|
local ret=0
|
|
|
|
shift
|
|
shift
|
|
shift
|
|
|
|
if [ "$t" != "equality" ] && [ "$t" != "inequality" ]
|
|
then
|
|
printf "\nERROR: Unknown test mode:\n%s\n\n" "$t" 1>&2
|
|
((errors++))
|
|
return $((ret + 1))
|
|
fi
|
|
|
|
if [ -n "$verbose" ] ; then printf "Binary %s %s" "$t" "$desc" ; fi
|
|
if ! good_hash=$(hash_binary_policy "$good_profile")
|
|
then
|
|
if [ -z "$verbose" ] ; then printf "Binary %s %s" "$t" "$desc" ; fi
|
|
printf "\nERROR: Error hashing the following \"known-good\" profile:\n%s\n\n" \
|
|
"$good_profile" 1>&2
|
|
((errors++))
|
|
return $((ret + 1))
|
|
fi
|
|
|
|
for profile in "$@"
|
|
do
|
|
if ! hash=$(hash_binary_policy "$profile")
|
|
then
|
|
if [ -z "$verbose" ] ; then printf "Binary %s %s" "$t" "$desc" ; fi
|
|
printf "\nERROR: Error hashing the following profile:\n%s\n\n" \
|
|
"$profile" 1>&2
|
|
((errors++))
|
|
((ret++))
|
|
elif [ "$t" == "equality" ] && [ "$hash" != "$good_hash" ]
|
|
then
|
|
if [ -z "$verbose" ] ; then printf "Binary %s %s" "$t" "$desc" ; fi
|
|
printf "\nFAIL: Hash values do not match\n" 2>&1
|
|
printf "known-good (%s) != profile-under-test (%s) for the following profile:\n%s\n\n" \
|
|
"$good_hash" "$hash" "$profile" 1>&2
|
|
((fails++))
|
|
((ret++))
|
|
elif [ "$t" == "inequality" ] && [ "$hash" == "$good_hash" ]
|
|
then
|
|
if [ -z "$verbose" ] ; then printf "Binary %s %s" "$t" "$desc" ; fi
|
|
printf "\nFAIL: Hash values match\n" 2>&1
|
|
printf "known-good (%s) == profile-under-test (%s) for the following profile:\n%s\n\n" \
|
|
"$good_hash" "$hash" "$profile" 1>&2
|
|
((fails++))
|
|
((ret++))
|
|
fi
|
|
done
|
|
|
|
if [ $ret -eq 0 ]
|
|
then
|
|
if [ -z "$verbose" ] ; then
|
|
printf "."
|
|
else
|
|
printf " ok\n"
|
|
|
|
fi
|
|
fi
|
|
return $ret
|
|
}
|
|
|
|
verify_binary_equality()
|
|
{
|
|
verify_binary "equality" "$@"
|
|
}
|
|
|
|
verify_binary_inequality()
|
|
{
|
|
verify_binary "inequality" "$@"
|
|
}
|
|
|
|
printf "Equality Tests:\n"
|
|
|
|
verify_binary_equality "dbus send" \
|
|
"/t { dbus send, }" \
|
|
"/t { dbus write, }" \
|
|
"/t { dbus w, }"
|
|
|
|
verify_binary_equality "dbus receive" \
|
|
"/t { dbus receive, }" \
|
|
"/t { dbus read, }" \
|
|
"/t { dbus r, }"
|
|
|
|
verify_binary_equality "dbus send + receive" \
|
|
"/t { dbus (send, receive), }" \
|
|
"/t { dbus (read, write), }" \
|
|
"/t { dbus (r, w), }" \
|
|
"/t { dbus (rw), }" \
|
|
"/t { dbus rw, }" \
|
|
|
|
verify_binary_equality "dbus all accesses" \
|
|
"/t { dbus (send, receive, bind, eavesdrop), }" \
|
|
"/t { dbus (read, write, bind, eavesdrop), }" \
|
|
"/t { dbus (r, w, bind, eavesdrop), }" \
|
|
"/t { dbus (rw, bind, eavesdrop), }" \
|
|
"/t { dbus (), }" \
|
|
"/t { dbus, }" \
|
|
|
|
verify_binary_equality "dbus implied accesses with a bus conditional" \
|
|
"/t { dbus (send, receive, bind, eavesdrop) bus=session, }" \
|
|
"/t { dbus (read, write, bind, eavesdrop) bus=session, }" \
|
|
"/t { dbus (r, w, bind, eavesdrop) bus=session, }" \
|
|
"/t { dbus (rw, bind, eavesdrop) bus=session, }" \
|
|
"/t { dbus () bus=session, }" \
|
|
"/t { dbus bus=session, }" \
|
|
|
|
verify_binary_equality "dbus implied accesses for services" \
|
|
"/t { dbus bind name=com.foo, }" \
|
|
"/t { dbus name=com.foo, }"
|
|
|
|
verify_binary_equality "dbus implied accesses for messages" \
|
|
"/t { dbus (send, receive) path=/com/foo interface=org.foo, }" \
|
|
"/t { dbus path=/com/foo interface=org.foo, }"
|
|
|
|
verify_binary_equality "dbus implied accesses for messages with peer names" \
|
|
"/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(name=com.foo), }" \
|
|
"/t { dbus path=/com/foo interface=org.foo peer=(name=com.foo), }" \
|
|
"/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(name=(com.foo)), }" \
|
|
"/t { dbus path=/com/foo interface=org.foo peer=(name=(com.foo)), }"
|
|
|
|
verify_binary_equality "dbus implied accesses for messages with peer labels" \
|
|
"/t { dbus (send, receive) path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }" \
|
|
"/t { dbus path=/com/foo interface=org.foo peer=(label=/usr/bin/app), }"
|
|
|
|
verify_binary_equality "dbus element parsing" \
|
|
"/t { dbus bus=b path=/ interface=i member=m peer=(name=n label=l), }" \
|
|
"/t { dbus bus=\"b\" path=\"/\" interface=\"i\" member=\"m\" peer=(name=\"n\" label=\"l\"), }" \
|
|
"/t { dbus bus=(b) path=(/) interface=(i) member=(m) peer=(name=(n) label=(l)), }" \
|
|
"/t { dbus bus=(\"b\") path=(\"/\") interface=(\"i\") member=(\"m\") peer=(name=(\"n\") label=(\"l\")), }" \
|
|
"/t { dbus bus =b path =/ interface =i member =m peer =(name =n label =l), }" \
|
|
"/t { dbus bus= b path= / interface= i member= m peer= (name= n label= l), }" \
|
|
"/t { dbus bus = b path = / interface = i member = m peer = ( name = n label = l ), }"
|
|
|
|
verify_binary_equality "dbus access parsing" \
|
|
"/t { dbus, }" \
|
|
"/t { dbus (), }" \
|
|
"/t { dbus (send, receive, bind, eavesdrop), }" \
|
|
"/t { dbus (send receive bind eavesdrop), }" \
|
|
"/t { dbus (send, receive bind, eavesdrop), }" \
|
|
"/t { dbus (send,receive,bind,eavesdrop), }" \
|
|
"/t { dbus (send,receive,,,,,,,,,,,,,,,,bind,eavesdrop), }" \
|
|
"/t { dbus (send,send,send,send send receive,bind eavesdrop), }" \
|
|
|
|
verify_binary_equality "dbus variable expansion" \
|
|
"/t { dbus (send, receive) path=/com/foo member=spork interface=org.foo peer=(name=com.foo label=/com/foo), }" \
|
|
"@{FOO}=foo
|
|
/t { dbus (send, receive) path=/com/@{FOO} member=spork interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \
|
|
"@{FOO}=foo
|
|
@{SPORK}=spork
|
|
/t { dbus (send, receive) path=/com/@{FOO} member=@{SPORK} interface=org.@{FOO} peer=(name=com.@{FOO} label=/com/@{FOO}), }" \
|
|
"@{FOO}=/com/foo
|
|
/t { dbus (send, receive) path=@{FOO} member=spork interface=org.foo peer=(name=com.foo label=@{FOO}), }" \
|
|
"@{FOO}=com
|
|
/t { dbus (send, receive) path=/@{FOO}/foo member=spork interface=org.foo peer=(name=@{FOO}.foo label=/@{FOO}/foo), }"
|
|
|
|
verify_binary_equality "dbus variable expansion, multiple values/rules" \
|
|
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \
|
|
"/t { dbus (send, receive) path=/com/{foo,bar}, }" \
|
|
"/t { dbus (send, receive) path={/com/foo,/com/bar}, }" \
|
|
"@{FOO}=foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/bar, }" \
|
|
"@{FOO}=foo bar
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=bar foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}={bar,foo}
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=foo
|
|
@{BAR}=bar
|
|
/t { dbus (send, receive) path=/com/{@{FOO},@{BAR}}, }" \
|
|
|
|
verify_binary_equality "dbus variable expansion, ensure rule de-duping occurs" \
|
|
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \
|
|
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, dbus (send, receive) path=/com/bar, }" \
|
|
"@{FOO}=bar foo bar foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=bar foo bar foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/@{FOO}, }"
|
|
|
|
verify_binary_equality "dbus minimization with all perms" \
|
|
"/t { dbus, }" \
|
|
"/t { dbus bus=session, dbus, }" \
|
|
"/t { dbus (send, receive, bind, eavesdrop), dbus, }"
|
|
|
|
verify_binary_equality "dbus minimization with bind" \
|
|
"/t { dbus bind, }" \
|
|
"/t { dbus bind bus=session, dbus bind, }" \
|
|
"/t { dbus bind bus=system name=com.foo, dbus bind, }"
|
|
|
|
verify_binary_equality "dbus minimization with send and a bus conditional" \
|
|
"/t { dbus send bus=system, }" \
|
|
"/t { dbus send bus=system path=/com/foo interface=com.foo member=bar, dbus send bus=system, }" \
|
|
"/t { dbus send bus=system peer=(label=/usr/bin/foo), dbus send bus=system, }"
|
|
|
|
verify_binary_equality "dbus minimization with an audit modifier" \
|
|
"/t { audit dbus eavesdrop, }" \
|
|
"/t { audit dbus eavesdrop bus=session, audit dbus eavesdrop, }"
|
|
|
|
verify_binary_equality "dbus minimization with a deny modifier" \
|
|
"/t { deny dbus send bus=system peer=(name=com.foo), }" \
|
|
"/t { deny dbus send bus=system peer=(name=com.foo label=/usr/bin/foo), deny dbus send bus=system peer=(name=com.foo), }" \
|
|
|
|
verify_binary_equality "dbus minimization found in dbus abstractions" \
|
|
"/t { dbus send bus=session, }" \
|
|
"/t { dbus send
|
|
bus=session
|
|
path=/org/freedesktop/DBus
|
|
interface=org.freedesktop.DBus
|
|
member={Hello,AddMatch,RemoveMatch,GetNameOwner,NameHasOwner,StartServiceByName}
|
|
peer=(name=org.freedesktop.DBus),
|
|
dbus send bus=session, }"
|
|
|
|
# verify slash filtering for dbus paths.
|
|
verify_binary_equality "dbus slash filtering for paths" \
|
|
"/t { dbus (send, receive) path=/com/foo, dbus (send, receive) path=/com/bar, }" \
|
|
"/t { dbus (send, receive) path=/com///foo, dbus (send, receive) path=///com/bar, }" \
|
|
"/t { dbus (send, receive) path=/com//{foo,bar}, }" \
|
|
"/t { dbus (send, receive) path={//com/foo,/com//bar}, }" \
|
|
"@{FOO}=/foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com/bar, }" \
|
|
"@{FOO}=/foo /bar
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=/bar //foo
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=//{bar,foo}
|
|
/t { dbus (send, receive) path=/com/@{FOO}, }" \
|
|
"@{FOO}=/foo
|
|
@{BAR}=bar
|
|
/t { dbus (send, receive) path=/com/@{FOO}, dbus (send, receive) path=/com//@{BAR}, }"
|
|
|
|
# Rules compatible with audit, deny, and audit deny
|
|
# note: change_profile does not support audit/allow/deny atm
|
|
for rule in "capability" "capability mac_admin" \
|
|
"network" "network tcp" "network inet6 tcp"\
|
|
"mount" "mount /a" "mount /a -> /b" "mount options in (ro) /a -> b" \
|
|
"remount" "remount /a" \
|
|
"umount" "umount /a" \
|
|
"pivot_root" "pivot_root /a" "pivot_root oldroot=/" \
|
|
"pivot_root oldroot=/ /a" "pivot_root oldroot=/ /a -> foo" \
|
|
"ptrace" "ptrace trace" "ptrace (readby,tracedby) peer=unconfined" \
|
|
"signal" "signal (send,receive)" "signal peer=unconfined" \
|
|
"signal receive set=(kill)" \
|
|
"dbus" "dbus send" "dbus bus=system" "dbus bind name=foo" \
|
|
"dbus peer=(label=foo)" "dbus eavesdrop" \
|
|
"unix" "unix (create, listen, accept)" "unix addr=@*" "unix addr=none" \
|
|
"unix peer=(label=foo)" \
|
|
"/f r" "/f w" "/f rwmlk" "/** r" "/**/ w" \
|
|
"file /f r" "file /f w" "file /f rwmlk" \
|
|
"link /a -> /b" "link subset /a -> /b" \
|
|
"l /a -> /b" "l subset /a -> /b" \
|
|
"file l /a -> /b" "l subset /a -> /b"
|
|
do
|
|
verify_binary_equality "allow modifier for \"${rule}\"" \
|
|
"/t { ${rule}, }" \
|
|
"/t { allow ${rule}, }"
|
|
|
|
verify_binary_equality "audit allow modifier for \"${rule}\"" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { audit allow ${rule}, }"
|
|
|
|
verify_binary_inequality "audit, deny, and audit deny modifiers for \"${rule}\"" \
|
|
"/t { ${rule}, }" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { audit allow ${rule}, }" \
|
|
"/t { deny ${rule}, }" \
|
|
"/t { audit deny ${rule}, }"
|
|
|
|
verify_binary_inequality "audit vs deny and audit deny modifiers for \"${rule}\"" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { deny ${rule}, }" \
|
|
"/t { audit deny ${rule}, }"
|
|
|
|
verify_binary_inequality "deny and audit deny modifiers for \"${rule}\"" \
|
|
"/t { deny ${rule}, }" \
|
|
"/t { audit deny ${rule}, }"
|
|
done
|
|
|
|
# Rules that need special treatment for the deny modifier
|
|
for rule in "/f ux" "/f Ux" "/f px" "/f Px" "/f cx" "/f Cx" "/f ix" \
|
|
"/f pux" "/f Pux" "/f pix" "/f Pix" \
|
|
"/f cux" "/f Cux" "/f cix" "/f Cix" \
|
|
"/* ux" "/* Ux" "/* px" "/* Px" "/* cx" "/* Cx" "/* ix" \
|
|
"/* pux" "/* Pux" "/* pix" "/* Pix" \
|
|
"/* cux" "/* Cux" "/* cix" "/* Cix" \
|
|
"/f px -> b " "/f Px -> b" "/f cx -> b" "/f Cx -> b" \
|
|
"/f pux -> b" "/f Pux -> b" "/f pix -> b" "/f Pix -> b" \
|
|
"/f cux -> b" "/f Cux -> b" "/f cix -> b" "/f Cix -> b" \
|
|
"/* px -> b" "/* Px -> b" "/* cx -> b" "/* Cx -> b" \
|
|
"/* pux -> b" "/* Pux -> b" "/* pix -> b" "/* Pix -> b" \
|
|
"/* cux -> b" "/* Cux -> b" "/* cix -> b" "/* Cix -> b" \
|
|
"file /f ux" "file /f Ux" "file /f px" "file /f Px" \
|
|
"file /f cx" "file /f Cx" "file /f ix" \
|
|
"file /f pux" "file /f Pux" "file /f pix" "file /f Pix" \
|
|
"/f cux" "/f Cux" "/f cix" "/f Cix" \
|
|
"file /* ux" "file /* Ux" "file /* px" "file /* Px" \
|
|
"file /* cx" "file /* Cx" "file /* ix" \
|
|
"file /* pux" "file /* Pux" "file /* pix" "file /* Pix" \
|
|
"file /* cux" "file /* Cux" "file /* cix" "file /* Cix" \
|
|
"file /f px -> b " "file /f Px -> b" "file /f cx -> b" "file /f Cx -> b" \
|
|
"file /f pux -> b" "file /f Pux -> b" "file /f pix -> b" "file /f Pix -> b" \
|
|
"file /f cux -> b" "file /f Cux -> b" "file /f cix -> b" "file /f Cix -> b" \
|
|
"file /* px -> b" "file /* Px -> b" "file /* cx -> b" "file /* Cx -> b" \
|
|
"file /* pux -> b" "file /* Pux -> b" "file /* pix -> b" "file /* Pix -> b" \
|
|
"file /* cux -> b" "file /* Cux -> b" "file /* cix -> b" "file /* Cix -> b"
|
|
|
|
do
|
|
verify_binary_equality "allow modifier for \"${rule}\"" \
|
|
"/t { ${rule}, }" \
|
|
"/t { allow ${rule}, }"
|
|
|
|
verify_binary_equality "audit allow modifier for \"${rule}\"" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { audit allow ${rule}, }"
|
|
|
|
# skip rules that don't end with x perm
|
|
if [ -n "${rule##*x}" ] ; then continue ; fi
|
|
|
|
verify_binary_inequality "deny, audit deny modifier for \"${rule}\"" \
|
|
"/t { ${rule}, }" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { audit allow ${rule}, }" \
|
|
"/t { deny ${rule% *} x, }" \
|
|
"/t { audit deny ${rule% *} x, }"
|
|
|
|
verify_binary_inequality "audit vs deny and audit deny modifiers for \"${rule}\"" \
|
|
"/t { audit ${rule}, }" \
|
|
"/t { deny ${rule% *} x, }" \
|
|
"/t { audit deny ${rule% *} x, }"
|
|
|
|
done
|
|
|
|
# verify deny and audit deny differ for x perms
|
|
for prefix in "/f" "/*" "file /f" "file /*" ; do
|
|
verify_binary_inequality "deny and audit deny x modifiers for \"${prefix}\"" \
|
|
"/t { deny ${prefix} x, }" \
|
|
"/t { audit deny ${prefix} x, }"
|
|
done
|
|
|
|
#Test equality of leading and trailing file permissions
|
|
for audit in "" "audit" ; do
|
|
for allow in "" "allow" "deny" ; do
|
|
for owner in "" "owner" ; do
|
|
for f in "" "file" ; do
|
|
prefix="$audit $allow $owner $f"
|
|
for perm in "r" "w" "a" "l" "k" "m" "rw" "ra" \
|
|
"rl" "rk" "rm" "wl" "wk" "wm" \
|
|
"rwl" "rwk" "rwm" "ral" "rak" \
|
|
"ram" "rlk" "rlm" "rkm" "wlk" \
|
|
"wlm" "wkm" "alk" "alm" "akm" \
|
|
"lkm" "rwlk" "rwlm" "rwkm" \
|
|
"ralk" "ralm" "wlkm" "alkm" \
|
|
"rwlkm" "ralkm" ; do
|
|
verify_binary_equality "leading and trailing perms for \"${perm}\"" \
|
|
"/t { ${prefix} /f ${perm}, }" \
|
|
"/t { ${prefix} ${perm} /f, }"
|
|
done
|
|
if [ "$allow" == "deny" ] ; then continue ; fi
|
|
for perm in "ux" "Ux" "px" "Px" "cx" "Cx" \
|
|
"ix" "pux" "Pux" "pix" "Pix" \
|
|
"cux" "Cux" "cix" "Cix"
|
|
do
|
|
verify_binary_equality "leading and trailing perms for \"${perm}\"" \
|
|
"/t { ${prefix} /f ${perm}, }" \
|
|
"/t { ${prefix} ${perm} /f, }"
|
|
done
|
|
for perm in "px" "Px" "cx" "Cx" \
|
|
"pux" "Pux" "pix" "Pix" \
|
|
"cux" "Cux" "cix" "Cix"
|
|
do
|
|
verify_binary_equality "leading and trailing perms for x-transition \"${perm}\"" \
|
|
"/t { ${prefix} /f ${perm} -> b, }" \
|
|
"/t { ${prefix} ${perm} /f -> b, }"
|
|
done
|
|
done
|
|
done
|
|
done
|
|
done
|
|
|
|
#Test rule overlap for x most specific match
|
|
for perm1 in "ux" "Ux" "px" "Px" "cx" "Cx" "ix" "pux" "Pux" \
|
|
"pix" "Pix" "cux" "Cux" "cix" "Cix" "px -> b" \
|
|
"Px -> b" "cx -> b" "Cx -> b" "pux -> b" "Pux ->b" \
|
|
"pix -> b" "Pix -> b" "cux -> b" "Cux -> b" \
|
|
"cix -> b" "Cix -> b"
|
|
do
|
|
for perm2 in "ux" "Ux" "px" "Px" "cx" "Cx" "ix" "pux" "Pux" \
|
|
"pix" "Pix" "cux" "Cux" "cix" "Cix" "px -> b" \
|
|
"Px -> b" "cx -> b" "Cx -> b" "pux -> b" "Pux ->b" \
|
|
"pix -> b" "Pix -> b" "cux -> b" "Cux -> b" \
|
|
"cix -> b" "Cix -> b"
|
|
do
|
|
if [ "$perm1" == "$perm2" ] ; then
|
|
verify_binary_equality "Exec perm \"${perm1}\" - most specific match: same as glob" \
|
|
"/t { /* ${perm1}, /f ${perm2}, }" \
|
|
"/t { /* ${perm1}, }"
|
|
else
|
|
verify_binary_inequality "Exec \"${perm1}\" vs \"${perm2}\" - most specific match: different from glob" \
|
|
"/t { /* ${perm1}, /f ${perm2}, }" \
|
|
"/t { /* ${perm1}, }"
|
|
fi
|
|
done
|
|
verify_binary_inequality "Exec \"${perm1}\" vs deny x - most specific match: different from glob" \
|
|
"/t { /* ${perm1}, audit deny /f x, }" \
|
|
"/t { /* ${perm1}, }"
|
|
|
|
done
|
|
|
|
#Test deny carves out permission
|
|
verify_binary_inequality "Deny removes r perm" \
|
|
"/t { /foo/[abc] r, audit deny /foo/b r, }" \
|
|
"/t { /foo/[abc] r, }"
|
|
|
|
verify_binary_equality "Deny removes r perm" \
|
|
"/t { /foo/[abc] r, audit deny /foo/b r, }" \
|
|
"/t { /foo/[ac] r, }"
|
|
|
|
#this one may not be true in the future depending on if the compiled profile
|
|
#is explicitly including deny permissions for dynamic composition
|
|
verify_binary_equality "Deny of ungranted perm" \
|
|
"/t { /foo/[abc] r, audit deny /foo/b w, }" \
|
|
"/t { /foo/[abc] r, }"
|
|
|
|
|
|
verify_binary_equality "change_profile == change_profile -> **" \
|
|
"/t { change_profile, }" \
|
|
"/t { change_profile -> **, }"
|
|
|
|
verify_binary_equality "change_profile /** == change_profile /** -> **" \
|
|
"/t { change_profile /**, }" \
|
|
"/t { change_profile /** -> **, }"
|
|
|
|
verify_binary_equality "change_profile /** == change_profile /** -> **" \
|
|
"/t { change_profile unsafe /**, }" \
|
|
"/t { change_profile unsafe /** -> **, }"
|
|
|
|
verify_binary_equality "change_profile /** == change_profile /** -> **" \
|
|
"/t { change_profile /**, }" \
|
|
"/t { change_profile safe /** -> **, }"
|
|
|
|
verify_binary_inequality "change_profile /** == change_profile /** -> **" \
|
|
"/t { change_profile /**, }" \
|
|
"/t { change_profile unsafe /**, }"
|
|
|
|
verify_binary_equality "profile name is hname in rule" \
|
|
":ns:/hname { signal peer=/hname, }" \
|
|
":ns:/hname { signal peer=@{profile_name}, }"
|
|
|
|
verify_binary_inequality "profile name is NOT fq name in rule" \
|
|
":ns:/hname { signal peer=:ns:/hname, }" \
|
|
":ns:/hname { signal peer=@{profile_name}, }"
|
|
|
|
verify_binary_equality "profile name is hname in sub pofile rule" \
|
|
":ns:/hname { profile child { signal peer=/hname//child, } }" \
|
|
":ns:/hname { profile child { signal peer=@{profile_name}, } }"
|
|
|
|
verify_binary_inequality "profile name is NOT fq name in sub profile rule" \
|
|
":ns:/hname { profile child { signal peer=:ns:/hname//child, } }" \
|
|
":ns:/hname { profile child { signal peer=@{profile_name}, } }"
|
|
|
|
verify_binary_equality "profile name is hname in hat rule" \
|
|
":ns:/hname { ^child { signal peer=/hname//child, } }" \
|
|
":ns:/hname { ^child { signal peer=@{profile_name}, } }"
|
|
|
|
verify_binary_inequality "profile name is NOT fq name in hat rule" \
|
|
":ns:/hname { ^child { signal peer=:ns:/hname//child, } }" \
|
|
":ns:/hname { ^child { signal peer=@{profile_name}, } }"
|
|
|
|
verify_binary_equality "@{profile_name} is literal in peer" \
|
|
"/{a,b} { signal peer=/\{a,b\}, }" \
|
|
"/{a,b} { signal peer=@{profile_name}, }"
|
|
|
|
verify_binary_equality "@{profile_name} is literal in peer with pattern" \
|
|
"/{a,b} { signal peer={/\{a,b\},c}, }" \
|
|
"/{a,b} { signal peer={@{profile_name},c}, }"
|
|
|
|
verify_binary_inequality "@{profile_name} is not pattern in peer" \
|
|
"/{a,b} { signal peer=/{a,b}, }" \
|
|
"/{a,b} { signal peer=@{profile_name}, }"
|
|
|
|
verify_binary_equality "@{profile_name} is literal in peer with esc sequence" \
|
|
"/\\\\a { signal peer=/\\\\a, }" \
|
|
"/\\\\a { signal peer=@{profile_name}, }"
|
|
|
|
verify_binary_equality "@{profile_name} is literal in peer with esc alt sequence" \
|
|
"/\\{a,b\\},c { signal peer=/\\{a,b\\},c, }" \
|
|
"/\\{a,b\\},c { signal peer=@{profile_name}, }"
|
|
|
|
|
|
|
|
# verify rlimit data conversions
|
|
verify_binary_equality "set rlimit rttime <= 12 weeks" \
|
|
"/t { set rlimit rttime <= 12 weeks, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7)) days, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24)) hours, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60)) minutes, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60)) seconds, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000)) ms, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)) us, }" \
|
|
"/t { set rlimit rttime <= $((12 * 7 * 24 * 60 * 60 * 1000 * 1000)), }"
|
|
|
|
verify_binary_equality "set rlimit cpu <= 42 weeks" \
|
|
"/t { set rlimit cpu <= 42 weeks, }" \
|
|
"/t { set rlimit cpu <= $((42 * 7)) days, }" \
|
|
"/t { set rlimit cpu <= $((42 * 7 * 24)) hours, }" \
|
|
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60)) minutes, }" \
|
|
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)) seconds, }" \
|
|
"/t { set rlimit cpu <= $((42 * 7 * 24 * 60 * 60)), }"
|
|
|
|
verify_binary_equality "set rlimit memlock <= 2GB" \
|
|
"/t { set rlimit memlock <= 2GB, }" \
|
|
"/t { set rlimit memlock <= $((2 * 1024)) MB, }" \
|
|
"/t { set rlimit memlock <= $((2 * 1024 * 1024)) KB, }" \
|
|
"/t { set rlimit memlock <= $((2 * 1024 * 1024 * 1024)) , }"
|
|
|
|
# Unfortunately we can not just compare an empty profile and hat to a
|
|
# ie. "/t { ^test { /f r, }}"
|
|
# to the second profile with the equivalent rule inserted manually
|
|
# because policy write permission "w" actually expands to multiple permissions
|
|
# under the hood, and the parser is not adding those permissions
|
|
# to the rules it auto generates
|
|
# So we insert the rule with "append" permissions, and rely on the parser
|
|
# merging permissions of rules.
|
|
# If the parser isn't adding the rules "append" is not equivalent to
|
|
# the "write" permission in the second profile and the test will fail.
|
|
# If the parser is adding the change_hat proc attr rules then the
|
|
# rules should merge and be equivalent.
|
|
verify_binary_equality "change_hat rules automatically inserted"\
|
|
"/t { owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}" \
|
|
"/t { owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}"
|
|
|
|
# verify slash filtering for unix socket address paths.
|
|
# see https://bugs.launchpad.net/apparmor/+bug/1856738
|
|
verify_binary_equality "unix rules addr conditional" \
|
|
"/t { unix bind addr=@/a/bar, }" \
|
|
"/t { unix bind addr=@/a//bar, }" \
|
|
"/t { unix bind addr=@//a/bar, }" \
|
|
"/t { unix bind addr=@/a///bar, }" \
|
|
"@{HOME}=/a/
|
|
/t { unix bind addr=@@{HOME}/bar, }" \
|
|
"@{HOME}=/a/
|
|
/t { unix bind addr=@//@{HOME}bar, }" \
|
|
"@{HOME}=/a/
|
|
/t { unix bind addr=@/@{HOME}/bar, }"
|
|
|
|
verify_binary_equality "unix rules peer addr conditional" \
|
|
"/t { unix peer=(addr=@/a/bar), }" \
|
|
"/t { unix peer=(addr=@/a//bar), }" \
|
|
"/t { unix peer=(addr=@//a/bar), }" \
|
|
"/t { unix peer=(addr=@/a///bar), }" \
|
|
"@{HOME}=/a/
|
|
/t { unix peer=(addr=@@{HOME}/bar), }" \
|
|
"@{HOME}=/a/
|
|
/t { unix peer=(addr=@//@{HOME}bar), }" \
|
|
"@{HOME}=/a/
|
|
/t { unix peer=(addr=@/@{HOME}/bar), }"
|
|
|
|
# verify slash filtering for mount rules
|
|
verify_binary_equality "mount rules slash filtering" \
|
|
"/t { mount /dev/foo -> /mnt/bar, }" \
|
|
"/t { mount ///dev/foo -> /mnt/bar, }" \
|
|
"/t { mount /dev/foo -> /mnt//bar, }" \
|
|
"/t { mount /dev///foo -> ////mnt/bar, }" \
|
|
"@{MNT}=/mnt/
|
|
/t { mount /dev///foo -> @{MNT}/bar, }" \
|
|
"@{FOO}=/foo
|
|
/t { mount /dev//@{FOO} -> /mnt/bar, }"
|
|
|
|
# verify slash filtering for link rules
|
|
verify_binary_equality "link rules slash filtering" \
|
|
"/t { link /dev/foo -> /mnt/bar, }" \
|
|
"/t { link ///dev/foo -> /mnt/bar, }" \
|
|
"/t { link /dev/foo -> /mnt//bar, }" \
|
|
"/t { link /dev///foo -> ////mnt/bar, }" \
|
|
"@{BAR}=/mnt/
|
|
/t { link /dev///foo -> @{BAR}/bar, }" \
|
|
"@{FOO}=/dev/
|
|
/t { link @{FOO}//foo -> /mnt/bar, }" \
|
|
"@{FOO}=/dev/
|
|
@{BAR}=/mnt/
|
|
/t { link @{FOO}/foo -> @{BAR}/bar, }"
|
|
|
|
verify_binary_equality "attachment slash filtering" \
|
|
"/t /bin/foo { }" \
|
|
"/t /bin//foo { }" \
|
|
"@{BAR}=/bin/
|
|
/t @{BAR}/foo { }" \
|
|
"@{FOO}=/foo
|
|
/t /bin/@{FOO} { }" \
|
|
"@{BAR}=/bin/
|
|
@{FOO}=/foo
|
|
/t @{BAR}/@{FOO} { }"
|
|
|
|
# This can potentially fail as ideally it requires a better dfa comparison
|
|
# routine as it can generates hormomorphic dfas. The enumeration of the
|
|
# dfas dumped will be different, even if the binary is the same
|
|
# Note: this test in the future will require -O filter-deny and
|
|
# -O minimize and -O remove-unreachable.
|
|
verify_binary_equality "mount specific deny doesn't affect non-overlapping" \
|
|
"/t { mount options=bind /e/ -> /**, }" \
|
|
"/t { audit deny mount /s/** -> /**,
|
|
mount options=bind /e/ -> /**, }"
|
|
|
|
if [ $fails -ne 0 ] || [ $errors -ne 0 ]
|
|
then
|
|
printf "ERRORS: %d\nFAILS: %d\n" $errors $fails 2>&1
|
|
exit $((fails + errors))
|
|
fi
|
|
|
|
[ -z "${verbose}" ] && printf "\n"
|
|
printf "PASS\n"
|
|
exit 0
|