diff options
Diffstat (limited to 'src/firejail/fs_whitelist.c')
-rw-r--r-- | src/firejail/fs_whitelist.c | 47 |
1 files changed, 27 insertions, 20 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 9bedcc708..531cc4e48 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -41,8 +41,10 @@ static int mkpath(const char* path, mode_t mode) { | |||
41 | mode |= 0111; | 41 | mode |= 0111; |
42 | 42 | ||
43 | // 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; | ||
44 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { | 45 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) == 0) { |
45 | EUID_USER(); | 46 | EUID_USER(); |
47 | userhome = 1; | ||
46 | } | 48 | } |
47 | 49 | ||
48 | // work on a copy of the path | 50 | // work on a copy of the path |
@@ -77,7 +79,9 @@ static int mkpath(const char* path, mode_t mode) { | |||
77 | perror("mkdir"); | 79 | perror("mkdir"); |
78 | close(parentfd); | 80 | close(parentfd); |
79 | free(dup); | 81 | free(dup); |
80 | EUID_ROOT(); | 82 | if (userhome) { |
83 | EUID_ROOT(); | ||
84 | } | ||
81 | return -1; | 85 | return -1; |
82 | } | 86 | } |
83 | } | 87 | } |
@@ -90,7 +94,9 @@ static int mkpath(const char* path, mode_t mode) { | |||
90 | perror("open"); | 94 | perror("open"); |
91 | close(parentfd); | 95 | close(parentfd); |
92 | free(dup); | 96 | free(dup); |
93 | EUID_ROOT(); | 97 | if (userhome) { |
98 | EUID_ROOT(); | ||
99 | } | ||
94 | return -1; | 100 | return -1; |
95 | } | 101 | } |
96 | // move on to next path segment | 102 | // move on to next path segment |
@@ -103,7 +109,9 @@ static int mkpath(const char* path, mode_t mode) { | |||
103 | fs_logger2("mkpath", path); | 109 | fs_logger2("mkpath", path); |
104 | 110 | ||
105 | free(dup); | 111 | free(dup); |
106 | EUID_ROOT(); | 112 | if (userhome) { |
113 | EUID_ROOT(); | ||
114 | } | ||
107 | return fd; | 115 | return fd; |
108 | } | 116 | } |
109 | 117 | ||
@@ -115,11 +123,12 @@ static void whitelist_path(ProfileEntry *entry) { | |||
115 | char *wfile = NULL; | 123 | char *wfile = NULL; |
116 | 124 | ||
117 | if (entry->home_dir) { | 125 | if (entry->home_dir) { |
118 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0) | 126 | if (strncmp(path, cfg.homedir, strlen(cfg.homedir)) != 0 || path[strlen(cfg.homedir)] != '/') |
119 | // symlink pointing outside /home, skip the mount | 127 | // either symlink pointing outside home directory |
128 | // or entire home directory, skip the mount | ||
120 | return; | 129 | return; |
121 | 130 | ||
122 | fname = path + strlen(cfg.homedir); | 131 | fname = path + strlen(cfg.homedir) + 1; // strlen("/home/user/") |
123 | 132 | ||
124 | 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) |
125 | errExit("asprintf"); | 134 | errExit("asprintf"); |
@@ -291,13 +300,15 @@ static void whitelist_path(ProfileEntry *entry) { | |||
291 | // check the last mount operation | 300 | // check the last mount operation |
292 | 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 |
293 | 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 | ||
294 | if (strncmp(mptr->dir, path, strlen(path)) != 0) | 305 | if (strncmp(mptr->dir, path, strlen(path)) != 0) |
295 | errLogExit("invalid whitelist mount"); | 306 | errLogExit("invalid whitelist mount"); |
296 | // 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! |
297 | // - there should be more than one '/' char in dest string | 308 | // - there should be more than one '/' char in dest string |
298 | if (mptr->dir == strrchr(mptr->dir, '/')) | 309 | if (mptr->dir == strrchr(mptr->dir, '/')) |
299 | errLogExit("invalid whitelist mount"); | 310 | errLogExit("invalid whitelist mount"); |
300 | // confirm the correct file is mounted on path | 311 | // confirm the right file was mounted |
301 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 312 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
302 | if (fd4 == -1) | 313 | if (fd4 == -1) |
303 | errExit("safe_fd"); | 314 | errExit("safe_fd"); |
@@ -374,7 +385,8 @@ void fs_whitelist(void) { | |||
374 | } | 385 | } |
375 | 386 | ||
376 | // remove trailing slashes and single dots | 387 | // remove trailing slashes and single dots |
377 | trim_trailing_slash_or_dot(new_name); | 388 | if (!nowhitelist_flag) |
389 | trim_trailing_slash_or_dot(new_name); | ||
378 | 390 | ||
379 | if (arg_debug || arg_debug_whitelists) | 391 | if (arg_debug || arg_debug_whitelists) |
380 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); | 392 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); |
@@ -387,7 +399,7 @@ void fs_whitelist(void) { | |||
387 | } | 399 | } |
388 | 400 | ||
389 | // extract the absolute path of the file | 401 | // extract the absolute path of the file |
390 | // realpath function will fail with ENOENT if the file is not found | 402 | // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission |
391 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr | 403 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr |
392 | char *fname; | 404 | char *fname; |
393 | if (strcmp(new_name, "/dev/fd") == 0) | 405 | if (strcmp(new_name, "/dev/fd") == 0) |
@@ -462,7 +474,7 @@ void fs_whitelist(void) { | |||
462 | } | 474 | } |
463 | 475 | ||
464 | // check for supported directories | 476 | // check for supported directories |
465 | if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { | 477 | if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0 && new_name[strlen(cfg.homedir)] == '/') { |
466 | // whitelisting home directory is disabled if --private option is present | 478 | // whitelisting home directory is disabled if --private option is present |
467 | if (arg_private) { | 479 | if (arg_private) { |
468 | if (arg_debug || arg_debug_whitelists) | 480 | if (arg_debug || arg_debug_whitelists) |
@@ -479,17 +491,10 @@ void fs_whitelist(void) { | |||
479 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", | 491 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", |
480 | __LINE__, fname, cfg.homedir); | 492 | __LINE__, fname, cfg.homedir); |
481 | 493 | ||
482 | // both path and absolute path are under /home | 494 | // both path and absolute path are in user home, |
483 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) == 0) { | 495 | // if not check if the symlink destination is owned by the user |
484 | // entire home directory is not allowed | 496 | if (strncmp(fname, cfg.homedir, strlen(cfg.homedir)) != 0 || fname[strlen(cfg.homedir)] != '/') { |
485 | if (*(fname + strlen(cfg.homedir)) != '/') { | ||
486 | free(fname); | ||
487 | goto errexit; | ||
488 | } | ||
489 | } | ||
490 | else { | ||
491 | if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { | 497 | if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { |
492 | // check if the file is owned by the user | ||
493 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) { | 498 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) { |
494 | free(fname); | 499 | free(fname); |
495 | goto errexit; | 500 | goto errexit; |
@@ -891,6 +896,7 @@ void fs_whitelist(void) { | |||
891 | if (arg_debug || arg_debug_whitelists) | 896 | if (arg_debug || arg_debug_whitelists) |
892 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); | 897 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); |
893 | free(entry->link); | 898 | free(entry->link); |
899 | entry->link = NULL; | ||
894 | entry = entry->next; | 900 | entry = entry->next; |
895 | continue; | 901 | continue; |
896 | } | 902 | } |
@@ -909,6 +915,7 @@ void fs_whitelist(void) { | |||
909 | close(fd); | 915 | close(fd); |
910 | } | 916 | } |
911 | free(entry->link); | 917 | free(entry->link); |
918 | entry->link = NULL; | ||
912 | } | 919 | } |
913 | 920 | ||
914 | entry = entry->next; | 921 | entry = entry->next; |