diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/fs.c | 76 | ||||
-rw-r--r-- | src/firejail/profile.c | 2 | ||||
-rw-r--r-- | src/include/common.h | 1 | ||||
-rw-r--r-- | src/man/firejail-profile.txt | 9 |
4 files changed, 66 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 @@ | |||
21 | #include <sys/mount.h> | 21 | #include <sys/mount.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <linux/limits.h> | 23 | #include <linux/limits.h> |
24 | #include <fnmatch.h> | ||
24 | #include <glob.h> | 25 | #include <glob.h> |
25 | #include <dirent.h> | 26 | #include <dirent.h> |
26 | #include <fcntl.h> | 27 | #include <fcntl.h> |
@@ -198,7 +199,7 @@ static void disable_file(OPERATION op, const char *filename, const char *emptydi | |||
198 | } | 199 | } |
199 | 200 | ||
200 | // Treat pattern as a shell glob pattern and blacklist matching files | 201 | // Treat pattern as a shell glob pattern and blacklist matching files |
201 | static void globbing(OPERATION op, const char *pattern, const char *emptydir, const char *emptyfile) { | 202 | static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len, const char *emptydir, const char *emptyfile) { |
202 | assert(pattern); | 203 | assert(pattern); |
203 | assert(emptydir); | 204 | assert(emptydir); |
204 | assert(emptyfile); | 205 | assert(emptyfile); |
@@ -209,29 +210,34 @@ static void globbing(OPERATION op, const char *pattern, const char *emptydir, co | |||
209 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf); | 210 | int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf); |
210 | if (globerr) { | 211 | if (globerr) { |
211 | fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); | 212 | fprintf(stderr, "Error: failed to glob pattern %s\n", pattern); |
212 | return; | 213 | exit(1); |
213 | } | 214 | } |
214 | 215 | ||
215 | size_t i; | 216 | size_t i, j; |
216 | for (i = 0; i < globbuf.gl_pathc; i++) { | 217 | for (i = 0; i < globbuf.gl_pathc; i++) { |
217 | char* match = globbuf.gl_pathv[i]; | 218 | char* path = globbuf.gl_pathv[i]; |
218 | assert(match); | 219 | assert(path); |
219 | disable_file(op, match, emptydir, emptyfile); | 220 | // noblacklist is expected to be short in normal cases, so stupid and correct brute force is okay |
221 | bool okay_to_blacklist = true; | ||
222 | for (j = 0; j < noblacklist_len; j++) { | ||
223 | int result = fnmatch(noblacklist[j], path, FNM_PATHNAME); | ||
224 | if (result == FNM_NOMATCH) | ||
225 | continue; | ||
226 | else if (result == 0) { | ||
227 | okay_to_blacklist = false; | ||
228 | break; | ||
229 | } | ||
230 | else { | ||
231 | fprintf(stderr, "Error: failed to compare path %s with pattern %s\n", path, noblacklist[j]); | ||
232 | exit(1); | ||
233 | } | ||
234 | } | ||
235 | if (okay_to_blacklist) | ||
236 | disable_file(op, path, emptydir, emptyfile); | ||
220 | } | 237 | } |
221 | globfree(&globbuf); | 238 | globfree(&globbuf); |
222 | } | 239 | } |
223 | 240 | ||
224 | static void expand_path(OPERATION op, const char *path, const char *fname, const char *emptydir, const char *emptyfile) { | ||
225 | assert(path); | ||
226 | assert(fname); | ||
227 | assert(emptydir); | ||
228 | assert(emptyfile); | ||
229 | char newname[strlen(path) + strlen(fname) + 1]; | ||
230 | sprintf(newname, "%s%s", path, fname); | ||
231 | |||
232 | globbing(op, newname, emptydir, emptyfile); | ||
233 | } | ||
234 | |||
235 | // blacklist files or directoies by mounting empty files on top of them | 241 | // blacklist files or directoies by mounting empty files on top of them |
236 | void fs_blacklist(const char *homedir) { | 242 | void fs_blacklist(const char *homedir) { |
237 | ProfileEntry *entry = cfg.profile; | 243 | ProfileEntry *entry = cfg.profile; |
@@ -241,6 +247,12 @@ void fs_blacklist(const char *homedir) { | |||
241 | char *emptydir = create_empty_dir(); | 247 | char *emptydir = create_empty_dir(); |
242 | char *emptyfile = create_empty_file(); | 248 | char *emptyfile = create_empty_file(); |
243 | 249 | ||
250 | // a statically allocated buffer works for all current needs | ||
251 | // TODO: if dynamic allocation is ever needed, we should probably add | ||
252 | // libraries that make it easy to do without introducing security bugs | ||
253 | char *noblacklist[32]; | ||
254 | size_t noblacklist_c = 0; | ||
255 | |||
244 | while (entry) { | 256 | while (entry) { |
245 | OPERATION op = OPERATION_MAX; | 257 | OPERATION op = OPERATION_MAX; |
246 | char *ptr; | 258 | char *ptr; |
@@ -283,6 +295,18 @@ void fs_blacklist(const char *homedir) { | |||
283 | continue; | 295 | continue; |
284 | } | 296 | } |
285 | 297 | ||
298 | // Process noblacklist command | ||
299 | if (strncmp(entry->data, "noblacklist ", 12) == 0) { | ||
300 | if (noblacklist_c >= sizeof(noblacklist) / sizeof(noblacklist[0])) { | ||
301 | fputs("Error: out of memory for noblacklist entries\n", stderr); | ||
302 | exit(1); | ||
303 | } | ||
304 | else | ||
305 | noblacklist[noblacklist_c++] = expand_home(entry->data + 12, homedir); | ||
306 | entry = entry->next; | ||
307 | continue; | ||
308 | } | ||
309 | |||
286 | // process blacklist command | 310 | // process blacklist command |
287 | if (strncmp(entry->data, "blacklist ", 10) == 0) { | 311 | if (strncmp(entry->data, "blacklist ", 10) == 0) { |
288 | ptr = entry->data + 10; | 312 | ptr = entry->data + 10; |
@@ -307,19 +331,27 @@ void fs_blacklist(const char *homedir) { | |||
307 | ptr = new_name; | 331 | ptr = new_name; |
308 | 332 | ||
309 | // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories | 333 | // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories |
334 | // TODO: should we look for more bin paths? | ||
310 | if (strncmp(ptr, "${PATH}", 7) == 0) { | 335 | if (strncmp(ptr, "${PATH}", 7) == 0) { |
311 | expand_path(op, "/bin", ptr + 7, emptydir, emptyfile); | 336 | char *fname = ptr + 7; |
312 | expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile); | 337 | size_t fname_len = strlen(fname); |
313 | expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile); | 338 | char **path, *paths[] = {"/bin", "/sbin", "/usr/bin", "/usr/sbin", NULL}; |
314 | expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile); | 339 | for (path = &paths[0]; *path; path++) { |
340 | char newname[strlen(*path) + fname_len + 1]; | ||
341 | sprintf(newname, "%s%s", *path, fname); | ||
342 | globbing(op, newname, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); | ||
343 | } | ||
315 | } | 344 | } |
316 | else | 345 | else |
317 | globbing(op, ptr, emptydir, emptyfile); | 346 | globbing(op, ptr, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); |
318 | 347 | ||
319 | if (new_name) | 348 | if (new_name) |
320 | free(new_name); | 349 | free(new_name); |
321 | entry = entry->next; | 350 | entry = entry->next; |
322 | } | 351 | } |
352 | |||
353 | size_t i; | ||
354 | for (i = 0; i < noblacklist_c; i++) free(noblacklist[i]); | ||
323 | } | 355 | } |
324 | 356 | ||
325 | //*********************************************** | 357 | //*********************************************** |
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) { | |||
334 | // rest of filesystem | 334 | // rest of filesystem |
335 | if (strncmp(ptr, "blacklist ", 10) == 0) | 335 | if (strncmp(ptr, "blacklist ", 10) == 0) |
336 | ptr += 10; | 336 | ptr += 10; |
337 | else if (strncmp(ptr, "noblacklist ", 12) == 0) | ||
338 | ptr += 12; | ||
337 | else if (strncmp(ptr, "read-only ", 10) == 0) | 339 | else if (strncmp(ptr, "read-only ", 10) == 0) |
338 | ptr += 10; | 340 | ptr += 10; |
339 | else if (strncmp(ptr, "tmpfs ", 6) == 0) | 341 | 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 @@ | |||
25 | #include <sys/types.h> | 25 | #include <sys/types.h> |
26 | #include <unistd.h> | 26 | #include <unistd.h> |
27 | #include <stdlib.h> | 27 | #include <stdlib.h> |
28 | #include <stdbool.h> | ||
28 | #include <stdint.h> | 29 | #include <stdint.h> |
29 | #include <stddef.h> | 30 | #include <stddef.h> |
30 | #include <string.h> | 31 | #include <string.h> |
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" | |||
87 | These profile entries define a chroot filesystem built on top of the existing | 87 | These profile entries define a chroot filesystem built on top of the existing |
88 | host filesystem. Each line describes a file element that is removed from | 88 | host filesystem. Each line describes a file element that is removed from |
89 | the filesystem (\fBblacklist\fR), a read-only file or directory (\fBread-only\fR), | 89 | the filesystem (\fBblacklist\fR), a read-only file or directory (\fBread-only\fR), |
90 | a filter for finer control of blacklisting (\fBnoblacklist\fR), | ||
90 | a tmpfs mounted on top of an existing directory (\fBtmpfs\fR), | 91 | a tmpfs mounted on top of an existing directory (\fBtmpfs\fR), |
91 | or mount-bind a directory or file on top of another directory or file (\fBbind\fR). | 92 | or mount-bind a directory or file on top of another directory or file (\fBbind\fR). |
92 | Use \fBprivate\fR to set private mode. | 93 | Use \fBprivate\fR to set private mode. |
@@ -117,6 +118,14 @@ Remove ifconfig command from the regular path directories. | |||
117 | \f\blacklist ${HOME}/.ssh | 118 | \f\blacklist ${HOME}/.ssh |
118 | Remove .ssh directory from user home directory. | 119 | Remove .ssh directory from user home directory. |
119 | .TP | 120 | .TP |
121 | \f\ noblacklist ${HOME}/config/evince | ||
122 | Prevent any new blacklist commands from blacklisting | ||
123 | config/evince in the user home directory. Useful for defining | ||
124 | exceptions before including a large blacklist from a file. Note | ||
125 | that blacklisting ${HOME}/config can still make | ||
126 | ${HOME}/config/evince effectively unreachable through filesystem | ||
127 | traversal. | ||
128 | .TP | ||
120 | \f\private | 129 | \f\private |
121 | Mount new /root and /home/user directories in temporary | 130 | Mount new /root and /home/user directories in temporary |
122 | filesystems. All modifications are discarded when the sandbox is | 131 | filesystems. All modifications are discarded when the sandbox is |