]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
f6281133 | 2 | |
d7e454ba | 3 | #include <poll.h> |
469830d1 | 4 | #include <sched.h> |
f6281133 LP |
5 | #include <stdlib.h> |
6 | #include <sys/eventfd.h> | |
469830d1 | 7 | #include <sys/mman.h> |
78e864e5 | 8 | #include <sys/personality.h> |
2a65bd94 ZJS |
9 | #include <sys/shm.h> |
10 | #include <sys/types.h> | |
11 | #include <unistd.h> | |
f6281133 | 12 | |
add00535 | 13 | #include "alloc-util.h" |
f6281133 LP |
14 | #include "fd-util.h" |
15 | #include "macro.h" | |
add00535 LP |
16 | #include "missing.h" |
17 | #include "nsflags.h" | |
f6281133 | 18 | #include "process-util.h" |
add00535 | 19 | #include "raw-clone.h" |
f6281133 | 20 | #include "seccomp-util.h" |
469830d1 | 21 | #include "set.h" |
aa34055f ZJS |
22 | #include "string-util.h" |
23 | #include "util.h" | |
469830d1 | 24 | #include "virt.h" |
f6281133 | 25 | |
da1921a5 ZJS |
26 | #if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__) |
27 | /* On these archs, socket() is implemented via the socketcall() syscall multiplexer, | |
28 | * and we can't restrict it hence via seccomp. */ | |
29 | # define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 1 | |
30 | #else | |
31 | # define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 0 | |
32 | #endif | |
33 | ||
f6281133 LP |
34 | static void test_seccomp_arch_to_string(void) { |
35 | uint32_t a, b; | |
36 | const char *name; | |
37 | ||
38 | a = seccomp_arch_native(); | |
39 | assert_se(a > 0); | |
40 | name = seccomp_arch_to_string(a); | |
41 | assert_se(name); | |
42 | assert_se(seccomp_arch_from_string(name, &b) >= 0); | |
43 | assert_se(a == b); | |
44 | } | |
45 | ||
aa34055f ZJS |
46 | static void test_architecture_table(void) { |
47 | const char *n, *n2; | |
48 | ||
49 | NULSTR_FOREACH(n, | |
50 | "native\0" | |
51 | "x86\0" | |
52 | "x86-64\0" | |
53 | "x32\0" | |
54 | "arm\0" | |
55 | "arm64\0" | |
56 | "mips\0" | |
57 | "mips64\0" | |
58 | "mips64-n32\0" | |
59 | "mips-le\0" | |
60 | "mips64-le\0" | |
61 | "mips64-le-n32\0" | |
62 | "ppc\0" | |
63 | "ppc64\0" | |
64 | "ppc64-le\0" | |
65 | "s390\0" | |
66 | "s390x\0") { | |
67 | uint32_t c; | |
68 | ||
69 | assert_se(seccomp_arch_from_string(n, &c) >= 0); | |
70 | n2 = seccomp_arch_to_string(c); | |
71 | log_info("seccomp-arch: %s → 0x%"PRIx32" → %s", n, c, n2); | |
72 | assert_se(streq_ptr(n, n2)); | |
73 | } | |
74 | } | |
75 | ||
f6281133 LP |
76 | static void test_syscall_filter_set_find(void) { |
77 | assert_se(!syscall_filter_set_find(NULL)); | |
78 | assert_se(!syscall_filter_set_find("")); | |
79 | assert_se(!syscall_filter_set_find("quux")); | |
80 | assert_se(!syscall_filter_set_find("@quux")); | |
81 | ||
82 | assert_se(syscall_filter_set_find("@clock") == syscall_filter_sets + SYSCALL_FILTER_SET_CLOCK); | |
83 | assert_se(syscall_filter_set_find("@default") == syscall_filter_sets + SYSCALL_FILTER_SET_DEFAULT); | |
84 | assert_se(syscall_filter_set_find("@raw-io") == syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO); | |
85 | } | |
86 | ||
87 | static void test_filter_sets(void) { | |
88 | unsigned i; | |
89 | int r; | |
90 | ||
91 | if (!is_seccomp_available()) | |
92 | return; | |
f6281133 LP |
93 | if (geteuid() != 0) |
94 | return; | |
95 | ||
96 | for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { | |
97 | pid_t pid; | |
98 | ||
99 | log_info("Testing %s", syscall_filter_sets[i].name); | |
100 | ||
101 | pid = fork(); | |
102 | assert_se(pid >= 0); | |
103 | ||
104 | if (pid == 0) { /* Child? */ | |
105 | int fd; | |
106 | ||
70526841 LP |
107 | /* if we look at the default set (or one that includes it), whitelist instead of blacklist */ |
108 | if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE)) | |
469830d1 | 109 | r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW); |
f6281133 | 110 | else |
469830d1 | 111 | r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN)); |
f6281133 LP |
112 | if (r < 0) |
113 | _exit(EXIT_FAILURE); | |
114 | ||
115 | /* Test the sycall filter with one random system call */ | |
116 | fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); | |
117 | if (IN_SET(i, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_DEFAULT)) | |
469830d1 | 118 | assert_se(fd < 0 && errno == EUCLEAN); |
f6281133 LP |
119 | else { |
120 | assert_se(fd >= 0); | |
121 | safe_close(fd); | |
122 | } | |
123 | ||
124 | _exit(EXIT_SUCCESS); | |
125 | } | |
126 | ||
7d4904fe | 127 | assert_se(wait_for_terminate_and_check(syscall_filter_sets[i].name, pid, WAIT_LOG) == EXIT_SUCCESS); |
f6281133 LP |
128 | } |
129 | } | |
130 | ||
add00535 | 131 | static void test_restrict_namespace(void) { |
86c2a9f1 | 132 | char *s = NULL; |
add00535 | 133 | unsigned long ul; |
469830d1 | 134 | pid_t pid; |
add00535 | 135 | |
86c2a9f1 YW |
136 | assert_se(namespace_flags_to_string(0, &s) == 0 && streq(s, "")); |
137 | s = mfree(s); | |
138 | assert_se(namespace_flags_to_string(CLONE_NEWNS, &s) == 0 && streq(s, "mnt")); | |
139 | s = mfree(s); | |
140 | assert_se(namespace_flags_to_string(CLONE_NEWNS|CLONE_NEWIPC, &s) == 0 && streq(s, "ipc mnt")); | |
141 | s = mfree(s); | |
142 | assert_se(namespace_flags_to_string(CLONE_NEWCGROUP, &s) == 0 && streq(s, "cgroup")); | |
143 | s = mfree(s); | |
144 | ||
145 | assert_se(namespace_flags_from_string("mnt", &ul) == 0 && ul == CLONE_NEWNS); | |
146 | assert_se(namespace_flags_from_string(NULL, &ul) == 0 && ul == 0); | |
147 | assert_se(namespace_flags_from_string("", &ul) == 0 && ul == 0); | |
148 | assert_se(namespace_flags_from_string("uts", &ul) == 0 && ul == CLONE_NEWUTS); | |
149 | assert_se(namespace_flags_from_string("mnt uts ipc", &ul) == 0 && ul == (CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC)); | |
150 | ||
151 | assert_se(namespace_flags_to_string(CLONE_NEWUTS, &s) == 0 && streq(s, "uts")); | |
152 | assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == CLONE_NEWUTS); | |
153 | s = mfree(s); | |
154 | assert_se(namespace_flags_from_string("ipc", &ul) == 0 && ul == CLONE_NEWIPC); | |
155 | assert_se(namespace_flags_to_string(ul, &s) == 0 && streq(s, "ipc")); | |
156 | s = mfree(s); | |
157 | ||
158 | assert_se(namespace_flags_to_string(NAMESPACE_FLAGS_ALL, &s) == 0); | |
add00535 | 159 | assert_se(streq(s, "cgroup ipc net mnt pid user uts")); |
86c2a9f1 YW |
160 | assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL); |
161 | s = mfree(s); | |
add00535 LP |
162 | |
163 | if (!is_seccomp_available()) | |
164 | return; | |
add00535 LP |
165 | if (geteuid() != 0) |
166 | return; | |
167 | ||
168 | pid = fork(); | |
169 | assert_se(pid >= 0); | |
170 | ||
171 | if (pid == 0) { | |
172 | ||
173 | assert_se(seccomp_restrict_namespaces(CLONE_NEWNS|CLONE_NEWNET) >= 0); | |
174 | ||
175 | assert_se(unshare(CLONE_NEWNS) == 0); | |
176 | assert_se(unshare(CLONE_NEWNET) == 0); | |
177 | assert_se(unshare(CLONE_NEWUTS) == -1); | |
178 | assert_se(errno == EPERM); | |
179 | assert_se(unshare(CLONE_NEWIPC) == -1); | |
180 | assert_se(errno == EPERM); | |
181 | assert_se(unshare(CLONE_NEWNET|CLONE_NEWUTS) == -1); | |
182 | assert_se(errno == EPERM); | |
183 | ||
184 | /* We use fd 0 (stdin) here, which of course will fail with EINVAL on setns(). Except of course our | |
185 | * seccomp filter worked, and hits first and makes it return EPERM */ | |
186 | assert_se(setns(0, CLONE_NEWNS) == -1); | |
187 | assert_se(errno == EINVAL); | |
188 | assert_se(setns(0, CLONE_NEWNET) == -1); | |
189 | assert_se(errno == EINVAL); | |
190 | assert_se(setns(0, CLONE_NEWUTS) == -1); | |
191 | assert_se(errno == EPERM); | |
192 | assert_se(setns(0, CLONE_NEWIPC) == -1); | |
193 | assert_se(errno == EPERM); | |
194 | assert_se(setns(0, CLONE_NEWNET|CLONE_NEWUTS) == -1); | |
195 | assert_se(errno == EPERM); | |
196 | assert_se(setns(0, 0) == -1); | |
197 | assert_se(errno == EPERM); | |
198 | ||
199 | pid = raw_clone(CLONE_NEWNS); | |
200 | assert_se(pid >= 0); | |
201 | if (pid == 0) | |
202 | _exit(EXIT_SUCCESS); | |
203 | pid = raw_clone(CLONE_NEWNET); | |
204 | assert_se(pid >= 0); | |
205 | if (pid == 0) | |
206 | _exit(EXIT_SUCCESS); | |
207 | pid = raw_clone(CLONE_NEWUTS); | |
208 | assert_se(pid < 0); | |
209 | assert_se(errno == EPERM); | |
210 | pid = raw_clone(CLONE_NEWIPC); | |
211 | assert_se(pid < 0); | |
212 | assert_se(errno == EPERM); | |
213 | pid = raw_clone(CLONE_NEWNET|CLONE_NEWUTS); | |
214 | assert_se(pid < 0); | |
215 | assert_se(errno == EPERM); | |
216 | ||
217 | _exit(EXIT_SUCCESS); | |
218 | } | |
219 | ||
7d4904fe | 220 | assert_se(wait_for_terminate_and_check("nsseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
add00535 LP |
221 | } |
222 | ||
469830d1 LP |
223 | static void test_protect_sysctl(void) { |
224 | pid_t pid; | |
225 | ||
226 | if (!is_seccomp_available()) | |
227 | return; | |
228 | if (geteuid() != 0) | |
229 | return; | |
230 | ||
231 | if (detect_container() > 0) /* in containers _sysctl() is likely missing anyway */ | |
232 | return; | |
233 | ||
234 | pid = fork(); | |
235 | assert_se(pid >= 0); | |
236 | ||
237 | if (pid == 0) { | |
2e64e8f4 | 238 | #if __NR__sysctl > 0 |
469830d1 LP |
239 | assert_se(syscall(__NR__sysctl, NULL) < 0); |
240 | assert_se(errno == EFAULT); | |
2e64e8f4 | 241 | #endif |
469830d1 LP |
242 | |
243 | assert_se(seccomp_protect_sysctl() >= 0); | |
244 | ||
2e64e8f4 | 245 | #if __NR__sysctl > 0 |
469830d1 LP |
246 | assert_se(syscall(__NR__sysctl, 0, 0, 0) < 0); |
247 | assert_se(errno == EPERM); | |
2e64e8f4 | 248 | #endif |
469830d1 LP |
249 | |
250 | _exit(EXIT_SUCCESS); | |
251 | } | |
252 | ||
7d4904fe | 253 | assert_se(wait_for_terminate_and_check("sysctlseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
254 | } |
255 | ||
256 | static void test_restrict_address_families(void) { | |
257 | pid_t pid; | |
258 | ||
259 | if (!is_seccomp_available()) | |
260 | return; | |
261 | if (geteuid() != 0) | |
262 | return; | |
263 | ||
264 | pid = fork(); | |
265 | assert_se(pid >= 0); | |
266 | ||
267 | if (pid == 0) { | |
268 | int fd; | |
269 | Set *s; | |
270 | ||
271 | fd = socket(AF_INET, SOCK_DGRAM, 0); | |
272 | assert_se(fd >= 0); | |
273 | safe_close(fd); | |
274 | ||
275 | fd = socket(AF_UNIX, SOCK_DGRAM, 0); | |
276 | assert_se(fd >= 0); | |
277 | safe_close(fd); | |
278 | ||
279 | fd = socket(AF_NETLINK, SOCK_DGRAM, 0); | |
280 | assert_se(fd >= 0); | |
281 | safe_close(fd); | |
282 | ||
283 | assert_se(s = set_new(NULL)); | |
284 | assert_se(set_put(s, INT_TO_PTR(AF_UNIX)) >= 0); | |
285 | ||
286 | assert_se(seccomp_restrict_address_families(s, false) >= 0); | |
287 | ||
288 | fd = socket(AF_INET, SOCK_DGRAM, 0); | |
289 | assert_se(fd >= 0); | |
290 | safe_close(fd); | |
291 | ||
ad8f1479 | 292 | fd = socket(AF_UNIX, SOCK_DGRAM, 0); |
dce0e620 | 293 | #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN |
ad8f1479 LP |
294 | assert_se(fd >= 0); |
295 | safe_close(fd); | |
296 | #else | |
dce0e620 | 297 | assert_se(fd < 0); |
469830d1 | 298 | assert_se(errno == EAFNOSUPPORT); |
ad8f1479 | 299 | #endif |
469830d1 LP |
300 | |
301 | fd = socket(AF_NETLINK, SOCK_DGRAM, 0); | |
302 | assert_se(fd >= 0); | |
303 | safe_close(fd); | |
304 | ||
305 | set_clear(s); | |
306 | ||
307 | assert_se(set_put(s, INT_TO_PTR(AF_INET)) >= 0); | |
308 | ||
309 | assert_se(seccomp_restrict_address_families(s, true) >= 0); | |
310 | ||
311 | fd = socket(AF_INET, SOCK_DGRAM, 0); | |
312 | assert_se(fd >= 0); | |
313 | safe_close(fd); | |
314 | ||
ad8f1479 | 315 | fd = socket(AF_UNIX, SOCK_DGRAM, 0); |
dce0e620 | 316 | #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN |
ad8f1479 LP |
317 | assert_se(fd >= 0); |
318 | safe_close(fd); | |
dce0e620 ZJS |
319 | #else |
320 | assert_se(fd < 0); | |
321 | assert_se(errno == EAFNOSUPPORT); | |
322 | #endif | |
ad8f1479 LP |
323 | |
324 | fd = socket(AF_NETLINK, SOCK_DGRAM, 0); | |
dce0e620 | 325 | #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN |
ad8f1479 LP |
326 | assert_se(fd >= 0); |
327 | safe_close(fd); | |
328 | #else | |
dce0e620 | 329 | assert_se(fd < 0); |
469830d1 | 330 | assert_se(errno == EAFNOSUPPORT); |
ad8f1479 | 331 | #endif |
469830d1 LP |
332 | |
333 | _exit(EXIT_SUCCESS); | |
334 | } | |
335 | ||
7d4904fe | 336 | assert_se(wait_for_terminate_and_check("socketseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
337 | } |
338 | ||
339 | static void test_restrict_realtime(void) { | |
340 | pid_t pid; | |
341 | ||
342 | if (!is_seccomp_available()) | |
343 | return; | |
344 | if (geteuid() != 0) | |
345 | return; | |
346 | ||
347 | if (detect_container() > 0) /* in containers RT privs are likely missing anyway */ | |
348 | return; | |
349 | ||
350 | pid = fork(); | |
351 | assert_se(pid >= 0); | |
352 | ||
353 | if (pid == 0) { | |
354 | assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) >= 0); | |
355 | assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) >= 0); | |
356 | assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0); | |
357 | assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0); | |
358 | assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0); | |
359 | ||
360 | assert_se(seccomp_restrict_realtime() >= 0); | |
361 | ||
362 | assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0); | |
363 | assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0); | |
364 | assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0); | |
365 | ||
366 | assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) < 0); | |
367 | assert_se(errno == EPERM); | |
368 | assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) < 0); | |
369 | assert_se(errno == EPERM); | |
370 | ||
371 | _exit(EXIT_SUCCESS); | |
372 | } | |
373 | ||
7d4904fe | 374 | assert_se(wait_for_terminate_and_check("realtimeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
375 | } |
376 | ||
2a65bd94 | 377 | static void test_memory_deny_write_execute_mmap(void) { |
469830d1 LP |
378 | pid_t pid; |
379 | ||
380 | if (!is_seccomp_available()) | |
381 | return; | |
382 | if (geteuid() != 0) | |
383 | return; | |
384 | ||
385 | pid = fork(); | |
386 | assert_se(pid >= 0); | |
387 | ||
388 | if (pid == 0) { | |
389 | void *p; | |
390 | ||
391 | p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); | |
392 | assert_se(p != MAP_FAILED); | |
393 | assert_se(munmap(p, page_size()) >= 0); | |
394 | ||
8a50cf69 LP |
395 | p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); |
396 | assert_se(p != MAP_FAILED); | |
397 | assert_se(munmap(p, page_size()) >= 0); | |
469830d1 | 398 | |
8a50cf69 LP |
399 | assert_se(seccomp_memory_deny_write_execute() >= 0); |
400 | ||
8a50cf69 | 401 | p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); |
4278d1f5 | 402 | #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) |
469830d1 LP |
403 | assert_se(p == MAP_FAILED); |
404 | assert_se(errno == EPERM); | |
2a65bd94 ZJS |
405 | #else /* unknown architectures */ |
406 | assert_se(p != MAP_FAILED); | |
407 | assert_se(munmap(p, page_size()) >= 0); | |
8a50cf69 | 408 | #endif |
469830d1 LP |
409 | |
410 | p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); | |
411 | assert_se(p != MAP_FAILED); | |
412 | assert_se(munmap(p, page_size()) >= 0); | |
413 | ||
414 | _exit(EXIT_SUCCESS); | |
415 | } | |
416 | ||
7d4904fe | 417 | assert_se(wait_for_terminate_and_check("memoryseccomp-mmap", pid, WAIT_LOG) == EXIT_SUCCESS); |
2a65bd94 ZJS |
418 | } |
419 | ||
420 | static void test_memory_deny_write_execute_shmat(void) { | |
421 | int shmid; | |
422 | pid_t pid; | |
423 | ||
424 | if (!is_seccomp_available()) | |
425 | return; | |
426 | if (geteuid() != 0) | |
427 | return; | |
428 | ||
429 | shmid = shmget(IPC_PRIVATE, page_size(), 0); | |
430 | assert_se(shmid >= 0); | |
431 | ||
432 | pid = fork(); | |
433 | assert_se(pid >= 0); | |
434 | ||
435 | if (pid == 0) { | |
436 | void *p; | |
437 | ||
438 | p = shmat(shmid, NULL, 0); | |
439 | assert_se(p != MAP_FAILED); | |
440 | assert_se(shmdt(p) == 0); | |
441 | ||
442 | p = shmat(shmid, NULL, SHM_EXEC); | |
443 | assert_se(p != MAP_FAILED); | |
444 | assert_se(shmdt(p) == 0); | |
445 | ||
446 | assert_se(seccomp_memory_deny_write_execute() >= 0); | |
447 | ||
448 | p = shmat(shmid, NULL, SHM_EXEC); | |
4278d1f5 | 449 | #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) |
2a65bd94 ZJS |
450 | assert_se(p == MAP_FAILED); |
451 | assert_se(errno == EPERM); | |
2a8d6e63 | 452 | #else /* __i386__, __powerpc64__, and "unknown" architectures */ |
2a65bd94 ZJS |
453 | assert_se(p != MAP_FAILED); |
454 | assert_se(shmdt(p) == 0); | |
455 | #endif | |
456 | ||
457 | p = shmat(shmid, NULL, 0); | |
458 | assert_se(p != MAP_FAILED); | |
459 | assert_se(shmdt(p) == 0); | |
460 | ||
461 | _exit(EXIT_SUCCESS); | |
462 | } | |
463 | ||
7d4904fe | 464 | assert_se(wait_for_terminate_and_check("memoryseccomp-shmat", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
465 | } |
466 | ||
467 | static void test_restrict_archs(void) { | |
468 | pid_t pid; | |
469 | ||
470 | if (!is_seccomp_available()) | |
471 | return; | |
472 | if (geteuid() != 0) | |
473 | return; | |
474 | ||
475 | pid = fork(); | |
476 | assert_se(pid >= 0); | |
477 | ||
478 | if (pid == 0) { | |
479 | _cleanup_set_free_ Set *s = NULL; | |
480 | ||
481 | assert_se(access("/", F_OK) >= 0); | |
482 | ||
483 | assert_se(s = set_new(NULL)); | |
484 | ||
485 | #ifdef __x86_64__ | |
486 | assert_se(set_put(s, UINT32_TO_PTR(SCMP_ARCH_X86+1)) >= 0); | |
487 | #endif | |
488 | assert_se(seccomp_restrict_archs(s) >= 0); | |
489 | ||
490 | assert_se(access("/", F_OK) >= 0); | |
491 | assert_se(seccomp_restrict_archs(NULL) >= 0); | |
492 | ||
493 | assert_se(access("/", F_OK) >= 0); | |
494 | ||
495 | _exit(EXIT_SUCCESS); | |
496 | } | |
497 | ||
7d4904fe | 498 | assert_se(wait_for_terminate_and_check("archseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
499 | } |
500 | ||
501 | static void test_load_syscall_filter_set_raw(void) { | |
502 | pid_t pid; | |
503 | ||
504 | if (!is_seccomp_available()) | |
505 | return; | |
506 | if (geteuid() != 0) | |
507 | return; | |
508 | ||
509 | pid = fork(); | |
510 | assert_se(pid >= 0); | |
511 | ||
512 | if (pid == 0) { | |
b4891260 | 513 | _cleanup_hashmap_free_ Hashmap *s = NULL; |
469830d1 LP |
514 | |
515 | assert_se(access("/", F_OK) >= 0); | |
516 | assert_se(poll(NULL, 0, 0) == 0); | |
517 | ||
518 | assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0); | |
519 | assert_se(access("/", F_OK) >= 0); | |
520 | assert_se(poll(NULL, 0, 0) == 0); | |
521 | ||
b4891260 | 522 | assert_se(s = hashmap_new(NULL)); |
f60a865a | 523 | #if SCMP_SYS(access) >= 0 |
b4891260 | 524 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_access + 1), INT_TO_PTR(-1)) >= 0); |
f60a865a | 525 | #else |
b4891260 | 526 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(-1)) >= 0); |
f60a865a | 527 | #endif |
469830d1 LP |
528 | |
529 | assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); | |
530 | ||
531 | assert_se(access("/", F_OK) < 0); | |
532 | assert_se(errno == EUCLEAN); | |
533 | ||
534 | assert_se(poll(NULL, 0, 0) == 0); | |
535 | ||
b4891260 | 536 | s = hashmap_free(s); |
469830d1 | 537 | |
b4891260 YW |
538 | assert_se(s = hashmap_new(NULL)); |
539 | #if SCMP_SYS(access) >= 0 | |
540 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_access + 1), INT_TO_PTR(EILSEQ)) >= 0); | |
541 | #else | |
542 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(EILSEQ)) >= 0); | |
543 | #endif | |
544 | ||
545 | assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); | |
546 | ||
547 | assert_se(access("/", F_OK) < 0); | |
548 | assert_se(errno == EILSEQ); | |
549 | ||
550 | assert_se(poll(NULL, 0, 0) == 0); | |
551 | ||
552 | s = hashmap_free(s); | |
553 | ||
554 | assert_se(s = hashmap_new(NULL)); | |
f60a865a | 555 | #if SCMP_SYS(poll) >= 0 |
b4891260 | 556 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_poll + 1), INT_TO_PTR(-1)) >= 0); |
f60a865a | 557 | #else |
b4891260 | 558 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(-1)) >= 0); |
f60a865a | 559 | #endif |
469830d1 LP |
560 | |
561 | assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); | |
562 | ||
563 | assert_se(access("/", F_OK) < 0); | |
b4891260 | 564 | assert_se(errno == EILSEQ); |
469830d1 LP |
565 | |
566 | assert_se(poll(NULL, 0, 0) < 0); | |
567 | assert_se(errno == EUNATCH); | |
568 | ||
b4891260 YW |
569 | s = hashmap_free(s); |
570 | ||
571 | assert_se(s = hashmap_new(NULL)); | |
572 | #if SCMP_SYS(poll) >= 0 | |
573 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_poll + 1), INT_TO_PTR(EILSEQ)) >= 0); | |
574 | #else | |
575 | assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(EILSEQ)) >= 0); | |
576 | #endif | |
577 | ||
578 | assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); | |
579 | ||
580 | assert_se(access("/", F_OK) < 0); | |
581 | assert_se(errno == EILSEQ); | |
582 | ||
583 | assert_se(poll(NULL, 0, 0) < 0); | |
584 | assert_se(errno == EILSEQ); | |
585 | ||
469830d1 LP |
586 | _exit(EXIT_SUCCESS); |
587 | } | |
588 | ||
7d4904fe | 589 | assert_se(wait_for_terminate_and_check("syscallrawseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
469830d1 LP |
590 | } |
591 | ||
78e864e5 | 592 | static void test_lock_personality(void) { |
e8132d63 | 593 | unsigned long current; |
78e864e5 TM |
594 | pid_t pid; |
595 | ||
596 | if (!is_seccomp_available()) | |
597 | return; | |
598 | if (geteuid() != 0) | |
599 | return; | |
600 | ||
e8132d63 LP |
601 | assert_se(opinionated_personality(¤t) >= 0); |
602 | ||
603 | log_info("current personality=%lu", current); | |
604 | ||
78e864e5 TM |
605 | pid = fork(); |
606 | assert_se(pid >= 0); | |
607 | ||
608 | if (pid == 0) { | |
e8132d63 | 609 | assert_se(seccomp_lock_personality(current) >= 0); |
78e864e5 | 610 | |
21022b9d | 611 | assert_se((unsigned long) safe_personality(current) == current); |
e8132d63 | 612 | |
21022b9d LP |
613 | /* Note, we also test that safe_personality() works correctly, by checkig whether errno is properly |
614 | * set, in addition to the return value */ | |
615 | errno = 0; | |
616 | assert_se(safe_personality(PER_LINUX | ADDR_NO_RANDOMIZE) == -EPERM); | |
617 | assert_se(errno == EPERM); | |
e8132d63 | 618 | |
21022b9d LP |
619 | assert_se(safe_personality(PER_LINUX | MMAP_PAGE_ZERO) == -EPERM); |
620 | assert_se(safe_personality(PER_LINUX | ADDR_COMPAT_LAYOUT) == -EPERM); | |
621 | assert_se(safe_personality(PER_LINUX | READ_IMPLIES_EXEC) == -EPERM); | |
622 | assert_se(safe_personality(PER_LINUX_32BIT) == -EPERM); | |
623 | assert_se(safe_personality(PER_SVR4) == -EPERM); | |
624 | assert_se(safe_personality(PER_BSD) == -EPERM); | |
625 | assert_se(safe_personality(current == PER_LINUX ? PER_LINUX32 : PER_LINUX) == -EPERM); | |
626 | assert_se(safe_personality(PER_LINUX32_3GB) == -EPERM); | |
627 | assert_se(safe_personality(PER_UW7) == -EPERM); | |
628 | assert_se(safe_personality(0x42) == -EPERM); | |
629 | ||
630 | assert_se(safe_personality(PERSONALITY_INVALID) == -EPERM); /* maybe remove this later */ | |
e8132d63 LP |
631 | |
632 | assert_se((unsigned long) personality(current) == current); | |
78e864e5 TM |
633 | _exit(EXIT_SUCCESS); |
634 | } | |
635 | ||
7d4904fe | 636 | assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); |
78e864e5 TM |
637 | } |
638 | ||
25e94f8c LP |
639 | static void test_filter_sets_ordered(void) { |
640 | size_t i; | |
641 | ||
642 | /* Ensure "@default" always remains at the beginning of the list */ | |
643 | assert_se(SYSCALL_FILTER_SET_DEFAULT == 0); | |
644 | assert_se(streq(syscall_filter_sets[0].name, "@default")); | |
645 | ||
646 | for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { | |
647 | const char *k, *p = NULL; | |
648 | ||
649 | /* Make sure each group has a description */ | |
650 | assert_se(!isempty(syscall_filter_sets[0].help)); | |
651 | ||
652 | /* Make sure the groups are ordered alphabetically, except for the first entry */ | |
653 | assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0); | |
654 | ||
655 | NULSTR_FOREACH(k, syscall_filter_sets[i].value) { | |
656 | ||
657 | /* Ensure each syscall list is in itself ordered, but groups before names */ | |
658 | assert_se(!p || | |
659 | (*p == '@' && *k != '@') || | |
660 | (((*p == '@' && *k == '@') || | |
661 | (*p != '@' && *k != '@')) && | |
662 | strcmp(p, k) < 0)); | |
663 | ||
664 | p = k; | |
665 | } | |
666 | } | |
667 | } | |
668 | ||
f6281133 LP |
669 | int main(int argc, char *argv[]) { |
670 | ||
add00535 LP |
671 | log_set_max_level(LOG_DEBUG); |
672 | ||
f6281133 | 673 | test_seccomp_arch_to_string(); |
aa34055f | 674 | test_architecture_table(); |
f6281133 LP |
675 | test_syscall_filter_set_find(); |
676 | test_filter_sets(); | |
add00535 | 677 | test_restrict_namespace(); |
469830d1 LP |
678 | test_protect_sysctl(); |
679 | test_restrict_address_families(); | |
680 | test_restrict_realtime(); | |
2a65bd94 ZJS |
681 | test_memory_deny_write_execute_mmap(); |
682 | test_memory_deny_write_execute_shmat(); | |
469830d1 LP |
683 | test_restrict_archs(); |
684 | test_load_syscall_filter_set_raw(); | |
78e864e5 | 685 | test_lock_personality(); |
25e94f8c | 686 | test_filter_sets_ordered(); |
f6281133 LP |
687 | |
688 | return 0; | |
689 | } |