static int try_mount_proc(struct container *ct) { /* Not requested by user */ if (!(ct->flags & CT_AUTO_PROC)) return 0; /* Container w/o pidns can work on existing proc */ if (!(ct->nsmask & CLONE_NEWPID) && !ct->root_path) return 0; /* Container with shared FS has no place for new proc */ if (!fs_private(ct)) return -1; return re_mount_proc(ct); }
// whitelist for /home/user directory void fs_whitelist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; char *new_name = NULL; int home_dir = 0; // /home/user directory flag int tmp_dir = 0; // /tmp directory flag int media_dir = 0; // /media directory flag int var_dir = 0; // /var directory flag int dev_dir = 0; // /dev directory flag int opt_dir = 0; // /opt directory flag // verify whitelist files, extract symbolic links, etc. while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } // resolve ${DOWNLOADS} if (strcmp(entry->data + 10, "${DOWNLOADS}") == 0) { char *tmp = resolve_downloads(); if (tmp) entry->data = tmp; else { *entry->data = '\0'; fprintf(stderr, "***\n"); fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n"); fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n"); fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n"); fprintf(stderr, "***\n"); continue; } } // replace ~/ or ${HOME} into /home/username new_name = expand_home(entry->data + 10, cfg.homedir); assert(new_name); if (arg_debug) fprintf(stderr, "Debug %d: new_name #%s#\n", __LINE__, new_name); // extract the absolute path of the file // realpath function will fail with ENOENT if the file is not found char *fname = realpath(new_name, NULL); if (!fname) { // file not found, blank the entry in the list and continue if (arg_debug || arg_debug_whitelists) { printf("Removed whitelist path: %s\n", entry->data); printf("\texpanded: %s\n", new_name); printf("\treal path: (null)\n"); printf("\t");fflush(0); perror("realpath"); } *entry->data = '\0'; continue; } // valid path referenced to filesystem root if (*new_name != '/') { if (arg_debug) fprintf(stderr, "Debug %d: \n", __LINE__); goto errexit; } // check for supported directories if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { // whitelisting home directory is disabled if --private or --private-home option is present if (arg_private) { if (arg_debug || arg_debug_whitelists) printf("Removed whitelist path %s, --private option is present\n", entry->data); *entry->data = '\0'; continue; } entry->home_dir = 1; home_dir = 1; // both path and absolute path are under /home if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", __LINE__, fname, cfg.homedir); goto errexit; } } else if (strncmp(new_name, "/tmp/", 5) == 0) { entry->tmp_dir = 1; tmp_dir = 1; // both path and absolute path are under /tmp if (strncmp(fname, "/tmp/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/media/", 7) == 0) { entry->media_dir = 1; media_dir = 1; // both path and absolute path are under /media if (strncmp(fname, "/media/", 7) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/var/", 5) == 0) { entry->var_dir = 1; var_dir = 1; // both path and absolute path are under /var if (strncmp(fname, "/var/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/dev/", 5) == 0) { entry->dev_dir = 1; dev_dir = 1; // both path and absolute path are under /dev if (strncmp(fname, "/dev/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else if (strncmp(new_name, "/opt/", 5) == 0) { entry->opt_dir = 1; opt_dir = 1; // both path and absolute path are under /dev if (strncmp(fname, "/opt/", 5) != 0) { if (arg_debug) fprintf(stderr, "Debug %d: fname #%s#\n", __LINE__, fname); goto errexit; } } else { if (arg_debug) fprintf(stderr, "Debug %d: \n", __LINE__); goto errexit; } // mark symbolic links if (is_link(new_name)) entry->link = new_name; else { free(new_name); new_name = NULL; } // change file name in entry->data if (strcmp(fname, entry->data + 10) != 0) { char *newdata; if (asprintf(&newdata, "whitelist %s", fname) == -1) errExit("asprintf"); entry->data = newdata; if (arg_debug || arg_debug_whitelists) printf("Replaced whitelist path: %s\n", entry->data); } free(fname); entry = entry->next; } // create mount points fs_build_mnt_dir(); // /home/user if (home_dir) { // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR int rv = mkdir(RUN_WHITELIST_HOME_USER_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_HOME_USER_DIR, getuid(), getgid()) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_HOME_USER_DIR, 0755) < 0) errExit("chmod"); if (mount(cfg.homedir, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount a tmpfs and initialize /home/user fs_private(); } // /tmp mountpoint if (tmp_dir) { // keep a copy of real /tmp directory in WHITELIST_TMP_DIR int rv = mkdir(RUN_WHITELIST_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_TMP_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_TMP_DIR, 0777) < 0) errExit("chmod"); if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /tmp if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /tmp directory\n"); if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) errExit("mounting tmpfs on /tmp"); fs_logger("mount tmpfs on /tmp"); } // /media mountpoint if (media_dir) { // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR int rv = mkdir(RUN_WHITELIST_MEDIA_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_MEDIA_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_MEDIA_DIR, 0755) < 0) errExit("chmod"); if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /media if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /media directory\n"); if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /media"); fs_logger("mount tmpfs on /media"); } // /var mountpoint if (var_dir) { // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR int rv = mkdir(RUN_WHITELIST_VAR_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_VAR_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_VAR_DIR, 0755) < 0) errExit("chmod"); if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /var if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /var directory\n"); if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /var"); fs_logger("mount tmpfs on /var"); } // /dev mountpoint if (dev_dir) { // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR int rv = mkdir(RUN_WHITELIST_DEV_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_DEV_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_DEV_DIR, 0755) < 0) errExit("chmod"); if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /dev if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /dev directory\n"); if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /dev"); fs_logger("mount tmpfs on /dev"); } // /opt mountpoint if (opt_dir) { // keep a copy of real /opt directory in RUN_WHITELIST_DEV_DIR int rv = mkdir(RUN_WHITELIST_OPT_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(RUN_WHITELIST_OPT_DIR, 0, 0) < 0) errExit("chown"); if (chmod(RUN_WHITELIST_OPT_DIR, 0755) < 0) errExit("chmod"); if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /opt if (arg_debug || arg_debug_whitelists) printf("Mounting tmpfs on /opt directory\n"); if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /opt"); fs_logger("mount tmpfs on /opt"); } // go through profile rules again, and interpret whitelist commands entry = cfg.profile; while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } //printf("here %d#%s#\n", __LINE__, entry->data); // whitelist the real file whitelist_path(entry); // create the link if any if (entry->link) { // if the link is already there, do not bother struct stat s; if (stat(entry->link, &s) != 0) { // create the path if necessary mkpath(entry->link, s.st_mode); int rv = symlink(entry->data + 10, entry->link); if (rv) fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); else if (arg_debug || arg_debug_whitelists) printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); } } entry = entry->next; } // mask the real home directory, currently mounted on RUN_WHITELIST_HOME_DIR if (home_dir) { if (mount("tmpfs", RUN_WHITELIST_HOME_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } // mask the real /tmp directory, currently mounted on RUN_WHITELIST_TMP_DIR if (tmp_dir) { if (mount("tmpfs", RUN_WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } if (new_name) free(new_name); return; errexit: fprintf(stderr, "Error: invalid whitelist path %s\n", new_name); exit(1); }
int sandbox(void* sandbox_arg) { // Get rid of unused parameter warning (void)sandbox_arg; pid_t child_pid = getpid(); if (arg_debug) printf("Initializing child process\n"); // close each end of the unused pipes close(parent_to_child_fds[1]); close(child_to_parent_fds[0]); // wait for parent to do base setup wait_for_other(parent_to_child_fds[0]); if (arg_debug && child_pid == 1) printf("PID namespace installed\n"); //**************************** // set hostname //**************************** if (cfg.hostname) { if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0) errExit("sethostname"); } //**************************** // mount namespace //**************************** // mount events are not forwarded between the host the sandbox if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { chk_chroot(); } //**************************** // log sandbox data //**************************** if (cfg.name) fs_logger2("sandbox name:", cfg.name); fs_logger2int("sandbox pid:", (int) sandbox_pid); if (cfg.chrootdir) fs_logger("sandbox filesystem: chroot"); else if (arg_overlay) fs_logger("sandbox filesystem: overlay"); else fs_logger("sandbox filesystem: local"); fs_logger("install mount namespace"); //**************************** // netfilter etc. //**************************** if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter netfilter(arg_netfilter_file); } if (arg_netfilter6 && any_bridge_configured()) { // assuming by default the client filter netfilter6(arg_netfilter6_file); } // load IBUS env variables if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { // do nothing - there are problems with ibus version 1.5.11 } else env_ibus_load(); // grab a copy of cp command fs_build_cp_command(); // trace pre-install if (arg_trace || arg_tracelog) fs_trace_preload(); //**************************** // configure filesystem //**************************** #ifdef HAVE_SECCOMP int enforce_seccomp = 0; #endif #ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); // redo cp command fs_build_cp_command(); // force caps and seccomp if not started as root if (getuid() != 0) { // force default seccomp inside the chroot, no keep or drop list // the list build on top of the default drop list is kept intact arg_seccomp = 1; #ifdef HAVE_SECCOMP enforce_seccomp = 1; #endif if (cfg.seccomp_list_drop) { free(cfg.seccomp_list_drop); cfg.seccomp_list_drop = NULL; } if (cfg.seccomp_list_keep) { free(cfg.seccomp_list_keep); cfg.seccomp_list_keep = NULL; } // disable all capabilities if (arg_caps_default_filter || arg_caps_list) fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); arg_caps_drop_all = 1; // drop all supplementary groups; /etc/group file inside chroot // is controlled by a regular usr arg_nogroups = 1; if (!arg_quiet) printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); } else arg_seccomp = 1; //**************************** // trace pre-install, this time inside chroot //**************************** if (arg_trace || arg_tracelog) fs_trace_preload(); } else #endif if (arg_overlay) fs_overlayfs(); else fs_basic_fs(); //**************************** // set hostname in /etc/hostname //**************************** if (cfg.hostname) { fs_hostname(cfg.hostname); } //**************************** // private mode //**************************** if (arg_private) { if (cfg.home_private) // --private= fs_private_homedir(); else // --private fs_private(); } if (arg_private_dev) fs_private_dev(); if (arg_private_etc) { fs_private_etc_list(); // create /etc/ld.so.preload file again if (arg_trace || arg_tracelog) fs_trace_preload(); } if (arg_private_bin) fs_private_bin_list(); if (arg_private_tmp) fs_private_tmp(); //**************************** // apply the profile file //**************************** if (cfg.profile) { // apply all whitelist commands ... fs_whitelist(); // ... followed by blacklist commands fs_blacklist(); } //**************************** // install trace //**************************** if (arg_trace || arg_tracelog) fs_trace(); //**************************** // update /proc, /dev, /boot directorymy //**************************** fs_proc_sys_dev_boot(); //**************************** // --nosound and fix for pulseaudio 7.0 //**************************** if (arg_nosound) pulseaudio_disable(); else pulseaudio_init(); //**************************** // networking //**************************** if (arg_nonetwork) { net_if_up("lo"); if (arg_debug) printf("Network namespace enabled, only loopback interface available\n"); } else if (any_bridge_configured() || any_interface_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); if (mac_not_zero(cfg.bridge0.macsandbox)) net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); sandbox_if_up(&cfg.bridge0); if (mac_not_zero(cfg.bridge1.macsandbox)) net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); sandbox_if_up(&cfg.bridge1); if (mac_not_zero(cfg.bridge2.macsandbox)) net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); sandbox_if_up(&cfg.bridge2); if (mac_not_zero(cfg.bridge3.macsandbox)) net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); sandbox_if_up(&cfg.bridge3); // add a default route if (cfg.defaultgw) { // set the default route if (net_add_route(0, 0, cfg.defaultgw)) fprintf(stderr, "Warning: cannot configure default route\n"); } // enable interfaces if (cfg.interface0.configured && cfg.interface0.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); net_if_up(cfg.interface0.dev); } if (cfg.interface1.configured && cfg.interface1.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); net_if_up(cfg.interface1.dev); } if (cfg.interface2.configured && cfg.interface2.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); net_if_up(cfg.interface2.dev); } if (cfg.interface3.configured && cfg.interface3.ip) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); net_if_up(cfg.interface3.dev); } if (arg_debug) printf("Network namespace enabled\n"); } // if any dns server is configured, it is time to set it now fs_resolvconf(); fs_logger_print(); fs_logger_change_owner(); // print network configuration if (!arg_quiet) { if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { printf("\n"); if (any_bridge_configured() || any_interface_configured()) net_ifprint(); if (cfg.defaultgw != 0) printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); if (cfg.dns1 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); if (cfg.dns2 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); if (cfg.dns3 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); printf("\n"); } } fs_delete_cp_command(); //**************************** // set application environment //**************************** prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } // set environment env_defaults(); // set user-supplied environment variables env_apply(); // set nice if (arg_nice) { errno = 0; int rv = nice(cfg.nice); (void) rv; if (errno) { fprintf(stderr, "Warning: cannot set nice value\n"); errno = 0; } } // clean /tmp/.X11-unix sockets fs_x11(); //**************************** // set security filters //**************************** // set capabilities if (!arg_noroot) set_caps(); // set rlimits set_rlimits(); // set seccomp #ifdef HAVE_SECCOMP // install protocol filter if (cfg.protocol) { protocol_filter(); // install filter protocol_filter_save(); // save filter in PROTOCOL_CFG } // if a keep list is available, disregard the drop list if (arg_seccomp == 1) { if (cfg.seccomp_list_keep) seccomp_filter_keep(); else if (cfg.seccomp_list_errno) seccomp_filter_errno(); else seccomp_filter_drop(enforce_seccomp); } #endif // set cpu affinity if (cfg.cpus) { save_cpu(); // save cpu affinity mask to CPU_CFG file set_cpu_affinity(); } // save cgroup in CGROUP_CFG file if (cfg.cgroup) save_cgroup(); //**************************************** // drop privileges or create a new user namespace //**************************************** save_nogroups(); if (arg_noroot) { int rv = unshare(CLONE_NEWUSER); if (rv == -1) { fprintf(stderr, "Error: cannot mount a new user namespace\n"); perror("unshare"); drop_privs(arg_nogroups); } } else drop_privs(arg_nogroups); // notify parent that new user namespace has been created so a proper // UID/GID map can be setup notify_other(child_to_parent_fds[1]); close(child_to_parent_fds[1]); // wait for parent to finish setting up a proper UID/GID map wait_for_other(parent_to_child_fds[0]); close(parent_to_child_fds[0]); // somehow, the new user namespace resets capabilities; // we need to do them again if (arg_noroot) { set_caps(); if (arg_debug) printf("noroot user namespace installed\n"); } //**************************************** // fork the application and monitor it //**************************************** pid_t app_pid = fork(); if (app_pid == -1) errExit("fork"); if (app_pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died start_application(); // start app } monitor_application(app_pid); // monitor application return 0; }
int sandbox(void* sandbox_arg) { // Get rid of unused parameter warning (void)sandbox_arg; pid_t child_pid = getpid(); if (arg_debug) printf("Initializing child process\n"); // close each end of the unused pipes close(parent_to_child_fds[1]); close(child_to_parent_fds[0]); // wait for parent to do base setup wait_for_other(parent_to_child_fds[0]); if (arg_debug && child_pid == 1) printf("PID namespace installed\n"); //**************************** // set hostname //**************************** if (cfg.hostname) { if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0) errExit("sethostname"); } //**************************** // mount namespace //**************************** // mount events are not forwarded between the host the sandbox if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { chk_chroot(); } //**************************** // netfilter //**************************** if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter netfilter(arg_netfilter_file); } //**************************** // trace pre-install //**************************** if (arg_trace) fs_trace_preload(); //**************************** // configure filesystem //**************************** #ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); // force caps and seccomp if not started as root if (getuid() != 0) { // force default seccomp inside the chroot, no keep or drop list // the list build on top of the default drop list is kept intact arg_seccomp = 1; if (arg_seccomp_list_drop) { free(arg_seccomp_list_drop); arg_seccomp_list_drop = NULL; } if (arg_seccomp_list_keep) { free(arg_seccomp_list_keep); arg_seccomp_list_keep = NULL; } // disable all capabilities if (arg_caps_default_filter || arg_caps_list) fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n"); arg_caps_drop_all = 1; // drop all supplementary groups; /etc/group file inside chroot // is controlled by a regular usr arg_nogroups = 1; printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); } //**************************** // trace pre-install, this time inside chroot //**************************** if (arg_trace) fs_trace_preload(); } else #endif if (arg_overlay) fs_overlayfs(); else fs_basic_fs(); //**************************** // set hostname in /etc/hostname //**************************** if (cfg.hostname) { fs_hostname(cfg.hostname); } //**************************** // apply the profile file //**************************** if (cfg.profile) fs_blacklist(cfg.homedir); //**************************** // private mode //**************************** if (arg_private) { if (cfg.home_private) // --private= fs_private_homedir(); else if (cfg.home_private_keep) // --private-home= fs_private_home_list(); else // --private fs_private(); } if (arg_private_dev) fs_private_dev(); if (arg_private_etc) fs_private_etc_list(); //**************************** // install trace //**************************** if (arg_trace) fs_trace(); //**************************** // update /proc, /dev, /boot directorymy //**************************** fs_proc_sys_dev_boot(); //**************************** // networking //**************************** if (arg_nonetwork) { net_if_up("lo"); if (arg_debug) printf("Network namespace enabled, only loopback interface available\n"); } else if (any_bridge_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); if (mac_not_zero(cfg.bridge0.macsandbox)) net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); sandbox_if_up(&cfg.bridge0); if (mac_not_zero(cfg.bridge1.macsandbox)) net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); sandbox_if_up(&cfg.bridge1); if (mac_not_zero(cfg.bridge2.macsandbox)) net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); sandbox_if_up(&cfg.bridge2); if (mac_not_zero(cfg.bridge3.macsandbox)) net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); sandbox_if_up(&cfg.bridge3); // add a default route if (cfg.defaultgw) { // set the default route if (net_add_route(0, 0, cfg.defaultgw)) fprintf(stderr, "Warning: cannot configure default route\n"); } if (arg_debug) printf("Network namespace enabled\n"); } // if any dns server is configured, it is time to set it now fs_resolvconf(); // print network configuration if (any_bridge_configured() || cfg.defaultgw || cfg.dns1) { printf("\n"); if (any_bridge_configured()) net_ifprint(); if (cfg.defaultgw != 0) printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); if (cfg.dns1 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); if (cfg.dns2 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); if (cfg.dns3 != 0) printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); printf("\n"); } //**************************** // start executable //**************************** prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } // set environment // fix qt 4.8 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) errExit("setenv"); if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, errExit("setenv"); if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0) errExit("setenv"); if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0) errExit("setenv"); if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) errExit("setenv"); // set prompt color to green //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); // set user-supplied environment variables env_apply(); // set capabilities if (!arg_noroot) set_caps(); // set rlimits set_rlimits(); // set seccomp #ifdef HAVE_SECCOMP // if a keep list is available, disregard the drop list if (arg_seccomp == 1) { if (arg_seccomp_list_keep) seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file else seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file } #endif // set cpu affinity if (cfg.cpus) { save_cpu(); // save cpu affinity mask to MNT_DIR/cpu file set_cpu_affinity(); } // save cgroup in MNT_DIR/cgroup file if (cfg.cgroup) save_cgroup(); //**************************************** // drop privileges or create a new user namespace //**************************************** save_nogroups(); if (arg_noroot) { int rv = unshare(CLONE_NEWUSER); if (rv == -1) { fprintf(stderr, "Error: cannot mount a new user namespace\n"); perror("unshare"); drop_privs(arg_nogroups); } } else drop_privs(arg_nogroups); // notify parent that new user namespace has been created so a proper // UID/GID map can be setup notify_other(child_to_parent_fds[1]); close(child_to_parent_fds[1]); // wait for parent to finish setting up a proper UID/GID map wait_for_other(parent_to_child_fds[0]); close(parent_to_child_fds[0]); // somehow, the new user namespace resets capabilities; // we need to do them again if (arg_noroot) { set_caps(); if (arg_debug) printf("User namespace (noroot) installed\n"); } //**************************************** // start the program without using a shell //**************************************** if (arg_shell_none) { if (arg_debug) { int i; for (i = cfg.original_program_index; i < cfg.original_argc; i++) { if (cfg.original_argv[i] == NULL) break; printf("execvp argument %d: %s\n", i - cfg.original_program_index, cfg.original_argv[i]); } } if (!arg_command) printf("Child process initialized\n"); execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index]); } //**************************************** // start the program using a shell //**************************************** else { // choose the shell requested by the user, or use bash as default char *sh; if (cfg.shell) sh = cfg.shell; else if (arg_zsh) sh = "/usr/bin/zsh"; else if (arg_csh) sh = "/bin/csh"; else sh = "/bin/bash"; char *arg[5]; int index = 0; arg[index++] = sh; arg[index++] = "-c"; assert(cfg.command_line); if (arg_debug) printf("Starting %s\n", cfg.command_line); if (arg_doubledash) arg[index++] = "--"; arg[index++] = cfg.command_line; arg[index] = NULL; assert(index < 5); if (arg_debug) { char *msg; if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1) errExit("asprintf"); logmsg(msg); free(msg); } if (arg_debug) { int i; for (i = 0; i < 5; i++) { if (arg[i] == NULL) break; printf("execvp argument %d: %s\n", i, arg[i]); } } if (!arg_command) printf("Child process initialized\n"); execvp(sh, arg); } perror("execvp"); return 0; }
// whitelist for /home/user directory void fs_whitelist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; char *new_name = NULL; int home_dir = 0; // /home/user directory flag int tmp_dir = 0; // /tmp directory flag int media_dir = 0; // /media directory flag int var_dir = 0; // /var directory flag int dev_dir = 0; // /dev directory flag // verify whitelist files, extract symbolic links, etc. while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } // replace ~/ or ${HOME} into /home/username new_name = expand_home(entry->data + 10, cfg.homedir); assert(new_name); // extract the absolute path of the file // realpath function will fail with ENOENT if the file is not found char *fname = realpath(new_name, NULL); if (!fname) { // file not found, blank the entry in the list and continue if (arg_debug) printf("Removed whitelist path: %s\n", entry->data); *entry->data = '\0'; continue; } // valid path referenced to filesystem root if (*new_name != '/') goto errexit; // check for supported directories if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { entry->home_dir = 1; home_dir = 1; // both path and absolute path are under /home if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0) goto errexit; } else if (strncmp(new_name, "/tmp/", 5) == 0) { entry->tmp_dir = 1; tmp_dir = 1; // both path and absolute path are under /tmp if (strncmp(fname, "/tmp/", 5) != 0) goto errexit; } else if (strncmp(new_name, "/media/", 7) == 0) { entry->media_dir = 1; media_dir = 1; // both path and absolute path are under /media if (strncmp(fname, "/media/", 7) != 0) goto errexit; } else if (strncmp(new_name, "/var/", 5) == 0) { entry->var_dir = 1; var_dir = 1; // both path and absolute path are under /var if (strncmp(fname, "/var/", 5) != 0) goto errexit; } else if (strncmp(new_name, "/dev/", 5) == 0) { entry->dev_dir = 1; dev_dir = 1; // both path and absolute path are under /dev if (strncmp(fname, "/dev/", 5) != 0) goto errexit; } else goto errexit; // mark symbolic links if (is_link(new_name)) entry->link = new_name; else free(new_name); // change file name in entry->data if (strcmp(fname, entry->data + 10) != 0) { char *newdata; if (asprintf(&newdata, "whitelist %s", fname) == -1) errExit("asprintf"); entry->data = newdata; if (arg_debug) printf("Replaced whitelist path: %s\n", entry->data); } free(fname); entry = entry->next; } // create mount points fs_build_mnt_dir(); // /home/user if (home_dir) { // keep a copy of real home dir in WHITELIST_HOME_DIR int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0) errExit("chown"); if (chmod(WHITELIST_HOME_DIR, 0755) < 0) errExit("chmod"); if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount a tmpfs and initialize /home/user fs_private(); } // /tmp mountpoint if (tmp_dir) { // keep a copy of real /tmp directory in WHITELIST_TMP_DIR int rv = mkdir(WHITELIST_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_TMP_DIR, 0, 0) < 0) errExit("chown"); if (chmod(WHITELIST_TMP_DIR, 0777) < 0) errExit("chmod"); if (mount("/tmp", WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /tmp if (arg_debug) printf("Mounting tmpfs on /tmp directory\n"); if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0) errExit("mounting tmpfs on /tmp"); } // /media mountpoint if (media_dir) { // keep a copy of real /media directory in WHITELIST_MEDIA_DIR int rv = mkdir(WHITELIST_MEDIA_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_MEDIA_DIR, 0, 0) < 0) errExit("chown"); if (chmod(WHITELIST_MEDIA_DIR, 0755) < 0) errExit("chmod"); if (mount("/media", WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /media if (arg_debug) printf("Mounting tmpfs on /media directory\n"); if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /media"); } // /media mountpoint if (var_dir) { // keep a copy of real /var directory in WHITELIST_VAR_DIR int rv = mkdir(WHITELIST_VAR_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_VAR_DIR, 0, 0) < 0) errExit("chown"); if (chmod(WHITELIST_VAR_DIR, 0755) < 0) errExit("chmod"); if (mount("/var", WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /var if (arg_debug) printf("Mounting tmpfs on /var directory\n"); if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /var"); } // /dev mountpoint if (dev_dir) { // keep a copy of real /dev directory in WHITELIST_DEV_DIR int rv = mkdir(WHITELIST_DEV_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_DEV_DIR, 0, 0) < 0) errExit("chown"); if (chmod(WHITELIST_DEV_DIR, 0755) < 0) errExit("chmod"); if (mount("/dev", WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // mount tmpfs on /var if (arg_debug) printf("Mounting tmpfs on /dev directory\n"); if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting tmpfs on /dev"); } // go through profile rules again, and interpret whitelist commands entry = cfg.profile; while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } //printf("here %d#%s#\n", __LINE__, entry->data); // whitelist the real file whitelist_path(entry); // create the link if any if (entry->link) { // if the link is already there, do not bother struct stat s; if (stat(entry->link, &s) != 0) { int rv = symlink(entry->data + 10, entry->link); if (rv) fprintf(stderr, "Warning cannot create symbolic link %s\n", entry->link); else if (arg_debug) printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); } } entry = entry->next; } // mask the real home directory, currently mounted on WHITELIST_HOME_DIR if (home_dir) { if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } // mask the real /tmp directory, currently mounted on WHITELIST_TMP_DIR if (tmp_dir) { if (mount("tmpfs", WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); } return; errexit: fprintf(stderr, "Error: invalid whitelist path %s\n", new_name); exit(1); }
int sandbox(void* sandbox_arg) { if (arg_debug) printf("Initializing child process\n"); //**************************** // wait for the parent to be initialized //**************************** char childstr[BUFLEN + 1]; FILE* stream; close(fds[1]); stream = fdopen(fds[0], "r"); *childstr = '\0'; if (fgets(childstr, BUFLEN, stream)) { // remove \n char *ptr = childstr; while(*ptr !='\0' && *ptr != '\n') ptr++; if (*ptr == '\0') errExit("fgets"); *ptr = '\0'; } else { fprintf(stderr, "Error: cannot establish communication with the parent, exiting...\n"); exit(1); } close(fds[0]); if (arg_debug && getpid() == 1) printf("PID namespace installed\n"); //**************************** // set hostname //**************************** if (cfg.hostname) { if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0) errExit("sethostname"); } //**************************** // mount namespace //**************************** // mount events are not forwarded between the host the sandbox if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { // if we are starting firejail inside firejail, we don't care about this char *mycont = getenv("container"); if (mycont == NULL) errExit("mounting filesystem as slave"); if (strcmp(mycont, "firejail") != 0) errExit("mounting filesystem as slave"); } //**************************** // trace pre-install //**************************** if (arg_trace) fs_trace_preload(); //**************************** // configure filesystem //**************************** int drop_caps = 0; if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); // force caps and seccomp if not started as root if (getuid() != 0) { arg_seccomp = 1; arg_caps = 0; drop_caps = 1; printf("Dropping all Linux capabilities and enforcing default seccomp filter\n"); } //**************************** // trace pre-install, this time inside chroot //**************************** if (arg_trace) fs_trace_preload(); } else if (arg_overlay) fs_overlayfs(); else fs_basic_fs(); //**************************** // set hostname in /etc/hostname //**************************** if (cfg.hostname) { fs_hostname(cfg.hostname); } //**************************** // apply the profile file //**************************** if (cfg.profile) fs_blacklist(cfg.homedir); //**************************** // private mode //**************************** if (arg_private) { if (cfg.home_private) fs_private_home(); else fs_private(); } //**************************** // install trace //**************************** if (arg_trace) fs_trace(); //**************************** // update /proc, /dev, /boot directory //**************************** fs_proc_sys_dev_boot(); //**************************** // networking //**************************** if (arg_nonetwork) { net_if_up("lo"); } else if (arg_noip) { net_if_up("lo"); if (cfg.bridge0.configured) net_if_up("eth0"); if (cfg.bridge1.configured) net_if_up("eth1"); if (cfg.bridge2.configured) net_if_up("eth2"); if (cfg.bridge3.configured) net_if_up("eth3"); } else if (any_bridge_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); if (cfg.bridge0.configured) { Bridge *br = &cfg.bridge0; net_if_up("eth0"); assert(br->ipaddress); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface eth0\n", PRINT_IP(br->ipaddress)); net_if_ip("eth0", br->ipaddress, br->mask); net_if_up("eth0"); } if (cfg.bridge1.configured) { Bridge *br = &cfg.bridge1; net_if_up("eth1"); assert(br->ipaddress); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface eth1\n", PRINT_IP(br->ipaddress)); net_if_ip("eth1", br->ipaddress, br->mask); net_if_up("eth1"); } if (cfg.bridge2.configured) { Bridge *br = &cfg.bridge2; net_if_up("eth2"); assert(br->ipaddress); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface eth2\n", PRINT_IP(br->ipaddress)); net_if_ip("eth2", br->ipaddress, br->mask); net_if_up("eth2"); } if (cfg.bridge3.configured) { Bridge *br = &cfg.bridge3; net_if_up("eth3"); assert(br->ipaddress); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface eth3\n", PRINT_IP(br->ipaddress)); net_if_ip("eth3", br->ipaddress, br->mask); net_if_up("eth3"); } // add a default route if (!cfg.defaultgw) { // set the default route as IP address of first bridge cfg.defaultgw = cfg.bridge0.ip; if (arg_debug) printf("Using first bridge address as default route\n"); } if (net_add_route(0, 0, cfg.defaultgw)) fprintf(stderr, "Warning: cannot configure default route\n"); if (arg_debug) printf("Network namespace enabled\n"); } net_ifprint(); //**************************** // start executable //**************************** prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } // set environment // fix qt 4.8 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) errExit("setenv"); if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, errExit("setenv"); if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0) errExit("setenv"); if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0) errExit("setenv"); if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0) errExit("setenv"); // set prompt color to green //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); // set capabilities if (arg_caps == 1) caps_filter(); if (drop_caps) caps_drop_all(); // set rlimits set_rlimits(); // set seccomp #ifdef HAVE_SECCOMP if (arg_seccomp == 1) seccomp_filter(); #endif // drop privileges drop_privs(); // set the shell char *sh; if (cfg.shell) sh = cfg.shell; else if (arg_zsh) sh = "/usr/bin/zsh"; else if (arg_csh) sh = "/bin/csh"; else sh = "/bin/bash"; char *arg[4]; arg[0] = sh; arg[1] = "-c"; assert(cfg.command_line); if (arg_debug) printf("Starting %s\n", cfg.command_line); arg[2] = cfg.command_line; arg[3] = NULL; if (!arg_command) printf("Child process initialized\n"); if (arg_debug) { char *msg; if (asprintf(&msg, "child pid %s, execvp into %s", childstr, cfg.command_line) == -1) errExit("asprintf"); logmsg(msg); free(msg); } execvp(sh, arg); perror("execvp"); return 0; }
// whitelist for /home/user directory void fs_whitelist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; // realpath function will fail with ENOENT if the file is not found // we need to expand the path before installing a new, empty home directory while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } char *new_name = expand_home(entry->data + 10, cfg.homedir); assert(new_name); char *fname = realpath(new_name, NULL); free(new_name); if (fname) { // change file name in entry->data if (strcmp(fname, entry->data + 10) != 0) { char *newdata; if (asprintf(&newdata, "whitelist %s", fname) == -1) errExit("asprintf"); entry->data = newdata; if (arg_debug) printf("Replaced whitelist path: %s\n", entry->data); } free(fname); } else { // file not found, blank the entry in the list if (arg_debug) printf("Removed whitelist path: %s\n", entry->data); *entry->data = '\0'; } entry = entry->next; } // create /tmp/firejail/mnt/whome directory fs_build_mnt_dir(); int rv = mkdir(WHITELIST_HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO); if (rv == -1) errExit("mkdir"); if (chown(WHITELIST_HOME_DIR, getuid(), getgid()) < 0) errExit("chown"); if (chmod(WHITELIST_HOME_DIR, 0755) < 0) errExit("chmod"); // keep a copy of real home dir in /tmp/firejail/mnt/whome if (mount(cfg.homedir, WHITELIST_HOME_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); // start building the new home directory by mounting a tmpfs fielsystem fs_private(); // go through profile rules again, and interpret whitelist commands entry = cfg.profile; while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { entry = entry->next; continue; } whitelist_path(entry->data + 10); entry = entry->next; } // mask the real home directory, currently mounted on /tmp/firejail/mnt/whome if (mount("tmpfs", WHITELIST_HOME_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mount tmpfs"); }