tests: Add NO_NEW_PRIVS regression tests

Test the profile transition limits imposed by NO_NEW_PRIVS to ensure
that behavior doesn't unexpectedly change.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
This commit is contained in:
Tyler Hicks 2019-08-01 05:26:11 +00:00
parent db1f391844
commit 9160204008
3 changed files with 104 additions and 3 deletions

View file

@ -171,7 +171,7 @@ ifdef USE_SYSTEM
endif
ifneq (,$(shell pkg-config --atleast-version 2.10.95 libapparmor && echo TRUE))
CONDITIONAL_TESTS+=exec_stack stackonexec stackprofile
CONDITIONAL_TESTS+=exec_stack nnp stackonexec stackprofile
else
$(warning ${nl}\
************************************************************************${nl}\
@ -181,7 +181,7 @@ ifdef USE_SYSTEM
endif
else
SRC+=aa_policy_cache.c
CONDITIONAL_TESTS+=exec_stack aa_policy_cache stackonexec stackprofile
CONDITIONAL_TESTS+=exec_stack aa_policy_cache nnp stackonexec stackprofile
endif
EXEC=$(SRC:%.c=%)

View file

@ -0,0 +1,82 @@
#! /bin/bash
# Copyright (C) 2019 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 nnp
#=DESCRIPTION
# Verifies AppArmor interactions with NO_NEW_PRIVS
#=END
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
. $bin/prologue.inc
settest transition
file=$tmpdir/file
okperm=rw
fileok="${file}:${okperm}"
getcon="/proc/*/attr/current:r"
setcon="/proc/*/attr/current:w"
setexec="/proc/*/attr/exec:w"
touch $file
# Verify file access by an unconfined process
runchecktest "NNP (unconfined - no NNP)" pass -f "$file"
runchecktest "NNP (unconfined - NNP)" pass -n -f "$file"
# Verify file access under simple confinement
genprofile "$fileok" "$getcon"
runchecktest "NNP (confined - no NNP)" pass -f "$file"
runchecktest "NNP (confined - NNP)" pass -n -f "$file"
# Verify that NNP allows ix transitions
genprofile image="$test" "$fileok" "$getcon"
runchecktest "NNP (ix - no NNP)" pass -- "$test" -f "$file"
runchecktest "NNP (ix - NNP)" pass -- "$test" -n -f "$file"
# Verify that NNP causes unconfined profile transition failures
# NNP-induced failures will use EPERM rather than EACCES
genprofile -I "$test":rux "$fileok"
runchecktest "NNP (ux - no NNP)" pass -- "$test" -f "$file"
runchecktest_errno EPERM "NNP (ux - NNP)" fail -n -- "$test" -f "$file"
# Verify that NNP causes discrete profile transition failures
genprofile "$bin/open":px -- image="$bin/open" "$fileok"
runchecktest "NNP (px - no NNP)" pass -- "$bin/open" "$file"
runchecktest_errno EPERM "NNP (px - NNP)" fail -n -- "$bin/open" "$file"
# Verify that NNP causes change onexec failures
genprofile "change_profile->":"$bin/open" "$setexec" -- image="$bin/open" "$fileok"
runchecktest "NNP (change onexec - no NNP)" pass -O "$bin/open" -- "$bin/open" "$file"
runchecktest_errno EPERM "NNP (change onexec - NNP)" fail -n -O "$bin/open" -- "$bin/open" "$file"
# Verify that NNP causes change profile failures
genprofile "change_profile->":"$bin/open" "$setcon" -- image="$bin/open"
runchecktest "NNP (change profile - no NNP)" pass -P "$bin/open"
runchecktest_errno EPERM "NNP (change profile - NNP)" fail -n -P "$bin/open"
if [ "$(kernel_features_istrue domain/stack)" != "true" ] ; then
echo " kernel does not support profile stacking - skipping stacking tests ..."
else
# Verify that NNP allows stack onexec of another profile
genprofile "$fileok" "$setexec" "change_profile->:&${bin}/open" -- image="$bin/open" "$fileok"
runchecktest "NNP (stack onexec - no NNP)" pass -o "$bin/open" -- "$bin/open" "$file"
runchecktest "NNP (stack onexec - NNP)" pass -n -o "$bin/open" -- "$bin/open" "$file"
# Verify that NNP allows stacking another profile
genprofile "$fileok" "$setcon" "change_profile->:&$bin/open" -- image="$bin/open" "$fileok"
runchecktest "NNP (stack profile - no NNP)" pass -p "$bin/open" -f "$file"
runchecktest "NNP (stack profile - NNP)" pass -n -p "$bin/open" -f "$file"
fi

View file

@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@ -281,6 +282,15 @@ static void handle_transition(int transition, const char *target)
}
}
static void set_no_new_privs(void)
{
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
int err = errno;
perror("FAIL - prctl (PR_SET_NO_NEW_PRIVS)");
exit(err);
}
}
static void exec(const char *prog, char **argv)
{
int err;
@ -299,6 +309,7 @@ static void usage(const char *prog)
" -P <LABEL>\tCall aa_change_profile(LABEL)\n"
" -o <LABEL>\tCall aa_stack_onexec(LABEL)\n"
" -p <LABEL>\tCall aa_stack_profile(LABEL)\n"
" -n\t\tSet NO_NEW_PRIVS\n"
" -L <LABEL>\tVerify that /proc/self/attr/exec contains LABEL\n"
" -M <MODE>\tVerify that /proc/self/attr/exec contains MODE. Set to \"%s\" if a NULL mode is expected.\n"
" -l <LABEL>\tVerify that /proc/self/attr/current contains LABEL\n"
@ -320,6 +331,8 @@ struct options {
int transition; /* CHANGE_PROFILE, STACK_ONEXEC, etc. */
const char *target; /* The target label of the transition */
bool no_new_privs;
const char *exec;
char **exec_argv;
};
@ -341,7 +354,7 @@ 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:l:m:O:P:o:p:")) != -1) {
while ((o = getopt(argc, argv, "f:L:M:l:m:nO:P:o:p:")) != -1) {
switch (o) {
case 'f': /* file */
opts->file = optarg;
@ -358,6 +371,9 @@ static void parse_opts(int argc, char **argv, struct options *opts)
case 'm': /* expected current mode */
opts->expected_current_mode = optarg;
break;
case 'n': /* NO_NEW_PRIVS */
opts->no_new_privs = true;
break;
case 'O': /* aa_change_profile */
set_transition(prog, opts, CHANGE_ONEXEC, optarg);
break;
@ -391,6 +407,9 @@ int main(int argc, char **argv)
parse_opts(argc, argv, &opts);
if (opts.no_new_privs)
set_no_new_privs();
if (opts.transition)
handle_transition(opts.transition, opts.target);