1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <grp.h>
6 #include <limits.h>
7 #include <sched.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/mount.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 #include <linux/unistd.h>
17 #include "../kselftest_harness.h"
18
19 #ifndef FD_NSFS_ROOT
20 #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
21 #endif
22
TEST(nsfs_net_handle)23 TEST(nsfs_net_handle)
24 {
25 struct file_handle *handle;
26 int mount_id;
27 int ret;
28 int fd;
29 int ns_fd;
30 struct stat st1, st2;
31
32 /* Drop to unprivileged uid/gid */
33 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
34 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
35
36 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
37 ASSERT_NE(handle, NULL);
38
39 /* Open a namespace file descriptor */
40 ns_fd = open("/proc/self/ns/net", O_RDONLY);
41 ASSERT_GE(ns_fd, 0);
42
43 /* Get handle for the namespace */
44 handle->handle_bytes = MAX_HANDLE_SZ;
45 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
46 if (ret < 0 && errno == EOPNOTSUPP) {
47 SKIP(free(handle); close(ns_fd);
48 return, "nsfs doesn't support file handles");
49 }
50 ASSERT_EQ(ret, 0);
51 ASSERT_GT(handle->handle_bytes, 0);
52
53 /* Try to open using FD_NSFS_ROOT as unprivileged user */
54 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
55 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
56 SKIP(free(handle); close(ns_fd);
57 return,
58 "open_by_handle_at with FD_NSFS_ROOT not supported");
59 }
60 if (fd < 0 && errno == EPERM) {
61 SKIP(free(handle); close(ns_fd);
62 return,
63 "Permission denied for unprivileged user (expected)");
64 }
65 ASSERT_GE(fd, 0);
66
67 /* Verify we opened the correct namespace */
68 ASSERT_EQ(fstat(ns_fd, &st1), 0);
69 ASSERT_EQ(fstat(fd, &st2), 0);
70 ASSERT_EQ(st1.st_ino, st2.st_ino);
71 ASSERT_EQ(st1.st_dev, st2.st_dev);
72
73 close(fd);
74 close(ns_fd);
75 free(handle);
76 }
77
TEST(nsfs_uts_handle)78 TEST(nsfs_uts_handle)
79 {
80 struct file_handle *handle;
81 int mount_id;
82 int ret;
83 int fd;
84 int ns_fd;
85 struct stat st1, st2;
86
87 /* Drop to unprivileged uid/gid */
88 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
89 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
90
91 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
92 ASSERT_NE(handle, NULL);
93
94 /* Open UTS namespace file descriptor */
95 ns_fd = open("/proc/self/ns/uts", O_RDONLY);
96 ASSERT_GE(ns_fd, 0);
97
98 /* Get handle for the namespace */
99 handle->handle_bytes = MAX_HANDLE_SZ;
100 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
101 if (ret < 0 && errno == EOPNOTSUPP) {
102 SKIP(free(handle); close(ns_fd);
103 return, "nsfs doesn't support file handles");
104 }
105 ASSERT_EQ(ret, 0);
106 ASSERT_GT(handle->handle_bytes, 0);
107
108 /* Try to open using FD_NSFS_ROOT */
109 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
110 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
111 SKIP(free(handle); close(ns_fd);
112 return,
113 "open_by_handle_at with FD_NSFS_ROOT not supported");
114 }
115 ASSERT_GE(fd, 0);
116
117 /* Verify we opened the correct namespace */
118 ASSERT_EQ(fstat(ns_fd, &st1), 0);
119 ASSERT_EQ(fstat(fd, &st2), 0);
120 ASSERT_EQ(st1.st_ino, st2.st_ino);
121 ASSERT_EQ(st1.st_dev, st2.st_dev);
122
123 close(fd);
124 close(ns_fd);
125 free(handle);
126 }
127
TEST(nsfs_ipc_handle)128 TEST(nsfs_ipc_handle)
129 {
130 struct file_handle *handle;
131 int mount_id;
132 int ret;
133 int fd;
134 int ns_fd;
135 struct stat st1, st2;
136
137 /* Drop to unprivileged uid/gid */
138 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
139 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
140
141 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
142 ASSERT_NE(handle, NULL);
143
144 /* Open IPC namespace file descriptor */
145 ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
146 ASSERT_GE(ns_fd, 0);
147
148 /* Get handle for the namespace */
149 handle->handle_bytes = MAX_HANDLE_SZ;
150 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
151 if (ret < 0 && errno == EOPNOTSUPP) {
152 SKIP(free(handle); close(ns_fd);
153 return, "nsfs doesn't support file handles");
154 }
155 ASSERT_EQ(ret, 0);
156 ASSERT_GT(handle->handle_bytes, 0);
157
158 /* Try to open using FD_NSFS_ROOT */
159 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
160 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
161 SKIP(free(handle); close(ns_fd);
162 return,
163 "open_by_handle_at with FD_NSFS_ROOT not supported");
164 }
165 ASSERT_GE(fd, 0);
166
167 /* Verify we opened the correct namespace */
168 ASSERT_EQ(fstat(ns_fd, &st1), 0);
169 ASSERT_EQ(fstat(fd, &st2), 0);
170 ASSERT_EQ(st1.st_ino, st2.st_ino);
171 ASSERT_EQ(st1.st_dev, st2.st_dev);
172
173 close(fd);
174 close(ns_fd);
175 free(handle);
176 }
177
TEST(nsfs_pid_handle)178 TEST(nsfs_pid_handle)
179 {
180 struct file_handle *handle;
181 int mount_id;
182 int ret;
183 int fd;
184 int ns_fd;
185 struct stat st1, st2;
186
187 /* Drop to unprivileged uid/gid */
188 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
189 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
190
191 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
192 ASSERT_NE(handle, NULL);
193
194 /* Open PID namespace file descriptor */
195 ns_fd = open("/proc/self/ns/pid", O_RDONLY);
196 ASSERT_GE(ns_fd, 0);
197
198 /* Get handle for the namespace */
199 handle->handle_bytes = MAX_HANDLE_SZ;
200 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
201 if (ret < 0 && errno == EOPNOTSUPP) {
202 SKIP(free(handle); close(ns_fd);
203 return, "nsfs doesn't support file handles");
204 }
205 ASSERT_EQ(ret, 0);
206 ASSERT_GT(handle->handle_bytes, 0);
207
208 /* Try to open using FD_NSFS_ROOT */
209 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
210 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
211 SKIP(free(handle); close(ns_fd);
212 return,
213 "open_by_handle_at with FD_NSFS_ROOT not supported");
214 }
215 ASSERT_GE(fd, 0);
216
217 /* Verify we opened the correct namespace */
218 ASSERT_EQ(fstat(ns_fd, &st1), 0);
219 ASSERT_EQ(fstat(fd, &st2), 0);
220 ASSERT_EQ(st1.st_ino, st2.st_ino);
221 ASSERT_EQ(st1.st_dev, st2.st_dev);
222
223 close(fd);
224 close(ns_fd);
225 free(handle);
226 }
227
TEST(nsfs_mnt_handle)228 TEST(nsfs_mnt_handle)
229 {
230 struct file_handle *handle;
231 int mount_id;
232 int ret;
233 int fd;
234 int ns_fd;
235 struct stat st1, st2;
236
237 /* Drop to unprivileged uid/gid */
238 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
239 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
240
241 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
242 ASSERT_NE(handle, NULL);
243
244 /* Open mount namespace file descriptor */
245 ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
246 ASSERT_GE(ns_fd, 0);
247
248 /* Get handle for the namespace */
249 handle->handle_bytes = MAX_HANDLE_SZ;
250 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
251 if (ret < 0 && errno == EOPNOTSUPP) {
252 SKIP(free(handle); close(ns_fd);
253 return, "nsfs doesn't support file handles");
254 }
255 ASSERT_EQ(ret, 0);
256 ASSERT_GT(handle->handle_bytes, 0);
257
258 /* Try to open using FD_NSFS_ROOT */
259 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
260 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
261 SKIP(free(handle); close(ns_fd);
262 return,
263 "open_by_handle_at with FD_NSFS_ROOT not supported");
264 }
265 ASSERT_GE(fd, 0);
266
267 /* Verify we opened the correct namespace */
268 ASSERT_EQ(fstat(ns_fd, &st1), 0);
269 ASSERT_EQ(fstat(fd, &st2), 0);
270 ASSERT_EQ(st1.st_ino, st2.st_ino);
271 ASSERT_EQ(st1.st_dev, st2.st_dev);
272
273 close(fd);
274 close(ns_fd);
275 free(handle);
276 }
277
TEST(nsfs_user_handle)278 TEST(nsfs_user_handle)
279 {
280 struct file_handle *handle;
281 int mount_id;
282 int ret;
283 int fd;
284 int ns_fd;
285 struct stat st1, st2;
286
287 /* Drop to unprivileged uid/gid */
288 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
289 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
290
291 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
292 ASSERT_NE(handle, NULL);
293
294 /* Open user namespace file descriptor */
295 ns_fd = open("/proc/self/ns/user", O_RDONLY);
296 ASSERT_GE(ns_fd, 0);
297
298 /* Get handle for the namespace */
299 handle->handle_bytes = MAX_HANDLE_SZ;
300 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
301 if (ret < 0 && errno == EOPNOTSUPP) {
302 SKIP(free(handle); close(ns_fd);
303 return, "nsfs doesn't support file handles");
304 }
305 ASSERT_EQ(ret, 0);
306 ASSERT_GT(handle->handle_bytes, 0);
307
308 /* Try to open using FD_NSFS_ROOT */
309 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
310 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
311 SKIP(free(handle); close(ns_fd);
312 return,
313 "open_by_handle_at with FD_NSFS_ROOT not supported");
314 }
315 ASSERT_GE(fd, 0);
316
317 /* Verify we opened the correct namespace */
318 ASSERT_EQ(fstat(ns_fd, &st1), 0);
319 ASSERT_EQ(fstat(fd, &st2), 0);
320 ASSERT_EQ(st1.st_ino, st2.st_ino);
321 ASSERT_EQ(st1.st_dev, st2.st_dev);
322
323 close(fd);
324 close(ns_fd);
325 free(handle);
326 }
327
TEST(nsfs_cgroup_handle)328 TEST(nsfs_cgroup_handle)
329 {
330 struct file_handle *handle;
331 int mount_id;
332 int ret;
333 int fd;
334 int ns_fd;
335 struct stat st1, st2;
336
337 /* Drop to unprivileged uid/gid */
338 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
339 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
340
341 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
342 ASSERT_NE(handle, NULL);
343
344 /* Open cgroup namespace file descriptor */
345 ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
346 if (ns_fd < 0) {
347 SKIP(free(handle); return, "cgroup namespace not available");
348 }
349
350 /* Get handle for the namespace */
351 handle->handle_bytes = MAX_HANDLE_SZ;
352 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
353 if (ret < 0 && errno == EOPNOTSUPP) {
354 SKIP(free(handle); close(ns_fd);
355 return, "nsfs doesn't support file handles");
356 }
357 ASSERT_EQ(ret, 0);
358 ASSERT_GT(handle->handle_bytes, 0);
359
360 /* Try to open using FD_NSFS_ROOT */
361 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
362 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
363 SKIP(free(handle); close(ns_fd);
364 return,
365 "open_by_handle_at with FD_NSFS_ROOT not supported");
366 }
367 ASSERT_GE(fd, 0);
368
369 /* Verify we opened the correct namespace */
370 ASSERT_EQ(fstat(ns_fd, &st1), 0);
371 ASSERT_EQ(fstat(fd, &st2), 0);
372 ASSERT_EQ(st1.st_ino, st2.st_ino);
373 ASSERT_EQ(st1.st_dev, st2.st_dev);
374
375 close(fd);
376 close(ns_fd);
377 free(handle);
378 }
379
TEST(nsfs_time_handle)380 TEST(nsfs_time_handle)
381 {
382 struct file_handle *handle;
383 int mount_id;
384 int ret;
385 int fd;
386 int ns_fd;
387 struct stat st1, st2;
388
389 /* Drop to unprivileged uid/gid */
390 ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
391 ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
392
393 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
394 ASSERT_NE(handle, NULL);
395
396 /* Open time namespace file descriptor */
397 ns_fd = open("/proc/self/ns/time", O_RDONLY);
398 if (ns_fd < 0) {
399 SKIP(free(handle); return, "time namespace not available");
400 }
401
402 /* Get handle for the namespace */
403 handle->handle_bytes = MAX_HANDLE_SZ;
404 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
405 if (ret < 0 && errno == EOPNOTSUPP) {
406 SKIP(free(handle); close(ns_fd);
407 return, "nsfs doesn't support file handles");
408 }
409 ASSERT_EQ(ret, 0);
410 ASSERT_GT(handle->handle_bytes, 0);
411
412 /* Try to open using FD_NSFS_ROOT */
413 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
414 if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
415 SKIP(free(handle); close(ns_fd);
416 return,
417 "open_by_handle_at with FD_NSFS_ROOT not supported");
418 }
419 ASSERT_GE(fd, 0);
420
421 /* Verify we opened the correct namespace */
422 ASSERT_EQ(fstat(ns_fd, &st1), 0);
423 ASSERT_EQ(fstat(fd, &st2), 0);
424 ASSERT_EQ(st1.st_ino, st2.st_ino);
425 ASSERT_EQ(st1.st_dev, st2.st_dev);
426
427 close(fd);
428 close(ns_fd);
429 free(handle);
430 }
431
TEST(nsfs_user_net_namespace_isolation)432 TEST(nsfs_user_net_namespace_isolation)
433 {
434 struct file_handle *handle;
435 int mount_id;
436 int ret;
437 int fd;
438 int ns_fd;
439 pid_t pid;
440 int status;
441 int pipefd[2];
442 char result;
443
444 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
445 ASSERT_NE(handle, NULL);
446
447 /* Create pipe for communication */
448 ASSERT_EQ(pipe(pipefd), 0);
449
450 /* Get handle for current network namespace */
451 ns_fd = open("/proc/self/ns/net", O_RDONLY);
452 ASSERT_GE(ns_fd, 0);
453
454 handle->handle_bytes = MAX_HANDLE_SZ;
455 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
456 if (ret < 0 && errno == EOPNOTSUPP) {
457 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
458 close(pipefd[1]);
459 return, "nsfs doesn't support file handles");
460 }
461 ASSERT_EQ(ret, 0);
462 close(ns_fd);
463
464 pid = fork();
465 ASSERT_GE(pid, 0);
466
467 if (pid == 0) {
468 /* Child process */
469 close(pipefd[0]);
470
471 /* First create new user namespace to drop privileges */
472 ret = unshare(CLONE_NEWUSER);
473 if (ret < 0) {
474 write(pipefd[1], "U",
475 1); /* Unable to create user namespace */
476 close(pipefd[1]);
477 exit(0);
478 }
479
480 /* Write uid/gid mappings to maintain some capabilities */
481 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
482 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
483 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
484
485 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
486 write(pipefd[1], "M", 1); /* Unable to set mappings */
487 close(pipefd[1]);
488 exit(0);
489 }
490
491 /* Disable setgroups to allow gid mapping */
492 write(setgroups_fd, "deny", 4);
493 close(setgroups_fd);
494
495 /* Map current uid/gid to root in the new namespace */
496 char mapping[64];
497 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
498 write(uid_map_fd, mapping, strlen(mapping));
499 close(uid_map_fd);
500
501 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
502 write(gid_map_fd, mapping, strlen(mapping));
503 close(gid_map_fd);
504
505 /* Now create new network namespace */
506 ret = unshare(CLONE_NEWNET);
507 if (ret < 0) {
508 write(pipefd[1], "N",
509 1); /* Unable to create network namespace */
510 close(pipefd[1]);
511 exit(0);
512 }
513
514 /* Try to open parent's network namespace handle from new user+net namespace */
515 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
516
517 if (fd >= 0) {
518 /* Should NOT succeed - we're in a different user namespace */
519 write(pipefd[1], "S", 1); /* Unexpected success */
520 close(fd);
521 } else if (errno == ESTALE) {
522 /* Expected: Stale file handle */
523 write(pipefd[1], "P", 1);
524 } else {
525 /* Other error */
526 write(pipefd[1], "F", 1);
527 }
528
529 close(pipefd[1]);
530 exit(0);
531 }
532
533 /* Parent process */
534 close(pipefd[1]);
535 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
536
537 waitpid(pid, &status, 0);
538 ASSERT_TRUE(WIFEXITED(status));
539 ASSERT_EQ(WEXITSTATUS(status), 0);
540
541 if (result == 'U') {
542 SKIP(free(handle); close(pipefd[0]);
543 return, "Cannot create new user namespace");
544 }
545 if (result == 'M') {
546 SKIP(free(handle); close(pipefd[0]);
547 return, "Cannot set uid/gid mappings");
548 }
549 if (result == 'N') {
550 SKIP(free(handle); close(pipefd[0]);
551 return, "Cannot create new network namespace");
552 }
553
554 /* Should fail with permission denied since we're in a different user namespace */
555 ASSERT_EQ(result, 'P');
556
557 close(pipefd[0]);
558 free(handle);
559 }
560
TEST(nsfs_user_uts_namespace_isolation)561 TEST(nsfs_user_uts_namespace_isolation)
562 {
563 struct file_handle *handle;
564 int mount_id;
565 int ret;
566 int fd;
567 int ns_fd;
568 pid_t pid;
569 int status;
570 int pipefd[2];
571 char result;
572
573 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
574 ASSERT_NE(handle, NULL);
575
576 /* Create pipe for communication */
577 ASSERT_EQ(pipe(pipefd), 0);
578
579 /* Get handle for current UTS namespace */
580 ns_fd = open("/proc/self/ns/uts", O_RDONLY);
581 ASSERT_GE(ns_fd, 0);
582
583 handle->handle_bytes = MAX_HANDLE_SZ;
584 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
585 if (ret < 0 && errno == EOPNOTSUPP) {
586 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
587 close(pipefd[1]);
588 return, "nsfs doesn't support file handles");
589 }
590 ASSERT_EQ(ret, 0);
591 close(ns_fd);
592
593 pid = fork();
594 ASSERT_GE(pid, 0);
595
596 if (pid == 0) {
597 /* Child process */
598 close(pipefd[0]);
599
600 /* First create new user namespace to drop privileges */
601 ret = unshare(CLONE_NEWUSER);
602 if (ret < 0) {
603 write(pipefd[1], "U",
604 1); /* Unable to create user namespace */
605 close(pipefd[1]);
606 exit(0);
607 }
608
609 /* Write uid/gid mappings to maintain some capabilities */
610 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
611 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
612 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
613
614 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
615 write(pipefd[1], "M", 1); /* Unable to set mappings */
616 close(pipefd[1]);
617 exit(0);
618 }
619
620 /* Disable setgroups to allow gid mapping */
621 write(setgroups_fd, "deny", 4);
622 close(setgroups_fd);
623
624 /* Map current uid/gid to root in the new namespace */
625 char mapping[64];
626 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
627 write(uid_map_fd, mapping, strlen(mapping));
628 close(uid_map_fd);
629
630 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
631 write(gid_map_fd, mapping, strlen(mapping));
632 close(gid_map_fd);
633
634 /* Now create new UTS namespace */
635 ret = unshare(CLONE_NEWUTS);
636 if (ret < 0) {
637 write(pipefd[1], "N",
638 1); /* Unable to create UTS namespace */
639 close(pipefd[1]);
640 exit(0);
641 }
642
643 /* Try to open parent's UTS namespace handle from new user+uts namespace */
644 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
645
646 if (fd >= 0) {
647 /* Should NOT succeed - we're in a different user namespace */
648 write(pipefd[1], "S", 1); /* Unexpected success */
649 close(fd);
650 } else if (errno == ESTALE) {
651 /* Expected: Stale file handle */
652 write(pipefd[1], "P", 1);
653 } else {
654 /* Other error */
655 write(pipefd[1], "F", 1);
656 }
657
658 close(pipefd[1]);
659 exit(0);
660 }
661
662 /* Parent process */
663 close(pipefd[1]);
664 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
665
666 waitpid(pid, &status, 0);
667 ASSERT_TRUE(WIFEXITED(status));
668 ASSERT_EQ(WEXITSTATUS(status), 0);
669
670 if (result == 'U') {
671 SKIP(free(handle); close(pipefd[0]);
672 return, "Cannot create new user namespace");
673 }
674 if (result == 'M') {
675 SKIP(free(handle); close(pipefd[0]);
676 return, "Cannot set uid/gid mappings");
677 }
678 if (result == 'N') {
679 SKIP(free(handle); close(pipefd[0]);
680 return, "Cannot create new UTS namespace");
681 }
682
683 /* Should fail with ESTALE since we're in a different user namespace */
684 ASSERT_EQ(result, 'P');
685
686 close(pipefd[0]);
687 free(handle);
688 }
689
TEST(nsfs_user_ipc_namespace_isolation)690 TEST(nsfs_user_ipc_namespace_isolation)
691 {
692 struct file_handle *handle;
693 int mount_id;
694 int ret;
695 int fd;
696 int ns_fd;
697 pid_t pid;
698 int status;
699 int pipefd[2];
700 char result;
701
702 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
703 ASSERT_NE(handle, NULL);
704
705 /* Create pipe for communication */
706 ASSERT_EQ(pipe(pipefd), 0);
707
708 /* Get handle for current IPC namespace */
709 ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
710 ASSERT_GE(ns_fd, 0);
711
712 handle->handle_bytes = MAX_HANDLE_SZ;
713 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
714 if (ret < 0 && errno == EOPNOTSUPP) {
715 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
716 close(pipefd[1]);
717 return, "nsfs doesn't support file handles");
718 }
719 ASSERT_EQ(ret, 0);
720 close(ns_fd);
721
722 pid = fork();
723 ASSERT_GE(pid, 0);
724
725 if (pid == 0) {
726 /* Child process */
727 close(pipefd[0]);
728
729 /* First create new user namespace to drop privileges */
730 ret = unshare(CLONE_NEWUSER);
731 if (ret < 0) {
732 write(pipefd[1], "U",
733 1); /* Unable to create user namespace */
734 close(pipefd[1]);
735 exit(0);
736 }
737
738 /* Write uid/gid mappings to maintain some capabilities */
739 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
740 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
741 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
742
743 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
744 write(pipefd[1], "M", 1); /* Unable to set mappings */
745 close(pipefd[1]);
746 exit(0);
747 }
748
749 /* Disable setgroups to allow gid mapping */
750 write(setgroups_fd, "deny", 4);
751 close(setgroups_fd);
752
753 /* Map current uid/gid to root in the new namespace */
754 char mapping[64];
755 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
756 write(uid_map_fd, mapping, strlen(mapping));
757 close(uid_map_fd);
758
759 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
760 write(gid_map_fd, mapping, strlen(mapping));
761 close(gid_map_fd);
762
763 /* Now create new IPC namespace */
764 ret = unshare(CLONE_NEWIPC);
765 if (ret < 0) {
766 write(pipefd[1], "N",
767 1); /* Unable to create IPC namespace */
768 close(pipefd[1]);
769 exit(0);
770 }
771
772 /* Try to open parent's IPC namespace handle from new user+ipc namespace */
773 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
774
775 if (fd >= 0) {
776 /* Should NOT succeed - we're in a different user namespace */
777 write(pipefd[1], "S", 1); /* Unexpected success */
778 close(fd);
779 } else if (errno == ESTALE) {
780 /* Expected: Stale file handle */
781 write(pipefd[1], "P", 1);
782 } else {
783 /* Other error */
784 write(pipefd[1], "F", 1);
785 }
786
787 close(pipefd[1]);
788 exit(0);
789 }
790
791 /* Parent process */
792 close(pipefd[1]);
793 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
794
795 waitpid(pid, &status, 0);
796 ASSERT_TRUE(WIFEXITED(status));
797 ASSERT_EQ(WEXITSTATUS(status), 0);
798
799 if (result == 'U') {
800 SKIP(free(handle); close(pipefd[0]);
801 return, "Cannot create new user namespace");
802 }
803 if (result == 'M') {
804 SKIP(free(handle); close(pipefd[0]);
805 return, "Cannot set uid/gid mappings");
806 }
807 if (result == 'N') {
808 SKIP(free(handle); close(pipefd[0]);
809 return, "Cannot create new IPC namespace");
810 }
811
812 /* Should fail with ESTALE since we're in a different user namespace */
813 ASSERT_EQ(result, 'P');
814
815 close(pipefd[0]);
816 free(handle);
817 }
818
TEST(nsfs_user_mnt_namespace_isolation)819 TEST(nsfs_user_mnt_namespace_isolation)
820 {
821 struct file_handle *handle;
822 int mount_id;
823 int ret;
824 int fd;
825 int ns_fd;
826 pid_t pid;
827 int status;
828 int pipefd[2];
829 char result;
830
831 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
832 ASSERT_NE(handle, NULL);
833
834 /* Create pipe for communication */
835 ASSERT_EQ(pipe(pipefd), 0);
836
837 /* Get handle for current mount namespace */
838 ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
839 ASSERT_GE(ns_fd, 0);
840
841 handle->handle_bytes = MAX_HANDLE_SZ;
842 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
843 if (ret < 0 && errno == EOPNOTSUPP) {
844 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
845 close(pipefd[1]);
846 return, "nsfs doesn't support file handles");
847 }
848 ASSERT_EQ(ret, 0);
849 close(ns_fd);
850
851 pid = fork();
852 ASSERT_GE(pid, 0);
853
854 if (pid == 0) {
855 /* Child process */
856 close(pipefd[0]);
857
858 /* First create new user namespace to drop privileges */
859 ret = unshare(CLONE_NEWUSER);
860 if (ret < 0) {
861 write(pipefd[1], "U",
862 1); /* Unable to create user namespace */
863 close(pipefd[1]);
864 exit(0);
865 }
866
867 /* Write uid/gid mappings to maintain some capabilities */
868 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
869 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
870 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
871
872 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
873 write(pipefd[1], "M", 1); /* Unable to set mappings */
874 close(pipefd[1]);
875 exit(0);
876 }
877
878 /* Disable setgroups to allow gid mapping */
879 write(setgroups_fd, "deny", 4);
880 close(setgroups_fd);
881
882 /* Map current uid/gid to root in the new namespace */
883 char mapping[64];
884 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
885 write(uid_map_fd, mapping, strlen(mapping));
886 close(uid_map_fd);
887
888 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
889 write(gid_map_fd, mapping, strlen(mapping));
890 close(gid_map_fd);
891
892 /* Now create new mount namespace */
893 ret = unshare(CLONE_NEWNS);
894 if (ret < 0) {
895 write(pipefd[1], "N",
896 1); /* Unable to create mount namespace */
897 close(pipefd[1]);
898 exit(0);
899 }
900
901 /* Try to open parent's mount namespace handle from new user+mnt namespace */
902 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
903
904 if (fd >= 0) {
905 /* Should NOT succeed - we're in a different user namespace */
906 write(pipefd[1], "S", 1); /* Unexpected success */
907 close(fd);
908 } else if (errno == ESTALE) {
909 /* Expected: Stale file handle */
910 write(pipefd[1], "P", 1);
911 } else {
912 /* Other error */
913 write(pipefd[1], "F", 1);
914 }
915
916 close(pipefd[1]);
917 exit(0);
918 }
919
920 /* Parent process */
921 close(pipefd[1]);
922 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
923
924 waitpid(pid, &status, 0);
925 ASSERT_TRUE(WIFEXITED(status));
926 ASSERT_EQ(WEXITSTATUS(status), 0);
927
928 if (result == 'U') {
929 SKIP(free(handle); close(pipefd[0]);
930 return, "Cannot create new user namespace");
931 }
932 if (result == 'M') {
933 SKIP(free(handle); close(pipefd[0]);
934 return, "Cannot set uid/gid mappings");
935 }
936 if (result == 'N') {
937 SKIP(free(handle); close(pipefd[0]);
938 return, "Cannot create new mount namespace");
939 }
940
941 /* Should fail with ESTALE since we're in a different user namespace */
942 ASSERT_EQ(result, 'P');
943
944 close(pipefd[0]);
945 free(handle);
946 }
947
TEST(nsfs_user_cgroup_namespace_isolation)948 TEST(nsfs_user_cgroup_namespace_isolation)
949 {
950 struct file_handle *handle;
951 int mount_id;
952 int ret;
953 int fd;
954 int ns_fd;
955 pid_t pid;
956 int status;
957 int pipefd[2];
958 char result;
959
960 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
961 ASSERT_NE(handle, NULL);
962
963 /* Create pipe for communication */
964 ASSERT_EQ(pipe(pipefd), 0);
965
966 /* Get handle for current cgroup namespace */
967 ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
968 if (ns_fd < 0) {
969 SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
970 return, "cgroup namespace not available");
971 }
972
973 handle->handle_bytes = MAX_HANDLE_SZ;
974 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
975 if (ret < 0 && errno == EOPNOTSUPP) {
976 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
977 close(pipefd[1]);
978 return, "nsfs doesn't support file handles");
979 }
980 ASSERT_EQ(ret, 0);
981 close(ns_fd);
982
983 pid = fork();
984 ASSERT_GE(pid, 0);
985
986 if (pid == 0) {
987 /* Child process */
988 close(pipefd[0]);
989
990 /* First create new user namespace to drop privileges */
991 ret = unshare(CLONE_NEWUSER);
992 if (ret < 0) {
993 write(pipefd[1], "U",
994 1); /* Unable to create user namespace */
995 close(pipefd[1]);
996 exit(0);
997 }
998
999 /* Write uid/gid mappings to maintain some capabilities */
1000 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1001 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1002 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1003
1004 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1005 write(pipefd[1], "M", 1); /* Unable to set mappings */
1006 close(pipefd[1]);
1007 exit(0);
1008 }
1009
1010 /* Disable setgroups to allow gid mapping */
1011 write(setgroups_fd, "deny", 4);
1012 close(setgroups_fd);
1013
1014 /* Map current uid/gid to root in the new namespace */
1015 char mapping[64];
1016 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1017 write(uid_map_fd, mapping, strlen(mapping));
1018 close(uid_map_fd);
1019
1020 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1021 write(gid_map_fd, mapping, strlen(mapping));
1022 close(gid_map_fd);
1023
1024 /* Now create new cgroup namespace */
1025 ret = unshare(CLONE_NEWCGROUP);
1026 if (ret < 0) {
1027 write(pipefd[1], "N",
1028 1); /* Unable to create cgroup namespace */
1029 close(pipefd[1]);
1030 exit(0);
1031 }
1032
1033 /* Try to open parent's cgroup namespace handle from new user+cgroup namespace */
1034 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1035
1036 if (fd >= 0) {
1037 /* Should NOT succeed - we're in a different user namespace */
1038 write(pipefd[1], "S", 1); /* Unexpected success */
1039 close(fd);
1040 } else if (errno == ESTALE) {
1041 /* Expected: Stale file handle */
1042 write(pipefd[1], "P", 1);
1043 } else {
1044 /* Other error */
1045 write(pipefd[1], "F", 1);
1046 }
1047
1048 close(pipefd[1]);
1049 exit(0);
1050 }
1051
1052 /* Parent process */
1053 close(pipefd[1]);
1054 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1055
1056 waitpid(pid, &status, 0);
1057 ASSERT_TRUE(WIFEXITED(status));
1058 ASSERT_EQ(WEXITSTATUS(status), 0);
1059
1060 if (result == 'U') {
1061 SKIP(free(handle); close(pipefd[0]);
1062 return, "Cannot create new user namespace");
1063 }
1064 if (result == 'M') {
1065 SKIP(free(handle); close(pipefd[0]);
1066 return, "Cannot set uid/gid mappings");
1067 }
1068 if (result == 'N') {
1069 SKIP(free(handle); close(pipefd[0]);
1070 return, "Cannot create new cgroup namespace");
1071 }
1072
1073 /* Should fail with ESTALE since we're in a different user namespace */
1074 ASSERT_EQ(result, 'P');
1075
1076 close(pipefd[0]);
1077 free(handle);
1078 }
1079
TEST(nsfs_user_pid_namespace_isolation)1080 TEST(nsfs_user_pid_namespace_isolation)
1081 {
1082 struct file_handle *handle;
1083 int mount_id;
1084 int ret;
1085 int fd;
1086 int ns_fd;
1087 pid_t pid;
1088 int status;
1089 int pipefd[2];
1090 char result;
1091
1092 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1093 ASSERT_NE(handle, NULL);
1094
1095 /* Create pipe for communication */
1096 ASSERT_EQ(pipe(pipefd), 0);
1097
1098 /* Get handle for current PID namespace */
1099 ns_fd = open("/proc/self/ns/pid", O_RDONLY);
1100 ASSERT_GE(ns_fd, 0);
1101
1102 handle->handle_bytes = MAX_HANDLE_SZ;
1103 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1104 if (ret < 0 && errno == EOPNOTSUPP) {
1105 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1106 close(pipefd[1]);
1107 return, "nsfs doesn't support file handles");
1108 }
1109 ASSERT_EQ(ret, 0);
1110 close(ns_fd);
1111
1112 pid = fork();
1113 ASSERT_GE(pid, 0);
1114
1115 if (pid == 0) {
1116 /* Child process */
1117 close(pipefd[0]);
1118
1119 /* First create new user namespace to drop privileges */
1120 ret = unshare(CLONE_NEWUSER);
1121 if (ret < 0) {
1122 write(pipefd[1], "U",
1123 1); /* Unable to create user namespace */
1124 close(pipefd[1]);
1125 exit(0);
1126 }
1127
1128 /* Write uid/gid mappings to maintain some capabilities */
1129 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1130 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1131 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1132
1133 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1134 write(pipefd[1], "M", 1); /* Unable to set mappings */
1135 close(pipefd[1]);
1136 exit(0);
1137 }
1138
1139 /* Disable setgroups to allow gid mapping */
1140 write(setgroups_fd, "deny", 4);
1141 close(setgroups_fd);
1142
1143 /* Map current uid/gid to root in the new namespace */
1144 char mapping[64];
1145 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1146 write(uid_map_fd, mapping, strlen(mapping));
1147 close(uid_map_fd);
1148
1149 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1150 write(gid_map_fd, mapping, strlen(mapping));
1151 close(gid_map_fd);
1152
1153 /* Now create new PID namespace - requires fork to take effect */
1154 ret = unshare(CLONE_NEWPID);
1155 if (ret < 0) {
1156 write(pipefd[1], "N",
1157 1); /* Unable to create PID namespace */
1158 close(pipefd[1]);
1159 exit(0);
1160 }
1161
1162 /* Fork again for PID namespace to take effect */
1163 pid_t child_pid = fork();
1164 if (child_pid < 0) {
1165 write(pipefd[1], "N",
1166 1); /* Unable to fork in PID namespace */
1167 close(pipefd[1]);
1168 exit(0);
1169 }
1170
1171 if (child_pid == 0) {
1172 /* Grandchild in new PID namespace */
1173 /* Try to open parent's PID namespace handle from new user+pid namespace */
1174 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1175
1176 if (fd >= 0) {
1177 /* Should NOT succeed - we're in a different user namespace */
1178 write(pipefd[1], "S",
1179 1); /* Unexpected success */
1180 close(fd);
1181 } else if (errno == ESTALE) {
1182 /* Expected: Stale file handle */
1183 write(pipefd[1], "P", 1);
1184 } else {
1185 /* Other error */
1186 write(pipefd[1], "F", 1);
1187 }
1188
1189 close(pipefd[1]);
1190 exit(0);
1191 }
1192
1193 /* Wait for grandchild */
1194 waitpid(child_pid, NULL, 0);
1195 exit(0);
1196 }
1197
1198 /* Parent process */
1199 close(pipefd[1]);
1200 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1201
1202 waitpid(pid, &status, 0);
1203 ASSERT_TRUE(WIFEXITED(status));
1204 ASSERT_EQ(WEXITSTATUS(status), 0);
1205
1206 if (result == 'U') {
1207 SKIP(free(handle); close(pipefd[0]);
1208 return, "Cannot create new user namespace");
1209 }
1210 if (result == 'M') {
1211 SKIP(free(handle); close(pipefd[0]);
1212 return, "Cannot set uid/gid mappings");
1213 }
1214 if (result == 'N') {
1215 SKIP(free(handle); close(pipefd[0]);
1216 return, "Cannot create new PID namespace");
1217 }
1218
1219 /* Should fail with ESTALE since we're in a different user namespace */
1220 ASSERT_EQ(result, 'P');
1221
1222 close(pipefd[0]);
1223 free(handle);
1224 }
1225
TEST(nsfs_user_time_namespace_isolation)1226 TEST(nsfs_user_time_namespace_isolation)
1227 {
1228 struct file_handle *handle;
1229 int mount_id;
1230 int ret;
1231 int fd;
1232 int ns_fd;
1233 pid_t pid;
1234 int status;
1235 int pipefd[2];
1236 char result;
1237
1238 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1239 ASSERT_NE(handle, NULL);
1240
1241 /* Create pipe for communication */
1242 ASSERT_EQ(pipe(pipefd), 0);
1243
1244 /* Get handle for current time namespace */
1245 ns_fd = open("/proc/self/ns/time", O_RDONLY);
1246 if (ns_fd < 0) {
1247 SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
1248 return, "time namespace not available");
1249 }
1250
1251 handle->handle_bytes = MAX_HANDLE_SZ;
1252 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1253 if (ret < 0 && errno == EOPNOTSUPP) {
1254 SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1255 close(pipefd[1]);
1256 return, "nsfs doesn't support file handles");
1257 }
1258 ASSERT_EQ(ret, 0);
1259 close(ns_fd);
1260
1261 pid = fork();
1262 ASSERT_GE(pid, 0);
1263
1264 if (pid == 0) {
1265 /* Child process */
1266 close(pipefd[0]);
1267
1268 /* First create new user namespace to drop privileges */
1269 ret = unshare(CLONE_NEWUSER);
1270 if (ret < 0) {
1271 write(pipefd[1], "U",
1272 1); /* Unable to create user namespace */
1273 close(pipefd[1]);
1274 exit(0);
1275 }
1276
1277 /* Write uid/gid mappings to maintain some capabilities */
1278 int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1279 int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1280 int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1281
1282 if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1283 write(pipefd[1], "M", 1); /* Unable to set mappings */
1284 close(pipefd[1]);
1285 exit(0);
1286 }
1287
1288 /* Disable setgroups to allow gid mapping */
1289 write(setgroups_fd, "deny", 4);
1290 close(setgroups_fd);
1291
1292 /* Map current uid/gid to root in the new namespace */
1293 char mapping[64];
1294 snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1295 write(uid_map_fd, mapping, strlen(mapping));
1296 close(uid_map_fd);
1297
1298 snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1299 write(gid_map_fd, mapping, strlen(mapping));
1300 close(gid_map_fd);
1301
1302 /* Now create new time namespace - requires fork to take effect */
1303 ret = unshare(CLONE_NEWTIME);
1304 if (ret < 0) {
1305 write(pipefd[1], "N",
1306 1); /* Unable to create time namespace */
1307 close(pipefd[1]);
1308 exit(0);
1309 }
1310
1311 /* Fork again for time namespace to take effect */
1312 pid_t child_pid = fork();
1313 if (child_pid < 0) {
1314 write(pipefd[1], "N",
1315 1); /* Unable to fork in time namespace */
1316 close(pipefd[1]);
1317 exit(0);
1318 }
1319
1320 if (child_pid == 0) {
1321 /* Grandchild in new time namespace */
1322 /* Try to open parent's time namespace handle from new user+time namespace */
1323 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1324
1325 if (fd >= 0) {
1326 /* Should NOT succeed - we're in a different user namespace */
1327 write(pipefd[1], "S",
1328 1); /* Unexpected success */
1329 close(fd);
1330 } else if (errno == ESTALE) {
1331 /* Expected: Stale file handle */
1332 write(pipefd[1], "P", 1);
1333 } else {
1334 /* Other error */
1335 write(pipefd[1], "F", 1);
1336 }
1337
1338 close(pipefd[1]);
1339 exit(0);
1340 }
1341
1342 /* Wait for grandchild */
1343 waitpid(child_pid, NULL, 0);
1344 exit(0);
1345 }
1346
1347 /* Parent process */
1348 close(pipefd[1]);
1349 ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1350
1351 waitpid(pid, &status, 0);
1352 ASSERT_TRUE(WIFEXITED(status));
1353 ASSERT_EQ(WEXITSTATUS(status), 0);
1354
1355 if (result == 'U') {
1356 SKIP(free(handle); close(pipefd[0]);
1357 return, "Cannot create new user namespace");
1358 }
1359 if (result == 'M') {
1360 SKIP(free(handle); close(pipefd[0]);
1361 return, "Cannot set uid/gid mappings");
1362 }
1363 if (result == 'N') {
1364 SKIP(free(handle); close(pipefd[0]);
1365 return, "Cannot create new time namespace");
1366 }
1367
1368 /* Should fail with ESTALE since we're in a different user namespace */
1369 ASSERT_EQ(result, 'P');
1370
1371 close(pipefd[0]);
1372 free(handle);
1373 }
1374
TEST(nsfs_open_flags)1375 TEST(nsfs_open_flags)
1376 {
1377 struct file_handle *handle;
1378 int mount_id;
1379 int ret;
1380 int fd;
1381 int ns_fd;
1382
1383 handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1384 ASSERT_NE(handle, NULL);
1385
1386 /* Open a namespace file descriptor */
1387 ns_fd = open("/proc/self/ns/net", O_RDONLY);
1388 ASSERT_GE(ns_fd, 0);
1389
1390 /* Get handle for the namespace */
1391 handle->handle_bytes = MAX_HANDLE_SZ;
1392 ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1393 if (ret < 0 && errno == EOPNOTSUPP) {
1394 SKIP(free(handle); close(ns_fd);
1395 return, "nsfs doesn't support file handles");
1396 }
1397 ASSERT_EQ(ret, 0);
1398 ASSERT_GT(handle->handle_bytes, 0);
1399
1400 /* Test invalid flags that should fail */
1401 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_WRONLY);
1402 ASSERT_LT(fd, 0);
1403 ASSERT_EQ(errno, EPERM);
1404
1405 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDWR);
1406 ASSERT_LT(fd, 0);
1407 ASSERT_EQ(errno, EPERM);
1408
1409 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TRUNC);
1410 ASSERT_LT(fd, 0);
1411 ASSERT_EQ(errno, EPERM);
1412
1413 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECT);
1414 ASSERT_LT(fd, 0);
1415 ASSERT_EQ(errno, EINVAL);
1416
1417 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TMPFILE);
1418 ASSERT_LT(fd, 0);
1419 ASSERT_EQ(errno, EINVAL);
1420
1421 fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECTORY);
1422 ASSERT_LT(fd, 0);
1423 ASSERT_EQ(errno, ENOTDIR);
1424
1425 close(ns_fd);
1426 free(handle);
1427 }
1428
1429 TEST_HARNESS_MAIN
1430