From cdde8872a75105b6b347db93315ec0ecd97d6289 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Thu, 9 Mar 2017 13:51:07 -0500 Subject: --nowhitelist --- README.md | 3 + RELNOTES | 3 +- src/bash_completion/firejail.bash_completion | 8 ++ src/firejail/firejail.h | 1 + src/firejail/fs.c | 4 +- src/firejail/fs_mkdir.c | 2 - src/firejail/fs_whitelist.c | 146 ++++++++++++++++++++------- src/firejail/main.c | 8 ++ src/firejail/profile.c | 2 + src/firejail/usage.c | 1 + src/man/firejail-profile.txt | 6 ++ src/man/firejail.txt | 4 + test/fs/whitelist.exp | 2 +- 13 files changed, 145 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index c8d6cf735..ceca43657 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,9 @@ Added AppImage type 2 support, and support for passing command line arguments to $ vncviewer + --nowhitelist=dirname_or_filename + Disable whitelist for this directory or file. + ````` ## New Profiles xiphos, Tor Browser Bundle, display (imagemagik), Wire, mumble, zoom, Guayadeque, qemu, keypass2, diff --git a/RELNOTES b/RELNOTES index 4651449eb..519415533 100644 --- a/RELNOTES +++ b/RELNOTES @@ -13,8 +13,6 @@ firejail (0.9.45) baseline; urgency=low * security: split seccomp filter code configuration in a separate executable * security: split file copying in private option in a separate executable * security: root exploit found by Sebastian Krahmer (CVE-2017-5180) - * security: ~/.pki directory whitelisted and later blacklisted. This affects - most browsers, and disables the custom certificates installed by the user. * feature: disable gnupg and systemd directories under /run/user * feature: test coverage (gcov) support * feature: allow root user access to /dev/shm (--noblacklist=/dev/shm) @@ -34,6 +32,7 @@ firejail (0.9.45) baseline; urgency=low * feature: follow-symlink-private-bin option in /etc/firejail/firejail.config * feature: xvfb X11 server support (--x11=xvfb) * feature: allow /tmp directory in mkdir and mkfile profile commands + * feature: implemented --noblacklist command * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire, * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma, * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator, diff --git a/src/bash_completion/firejail.bash_completion b/src/bash_completion/firejail.bash_completion index 0f71c74dc..38548c42b 100644 --- a/src/bash_completion/firejail.bash_completion +++ b/src/bash_completion/firejail.bash_completion @@ -43,10 +43,18 @@ _firejail() _filedir return 0 ;; + --noblacklist) + _filedir + return 0 + ;; --whitelist) _filedir return 0 ;; + --nowhitelist) + _filedir + return 0 + ;; --read-only) _filedir return 0 diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index a41d5fa17..74e5b2c6b 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -650,6 +650,7 @@ void x11_start(int argc, char **argv); void x11_start_xpra(int argc, char **argv); void x11_start_xephyr(int argc, char **argv); void x11_block(void); +void x11_start_xvfb(int argc, char **argv); // ls.c enum { diff --git a/src/firejail/fs.c b/src/firejail/fs.c index a06f3a35d..712e5fb0a 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -255,7 +255,9 @@ void fs_blacklist(void) { char *ptr; // whitelist commands handled by fs_whitelist() - if (strncmp(entry->data, "whitelist ", 10) == 0 || *entry->data == '\0') { + if (strncmp(entry->data, "whitelist ", 10) == 0 || + strncmp(entry->data, "nowhitelist ", 12) == 0 || + *entry->data == '\0') { entry = entry->next; continue; } diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c index 35d043dde..f90b7df60 100644 --- a/src/firejail/fs_mkdir.c +++ b/src/firejail/fs_mkdir.c @@ -57,8 +57,6 @@ static void mkdir_recursive(char *path) { void fs_mkdir(const char *name) { EUID_ASSERT(); -printf("****************************\n"); - // check directory name invalid_filename(name); diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 1794e4b35..7ad5ffeb8 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c @@ -35,7 +35,7 @@ static char *dentry[] = { }; #define MAXBUF 4098 -static char *resolve_downloads(void) { +static char *resolve_downloads(int nowhitelist_flag) { char *fname; struct stat s; @@ -50,8 +50,14 @@ static char *resolve_downloads(void) { printf("Downloads directory resolved as \"%s\"\n", fname); char *rv; - if (asprintf(&rv, "whitelist ~/%s", dentry[i]) == -1) - errExit("asprintf"); + if (nowhitelist_flag) { + if (asprintf(&rv, "nowhitelist ~/%s", dentry[i]) == -1) + errExit("asprintf"); + } + else { + if (asprintf(&rv, "whitelist ~/%s", dentry[i]) == -1) + errExit("asprintf"); + } free(fname); return rv; } @@ -101,8 +107,14 @@ static char *resolve_downloads(void) { } char *rv; - if (asprintf(&rv, "whitelist ~/%s", ptr + 24) == -1) - errExit("asprintf"); + if (nowhitelist_flag) { + if (asprintf(&rv, "nowhitelist ~/%s", ptr + 24) == -1) + errExit("asprintf"); + } + else { + if (asprintf(&rv, "whitelist ~/%s", ptr + 24) == -1) + errExit("asprintf"); + } return rv; } else @@ -309,38 +321,54 @@ void fs_whitelist(void) { int var_dir = 0; // /var directory flag int dev_dir = 0; // /dev directory flag int opt_dir = 0; // /opt directory flag - int srv_dir = 0; // /srv directory flag + int srv_dir = 0; // /srv directory flag + + size_t nowhitelist_c = 0; + size_t nowhitelist_m = 32; + char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist)); + if (nowhitelist == NULL) + errExit("failed allocating memory for nowhitelist entries"); + // verify whitelist files, extract symbolic links, etc. while (entry) { - // handle only whitelist commands - if (strncmp(entry->data, "whitelist ", 10)) { + int nowhitelist_flag = 0; + + // handle only whitelist and nowhitelist commands + if (strncmp(entry->data, "whitelist ", 10) == 0) + nowhitelist_flag = 0; + else if (strncmp(entry->data, "nowhitelist ", 12) == 0) + nowhitelist_flag = 1; + else { entry = entry->next; continue; } + char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; // resolve ${DOWNLOADS} - if (strcmp(entry->data + 10, "${DOWNLOADS}") == 0) { - char *tmp = resolve_downloads(); - if (tmp) + if (strcmp(dataptr, "${DOWNLOADS}") == 0) { + char *tmp = resolve_downloads(nowhitelist_flag); + if (tmp) { entry->data = tmp; + dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; + } else { + if (!nowhitelist_flag) { + 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"); + } *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 -// if (new_name) -// free(new_name); - new_name = expand_home(entry->data + 10, cfg.homedir); + new_name = expand_home(dataptr, cfg.homedir); assert(new_name); if (arg_debug) - fprintf(stderr, "Debug %d: new_name #%s#\n", __LINE__, new_name); + fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); // valid path referenced to filesystem root if (*new_name != '/') { @@ -356,37 +384,56 @@ void fs_whitelist(void) { 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("Removed whitelist/nowhitelist 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'; // if 1 the file was not found; mount an empty directory - if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { - if(!arg_private) - home_dir = 1; + if (!nowhitelist_flag) { + if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { + if(!arg_private) + home_dir = 1; + } + else if (strncmp(new_name, "/tmp/", 5) == 0) + tmp_dir = 1; + else if (strncmp(new_name, "/media/", 7) == 0) + media_dir = 1; + else if (strncmp(new_name, "/mnt/", 5) == 0) + mnt_dir = 1; + else if (strncmp(new_name, "/var/", 5) == 0) + var_dir = 1; + else if (strncmp(new_name, "/dev/", 5) == 0) + dev_dir = 1; + else if (strncmp(new_name, "/opt/", 5) == 0) + opt_dir = 1; + else if (strncmp(new_name, "/srv/", 5) == 0) + opt_dir = 1; } - else if (strncmp(new_name, "/tmp/", 5) == 0) - tmp_dir = 1; - else if (strncmp(new_name, "/media/", 7) == 0) - media_dir = 1; - else if (strncmp(new_name, "/mnt/", 5) == 0) - mnt_dir = 1; - else if (strncmp(new_name, "/var/", 5) == 0) - var_dir = 1; - else if (strncmp(new_name, "/dev/", 5) == 0) - dev_dir = 1; - else if (strncmp(new_name, "/opt/", 5) == 0) - opt_dir = 1; - else if (strncmp(new_name, "/srv/", 5) == 0) - opt_dir = 1; + *entry->data = '\0'; + continue; + } + + if (nowhitelist_flag) { + // store the path in nowhitelist array + if (arg_debug || arg_debug_whitelists) + printf("Storing nowhitelist %s\n", fname); + + if (nowhitelist_c >= nowhitelist_m) { + nowhitelist_m *= 2; + nowhitelist = realloc(nowhitelist, sizeof(*nowhitelist) * nowhitelist_m); + if (nowhitelist == NULL) + errExit("failed increasing memory for nowhitelist entries"); + } + nowhitelist[nowhitelist_c++] = fname; + *entry->data = 0; continue; } + // check for supported directories if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { // whitelisting home directory is disabled if --private option is present @@ -479,6 +526,27 @@ void fs_whitelist(void) { goto errexit; } + // check if the path is in nowhitelist array + if (nowhitelist_flag == 0) { + size_t i; + int found = 0; + for (i = 0; i < nowhitelist_c; i++) { + if (nowhitelist[i] == NULL) + break; + if (strcmp(nowhitelist[i], fname) == 0) { + found = 1; + break; + } + } + if (found) { + if (arg_debug || arg_debug_whitelists) + printf("Skip nowhitelisted path %s\n", fname); + *entry->data = 0; + free(fname); + continue; + } + } + // mark symbolic links if (is_link(new_name)) entry->link = new_name; diff --git a/src/firejail/main.c b/src/firejail/main.c index 5951e5d16..aead29957 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -1269,6 +1269,14 @@ int main(int argc, char **argv) { else exit_err_feature("whitelist"); } + else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) { + char *line; + if (asprintf(&line, "nowhitelist %s", argv[i] + 14) == -1) + errExit("asprintf"); + + profile_check_line(line, 0, NULL); // will exit if something wrong + profile_add(line); + } #endif else if (strncmp(argv[i], "--read-only=", 12) == 0) { diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 9f6688d4a..c4feadad0 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -960,6 +960,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; #endif } + else if (strncmp(ptr, "nowhitelist ", 12) == 0) + ptr += 12; else if (strncmp(ptr, "read-only ", 10) == 0) ptr += 10; else if (strncmp(ptr, "read-write ", 11) == 0) diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 4d2054a72..9c91b4630 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -127,6 +127,7 @@ void usage(void) { printf(" --noroot - install a user namespace with only the current user.\n"); #endif printf(" --nonewprivs - sets the NO_NEW_PRIVS prctl.\n"); + printf(" --nowhitelist=filename - disable whitelist for file or directory .\n"); printf(" --output=logfile - stdout logging and log rotation.\n"); printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n"); printf(" --overlay-named=name - mount a filesystem overlay on top of the current\n"); diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index cf2398ad4..76cd4d4fa 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -102,6 +102,12 @@ If the file name matches file_name, the file will not be blacklisted in any blac Example: "noblacklist ${HOME}/.mozilla" +.TP +\fBnowhitelist file_name +If the file name matches file_name, the file will not be whitelisted in any whitelist commands that follow. + +Example: "nowhitelist ~/.config" + .TP \fBignore Ignore command. diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 2b6069a7a..f603daecb 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -1066,6 +1066,10 @@ Example: .br $ firejail \-\-nosound firefox +.TP +\fB\-\-nowhitelist=dirname_or_filename +Disable whitelist for this directory or file. + .TP \fB\-\-output=logfile stdout logging and log rotation. Copy stdout and stderr to logfile, and keep the size of the file under 500KB using log diff --git a/test/fs/whitelist.exp b/test/fs/whitelist.exp index 20492c739..8ebad48f0 100755 --- a/test/fs/whitelist.exp +++ b/test/fs/whitelist.exp @@ -161,7 +161,7 @@ expect { } expect { timeout {puts "TESTING ERROR 31\n";exit} - "exiting" + "cannot sync with peer" } sleep 1 -- cgit v1.2.3-70-g09d2