diff options
Diffstat (limited to 'src/firejail/fs.c')
-rw-r--r-- | src/firejail/fs.c | 76 |
1 files changed, 54 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 | //*********************************************** |