xref: /linux/tools/testing/selftests/landlock/common.h (revision 4e73826089ce899357580bbf6e0afe4e6f9900b7)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Landlock test helpers
4  *
5  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6  * Copyright © 2019-2020 ANSSI
7  * Copyright © 2021 Microsoft Corporation
8  */
9 
10 #include <errno.h>
11 #include <linux/landlock.h>
12 #include <linux/securebits.h>
13 #include <sys/capability.h>
14 #include <sys/socket.h>
15 #include <sys/syscall.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19 
20 #include "../kselftest_harness.h"
21 
22 #ifndef __maybe_unused
23 #define __maybe_unused __attribute__((__unused__))
24 #endif
25 
26 /*
27  * TEST_F_FORK() is useful when a test drop privileges but the corresponding
28  * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory
29  * where write actions are denied).  For convenience, FIXTURE_TEARDOWN() is
30  * also called when the test failed, but not when FIXTURE_SETUP() failed.  For
31  * this to be possible, we must not call abort() but instead exit smoothly
32  * (hence the step print).
33  */
34 /* clang-format off */
35 #define TEST_F_FORK(fixture_name, test_name) \
36 	static void fixture_name##_##test_name##_child( \
37 		struct __test_metadata *_metadata, \
38 		FIXTURE_DATA(fixture_name) *self, \
39 		const FIXTURE_VARIANT(fixture_name) *variant); \
40 	TEST_F(fixture_name, test_name) \
41 	{ \
42 		int status; \
43 		const pid_t child = fork(); \
44 		if (child < 0) \
45 			abort(); \
46 		if (child == 0) { \
47 			_metadata->no_print = 1; \
48 			fixture_name##_##test_name##_child(_metadata, self, variant); \
49 			if (_metadata->skip) \
50 				_exit(255); \
51 			if (_metadata->passed) \
52 				_exit(0); \
53 			_exit(_metadata->step); \
54 		} \
55 		if (child != waitpid(child, &status, 0)) \
56 			abort(); \
57 		if (WIFSIGNALED(status) || !WIFEXITED(status)) { \
58 			_metadata->passed = 0; \
59 			_metadata->step = 1; \
60 			return; \
61 		} \
62 		switch (WEXITSTATUS(status)) { \
63 		case 0: \
64 			_metadata->passed = 1; \
65 			break; \
66 		case 255: \
67 			_metadata->passed = 1; \
68 			_metadata->skip = 1; \
69 			break; \
70 		default: \
71 			_metadata->passed = 0; \
72 			_metadata->step = WEXITSTATUS(status); \
73 			break; \
74 		} \
75 	} \
76 	static void fixture_name##_##test_name##_child( \
77 		struct __test_metadata __attribute__((unused)) *_metadata, \
78 		FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
79 		const FIXTURE_VARIANT(fixture_name) \
80 			__attribute__((unused)) *variant)
81 /* clang-format on */
82 
83 #ifndef landlock_create_ruleset
84 static inline int
85 landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
86 			const size_t size, const __u32 flags)
87 {
88 	return syscall(__NR_landlock_create_ruleset, attr, size, flags);
89 }
90 #endif
91 
92 #ifndef landlock_add_rule
93 static inline int landlock_add_rule(const int ruleset_fd,
94 				    const enum landlock_rule_type rule_type,
95 				    const void *const rule_attr,
96 				    const __u32 flags)
97 {
98 	return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
99 		       flags);
100 }
101 #endif
102 
103 #ifndef landlock_restrict_self
104 static inline int landlock_restrict_self(const int ruleset_fd,
105 					 const __u32 flags)
106 {
107 	return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
108 }
109 #endif
110 
111 static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
112 {
113 	cap_t cap_p;
114 	/* Only these three capabilities are useful for the tests. */
115 	const cap_value_t caps[] = {
116 		/* clang-format off */
117 		CAP_DAC_OVERRIDE,
118 		CAP_MKNOD,
119 		CAP_NET_ADMIN,
120 		CAP_NET_BIND_SERVICE,
121 		CAP_SYS_ADMIN,
122 		CAP_SYS_CHROOT,
123 		/* clang-format on */
124 	};
125 	const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED;
126 
127 	if ((cap_get_secbits() & noroot) != noroot)
128 		EXPECT_EQ(0, cap_set_secbits(noroot));
129 
130 	cap_p = cap_get_proc();
131 	EXPECT_NE(NULL, cap_p)
132 	{
133 		TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
134 	}
135 	EXPECT_NE(-1, cap_clear(cap_p))
136 	{
137 		TH_LOG("Failed to cap_clear: %s", strerror(errno));
138 	}
139 	if (!drop_all) {
140 		EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED,
141 					   ARRAY_SIZE(caps), caps, CAP_SET))
142 		{
143 			TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
144 		}
145 	}
146 
147 	/* Automatically resets ambient capabilities. */
148 	EXPECT_NE(-1, cap_set_proc(cap_p))
149 	{
150 		TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
151 	}
152 	EXPECT_NE(-1, cap_free(cap_p))
153 	{
154 		TH_LOG("Failed to cap_free: %s", strerror(errno));
155 	}
156 
157 	/* Quickly checks that ambient capabilities are cleared. */
158 	EXPECT_NE(-1, cap_get_ambient(caps[0]));
159 }
160 
161 /* We cannot put such helpers in a library because of kselftest_harness.h . */
162 static void __maybe_unused disable_caps(struct __test_metadata *const _metadata)
163 {
164 	_init_caps(_metadata, false);
165 }
166 
167 static void __maybe_unused drop_caps(struct __test_metadata *const _metadata)
168 {
169 	_init_caps(_metadata, true);
170 }
171 
172 static void _change_cap(struct __test_metadata *const _metadata,
173 			const cap_flag_t flag, const cap_value_t cap,
174 			const cap_flag_value_t value)
175 {
176 	cap_t cap_p;
177 
178 	cap_p = cap_get_proc();
179 	EXPECT_NE(NULL, cap_p)
180 	{
181 		TH_LOG("Failed to cap_get_proc: %s", strerror(errno));
182 	}
183 	EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value))
184 	{
185 		TH_LOG("Failed to cap_set_flag: %s", strerror(errno));
186 	}
187 	EXPECT_NE(-1, cap_set_proc(cap_p))
188 	{
189 		TH_LOG("Failed to cap_set_proc: %s", strerror(errno));
190 	}
191 	EXPECT_NE(-1, cap_free(cap_p))
192 	{
193 		TH_LOG("Failed to cap_free: %s", strerror(errno));
194 	}
195 }
196 
197 static void __maybe_unused set_cap(struct __test_metadata *const _metadata,
198 				   const cap_value_t cap)
199 {
200 	_change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_SET);
201 }
202 
203 static void __maybe_unused clear_cap(struct __test_metadata *const _metadata,
204 				     const cap_value_t cap)
205 {
206 	_change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_CLEAR);
207 }
208 
209 static void __maybe_unused
210 set_ambient_cap(struct __test_metadata *const _metadata, const cap_value_t cap)
211 {
212 	_change_cap(_metadata, CAP_INHERITABLE, cap, CAP_SET);
213 
214 	EXPECT_NE(-1, cap_set_ambient(cap, CAP_SET))
215 	{
216 		TH_LOG("Failed to set ambient capability %d: %s", cap,
217 		       strerror(errno));
218 	}
219 }
220 
221 static void __maybe_unused clear_ambient_cap(
222 	struct __test_metadata *const _metadata, const cap_value_t cap)
223 {
224 	EXPECT_EQ(1, cap_get_ambient(cap));
225 	_change_cap(_metadata, CAP_INHERITABLE, cap, CAP_CLEAR);
226 	EXPECT_EQ(0, cap_get_ambient(cap));
227 }
228 
229 /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */
230 static int __maybe_unused recv_fd(int usock)
231 {
232 	int fd_rx;
233 	union {
234 		/* Aligned ancillary data buffer. */
235 		char buf[CMSG_SPACE(sizeof(fd_rx))];
236 		struct cmsghdr _align;
237 	} cmsg_rx = {};
238 	char data = '\0';
239 	struct iovec io = {
240 		.iov_base = &data,
241 		.iov_len = sizeof(data),
242 	};
243 	struct msghdr msg = {
244 		.msg_iov = &io,
245 		.msg_iovlen = 1,
246 		.msg_control = &cmsg_rx.buf,
247 		.msg_controllen = sizeof(cmsg_rx.buf),
248 	};
249 	struct cmsghdr *cmsg;
250 	int res;
251 
252 	res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC);
253 	if (res < 0)
254 		return -errno;
255 
256 	cmsg = CMSG_FIRSTHDR(&msg);
257 	if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx)))
258 		return -EIO;
259 
260 	memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx));
261 	return fd_rx;
262 }
263 
264 /* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */
265 static int __maybe_unused send_fd(int usock, int fd_tx)
266 {
267 	union {
268 		/* Aligned ancillary data buffer. */
269 		char buf[CMSG_SPACE(sizeof(fd_tx))];
270 		struct cmsghdr _align;
271 	} cmsg_tx = {};
272 	char data_tx = '.';
273 	struct iovec io = {
274 		.iov_base = &data_tx,
275 		.iov_len = sizeof(data_tx),
276 	};
277 	struct msghdr msg = {
278 		.msg_iov = &io,
279 		.msg_iovlen = 1,
280 		.msg_control = &cmsg_tx.buf,
281 		.msg_controllen = sizeof(cmsg_tx.buf),
282 	};
283 	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
284 
285 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx));
286 	cmsg->cmsg_level = SOL_SOCKET;
287 	cmsg->cmsg_type = SCM_RIGHTS;
288 	memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx));
289 
290 	if (sendmsg(usock, &msg, 0) < 0)
291 		return -errno;
292 	return 0;
293 }
294 
295 static void __maybe_unused
296 enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
297 {
298 	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
299 	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
300 	{
301 		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
302 	}
303 }
304