162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Landlock test helpers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 662306a36Sopenharmony_ci * Copyright © 2019-2020 ANSSI 762306a36Sopenharmony_ci * Copyright © 2021 Microsoft Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <errno.h> 1162306a36Sopenharmony_ci#include <linux/landlock.h> 1262306a36Sopenharmony_ci#include <sys/capability.h> 1362306a36Sopenharmony_ci#include <sys/socket.h> 1462306a36Sopenharmony_ci#include <sys/syscall.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <sys/wait.h> 1762306a36Sopenharmony_ci#include <unistd.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "../kselftest_harness.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#ifndef __maybe_unused 2262306a36Sopenharmony_ci#define __maybe_unused __attribute__((__unused__)) 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * TEST_F_FORK() is useful when a test drop privileges but the corresponding 2762306a36Sopenharmony_ci * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory 2862306a36Sopenharmony_ci * where write actions are denied). For convenience, FIXTURE_TEARDOWN() is 2962306a36Sopenharmony_ci * also called when the test failed, but not when FIXTURE_SETUP() failed. For 3062306a36Sopenharmony_ci * this to be possible, we must not call abort() but instead exit smoothly 3162306a36Sopenharmony_ci * (hence the step print). 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci/* clang-format off */ 3462306a36Sopenharmony_ci#define TEST_F_FORK(fixture_name, test_name) \ 3562306a36Sopenharmony_ci static void fixture_name##_##test_name##_child( \ 3662306a36Sopenharmony_ci struct __test_metadata *_metadata, \ 3762306a36Sopenharmony_ci FIXTURE_DATA(fixture_name) *self, \ 3862306a36Sopenharmony_ci const FIXTURE_VARIANT(fixture_name) *variant); \ 3962306a36Sopenharmony_ci TEST_F(fixture_name, test_name) \ 4062306a36Sopenharmony_ci { \ 4162306a36Sopenharmony_ci int status; \ 4262306a36Sopenharmony_ci const pid_t child = fork(); \ 4362306a36Sopenharmony_ci if (child < 0) \ 4462306a36Sopenharmony_ci abort(); \ 4562306a36Sopenharmony_ci if (child == 0) { \ 4662306a36Sopenharmony_ci _metadata->no_print = 1; \ 4762306a36Sopenharmony_ci fixture_name##_##test_name##_child(_metadata, self, variant); \ 4862306a36Sopenharmony_ci if (_metadata->skip) \ 4962306a36Sopenharmony_ci _exit(255); \ 5062306a36Sopenharmony_ci if (_metadata->passed) \ 5162306a36Sopenharmony_ci _exit(0); \ 5262306a36Sopenharmony_ci _exit(_metadata->step); \ 5362306a36Sopenharmony_ci } \ 5462306a36Sopenharmony_ci if (child != waitpid(child, &status, 0)) \ 5562306a36Sopenharmony_ci abort(); \ 5662306a36Sopenharmony_ci if (WIFSIGNALED(status) || !WIFEXITED(status)) { \ 5762306a36Sopenharmony_ci _metadata->passed = 0; \ 5862306a36Sopenharmony_ci _metadata->step = 1; \ 5962306a36Sopenharmony_ci return; \ 6062306a36Sopenharmony_ci } \ 6162306a36Sopenharmony_ci switch (WEXITSTATUS(status)) { \ 6262306a36Sopenharmony_ci case 0: \ 6362306a36Sopenharmony_ci _metadata->passed = 1; \ 6462306a36Sopenharmony_ci break; \ 6562306a36Sopenharmony_ci case 255: \ 6662306a36Sopenharmony_ci _metadata->passed = 1; \ 6762306a36Sopenharmony_ci _metadata->skip = 1; \ 6862306a36Sopenharmony_ci break; \ 6962306a36Sopenharmony_ci default: \ 7062306a36Sopenharmony_ci _metadata->passed = 0; \ 7162306a36Sopenharmony_ci _metadata->step = WEXITSTATUS(status); \ 7262306a36Sopenharmony_ci break; \ 7362306a36Sopenharmony_ci } \ 7462306a36Sopenharmony_ci } \ 7562306a36Sopenharmony_ci static void fixture_name##_##test_name##_child( \ 7662306a36Sopenharmony_ci struct __test_metadata __attribute__((unused)) *_metadata, \ 7762306a36Sopenharmony_ci FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ 7862306a36Sopenharmony_ci const FIXTURE_VARIANT(fixture_name) \ 7962306a36Sopenharmony_ci __attribute__((unused)) *variant) 8062306a36Sopenharmony_ci/* clang-format on */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#ifndef landlock_create_ruleset 8362306a36Sopenharmony_cistatic inline int 8462306a36Sopenharmony_cilandlock_create_ruleset(const struct landlock_ruleset_attr *const attr, 8562306a36Sopenharmony_ci const size_t size, const __u32 flags) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return syscall(__NR_landlock_create_ruleset, attr, size, flags); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#ifndef landlock_add_rule 9262306a36Sopenharmony_cistatic inline int landlock_add_rule(const int ruleset_fd, 9362306a36Sopenharmony_ci const enum landlock_rule_type rule_type, 9462306a36Sopenharmony_ci const void *const rule_attr, 9562306a36Sopenharmony_ci const __u32 flags) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, 9862306a36Sopenharmony_ci flags); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#ifndef landlock_restrict_self 10362306a36Sopenharmony_cistatic inline int landlock_restrict_self(const int ruleset_fd, 10462306a36Sopenharmony_ci const __u32 flags) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci cap_t cap_p; 11362306a36Sopenharmony_ci /* Only these three capabilities are useful for the tests. */ 11462306a36Sopenharmony_ci const cap_value_t caps[] = { 11562306a36Sopenharmony_ci CAP_DAC_OVERRIDE, 11662306a36Sopenharmony_ci CAP_MKNOD, 11762306a36Sopenharmony_ci CAP_SYS_ADMIN, 11862306a36Sopenharmony_ci CAP_SYS_CHROOT, 11962306a36Sopenharmony_ci }; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci cap_p = cap_get_proc(); 12262306a36Sopenharmony_ci EXPECT_NE(NULL, cap_p) 12362306a36Sopenharmony_ci { 12462306a36Sopenharmony_ci TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci EXPECT_NE(-1, cap_clear(cap_p)) 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci TH_LOG("Failed to cap_clear: %s", strerror(errno)); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci if (!drop_all) { 13162306a36Sopenharmony_ci EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 13262306a36Sopenharmony_ci ARRAY_SIZE(caps), caps, CAP_SET)) 13362306a36Sopenharmony_ci { 13462306a36Sopenharmony_ci TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci EXPECT_NE(-1, cap_set_proc(cap_p)) 13862306a36Sopenharmony_ci { 13962306a36Sopenharmony_ci TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci EXPECT_NE(-1, cap_free(cap_p)) 14262306a36Sopenharmony_ci { 14362306a36Sopenharmony_ci TH_LOG("Failed to cap_free: %s", strerror(errno)); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* We cannot put such helpers in a library because of kselftest_harness.h . */ 14862306a36Sopenharmony_cistatic void __maybe_unused disable_caps(struct __test_metadata *const _metadata) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci _init_caps(_metadata, false); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void __maybe_unused drop_caps(struct __test_metadata *const _metadata) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci _init_caps(_metadata, true); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void _effective_cap(struct __test_metadata *const _metadata, 15962306a36Sopenharmony_ci const cap_value_t caps, const cap_flag_value_t value) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci cap_t cap_p; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci cap_p = cap_get_proc(); 16462306a36Sopenharmony_ci EXPECT_NE(NULL, cap_p) 16562306a36Sopenharmony_ci { 16662306a36Sopenharmony_ci TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) 16962306a36Sopenharmony_ci { 17062306a36Sopenharmony_ci TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci EXPECT_NE(-1, cap_set_proc(cap_p)) 17362306a36Sopenharmony_ci { 17462306a36Sopenharmony_ci TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci EXPECT_NE(-1, cap_free(cap_p)) 17762306a36Sopenharmony_ci { 17862306a36Sopenharmony_ci TH_LOG("Failed to cap_free: %s", strerror(errno)); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void __maybe_unused set_cap(struct __test_metadata *const _metadata, 18362306a36Sopenharmony_ci const cap_value_t caps) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci _effective_cap(_metadata, caps, CAP_SET); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void __maybe_unused clear_cap(struct __test_metadata *const _metadata, 18962306a36Sopenharmony_ci const cap_value_t caps) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci _effective_cap(_metadata, caps, CAP_CLEAR); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */ 19562306a36Sopenharmony_cistatic int __maybe_unused recv_fd(int usock) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci int fd_rx; 19862306a36Sopenharmony_ci union { 19962306a36Sopenharmony_ci /* Aligned ancillary data buffer. */ 20062306a36Sopenharmony_ci char buf[CMSG_SPACE(sizeof(fd_rx))]; 20162306a36Sopenharmony_ci struct cmsghdr _align; 20262306a36Sopenharmony_ci } cmsg_rx = {}; 20362306a36Sopenharmony_ci char data = '\0'; 20462306a36Sopenharmony_ci struct iovec io = { 20562306a36Sopenharmony_ci .iov_base = &data, 20662306a36Sopenharmony_ci .iov_len = sizeof(data), 20762306a36Sopenharmony_ci }; 20862306a36Sopenharmony_ci struct msghdr msg = { 20962306a36Sopenharmony_ci .msg_iov = &io, 21062306a36Sopenharmony_ci .msg_iovlen = 1, 21162306a36Sopenharmony_ci .msg_control = &cmsg_rx.buf, 21262306a36Sopenharmony_ci .msg_controllen = sizeof(cmsg_rx.buf), 21362306a36Sopenharmony_ci }; 21462306a36Sopenharmony_ci struct cmsghdr *cmsg; 21562306a36Sopenharmony_ci int res; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC); 21862306a36Sopenharmony_ci if (res < 0) 21962306a36Sopenharmony_ci return -errno; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci cmsg = CMSG_FIRSTHDR(&msg); 22262306a36Sopenharmony_ci if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx))) 22362306a36Sopenharmony_ci return -EIO; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx)); 22662306a36Sopenharmony_ci return fd_rx; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */ 23062306a36Sopenharmony_cistatic int __maybe_unused send_fd(int usock, int fd_tx) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci union { 23362306a36Sopenharmony_ci /* Aligned ancillary data buffer. */ 23462306a36Sopenharmony_ci char buf[CMSG_SPACE(sizeof(fd_tx))]; 23562306a36Sopenharmony_ci struct cmsghdr _align; 23662306a36Sopenharmony_ci } cmsg_tx = {}; 23762306a36Sopenharmony_ci char data_tx = '.'; 23862306a36Sopenharmony_ci struct iovec io = { 23962306a36Sopenharmony_ci .iov_base = &data_tx, 24062306a36Sopenharmony_ci .iov_len = sizeof(data_tx), 24162306a36Sopenharmony_ci }; 24262306a36Sopenharmony_ci struct msghdr msg = { 24362306a36Sopenharmony_ci .msg_iov = &io, 24462306a36Sopenharmony_ci .msg_iovlen = 1, 24562306a36Sopenharmony_ci .msg_control = &cmsg_tx.buf, 24662306a36Sopenharmony_ci .msg_controllen = sizeof(cmsg_tx.buf), 24762306a36Sopenharmony_ci }; 24862306a36Sopenharmony_ci struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx)); 25162306a36Sopenharmony_ci cmsg->cmsg_level = SOL_SOCKET; 25262306a36Sopenharmony_ci cmsg->cmsg_type = SCM_RIGHTS; 25362306a36Sopenharmony_ci memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (sendmsg(usock, &msg, 0) < 0) 25662306a36Sopenharmony_ci return -errno; 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 259