aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar smitsohu <smitsohu@gmail.com>2019-06-17 03:46:11 +0200
committerLibravatar smitsohu <smitsohu@gmail.com>2019-06-17 03:46:11 +0200
commitdba9dff9c52e436a37c82a72ec82c95bcd9684ce (patch)
treeb0e77cb4190e2adc377964b617f4ef5402b902c1
parenttighten gnome-maps (diff)
downloadfirejail-dba9dff9c52e436a37c82a72ec82c95bcd9684ce.tar.gz
firejail-dba9dff9c52e436a37c82a72ec82c95bcd9684ce.tar.zst
firejail-dba9dff9c52e436a37c82a72ec82c95bcd9684ce.zip
streamline remounting (ro,rw,noexec)
-rw-r--r--src/firejail/firejail.h19
-rw-r--r--src/firejail/fs.c215
-rw-r--r--src/firejail/pulseaudio.c4
-rw-r--r--src/firejail/sandbox.c2
-rw-r--r--src/firejail/x11.c4
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);
364void preproc_clean_run(void); 364void preproc_clean_run(void);
365 365
366// fs.c 366// fs.c
367typedef 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
368void fs_blacklist(void); 378void fs_blacklist(void);
369// mount a writable tmpfs 379// mount a writable tmpfs
370void fs_tmpfs(const char *dir, unsigned check_owner); 380void 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
372void fs_rdonly(const char *dir); 382void fs_remount(const char *dir, OPERATION op);
373void fs_rdonly_rec(const char *dir); 383void fs_remount_rec(const char *dir, OPERATION op);
374// remount a directory noexec, nodev and nosuid
375void fs_noexec(const char *dir);
376void fs_noexec_rec(const char *dir);
377// mount /proc and /sys directories 384// mount /proc and /sys directories
378void fs_proc_sys_dev_boot(void); 385void 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
42static int mount_warning = 0;
43static void fs_rdwr(const char *dir);
44static void fs_rdwr_rec(const char *dir);
45
46
47
48//*********************************************** 42//***********************************************
49// process profile file 43// process profile file
50//*********************************************** 44//***********************************************
51typedef enum { 45static 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
61typedef enum { 54typedef 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 481void fs_remount(const char *dir, OPERATION op) {
497void 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
520void 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
548static 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
583static 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
611void 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 534void fs_remount_rec(const char *dir, OPERATION op) {
634void 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;