diff options
-rw-r--r-- | tools/testing/selftests/landlock/audit.h | 437 | ||||
-rw-r--r-- | tools/testing/selftests/landlock/audit_test.c | 332 | ||||
-rw-r--r-- | tools/testing/selftests/landlock/common.h | 2 | ||||
-rw-r--r-- | tools/testing/selftests/landlock/config | 1 |
4 files changed, 772 insertions, 0 deletions
diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h new file mode 100644 index 000000000000..08a5c53bd6f5 --- /dev/null +++ b/tools/testing/selftests/landlock/audit.h @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Landlock audit helpers + * + * Copyright © 2024-2025 Microsoft Corporation + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <linux/audit.h> +#include <linux/limits.h> +#include <linux/netlink.h> +#include <regex.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#ifndef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) +#endif + +#define REGEX_LANDLOCK_PREFIX "^audit([0-9.:]\\+): domain=\\([0-9a-f]\\+\\)" + +struct audit_filter { + __u32 record_type; + size_t exe_len; + char exe[PATH_MAX]; +}; + +struct audit_message { + struct nlmsghdr header; + union { + struct audit_status status; + struct audit_features features; + struct audit_rule_data rule; + struct nlmsgerr err; + char data[PATH_MAX + 200]; + }; +}; + +static const struct timeval audit_tv_dom_drop = { + /* + * Because domain deallocation is tied to asynchronous credential + * freeing, receiving such event may take some time. In practice, + * on a small VM, it should not exceed 100k usec, but let's wait up + * to 1 second to be safe. + */ + .tv_sec = 1, +}; + +static const struct timeval audit_tv_default = { + .tv_usec = 1, +}; + +static int audit_send(const int fd, const struct audit_message *const msg) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + int ret; + + do { + ret = sendto(fd, msg, msg->header.nlmsg_len, 0, + (struct sockaddr *)&addr, sizeof(addr)); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -errno; + + if (ret != msg->header.nlmsg_len) + return -E2BIG; + + return 0; +} + +static int audit_recv(const int fd, struct audit_message *msg) +{ + struct sockaddr_nl addr; + socklen_t addrlen = sizeof(addr); + struct audit_message msg_tmp; + int err; + + if (!msg) + msg = &msg_tmp; + + do { + err = recvfrom(fd, msg, sizeof(*msg), 0, + (struct sockaddr *)&addr, &addrlen); + } while (err < 0 && errno == EINTR); + + if (err < 0) + return -errno; + + if (addrlen != sizeof(addr) || addr.nl_pid != 0) + return -EINVAL; + + /* Checks Netlink error or end of messages. */ + if (msg->header.nlmsg_type == NLMSG_ERROR) + return msg->err.error; + + return 0; +} + +static int audit_request(const int fd, + const struct audit_message *const request, + struct audit_message *reply) +{ + struct audit_message msg_tmp; + bool first_reply = true; + int err; + + err = audit_send(fd, request); + if (err) + return err; + + if (!reply) + reply = &msg_tmp; + + do { + if (first_reply) + first_reply = false; + else + reply = &msg_tmp; + + err = audit_recv(fd, reply); + if (err) + return err; + } while (reply->header.nlmsg_type != NLMSG_ERROR && + reply->err.msg.nlmsg_type != request->header.nlmsg_type); + + return reply->err.error; +} + +static int audit_filter_exe(const int audit_fd, + const struct audit_filter *const filter, + const __u16 type) +{ + struct audit_message msg = { + .header = { + .nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)) + + NLMSG_ALIGN(filter->exe_len), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .rule = { + .flags = AUDIT_FILTER_EXCLUDE, + .action = AUDIT_NEVER, + .field_count = 1, + .fields[0] = filter->record_type, + .fieldflags[0] = AUDIT_NOT_EQUAL, + .values[0] = filter->exe_len, + .buflen = filter->exe_len, + } + }; + + if (filter->record_type != AUDIT_EXE) + return -EINVAL; + + memcpy(msg.rule.buf, filter->exe, filter->exe_len); + return audit_request(audit_fd, &msg, NULL); +} + +static int audit_filter_drop(const int audit_fd, const __u16 type) +{ + struct audit_message msg = { + .header = { + .nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .rule = { + .flags = AUDIT_FILTER_EXCLUDE, + .action = AUDIT_NEVER, + .field_count = 1, + .fields[0] = AUDIT_MSGTYPE, + .fieldflags[0] = AUDIT_NOT_EQUAL, + .values[0] = AUDIT_LANDLOCK_DOMAIN, + } + }; + + return audit_request(audit_fd, &msg, NULL); +} + +static int audit_set_status(int fd, __u32 key, __u32 val) +{ + const struct audit_message msg = { + .header = { + .nlmsg_len = NLMSG_SPACE(sizeof(msg.status)), + .nlmsg_type = AUDIT_SET, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .status = { + .mask = key, + .enabled = key == AUDIT_STATUS_ENABLED ? val : 0, + .pid = key == AUDIT_STATUS_PID ? val : 0, + } + }; + + return audit_request(fd, &msg, NULL); +} + +/* + * @domain_id: The domain ID extracted from the audit message (if the first part + * of @pattern is REGEX_LANDLOCK_PREFIX). It is set to 0 if the domain ID is + * not found. + */ +static int audit_match_record(int audit_fd, const __u16 type, + const char *const pattern, __u64 *domain_id) +{ + struct audit_message msg; + int ret, err = 0; + bool matches_record = !type; + regmatch_t matches[2]; + regex_t regex; + + ret = regcomp(®ex, pattern, 0); + if (ret) + return -EINVAL; + + do { + memset(&msg, 0, sizeof(msg)); + err = audit_recv(audit_fd, &msg); + if (err) + goto out; + + if (msg.header.nlmsg_type == type) + matches_record = true; + } while (!matches_record); + + ret = regexec(®ex, msg.data, ARRAY_SIZE(matches), matches, 0); + if (ret) { + printf("DATA: %s\n", msg.data); + printf("ERROR: no match for pattern: %s\n", pattern); + err = -ENOENT; + } + + if (domain_id) { + *domain_id = 0; + if (matches[1].rm_so != -1) { + int match_len = matches[1].rm_eo - matches[1].rm_so; + /* The maximal characters of a 2^64 hexadecimal number is 17. */ + char dom_id[18]; + + if (match_len > 0 && match_len < sizeof(dom_id)) { + memcpy(dom_id, msg.data + matches[1].rm_so, + match_len); + dom_id[match_len] = '\0'; + if (domain_id) + *domain_id = strtoull(dom_id, NULL, 16); + } + } + } + +out: + regfree(®ex); + return err; +} + +static int __maybe_unused matches_log_domain_allocated(int audit_fd, + __u64 *domain_id) +{ + return audit_match_record( + audit_fd, AUDIT_LANDLOCK_DOMAIN, + REGEX_LANDLOCK_PREFIX + " status=allocated mode=enforcing pid=[0-9]\\+ uid=[0-9]\\+" + " exe=\"[^\"]\\+\" comm=\".*_test\"$", + domain_id); +} + +static int __maybe_unused matches_log_domain_deallocated( + int audit_fd, unsigned int num_denials, __u64 *domain_id) +{ + static const char log_template[] = REGEX_LANDLOCK_PREFIX + " status=deallocated denials=%u$"; + char log_match[sizeof(log_template) + 10]; + int log_match_len; + + log_match_len = snprintf(log_match, sizeof(log_match), log_template, + num_denials); + if (log_match_len > sizeof(log_match)) + return -E2BIG; + + return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, + domain_id); +} + +struct audit_records { + size_t access; + size_t domain; +}; + +static int audit_count_records(int audit_fd, struct audit_records *records) +{ + struct audit_message msg; + int err; + + records->access = 0; + records->domain = 0; + + do { + memset(&msg, 0, sizeof(msg)); + err = audit_recv(audit_fd, &msg); + if (err) { + if (err == -EAGAIN) + return 0; + else + return err; + } + + switch (msg.header.nlmsg_type) { + case AUDIT_LANDLOCK_ACCESS: + records->access++; + break; + case AUDIT_LANDLOCK_DOMAIN: + records->domain++; + break; + } + } while (true); + + return 0; +} + +static int audit_init(void) +{ + int fd, err; + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); + if (fd < 0) + return -errno; + + err = audit_set_status(fd, AUDIT_STATUS_ENABLED, 1); + if (err) + return err; + + err = audit_set_status(fd, AUDIT_STATUS_PID, getpid()); + if (err) + return err; + + /* Sets a timeout for negative tests. */ + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, + sizeof(audit_tv_default)); + if (err) + return -errno; + + return fd; +} + +static int audit_init_filter_exe(struct audit_filter *filter, const char *path) +{ + char *absolute_path = NULL; + + /* It is assume that there is not already filtering rules. */ + filter->record_type = AUDIT_EXE; + if (!path) { + filter->exe_len = readlink("/proc/self/exe", filter->exe, + sizeof(filter->exe) - 1); + if (filter->exe_len < 0) + return -errno; + + return 0; + } + + absolute_path = realpath(path, NULL); + if (!absolute_path) + return -errno; + + /* No need for the terminating NULL byte. */ + filter->exe_len = strlen(absolute_path); + if (filter->exe_len > sizeof(filter->exe)) + return -E2BIG; + + memcpy(filter->exe, absolute_path, filter->exe_len); + free(absolute_path); + return 0; +} + +static int audit_cleanup(int audit_fd, struct audit_filter *filter) +{ + struct audit_filter new_filter; + + if (audit_fd < 0 || !filter) { + int err; + + /* + * Simulates audit_init_with_exe_filter() when called from + * FIXTURE_TEARDOWN_PARENT(). + */ + audit_fd = audit_init(); + if (audit_fd < 0) + return audit_fd; + + filter = &new_filter; + err = audit_init_filter_exe(filter, NULL); + if (err) + return err; + } + + /* Filters might not be in place. */ + audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE); + audit_filter_drop(audit_fd, AUDIT_DEL_RULE); + + /* + * Because audit_cleanup() might not be called by the test auditd + * process, it might not be possible to explicitly set it. Anyway, + * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd + * process will exit. + */ + return close(audit_fd); +} + +static int audit_init_with_exe_filter(struct audit_filter *filter) +{ + int fd, err; + + fd = audit_init(); + if (fd < 0) + return fd; + + err = audit_init_filter_exe(filter, NULL); + if (err) + return err; + + err = audit_filter_exe(fd, filter, AUDIT_ADD_RULE); + if (err) + return err; + + return fd; +} diff --git a/tools/testing/selftests/landlock/audit_test.c b/tools/testing/selftests/landlock/audit_test.c new file mode 100644 index 000000000000..59764dc18ecb --- /dev/null +++ b/tools/testing/selftests/landlock/audit_test.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Landlock tests - Audit + * + * Copyright © 2024-2025 Microsoft Corporation + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <linux/landlock.h> +#include <sys/mount.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "audit.h" +#include "common.h" + +static int matches_log_signal(struct __test_metadata *const _metadata, + int audit_fd, const pid_t opid, __u64 *domain_id) +{ + static const char log_template[] = REGEX_LANDLOCK_PREFIX + " blockers=scope\\.signal opid=%d ocomm=\"audit_test\"$"; + char log_match[sizeof(log_template) + 10]; + int log_match_len; + + log_match_len = + snprintf(log_match, sizeof(log_match), log_template, opid); + if (log_match_len > sizeof(log_match)) + return -E2BIG; + + return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, + domain_id); +} + +FIXTURE(audit) +{ + struct audit_filter audit_filter; + int audit_fd; + __u64(*domain_stack)[16]; +}; + +FIXTURE_SETUP(audit) +{ + disable_caps(_metadata); + set_cap(_metadata, CAP_AUDIT_CONTROL); + self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); + EXPECT_LE(0, self->audit_fd) + { + const char *error_msg; + + /* kill "$(auditctl -s | sed -ne 's/^pid \([0-9]\+\)$/\1/p')" */ + if (self->audit_fd == -EEXIST) + error_msg = "socket already in use (e.g. auditd)"; + else + error_msg = strerror(-self->audit_fd); + TH_LOG("Failed to initialize audit: %s", error_msg); + } + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + self->domain_stack = mmap(NULL, sizeof(*self->domain_stack), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, self->domain_stack); + memset(self->domain_stack, 0, sizeof(*self->domain_stack)); +} + +FIXTURE_TEARDOWN(audit) +{ + EXPECT_EQ(0, munmap(self->domain_stack, sizeof(*self->domain_stack))); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); +} + +TEST_F(audit, layers) +{ + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + int status, ruleset_fd, i; + __u64 prev_dom = 3; + pid_t child; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + for (i = 0; i < ARRAY_SIZE(*self->domain_stack); i++) { + __u64 denial_dom = 1; + __u64 allocated_dom = 2; + + EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); + + /* Creates a denial to get the domain ID. */ + EXPECT_EQ(-1, kill(getppid(), 0)); + EXPECT_EQ(EPERM, errno); + EXPECT_EQ(0, + matches_log_signal(_metadata, self->audit_fd, + getppid(), &denial_dom)); + EXPECT_EQ(0, matches_log_domain_allocated( + self->audit_fd, &allocated_dom)); + EXPECT_NE(denial_dom, 1); + EXPECT_NE(denial_dom, 0); + EXPECT_EQ(denial_dom, allocated_dom); + + /* Checks that the new domain is younger than the previous one. */ + EXPECT_GT(allocated_dom, prev_dom); + prev_dom = allocated_dom; + (*self->domain_stack)[i] = allocated_dom; + } + + /* Checks that we reached the maximum number of layers. */ + EXPECT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); + EXPECT_EQ(E2BIG, errno); + + /* Updates filter rules to match the drop record. */ + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_filter_drop(self->audit_fd, AUDIT_ADD_RULE)); + EXPECT_EQ(0, + audit_filter_exe(self->audit_fd, &self->audit_filter, + AUDIT_DEL_RULE)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + _exit(_metadata->exit_code); + return; + } + + ASSERT_EQ(child, waitpid(child, &status, 0)); + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; + + /* Purges log from deallocated domains. */ + EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_dom_drop, sizeof(audit_tv_dom_drop))); + for (i = ARRAY_SIZE(*self->domain_stack) - 1; i >= 0; i--) { + __u64 deallocated_dom = 2; + + EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 1, + &deallocated_dom)); + EXPECT_EQ((*self->domain_stack)[i], deallocated_dom) + { + TH_LOG("Failed to match domain %llx (#%d)", + (*self->domain_stack)[i], i); + } + } + EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_default, sizeof(audit_tv_default))); + + EXPECT_EQ(0, close(ruleset_fd)); +} + +FIXTURE(audit_flags) +{ + struct audit_filter audit_filter; + int audit_fd; + __u64 *domain_id; +}; + +FIXTURE_VARIANT(audit_flags) +{ + const int restrict_flags; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit_flags, default) { + /* clang-format on */ + .restrict_flags = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit_flags, same_exec_off) { + /* clang-format on */ + .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit_flags, subdomains_off) { + /* clang-format on */ + .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(audit_flags, cross_exec_on) { + /* clang-format on */ + .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON, +}; + +FIXTURE_SETUP(audit_flags) +{ + disable_caps(_metadata); + set_cap(_metadata, CAP_AUDIT_CONTROL); + self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); + EXPECT_LE(0, self->audit_fd) + { + const char *error_msg; + + /* kill "$(auditctl -s | sed -ne 's/^pid \([0-9]\+\)$/\1/p')" */ + if (self->audit_fd == -EEXIST) + error_msg = "socket already in use (e.g. auditd)"; + else + error_msg = strerror(-self->audit_fd); + TH_LOG("Failed to initialize audit: %s", error_msg); + } + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + self->domain_id = mmap(NULL, sizeof(*self->domain_id), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, self->domain_id); + /* Domain IDs are greater or equal to 2^32. */ + *self->domain_id = 1; +} + +FIXTURE_TEARDOWN(audit_flags) +{ + EXPECT_EQ(0, munmap(self->domain_id, sizeof(*self->domain_id))); + + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); +} + +TEST_F(audit_flags, signal) +{ + int status; + pid_t child; + struct audit_records records; + __u64 deallocated_dom = 2; + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPE_SIGNAL, + }; + int ruleset_fd; + + /* Add filesystem restrictions. */ + ruleset_fd = landlock_create_ruleset(&ruleset_attr, + sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, + variant->restrict_flags)); + EXPECT_EQ(0, close(ruleset_fd)); + + /* First signal checks to test log entries. */ + EXPECT_EQ(-1, kill(getppid(), 0)); + EXPECT_EQ(EPERM, errno); + + if (variant->restrict_flags & + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + EXPECT_EQ(-EAGAIN, matches_log_signal( + _metadata, self->audit_fd, + getppid(), self->domain_id)); + EXPECT_EQ(*self->domain_id, 1); + } else { + __u64 allocated_dom = 3; + + EXPECT_EQ(0, matches_log_signal( + _metadata, self->audit_fd, + getppid(), self->domain_id)); + + /* Checks domain information records. */ + EXPECT_EQ(0, matches_log_domain_allocated( + self->audit_fd, &allocated_dom)); + EXPECT_NE(*self->domain_id, 1); + EXPECT_NE(*self->domain_id, 0); + EXPECT_EQ(*self->domain_id, allocated_dom); + } + + /* Second signal checks to test audit_count_records(). */ + EXPECT_EQ(-1, kill(getppid(), 0)); + EXPECT_EQ(EPERM, errno); + + /* Makes sure there is no superfluous logged records. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + if (variant->restrict_flags & + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + EXPECT_EQ(0, records.access); + } else { + EXPECT_EQ(1, records.access); + } + EXPECT_EQ(0, records.domain); + + /* Updates filter rules to match the drop record. */ + set_cap(_metadata, CAP_AUDIT_CONTROL); + EXPECT_EQ(0, audit_filter_drop(self->audit_fd, AUDIT_ADD_RULE)); + EXPECT_EQ(0, + audit_filter_exe(self->audit_fd, &self->audit_filter, + AUDIT_DEL_RULE)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); + + _exit(_metadata->exit_code); + return; + } + + ASSERT_EQ(child, waitpid(child, &status, 0)); + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; + + if (variant->restrict_flags & + LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) { + EXPECT_EQ(-EAGAIN, + matches_log_domain_deallocated(self->audit_fd, 0, + &deallocated_dom)); + EXPECT_EQ(deallocated_dom, 2); + } else { + EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_dom_drop, + sizeof(audit_tv_dom_drop))); + EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 2, + &deallocated_dom)); + EXPECT_NE(deallocated_dom, 2); + EXPECT_NE(deallocated_dom, 0); + EXPECT_EQ(deallocated_dom, *self->domain_id); + EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO, + &audit_tv_default, + sizeof(audit_tv_default))); + } +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h index 076a9a625c98..54b6f80fc697 100644 --- a/tools/testing/selftests/landlock/common.h +++ b/tools/testing/selftests/landlock/common.h @@ -11,6 +11,7 @@ #include <errno.h> #include <linux/securebits.h> #include <sys/capability.h> +#include <sys/prctl.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/wait.h> @@ -37,6 +38,7 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) /* Only these three capabilities are useful for the tests. */ const cap_value_t caps[] = { /* clang-format off */ + CAP_AUDIT_CONTROL, CAP_DAC_OVERRIDE, CAP_MKNOD, CAP_NET_ADMIN, diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config index 425de4c20271..8fe9b461b1fd 100644 --- a/tools/testing/selftests/landlock/config +++ b/tools/testing/selftests/landlock/config @@ -1,4 +1,5 @@ CONFIG_AF_UNIX_OOB=y +CONFIG_AUDIT=y CONFIG_CGROUPS=y CONFIG_CGROUP_SCHED=y CONFIG_INET=y |