Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/audit/utils.c
Show All 24 Lines | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/extattr.h> | #include <sys/extattr.h> | ||||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||
#include <bsm/libbsm.h> | #include <bsm/libbsm.h> | ||||
#include <bsm/auditd_lib.h> | |||||
#include <security/audit/audit_ioctl.h> | #include <security/audit/audit_ioctl.h> | ||||
#include <atf-c.h> | #include <atf-c.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <time.h> | #include <time.h> | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | skip_if_extattr_not_supported(const char *path) | ||||
* the extattr_list_file returns EOPNOTSUPP. | * the extattr_list_file returns EOPNOTSUPP. | ||||
*/ | */ | ||||
result = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); | result = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); | ||||
if (result == -1 && errno == EOPNOTSUPP) { | if (result == -1 && errno == EOPNOTSUPP) { | ||||
atf_tc_skip("File system does not support extattrs."); | atf_tc_skip("File system does not support extattrs."); | ||||
} | } | ||||
} | } | ||||
FILE | static bool | ||||
*setup(struct pollfd fd[], const char *name) | is_auditd_running(void) | ||||
{ | { | ||||
int trigger; | |||||
int err; | |||||
/* | |||||
* AUDIT_TRIGGER_INITIALIZE is a no-op message on FreeBSD and can | |||||
* therefore be used to check whether auditd has already been started. | |||||
* This is significantly cheaper than running `service auditd onestatus` | |||||
* for each test case. It is also slightly less racy since it will only | |||||
* return true once auditd() has opened the trigger file rather than | |||||
* just when the pidfile has been created. | |||||
*/ | |||||
trigger = AUDIT_TRIGGER_INITIALIZE; | |||||
err = auditon(A_SENDTRIGGER, &trigger, sizeof(trigger)); | |||||
if (err == 0) { | |||||
fprintf(stderr, "auditd(8) is running.\n"); | |||||
return (true); | |||||
} else { | |||||
/* | |||||
* A_SENDTRIGGER returns ENODEV if auditd isn't listening, | |||||
* all other error codes indicate a fatal error. | |||||
*/ | |||||
ATF_REQUIRE_MSG(errno == ENODEV, | |||||
"Unexpected error from auditon(2): %s", strerror(errno)); | |||||
return (false); | |||||
} | |||||
} | |||||
FILE * | |||||
setup(struct pollfd fd[], const char *name) | |||||
{ | |||||
au_mask_t fmask, nomask; | au_mask_t fmask, nomask; | ||||
FILE *pipestream; | |||||
fmask = get_audit_mask(name); | fmask = get_audit_mask(name); | ||||
nomask = get_audit_mask("no"); | nomask = get_audit_mask("no"); | ||||
FILE *pipestream; | |||||
ATF_REQUIRE((fd[0].fd = open("/dev/auditpipe", O_RDONLY)) != -1); | ATF_REQUIRE((fd[0].fd = open("/dev/auditpipe", O_RDONLY)) != -1); | ||||
ATF_REQUIRE((pipestream = fdopen(fd[0].fd, "r")) != NULL); | ATF_REQUIRE((pipestream = fdopen(fd[0].fd, "r")) != NULL); | ||||
fd[0].events = POLLIN; | fd[0].events = POLLIN; | ||||
/* | /* | ||||
* Disable stream buffering for read operations from /dev/auditpipe. | * Disable stream buffering for read operations from /dev/auditpipe. | ||||
* Otherwise it is possible that fread(3), called via au_read_rec(3), | * 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 | * can store buffered data in user-space unbeknown to ppoll(2), which | ||||
* as a result, reports that /dev/auditpipe is empty. | * as a result, reports that /dev/auditpipe is empty. | ||||
*/ | */ | ||||
ATF_REQUIRE_EQ(0, setvbuf(pipestream, NULL, _IONBF, 0)); | ATF_REQUIRE_EQ(0, setvbuf(pipestream, NULL, _IONBF, 0)); | ||||
/* Set local preselection audit_class as "no" for audit startup */ | /* Set local preselection audit_class as "no" for audit startup */ | ||||
set_preselect_mode(fd[0].fd, &nomask); | set_preselect_mode(fd[0].fd, &nomask); | ||||
ATF_REQUIRE_EQ(0, system("service auditd onestatus || \ | if (!is_auditd_running()) { | ||||
{ service auditd onestart && touch started_auditd ; }")); | fprintf(stderr, "Running audit_quick_start() for testing... "); | ||||
/* | |||||
/* If 'started_auditd' exists, that means we started auditd(8) */ | * Previously, this test started auditd using | ||||
if (atf_utils_file_exists("started_auditd")) | * `service auditd onestart`. However, there is a race condition | ||||
* there since service can return before auditd(8) has | |||||
* fully started (once the daemon parent process has forked) | |||||
* and this can cause check_audit_startup() to fail sometimes. | |||||
* | |||||
* In the CheriBSD CI this caused the first test executed by | |||||
* kyua (administrative:acct_failure) to fail every time, but | |||||
* subsequent ones would almost always succeed. | |||||
* | |||||
* To avoid this problem (and as a nice side-effect this speeds | |||||
* up the test quite a bit), we register this process as a | |||||
* "fake" auditd(8) using the audit_quick_start() function from | |||||
* libauditd. | |||||
*/ | |||||
atf_utils_create_file("started_fake_auditd", "yes\n"); | |||||
ATF_REQUIRE(atf_utils_file_exists("started_fake_auditd")); | |||||
ATF_REQUIRE_EQ_MSG(0, audit_quick_start(), | |||||
asomers: If two tests are running in parallel, they might both try to start fake auditd. The only thing… | |||||
Done Inline ActionsAh yes, I keep forgetting that kyua can actually run tests in parallel. arichardson: Ah yes, I keep forgetting that kyua can actually run tests in parallel.
I'm not sure what… | |||||
"Failed to start fake auditd: %m"); | |||||
fprintf(stderr, "done.\n"); | |||||
/* audit_quick_start() should log an audit start event. */ | |||||
check_audit_startup(fd, "audit startup", pipestream); | check_audit_startup(fd, "audit startup", pipestream); | ||||
/* | |||||
* If we exit cleanly shutdown audit_quick_start(), if not | |||||
* cleanup() will take care of it. | |||||
* This is not required, but makes it easier to run individual | |||||
* tests outside of kyua. | |||||
*/ | |||||
atexit(cleanup); | |||||
} | |||||
/* Set local preselection parameters specific to "name" audit_class */ | /* Set local preselection parameters specific to "name" audit_class */ | ||||
set_preselect_mode(fd[0].fd, &fmask); | set_preselect_mode(fd[0].fd, &fmask); | ||||
return (pipestream); | return (pipestream); | ||||
} | } | ||||
void | void | ||||
cleanup(void) | cleanup(void) | ||||
{ | { | ||||
if (atf_utils_file_exists("started_auditd")) | if (atf_utils_file_exists("started_fake_auditd")) { | ||||
system("service auditd onestop > /dev/null 2>&1"); | fprintf(stderr, "Running audit_quick_stop()... "); | ||||
if (audit_quick_stop() != 0) { | |||||
fprintf(stderr, "Failed to stop fake auditd: %m\n"); | |||||
abort(); | |||||
} | |||||
fprintf(stderr, "done.\n"); | |||||
unlink("started_fake_auditd"); | |||||
} | |||||
} | } |
If two tests are running in parallel, they might both try to start fake auditd. The only thing that saves you is the fact that the audit tests are currently all marked as is_exclusive.