cgroups: restrict allowed keys in 'unified' section
[project/procd.git] / jail / seccomp-oci.c
1 /*
2 * parse and setup OCI seccomp filter
3 * Copyright (c) 2020 Daniel Golle <daniel@makrotopia.org>
4 * seccomp example with syscall reporting
5 * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
6 * Authors:
7 * Kees Cook <keescook@chromium.org>
8 * Will Drewry <wad@chromium.org>
9 *
10 * Use of this source code is governed by a BSD-style license that can be
11 * found in the LICENSE file.
12 */
13 #define _GNU_SOURCE 1
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
18 #include <libubox/utils.h>
19 #include <libubox/blobmsg.h>
20 #include <libubox/blobmsg_json.h>
21
22 #include "log.h"
23 #include "seccomp-bpf.h"
24 #include "seccomp-oci.h"
25 #include "../syscall-names.h"
26 #include "seccomp-syscalls-helpers.h"
27
28 static uint32_t resolve_action(char *actname)
29 {
30 if (!strcmp(actname, "SCMP_ACT_KILL"))
31 return SECCOMP_RET_KILL;
32 else if (!strcmp(actname, "SCMP_ACT_KILL_PROCESS"))
33 return SECCOMP_RET_KILLPROCESS;
34 else if (!strcmp(actname, "SCMP_ACT_TRAP"))
35 return SECCOMP_RET_TRAP;
36 else if (!strcmp(actname, "SCMP_ACT_ERRNO"))
37 return SECCOMP_RET_ERRNO;
38 else if (!strcmp(actname, "SCMP_ACT_ERROR"))
39 return SECCOMP_RET_ERRNO;
40 else if (!strcmp(actname, "SCMP_ACT_TRACE"))
41 return SECCOMP_RET_TRACE;
42 else if (!strcmp(actname, "SCMP_ACT_ALLOW"))
43 return SECCOMP_RET_ALLOW;
44 else if (!strcmp(actname, "SCMP_ACT_LOG"))
45 return SECCOMP_RET_LOGALLOW;
46 else {
47 ERROR("unknown seccomp action %s\n", actname);
48 return SECCOMP_RET_KILL;
49 }
50 }
51
52 static uint32_t resolve_architecture(char *archname)
53 {
54 if (!strcmp(archname, "SCMP_ARCH_X86"))
55 return AUDIT_ARCH_I386;
56 else if (!strcmp(archname, "SCMP_ARCH_X86_64"))
57 return AUDIT_ARCH_X86_64;
58 else if (!strcmp(archname, "SCMP_ARCH_X32"))
59 /*
60 * return AUDIT_ARCH_X86_64;
61 * 32-bit userland on 64-bit kernel is not supported yet
62 */
63 return 0;
64 else if (!strcmp(archname, "SCMP_ARCH_ARM"))
65 return AUDIT_ARCH_ARM;
66 else if (!strcmp(archname, "SCMP_ARCH_AARCH64"))
67 return AUDIT_ARCH_AARCH64;
68 else if (!strcmp(archname, "SCMP_ARCH_MIPS"))
69 return AUDIT_ARCH_MIPS;
70 else if (!strcmp(archname, "SCMP_ARCH_MIPS64"))
71 return AUDIT_ARCH_MIPS64;
72 else if (!strcmp(archname, "SCMP_ARCH_MIPS64N32"))
73 return AUDIT_ARCH_MIPS64N32;
74 else if (!strcmp(archname, "SCMP_ARCH_MIPSEL"))
75 return AUDIT_ARCH_MIPSEL;
76 else if (!strcmp(archname, "SCMP_ARCH_MIPSEL64"))
77 return AUDIT_ARCH_MIPSEL64;
78 else if (!strcmp(archname, "SCMP_ARCH_MIPSEL64N32"))
79 return AUDIT_ARCH_MIPSEL64N32;
80 else if (!strcmp(archname, "SCMP_ARCH_PPC"))
81 return AUDIT_ARCH_PPC;
82 else if (!strcmp(archname, "SCMP_ARCH_PPC64"))
83 return AUDIT_ARCH_PPC64;
84 else if (!strcmp(archname, "SCMP_ARCH_PPC64LE"))
85 return AUDIT_ARCH_PPC64LE;
86 else if (!strcmp(archname, "SCMP_ARCH_S390"))
87 return AUDIT_ARCH_S390;
88 else if (!strcmp(archname, "SCMP_ARCH_S390X"))
89 return AUDIT_ARCH_S390X;
90 else if (!strcmp(archname, "SCMP_ARCH_PARISC"))
91 return AUDIT_ARCH_PARISC;
92 else if (!strcmp(archname, "SCMP_ARCH_PARISC64"))
93 return AUDIT_ARCH_PARISC64;
94 else {
95 ERROR("unknown seccomp architecture %s\n", archname);
96 return 0;
97 }
98 }
99
100 enum {
101 OCI_LINUX_SECCOMP_DEFAULTACTION,
102 OCI_LINUX_SECCOMP_ARCHITECTURES,
103 OCI_LINUX_SECCOMP_FLAGS,
104 OCI_LINUX_SECCOMP_SYSCALLS,
105 __OCI_LINUX_SECCOMP_MAX,
106 };
107
108 static const struct blobmsg_policy oci_linux_seccomp_policy[] = {
109 [OCI_LINUX_SECCOMP_DEFAULTACTION] = { "defaultAction", BLOBMSG_TYPE_STRING },
110 [OCI_LINUX_SECCOMP_ARCHITECTURES] = { "architectures", BLOBMSG_TYPE_ARRAY },
111 [OCI_LINUX_SECCOMP_FLAGS] = { "flags", BLOBMSG_TYPE_ARRAY },
112 [OCI_LINUX_SECCOMP_SYSCALLS] = { "syscalls", BLOBMSG_TYPE_ARRAY },
113 };
114
115 enum {
116 OCI_LINUX_SECCOMP_SYSCALLS_NAMES,
117 OCI_LINUX_SECCOMP_SYSCALLS_ACTION,
118 OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET,
119 OCI_LINUX_SECCOMP_SYSCALLS_ARGS,
120 __OCI_LINUX_SECCOMP_SYSCALLS_MAX
121 };
122
123 static const struct blobmsg_policy oci_linux_seccomp_syscalls_policy[] = {
124 [OCI_LINUX_SECCOMP_SYSCALLS_NAMES] = { "names", BLOBMSG_TYPE_ARRAY },
125 [OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET] = { "errnoRet", BLOBMSG_TYPE_INT32 },
126 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS] = { "args", BLOBMSG_TYPE_ARRAY },
127 [OCI_LINUX_SECCOMP_SYSCALLS_ACTION] = { "action", BLOBMSG_TYPE_STRING },
128 };
129
130 enum {
131 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX,
132 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE,
133 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO,
134 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP,
135 __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX
136 };
137
138 static const struct blobmsg_policy oci_linux_seccomp_syscalls_args_policy[] = {
139 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX] = { "index", BLOBMSG_TYPE_INT32 },
140 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE] = { "value", BLOBMSG_TYPE_INT64 },
141 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO] = { "valueTwo", BLOBMSG_TYPE_INT64 },
142 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP] = { "op", BLOBMSG_TYPE_STRING },
143 };
144
145 struct sock_fprog *parseOCIlinuxseccomp(struct blob_attr *msg)
146 {
147 struct blob_attr *tb[__OCI_LINUX_SECCOMP_MAX];
148 struct blob_attr *tbn[__OCI_LINUX_SECCOMP_SYSCALLS_MAX];
149 struct blob_attr *tba[__OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX];
150 struct blob_attr *cur, *curn, *curarg;
151 int rem, remn, remargs, sc;
152 struct sock_filter *filter;
153 struct sock_fprog *prog;
154 int sz = 5, idx = 0;
155 uint32_t default_policy = 0;
156 uint32_t seccomp_arch;
157
158 blobmsg_parse(oci_linux_seccomp_policy, __OCI_LINUX_SECCOMP_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
159
160 if (!tb[OCI_LINUX_SECCOMP_DEFAULTACTION]) {
161 ERROR("seccomp: no default action set\n");
162 return NULL;
163 }
164
165 default_policy = resolve_action(blobmsg_get_string(tb[OCI_LINUX_SECCOMP_DEFAULTACTION]));
166
167 /* verify architecture while ignoring the x86_64 anomaly for now */
168 blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_ARCHITECTURES], rem) {
169 seccomp_arch = resolve_architecture(blobmsg_get_string(cur));
170 /* take the first useful arch for now */
171 if (seccomp_arch)
172 break;
173 }
174
175 if (ARCH_NR != seccomp_arch) {
176 ERROR("seccomp architecture doesn't match system\n");
177 return NULL;
178 }
179
180 blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_SYSCALLS], rem) {
181 blobmsg_parse(oci_linux_seccomp_syscalls_policy, __OCI_LINUX_SECCOMP_SYSCALLS_MAX, tbn, blobmsg_data(cur), blobmsg_len(cur));
182 blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_NAMES], remn)
183 sz += 2;
184
185 if (tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS])
186 blobmsg_for_each_attr(curarg, tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS], remargs)
187 sz++;
188 }
189
190 prog = malloc(sizeof(struct sock_fprog));
191 if (!prog)
192 return NULL;
193
194 filter = calloc(sz, sizeof(struct sock_filter));
195 if (!filter) {
196 ERROR("failed to allocate memory for seccomp filter\n");
197 goto errout2;
198 }
199
200 /* validate arch */
201 set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr);
202 set_filter(&filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 1, 0, ARCH_NR);
203 set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL);
204
205 /* get syscall */
206 set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr);
207
208 blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_SYSCALLS], rem) {
209 uint32_t action;
210 blobmsg_parse(oci_linux_seccomp_syscalls_policy, __OCI_LINUX_SECCOMP_SYSCALLS_MAX, tbn, blobmsg_data(cur), blobmsg_len(cur));
211 action = resolve_action(blobmsg_get_string(tbn[OCI_LINUX_SECCOMP_SYSCALLS_ACTION]));
212 if (tbn[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET]) {
213 if (action != SECCOMP_RET_ERRNO)
214 goto errout1;
215
216 action = SECCOMP_RET_ERROR(blobmsg_get_u32(tbn[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET]));
217 } else if (action == SECCOMP_RET_ERRNO)
218 action = SECCOMP_RET_ERROR(EPERM);
219
220 blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_NAMES], remn) {
221 sc = find_syscall(blobmsg_get_string(curn));
222 if (sc == -1) {
223 ERROR("unknown syscall '%s'\n", blobmsg_get_string(curn));
224 goto errout1;
225 }
226
227 /* add rule to filter */
228 set_filter(&filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 1, sc);
229 set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, action);
230
231 }
232 blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS], remn) {
233 blobmsg_parse(oci_linux_seccomp_syscalls_args_policy, __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX, tba, blobmsg_data(curn), blobmsg_len(curn));
234 /* ToDo: process args */
235 }
236 }
237
238 set_filter(&filter[idx], BPF_RET + BPF_K, 0, 0, default_policy);
239
240 prog->len = (unsigned short) idx + 1;
241 prog->filter = filter;
242
243 return prog;
244
245 errout1:
246 free(prog->filter);
247 errout2:
248 free(prog);
249 return NULL;
250 }
251
252
253 int applyOCIlinuxseccomp(struct sock_fprog *prog)
254 {
255 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
256 ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n");
257 goto errout;
258 }
259
260 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prog)) {
261 ERROR("prctl(PR_SET_SECCOMP) failed: %m\n");
262 goto errout;
263 }
264 free(prog);
265
266 return 0;
267
268 errout:
269 free(prog->filter);
270 free(prog);
271 return errno;
272 }