aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar startx2017 <vradu.startx@yandex.com>2018-10-09 08:07:02 -0400
committerLibravatar startx2017 <vradu.startx@yandex.com>2018-10-09 08:07:02 -0400
commit009f55ffbcbb19dd40bc96e4dfaa28c2ef34093f (patch)
treecd0b34879cbb628b2e55529cb713f094feb590fd /src
parentmainline merge: fixed vim missing from firecfg.config (diff)
downloadfirejail-009f55ffbcbb19dd40bc96e4dfaa28c2ef34093f.tar.gz
firejail-009f55ffbcbb19dd40bc96e4dfaa28c2ef34093f.tar.zst
firejail-009f55ffbcbb19dd40bc96e4dfaa28c2ef34093f.zip
mainline merge:
cleanup regression: fix whitelisting of symlinks to other home dirs, small improvements tiny memleaks incomplete fix: whitelisting of symlinks to other home dirs mount empty home if macro can't be whitelisted
Diffstat (limited to 'src')
-rw-r--r--src/firejail/firejail.h1
-rw-r--r--src/firejail/fs_whitelist.c107
-rw-r--r--src/firejail/macros.c2
3 files changed, 53 insertions, 57 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 0dbe1f896..4562dd8c2 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -456,6 +456,7 @@ char *expand_home(const char *path, const char *homedir);
456char *resolve_macro(const char *name); 456char *resolve_macro(const char *name);
457void invalid_filename(const char *fname, int globbing); 457void invalid_filename(const char *fname, int globbing);
458int is_macro(const char *name); 458int is_macro(const char *name);
459int macro_id(const char *name);
459 460
460// util.c 461// util.c
461void errLogExit(char* fmt, ...); 462void errLogExit(char* fmt, ...);
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 1fd1fb675..6dd4a7e2d 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -35,27 +35,16 @@
35#define EMPTY_STRING ("") 35#define EMPTY_STRING ("")
36#define MAXBUF 4098 36#define MAXBUF 4098
37 37
38// returns mallocated memory
39char *parse_nowhitelist(int nowhitelist_flag, char *ptr1) {
40 char *rv;
41 if (nowhitelist_flag) {
42 if (asprintf(&rv, "nowhitelist ~/%s", ptr1) == -1)
43 errExit("asprintf");
44 }
45 else {
46 if (asprintf(&rv, "whitelist ~/%s", ptr1) == -1)
47 errExit("asprintf");
48 }
49 return rv;
50}
51 38
52static int mkpath(const char* path, mode_t mode) { 39static int mkpath(const char* path, mode_t mode) {
53 assert(path && *path); 40 assert(path && *path);
54 mode |= 0111; 41 mode |= 0111;
55 42
56 // create directories with uid/gid as root or as current user if inside home directory 43 // create directories with uid/gid as root or as current user if inside home directory
44 int userhome = 0;
57 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { 45 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) {
58 EUID_USER(); 46 EUID_USER();
47 userhome = 1;
59 } 48 }
60 49
61 // work on a copy of the path 50 // work on a copy of the path
@@ -90,7 +79,9 @@ static int mkpath(const char* path, mode_t mode) {
90 perror("mkdir"); 79 perror("mkdir");
91 close(parentfd); 80 close(parentfd);
92 free(dup); 81 free(dup);
93 EUID_ROOT(); 82 if (userhome) {
83 EUID_ROOT();
84 }
94 return -1; 85 return -1;
95 } 86 }
96 } 87 }
@@ -103,7 +94,9 @@ static int mkpath(const char* path, mode_t mode) {
103 perror("open"); 94 perror("open");
104 close(parentfd); 95 close(parentfd);
105 free(dup); 96 free(dup);
106 EUID_ROOT(); 97 if (userhome) {
98 EUID_ROOT();
99 }
107 return -1; 100 return -1;
108 } 101 }
109 // move on to next path segment 102 // move on to next path segment
@@ -116,7 +109,9 @@ static int mkpath(const char* path, mode_t mode) {
116 fs_logger2("mkpath", path); 109 fs_logger2("mkpath", path);
117 110
118 free(dup); 111 free(dup);
119 EUID_ROOT(); 112 if (userhome) {
113 EUID_ROOT();
114 }
120 return fd; 115 return fd;
121} 116}
122 117
@@ -128,11 +123,12 @@ static void whitelist_path(ProfileEntry *entry) {
128 char *wfile = NULL; 123 char *wfile = NULL;
129 124
130 if (entry->home_dir) { 125 if (entry->home_dir) {
131 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0) 126 if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0 || path[strlen(cfg.homedir)] != '/')
132 // symlink pointing outside /home, skip the mount 127 // either symlink pointing outside home directory
128 // or entire home directory, skip the mount
133 return; 129 return;
134 130
135 fname = path + strlen(cfg.homedir); 131 fname = path + strlen(cfg.homedir) + 1; // strlen("/home/user/")
136 132
137 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) 133 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1)
138 errExit("asprintf"); 134 errExit("asprintf");
@@ -304,13 +300,15 @@ static void whitelist_path(ProfileEntry *entry) {
304 // check the last mount operation 300 // check the last mount operation
305 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found 301 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found
306 302
303 // confirm the file was mounted on the right target
304 // strcmp does not work here, because mptr->dir can be a child mount
307 if (strncmp(mptr->dir, path, strlen(path)) != 0) 305 if (strncmp(mptr->dir, path, strlen(path)) != 0)
308 errLogExit("invalid whitelist mount"); 306 errLogExit("invalid whitelist mount");
309 // No mounts are allowed on top level directories. A destination such as "/etc" is very bad! 307 // No mounts are allowed on top level directories. A destination such as "/etc" is very bad!
310 // - there should be more than one '/' char in dest string 308 // - there should be more than one '/' char in dest string
311 if (mptr->dir == strrchr(mptr->dir, '/')) 309 if (mptr->dir == strrchr(mptr->dir, '/'))
312 errLogExit("invalid whitelist mount"); 310 errLogExit("invalid whitelist mount");
313 // confirm the correct file is mounted on path 311 // confirm the right file was mounted
314 int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 312 int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
315 if (fd4 == -1) 313 if (fd4 == -1)
316 errExit("safe_fd"); 314 errExit("safe_fd");
@@ -369,37 +367,31 @@ void fs_whitelist(void) {
369 } 367 }
370 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; 368 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
371 369
372 // resolve macros 370 // replace ~/ or ${HOME} into /home/username or resolve macro
373 if (is_macro(dataptr)) { 371 new_name = expand_home(dataptr, cfg.homedir);
374 char *tmp = resolve_macro(dataptr); // returns allocated mem 372 assert(new_name);
375 if (tmp != NULL) { 373
376 char *tmp1 = parse_nowhitelist(nowhitelist_flag, tmp); 374 // skip command if resolving the macro was not successful
377 assert(tmp1); 375 if (is_macro(new_name) && macro_id(new_name) > -1) {
378 free(tmp); 376 // mount empty home directory and print a warning
379 tmp = tmp1; 377 if (!nowhitelist_flag && !arg_private) {
380 } 378 home_dir = 1;
381 if (tmp) { 379 if (!arg_quiet) {
382 entry->data = tmp;
383 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
384 }
385 else {
386 if (!nowhitelist_flag && !arg_quiet && !arg_private) {
387 fprintf(stderr, "***\n"); 380 fprintf(stderr, "***\n");
388 fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", dataptr); 381 fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", new_name);
389 fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n"); 382 fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n");
390 fprintf(stderr, "***\n"); 383 fprintf(stderr, "***\n");
391 } 384 }
392 entry->data = EMPTY_STRING;
393 continue;
394 } 385 }
386 entry->data = EMPTY_STRING;
387 entry = entry->next;
388 free(new_name);
389 continue;
395 } 390 }
396 391
397 // replace ~/ or ${HOME} into /home/username
398 new_name = expand_home(dataptr, cfg.homedir);
399 assert(new_name);
400
401 // remove trailing slashes and single dots 392 // remove trailing slashes and single dots
402 trim_trailing_slash_or_dot(new_name); 393 if (!nowhitelist_flag)
394 trim_trailing_slash_or_dot(new_name);
403 395
404 if (arg_debug || arg_debug_whitelists) 396 if (arg_debug || arg_debug_whitelists)
405 fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); 397 fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist");
@@ -412,7 +404,7 @@ void fs_whitelist(void) {
412 } 404 }
413 405
414 // extract the absolute path of the file 406 // extract the absolute path of the file
415 // realpath function will fail with ENOENT if the file is not found 407 // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission
416 // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr 408 // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr
417 char *fname; 409 char *fname;
418 if (strcmp(new_name, "/dev/fd") == 0) 410 if (strcmp(new_name, "/dev/fd") == 0)
@@ -438,7 +430,7 @@ void fs_whitelist(void) {
438 430
439 // if 1 the file was not found; mount an empty directory 431 // if 1 the file was not found; mount an empty directory
440 if (!nowhitelist_flag) { 432 if (!nowhitelist_flag) {
441 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { 433 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0 && new_name[strlen(cfg.homedir)] == '/') {
442 if(!arg_private) 434 if(!arg_private)
443 home_dir = 1; 435 home_dir = 1;
444 } 436 }
@@ -465,6 +457,8 @@ void fs_whitelist(void) {
465 } 457 }
466 458
467 entry->data = EMPTY_STRING; 459 entry->data = EMPTY_STRING;
460 entry = entry->next;
461 free(new_name);
468 continue; 462 continue;
469 } 463 }
470 else if (arg_debug_whitelists) 464 else if (arg_debug_whitelists)
@@ -483,18 +477,22 @@ void fs_whitelist(void) {
483 } 477 }
484 nowhitelist[nowhitelist_c++] = fname; 478 nowhitelist[nowhitelist_c++] = fname;
485 entry->data = EMPTY_STRING; 479 entry->data = EMPTY_STRING;
480 entry = entry->next;
481 free(new_name);
486 continue; 482 continue;
487 } 483 }
488 484
489 // check for supported directories 485 // check for supported directories
490 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { 486 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0 && new_name[strlen(cfg.homedir)] == '/') {
491 // whitelisting home directory is disabled if --private option is present 487 // whitelisting home directory is disabled if --private option is present
492 if (arg_private) { 488 if (arg_private) {
493 if (arg_debug || arg_debug_whitelists) 489 if (arg_debug || arg_debug_whitelists)
494 printf("\"%s\" disabled by --private\n", entry->data); 490 printf("\"%s\" disabled by --private\n", entry->data);
495 491
496 entry->data = EMPTY_STRING; 492 entry->data = EMPTY_STRING;
493 entry = entry->next;
497 free(fname); 494 free(fname);
495 free(new_name);
498 continue; 496 continue;
499 } 497 }
500 498
@@ -504,17 +502,10 @@ void fs_whitelist(void) {
504 fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", 502 fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n",
505 __LINE__, fname, cfg.homedir); 503 __LINE__, fname, cfg.homedir);
506 504
507 // both path and absolute path are under /home 505 // both path and absolute path are in user home,
508 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { 506 // if not check if the symlink destination is owned by the user
509 // entire home directory is not allowed 507 if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0 || fname[strlen(cfg.homedir)] != '/') {
510 if (*(fname + strlen(cfg.homedir)) != '/') {
511 free(fname);
512 goto errexit;
513 }
514 }
515 else {
516 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { 508 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) {
517 // check if the file is owned by the user
518 if (stat(fname, &s) == 0 && s.st_uid != getuid()) { 509 if (stat(fname, &s) == 0 && s.st_uid != getuid()) {
519 free(fname); 510 free(fname);
520 goto errexit; 511 goto errexit;
@@ -659,7 +650,9 @@ void fs_whitelist(void) {
659 if (arg_debug || arg_debug_whitelists) 650 if (arg_debug || arg_debug_whitelists)
660 printf("Skip nowhitelisted path %s\n", fname); 651 printf("Skip nowhitelisted path %s\n", fname);
661 entry->data = EMPTY_STRING; 652 entry->data = EMPTY_STRING;
653 entry = entry->next;
662 free(fname); 654 free(fname);
655 free(new_name);
663 continue; 656 continue;
664 } 657 }
665 } 658 }
@@ -916,6 +909,7 @@ void fs_whitelist(void) {
916 if (arg_debug || arg_debug_whitelists) 909 if (arg_debug || arg_debug_whitelists)
917 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); 910 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link);
918 free(entry->link); 911 free(entry->link);
912 entry->link = NULL;
919 entry = entry->next; 913 entry = entry->next;
920 continue; 914 continue;
921 } 915 }
@@ -934,6 +928,7 @@ void fs_whitelist(void) {
934 close(fd); 928 close(fd);
935 } 929 }
936 free(entry->link); 930 free(entry->link);
931 entry->link = NULL;
937 } 932 }
938 933
939 entry = entry->next; 934 entry = entry->next;
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
index 27893938f..4bf3d3589 100644
--- a/src/firejail/macros.c
+++ b/src/firejail/macros.c
@@ -69,7 +69,7 @@ Macro macro[] = {
69}; 69};
70 70
71// return -1 if not found 71// return -1 if not found
72static int macro_id(const char *name) { 72int macro_id(const char *name) {
73 int i = 0; 73 int i = 0;
74 while (macro[i].name != NULL) { 74 while (macro[i].name != NULL) {
75 if (strcmp(name, macro[i].name) == 0) 75 if (strcmp(name, macro[i].name) == 0)