From 02185620d802d5998c5030fc01f5d1f72cde19b4e3fe8b8a6227a4e26c302932 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Tue, 14 Jun 2022 20:25:23 +0000 Subject: [PATCH] - remove patches fix-internet-access.patch and fix-CVE-2022-31214.patch as they are integrated upstream - update to version 0.9.70: - security: CVE-2022-31214 - root escalation in --join logic - Reported by Matthias Gerstner, working exploit code was provided to our - development team. In the same time frame, the problem was independently - reported by Birk Blechschmidt. Full working exploit code was also provided. - feature: enable shell tab completion with --tab (#4936) - feature: disable user profiles at compile time (#4990) - feature: Allow resolution of .local names with avahi-daemon in the apparmor - profile (#5088) - feature: always log seccomp errors (#5110) - feature: firecfg --guide, guided user configuration (#5111) - feature: --oom, kernel OutOfMemory-killer (#5122) - modif: --ids feature needs to be enabled at compile time (#5155) - modif: --nettrace only available to root user - rework: whitelist restructuring (#4985) - rework: firemon, speed up and lots of fixes - bugfix: --private-cwd not expanding macros, broken hyperrogue (#4910) - bugfix: nogroups + wrc prints confusing messages (#4930 #4933) - bugfix: openSUSE Leap - whitelist-run-common.inc (#4954) - bugfix: fix printing in evince (#5011) - bugfix: gcov: fix gcov functions always declared as dummy (#5028) - bugfix: Stop warning on safe supplementary group clean (#5114) - build: remove ultimately unused INSTALL and RANLIB check macros (#5133) - build: mkdeb.sh.in: pass remaining arguments to ./configure (#5154) - ci: replace centos (EOL) with almalinux (#4912) - ci: fix --version not printing compile-time features (#5147) - ci: print version after install & fix apparmor support on build_apparmor - (#5148) OBS-URL: https://build.opensuse.org/package/show/Virtualization/firejail?expand=0&rev=47 --- firejail-0.9.68.tar.xz | 3 - firejail-0.9.68.tar.xz.asc | 11 - firejail-0.9.70.tar.xz | 3 + firejail-0.9.70.tar.xz.asc | 11 + firejail.changes | 42 + firejail.spec | 8 +- fix-CVE-2022-31214.patch | 2244 ------------------------------------ fix-internet-access.patch | 22 - 8 files changed, 57 insertions(+), 2287 deletions(-) delete mode 100644 firejail-0.9.68.tar.xz delete mode 100644 firejail-0.9.68.tar.xz.asc create mode 100644 firejail-0.9.70.tar.xz create mode 100644 firejail-0.9.70.tar.xz.asc delete mode 100644 fix-CVE-2022-31214.patch delete mode 100644 fix-internet-access.patch diff --git a/firejail-0.9.68.tar.xz b/firejail-0.9.68.tar.xz deleted file mode 100644 index ba60bb5..0000000 --- a/firejail-0.9.68.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a322395597d89d2e5ea21fb11cb3f2afc44b00fca5439bf44c7636c5cffa652f -size 477332 diff --git a/firejail-0.9.68.tar.xz.asc b/firejail-0.9.68.tar.xz.asc deleted file mode 100644 index 2ab91e4..0000000 --- a/firejail-0.9.68.tar.xz.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQEzBAABCgAdFiEE+VEWSZX1xABqc0EeLMs2rfxYSacFAmH/zu0ACgkQLMs2rfxY -Saf2WAf/UI98s9MugTAq45CIuxaqzhbbGc435Lwo2NgS2LCYKoJOmes6UdyLPUa1 -aawBImtfqTyOXWrWnKjYBl7fIVATKpP7Ddm2+y6RJ+px/4dRUWNLVqEvka5BLYNS -HrYP84a1vxqeg0LVOMcmD701mTmbT68jwpjD2Ai2ZkiRGXS5KfBWIRL+WR7PAorj -jDxqUSorEF8x316d+0doy9NyeCXS5A1aqTmjnTxZ3RBfkg+Zq33S+x+2ktepdnDH -q/Fv9W4C/GVoXBj6PKtk4JXFUJIeYUYCXE9sq2bpCEAdom5J+EpUMo+42G1/xLYL -mFP0G113+ciMoLWkjJMNQH6KbFjCsQ== -=6MJb ------END PGP SIGNATURE----- diff --git a/firejail-0.9.70.tar.xz b/firejail-0.9.70.tar.xz new file mode 100644 index 0000000..682bb01 --- /dev/null +++ b/firejail-0.9.70.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b77b67a4db7c01d69cb033a50aa7b1132dfaeb2cd97ce6412285235265b71b17 +size 485096 diff --git a/firejail-0.9.70.tar.xz.asc b/firejail-0.9.70.tar.xz.asc new file mode 100644 index 0000000..a088f42 --- /dev/null +++ b/firejail-0.9.70.tar.xz.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- + +iQEzBAABCgAdFiEE+VEWSZX1xABqc0EeLMs2rfxYSacFAmKiC7EACgkQLMs2rfxY +SacmLAf+MhUh5ylaEjCSUKCYJKC7E/OoSYWm2/bRWl3KIeREeL59wxgb3n9ulqGD +MWKHuwetVtRMg5rO7D9LUHUEY80nZw/8iDC8QLzfOsZpS3dQF26Ab4bqaIo/HEQr +9eDk3SIHGqhP62qsAjaGACOOlVDeJXWx5h4M9cTe7VN+IFT7XtN7ytDc23/UZF9o +PmViKz9dyiXX6omt7mVddJx+OBeRUmSsTknmbNafz38aIikoJwivgn3Fc8PxGNzI +lwgHU1Kz4fenTZp2500Cof7rFqQwTdqcZbNIrt1xwQgBF/tdc2Bb4+MkfgiRYhGa +BV/EsPB7vysgGFluZsIY17Ptjc91lw== +=pzNZ +-----END PGP SIGNATURE----- diff --git a/firejail.changes b/firejail.changes index bc0f38f..5c66777 100644 --- a/firejail.changes +++ b/firejail.changes @@ -1,3 +1,45 @@ +------------------------------------------------------------------- +Tue Jun 14 20:21:18 UTC 2022 - Sebastian Wagner + +- remove patches fix-internet-access.patch and fix-CVE-2022-31214.patch + as they are integrated upstream +- update to version 0.9.70: + - security: CVE-2022-31214 - root escalation in --join logic + - Reported by Matthias Gerstner, working exploit code was provided to our + - development team. In the same time frame, the problem was independently + - reported by Birk Blechschmidt. Full working exploit code was also provided. + - feature: enable shell tab completion with --tab (#4936) + - feature: disable user profiles at compile time (#4990) + - feature: Allow resolution of .local names with avahi-daemon in the apparmor + - profile (#5088) + - feature: always log seccomp errors (#5110) + - feature: firecfg --guide, guided user configuration (#5111) + - feature: --oom, kernel OutOfMemory-killer (#5122) + - modif: --ids feature needs to be enabled at compile time (#5155) + - modif: --nettrace only available to root user + - rework: whitelist restructuring (#4985) + - rework: firemon, speed up and lots of fixes + - bugfix: --private-cwd not expanding macros, broken hyperrogue (#4910) + - bugfix: nogroups + wrc prints confusing messages (#4930 #4933) + - bugfix: openSUSE Leap - whitelist-run-common.inc (#4954) + - bugfix: fix printing in evince (#5011) + - bugfix: gcov: fix gcov functions always declared as dummy (#5028) + - bugfix: Stop warning on safe supplementary group clean (#5114) + - build: remove ultimately unused INSTALL and RANLIB check macros (#5133) + - build: mkdeb.sh.in: pass remaining arguments to ./configure (#5154) + - ci: replace centos (EOL) with almalinux (#4912) + - ci: fix --version not printing compile-time features (#5147) + - ci: print version after install & fix apparmor support on build_apparmor + - (#5148) + - docs: Refer to firejail.config in configuration files (#4916) + - docs: firejail.config: add warning about allow-tray (#4946) + - docs: mention that the protocol command accumulates (#5043) + - docs: mention inconsistent homedir bug involving --private=dir (#5052) + - docs: mention capabilities(7) on --caps (#5078) + - new profiles: onionshare, onionshare-cli, opera-developer, songrec + - new profiles: node-gyp, npx, semver, ping-hardened + - removed profiles: nvm + ------------------------------------------------------------------- Wed Jun 8 21:08:03 UTC 2022 - Sebastian Wagner diff --git a/firejail.spec b/firejail.spec index d129126..0806dbf 100644 --- a/firejail.spec +++ b/firejail.spec @@ -17,7 +17,7 @@ Name: firejail -Version: 0.9.68 +Version: 0.9.70 Release: 0 Summary: Linux namepaces sandbox program License: GPL-2.0-only @@ -27,10 +27,6 @@ Source0: https://github.com/netblue30/%{name}/releases/download/%{version Source1: https://github.com/netblue30/%{name}/releases/download/%{version}/%{name}-%{version}.tar.xz.asc # https://firejail.wordpress.com/download-2/ Source2: %{name}.keyring -# PATCH-FIX-UPSTREAM fix-internet-access.patch -- from https://github.com/netblue30/firejail/commit/bb334a8fd4f0911a8dfa1538d02fbd0574b81333.patch -Patch0: fix-internet-access.patch -# PATCH-FIX-UPSTREAM fix-CVE-2022-31214.patch -- from https://github.com/netblue30/firejail/commit/27cde3d7d1e4e16d4190932347c7151dc2a84c50 and https://github.com/netblue30/firejail/commit/dab835e7a0eb287822016f5ae4e87f46e1d363e7.patch and https://github.com/netblue30/firejail/commit/1884ea22a90d225950d81c804f1771b42ae55f54 -Patch1: fix-CVE-2022-31214.patch BuildRequires: fdupes BuildRequires: gcc-c++ BuildRequires: libapparmor-devel @@ -69,8 +65,6 @@ Optional dependency offering zsh completion for firejail %prep %setup -q sed -i '1s/^#!\/usr\/bin\/env /#!\/usr\/bin\//' contrib/fj-mkdeb.py contrib/fjclip.py contrib/fjdisplay.py contrib/fjresize.py contrib/sort.py contrib/fix_private-bin.py contrib/jail_prober.py -%patch0 -p1 -%patch1 -p1 %build %configure --docdir=%{_docdir}/%{name} \ diff --git a/fix-CVE-2022-31214.patch b/fix-CVE-2022-31214.patch deleted file mode 100644 index b7ef72b..0000000 --- a/fix-CVE-2022-31214.patch +++ /dev/null @@ -1,2244 +0,0 @@ -From 27cde3d7d1e4e16d4190932347c7151dc2a84c50 Mon Sep 17 00:00:00 2001 -From: smitsohu -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 - #include - #include -+#include - #include - #include - #include -@@ -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 - #include - #include - #include -@@ -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 - #include - #include -+#include - - #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 -+#include -+ -+#include -+#ifndef O_PATH -+#define O_PATH 010000000 -+#endif -+ -+#include -+#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 - #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 -+#include - - 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 --#include - #include --#include -+#include - - 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 - #include - #include --#include - #include - #include - #include -@@ -32,32 +31,94 @@ - #include - #include - #include -+#include - #include "../include/common.h" -+ -+#include -+#ifndef O_PATH -+#define O_PATH 010000000 -+#endif -+ -+#include -+#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 -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 -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 diff --git a/fix-internet-access.patch b/fix-internet-access.patch deleted file mode 100644 index 7b08547..0000000 --- a/fix-internet-access.patch +++ /dev/null @@ -1,22 +0,0 @@ -From bb334a8fd4f0911a8dfa1538d02fbd0574b81333 Mon Sep 17 00:00:00 2001 -From: netblue30 -Date: Tue, 22 Feb 2022 09:32:46 -0500 -Subject: [PATCH] openSUSE Leap - whitelist-run-common.inc (#4954) - ---- - RELNOTES | 1 + - etc/inc/whitelist-run-common.inc | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/etc/inc/whitelist-run-common.inc b/etc/inc/whitelist-run-common.inc -index d74655a087..26160a10b9 100644 ---- a/etc/inc/whitelist-run-common.inc -+++ b/etc/inc/whitelist-run-common.inc -@@ -7,6 +7,7 @@ whitelist /run/cups/cups.sock - whitelist /run/dbus/system_bus_socket - whitelist /run/media - whitelist /run/resolvconf/resolv.conf -+whitelist /run/netconfig/resolv.conf # openSUSE Leap - whitelist /run/shm - whitelist /run/systemd/journal/dev-log - whitelist /run/systemd/journal/socket