]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/seccomp.c
utils: dialback setproctitle failure message
[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;
d58c6ad0
SH
299 compat_ctx = get_new_ctx(lxc_seccomp_arch_i386,
300 default_policy_action);
301 if (!compat_ctx)
302 goto bad;
303 }
304
50798138
SH
305 if (default_policy_action != SCMP_ACT_KILL) {
306 ret = seccomp_reset(conf->seccomp_ctx, default_policy_action);
307 if (ret != 0) {
308 ERROR("Error re-initializing seccomp");
309 return -1;
310 }
311 if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) {
312 ERROR("failed to turn off n-new-privs");
313 return -1;
314 }
315 }
316
317 while (fgets(line, 1024, f)) {
50798138
SH
318
319 if (line[0] == '#')
320 continue;
321 if (strlen(line) == 0)
322 continue;
323 remove_trailing_newlines(line);
324 INFO("processing: .%s.", line);
325 if (line[0] == '[') {
326 // read the architecture for next set of rules
327 if (strcmp(line, "[x86]") == 0 ||
d58c6ad0
SH
328 strcmp(line, "[X86]") == 0) {
329 if (native_arch != lxc_seccomp_arch_i386 &&
330 native_arch != lxc_seccomp_arch_amd64) {
331 cur_rule_arch = lxc_seccomp_arch_unknown;
332 continue;
333 }
334 cur_rule_arch = lxc_seccomp_arch_i386;
d58c6ad0
SH
335 } else if (strcmp(line, "[X86_64]") == 0 ||
336 strcmp(line, "[x86_64]") == 0) {
337 if (native_arch != lxc_seccomp_arch_amd64) {
338 cur_rule_arch = lxc_seccomp_arch_unknown;
339 continue;
340 }
341 cur_rule_arch = lxc_seccomp_arch_amd64;
d58c6ad0
SH
342 } else if (strcmp(line, "[all]") == 0 ||
343 strcmp(line, "[ALL]") == 0) {
344 cur_rule_arch = lxc_seccomp_arch_all;
d58c6ad0 345 }
2b0ae718 346#ifdef SCMP_ARCH_ARM
50798138 347 else if (strcmp(line, "[arm]") == 0 ||
d58c6ad0
SH
348 strcmp(line, "[ARM]") == 0) {
349 if (native_arch != lxc_seccomp_arch_arm) {
350 cur_rule_arch = lxc_seccomp_arch_unknown;
351 continue;
352 }
353 cur_rule_arch = lxc_seccomp_arch_arm;
d58c6ad0 354 }
b4067426 355#endif
9d291dd2
BP
356#ifdef SCMP_ARCH_AARCH64
357 else if (strcmp(line, "[arm64]") == 0 ||
358 strcmp(line, "[ARM64]") == 0) {
359 if (native_arch != lxc_seccomp_arch_arm64) {
360 cur_rule_arch = lxc_seccomp_arch_unknown;
361 continue;
362 }
363 cur_rule_arch = lxc_seccomp_arch_arm64;
364 }
365#endif
b4067426
BP
366#ifdef SCMP_ARCH_PPC64LE
367 else if (strcmp(line, "[ppc64le]") == 0 ||
368 strcmp(line, "[PPC64LE]") == 0) {
369 if (native_arch != lxc_seccomp_arch_ppc64le) {
370 cur_rule_arch = lxc_seccomp_arch_unknown;
371 continue;
372 }
373 cur_rule_arch = lxc_seccomp_arch_ppc64le;
374 }
375#endif
376#ifdef SCMP_ARCH_PPC64
377 else if (strcmp(line, "[ppc64]") == 0 ||
378 strcmp(line, "[PPC64]") == 0) {
379 if (native_arch != lxc_seccomp_arch_ppc64) {
380 cur_rule_arch = lxc_seccomp_arch_unknown;
381 continue;
382 }
383 cur_rule_arch = lxc_seccomp_arch_ppc64;
384 }
385#endif
386#ifdef SCMP_ARCH_PPC
387 else if (strcmp(line, "[ppc]") == 0 ||
388 strcmp(line, "[PPC]") == 0) {
389 if (native_arch != lxc_seccomp_arch_ppc) {
390 cur_rule_arch = lxc_seccomp_arch_unknown;
391 continue;
392 }
393 cur_rule_arch = lxc_seccomp_arch_ppc;
394 }
2b0ae718 395#endif
50798138
SH
396 else
397 goto bad_arch;
d58c6ad0 398
50798138
SH
399 continue;
400 }
401
d58c6ad0
SH
402 /* irrelevant arch - i.e. arm on i386 */
403 if (cur_rule_arch == lxc_seccomp_arch_unknown)
404 continue;
405
406 /* read optional action which follows the syscall */
50798138
SH
407 action = get_and_clear_v2_action(line, default_rule_action);
408 if (action == -1) {
409 ERROR("Failed to interpret action");
410 goto bad_rule;
411 }
d58c6ad0 412
d6417887
WB
413 if (cur_rule_arch == native_arch ||
414 cur_rule_arch == lxc_seccomp_arch_native ||
415 compat_arch == SCMP_ARCH_NATIVE) {
416 INFO("Adding native rule for %s action %d", line, action);
cd75548b 417 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
d58c6ad0 418 goto bad_rule;
50798138 419 }
d6417887
WB
420 else if (cur_rule_arch != lxc_seccomp_arch_all) {
421 INFO("Adding compat-only rule for %s action %d", line, action);
422 if (!do_resolve_add_rule(compat_arch, line, compat_ctx, action))
423 goto bad_rule;
424 }
425 else {
426 INFO("Adding native rule for %s action %d", line, action);
427 if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action))
428 goto bad_rule;
d58c6ad0 429 INFO("Adding compat rule for %s action %d", line, action);
d6417887 430 if (!do_resolve_add_rule(compat_arch, line, compat_ctx, action))
d58c6ad0 431 goto bad_rule;
50798138
SH
432 }
433 }
d58c6ad0
SH
434
435 if (compat_ctx) {
436 INFO("Merging in the compat seccomp ctx into the main one");
437 if (seccomp_merge(conf->seccomp_ctx, compat_ctx) != 0) {
438 ERROR("Error merging i386 seccomp contexts");
439 goto bad;
50798138
SH
440 }
441 }
6166fa6d 442
50798138
SH
443 return 0;
444
445bad_arch:
446 ERROR("Unsupported arch: %s", line);
447bad_rule:
d58c6ad0
SH
448bad:
449 if (compat_ctx)
450 seccomp_release(compat_ctx);
50798138 451 return -1;
d58c6ad0
SH
452}
453#else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
454static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
455{
50798138 456 return -1;
50798138 457}
d58c6ad0 458#endif /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
50798138 459
8f2c3a70
SH
460/*
461 * The first line of the config file has a policy language version
462 * the second line has some directives
463 * then comes policy subject to the directives
464 * right now version must be '1'
465 * the directives must include 'whitelist' (only type of policy currently
466 * supported) and can include 'debug' (though debug is not yet supported).
467 */
468static int parse_config(FILE *f, struct lxc_conf *conf)
469{
470 char line[1024];
471 int ret, version;
472
473 ret = fscanf(f, "%d\n", &version);
50798138 474 if (ret != 1 || (version != 1 && version != 2)) {
8f2c3a70
SH
475 ERROR("invalid version");
476 return -1;
477 }
478 if (!fgets(line, 1024, f)) {
479 ERROR("invalid config file");
480 return -1;
481 }
50798138 482 if (version == 1 && !strstr(line, "whitelist")) {
8f2c3a70
SH
483 ERROR("only whitelist policy is supported");
484 return -1;
485 }
50798138 486
8f2c3a70
SH
487 if (strstr(line, "debug")) {
488 ERROR("debug not yet implemented");
489 return -1;
490 }
50798138
SH
491
492 if (version == 1)
493 return parse_config_v1(f, conf);
494 return parse_config_v2(f, line, conf);
8f2c3a70
SH
495}
496
cd75548b
SH
497/*
498 * use_seccomp: return true if we should try and apply a seccomp policy
499 * if defined for the container.
500 * This will return false if
501 * 1. seccomp is not enabled in the kernel
502 * 2. a seccomp policy is already enabled for this task
503 */
504static bool use_seccomp(void)
d58c6ad0
SH
505{
506 FILE *f = fopen("/proc/self/status", "r");
507 char line[1024];
cd75548b
SH
508 bool already_enabled = false;
509 bool found = false;
d58c6ad0
SH
510 int ret, v;
511
512 if (!f)
cd75548b 513 return true;
d58c6ad0
SH
514
515 while (fgets(line, 1024, f)) {
516 if (strncmp(line, "Seccomp:", 8) == 0) {
cd75548b 517 found = true;
d58c6ad0 518 ret = sscanf(line+8, "%d", &v);
cd75548b
SH
519 if (ret == 1 && v != 0)
520 already_enabled = true;
521 break;
d58c6ad0
SH
522 }
523 }
524
d58c6ad0 525 fclose(f);
cd75548b
SH
526 if (!found) { /* no Seccomp line, no seccomp in kernel */
527 INFO("Seccomp is not enabled in the kernel");
528 return false;
529 }
530 if (already_enabled) { /* already seccomp-confined */
531 INFO("Already seccomp-confined, not loading new policy");
532 return false;
533 }
534 return true;
d58c6ad0
SH
535}
536
8f2c3a70
SH
537int lxc_read_seccomp_config(struct lxc_conf *conf)
538{
539 FILE *f;
540 int ret;
541
769872f9
SH
542 if (!conf->seccomp)
543 return 0;
544
cd75548b 545 if (!use_seccomp())
d58c6ad0 546 return 0;
769872f9
SH
547#if HAVE_SCMP_FILTER_CTX
548 /* XXX for debug, pass in SCMP_ACT_TRAP */
50798138 549 conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
769872f9
SH
550 ret = !conf->seccomp_ctx;
551#else
50798138 552 ret = seccomp_init(SCMP_ACT_KILL) < 0;
769872f9
SH
553#endif
554 if (ret) {
8f2c3a70
SH
555 ERROR("failed initializing seccomp");
556 return -1;
557 }
8f2c3a70
SH
558
559 /* turn of no-new-privs. We don't want it in lxc, and it breaks
560 * with apparmor */
769872f9
SH
561 if (seccomp_attr_set(
562#if HAVE_SCMP_FILTER_CTX
563 conf->seccomp_ctx,
564#endif
565 SCMP_FLTATR_CTL_NNP, 0)) {
959aee9c 566 ERROR("failed to turn off n-new-privs");
8f2c3a70
SH
567 return -1;
568 }
569
570 f = fopen(conf->seccomp, "r");
571 if (!f) {
959aee9c 572 SYSERROR("failed to open seccomp policy file %s", conf->seccomp);
8f2c3a70
SH
573 return -1;
574 }
575 ret = parse_config(f, conf);
576 fclose(f);
577 return ret;
578}
579
580int lxc_seccomp_load(struct lxc_conf *conf)
581{
582 int ret;
583 if (!conf->seccomp)
584 return 0;
cd75548b 585 if (!use_seccomp())
d58c6ad0 586 return 0;
769872f9
SH
587 ret = seccomp_load(
588#if HAVE_SCMP_FILTER_CTX
589 conf->seccomp_ctx
590#endif
591 );
8f2c3a70
SH
592 if (ret < 0) {
593 ERROR("Error loading the seccomp policy");
594 return -1;
595 }
596 return 0;
597}
769872f9
SH
598
599void lxc_seccomp_free(struct lxc_conf *conf) {
f10fad2f
ME
600 free(conf->seccomp);
601 conf->seccomp = NULL;
769872f9
SH
602#if HAVE_SCMP_FILTER_CTX
603 if (conf->seccomp_ctx) {
604 seccomp_release(conf->seccomp_ctx);
605 conf->seccomp_ctx = NULL;
606 }
607#endif
608}