]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/seccomp.c
seccomp: fix 32-bit rules
[mirror_lxc.git] / src / lxc / seccomp.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright Canonical, Inc. 2012
5 *
6 * Authors:
7 * Serge Hallyn <serge.hallyn@canonical.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <seccomp.h>
28 #include <errno.h>
29 #include <seccomp.h>
30 #include <sys/utsname.h>
31
32 #include "config.h"
33 #include "lxcseccomp.h"
34 #include "log.h"
35
36 lxc_log_define(lxc_seccomp, lxc);
37
38 static int parse_config_v1(FILE *f, struct lxc_conf *conf)
39 {
40 char line[1024];
41 int ret;
42
43 while (fgets(line, 1024, f)) {
44 int nr;
45 ret = sscanf(line, "%d", &nr);
46 if (ret != 1)
47 return -1;
48 ret = seccomp_rule_add(
49 #if HAVE_SCMP_FILTER_CTX
50 conf->seccomp_ctx,
51 #endif
52 SCMP_ACT_ALLOW, nr, 0);
53 if (ret < 0) {
54 ERROR("failed loading allow rule for %d", nr);
55 return ret;
56 }
57 }
58 return 0;
59 }
60
61 #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
62 static void remove_trailing_newlines(char *l)
63 {
64 char *p = l;
65
66 while (*p)
67 p++;
68 while (--p >= l && *p == '\n')
69 *p = '\0';
70 }
71
72 static uint32_t get_v2_default_action(char *line)
73 {
74 uint32_t ret_action = -1;
75
76 while (*line == ' ') line++;
77 // after 'whitelist' or 'blacklist' comes default behavior
78 if (strncmp(line, "kill", 4) == 0)
79 ret_action = SCMP_ACT_KILL;
80 else if (strncmp(line, "errno", 5) == 0) {
81 int e;
82 if (sscanf(line+5, "%d", &e) != 1) {
83 ERROR("Bad errno value in %s", line);
84 return -2;
85 }
86 ret_action = SCMP_ACT_ERRNO(e);
87 } else if (strncmp(line, "allow", 5) == 0)
88 ret_action = SCMP_ACT_ALLOW;
89 else if (strncmp(line, "trap", 4) == 0)
90 ret_action = SCMP_ACT_TRAP;
91 return ret_action;
92 }
93
94 static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action)
95 {
96 char *p = strchr(line, ' ');
97 uint32_t ret;
98
99 if (!p)
100 return def_action;
101 *p = '\0';
102 p++;
103 while (*p == ' ')
104 p++;
105 if (!*p || *p == '#')
106 return def_action;
107 ret = get_v2_default_action(p);
108 switch(ret) {
109 case -2: return -1;
110 case -1: return def_action;
111 default: return ret;
112 }
113 }
114 #endif
115
116 #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
117 enum lxc_hostarch_t {
118 lxc_seccomp_arch_all = 0,
119 lxc_seccomp_arch_native,
120 lxc_seccomp_arch_i386,
121 lxc_seccomp_arch_amd64,
122 lxc_seccomp_arch_arm,
123 lxc_seccomp_arch_unknown = 999,
124 };
125
126 int get_hostarch(void)
127 {
128 struct utsname uts;
129 if (uname(&uts) < 0) {
130 SYSERROR("Failed to read host arch");
131 return -1;
132 }
133 if (strcmp(uts.machine, "i686") == 0)
134 return lxc_seccomp_arch_i386;
135 else if (strcmp(uts.machine, "x86_64") == 0)
136 return lxc_seccomp_arch_amd64;
137 else if (strncmp(uts.machine, "armv7", 5) == 0)
138 return lxc_seccomp_arch_arm;
139 return lxc_seccomp_arch_unknown;
140 }
141
142 scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action)
143 {
144 scmp_filter_ctx ctx;
145 int ret;
146 uint32_t arch;
147
148 switch(n_arch) {
149 case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break;
150 case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break;
151 case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break;
152 default: return NULL;
153 }
154
155 if ((ctx = seccomp_init(default_policy_action)) == NULL) {
156 ERROR("Error initializing seccomp context");
157 return NULL;
158 }
159 if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) {
160 ERROR("failed to turn off n-new-privs");
161 seccomp_release(ctx);
162 return NULL;
163 }
164 ret = seccomp_arch_add(ctx, arch);
165 if (ret != 0) {
166 ERROR("Seccomp error %d (%s) adding arch: %d", ret,
167 strerror(ret), (int)n_arch);
168 seccomp_release(ctx);
169 return NULL;
170 }
171 if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) {
172 ERROR("Seccomp error removing native arch");
173 seccomp_release(ctx);
174 return NULL;
175 }
176
177 return ctx;
178 }
179
180 bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
181 uint32_t action)
182 {
183 int nr, ret;
184
185 if (arch && seccomp_arch_exist(ctx, arch) != 0) {
186 ERROR("BUG: seccomp: rule and context arch do not match (arch %d)", arch);
187 return false;
188 }
189 nr = seccomp_syscall_resolve_name(line);
190 if (nr == __NR_SCMP_ERROR) {
191 WARN("Seccomp: failed to resolve syscall: %s", line);
192 WARN("This syscall will NOT be blacklisted");
193 return true;
194 }
195 if (nr < 0) {
196 WARN("Seccomp: got negative # for syscall: %s", line);
197 WARN("This syscall will NOT be blacklisted");
198 return true;
199 }
200 ret = seccomp_rule_add_exact(ctx, action, nr, 0);
201 if (ret < 0) {
202 ERROR("failed (%d) loading rule for %s (nr %d action %d)", ret, line, nr, action);
203 return false;
204 }
205 return true;
206 }
207
208 /*
209 * v2 consists of
210 * [x86]
211 * open
212 * read
213 * write
214 * close
215 * # a comment
216 * [x86_64]
217 * open
218 * read
219 * write
220 * close
221 */
222 static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
223 {
224 char *p;
225 int ret;
226 scmp_filter_ctx compat_ctx = NULL;
227 bool blacklist = false;
228 uint32_t default_policy_action = -1, default_rule_action = -1, action;
229 enum lxc_hostarch_t native_arch = get_hostarch(),
230 cur_rule_arch = native_arch;
231
232 if (strncmp(line, "blacklist", 9) == 0)
233 blacklist = true;
234 else if (strncmp(line, "whitelist", 9) != 0) {
235 ERROR("Bad seccomp policy style: %s", line);
236 return -1;
237 }
238
239 if ((p = strchr(line, ' '))) {
240 default_policy_action = get_v2_default_action(p+1);
241 if (default_policy_action == -2)
242 return -1;
243 }
244
245 /* for blacklist, allow any syscall which has no rule */
246 if (blacklist) {
247 if (default_policy_action == -1)
248 default_policy_action = SCMP_ACT_ALLOW;
249 if (default_rule_action == -1)
250 default_rule_action = SCMP_ACT_KILL;
251 } else {
252 if (default_policy_action == -1)
253 default_policy_action = SCMP_ACT_KILL;
254 if (default_rule_action == -1)
255 default_rule_action = SCMP_ACT_ALLOW;
256 }
257
258 if (native_arch == lxc_seccomp_arch_amd64) {
259 cur_rule_arch = lxc_seccomp_arch_all;
260 compat_ctx = get_new_ctx(lxc_seccomp_arch_i386,
261 default_policy_action);
262 if (!compat_ctx)
263 goto bad;
264 }
265
266 if (default_policy_action != SCMP_ACT_KILL) {
267 ret = seccomp_reset(conf->seccomp_ctx, default_policy_action);
268 if (ret != 0) {
269 ERROR("Error re-initializing seccomp");
270 return -1;
271 }
272 if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) {
273 ERROR("failed to turn off n-new-privs");
274 return -1;
275 }
276 }
277
278 while (fgets(line, 1024, f)) {
279
280 if (line[0] == '#')
281 continue;
282 if (strlen(line) == 0)
283 continue;
284 remove_trailing_newlines(line);
285 INFO("processing: .%s.", line);
286 if (line[0] == '[') {
287 // read the architecture for next set of rules
288 if (strcmp(line, "[x86]") == 0 ||
289 strcmp(line, "[X86]") == 0) {
290 if (native_arch != lxc_seccomp_arch_i386 &&
291 native_arch != lxc_seccomp_arch_amd64) {
292 cur_rule_arch = lxc_seccomp_arch_unknown;
293 continue;
294 }
295 cur_rule_arch = lxc_seccomp_arch_i386;
296 if (native_arch == lxc_seccomp_arch_amd64) {
297 if (compat_ctx)
298 continue;
299 compat_ctx = get_new_ctx(lxc_seccomp_arch_i386,
300 default_policy_action);
301 if (!compat_ctx)
302 goto bad;
303 }
304 } else if (strcmp(line, "[X86_64]") == 0 ||
305 strcmp(line, "[x86_64]") == 0) {
306 if (native_arch != lxc_seccomp_arch_amd64) {
307 cur_rule_arch = lxc_seccomp_arch_unknown;
308 continue;
309 }
310 cur_rule_arch = lxc_seccomp_arch_amd64;
311 } else if (strcmp(line, "[all]") == 0 ||
312 strcmp(line, "[ALL]") == 0) {
313 cur_rule_arch = lxc_seccomp_arch_all;
314 if (native_arch == lxc_seccomp_arch_amd64 && !compat_ctx) {
315 if (compat_ctx)
316 continue;
317 compat_ctx = get_new_ctx(lxc_seccomp_arch_i386,
318 default_policy_action);
319 if (!compat_ctx)
320 goto bad;
321 }
322 }
323 #ifdef SCMP_ARCH_ARM
324 else if (strcmp(line, "[arm]") == 0 ||
325 strcmp(line, "[ARM]") == 0) {
326 if (native_arch != lxc_seccomp_arch_arm) {
327 cur_rule_arch = lxc_seccomp_arch_unknown;
328 continue;
329 }
330 cur_rule_arch = lxc_seccomp_arch_arm;
331 }
332 #endif
333 else
334 goto bad_arch;
335
336 continue;
337 }
338
339 /* irrelevant arch - i.e. arm on i386 */
340 if (cur_rule_arch == lxc_seccomp_arch_unknown)
341 continue;
342
343 /* read optional action which follows the syscall */
344 action = get_and_clear_v2_action(line, default_rule_action);
345 if (action == -1) {
346 ERROR("Failed to interpret action");
347 goto bad_rule;
348 }
349
350 /*
351 * TODO generalize - if !is_compat_only(native_arch, cur_rule_arch)
352 *
353 * in other words, the rule is 32-bit only, on 64-bit host; don't run
354 * the rule against the native arch.
355 */
356 if (!(cur_rule_arch == lxc_seccomp_arch_i386 &&
357 native_arch == lxc_seccomp_arch_amd64)) {
358 INFO("Adding non-compat rule for %s action %d", line, action);
359 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
360 goto bad_rule;
361 }
362
363 /*
364 * TODO generalize - if need_compat(native_arch, cur_rule_arch)
365 */
366 if (native_arch == lxc_seccomp_arch_amd64 &&
367 cur_rule_arch != lxc_seccomp_arch_amd64) {
368 int nr1, nr2;
369 INFO("Adding compat rule for %s action %d", line, action);
370 nr1 = seccomp_syscall_resolve_name_arch(SCMP_ARCH_X86, line);
371 nr2 = seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE, line);
372 if (nr1 == nr2) {
373 /* If the syscall # is the same for 32- and 64-bit, then we cannot
374 * apply it to the compat_ctx. So apply it to the noncompat ctx.
375 * We may already have done so, but that's ok
376 */
377 INFO("Adding non-compat rule bc nr1 == nr2 (%d, %d)", nr1, nr2);
378 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
379 goto bad_rule;
380 continue;
381 }
382 INFO("Really adding compat rule bc nr1 == nr2 (%d, %d)", nr1, nr2);
383 if (!do_resolve_add_rule(SCMP_ARCH_X86, line,
384 compat_ctx, action))
385 goto bad_rule;
386 }
387 }
388
389 if (compat_ctx) {
390 INFO("Merging in the compat seccomp ctx into the main one");
391 if (seccomp_merge(conf->seccomp_ctx, compat_ctx) != 0) {
392 ERROR("Error merging i386 seccomp contexts");
393 goto bad;
394 }
395 }
396 return 0;
397
398 bad_arch:
399 ERROR("Unsupported arch: %s", line);
400 bad_rule:
401 bad:
402 if (compat_ctx)
403 seccomp_release(compat_ctx);
404 return -1;
405 }
406 #else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
407 static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
408 {
409 return -1;
410 }
411 #endif /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
412
413 /*
414 * The first line of the config file has a policy language version
415 * the second line has some directives
416 * then comes policy subject to the directives
417 * right now version must be '1'
418 * the directives must include 'whitelist' (only type of policy currently
419 * supported) and can include 'debug' (though debug is not yet supported).
420 */
421 static int parse_config(FILE *f, struct lxc_conf *conf)
422 {
423 char line[1024];
424 int ret, version;
425
426 ret = fscanf(f, "%d\n", &version);
427 if (ret != 1 || (version != 1 && version != 2)) {
428 ERROR("invalid version");
429 return -1;
430 }
431 if (!fgets(line, 1024, f)) {
432 ERROR("invalid config file");
433 return -1;
434 }
435 if (version == 1 && !strstr(line, "whitelist")) {
436 ERROR("only whitelist policy is supported");
437 return -1;
438 }
439
440 if (strstr(line, "debug")) {
441 ERROR("debug not yet implemented");
442 return -1;
443 }
444
445 if (version == 1)
446 return parse_config_v1(f, conf);
447 return parse_config_v2(f, line, conf);
448 }
449
450 /*
451 * use_seccomp: return true if we should try and apply a seccomp policy
452 * if defined for the container.
453 * This will return false if
454 * 1. seccomp is not enabled in the kernel
455 * 2. a seccomp policy is already enabled for this task
456 */
457 static bool use_seccomp(void)
458 {
459 FILE *f = fopen("/proc/self/status", "r");
460 char line[1024];
461 bool already_enabled = false;
462 bool found = false;
463 int ret, v;
464
465 if (!f)
466 return true;
467
468 while (fgets(line, 1024, f)) {
469 if (strncmp(line, "Seccomp:", 8) == 0) {
470 found = true;
471 ret = sscanf(line+8, "%d", &v);
472 if (ret == 1 && v != 0)
473 already_enabled = true;
474 break;
475 }
476 }
477
478 fclose(f);
479 if (!found) { /* no Seccomp line, no seccomp in kernel */
480 INFO("Seccomp is not enabled in the kernel");
481 return false;
482 }
483 if (already_enabled) { /* already seccomp-confined */
484 INFO("Already seccomp-confined, not loading new policy");
485 return false;
486 }
487 return true;
488 }
489
490 int lxc_read_seccomp_config(struct lxc_conf *conf)
491 {
492 FILE *f;
493 int ret;
494
495 if (!conf->seccomp)
496 return 0;
497
498 if (!use_seccomp())
499 return 0;
500 #if HAVE_SCMP_FILTER_CTX
501 /* XXX for debug, pass in SCMP_ACT_TRAP */
502 conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
503 ret = !conf->seccomp_ctx;
504 #else
505 ret = seccomp_init(SCMP_ACT_KILL) < 0;
506 #endif
507 if (ret) {
508 ERROR("failed initializing seccomp");
509 return -1;
510 }
511
512 /* turn of no-new-privs. We don't want it in lxc, and it breaks
513 * with apparmor */
514 if (seccomp_attr_set(
515 #if HAVE_SCMP_FILTER_CTX
516 conf->seccomp_ctx,
517 #endif
518 SCMP_FLTATR_CTL_NNP, 0)) {
519 ERROR("failed to turn off n-new-privs");
520 return -1;
521 }
522
523 f = fopen(conf->seccomp, "r");
524 if (!f) {
525 SYSERROR("failed to open seccomp policy file %s", conf->seccomp);
526 return -1;
527 }
528 ret = parse_config(f, conf);
529 fclose(f);
530 return ret;
531 }
532
533 int lxc_seccomp_load(struct lxc_conf *conf)
534 {
535 int ret;
536 if (!conf->seccomp)
537 return 0;
538 if (!use_seccomp())
539 return 0;
540 ret = seccomp_load(
541 #if HAVE_SCMP_FILTER_CTX
542 conf->seccomp_ctx
543 #endif
544 );
545 if (ret < 0) {
546 ERROR("Error loading the seccomp policy");
547 return -1;
548 }
549 return 0;
550 }
551
552 void lxc_seccomp_free(struct lxc_conf *conf) {
553 if (conf->seccomp) {
554 free(conf->seccomp);
555 conf->seccomp = NULL;
556 }
557 #if HAVE_SCMP_FILTER_CTX
558 if (conf->seccomp_ctx) {
559 seccomp_release(conf->seccomp_ctx);
560 conf->seccomp_ctx = NULL;
561 }
562 #endif
563 }