From da90151010a39ccd106fbf9f20b449a1f0119bbe Mon Sep 17 00:00:00 2001 From: netblue30 Date: Thu, 12 Nov 2015 08:33:34 -0500 Subject: whitelist support for /tmp --- etc/disable-devel.inc | 1 + etc/disable-secret.inc | 1 + src/firejail/firejail.h | 9 ++- src/firejail/fs_whitelist.c | 188 +++++++++++++++++++++++++++++++------------- src/firejail/profile.c | 1 + 5 files changed, 144 insertions(+), 56 deletions(-) diff --git a/etc/disable-devel.inc b/etc/disable-devel.inc index c95d051ce..39e301191 100644 --- a/etc/disable-devel.inc +++ b/etc/disable-devel.inc @@ -8,6 +8,7 @@ blacklist /usr/bin/c9* blacklist /usr/bin/c8* blacklist /usr/bin/c++* blacklist /usr/bin/ld +blacklist /usr/bin/gdb # Valgrind blacklist /usr/bin/valgrind* diff --git a/etc/disable-secret.inc b/etc/disable-secret.inc index 3feaa5f9a..4ac7a330b 100644 --- a/etc/disable-secret.inc +++ b/etc/disable-secret.inc @@ -9,3 +9,4 @@ blacklist ${HOME}/.gnupg blacklist ${HOME}/.local/share/recently-used.xbel blacklist ${HOME}/*.kdb blacklist ${HOME}/*.key +blacklist /etc/shadow diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 58c497cd8..e3334bd2e 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -39,7 +39,8 @@ #define DRI_DIR "/run/firejail/mnt/dri" #define PULSE_DIR "/run/firejail/mnt/pulse" #define DEVLOG_FILE "/run/firejail/mnt/devlog" -#define WHITELIST_HOME_DIR "/run/firejail/mnt/whome" +#define WHITELIST_HOME_DIR "/run/firejail/mnt/orig-home" +#define WHITELIST_TMP_DIR "/run/firejail/mnt/orig-tmp" #define XAUTHORITY_FILE "/run/firejail/mnt/.Xauthority" #define HOSTNAME_FILE "/run/firejail/mnt/hostname" #define RESOLVCONF_FILE "/run/firejail/mnt/resolv.conf" @@ -86,8 +87,12 @@ typedef struct interface_t { typedef struct profile_entry_t { struct profile_entry_t *next; - char *data; // expanded name of the file + char *data; // command + + // whitelist command parameters char *link; // link name - set if the file is a link + unsigned home_dir:1; // whitelist in /home/user directory + unsigned tmp_dir:1; // whitelist in /tmp directory }ProfileEntry; typedef struct config_t { diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index c856359f6..fd9115a5e 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c @@ -56,26 +56,40 @@ static int mkpath(const char* path, mode_t mode) { return 0; } -static void whitelist_path(const char *path) { +static void whitelist_path(ProfileEntry *entry) { + assert(entry); + char *path = entry->data + 10; assert(path); + const char *fname; + char *wfile; - // fname needs to start with /home/username - if (strncmp(path, cfg.homedir, strlen(cfg.homedir))) { - fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); - exit(1); - } - - const char *fname = path + strlen(cfg.homedir); - if (*fname == '\0') { - fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); - exit(1); + if (entry->home_dir) { +printf("here %d\n", __LINE__); + fname = path + strlen(cfg.homedir); + if (*fname == '\0') { + fprintf(stderr, "Error: file %s is not in user home directory, exiting...\n", path); + exit(1); + } + + if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1) + errExit("asprintf"); } + else if (entry->tmp_dir) { +printf("here %d\n", __LINE__); + fname = path + 4; // strlen("/tmp") + if (*fname == '\0') { + fprintf(stderr, "Error: file %s is not in /tmp directory, exiting...\n", path); + exit(1); + } - char *wfile; - if (asprintf(&wfile, "%s/%s", WHITELIST_HOME_DIR, fname) == -1) - errExit("asprintf"); + if (asprintf(&wfile, "%s/%s", WHITELIST_TMP_DIR, fname) == -1) + errExit("asprintf"); + } // check if the file exists +printf("here %d %s\n", __LINE__, wfile); +system("ls -l /run/firejail/mnt/orig-tmp"); + struct stat s; if (stat(wfile, &s) == 0) { if (arg_debug) @@ -132,9 +146,12 @@ void fs_whitelist(void) { 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 + + char *new_name = NULL; + int home_dir = 0; // /home/user directory flag + int tmp_dir = 0; // /tmp directory flag + + // verify whitelist files, extract symbolic links, etc. while (entry) { // handle only whitelist commands if (strncmp(entry->data, "whitelist ", 10)) { @@ -142,10 +159,42 @@ void fs_whitelist(void) { continue; } - char *new_name = expand_home(entry->data + 10, cfg.homedir); - + // 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 home directory or tmp directory + 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 + goto errexit; // mark symbolic links if (is_link(new_name)) @@ -153,44 +202,60 @@ void fs_whitelist(void) { else 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 + // 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("Removed whitelist path: %s\n", entry->data); - *entry->data = '\0'; + printf("Replaced whitelist path: %s\n", entry->data); } + free(fname); entry = entry->next; } - // create /tmp/firejail/mnt/whome directory + // create mount points 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(); + // /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 /tmpt"); + } // go through profile rules again, and interpret whitelist commands entry = cfg.profile; @@ -201,8 +266,9 @@ void fs_whitelist(void) { continue; } +//printf("here %d#%s#\n", __LINE__, entry->data); // whitelist the real file - whitelist_path(entry->data + 10); + whitelist_path(entry); // create the link if any if (entry->link) { @@ -220,7 +286,21 @@ void fs_whitelist(void) { 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"); + // 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); } diff --git a/src/firejail/profile.c b/src/firejail/profile.c index aeeacfde8..73407d9c0 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -398,6 +398,7 @@ void profile_add(char *str) { ProfileEntry *prf = malloc(sizeof(ProfileEntry)); if (!prf) errExit("malloc"); + memset(prf, 0, sizeof(ProfileEntry)); prf->next = NULL; prf->data = str; -- cgit v1.2.3-54-g00ecf