]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-seccomp.c
seccomp: arm64/x32 do not have _sysctl
[thirdparty/systemd.git] / src / test / test-seccomp.c
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
20 #include <sched.h>
21 #include <stdlib.h>
22 #include <sys/eventfd.h>
23 #include <sys/mman.h>
24 #include <sys/poll.h>
25 #include <sys/shm.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28
29 #include "alloc-util.h"
30 #include "fd-util.h"
31 #include "macro.h"
32 #include "missing.h"
33 #include "nsflags.h"
34 #include "process-util.h"
35 #include "raw-clone.h"
36 #include "seccomp-util.h"
37 #include "set.h"
38 #include "string-util.h"
39 #include "util.h"
40 #include "virt.h"
41
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
51 static 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
63 static 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
93 static 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
104 static void test_filter_sets(void) {
105 unsigned i;
106 int r;
107
108 if (!is_seccomp_available())
109 return;
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 */
125 r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW);
126 else
127 r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN));
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))
134 assert_se(fd < 0 && errno == EUCLEAN);
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
147 static void test_restrict_namespace(void) {
148 _cleanup_free_ char *s = NULL;
149 unsigned long ul;
150 pid_t pid;
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;
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
232 static 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 #if __NR__sysctl > 0
248 assert_se(syscall(__NR__sysctl, NULL) < 0);
249 assert_se(errno == EFAULT);
250 #endif
251
252 assert_se(seccomp_protect_sysctl() >= 0);
253
254 #if __NR__sysctl > 0
255 assert_se(syscall(__NR__sysctl, 0, 0, 0) < 0);
256 assert_se(errno == EPERM);
257 #endif
258
259 _exit(EXIT_SUCCESS);
260 }
261
262 assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS);
263 }
264
265 static void test_restrict_address_families(void) {
266 pid_t pid;
267
268 if (!is_seccomp_available())
269 return;
270 if (geteuid() != 0)
271 return;
272
273 pid = fork();
274 assert_se(pid >= 0);
275
276 if (pid == 0) {
277 int fd;
278 Set *s;
279
280 fd = socket(AF_INET, SOCK_DGRAM, 0);
281 assert_se(fd >= 0);
282 safe_close(fd);
283
284 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
285 assert_se(fd >= 0);
286 safe_close(fd);
287
288 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
289 assert_se(fd >= 0);
290 safe_close(fd);
291
292 assert_se(s = set_new(NULL));
293 assert_se(set_put(s, INT_TO_PTR(AF_UNIX)) >= 0);
294
295 assert_se(seccomp_restrict_address_families(s, false) >= 0);
296
297 fd = socket(AF_INET, SOCK_DGRAM, 0);
298 assert_se(fd >= 0);
299 safe_close(fd);
300
301 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
302 #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
303 assert_se(fd >= 0);
304 safe_close(fd);
305 #else
306 assert_se(fd < 0);
307 assert_se(errno == EAFNOSUPPORT);
308 #endif
309
310 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
311 assert_se(fd >= 0);
312 safe_close(fd);
313
314 set_clear(s);
315
316 assert_se(set_put(s, INT_TO_PTR(AF_INET)) >= 0);
317
318 assert_se(seccomp_restrict_address_families(s, true) >= 0);
319
320 fd = socket(AF_INET, SOCK_DGRAM, 0);
321 assert_se(fd >= 0);
322 safe_close(fd);
323
324 fd = socket(AF_UNIX, SOCK_DGRAM, 0);
325 #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
326 assert_se(fd >= 0);
327 safe_close(fd);
328 #else
329 assert_se(fd < 0);
330 assert_se(errno == EAFNOSUPPORT);
331 #endif
332
333 fd = socket(AF_NETLINK, SOCK_DGRAM, 0);
334 #if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN
335 assert_se(fd >= 0);
336 safe_close(fd);
337 #else
338 assert_se(fd < 0);
339 assert_se(errno == EAFNOSUPPORT);
340 #endif
341
342 _exit(EXIT_SUCCESS);
343 }
344
345 assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS);
346 }
347
348 static void test_restrict_realtime(void) {
349 pid_t pid;
350
351 if (!is_seccomp_available())
352 return;
353 if (geteuid() != 0)
354 return;
355
356 if (detect_container() > 0) /* in containers RT privs are likely missing anyway */
357 return;
358
359 pid = fork();
360 assert_se(pid >= 0);
361
362 if (pid == 0) {
363 assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) >= 0);
364 assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) >= 0);
365 assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0);
366 assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0);
367 assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0);
368
369 assert_se(seccomp_restrict_realtime() >= 0);
370
371 assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0);
372 assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0);
373 assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0);
374
375 assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) < 0);
376 assert_se(errno == EPERM);
377 assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) < 0);
378 assert_se(errno == EPERM);
379
380 _exit(EXIT_SUCCESS);
381 }
382
383 assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS);
384 }
385
386 static void test_memory_deny_write_execute_mmap(void) {
387 pid_t pid;
388
389 if (!is_seccomp_available())
390 return;
391 if (geteuid() != 0)
392 return;
393
394 pid = fork();
395 assert_se(pid >= 0);
396
397 if (pid == 0) {
398 void *p;
399
400 p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
401 assert_se(p != MAP_FAILED);
402 assert_se(munmap(p, page_size()) >= 0);
403
404 p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
405 assert_se(p != MAP_FAILED);
406 assert_se(munmap(p, page_size()) >= 0);
407
408 assert_se(seccomp_memory_deny_write_execute() >= 0);
409
410 p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
411 #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__)
412 assert_se(p == MAP_FAILED);
413 assert_se(errno == EPERM);
414 #else /* unknown architectures */
415 assert_se(p != MAP_FAILED);
416 assert_se(munmap(p, page_size()) >= 0);
417 #endif
418
419 p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
420 assert_se(p != MAP_FAILED);
421 assert_se(munmap(p, page_size()) >= 0);
422
423 _exit(EXIT_SUCCESS);
424 }
425
426 assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS);
427 }
428
429 static void test_memory_deny_write_execute_shmat(void) {
430 int shmid;
431 pid_t pid;
432
433 if (!is_seccomp_available())
434 return;
435 if (geteuid() != 0)
436 return;
437
438 shmid = shmget(IPC_PRIVATE, page_size(), 0);
439 assert_se(shmid >= 0);
440
441 pid = fork();
442 assert_se(pid >= 0);
443
444 if (pid == 0) {
445 void *p;
446
447 p = shmat(shmid, NULL, 0);
448 assert_se(p != MAP_FAILED);
449 assert_se(shmdt(p) == 0);
450
451 p = shmat(shmid, NULL, SHM_EXEC);
452 assert_se(p != MAP_FAILED);
453 assert_se(shmdt(p) == 0);
454
455 assert_se(seccomp_memory_deny_write_execute() >= 0);
456
457 p = shmat(shmid, NULL, SHM_EXEC);
458 #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
459 assert_se(p == MAP_FAILED);
460 assert_se(errno == EPERM);
461 #else /* __i386__, __powerpc64__, and "unknown" architectures */
462 assert_se(p != MAP_FAILED);
463 assert_se(shmdt(p) == 0);
464 #endif
465
466 p = shmat(shmid, NULL, 0);
467 assert_se(p != MAP_FAILED);
468 assert_se(shmdt(p) == 0);
469
470 _exit(EXIT_SUCCESS);
471 }
472
473 assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS);
474 }
475
476 static void test_restrict_archs(void) {
477 pid_t pid;
478
479 if (!is_seccomp_available())
480 return;
481 if (geteuid() != 0)
482 return;
483
484 pid = fork();
485 assert_se(pid >= 0);
486
487 if (pid == 0) {
488 _cleanup_set_free_ Set *s = NULL;
489
490 assert_se(access("/", F_OK) >= 0);
491
492 assert_se(s = set_new(NULL));
493
494 #ifdef __x86_64__
495 assert_se(set_put(s, UINT32_TO_PTR(SCMP_ARCH_X86+1)) >= 0);
496 #endif
497 assert_se(seccomp_restrict_archs(s) >= 0);
498
499 assert_se(access("/", F_OK) >= 0);
500 assert_se(seccomp_restrict_archs(NULL) >= 0);
501
502 assert_se(access("/", F_OK) >= 0);
503
504 _exit(EXIT_SUCCESS);
505 }
506
507 assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS);
508 }
509
510 static void test_load_syscall_filter_set_raw(void) {
511 pid_t pid;
512
513 if (!is_seccomp_available())
514 return;
515 if (geteuid() != 0)
516 return;
517
518 pid = fork();
519 assert_se(pid >= 0);
520
521 if (pid == 0) {
522 _cleanup_set_free_ Set *s = NULL;
523
524 assert_se(access("/", F_OK) >= 0);
525 assert_se(poll(NULL, 0, 0) == 0);
526
527 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0);
528 assert_se(access("/", F_OK) >= 0);
529 assert_se(poll(NULL, 0, 0) == 0);
530
531 assert_se(s = set_new(NULL));
532 assert_se(set_put(s, UINT32_TO_PTR(__NR_access + 1)) >= 0);
533
534 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0);
535
536 assert_se(access("/", F_OK) < 0);
537 assert_se(errno == EUCLEAN);
538
539 assert_se(poll(NULL, 0, 0) == 0);
540
541 s = set_free(s);
542
543 assert_se(s = set_new(NULL));
544 assert_se(set_put(s, UINT32_TO_PTR(__NR_poll + 1)) >= 0);
545
546 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0);
547
548 assert_se(access("/", F_OK) < 0);
549 assert_se(errno == EUCLEAN);
550
551 assert_se(poll(NULL, 0, 0) < 0);
552 assert_se(errno == EUNATCH);
553
554 _exit(EXIT_SUCCESS);
555 }
556
557 assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS);
558 }
559
560 int main(int argc, char *argv[]) {
561
562 log_set_max_level(LOG_DEBUG);
563
564 test_seccomp_arch_to_string();
565 test_architecture_table();
566 test_syscall_filter_set_find();
567 test_filter_sets();
568 test_restrict_namespace();
569 test_protect_sysctl();
570 test_restrict_address_families();
571 test_restrict_realtime();
572 test_memory_deny_write_execute_mmap();
573 test_memory_deny_write_execute_shmat();
574 test_restrict_archs();
575 test_load_syscall_filter_set_raw();
576
577 return 0;
578 }