root/tools/testing/selftests/landlock/common.h
/* 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/securebits.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

#include "kselftest_harness.h"
#include "wrappers.h"

#define TMP_DIR "tmp"

/* TEST_F_FORK() should not be used for new tests. */
#define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name)

static const char bin_sandbox_and_launch[] = "./sandbox-and-launch";
static const char bin_wait_pipe[] = "./wait-pipe";
static const char bin_wait_pipe_sandbox[] = "./wait-pipe-sandbox";

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_AUDIT_CONTROL,
                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));
        }
}

static void __maybe_unused
drop_access_rights(struct __test_metadata *const _metadata,
                   const struct landlock_ruleset_attr *const ruleset_attr)
{
        int ruleset_fd;

        ruleset_fd =
                landlock_create_ruleset(ruleset_attr, sizeof(*ruleset_attr), 0);
        EXPECT_LE(0, ruleset_fd)
        {
                TH_LOG("Failed to create a ruleset: %s", strerror(errno));
        }
        enforce_ruleset(_metadata, ruleset_fd);
        EXPECT_EQ(0, close(ruleset_fd));
}

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;
                };
                struct sockaddr_storage _largest;
        };
};

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';
}