Index: head/tests/sys/audit/process-control.c =================================================================== --- head/tests/sys/audit/process-control.c (revision 336417) +++ head/tests/sys/audit/process-control.c (revision 336418) @@ -1,1544 +1,1674 @@ /*- * Copyright (c) 2018 Aniket Pandey * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * SUCH DAMAGE. * * $FreeBSD$ */ #include +#include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include "utils.h" static pid_t pid; static int filedesc, status; static struct pollfd fds[1]; static char pcregex[80]; static const char *auclass = "pc"; ATF_TC_WITH_CLEANUP(fork_success); ATF_TC_HEAD(fork_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "fork(2) call"); } ATF_TC_BODY(fork_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fork.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Check if fork(2) succeded. If so, exit from the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) check_audit(fds, pcregex, pipefd); else _exit(0); } ATF_TC_CLEANUP(fork_success, tc) { cleanup(); } /* * No fork(2) in failure mode since possibilities for failure are only when * user is not privileged or when the number of processes exceed KERN_MAXPROC. */ +ATF_TC_WITH_CLEANUP(_exit_success); +ATF_TC_HEAD(_exit_success, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " + "_exit(2) call"); +} + +ATF_TC_BODY(_exit_success, tc) +{ + FILE *pipefd = setup(fds, auclass); + ATF_REQUIRE((pid = fork()) != -1); + if (pid) { + snprintf(pcregex, sizeof(pcregex), "exit.*%d.*success", pid); + check_audit(fds, pcregex, pipefd); + } + else + _exit(0); +} + +ATF_TC_CLEANUP(_exit_success, tc) +{ + cleanup(); +} + +/* + * _exit(2) never returns, hence the auditing by default is always successful + */ + + ATF_TC_WITH_CLEANUP(rfork_success); ATF_TC_HEAD(rfork_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "rfork(2) call"); } ATF_TC_BODY(rfork_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE((pid = rfork(RFPROC)) != -1); if (pid) check_audit(fds, pcregex, pipefd); else _exit(0); } ATF_TC_CLEANUP(rfork_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rfork_failure); ATF_TC_HEAD(rfork_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "rfork(2) call"); } ATF_TC_BODY(rfork_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid argument */ ATF_REQUIRE_EQ(-1, rfork(-1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rfork_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait4_success); ATF_TC_HEAD(wait4_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "wait4(2) call"); } ATF_TC_BODY(wait4_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,success", pid); ATF_REQUIRE((pid = fork()) != -1); if (pid) { FILE *pipefd = setup(fds, auclass); /* wpid = -1 : Wait for any child process */ ATF_REQUIRE(wait4(-1, &status, 0, NULL) != -1); check_audit(fds, pcregex, pipefd); } else _exit(0); } ATF_TC_CLEANUP(wait4_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait4_failure); ATF_TC_HEAD(wait4_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "wait4(2) call"); } ATF_TC_BODY(wait4_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: No child process to wait for */ ATF_REQUIRE_EQ(-1, wait4(-1, NULL, 0, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(wait4_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait6_success); ATF_TC_HEAD(wait6_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "wait6(2) call"); } ATF_TC_BODY(wait6_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,success", pid); ATF_REQUIRE((pid = fork()) != -1); if (pid) { FILE *pipefd = setup(fds, auclass); ATF_REQUIRE(wait6(P_ALL, 0, &status, WEXITED, NULL,NULL) != -1); check_audit(fds, pcregex, pipefd); } else _exit(0); } ATF_TC_CLEANUP(wait6_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(wait6_failure); ATF_TC_HEAD(wait6_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "wait6(2) call"); } ATF_TC_BODY(wait6_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid argument */ ATF_REQUIRE_EQ(-1, wait6(0, 0, NULL, 0, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(wait6_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(kill_success); ATF_TC_HEAD(kill_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "kill(2) call"); } ATF_TC_BODY(kill_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Don't send any signal to anyone, live in peace! */ ATF_REQUIRE_EQ(0, kill(0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(kill_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(kill_failure); ATF_TC_HEAD(kill_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "kill(2) call"); } ATF_TC_BODY(kill_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* * Failure reason: Non existent process with PID '-2' * Note: '-1' is not used as it means sending no signal to * all non-system processes: A successful invocation */ ATF_REQUIRE_EQ(-1, kill(0, -2)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(kill_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chdir_success); ATF_TC_HEAD(chdir_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "chdir(2) call"); } ATF_TC_BODY(chdir_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chdir.*/.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, chdir("/")); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chdir_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chdir_failure); ATF_TC_HEAD(chdir_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "chdir(2) call"); } ATF_TC_BODY(chdir_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chdir.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Bad address */ ATF_REQUIRE_EQ(-1, chdir(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chdir_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(fchdir_success); ATF_TC_HEAD(fchdir_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "fchdir(2) call"); } ATF_TC_BODY(fchdir_success, tc) { /* Build an absolute path to the test-case directory */ char dirpath[50]; ATF_REQUIRE(getcwd(dirpath, sizeof(dirpath)) != NULL); ATF_REQUIRE((filedesc = open(dirpath, O_RDONLY)) != -1); /* Audit record generated by fchdir(2) does not contain filedesc */ pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, fchdir(filedesc)); check_audit(fds, pcregex, pipefd); close(filedesc); } ATF_TC_CLEANUP(fchdir_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(fchdir_failure); ATF_TC_HEAD(fchdir_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "fchdir(2) call"); } ATF_TC_BODY(fchdir_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Bad directory address */ ATF_REQUIRE_EQ(-1, fchdir(-1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(fchdir_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chroot_success); ATF_TC_HEAD(chroot_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "chroot(2) call"); } ATF_TC_BODY(chroot_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* We don't want to change the root directory, hence '/' */ ATF_REQUIRE_EQ(0, chroot("/")); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chroot_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(chroot_failure); ATF_TC_HEAD(chroot_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "chroot(2) call"); } ATF_TC_BODY(chroot_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, chroot(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(chroot_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(umask_success); ATF_TC_HEAD(umask_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "umask(2) call"); } ATF_TC_BODY(umask_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "umask.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); umask(0); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(umask_success, tc) { cleanup(); } /* * umask(2) system call never fails. Hence, no test case for failure mode */ ATF_TC_WITH_CLEANUP(setuid_success); ATF_TC_HEAD(setuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setuid(2) call"); } ATF_TC_BODY(setuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* Since we're privileged, we'll let ourselves be privileged! */ ATF_REQUIRE_EQ(0, setuid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setuid_success, tc) { cleanup(); } /* * setuid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(seteuid_success); ATF_TC_HEAD(seteuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "seteuid(2) call"); } ATF_TC_BODY(seteuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "seteuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* This time, we'll let ourselves be 'effectively' privileged! */ ATF_REQUIRE_EQ(0, seteuid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(seteuid_success, tc) { cleanup(); } /* * seteuid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setgid_success); ATF_TC_HEAD(setgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setgid(2) call"); } ATF_TC_BODY(setgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setgid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgid_success, tc) { cleanup(); } /* * setgid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setegid_success); ATF_TC_HEAD(setegid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setegid(2) call"); } ATF_TC_BODY(setegid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setegid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setegid(0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setegid_success, tc) { cleanup(); } /* * setegid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setregid_success); ATF_TC_HEAD(setregid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setregid(2) call"); } ATF_TC_BODY(setregid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setregid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setregid(-1, -1) does not change any real or effective GIDs */ ATF_REQUIRE_EQ(0, setregid(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setregid_success, tc) { cleanup(); } /* * setregid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setreuid_success); ATF_TC_HEAD(setreuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setreuid(2) call"); } ATF_TC_BODY(setreuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setreuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setreuid(-1, -1) does not change any real or effective UIDs */ ATF_REQUIRE_EQ(0, setreuid(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setreuid_success, tc) { cleanup(); } /* * setregid(2) fails only when the current user is not root. So no test case for * failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setresuid_success); ATF_TC_HEAD(setresuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setresuid(2) call"); } ATF_TC_BODY(setresuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setresuid.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); /* setresuid(-1, -1, -1) does not change real, effective & saved UIDs */ ATF_REQUIRE_EQ(0, setresuid(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setresuid_success, tc) { cleanup(); } /* * setresuid(2) fails only when the current user is not root. So no test case * for failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(setresgid_success); ATF_TC_HEAD(setresgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setresgid(2) call"); } ATF_TC_BODY(setresgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setresgid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); /* setresgid(-1, -1, -1) does not change real, effective & saved GIDs */ ATF_REQUIRE_EQ(0, setresgid(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setresgid_success, tc) { cleanup(); } /* * setresgid(2) fails only when the current user is not root. So no test case * for failure mode since the required_user="root" */ ATF_TC_WITH_CLEANUP(getresuid_success); ATF_TC_HEAD(getresuid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "getresuid(2) call"); } ATF_TC_BODY(getresuid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, getresuid(NULL, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresuid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresuid_failure); ATF_TC_HEAD(getresuid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "getresuid(2) call"); } ATF_TC_BODY(getresuid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid address "-1" */ ATF_REQUIRE_EQ(-1, getresuid((uid_t *)-1, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresuid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresgid_success); ATF_TC_HEAD(getresgid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "getresgid(2) call"); } ATF_TC_BODY(getresgid_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, getresgid(NULL, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresgid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(getresgid_failure); ATF_TC_HEAD(getresgid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "getresgid(2) call"); } ATF_TC_BODY(getresgid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); /* Failure reason: Invalid address "-1" */ ATF_REQUIRE_EQ(-1, getresgid((gid_t *)-1, NULL, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(getresgid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpriority_success); ATF_TC_HEAD(setpriority_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setpriority(2) call"); } ATF_TC_BODY(setpriority_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setpriority(PRIO_PROCESS, 0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpriority_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpriority_failure); ATF_TC_HEAD(setpriority_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setpriority(2) call"); } ATF_TC_BODY(setpriority_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setpriority(-1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpriority_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setgroups_success); ATF_TC_HEAD(setgroups_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setgroups(2) call"); } ATF_TC_BODY(setgroups_success, tc) { gid_t gids[5]; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*success", pid); /* Retrieve the current group access list to be used with setgroups */ ATF_REQUIRE(getgroups(sizeof(gids)/sizeof(gids[0]), gids) != -1); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setgroups(sizeof(gids)/sizeof(gids[0]), gids)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgroups_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setgroups_failure); ATF_TC_HEAD(setgroups_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setgroups(2) call"); } ATF_TC_BODY(setgroups_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setgroups(-1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setgroups_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpgrp_success); ATF_TC_HEAD(setpgrp_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setpgrp(2) call"); } ATF_TC_BODY(setpgrp_success, tc) { /* Main procedure is carried out from within the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) { ATF_REQUIRE(wait(&status) != -1); } else { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setpgrp(0, 0)); check_audit(fds, pcregex, pipefd); } } ATF_TC_CLEANUP(setpgrp_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setpgrp_failure); ATF_TC_HEAD(setpgrp_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setpgrp(2) call"); } ATF_TC_BODY(setpgrp_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setpgrp(-1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setpgrp_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setsid_success); ATF_TC_HEAD(setsid_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setsid(2) call"); } ATF_TC_BODY(setsid_success, tc) { /* Main procedure is carried out from within the child process */ ATF_REQUIRE((pid = fork()) != -1); if (pid) { ATF_REQUIRE(wait(&status) != -1); } else { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE(setsid() != -1); check_audit(fds, pcregex, pipefd); } } ATF_TC_CLEANUP(setsid_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setsid_failure); ATF_TC_HEAD(setsid_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setsid(2) call"); } ATF_TC_BODY(setsid_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*return,failure", pid); /* * Here, we are intentionally ignoring the output of the setsid() * call because it may or may not be a process leader already. But it * ensures that the next invocation of setsid() will definitely fail. */ setsid(); FILE *pipefd = setup(fds, auclass); /* * Failure reason: [EPERM] Creating a new session is not permitted * as the PID of calling process matches the PGID of a process group * created by premature setsid() call. */ ATF_REQUIRE_EQ(-1, setsid()); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setsid_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setrlimit_success); ATF_TC_HEAD(setrlimit_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setrlimit(2) call"); } ATF_TC_BODY(setrlimit_success, tc) { struct rlimit rlp; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*success", pid); /* Retrieve the system resource consumption limit to be used later on */ ATF_REQUIRE_EQ(0, getrlimit(RLIMIT_FSIZE, &rlp)); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setrlimit(RLIMIT_FSIZE, &rlp)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setrlimit_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setrlimit_failure); ATF_TC_HEAD(setrlimit_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setrlimit(2) call"); } ATF_TC_BODY(setrlimit_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setrlimit(RLIMIT_FSIZE, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setrlimit_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(mlock_success); ATF_TC_HEAD(mlock_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "mlock(2) call"); } ATF_TC_BODY(mlock_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, mlock(NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(mlock_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(mlock_failure); ATF_TC_HEAD(mlock_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "mlock(2) call"); } ATF_TC_BODY(mlock_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, mlock((void *)(-1), -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(mlock_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(munlock_success); ATF_TC_HEAD(munlock_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "munlock(2) call"); } ATF_TC_BODY(munlock_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, munlock(NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(munlock_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(munlock_failure); ATF_TC_HEAD(munlock_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "munlock(2) call"); } ATF_TC_BODY(munlock_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, munlock((void *)(-1), -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(munlock_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(minherit_success); ATF_TC_HEAD(minherit_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "minherit(2) call"); } ATF_TC_BODY(minherit_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, minherit(NULL, 0, INHERIT_ZERO)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(minherit_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(minherit_failure); ATF_TC_HEAD(minherit_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "minherit(2) call"); } ATF_TC_BODY(minherit_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, minherit((void *)(-1), -1, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(minherit_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setlogin_success); ATF_TC_HEAD(setlogin_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "setlogin(2) call"); } ATF_TC_BODY(setlogin_success, tc) { char *name; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,success", pid); /* Retrieve the current user's login name to be used with setlogin(2) */ ATF_REQUIRE((name = getlogin()) != NULL); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, setlogin(name)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setlogin_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(setlogin_failure); ATF_TC_HEAD(setlogin_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "setlogin(2) call"); } ATF_TC_BODY(setlogin_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, setlogin(NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(setlogin_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rtprio_success); ATF_TC_HEAD(rtprio_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "rtprio(2) call"); } ATF_TC_BODY(rtprio_success, tc) { struct rtprio rtp; pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, rtprio(RTP_LOOKUP, 0, &rtp)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rtprio_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(rtprio_failure); ATF_TC_HEAD(rtprio_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "rtprio(2) call"); } ATF_TC_BODY(rtprio_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, rtprio(-1, -1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(rtprio_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(profil_success); ATF_TC_HEAD(profil_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "profil(2) call"); } ATF_TC_BODY(profil_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,success", pid); char samples[20]; FILE *pipefd = setup(fds, auclass); /* Set scale argument as 0 to disable profiling of current process */ ATF_REQUIRE_EQ(0, profil(samples, sizeof(samples), 0, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(profil_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(profil_failure); ATF_TC_HEAD(profil_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "profil(2) call"); } ATF_TC_BODY(profil_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, profil((char *)(SIZE_MAX), -1, -1, -1)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(profil_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ptrace_success); ATF_TC_HEAD(ptrace_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "ptrace(2) call"); } ATF_TC_BODY(ptrace_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, ptrace(PT_TRACE_ME, 0, NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ptrace_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ptrace_failure); ATF_TC_HEAD(ptrace_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "ptrace(2) call"); } ATF_TC_BODY(ptrace_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, ptrace(-1, 0, NULL, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ptrace_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ktrace_success); ATF_TC_HEAD(ktrace_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "ktrace(2) call"); } ATF_TC_BODY(ktrace_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,success", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(0, ktrace(NULL, KTROP_CLEAR, KTRFAC_SYSCALL, pid)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ktrace_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(ktrace_failure); ATF_TC_HEAD(ktrace_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "ktrace(2) call"); } ATF_TC_BODY(ktrace_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, ktrace(NULL, -1, -1, 0)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(ktrace_failure, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(procctl_success); ATF_TC_HEAD(procctl_success, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " "procctl(2) call"); } ATF_TC_BODY(procctl_success, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,success", pid); struct procctl_reaper_status reapstat; FILE *pipefd = setup(fds, auclass); /* Retrieve information about the reaper of current process (pid) */ ATF_REQUIRE_EQ(0, procctl(P_PID, pid, PROC_REAP_STATUS, &reapstat)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(procctl_success, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(procctl_failure); ATF_TC_HEAD(procctl_failure, tc) { atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " "procctl(2) call"); } ATF_TC_BODY(procctl_failure, tc) { pid = getpid(); snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,failure", pid); FILE *pipefd = setup(fds, auclass); ATF_REQUIRE_EQ(-1, procctl(-1, -1, -1, NULL)); check_audit(fds, pcregex, pipefd); } ATF_TC_CLEANUP(procctl_failure, tc) { cleanup(); } +ATF_TC_WITH_CLEANUP(cap_enter_success); +ATF_TC_HEAD(cap_enter_success, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " + "cap_enter(2) call"); +} + +ATF_TC_BODY(cap_enter_success, tc) +{ + int capinfo; + size_t len = sizeof(capinfo); + const char *capname = "kern.features.security_capability_mode"; + ATF_REQUIRE_EQ(0, sysctlbyname(capname, &capinfo, &len, NULL, 0)); + + /* Without CAPABILITY_MODE enabled, cap_enter() returns ENOSYS */ + if (!capinfo) + atf_tc_skip("Capsicum is not enabled in the system"); + + FILE *pipefd = setup(fds, auclass); + ATF_REQUIRE((pid = fork()) != -1); + if (pid) { + snprintf(pcregex, sizeof(pcregex), + "cap_enter.*%d.*return,success", pid); + ATF_REQUIRE(wait(&status) != -1); + check_audit(fds, pcregex, pipefd); + } + else { + ATF_REQUIRE_EQ(0, cap_enter()); + _exit(0); + } +} + +ATF_TC_CLEANUP(cap_enter_success, tc) +{ + cleanup(); +} + + +ATF_TC_WITH_CLEANUP(cap_getmode_success); +ATF_TC_HEAD(cap_getmode_success, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " + "cap_getmode(2) call"); +} + +ATF_TC_BODY(cap_getmode_success, tc) +{ + int capinfo, modep; + size_t len = sizeof(capinfo); + const char *capname = "kern.features.security_capability_mode"; + ATF_REQUIRE_EQ(0, sysctlbyname(capname, &capinfo, &len, NULL, 0)); + + /* Without CAPABILITY_MODE enabled, cap_getmode() returns ENOSYS */ + if (!capinfo) + atf_tc_skip("Capsicum is not enabled in the system"); + + pid = getpid(); + snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*success", pid); + + FILE *pipefd = setup(fds, auclass); + ATF_REQUIRE_EQ(0, cap_getmode(&modep)); + check_audit(fds, pcregex, pipefd); +} + +ATF_TC_CLEANUP(cap_getmode_success, tc) +{ + cleanup(); +} + + +ATF_TC_WITH_CLEANUP(cap_getmode_failure); +ATF_TC_HEAD(cap_getmode_failure, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " + "cap_getmode(2) call"); +} + +ATF_TC_BODY(cap_getmode_failure, tc) +{ + pid = getpid(); + snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*failure", pid); + + FILE *pipefd = setup(fds, auclass); + /* cap_getmode(2) can either fail with EFAULT or ENOSYS */ + ATF_REQUIRE_EQ(-1, cap_getmode(NULL)); + check_audit(fds, pcregex, pipefd); +} + +ATF_TC_CLEANUP(cap_getmode_failure, tc) +{ + cleanup(); +} + + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, fork_success); + ATF_TP_ADD_TC(tp, _exit_success); ATF_TP_ADD_TC(tp, rfork_success); ATF_TP_ADD_TC(tp, rfork_failure); ATF_TP_ADD_TC(tp, wait4_success); ATF_TP_ADD_TC(tp, wait4_failure); ATF_TP_ADD_TC(tp, wait6_success); ATF_TP_ADD_TC(tp, wait6_failure); ATF_TP_ADD_TC(tp, kill_success); ATF_TP_ADD_TC(tp, kill_failure); ATF_TP_ADD_TC(tp, chdir_success); ATF_TP_ADD_TC(tp, chdir_failure); ATF_TP_ADD_TC(tp, fchdir_success); ATF_TP_ADD_TC(tp, fchdir_failure); ATF_TP_ADD_TC(tp, chroot_success); ATF_TP_ADD_TC(tp, chroot_failure); ATF_TP_ADD_TC(tp, umask_success); ATF_TP_ADD_TC(tp, setuid_success); ATF_TP_ADD_TC(tp, seteuid_success); ATF_TP_ADD_TC(tp, setgid_success); ATF_TP_ADD_TC(tp, setegid_success); ATF_TP_ADD_TC(tp, setreuid_success); ATF_TP_ADD_TC(tp, setregid_success); ATF_TP_ADD_TC(tp, setresuid_success); ATF_TP_ADD_TC(tp, setresgid_success); ATF_TP_ADD_TC(tp, getresuid_success); ATF_TP_ADD_TC(tp, getresuid_failure); ATF_TP_ADD_TC(tp, getresgid_success); ATF_TP_ADD_TC(tp, getresgid_failure); ATF_TP_ADD_TC(tp, setpriority_success); ATF_TP_ADD_TC(tp, setpriority_failure); ATF_TP_ADD_TC(tp, setgroups_success); ATF_TP_ADD_TC(tp, setgroups_failure); ATF_TP_ADD_TC(tp, setpgrp_success); ATF_TP_ADD_TC(tp, setpgrp_failure); ATF_TP_ADD_TC(tp, setsid_success); ATF_TP_ADD_TC(tp, setsid_failure); ATF_TP_ADD_TC(tp, setrlimit_success); ATF_TP_ADD_TC(tp, setrlimit_failure); ATF_TP_ADD_TC(tp, mlock_success); ATF_TP_ADD_TC(tp, mlock_failure); ATF_TP_ADD_TC(tp, munlock_success); ATF_TP_ADD_TC(tp, munlock_failure); ATF_TP_ADD_TC(tp, minherit_success); ATF_TP_ADD_TC(tp, minherit_failure); ATF_TP_ADD_TC(tp, setlogin_success); ATF_TP_ADD_TC(tp, setlogin_failure); ATF_TP_ADD_TC(tp, rtprio_success); ATF_TP_ADD_TC(tp, rtprio_failure); ATF_TP_ADD_TC(tp, profil_success); ATF_TP_ADD_TC(tp, profil_failure); ATF_TP_ADD_TC(tp, ptrace_success); ATF_TP_ADD_TC(tp, ptrace_failure); ATF_TP_ADD_TC(tp, ktrace_success); ATF_TP_ADD_TC(tp, ktrace_failure); ATF_TP_ADD_TC(tp, procctl_success); ATF_TP_ADD_TC(tp, procctl_failure); + + ATF_TP_ADD_TC(tp, cap_enter_success); + ATF_TP_ADD_TC(tp, cap_getmode_success); + ATF_TP_ADD_TC(tp, cap_getmode_failure); return (atf_no_error()); } Index: head/tests/sys/audit/utils.c =================================================================== --- head/tests/sys/audit/utils.c (revision 336417) +++ head/tests/sys/audit/utils.c (revision 336418) @@ -1,233 +1,241 @@ /*- * Copyright 2018 Aniket Pandey * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" /* * Checks the presence of "auditregex" in auditpipe(4) after the * corresponding system call has been triggered. */ static bool get_records(const char *auditregex, FILE *pipestream) { uint8_t *buff; tokenstr_t token; ssize_t size = 1024; char membuff[size]; char del[] = ","; int reclen, bytes = 0; FILE *memstream; /* * Open a stream on 'membuff' (address to memory buffer) for storing * the audit records in the default mode.'reclen' is the length of the * available records from auditpipe which is passed to the functions * au_fetch_tok(3) and au_print_flags_tok(3) for further use. */ ATF_REQUIRE((memstream = fmemopen(membuff, size, "w")) != NULL); ATF_REQUIRE((reclen = au_read_rec(pipestream, &buff)) != -1); /* * Iterate through each BSM token, extracting the bits that are * required to start processing the token sequences. */ while (bytes < reclen) { if (au_fetch_tok(&token, buff + bytes, reclen - bytes) == -1) { perror("au_read_rec"); atf_tc_fail("Incomplete Audit Record"); } /* Print the tokens as they are obtained, in the default form */ au_print_flags_tok(memstream, &token, del, AU_OFLAG_NONE); bytes += token.len; } free(buff); ATF_REQUIRE_EQ(0, fclose(memstream)); return (atf_utils_grep_string("%s", membuff, auditregex)); } /* * Override the system-wide audit mask settings in /etc/security/audit_control * and set the auditpipe's maximum allowed queue length limit */ static void set_preselect_mode(int filedesc, au_mask_t *fmask) { int qlimit_max; int fmode = AUDITPIPE_PRESELECT_MODE_LOCAL; /* Set local preselection mode for auditing */ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_MODE, &fmode) < 0) atf_tc_fail("Preselection mode: %s", strerror(errno)); /* Set local preselection flag corresponding to the audit_event */ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_FLAGS, fmask) < 0) atf_tc_fail("Preselection flag: %s", strerror(errno)); /* Set local preselection flag for non-attributable audit_events */ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_NAFLAGS, fmask) < 0) atf_tc_fail("Preselection naflag: %s", strerror(errno)); /* Query the maximum possible queue length limit for auditpipe */ if (ioctl(filedesc, AUDITPIPE_GET_QLIMIT_MAX, &qlimit_max) < 0) atf_tc_fail("Query max-limit: %s", strerror(errno)); /* Set the queue length limit as obtained from previous step */ if (ioctl(filedesc, AUDITPIPE_SET_QLIMIT, &qlimit_max) < 0) atf_tc_fail("Set max-qlimit: %s", strerror(errno)); /* This removes any outstanding record on the auditpipe */ if (ioctl(filedesc, AUDITPIPE_FLUSH) < 0) atf_tc_fail("Auditpipe flush: %s", strerror(errno)); } /* * Get the corresponding audit_mask for class-name "name" then set the * success and failure bits for fmask to be used as the ioctl argument */ static au_mask_t get_audit_mask(const char *name) { au_mask_t fmask; au_class_ent_t *class; ATF_REQUIRE((class = getauclassnam(name)) != NULL); fmask.am_success = class->ac_class; fmask.am_failure = class->ac_class; return (fmask); } /* * Loop until the auditpipe returns something, check if it is what * we want, else repeat the procedure until ppoll(2) times out. */ static void check_auditpipe(struct pollfd fd[], const char *auditregex, FILE *pipestream) { struct timespec currtime, endtime, timeout; /* Set the expire time for poll(2) while waiting for syscall audit */ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC, &endtime)); endtime.tv_sec += 10; timeout.tv_nsec = endtime.tv_nsec; for (;;) { /* Update the time left for auditpipe to return any event */ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC, &currtime)); timeout.tv_sec = endtime.tv_sec - currtime.tv_sec; switch (ppoll(fd, 1, &timeout, NULL)) { /* ppoll(2) returns, check if it's what we want */ case 1: if (fd[0].revents & POLLIN) { if (get_records(auditregex, pipestream)) return; } else { atf_tc_fail("Auditpipe returned an " "unknown event %#x", fd[0].revents); } break; /* poll(2) timed out */ case 0: atf_tc_fail("%s not found in auditpipe within the " "time limit", auditregex); break; /* poll(2) standard error */ case -1: atf_tc_fail("Poll: %s", strerror(errno)); break; default: atf_tc_fail("Poll returned too many file descriptors"); } } } /* * Wrapper functions around static "check_auditpipe" */ static void check_audit_startup(struct pollfd fd[], const char *auditrgx, FILE *pipestream){ check_auditpipe(fd, auditrgx, pipestream); } void check_audit(struct pollfd fd[], const char *auditrgx, FILE *pipestream) { check_auditpipe(fd, auditrgx, pipestream); /* Teardown: /dev/auditpipe's instance opened for this test-suite */ ATF_REQUIRE_EQ(0, fclose(pipestream)); } FILE *setup(struct pollfd fd[], const char *name) { au_mask_t fmask, nomask; fmask = get_audit_mask(name); nomask = get_audit_mask("no"); FILE *pipestream; ATF_REQUIRE((fd[0].fd = open("/dev/auditpipe", O_RDONLY)) != -1); ATF_REQUIRE((pipestream = fdopen(fd[0].fd, "r")) != NULL); fd[0].events = POLLIN; + /* + * Disable stream buffering for read operations from /dev/auditpipe. + * Otherwise it is possible that fread(3), called via au_read_rec(3), + * can store buffered data in user-space unbeknown to ppoll(2), which + * as a result, reports that /dev/auditpipe is empty. + */ + ATF_REQUIRE_EQ(0, setvbuf(pipestream, NULL, _IONBF, 0)); + /* Set local preselection audit_class as "no" for audit startup */ set_preselect_mode(fd[0].fd, &nomask); ATF_REQUIRE_EQ(0, system("service auditd onestatus || \ { service auditd onestart && touch started_auditd ; }")); /* If 'started_auditd' exists, that means we started auditd(8) */ if (atf_utils_file_exists("started_auditd")) check_audit_startup(fd, "audit startup", pipestream); /* Set local preselection parameters specific to "name" audit_class */ set_preselect_mode(fd[0].fd, &fmask); return (pipestream); } void cleanup(void) { if (atf_utils_file_exists("started_auditd")) system("service auditd onestop > /dev/null 2>&1"); }