/** * Function responsible for setting up the epoll_ctl syscall for * the seccomp filter sandbox. * * Note: basically allows everything but will keep for now.. */ static int sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void) filter; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1, SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1, SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1, SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_DEL)); if (rc) return rc; return 0; }
/** * Function responsible for setting up the socket syscall for * the seccomp filter sandbox. */ static int sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; int i; (void) filter; #ifdef __i386__ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket)); if (rc) return rc; #endif rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM)); if (rc) return rc; for (i = 0; i < 2; ++i) { const int pf = i ? PF_INET : PF_INET6; rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), SCMP_CMP(0, SCMP_CMP_EQ, pf), SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP)); if (rc) return rc; rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), SCMP_CMP(0, SCMP_CMP_EQ, pf), SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_DGRAM), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP)); if (rc) return rc; } rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW), SCMP_CMP(2, SCMP_CMP_EQ, 0)); if (rc) return rc; return 0; }
/** * Function responsible for setting up the getsockopt syscall for * the seccomp filter sandbox. */ static int sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void) filter; #ifdef __i386__ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt)); if (rc) return rc; #endif rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), SCMP_CMP(2, SCMP_CMP_EQ, SO_ERROR)); if (rc) return rc; #ifdef HAVE_SYSTEMD rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); if (rc) return rc; #endif #ifdef HAVE_LINUX_NETFILTER_IPV4_H rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), SCMP_CMP(2, SCMP_CMP_EQ, SO_ORIGINAL_DST)); if (rc) return rc; #endif #ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_IPV6), SCMP_CMP(2, SCMP_CMP_EQ, IP6T_SO_ORIGINAL_DST)); if (rc) return rc; #endif return 0; }
int main(int const argc, char * const * const argv) { try { alarm(10); close(fileno(stdin)); for (int fd = fileno(stderr); fd != 1024; ++fd) close(fd); dup2(fileno(stdout), fileno(stderr)); limitResource(RLIMIT_CPU, 9, 11); limitResource(RLIMIT_AS, 512*1024*1024); limitResource(RLIMIT_DATA, 512*1024*1024); limitResource(RLIMIT_FSIZE, 10*1024*1024); limitResource(RLIMIT_LOCKS, 0); limitResource(RLIMIT_MEMLOCK, 0); limitResource(RLIMIT_NPROC, 16); if (argc < 2) e("params: program args"); if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) e("prctl"); scmp_filter_ctx const ctx = seccomp_init(SCMP_ACT_TRAP); if (!ctx) e("seccomp_init"); if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 1, SCMP_CMP(0, SCMP_CMP_MASKED_EQ, CLONE_THREAD, CLONE_THREAD)) != 0) e("seccomp_rule_add"); for (auto && p : rules) if (seccomp_rule_add(ctx, p.second, p.first, 0) != 0) e("seccomp_rule_add"); if (seccomp_load(ctx) < 0) e("seccomp_load"); execv(argv[1], argv + 1); e("execv"); } catch (std::exception const & e) { std::cerr << "exception: " << e.what() << '\n'; return 1; } }
/** * Function responsible for setting up the setsockopt syscall for * the seccomp filter sandbox. */ static int sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void) filter; #ifdef __i386__ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt)); if (rc) return rc; #endif rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR)); if (rc) return rc; rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); if (rc) return rc; rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), SCMP_CMP(2, SCMP_CMP_EQ, SO_RCVBUF)); if (rc) return rc; #ifdef IP_TRANSPARENT rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), SCMP_CMP(2, SCMP_CMP_EQ, IP_TRANSPARENT)); if (rc) return rc; #endif return 0; }
/** * Function responsible for setting up the rt_sigaction syscall for * the seccomp filter sandbox. */ static int sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { unsigned i; int rc; int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD, #ifdef SIGXFSZ SIGXFSZ #endif }; (void) filter; for (i = 0; i < ARRAY_LENGTH(param); i++) { rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), SCMP_CMP(0, SCMP_CMP_EQ, param[i])); if (rc) break; } return rc; }
/** * Function responsible for setting up the accept4 syscall for * the seccomp filter sandbox. */ static int sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void)filter; #ifdef __i386__ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), SCMP_CMP(0, SCMP_CMP_EQ, 18)); if (rc) { return rc; } #endif rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), SCMP_CMP_MASKED(3, SOCK_CLOEXEC|SOCK_NONBLOCK, 0)); if (rc) { return rc; } return 0; }
/** * Function responsible for setting up the stat64 syscall for * the seccomp filter sandbox. */ static int sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; sandbox_cfg_t *elem = NULL; // for each dynamic parameter filters for (elem = filter; elem != NULL; elem = elem->next) { smp_param_t *param = elem->param; if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open) || param->syscall == SCMP_SYS(stat64))) { rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), 1, SCMP_CMP(0, SCMP_CMP_EQ, param->value)); if (rc != 0) { log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " "libseccomp error %d", rc); return rc; } } } return 0; }
/** * Protects all the strings in the sandbox's parameter list configuration. It * works by calculating the total amount of memory required by the parameter * list, allocating the memory using mmap, and protecting it from writes with * mprotect(). */ static int prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) { int ret = 0; size_t pr_mem_size = 0, pr_mem_left = 0; char *pr_mem_next = NULL, *pr_mem_base; sandbox_cfg_t *el = NULL; strmap_t *locations = NULL; // get total number of bytes required to mmap. (Overestimate.) for (el = cfg; el != NULL; el = el->next) { pr_mem_size += strlen((char*) el->param->value) + 1; if (el->param->value2) pr_mem_size += strlen((char*) el->param->value2) + 1; } // allocate protected memory with MALLOC_MP_LIM canary pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (pr_mem_base == MAP_FAILED) { log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s", strerror(errno)); ret = -1; goto out; } pr_mem_next = pr_mem_base + MALLOC_MP_LIM; pr_mem_left = pr_mem_size; locations = strmap_new(); // change el value pointer to protected for (el = cfg; el != NULL; el = el->next) { if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, &el->param->value) < 0) { ret = -2; goto out; } if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, &el->param->value2) < 0) { ret = -2; goto out; } el->param->prot = 1; } // protecting from writes if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) { log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s", strerror(errno)); ret = -3; goto out; } /* * Setting sandbox restrictions so the string memory cannot be tampered with */ // no mremap of the protected base address ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); if (ret) { log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!"); goto out; } // no munmap of the protected base address ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); if (ret) { log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!"); goto out; } /* * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but * never over the memory region used by the protected strings. * * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but * had to be removed due to limitation of libseccomp regarding intervals. * * There is a restriction on how much you can mprotect with R|W up to the * size of the canary. */ ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base), SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); if (ret) { log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!"); goto out; } ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size + MALLOC_MP_LIM), SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); if (ret) { log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!"); goto out; } out: strmap_free(locations, NULL); return ret; }
/** * Protects all the strings in the sandbox's parameter list configuration. It * works by calculating the total amount of memory required by the parameter * list, allocating the memory using mmap, and protecting it from writes with * mprotect(). */ static int prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) { int ret = 0; size_t pr_mem_size = 0, pr_mem_left = 0; char *pr_mem_next = NULL, *pr_mem_base; sandbox_cfg_t *el = NULL; // get total number of bytes required to mmap for (el = cfg; el != NULL; el = el->next) { pr_mem_size += strlen((char*) ((smp_param_t*)el->param)->value) + 1; } // allocate protected memory with MALLOC_MP_LIM canary pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (pr_mem_base == MAP_FAILED) { log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s", strerror(errno)); ret = -1; goto out; } pr_mem_next = pr_mem_base + MALLOC_MP_LIM; pr_mem_left = pr_mem_size; // change el value pointer to protected for (el = cfg; el != NULL; el = el->next) { char *param_val = (char*)((smp_param_t *)el->param)->value; size_t param_size = strlen(param_val) + 1; if (pr_mem_left >= param_size) { // copy to protected memcpy(pr_mem_next, param_val, param_size); // re-point el parameter to protected { void *old_val = (void *) ((smp_param_t*)el->param)->value; tor_free(old_val); } ((smp_param_t*)el->param)->value = (intptr_t) pr_mem_next; ((smp_param_t*)el->param)->prot = 1; // move next available protected memory pr_mem_next += param_size; pr_mem_left -= param_size; } else { log_err(LD_BUG,"(Sandbox) insufficient protected memory!"); ret = -2; goto out; } } // protecting from writes if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) { log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s", strerror(errno)); ret = -3; goto out; } /* * Setting sandbox restrictions so the string memory cannot be tampered with */ // no mremap of the protected base address ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), 1, SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); if (ret) { log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!"); return ret; } // no munmap of the protected base address ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), 1, SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); if (ret) { log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!"); return ret; } /* * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but * never over the memory region used by the protected strings. * * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but * had to be removed due to limitation of libseccomp regarding intervals. * * There is a restriction on how much you can mprotect with R|W up to the * size of the canary. */ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2, SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base), SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); if (ret) { log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!"); return ret; } ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2, SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size + MALLOC_MP_LIM), SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); if (ret) { log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!"); return ret; } out: return ret; }
/** * Function responsible for setting up the socket syscall for * the seccomp filter sandbox. */ static int sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void) filter; #ifdef __i386__ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0); if (rc) return rc; #endif rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3, SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3, SCMP_CMP(0, SCMP_CMP_EQ, PF_INET), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3, SCMP_CMP(0, SCMP_CMP_EQ, PF_INET), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3, SCMP_CMP(0, SCMP_CMP_EQ, PF_INET), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK), SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP)); if (rc) return rc; rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3, SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK), SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW), SCMP_CMP(2, SCMP_CMP_EQ, 0)); if (rc) return rc; return 0; }
static int parse_line(char *line, struct seccomp_args *sargs) { // strtok_r needs a pointer to keep track of where it is in the // string. char *buf_saveptr; // Initialize our struct sargs->length = 0; sargs->syscall_nr = -1; if (strlen(line) == 0) return PARSE_ERROR; // Initialize tokenizer and obtain first token. char *buf_token = strtok_r(line, " \t", &buf_saveptr); if (buf_token == NULL) return PARSE_ERROR; // syscall not available on this arch/kernel sargs->syscall_nr = seccomp_syscall_resolve_name(buf_token); if (sargs->syscall_nr == __NR_SCMP_ERROR) return PARSE_INVALID_SYSCALL; // Parse for syscall arguments. Since we haven't yet searched for the // next token, buf_token is still the syscall itself so start 'pos' as // -1 and only if there is an arg to parse, increment it. int pos = -1; while (pos < SC_ARGS_MAXLENGTH) { buf_token = strtok_r(NULL, " \t", &buf_saveptr); if (buf_token == NULL) break; // we found a token, so increment position and process it pos++; if (strcmp(buf_token, "-") == 0) // skip arg continue; enum scmp_compare op = -1; scmp_datum_t value = 0; if (strlen(buf_token) == 0) { return PARSE_ERROR; } else if (strlen(buf_token) == 1) { // syscall N (length of '1' indicates a single digit) op = SCMP_CMP_EQ; value = read_number(buf_token); } else if (strncmp(buf_token, ">=", 2) == 0) { // syscall >=N op = SCMP_CMP_GE; value = read_number(&buf_token[2]); } else if (strncmp(buf_token, "<=", 2) == 0) { // syscall <=N op = SCMP_CMP_LE; value = read_number(&buf_token[2]); } else if (strncmp(buf_token, "!", 1) == 0) { // syscall !N op = SCMP_CMP_NE; value = read_number(&buf_token[1]); } else if (strncmp(buf_token, ">", 1) == 0) { // syscall >N op = SCMP_CMP_GT; value = read_number(&buf_token[1]); } else if (strncmp(buf_token, "<", 1) == 0) { // syscall <N op = SCMP_CMP_LT; value = read_number(&buf_token[1]); } else { // syscall NNN op = SCMP_CMP_EQ; value = read_number(buf_token); } if (errno != 0) return PARSE_ERROR; sargs->arg_cmp[sargs->length] = SCMP_CMP(pos, op, value); sargs->length++; //printf("\nDEBUG: SCMP_CMP(%d, %d, %llu)\n", pos, op, value); } // too many args if (pos >= SC_ARGS_MAXLENGTH) return PARSE_ERROR; return PARSE_OK; }
/** * Initialize seccomp. * * @param remote file descriptor to talk with the unprivileged process * @param monitored monitored child * @return negative on failures or 0 if everything was setup */ int priv_seccomp_init(int remote, int child) { int rc = -1; scmp_filter_ctx ctx; log_debug("seccomp", "initialize libseccomp filter"); monitored = child; if (priv_seccomp_trap_install() < 0) { log_warn("seccomp", "unable to install SIGSYS handler"); goto failure_scmp; } if ((ctx = seccomp_init(SCMP_ACT_TRAP)) == NULL) { log_warnx("seccomp", "unable to initialize libseccomp subsystem"); goto failure_scmp; } if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1, SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0) { errno = -rc; log_warn("seccomp", "unable to allow read/write on remote socket"); goto failure_scmp; } /* We are far more generic from here. */ if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0)) < 0 || /* write needed for */ (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(bind), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(wait4), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0)) < 0 || /* The following are for resolving addresses */ (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0)) < 0 || (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0)) < 0) { errno = -rc; log_warn("seccomp", "unable to build seccomp rules"); goto failure_scmp; } if ((rc = seccomp_load(ctx)) < 0) { errno = -rc; log_warn("seccomp", "unable to load libseccomp filter"); goto failure_scmp; } failure_scmp: seccomp_release(ctx); return rc; }
void GenHash::calcGenHash() { sandbox_init(); // cannot excute execve prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); kDebug() << "execve" << execve("ls",NULL,NULL); if (child_pid){ KParts::ReadOnlyPart * part = qobject_cast<KParts::ReadOnlyPart *>(parent()); QFile file(KFileDialog::getOpenFileName(KUrl("kfiledialog:///konqueror"), i18n("*"), part->widget(), i18n("Open File To make MD5."))); if (!file.open(QIODevice::ReadOnly)) { return; } // TODO QFile //char s[1024]; // write file content { close(pipe_fd[0]); QByteArray s = file.readAll(); char *ch = s.data(); if (write(pipe_fd[1], ch, s.size()) < 0) { perror("write"); } close(pipe_fd[1]); } //read result { close(pipe_result_fd[1]); char result[128]; if (read(pipe_result_fd[0], &result[0], 128) < 0){ perror("read"); } close(pipe_result_fd[0]); KMessageBox::information(part->widget(),i18n("Md5 : %1").arg(QString(QByteArray(result, 128)))); } }else{ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 3, SCMP_A0(SCMP_CMP_EQ, (scmp_datum_t)pipe_fd[0]), SCMP_A1(SCMP_CMP_EQ, (scmp_datum_t)buff), SCMP_A2(SCMP_CMP_LE, 1024)); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_CMP(0, SCMP_CMP_EQ, (scmp_datum_t)pipe_result_fd[1])); seccomp_load(ctx); seccomp_release(ctx); calcMD5(); } }
int main(int argc, char* argv[], char* envp[]) { if (argc<3 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "--version")) { fprintf(stderr, "Usage: limit_syscalls syscall1 syscall2 ... syscallN -- program [arguments]\n"); fprintf(stderr, "Example:\n" " limit_syscalls execve exit write read open close mmap2 fstat64 access mprotect set_thread_area -- /bin/echo qqq\n"); fprintf(stderr, "Advanced:\n"); fprintf(stderr, " LIMIT_SYSCALLS_DEFAULT_ACTION={a,k,eN} - by default allow, kill or return error N\n"); fprintf(stderr, " some_syscall,A0>=3,A4<<1000,A1!=4,A2&&0x0F==0x0F - compare arguments\n"); fprintf(stderr, " some_syscall,{a,k,eN} - allow, kill or return error N for this rule\n"); fprintf(stderr, "Some more example:\n"); fprintf(stderr," LIMIT_SYSCALLS_DEFAULT_ACTION=a limit_syscalls 'write,A0==1,e0' -- /usr/bin/printf --help\n"); fprintf(stderr," (this makes write to stdout in /usr/bin/printf silently fail, looping it)\n"); return 126; } // ECHRNG, just to provide more or less noticable message when we block a syscall uint32_t default_action = SCMP_ACT_ERRNO(44); if (getenv("LIMIT_SYSCALLS_DEFAULT_ACTION")) { const char* e = getenv("LIMIT_SYSCALLS_DEFAULT_ACTION"); if(!strcmp(e, "a")) default_action=SCMP_ACT_ALLOW; else if(!strcmp(e, "k")) default_action=SCMP_ACT_KILL; else if(e[0] == 'e') { int errno_; if (sscanf(e+1,"%i", &errno_)!=1) { fprintf(stderr, "LIMIT_SYSCALLS_DEFAULT_ACTION=e<number> expected\n"); return 109; } default_action=SCMP_ACT_ERRNO(errno_); } else { fprintf(stderr, "LIMIT_SYSCALLS_DEFAULT_ACTION should be a, k or e<number>\n"); return 110; } } scmp_filter_ctx ctx = seccomp_init(default_action); if (!ctx) { perror("seccomp_init"); return 126; } int i; for (i=1; i<argc; ++i) { if (!strcmp(argv[i], "--")) break; const char* syscall_name = strtok(argv[i], ","); int syscall = seccomp_syscall_resolve_name(argv[i]); if (syscall == __NR_SCMP_ERROR) { fprintf(stderr, "Failed to resolve syscall %s\n", syscall_name); return 125; } int nargs = 0; struct scmp_arg_cmp args[6]; uint32_t action = SCMP_ACT_ALLOW; const char* aa = strtok(NULL, ","); for (;aa; aa=strtok(NULL, ",")) { if (aa[0]=='A') { if(nargs==6) { fprintf(stderr, "Maximum comparator count (6) exceed in %s\n", argv[i]); return 103; } if( !(aa[1]>='0' && aa[1]<='5') ) { fprintf(stderr, "A[0-5] expected in %s\n", argv[i]); return 100; } int cmp = 0; /* invalid value */ if(!strncmp(aa+2, "!=", 2)) cmp=SCMP_CMP_NE; if(!strncmp(aa+2, "<<", 2)) cmp=SCMP_CMP_LT; if(!strncmp(aa+2, "<=", 2)) cmp=SCMP_CMP_LE; if(!strncmp(aa+2, "==", 2)) cmp=SCMP_CMP_EQ; if(!strncmp(aa+2, ">=", 2)) cmp=SCMP_CMP_GE; if(!strncmp(aa+2, ">>", 2)) cmp=SCMP_CMP_GT; if(!strncmp(aa+2, "&&", 2)) cmp=SCMP_CMP_MASKED_EQ; if (!cmp) { fprintf(stderr, "After An there should be comparison operator like" " != << <= == => >> ot &&; in %s\n", argv[i]); return 101; } if (cmp != SCMP_CMP_MASKED_EQ) { scmp_datum_t datum; if(sscanf(aa+4, "%lli", &datum)!=1) { fprintf(stderr, "After AxOP there should be some sort of number in %s\n", argv[i]); return 102; } args[nargs++] = SCMP_CMP(aa[1]-'0', cmp, datum); } else { scmp_datum_t mask; scmp_datum_t datum; if(sscanf(aa+4, "%lli==%lli", &mask, &datum)!=2) { fprintf(stderr, "After Ax&& there should be number==number; in %s\n", argv[i]); return 104; } args[nargs++] = SCMP_CMP(aa[1]-'0', SCMP_CMP_MASKED_EQ, mask, datum); } } else if (aa[0]=='e') { int errno_; if (sscanf(aa+1,"%i", &errno_)!=1) { fprintf(stderr, "After e should be number in %s\n", argv[i]); return 105; } action = SCMP_ACT_ERRNO(errno_); } else if (aa[0]=='k') { action = SCMP_ACT_KILL; } else if (aa[0]=='a') { action = SCMP_ACT_ALLOW; } else { fprintf(stderr, "Unknown %c in %s\n", aa[0], argv[i]); return 107; } } int ret = seccomp_rule_add_hack(ctx, action, syscall, nargs, args); if (ret!=0) { fprintf(stderr, "seccomp_rule_add returned %d\n", ret); return 124; } } int ret = seccomp_load(ctx); if (ret!=0) { fprintf(stderr, "seccomp_load returned %d\n", ret); } execve(argv[i+1], argv+i+1, envp); perror("execve"); return 123; }
/** * Function responsible for setting up the mmap2 syscall for * the seccomp filter sandbox. */ static int sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc = 0; (void)filter; rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), SCMP_CMP(3, SCMP_CMP_EQ,MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS)); if (rc) { return rc; } rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_EXEC), SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_DENYWRITE)); if (rc) { return rc; } return 0; }
int main(int argc, char *argv[]) { const char *argv0 = argv[0]; int opt, optidx; bool separate_read_write = false; bool proxy = false; struct sockaddr_in proxy_sin; bool join = false; #ifdef HAVE_SECCOMP int nrules = 0; uint32_t def_action = SCMP_ACT_ALLOW; char *fixed = (char *)mmap((void *)0x800000, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); #endif struct option longopts[] = { {"abstract" , no_argument , 0 , 'a'} , {"directory" , required_argument , 0 , 'd'} , {"environment" , required_argument , 0 , 'e'} , {"join" , no_argument , 0 , 'j'} , {"policy" , required_argument , 0 , 'o'} , {"proxy" , required_argument , 0 , 'p'} , {"seccomp" , required_argument , 0 , 'c'} , {"separate" , no_argument , 0 , 's'} , {0 , 0 , 0 , 0} , }; while ((opt = getopt_long(argc, argv, "+0ac:d:e:hjo:p:s", longopts, &optidx)) != -1) { switch (opt) { case 'a': af_unix = true; break; case 'c': { #ifdef HAVE_SECCOMP char *pp = optarg, *p, *qq, *q, *saved0, *saved1; for (; ; pp = NULL) { p = strtok_r(pp, " \t,", &saved0); if (! p) break; bool negative = false; int num, pos, nfilters = 0; enum scmp_compare op; if (nrules >= SIZE(scmp_rules)) errx(BAD_ARG, "too many rules"); if (*p == '+') p++; else if (*p == '-') p++, negative = true; scmp_rules[nrules].action = negative ? SCMP_ACT_TRAP : SCMP_ACT_ALLOW; for (qq = p; ; qq = NULL) { q = strtok_r(qq, ":", &saved1); if (! q) break; if (qq) { num = seccomp_syscall_resolve_name(q); if (num < 0) errx(BAD_ARG, "unknown syscall \"%s\"", q); scmp_rules[nrules].syscall = num; } else { if (nfilters >= SIZE(scmp_rules[0].arg_array)) errx(BAD_ARG, "too many filters"); if (! ('0' <= *q && *q <= '5')) errx(BAD_ARG, "argument position should be 0-5"); pos = *q-'0'; REP(i, nfilters) if (pos == scmp_rules[nrules].arg_array[i].arg) errx(BAD_ARG, "duplicate argument position %d", pos); q++; if (*q == '=') q++, op = SCMP_CMP_EQ; else if (*q == '!' && q[1] == '=') q += 2, op = SCMP_CMP_NE; else if (*q == '<') { if (q[1] == '=') q += 2, op = SCMP_CMP_LE; else q++, op = SCMP_CMP_LT; } else if (*q == '>') { if (q[1] == '=') q += 2, op = SCMP_CMP_GE; else q++, op = SCMP_CMP_GT; } else errx(BAD_ARG, "unknown operator \"%c\"", *q); scmp_datum_t val = strtol(q, &q, 0); if (*q) errx(BAD_ARG, "invalid number"); scmp_rules[nrules].arg_array[nfilters++] = SCMP_CMP(pos, op, val); } } scmp_rules[nrules].arg_cnt = nfilters; nrules++; } #else errx(ERR_SYSCALL, "HAVE_SECCOMP not enabled"); #endif break; } case 'd': Chdir(optarg); break; case 'h': show_help(STDOUT_FILENO, argv0); break; case 'j': join = true; break; case 'e': putenv(optarg); break; case 'o': #ifdef HAVE_SECCOMP if (optarg[0] == 'k') def_action = SCMP_ACT_KILL; else if (optarg[0] == 't') def_action = SCMP_ACT_TRAP; else if (optarg[0] == 'a') def_action = SCMP_ACT_ALLOW; #else errx(ERR_SYSCALL, "HAVE_SECCOMP not enabled"); #endif break; case 'p': // proxy { char *p = strchr(optarg, ':'); if (! p) errx(BAD_ARG, "no semicolon"); *p = '\0'; proxy_sin.sin_family = AF_INET; if (inet_aton(optarg, &proxy_sin.sin_addr) < 0) errx(BAD_ARG, "gethostbyname: %s", strerror(errno)); proxy_sin.sin_port = htons(strtol(p+1, &p, 10)); if (*p) errx(BAD_ARG, "port"); proxy = true; } break; case 's': separate_read_write = true; break; default: show_help(STDERR_FILENO, argv0); break; } } argc -= optind; argv += optind; if (argc < 1) { show_help(STDERR_FILENO, argv0); return BAD_ARG; } char buf[4096]; int pipe_p2c[2], pipe_c2p1[2], pipe_c2p2[2]; if (af_unix) { unix_p2c.sun_family = AF_UNIX; unix_c2p1.sun_family = AF_UNIX; unix_c2p2.sun_family = AF_UNIX; unix_p2c.sun_path[0] = '\0'; unix_c2p1.sun_path[0] = '\0'; unix_c2p2.sun_path[0] = '\0'; sprintf(unix_p2c.sun_path+1, "/tmp/unix/%d-%d.in", getuid(), getpid()); sprintf(unix_c2p1.sun_path+1, "/tmp/unix/%d-%d.out", getuid(), getpid()); sprintf(unix_c2p2.sun_path+1, "/tmp/unix/%d-%d.err", getuid(), getpid()); pipe_p2c[0] = socket(AF_UNIX, SOCK_STREAM, 0); pipe_c2p1[1] = socket(AF_UNIX, SOCK_STREAM, 0); if (! join) pipe_c2p2[1] = socket(AF_UNIX, SOCK_STREAM, 0); atexit(atexit_rm); if (bind(pipe_p2c[0], &unix_p2c, sizeof unix_p2c) < 0 || bind(pipe_c2p1[1], &unix_c2p1, sizeof unix_c2p1) < 0) return perror(""), 0; if (! join && bind(pipe_c2p2[1], &unix_c2p2, sizeof unix_c2p2) < 0) return perror(""), 0; if (listen(pipe_p2c[0], 1) < 0 || listen(pipe_c2p1[1], 1) < 0) return perror(""), 0; if (! join && listen(pipe_c2p2[1], 1) < 0) return perror(""), 0; } else { Pipe(pipe_p2c); Pipe(pipe_c2p1); if (! join) Pipe(pipe_c2p2); } child = Fork(); if (! child) { // child if (af_unix) { // close bound sockets close(pipe_p2c[0]); close(pipe_c2p1[1]); close(pipe_c2p2[1]); // domain sockets for client pipe_p2c[1] = socket(AF_UNIX, SOCK_STREAM, 0); pipe_c2p1[0] = socket(AF_UNIX, SOCK_STREAM, 0); if (! join) pipe_c2p2[0] = socket(AF_UNIX, SOCK_STREAM, 0); if (connect(pipe_p2c[1], &unix_p2c, sizeof unix_p2c) < 0 || connect(pipe_c2p1[0], &unix_c2p1, sizeof unix_c2p1) < 0) return 0; dup2(pipe_p2c[1], STDIN_FILENO); close(pipe_p2c[1]); dup2(pipe_c2p1[0], STDOUT_FILENO); close(pipe_c2p1[0]); if (join) dup2(STDOUT_FILENO, STDERR_FILENO); else { connect(pipe_c2p2[0], &unix_c2p2, sizeof unix_c2p2); dup2(pipe_c2p2[0], STDERR_FILENO); close(pipe_c2p2[0]); } } else { close(pipe_p2c[1]); dup2(pipe_p2c[0], STDIN_FILENO); close(pipe_p2c[0]); close(pipe_c2p1[0]); dup2(pipe_c2p1[1], STDOUT_FILENO); close(pipe_c2p1[1]); if (join) dup2(STDOUT_FILENO, STDERR_FILENO); else { close(pipe_c2p2[0]); dup2(pipe_c2p2[1], STDERR_FILENO); close(pipe_c2p2[1]); } } #ifdef HAVE_SECCOMP scmp_filter_ctx ctx = seccomp_init(def_action); if (ctx == NULL) return 1; REP(i, nrules) { int ret = seccomp_rule_add_array(ctx, scmp_rules[i].action, scmp_rules[i].syscall, scmp_rules[i].arg_cnt, scmp_rules[i].arg_array); if (ret < 0) return 1; } if (seccomp_load(ctx) < 0) return 1; seccomp_release(ctx); strcpy(fixed, *argv); execv(fixed, argv); // execve will change the first argument #else execvp(*argv, argv); #endif } else {
gboolean tracker_seccomp_init (void) { scmp_filter_ctx ctx; ctx = seccomp_init (SCMP_ACT_TRAP); if (ctx == NULL) return FALSE; /* Memory management */ ALLOW_RULE (brk); ALLOW_RULE (mmap); ALLOW_RULE (mmap2); ALLOW_RULE (munmap); ALLOW_RULE (mremap); ALLOW_RULE (mprotect); ALLOW_RULE (madvise); ERROR_RULE (mlock, EPERM); ERROR_RULE (mlock2, EPERM); ERROR_RULE (munlock, EPERM); ERROR_RULE (mlockall, EPERM); ERROR_RULE (munlockall, EPERM); /* Process management */ ALLOW_RULE (exit_group); ALLOW_RULE (getuid); ALLOW_RULE (getuid32); ALLOW_RULE (geteuid); ALLOW_RULE (geteuid32); ALLOW_RULE (getppid); ALLOW_RULE (gettid); ALLOW_RULE (exit); ALLOW_RULE (getrusage); ALLOW_RULE (getrlimit); /* Basic filesystem access */ ALLOW_RULE (fstat); ALLOW_RULE (fstat64); ALLOW_RULE (stat); ALLOW_RULE (stat64); ALLOW_RULE (statfs); ALLOW_RULE (statfs64); ALLOW_RULE (lstat); ALLOW_RULE (lstat64); ALLOW_RULE (access); ALLOW_RULE (getdents); ALLOW_RULE (getdents64); ALLOW_RULE (readlink); ALLOW_RULE (readlinkat); ALLOW_RULE (utime); ALLOW_RULE (time); ALLOW_RULE (fsync); ALLOW_RULE (umask); /* Processes and threads */ ALLOW_RULE (clone); ALLOW_RULE (futex); ALLOW_RULE (set_robust_list); ALLOW_RULE (rt_sigaction); ALLOW_RULE (rt_sigprocmask); ALLOW_RULE (sched_yield); ALLOW_RULE (sched_getaffinity); ALLOW_RULE (nanosleep); /* Main loops */ ALLOW_RULE (poll); ALLOW_RULE (ppoll); ALLOW_RULE (fcntl); ALLOW_RULE (fcntl64); ALLOW_RULE (eventfd); ALLOW_RULE (eventfd2); ALLOW_RULE (pipe); ALLOW_RULE (pipe2); /* System */ ALLOW_RULE (uname); ALLOW_RULE (sysinfo); ALLOW_RULE (prctl); ALLOW_RULE (getrandom); ALLOW_RULE (clock_gettime); ALLOW_RULE (clock_getres); ALLOW_RULE (gettimeofday); /* Descriptors */ ALLOW_RULE (close); ALLOW_RULE (read); ALLOW_RULE (pread64); ALLOW_RULE (lseek); ALLOW_RULE (_llseek); ALLOW_RULE (fadvise64); ALLOW_RULE (write); ALLOW_RULE (writev); ALLOW_RULE (dup); /* Needed by some GStreamer modules doing crazy stuff, less * scary thanks to the restriction below about sockets being * local. */ ALLOW_RULE (connect); ALLOW_RULE (send); ALLOW_RULE (sendto); ALLOW_RULE (sendmsg); ALLOW_RULE (recv); ALLOW_RULE (recvmsg); ALLOW_RULE (recvfrom); ALLOW_RULE (getsockname); ALLOW_RULE (getpeername); ALLOW_RULE (shutdown); /* Special requirements for socket/socketpair, only on AF_UNIX/AF_LOCAL */ if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 1, SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 1, SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) < 0) goto out; /* Special requirements for ioctl, allowed on stdout/stderr */ if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_CMP(0, SCMP_CMP_EQ, 1)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_CMP(0, SCMP_CMP_EQ, 2)) < 0) goto out; /* Special requirements for open, allow O_RDONLY calls, but fail * if write permissions are requested. */ if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1, SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ERRNO (EACCES), SCMP_SYS(open), 1, SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ERRNO (EACCES), SCMP_SYS(open), 1, SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)) < 0) goto out; /* Special requirements for dup2/dup3, no fiddling with stdin/out/err */ if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup2), 1, SCMP_CMP(1, SCMP_CMP_GT, 2)) < 0) goto out; if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup3), 1, SCMP_CMP(1, SCMP_CMP_GT, 2)) < 0) goto out; g_debug ("Loading seccomp rules."); if (seccomp_load (ctx) >= 0) return TRUE; out: g_critical ("Failed to load seccomp rules."); seccomp_release (ctx); return FALSE; }