| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /* SPDX-License-Identifier: GPL-2.0 */
- /*
- * Landlock test helpers
- *
- * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
- * Copyright © 2019-2020 ANSSI
- * Copyright © 2021 Microsoft Corporation
- */
- #include <arpa/inet.h>
- #include <errno.h>
- #include <linux/landlock.h>
- #include <linux/securebits.h>
- #include <sys/capability.h>
- #include <sys/socket.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sys/un.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include "../kselftest_harness.h"
- #define TMP_DIR "tmp"
- #ifndef __maybe_unused
- #define __maybe_unused __attribute__((__unused__))
- #endif
- /* TEST_F_FORK() should not be used for new tests. */
- #define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name)
- #ifndef landlock_create_ruleset
- static inline int
- landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
- const size_t size, const __u32 flags)
- {
- return syscall(__NR_landlock_create_ruleset, attr, size, flags);
- }
- #endif
- #ifndef landlock_add_rule
- static inline int landlock_add_rule(const int ruleset_fd,
- const enum landlock_rule_type rule_type,
- const void *const rule_attr,
- const __u32 flags)
- {
- return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
- flags);
- }
- #endif
- #ifndef landlock_restrict_self
- static inline int landlock_restrict_self(const int ruleset_fd,
- const __u32 flags)
- {
- return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
- }
- #endif
- static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
- {
- cap_t cap_p;
- /* Only these three capabilities are useful for the tests. */
- const cap_value_t caps[] = {
- /* clang-format off */
- CAP_DAC_OVERRIDE,
- CAP_MKNOD,
- CAP_NET_ADMIN,
- CAP_NET_BIND_SERVICE,
- CAP_SETUID,
- CAP_SYS_ADMIN,
- CAP_SYS_CHROOT,
- /* clang-format on */
- };
- const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED;
- if ((cap_get_secbits() & noroot) != noroot)
- EXPECT_EQ(0, cap_set_secbits(noroot));
- cap_p = cap_get_proc();
- EXPECT_NE(NULL, cap_p);
- EXPECT_NE(-1, cap_clear(cap_p));
- if (!drop_all) {
- EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED,
- ARRAY_SIZE(caps), caps, CAP_SET));
- }
- /* Automatically resets ambient capabilities. */
- EXPECT_NE(-1, cap_set_proc(cap_p))
- {
- TH_LOG("Failed to set capabilities: %s", strerror(errno));
- }
- EXPECT_NE(-1, cap_free(cap_p));
- /* Quickly checks that ambient capabilities are cleared. */
- EXPECT_NE(-1, cap_get_ambient(caps[0]));
- }
- /* We cannot put such helpers in a library because of kselftest_harness.h . */
- static void __maybe_unused disable_caps(struct __test_metadata *const _metadata)
- {
- _init_caps(_metadata, false);
- }
- static void __maybe_unused drop_caps(struct __test_metadata *const _metadata)
- {
- _init_caps(_metadata, true);
- }
- static void _change_cap(struct __test_metadata *const _metadata,
- const cap_flag_t flag, const cap_value_t cap,
- const cap_flag_value_t value)
- {
- cap_t cap_p;
- cap_p = cap_get_proc();
- EXPECT_NE(NULL, cap_p);
- EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value));
- EXPECT_NE(-1, cap_set_proc(cap_p))
- {
- TH_LOG("Failed to set capability %d: %s", cap, strerror(errno));
- }
- EXPECT_NE(-1, cap_free(cap_p));
- }
- static void __maybe_unused set_cap(struct __test_metadata *const _metadata,
- const cap_value_t cap)
- {
- _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_SET);
- }
- static void __maybe_unused clear_cap(struct __test_metadata *const _metadata,
- const cap_value_t cap)
- {
- _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_CLEAR);
- }
- static void __maybe_unused
- set_ambient_cap(struct __test_metadata *const _metadata, const cap_value_t cap)
- {
- _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_SET);
- EXPECT_NE(-1, cap_set_ambient(cap, CAP_SET))
- {
- TH_LOG("Failed to set ambient capability %d: %s", cap,
- strerror(errno));
- }
- }
- static void __maybe_unused clear_ambient_cap(
- struct __test_metadata *const _metadata, const cap_value_t cap)
- {
- EXPECT_EQ(1, cap_get_ambient(cap));
- _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_CLEAR);
- EXPECT_EQ(0, cap_get_ambient(cap));
- }
- /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */
- static int __maybe_unused recv_fd(int usock)
- {
- int fd_rx;
- union {
- /* Aligned ancillary data buffer. */
- char buf[CMSG_SPACE(sizeof(fd_rx))];
- struct cmsghdr _align;
- } cmsg_rx = {};
- char data = '\0';
- struct iovec io = {
- .iov_base = &data,
- .iov_len = sizeof(data),
- };
- struct msghdr msg = {
- .msg_iov = &io,
- .msg_iovlen = 1,
- .msg_control = &cmsg_rx.buf,
- .msg_controllen = sizeof(cmsg_rx.buf),
- };
- struct cmsghdr *cmsg;
- int res;
- res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC);
- if (res < 0)
- return -errno;
- cmsg = CMSG_FIRSTHDR(&msg);
- if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx)))
- return -EIO;
- memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx));
- return fd_rx;
- }
- /* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */
- static int __maybe_unused send_fd(int usock, int fd_tx)
- {
- union {
- /* Aligned ancillary data buffer. */
- char buf[CMSG_SPACE(sizeof(fd_tx))];
- struct cmsghdr _align;
- } cmsg_tx = {};
- char data_tx = '.';
- struct iovec io = {
- .iov_base = &data_tx,
- .iov_len = sizeof(data_tx),
- };
- struct msghdr msg = {
- .msg_iov = &io,
- .msg_iovlen = 1,
- .msg_control = &cmsg_tx.buf,
- .msg_controllen = sizeof(cmsg_tx.buf),
- };
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx));
- if (sendmsg(usock, &msg, 0) < 0)
- return -errno;
- return 0;
- }
- static void __maybe_unused
- enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
- {
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
- ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
- {
- TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
- }
- }
- struct protocol_variant {
- int domain;
- int type;
- int protocol;
- };
- struct service_fixture {
- struct protocol_variant protocol;
- /* port is also stored in ipv4_addr.sin_port or ipv6_addr.sin6_port */
- unsigned short port;
- union {
- struct sockaddr_in ipv4_addr;
- struct sockaddr_in6 ipv6_addr;
- struct {
- struct sockaddr_un unix_addr;
- socklen_t unix_addr_len;
- };
- };
- };
- static pid_t __maybe_unused sys_gettid(void)
- {
- return syscall(__NR_gettid);
- }
- static void __maybe_unused set_unix_address(struct service_fixture *const srv,
- const unsigned short index)
- {
- srv->unix_addr.sun_family = AF_UNIX;
- sprintf(srv->unix_addr.sun_path,
- "_selftests-landlock-abstract-unix-tid%d-index%d", sys_gettid(),
- index);
- srv->unix_addr_len = SUN_LEN(&srv->unix_addr);
- srv->unix_addr.sun_path[0] = '\0';
- }
|