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