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