// 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); }
// 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); }
// 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"); }