int lxc_read_seccomp_config(struct lxc_conf *conf) { FILE *f; int ret; int check_seccomp_attr_set; if (!conf->seccomp) return 0; if (!use_seccomp()) return 0; #if HAVE_SCMP_FILTER_CTX /* XXX for debug, pass in SCMP_ACT_TRAP */ conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL); ret = !conf->seccomp_ctx; #else ret = seccomp_init(SCMP_ACT_KILL) < 0; #endif if (ret) { ERROR("Failed initializing seccomp."); return -1; } /* turn off no-new-privs. We don't want it in lxc, and it breaks * with apparmor */ #if HAVE_SCMP_FILTER_CTX check_seccomp_attr_set = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0); #else check_seccomp_attr_set = seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0); #endif if (check_seccomp_attr_set) { ERROR("Failed to turn off n-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif f = fopen(conf->seccomp, "r"); if (!f) { SYSERROR("Failed to open seccomp policy file %s.", conf->seccomp); return -1; } ret = parse_config(f, conf); fclose(f); return ret; }
scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action) { scmp_filter_ctx ctx; int ret; uint32_t arch; switch(n_arch) { case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break; case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break; case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break; #ifdef SCMP_ARCH_PPC64LE case lxc_seccomp_arch_ppc64le: arch = SCMP_ARCH_PPC64LE; break; #endif #ifdef SCMP_ARCH_PPC64 case lxc_seccomp_arch_ppc64: arch = SCMP_ARCH_PPC64; break; #endif #ifdef SCMP_ARCH_PPC case lxc_seccomp_arch_ppc: arch = SCMP_ARCH_PPC; break; #endif default: return NULL; } if ((ctx = seccomp_init(default_policy_action)) == NULL) { ERROR("Error initializing seccomp context"); return NULL; } if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs"); seccomp_release(ctx); return NULL; } ret = seccomp_arch_add(ctx, arch); if (ret != 0) { ERROR("Seccomp error %d (%s) adding arch: %d", ret, strerror(ret), (int)n_arch); seccomp_release(ctx); return NULL; } if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) { ERROR("Seccomp error removing native arch"); seccomp_release(ctx); return NULL; } return ctx; }
int lxc_read_seccomp_config(struct lxc_conf *conf) { FILE *f; int ret; if (!conf->seccomp) return 0; #if HAVE_SCMP_FILTER_CTX /* XXX for debug, pass in SCMP_ACT_TRAP */ conf->seccomp_ctx = seccomp_init(SCMP_ACT_ERRNO(31)); ret = !conf->seccomp_ctx; #else ret = seccomp_init(SCMP_ACT_ERRNO(31)) < 0; #endif if (ret) { ERROR("failed initializing seccomp"); return -1; } /* turn of no-new-privs. We don't want it in lxc, and it breaks * with apparmor */ if (seccomp_attr_set( #if HAVE_SCMP_FILTER_CTX conf->seccomp_ctx, #endif SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs\n"); return -1; } process_lock(); f = fopen(conf->seccomp, "r"); process_unlock(); if (!f) { SYSERROR("failed to open seccomp policy file %s\n", conf->seccomp); return -1; } ret = parse_config(f, conf); process_lock(); fclose(f); process_unlock(); return ret; }
scmp_filter_ctx sc_prepare_seccomp_context(const char *filter_profile) { int rc = 0; scmp_filter_ctx ctx = NULL; FILE *f = NULL; size_t lineno = 0; uid_t real_uid, effective_uid, saved_uid; struct preprocess pre; struct seccomp_args sargs; debug("preparing seccomp profile associated with security tag %s", filter_profile); // initialize hsearch map sc_map_init(); ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { errno = ENOMEM; die("seccomp_init() failed"); } // Setup native arch and any compatibility archs sc_add_seccomp_archs(ctx); // Disable NO_NEW_PRIVS because it interferes with exec transitions in // AppArmor. Unfortunately this means that security policies must be // very careful to not allow the following otherwise apps can escape // the sandbox: // - seccomp syscall // - prctl with PR_SET_SECCOMP // - ptrace (trace) in AppArmor // - capability sys_admin in AppArmor // Note that with NO_NEW_PRIVS disabled, CAP_SYS_ADMIN is required to // change the seccomp sandbox. if (getresuid(&real_uid, &effective_uid, &saved_uid) != 0) die("could not find user IDs"); // If running privileged or capable of raising, disable nnp if (real_uid == 0 || effective_uid == 0 || saved_uid == 0) if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0) != 0) die("Cannot disable nnp"); // Note that secure_gettenv will always return NULL when suid, so // SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR can't be (ab)used in that case. if (secure_getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR") != NULL) filter_profile_dir = secure_getenv("SNAPPY_LAUNCHER_SECCOMP_PROFILE_DIR"); char profile_path[512]; // arbitrary path name limit must_snprintf(profile_path, sizeof(profile_path), "%s/%s", filter_profile_dir, filter_profile); f = fopen(profile_path, "r"); if (f == NULL) { fprintf(stderr, "Can not open %s (%s)\n", profile_path, strerror(errno)); die("aborting"); } // Note, preprocess_filter() die()s on error preprocess_filter(f, &pre); if (pre.unrestricted) { seccomp_release(ctx); ctx = NULL; goto out; } // FIXME: right now complain mode is the equivalent to unrestricted. // We'll want to change this once we seccomp logging is in order. if (pre.complain) { seccomp_release(ctx); ctx = NULL; goto out; } char buf[SC_MAX_LINE_LENGTH]; while (fgets(buf, sizeof(buf), f) != NULL) { lineno++; // skip policy-irrelevant lines if (validate_and_trim_line(buf, sizeof(buf), lineno) == 0) continue; char *buf_copy = strdup(buf); if (buf_copy == NULL) die("Out of memory"); int pr_rc = parse_line(buf_copy, &sargs); free(buf_copy); if (pr_rc != PARSE_OK) { // as this is a syscall whitelist an invalid syscall // is ok and the error can be ignored if (pr_rc == PARSE_INVALID_SYSCALL) continue; die("could not parse line"); } rc = seccomp_rule_add_exact_array(ctx, SCMP_ACT_ALLOW, sargs.syscall_nr, sargs.length, sargs.arg_cmp); if (rc != 0) { rc = seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, sargs.syscall_nr, sargs.length, sargs.arg_cmp); if (rc != 0) { fprintf(stderr, "seccomp_rule_add_array failed with %i for '%s'\n", rc, buf); errno = 0; die("aborting"); } } } out: if (f != NULL) { if (fclose(f) != 0) die("could not close seccomp file"); } sc_map_destroy(); return ctx; }
int pcm_json_to_seccomp(char **hook) { json_t *str, *rules, *hook_obj; u_int32_t default_action; int i, rc; if(hook) *hook = NULL; // initialize to a sane value /* Verify it's what we expect */ if(! json_is_object(PCM_GLOBAL.policy)) { errx(EXIT_FAILURE, "root is not an object"); } /* Get the default action */ str = json_object_get(PCM_GLOBAL.policy, "default"); if(! json_is_string(str)) { errx(EXIT_FAILURE, "expected string, didn't get it :/"); } default_action = pcm_string_to_policy(json_string_value(str)); if(default_action == -1) { errx(EXIT_FAILURE, "converting default policy string failed, expecting ALLOW, KILL, or ERRNO"); } /* Initialize seccomp with the default action */ PCM_GLOBAL.seccomp = seccomp_init(default_action); if(PCM_GLOBAL.seccomp == NULL) { errx(EXIT_FAILURE, "Initializing seccomp context failed"); } str = json_object_get(PCM_GLOBAL.policy, "no_new_privs"); if(str) { // for another day, default version of jansson on this system doesn't have it. // int status; // status = json_boolean(str); // we'll assume they want to disable it. rc = seccomp_attr_set(PCM_GLOBAL.seccomp, SCMP_FLTATR_CTL_NNP, 0); if(! rc) { errx(EXIT_FAILURE, "Unable to set no_new_privs attribute to off (%m) and rc = %d, -rc = %d\n", rc, -rc); } } /* And loop over the rules */ rules = json_object_get(PCM_GLOBAL.policy, "rules"); if(! json_is_array(rules)) { errx(EXIT_FAILURE, "Getting policy rules from json failed"); } if(hook) { // XXX, should the file hook value overwrite env var etc? // should we just pcm_hook_set() here? hook_obj = json_object_get(PCM_GLOBAL.policy, "hook"); if(hook_obj) { if(! json_is_string(hook_obj)) { errx(EXIT_FAILURE, "expected hook string, didn't get it :/"); } *hook = strdup(json_string_value(hook_obj)); } } for(i = 0; i < json_array_size(rules); i++) { json_t *rule; rule = json_array_get(rules, i); if(! json_is_object(rule)) { errx(EXIT_FAILURE, "Expected rule object, got something else instead"); } pcm_json_rule_to_seccomp(rule); } pcm_policy_free(); return 0; }
int setup_seccomp(uint64_t cap_list_retain) { scmp_filter_ctx seccomp; int r; seccomp = seccomp_init(SCMP_ACT_ALLOW); if (!seccomp) return log_oom(); r = seccomp_add_secondary_archs(seccomp); if (r < 0) { log_error_errno(r, "Failed to add secondary archs to seccomp filter: %m"); goto finish; } r = seccomp_add_default_syscall_filter(seccomp, cap_list_retain); if (r < 0) goto finish; /* Audit is broken in containers, much of the userspace audit hookup will fail if running inside a container. We don't care and just turn off creation of audit sockets. This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail with EAFNOSUPPORT which audit userspace uses as indication that audit is disabled in the kernel. */ r = seccomp_rule_add( seccomp, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 2, SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); if (r < 0) { log_error_errno(r, "Failed to add audit seccomp rule: %m"); goto finish; } r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); if (r < 0) { log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); goto finish; } r = seccomp_load(seccomp); if (r == -EINVAL) { log_debug_errno(r, "Kernel is probably not configured with CONFIG_SECCOMP. Disabling seccomp audit filter: %m"); r = 0; goto finish; } if (r < 0) { log_error_errno(r, "Failed to install seccomp audit filter: %m"); goto finish; } finish: seccomp_release(seccomp); return r; }
/* * v2 consists of * [x86] * open * read * write * close * # a comment * [x86_64] * open * read * write * close */ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) { char *p; int ret; scmp_filter_ctx compat_ctx = NULL; bool blacklist = false; uint32_t default_policy_action = -1, default_rule_action = -1, action; enum lxc_hostarch_t native_arch = get_hostarch(), cur_rule_arch = native_arch; if (strncmp(line, "blacklist", 9) == 0) blacklist = true; else if (strncmp(line, "whitelist", 9) != 0) { ERROR("Bad seccomp policy style: %s", line); return -1; } if ((p = strchr(line, ' '))) { default_policy_action = get_v2_default_action(p+1); if (default_policy_action == -2) return -1; } /* for blacklist, allow any syscall which has no rule */ if (blacklist) { if (default_policy_action == -1) default_policy_action = SCMP_ACT_ALLOW; if (default_rule_action == -1) default_rule_action = SCMP_ACT_KILL; } else { if (default_policy_action == -1) default_policy_action = SCMP_ACT_KILL; if (default_rule_action == -1) default_rule_action = SCMP_ACT_ALLOW; } if (native_arch == lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_all; compat_ctx = get_new_ctx(lxc_seccomp_arch_i386, default_policy_action); if (!compat_ctx) goto bad; } if (default_policy_action != SCMP_ACT_KILL) { ret = seccomp_reset(conf->seccomp_ctx, default_policy_action); if (ret != 0) { ERROR("Error re-initializing seccomp"); return -1; } if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs"); return -1; } } while (fgets(line, 1024, f)) { if (line[0] == '#') continue; if (strlen(line) == 0) continue; remove_trailing_newlines(line); INFO("processing: .%s.", line); if (line[0] == '[') { // read the architecture for next set of rules if (strcmp(line, "[x86]") == 0 || strcmp(line, "[X86]") == 0) { if (native_arch != lxc_seccomp_arch_i386 && native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_i386; if (native_arch == lxc_seccomp_arch_amd64) { if (compat_ctx) continue; compat_ctx = get_new_ctx(lxc_seccomp_arch_i386, default_policy_action); if (!compat_ctx) goto bad; } } else if (strcmp(line, "[X86_64]") == 0 || strcmp(line, "[x86_64]") == 0) { if (native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_amd64; } else if (strcmp(line, "[all]") == 0 || strcmp(line, "[ALL]") == 0) { cur_rule_arch = lxc_seccomp_arch_all; if (native_arch == lxc_seccomp_arch_amd64 && !compat_ctx) { if (compat_ctx) continue; compat_ctx = get_new_ctx(lxc_seccomp_arch_i386, default_policy_action); if (!compat_ctx) goto bad; } } #ifdef SCMP_ARCH_ARM else if (strcmp(line, "[arm]") == 0 || strcmp(line, "[ARM]") == 0) { if (native_arch != lxc_seccomp_arch_arm) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_arm; } #endif #ifdef SCMP_ARCH_PPC64LE else if (strcmp(line, "[ppc64le]") == 0 || strcmp(line, "[PPC64LE]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64le) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64le; } #endif #ifdef SCMP_ARCH_PPC64 else if (strcmp(line, "[ppc64]") == 0 || strcmp(line, "[PPC64]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64; } #endif #ifdef SCMP_ARCH_PPC else if (strcmp(line, "[ppc]") == 0 || strcmp(line, "[PPC]") == 0) { if (native_arch != lxc_seccomp_arch_ppc) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc; } #endif else goto bad_arch; continue; } /* irrelevant arch - i.e. arm on i386 */ if (cur_rule_arch == lxc_seccomp_arch_unknown) continue; /* read optional action which follows the syscall */ action = get_and_clear_v2_action(line, default_rule_action); if (action == -1) { ERROR("Failed to interpret action"); goto bad_rule; } /* * TODO generalize - if !is_compat_only(native_arch, cur_rule_arch) * * in other words, the rule is 32-bit only, on 64-bit host; don't run * the rule against the native arch. */ if (!(cur_rule_arch == lxc_seccomp_arch_i386 && native_arch == lxc_seccomp_arch_amd64)) { INFO("Adding non-compat rule for %s action %d", line, action); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; } /* * TODO generalize - if need_compat(native_arch, cur_rule_arch) */ if (native_arch == lxc_seccomp_arch_amd64 && cur_rule_arch != lxc_seccomp_arch_amd64) { int nr1, nr2; INFO("Adding compat rule for %s action %d", line, action); nr1 = seccomp_syscall_resolve_name_arch(SCMP_ARCH_X86, line); nr2 = seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE, line); if (nr1 == nr2) { /* If the syscall # is the same for 32- and 64-bit, then we cannot * apply it to the compat_ctx. So apply it to the noncompat ctx. * We may already have done so, but that's ok */ INFO("Adding non-compat rule bc nr1 == nr2 (%d, %d)", nr1, nr2); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; continue; } INFO("Really adding compat rule bc nr1 == nr2 (%d, %d)", nr1, nr2); if (!do_resolve_add_rule(SCMP_ARCH_X86, line, compat_ctx, action)) goto bad_rule; } } if (compat_ctx) { INFO("Merging in the compat seccomp ctx into the main one"); if (seccomp_merge(conf->seccomp_ctx, compat_ctx) != 0) { ERROR("Error merging i386 seccomp contexts"); goto bad; } } return 0; bad_arch: ERROR("Unsupported arch: %s", line); bad_rule: bad: if (compat_ctx) seccomp_release(compat_ctx); return -1; }
/* * v2 consists of * [x86] * open * read * write * close * # a comment * [x86_64] * open * read * write * close */ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) { char *p; int ret; scmp_filter_ctx compat_ctx[2] = {NULL, NULL}; bool blacklist = false; uint32_t default_policy_action = -1, default_rule_action = -1, action; enum lxc_hostarch_t native_arch = get_hostarch(), cur_rule_arch = native_arch; uint32_t compat_arch[2] = {SCMP_ARCH_NATIVE, SCMP_ARCH_NATIVE}; if (strncmp(line, "blacklist", 9) == 0) blacklist = true; else if (strncmp(line, "whitelist", 9) != 0) { ERROR("Bad seccomp policy style: %s.", line); return -1; } if ((p = strchr(line, ' '))) { default_policy_action = get_v2_default_action(p + 1); if (default_policy_action == -2) return -1; } /* for blacklist, allow any syscall which has no rule */ if (blacklist) { if (default_policy_action == -1) default_policy_action = SCMP_ACT_ALLOW; if (default_rule_action == -1) default_rule_action = SCMP_ACT_KILL; } else { if (default_policy_action == -1) default_policy_action = SCMP_ACT_KILL; if (default_rule_action == -1) default_rule_action = SCMP_ACT_ALLOW; } if (native_arch == lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_X86; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_i386, default_policy_action); compat_arch[1] = SCMP_ARCH_X32; compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_x32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; #ifdef SCMP_ARCH_PPC } else if (native_arch == lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_PPC; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_ppc, default_policy_action); if (!compat_ctx[0]) goto bad; #endif #ifdef SCMP_ARCH_ARM } else if (native_arch == lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_ARM; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_arm, default_policy_action); if (!compat_ctx[0]) goto bad; #endif #ifdef SCMP_ARCH_MIPS } else if (native_arch == lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_MIPS; compat_arch[1] = SCMP_ARCH_MIPS64N32; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_mips, default_policy_action); compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_mips64n32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; } else if (native_arch == lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_all; compat_arch[0] = SCMP_ARCH_MIPSEL; compat_arch[1] = SCMP_ARCH_MIPSEL64N32; compat_ctx[0] = get_new_ctx(lxc_seccomp_arch_mipsel, default_policy_action); compat_ctx[1] = get_new_ctx(lxc_seccomp_arch_mipsel64n32, default_policy_action); if (!compat_ctx[0] || !compat_ctx[1]) goto bad; #endif } if (default_policy_action != SCMP_ACT_KILL) { ret = seccomp_reset(conf->seccomp_ctx, default_policy_action); if (ret != 0) { ERROR("Error re-initializing Seccomp."); return -1; } if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("Failed to turn off n-new-privs."); return -1; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif } while (fgets(line, 1024, f)) { if (line[0] == '#') continue; if (strlen(line) == 0) continue; remove_trailing_newlines(line); INFO("processing: .%s.", line); if (line[0] == '[') { // read the architecture for next set of rules if (strcmp(line, "[x86]") == 0 || strcmp(line, "[X86]") == 0) { if (native_arch != lxc_seccomp_arch_i386 && native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_i386; } else if (strcmp(line, "[x32]") == 0 || strcmp(line, "[X32]") == 0) { if (native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_x32; } else if (strcmp(line, "[X86_64]") == 0 || strcmp(line, "[x86_64]") == 0) { if (native_arch != lxc_seccomp_arch_amd64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_amd64; } else if (strcmp(line, "[all]") == 0 || strcmp(line, "[ALL]") == 0) { cur_rule_arch = lxc_seccomp_arch_all; } #ifdef SCMP_ARCH_ARM else if (strcmp(line, "[arm]") == 0 || strcmp(line, "[ARM]") == 0) { if (native_arch != lxc_seccomp_arch_arm && native_arch != lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_arm; } #endif #ifdef SCMP_ARCH_AARCH64 else if (strcmp(line, "[arm64]") == 0 || strcmp(line, "[ARM64]") == 0) { if (native_arch != lxc_seccomp_arch_arm64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_arm64; } #endif #ifdef SCMP_ARCH_PPC64LE else if (strcmp(line, "[ppc64le]") == 0 || strcmp(line, "[PPC64LE]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64le) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64le; } #endif #ifdef SCMP_ARCH_PPC64 else if (strcmp(line, "[ppc64]") == 0 || strcmp(line, "[PPC64]") == 0) { if (native_arch != lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc64; } #endif #ifdef SCMP_ARCH_PPC else if (strcmp(line, "[ppc]") == 0 || strcmp(line, "[PPC]") == 0) { if (native_arch != lxc_seccomp_arch_ppc && native_arch != lxc_seccomp_arch_ppc64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_ppc; } #endif #ifdef SCMP_ARCH_MIPS else if (strcmp(line, "[mips64]") == 0 || strcmp(line, "[MIPS64]") == 0) { if (native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips64; } else if (strcmp(line, "[mips64n32]") == 0 || strcmp(line, "[MIPS64N32]") == 0) { if (native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips64n32; } else if (strcmp(line, "[mips]") == 0 || strcmp(line, "[MIPS]") == 0) { if (native_arch != lxc_seccomp_arch_mips && native_arch != lxc_seccomp_arch_mips64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mips; } else if (strcmp(line, "[mipsel64]") == 0 || strcmp(line, "[MIPSEL64]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel64; } else if (strcmp(line, "[mipsel64n32]") == 0 || strcmp(line, "[MIPSEL64N32]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel64n32; } else if (strcmp(line, "[mipsel]") == 0 || strcmp(line, "[MIPSEL]") == 0) { if (native_arch != lxc_seccomp_arch_mipsel && native_arch != lxc_seccomp_arch_mipsel64) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_mipsel; } #endif #ifdef SCMP_ARCH_S390X else if (strcmp(line, "[s390x]") == 0 || strcmp(line, "[S390X]") == 0) { if (native_arch != lxc_seccomp_arch_s390x) { cur_rule_arch = lxc_seccomp_arch_unknown; continue; } cur_rule_arch = lxc_seccomp_arch_s390x; } #endif else goto bad_arch; continue; } /* irrelevant arch - i.e. arm on i386 */ if (cur_rule_arch == lxc_seccomp_arch_unknown) continue; /* read optional action which follows the syscall */ action = get_and_clear_v2_action(line, default_rule_action); if (action == -1) { ERROR("Failed to interpret action."); goto bad_rule; } if (cur_rule_arch == native_arch || cur_rule_arch == lxc_seccomp_arch_native || compat_arch[0] == SCMP_ARCH_NATIVE) { INFO("Adding native rule for %s action %d.", line, action); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; } else if (cur_rule_arch != lxc_seccomp_arch_all) { int arch_index = cur_rule_arch == lxc_seccomp_arch_mips64n32 || cur_rule_arch == lxc_seccomp_arch_mipsel64n32 ? 1 : 0; INFO("Adding compat-only rule for %s action %d.", line, action); if (!do_resolve_add_rule(compat_arch[arch_index], line, compat_ctx[arch_index], action)) goto bad_rule; } else { INFO("Adding native rule for %s action %d.", line, action); if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line, conf->seccomp_ctx, action)) goto bad_rule; INFO("Adding compat rule for %s action %d.", line, action); if (!do_resolve_add_rule(compat_arch[0], line, compat_ctx[0], action)) goto bad_rule; if (compat_arch[1] != SCMP_ARCH_NATIVE && !do_resolve_add_rule(compat_arch[1], line, compat_ctx[1], action)) goto bad_rule; } } if (compat_ctx[0]) { INFO("Merging in the compat Seccomp ctx into the main one."); if (seccomp_merge(conf->seccomp_ctx, compat_ctx[0]) != 0 || (compat_ctx[1] != NULL && seccomp_merge(conf->seccomp_ctx, compat_ctx[1]) != 0)) { ERROR("Error merging compat Seccomp contexts."); goto bad; } } return 0; bad_arch: ERROR("Unsupported arch: %s.", line); bad_rule: bad: if (compat_ctx[0]) seccomp_release(compat_ctx[0]); if (compat_ctx[1]) seccomp_release(compat_ctx[1]); return -1; }
scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action) { scmp_filter_ctx ctx; int ret; uint32_t arch; switch(n_arch) { case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break; case lxc_seccomp_arch_x32: arch = SCMP_ARCH_X32; break; case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break; case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break; #ifdef SCMP_ARCH_AARCH64 case lxc_seccomp_arch_arm64: arch = SCMP_ARCH_AARCH64; break; #endif #ifdef SCMP_ARCH_PPC64LE case lxc_seccomp_arch_ppc64le: arch = SCMP_ARCH_PPC64LE; break; #endif #ifdef SCMP_ARCH_PPC64 case lxc_seccomp_arch_ppc64: arch = SCMP_ARCH_PPC64; break; #endif #ifdef SCMP_ARCH_PPC case lxc_seccomp_arch_ppc: arch = SCMP_ARCH_PPC; break; #endif #ifdef SCMP_ARCH_MIPS case lxc_seccomp_arch_mips: arch = SCMP_ARCH_MIPS; break; case lxc_seccomp_arch_mips64: arch = SCMP_ARCH_MIPS64; break; case lxc_seccomp_arch_mips64n32: arch = SCMP_ARCH_MIPS64N32; break; case lxc_seccomp_arch_mipsel: arch = SCMP_ARCH_MIPSEL; break; case lxc_seccomp_arch_mipsel64: arch = SCMP_ARCH_MIPSEL64; break; case lxc_seccomp_arch_mipsel64n32: arch = SCMP_ARCH_MIPSEL64N32; break; #endif #ifdef SCMP_ARCH_S390X case lxc_seccomp_arch_s390x: arch = SCMP_ARCH_S390X; break; #endif default: return NULL; } if ((ctx = seccomp_init(default_policy_action)) == NULL) { ERROR("Error initializing seccomp context."); return NULL; } if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("Failed to turn off n-new-privs."); seccomp_release(ctx); return NULL; } #ifdef SCMP_FLTATR_ATL_TSKIP if (seccomp_attr_set(ctx, SCMP_FLTATR_ATL_TSKIP, 1)) { WARN("Failed to turn on seccomp nop-skip, continuing"); } #endif ret = seccomp_arch_add(ctx, arch); if (ret != 0) { ERROR("Seccomp error %d (%s) adding arch: %d", ret, strerror(-ret), (int)n_arch); seccomp_release(ctx); return NULL; } if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) { ERROR("Seccomp error removing native arch"); seccomp_release(ctx); return NULL; } return ctx; }
/* * v2 consists of * [x86] * open * read * write * close * # a comment * [x86_64] * open * read * write * close */ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) { #if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH char *p; int ret; scmp_filter_ctx *ctx = NULL; bool blacklist = false; uint32_t default_policy_action = -1, default_rule_action = -1, action; uint32_t arch = SCMP_ARCH_NATIVE; if (strncmp(line, "blacklist", 9) == 0) blacklist = true; else if (strncmp(line, "whitelist", 9) != 0) { ERROR("Bad seccomp policy style: %s", line); return -1; } if ((p = strchr(line, ' '))) { default_policy_action = get_v2_default_action(p+1); if (default_policy_action == -2) return -1; } /* for blacklist, allow any syscall which has no rule */ if (blacklist) { if (default_policy_action == -1) default_policy_action = SCMP_ACT_ALLOW; if (default_rule_action == -1) default_rule_action = SCMP_ACT_KILL; } else { if (default_policy_action == -1) default_policy_action = SCMP_ACT_KILL; if (default_rule_action == -1) default_rule_action = SCMP_ACT_ALLOW; } if (default_policy_action != SCMP_ACT_KILL) { ret = seccomp_reset(conf->seccomp_ctx, default_policy_action); if (ret != 0) { ERROR("Error re-initializing seccomp"); return -1; } if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs"); return -1; } } while (fgets(line, 1024, f)) { int nr; if (line[0] == '#') continue; if (strlen(line) == 0) continue; remove_trailing_newlines(line); INFO("processing: .%s.", line); if (line[0] == '[') { // read the architecture for next set of rules if (strcmp(line, "[x86]") == 0 || strcmp(line, "[X86]") == 0) arch = SCMP_ARCH_X86; else if (strcmp(line, "[X86_64]") == 0 || strcmp(line, "[x86_64]") == 0) arch = SCMP_ARCH_X86_64; #ifdef SCMP_ARCH_ARM else if (strcmp(line, "[arm]") == 0 || strcmp(line, "[ARM]") == 0) arch = SCMP_ARCH_ARM; #endif else goto bad_arch; if (ctx) { ERROR("Only two arch sections per policy supported"); goto bad_arch; } if ((ctx = seccomp_init(default_policy_action)) == NULL) { ERROR("Error initializing seccomp context"); return -1; } if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) { ERROR("failed to turn off n-new-privs"); seccomp_release(ctx); return -1; } ret = seccomp_arch_add(ctx, arch); if (ret == -EEXIST) { seccomp_release(ctx); ctx = NULL; continue; } if (ret != 0) { ERROR("Error %d adding arch: %s", ret, line); goto bad_arch; } if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) { ERROR("Error removing native arch from %s", line); goto bad_arch; } continue; } action = get_and_clear_v2_action(line, default_rule_action); if (action == -1) { ERROR("Failed to interpret action"); goto bad_rule; } nr = seccomp_syscall_resolve_name_arch(arch, line); if (nr < 0) { ERROR("Failed to resolve syscall: %s", line); goto bad_rule; } ret = seccomp_rule_add(ctx ? ctx : conf->seccomp_ctx, action, nr, 0); if (ret < 0) { ERROR("failed (%d) loading rule for %s", ret, line); goto bad_rule; } } if (ctx) { if (seccomp_merge(conf->seccomp_ctx, ctx) != 0) { seccomp_release(ctx); ERROR("Error merging seccomp contexts"); return -1; } } return 0; bad_arch: ERROR("Unsupported arch: %s", line); bad_rule: if (ctx) seccomp_release(ctx); return -1; #else return -1; #endif }