diff --git a/test.c b/test.c index 015f5be..0ca1c35 100644 --- a/test.c +++ b/test.c @@ -4,6 +4,7 @@ #include #include #include +#include int xqssb_enable_policy(struct qssb_policy *policy) { @@ -23,10 +24,70 @@ int test_default_main(int argc, char *argv[]) return ret; } -int test_seccomp_blacklisted(int argc, char *argv[]) +static int test_expected_kill(int (*f)()) +{ + pid_t pid = fork(); + if(pid == 0) + { + return f(); + } + int status = 0; + waitpid(pid, &status, 0); + + if(WIFSIGNALED(status)) + { + int c = WTERMSIG(status); + if(c == SIGSYS) + { + printf("Got expected signal\n"); + return 0; + } + printf("Unexpected status code: %i\n", c); + return 1; + } + else + { + int c = WEXITSTATUS(status); + printf("Process was not killed, test fails. Status code of exit: %i\n", c); + return 1; + } + return 0; +} + + +static int test_successful_exit(int (*f)()) +{ + pid_t pid = fork(); + if(pid == 0) + { + return f(); + } + int status = 0; + waitpid(pid, &status, 0); + + if(WIFSIGNALED(status)) + { + int c = WTERMSIG(status); + printf("Received signal, which was not expected. Signal was: %i\n", c); + return 1; + } + else + { + int c = WEXITSTATUS(status); + if(c != 0) + { + printf("Process failed to exit properly. Status code is: %i\n", c); + } + return c; + } + printf("Process exited sucessfully as expected"); + return 0; +} + + +static int do_test_seccomp_blacklisted() { struct qssb_policy *policy = qssb_init_policy(); - qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); @@ -35,9 +96,16 @@ int test_seccomp_blacklisted(int argc, char *argv[]) uid_t pid = geteuid(); pid = getuid(); return 0; + + +} +int test_seccomp_blacklisted(int argc, char *argv[]) +{ + return test_expected_kill(&do_test_seccomp_blacklisted); } -int test_seccomp_blacklisted_call_permitted(int argc, char *argv[]) + +static int do_test_seccomp_blacklisted_call_permitted() { struct qssb_policy *policy = qssb_init_policy(); @@ -50,19 +118,20 @@ int test_seccomp_blacklisted_call_permitted(int argc, char *argv[]) return 0; } -int test_seccomp_x32_kill(int argc, char *argv[]) + +int test_seccomp_blacklisted_call_permitted(int argc, char *argv[]) +{ + return test_successful_exit(&do_test_seccomp_blacklisted_call_permitted); +} + +static int do_test_seccomp_x32_kill() { struct qssb_policy *policy = qssb_init_policy(); qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); - int ret = qssb_enable_policy(policy); - if(ret != 0) - { - fprintf(stderr, "Error: Enabling is expected to succeed. Returning 0 to indicate failure of this test\n"); - return 0; - } + xqssb_enable_policy(policy); /* Attempt to bypass by falling back to x32 should be blocked */ syscall(QSSB_SYS(getuid)+__X32_SYSCALL_BIT); @@ -70,6 +139,11 @@ int test_seccomp_x32_kill(int argc, char *argv[]) return 0; } +int test_seccomp_x32_kill(int argc, char *argv[]) +{ + return test_expected_kill(&do_test_seccomp_x32_kill); +} + /* Tests whether seccomp rules end with a policy matching all syscalls */ int test_seccomp_require_last_matchall(int argc, char *argv[]) { @@ -77,13 +151,18 @@ int test_seccomp_require_last_matchall(int argc, char *argv[]) qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid)); - return qssb_enable_policy(policy); + int status = qssb_enable_policy(policy); + if(status == 0) + { + printf("Failed. Should not have been enabled!"); + return 1; + } + return 0; } -int test_seccomp_errno(int argc, char *argv[]) +static int do_test_seccomp_errno() { struct qssb_policy *policy = qssb_init_policy(); - policy->not_dumpable = 0; qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_RET_ERROR, QSSB_SYS(close)); qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW); @@ -96,6 +175,13 @@ int test_seccomp_errno(int argc, char *argv[]) return fd == -1 ? 0 : 1; } + + +int test_seccomp_errno(int argc, char *argv[]) +{ + return test_successful_exit(&do_test_seccomp_errno); +} + int test_landlock(int argc, char *argv[]) { struct qssb_policy *policy = qssb_init_policy(); @@ -138,14 +224,14 @@ int test_nofs(int argc, char *argv[]) if(s == -1) { fprintf(stderr, "Failed to open socket but this was not requested by policy\n"); - return 0; + return 1; } /* Expect seccomp to take care of this */ if(open("/test", O_CREAT | O_WRONLY) >= 0) { - fprintf(stderr, "Failed: Do not expect write access\n"); - return -1; + fprintf(stderr, "Failed: We do not expect write access\n"); + return 1; } return 0; @@ -185,25 +271,23 @@ struct dispatcher { char *name; int (*f)(int, char **); - bool must_exit_zero; }; struct dispatcher dispatchers[] = { - { "default", &test_default_main, true }, - { "seccomp-blacklisted", &test_seccomp_blacklisted, false }, - { "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted, true }, - { "seccomp-x32-kill", &test_seccomp_x32_kill, false}, - { "seccomp-require-last-matchall", &test_seccomp_require_last_matchall, false}, - { "seccomp-errno", &test_seccomp_errno, true}, - { "landlock", &test_landlock, true }, - { "landlock-deny-write", &test_landlock_deny_write, true }, - { "no_fs", &test_nofs, false}, - { "no_new_fds", &test_no_new_fds, true} + { "default", &test_default_main }, + { "seccomp-blacklisted", &test_seccomp_blacklisted}, + { "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted}, + { "seccomp-x32-kill", &test_seccomp_x32_kill}, + { "seccomp-require-last-matchall", &test_seccomp_require_last_matchall}, + { "seccomp-errno", &test_seccomp_errno}, + { "landlock", &test_landlock}, + { "landlock-deny-write", &test_landlock_deny_write }, + { "no_fs", &test_nofs}, + { "no_new_fds", &test_no_new_fds} }; int main(int argc, char *argv[]) { - if(argc < 2) { fprintf(stderr, "Usage: %s [testname]\n", argv[0]); @@ -214,7 +298,7 @@ int main(int argc, char *argv[]) { for(unsigned int i = 0; i < sizeof(dispatchers)/sizeof(dispatchers[0]); i++) { - printf("%s:%i\n", dispatchers[i].name, dispatchers[i].must_exit_zero ? 1 : 0); + printf("%s\n", dispatchers[i].name); } return EXIT_SUCCESS; } diff --git a/test.sh b/test.sh index bcb57e8..19d57ce 100755 --- a/test.sh +++ b/test.sh @@ -32,8 +32,7 @@ function runtest_success() function runtest() { testname="$1" - must_exit_zero="$2" - test_log_file="$3" + test_log_file="$2" echo "Running: $testname. Date: $(date)" > "${test_log_file}" @@ -42,24 +41,14 @@ function runtest() (./test $1 || exit $?) &>> "${test_log_file}" ret=$? SUCCESS="no" - if [ $must_exit_zero -eq 1 ] ; then - if [ $ret -eq 0 ] ; then - runtest_success - SUCCESS="yes" - else - runtest_fail - fi + if [ $ret -eq 0 ] ; then + runtest_success + SUCCESS="yes" else - if [ $ret -eq 0 ] ; then - runtest_fail - else - runtest_success - SUCCESS="yes" - fi + runtest_fail fi - echo "Finished: ${testname}. Date: $(date). Return code: $ret. Success: $SUCCESS" >> "${test_log_file}" - + echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}" } GIT_ID=$( git log --pretty="format:%h" -n1 ) @@ -73,9 +62,8 @@ LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/qssb_test_${GIT_ID}_${TIMESTAMP}" [ -d "$LOG_OUTPUT_DIR_PATH" ] || mkdir -p "$LOG_OUTPUT_DIR_PATH" for test in $( ./test --dumptests ) ; do - testname=$( echo $test | cut -d":" -f1 ) - must_exit_zero=$( echo "$test" | cut -d":" -f2 ) - runtest "$testname" $must_exit_zero "${LOG_OUTPUT_DIR_PATH}/log.${testname}" + testname=$( echo $test ) + runtest "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}" done echo echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"