diff options
-rw-r--r-- | src/firejail/firejail.h | 19 | ||||
-rw-r--r-- | src/firejail/fs.c | 215 | ||||
-rw-r--r-- | src/firejail/pulseaudio.c | 4 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 2 | ||||
-rw-r--r-- | src/firejail/x11.c | 4 |
5 files changed, 76 insertions, 168 deletions
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index fd6cb9ff2..912a1864a 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -364,16 +364,23 @@ void preproc_mount_mnt_dir(void); | |||
364 | void preproc_clean_run(void); | 364 | void preproc_clean_run(void); |
365 | 365 | ||
366 | // fs.c | 366 | // fs.c |
367 | typedef enum { | ||
368 | BLACKLIST_FILE, | ||
369 | BLACKLIST_NOLOG, | ||
370 | MOUNT_READONLY, | ||
371 | MOUNT_TMPFS, | ||
372 | MOUNT_NOEXEC, | ||
373 | MOUNT_RDWR, | ||
374 | OPERATION_MAX | ||
375 | } OPERATION; | ||
376 | |||
367 | // blacklist files or directories by mounting empty files on top of them | 377 | // blacklist files or directories by mounting empty files on top of them |
368 | void fs_blacklist(void); | 378 | void fs_blacklist(void); |
369 | // mount a writable tmpfs | 379 | // mount a writable tmpfs |
370 | void fs_tmpfs(const char *dir, unsigned check_owner); | 380 | void fs_tmpfs(const char *dir, unsigned check_owner); |
371 | // remount a directory read-only | 381 | // remount noexec/nodev/nosuid or read-only or read-write |
372 | void fs_rdonly(const char *dir); | 382 | void fs_remount(const char *dir, OPERATION op); |
373 | void fs_rdonly_rec(const char *dir); | 383 | void fs_remount_rec(const char *dir, OPERATION op); |
374 | // remount a directory noexec, nodev and nosuid | ||
375 | void fs_noexec(const char *dir); | ||
376 | void fs_noexec_rec(const char *dir); | ||
377 | // mount /proc and /sys directories | 384 | // mount /proc and /sys directories |
378 | void fs_proc_sys_dev_boot(void); | 385 | void fs_proc_sys_dev_boot(void); |
379 | // build a basic read-only filesystem | 386 | // build a basic read-only filesystem |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index f3ef97aeb..36683003f 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -39,24 +39,17 @@ | |||
39 | //#define TEST_NO_BLACKLIST_MATCHING | 39 | //#define TEST_NO_BLACKLIST_MATCHING |
40 | 40 | ||
41 | 41 | ||
42 | static int mount_warning = 0; | ||
43 | static void fs_rdwr(const char *dir); | ||
44 | static void fs_rdwr_rec(const char *dir); | ||
45 | |||
46 | |||
47 | |||
48 | //*********************************************** | 42 | //*********************************************** |
49 | // process profile file | 43 | // process profile file |
50 | //*********************************************** | 44 | //*********************************************** |
51 | typedef enum { | 45 | static char *opstr[] = { |
52 | BLACKLIST_FILE, | 46 | [BLACKLIST_FILE] = "blacklist", |
53 | BLACKLIST_NOLOG, | 47 | [BLACKLIST_NOLOG] = "blacklist-nolog", |
54 | MOUNT_READONLY, | 48 | [MOUNT_READONLY] = "read-only", |
55 | MOUNT_TMPFS, | 49 | [MOUNT_TMPFS] = "tmpfs", |
56 | MOUNT_NOEXEC, | 50 | [MOUNT_NOEXEC] = "noexec", |
57 | MOUNT_RDWR, | 51 | [MOUNT_RDWR] = "read-write", |
58 | OPERATION_MAX | 52 | }; |
59 | } OPERATION; | ||
60 | 53 | ||
61 | typedef enum { | 54 | typedef enum { |
62 | UNSUCCESSFUL, | 55 | UNSUCCESSFUL, |
@@ -153,17 +146,9 @@ static void disable_file(OPERATION op, const char *filename) { | |||
153 | fs_logger2("blacklist-nolog", fname); | 146 | fs_logger2("blacklist-nolog", fname); |
154 | } | 147 | } |
155 | } | 148 | } |
156 | else if (op == MOUNT_READONLY) { | 149 | else if (op == MOUNT_READONLY | op == MOUNT_RDWR | op == MOUNT_NOEXEC) { |
157 | fs_rdonly_rec(fname); | 150 | fs_remount_rec(fname, op); |
158 | // todo: last_disable = SUCCESSFUL; | 151 | // todo: last_disable = SUCCESSFUL; |
159 | } | ||
160 | else if (op == MOUNT_RDWR) { | ||
161 | fs_rdwr_rec(fname); | ||
162 | // todo: last_disable = SUCCESSFUL; | ||
163 | } | ||
164 | else if (op == MOUNT_NOEXEC) { | ||
165 | fs_noexec_rec(fname); | ||
166 | // todo: last_disable = SUCCESSFUL; | ||
167 | } | 152 | } |
168 | else if (op == MOUNT_TMPFS) { | 153 | else if (op == MOUNT_TMPFS) { |
169 | if (S_ISDIR(s.st_mode)) { | 154 | if (S_ISDIR(s.st_mode)) { |
@@ -493,145 +478,60 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
493 | close(fd); | 478 | close(fd); |
494 | } | 479 | } |
495 | 480 | ||
496 | // remount directory read-only | 481 | void fs_remount(const char *dir, OPERATION op) { |
497 | void fs_rdonly(const char *dir) { | ||
498 | assert(dir); | 482 | assert(dir); |
499 | // check directory exists | 483 | // check directory exists |
500 | struct stat s; | 484 | struct stat s; |
501 | int rv = stat(dir, &s); | 485 | int rv = stat(dir, &s); |
502 | if (rv == 0) { | 486 | if (rv == 0) { |
487 | if (op == MOUNT_RDWR) { | ||
488 | // allow only user owned directories, except the user is root | ||
489 | if (getuid() != 0 && s.st_uid != getuid()) { | ||
490 | fwarning("you are not allowed to change %s to read-write\n", dir); | ||
491 | return; | ||
492 | } | ||
493 | } | ||
503 | unsigned long flags = 0; | 494 | unsigned long flags = 0; |
504 | get_mount_flags(dir, &flags); | 495 | if (get_mount_flags(dir, &flags) != 0) { |
505 | if ((flags & MS_RDONLY) == MS_RDONLY) | 496 | fwarning("not remounting %s\n", dir); |
506 | return; | 497 | return; |
507 | flags |= MS_RDONLY; | ||
508 | if (arg_debug) | ||
509 | printf("Mounting read-only %s\n", dir); | ||
510 | // mount --bind /bin /bin | ||
511 | // mount --bind -o remount,ro /bin | ||
512 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
513 | mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | ||
514 | errExit("mount read-only"); | ||
515 | fs_logger2("read-only", dir); | ||
516 | } | ||
517 | } | ||
518 | |||
519 | // remount directory read-only recursively | ||
520 | void fs_rdonly_rec(const char *dir) { | ||
521 | assert(dir); | ||
522 | // get mount point of the directory | ||
523 | int mountid = get_mount_id(dir); | ||
524 | if (mountid == -1) | ||
525 | return; | ||
526 | if (mountid == -2) { | ||
527 | // falling back to a simple remount on old kernels | ||
528 | if (!mount_warning) { | ||
529 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
530 | mount_warning = 1; | ||
531 | } | 498 | } |
532 | fs_rdonly(dir); | 499 | if (op == MOUNT_RDWR) { |
533 | return; | 500 | if ((flags & MS_RDONLY) == 0) |
534 | } | 501 | return; |
535 | // build array with all mount points that need to get remounted | 502 | flags &= ~MS_RDONLY; |
536 | char **arr = build_mount_array(mountid, dir); | ||
537 | assert(arr); | ||
538 | // remount | ||
539 | char **tmp = arr; | ||
540 | while (*tmp) { | ||
541 | fs_rdonly(*tmp); | ||
542 | free(*tmp++); | ||
543 | } | ||
544 | free(arr); | ||
545 | } | ||
546 | |||
547 | // remount directory read-write | ||
548 | static void fs_rdwr(const char *dir) { | ||
549 | assert(dir); | ||
550 | // check directory exists | ||
551 | struct stat s; | ||
552 | int rv = stat(dir, &s); | ||
553 | if (rv == 0) { | ||
554 | // allow only user owned directories, except the user is root | ||
555 | uid_t u = getuid(); | ||
556 | if (u != 0 && s.st_uid != u) { | ||
557 | fwarning("you are not allowed to change %s to read-write\n", dir); | ||
558 | return; | ||
559 | } | 503 | } |
560 | unsigned long flags = 0; | 504 | else if (op == MOUNT_NOEXEC) { |
561 | get_mount_flags(dir, &flags); | 505 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) |
562 | if ((flags & MS_RDONLY) == 0) | 506 | return; |
563 | return; | 507 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; |
564 | flags &= ~MS_RDONLY; | 508 | } |
509 | else if (op == MOUNT_READONLY) { | ||
510 | if ((flags & MS_RDONLY) == MS_RDONLY) | ||
511 | return; | ||
512 | flags |= MS_RDONLY; | ||
513 | } | ||
514 | else | ||
515 | assert(0); | ||
516 | |||
565 | if (arg_debug) | 517 | if (arg_debug) |
566 | printf("Mounting read-write %s\n", dir); | 518 | printf("Mounting %s %s\n", opstr[op], dir); |
567 | // mount --bind /bin /bin | 519 | // mount --bind /bin /bin |
568 | // mount --bind -o remount,rw /bin | 520 | // mount --bind -o remount,rw /bin |
569 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || | 521 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || |
570 | mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | 522 | mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) |
571 | errExit("mount read-write"); | 523 | errExit("remounting"); |
572 | fs_logger2("read-write", dir); | ||
573 | // run a sanity check on /proc/self/mountinfo | 524 | // run a sanity check on /proc/self/mountinfo |
574 | MountData *mptr = get_last_mount(); | 525 | MountData *mptr = get_last_mount(); |
575 | size_t len = strlen(dir); | 526 | size_t len = strlen(dir); |
576 | if (strncmp(mptr->dir, dir, len) != 0 || | 527 | if (strncmp(mptr->dir, dir, len) != 0 || |
577 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) | 528 | (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) |
578 | errLogExit("invalid read-write mount"); | 529 | errLogExit("invalid %s mount", opstr[op]); |
579 | } | 530 | fs_logger2(opstr[op], dir); |
580 | } | ||
581 | |||
582 | // remount directory read-write recursively | ||
583 | static void fs_rdwr_rec(const char *dir) { | ||
584 | assert(dir); | ||
585 | // get mount point of the directory | ||
586 | int mountid = get_mount_id(dir); | ||
587 | if (mountid == -1) | ||
588 | return; | ||
589 | if (mountid == -2) { | ||
590 | // falling back to a simple remount on old kernels | ||
591 | if (!mount_warning) { | ||
592 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | ||
593 | mount_warning = 1; | ||
594 | } | ||
595 | fs_rdwr(dir); | ||
596 | return; | ||
597 | } | ||
598 | // build array with all mount points that need to get remounted | ||
599 | char **arr = build_mount_array(mountid, dir); | ||
600 | assert(arr); | ||
601 | // remount | ||
602 | char **tmp = arr; | ||
603 | while (*tmp) { | ||
604 | fs_rdwr(*tmp); | ||
605 | free(*tmp++); | ||
606 | } | ||
607 | free(arr); | ||
608 | } | ||
609 | |||
610 | // remount directory noexec, nodev, nosuid | ||
611 | void fs_noexec(const char *dir) { | ||
612 | assert(dir); | ||
613 | // check directory exists | ||
614 | struct stat s; | ||
615 | int rv = stat(dir, &s); | ||
616 | if (rv == 0) { | ||
617 | unsigned long flags = 0; | ||
618 | get_mount_flags(dir, &flags); | ||
619 | if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) | ||
620 | return; | ||
621 | flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; | ||
622 | if (arg_debug) | ||
623 | printf("Mounting noexec %s\n", dir); | ||
624 | // mount --bind /bin /bin | ||
625 | // mount --bind -o remount,noexec /bin | ||
626 | if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || | ||
627 | mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) | ||
628 | errExit("mount noexec"); | ||
629 | fs_logger2("noexec", dir); | ||
630 | } | 531 | } |
631 | } | 532 | } |
632 | 533 | ||
633 | // remount directory noexec, nodev, nosuid recursively | 534 | void fs_remount_rec(const char *dir, OPERATION op) { |
634 | void fs_noexec_rec(const char *dir) { | ||
635 | assert(dir); | 535 | assert(dir); |
636 | // get mount point of the directory | 536 | // get mount point of the directory |
637 | int mountid = get_mount_id(dir); | 537 | int mountid = get_mount_id(dir); |
@@ -639,11 +539,12 @@ void fs_noexec_rec(const char *dir) { | |||
639 | return; | 539 | return; |
640 | if (mountid == -2) { | 540 | if (mountid == -2) { |
641 | // falling back to a simple remount on old kernels | 541 | // falling back to a simple remount on old kernels |
542 | static int mount_warning = 0; | ||
642 | if (!mount_warning) { | 543 | if (!mount_warning) { |
643 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); | 544 | fwarning("read-only, read-write and noexec options are not applied recursively\n"); |
644 | mount_warning = 1; | 545 | mount_warning = 1; |
645 | } | 546 | } |
646 | fs_noexec(dir); | 547 | fs_remount(dir, op); |
647 | return; | 548 | return; |
648 | } | 549 | } |
649 | // build array with all mount points that need to get remounted | 550 | // build array with all mount points that need to get remounted |
@@ -652,7 +553,7 @@ void fs_noexec_rec(const char *dir) { | |||
652 | // remount | 553 | // remount |
653 | char **tmp = arr; | 554 | char **tmp = arr; |
654 | while (*tmp) { | 555 | while (*tmp) { |
655 | fs_noexec(*tmp); | 556 | fs_remount(*tmp, op); |
656 | free(*tmp++); | 557 | free(*tmp++); |
657 | } | 558 | } |
658 | free(arr); | 559 | free(arr); |
@@ -827,22 +728,22 @@ void fs_basic_fs(void) { | |||
827 | if (arg_debug) | 728 | if (arg_debug) |
828 | printf("Basic read-only filesystem:\n"); | 729 | printf("Basic read-only filesystem:\n"); |
829 | if (!arg_writable_etc) { | 730 | if (!arg_writable_etc) { |
830 | fs_rdonly("/etc"); | 731 | fs_remount("/etc", MOUNT_READONLY); |
831 | if (uid) | 732 | if (uid) |
832 | fs_noexec("/etc"); | 733 | fs_remount("/etc", MOUNT_NOEXEC); |
833 | } | 734 | } |
834 | if (!arg_writable_var) { | 735 | if (!arg_writable_var) { |
835 | fs_rdonly("/var"); | 736 | fs_remount("/var", MOUNT_READONLY); |
836 | if (uid) | 737 | if (uid) |
837 | fs_noexec("/var"); | 738 | fs_remount("/var", MOUNT_NOEXEC); |
838 | } | 739 | } |
839 | fs_rdonly("/bin"); | 740 | fs_remount("/bin", MOUNT_READONLY); |
840 | fs_rdonly("/sbin"); | 741 | fs_remount("/sbin", MOUNT_READONLY); |
841 | fs_rdonly("/lib"); | 742 | fs_remount("/lib", MOUNT_READONLY); |
842 | fs_rdonly("/lib64"); | 743 | fs_remount("/lib64", MOUNT_READONLY); |
843 | fs_rdonly("/lib32"); | 744 | fs_remount("/lib32", MOUNT_READONLY); |
844 | fs_rdonly("/libx32"); | 745 | fs_remount("/libx32", MOUNT_READONLY); |
845 | fs_rdonly("/usr"); | 746 | fs_remount("/usr", MOUNT_READONLY); |
846 | 747 | ||
847 | // update /var directory in order to support multiple sandboxes running on the same root directory | 748 | // update /var directory in order to support multiple sandboxes running on the same root directory |
848 | fs_var_lock(); | 749 | fs_var_lock(); |
@@ -851,7 +752,7 @@ void fs_basic_fs(void) { | |||
851 | if (!arg_writable_var_log) | 752 | if (!arg_writable_var_log) |
852 | fs_var_log(); | 753 | fs_var_log(); |
853 | else | 754 | else |
854 | fs_rdwr("/var/log"); | 755 | fs_remount("/var/log", MOUNT_RDWR); |
855 | 756 | ||
856 | fs_var_lib(); | 757 | fs_var_lib(); |
857 | fs_var_cache(); | 758 | fs_var_cache(); |
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index e3f237b8e..a62d123ae 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -88,7 +88,7 @@ void pulseaudio_init(void) { | |||
88 | if (mkdir(RUN_PULSE_DIR, 0700) == -1) | 88 | if (mkdir(RUN_PULSE_DIR, 0700) == -1) |
89 | errExit("mkdir"); | 89 | errExit("mkdir"); |
90 | // mount it nosuid, noexec, nodev | 90 | // mount it nosuid, noexec, nodev |
91 | fs_noexec(RUN_PULSE_DIR); | 91 | fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC); |
92 | 92 | ||
93 | // create the new client.conf file | 93 | // create the new client.conf file |
94 | char *pulsecfg = NULL; | 94 | char *pulsecfg = NULL; |
@@ -155,7 +155,7 @@ void pulseaudio_init(void) { | |||
155 | if (fstatvfs(fd, &vfs) == -1) | 155 | if (fstatvfs(fd, &vfs) == -1) |
156 | errExit("fstatvfs"); | 156 | errExit("fstatvfs"); |
157 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) | 157 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) |
158 | fs_rdonly(RUN_PULSE_DIR); | 158 | fs_remount(RUN_PULSE_DIR, MOUNT_READONLY); |
159 | // mount via the link in /proc/self/fd | 159 | // mount via the link in /proc/self/fd |
160 | char *proc; | 160 | char *proc; |
161 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | 161 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 2c5c5fc12..0c08a76c6 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -1106,7 +1106,7 @@ int sandbox(void* sandbox_arg) { | |||
1106 | (void) rv; | 1106 | (void) rv; |
1107 | } | 1107 | } |
1108 | // make seccomp filters read-only | 1108 | // make seccomp filters read-only |
1109 | fs_rdonly(RUN_SECCOMP_DIR); | 1109 | fs_remount(RUN_SECCOMP_DIR, MOUNT_READONLY); |
1110 | #endif | 1110 | #endif |
1111 | 1111 | ||
1112 | // set capabilities | 1112 | // set capabilities |
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index 9d821d980..7cfc5b683 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -1169,7 +1169,7 @@ void x11_xorg(void) { | |||
1169 | umount("/tmp"); | 1169 | umount("/tmp"); |
1170 | 1170 | ||
1171 | // remount RUN_XAUTHORITY_SEC_FILE noexec, nodev, nosuid | 1171 | // remount RUN_XAUTHORITY_SEC_FILE noexec, nodev, nosuid |
1172 | fs_noexec(RUN_XAUTHORITY_SEC_FILE); | 1172 | fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_NOEXEC); |
1173 | 1173 | ||
1174 | // Ensure there is already a file in the usual location, so that bind-mount below will work. | 1174 | // Ensure there is already a file in the usual location, so that bind-mount below will work. |
1175 | char *dest; | 1175 | char *dest; |
@@ -1202,7 +1202,7 @@ void x11_xorg(void) { | |||
1202 | if (fstatvfs(fd, &vfs) == -1) | 1202 | if (fstatvfs(fd, &vfs) == -1) |
1203 | errExit("fstatvfs"); | 1203 | errExit("fstatvfs"); |
1204 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) | 1204 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) |
1205 | fs_rdonly(RUN_XAUTHORITY_SEC_FILE); | 1205 | fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_READONLY); |
1206 | 1206 | ||
1207 | // mount via the link in /proc/self/fd | 1207 | // mount via the link in /proc/self/fd |
1208 | char *proc; | 1208 | char *proc; |