From dba9dff9c52e436a37c82a72ec82c95bcd9684ce Mon Sep 17 00:00:00 2001 From: smitsohu Date: Mon, 17 Jun 2019 03:46:11 +0200 Subject: streamline remounting (ro,rw,noexec) --- src/firejail/firejail.h | 19 ++-- src/firejail/fs.c | 215 +++++++++++++--------------------------------- src/firejail/pulseaudio.c | 4 +- src/firejail/sandbox.c | 2 +- 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); void preproc_clean_run(void); // fs.c +typedef enum { + BLACKLIST_FILE, + BLACKLIST_NOLOG, + MOUNT_READONLY, + MOUNT_TMPFS, + MOUNT_NOEXEC, + MOUNT_RDWR, + OPERATION_MAX +} OPERATION; + // blacklist files or directories by mounting empty files on top of them void fs_blacklist(void); // mount a writable tmpfs void fs_tmpfs(const char *dir, unsigned check_owner); -// remount a directory read-only -void fs_rdonly(const char *dir); -void fs_rdonly_rec(const char *dir); -// remount a directory noexec, nodev and nosuid -void fs_noexec(const char *dir); -void fs_noexec_rec(const char *dir); +// remount noexec/nodev/nosuid or read-only or read-write +void fs_remount(const char *dir, OPERATION op); +void fs_remount_rec(const char *dir, OPERATION op); // mount /proc and /sys directories void fs_proc_sys_dev_boot(void); // 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 @@ //#define TEST_NO_BLACKLIST_MATCHING -static int mount_warning = 0; -static void fs_rdwr(const char *dir); -static void fs_rdwr_rec(const char *dir); - - - //*********************************************** // process profile file //*********************************************** -typedef enum { - BLACKLIST_FILE, - BLACKLIST_NOLOG, - MOUNT_READONLY, - MOUNT_TMPFS, - MOUNT_NOEXEC, - MOUNT_RDWR, - OPERATION_MAX -} OPERATION; +static char *opstr[] = { + [BLACKLIST_FILE] = "blacklist", + [BLACKLIST_NOLOG] = "blacklist-nolog", + [MOUNT_READONLY] = "read-only", + [MOUNT_TMPFS] = "tmpfs", + [MOUNT_NOEXEC] = "noexec", + [MOUNT_RDWR] = "read-write", +}; typedef enum { UNSUCCESSFUL, @@ -153,17 +146,9 @@ static void disable_file(OPERATION op, const char *filename) { fs_logger2("blacklist-nolog", fname); } } - else if (op == MOUNT_READONLY) { - fs_rdonly_rec(fname); -// todo: last_disable = SUCCESSFUL; - } - else if (op == MOUNT_RDWR) { - fs_rdwr_rec(fname); -// todo: last_disable = SUCCESSFUL; - } - else if (op == MOUNT_NOEXEC) { - fs_noexec_rec(fname); -// todo: last_disable = SUCCESSFUL; + else if (op == MOUNT_READONLY | op == MOUNT_RDWR | op == MOUNT_NOEXEC) { + fs_remount_rec(fname, op); + // todo: last_disable = SUCCESSFUL; } else if (op == MOUNT_TMPFS) { if (S_ISDIR(s.st_mode)) { @@ -493,145 +478,60 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { close(fd); } -// remount directory read-only -void fs_rdonly(const char *dir) { +void fs_remount(const char *dir, OPERATION op) { assert(dir); // check directory exists struct stat s; int rv = stat(dir, &s); if (rv == 0) { + if (op == MOUNT_RDWR) { + // allow only user owned directories, except the user is root + if (getuid() != 0 && s.st_uid != getuid()) { + fwarning("you are not allowed to change %s to read-write\n", dir); + return; + } + } unsigned long flags = 0; - get_mount_flags(dir, &flags); - if ((flags & MS_RDONLY) == MS_RDONLY) + if (get_mount_flags(dir, &flags) != 0) { + fwarning("not remounting %s\n", dir); return; - flags |= MS_RDONLY; - if (arg_debug) - printf("Mounting read-only %s\n", dir); - // mount --bind /bin /bin - // mount --bind -o remount,ro /bin - if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || - mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) - errExit("mount read-only"); - fs_logger2("read-only", dir); - } -} - -// remount directory read-only recursively -void fs_rdonly_rec(const char *dir) { - assert(dir); - // get mount point of the directory - int mountid = get_mount_id(dir); - if (mountid == -1) - return; - if (mountid == -2) { - // falling back to a simple remount on old kernels - if (!mount_warning) { - fwarning("read-only, read-write and noexec options are not applied recursively\n"); - mount_warning = 1; } - fs_rdonly(dir); - return; - } - // build array with all mount points that need to get remounted - char **arr = build_mount_array(mountid, dir); - assert(arr); - // remount - char **tmp = arr; - while (*tmp) { - fs_rdonly(*tmp); - free(*tmp++); - } - free(arr); -} - -// remount directory read-write -static void fs_rdwr(const char *dir) { - assert(dir); - // check directory exists - struct stat s; - int rv = stat(dir, &s); - if (rv == 0) { - // allow only user owned directories, except the user is root - uid_t u = getuid(); - if (u != 0 && s.st_uid != u) { - fwarning("you are not allowed to change %s to read-write\n", dir); - return; + if (op == MOUNT_RDWR) { + if ((flags & MS_RDONLY) == 0) + return; + flags &= ~MS_RDONLY; } - unsigned long flags = 0; - get_mount_flags(dir, &flags); - if ((flags & MS_RDONLY) == 0) - return; - flags &= ~MS_RDONLY; + else if (op == MOUNT_NOEXEC) { + if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) + return; + flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; + } + else if (op == MOUNT_READONLY) { + if ((flags & MS_RDONLY) == MS_RDONLY) + return; + flags |= MS_RDONLY; + } + else + assert(0); + if (arg_debug) - printf("Mounting read-write %s\n", dir); + printf("Mounting %s %s\n", opstr[op], dir); // mount --bind /bin /bin // mount --bind -o remount,rw /bin if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) - errExit("mount read-write"); - fs_logger2("read-write", dir); + errExit("remounting"); // run a sanity check on /proc/self/mountinfo MountData *mptr = get_last_mount(); size_t len = strlen(dir); if (strncmp(mptr->dir, dir, len) != 0 || (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) - errLogExit("invalid read-write mount"); - } -} - -// remount directory read-write recursively -static void fs_rdwr_rec(const char *dir) { - assert(dir); - // get mount point of the directory - int mountid = get_mount_id(dir); - if (mountid == -1) - return; - if (mountid == -2) { - // falling back to a simple remount on old kernels - if (!mount_warning) { - fwarning("read-only, read-write and noexec options are not applied recursively\n"); - mount_warning = 1; - } - fs_rdwr(dir); - return; - } - // build array with all mount points that need to get remounted - char **arr = build_mount_array(mountid, dir); - assert(arr); - // remount - char **tmp = arr; - while (*tmp) { - fs_rdwr(*tmp); - free(*tmp++); - } - free(arr); -} - -// remount directory noexec, nodev, nosuid -void fs_noexec(const char *dir) { - assert(dir); - // check directory exists - struct stat s; - int rv = stat(dir, &s); - if (rv == 0) { - unsigned long flags = 0; - get_mount_flags(dir, &flags); - if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) - return; - flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; - if (arg_debug) - printf("Mounting noexec %s\n", dir); - // mount --bind /bin /bin - // mount --bind -o remount,noexec /bin - if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || - mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) - errExit("mount noexec"); - fs_logger2("noexec", dir); + errLogExit("invalid %s mount", opstr[op]); + fs_logger2(opstr[op], dir); } } -// remount directory noexec, nodev, nosuid recursively -void fs_noexec_rec(const char *dir) { +void fs_remount_rec(const char *dir, OPERATION op) { assert(dir); // get mount point of the directory int mountid = get_mount_id(dir); @@ -639,11 +539,12 @@ void fs_noexec_rec(const char *dir) { return; if (mountid == -2) { // falling back to a simple remount on old kernels + static int mount_warning = 0; if (!mount_warning) { fwarning("read-only, read-write and noexec options are not applied recursively\n"); mount_warning = 1; } - fs_noexec(dir); + fs_remount(dir, op); return; } // build array with all mount points that need to get remounted @@ -652,7 +553,7 @@ void fs_noexec_rec(const char *dir) { // remount char **tmp = arr; while (*tmp) { - fs_noexec(*tmp); + fs_remount(*tmp, op); free(*tmp++); } free(arr); @@ -827,22 +728,22 @@ void fs_basic_fs(void) { if (arg_debug) printf("Basic read-only filesystem:\n"); if (!arg_writable_etc) { - fs_rdonly("/etc"); + fs_remount("/etc", MOUNT_READONLY); if (uid) - fs_noexec("/etc"); + fs_remount("/etc", MOUNT_NOEXEC); } if (!arg_writable_var) { - fs_rdonly("/var"); + fs_remount("/var", MOUNT_READONLY); if (uid) - fs_noexec("/var"); + fs_remount("/var", MOUNT_NOEXEC); } - fs_rdonly("/bin"); - fs_rdonly("/sbin"); - fs_rdonly("/lib"); - fs_rdonly("/lib64"); - fs_rdonly("/lib32"); - fs_rdonly("/libx32"); - fs_rdonly("/usr"); + fs_remount("/bin", MOUNT_READONLY); + fs_remount("/sbin", MOUNT_READONLY); + fs_remount("/lib", MOUNT_READONLY); + fs_remount("/lib64", MOUNT_READONLY); + fs_remount("/lib32", MOUNT_READONLY); + fs_remount("/libx32", MOUNT_READONLY); + fs_remount("/usr", MOUNT_READONLY); // update /var directory in order to support multiple sandboxes running on the same root directory fs_var_lock(); @@ -851,7 +752,7 @@ void fs_basic_fs(void) { if (!arg_writable_var_log) fs_var_log(); else - fs_rdwr("/var/log"); + fs_remount("/var/log", MOUNT_RDWR); fs_var_lib(); 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) { if (mkdir(RUN_PULSE_DIR, 0700) == -1) errExit("mkdir"); // mount it nosuid, noexec, nodev - fs_noexec(RUN_PULSE_DIR); + fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC); // create the new client.conf file char *pulsecfg = NULL; @@ -155,7 +155,7 @@ void pulseaudio_init(void) { if (fstatvfs(fd, &vfs) == -1) errExit("fstatvfs"); if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) - fs_rdonly(RUN_PULSE_DIR); + fs_remount(RUN_PULSE_DIR, MOUNT_READONLY); // mount via the link in /proc/self/fd char *proc; 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) { (void) rv; } // make seccomp filters read-only - fs_rdonly(RUN_SECCOMP_DIR); + fs_remount(RUN_SECCOMP_DIR, MOUNT_READONLY); #endif // 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) { umount("/tmp"); // remount RUN_XAUTHORITY_SEC_FILE noexec, nodev, nosuid - fs_noexec(RUN_XAUTHORITY_SEC_FILE); + fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_NOEXEC); // Ensure there is already a file in the usual location, so that bind-mount below will work. char *dest; @@ -1202,7 +1202,7 @@ void x11_xorg(void) { if (fstatvfs(fd, &vfs) == -1) errExit("fstatvfs"); if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) - fs_rdonly(RUN_XAUTHORITY_SEC_FILE); + fs_remount(RUN_XAUTHORITY_SEC_FILE, MOUNT_READONLY); // mount via the link in /proc/self/fd char *proc; -- cgit v1.2.3-70-g09d2