apparmor/parser/tst/equality.sh
John Johansen d4b0fef10a parser: fix rule flag generation change_mount type rules
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 commit 86d193e183)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-06-21 01:18:14 -07:00

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