]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-seccomp.c
shared/seccomp-util: add parentheses and no. after syscall name
[thirdparty/systemd.git] / src / test / test-seccomp.c
CommitLineData
f6281133
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
469830d1 20#include <sched.h>
f6281133
LP
21#include <stdlib.h>
22#include <sys/eventfd.h>
469830d1 23#include <sys/mman.h>
469830d1 24#include <sys/poll.h>
2a65bd94
ZJS
25#include <sys/shm.h>
26#include <sys/types.h>
27#include <unistd.h>
f6281133 28
add00535 29#include "alloc-util.h"
f6281133
LP
30#include "fd-util.h"
31#include "macro.h"
add00535
LP
32#include "missing.h"
33#include "nsflags.h"
f6281133 34#include "process-util.h"
add00535 35#include "raw-clone.h"
f6281133 36#include "seccomp-util.h"
469830d1 37#include "set.h"
aa34055f
ZJS
38#include "string-util.h"
39#include "util.h"
469830d1 40#include "virt.h"
f6281133 41
da1921a5
ZJS
42#if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__)
43/* On these archs, socket() is implemented via the socketcall() syscall multiplexer,
44 * and we can't restrict it hence via seccomp. */
45# define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 1
46#else
47# define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 0
48#endif
49
50
f6281133
LP
51static void test_seccomp_arch_to_string(void) {
52 uint32_t a, b;
53 const char *name;
54
55 a = seccomp_arch_native();
56 assert_se(a > 0);
57 name = seccomp_arch_to_string(a);
58 assert_se(name);
59 assert_se(seccomp_arch_from_string(name, &b) >= 0);
60 assert_se(a == b);
61}
62
aa34055f
ZJS
63static void test_architecture_table(void) {
64 const char *n, *n2;
65
66 NULSTR_FOREACH(n,
67 "native\0"
68 "x86\0"
69 "x86-64\0"
70 "x32\0"
71 "arm\0"
72 "arm64\0"
73 "mips\0"
74 "mips64\0"
75 "mips64-n32\0"
76 "mips-le\0"
77 "mips64-le\0"
78 "mips64-le-n32\0"
79 "ppc\0"
80 "ppc64\0"
81 "ppc64-le\0"
82 "s390\0"
83 "s390x\0") {
84 uint32_t c;
85
86 assert_se(seccomp_arch_from_string(n, &c) >= 0);
87 n2 = seccomp_arch_to_string(c);
88 log_info("seccomp-arch: %s → 0x%"PRIx32" → %s", n, c, n2);
89 assert_se(streq_ptr(n, n2));
90 }
91}
92
f6281133
LP
93static void test_syscall_filter_set_find(void) {
94 assert_se(!syscall_filter_set_find(NULL));
95 assert_se(!syscall_filter_set_find(""));
96 assert_se(!syscall_filter_set_find("quux"));
97 assert_se(!syscall_filter_set_find("@quux"));
98
99 assert_se(syscall_filter_set_find("@clock") == syscall_filter_sets + SYSCALL_FILTER_SET_CLOCK);
100 assert_se(syscall_filter_set_find("@default") == syscall_filter_sets + SYSCALL_FILTER_SET_DEFAULT);
101 assert_se(syscall_filter_set_find("@raw-io") == syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO);
102}
103
104static void test_filter_sets(void) {
105 unsigned i;
106 int r;
107
108 if (!is_seccomp_available())
109 return;
f6281133
LP
110 if (geteuid() != 0)
111 return;
112
113 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
114 pid_t pid;
115
116 log_info("Testing %s", syscall_filter_sets[i].name);
117
118 pid = fork();
119 assert_se(pid >= 0);
120
121 if (pid == 0) { /* Child? */
122 int fd;
123
124 if (i == SYSCALL_FILTER_SET_DEFAULT) /* if we look at the default set, whitelist instead of blacklist */
469830d1 125 r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW);
f6281133 126 else
469830d1 127 r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN));
f6281133
LP
128 if (r < 0)
129 _exit(EXIT_FAILURE);
130
131 /* Test the sycall filter with one random system call */
132 fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
133 if (IN_SET(i, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_DEFAULT))
469830d1 134 assert_se(fd < 0 && errno == EUCLEAN);
f6281133
LP
135 else {
136 assert_se(fd >= 0);
137 safe_close(fd);
138 }
139
140 _exit(EXIT_SUCCESS);
141 }
142
143 assert_se(wait_for_terminate_and_warn(syscall_filter_sets[i].name, pid, true) == EXIT_SUCCESS);
144 }
145}
146
add00535
LP
147static void test_restrict_namespace(void) {
148 _cleanup_free_ char *s = NULL;
add00535 149 unsigned long ul;
469830d1 150 pid_t pid;
add00535
LP
151
152 assert_se(namespace_flag_to_string(0) == NULL);
153 assert_se(streq(namespace_flag_to_string(CLONE_NEWNS), "mnt"));
154 assert_se(namespace_flag_to_string(CLONE_NEWNS|CLONE_NEWIPC) == NULL);
155 assert_se(streq(namespace_flag_to_string(CLONE_NEWCGROUP), "cgroup"));
156
157 assert_se(namespace_flag_from_string("mnt") == CLONE_NEWNS);
158 assert_se(namespace_flag_from_string(NULL) == 0);
159 assert_se(namespace_flag_from_string("") == 0);
160 assert_se(namespace_flag_from_string("uts") == CLONE_NEWUTS);
161 assert_se(namespace_flag_from_string(namespace_flag_to_string(CLONE_NEWUTS)) == CLONE_NEWUTS);
162 assert_se(streq(namespace_flag_to_string(namespace_flag_from_string("ipc")), "ipc"));
163
164 assert_se(namespace_flag_from_string_many(NULL, &ul) == 0 && ul == 0);
165 assert_se(namespace_flag_from_string_many("", &ul) == 0 && ul == 0);
166 assert_se(namespace_flag_from_string_many("mnt uts ipc", &ul) == 0 && ul == (CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC));
167
168 assert_se(namespace_flag_to_string_many(NAMESPACE_FLAGS_ALL, &s) == 0);
169 assert_se(streq(s, "cgroup ipc net mnt pid user uts"));
170 assert_se(namespace_flag_from_string_many(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
171
172 if (!is_seccomp_available())
173 return;
add00535
LP
174 if (geteuid() != 0)
175 return;
176
177 pid = fork();
178 assert_se(pid >= 0);
179
180 if (pid == 0) {
181
182 assert_se(seccomp_restrict_namespaces(CLONE_NEWNS|CLONE_NEWNET) >= 0);
183
184 assert_se(unshare(CLONE_NEWNS) == 0);
185 assert_se(unshare(CLONE_NEWNET) == 0);
186 assert_se(unshare(CLONE_NEWUTS) == -1);
187 assert_se(errno == EPERM);
188 assert_se(unshare(CLONE_NEWIPC) == -1);
189 assert_se(errno == EPERM);
190 assert_se(unshare(CLONE_NEWNET|CLONE_NEWUTS) == -1);
191 assert_se(errno == EPERM);
192
193 /* We use fd 0 (stdin) here, which of course will fail with EINVAL on setns(). Except of course our
194 * seccomp filter worked, and hits first and makes it return EPERM */
195 assert_se(setns(0, CLONE_NEWNS) == -1);
196 assert_se(errno == EINVAL);
197 assert_se(setns(0, CLONE_NEWNET) == -1);
198 assert_se(errno == EINVAL);
199 assert_se(setns(0, CLONE_NEWUTS) == -1);
200 assert_se(errno == EPERM);
201 assert_se(setns(0, CLONE_NEWIPC) == -1);
202 assert_se(errno == EPERM);
203 assert_se(setns(0, CLONE_NEWNET|CLONE_NEWUTS) == -1);
204 assert_se(errno == EPERM);
205 assert_se(setns(0, 0) == -1);
206 assert_se(errno == EPERM);
207
208 pid = raw_clone(CLONE_NEWNS);
209 assert_se(pid >= 0);
210 if (pid == 0)
211 _exit(EXIT_SUCCESS);
212 pid = raw_clone(CLONE_NEWNET);
213 assert_se(pid >= 0);
214 if (pid == 0)
215 _exit(EXIT_SUCCESS);
216 pid = raw_clone(CLONE_NEWUTS);
217 assert_se(pid < 0);
218 assert_se(errno == EPERM);
219 pid = raw_clone(CLONE_NEWIPC);
220 assert_se(pid < 0);
221 assert_se(errno == EPERM);
222 pid = raw_clone(CLONE_NEWNET|CLONE_NEWUTS);
223 assert_se(pid < 0);
224 assert_se(errno == EPERM);
225
226 _exit(EXIT_SUCCESS);
227 }
228
229 assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS);
230}
231
469830d1
LP
232static void test_protect_sysctl(void) {
233 pid_t pid;
234
235 if (!is_seccomp_available())
236 return;
237 if (geteuid() != 0)
238 return;
239
240 if (detect_container() > 0) /* in containers _sysctl() is likely missing anyway */
241 return;
242
243 pid = fork();
244 assert_se(pid >= 0);
245
246 if (pid == 0) {
247 assert_se(syscall(__NR__sysctl, NULL) < 0);
248 assert_se(errno == EFAULT);
249
250 assert_se(seccomp_protect_sysctl() >= 0);
251
252 assert_se(syscall(__NR__sysctl, 0, 0, 0) < 0);
253 assert_se(errno == EPERM);
254
255 _exit(EXIT_SUCCESS);
256 }
257
258 assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS);
259}
260
261static void test_restrict_address_families(void) {
262 pid_t pid;
263
264 if (!is_seccomp_available())
265 return;
266 if (geteuid() != 0)
267 return;
268
269 pid = fork();
270 assert_se(pid >= 0);
271
272 if (pid == 0) {
273 int fd;
274 Set *s;
275
276 fd = socket(AF_INET, SOCK_DGRAM, 0);
277 assert_se(fd >= 0);
278 safe_close(fd);
279
280 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
281 assert_se(fd >= 0);
282 safe_close(fd);
283
284 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
285 assert_se(fd >= 0);
286 safe_close(fd);
287
288 assert_se(s = set_new(NULL));
289 assert_se(set_put(s, INT_TO_PTR(AF_UNIX)) >= 0);
290
291 assert_se(seccomp_restrict_address_families(s, false) >= 0);
292
293 fd = socket(AF_INET, SOCK_DGRAM, 0);
294 assert_se(fd >= 0);
295 safe_close(fd);
296
ad8f1479 297 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
dce0e620 298#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
ad8f1479
LP
299 assert_se(fd >= 0);
300 safe_close(fd);
301#else
dce0e620 302 assert_se(fd < 0);
469830d1 303 assert_se(errno == EAFNOSUPPORT);
ad8f1479 304#endif
469830d1
LP
305
306 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
307 assert_se(fd >= 0);
308 safe_close(fd);
309
310 set_clear(s);
311
312 assert_se(set_put(s, INT_TO_PTR(AF_INET)) >= 0);
313
314 assert_se(seccomp_restrict_address_families(s, true) >= 0);
315
316 fd = socket(AF_INET, SOCK_DGRAM, 0);
317 assert_se(fd >= 0);
318 safe_close(fd);
319
ad8f1479 320 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
dce0e620 321#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
ad8f1479
LP
322 assert_se(fd >= 0);
323 safe_close(fd);
dce0e620
ZJS
324#else
325 assert_se(fd < 0);
326 assert_se(errno == EAFNOSUPPORT);
327#endif
ad8f1479
LP
328
329 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
dce0e620 330#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
ad8f1479
LP
331 assert_se(fd >= 0);
332 safe_close(fd);
333#else
dce0e620 334 assert_se(fd < 0);
469830d1 335 assert_se(errno == EAFNOSUPPORT);
ad8f1479 336#endif
469830d1
LP
337
338 _exit(EXIT_SUCCESS);
339 }
340
341 assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS);
342}
343
344static void test_restrict_realtime(void) {
345 pid_t pid;
346
347 if (!is_seccomp_available())
348 return;
349 if (geteuid() != 0)
350 return;
351
352 if (detect_container() > 0) /* in containers RT privs are likely missing anyway */
353 return;
354
355 pid = fork();
356 assert_se(pid >= 0);
357
358 if (pid == 0) {
359 assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) >= 0);
360 assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) >= 0);
361 assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0);
362 assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0);
363 assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0);
364
365 assert_se(seccomp_restrict_realtime() >= 0);
366
367 assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0);
368 assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0);
369 assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0);
370
371 assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) < 0);
372 assert_se(errno == EPERM);
373 assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) < 0);
374 assert_se(errno == EPERM);
375
376 _exit(EXIT_SUCCESS);
377 }
378
379 assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS);
380}
381
2a65bd94 382static void test_memory_deny_write_execute_mmap(void) {
469830d1
LP
383 pid_t pid;
384
385 if (!is_seccomp_available())
386 return;
387 if (geteuid() != 0)
388 return;
389
390 pid = fork();
391 assert_se(pid >= 0);
392
393 if (pid == 0) {
394 void *p;
395
396 p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
397 assert_se(p != MAP_FAILED);
398 assert_se(munmap(p, page_size()) >= 0);
399
8a50cf69
LP
400 p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
401 assert_se(p != MAP_FAILED);
402 assert_se(munmap(p, page_size()) >= 0);
469830d1 403
8a50cf69
LP
404 assert_se(seccomp_memory_deny_write_execute() >= 0);
405
8a50cf69 406 p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
4278d1f5 407#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__)
469830d1
LP
408 assert_se(p == MAP_FAILED);
409 assert_se(errno == EPERM);
2a65bd94
ZJS
410#else /* unknown architectures */
411 assert_se(p != MAP_FAILED);
412 assert_se(munmap(p, page_size()) >= 0);
8a50cf69 413#endif
469830d1
LP
414
415 p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
416 assert_se(p != MAP_FAILED);
417 assert_se(munmap(p, page_size()) >= 0);
418
419 _exit(EXIT_SUCCESS);
420 }
421
2a65bd94
ZJS
422 assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS);
423}
424
425static void test_memory_deny_write_execute_shmat(void) {
426 int shmid;
427 pid_t pid;
428
429 if (!is_seccomp_available())
430 return;
431 if (geteuid() != 0)
432 return;
433
434 shmid = shmget(IPC_PRIVATE, page_size(), 0);
435 assert_se(shmid >= 0);
436
437 pid = fork();
438 assert_se(pid >= 0);
439
440 if (pid == 0) {
441 void *p;
442
443 p = shmat(shmid, NULL, 0);
444 assert_se(p != MAP_FAILED);
445 assert_se(shmdt(p) == 0);
446
447 p = shmat(shmid, NULL, SHM_EXEC);
448 assert_se(p != MAP_FAILED);
449 assert_se(shmdt(p) == 0);
450
451 assert_se(seccomp_memory_deny_write_execute() >= 0);
452
453 p = shmat(shmid, NULL, SHM_EXEC);
4278d1f5 454#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
2a65bd94
ZJS
455 assert_se(p == MAP_FAILED);
456 assert_se(errno == EPERM);
2a8d6e63 457#else /* __i386__, __powerpc64__, and "unknown" architectures */
2a65bd94
ZJS
458 assert_se(p != MAP_FAILED);
459 assert_se(shmdt(p) == 0);
460#endif
461
462 p = shmat(shmid, NULL, 0);
463 assert_se(p != MAP_FAILED);
464 assert_se(shmdt(p) == 0);
465
466 _exit(EXIT_SUCCESS);
467 }
468
469 assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS);
469830d1
LP
470}
471
472static void test_restrict_archs(void) {
473 pid_t pid;
474
475 if (!is_seccomp_available())
476 return;
477 if (geteuid() != 0)
478 return;
479
480 pid = fork();
481 assert_se(pid >= 0);
482
483 if (pid == 0) {
484 _cleanup_set_free_ Set *s = NULL;
485
486 assert_se(access("/", F_OK) >= 0);
487
488 assert_se(s = set_new(NULL));
489
490#ifdef __x86_64__
491 assert_se(set_put(s, UINT32_TO_PTR(SCMP_ARCH_X86+1)) >= 0);
492#endif
493 assert_se(seccomp_restrict_archs(s) >= 0);
494
495 assert_se(access("/", F_OK) >= 0);
496 assert_se(seccomp_restrict_archs(NULL) >= 0);
497
498 assert_se(access("/", F_OK) >= 0);
499
500 _exit(EXIT_SUCCESS);
501 }
502
503 assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS);
504}
505
506static void test_load_syscall_filter_set_raw(void) {
507 pid_t pid;
508
509 if (!is_seccomp_available())
510 return;
511 if (geteuid() != 0)
512 return;
513
514 pid = fork();
515 assert_se(pid >= 0);
516
517 if (pid == 0) {
518 _cleanup_set_free_ Set *s = NULL;
519
520 assert_se(access("/", F_OK) >= 0);
521 assert_se(poll(NULL, 0, 0) == 0);
522
523 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0);
524 assert_se(access("/", F_OK) >= 0);
525 assert_se(poll(NULL, 0, 0) == 0);
526
527 assert_se(s = set_new(NULL));
528 assert_se(set_put(s, UINT32_TO_PTR(__NR_access + 1)) >= 0);
529
530 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0);
531
532 assert_se(access("/", F_OK) < 0);
533 assert_se(errno == EUCLEAN);
534
535 assert_se(poll(NULL, 0, 0) == 0);
536
537 s = set_free(s);
538
539 assert_se(s = set_new(NULL));
540 assert_se(set_put(s, UINT32_TO_PTR(__NR_poll + 1)) >= 0);
541
542 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0);
543
544 assert_se(access("/", F_OK) < 0);
545 assert_se(errno == EUCLEAN);
546
547 assert_se(poll(NULL, 0, 0) < 0);
548 assert_se(errno == EUNATCH);
549
550 _exit(EXIT_SUCCESS);
551 }
552
553 assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS);
554}
555
f6281133
LP
556int main(int argc, char *argv[]) {
557
add00535
LP
558 log_set_max_level(LOG_DEBUG);
559
f6281133 560 test_seccomp_arch_to_string();
aa34055f 561 test_architecture_table();
f6281133
LP
562 test_syscall_filter_set_find();
563 test_filter_sets();
add00535 564 test_restrict_namespace();
469830d1
LP
565 test_protect_sysctl();
566 test_restrict_address_families();
567 test_restrict_realtime();
2a65bd94
ZJS
568 test_memory_deny_write_execute_mmap();
569 test_memory_deny_write_execute_shmat();
469830d1
LP
570 test_restrict_archs();
571 test_load_syscall_filter_set_raw();
f6281133
LP
572
573 return 0;
574}