From 829d9e0c4c9ae700ab02e2809ac1d5eb72532357 Mon Sep 17 00:00:00 2001 From: sarneaud Date: Tue, 1 Sep 2015 14:29:26 +1000 Subject: Simple implementation of noblacklist command. --- src/firejail/fs.c | 76 +++++++++++++++++++++++++++++++++++--------------- src/firejail/profile.c | 2 ++ src/include/common.h | 1 + 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 8a6dfc674..8632952a4 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -198,7 +199,7 @@ static void disable_file(OPERATION op, const char *filename, const char *emptydi } // Treat pattern as a shell glob pattern and blacklist matching files -static void globbing(OPERATION op, const char *pattern, const char *emptydir, const char *emptyfile) { +static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len, const char *emptydir, const char *emptyfile) { assert(pattern); assert(emptydir); assert(emptyfile); @@ -209,29 +210,34 @@ static void globbing(OPERATION op, const char *pattern, const char *emptydir, co int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf); if (globerr) { fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); - return; + exit(1); } - size_t i; + size_t i, j; for (i = 0; i < globbuf.gl_pathc; i++) { - char* match = globbuf.gl_pathv[i]; - assert(match); - disable_file(op, match, emptydir, emptyfile); + char* path = globbuf.gl_pathv[i]; + assert(path); + // noblacklist is expected to be short in normal cases, so stupid and correct brute force is okay + bool okay_to_blacklist = true; + for (j = 0; j < noblacklist_len; j++) { + int result = fnmatch(noblacklist[j], path, FNM_PATHNAME); + if (result == FNM_NOMATCH) + continue; + else if (result == 0) { + okay_to_blacklist = false; + break; + } + else { + fprintf(stderr, "Error: failed to compare path %s with pattern %s\n", path, noblacklist[j]); + exit(1); + } + } + if (okay_to_blacklist) + disable_file(op, path, emptydir, emptyfile); } globfree(&globbuf); } -static void expand_path(OPERATION op, const char *path, const char *fname, const char *emptydir, const char *emptyfile) { - assert(path); - assert(fname); - assert(emptydir); - assert(emptyfile); - char newname[strlen(path) + strlen(fname) + 1]; - sprintf(newname, "%s%s", path, fname); - - globbing(op, newname, emptydir, emptyfile); -} - // blacklist files or directoies by mounting empty files on top of them void fs_blacklist(const char *homedir) { ProfileEntry *entry = cfg.profile; @@ -241,6 +247,12 @@ void fs_blacklist(const char *homedir) { char *emptydir = create_empty_dir(); char *emptyfile = create_empty_file(); + // a statically allocated buffer works for all current needs + // TODO: if dynamic allocation is ever needed, we should probably add + // libraries that make it easy to do without introducing security bugs + char *noblacklist[32]; + size_t noblacklist_c = 0; + while (entry) { OPERATION op = OPERATION_MAX; char *ptr; @@ -283,6 +295,18 @@ void fs_blacklist(const char *homedir) { continue; } + // Process noblacklist command + if (strncmp(entry->data, "noblacklist ", 12) == 0) { + if (noblacklist_c >= sizeof(noblacklist) / sizeof(noblacklist[0])) { + fputs("Error: out of memory for noblacklist entries\n", stderr); + exit(1); + } + else + noblacklist[noblacklist_c++] = expand_home(entry->data + 12, homedir); + entry = entry->next; + continue; + } + // process blacklist command if (strncmp(entry->data, "blacklist ", 10) == 0) { ptr = entry->data + 10; @@ -307,19 +331,27 @@ void fs_blacklist(const char *homedir) { ptr = new_name; // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories + // TODO: should we look for more bin paths? if (strncmp(ptr, "${PATH}", 7) == 0) { - expand_path(op, "/bin", ptr + 7, emptydir, emptyfile); - expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile); - expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile); - expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile); + char *fname = ptr + 7; + size_t fname_len = strlen(fname); + char **path, *paths[] = {"/bin", "/sbin", "/usr/bin", "/usr/sbin", NULL}; + for (path = &paths[0]; *path; path++) { + char newname[strlen(*path) + fname_len + 1]; + sprintf(newname, "%s%s", *path, fname); + globbing(op, newname, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); + } } else - globbing(op, ptr, emptydir, emptyfile); + globbing(op, ptr, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); if (new_name) free(new_name); entry = entry->next; } + + size_t i; + for (i = 0; i < noblacklist_c; i++) free(noblacklist[i]); } //*********************************************** diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 1093e1503..778478321 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -334,6 +334,8 @@ int profile_check_line(char *ptr, int lineno) { // rest of filesystem if (strncmp(ptr, "blacklist ", 10) == 0) ptr += 10; + else if (strncmp(ptr, "noblacklist ", 12) == 0) + ptr += 12; else if (strncmp(ptr, "read-only ", 10) == 0) ptr += 10; else if (strncmp(ptr, "tmpfs ", 6) == 0) diff --git a/src/include/common.h b/src/include/common.h index 7ce1e9290..a42e7ad9e 100644 --- a/src/include/common.h +++ b/src/include/common.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-70-g09d2 From 91e11d155455ee6d89537942b36d09fbe86ff6ff Mon Sep 17 00:00:00 2001 From: sarneaud Date: Tue, 1 Sep 2015 14:37:50 +1000 Subject: Update profiles to use the new noblacklist command. --- etc/chromium.profile | 3 ++- etc/filezilla.profile | 4 +++- etc/firefox.profile | 3 ++- etc/midori.profile | 5 +++-- etc/opera.profile | 3 ++- etc/server.profile | 4 +++- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/etc/chromium.profile b/etc/chromium.profile index 13559a5a8..6c3a5f2f8 100644 --- a/etc/chromium.profile +++ b/etc/chromium.profile @@ -1,7 +1,8 @@ # Chromium browser profile +noblacklist ${HOME}/.config/chromium include /etc/firejail/disable-mgmt.inc include /etc/firejail/disable-secret.inc -include /etc/firejail/disable-common.inc chromium +include /etc/firejail/disable-common.inc include /etc/firejail/disable-history.inc netfilter diff --git a/etc/filezilla.profile b/etc/filezilla.profile index dc5086595..437fa6d43 100644 --- a/etc/filezilla.profile +++ b/etc/filezilla.profile @@ -1,7 +1,9 @@ # FileZilla profile +noblacklist ${HOME}/.filezilla +noblacklist ${HOME}/.config/filezilla include /etc/firejail/disable-mgmt.inc include /etc/firejail/disable-secret.inc -include /etc/firejail/disable-common.inc .filezilla +include /etc/firejail/disable-common.inc include /etc/firejail/disable-history.inc caps.drop all seccomp diff --git a/etc/firefox.profile b/etc/firefox.profile index cd504ab44..e5b820d39 100644 --- a/etc/firefox.profile +++ b/etc/firefox.profile @@ -1,7 +1,8 @@ # Firejail profile for Mozilla Firefox (Iceweasel in Debian) +noblacklist ${HOME}/.mozilla include /etc/firejail/disable-mgmt.inc include /etc/firejail/disable-secret.inc -include /etc/firejail/disable-common.inc .mozilla +include /etc/firejail/disable-common.inc include /etc/firejail/disable-history.inc caps.drop all seccomp diff --git a/etc/midori.profile b/etc/midori.profile index b21bc94ef..5bc864e31 100644 --- a/etc/midori.profile +++ b/etc/midori.profile @@ -1,7 +1,8 @@ -# Midory browser profile +# Midori browser profile +noblacklist ${HOME}/.config/midori include /etc/firejail/disable-mgmt.inc include /etc/firejail/disable-secret.inc -include /etc/firejail/disable-common.inc midori +include /etc/firejail/disable-common.inc include /etc/firejail/disable-history.inc caps.drop all seccomp diff --git a/etc/opera.profile b/etc/opera.profile index 8f8dbc609..d55c0aaa3 100644 --- a/etc/opera.profile +++ b/etc/opera.profile @@ -1,7 +1,8 @@ # Chromium browser profile +noblacklist ${HOME}/.config/opera include /etc/firejail/disable-mgmt.inc include /etc/firejail/disable-secret.inc -include /etc/firejail/disable-common.inc opera +include /etc/firejail/disable-common.inc include /etc/firejail/disable-history.inc netfilter noroot diff --git a/etc/server.profile b/etc/server.profile index 1c6461094..5b706df9a 100644 --- a/etc/server.profile +++ b/etc/server.profile @@ -1,6 +1,8 @@ # generic server profile # it allows /sbin and /usr/sbin directories - this is where servers are installed -include /etc/firejail/disable-mgmt.inc sbin +noblacklist /sbin +noblacklist /usr/sbin +include /etc/firejail/disable-mgmt.inc private private-dev seccomp -- cgit v1.2.3-70-g09d2 From 2aa7ec97db26c567a6b2d45cd906c062960584dd Mon Sep 17 00:00:00 2001 From: sarneaud Date: Tue, 1 Sep 2015 15:07:30 +1000 Subject: Add noblacklist command to firejail. * Basic implementation * Updates to standard profiles * Update to firejail-profile manpage --- src/man/firejail-profile.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 5167a4c42..64565ab0b 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -87,6 +87,7 @@ Example: "include ${HOME}/myprofiles/profile1" will load "~/myprofiles/profile1" These profile entries define a chroot filesystem built on top of the existing host filesystem. Each line describes a file element that is removed from the filesystem (\fBblacklist\fR), a read-only file or directory (\fBread-only\fR), +a filter for finer control of blacklisting (\fBnoblacklist\fR), a tmpfs mounted on top of an existing directory (\fBtmpfs\fR), or mount-bind a directory or file on top of another directory or file (\fBbind\fR). Use \fBprivate\fR to set private mode. @@ -117,6 +118,14 @@ Remove ifconfig command from the regular path directories. \f\blacklist ${HOME}/.ssh Remove .ssh directory from user home directory. .TP +\f\ noblacklist ${HOME}/config/evince +Prevent any new blacklist commands from blacklisting +config/evince in the user home directory. Useful for defining +exceptions before including a large blacklist from a file. Note +that blacklisting ${HOME}/config can still make +${HOME}/config/evince effectively unreachable through filesystem +traversal. +.TP \f\private Mount new /root and /home/user directories in temporary filesystems. All modifications are discarded when the sandbox is -- cgit v1.2.3-70-g09d2