]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-namespace.c
b301f3ea4bf9d57a456b81b1c439ed27b69e28b4
[thirdparty/systemd.git] / src / test / test-namespace.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <sched.h>
5 #include <stdlib.h>
6 #include <sys/socket.h>
7 #include <sys/stat.h>
8 #include <sysexits.h>
9 #include <unistd.h>
10
11 #include "sd-id128.h"
12
13 #include "alloc-util.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "namespace-util.h"
17 #include "namespace.h"
18 #include "pidref.h"
19 #include "process-util.h"
20 #include "string-util.h"
21 #include "tests.h"
22 #include "uid-range.h"
23 #include "user-util.h"
24 #include "virt.h"
25
26 TEST(namespace_cleanup_tmpdir) {
27 {
28 _cleanup_(namespace_cleanup_tmpdirp) char *dir;
29 assert_se(dir = strdup(RUN_SYSTEMD_EMPTY));
30 }
31
32 {
33 _cleanup_(namespace_cleanup_tmpdirp) char *dir;
34 assert_se(dir = strdup("/tmp/systemd-test-namespace.XXXXXX"));
35 assert_se(mkdtemp(dir));
36 }
37 }
38
39 static void test_tmpdir_one(const char *id, const char *A, const char *B) {
40 _cleanup_free_ char *a, *b;
41 struct stat x, y;
42 char *c, *d;
43
44 assert_se(setup_tmp_dirs(id, &a, &b) == 0);
45
46 assert_se(stat(a, &x) >= 0);
47 assert_se(stat(b, &y) >= 0);
48
49 assert_se(S_ISDIR(x.st_mode));
50 assert_se(S_ISDIR(y.st_mode));
51
52 if (!streq(a, RUN_SYSTEMD_EMPTY)) {
53 assert_se(startswith(a, A));
54 assert_se((x.st_mode & 01777) == 0700);
55 c = strjoina(a, "/tmp");
56 assert_se(stat(c, &x) >= 0);
57 assert_se(S_ISDIR(x.st_mode));
58 assert_se(FLAGS_SET(x.st_mode, 01777));
59 assert_se(rmdir(c) >= 0);
60 assert_se(rmdir(a) >= 0);
61 }
62
63 if (!streq(b, RUN_SYSTEMD_EMPTY)) {
64 assert_se(startswith(b, B));
65 assert_se((y.st_mode & 01777) == 0700);
66 d = strjoina(b, "/tmp");
67 assert_se(stat(d, &y) >= 0);
68 assert_se(S_ISDIR(y.st_mode));
69 assert_se(FLAGS_SET(y.st_mode, 01777));
70 assert_se(rmdir(d) >= 0);
71 assert_se(rmdir(b) >= 0);
72 }
73 }
74
75 TEST(tmpdir) {
76 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
77 sd_id128_t bid;
78
79 assert_se(sd_id128_get_boot(&bid) >= 0);
80
81 x = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
82 y = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
83 assert_se(x && y);
84
85 test_tmpdir_one("abcd.service", x, y);
86
87 z = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
88 zz = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
89
90 assert_se(z && zz);
91
92 test_tmpdir_one("sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device", z, zz);
93 }
94
95 static void test_shareable_ns(unsigned long nsflag) {
96 _cleanup_close_pair_ int s[2] = EBADF_PAIR;
97 bool permission_denied = false;
98 pid_t pid1, pid2, pid3;
99 int r, n = 0;
100 siginfo_t si;
101
102 if (geteuid() > 0) {
103 (void) log_tests_skipped("not root");
104 return;
105 }
106
107 assert_se(socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, s) >= 0);
108
109 pid1 = fork();
110 assert_se(pid1 >= 0);
111
112 if (pid1 == 0) {
113 r = setup_shareable_ns(s, nsflag);
114 assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r));
115 _exit(r >= 0 ? r : EX_NOPERM);
116 }
117
118 pid2 = fork();
119 assert_se(pid2 >= 0);
120
121 if (pid2 == 0) {
122 r = setup_shareable_ns(s, nsflag);
123 assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r));
124 _exit(r >= 0 ? r : EX_NOPERM);
125 }
126
127 pid3 = fork();
128 assert_se(pid3 >= 0);
129
130 if (pid3 == 0) {
131 r = setup_shareable_ns(s, nsflag);
132 assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r));
133 _exit(r >= 0 ? r : EX_NOPERM);
134 }
135
136 r = wait_for_terminate(pid1, &si);
137 assert_se(r >= 0);
138 assert_se(si.si_code == CLD_EXITED);
139 if (si.si_status == EX_NOPERM)
140 permission_denied = true;
141 else
142 n += si.si_status;
143
144 r = wait_for_terminate(pid2, &si);
145 assert_se(r >= 0);
146 assert_se(si.si_code == CLD_EXITED);
147 if (si.si_status == EX_NOPERM)
148 permission_denied = true;
149 else
150 n += si.si_status;
151
152 r = wait_for_terminate(pid3, &si);
153 assert_se(r >= 0);
154 assert_se(si.si_code == CLD_EXITED);
155 if (si.si_status == EX_NOPERM)
156 permission_denied = true;
157 else
158 n += si.si_status;
159
160 /* LSMs can cause setup_shareable_ns() to fail with permission denied, do not fail the test in that
161 * case (e.g.: LXC with AppArmor on kernel < v6.2). */
162 if (permission_denied)
163 return (void) log_tests_skipped("insufficient privileges");
164
165 assert_se(n == 1);
166 }
167
168 TEST(netns) {
169 test_shareable_ns(CLONE_NEWNET);
170 }
171
172 TEST(ipcns) {
173 test_shareable_ns(CLONE_NEWIPC);
174 }
175
176 TEST(fd_is_namespace) {
177 _cleanup_close_ int fd = -EBADF;
178
179 ASSERT_OK_ZERO(fd_is_namespace(STDIN_FILENO, NAMESPACE_NET));
180 ASSERT_OK_ZERO(fd_is_namespace(STDOUT_FILENO, NAMESPACE_NET));
181 ASSERT_OK_ZERO(fd_is_namespace(STDERR_FILENO, NAMESPACE_NET));
182
183 fd = namespace_open_by_type(NAMESPACE_MOUNT);
184 if (IN_SET(fd, -ENOSYS, -ENOPKG)) {
185 log_notice("Path %s not found, skipping test", "/proc/self/ns/mnt");
186 return;
187 }
188 ASSERT_OK(fd);
189 ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_MOUNT));
190 ASSERT_OK_ZERO(fd_is_namespace(fd, NAMESPACE_NET));
191 fd = safe_close(fd);
192
193 ASSERT_OK(fd = namespace_open_by_type(NAMESPACE_IPC));
194 ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_IPC));
195 fd = safe_close(fd);
196
197 ASSERT_OK(fd = namespace_open_by_type(NAMESPACE_NET));
198 ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_NET));
199 }
200
201 TEST(protect_kernel_logs) {
202 static const NamespaceParameters p = {
203 .runtime_scope = RUNTIME_SCOPE_SYSTEM,
204 .protect_kernel_logs = true,
205 };
206 pid_t pid;
207 int r;
208
209 if (geteuid() > 0) {
210 (void) log_tests_skipped("not root");
211 return;
212 }
213
214 /* In a container we likely don't have access to /dev/kmsg */
215 if (detect_container() > 0) {
216 (void) log_tests_skipped("in container");
217 return;
218 }
219
220 pid = fork();
221 assert_se(pid >= 0);
222
223 if (pid == 0) {
224 _cleanup_close_ int fd = -EBADF;
225
226 fd = open("/dev/kmsg", O_RDONLY | O_CLOEXEC);
227 assert_se(fd > 0);
228
229 r = setup_namespace(&p, NULL);
230 assert_se(r == 0);
231
232 assert_se(setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) >= 0);
233 assert_se(open("/dev/kmsg", O_RDONLY | O_CLOEXEC) < 0);
234 assert_se(errno == EACCES);
235
236 _exit(EXIT_SUCCESS);
237 }
238
239 assert_se(wait_for_terminate_and_check("ns-kernellogs", pid, WAIT_LOG) == EXIT_SUCCESS);
240 }
241
242 TEST(idmapping_supported) {
243 assert_se(is_idmapping_supported("/run") >= 0);
244 assert_se(is_idmapping_supported("/var/lib") >= 0);
245 assert_se(is_idmapping_supported("/var/cache") >= 0);
246 assert_se(is_idmapping_supported("/var/log") >= 0);
247 assert_se(is_idmapping_supported("/etc") >= 0);
248 }
249
250 TEST(namespace_is_init) {
251 int r;
252
253 for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++) {
254 r = namespace_is_init(t);
255 if (r == -EBADR)
256 log_info_errno(r, "In root namespace of type '%s': don't know", namespace_info[t].proc_name);
257 else {
258 ASSERT_OK(r);
259 log_info("In root namespace of type '%s': %s", namespace_info[t].proc_name, yes_no(r));
260 }
261 }
262 }
263
264 TEST(userns_get_base_uid) {
265 _cleanup_close_ int fd = -EBADF;
266
267 fd = userns_acquire("0 1 1", "0 2 1", /* setgroups_deny= */ true);
268 if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
269 return (void) log_tests_skipped("userns is not supported");
270 if (ERRNO_IS_NEG_PRIVILEGE(fd))
271 return (void) log_tests_skipped("lacking userns privileges");
272
273 uid_t base_uid, base_gid;
274 ASSERT_OK(userns_get_base_uid(fd, &base_uid, &base_gid));
275 ASSERT_EQ(base_uid, 1U);
276 ASSERT_EQ(base_gid, 2U);
277
278 ASSERT_ERROR(userns_get_base_uid(fd, &base_uid, NULL), EUCLEAN);
279
280 fd = safe_close(fd);
281
282 fd = userns_acquire_empty();
283 ASSERT_OK(fd);
284
285 ASSERT_ERROR(userns_get_base_uid(fd, &base_uid, &base_gid), ENOMSG);
286 }
287
288 TEST(process_is_owned_by_uid) {
289 int r;
290
291 /* Test our own PID */
292 _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
293 ASSERT_OK(pidref_set_self(&pid));
294 ASSERT_OK_POSITIVE(process_is_owned_by_uid(&pid, getuid()));
295 pidref_done(&pid);
296
297 if (getuid() != 0)
298 return (void) log_tests_skipped("lacking userns privileges");
299
300 _cleanup_(uid_range_freep) UIDRange *range = NULL;
301 ASSERT_OK(uid_range_load_userns(/* path= */ NULL, UID_RANGE_USERNS_INSIDE, &range));
302 if (!uid_range_contains(range, 1))
303 return (void) log_tests_skipped("UID 1 not included in userns UID delegation, skipping test");
304
305 /* Test a child that runs as uid 1 */
306 _cleanup_close_pair_ int p[2] = EBADF_PAIR;
307 ASSERT_OK_ERRNO(pipe2(p, O_CLOEXEC));
308
309 r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pid);
310 ASSERT_OK(r);
311 if (r == 0) {
312 p[0] = safe_close(p[0]);
313 ASSERT_OK(fully_set_uid_gid(1, 1, NULL, 0));
314 ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1);
315 p[1] = safe_close(p[1]);
316 freeze();
317 }
318
319 p[1] = safe_close(p[1]);
320 char x = 0;
321 ASSERT_OK_EQ_ERRNO(read(p[0], &x, 1), 1);
322 ASSERT_EQ(x, 'x');
323 p[0] = safe_close(p[0]);
324
325 ASSERT_OK_ZERO(process_is_owned_by_uid(&pid, getuid()));
326
327 ASSERT_OK(pidref_kill(&pid, SIGKILL));
328 ASSERT_OK(pidref_wait_for_terminate(&pid, /* ret= */ NULL));
329
330 /* Test a child that runs in a userns as uid 1, but the userns is owned by us */
331 ASSERT_OK_ERRNO(pipe2(p, O_CLOEXEC));
332
333 _cleanup_close_pair_ int pp[2] = EBADF_PAIR;
334 ASSERT_OK_ERRNO(pipe2(pp, O_CLOEXEC));
335
336 r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
337 ASSERT_OK(r);
338 if (r == 0) {
339 p[0] = safe_close(p[0]);
340 pp[1] = safe_close(pp[1]);
341
342 x = 0;
343 ASSERT_OK_EQ_ERRNO(read(pp[0], &x, 1), 1);
344 ASSERT_EQ(x, 'x');
345 pp[0] = safe_close(pp[0]);
346
347 ASSERT_OK(reset_uid_gid());
348
349 ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1);
350 p[1] = safe_close(p[1]);
351 freeze();
352 }
353
354 p[1] = safe_close(p[1]);
355 pp[0] = safe_close(pp[0]);
356
357 ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "uid_map"), "0 1 1\n", 0));
358 ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "setgroups"), "deny", 0));
359 ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "gid_map"), "0 1 1\n", 0));
360
361 ASSERT_OK_EQ_ERRNO(write(pp[1], &(const char[]) { 'x' }, 1), 1);
362 pp[1] = safe_close(pp[1]);
363
364 x = 0;
365 ASSERT_OK_EQ_ERRNO(read(p[0], &x, 1), 1);
366 ASSERT_EQ(x, 'x');
367 p[0] = safe_close(p[0]);
368
369 ASSERT_OK_POSITIVE(process_is_owned_by_uid(&pid, getuid()));
370
371 ASSERT_OK(pidref_kill(&pid, SIGKILL));
372 ASSERT_OK(pidref_wait_for_terminate(&pid, /* ret= */ NULL));
373 }
374
375 TEST(namespace_get_leader) {
376 int r;
377
378 _cleanup_(pidref_done) PidRef original = PIDREF_NULL;
379 ASSERT_OK(pidref_set_self(&original));
380
381 _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
382 r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_NEW_MOUNTNS|FORK_WAIT|FORK_LOG, &pid);
383 ASSERT_OK(r);
384 if (r == 0) {
385
386 _cleanup_(pidref_done) PidRef pid2 = PIDREF_NULL;
387 r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT|FORK_LOG, &pid2);
388 ASSERT_OK(r);
389
390 if (r == 0) {
391 log_info("PID hierarchy: " PID_FMT " ← " PID_FMT " ← " PID_FMT, original.pid, pid.pid, pid2.pid);
392
393 _cleanup_(pidref_done) PidRef self = PIDREF_NULL;
394 ASSERT_OK(pidref_set_self(&self));
395 ASSERT_TRUE(pidref_equal(&self, &pid2));
396
397 _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
398 ASSERT_OK(pidref_set_parent(&parent));
399 ASSERT_TRUE(pidref_equal(&parent, &pid));
400 ASSERT_TRUE(!pidref_equal(&self, &pid));
401 ASSERT_TRUE(!pidref_equal(&self, &parent));
402
403 _cleanup_(pidref_done) PidRef grandparent = PIDREF_NULL;
404 ASSERT_OK(pidref_get_ppid_as_pidref(&parent, &grandparent));
405 ASSERT_TRUE(pidref_equal(&grandparent, &original));
406 ASSERT_TRUE(!pidref_equal(&grandparent, &self));
407 ASSERT_TRUE(!pidref_equal(&grandparent, &pid));
408 ASSERT_TRUE(!pidref_equal(&grandparent, &pid2));
409 ASSERT_TRUE(!pidref_equal(&grandparent, &parent));
410
411 _cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
412 ASSERT_OK(namespace_get_leader(&self, NAMESPACE_MOUNT, &leader));
413 ASSERT_TRUE(pidref_equal(&parent, &leader));
414 ASSERT_TRUE(pidref_equal(&pid, &leader));
415 ASSERT_TRUE(!pidref_equal(&self, &leader));
416 ASSERT_TRUE(!pidref_equal(&pid2, &leader));
417 ASSERT_TRUE(!pidref_equal(&original, &leader));
418 ASSERT_TRUE(!pidref_equal(&grandparent, &leader));
419 }
420 }
421 }
422
423 TEST(detach_mount_namespace_harder) {
424 _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
425 _cleanup_close_pair_ int p[2] = EBADF_PAIR;
426 char x = 0;
427 int r;
428
429 ASSERT_OK_ERRNO(pipe2(p, O_CLOEXEC));
430
431 ASSERT_OK(r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG, &pid));
432 if (r == 0) {
433 p[0] = safe_close(p[0]);
434
435 ASSERT_OK(detach_mount_namespace_harder(0, 0));
436
437 ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1);
438 freeze();
439 }
440
441 p[1] = safe_close(p[1]);
442 ASSERT_OK_EQ_ERRNO(read(p[0], &x, 1), 1);
443 ASSERT_EQ(x, 'x');
444
445 ASSERT_OK_POSITIVE(pidref_in_same_namespace(NULL, &pid, NAMESPACE_USER));
446 ASSERT_OK_ZERO(pidref_in_same_namespace(NULL, &pid, NAMESPACE_MOUNT));
447 }
448
449 static int intro(void) {
450 if (!have_namespaces())
451 return log_tests_skipped("Don't have namespace support or lacking privileges");
452
453 return EXIT_SUCCESS;
454 }
455
456 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);