aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2021-05-23 19:04:18 +0200
committerLibravatar GitHub <noreply@github.com>2021-05-23 19:04:18 +0200
commit41a895a4c661d5aeec0b804762aeec680ff4ec40 (patch)
treed62c78fdf1d8c66a6ea8f34751eed83b088eb452 /src
parentMerge pull request #4300 from netblue30/gcc11 (diff)
parentwhitelist testing (#4229, #4297, #4300) (diff)
downloadfirejail-41a895a4c661d5aeec0b804762aeec680ff4ec40.tar.gz
firejail-41a895a4c661d5aeec0b804762aeec680ff4ec40.tar.zst
firejail-41a895a4c661d5aeec0b804762aeec680ff4ec40.zip
Merge pull request #4302 from smitsohu/whitelist2
Whitelist2 follow-up
Diffstat (limited to 'src')
-rw-r--r--src/firejail/fs_whitelist.c74
1 files changed, 43 insertions, 31 deletions
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index c7dbe6496..77bb5e5bb 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -104,21 +104,19 @@ static int whitelist_mkpath(const char* path, mode_t mode) {
104 return fd; 104 return fd;
105} 105}
106 106
107static void whitelist_file(int dirfd, const char *topdir, const char *relpath, const char *path) { 107static void whitelist_file(int dirfd, const char *relpath, const char *path) {
108 assert(topdir && relpath && path); 108 assert(relpath && path);
109
110 if (arg_debug || arg_debug_whitelists)
111 printf("Debug %d: dirfd: %d; topdir: %s; relpath: %s; path: %s\n", __LINE__, dirfd, topdir, relpath, path);
112 109
113 // open mount source, using a file descriptor that refers to the 110 // open mount source, using a file descriptor that refers to the
114 // top level directory 111 // top level directory
115 // as the top level directory was opened before mounting the tmpfs 112 // as the top level directory was opened before mounting the tmpfs
116 // we still have full access to all directory contents 113 // we still have full access to all directory contents
117 // take care to not follow symbolic links 114 // take care to not follow symbolic links (dirfd was obtained without
115 // following a link, too)
118 int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC); 116 int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC);
119 if (fd == -1) { 117 if (fd == -1) {
120 if (arg_debug || arg_debug_whitelists) 118 if (arg_debug || arg_debug_whitelists)
121 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 119 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
122 return; 120 return;
123 } 121 }
124 struct stat s; 122 struct stat s;
@@ -126,14 +124,15 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
126 errExit("fstat"); 124 errExit("fstat");
127 if (S_ISLNK(s.st_mode)) { 125 if (S_ISLNK(s.st_mode)) {
128 if (arg_debug || arg_debug_whitelists) 126 if (arg_debug || arg_debug_whitelists)
129 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 127 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
130 close(fd); 128 close(fd);
131 return; 129 return;
132 } 130 }
133 131
134 // create mount target as root, except if inside home or run/user/$UID directory 132 // create mount target as root, except if inside home or run/user/$UID directory
135 int userprivs = 0; 133 int userprivs = 0;
136 if (strcmp(topdir, cfg.homedir) == 0 || strcmp(topdir, runuser) == 0) { 134 if ((strncmp(path, cfg.homedir, homedir_len) == 0 && path[homedir_len] == '/') ||
135 (strncmp(path, runuser, runuser_len) == 0 && path[runuser_len] == '/')) {
137 EUID_USER(); 136 EUID_USER();
138 userprivs = 1; 137 userprivs = 1;
139 } 138 }
@@ -145,7 +144,7 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
145 // if there is a symlink somewhere in the path of the mount target, 144 // if there is a symlink somewhere in the path of the mount target,
146 // assume the file is whitelisted already 145 // assume the file is whitelisted already
147 if (arg_debug || arg_debug_whitelists) 146 if (arg_debug || arg_debug_whitelists)
148 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 147 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
149 close(fd); 148 close(fd);
150 if (userprivs) 149 if (userprivs)
151 EUID_ROOT(); 150 EUID_ROOT();
@@ -163,7 +162,7 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
163 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { 162 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) {
164 if (arg_debug || arg_debug_whitelists) { 163 if (arg_debug || arg_debug_whitelists) {
165 perror("mkdir"); 164 perror("mkdir");
166 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 165 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
167 } 166 }
168 close(fd); 167 close(fd);
169 close(fd2); 168 close(fd2);
@@ -181,7 +180,7 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
181 if (fd3 == -1) { 180 if (fd3 == -1) {
182 if (errno != EEXIST && (arg_debug || arg_debug_whitelists)) { 181 if (errno != EEXIST && (arg_debug || arg_debug_whitelists)) {
183 perror("open"); 182 perror("open");
184 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 183 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
185 } 184 }
186 close(fd); 185 close(fd);
187 close(fd2); 186 close(fd2);
@@ -229,15 +228,13 @@ static void whitelist_file(int dirfd, const char *topdir, const char *relpath, c
229 fs_logger2("whitelist", path); 228 fs_logger2("whitelist", path);
230} 229}
231 230
232static void whitelist_symlink(const char *topdir, const char *link, const char *target) { 231static void whitelist_symlink(const char *link, const char *target) {
233 assert(topdir && link && target); 232 assert(link && target);
234
235 if (arg_debug || arg_debug_whitelists)
236 printf("Debug %d: topdir: %s; link: %s; target: %s\n", __LINE__, topdir, link, target);
237 233
238 // create files as root, except if inside home or run/user/$UID directory 234 // create files as root, except if inside home or run/user/$UID directory
239 int userprivs = 0; 235 int userprivs = 0;
240 if (strcmp(topdir, cfg.homedir) == 0 || strcmp(topdir, runuser) == 0) { 236 if ((strncmp(link, cfg.homedir, homedir_len) == 0 && link[homedir_len] == '/') ||
237 (strncmp(link, runuser, runuser_len) == 0 && link[runuser_len] == '/')) {
241 EUID_USER(); 238 EUID_USER();
242 userprivs = 1; 239 userprivs = 1;
243 } 240 }
@@ -307,13 +304,17 @@ static void globbing(const char *pattern) {
307} 304}
308 305
309// mount tmpfs on all top level directories 306// mount tmpfs on all top level directories
307// home directories *inside* /run/user/$UID are not fully supported
310static void tmpfs_topdirs(const TopDir *topdirs) { 308static void tmpfs_topdirs(const TopDir *topdirs) {
311 int tmpfs_home = 0; 309 int tmpfs_home = 0;
312 int tmpfs_runuser = 0; 310 int tmpfs_runuser = 0;
313 311
314 int i; 312 int i;
315 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { 313 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
316 // do user home and /run/user/$UID last 314 // do nested top level directories last
315 // this way '--whitelist=nested_top_level_dir'
316 // yields the full, unmodified directory
317 // instead of the tmpfs
317 if (strcmp(topdirs[i].path, cfg.homedir) == 0) { 318 if (strcmp(topdirs[i].path, cfg.homedir) == 0) {
318 tmpfs_home = 1; 319 tmpfs_home = 1;
319 continue; 320 continue;
@@ -352,7 +353,7 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
352 // restore /run/user/$UID directory 353 // restore /run/user/$UID directory
353 // get path relative to /run 354 // get path relative to /run
354 const char *rel = runuser + 5; 355 const char *rel = runuser + 5;
355 whitelist_file(topdirs[i].fd, topdirs[i].path, rel, runuser); 356 whitelist_file(topdirs[i].fd, rel, runuser);
356 } 357 }
357 else if (strcmp(topdirs[i].path, "/tmp") == 0) { 358 else if (strcmp(topdirs[i].path, "/tmp") == 0) {
358 // fix pam-tmpdir (#2685) 359 // fix pam-tmpdir (#2685)
@@ -376,11 +377,12 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
376 377
377 // restore user home directory if it is masked by the tmpfs 378 // restore user home directory if it is masked by the tmpfs
378 // creates path owned by root 379 // creates path owned by root
380 // does nothing if user home directory doesn't exist
379 size_t topdir_len = strlen(topdirs[i].path); 381 size_t topdir_len = strlen(topdirs[i].path);
380 if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') { 382 if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') {
381 // get path relative to top level directory 383 // get path relative to top level directory
382 const char *rel = cfg.homedir + topdir_len + 1; 384 const char *rel = cfg.homedir + topdir_len + 1;
383 whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir); 385 whitelist_file(topdirs[i].fd, rel, cfg.homedir);
384 } 386 }
385 387
386 selinux_relabel_path(topdirs[i].path, topdirs[i].path); 388 selinux_relabel_path(topdirs[i].path, topdirs[i].path);
@@ -388,7 +390,8 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
388 390
389 // user home directory 391 // user home directory
390 if (tmpfs_home) 392 if (tmpfs_home)
391 fs_private(); // checks owner if outside /home 393 // checks owner if outside /home
394 fs_private();
392 395
393 // /run/user/$UID directory 396 // /run/user/$UID directory
394 if (tmpfs_runuser) { 397 if (tmpfs_runuser) {
@@ -437,8 +440,7 @@ static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
437 } 440 }
438 // do nothing if directory is disabled by administrator 441 // do nothing if directory is disabled by administrator
439 if (reject_topdir(dir)) { 442 if (reject_topdir(dir)) {
440 fwarning("skipping whitelist %s because\n" 443 fmessage("Whitelist top level directory %s is disabled in Firejail configuration file\n", dir);
441 "whitelist top level directory is disabled in Firejail configuration file\n", path);
442 return NULL; 444 return NULL;
443 } 445 }
444 446
@@ -453,15 +455,14 @@ static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
453 TopDir *rv = topdirs + cnt; 455 TopDir *rv = topdirs + cnt;
454 cnt++; 456 cnt++;
455 457
456 char *dup = strdup(dir); 458 rv->path = strdup(dir);
457 if (!dup) 459 if (!rv->path)
458 errExit("strdup"); 460 errExit("strdup");
459 rv->path = dup;
460 461
461 // open the directory, don't follow symbolic links 462 // open the directory, don't follow symbolic links
462 rv->fd = safer_openat(-1, dup, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC); 463 rv->fd = safer_openat(-1, rv->path, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC);
463 if (rv->fd == -1) { 464 if (rv->fd == -1) {
464 fprintf(stderr, "Error: cannot open %s\n", dup); 465 fprintf(stderr, "Error: cannot open %s\n", rv->path);
465 exit(1); 466 exit(1);
466 } 467 }
467 468
@@ -592,6 +593,10 @@ void fs_whitelist(void) {
592 if (strstr(new_name, "..")) 593 if (strstr(new_name, ".."))
593 whitelist_error(new_name); 594 whitelist_error(new_name);
594 595
596 // /run/firejail is not allowed
597 if (strncmp(new_name, RUN_FIREJAIL_DIR, strlen(RUN_FIREJAIL_DIR)) == 0)
598 whitelist_error(new_name);
599
595 TopDir *current_top = NULL; 600 TopDir *current_top = NULL;
596 if (!nowhitelist_flag) { 601 if (!nowhitelist_flag) {
597 // extract whitelist top level directory 602 // extract whitelist top level directory
@@ -649,6 +654,10 @@ void fs_whitelist(void) {
649 continue; 654 continue;
650 } 655 }
651 656
657 // /run/firejail is not allowed
658 if (strncmp(fname, RUN_FIREJAIL_DIR, strlen(RUN_FIREJAIL_DIR)) == 0)
659 whitelist_error(fname);
660
652 if (nowhitelist_flag) { 661 if (nowhitelist_flag) {
653 // store the path in nowhitelist array 662 // store the path in nowhitelist array
654 if (arg_debug || arg_debug_whitelists) 663 if (arg_debug || arg_debug_whitelists)
@@ -727,12 +736,15 @@ void fs_whitelist(void) {
727 if (strncmp(file, topdir, topdir_len) == 0 && file[topdir_len] == '/') { 736 if (strncmp(file, topdir, topdir_len) == 0 && file[topdir_len] == '/') {
728 // get path relative to top level directory 737 // get path relative to top level directory
729 const char *rel = file + topdir_len + 1; 738 const char *rel = file + topdir_len + 1;
730 whitelist_file(dirfd, topdir, rel, file); 739
740 if (arg_debug || arg_debug_whitelists)
741 printf("Debug %d: file: %s; dirfd: %d; topdir: %s; rel: %s\n", __LINE__, file, dirfd, topdir, rel);
742 whitelist_file(dirfd, rel, file);
731 } 743 }
732 744
733 // create the link if any 745 // create the link if any
734 if (link) 746 if (link)
735 whitelist_symlink(topdir, link, file); 747 whitelist_symlink(link, file);
736 748
737 free(link); 749 free(link);
738 free(file); 750 free(file);