tests: Add aa_query_label() regression tests

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@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
Tyler Hicks 2013-09-27 17:33:09 -07:00
parent c70710d4c7
commit 5b908d7502
4 changed files with 398 additions and 0 deletions

View file

@ -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 \

View file

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

View file

@ -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);
}

View file

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