[apparmor] [PATCH 4/4] tests: Add aa_query_label() regression tests
John Johansen
john.johansen at canonical.com
Fri Sep 27 22:26:04 UTC 2013
On 09/26/2013 04:08 PM, Tyler Hicks wrote:
> This is a regression test to load a profile, query it from userspace
> using aa_query_label(), and then verify the results.
>
> The query interface is tested by the dbus mediation regression tests,
> but this test helps in finding bugs specific to AppArmor, which may
> possibly be caused by the parser, kernel, and/or libapparmor.
>
> Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
yea tests! Okay I think I've been through this enough to give it an ack
thanks ty
Acked-by: John Johansen <john.johansen at canonical.com>
> ---
> tests/regression/apparmor/Makefile | 2 +
> tests/regression/apparmor/prologue.inc | 9 ++
> tests/regression/apparmor/query_label.c | 180 +++++++++++++++++++++++++++
> tests/regression/apparmor/query_label.sh | 207 +++++++++++++++++++++++++++++++
> 4 files changed, 398 insertions(+)
> create mode 100644 tests/regression/apparmor/query_label.c
> create mode 100644 tests/regression/apparmor/query_label.sh
>
> diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
> index bf0c0b1..370690f 100644
> --- a/tests/regression/apparmor/Makefile
> +++ b/tests/regression/apparmor/Makefile
> @@ -51,6 +51,7 @@ SRC=access.c \
> ptrace.c \
> ptrace_helper.c \
> pwrite.c \
> + query_label.c \
> rename.c \
> readdir.c \
> rw.c \
> @@ -145,6 +146,7 @@ TESTS=access \
> pipe \
> ptrace \
> pwrite \
> + query_label \
> regex \
> rename \
> readdir \
> diff --git a/tests/regression/apparmor/prologue.inc b/tests/regression/apparmor/prologue.inc
> index 84ad777..b50d3d5 100755
> --- a/tests/regression/apparmor/prologue.inc
> +++ b/tests/regression/apparmor/prologue.inc
> @@ -36,6 +36,15 @@ required_features()
> done
> }
>
> +requires_query_interface()
> +{
> + if [ ! -e "/sys/kernel/security/apparmor/.access" ]
> + then
> + echo "Kernel query interface not supported. Skipping tests ..."
> + exit 0
> + fi
> +}
> +
> fatalerror()
> {
> # global _fatal
> diff --git a/tests/regression/apparmor/query_label.c b/tests/regression/apparmor/query_label.c
> new file mode 100644
> index 0000000..be945cb
> --- /dev/null
> +++ b/tests/regression/apparmor/query_label.c
> @@ -0,0 +1,180 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/apparmor.h>
> +
> +#define OPT_EXPECT "--expect="
> +#define OPT_EXPECT_LEN strlen(OPT_EXPECT)
> +
> +#define OPT_LABEL "--label="
> +#define OPT_LABEL_LEN strlen(OPT_LABEL)
> +
> +#define OPT_TYPE_DBUS "--dbus="
> +#define OPT_TYPE_DBUS_LEN strlen(OPT_TYPE_DBUS)
> +
> +static char *progname = NULL;
> +
> +void usage(void)
> +{
> + fprintf(stderr, "Usage: %s --expect=EXPECTED --label=LABEL --CLASS=PERMS QUERY...\n\n",
> + progname);
> + fprintf(stderr, " EXPECTED\tA comma separated list of allow, audit, and/or anything.\n");
> + fprintf(stderr, "\t\t\"anything\" is a special keyword that matches any condition\n");
> + fprintf(stderr, "\t\tand cannot be used with other keywords. Additionally,\n");
> + fprintf(stderr, "\t\tEXPECTED can be empty to indicate neither, allow or audit,\n");
> + fprintf(stderr, "\t\tin the results.\n");
> + fprintf(stderr, " LABEL\t\tThe AppArmor label to use in the query\n");
> + fprintf(stderr, " CLASS\t\tThe rule class and may consist of:\n");
> + fprintf(stderr, "\t\t dbus\n");
> + fprintf(stderr, " PERMS\t\tA comma separated list of permissions. Possibilities\n");
> + fprintf(stderr, "\t\tfor the supported rule classes are:\n");
> + fprintf(stderr, "\t\t dbus: send,receive,bind\n");
> + fprintf(stderr, "\t\tAdditionaly, PERMS can be empty to indicate an empty mask\n");
> + exit(1);
> +}
> +
> +static int parse_expected(int *should_allow, int *should_audit, char *expected)
> +{
> + char *expect;
> +
> + *should_allow = *should_audit = 0;
> +
> + expect = strtok(expected, ",");
> + while (expect) {
> + if (!strcmp(expect, "allow")) {
> + *should_allow = 1;
> + } else if (!strcmp(expect, "audit")) {
> + *should_audit = 1;
> + } else if (!strcmp(expect, "anything")) {
> + *should_allow = *should_audit = -1;
> + } else {
> + fprintf(stderr, "FAIL: unknown expect: %s\n", expect);
> + return 1;
> + }
> +
> + expect = strtok(NULL, ",");
> + }
> +
> + return 0;
> +}
> +
> +static int parse_dbus_perms(uint32_t *mask, char *perms)
> +{
> + char *perm;
> +
> + *mask = 0;
> +
> + perm = strtok(perms, ",");
> + while (perm) {
> + if (!strcmp(perm, "send"))
> + *mask |= AA_DBUS_SEND;
> + else if (!strcmp(perm, "receive"))
> + *mask |= AA_DBUS_RECEIVE;
> + else if (!strcmp(perm, "bind"))
> + *mask |= AA_DBUS_BIND;
> + else {
> + fprintf(stderr, "FAIL: unknown perm: %s\n", perm);
> + return 1;
> + }
> +
> + perm = strtok(NULL, ",");
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t build_query(char **qstr, const char *label, int class,
> + int argc, char **argv)
> +{
> + int size, label_size, i;
> + char *buffer, *to;
> +
> + label_size = strlen(label);
> + size = label_size + 1;
> + for (i = 0; i < argc; i++) {
> + if (argv[i])
> + size += strlen(argv[i]);
> + }
> +
> + buffer = malloc(size + argc + 1 + AA_QUERY_CMD_LABEL_SIZE);
> + if (!buffer)
> + return -1;
> +
> + to = buffer + AA_QUERY_CMD_LABEL_SIZE;
> + strcpy(to, label);
> + to += label_size;
> + *(to)++ = '\0';
> + *(to)++ = class;
> +
> + for (i = 0; i < argc; to++, i++) {
> + char *arg = argv[i];
> +
> + if (!arg)
> + arg = "";
> + to = stpcpy(to, arg);
> + }
> + *qstr = buffer;
> +
> + /* don't include trailing \0 in size */
> + return size + argc + AA_QUERY_CMD_LABEL_SIZE;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + char *label, *class_str, *query;
> + int class, should_allow, allowed, should_audit, audited, rc;
> + uint32_t mask;
> + ssize_t query_len;
> +
> + progname = argv[0];
> +
> + if (argc < 5)
> + usage();
> +
> + if (!strncmp(argv[1], OPT_EXPECT, OPT_EXPECT_LEN)) {
> + rc = parse_expected(&should_allow, &should_audit,
> + argv[1] + OPT_EXPECT_LEN);
> + if (rc)
> + usage();
> + }
> +
> + if (!strncmp(argv[2], OPT_LABEL, OPT_LABEL_LEN))
> + label = argv[2] + OPT_LABEL_LEN;
> + else
> + usage();
> +
> + class_str = argv[3];
> + if (!strncmp(class_str, OPT_TYPE_DBUS, OPT_TYPE_DBUS_LEN)) {
> + class = AA_CLASS_DBUS;
> + rc = parse_dbus_perms(&mask, class_str + OPT_TYPE_DBUS_LEN);
> + if (rc)
> + usage();
> + } else {
> + fprintf(stderr, "FAIL: unknown rule class: %s\n", class_str);
> + usage();
> + }
> +
> + query_len = build_query(&query, label, class, argc - 4, argv + 4);
> + if (query_len < 0) {
> + fprintf(stderr, "FAIL: failed to allocate memory for query string\n");
> + exit(1);
> + }
> +
> + rc = aa_query_label(mask, query, query_len, &allowed, &audited);
> + free(query);
> + if (rc < 0) {
> + fprintf(stderr, "FAIL: failed to perform query: %m\n");
> + exit(1);
> + }
> +
> + if ((should_allow == -1 && should_audit == -1) ||
> + (allowed == should_allow && audited == should_audit)) {
> + printf("PASS\n");
> + } else {
> + fprintf(stderr, "FAIL: the access should %sbe allowed and should %sbe audited\n",
> + allowed ? "" : "not ", audited ? "" : "not ");
> + exit(1);
> + }
> +
> + exit(0);
> +}
> diff --git a/tests/regression/apparmor/query_label.sh b/tests/regression/apparmor/query_label.sh
> new file mode 100644
> index 0000000..92e588e
> --- /dev/null
> +++ b/tests/regression/apparmor/query_label.sh
> @@ -0,0 +1,207 @@
> +#! /bin/bash
> +# Copyright (C) 2013 Canonical, Ltd.
> +#
> +# This program is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU General Public License as
> +# published by the Free Software Foundation, version 2 of the
> +# License.
> +
> +#=NAME query_label
> +#=DESCRIPTION
> +# This test verifies the results returned from aa_query_label()
> +#=END
> +
> +pwd=`dirname $0`
> +pwd=`cd $pwd ; /bin/pwd`
> +
> +bin=$pwd
> +
> +. $bin/prologue.inc
> +requires_query_interface
> +
> +settest query_label
> +
> +expect=""
> +perms=""
> +label="--label=$test"
> +
> +dbus_send="--dbus=send"
> +dbus_receive="--dbus=receive"
> +dbus_bind="--dbus=bind"
> +dbus_send_receive="--dbus=send,receive"
> +dbus_all="--dbus=send,receive,bind"
> +dbus_none="--dbus="
> +
> +dbus_msg_query="session com.foo.bar /usr/bin/bar /com/foo/bar com.foo.bar Method"
> +dbus_svc_query="session com.foo.baz"
> +
> +# Generate a profile for $test, granting all file access and anything specified
> +# in $@
> +genqueryprofile()
> +{
> + genprofile --stdin <<EOF
> +$test {
> + file,
> + $@
> +}
> +EOF
> +}
> +
> +# Generate the --expect parameter for query_label. Example usage:
> +# `expect audit` would generate --expect=audit
> +# `expect allow audit` would generate --expect=allow,audit
> +# `expect` would generate --expect=
> +expect()
> +{
> + local old=$IFS
> +
> + IFS=","
> + expect=$(printf "%s=%s" "--expect" "$*")
> + IFS=$old
> +}
> +
> +# Generate the --CLASS=PERMS parameter query_label. Example usage:
> +# `perms dbus send` would generate --dbus=send
> +# `perms dbus send bind` would generate --dbus=send,bind
> +# `perms dbus` would generate --dbus=
> +perms()
> +{
> + local old=$IFS
> + local class=$1
> +
> + shift
> + IFS=","
> + perms=$(printf "%s%s=%s" "--" "$class" "$*")
> + IFS=$old
> +}
> +
> +# Gather up the globals ($expect, $label, $perms) and call runchecktest
> +# @1: the test description
> +# @2: pass or fail
> +# @3: the query string
> +querytest()
> +{
> + local desc=$1
> + local pf=$2
> +
> + shift
> + shift
> + runchecktest "$desc" "$pf" "$expect" "$label" "$perms" $*
> +}
> +
> +# Check querying of a label that the kernel doesn't know about
> +# aa_query_label() should return an error
> +expect anything
> +perms dbus send
> +querytest "QUERY no profile loaded" fail $dbus_msg_query
> +
> +# Check querying with an empty mask - aa_query_label() should error out
> +genqueryprofile "dbus,"
> +expect anything
> +perms dbus # no perms
> +querytest "QUERY empty mask" fail $dbus_msg_query
> +
> +# Check dbus - allowed without auditing
> +genqueryprofile "dbus,"
> +expect allow
> +perms dbus send
> +querytest "QUERY dbus (msg send)" pass $dbus_msg_query
> +perms dbus receive
> +querytest "QUERY dbus (msg receive)" pass $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY dbus (msg send & receive)" pass $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY dbus (svc)" pass $dbus_svc_query
> +
> +# Check deny dbus - denied without auditing
> +genqueryprofile "deny dbus,"
> +expect # neither allow, nor audit
> +perms dbus send
> +querytest "QUERY deny dbus (msg send)" pass $dbus_msg_query
> +perms dbus receive
> +querytest "QUERY deny dbus (msg receive)" pass $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY deny dbus (msg send & receive)" pass $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY deny dbus (svc)" pass $dbus_svc_query
> +
> +# Check audit dbus - allowed, but audited
> +genqueryprofile "audit dbus,"
> +expect allow audit
> +perms dbus send
> +querytest "QUERY audit dbus (msg send)" pass $dbus_msg_query
> +perms dbus receive
> +querytest "QUERY audit dbus (msg receive)" pass $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY audit dbus (msg send & receive)" pass $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY audit dbus (svc)" pass $dbus_svc_query
> +
> +# Check audit deny dbus - explicit deny without auditing
> +genqueryprofile "audit deny dbus,"
> +expect audit
> +perms dbus send
> +querytest "QUERY audit deny dbus (msg send)" pass $dbus_msg_query
> +perms dbus receive
> +querytest "QUERY audit deny dbus (msg receive)" pass $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY audit deny dbus (msg send & receive)" pass $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY audit deny dbus (svc)" pass $dbus_svc_query
> +
> +# Check dbus send - ensure that receive and bind bits aren't set
> +genqueryprofile "dbus send,"
> +expect allow
> +perms dbus send
> +querytest "QUERY dbus send (msg send)" pass $dbus_msg_query
> +perms dbus receive
> +querytest "QUERY dbus send (msg receive)" fail $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY dbus send (msg send & receive)" fail $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY dbus send (msg bind)" fail $dbus_msg_query
> +perms dbus send bind
> +querytest "QUERY dbus send (msg send & bind)" fail $dbus_msg_query
> +
> +# Check dbus receive - ensure that send and bind bits aren't set
> +genqueryprofile "dbus receive,"
> +expect allow
> +perms dbus receive
> +querytest "QUERY dbus receive (msg receive)" pass $dbus_msg_query
> +perms dbus send
> +querytest "QUERY dbus receive (msg send)" fail $dbus_msg_query
> +perms dbus send receive
> +querytest "QUERY dbus receive (msg send & receive)" fail $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY dbus receive (msg bind)" fail $dbus_msg_query
> +perms dbus receive bind
> +querytest "QUERY dbus receive (msg receive & bind)" fail $dbus_msg_query
> +
> +# Check dbus bind - ensure that send and receive bits aren't set
> +genqueryprofile "dbus bind,"
> +expect allow
> +perms dbus bind
> +querytest "QUERY dbus bind (svc bind)" pass $dbus_svc_query
> +perms dbus send
> +querytest "QUERY dbus bind (svc send)" fail $dbus_svc_query
> +perms dbus send bind
> +querytest "QUERY dbus bind (svc send & bind)" fail $dbus_svc_query
> +perms dbus receive
> +querytest "QUERY dbus bind (svc receive)" fail $dbus_svc_query
> +perms dbus receive bind
> +querytest "QUERY dbus bind (svc receive & bind)" fail $dbus_svc_query
> +
> +# Check dbus - ensure that send and receive bits aren't set in service queries
> +# and the bind bit isn't set in message queries
> +genqueryprofile "dbus,"
> +expect allow
> +perms dbus send receive
> +querytest "QUERY dbus (msg send & receive)" pass $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY dbus (msg bind)" fail $dbus_msg_query
> +perms dbus bind
> +querytest "QUERY dbus (svc bind)" pass $dbus_svc_query
> +perms dbus send
> +querytest "QUERY dbus (svc send)" fail $dbus_svc_query
> +perms dbus receive
> +querytest "QUERY dbus (svc receive)" fail $dbus_svc_query
>
More information about the AppArmor
mailing list