[apparmor] [PATCH 4/4] tests: Add aa_query_label() regression tests

Tyler Hicks tyhicks at canonical.com
Thu Sep 26 23:08:10 UTC 2013


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>
---
 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
-- 
1.8.3.2




More information about the AppArmor mailing list