The Pedigree Project 0.1
uts-namespace-contract-test/admission.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <sched.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/fsuid.h>
9#include <sys/mman.h>
10
11static int privileged_failures(void) {
12 int failed = 0, saved = -1, regular = -1;
13 struct ns_identity original, current;
14 CHECK(ns_set("admission", "admission-domain") == 0);
15 saved = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
16 CHECK(saved >= 0 && ns_fd_identity(saved, &original) == 0);
17 regular = memfd_create("namespace-admission", MFD_CLOEXEC);
18 CHECK(regular >= 0);
19 errno = 0;
20 CHECK(unshare(1) == -1 && errno == EINVAL);
21 errno = 0;
22 CHECK(unshare(CLONE_NEWNS) == -1 && errno == EOPNOTSUPP);
23 errno = 0;
24 CHECK(unshare(CLONE_NEWUTS | CLONE_NEWNS) == -1 && errno == EOPNOTSUPP);
25 errno = 0;
26 CHECK(setns(-1, CLONE_NEWUTS) == -1 && errno == EBADF);
27 errno = 0;
28 CHECK(setns(regular, 0) == -1 && errno == EINVAL);
29 errno = 0;
30 CHECK(setns(saved, CLONE_NEWNS) == -1 && errno == EINVAL);
31 errno = 0;
32 CHECK(setns(saved, CLONE_NEWUTS | CLONE_NEWNS) == -1 && errno == EINVAL);
33 errno = 0;
34 CHECK(setns(saved, 1) == -1 && errno == EINVAL);
35 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &current) == 0 && ns_same(original, current));
36 CHECK(ns_expect("admission", "admission-domain") == 0);
37 CHECK(setns(saved, 0) == 0 && setns(saved, CLONE_NEWUTS) == 0);
38 char byte = '?';
39 CHECK(read(saved, &byte, 1) == -1);
40 CHECK(write(saved, &byte, 1) == -1);
41 void* invalid = mmap(NULL, ns_page, PROT_READ, MAP_SHARED, saved, 0);
42 if (invalid != MAP_FAILED)
43 munmap(invalid, ns_page);
44 CHECK(invalid == MAP_FAILED);
45 CHECK(ns_expect("admission", "admission-domain") == 0);
46out:
47 if (saved >= 0)
48 close(saved);
49 if (regular >= 0)
50 close(regular);
51 return failed;
52}
53
54static int unprivileged(int command, int report, void* argument) {
55 (void)command;
56 (void)report;
57 int failed = 0, saved = *(int*)argument;
58 struct ns_identity before, after;
59 CHECK(ns_fd_identity(saved, &before) == 0);
60 CHECK(setresuid(0, 1001, 0) == 0);
61 setfsuid(0);
62 CHECK(geteuid() == 1001 && setfsuid((uid_t)-1) == 0);
63 CHECK(unshare(0) == 0);
64 errno = 0;
65 CHECK(unshare(CLONE_NEWUTS) == -1 && errno == EPERM);
66 errno = 0;
67 CHECK(setns(saved, 0) == -1 && errno == EPERM);
68 errno = 0;
69 CHECK(sethostname("denied", 6) == -1 && errno == EPERM);
70 errno = 0;
71 CHECK(setdomainname("denied", 6) == -1 && errno == EPERM);
72 errno = 0;
73 CHECK(sethostname(NULL, 65) == -1 && errno == EPERM);
74 errno = 0;
75 CHECK(setdomainname(NULL, 0) == -1 && errno == EPERM);
76 errno = 0;
77 CHECK(setns(-1, CLONE_NEWUTS) == -1 && errno == EBADF);
78 errno = 0;
79 CHECK(setns(saved, CLONE_NEWNS) == -1 && errno == EINVAL);
80 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &after) == 0 && ns_same(before, after));
81 CHECK(ns_expect("admission", "admission-domain") == 0);
82out:
83 return failed;
84}
85
86static int administrative_credentials(void) {
87 int failed = 0, saved = -1;
88 struct ns_peer child = NS_PEER_INITIALIZER;
89 saved = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
90 CHECK(saved >= 0);
91 CHECK(ns_spawn(&child, unprivileged, &saved) == 0 && ns_join(&child) == 0);
92 CHECK(ns_expect("admission", "admission-domain") == 0);
93 setfsuid(1001);
94 setfsgid(1002);
95 CHECK(geteuid() == 0 && setfsuid((uid_t)-1) == 1001);
96 CHECK(unshare(CLONE_NEWUTS) == 0 && ns_set("admin-fs-override", "admin-fs-override") == 0);
97 CHECK(setns(saved, 0) == 0 && ns_expect("admission", "admission-domain") == 0);
98out:
99 setfsuid(0);
100 setfsgid(0);
101 ns_cleanup(&child);
102 if (saved >= 0)
103 close(saved);
104 return failed;
105}
106
107static int namespace_capacity(void) {
108 int failed = 0, original = -1, held[300], count = 0, exhausted = 0;
109 struct ns_identity before, after;
110 original = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
111 CHECK(original >= 0);
112 while (count < (int)(sizeof(held) / sizeof(held[0]))) {
113 int fd = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
114 CHECK(fd >= 0);
115 held[count++] = fd;
116 CHECK(ns_fd_identity(fd, &before) == 0);
117 errno = 0;
118 if (unshare(CLONE_NEWUTS)) {
119 CHECK(errno == ENOSPC);
120 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &after) == 0 && ns_same(before, after));
121 exhausted = 1;
122 break;
123 }
124 }
125 CHECK(exhausted && count > 2);
126 /* This namespace has no membership or alias left once its descriptor closes. */
127 CHECK(close(held[1]) == 0);
128 held[1] = -1;
129 CHECK(unshare(CLONE_NEWUTS) == 0);
130 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &after) == 0 && !ns_same(before, after));
131out:
132 if (original >= 0) {
133 if (setns(original, 0))
134 failed = 1;
135 close(original);
136 }
137 for (int i = 0; i < count; ++i)
138 if (held[i] >= 0)
139 close(held[i]);
140 return failed;
141}
142
143int ns_admission(void) {
144 return privileged_failures() || administrative_credentials() || namespace_capacity();
145}