]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/seccomp.c
Merge pull request #692 from fwilson42/master
[mirror_lxc.git] / src / lxc / seccomp.c
CommitLineData
8f2c3a70
SH
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
250b1eec 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
8f2c3a70
SH
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>
d58c6ad0 30#include <sys/utsname.h>
6166fa6d 31#include <sys/mount.h>
f2363e38 32
769872f9 33#include "config.h"
8f2c3a70 34#include "lxcseccomp.h"
8f2c3a70
SH
35#include "log.h"
36
37lxc_log_define(lxc_seccomp, lxc);
38
50798138
SH
39static int parse_config_v1(FILE *f, struct lxc_conf *conf)
40{
41 char line[1024];
42 int ret;
43
44 while (fgets(line, 1024, f)) {
45 int nr;
46 ret = sscanf(line, "%d", &nr);
47 if (ret != 1)
48 return -1;
49 ret = seccomp_rule_add(
50#if HAVE_SCMP_FILTER_CTX
51 conf->seccomp_ctx,
52#endif
53 SCMP_ACT_ALLOW, nr, 0);
54 if (ret < 0) {
55 ERROR("failed loading allow rule for %d", nr);
56 return ret;
57 }
58 }
59 return 0;
60}
61
2b0ae718 62#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
50798138
SH
63static void remove_trailing_newlines(char *l)
64{
65 char *p = l;
66
67 while (*p)
68 p++;
69 while (--p >= l && *p == '\n')
70 *p = '\0';
71}
72
73static uint32_t get_v2_default_action(char *line)
74{
75 uint32_t ret_action = -1;
76
77 while (*line == ' ') line++;
78 // after 'whitelist' or 'blacklist' comes default behavior
79 if (strncmp(line, "kill", 4) == 0)
80 ret_action = SCMP_ACT_KILL;
81 else if (strncmp(line, "errno", 5) == 0) {
82 int e;
83 if (sscanf(line+5, "%d", &e) != 1) {
84 ERROR("Bad errno value in %s", line);
85 return -2;
86 }
87 ret_action = SCMP_ACT_ERRNO(e);
88 } else if (strncmp(line, "allow", 5) == 0)
89 ret_action = SCMP_ACT_ALLOW;
90 else if (strncmp(line, "trap", 4) == 0)
91 ret_action = SCMP_ACT_TRAP;
92 return ret_action;
93}
94
95static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action)
96{
97 char *p = strchr(line, ' ');
98 uint32_t ret;
99
100 if (!p)
101 return def_action;
102 *p = '\0';
103 p++;
104 while (*p == ' ')
105 p++;
106 if (!*p || *p == '#')
107 return def_action;
108 ret = get_v2_default_action(p);
109 switch(ret) {
110 case -2: return -1;
111 case -1: return def_action;
112 default: return ret;
113 }
114}
2b0ae718 115#endif
50798138 116
d58c6ad0
SH
117#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
118enum lxc_hostarch_t {
119 lxc_seccomp_arch_all = 0,
120 lxc_seccomp_arch_native,
121 lxc_seccomp_arch_i386,
122 lxc_seccomp_arch_amd64,
123 lxc_seccomp_arch_arm,
9d291dd2 124 lxc_seccomp_arch_arm64,
b4067426
BP
125 lxc_seccomp_arch_ppc64,
126 lxc_seccomp_arch_ppc64le,
127 lxc_seccomp_arch_ppc,
d58c6ad0
SH
128 lxc_seccomp_arch_unknown = 999,
129};
130
131int get_hostarch(void)
132{
133 struct utsname uts;
134 if (uname(&uts) < 0) {
135 SYSERROR("Failed to read host arch");
136 return -1;
137 }
138 if (strcmp(uts.machine, "i686") == 0)
139 return lxc_seccomp_arch_i386;
140 else if (strcmp(uts.machine, "x86_64") == 0)
141 return lxc_seccomp_arch_amd64;
142 else if (strncmp(uts.machine, "armv7", 5) == 0)
143 return lxc_seccomp_arch_arm;
9d291dd2
BP
144 else if (strncmp(uts.machine, "aarch64", 7) == 0)
145 return lxc_seccomp_arch_arm64;
b4067426
BP
146 else if (strncmp(uts.machine, "ppc64le", 7) == 0)
147 return lxc_seccomp_arch_ppc64le;
148 else if (strncmp(uts.machine, "ppc64", 5) == 0)
149 return lxc_seccomp_arch_ppc64;
150 else if (strncmp(uts.machine, "ppc", 3) == 0)
151 return lxc_seccomp_arch_ppc;
d58c6ad0
SH
152 return lxc_seccomp_arch_unknown;
153}
154
155scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action)
156{
157 scmp_filter_ctx ctx;
158 int ret;
159 uint32_t arch;
160
161 switch(n_arch) {
162 case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break;
163 case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break;
164 case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break;
9d291dd2
BP
165#ifdef SCMP_ARCH_AARCH64
166 case lxc_seccomp_arch_arm64: arch = SCMP_ARCH_AARCH64; break;
167#endif
b4067426
BP
168#ifdef SCMP_ARCH_PPC64LE
169 case lxc_seccomp_arch_ppc64le: arch = SCMP_ARCH_PPC64LE; break;
170#endif
171#ifdef SCMP_ARCH_PPC64
172 case lxc_seccomp_arch_ppc64: arch = SCMP_ARCH_PPC64; break;
173#endif
174#ifdef SCMP_ARCH_PPC
175 case lxc_seccomp_arch_ppc: arch = SCMP_ARCH_PPC; break;
176#endif
d58c6ad0
SH
177 default: return NULL;
178 }
179
180 if ((ctx = seccomp_init(default_policy_action)) == NULL) {
181 ERROR("Error initializing seccomp context");
182 return NULL;
183 }
184 if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) {
185 ERROR("failed to turn off n-new-privs");
186 seccomp_release(ctx);
187 return NULL;
188 }
189 ret = seccomp_arch_add(ctx, arch);
190 if (ret != 0) {
191 ERROR("Seccomp error %d (%s) adding arch: %d", ret,
192 strerror(ret), (int)n_arch);
193 seccomp_release(ctx);
194 return NULL;
195 }
196 if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) {
197 ERROR("Seccomp error removing native arch");
198 seccomp_release(ctx);
199 return NULL;
200 }
201
202 return ctx;
203}
204
205bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
206 uint32_t action)
207{
208 int nr, ret;
209
cd75548b 210 if (arch && seccomp_arch_exist(ctx, arch) != 0) {
d58c6ad0
SH
211 ERROR("BUG: seccomp: rule and context arch do not match (arch %d)", arch);
212 return false;
213 }
6166fa6d
SH
214
215 if (strncmp(line, "reject_force_umount", 19) == 0) {
216 INFO("Setting seccomp rule to reject force umounts\n");
217 ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(umount2),
218 1, SCMP_A1(SCMP_CMP_MASKED_EQ , MNT_FORCE , MNT_FORCE ));
219 if (ret < 0) {
220 ERROR("failed (%d) loading rule to reject force umount", ret);
221 return false;
222 }
223 return true;
224 }
225
cd75548b 226 nr = seccomp_syscall_resolve_name(line);
d58c6ad0
SH
227 if (nr == __NR_SCMP_ERROR) {
228 WARN("Seccomp: failed to resolve syscall: %s", line);
229 WARN("This syscall will NOT be blacklisted");
230 return true;
231 }
232 if (nr < 0) {
233 WARN("Seccomp: got negative # for syscall: %s", line);
234 WARN("This syscall will NOT be blacklisted");
235 return true;
236 }
237 ret = seccomp_rule_add_exact(ctx, action, nr, 0);
238 if (ret < 0) {
239 ERROR("failed (%d) loading rule for %s (nr %d action %d)", ret, line, nr, action);
240 return false;
241 }
242 return true;
243}
244
50798138
SH
245/*
246 * v2 consists of
247 * [x86]
248 * open
249 * read
250 * write
251 * close
252 * # a comment
253 * [x86_64]
254 * open
255 * read
256 * write
257 * close
258 */
259static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
260{
50798138
SH
261 char *p;
262 int ret;
d58c6ad0 263 scmp_filter_ctx compat_ctx = NULL;
50798138
SH
264 bool blacklist = false;
265 uint32_t default_policy_action = -1, default_rule_action = -1, action;
d58c6ad0
SH
266 enum lxc_hostarch_t native_arch = get_hostarch(),
267 cur_rule_arch = native_arch;
d6417887 268 uint32_t compat_arch = SCMP_ARCH_NATIVE;
50798138
SH
269
270 if (strncmp(line, "blacklist", 9) == 0)
271 blacklist = true;
272 else if (strncmp(line, "whitelist", 9) != 0) {
273 ERROR("Bad seccomp policy style: %s", line);
274 return -1;
275 }
276
277 if ((p = strchr(line, ' '))) {
278 default_policy_action = get_v2_default_action(p+1);
279 if (default_policy_action == -2)
280 return -1;
281 }
282
283 /* for blacklist, allow any syscall which has no rule */
284 if (blacklist) {
285 if (default_policy_action == -1)
286 default_policy_action = SCMP_ACT_ALLOW;
287 if (default_rule_action == -1)
288 default_rule_action = SCMP_ACT_KILL;
289 } else {
290 if (default_policy_action == -1)
291 default_policy_action = SCMP_ACT_KILL;
292 if (default_rule_action == -1)
293 default_rule_action = SCMP_ACT_ALLOW;
294 }
295
d58c6ad0
SH
296 if (native_arch == lxc_seccomp_arch_amd64) {
297 cur_rule_arch = lxc_seccomp_arch_all;
d6417887 298 compat_arch = SCMP_ARCH_X86;
473ebc77
SH
299 // Detect if we are on x86_64 kernel with 32-bit userspace
300 if (seccomp_arch_exist(conf->seccomp_ctx, SCMP_ARCH_X86)) {
301 compat_ctx = conf->seccomp_ctx;
302 conf->seccomp_ctx = get_new_ctx(lxc_seccomp_arch_amd64,
303 default_policy_action);
304 if (!conf->seccomp_ctx)
305 goto bad;
306 } else {
307 compat_ctx = get_new_ctx(lxc_seccomp_arch_i386,
308 default_policy_action);
309 if (!compat_ctx)
310 goto bad;
311 }
d58c6ad0
SH
312 }
313
50798138
SH
314 if (default_policy_action != SCMP_ACT_KILL) {
315 ret = seccomp_reset(conf->seccomp_ctx, default_policy_action);
316 if (ret != 0) {
317 ERROR("Error re-initializing seccomp");
318 return -1;
319 }
320 if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) {
321 ERROR("failed to turn off n-new-privs");
322 return -1;
323 }
324 }
325
326 while (fgets(line, 1024, f)) {
50798138
SH
327
328 if (line[0] == '#')
329 continue;
330 if (strlen(line) == 0)
331 continue;
332 remove_trailing_newlines(line);
333 INFO("processing: .%s.", line);
334 if (line[0] == '[') {
335 // read the architecture for next set of rules
336 if (strcmp(line, "[x86]") == 0 ||
d58c6ad0
SH
337 strcmp(line, "[X86]") == 0) {
338 if (native_arch != lxc_seccomp_arch_i386 &&
339 native_arch != lxc_seccomp_arch_amd64) {
340 cur_rule_arch = lxc_seccomp_arch_unknown;
341 continue;
342 }
343 cur_rule_arch = lxc_seccomp_arch_i386;
d58c6ad0
SH
344 } else if (strcmp(line, "[X86_64]") == 0 ||
345 strcmp(line, "[x86_64]") == 0) {
346 if (native_arch != lxc_seccomp_arch_amd64) {
347 cur_rule_arch = lxc_seccomp_arch_unknown;
348 continue;
349 }
350 cur_rule_arch = lxc_seccomp_arch_amd64;
d58c6ad0
SH
351 } else if (strcmp(line, "[all]") == 0 ||
352 strcmp(line, "[ALL]") == 0) {
353 cur_rule_arch = lxc_seccomp_arch_all;
d58c6ad0 354 }
2b0ae718 355#ifdef SCMP_ARCH_ARM
50798138 356 else if (strcmp(line, "[arm]") == 0 ||
d58c6ad0
SH
357 strcmp(line, "[ARM]") == 0) {
358 if (native_arch != lxc_seccomp_arch_arm) {
359 cur_rule_arch = lxc_seccomp_arch_unknown;
360 continue;
361 }
362 cur_rule_arch = lxc_seccomp_arch_arm;
d58c6ad0 363 }
b4067426 364#endif
9d291dd2
BP
365#ifdef SCMP_ARCH_AARCH64
366 else if (strcmp(line, "[arm64]") == 0 ||
367 strcmp(line, "[ARM64]") == 0) {
368 if (native_arch != lxc_seccomp_arch_arm64) {
369 cur_rule_arch = lxc_seccomp_arch_unknown;
370 continue;
371 }
372 cur_rule_arch = lxc_seccomp_arch_arm64;
373 }
374#endif
b4067426
BP
375#ifdef SCMP_ARCH_PPC64LE
376 else if (strcmp(line, "[ppc64le]") == 0 ||
377 strcmp(line, "[PPC64LE]") == 0) {
378 if (native_arch != lxc_seccomp_arch_ppc64le) {
379 cur_rule_arch = lxc_seccomp_arch_unknown;
380 continue;
381 }
382 cur_rule_arch = lxc_seccomp_arch_ppc64le;
383 }
384#endif
385#ifdef SCMP_ARCH_PPC64
386 else if (strcmp(line, "[ppc64]") == 0 ||
387 strcmp(line, "[PPC64]") == 0) {
388 if (native_arch != lxc_seccomp_arch_ppc64) {
389 cur_rule_arch = lxc_seccomp_arch_unknown;
390 continue;
391 }
392 cur_rule_arch = lxc_seccomp_arch_ppc64;
393 }
394#endif
395#ifdef SCMP_ARCH_PPC
396 else if (strcmp(line, "[ppc]") == 0 ||
397 strcmp(line, "[PPC]") == 0) {
398 if (native_arch != lxc_seccomp_arch_ppc) {
399 cur_rule_arch = lxc_seccomp_arch_unknown;
400 continue;
401 }
402 cur_rule_arch = lxc_seccomp_arch_ppc;
403 }
2b0ae718 404#endif
50798138
SH
405 else
406 goto bad_arch;
d58c6ad0 407
50798138
SH
408 continue;
409 }
410
d58c6ad0
SH
411 /* irrelevant arch - i.e. arm on i386 */
412 if (cur_rule_arch == lxc_seccomp_arch_unknown)
413 continue;
414
415 /* read optional action which follows the syscall */
50798138
SH
416 action = get_and_clear_v2_action(line, default_rule_action);
417 if (action == -1) {
418 ERROR("Failed to interpret action");
419 goto bad_rule;
420 }
d58c6ad0 421
d6417887
WB
422 if (cur_rule_arch == native_arch ||
423 cur_rule_arch == lxc_seccomp_arch_native ||
424 compat_arch == SCMP_ARCH_NATIVE) {
425 INFO("Adding native rule for %s action %d", line, action);
cd75548b 426 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
d58c6ad0 427 goto bad_rule;
50798138 428 }
d6417887
WB
429 else if (cur_rule_arch != lxc_seccomp_arch_all) {
430 INFO("Adding compat-only rule for %s action %d", line, action);
431 if (!do_resolve_add_rule(compat_arch, line, compat_ctx, action))
432 goto bad_rule;
433 }
434 else {
435 INFO("Adding native rule for %s action %d", line, action);
436 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
437 goto bad_rule;
d58c6ad0 438 INFO("Adding compat rule for %s action %d", line, action);
d6417887 439 if (!do_resolve_add_rule(compat_arch, line, compat_ctx, action))
d58c6ad0 440 goto bad_rule;
50798138
SH
441 }
442 }
d58c6ad0
SH
443
444 if (compat_ctx) {
445 INFO("Merging in the compat seccomp ctx into the main one");
446 if (seccomp_merge(conf->seccomp_ctx, compat_ctx) != 0) {
447 ERROR("Error merging i386 seccomp contexts");
448 goto bad;
50798138
SH
449 }
450 }
6166fa6d 451
50798138
SH
452 return 0;
453
454bad_arch:
455 ERROR("Unsupported arch: %s", line);
456bad_rule:
d58c6ad0
SH
457bad:
458 if (compat_ctx)
459 seccomp_release(compat_ctx);
50798138 460 return -1;
d58c6ad0
SH
461}
462#else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
463static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
464{
50798138 465 return -1;
50798138 466}
d58c6ad0 467#endif /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
50798138 468
8f2c3a70
SH
469/*
470 * The first line of the config file has a policy language version
471 * the second line has some directives
472 * then comes policy subject to the directives
473 * right now version must be '1'
474 * the directives must include 'whitelist' (only type of policy currently
475 * supported) and can include 'debug' (though debug is not yet supported).
476 */
477static int parse_config(FILE *f, struct lxc_conf *conf)
478{
479 char line[1024];
480 int ret, version;
481
482 ret = fscanf(f, "%d\n", &version);
50798138 483 if (ret != 1 || (version != 1 && version != 2)) {
8f2c3a70
SH
484 ERROR("invalid version");
485 return -1;
486 }
487 if (!fgets(line, 1024, f)) {
488 ERROR("invalid config file");
489 return -1;
490 }
50798138 491 if (version == 1 && !strstr(line, "whitelist")) {
8f2c3a70
SH
492 ERROR("only whitelist policy is supported");
493 return -1;
494 }
50798138 495
8f2c3a70
SH
496 if (strstr(line, "debug")) {
497 ERROR("debug not yet implemented");
498 return -1;
499 }
50798138
SH
500
501 if (version == 1)
502 return parse_config_v1(f, conf);
503 return parse_config_v2(f, line, conf);
8f2c3a70
SH
504}
505
cd75548b
SH
506/*
507 * use_seccomp: return true if we should try and apply a seccomp policy
508 * if defined for the container.
509 * This will return false if
510 * 1. seccomp is not enabled in the kernel
511 * 2. a seccomp policy is already enabled for this task
512 */
513static bool use_seccomp(void)
d58c6ad0
SH
514{
515 FILE *f = fopen("/proc/self/status", "r");
516 char line[1024];
cd75548b
SH
517 bool already_enabled = false;
518 bool found = false;
d58c6ad0
SH
519 int ret, v;
520
521 if (!f)
cd75548b 522 return true;
d58c6ad0
SH
523
524 while (fgets(line, 1024, f)) {
525 if (strncmp(line, "Seccomp:", 8) == 0) {
cd75548b 526 found = true;
d58c6ad0 527 ret = sscanf(line+8, "%d", &v);
cd75548b
SH
528 if (ret == 1 && v != 0)
529 already_enabled = true;
530 break;
d58c6ad0
SH
531 }
532 }
533
d58c6ad0 534 fclose(f);
cd75548b
SH
535 if (!found) { /* no Seccomp line, no seccomp in kernel */
536 INFO("Seccomp is not enabled in the kernel");
537 return false;
538 }
539 if (already_enabled) { /* already seccomp-confined */
540 INFO("Already seccomp-confined, not loading new policy");
541 return false;
542 }
543 return true;
d58c6ad0
SH
544}
545
8f2c3a70
SH
546int lxc_read_seccomp_config(struct lxc_conf *conf)
547{
548 FILE *f;
549 int ret;
550
769872f9
SH
551 if (!conf->seccomp)
552 return 0;
553
cd75548b 554 if (!use_seccomp())
d58c6ad0 555 return 0;
769872f9
SH
556#if HAVE_SCMP_FILTER_CTX
557 /* XXX for debug, pass in SCMP_ACT_TRAP */
50798138 558 conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
769872f9
SH
559 ret = !conf->seccomp_ctx;
560#else
50798138 561 ret = seccomp_init(SCMP_ACT_KILL) < 0;
769872f9
SH
562#endif
563 if (ret) {
8f2c3a70
SH
564 ERROR("failed initializing seccomp");
565 return -1;
566 }
8f2c3a70
SH
567
568 /* turn of no-new-privs. We don't want it in lxc, and it breaks
569 * with apparmor */
769872f9
SH
570 if (seccomp_attr_set(
571#if HAVE_SCMP_FILTER_CTX
572 conf->seccomp_ctx,
573#endif
574 SCMP_FLTATR_CTL_NNP, 0)) {
959aee9c 575 ERROR("failed to turn off n-new-privs");
8f2c3a70
SH
576 return -1;
577 }
578
579 f = fopen(conf->seccomp, "r");
580 if (!f) {
959aee9c 581 SYSERROR("failed to open seccomp policy file %s", conf->seccomp);
8f2c3a70
SH
582 return -1;
583 }
584 ret = parse_config(f, conf);
585 fclose(f);
586 return ret;
587}
588
589int lxc_seccomp_load(struct lxc_conf *conf)
590{
591 int ret;
592 if (!conf->seccomp)
593 return 0;
cd75548b 594 if (!use_seccomp())
d58c6ad0 595 return 0;
769872f9
SH
596 ret = seccomp_load(
597#if HAVE_SCMP_FILTER_CTX
598 conf->seccomp_ctx
599#endif
600 );
8f2c3a70
SH
601 if (ret < 0) {
602 ERROR("Error loading the seccomp policy");
603 return -1;
604 }
605 return 0;
606}
769872f9
SH
607
608void lxc_seccomp_free(struct lxc_conf *conf) {
f10fad2f
ME
609 free(conf->seccomp);
610 conf->seccomp = NULL;
769872f9
SH
611#if HAVE_SCMP_FILTER_CTX
612 if (conf->seccomp_ctx) {
613 seccomp_release(conf->seccomp_ctx);
614 conf->seccomp_ctx = NULL;
615 }
616#endif
617}