This test will soon be made to do more than just stack a new profile. It will be extended to allow for changing to a new profile and, therefore, be renamed.
Signed-off-by: Tyler Hicks <[email protected]> --- tests/regression/apparmor/Makefile | 4 +- tests/regression/apparmor/exec_stack.sh | 2 +- tests/regression/apparmor/stacking.c | 337 ------------------------------ tests/regression/apparmor/stackonexec.sh | 2 +- tests/regression/apparmor/stackprofile.sh | 2 +- tests/regression/apparmor/transition.c | 337 ++++++++++++++++++++++++++++++ 6 files changed, 342 insertions(+), 342 deletions(-) delete mode 100644 tests/regression/apparmor/stacking.c create mode 100644 tests/regression/apparmor/transition.c diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile index ca9a294..87f756a 100644 --- a/tests/regression/apparmor/Makefile +++ b/tests/regression/apparmor/Makefile @@ -168,7 +168,7 @@ ifdef USE_SYSTEM endif ifneq (,$(shell pkg-config --atleast-version 2.10.95 libapparmor && echo TRUE)) - SRC+=stacking.c + SRC+=transition.c CONDITIONAL_TESTS+=exec_stack stackonexec stackprofile else $(warning ${nl}\ @@ -177,7 +177,7 @@ ifdef USE_SYSTEM ************************************************************************${nl}) endif else - SRC+=aa_policy_cache.c stacking.c + SRC+=aa_policy_cache.c transition.c CONDITIONAL_TESTS+=exec_stack aa_policy_cache stackonexec stackprofile endif diff --git a/tests/regression/apparmor/exec_stack.sh b/tests/regression/apparmor/exec_stack.sh index ef12015..2423dea 100755 --- a/tests/regression/apparmor/exec_stack.sh +++ b/tests/regression/apparmor/exec_stack.sh @@ -20,7 +20,7 @@ bin=$pwd . $bin/prologue.inc requires_kernel_features domain/stack -settest stacking +settest transition file=$tmpdir/file otherfile=$tmpdir/file2 diff --git a/tests/regression/apparmor/stacking.c b/tests/regression/apparmor/stacking.c deleted file mode 100644 index ac1afce..0000000 --- a/tests/regression/apparmor/stacking.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright (C) 2014-2016 Canonical, Ltd. - * - * 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. - */ - -#define _GNU_SOURCE - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/apparmor.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "changehat.h" /* for do_open() */ - -#define STACK_DELIM "//&" -#define STACK_DELIM_LEN strlen(STACK_DELIM) - -#define NO_MODE "(null)" - -static void file_io(const char *file) -{ - int rc = do_open(file); - - if (rc != 0) - exit(rc); -} - -struct single_label { - const char *label; - size_t len; -}; - -#define MAX_LABELS 32 - -struct compound_label { - size_t num_labels; - struct single_label labels[MAX_LABELS]; -}; - -/** - * Initializes @sl by parsing @compound_label. Returns a pointer to the - * location of the next label in the compound label string, which should be - * passed in as @compound_label the next time that next_label() is called. NULL - * is returned when there are no more labels in @compound_label. - */ -static const char *next_label(struct single_label *sl, - const char *compound_label) -{ - const char *delim; - - if (!compound_label || compound_label[0] == '\0') - return NULL; - - delim = strstr(compound_label, STACK_DELIM); - if (!delim) { - sl->label = compound_label; - sl->len = strlen(sl->label); - return sl->label + sl->len; - } - - sl->label = compound_label; - sl->len = delim - sl->label; - return delim + STACK_DELIM_LEN; -} - -/* Returns true if the compound label was constructed successfully */ -static bool compound_label_init(struct compound_label *cl, - const char *compound_label) -{ - memset(cl, 0, sizeof(*cl)); - while ((compound_label = next_label(&cl->labels[cl->num_labels], - compound_label))) { - cl->num_labels++; - - if (cl->num_labels == MAX_LABELS) - return false; - } - - return true; -} - -/* Returns true if the compound label contains the single label */ -static bool compound_label_contains(struct compound_label *cl, - struct single_label *sl) -{ - bool matched = false; - size_t i; - - for (i = 0; !matched && i < cl->num_labels; i++) { - if (cl->labels[i].len != sl->len) - continue; - - if (strncmp(cl->labels[i].label, sl->label, sl->len)) - continue; - - matched = true; - } - - return matched; -} - -/* Returns true if the two compound labels contain the same label sets */ -static bool compound_labels_equal(struct compound_label *cl1, - struct compound_label *cl2) -{ - size_t i; - - if (cl1->num_labels != cl2->num_labels) - return false; - - for (i = 0; i < cl1->num_labels; i++) { - if (!compound_label_contains(cl2, &cl1->labels[i])) - return false; - } - - return true; -} - -/** - * Verifies that the current confinement context matches the expected context. - * - * Either @expected_label or @expected_mode can be NULL if their values should - * not be verified. If a NULL mode is expected, as what happens when an - * unconfined process calls aa_getcon(2), then @expected_mode should be equal - * to NO_MODE. - */ -static void verify_confinement_context(const char *expected_label, - const char *expected_mode) -{ - char *label, *mode; - int expected_rc, rc; - bool null_expected_mode = expected_mode ? - strcmp(NO_MODE, expected_mode) == 0 : false; - - rc = aa_getcon(&label, &mode); - if (rc < 0) { - int err = errno; - fprintf(stderr, "FAIL - aa_getcon: %m"); - exit(err); - } - - if (expected_label) { - struct compound_label cl, expected_cl; - - if (!compound_label_init(&cl, label)) { - fprintf(stderr, "FAIL - could not parse current compound label: %s\n", - label); - rc = EINVAL; - goto err; - } - - if (!compound_label_init(&expected_cl, expected_label)) { - fprintf(stderr, "FAIL - could not parse expected compound label: %s\n", - expected_label); - rc = EINVAL; - goto err; - } - - if (!compound_labels_equal(&cl, &expected_cl)) { - fprintf(stderr, "FAIL - label \"%s\" != expected_label \"%s\"\n", - label, expected_label); - rc = EINVAL; - goto err; - } - } - - if (expected_mode && - ((!mode && !null_expected_mode) || - (mode && strcmp(mode, expected_mode)))) { - fprintf(stderr, "FAIL - mode \"%s\" != expected_mode \"%s\"\n", - mode, expected_mode); - rc = EINVAL; - goto err; - } - - expected_rc = expected_label ? strlen(expected_label) : strlen(label); - - /** - * Add the expected bytes following the returned label string: - * - * ' ' + '(' + mode + ')' - */ - if (expected_mode && !null_expected_mode) - expected_rc += 1 + 1 + strlen(expected_mode) + 1; - else if (mode) - expected_rc += 1 + 1 + strlen(mode) + 1; - - expected_rc++; /* Trailing NUL terminator */ - - if (rc != expected_rc) { - fprintf(stderr, "FAIL - rc (%d) != expected_rc (%d)\n", - rc, expected_rc); - rc = EINVAL; - goto err; - } - - return; -err: - free(label); - exit(EINVAL); -} - -static void stack_onexec(const char *label) -{ - if (aa_stack_onexec(label) != 0) { - int err = errno; - perror("FAIL - aa_stack_onexec"); - exit(err); - } -} - -static void stack_profile(const char *label) -{ - if (aa_stack_profile(label) != 0) { - int err = errno; - perror("FAIL - aa_stack_profile"); - exit(err); - } -} - -static void exec(const char *prog, char **argv) -{ - int err; - - execv(prog, argv); - err = errno; - perror("FAIL - execv"); - exit(err); -} - -static void usage(const char *prog) -{ - fprintf(stderr, - "%s: [-o <LABEL> | -p <LABEL>] [-l <LABEL>] [-m <MODE>] [-f <FILE>] [-- ... [-- ...]]\n" - " -o <LABEL>\tCall aa_stack_onexec(LABEL)\n" - " -p <LABEL>\tCall aa_stack_profile(LABEL)\n" - " -l <LABEL>\tVerify that aa_getcon() returns LABEL\n" - " -m <MODE>\tVerify that aa_getcon() returns MODE. Set to \"%s\" if a NULL mode is expected.\n" - " -f <FILE>\tOpen FILE and attempt to write to and read from it\n\n" - "If \"--\" is encountered, execv() will be called using the following argument\n" - "as the program to execute and passing it all of the arguments following the\n" - "program name.\n", prog, NO_MODE); - exit(EINVAL); -} - -struct options { - const char *file; - const char *expected_label; - const char *expected_mode; - const char *stack_onexec; - const char *stack_profile; - const char *exec; - char **exec_argv; -}; - -static void parse_opts(int argc, char **argv, struct options *opts) -{ - int o; - - memset(opts, 0, sizeof(*opts)); - while ((o = getopt(argc, argv, "f:l:m:o:p:")) != -1) { - switch (o) { - case 'f': /* file */ - opts->file = optarg; - break; - case 'l': /* expected label */ - opts->expected_label = optarg; - break; - case 'm': /* expected mode */ - opts->expected_mode = optarg; - break; - case 'o': /* aa_stack_onexec */ - opts->stack_onexec = optarg; - break; - case 'p': /* aa_stack_profile */ - opts->stack_profile = optarg; - break; - default: /* '?' */ - usage(argv[0]); - } - } - - /* Can only specify one or the other */ - if (opts->stack_onexec && opts->stack_profile) { - usage(argv[0]); - } - - if (optind < argc) { - /* Ensure that the previous option was "--" */ - if (optind == 0 || strcmp("--", argv[optind - 1])) - usage(argv[0]); - - opts->exec = argv[optind]; - opts->exec_argv = &argv[optind]; - } -} - -int main(int argc, char **argv) -{ - struct options opts; - - parse_opts(argc, argv, &opts); - - if (opts.stack_onexec) - stack_onexec(opts.stack_onexec); - else if (opts.stack_profile) - stack_profile(opts.stack_profile); - - if (opts.file) - file_io(opts.file); - - if (opts.expected_label || opts.expected_mode) - verify_confinement_context(opts.expected_label, - opts.expected_mode); - - if (opts.exec) - exec(opts.exec, opts.exec_argv); - - printf("PASS\n"); - exit(0); -} - diff --git a/tests/regression/apparmor/stackonexec.sh b/tests/regression/apparmor/stackonexec.sh index 7bad824..565fbfc 100755 --- a/tests/regression/apparmor/stackonexec.sh +++ b/tests/regression/apparmor/stackonexec.sh @@ -20,7 +20,7 @@ bin=$pwd . $bin/prologue.inc requires_kernel_features domain/stack -settest stacking +settest transition file=$tmpdir/file otherfile=$tmpdir/file2 diff --git a/tests/regression/apparmor/stackprofile.sh b/tests/regression/apparmor/stackprofile.sh index 7f248a1..efe8a7c 100755 --- a/tests/regression/apparmor/stackprofile.sh +++ b/tests/regression/apparmor/stackprofile.sh @@ -20,7 +20,7 @@ bin=$pwd . $bin/prologue.inc requires_kernel_features domain/stack -settest stacking +settest transition file=$tmpdir/file otherfile=$tmpdir/file2 diff --git a/tests/regression/apparmor/transition.c b/tests/regression/apparmor/transition.c new file mode 100644 index 0000000..ac1afce --- /dev/null +++ b/tests/regression/apparmor/transition.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2014-2016 Canonical, Ltd. + * + * 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. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/apparmor.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "changehat.h" /* for do_open() */ + +#define STACK_DELIM "//&" +#define STACK_DELIM_LEN strlen(STACK_DELIM) + +#define NO_MODE "(null)" + +static void file_io(const char *file) +{ + int rc = do_open(file); + + if (rc != 0) + exit(rc); +} + +struct single_label { + const char *label; + size_t len; +}; + +#define MAX_LABELS 32 + +struct compound_label { + size_t num_labels; + struct single_label labels[MAX_LABELS]; +}; + +/** + * Initializes @sl by parsing @compound_label. Returns a pointer to the + * location of the next label in the compound label string, which should be + * passed in as @compound_label the next time that next_label() is called. NULL + * is returned when there are no more labels in @compound_label. + */ +static const char *next_label(struct single_label *sl, + const char *compound_label) +{ + const char *delim; + + if (!compound_label || compound_label[0] == '\0') + return NULL; + + delim = strstr(compound_label, STACK_DELIM); + if (!delim) { + sl->label = compound_label; + sl->len = strlen(sl->label); + return sl->label + sl->len; + } + + sl->label = compound_label; + sl->len = delim - sl->label; + return delim + STACK_DELIM_LEN; +} + +/* Returns true if the compound label was constructed successfully */ +static bool compound_label_init(struct compound_label *cl, + const char *compound_label) +{ + memset(cl, 0, sizeof(*cl)); + while ((compound_label = next_label(&cl->labels[cl->num_labels], + compound_label))) { + cl->num_labels++; + + if (cl->num_labels == MAX_LABELS) + return false; + } + + return true; +} + +/* Returns true if the compound label contains the single label */ +static bool compound_label_contains(struct compound_label *cl, + struct single_label *sl) +{ + bool matched = false; + size_t i; + + for (i = 0; !matched && i < cl->num_labels; i++) { + if (cl->labels[i].len != sl->len) + continue; + + if (strncmp(cl->labels[i].label, sl->label, sl->len)) + continue; + + matched = true; + } + + return matched; +} + +/* Returns true if the two compound labels contain the same label sets */ +static bool compound_labels_equal(struct compound_label *cl1, + struct compound_label *cl2) +{ + size_t i; + + if (cl1->num_labels != cl2->num_labels) + return false; + + for (i = 0; i < cl1->num_labels; i++) { + if (!compound_label_contains(cl2, &cl1->labels[i])) + return false; + } + + return true; +} + +/** + * Verifies that the current confinement context matches the expected context. + * + * Either @expected_label or @expected_mode can be NULL if their values should + * not be verified. If a NULL mode is expected, as what happens when an + * unconfined process calls aa_getcon(2), then @expected_mode should be equal + * to NO_MODE. + */ +static void verify_confinement_context(const char *expected_label, + const char *expected_mode) +{ + char *label, *mode; + int expected_rc, rc; + bool null_expected_mode = expected_mode ? + strcmp(NO_MODE, expected_mode) == 0 : false; + + rc = aa_getcon(&label, &mode); + if (rc < 0) { + int err = errno; + fprintf(stderr, "FAIL - aa_getcon: %m"); + exit(err); + } + + if (expected_label) { + struct compound_label cl, expected_cl; + + if (!compound_label_init(&cl, label)) { + fprintf(stderr, "FAIL - could not parse current compound label: %s\n", + label); + rc = EINVAL; + goto err; + } + + if (!compound_label_init(&expected_cl, expected_label)) { + fprintf(stderr, "FAIL - could not parse expected compound label: %s\n", + expected_label); + rc = EINVAL; + goto err; + } + + if (!compound_labels_equal(&cl, &expected_cl)) { + fprintf(stderr, "FAIL - label \"%s\" != expected_label \"%s\"\n", + label, expected_label); + rc = EINVAL; + goto err; + } + } + + if (expected_mode && + ((!mode && !null_expected_mode) || + (mode && strcmp(mode, expected_mode)))) { + fprintf(stderr, "FAIL - mode \"%s\" != expected_mode \"%s\"\n", + mode, expected_mode); + rc = EINVAL; + goto err; + } + + expected_rc = expected_label ? strlen(expected_label) : strlen(label); + + /** + * Add the expected bytes following the returned label string: + * + * ' ' + '(' + mode + ')' + */ + if (expected_mode && !null_expected_mode) + expected_rc += 1 + 1 + strlen(expected_mode) + 1; + else if (mode) + expected_rc += 1 + 1 + strlen(mode) + 1; + + expected_rc++; /* Trailing NUL terminator */ + + if (rc != expected_rc) { + fprintf(stderr, "FAIL - rc (%d) != expected_rc (%d)\n", + rc, expected_rc); + rc = EINVAL; + goto err; + } + + return; +err: + free(label); + exit(EINVAL); +} + +static void stack_onexec(const char *label) +{ + if (aa_stack_onexec(label) != 0) { + int err = errno; + perror("FAIL - aa_stack_onexec"); + exit(err); + } +} + +static void stack_profile(const char *label) +{ + if (aa_stack_profile(label) != 0) { + int err = errno; + perror("FAIL - aa_stack_profile"); + exit(err); + } +} + +static void exec(const char *prog, char **argv) +{ + int err; + + execv(prog, argv); + err = errno; + perror("FAIL - execv"); + exit(err); +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "%s: [-o <LABEL> | -p <LABEL>] [-l <LABEL>] [-m <MODE>] [-f <FILE>] [-- ... [-- ...]]\n" + " -o <LABEL>\tCall aa_stack_onexec(LABEL)\n" + " -p <LABEL>\tCall aa_stack_profile(LABEL)\n" + " -l <LABEL>\tVerify that aa_getcon() returns LABEL\n" + " -m <MODE>\tVerify that aa_getcon() returns MODE. Set to \"%s\" if a NULL mode is expected.\n" + " -f <FILE>\tOpen FILE and attempt to write to and read from it\n\n" + "If \"--\" is encountered, execv() will be called using the following argument\n" + "as the program to execute and passing it all of the arguments following the\n" + "program name.\n", prog, NO_MODE); + exit(EINVAL); +} + +struct options { + const char *file; + const char *expected_label; + const char *expected_mode; + const char *stack_onexec; + const char *stack_profile; + const char *exec; + char **exec_argv; +}; + +static void parse_opts(int argc, char **argv, struct options *opts) +{ + int o; + + memset(opts, 0, sizeof(*opts)); + while ((o = getopt(argc, argv, "f:l:m:o:p:")) != -1) { + switch (o) { + case 'f': /* file */ + opts->file = optarg; + break; + case 'l': /* expected label */ + opts->expected_label = optarg; + break; + case 'm': /* expected mode */ + opts->expected_mode = optarg; + break; + case 'o': /* aa_stack_onexec */ + opts->stack_onexec = optarg; + break; + case 'p': /* aa_stack_profile */ + opts->stack_profile = optarg; + break; + default: /* '?' */ + usage(argv[0]); + } + } + + /* Can only specify one or the other */ + if (opts->stack_onexec && opts->stack_profile) { + usage(argv[0]); + } + + if (optind < argc) { + /* Ensure that the previous option was "--" */ + if (optind == 0 || strcmp("--", argv[optind - 1])) + usage(argv[0]); + + opts->exec = argv[optind]; + opts->exec_argv = &argv[optind]; + } +} + +int main(int argc, char **argv) +{ + struct options opts; + + parse_opts(argc, argv, &opts); + + if (opts.stack_onexec) + stack_onexec(opts.stack_onexec); + else if (opts.stack_profile) + stack_profile(opts.stack_profile); + + if (opts.file) + file_io(opts.file); + + if (opts.expected_label || opts.expected_mode) + verify_confinement_context(opts.expected_label, + opts.expected_mode); + + if (opts.exec) + exec(opts.exec, opts.exec_argv); + + printf("PASS\n"); + exit(0); +} + -- 2.7.4 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
