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