static void duplicate(char *name) { char *fname = check_dir_or_file(name); if (arg_debug) printf("Private home: duplicating %s\n", fname); assert(strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0); struct stat s; if (lstat(fname, &s) == -1) { free(fname); return; } else if (S_ISDIR(s.st_mode)) { // create the directory in RUN_HOME_DIR char *name; char *ptr = strrchr(fname, '/'); ptr++; if (asprintf(&name, "%s/%s", RUN_HOME_DIR, ptr) == -1) errExit("asprintf"); mkdir_attr(name, 0755, getuid(), getgid()); sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, name); free(name); } else sbox_run(SBOX_USER| SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FCOPY, fname, RUN_HOME_DIR); fs_logger2("clone", fname); fs_logger_print(); // save the current log free(fname); }
void netfilter(const char *fname) { // find iptables command struct stat s; char *iptables = NULL; char *iptables_restore = NULL; if (stat("/sbin/iptables", &s) == 0) { iptables = "/sbin/iptables"; iptables_restore = "/sbin/iptables-restore"; } else if (stat("/usr/sbin/iptables", &s) == 0) { iptables = "/usr/sbin/iptables"; iptables_restore = "/usr/sbin/iptables-restore"; } if (iptables == NULL || iptables_restore == NULL) { fprintf(stderr, "Error: iptables command not found, netfilter not configured\n"); return; } // read filter char *filter = client_filter; int allocated = 0; if (netfilter_default) fname = netfilter_default; if (fname) { filter = read_text_file_or_exit(fname); allocated = 1; } // create the filter file FILE *fp = fopen(SBOX_STDIN_FILE, "w"); if (!fp) { fprintf(stderr, "Error: cannot open %s\n", SBOX_STDIN_FILE); exit(1); } fprintf(fp, "%s\n", filter); fclose(fp); // push filter if (arg_debug) printf("Installing network filter:\n%s\n", filter); // first run of iptables on this platform installs a number of kernel modules such as ip_tables, x_tables, iptable_filter // we run this command with caps and seccomp disabled in order to allow the loading of these modules sbox_run(SBOX_ROOT /* | SBOX_CAPS_NETWORK | SBOX_SECCOMP*/ | SBOX_STDIN_FROM_FILE, 1, iptables_restore); unlink(SBOX_STDIN_FILE); // debug if (arg_debug) sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, iptables, "-vL"); if (allocated) free(filter); return; }
void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { assert(br); if (br->configured == 0) return; // create a veth pair char *dev; if (br->veth_name == NULL) { if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0) errExit("asprintf"); } else dev = br->veth_name; char *cstr; if (asprintf(&cstr, "%d", child) == -1) errExit("asprintf"); sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); free(cstr); char *msg; if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1) errExit("asprintf"); logmsg(msg); fflush(0); free(msg); }
void network_main(pid_t child) { char *cstr; if (asprintf(&cstr, "%d", child) == -1) errExit("asprintf"); // create veth pair or macvlan device if (cfg.bridge0.configured) { if (cfg.bridge0.macvlan == 0) { net_configure_veth_pair(&cfg.bridge0, "eth0", child); } else sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); } if (cfg.bridge1.configured) { if (cfg.bridge1.macvlan == 0) net_configure_veth_pair(&cfg.bridge1, "eth1", child); else sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); } if (cfg.bridge2.configured) { if (cfg.bridge2.macvlan == 0) net_configure_veth_pair(&cfg.bridge2, "eth2", child); else sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); } if (cfg.bridge3.configured) { if (cfg.bridge3.macvlan == 0) net_configure_veth_pair(&cfg.bridge3, "eth3", child); else sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); } // move interfaces in sandbox if (cfg.interface0.configured) { sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); } if (cfg.interface1.configured) { sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); } if (cfg.interface2.configured) { sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } if (cfg.interface3.configured) { sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } free(cstr); }
// keep filter for seccomp option int seccomp_filter_keep(void) { if (arg_debug) printf("Build drop seccomp filter\n"); // build the seccomp filter as a regular user sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, PATH_FSECCOMP, "keep", RUN_SECCOMP_CFG, cfg.seccomp_list_keep); if (arg_debug) printf("seccomp filter configured\n"); return seccomp_load(RUN_SECCOMP_CFG); }
void seccomp_print_filter(pid_t pid) { EUID_ASSERT(); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission denied.\n"); exit(1); } } // find the seccomp filter EUID_ROOT(); char *fname; if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_CFG) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == -1) { printf("Cannot access seccomp filter.\n"); exit(1); } // read and print the filter - run this as root, the user doesn't have access sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FSECCOMP, "print", fname); free(fname); exit(0); }
// drop filter for seccomp option int seccomp_filter_drop(int enforce_seccomp) { // default seccomp if (cfg.seccomp_list_drop == NULL && cfg.seccomp_list == NULL) { #if defined(__x86_64__) seccomp_filter_32(); #endif #if defined(__i386__) seccomp_filter_64(); #endif } // default seccomp filter with additional drop list else if (cfg.seccomp_list && cfg.seccomp_list_drop == NULL) { #if defined(__x86_64__) seccomp_filter_32(); #endif #if defined(__i386__) seccomp_filter_64(); #endif if (arg_debug) printf("Build default+drop seccomp filter\n"); // build the seccomp filter as a regular user int rv; if (arg_allow_debuggers) rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 6, PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list, "allow-debuggers"); else rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, PATH_FSECCOMP, "default", "drop", RUN_SECCOMP_CFG, cfg.seccomp_list); if (rv) exit(rv); } // drop list without defaults - secondary filters are not installed else if (cfg.seccomp_list == NULL && cfg.seccomp_list_drop) { if (arg_debug) printf("Build drop seccomp filter\n"); // build the seccomp filter as a regular user int rv; if (arg_allow_debuggers) rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop, "allow-debuggers"); else rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 4, PATH_FSECCOMP, "drop", RUN_SECCOMP_CFG, cfg.seccomp_list_drop); if (rv) exit(rv); } else { assert(0); } // load the filter if (seccomp_load(RUN_SECCOMP_CFG) == 0) { if (arg_debug) printf("seccomp filter configured\n"); } else if (enforce_seccomp) { fprintf(stderr, "Error: a seccomp-enabled Linux kernel is required, exiting...\n"); exit(1); } if (arg_debug && access(PATH_FSECCOMP, X_OK) == 0) sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FSECCOMP, "print", RUN_SECCOMP_CFG); return seccomp_load(RUN_SECCOMP_CFG); }