From 2ada2322f6d8dc31c22b563acd7c2fb9d87cb8a3 Mon Sep 17 00:00:00 2001 From: Toni Uhlig Date: Thu, 21 Jun 2018 16:21:45 +0200 Subject: introduced firejail alike filesystem managment (modified source from firejail) Signed-off-by: Toni Uhlig --- src/filesystem.c | 495 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/filesystem.c (limited to 'src/filesystem.c') diff --git a/src/filesystem.c b/src/filesystem.c new file mode 100644 index 0000000..1a55111 --- /dev/null +++ b/src/filesystem.c @@ -0,0 +1,495 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#else +#define POTD_RODIR "/var/run/potd-rodir" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "utils.h" + +typedef struct MountData { + /* + * the pathname of the directory in the filesystem which + * forms the root of this mount + */ + char *fsname; + /* mount destination */ + char *dir; + /* filesystem type */ + char *fstype; +} MountData; + +typedef enum { + BLACKLIST_FILE, + BLACKLIST_NOLOG, + MOUNT_READONLY, + MOUNT_TMPFS, + MOUNT_NOEXEC, + MOUNT_RDWR, + OPERATION_MAX +} OPERATION; + +typedef enum { + UNSUCCESSFUL, + SUCCESSFUL +} LAST_DISABLE_OPERATION; +LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL; + +static void disable_file(OPERATION op, const char *filename); +static int get_mount_flags(const char *path, unsigned long *flags); +static MountData * +get_last_mount(void); +static void fs_rdonly(const char *dir); +static void fs_rdwr(const char *dir); +static void fs_noexec(const char *dir); + +#define MAX_BUF 4096 +static char mbuf[MAX_BUF]; +static MountData mdata; + + +static void disable_file(OPERATION op, const char *filename) +{ + char *fname; + struct stat s; + + assert(filename); + assert(op dir, path, strlen(path)) != 0) + FATAL("%s: invalid read-write mount for '%s'", __func__, path); + + free(path); +} + +static void fs_noexec(const char *dir) +{ + struct stat s; + int rv; + unsigned long flags = 0; + + assert(dir); + + // check directory exists + rv = stat(dir, &s); + if (rv == 0) { + // mount --bind /bin /bin + // mount --bind -o remount,ro /bin + 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 (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0 || + mount(NULL, dir, NULL, flags|MS_BIND|MS_REMOUNT|MS_REC, NULL) < 0) + { + FATAL("%s: mount noexec for '%s'", __func__, dir); + } + } +} + +// Disable /mnt, /media, /run/mount and /run/media access +void fs_mnt(void) +{ + disable_file(BLACKLIST_FILE, "/mnt"); + disable_file(BLACKLIST_FILE, "/media"); + disable_file(BLACKLIST_FILE, "/run/mount"); + disable_file(BLACKLIST_FILE, "//run/media"); +} + +// mount /proc and /sys directories +void fs_proc_sys(void) +{ + D("%s: Remounting /proc and /proc/sys filesystems", __func__); + if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) + FATAL("%s: mounting /proc", __func__); + + // remount /proc/sys readonly + if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0 || + mount(NULL, "/proc/sys", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID| + MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) + { + FATAL("%s: mounting /proc/sys", __func__); + } + + /* Mount a version of /sys that describes the network namespace */ + D("%s: Remounting /sys directory", __func__); + if (umount2("/sys", MNT_DETACH) < 0) + W("%s: failed to unmount /sys", __func__); + if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0) { + W("%s: failed to mount /sys", __func__); + } else { + W("%s: remount /sys", __func__); + } + + disable_file(BLACKLIST_FILE, "/sys/firmware"); + disable_file(BLACKLIST_FILE, "/sys/hypervisor"); + disable_file(BLACKLIST_FILE, "/sys/power"); + disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); + disable_file(BLACKLIST_FILE, "/sys/kernel/vmcoreinfo"); + disable_file(BLACKLIST_FILE, "/sys/kernel/uevent_helper"); + + // various /proc/sys files + disable_file(BLACKLIST_FILE, "/proc/sys/security"); + disable_file(BLACKLIST_FILE, "/proc/sys/efi/vars"); + disable_file(BLACKLIST_FILE, "/proc/sys/fs/binfmt_misc"); + disable_file(BLACKLIST_FILE, "/proc/sys/kernel/core_pattern"); + disable_file(BLACKLIST_FILE, "/proc/sys/kernel/modprobe"); + disable_file(BLACKLIST_FILE, "/proc/sysrq-trigger"); + disable_file(BLACKLIST_FILE, "/proc/sys/kernel/hotplug"); + disable_file(BLACKLIST_FILE, "/proc/sys/vm/panic_on_oom"); + + // various /proc files + disable_file(BLACKLIST_FILE, "/proc/irq"); + disable_file(BLACKLIST_FILE, "/proc/bus"); + disable_file(BLACKLIST_FILE, "/proc/config.gz"); + disable_file(BLACKLIST_FILE, "/proc/sched_debug"); + disable_file(BLACKLIST_FILE, "/proc/timer_list"); + disable_file(BLACKLIST_FILE, "/proc/timer_stats"); + disable_file(BLACKLIST_FILE, "/proc/kcore"); + disable_file(BLACKLIST_FILE, "/proc/kallsyms"); + disable_file(BLACKLIST_FILE, "/proc/mem"); + disable_file(BLACKLIST_FILE, "/proc/kmem"); + + // remove kernel symbol information + disable_file(BLACKLIST_FILE, "/usr/src/linux"); + disable_file(BLACKLIST_FILE, "/lib/modules"); + disable_file(BLACKLIST_FILE, "/usr/lib/debug"); + disable_file(BLACKLIST_FILE, "/boot"); + + // disable /selinux + disable_file(BLACKLIST_FILE, "/selinux"); + + // disable /dev/port + disable_file(BLACKLIST_FILE, "/dev/port"); + + // disable /dev/kmsg and /proc/kmsg + disable_file(BLACKLIST_FILE, "/dev/kmsg"); + disable_file(BLACKLIST_FILE, "/proc/kmsg"); +} + +void fs_var_lock(void) +{ + char *lnk; + + if (is_dir("/var/lock")) { + D("%s: Mounting tmpfs on /var/lock", __func__); + if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV| + MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) + { + FATAL("%s: mounting /lock", __func__); + } + } else { + lnk = realpath("/var/lock", NULL); + if (lnk) { + if (!is_dir(lnk)) { + // create directory + mkdir_attr(lnk, S_IRWXU|S_IRWXG|S_IRWXO, 0, 0); + } + D("%s: Mounting tmpfs on %s on behalf of /var/lock", __func__, lnk); + if (mount("tmpfs", lnk, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV| + MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) + { + FATAL("%s: mounting /var/lock", __func__); + } + free(lnk); + } else { + W("%s: /var/lock not mounted", __func__); + } + } +} + +void fs_var_tmp(void) +{ + struct stat s; + + if (stat("/var/tmp", &s) == 0) { + if (!is_link("/var/tmp")) { + D("%s: Mounting tmpfs on /var/tmp", __func__); + if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV| + MS_STRICTATIME | MS_REC, "mode=1777,gid=0") < 0) + { + FATAL("%s: mounting /var/tmp", __func__); + } + } + } else { + W("%s: /var/tmp not mounted", __func__); + } +} + +// build a basic read-only filesystem +void fs_basic_fs(void) +{ + D("%s: Mounting read-only /etc, /var, /bin, /sbin, /lib, /lib32, " + "/lib64, /usr", __func__); + fs_rdonly("/etc"); + fs_rdonly("/var"); + fs_rdonly("/bin"); + fs_rdonly("/sbin"); + fs_rdonly("/lib"); + fs_rdonly("/lib64"); + fs_rdonly("/lib32"); + fs_rdonly("/libx32"); + fs_rdonly("/usr"); + + // update /var directory in order to support multiple sandboxes running on the same root directory + fs_var_lock(); + fs_var_tmp(); + fs_rdwr("/var/log"); + fs_rdwr("/var/lib"); + fs_rdwr("/var/cache"); + fs_rdwr("/var/utmp"); +} + -- cgit v1.2.3