aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2018-10-01 01:04:30 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2018-10-01 01:04:30 +0200
commit51eeef2059f00de117472046601e10a9fd958d51 (patch)
tree1cafc226fce3784518f691f250e92789c5af4a99
parentcleanup (diff)
downloadfirejail-51eeef2059f00de117472046601e10a9fd958d51.tar.gz
firejail-51eeef2059f00de117472046601e10a9fd958d51.tar.zst
firejail-51eeef2059f00de117472046601e10a9fd958d51.zip
regression: fix whitelisting of symlinks to other home dirs, small improvements
handling of home dir paths is more explicit and rigorous now, which should make it easier to audit. Also this should come handy if one day fs_private() supports home directories outside /home rf. #2123
-rw-r--r--src/firejail/fs_whitelist.c47
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;