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
landlock_create_ruleset(const struct landlock_ruleset_attr * const attr,const size_t size,const __u32 flags)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
landlock_add_rule(const int ruleset_fd,const enum landlock_rule_type rule_type,const void * const rule_attr,const __u32 flags)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
landlock_restrict_self(const int ruleset_fd,const __u32 flags)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
_init_caps(struct __test_metadata * const _metadata,bool drop_all)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 . */
disable_caps(struct __test_metadata * const _metadata)162 static void __maybe_unused disable_caps(struct __test_metadata *const _metadata)
163 {
164 _init_caps(_metadata, false);
165 }
166
drop_caps(struct __test_metadata * const _metadata)167 static void __maybe_unused drop_caps(struct __test_metadata *const _metadata)
168 {
169 _init_caps(_metadata, true);
170 }
171
_change_cap(struct __test_metadata * const _metadata,const cap_flag_t flag,const cap_value_t cap,const cap_flag_value_t value)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
set_cap(struct __test_metadata * const _metadata,const cap_value_t cap)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
clear_cap(struct __test_metadata * const _metadata,const cap_value_t cap)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
set_ambient_cap(struct __test_metadata * const _metadata,const cap_value_t cap)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
clear_ambient_cap(struct __test_metadata * const _metadata,const cap_value_t cap)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. */
recv_fd(int usock)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. */
send_fd(int usock,int fd_tx)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
enforce_ruleset(struct __test_metadata * const _metadata,const int ruleset_fd)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