diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index bf51a4c9315..45075d1379f 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -434,13 +434,15 @@ void fs_proc_sys_dev_boot(void); void disable_config(void); // build a basic read-only filesystem void fs_basic_fs(void); -// mount overlayfs on top of / directory -char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); -void fs_overlayfs(void); void fs_private_tmp(void); void fs_private_cache(void); void fs_mnt(const int enforce); +// fs_overlayfs.c +char *fs_check_overlay_dir(const char *subdirname, int allow_reuse); +void fs_overlayfs(void); +int remove_overlay_directory(void); + // chroot.c // chroot into an existing directory; mount existing /dev and update /etc/resolv.conf void fs_check_chroot_dir(void); @@ -531,7 +533,6 @@ void wait_for_other(int fd); void notify_other(int fd); uid_t pid_get_uid(pid_t pid); uid_t get_group_id(const char *group); -int remove_overlay_directory(void); void flush_stdin(void); int create_empty_dir_as_user(const char *dir, mode_t mode); void create_empty_dir_as_root(const char *dir, mode_t mode); diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 1a9a8df0deb..0b9a3803571 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -20,9 +20,7 @@ #include "firejail.h" #include "../include/gcov_wrapper.h" #include -#include #include -#include #include #include #include @@ -34,7 +32,7 @@ #endif #define MAX_BUF 4096 -#define EMPTY_STRING ("") + // check noblacklist statements not matched by a proper blacklist in disable-*.inc files //#define TEST_NO_BLACKLIST_MATCHING @@ -896,367 +894,6 @@ void fs_basic_fs(void) { } - -#ifdef HAVE_OVERLAYFS -char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { - assert(subdirname); - EUID_ASSERT(); - struct stat s; - char *dirname; - - if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) - errExit("asprintf"); - // check if ~/.firejail already exists - if (lstat(dirname, &s) == 0) { - if (!S_ISDIR(s.st_mode)) { - if (S_ISLNK(s.st_mode)) - fprintf(stderr, "Error: %s is a symbolic link\n", dirname); - else - fprintf(stderr, "Error: %s is not a directory\n", dirname); - exit(1); - } - if (s.st_uid != getuid()) { - fprintf(stderr, "Error: %s is not owned by the current user\n", dirname); - exit(1); - } - } - else { - // create ~/.firejail directory - create_empty_dir_as_user(dirname, 0700); - if (stat(dirname, &s) == -1) { - fprintf(stderr, "Error: cannot create directory %s\n", dirname); - exit(1); - } - } - free(dirname); - - // check overlay directory - if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1) - errExit("asprintf"); - if (lstat(dirname, &s) == 0) { - if (!S_ISDIR(s.st_mode)) { - if (S_ISLNK(s.st_mode)) - fprintf(stderr, "Error: %s is a symbolic link\n", dirname); - else - fprintf(stderr, "Error: %s is not a directory\n", dirname); - exit(1); - } - if (s.st_uid != 0) { - fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname); - exit(1); - } - if (allow_reuse == 0) { - fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n"); - exit(1); - } - } - - return dirname; -} - - - -// mount overlayfs on top of / directory -// mounting an overlay and chrooting into it: -// -// Old Ubuntu kernel -// # cd ~ -// # mkdir -p overlay/root -// # mkdir -p overlay/diff -// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root -// # chroot /root/overlay/root -// to shutdown, first exit the chroot and then unmount the overlay -// # exit -// # umount /root/overlay/root -// -// Kernels 3.18+ -// # cd ~ -// # mkdir -p overlay/root -// # mkdir -p overlay/diff -// # mkdir -p overlay/work -// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root -// # cat /etc/mtab | grep overlay -// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0 -// # chroot /root/overlay/root -// to shutdown, first exit the chroot and then unmount the overlay -// # exit -// # umount /root/overlay/root - - -// to do: fix the code below; also, it might work without /dev, but consider keeping /dev/shm; add locking mechanism for overlay-clean -#include -void fs_overlayfs(void) { - struct stat s; - - // check kernel version - struct utsname u; - int rv = uname(&u); - if (rv != 0) - errExit("uname"); - int major; - int minor; - if (2 != sscanf(u.release, "%d.%d", &major, &minor)) { - fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version); - exit(1); - } - - if (arg_debug) - printf("Linux kernel version %d.%d\n", major, minor); - int oldkernel = 0; - if (major < 3) { - fprintf(stderr, "Error: minimum kernel version required 3.x\n"); - exit(1); - } - if (major == 3 && minor < 18) - oldkernel = 1; - - // mounting an overlayfs on top of / seems to be broken for kernels > 4.19 - // we disable overlayfs for now, pending fixing - if (major >= 4 &&minor >= 19) { - fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n"); - exit(1); - } - - char *oroot = RUN_OVERLAY_ROOT; - mkdir_attr(oroot, 0755, 0, 0); - - // set base for working and diff directories - char *basedir = RUN_MNT_DIR; - int basefd = -1; - - if (arg_overlay_keep) { - basedir = cfg.overlay_dir; - assert(basedir); - // get a file descriptor for ~/.firejail, fails if there is any symlink - char *firejail; - if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1) - errExit("asprintf"); - int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); - if (fd == -1) - errExit("safer_openat"); - free(firejail); - // create basedir if it doesn't exist - // the new directory will be owned by root - const char *dirname = gnu_basename(basedir); - if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) { - perror("mkdir"); - fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir); - exit(1); - } - // open basedir - basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); - close(fd); - } - else { - basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); - } - if (basefd == -1) { - perror("open"); - fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir); - exit(1); - } - - // confirm once more base is owned by root - if (fstat(basefd, &s) == -1) - errExit("fstat"); - if (s.st_uid != 0) { - fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir); - exit(1); - } - // confirm permissions of base are 0755 - if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { - fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir); - exit(1); - } - - // create diff and work directories inside base - // no need to check arg_overlay_reuse - char *odiff; - if (asprintf(&odiff, "%s/odiff", basedir) == -1) - errExit("asprintf"); - // the new directory will be owned by root - if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) { - perror("mkdir"); - fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff); - exit(1); - } - ASSERT_PERMS(odiff, 0, 0, 0755); - - char *owork; - if (asprintf(&owork, "%s/owork", basedir) == -1) - errExit("asprintf"); - // the new directory will be owned by root - if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) { - perror("mkdir"); - fprintf(stderr, "Error: cannot create overlay directory %s\n", owork); - exit(1); - } - ASSERT_PERMS(owork, 0, 0, 0755); - - // mount overlayfs - if (arg_debug) - printf("Mounting OverlayFS\n"); - char *option; - if (oldkernel) { // old Ubuntu/OpenSUSE kernels - if (arg_overlay_keep) { - fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n"); - exit(1); - } - if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1) - errExit("asprintf"); - if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0) - errExit("mounting overlayfs"); - } - else { // kernel 3.18 or newer - if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1) - errExit("asprintf"); - if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) { - fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor); - errExit("mounting overlayfs"); - } - - //*************************** - // issue #263 start code - // My setup has a separate mount point for /home. When the overlay is mounted, - // the overlay does not contain the original /home contents. - // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work - // @dshmgh, Jan 2016 - { - char *overlayhome; - struct stat s; - char *hroot; - char *hdiff; - char *hwork; - - // dons add debug - if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork); - - // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it? - // must create var for oroot/cfg.homedir - if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1) - errExit("asprintf"); - if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome); - - // if no homedir in overlay -- create another overlay for /home - if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) { - - // no need to check arg_overlay_reuse - if (asprintf(&hdiff, "%s/hdiff", basedir) == -1) - errExit("asprintf"); - // the new directory will be owned by root - if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) { - perror("mkdir"); - fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff); - exit(1); - } - ASSERT_PERMS(hdiff, 0, 0, 0755); - - // no need to check arg_overlay_reuse - if (asprintf(&hwork, "%s/hwork", basedir) == -1) - errExit("asprintf"); - // the new directory will be owned by root - if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) { - perror("mkdir"); - fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork); - exit(1); - } - ASSERT_PERMS(hwork, 0, 0, 0755); - - // no homedir in overlay so now mount another overlay for /home - if (asprintf(&hroot, "%s/home", oroot) == -1) - errExit("asprintf"); - if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) - errExit("asprintf"); - if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0) - errExit("mounting overlayfs for mounted home directory"); - - printf("OverlayFS for /home configured in %s directory\n", basedir); - free(hroot); - free(hdiff); - free(hwork); - - } // stat(overlayhome) - free(overlayhome); - } - // issue #263 end code - //*************************** - } - fmessage("OverlayFS configured in %s directory\n", basedir); - close(basefd); - - // /dev, /run and /tmp are not covered by the overlay - // mount-bind dev directory - if (arg_debug) - printf("Mounting /dev\n"); - char *dev; - if (asprintf(&dev, "%s/dev", oroot) == -1) - errExit("asprintf"); - if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0) - errExit("mounting /dev"); - fs_logger("whitelist /dev"); - - // mount-bind run directory - if (arg_debug) - printf("Mounting /run\n"); - char *run; - if (asprintf(&run, "%s/run", oroot) == -1) - errExit("asprintf"); - if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0) - errExit("mounting /run"); - fs_logger("whitelist /run"); - - // mount-bind tmp directory - if (arg_debug) - printf("Mounting /tmp\n"); - char *tmp; - if (asprintf(&tmp, "%s/tmp", oroot) == -1) - errExit("asprintf"); - if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0) - errExit("mounting /tmp"); - fs_logger("whitelist /tmp"); - - // chroot in the new filesystem - __gcov_flush(); - - if (chroot(oroot) == -1) - errExit("chroot"); - - // mount a new proc filesystem - if (arg_debug) - printf("Mounting /proc filesystem representing the PID namespace\n"); - if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) - errExit("mounting /proc"); - - // update /var directory in order to support multiple sandboxes running on the same root directory -// if (!arg_private_dev) -// fs_dev_shm(); - fs_var_lock(); - if (!arg_keep_var_tmp) - fs_var_tmp(); - if (!arg_writable_var_log) - fs_var_log(); - fs_var_lib(); - fs_var_cache(); - fs_var_utmp(); - fs_machineid(); - - // don't leak user information - restrict_users(); - - // when starting as root, firejail config is not disabled; - if (getuid() != 0) - disable_config(); - - // cleanup and exit - free(option); - free(odiff); - free(owork); - free(dev); - free(run); - free(tmp); -} -#endif - // this function is called from sandbox.c before blacklist/whitelist functions void fs_private_tmp(void) { EUID_ASSERT(); @@ -1280,7 +917,6 @@ void fs_private_tmp(void) { // whitelist x11 directory profile_add("whitelist /tmp/.X11-unix"); - // read-only x11 directory profile_add("read-only /tmp/.X11-unix"); // whitelist sndio directory diff --git a/src/firejail/fs_overlayfs.c b/src/firejail/fs_overlayfs.c new file mode 100644 index 00000000000..fe3761cb6e3 --- /dev/null +++ b/src/firejail/fs_overlayfs.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2014-2021 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifdef HAVE_OVERLAYFS +#include "firejail.h" +#include "../include/gcov_wrapper.h" +#include +#include +#include +#include + +#include +#ifndef O_PATH +#define O_PATH 010000000 +#endif + + +char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { + assert(subdirname); + EUID_ASSERT(); + struct stat s; + char *dirname; + + if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1) + errExit("asprintf"); + // check if ~/.firejail already exists + if (lstat(dirname, &s) == 0) { + if (!S_ISDIR(s.st_mode)) { + if (S_ISLNK(s.st_mode)) + fprintf(stderr, "Error: %s is a symbolic link\n", dirname); + else + fprintf(stderr, "Error: %s is not a directory\n", dirname); + exit(1); + } + if (s.st_uid != getuid()) { + fprintf(stderr, "Error: %s is not owned by the current user\n", dirname); + exit(1); + } + } + else { + // create ~/.firejail directory + create_empty_dir_as_user(dirname, 0700); + if (stat(dirname, &s) == -1) { + fprintf(stderr, "Error: cannot create directory %s\n", dirname); + exit(1); + } + } + free(dirname); + + // check overlay directory + if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1) + errExit("asprintf"); + if (lstat(dirname, &s) == 0) { + if (!S_ISDIR(s.st_mode)) { + if (S_ISLNK(s.st_mode)) + fprintf(stderr, "Error: %s is a symbolic link\n", dirname); + else + fprintf(stderr, "Error: %s is not a directory\n", dirname); + exit(1); + } + if (s.st_uid != 0) { + fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname); + exit(1); + } + if (allow_reuse == 0) { + fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n"); + exit(1); + } + } + + return dirname; +} + + +// mount overlayfs on top of / directory +// mounting an overlay and chrooting into it: +// +// Old Ubuntu kernel +// # cd ~ +// # mkdir -p overlay/root +// # mkdir -p overlay/diff +// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root +// # chroot /root/overlay/root +// to shutdown, first exit the chroot and then unmount the overlay +// # exit +// # umount /root/overlay/root +// +// Kernels 3.18+ +// # cd ~ +// # mkdir -p overlay/root +// # mkdir -p overlay/diff +// # mkdir -p overlay/work +// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root +// # cat /etc/mtab | grep overlay +// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0 +// # chroot /root/overlay/root +// to shutdown, first exit the chroot and then unmount the overlay +// # exit +// # umount /root/overlay/root + +// to do: fix the code below +#include +void fs_overlayfs(void) { + struct stat s; + + // check kernel version + struct utsname u; + int rv = uname(&u); + if (rv != 0) + errExit("uname"); + int major; + int minor; + if (2 != sscanf(u.release, "%d.%d", &major, &minor)) { + fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version); + exit(1); + } + + if (arg_debug) + printf("Linux kernel version %d.%d\n", major, minor); + int oldkernel = 0; + if (major < 3) { + fprintf(stderr, "Error: minimum kernel version required 3.x\n"); + exit(1); + } + if (major == 3 && minor < 18) + oldkernel = 1; + + // mounting an overlayfs on top of / seems to be broken for kernels > 4.19 + // we disable overlayfs for now, pending fixing + if (major >= 4 &&minor >= 19) { + fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n"); + exit(1); + } + + char *oroot = RUN_OVERLAY_ROOT; + mkdir_attr(oroot, 0755, 0, 0); + + // set base for working and diff directories + char *basedir = RUN_MNT_DIR; + int basefd = -1; + + if (arg_overlay_keep) { + basedir = cfg.overlay_dir; + assert(basedir); + // get a file descriptor for ~/.firejail, fails if there is any symlink + char *firejail; + if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1) + errExit("asprintf"); + int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); + if (fd == -1) + errExit("safer_openat"); + free(firejail); + // create basedir if it doesn't exist + // the new directory will be owned by root + const char *dirname = gnu_basename(basedir); + if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) { + perror("mkdir"); + fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir); + exit(1); + } + // open basedir + basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); + close(fd); + } + else { + basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); + } + if (basefd == -1) { + perror("open"); + fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir); + exit(1); + } + + // confirm once more base is owned by root + if (fstat(basefd, &s) == -1) + errExit("fstat"); + if (s.st_uid != 0) { + fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir); + exit(1); + } + // confirm permissions of base are 0755 + if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { + fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir); + exit(1); + } + + // create diff and work directories inside base + // no need to check arg_overlay_reuse + char *odiff; + if (asprintf(&odiff, "%s/odiff", basedir) == -1) + errExit("asprintf"); + // the new directory will be owned by root + if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) { + perror("mkdir"); + fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff); + exit(1); + } + ASSERT_PERMS(odiff, 0, 0, 0755); + + char *owork; + if (asprintf(&owork, "%s/owork", basedir) == -1) + errExit("asprintf"); + // the new directory will be owned by root + if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) { + perror("mkdir"); + fprintf(stderr, "Error: cannot create overlay directory %s\n", owork); + exit(1); + } + ASSERT_PERMS(owork, 0, 0, 0755); + + // mount overlayfs + if (arg_debug) + printf("Mounting OverlayFS\n"); + char *option; + if (oldkernel) { // old Ubuntu/OpenSUSE kernels + if (arg_overlay_keep) { + fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n"); + exit(1); + } + if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1) + errExit("asprintf"); + if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0) + errExit("mounting overlayfs"); + } + else { // kernel 3.18 or newer + if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1) + errExit("asprintf"); + if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) { + fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor); + errExit("mounting overlayfs"); + } + + //*************************** + // issue #263 start code + // My setup has a separate mount point for /home. When the overlay is mounted, + // the overlay does not contain the original /home contents. + // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work + // @dshmgh, Jan 2016 + { + char *overlayhome; + struct stat s; + char *hroot; + char *hdiff; + char *hwork; + + // dons add debug + if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork); + + // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it? + // must create var for oroot/cfg.homedir + if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1) + errExit("asprintf"); + if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome); + + // if no homedir in overlay -- create another overlay for /home + if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) { + + // no need to check arg_overlay_reuse + if (asprintf(&hdiff, "%s/hdiff", basedir) == -1) + errExit("asprintf"); + // the new directory will be owned by root + if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) { + perror("mkdir"); + fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff); + exit(1); + } + ASSERT_PERMS(hdiff, 0, 0, 0755); + + // no need to check arg_overlay_reuse + if (asprintf(&hwork, "%s/hwork", basedir) == -1) + errExit("asprintf"); + // the new directory will be owned by root + if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) { + perror("mkdir"); + fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork); + exit(1); + } + ASSERT_PERMS(hwork, 0, 0, 0755); + + // no homedir in overlay so now mount another overlay for /home + if (asprintf(&hroot, "%s/home", oroot) == -1) + errExit("asprintf"); + if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1) + errExit("asprintf"); + if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0) + errExit("mounting overlayfs for mounted home directory"); + + printf("OverlayFS for /home configured in %s directory\n", basedir); + free(hroot); + free(hdiff); + free(hwork); + + } // stat(overlayhome) + free(overlayhome); + } + // issue #263 end code + //*************************** + } + fmessage("OverlayFS configured in %s directory\n", basedir); + close(basefd); + + // /dev, /run and /tmp are not covered by the overlay + // mount-bind dev directory + if (arg_debug) + printf("Mounting /dev\n"); + char *dev; + if (asprintf(&dev, "%s/dev", oroot) == -1) + errExit("asprintf"); + if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mounting /dev"); + fs_logger("whitelist /dev"); + + // mount-bind run directory + if (arg_debug) + printf("Mounting /run\n"); + char *run; + if (asprintf(&run, "%s/run", oroot) == -1) + errExit("asprintf"); + if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mounting /run"); + fs_logger("whitelist /run"); + + // mount-bind tmp directory + if (arg_debug) + printf("Mounting /tmp\n"); + char *tmp; + if (asprintf(&tmp, "%s/tmp", oroot) == -1) + errExit("asprintf"); + if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0) + errExit("mounting /tmp"); + fs_logger("whitelist /tmp"); + + // chroot in the new filesystem + __gcov_flush(); + + if (chroot(oroot) == -1) + errExit("chroot"); + + // mount a new proc filesystem + if (arg_debug) + printf("Mounting /proc filesystem representing the PID namespace\n"); + if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) + errExit("mounting /proc"); + + // update /var directory in order to support multiple sandboxes running on the same root directory +// if (!arg_private_dev) +// fs_dev_shm(); + fs_var_lock(); + if (!arg_keep_var_tmp) + fs_var_tmp(); + if (!arg_writable_var_log) + fs_var_log(); + fs_var_lib(); + fs_var_cache(); + fs_var_utmp(); + fs_machineid(); + + // don't leak user information + restrict_users(); + + // when starting as root, firejail config is not disabled; + if (getuid() != 0) + disable_config(); + + // cleanup and exit + free(option); + free(odiff); + free(owork); + free(dev); + free(run); + free(tmp); +} + + +static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { + (void) sb; + (void) typeflag; + (void) ftwbuf; + assert(fpath); + + if (strcmp(fpath, ".") == 0) // rmdir would fail with EINVAL + return 0; + + if (remove(fpath)) { // removes the link not the actual file + fprintf(stderr, "Error: cannot remove file: %s\n", strerror(errno)); + exit(1); + } + + return 0; +} + +int remove_overlay_directory(void) { + EUID_ASSERT(); + sleep(1); + + char *path; + if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) + errExit("asprintf"); + + if (access(path, F_OK) == 0) { + pid_t child = fork(); + if (child < 0) + errExit("fork"); + if (child == 0) { + // open ~/.firejail + int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); + if (fd == -1) { + fprintf(stderr, "Error: cannot open %s\n", path); + exit(1); + } + struct stat s; + if (fstat(fd, &s) == -1) + errExit("fstat"); + if (!S_ISDIR(s.st_mode)) { + if (S_ISLNK(s.st_mode)) + fprintf(stderr, "Error: %s is a symbolic link\n", path); + else + fprintf(stderr, "Error: %s is not a directory\n", path); + exit(1); + } + if (s.st_uid != getuid()) { + fprintf(stderr, "Error: %s is not owned by the current user\n", path); + exit(1); + } + // chdir to ~/.firejail + if (fchdir(fd) == -1) + errExit("fchdir"); + close(fd); + + EUID_ROOT(); + // FTW_PHYS - do not follow symbolic links + if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1) + errExit("nftw"); + + EUID_USER(); + // remove ~/.firejail + if (rmdir(path) == -1) + errExit("rmdir"); + + __gcov_flush(); + + _exit(0); + } + // wait for the child to finish + waitpid(child, NULL, 0); + // check if ~/.firejail was deleted + if (access(path, F_OK) == 0) + return 1; + } + return 0; +} + +#endif // HAVE_OVERLAYFS diff --git a/src/firejail/util.c b/src/firejail/util.c index f0df45eb2a3..8749f79cc70 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -20,8 +20,6 @@ #define _XOPEN_SOURCE 500 #include "firejail.h" #include "../include/gcov_wrapper.h" -#include -#include #include #include #include @@ -32,9 +30,6 @@ #include #include -#include -#include - #include #ifndef O_PATH #define O_PATH 010000000 @@ -964,8 +959,6 @@ uid_t pid_get_uid(pid_t pid) { } - - uid_t get_group_id(const char *group) { // find tty group id gid_t gid = 0; @@ -977,86 +970,6 @@ uid_t get_group_id(const char *group) { } -static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { - (void) sb; - (void) typeflag; - (void) ftwbuf; - assert(fpath); - - if (strcmp(fpath, ".") == 0) - return 0; - - if (remove(fpath)) { // removes the link not the actual file - perror("remove"); - fprintf(stderr, "Error: cannot remove file from user .firejail directory: %s\n", fpath); - exit(1); - } - - return 0; -} - - -int remove_overlay_directory(void) { - EUID_ASSERT(); - sleep(1); - - char *path; - if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) - errExit("asprintf"); - - if (access(path, F_OK) == 0) { - pid_t child = fork(); - if (child < 0) - errExit("fork"); - if (child == 0) { - // open ~/.firejail - int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); - if (fd == -1) { - fprintf(stderr, "Error: cannot open %s\n", path); - exit(1); - } - struct stat s; - if (fstat(fd, &s) == -1) - errExit("fstat"); - if (!S_ISDIR(s.st_mode)) { - if (S_ISLNK(s.st_mode)) - fprintf(stderr, "Error: %s is a symbolic link\n", path); - else - fprintf(stderr, "Error: %s is not a directory\n", path); - exit(1); - } - if (s.st_uid != getuid()) { - fprintf(stderr, "Error: %s is not owned by the current user\n", path); - exit(1); - } - // chdir to ~/.firejail - if (fchdir(fd) == -1) - errExit("fchdir"); - close(fd); - - EUID_ROOT(); - // FTW_PHYS - do not follow symbolic links - if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1) - errExit("nftw"); - - EUID_USER(); - // remove ~/.firejail - if (rmdir(path) == -1) - errExit("rmdir"); - - __gcov_flush(); - - _exit(0); - } - // wait for the child to finish - waitpid(child, NULL, 0); - // check if ~/.firejail was deleted - if (access(path, F_OK) == 0) - return 1; - } - return 0; -} - // flush stdin if it is connected to a tty and has input void flush_stdin(void) { if (!isatty(STDIN_FILENO))