[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