apparmor/parser/tst/equality.sh

309 lines
11 KiB
Bash
Raw Normal View History

#!/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
APPARMOR_PARSER="${APPARMOR_PARSER:-../apparmor_parser}"
fails=0
errors=0
hash_binary_policy()
{
printf %s "$1" | ${APPARMOR_PARSER} -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
printf "Binary %s %s" "$t" "$desc"
good_hash=$(hash_binary_policy "$good_profile")
if [ $? -ne 0 ]
then
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
hash=$(hash_binary_policy "$profile")
if [ $? -ne 0 ]
then
printf "\nERROR: Error hashing the following profile:\n%s\n\n" \
"$profile" 1>&2
((errors++))
((ret++))
elif [ "$t" == "equality" ] && [ "$hash" != "$good_hash" ]
then
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
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
printf " ok\n"
fi
return $ret
}
verify_binary_equality()
{
verify_binary "equality" "$@"
}
verify_binary_inequality()
{
verify_binary "inequality" "$@"
}
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, }"
# Rules compatible with audit, deny, and audit deny
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"
do
verify_binary_equality "allow modifier for \"${rule}\"" \
"/t { ${rule}, }" \
"/t { 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}, }"
done
# Rules that need special treatment for the deny modifier
for rule in "/f ux" "/f Ux" "/f px" "/f Px" "/f ix" \
"file /f ux" "file /f UX" "file /f px" "file /f Px" "file /f ix"
do
verify_binary_equality "allow modifier for \"${rule}\"" \
"/t { ${rule}, }" \
"/t { allow ${rule}, }" \
verify_binary_inequality "deny, audit deny modifier for \"${rule}\"" \
"/t { ${rule}, }" \
"/t { audit ${rule}, }" \
"/t { audit allow ${rule}, }" \
"/t { deny /f x, }" \
"/t { audit deny /f x, }"
done
if [ $fails -ne 0 -o $errors -ne 0 ]
then
printf "ERRORS: %d\nFAILS: %d\n" $errors $fails 2>&1
exit $(($fails + $errors))
fi
printf "PASS\n"
exit 0