Skip to content

Commit

Permalink
add /run whitelist support
Browse files Browse the repository at this point in the history
  • Loading branch information
smitsohu committed May 7, 2021
1 parent 14b104a commit 4019e3c
Showing 1 changed file with 76 additions and 41 deletions.
117 changes: 76 additions & 41 deletions src/firejail/fs_whitelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,50 +307,84 @@ static void globbing(const char *pattern) {
}

// mount tmpfs on all top level directories
static void tmpfs_topdirs(TopDir *topdirs) {
// process user home directory first
static void tmpfs_topdirs(const TopDir *topdirs) {
int i;
for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
if (strcmp(topdirs[i].path, cfg.homedir) == 0) {
fs_private();
break;
if (strcmp(topdirs[i].path, cfg.homedir) == 0 ||
strcmp(topdirs[i].path, runuser) == 0)
continue;

// special case /run
// open /run/firejail, so it can be restored right after mounting the tmpfs
int fd = -1;
if (strcmp(topdirs[i].path, "/run") == 0) {
fd = open(RUN_FIREJAIL_DIR, O_PATH|O_CLOEXEC);
if (fd == -1)
errExit("open");
}
}

for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
if (strcmp(topdirs[i].path, cfg.homedir) != 0) {
// mount the tmpfs
fs_tmpfs(topdirs[i].path, 0);
selinux_relabel_path(topdirs[i].path, topdirs[i].path);
// mount tmpfs
fs_tmpfs(topdirs[i].path, 0);

// init tmpfs
if (strcmp(topdirs[i].path, "/run") == 0) {
// restore /run/firejail directory
if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1)
errExit("mkdir");
char *proc;
if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
errExit("asprintf");
if (mount(proc, RUN_FIREJAIL_DIR, NULL, MS_BIND | MS_REC, NULL) < 0)
errExit("mount bind");
free(proc);
close(fd);
fs_logger2("whitelist", RUN_FIREJAIL_DIR);

// init tmpfs
// restore /run/user/$UID directory
// get path relative to /run
const char *rel = runuser + 5;
whitelist_file(topdirs[i].fd, topdirs[i].path, rel, runuser);
}
else if (strcmp(topdirs[i].path, "/tmp") == 0) {
// fix pam-tmpdir (#2685)
if (strcmp(topdirs[i].path, "/tmp") == 0) {
const char *env = env_get("TMP");
if (env) {
char *pamtmpdir;
if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1)
errExit("asprintf");
if (strcmp(env, pamtmpdir) == 0) {
// create empty user-owned /tmp/user/$UID directory
mkdir_attr("/tmp/user", 0711, 0, 0);
selinux_relabel_path("/tmp/user", "/tmp/user");
fs_logger("mkdir /tmp/user");
mkdir_attr(pamtmpdir, 0700, getuid(), 0);
selinux_relabel_path(pamtmpdir, pamtmpdir);
fs_logger2("mkdir", pamtmpdir);
}
free(pamtmpdir);
const char *env = env_get("TMP");
if (env) {
char *pamtmpdir;
if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1)
errExit("asprintf");
if (strcmp(env, pamtmpdir) == 0) {
// create empty user-owned /tmp/user/$UID directory
mkdir_attr("/tmp/user", 0711, 0, 0);
selinux_relabel_path("/tmp/user", "/tmp/user");
fs_logger("mkdir /tmp/user");
mkdir_attr(pamtmpdir, 0700, getuid(), 0);
selinux_relabel_path(pamtmpdir, pamtmpdir);
fs_logger2("mkdir", pamtmpdir);
}
free(pamtmpdir);
}
}

// bring back user home directory if it is masked by the tmpfs
size_t topdir_len = strlen(topdirs[i].path);
if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') {
// get path relative to top level directory
const char *rel = cfg.homedir + topdir_len + 1;
whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir);
}
// restore user home directory if it is maked by the tmpfs
size_t topdir_len = strlen(topdirs[i].path);
if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') {
// get path relative to top level directory
const char *rel = cfg.homedir + topdir_len + 1;
whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir);
}

selinux_relabel_path(topdirs[i].path, topdirs[i].path);
}

// nested top level directories
for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
// home directory
if (strcmp(topdirs[i].path, cfg.homedir) == 0)
fs_private(); // checks owner if outside /home
// /run/user/$UID
else if (strcmp(topdirs[i].path, runuser) == 0) {
fs_tmpfs(topdirs[i].path, 0);
selinux_relabel_path(topdirs[i].path, topdirs[i].path);
}
}
}
Expand All @@ -372,10 +406,9 @@ static int reject_topdir(const char *dir) {
static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
assert(dir && path);

// /proc, /run and /sys are not allowed
// /proc and /sys are not allowed
if (strcmp(dir, "/") == 0 ||
strcmp(dir, "/proc") == 0 ||
strcmp(dir, "/run") == 0 ||
strcmp(dir, "/sys") == 0)
whitelist_error(path);

Expand Down Expand Up @@ -448,14 +481,16 @@ static char *extract_topdir(const char *path) {
if (!dup)
errExit("strdup");

// user home is treated as top level directory
// user home directory can be anywhere; disconnect user home
// whitelisting from top level directory whitelisting
// by treating user home as separate whitelist top level directory
if (strncmp(dup, cfg.homedir, homedir_len) == 0 && dup[homedir_len] == '/')
dup[homedir_len] = '\0';
// whitelisting in /run and /sys is not allowed,
// but /run/user/$UID and /sys/module are exceptions
// and are treated as top level directories here
// /run/user/$UID is treated as top level directory
else if (strncmp(dup, runuser, runuser_len) == 0 && dup[runuser_len] == '/')
dup[runuser_len] = '\0';
// whitelisting in /sys is not allowed, but /sys/module is an exception
// and is treated as top level directory here
else if (strncmp(dup, "/sys/module", 11) == 0 && dup[11] == '/')
dup[11] = '\0';
// treat /usr subdirectories as top level directories
Expand Down

0 comments on commit 4019e3c

Please sign in to comment.