]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-seccomp.c
core: add new RestrictNamespaces= unit file setting
[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 <stdlib.h>
21 #include <sys/eventfd.h>
22 #include <unistd.h>
23 #include <sched.h>
24
25 #include "alloc-util.h"
26 #include "fd-util.h"
27 #include "macro.h"
28 #include "missing.h"
29 #include "nsflags.h"
30 #include "process-util.h"
31 #include "raw-clone.h"
32 #include "seccomp-util.h"
33 #include "string-util.h"
34 #include "util.h"
35
36 static void test_seccomp_arch_to_string(void) {
37 uint32_t a, b;
38 const char *name;
39
40 a = seccomp_arch_native();
41 assert_se(a > 0);
42 name = seccomp_arch_to_string(a);
43 assert_se(name);
44 assert_se(seccomp_arch_from_string(name, &b) >= 0);
45 assert_se(a == b);
46 }
47
48 static void test_architecture_table(void) {
49 const char *n, *n2;
50
51 NULSTR_FOREACH(n,
52 "native\0"
53 "x86\0"
54 "x86-64\0"
55 "x32\0"
56 "arm\0"
57 "arm64\0"
58 "mips\0"
59 "mips64\0"
60 "mips64-n32\0"
61 "mips-le\0"
62 "mips64-le\0"
63 "mips64-le-n32\0"
64 "ppc\0"
65 "ppc64\0"
66 "ppc64-le\0"
67 "s390\0"
68 "s390x\0") {
69 uint32_t c;
70
71 assert_se(seccomp_arch_from_string(n, &c) >= 0);
72 n2 = seccomp_arch_to_string(c);
73 log_info("seccomp-arch: %s → 0x%"PRIx32" → %s", n, c, n2);
74 assert_se(streq_ptr(n, n2));
75 }
76 }
77
78 static void test_syscall_filter_set_find(void) {
79 assert_se(!syscall_filter_set_find(NULL));
80 assert_se(!syscall_filter_set_find(""));
81 assert_se(!syscall_filter_set_find("quux"));
82 assert_se(!syscall_filter_set_find("@quux"));
83
84 assert_se(syscall_filter_set_find("@clock") == syscall_filter_sets + SYSCALL_FILTER_SET_CLOCK);
85 assert_se(syscall_filter_set_find("@default") == syscall_filter_sets + SYSCALL_FILTER_SET_DEFAULT);
86 assert_se(syscall_filter_set_find("@raw-io") == syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO);
87 }
88
89 static void test_filter_sets(void) {
90 unsigned i;
91 int r;
92
93 if (!is_seccomp_available())
94 return;
95
96 if (geteuid() != 0)
97 return;
98
99 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
100 pid_t pid;
101
102 log_info("Testing %s", syscall_filter_sets[i].name);
103
104 pid = fork();
105 assert_se(pid >= 0);
106
107 if (pid == 0) { /* Child? */
108 int fd;
109
110 if (i == SYSCALL_FILTER_SET_DEFAULT) /* if we look at the default set, whitelist instead of blacklist */
111 r = seccomp_load_filter_set(SCMP_ACT_ERRNO(EPERM), syscall_filter_sets + i, SCMP_ACT_ALLOW);
112 else
113 r = seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EPERM));
114 if (r < 0)
115 _exit(EXIT_FAILURE);
116
117 /* Test the sycall filter with one random system call */
118 fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
119 if (IN_SET(i, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_DEFAULT))
120 assert_se(fd < 0 && errno == EPERM);
121 else {
122 assert_se(fd >= 0);
123 safe_close(fd);
124 }
125
126 _exit(EXIT_SUCCESS);
127 }
128
129 assert_se(wait_for_terminate_and_warn(syscall_filter_sets[i].name, pid, true) == EXIT_SUCCESS);
130 }
131 }
132
133 static void test_restrict_namespace(void) {
134 _cleanup_free_ char *s = NULL;
135 pid_t pid;
136 unsigned long ul;
137
138 assert_se(namespace_flag_to_string(0) == NULL);
139 assert_se(streq(namespace_flag_to_string(CLONE_NEWNS), "mnt"));
140 assert_se(namespace_flag_to_string(CLONE_NEWNS|CLONE_NEWIPC) == NULL);
141 assert_se(streq(namespace_flag_to_string(CLONE_NEWCGROUP), "cgroup"));
142
143 assert_se(namespace_flag_from_string("mnt") == CLONE_NEWNS);
144 assert_se(namespace_flag_from_string(NULL) == 0);
145 assert_se(namespace_flag_from_string("") == 0);
146 assert_se(namespace_flag_from_string("uts") == CLONE_NEWUTS);
147 assert_se(namespace_flag_from_string(namespace_flag_to_string(CLONE_NEWUTS)) == CLONE_NEWUTS);
148 assert_se(streq(namespace_flag_to_string(namespace_flag_from_string("ipc")), "ipc"));
149
150 assert_se(namespace_flag_from_string_many(NULL, &ul) == 0 && ul == 0);
151 assert_se(namespace_flag_from_string_many("", &ul) == 0 && ul == 0);
152 assert_se(namespace_flag_from_string_many("mnt uts ipc", &ul) == 0 && ul == (CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC));
153
154 assert_se(namespace_flag_to_string_many(NAMESPACE_FLAGS_ALL, &s) == 0);
155 assert_se(streq(s, "cgroup ipc net mnt pid user uts"));
156 assert_se(namespace_flag_from_string_many(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
157
158 if (!is_seccomp_available())
159 return;
160
161 if (geteuid() != 0)
162 return;
163
164 pid = fork();
165 assert_se(pid >= 0);
166
167 if (pid == 0) {
168
169 assert_se(seccomp_restrict_namespaces(CLONE_NEWNS|CLONE_NEWNET) >= 0);
170
171 assert_se(unshare(CLONE_NEWNS) == 0);
172 assert_se(unshare(CLONE_NEWNET) == 0);
173 assert_se(unshare(CLONE_NEWUTS) == -1);
174 assert_se(errno == EPERM);
175 assert_se(unshare(CLONE_NEWIPC) == -1);
176 assert_se(errno == EPERM);
177 assert_se(unshare(CLONE_NEWNET|CLONE_NEWUTS) == -1);
178 assert_se(errno == EPERM);
179
180 /* We use fd 0 (stdin) here, which of course will fail with EINVAL on setns(). Except of course our
181 * seccomp filter worked, and hits first and makes it return EPERM */
182 assert_se(setns(0, CLONE_NEWNS) == -1);
183 assert_se(errno == EINVAL);
184 assert_se(setns(0, CLONE_NEWNET) == -1);
185 assert_se(errno == EINVAL);
186 assert_se(setns(0, CLONE_NEWUTS) == -1);
187 assert_se(errno == EPERM);
188 assert_se(setns(0, CLONE_NEWIPC) == -1);
189 assert_se(errno == EPERM);
190 assert_se(setns(0, CLONE_NEWNET|CLONE_NEWUTS) == -1);
191 assert_se(errno == EPERM);
192 assert_se(setns(0, 0) == -1);
193 assert_se(errno == EPERM);
194
195 pid = raw_clone(CLONE_NEWNS);
196 assert_se(pid >= 0);
197 if (pid == 0)
198 _exit(EXIT_SUCCESS);
199 pid = raw_clone(CLONE_NEWNET);
200 assert_se(pid >= 0);
201 if (pid == 0)
202 _exit(EXIT_SUCCESS);
203 pid = raw_clone(CLONE_NEWUTS);
204 assert_se(pid < 0);
205 assert_se(errno == EPERM);
206 pid = raw_clone(CLONE_NEWIPC);
207 assert_se(pid < 0);
208 assert_se(errno == EPERM);
209 pid = raw_clone(CLONE_NEWNET|CLONE_NEWUTS);
210 assert_se(pid < 0);
211 assert_se(errno == EPERM);
212
213 _exit(EXIT_SUCCESS);
214 }
215
216 assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS);
217 }
218
219 int main(int argc, char *argv[]) {
220
221 log_set_max_level(LOG_DEBUG);
222
223 test_seccomp_arch_to_string();
224 test_architecture_table();
225 test_syscall_filter_set_find();
226 test_filter_sets();
227 test_restrict_namespace();
228
229 return 0;
230 }