firejail/fix-CVE-2022-31214.patch

2245 lines
60 KiB
Diff

From 27cde3d7d1e4e16d4190932347c7151dc2a84c50 Mon Sep 17 00:00:00 2001
From: smitsohu <smitsohu@gmail.com>
Date: Wed, 8 Jun 2022 12:12:04 +0200
Subject: [PATCH] fixing CVE-2022-31214
---
src/firejail/caps.c | 36 +--
src/firejail/cgroup.c | 24 --
src/firejail/cpu.c | 49 +---
src/firejail/firejail.h | 31 ++-
src/firejail/fs.c | 1 +
src/firejail/fs_logger.c | 20 +-
src/firejail/join.c | 496 ++++++++++++++++++------------------
src/firejail/ls.c | 32 +--
src/firejail/main.c | 35 ++-
src/firejail/network_main.c | 45 +---
src/firejail/preproc.c | 6 +
src/firejail/process.c | 244 ++++++++++++++++++
src/firejail/protocol.c | 20 +-
src/firejail/run_files.c | 52 ++++
src/firejail/seccomp.c | 51 ++--
src/firejail/shutdown.c | 62 +----
src/firejail/util.c | 157 ++----------
src/include/common.h | 3 +-
src/include/rundefs.h | 1 +
src/lib/common.c | 77 +++++-
20 files changed, 781 insertions(+), 661 deletions(-)
create mode 100644 src/firejail/process.c
diff --git a/src/firejail/caps.c b/src/firejail/caps.c
index c5c06c6751..d88a991327 100644
--- a/src/firejail/caps.c
+++ b/src/firejail/caps.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <linux/filter.h>
#include <stddef.h>
+#include <fcntl.h>
#include <linux/capability.h>
#include <linux/audit.h>
#include <sys/prctl.h>
@@ -381,34 +382,21 @@ void caps_keep_list(const char *clist) {
}
#define MAXBUF 4098
-static uint64_t extract_caps(int pid) {
- EUID_ASSERT();
-
- char *file;
- if (asprintf(&file, "/proc/%d/status", pid) == -1)
- errExit("asprintf");
-
- EUID_ROOT(); // grsecurity
- FILE *fp = fopen(file, "re");
- EUID_USER(); // grsecurity
- if (!fp)
- goto errexit;
+static uint64_t extract_caps(ProcessHandle process) {
+ FILE *fp = process_fopen(process, "status");
char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp)) {
if (strncmp(buf, "CapBnd:\t", 8) == 0) {
- char *ptr = buf + 8;
unsigned long long val;
- sscanf(ptr, "%llx", &val);
- free(file);
- fclose(fp);
- return val;
+ if (sscanf(buf + 8, "%llx", &val) == 1) {
+ fclose(fp);
+ return val;
+ }
+ break;
}
}
- fclose(fp);
-errexit:
- free(file);
fprintf(stderr, "Error: cannot read caps configuration\n");
exit(1);
}
@@ -416,13 +404,11 @@ static uint64_t extract_caps(int pid) {
void caps_print_filter(pid_t pid) {
EUID_ASSERT();
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ uint64_t caps = extract_caps(sandbox);
+ unpin_process(sandbox);
- uint64_t caps = extract_caps(pid);
int i;
uint64_t mask;
int elems = sizeof(capslist) / sizeof(capslist[0]);
diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c
index f1e16187fb..c8cb96f98d 100644
--- a/src/firejail/cgroup.c
+++ b/src/firejail/cgroup.c
@@ -46,30 +46,6 @@ void save_cgroup(void) {
exit(1);
}
-void load_cgroup(const char *fname) {
- if (!fname)
- return;
-
- FILE *fp = fopen(fname, "re");
- if (fp) {
- char buf[MAXBUF];
- if (fgets(buf, MAXBUF, fp)) {
- cfg.cgroup = strdup(buf);
- if (!cfg.cgroup)
- errExit("strdup");
- }
- else
- goto errout;
-
- fclose(fp);
- return;
- }
-errout:
- fwarning("cannot load control group\n");
- if (fp)
- fclose(fp);
-}
-
static int is_cgroup_path(const char *fname) {
// path starts with /sys/fs/cgroup
if (strncmp(fname, "/sys/fs/cgroup", 14) != 0)
diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c
index 1ec510456a..917726359b 100644
--- a/src/firejail/cpu.c
+++ b/src/firejail/cpu.c
@@ -18,6 +18,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "firejail.h"
+#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -87,22 +88,6 @@ void save_cpu(void) {
}
}
-void load_cpu(const char *fname) {
- if (!fname)
- return;
-
- FILE *fp = fopen(fname, "re");
- if (fp) {
- unsigned tmp;
- int rv = fscanf(fp, "%x", &tmp);
- if (rv)
- cfg.cpus = (uint32_t) tmp;
- fclose(fp);
- }
- else
- fwarning("cannot load cpu affinity mask\n");
-}
-
void set_cpu_affinity(void) {
// set cpu affinity
cpu_set_t mask;
@@ -131,21 +116,8 @@ void set_cpu_affinity(void) {
}
}
-static void print_cpu(int pid) {
- char *file;
- if (asprintf(&file, "/proc/%d/status", pid) == -1) {
- errExit("asprintf");
- exit(1);
- }
-
- EUID_ROOT(); // grsecurity
- FILE *fp = fopen(file, "re");
- EUID_USER(); // grsecurity
- if (!fp) {
- printf(" Error: cannot open %s\n", file);
- free(file);
- return;
- }
+static void print_cpu(ProcessHandle process) {
+ FILE *fp = process_fopen(process, "status");
#define MAXBUF 4096
char buf[MAXBUF];
@@ -153,28 +125,21 @@ static void print_cpu(int pid) {
if (strncmp(buf, "Cpus_allowed_list:", 18) == 0) {
printf(" %s", buf);
fflush(0);
- free(file);
fclose(fp);
return;
}
}
fclose(fp);
- free(file);
}
-// allow any user to run --cpu.print
+// TODO: allow any user to run --cpu.print
void cpu_print_filter(pid_t pid) {
EUID_ASSERT();
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // now check if the pid belongs to a firejail sandbox
- if (is_ready_for_join(pid) == false) {
- fprintf(stderr, "Error: no valid sandbox\n");
- exit(1);
- }
+ print_cpu(sandbox);
+ unpin_process(sandbox);
- print_cpu(pid);
exit(0);
}
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 38408b5345..7e1d45c011 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -477,11 +477,29 @@ void top(void);
// usage.c
void usage(void);
+// process.c
+typedef struct processhandle_instance_t * ProcessHandle;
+
+ProcessHandle pin_process(pid_t pid);
+void unpin_process(ProcessHandle process);
+pid_t process_get_pid(ProcessHandle process);
+int process_get_fd(ProcessHandle process);
+int process_stat_nofail(ProcessHandle process, const char *fname, struct stat *s);
+int process_stat(ProcessHandle process, const char *fname, struct stat *s);
+int process_open_nofail(ProcessHandle process, const char *fname);
+int process_open(ProcessHandle process, const char *fname);
+FILE *process_fopen(ProcessHandle process, const char *fname);
+int process_join_namespace(ProcessHandle process, char *type);
+void process_send_signal(ProcessHandle process, int signum);
+ProcessHandle pin_parent_process(ProcessHandle process);
+ProcessHandle pin_child_process(ProcessHandle process, pid_t child);
+void process_rootfs_chroot(ProcessHandle process);
+int process_rootfs_stat(ProcessHandle process, const char *fname, struct stat *s);
+int process_rootfs_open(ProcessHandle process, const char *fname);
+
// join.c
+ProcessHandle pin_sandbox_process(pid_t pid);
void join(pid_t pid, int argc, char **argv, int index) __attribute__((noreturn));
-bool is_ready_for_join(const pid_t pid);
-void check_join_permission(pid_t pid);
-pid_t switch_to_child(pid_t pid);
// shutdown.c
void shut(pid_t pid);
@@ -648,13 +666,11 @@ void set_rlimits(void);
// cpu.c
void read_cpu_list(const char *str);
void set_cpu_affinity(void);
-void load_cpu(const char *fname);
void save_cpu(void);
void cpu_print_filter(pid_t pid) __attribute__((noreturn));
// cgroup.c
void save_cgroup(void);
-void load_cgroup(const char *fname);
void check_cgroup_file(const char *fname);
void set_cgroup(const char *fname, pid_t pid);
@@ -862,9 +878,7 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
#define PATH_FSECCOMP_MAIN (LIBDIR "/firejail/fseccomp") // when called from main thread
#define PATH_FSECCOMP ( RUN_FIREJAIL_LIB_DIR "/fseccomp") // when called from sandbox thread
-// FSEC_PRINT is run outside of sandbox by --seccomp.print
-// it is also run from inside the sandbox by --debug; in this case we do an access(filename, X_OK) test first
-#define PATH_FSEC_PRINT (LIBDIR "/firejail/fsec-print")
+#define PATH_FSEC_PRINT (RUN_FIREJAIL_LIB_DIR "/fsec-print")
#define PATH_FSEC_OPTIMIZE (RUN_FIREJAIL_LIB_DIR "/fsec-optimize")
@@ -899,6 +913,7 @@ void delete_bandwidth_run_file(pid_t pid);
void set_name_run_file(pid_t pid);
void set_x11_run_file(pid_t pid, int display);
void set_profile_run_file(pid_t pid, const char *fname);
+int set_sandbox_run_file(pid_t pid, pid_t child);
// dbus.c
int dbus_check_name(const char *name);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index c03cd7a12e..f30e56e8d8 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -834,6 +834,7 @@ void disable_config(void) {
free(fname);
// disable run time information
+ disable_file(BLACKLIST_FILE, RUN_FIREJAIL_SANDBOX_DIR);
disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR);
disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR);
disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR);
diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c
index 06f03dac52..8b6c41278e 100644
--- a/src/firejail/fs_logger.c
+++ b/src/firejail/fs_logger.c
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <fcntl.h>
#define MAXBUF 4098
@@ -120,24 +121,21 @@ void fs_logger_change_owner(void) {
void fs_logger_print_log(pid_t pid) {
EUID_ASSERT();
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ // chroot in the sandbox
+ process_rootfs_chroot(sandbox);
+ unpin_process(sandbox);
- // print RUN_FSLOGGER_FILE
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_FSLOGGER_FILE) == -1)
- errExit("asprintf");
+ drop_privs(0);
- EUID_ROOT();
- FILE *fp = fopen(fname, "re");
- free(fname);
+ // print RUN_FSLOGGER_FILE
+ FILE *fp = fopen(RUN_FSLOGGER_FILE, "re");
if (!fp) {
fprintf(stderr, "Error: Cannot open filesystem log\n");
exit(1);
}
+
char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp))
printf("%s", buf);
diff --git a/src/firejail/join.c b/src/firejail/join.c
index 5d73f71be2..5acdcb0601 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -42,6 +42,7 @@ static uint64_t caps = 0;
static unsigned display = 0;
#define BUFLEN 4096
+
static void signal_handler(int sig){
flush_stdin();
@@ -58,46 +59,6 @@ static void install_handler(void) {
sigaction(SIGTERM, &sga, NULL);
}
-#ifdef HAVE_APPARMOR
-static void extract_apparmor(pid_t pid) {
- if (checkcfg(CFG_APPARMOR)) {
- EUID_USER();
- if (aa_is_enabled() == 1) {
- // get pid of next child process
- pid_t child;
- if (find_child(pid, &child) == 1)
- child = pid; // no child, proceed with current pid
-
- // get name of AppArmor profile
- char *fname;
- if (asprintf(&fname, "/proc/%d/attr/current", child) == -1)
- errExit("asprintf");
- EUID_ROOT();
- int fd = open(fname, O_RDONLY|O_CLOEXEC);
- EUID_USER();
- free(fname);
- if (fd == -1)
- goto errexit;
- char buf[BUFLEN];
- ssize_t rv = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (rv < 0)
- goto errexit;
- buf[rv] = '\0';
- // process confined by Firejail's AppArmor policy?
- if (strncmp(buf, "firejail-default", 16) == 0)
- arg_apparmor = 1;
- }
- EUID_ROOT();
- }
- return;
-
-errexit:
- fprintf(stderr, "Error: cannot read /proc file\n");
- exit(1);
-}
-#endif // HAVE_APPARMOR
-
static void extract_x11_display(pid_t pid) {
char *fname;
if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1)
@@ -150,191 +111,140 @@ static void extract_command(int argc, char **argv, int index) {
build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index, true);
}
-static void extract_nogroups(pid_t pid) {
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_GROUPS_CFG) == -1)
- errExit("asprintf");
+static int open_shell(void) {
+ EUID_ASSERT();
+ assert(cfg.shell);
- struct stat s;
- if (stat(fname, &s) == -1) {
- free(fname);
- return;
+ if (arg_debug)
+ printf("Opening shell %s\n", cfg.shell);
+ // file descriptor will leak if not opened with O_CLOEXEC !!
+ int fd = open(cfg.shell, O_PATH|O_CLOEXEC);
+ if (fd == -1) {
+ fprintf(stderr, "Error: cannot open shell %s\n", cfg.shell);
+ exit(1);
}
- arg_nogroups = 1;
- free(fname);
-}
-
-static void extract_nonewprivs(pid_t pid) {
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_NONEWPRIVS_CFG) == -1)
+ // file descriptor needs to reach final fexecve
+ if (asprintf(&cfg.keep_fd, "%s,%d", cfg.keep_fd ? cfg.keep_fd : "", fd) == -1)
errExit("asprintf");
- struct stat s;
- if (stat(fname, &s) == -1) {
- free(fname);
- return;
- }
-
- arg_nonewprivs = 1;
- free(fname);
+ return fd;
}
-static void extract_cpu(pid_t pid) {
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_CPU_CFG) == -1)
- errExit("asprintf");
-
+static void extract_nogroups(ProcessHandle sandbox) {
struct stat s;
- if (stat(fname, &s) == -1) {
- free(fname);
- return;
- }
- // there is a CPU_CFG file, load it!
- load_cpu(fname);
- free(fname);
+ if (process_rootfs_stat(sandbox, RUN_GROUPS_CFG, &s) == 0)
+ arg_nogroups = 1;
}
-static void extract_cgroup(pid_t pid) {
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_CGROUP_CFG) == -1)
- errExit("asprintf");
-
+static void extract_nonewprivs(ProcessHandle sandbox) {
struct stat s;
- if (stat(fname, &s) == -1) {
- free(fname);
- return;
- }
- // there is a cgroup file CGROUP_CFG, load it!
- load_cgroup(fname);
- free(fname);
+ if (process_rootfs_stat(sandbox, RUN_NONEWPRIVS_CFG, &s) == 0)
+ arg_nonewprivs = 1;
}
-static void extract_caps(pid_t pid) {
- // open stat file
- char *file;
- if (asprintf(&file, "/proc/%u/status", pid) == -1) {
- perror("asprintf");
- exit(1);
- }
- FILE *fp = fopen(file, "re");
- if (!fp)
- goto errexit;
+static void extract_caps(ProcessHandle sandbox) {
+ // open status file
+ FILE *fp = process_fopen(sandbox, "status");
char buf[BUFLEN];
- while (fgets(buf, BUFLEN - 1, fp)) {
+ while (fgets(buf, BUFLEN, fp)) {
if (strncmp(buf, "CapBnd:", 7) == 0) {
- char *ptr = buf + 7;
unsigned long long val;
- if (sscanf(ptr, "%llx", &val) != 1)
+ if (sscanf(buf + 7, "%llx", &val) != 1)
goto errexit;
apply_caps = 1;
caps = val;
}
else if (strncmp(buf, "NoNewPrivs:", 11) == 0) {
- char *ptr = buf + 11;
int val;
- if (sscanf(ptr, "%d", &val) != 1)
+ if (sscanf(buf + 11, "%d", &val) != 1)
goto errexit;
if (val)
arg_nonewprivs = 1;
}
}
fclose(fp);
- free(file);
return;
errexit:
- fprintf(stderr, "Error: cannot read stat file for process %u\n", pid);
+ fprintf(stderr, "Error: cannot read /proc/%d/status\n", process_get_pid(sandbox));
exit(1);
}
-static void extract_user_namespace(pid_t pid) {
+static void extract_user_namespace(ProcessHandle sandbox) {
// test user namespaces available in the kernel
- struct stat s1;
- struct stat s2;
- struct stat s3;
- if (stat("/proc/self/ns/user", &s1) == 0 &&
- stat("/proc/self/uid_map", &s2) == 0 &&
- stat("/proc/self/gid_map", &s3) == 0);
- else
+ struct stat self_userns;
+ if (stat("/proc/self/ns/user", &self_userns) != 0)
return;
- // read uid map
- char *uidmap;
- if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1)
- errExit("asprintf");
- FILE *fp = fopen(uidmap, "re");
- if (!fp) {
- free(uidmap);
- return;
- }
+ // check sandbox user namespace
+ struct stat dest_userns;
+ process_stat(sandbox, "ns/user", &dest_userns);
- // check uid map
- int u1;
- int u2;
- if (fscanf(fp, "%d %d", &u1, &u2) == 2) {
- if (arg_debug)
- printf("User namespace detected: %s, %d, %d\n", uidmap, u1, u2);
- if (u1 != 0 || u2 != 0)
- arg_noroot = 1;
- }
+ if (dest_userns.st_ino != self_userns.st_ino ||
+ dest_userns.st_dev != self_userns.st_dev)
+ arg_noroot = 1;
+}
+
+static void extract_cpu(ProcessHandle sandbox) {
+ int fd = process_rootfs_open(sandbox, RUN_CPU_CFG);
+ if (fd < 0)
+ return; // not configured
+
+ FILE *fp = fdopen(fd, "r");
+ if (!fp)
+ errExit("fdopen");
+
+ unsigned tmp;
+ if (fscanf(fp, "%x", &tmp) == 1)
+ cfg.cpus = (uint32_t) tmp;
fclose(fp);
- free(uidmap);
}
-static void extract_umask(pid_t pid) {
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_UMASK_FILE) == -1)
- errExit("asprintf");
+static void extract_cgroup(ProcessHandle sandbox) {
+ int fd = process_rootfs_open(sandbox, RUN_CGROUP_CFG);
+ if (fd < 0)
+ return; // not configured
- FILE *fp = fopen(fname, "re");
- free(fname);
- if (!fp) {
- fprintf(stderr, "Error: cannot open umask file\n");
- exit(1);
- }
- if (fscanf(fp, "%3o", &orig_umask) != 1) {
- fprintf(stderr, "Error: cannot read umask\n");
- exit(1);
+ FILE *fp = fdopen(fd, "r");
+ if (!fp)
+ errExit("fdopen");
+
+ char buf[BUFLEN];
+ if (fgets(buf, BUFLEN, fp)) {
+ cfg.cgroup = strdup(buf);
+ if (!cfg.cgroup)
+ errExit("strdup");
}
fclose(fp);
}
-static int open_shell(void) {
- EUID_ASSERT();
- assert(cfg.shell);
-
- if (arg_debug)
- printf("Opening shell %s\n", cfg.shell);
- // file descriptor will leak if not opened with O_CLOEXEC !!
- int fd = open(cfg.shell, O_PATH|O_CLOEXEC);
- if (fd == -1) {
- fprintf(stderr, "Error: cannot open shell %s\n", cfg.shell);
+static void extract_umask(ProcessHandle sandbox) {
+ int fd = process_rootfs_open(sandbox, RUN_UMASK_FILE);
+ if (fd < 0) {
+ fprintf(stderr, "Error: cannot open umask file\n");
exit(1);
}
- // pass file descriptor through to the final fexecve
- if (asprintf(&cfg.keep_fd, "%s,%d", cfg.keep_fd ? cfg.keep_fd : "", fd) == -1)
- errExit("asprintf");
+ FILE *fp = fdopen(fd, "r");
+ if (!fp)
+ errExit("fdopen");
- return fd;
+ if (fscanf(fp, "%3o", &orig_umask) != 1) {
+ fprintf(stderr, "Error: cannot read umask\n");
+ exit(1);
+ }
+ fclose(fp);
}
-// return false if the sandbox identified by pid is not fully set up yet or if
-// it is no firejail sandbox at all, return true if the sandbox is complete
-bool is_ready_for_join(const pid_t pid) {
- EUID_ASSERT();
+// returns false if the sandbox is not fully set up yet,
+// or true if the sandbox is complete
+static bool has_join_file(ProcessHandle sandbox) {
// check if a file /run/firejail/mnt/join exists
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_JOIN_FILE) == -1)
- errExit("asprintf");
- EUID_ROOT();
- int fd = open(fname, O_RDONLY|O_CLOEXEC);
- EUID_USER();
- free(fname);
+ int fd = process_rootfs_open(sandbox, RUN_JOIN_FILE);
if (fd == -1)
return false;
struct stat s;
@@ -354,84 +264,183 @@ bool is_ready_for_join(const pid_t pid) {
}
#define SNOOZE 10000 // sleep interval in microseconds
-void check_join_permission(pid_t pid) {
+static void check_joinable(ProcessHandle sandbox) {
// check if pid belongs to a fully set up firejail sandbox
unsigned long i;
- for (i = SNOOZE; is_ready_for_join(pid) == false; i += SNOOZE) { // give sandbox some time to start up
+ for (i = SNOOZE; has_join_file(sandbox) == false; i += SNOOZE) { // give sandbox some time to start up
if (i > join_timeout) {
fprintf(stderr, "Error: no valid sandbox\n");
exit(1);
}
usleep(SNOOZE);
}
- // check privileges for non-root users
- uid_t uid = getuid();
- if (uid != 0) {
- uid_t sandbox_uid = pid_get_uid(pid);
- if (uid != sandbox_uid) {
- fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n");
- exit(1);
- }
+}
+
+static ProcessHandle find_pidns_parent(pid_t pid) {
+ // identify current pid namespace
+ struct stat self_pidns;
+ if (stat("/proc/self/ns/pid", &self_pidns) < 0)
+ errExit("stat");
+
+ ProcessHandle process = pin_process(pid);
+
+ // in case pid is member of a different pid namespace
+ // find parent who created that namespace
+ while (1) {
+ struct stat dest_pidns;
+ process_stat(process, "ns/pid", &dest_pidns);
+
+ if (dest_pidns.st_ino == self_pidns.st_ino &&
+ dest_pidns.st_dev == self_pidns.st_dev)
+ break; // always true for init process
+
+ // next parent process
+ ProcessHandle next = pin_parent_process(process);
+ unpin_process(process);
+ process = next;
}
+
+ return process;
}
-pid_t switch_to_child(pid_t pid) {
- EUID_ASSERT();
- EUID_ROOT();
- pid_t rv = pid;
- errno = 0;
- char *comm = pid_proc_comm(pid);
- if (!comm) {
- if (errno == ENOENT)
- fprintf(stderr, "Error: cannot find process with pid %d\n", pid);
- else
- fprintf(stderr, "Error: cannot read /proc file\n");
+static void check_firejail_comm(ProcessHandle process) {
+ // open /proc/pid/comm
+ // note: comm value is under control of the target process
+ FILE *fp = process_fopen(process, "comm");
+
+ char comm[16];
+ if (fscanf(fp, "%15s", comm) != 1) {
+ fprintf(stderr, "Error: cannot read /proc file\n");
exit(1);
}
- EUID_USER();
+ fclose(fp);
- if (strcmp(comm, "firejail") == 0) {
- if (find_child(pid, &rv) == 1) {
- fprintf(stderr, "Error: no valid sandbox\n");
- exit(1);
- }
- fmessage("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) rv);
+ if (strcmp(comm, "firejail") != 0) {
+ fprintf(stderr, "Error: no valid sandbox\n");
+ exit(1);
}
- free(comm);
- return rv;
+
+ return;
}
+static void check_firejail_credentials(ProcessHandle process) {
+ // open /proc/pid/status
+ FILE *fp = process_fopen(process, "status");
+ uid_t ruid = -1;
+ uid_t suid = -1;
+ char buf[4096];
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (sscanf(buf, "Uid: %u %*u %u", &ruid, &suid) == 2)
+ break;
+ }
+ fclose(fp);
-void join(pid_t pid, int argc, char **argv, int index) {
+ // target process should be privileged and owned by the user
+ if (suid != 0)
+ goto errexit;
+ uid_t u = getuid();
+ if (ruid != u && u != 0)
+ goto errexit;
+
+ return;
+
+errexit:
+ fprintf(stderr, "Error: no valid sandbox\n");
+ exit(1);
+}
+
+static pid_t read_sandbox_pidfile(pid_t parent) {
+ char *pidfile;
+ if (asprintf(&pidfile, "%s/%d", RUN_FIREJAIL_SANDBOX_DIR, parent) == -1)
+ errExit("asprintf");
+
+ // open the pidfile
+ EUID_ROOT();
+ int pidfile_fd = open(pidfile, O_RDWR|O_CLOEXEC);
+ free(pidfile);
+ EUID_USER();
+ if (pidfile_fd < 0)
+ goto errexit;
+
+ // assume pidfile is outdated if parent doesn't hold a lock
+ struct flock pidfile_lock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0,
+ .l_pid = 0,
+ };
+ if (fcntl(pidfile_fd, F_GETLK, &pidfile_lock) < 0)
+ errExit("fcntl");
+ if (pidfile_lock.l_type == F_UNLCK)
+ goto errexit;
+ if (pidfile_lock.l_pid != parent)
+ goto errexit;
+
+ // read pidfile
+ pid_t sandbox;
+ FILE *fp = fdopen(pidfile_fd, "r");
+ if (!fp)
+ errExit("fdopen");
+ if (fscanf(fp, "%d", &sandbox) != 1)
+ goto errexit;
+ fclose(fp);
+
+ return sandbox;
+
+errexit:
+ fprintf(stderr, "Error: no valid sandbox\n");
+ exit(1);
+}
+
+static ProcessHandle switch_to_sandbox(ProcessHandle parent) {
+ // firejail forks many children, identify the sandbox child
+ // using a pidfile created by the sandbox parent
+ pid_t pid = read_sandbox_pidfile(process_get_pid(parent));
+
+ // pin the sandbox child
+ fmessage("Switching to pid %d, the first child process inside the sandbox\n", pid);
+ ProcessHandle sandbox = pin_child_process(parent, pid);
+
+ return sandbox;
+}
+
+ProcessHandle pin_sandbox_process(pid_t pid) {
EUID_ASSERT();
- pid_t parent = pid;
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle parent = find_pidns_parent(pid);
+ check_firejail_comm(parent);
+ check_firejail_credentials(parent);
+
+ ProcessHandle sandbox = switch_to_sandbox(parent);
+ check_joinable(sandbox);
+
+ unpin_process(parent);
+ return sandbox;
+}
- // exit if no permission to join the sandbox
- check_join_permission(pid);
- extract_x11_display(parent);
+
+void join(pid_t pid, int argc, char **argv, int index) {
+ EUID_ASSERT();
+ ProcessHandle sandbox = pin_sandbox_process(pid);
+
+ extract_x11_display(pid);
int shfd = -1;
if (!arg_shell_none)
shfd = open_shell();
- EUID_ROOT();
// in user mode set caps seccomp, cpu, cgroup, etc
if (getuid() != 0) {
- extract_nonewprivs(pid); // redundant on Linux >= 4.10; duplicated in function extract_caps
- extract_caps(pid);
- extract_cpu(pid);
- extract_cgroup(pid);
- extract_nogroups(pid);
- extract_user_namespace(pid);
- extract_umask(pid);
-#ifdef HAVE_APPARMOR
- extract_apparmor(pid);
-#endif
+ extract_nonewprivs(sandbox); // redundant on Linux >= 4.10; duplicated in function extract_caps
+ extract_caps(sandbox);
+ extract_cpu(sandbox);
+ extract_cgroup(sandbox);
+ extract_nogroups(sandbox);
+ extract_user_namespace(sandbox);
+ extract_umask(sandbox);
}
// set cgroup
@@ -439,20 +448,21 @@ void join(pid_t pid, int argc, char **argv, int index) {
set_cgroup(cfg.cgroup, getpid());
// join namespaces
+ EUID_ROOT();
if (arg_join_network) {
- if (join_namespace(pid, "net"))
+ if (process_join_namespace(sandbox, "net"))
exit(1);
}
else if (arg_join_filesystem) {
- if (join_namespace(pid, "mnt"))
+ if (process_join_namespace(sandbox, "mnt"))
exit(1);
}
else {
- if (join_namespace(pid, "ipc") ||
- join_namespace(pid, "net") ||
- join_namespace(pid, "pid") ||
- join_namespace(pid, "uts") ||
- join_namespace(pid, "mnt"))
+ if (process_join_namespace(sandbox, "ipc") ||
+ process_join_namespace(sandbox, "net") ||
+ process_join_namespace(sandbox, "pid") ||
+ process_join_namespace(sandbox, "uts") ||
+ process_join_namespace(sandbox, "mnt"))
exit(1);
}
@@ -463,42 +473,25 @@ void join(pid_t pid, int argc, char **argv, int index) {
// drop discretionary access control capabilities for root sandboxes
caps_drop_dac_override();
- // chroot into /proc/PID/root directory
- char *rootdir;
- if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
- errExit("asprintf");
-
- int rv;
if (!arg_join_network) {
- rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option
- if (rv == 0)
- printf("changing root to %s\n", rootdir);
- }
+ // mount namespace doesn't know about --chroot
+ fmessage("Changing root to /proc/%d/root\n", process_get_pid(sandbox));
+ process_rootfs_chroot(sandbox);
- EUID_USER();
- if (chdir("/") < 0)
- errExit("chdir");
- if (cfg.homedir) {
- struct stat s;
- if (stat(cfg.homedir, &s) == 0) {
- /* coverity[toctou] */
- if (chdir(cfg.homedir) < 0)
- errExit("chdir");
- }
+ // load seccomp filters
+ if (getuid() != 0)
+ seccomp_load_file_list();
}
// set caps filter
- EUID_ROOT();
if (apply_caps == 1) // not available for uid 0
caps_set(caps);
- if (getuid() != 0)
- seccomp_load_file_list();
- // mount user namespace or drop privileges
+ // user namespace
if (arg_noroot) { // not available for uid 0
if (arg_debug)
printf("Joining user namespace\n");
- if (join_namespace(1, "user"))
+ if (process_join_namespace(sandbox, "user"))
exit(1);
// user namespace resets capabilities
@@ -506,6 +499,8 @@ void join(pid_t pid, int argc, char **argv, int index) {
if (apply_caps == 1) // not available for uid 0
caps_set(caps);
}
+ EUID_USER();
+ unpin_process(sandbox);
// set nonewprivs
if (arg_nonewprivs == 1) { // not available for uid 0
@@ -514,7 +509,6 @@ void join(pid_t pid, int argc, char **argv, int index) {
printf("NO_NEW_PRIVS set\n");
}
- EUID_USER();
int cwd = 0;
if (cfg.cwd) {
if (chdir(cfg.cwd) == 0)
@@ -579,6 +573,8 @@ void join(pid_t pid, int argc, char **argv, int index) {
__builtin_unreachable();
}
EUID_USER();
+ unpin_process(sandbox);
+
if (shfd != -1)
close(shfd);
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index 4156a7b250..5ffd468a43 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -286,11 +286,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
EUID_ASSERT();
assert(path1);
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
-
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
// expand paths
char *fname1 = expand_path(path1);
@@ -337,18 +333,9 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
op = SANDBOX_FS_CAT;
}
- // sandbox root directory
- char *rootdir;
- if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
- errExit("asprintf");
-
if (op == SANDBOX_FS_LS || op == SANDBOX_FS_CAT) {
- EUID_ROOT();
- // chroot
- if (chroot(rootdir) < 0)
- errExit("chroot");
- if (chdir("/") < 0)
- errExit("chdir");
+ // chroot into the sandbox
+ process_rootfs_chroot(sandbox);
// drop privileges
drop_privs(0);
@@ -410,11 +397,8 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
if (child < 0)
errExit("fork");
if (child == 0) {
- // chroot
- if (chroot(rootdir) < 0)
- errExit("chroot");
- if (chdir("/") < 0)
- errExit("chdir");
+ // chroot into the sandbox
+ process_rootfs_chroot(sandbox);
// drop privileges
drop_privs(0);
@@ -442,10 +426,6 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
EUID_USER();
}
- if (fname2)
- free(fname2);
- free(fname1);
- free(rootdir);
-
+ unpin_process(sandbox);
exit(0);
}
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 7344be23a2..7081e067f4 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1011,6 +1011,7 @@ int main(int argc, char **argv, char **envp) {
int prog_index = -1; // index in argv where the program command starts
int lockfd_network = -1;
int lockfd_directory = -1;
+ int lockfd_sandboxfile = -1;
int option_cgroup = 0;
int custom_profile = 0; // custom profile loaded
int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot)
@@ -3119,6 +3120,9 @@ int main(int argc, char **argv, char **envp) {
errExit("clone");
EUID_USER();
+ // sandbox pidfile
+ lockfd_sandboxfile = set_sandbox_run_file(getpid(), child);
+
if (!arg_command && !arg_quiet) {
fmessage("Parent pid %u, child pid %u\n", sandbox_pid, child);
// print the path of the new log directory
@@ -3301,12 +3305,28 @@ int main(int argc, char **argv, char **envp) {
// lock netfilter firewall
if (arg_netlock) {
- char *cmd;
- if (asprintf(&cmd, "firejail --netlock=%d&", getpid()) == -1)
- errExit("asprintf");
- int rv = system(cmd);
- (void) rv;
- free(cmd);
+ pid_t netlock_child = fork();
+ if (netlock_child < 0)
+ errExit("fork");
+ if (netlock_child == 0) {
+ close_all(NULL, 0);
+ // drop privileges
+ if (setresgid(-1, getgid(), getgid()) != 0)
+ errExit("setresgid");
+ if (setresuid(-1, getuid(), getuid()) != 0)
+ errExit("setresuid");
+
+ char arg[64];
+ snprintf(arg, sizeof(arg), "--netlock=%d", getpid());
+
+ char *cmd[3];
+ cmd[0] = BINDIR "/firejail";
+ cmd[1] = arg;
+ cmd[2] = NULL;
+ execvp(cmd[0], cmd);
+ perror("Cannot start netlock");
+ _exit(1);
+ }
}
int status = 0;
@@ -3326,7 +3346,8 @@ int main(int argc, char **argv, char **envp) {
// end of signal-safe code
//*****************************
-
+ // release lock
+ close(lockfd_sandboxfile);
if (WIFEXITED(status)){
myexit(WEXITSTATUS(status));
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c
index dd66ecc55c..46ddf269e8 100644
--- a/src/firejail/network_main.c
+++ b/src/firejail/network_main.c
@@ -271,44 +271,25 @@ void net_check_cfg(void) {
#define MAXBUF 4096
void net_dns_print(pid_t pid) {
EUID_ASSERT();
- // drop privileges - will not be able to read /etc/resolv.conf for --noroot option
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ // chroot in the sandbox
+ process_rootfs_chroot(sandbox);
+ unpin_process(sandbox);
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ drop_privs(0);
- EUID_ROOT();
- if (join_namespace(pid, "mnt"))
+ // read /etc/resolv.conf
+ FILE *fp = fopen("/etc/resolv.conf", "re");
+ if (!fp) {
+ fprintf(stderr, "Error: cannot read /etc/resolv.conf\n");
exit(1);
-
- pid_t child = fork();
- if (child < 0)
- errExit("fork");
- if (child == 0) {
- caps_drop_all();
- if (chdir("/") < 0)
- errExit("chdir");
-
- // access /etc/resolv.conf
- FILE *fp = fopen("/etc/resolv.conf", "re");
- if (!fp) {
- fprintf(stderr, "Error: cannot access /etc/resolv.conf\n");
- exit(1);
- }
-
- char buf[MAXBUF];
- while (fgets(buf, MAXBUF, fp))
- printf("%s", buf);
- printf("\n");
- fclose(fp);
- exit(0);
}
- // wait for the child to finish
- waitpid(child, NULL, 0);
- flush_stdin();
+ char buf[MAXBUF];
+ while (fgets(buf, MAXBUF, fp))
+ printf("%s", buf);
+
exit(0);
}
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index 0517f3506a..c117150b88 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -38,6 +38,12 @@ void preproc_build_firejail_dir(void) {
create_empty_dir_as_root(RUN_FIREJAIL_DIR, 0755);
}
+ // restricted search permission
+ // only root should be able to lock files in this directory
+ if (stat(RUN_FIREJAIL_SANDBOX_DIR, &s)) {
+ create_empty_dir_as_root(RUN_FIREJAIL_SANDBOX_DIR, 0700);
+ }
+
if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) {
create_empty_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755);
}
diff --git a/src/firejail/process.c b/src/firejail/process.c
new file mode 100644
index 0000000000..5adb4f8cc2
--- /dev/null
+++ b/src/firejail/process.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2014-2022 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.
+ */
+#include "firejail.h"
+#include <errno.h>
+#include <signal.h>
+
+#include <fcntl.h>
+#ifndef O_PATH
+#define O_PATH 010000000
+#endif
+
+#include <sys/syscall.h>
+#ifndef __NR_pidfd_send_signal
+#define __NR_pidfd_send_signal 424
+#endif
+
+#define BUFLEN 4096
+
+struct processhandle_instance_t {
+ pid_t pid;
+ int fd; // file descriptor referring to /proc/[PID]
+};
+
+
+ProcessHandle pin_process(pid_t pid) {
+ EUID_ASSERT();
+
+ ProcessHandle rv = malloc(sizeof(struct processhandle_instance_t));
+ if (!rv)
+ errExit("malloc");
+ rv->pid = pid;
+
+ char proc[64];
+ snprintf(proc, sizeof(proc), "/proc/%d", pid);
+
+ EUID_ROOT();
+ int fd = open(proc, O_RDONLY|O_CLOEXEC);
+ EUID_USER();
+ if (fd < 0) {
+ if (errno == ENOENT)
+ fprintf(stderr, "Error: cannot find process with pid %d\n", pid);
+ else
+ fprintf(stderr, "Error: cannot open %s: %s\n", proc, strerror(errno));
+ exit(1);
+ }
+ rv->fd = fd;
+
+ return rv;
+}
+
+void unpin_process(ProcessHandle process) {
+ close(process->fd);
+ free(process);
+}
+
+pid_t process_get_pid(ProcessHandle process) {
+ return process->pid;
+}
+
+int process_get_fd(ProcessHandle process) {
+ return process->fd;
+}
+
+/*********************************************
+ * access path in proc filesystem
+ *********************************************/
+
+int process_stat_nofail(ProcessHandle process, const char *fname, struct stat *s) {
+ EUID_ASSERT();
+ assert(fname[0] != '/');
+
+ EUID_ROOT();
+ int rv = fstatat(process_get_fd(process), fname, s, 0);
+ EUID_USER();
+
+ return rv;
+}
+
+int process_stat(ProcessHandle process, const char *fname, struct stat *s) {
+ int rv = process_stat_nofail(process, fname, s);
+ if (rv) {
+ fprintf(stderr, "Error: cannot stat /proc/%d/%s: %s\n", process->pid, fname, strerror(errno));
+ exit(1);
+ }
+
+ return rv;
+}
+
+int process_open_nofail(ProcessHandle process, const char *fname) {
+ EUID_ASSERT();
+ assert(fname[0] != '/');
+
+ EUID_ROOT();
+ int rv = openat(process_get_fd(process), fname, O_RDONLY|O_CLOEXEC);
+ EUID_USER();
+
+ return rv;
+}
+
+int process_open(ProcessHandle process, const char *fname) {
+ int rv = process_open_nofail(process, fname);
+ if (rv < 0) {
+ fprintf(stderr, "Error: cannot open /proc/%d/%s: %s\n", process->pid, fname, strerror(errno));
+ exit(1);
+ }
+
+ return rv;
+}
+
+FILE *process_fopen(ProcessHandle process, const char *fname) {
+ int fd = process_open(process, fname);
+ FILE *rv = fdopen(fd, "r");
+ if (!rv)
+ errExit("fdopen");
+
+ return rv;
+}
+
+int process_join_namespace(ProcessHandle process, char *type) {
+ return join_namespace_by_fd(process_get_fd(process), type);
+}
+
+/*********************************************
+ * sending a signal
+ *********************************************/
+
+void process_send_signal(ProcessHandle process, int signum) {
+ fmessage("Sending signal %d to pid %d\n", signum, process_get_pid(process));
+
+ if (syscall(__NR_pidfd_send_signal, process_get_fd(process), signum, NULL, 0) == -1 && errno == ENOSYS)
+ kill(process_get_pid(process), signum);
+}
+
+/*********************************************
+ * parent and child process
+ *********************************************/
+
+static pid_t process_parent_pid(ProcessHandle process) {
+ pid_t rv = 0;
+
+ FILE *fp = process_fopen(process, "status");
+ char buf[BUFLEN];
+ while (fgets(buf, BUFLEN, fp)) {
+ if (sscanf(buf, "PPid: %d", &rv) == 1)
+ break;
+ }
+ fclose(fp);
+
+ return rv;
+}
+
+static ProcessHandle pin_process_relative_to(ProcessHandle process, pid_t pid) {
+ ProcessHandle rv = malloc(sizeof(struct processhandle_instance_t));
+ if (!rv)
+ errExit("malloc");
+ rv->pid = pid;
+
+ char proc[64];
+ snprintf(proc, sizeof(proc), "../%d", pid);
+
+ rv->fd = process_open(process, proc);
+
+ return rv;
+}
+
+ProcessHandle pin_parent_process(ProcessHandle process) {
+ ProcessHandle parent = pin_process_relative_to(process, process_parent_pid(process));
+ return parent;
+}
+
+ProcessHandle pin_child_process(ProcessHandle process, pid_t child_pid) {
+ ProcessHandle child = pin_process_relative_to(process, child_pid);
+
+ // verify parent/child relationship
+ if (process_parent_pid(child) != process_get_pid(process)) {
+ fprintf(stderr, "Error: cannot find child process of pid %d\n", process_get_pid(process));
+ exit(1);
+ }
+
+ return child;
+}
+
+/*********************************************
+ * access process rootfs
+ *********************************************/
+
+void process_rootfs_chroot(ProcessHandle process) {
+ if (fchdir(process_get_fd(process)) < 0)
+ errExit("fchdir");
+
+ int called_as_root = 0;
+ if (geteuid() == 0)
+ called_as_root = 1;
+ if (called_as_root == 0)
+ EUID_ROOT();
+
+ if (chroot("root") < 0)
+ errExit("chroot");
+
+ if (called_as_root == 0)
+ EUID_USER();
+
+ if (chdir("/") < 0)
+ errExit("chdir");
+}
+
+int process_rootfs_stat(ProcessHandle process, const char *fname, struct stat *s) {
+ char *proc;
+ if (asprintf(&proc, "root%s", fname) < 0)
+ errExit("asprintf");
+
+ int rv = process_stat_nofail(process, proc, s);
+
+ free(proc);
+ return rv;
+}
+
+int process_rootfs_open(ProcessHandle process, const char *fname) {
+ char *proc;
+ if (asprintf(&proc, "root%s", fname) < 0)
+ errExit("asprintf");
+
+ int rv = process_open_nofail(process, proc);
+
+ free(proc);
+ return rv;
+}
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c
index 37e541f509..37782b756a 100644
--- a/src/firejail/protocol.c
+++ b/src/firejail/protocol.c
@@ -63,27 +63,23 @@ void protocol_print_filter(pid_t pid) {
(void) pid;
#ifdef SYS_socket
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ // chroot in the sandbox
+ process_rootfs_chroot(sandbox);
+ unpin_process(sandbox);
// find the seccomp filter
- EUID_ROOT();
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_PROTOCOL_CFG) == -1)
- errExit("asprintf");
-
struct stat s;
- if (stat(fname, &s) == -1) {
+ if (stat(RUN_PROTOCOL_CFG, &s) != 0) {
printf("Cannot access seccomp filter.\n");
exit(1);
}
// read and print the filter
- protocol_filter_load(fname);
- free(fname);
+ EUID_ROOT();
+ protocol_filter_load(RUN_PROTOCOL_CFG);
+
if (cfg.protocol)
printf("%s\n", cfg.protocol);
exit(0);
diff --git a/src/firejail/run_files.c b/src/firejail/run_files.c
index c971a4f53d..8b8bbae121 100644
--- a/src/firejail/run_files.c
+++ b/src/firejail/run_files.c
@@ -20,8 +20,18 @@
#include "firejail.h"
#include "../include/pid.h"
+#include <fcntl.h>
#define BUFLEN 4096
+static void delete_sandbox_run_file(pid_t pid) {
+ char *fname;
+ if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_SANDBOX_DIR, pid) == -1)
+ errExit("asprintf");
+ int rv = unlink(fname);
+ (void) rv;
+ free(fname);
+}
+
static void delete_x11_run_file(pid_t pid) {
char *fname;
if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1)
@@ -68,6 +78,7 @@ static void delete_network_run_file(pid_t pid) {
void delete_run_files(pid_t pid) {
+ delete_sandbox_run_file(pid);
delete_bandwidth_run_file(pid);
delete_network_run_file(pid);
delete_name_run_file(pid);
@@ -152,3 +163,44 @@ void set_profile_run_file(pid_t pid, const char *fname) {
EUID_USER();
free(runfile);
}
+
+int set_sandbox_run_file(pid_t pid, pid_t child) {
+ char *runfile;
+ if (asprintf(&runfile, "%s/%d", RUN_FIREJAIL_SANDBOX_DIR, pid) == -1)
+ errExit("asprintf");
+
+ EUID_ROOT();
+ // the file is deleted first
+ // this file should be opened with O_CLOEXEC set
+ int fd = open(runfile, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ fprintf(stderr, "Error: cannot create %s\n", runfile);
+ exit(1);
+ }
+ free(runfile);
+ EUID_USER();
+
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%d\n", child);
+ size_t len = strlen(buf);
+ size_t done = 0;
+ while (done != len) {
+ ssize_t rv = write(fd, buf + done, len - done);
+ if (rv < 0)
+ errExit("write");
+ done += rv;
+ }
+
+ // set exclusive lock on the file
+ // the lock is never inherited, and is released if this process dies ungracefully
+ struct flock sandboxlock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0,
+ };
+ if (fcntl(fd, F_SETLK, &sandboxlock) < 0)
+ errExit("fcntl");
+
+ return fd;
+}
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c
index 9fcf74c02f..e8959f2634 100644
--- a/src/firejail/seccomp.c
+++ b/src/firejail/seccomp.c
@@ -21,6 +21,7 @@
#include "firejail.h"
#include "../include/seccomp.h"
#include <sys/mman.h>
+#include <sys/wait.h>
typedef struct filter_list {
struct filter_list *next;
@@ -425,26 +426,20 @@ int seccomp_filter_mdwx(bool native) {
void seccomp_print_filter(pid_t pid) {
EUID_ASSERT();
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid = switch_to_child(pid);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // exit if no permission to join the sandbox
- check_join_permission(pid);
+ // chroot in the sandbox
+ process_rootfs_chroot(sandbox);
+ unpin_process(sandbox);
- // find the seccomp list file
- EUID_ROOT();
- char *fname;
- if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_LIST) == -1)
- errExit("asprintf");
-
- int fd = open(fname, O_RDONLY|O_CLOEXEC);
- if (fd < 0)
- goto errexit;
+ drop_privs(0);
- FILE *fp = fdopen(fd, "r");
- if (!fp)
- goto errexit;
- free(fname);
+ // find the seccomp list file
+ FILE *fp = fopen(RUN_SECCOMP_LIST, "re");
+ if (!fp) {
+ printf("Cannot access seccomp filter.\n");
+ exit(1);
+ }
char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp)) {
@@ -453,21 +448,21 @@ void seccomp_print_filter(pid_t pid) {
if (ptr)
*ptr = '\0';
- if (asprintf(&fname, "/proc/%d/root%s", pid, buf) == -1)
- errExit("asprintf");
- printf("FILE: %s\n", fname); fflush(0);
+ printf("FILE: %s\n", buf); fflush(0);
- // read and print the filter - run this as root, the user doesn't have access
- sbox_run(SBOX_ROOT | SBOX_SECCOMP, 2, PATH_FSEC_PRINT, fname);
- fflush(0);
+ // read and print the filter
+ pid_t child = fork();
+ if (child < 0)
+ errExit("fork");
+ if (child == 0) {
+ execl(PATH_FSEC_PRINT, PATH_FSEC_PRINT, buf, NULL);
+ errExit("execl");
+ }
+ waitpid(child, NULL, 0);
printf("\n"); fflush(0);
- free(fname);
}
fclose(fp);
- exit(0);
-errexit:
- printf("Cannot access seccomp filter.\n");
- exit(1);
+ exit(0);
}
diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c
index 44fdd58abd..fb1ddef73b 100644
--- a/src/firejail/shutdown.c
+++ b/src/firejail/shutdown.c
@@ -18,85 +18,43 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "firejail.h"
-#include <sys/stat.h>
-#include <sys/wait.h>
#include <fcntl.h>
-#include <sys/prctl.h>
+#include <signal.h>
void shut(pid_t pid) {
EUID_ASSERT();
- EUID_ROOT();
- char *comm = pid_proc_comm(pid);
- EUID_USER();
- if (comm) {
- if (strcmp(comm, "firejail") != 0) {
- fprintf(stderr, "Error: this is not a firejail sandbox\n");
- exit(1);
- }
- free(comm);
- }
- else {
- fprintf(stderr, "Error: cannot find process %d\n", pid);
- exit(1);
- }
+ ProcessHandle sandbox = pin_sandbox_process(pid);
- // check privileges for non-root users
- uid_t uid = getuid();
- if (uid != 0) {
- uid_t sandbox_uid = pid_get_uid(pid);
- if (uid != sandbox_uid) {
- fprintf(stderr, "Error: permission is denied to shutdown a sandbox created by a different user.\n");
- exit(1);
- }
- }
-
- printf("Sending SIGTERM to %u\n", pid);
- kill(pid, SIGTERM);
+ process_send_signal(sandbox, SIGTERM);
// wait for not more than 11 seconds
int monsec = 11;
- char *monfile;
- if (asprintf(&monfile, "/proc/%d/cmdline", pid) == -1)
- errExit("asprintf");
int killdone = 0;
while (monsec) {
sleep(1);
monsec--;
- EUID_ROOT();
- FILE *fp = fopen(monfile, "re");
- EUID_USER();
- if (!fp) {
+ int monfd = process_open_nofail(sandbox, "cmdline");
+ if (monfd < 0) {
killdone = 1;
break;
}
char c;
- size_t count = fread(&c, 1, 1, fp);
- fclose(fp);
+ ssize_t count = read(monfd, &c, 1);
+ close(monfd);
if (count == 0) {
// all done
killdone = 1;
break;
}
}
- free(monfile);
-
// force SIGKILL
- if (!killdone) {
- // kill the process and its child
- pid_t child;
- if (find_child(pid, &child) == 0) {
- printf("Sending SIGKILL to %u\n", child);
- kill(child, SIGKILL);
- }
- printf("Sending SIGKILL to %u\n", pid);
- kill(pid, SIGKILL);
- }
+ if (!killdone)
+ process_send_signal(sandbox, SIGKILL);
- EUID_ROOT();
- delete_run_files(pid);
+ unpin_process(sandbox);
}
diff --git a/src/firejail/util.c b/src/firejail/util.c
index eb7f056248..a35ad469e8 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -41,7 +41,7 @@
#endif
#define MAX_GROUPS 1024
-#define MAXBUF 4098
+#define MAXBUF 4096
#define EMPTY_STRING ("")
@@ -802,77 +802,6 @@ void check_unsigned(const char *str, const char *msg) {
}
-#define BUFLEN 4096
-// find the first child for this parent; return 1 if error
-int find_child(pid_t parent, pid_t *child) {
- EUID_ASSERT();
- *child = 0; // use it to flag a found child
-
- DIR *dir;
- EUID_ROOT(); // grsecurity fix
- if (!(dir = opendir("/proc"))) {
- // sleep 2 seconds and try again
- sleep(2);
- if (!(dir = opendir("/proc"))) {
- fprintf(stderr, "Error: cannot open /proc directory\n");
- exit(1);
- }
- }
-
- struct dirent *entry;
- char *end;
- while (*child == 0 && (entry = readdir(dir))) {
- pid_t pid = strtol(entry->d_name, &end, 10);
- if (end == entry->d_name || *end)
- continue;
- if (pid == parent)
- continue;
-
- // open stat file
- char *file;
- if (asprintf(&file, "/proc/%u/status", pid) == -1) {
- perror("asprintf");
- exit(1);
- }
- FILE *fp = fopen(file, "re");
- if (!fp) {
- free(file);
- continue;
- }
-
- // look for firejail executable name
- char buf[BUFLEN];
- while (fgets(buf, BUFLEN - 1, fp)) {
- if (strncmp(buf, "PPid:", 5) == 0) {
- char *ptr = buf + 5;
- while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
- ptr++;
- }
- if (*ptr == '\0') {
- fprintf(stderr, "Error: cannot read /proc file\n");
- exit(1);
- }
- if (parent == atoi(ptr)) {
- // we don't want /usr/bin/xdg-dbus-proxy!
- char *cmdline = pid_proc_cmdline(pid);
- if (cmdline) {
- if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0)
- *child = pid;
- free(cmdline);
- }
- }
- break; // stop reading the file
- }
- }
- fclose(fp);
- free(file);
- }
- closedir(dir);
- EUID_USER();
- return (*child)? 0:1; // 0 = found, 1 = not found
-}
-
-
void extract_command_name(int index, char **argv) {
EUID_ASSERT();
assert(argv);
@@ -961,14 +890,14 @@ void wait_for_other(int fd) {
//****************************
// wait for the parent to be initialized
//****************************
- char childstr[BUFLEN + 1];
+ char childstr[MAXBUF + 1];
int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (newfd == -1)
errExit("fcntl");
FILE* stream;
stream = fdopen(newfd, "r");
*childstr = '\0';
- if (fgets(childstr, BUFLEN, stream)) {
+ if (fgets(childstr, MAXBUF, stream)) {
// remove \n)
char *ptr = childstr;
while(*ptr !='\0' && *ptr != '\n')
@@ -1020,50 +949,6 @@ void notify_other(int fd) {
fclose(stream);
}
-uid_t pid_get_uid(pid_t pid) {
- EUID_ASSERT();
- uid_t rv = 0;
-
- // open status file
- char *file;
- if (asprintf(&file, "/proc/%u/status", pid) == -1) {
- perror("asprintf");
- exit(1);
- }
- EUID_ROOT(); // grsecurity fix
- FILE *fp = fopen(file, "re");
- if (!fp) {
- free(file);
- fprintf(stderr, "Error: cannot open /proc file\n");
- exit(1);
- }
-
- // extract uid
- static const int PIDS_BUFLEN = 1024;
- char buf[PIDS_BUFLEN];
- while (fgets(buf, PIDS_BUFLEN - 1, fp)) {
- if (strncmp(buf, "Uid:", 4) == 0) {
- char *ptr = buf + 4;
- while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
- ptr++;
- }
- if (*ptr == '\0') {
- fprintf(stderr, "Error: cannot read /proc file\n");
- exit(1);
- }
-
- rv = atoi(ptr);
- break; // break regardless!
- }
- }
-
- fclose(fp);
- free(file);
- EUID_USER(); // grsecurity fix
-
- return rv;
-}
-
gid_t get_group_id(const char *groupname) {
gid_t gid = 0;
@@ -1144,12 +1029,14 @@ void create_empty_dir_as_root(const char *dir, mode_t mode) {
/* coverity[toctou] */
// don't fail if directory already exists. This can be the case in a race
// condition, when two jails launch at the same time. See #1013
- if (mkdir(dir, mode) == -1 && errno != EEXIST)
+ mode_t tmp = umask(~mode); // let's avoid an extra chmod race
+ int rv = mkdir(dir, mode);
+ umask(tmp);
+ if (rv < 0 && errno != EEXIST)
errExit("mkdir");
- if (set_perms(dir, 0, 0, mode))
- errExit("set_perms");
- ASSERT_PERMS(dir, 0, 0, mode);
}
+
+ ASSERT_PERMS(dir, 0, 0, mode);
}
void create_empty_file_as_root(const char *fname, mode_t mode) {
@@ -1163,12 +1050,15 @@ void create_empty_file_as_root(const char *fname, mode_t mode) {
/* coverity[toctou] */
// don't fail if file already exists. This can be the case in a race
// condition, when two jails launch at the same time. Compare to #1013
- FILE *fp = fopen(fname, "we");
- if (!fp)
- errExit("fopen");
- SET_PERMS_STREAM(fp, 0, 0, mode);
- fclose(fp);
+ mode_t tmp = umask(~mode); // let's avoid an extra chmod race
+ int fd = open(fname, O_RDONLY|O_CREAT|O_CLOEXEC, mode);
+ umask(tmp);
+ if (fd < 0)
+ errExit("open");
+ close(fd);
}
+
+ ASSERT_PERMS(fname, 0, 0, mode);
}
// return 1 if error
@@ -1463,8 +1353,8 @@ int has_handler(pid_t pid, int signal) {
EUID_USER();
free(fname);
if (fp) {
- char buf[BUFLEN];
- while (fgets(buf, BUFLEN, fp)) {
+ char buf[MAXBUF];
+ while (fgets(buf, MAXBUF, fp)) {
if (strncmp(buf, "SigCgt:", 7) == 0) {
unsigned long long val;
if (sscanf(buf + 7, "%llx", &val) != 1) {
@@ -1484,11 +1374,7 @@ int has_handler(pid_t pid, int signal) {
}
void enter_network_namespace(pid_t pid) {
- // in case the pid is that of a firejail process, use the pid of the first child process
- pid_t child = switch_to_child(pid);
-
- // exit if no permission to join the sandbox
- check_join_permission(child);
+ ProcessHandle sandbox = pin_sandbox_process(pid);
// check network namespace
char *name;
@@ -1502,10 +1388,11 @@ void enter_network_namespace(pid_t pid) {
// join the namespace
EUID_ROOT();
- if (join_namespace(child, "net")) {
+ if (process_join_namespace(sandbox, "net")) {
fprintf(stderr, "Error: cannot join the network namespace\n");
exit(1);
}
+ unpin_process(sandbox);
}
// return 1 if error, 0 if a valid pid was found
diff --git a/src/include/common.h b/src/include/common.h
index c9640435a2..ed6560701a 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -134,7 +134,8 @@ static inline int mac_not_zero(const unsigned char mac[6]) {
void timetrace_start(void);
float timetrace_end(void);
-int join_namespace(pid_t pid, char *type);
+int join_namespace_by_fd(int dirfd, char *typestr);
+int join_namespace(pid_t pid, char *typestr);
int name2pid(const char *name, pid_t *pid);
char *pid_proc_comm(const pid_t pid);
char *pid_proc_cmdline(const pid_t pid);
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index 4ba3e27f4d..2f6b474612 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -23,6 +23,7 @@
// filesystem
#define RUN_FIREJAIL_BASEDIR "/run"
#define RUN_FIREJAIL_DIR RUN_FIREJAIL_BASEDIR "/firejail"
+#define RUN_FIREJAIL_SANDBOX_DIR RUN_FIREJAIL_DIR "/sandbox"
#define RUN_FIREJAIL_APPIMAGE_DIR RUN_FIREJAIL_DIR "/appimage"
#define RUN_FIREJAIL_NAME_DIR RUN_FIREJAIL_DIR "/name" // also used in src/lib/pid.c - todo: move it in a common place
#define RUN_FIREJAIL_LIB_DIR RUN_FIREJAIL_DIR "/lib"
diff --git a/src/lib/common.c b/src/lib/common.c
index 8e84fab262..111366782e 100644
--- a/src/lib/common.c
+++ b/src/lib/common.c
@@ -22,7 +22,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
-#include <fcntl.h>
#include <sys/syscall.h>
#include <errno.h>
#include <unistd.h>
@@ -32,32 +31,94 @@
#include <string.h>
#include <time.h>
#include <limits.h>
+#include <sched.h>
#include "../include/common.h"
+
+#include <fcntl.h>
+#ifndef O_PATH
+#define O_PATH 010000000
+#endif
+
+#include <sys/ioctl.h>
+#ifndef NSIO
+#define NSIO 0xb7
+#endif
+#ifndef NS_GET_USERNS
+#define NS_GET_USERNS _IO(NSIO, 0x1)
+#endif
+
#define BUFLEN 4096
-int join_namespace(pid_t pid, char *type) {
+
+int join_namespace_by_fd(int dirfd, char *typestr) {
+ int type;
+ if (strcmp(typestr, "net") == 0)
+ type = CLONE_NEWNET;
+ else if (strcmp(typestr, "mnt") == 0)
+ type = CLONE_NEWNS;
+ else if (strcmp(typestr, "ipc") == 0)
+ type = CLONE_NEWIPC;
+ else if (strcmp(typestr, "pid") == 0)
+ type = CLONE_NEWPID;
+ else if (strcmp(typestr, "uts") == 0)
+ type = CLONE_NEWUTS;
+ else if (strcmp(typestr, "user") == 0)
+ type = CLONE_NEWUSER;
+ else
+ assert(0);
+
char *path;
- if (asprintf(&path, "/proc/%u/ns/%s", pid, type) == -1)
+ if (asprintf(&path, "ns/%s", typestr) == -1)
errExit("asprintf");
- int fd = open(path, O_RDONLY);
+ int fd = openat(dirfd, path, O_RDONLY|O_CLOEXEC);
+ free(path);
if (fd < 0)
goto errout;
- if (syscall(__NR_setns, fd, 0) < 0) {
+ // require that target namespace is owned by
+ // the current user namespace (Linux >= 4.9)
+ struct stat self_userns;
+ if (stat("/proc/self/ns/user", &self_userns) == 0) {
+ int usernsfd = ioctl(fd, NS_GET_USERNS);
+ if (usernsfd != -1) {
+ struct stat dest_userns;
+ if (fstat(usernsfd, &dest_userns) < 0)
+ errExit("fstat");
+ close(usernsfd);
+ if (dest_userns.st_ino != self_userns.st_ino ||
+ dest_userns.st_dev != self_userns.st_dev) {
+ close(fd);
+ goto errout;
+ }
+ }
+ }
+
+ if (syscall(__NR_setns, fd, type) < 0) {
close(fd);
goto errout;
}
close(fd);
- free(path);
return 0;
errout:
- free(path);
- fprintf(stderr, "Error: cannot join namespace %s\n", type);
+ fprintf(stderr, "Error: cannot join namespace %s\n", typestr);
return -1;
+}
+int join_namespace(pid_t pid, char *typestr) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/%d", pid);
+ int fd = open(path, O_PATH|O_CLOEXEC);
+ if (fd < 0) {
+ fprintf(stderr, "Error: cannot open %s: %s\n", path, strerror(errno));
+ exit(1);
+ }
+
+ int rv = join_namespace_by_fd(fd, typestr);
+ close(fd);
+ return rv;
}
// return 1 if error
From dab835e7a0eb287822016f5ae4e87f46e1d363e7 Mon Sep 17 00:00:00 2001
From: smitsohu <smitsohu@gmail.com>
Date: Wed, 8 Jun 2022 15:20:44 +0200
Subject: [PATCH] CVE-2022-31214: fixing the fix
---
src/firejail/fs_etc.c | 6 +++---
src/firejail/process.c | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c
index deaee31bba..e87d3b726c 100644
--- a/src/firejail/fs_etc.c
+++ b/src/firejail/fs_etc.c
@@ -104,7 +104,7 @@ static void build_dirs(char *src, char *dst, size_t src_prefix_len, size_t dst_p
*q = '\0';
*r = '/';
r = q;
- create_empty_dir_as_root(dst, s.st_mode);
+ mkdir_attr(dst, s.st_mode, 0, 0);
}
if (!last) {
// If we're not at the final terminating null, restore
@@ -330,9 +330,9 @@ void fs_rebuild_etc(void) {
symlink_done = 1;
}
else if (S_ISDIR(s.st_mode))
- create_empty_dir_as_root(dest, s.st_mode);
+ create_empty_dir_as_root(dest, S_IRWXU);
else
- create_empty_file_as_root(dest, s.st_mode);
+ create_empty_file_as_root(dest, S_IRUSR | S_IWUSR);
// bind-mount src on top of dest
if (!symlink_done) {
diff --git a/src/firejail/process.c b/src/firejail/process.c
index 5adb4f8cc2..fa6b1394d6 100644
--- a/src/firejail/process.c
+++ b/src/firejail/process.c
@@ -96,7 +96,7 @@ int process_stat_nofail(ProcessHandle process, const char *fname, struct stat *s
int process_stat(ProcessHandle process, const char *fname, struct stat *s) {
int rv = process_stat_nofail(process, fname, s);
if (rv) {
- fprintf(stderr, "Error: cannot stat /proc/%d/%s: %s\n", process->pid, fname, strerror(errno));
+ fprintf(stderr, "Error: cannot stat /proc/%d/%s: %s\n", process_get_pid(process), fname, strerror(errno));
exit(1);
}
@@ -117,7 +117,7 @@ int process_open_nofail(ProcessHandle process, const char *fname) {
int process_open(ProcessHandle process, const char *fname) {
int rv = process_open_nofail(process, fname);
if (rv < 0) {
- fprintf(stderr, "Error: cannot open /proc/%d/%s: %s\n", process->pid, fname, strerror(errno));
+ fprintf(stderr, "Error: cannot open /proc/%d/%s: %s\n", process_get_pid(process), fname, strerror(errno));
exit(1);
}
From 1884ea22a90d225950d81c804f1771b42ae55f54 Mon Sep 17 00:00:00 2001
From: smitsohu <smitsohu@gmail.com>
Date: Wed, 8 Jun 2022 15:42:35 +0200
Subject: [PATCH] CVE-2022-31214: fixing the fix, one more time
the previous commit "CVE-2022-31214: fixing the fix"
made private-etc=fonts,fonts and similar commands
fail with an error
fix that regression by tolerating already existing
directories
---
src/firejail/fs_etc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c
index e87d3b726c..77fa00d6bb 100644
--- a/src/firejail/fs_etc.c
+++ b/src/firejail/fs_etc.c
@@ -104,7 +104,10 @@ static void build_dirs(char *src, char *dst, size_t src_prefix_len, size_t dst_p
*q = '\0';
*r = '/';
r = q;
- mkdir_attr(dst, s.st_mode, 0, 0);
+ if (mkdir(dst, 0700) != 0 && errno != EEXIST)
+ errExit("mkdir");
+ if (chmod(dst, s.st_mode) != 0)
+ errExit("chmod");
}
if (!last) {
// If we're not at the final terminating null, restore