diff options
Diffstat (limited to 'src/firejail/fs.c')
-rw-r--r-- | src/firejail/fs.c | 210 |
1 files changed, 114 insertions, 96 deletions
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 09de11de9..01182bd2c 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -54,16 +54,10 @@ static char *opstr[] = { | |||
54 | [MOUNT_RDWR_NOCHECK] = "read-write", | 54 | [MOUNT_RDWR_NOCHECK] = "read-write", |
55 | }; | 55 | }; |
56 | 56 | ||
57 | typedef enum { | ||
58 | UNSUCCESSFUL, | ||
59 | SUCCESSFUL | ||
60 | } LAST_DISABLE_OPERATION; | ||
61 | LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL; | ||
62 | |||
63 | static void disable_file(OPERATION op, const char *filename) { | 57 | static void disable_file(OPERATION op, const char *filename) { |
64 | assert(filename); | 58 | assert(filename); |
65 | assert(op <OPERATION_MAX); | 59 | assert(op <OPERATION_MAX); |
66 | last_disable = UNSUCCESSFUL; | 60 | EUID_ASSERT(); |
67 | 61 | ||
68 | // Resolve all symlinks | 62 | // Resolve all symlinks |
69 | char* fname = realpath(filename, NULL); | 63 | char* fname = realpath(filename, NULL); |
@@ -71,20 +65,24 @@ static void disable_file(OPERATION op, const char *filename) { | |||
71 | return; | 65 | return; |
72 | } | 66 | } |
73 | if (fname == NULL && errno == EACCES) { | 67 | if (fname == NULL && errno == EACCES) { |
74 | if (arg_debug) | ||
75 | printf("Debug: no access to file %s, forcing mount\n", filename); | ||
76 | // realpath and stat functions will fail on FUSE filesystems | 68 | // realpath and stat functions will fail on FUSE filesystems |
77 | // they don't seem to like a uid of 0 | 69 | // they don't seem to like a uid of 0 |
78 | // force mounting | 70 | // force mounting |
79 | int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); | 71 | int fd = open(filename, O_PATH|O_CLOEXEC); |
80 | if (rv == 0) | 72 | if (fd < 0) { |
81 | last_disable = SUCCESSFUL; | 73 | if (arg_debug) |
82 | else { | 74 | printf("Warning (blacklisting): cannot open %s: %s\n", filename, strerror(errno)); |
83 | rv = mount(RUN_RO_FILE, filename, "none", MS_BIND, "mode=400,gid=0"); | 75 | return; |
84 | if (rv == 0) | ||
85 | last_disable = SUCCESSFUL; | ||
86 | } | 76 | } |
87 | if (last_disable == SUCCESSFUL) { | 77 | |
78 | EUID_ROOT(); | ||
79 | int err = bind_mount_path_to_fd(RUN_RO_DIR, fd); | ||
80 | if (err < 0) | ||
81 | err = bind_mount_path_to_fd(RUN_RO_FILE, fd); | ||
82 | EUID_USER(); | ||
83 | close(fd); | ||
84 | |||
85 | if (err == 0) { | ||
88 | if (arg_debug) | 86 | if (arg_debug) |
89 | printf("Disable %s\n", filename); | 87 | printf("Disable %s\n", filename); |
90 | if (op == BLACKLIST_FILE) | 88 | if (op == BLACKLIST_FILE) |
@@ -92,21 +90,18 @@ static void disable_file(OPERATION op, const char *filename) { | |||
92 | else | 90 | else |
93 | fs_logger2("blacklist-nolog", filename); | 91 | fs_logger2("blacklist-nolog", filename); |
94 | } | 92 | } |
95 | else { | 93 | else if (arg_debug) |
96 | if (arg_debug) | 94 | printf("Warning (blacklisting): cannot mount on %s\n", filename); |
97 | printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename); | ||
98 | } | ||
99 | 95 | ||
100 | return; | 96 | return; |
101 | } | 97 | } |
102 | 98 | ||
103 | // if the file is not present, do nothing | 99 | // if the file is not present, do nothing |
100 | assert(fname); | ||
104 | struct stat s; | 101 | struct stat s; |
105 | if (fname == NULL) | 102 | if (stat(fname, &s) < 0) { |
106 | return; | ||
107 | if (stat(fname, &s) == -1) { | ||
108 | if (arg_debug) | 103 | if (arg_debug) |
109 | fwarning("%s does not exist, skipping...\n", fname); | 104 | printf("Warning (blacklisting): cannot access %s: %s\n", fname, strerror(errno)); |
110 | free(fname); | 105 | free(fname); |
111 | return; | 106 | return; |
112 | } | 107 | } |
@@ -115,8 +110,10 @@ static void disable_file(OPERATION op, const char *filename) { | |||
115 | // we migth have a file found in ${PATH} pointing to /usr/bin/firejail | 110 | // we migth have a file found in ${PATH} pointing to /usr/bin/firejail |
116 | // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird | 111 | // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird |
117 | // and expects Firefox to open in the same sandbox | 112 | // and expects Firefox to open in the same sandbox |
118 | if (strcmp(BINDIR "/firejail", fname) == 0) | 113 | if (strcmp(BINDIR "/firejail", fname) == 0) { |
114 | free(fname); | ||
119 | return; | 115 | return; |
116 | } | ||
120 | 117 | ||
121 | // modify the file | 118 | // modify the file |
122 | if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { | 119 | if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { |
@@ -141,15 +138,25 @@ static void disable_file(OPERATION op, const char *filename) { | |||
141 | printf(" - no logging\n"); | 138 | printf(" - no logging\n"); |
142 | } | 139 | } |
143 | 140 | ||
141 | int fd = open(fname, O_PATH|O_CLOEXEC); | ||
142 | if (fd < 0) { | ||
143 | if (arg_debug) | ||
144 | printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno)); | ||
145 | free(fname); | ||
146 | return; | ||
147 | } | ||
148 | EUID_ROOT(); | ||
144 | if (S_ISDIR(s.st_mode)) { | 149 | if (S_ISDIR(s.st_mode)) { |
145 | if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | 150 | if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0) |
146 | errExit("disable file"); | 151 | errExit("disable file"); |
147 | } | 152 | } |
148 | else { | 153 | else { |
149 | if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) | 154 | if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0) |
150 | errExit("disable file"); | 155 | errExit("disable file"); |
151 | } | 156 | } |
152 | last_disable = SUCCESSFUL; | 157 | EUID_USER(); |
158 | close(fd); | ||
159 | |||
153 | if (op == BLACKLIST_FILE) | 160 | if (op == BLACKLIST_FILE) |
154 | fs_logger2("blacklist", fname); | 161 | fs_logger2("blacklist", fname); |
155 | else | 162 | else |
@@ -158,7 +165,6 @@ static void disable_file(OPERATION op, const char *filename) { | |||
158 | } | 165 | } |
159 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { | 166 | else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { |
160 | fs_remount_rec(fname, op); | 167 | fs_remount_rec(fname, op); |
161 | // todo: last_disable = SUCCESSFUL; | ||
162 | } | 168 | } |
163 | else if (op == MOUNT_TMPFS) { | 169 | else if (op == MOUNT_TMPFS) { |
164 | if (S_ISDIR(s.st_mode)) { | 170 | if (S_ISDIR(s.st_mode)) { |
@@ -169,9 +175,10 @@ static void disable_file(OPERATION op, const char *filename) { | |||
169 | exit(1); | 175 | exit(1); |
170 | } | 176 | } |
171 | } | 177 | } |
178 | // fs_tmpfs returns with EUID 0 | ||
172 | fs_tmpfs(fname, getuid()); | 179 | fs_tmpfs(fname, getuid()); |
173 | selinux_relabel_path(fname, fname); | 180 | selinux_relabel_path(fname, fname); |
174 | last_disable = SUCCESSFUL; | 181 | EUID_USER(); |
175 | } | 182 | } |
176 | else | 183 | else |
177 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); | 184 | fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); |
@@ -191,6 +198,7 @@ static int *nbcheck = NULL; | |||
191 | // Treat pattern as a shell glob pattern and blacklist matching files | 198 | // Treat pattern as a shell glob pattern and blacklist matching files |
192 | static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { | 199 | static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { |
193 | assert(pattern); | 200 | assert(pattern); |
201 | EUID_ASSERT(); | ||
194 | 202 | ||
195 | #ifdef TEST_NO_BLACKLIST_MATCHING | 203 | #ifdef TEST_NO_BLACKLIST_MATCHING |
196 | if (nbcheck_start == 0) { | 204 | if (nbcheck_start == 0) { |
@@ -264,6 +272,7 @@ void fs_blacklist(void) { | |||
264 | if (noblacklist == NULL) | 272 | if (noblacklist == NULL) |
265 | errExit("failed allocating memory for noblacklist entries"); | 273 | errExit("failed allocating memory for noblacklist entries"); |
266 | 274 | ||
275 | EUID_USER(); | ||
267 | while (entry) { | 276 | while (entry) { |
268 | OPERATION op = OPERATION_MAX; | 277 | OPERATION op = OPERATION_MAX; |
269 | char *ptr; | 278 | char *ptr; |
@@ -294,11 +303,13 @@ void fs_blacklist(void) { | |||
294 | if (arg_debug) | 303 | if (arg_debug) |
295 | printf("Mount-bind %s on top of %s\n", dname1, dname2); | 304 | printf("Mount-bind %s on top of %s\n", dname1, dname2); |
296 | // preserve dname2 mode and ownership | 305 | // preserve dname2 mode and ownership |
306 | // EUID_ROOT(); - option not accessible to non-root users | ||
297 | if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) | 307 | if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) |
298 | errExit("mount bind"); | 308 | errExit("mount bind"); |
299 | /* coverity[toctou] */ | 309 | /* coverity[toctou] */ |
300 | if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) | 310 | if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) |
301 | errExit("set_perms"); | 311 | errExit("set_perms"); |
312 | // EUID_USER(); | ||
302 | 313 | ||
303 | entry = entry->next; | 314 | entry = entry->next; |
304 | continue; | 315 | continue; |
@@ -376,16 +387,12 @@ void fs_blacklist(void) { | |||
376 | op = MOUNT_TMPFS; | 387 | op = MOUNT_TMPFS; |
377 | } | 388 | } |
378 | else if (strncmp(entry->data, "mkdir ", 6) == 0) { | 389 | else if (strncmp(entry->data, "mkdir ", 6) == 0) { |
379 | EUID_USER(); | ||
380 | fs_mkdir(entry->data + 6); | 390 | fs_mkdir(entry->data + 6); |
381 | EUID_ROOT(); | ||
382 | entry = entry->next; | 391 | entry = entry->next; |
383 | continue; | 392 | continue; |
384 | } | 393 | } |
385 | else if (strncmp(entry->data, "mkfile ", 7) == 0) { | 394 | else if (strncmp(entry->data, "mkfile ", 7) == 0) { |
386 | EUID_USER(); | ||
387 | fs_mkfile(entry->data + 7); | 395 | fs_mkfile(entry->data + 7); |
388 | EUID_ROOT(); | ||
389 | entry = entry->next; | 396 | entry = entry->next; |
390 | continue; | 397 | continue; |
391 | } | 398 | } |
@@ -441,6 +448,8 @@ void fs_blacklist(void) { | |||
441 | for (i = 0; i < noblacklist_c; i++) | 448 | for (i = 0; i < noblacklist_c; i++) |
442 | free(noblacklist[i]); | 449 | free(noblacklist[i]); |
443 | free(noblacklist); | 450 | free(noblacklist); |
451 | |||
452 | EUID_ROOT(); | ||
444 | } | 453 | } |
445 | 454 | ||
446 | //*********************************************** | 455 | //*********************************************** |
@@ -449,6 +458,7 @@ void fs_blacklist(void) { | |||
449 | 458 | ||
450 | // mount a writable tmpfs on directory; requires a resolved path | 459 | // mount a writable tmpfs on directory; requires a resolved path |
451 | void fs_tmpfs(const char *dir, unsigned check_owner) { | 460 | void fs_tmpfs(const char *dir, unsigned check_owner) { |
461 | EUID_USER(); | ||
452 | assert(dir); | 462 | assert(dir); |
453 | if (arg_debug) | 463 | if (arg_debug) |
454 | printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); | 464 | printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); |
@@ -473,6 +483,7 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
473 | errExit("fstatvfs"); | 483 | errExit("fstatvfs"); |
474 | unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND); | 484 | unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND); |
475 | // mount via the symbolic link in /proc/self/fd | 485 | // mount via the symbolic link in /proc/self/fd |
486 | EUID_ROOT(); | ||
476 | char *proc; | 487 | char *proc; |
477 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 488 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
478 | errExit("asprintf"); | 489 | errExit("asprintf"); |
@@ -490,38 +501,42 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
490 | 501 | ||
491 | // remount path, preserving other mount flags; requires a resolved path | 502 | // remount path, preserving other mount flags; requires a resolved path |
492 | static void fs_remount_simple(const char *path, OPERATION op) { | 503 | static void fs_remount_simple(const char *path, OPERATION op) { |
504 | EUID_ASSERT(); | ||
493 | assert(path); | 505 | assert(path); |
494 | 506 | ||
495 | // open path without following symbolic links | 507 | // open path without following symbolic links |
496 | int fd1 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 508 | int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
497 | if (fd1 == -1) | 509 | if (fd < 0) |
498 | goto out; | 510 | goto out; |
499 | struct stat s1; | 511 | |
500 | if (fstat(fd1, &s1) == -1) { | 512 | struct stat s; |
513 | if (fstat(fd, &s) < 0) { | ||
501 | // fstat can fail with EACCES if path is a FUSE mount, | 514 | // fstat can fail with EACCES if path is a FUSE mount, |
502 | // mounted without 'allow_root' or 'allow_other' | 515 | // mounted without 'allow_root' or 'allow_other' |
503 | if (errno != EACCES) | 516 | if (errno != EACCES) |
504 | errExit("fstat"); | 517 | errExit("fstat"); |
505 | close(fd1); | 518 | close(fd); |
506 | goto out; | 519 | goto out; |
507 | } | 520 | } |
508 | // get mount flags | 521 | // get mount flags |
509 | struct statvfs buf; | 522 | struct statvfs buf; |
510 | if (fstatvfs(fd1, &buf) == -1) | 523 | if (fstatvfs(fd, &buf) < 0) { |
511 | errExit("fstatvfs"); | 524 | close(fd); |
525 | goto out; | ||
526 | } | ||
512 | unsigned long flags = buf.f_flag; | 527 | unsigned long flags = buf.f_flag; |
513 | 528 | ||
514 | // read-write option | 529 | // read-write option |
515 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { | 530 | if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { |
516 | // nothing to do if there is no read-only flag | 531 | // nothing to do if there is no read-only flag |
517 | if ((flags & MS_RDONLY) == 0) { | 532 | if ((flags & MS_RDONLY) == 0) { |
518 | close(fd1); | 533 | close(fd); |
519 | return; | 534 | return; |
520 | } | 535 | } |
521 | // allow only user owned directories, except the user is root | 536 | // allow only user owned directories, except the user is root |
522 | if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { | 537 | if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s.st_uid != getuid()) { |
523 | fwarning("you are not allowed to change %s to read-write\n", path); | 538 | fwarning("you are not allowed to change %s to read-write\n", path); |
524 | close(fd1); | 539 | close(fd); |
525 | return; | 540 | return; |
526 | } | 541 | } |
527 | flags &= ~MS_RDONLY; | 542 | flags &= ~MS_RDONLY; |
@@ -530,7 +545,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
530 | else if (op == MOUNT_NOEXEC) { | 545 | else if (op == MOUNT_NOEXEC) { |
531 | // nothing to do if path is mounted noexec already | 546 | // nothing to do if path is mounted noexec already |
532 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { | 547 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { |
533 | close(fd1); | 548 | close(fd); |
534 | return; | 549 | return; |
535 | } | 550 | } |
536 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; | 551 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; |
@@ -539,7 +554,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
539 | else if (op == MOUNT_READONLY) { | 554 | else if (op == MOUNT_READONLY) { |
540 | // nothing to do if path is mounted read-only already | 555 | // nothing to do if path is mounted read-only already |
541 | if ((flags & MS_RDONLY) == MS_RDONLY) { | 556 | if ((flags & MS_RDONLY) == MS_RDONLY) { |
542 | close(fd1); | 557 | close(fd); |
543 | return; | 558 | return; |
544 | } | 559 | } |
545 | flags |= MS_RDONLY; | 560 | flags |= MS_RDONLY; |
@@ -549,29 +564,37 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
549 | 564 | ||
550 | if (arg_debug) | 565 | if (arg_debug) |
551 | printf("Mounting %s %s\n", opstr[op], path); | 566 | printf("Mounting %s %s\n", opstr[op], path); |
567 | |||
568 | // make path a mount point: | ||
552 | // mount --bind path path | 569 | // mount --bind path path |
553 | char *proc; | 570 | EUID_ROOT(); |
554 | if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) | 571 | int err = bind_mount_by_fd(fd, fd); |
555 | errExit("asprintf"); | 572 | EUID_USER(); |
556 | if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | 573 | if (err) { |
557 | errExit("mount"); | 574 | close(fd); |
558 | free(proc); | 575 | goto out; |
576 | } | ||
559 | 577 | ||
560 | // mount --bind -o remount,ro path | 578 | // remount the mount point |
561 | // need to open path again without following symbolic links | 579 | // need to open path again |
562 | int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 580 | int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
563 | if (fd2 == -1) | 581 | close(fd); // earliest timepoint to close fd |
564 | errExit("open"); | 582 | if (fd2 < 0) |
583 | goto out; | ||
584 | |||
585 | // device and inode number should be the same | ||
565 | struct stat s2; | 586 | struct stat s2; |
566 | if (fstat(fd2, &s2) == -1) | 587 | if (fstat(fd2, &s2) < 0) |
567 | errExit("fstat"); | 588 | errExit("fstat"); |
568 | // device and inode number should be the same | 589 | if (s.st_dev != s2.st_dev || s.st_ino != s2.st_ino) |
569 | if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) | ||
570 | errLogExit("invalid %s mount", opstr[op]); | 590 | errLogExit("invalid %s mount", opstr[op]); |
571 | if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) | 591 | |
572 | errExit("asprintf"); | 592 | EUID_ROOT(); |
573 | if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | 593 | err = remount_by_fd(fd2, flags); |
574 | errExit("mount"); | 594 | EUID_USER(); |
595 | close(fd2); | ||
596 | if (err) | ||
597 | goto out; | ||
575 | 598 | ||
576 | // run a sanity check on /proc/self/mountinfo and confirm that target of the last | 599 | // run a sanity check on /proc/self/mountinfo and confirm that target of the last |
577 | // mount operation was path; if there are other mount points contained inside path, | 600 | // mount operation was path; if there are other mount points contained inside path, |
@@ -582,10 +605,8 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
582 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | 605 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) |
583 | && strcmp(path, "/") != 0) // support read-only=/ | 606 | && strcmp(path, "/") != 0) // support read-only=/ |
584 | errLogExit("invalid %s mount", opstr[op]); | 607 | errLogExit("invalid %s mount", opstr[op]); |
608 | |||
585 | fs_logger2(opstr[op], path); | 609 | fs_logger2(opstr[op], path); |
586 | free(proc); | ||
587 | close(fd1); | ||
588 | close(fd2); | ||
589 | return; | 610 | return; |
590 | 611 | ||
591 | out: | 612 | out: |
@@ -594,7 +615,9 @@ out: | |||
594 | 615 | ||
595 | // remount recursively; requires a resolved path | 616 | // remount recursively; requires a resolved path |
596 | static void fs_remount_rec(const char *dir, OPERATION op) { | 617 | static void fs_remount_rec(const char *dir, OPERATION op) { |
618 | EUID_ASSERT(); | ||
597 | assert(dir); | 619 | assert(dir); |
620 | |||
598 | struct stat s; | 621 | struct stat s; |
599 | if (stat(dir, &s) != 0) | 622 | if (stat(dir, &s) != 0) |
600 | return; | 623 | return; |
@@ -632,6 +655,9 @@ static void fs_remount_rec(const char *dir, OPERATION op) { | |||
632 | // resolve a path and remount it | 655 | // resolve a path and remount it |
633 | void fs_remount(const char *path, OPERATION op, int rec) { | 656 | void fs_remount(const char *path, OPERATION op, int rec) { |
634 | assert(path); | 657 | assert(path); |
658 | assert(geteuid() == 0); | ||
659 | EUID_USER(); | ||
660 | |||
635 | char *rpath = realpath(path, NULL); | 661 | char *rpath = realpath(path, NULL); |
636 | if (rpath) { | 662 | if (rpath) { |
637 | if (rec) | 663 | if (rec) |
@@ -640,10 +666,12 @@ void fs_remount(const char *path, OPERATION op, int rec) { | |||
640 | fs_remount_simple(rpath, op); | 666 | fs_remount_simple(rpath, op); |
641 | free(rpath); | 667 | free(rpath); |
642 | } | 668 | } |
669 | EUID_ROOT(); | ||
643 | } | 670 | } |
644 | 671 | ||
645 | // Disable /mnt, /media, /run/mount and /run/media access | 672 | // Disable /mnt, /media, /run/mount and /run/media access |
646 | void fs_mnt(const int enforce) { | 673 | void fs_mnt(const int enforce) { |
674 | EUID_USER(); | ||
647 | if (enforce) { | 675 | if (enforce) { |
648 | // disable-mnt set in firejail.config | 676 | // disable-mnt set in firejail.config |
649 | // overriding with noblacklist is not possible in this case | 677 | // overriding with noblacklist is not possible in this case |
@@ -653,13 +681,12 @@ void fs_mnt(const int enforce) { | |||
653 | disable_file(BLACKLIST_FILE, "/run/media"); | 681 | disable_file(BLACKLIST_FILE, "/run/media"); |
654 | } | 682 | } |
655 | else { | 683 | else { |
656 | EUID_USER(); | ||
657 | profile_add("blacklist /mnt"); | 684 | profile_add("blacklist /mnt"); |
658 | profile_add("blacklist /media"); | 685 | profile_add("blacklist /media"); |
659 | profile_add("blacklist /run/mount"); | 686 | profile_add("blacklist /run/mount"); |
660 | profile_add("blacklist /run/media"); | 687 | profile_add("blacklist /run/media"); |
661 | EUID_ROOT(); | ||
662 | } | 688 | } |
689 | EUID_ROOT(); | ||
663 | } | 690 | } |
664 | 691 | ||
665 | 692 | ||
@@ -674,7 +701,6 @@ void fs_proc_sys_dev_boot(void) { | |||
674 | errExit("mounting /proc/sys"); | 701 | errExit("mounting /proc/sys"); |
675 | fs_logger("read-only /proc/sys"); | 702 | fs_logger("read-only /proc/sys"); |
676 | 703 | ||
677 | |||
678 | /* Mount a version of /sys that describes the network namespace */ | 704 | /* Mount a version of /sys that describes the network namespace */ |
679 | if (arg_debug) | 705 | if (arg_debug) |
680 | printf("Remounting /sys directory\n"); | 706 | printf("Remounting /sys directory\n"); |
@@ -689,13 +715,13 @@ void fs_proc_sys_dev_boot(void) { | |||
689 | else | 715 | else |
690 | fs_logger("remount /sys"); | 716 | fs_logger("remount /sys"); |
691 | 717 | ||
718 | EUID_USER(); | ||
719 | |||
692 | disable_file(BLACKLIST_FILE, "/sys/firmware"); | 720 | disable_file(BLACKLIST_FILE, "/sys/firmware"); |
693 | disable_file(BLACKLIST_FILE, "/sys/hypervisor"); | 721 | disable_file(BLACKLIST_FILE, "/sys/hypervisor"); |
694 | { // allow user access to some directories in /sys/ by specifying 'noblacklist' option | 722 | { // allow user access to some directories in /sys/ by specifying 'noblacklist' option |
695 | EUID_USER(); | ||
696 | profile_add("blacklist /sys/fs"); | 723 | profile_add("blacklist /sys/fs"); |
697 | profile_add("blacklist /sys/module"); | 724 | profile_add("blacklist /sys/module"); |
698 | EUID_ROOT(); | ||
699 | } | 725 | } |
700 | disable_file(BLACKLIST_FILE, "/sys/power"); | 726 | disable_file(BLACKLIST_FILE, "/sys/power"); |
701 | disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); | 727 | disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); |
@@ -739,12 +765,8 @@ void fs_proc_sys_dev_boot(void) { | |||
739 | // disable /dev/port | 765 | // disable /dev/port |
740 | disable_file(BLACKLIST_FILE, "/dev/port"); | 766 | disable_file(BLACKLIST_FILE, "/dev/port"); |
741 | 767 | ||
742 | |||
743 | |||
744 | // disable various ipc sockets in /run/user | 768 | // disable various ipc sockets in /run/user |
745 | if (!arg_writable_run_user) { | 769 | if (!arg_writable_run_user) { |
746 | struct stat s; | ||
747 | |||
748 | char *fname; | 770 | char *fname; |
749 | if (asprintf(&fname, "/run/user/%d", getuid()) == -1) | 771 | if (asprintf(&fname, "/run/user/%d", getuid()) == -1) |
750 | errExit("asprintf"); | 772 | errExit("asprintf"); |
@@ -755,8 +777,7 @@ void fs_proc_sys_dev_boot(void) { | |||
755 | errExit("asprintf"); | 777 | errExit("asprintf"); |
756 | if (create_empty_dir_as_user(fnamegpg, 0700)) | 778 | if (create_empty_dir_as_user(fnamegpg, 0700)) |
757 | fs_logger2("create", fnamegpg); | 779 | fs_logger2("create", fnamegpg); |
758 | if (stat(fnamegpg, &s) == 0) | 780 | disable_file(BLACKLIST_FILE, fnamegpg); |
759 | disable_file(BLACKLIST_FILE, fnamegpg); | ||
760 | free(fnamegpg); | 781 | free(fnamegpg); |
761 | 782 | ||
762 | // disable /run/user/{uid}/systemd | 783 | // disable /run/user/{uid}/systemd |
@@ -765,8 +786,7 @@ void fs_proc_sys_dev_boot(void) { | |||
765 | errExit("asprintf"); | 786 | errExit("asprintf"); |
766 | if (create_empty_dir_as_user(fnamesysd, 0755)) | 787 | if (create_empty_dir_as_user(fnamesysd, 0755)) |
767 | fs_logger2("create", fnamesysd); | 788 | fs_logger2("create", fnamesysd); |
768 | if (stat(fnamesysd, &s) == 0) | 789 | disable_file(BLACKLIST_FILE, fnamesysd); |
769 | disable_file(BLACKLIST_FILE, fnamesysd); | ||
770 | free(fnamesysd); | 790 | free(fnamesysd); |
771 | } | 791 | } |
772 | free(fname); | 792 | free(fname); |
@@ -777,30 +797,26 @@ void fs_proc_sys_dev_boot(void) { | |||
777 | disable_file(BLACKLIST_FILE, "/dev/kmsg"); | 797 | disable_file(BLACKLIST_FILE, "/dev/kmsg"); |
778 | disable_file(BLACKLIST_FILE, "/proc/kmsg"); | 798 | disable_file(BLACKLIST_FILE, "/proc/kmsg"); |
779 | } | 799 | } |
800 | |||
801 | EUID_ROOT(); | ||
780 | } | 802 | } |
781 | 803 | ||
782 | // disable firejail configuration in ~/.config/firejail | 804 | // disable firejail configuration in ~/.config/firejail |
783 | void disable_config(void) { | 805 | void disable_config(void) { |
784 | struct stat s; | 806 | EUID_USER(); |
785 | |||
786 | char *fname; | 807 | char *fname; |
787 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) | 808 | if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) |
788 | errExit("asprintf"); | 809 | errExit("asprintf"); |
789 | if (stat(fname, &s) == 0) | 810 | disable_file(BLACKLIST_FILE, fname); |
790 | disable_file(BLACKLIST_FILE, fname); | ||
791 | free(fname); | 811 | free(fname); |
792 | 812 | ||
793 | // disable run time information | 813 | // disable run time information |
794 | if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) | 814 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); |
795 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); | 815 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); |
796 | if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) | 816 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); |
797 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); | 817 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR); |
798 | if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) | 818 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); |
799 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); | 819 | EUID_ROOT(); |
800 | if (stat(RUN_FIREJAIL_PROFILE_DIR, &s) == 0) | ||
801 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR); | ||
802 | if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0) | ||
803 | disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR); | ||
804 | } | 820 | } |
805 | 821 | ||
806 | 822 | ||
@@ -862,6 +878,7 @@ void fs_basic_fs(void) { | |||
862 | #ifdef HAVE_OVERLAYFS | 878 | #ifdef HAVE_OVERLAYFS |
863 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | 879 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { |
864 | assert(subdirname); | 880 | assert(subdirname); |
881 | EUID_ASSERT(); | ||
865 | struct stat s; | 882 | struct stat s; |
866 | char *dirname; | 883 | char *dirname; |
867 | 884 | ||
@@ -1221,6 +1238,7 @@ void fs_overlayfs(void) { | |||
1221 | 1238 | ||
1222 | // this function is called from sandbox.c before blacklist/whitelist functions | 1239 | // this function is called from sandbox.c before blacklist/whitelist functions |
1223 | void fs_private_tmp(void) { | 1240 | void fs_private_tmp(void) { |
1241 | EUID_ASSERT(); | ||
1224 | if (arg_debug) | 1242 | if (arg_debug) |
1225 | printf("Generate private-tmp whitelist commands\n"); | 1243 | printf("Generate private-tmp whitelist commands\n"); |
1226 | 1244 | ||
@@ -1241,8 +1259,8 @@ void fs_private_tmp(void) { | |||
1241 | 1259 | ||
1242 | // whitelist x11 directory | 1260 | // whitelist x11 directory |
1243 | profile_add("whitelist /tmp/.X11-unix"); | 1261 | profile_add("whitelist /tmp/.X11-unix"); |
1244 | // read-only x11 directory | 1262 | // read-only x11 directory |
1245 | profile_add("read-only /tmp/.X11-unix"); | 1263 | profile_add("read-only /tmp/.X11-unix"); |
1246 | 1264 | ||
1247 | // whitelist any pulse* file in /tmp directory | 1265 | // whitelist any pulse* file in /tmp directory |
1248 | // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user | 1266 | // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user |