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