apparmor/parser/tst/equality.sh
Steve Beattie ef4e59256b parser: fix equality and valgrind test scripts to use features file
This patch fixes the equality test script and the valgrind wrapper
script to make the parser under test use the features.all features file
from the features_files/ subdirectory. Otherwise, the equality tests
will fail on systems where the not all of the current language features
are supported. The equality fix does so in a way to make the script work
correctly regardless of the directory it is run from.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-19 04:35:08 -07:00

310 lines
11 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
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
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