aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bash_completion/firejail.bash_completion.in8
-rw-r--r--src/common.mk.in9
-rw-r--r--src/fbuilder/build_bin.c2
-rw-r--r--src/fbuilder/build_fs.c5
-rw-r--r--src/fbuilder/build_home.c4
-rw-r--r--src/fbuilder/build_profile.c182
-rw-r--r--src/fbuilder/build_seccomp.c33
-rw-r--r--src/fbuilder/fbuilder.h3
-rw-r--r--src/fbuilder/filedb.c2
-rw-r--r--src/fbuilder/main.c27
-rw-r--r--src/fbuilder/utils.c2
-rw-r--r--src/fcopy/main.c37
-rw-r--r--src/fids/Makefile.in18
-rw-r--r--src/fids/blake2b.c176
-rw-r--r--src/fids/config16
-rw-r--r--src/fids/db.c158
-rw-r--r--src/fids/db_exclude.c56
-rw-r--r--src/fids/fids.h51
-rw-r--r--src/fids/main.c378
-rw-r--r--src/firecfg/desktop_files.c17
-rw-r--r--src/firecfg/firecfg.config77
-rw-r--r--src/firecfg/firecfg.h2
-rw-r--r--src/firecfg/main.c10
-rw-r--r--src/firecfg/sound.c2
-rw-r--r--src/firecfg/util.c2
-rw-r--r--src/firejail/appimage.c43
-rw-r--r--src/firejail/appimage_size.c2
-rw-r--r--src/firejail/arp.c20
-rw-r--r--src/firejail/bandwidth.c34
-rw-r--r--src/firejail/caps.c4
-rw-r--r--src/firejail/cgroup.c89
-rw-r--r--src/firejail/checkcfg.c60
-rw-r--r--src/firejail/chroot.c57
-rw-r--r--src/firejail/cmdline.c34
-rw-r--r--src/firejail/cpu.c8
-rw-r--r--src/firejail/dbus.c35
-rw-r--r--src/firejail/dhcp.c16
-rw-r--r--src/firejail/env.c16
-rw-r--r--src/firejail/firejail.h126
-rw-r--r--src/firejail/fs.c698
-rw-r--r--src/firejail/fs_bin.c47
-rw-r--r--src/firejail/fs_dev.c13
-rw-r--r--src/firejail/fs_etc.c141
-rw-r--r--src/firejail/fs_home.c212
-rw-r--r--src/firejail/fs_hostname.c121
-rw-r--r--src/firejail/fs_lib.c70
-rw-r--r--src/firejail/fs_lib2.c8
-rw-r--r--src/firejail/fs_logger.c27
-rw-r--r--src/firejail/fs_mkdir.c8
-rw-r--r--src/firejail/fs_overlayfs.c470
-rw-r--r--src/firejail/fs_trace.c49
-rw-r--r--src/firejail/fs_var.c26
-rw-r--r--src/firejail/fs_whitelist.c1431
-rw-r--r--src/firejail/ids.c89
-rw-r--r--src/firejail/join.c32
-rw-r--r--src/firejail/ls.c39
-rw-r--r--src/firejail/macros.c68
-rw-r--r--src/firejail/main.c773
-rw-r--r--src/firejail/mountinfo.c238
-rw-r--r--src/firejail/netfilter.c92
-rw-r--r--src/firejail/netns.c2
-rw-r--r--src/firejail/network.c4
-rw-r--r--src/firejail/network_main.c4
-rw-r--r--src/firejail/no_sandbox.c21
-rw-r--r--src/firejail/output.c14
-rw-r--r--src/firejail/paths.c4
-rw-r--r--src/firejail/preproc.c4
-rw-r--r--src/firejail/profile.c213
-rw-r--r--src/firejail/protocol.c6
-rw-r--r--src/firejail/pulseaudio.c128
-rw-r--r--src/firejail/restrict_users.c35
-rw-r--r--src/firejail/restricted_shell.c6
-rw-r--r--src/firejail/rlimit.c28
-rw-r--r--src/firejail/run_files.c8
-rw-r--r--src/firejail/run_symlink.c5
-rw-r--r--src/firejail/sandbox.c140
-rw-r--r--src/firejail/sbox.c26
-rw-r--r--src/firejail/seccomp.c19
-rw-r--r--src/firejail/selinux.c33
-rw-r--r--src/firejail/shutdown.c10
-rw-r--r--src/firejail/usage.c35
-rw-r--r--src/firejail/util.c670
-rw-r--r--src/firejail/x11.c114
-rw-r--r--src/firemon/apparmor.c2
-rw-r--r--src/firemon/arp.c2
-rw-r--r--src/firemon/caps.c2
-rw-r--r--src/firemon/cgroup.c2
-rw-r--r--src/firemon/cpu.c2
-rw-r--r--src/firemon/firemon.c4
-rw-r--r--src/firemon/firemon.h2
-rw-r--r--src/firemon/interface.c7
-rw-r--r--src/firemon/list.c2
-rw-r--r--src/firemon/netstats.c8
-rw-r--r--src/firemon/procevent.c5
-rw-r--r--src/firemon/route.c2
-rw-r--r--src/firemon/seccomp.c2
-rw-r--r--src/firemon/top.c8
-rw-r--r--src/firemon/tree.c2
-rw-r--r--src/firemon/usage.c4
-rw-r--r--src/firemon/x11.c2
-rw-r--r--src/fldd/main.c17
-rw-r--r--src/fnet/arp.c2
-rw-r--r--src/fnet/fnet.h2
-rw-r--r--src/fnet/interface.c2
-rw-r--r--src/fnet/main.c2
-rw-r--r--src/fnet/veth.c2
-rw-r--r--src/fnetfilter/main.c7
-rw-r--r--src/fnettrace/Makefile.in17
-rw-r--r--src/fnettrace/fnettrace.h73
-rw-r--r--src/fnettrace/hostnames.c124
-rw-r--r--src/fnettrace/main.c665
-rw-r--r--src/fnettrace/radix.c155
-rw-r--r--src/fnettrace/radix.h27
-rw-r--r--src/fnettrace/static-ip-map4044
-rw-r--r--src/fnettrace/tail.c63
-rw-r--r--src/fsec-optimize/fsec_optimize.h2
-rw-r--r--src/fsec-optimize/main.c2
-rw-r--r--src/fsec-optimize/optimizer.c2
-rw-r--r--src/fsec-print/fsec_print.h2
-rw-r--r--src/fsec-print/main.c2
-rw-r--r--src/fsec-print/print.c2
-rw-r--r--src/fseccomp/fseccomp.h2
-rw-r--r--src/fseccomp/main.c2
-rw-r--r--src/fseccomp/protocol.c2
-rw-r--r--src/fseccomp/seccomp.c2
-rw-r--r--src/fseccomp/seccomp_file.c2
-rw-r--r--src/fseccomp/seccomp_secondary.c2
-rwxr-xr-xsrc/fshaper/fshaper.sh2
-rw-r--r--src/ftee/ftee.h2
-rw-r--r--src/ftee/main.c2
-rw-r--r--src/include/common.h27
-rw-r--r--src/include/euid_common.h2
-rw-r--r--src/include/firejail_user.h2
-rw-r--r--src/include/gcov_wrapper.h46
-rw-r--r--src/include/ldd_utils.h2
-rw-r--r--src/include/pid.h2
-rw-r--r--src/include/rundefs.h18
-rw-r--r--src/include/seccomp.h2
-rw-r--r--src/include/syscall.h2
-rw-r--r--src/jailcheck/Makefile.in (renamed from src/jailtest/Makefile.in)6
-rw-r--r--src/jailcheck/access.c (renamed from src/jailtest/access.c)8
-rw-r--r--src/jailcheck/apparmor.c (renamed from src/jailtest/apparmor.c)4
-rw-r--r--src/jailcheck/jailcheck.h (renamed from src/jailtest/jailtest.h)10
-rw-r--r--src/jailcheck/main.c (renamed from src/jailtest/main.c)29
-rw-r--r--src/jailcheck/network.c57
-rw-r--r--src/jailcheck/noexec.c (renamed from src/jailtest/noexec.c)8
-rw-r--r--src/jailcheck/seccomp.c (renamed from src/jailtest/seccomp.c)4
-rw-r--r--src/jailcheck/sysfiles.c (renamed from src/jailtest/sysfiles.c)6
-rw-r--r--src/jailcheck/utils.c (renamed from src/jailtest/utils.c)4
-rw-r--r--src/jailcheck/virtual.c (renamed from src/jailtest/virtual.c)6
-rw-r--r--src/lib/common.c112
-rw-r--r--src/lib/errno.c2
-rw-r--r--src/lib/firejail_user.c2
-rw-r--r--src/lib/ldd_utils.c4
-rw-r--r--src/lib/pid.c2
-rw-r--r--src/lib/syscall.c15
-rw-r--r--src/libpostexecseccomp/libpostexecseccomp.c2
-rw-r--r--src/libtrace/libtrace.c16
-rw-r--r--src/libtracelog/libtracelog.c2
-rw-r--r--src/man/Makefile.in2
-rw-r--r--src/man/firecfg.txt4
-rw-r--r--src/man/firejail-login.txt2
-rw-r--r--src/man/firejail-profile.txt175
-rw-r--r--src/man/firejail-users.txt2
-rw-r--r--src/man/firejail.txt323
-rw-r--r--src/man/firemon.txt4
-rw-r--r--src/man/jailcheck.txt (renamed from src/man/jailtest.txt)29
-rwxr-xr-xsrc/man/preproc.awk2
-rw-r--r--src/profstats/Makefile.in2
-rw-r--r--src/profstats/main.c75
-rwxr-xr-xsrc/tools/check-caps.sh2
-rw-r--r--src/tools/extract_caps.c2
-rw-r--r--src/tools/extract_errnos.sh2
-rw-r--r--src/tools/extract_seccomp.c2
-rw-r--r--src/tools/extract_syscalls.c2
-rwxr-xr-xsrc/tools/mkcoverit.sh2
-rw-r--r--src/tools/testuid.c2
-rw-r--r--src/tools/ttytest.c2
-rw-r--r--src/tools/unixsocket.c2
-rw-r--r--src/zsh_completion/_firejail.in14
180 files changed, 10813 insertions, 3533 deletions
diff --git a/src/bash_completion/firejail.bash_completion.in b/src/bash_completion/firejail.bash_completion.in
index f68edf380..ff411c807 100644
--- a/src/bash_completion/firejail.bash_completion.in
+++ b/src/bash_completion/firejail.bash_completion.in
@@ -5,7 +5,7 @@
5# http://bash-completion.alioth.debian.org 5# http://bash-completion.alioth.debian.org
6#******************************************************************* 6#*******************************************************************
7 7
8__interfaces(){ 8__interfaces() {
9 cut -f 1 -d ':' /proc/net/dev | tail -n +3 | grep -v lo | xargs 9 cut -f 1 -d ':' /proc/net/dev | tail -n +3 | grep -v lo | xargs
10} 10}
11 11
@@ -90,11 +90,11 @@ _firejail()
90 _filedir 90 _filedir
91 return 0 91 return 0
92 ;; 92 ;;
93 --net) 93 --net)
94 comps=$(__interfaces) 94 comps=$(__interfaces)
95 COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) 95 COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
96 return 0 96 return 0
97 ;; 97 ;;
98 esac 98 esac
99 99
100 $split && return 0 100 $split && return 0
diff --git a/src/common.mk.in b/src/common.mk.in
index b379aef7f..38c05bc69 100644
--- a/src/common.mk.in
+++ b/src/common.mk.in
@@ -15,7 +15,6 @@ HAVE_NETWORK=@HAVE_NETWORK@
15HAVE_USERNS=@HAVE_USERNS@ 15HAVE_USERNS=@HAVE_USERNS@
16HAVE_X11=@HAVE_X11@ 16HAVE_X11=@HAVE_X11@
17HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ 17HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@
18HAVE_WHITELIST=@HAVE_WHITELIST@
19HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ 18HAVE_GLOBALCFG=@HAVE_GLOBALCFG@
20HAVE_APPARMOR=@HAVE_APPARMOR@ 19HAVE_APPARMOR=@HAVE_APPARMOR@
21HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ 20HAVE_OVERLAYFS=@HAVE_OVERLAYFS@
@@ -23,11 +22,13 @@ HAVE_FIRETUNNEL=@HAVE_FIRETUNNEL@
23HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ 22HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@
24HAVE_GCOV=@HAVE_GCOV@ 23HAVE_GCOV=@HAVE_GCOV@
25HAVE_SELINUX=@HAVE_SELINUX@ 24HAVE_SELINUX=@HAVE_SELINUX@
25HAVE_SUID=@HAVE_SUID@
26HAVE_DBUSPROXY=@HAVE_DBUSPROXY@ 26HAVE_DBUSPROXY=@HAVE_DBUSPROXY@
27HAVE_USERTMPFS=@HAVE_USERTMPFS@ 27HAVE_USERTMPFS=@HAVE_USERTMPFS@
28HAVE_OUTPUT=@HAVE_OUTPUT@ 28HAVE_OUTPUT=@HAVE_OUTPUT@
29HAVE_LTS=@HAVE_LTS@ 29HAVE_LTS=@HAVE_LTS@
30HAVE_FORCE_NONEWPRIVS=@HAVE_FORCE_NONEWPRIVS@ 30HAVE_FORCE_NONEWPRIVS=@HAVE_FORCE_NONEWPRIVS@
31HAVE_ONLY_SYSCFG_PROFILES=@HAVE_ONLY_SYSCFG_PROFILES@
31 32
32H_FILE_LIST = $(sort $(wildcard *.h)) 33H_FILE_LIST = $(sort $(wildcard *.h))
33C_FILE_LIST = $(sort $(wildcard *.c)) 34C_FILE_LIST = $(sort $(wildcard *.c))
@@ -36,11 +37,11 @@ BINOBJS = $(foreach file, $(OBJS), $file)
36 37
37CFLAGS = @CFLAGS@ 38CFLAGS = @CFLAGS@
38CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV) 39CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' $(HAVE_GCOV)
39CFLAGS += -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' -DBINDIR='"$(bindir)"' 40CFLAGS += -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' -DBINDIR='"$(bindir)"' -DVARDIR='"/var/lib/firejail"'
40MANFLAGS = $(HAVE_LTS) $(HAVE_OUTPUT) $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_USERTMPFS) $(HAVE_DBUSPROXY) $(HAVE_FIRETUNNEL) $(HAVE_GLOBALCFG) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) $(HAVE_SELINUX) $(HAVE_FORCE_NONEWPRIVS) 41MANFLAGS = $(HAVE_LTS) $(HAVE_OUTPUT) $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_USERTMPFS) $(HAVE_DBUSPROXY) $(HAVE_FIRETUNNEL) $(HAVE_GLOBALCFG) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_FILE_TRANSFER) $(HAVE_SELINUX) $(HAVE_SUID) $(HAVE_FORCE_NONEWPRIVS) $(HAVE_ONLY_SYSCFG_PROFILES)
41CFLAGS += $(MANFLAGS) 42CFLAGS += $(MANFLAGS)
42CFLAGS += -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -Wformat -Wformat-security 43CFLAGS += -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -Wformat -Wformat-security
43LDFLAGS += -pie -fPIE -Wl,-z,relro -Wl,-z,now -lpthread 44LDFLAGS += -pie -fPIE -Wl,-z,relro -Wl,-z,now
44EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ 45EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@
45 46
46ifdef NO_EXTRA_CFLAGS 47ifdef NO_EXTRA_CFLAGS
diff --git a/src/fbuilder/build_bin.c b/src/fbuilder/build_bin.c
index 9577042c4..041c52629 100644
--- a/src/fbuilder/build_bin.c
+++ b/src/fbuilder/build_bin.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fbuilder/build_fs.c b/src/fbuilder/build_fs.c
index 8700e0ba1..4766337ff 100644
--- a/src/fbuilder/build_fs.c
+++ b/src/fbuilder/build_fs.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -236,9 +236,6 @@ void build_share(const char *fname, FILE *fp) {
236//******************************************* 236//*******************************************
237static FileDB *tmp_out = NULL; 237static FileDB *tmp_out = NULL;
238static void tmp_callback(char *ptr) { 238static void tmp_callback(char *ptr) {
239 // skip strace file
240 if (strncmp(ptr, "/tmp/firejail-strace", 20) == 0)
241 return;
242 if (strncmp(ptr, "/tmp/runtime-", 13) == 0) 239 if (strncmp(ptr, "/tmp/runtime-", 13) == 0)
243 return; 240 return;
244 if (strcmp(ptr, "/tmp") == 0) 241 if (strcmp(ptr, "/tmp") == 0)
diff --git a/src/fbuilder/build_home.c b/src/fbuilder/build_home.c
index b3ec6cffd..d6d421259 100644
--- a/src/fbuilder/build_home.c
+++ b/src/fbuilder/build_home.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -68,6 +68,8 @@ void process_home(const char *fname, char *home, int home_len) {
68 ptr += 7; 68 ptr += 7;
69 else if (strncmp(ptr, "open /home", 10) == 0) 69 else if (strncmp(ptr, "open /home", 10) == 0)
70 ptr += 5; 70 ptr += 5;
71 else if (strncmp(ptr, "opendir /home", 13) == 0)
72 ptr += 8;
71 else 73 else
72 continue; 74 continue;
73 75
diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c
index fb53f70a6..941f43562 100644
--- a/src/fbuilder/build_profile.c
+++ b/src/fbuilder/build_profile.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,22 +22,6 @@
22#include <sys/wait.h> 22#include <sys/wait.h>
23 23
24#define TRACE_OUTPUT "/tmp/firejail-trace.XXXXXX" 24#define TRACE_OUTPUT "/tmp/firejail-trace.XXXXXX"
25#define STRACE_OUTPUT "/tmp/firejail-strace.XXXXXX"
26
27/* static char *cmdlist[] = { */
28/* "/usr/bin/firejail", */
29/* "--quiet", */
30/* "--output=" TRACE_OUTPUT, */
31/* "--noprofile", */
32/* "--caps.drop=all", */
33/* "--nonewprivs", */
34/* "--trace", */
35/* "--shell=none", */
36/* "/usr/bin/strace", // also used as a marker in build_profile() */
37/* "-c", */
38/* "-f", */
39/* "-o" STRACE_OUTPUT, */
40/* }; */
41 25
42void build_profile(int argc, char **argv, int index, FILE *fp) { 26void build_profile(int argc, char **argv, int index, FILE *fp) {
43 // next index is the application name 27 // next index is the application name
@@ -47,78 +31,42 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
47 } 31 }
48 32
49 char trace_output[] = "/tmp/firejail-trace.XXXXXX"; 33 char trace_output[] = "/tmp/firejail-trace.XXXXXX";
50 char strace_output[] = "/tmp/firejail-strace.XXXXXX";
51
52 int tfile = mkstemp(trace_output); 34 int tfile = mkstemp(trace_output);
53 int stfile = mkstemp(strace_output); 35 if(tfile == -1)
54 if(tfile == -1 || stfile == -1)
55 errExit("mkstemp"); 36 errExit("mkstemp");
56
57 // close the files, firejail/strace will overwrite them!
58 close(tfile); 37 close(tfile);
59 close(stfile);
60
61 38
62 char *output; 39 char *output;
63 char *stroutput;
64 if(asprintf(&output,"--trace=%s",trace_output) == -1) 40 if(asprintf(&output,"--trace=%s",trace_output) == -1)
65 errExit("asprintf"); 41 errExit("asprintf");
66 if(asprintf(&stroutput,"-o%s",strace_output) == -1)
67 errExit("asprintf");
68
69 char *cmdlist[] = {
70 BINDIR "/firejail",
71 "--quiet",
72 "--noprofile",
73 "--caps.drop=all",
74 "--nonewprivs",
75 output,
76 "--shell=none",
77 "/usr/bin/strace", // also used as a marker in build_profile()
78 "-c",
79 "-f",
80 stroutput,
81 };
82
83 // detect strace and check if Yama LSM allows us to use it
84 int have_strace = 0;
85 int have_yama_permission = 1;
86 if (access("/usr/bin/strace", X_OK) == 0) {
87 have_strace = 1;
88 FILE *ps = fopen("/proc/sys/kernel/yama/ptrace_scope", "r");
89 if (ps) {
90 unsigned val;
91 if (fscanf(ps, "%u", &val) == 1)
92 have_yama_permission = (val < 2);
93 fclose(ps);
94 }
95 }
96 42
97 // calculate command length 43 // calculate command length
98 unsigned len = (int) sizeof(cmdlist) / sizeof(char*) + argc - index + 1; 44 unsigned len = 64; // plenty of space for firejail command line
99 if (arg_debug) 45 len += argc - index; // program command line
100 printf("command len %d + %d + 1\n", (int) (sizeof(cmdlist) / sizeof(char*)), argc - index); 46 len += 1; // NULL
101 char *cmd[len];
102 cmd[0] = cmdlist[0]; // explicit assignment to clean scan-build error
103 47
104 // build command 48 // build command
105 // skip strace if not installed, or no permission to use it 49 char *cmd[len];
106 int skip_strace = !(have_strace && have_yama_permission); 50 unsigned curr_len = 0;
107 unsigned i = 0; 51 cmd[curr_len++] = BINDIR "/firejail";
108 for (i = 0; i < (int) sizeof(cmdlist) / sizeof(char*); i++) { 52 cmd[curr_len++] = "--quiet";
109 if (skip_strace && strcmp(cmdlist[i], "/usr/bin/strace") == 0) 53 cmd[curr_len++] = "--noprofile";
110 break; 54 cmd[curr_len++] = "--caps.drop=all";
111 cmd[i] = cmdlist[i]; 55 cmd[curr_len++] = "--seccomp=!chroot";
112 } 56 cmd[curr_len++] = "--shell=none";
113 57 cmd[curr_len++] = output;
114 int i2 = index; 58 if (arg_appimage)
115 for (; i < (len - 1); i++, i2++) 59 cmd[curr_len++] = "--appimage";
116 cmd[i] = argv[i2]; 60
117 assert(i < len); 61 int i;
118 cmd[i] = NULL; 62 for (i = index; i < argc; i++)
63 cmd[curr_len++] = argv[i];
64
65 assert(curr_len < len);
66 cmd[curr_len] = NULL;
119 67
120 if (arg_debug) { 68 if (arg_debug) {
121 for (i = 0; i < len; i++) 69 for (i = 0; cmd[i]; i++)
122 printf("%s%s\n", (i)?"\t":"", cmd[i]); 70 printf("%s%s\n", (i)?"\t":"", cmd[i]);
123 } 71 }
124 72
@@ -140,14 +88,14 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
140 88
141 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 89 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
142 if (fp == stdout) 90 if (fp == stdout)
143 printf("--- Built profile beings after this line ---\n"); 91 printf("--- Built profile begins after this line ---\n");
144 fprintf(fp, "# Save this file as \"application.profile\" (change \"application\" with the\n"); 92 fprintf(fp, "# Save this file as \"application.profile\" (change \"application\" with the\n");
145 fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n"); 93 fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n");
146 fprintf(fp, "# automatically every time you sandbox your application.\n#\n"); 94 fprintf(fp, "# automatically every time you sandbox your application.\n#\n");
147 fprintf(fp, "# Run \"firejail application\" to test it. In the file there are\n"); 95 fprintf(fp, "# Run \"firejail application\" to test it. In the file there are\n");
148 fprintf(fp, "# some other commands you can try. Enable them by removing the \"#\".\n"); 96 fprintf(fp, "# some other commands you can try. Enable them by removing the \"#\".\n\n");
149 97
150 fprintf(fp, "\n# Firejail profile for %s\n", argv[index]); 98 fprintf(fp, "# Firejail profile for %s\n", argv[index]);
151 fprintf(fp, "# Persistent local customizations\n"); 99 fprintf(fp, "# Persistent local customizations\n");
152 fprintf(fp, "#include %s.local\n", argv[index]); 100 fprintf(fp, "#include %s.local\n", argv[index]);
153 fprintf(fp, "# Persistent global definitions\n"); 101 fprintf(fp, "# Persistent global definitions\n");
@@ -158,58 +106,62 @@ void build_profile(int argc, char **argv, int index, FILE *fp) {
158 fprintf(fp, "### Enable as many of them as you can! A very important one is\n"); 106 fprintf(fp, "### Enable as many of them as you can! A very important one is\n");
159 fprintf(fp, "### \"disable-exec.inc\". This will make among other things your home\n"); 107 fprintf(fp, "### \"disable-exec.inc\". This will make among other things your home\n");
160 fprintf(fp, "### and /tmp directories non-executable.\n"); 108 fprintf(fp, "### and /tmp directories non-executable.\n");
161 fprintf(fp, "include disable-common.inc\n"); 109 fprintf(fp, "include disable-common.inc\t# dangerous directories like ~/.ssh and ~/.gnupg\n");
162 fprintf(fp, "#include disable-devel.inc\n"); 110 fprintf(fp, "#include disable-devel.inc\t# development tools such as gcc and gdb\n");
163 fprintf(fp, "#include disable-exec.inc\n"); 111 fprintf(fp, "#include disable-exec.inc\t# non-executable directories such as /var, /tmp, and /home\n");
164 fprintf(fp, "#include disable-interpreters.inc\n"); 112 fprintf(fp, "#include disable-interpreters.inc\t# perl, python, lua etc.\n");
165 fprintf(fp, "include disable-passwdmgr.inc\n"); 113 fprintf(fp, "include disable-programs.inc\t# user configuration for programs such as firefox, vlc etc.\n");
166 fprintf(fp, "include disable-programs.inc\n"); 114 fprintf(fp, "#include disable-shell.inc\t# sh, bash, zsh etc.\n");
167 fprintf(fp, "#include disable-xdg.inc\n"); 115 fprintf(fp, "#include disable-xdg.inc\t# standard user directories: Documents, Pictures, Videos, Music\n");
168 fprintf(fp, "\n"); 116 fprintf(fp, "\n");
169 117
170 fprintf(fp, "### Home Directory Whitelisting ###\n"); 118 fprintf(fp, "### Home Directory Whitelisting ###\n");
171 fprintf(fp, "### If something goes wrong, this section is the first one to comment out.\n"); 119 fprintf(fp, "### If something goes wrong, this section is the first one to comment out.\n");
172 fprintf(fp, "### Instead, you'll have to relay on the basic blacklisting above.\n"); 120 fprintf(fp, "### Instead, you'll have to relay on the basic blacklisting above.\n");
173 build_home(trace_output, fp); 121 build_home(trace_output, fp);
122 fprintf(fp, "\n");
174 123
175 fprintf(fp, "\n### The Rest of the Filesystem ###\n"); 124 fprintf(fp, "### Filesystem Whitelisting ###\n");
176 build_share(trace_output, fp); 125 build_share(trace_output, fp);
126 //todo: include whitelist-runuser-common.inc
177 build_var(trace_output, fp); 127 build_var(trace_output, fp);
178 build_bin(trace_output, fp); 128 fprintf(fp, "\n");
179 build_dev(trace_output, fp);
180 fprintf(fp, "#nodvd\n");
181 fprintf(fp, "#noinput\n");
182 fprintf(fp, "#notv\n");
183 fprintf(fp, "#nou2f\n");
184 fprintf(fp, "#novideo\n");
185 build_etc(trace_output, fp);
186 build_tmp(trace_output, fp);
187 129
188 fprintf(fp, "\n### Security Filters ###\n"); 130 fprintf(fp, "#apparmor\t# if you have AppArmor running, try this one!\n");
189 fprintf(fp, "#apparmor\n");
190 fprintf(fp, "caps.drop all\n"); 131 fprintf(fp, "caps.drop all\n");
132 fprintf(fp, "ipc-namespace\n");
191 fprintf(fp, "netfilter\n"); 133 fprintf(fp, "netfilter\n");
192 fprintf(fp, "#nogroups\n"); 134 fprintf(fp, "#no3d\t# disable 3D acceleration\n");
193 fprintf(fp, "#noroot\n"); 135 fprintf(fp, "#nodvd\t# disable DVD and CD devices\n");
136 fprintf(fp, "#nogroups\t# disable supplementary user groups\n");
137 fprintf(fp, "#noinput\t# disable input devices\n");
194 fprintf(fp, "nonewprivs\n"); 138 fprintf(fp, "nonewprivs\n");
139 fprintf(fp, "noroot\n");
140 fprintf(fp, "#notv\t# disable DVB TV devices\n");
141 fprintf(fp, "#nou2f\t# disable U2F devices\n");
142 fprintf(fp, "#novideo\t# disable video capture devices\n");
195 build_protocol(trace_output, fp); 143 build_protocol(trace_output, fp);
144 fprintf(fp, "seccomp !chroot\t# allowing chroot, just in case this is an Electron app\n");
145 fprintf(fp, "shell none\n");
146 fprintf(fp, "tracelog\n");
147 fprintf(fp, "\n");
148
149 fprintf(fp, "#disable-mnt\t# no access to /mnt, /media, /run/mount and /run/media\n");
150 build_bin(trace_output, fp);
151 fprintf(fp, "#private-cache\t# run with an empty ~/.cache directory\n");
152 build_dev(trace_output, fp);
153 build_etc(trace_output, fp);
154 fprintf(fp, "#private-lib\n");
155 build_tmp(trace_output, fp);
156 fprintf(fp, "\n");
157
158 fprintf(fp, "#dbus-user none\n");
159 fprintf(fp, "#dbus-system none\n");
160 fprintf(fp, "\n");
161 fprintf(fp, "#memory-deny-write-execute\n");
196 162
197 fprintf(fp, "seccomp\n"); 163 if (!arg_debug)
198 if (!have_strace) {
199 fprintf(fp, "### If you install strace on your system, Firejail will also create a\n");
200 fprintf(fp, "### whitelisted seccomp filter.\n");
201 }
202 else if (!have_yama_permission)
203 fprintf(fp, "### Yama security module prevents creation of a whitelisted seccomp filter\n");
204 else
205 build_seccomp(strace_output, fp);
206 fprintf(fp, "#shell none\n");
207 fprintf(fp, "#tracelog\n");
208
209 if (!arg_debug) {
210 unlink(trace_output); 164 unlink(trace_output);
211 unlink(strace_output);
212 }
213 } 165 }
214 else { 166 else {
215 fprintf(stderr, "Error: cannot run the sandbox\n"); 167 fprintf(stderr, "Error: cannot run the sandbox\n");
diff --git a/src/fbuilder/build_seccomp.c b/src/fbuilder/build_seccomp.c
index dc3cce456..7b4727e1a 100644
--- a/src/fbuilder/build_seccomp.c
+++ b/src/fbuilder/build_seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,6 +20,7 @@
20 20
21#include "fbuilder.h" 21#include "fbuilder.h"
22 22
23#if 0
23void build_seccomp(const char *fname, FILE *fp) { 24void build_seccomp(const char *fname, FILE *fp) {
24 assert(fname); 25 assert(fname);
25 assert(fp); 26 assert(fp);
@@ -78,15 +79,17 @@ void build_seccomp(const char *fname, FILE *fp) {
78 79
79 fclose(fp2); 80 fclose(fp2);
80} 81}
82#endif
81 83
82//*************************************** 84//***************************************
83// protocol 85// protocol
84//*************************************** 86//***************************************
85int unix_s = 0; 87static int unix_s = 0;
86int inet = 0; 88static int inet = 0;
87int inet6 = 0; 89static int inet6 = 0;
88int netlink = 0; 90static int netlink = 0;
89int packet = 0; 91static int packet = 0;
92static int bluetooth = 0;
90static void process_protocol(const char *fname) { 93static void process_protocol(const char *fname) {
91 assert(fname); 94 assert(fname);
92 95
@@ -135,6 +138,8 @@ static void process_protocol(const char *fname) {
135 netlink = 1; 138 netlink = 1;
136 else if (strncmp(ptr, "AF_PACKET ", 10) == 0) 139 else if (strncmp(ptr, "AF_PACKET ", 10) == 0)
137 packet = 1; 140 packet = 1;
141 else if (strncmp(ptr, "AF_BLUETOOTH ", 13) == 0)
142 bluetooth = 1;
138 } 143 }
139 144
140 fclose(fp); 145 fclose(fp);
@@ -161,22 +166,22 @@ void build_protocol(const char *fname, FILE *fp) {
161 } 166 }
162 167
163 int net = 0; 168 int net = 0;
164 if (unix_s || inet || inet6 || netlink || packet) { 169 if (unix_s || inet || inet6 || netlink || packet || bluetooth) {
165 fprintf(fp, "protocol "); 170 fprintf(fp, "protocol ");
166 if (unix_s) 171 if (unix_s)
167 fprintf(fp, "unix,"); 172 fprintf(fp, "unix,");
168 if (inet) { 173 if (inet || inet6) {
169 fprintf(fp, "inet,"); 174 fprintf(fp, "inet,inet6,");
170 net = 1;
171 }
172 if (inet6) {
173 fprintf(fp, "inet6,");
174 net = 1; 175 net = 1;
175 } 176 }
176 if (netlink) 177 if (netlink)
177 fprintf(fp, "netlink,"); 178 fprintf(fp, "netlink,");
178 if (packet) { 179 if (packet) {
179 fprintf(fp, "packet"); 180 fprintf(fp, "packet,");
181 net = 1;
182 }
183 if (bluetooth) {
184 fprintf(fp, "bluetooth");
180 net = 1; 185 net = 1;
181 } 186 }
182 fprintf(fp, "\n"); 187 fprintf(fp, "\n");
diff --git a/src/fbuilder/fbuilder.h b/src/fbuilder/fbuilder.h
index 08dd35e10..3e23d7854 100644
--- a/src/fbuilder/fbuilder.h
+++ b/src/fbuilder/fbuilder.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -31,6 +31,7 @@
31#define MAX_BUF 4096 31#define MAX_BUF 4096
32// main.c 32// main.c
33extern int arg_debug; 33extern int arg_debug;
34extern int arg_appimage;
34 35
35// build_profile.c 36// build_profile.c
36void build_profile(int argc, char **argv, int index, FILE *fp); 37void build_profile(int argc, char **argv, int index, FILE *fp);
diff --git a/src/fbuilder/filedb.c b/src/fbuilder/filedb.c
index 94a226cb7..454b9f40b 100644
--- a/src/fbuilder/filedb.c
+++ b/src/fbuilder/filedb.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fbuilder/main.c b/src/fbuilder/main.c
index f4917aefc..aa49b2489 100644
--- a/src/fbuilder/main.c
+++ b/src/fbuilder/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,6 +19,7 @@
19*/ 19*/
20#include "fbuilder.h" 20#include "fbuilder.h"
21int arg_debug = 0; 21int arg_debug = 0;
22int arg_appimage = 0;
22 23
23static void usage(void) { 24static void usage(void) {
24 printf("Firejail profile builder\n"); 25 printf("Firejail profile builder\n");
@@ -39,7 +40,7 @@ printf("\n");
39 int i; 40 int i;
40 int prog_index = 0; 41 int prog_index = 0;
41 FILE *fp = stdout; 42 FILE *fp = stdout;
42 int prof_file = 0; 43 char *prof_file = NULL;
43 44
44 // parse arguments and extract program index 45 // parse arguments and extract program index
45 for (i = 1; i < argc; i++) { 46 for (i = 1; i < argc; i++) {
@@ -49,6 +50,8 @@ printf("\n");
49 } 50 }
50 else if (strcmp(argv[i], "--debug") == 0) 51 else if (strcmp(argv[i], "--debug") == 0)
51 arg_debug = 1; 52 arg_debug = 1;
53 else if (strcmp(argv[i], "--appimage") == 0)
54 arg_appimage = 1;
52 else if (strcmp(argv[i], "--build") == 0) 55 else if (strcmp(argv[i], "--build") == 0)
53 ; // do nothing, this is passed down from firejail 56 ; // do nothing, this is passed down from firejail
54 else if (strncmp(argv[i], "--build=", 8) == 0) { 57 else if (strncmp(argv[i], "--build=", 8) == 0) {
@@ -58,18 +61,23 @@ printf("\n");
58 exit(1); 61 exit(1);
59 } 62 }
60 63
64 // don't run if the file exists
65 if (access(argv[i] + 8, F_OK) == 0) {
66 fprintf(stderr, "Error: the profile file already exists. Please use a different file name.\n");
67 exit(1);
68 }
69
61 // check file access 70 // check file access
62 fp = fopen(argv[i] + 8, "w"); 71 fp = fopen(argv[i] + 8, "w");
63 if (!fp) { 72 if (!fp) {
64 fprintf(stderr, "Error fbuild: cannot open profile file.\n"); 73 fprintf(stderr, "Error: cannot open profile file.\n");
65 exit(1); 74 exit(1);
66 } 75 }
67 prof_file = 1; 76 prof_file = argv[i] + 8;
68 // do nothing, this is passed down from firejail
69 } 77 }
70 else { 78 else {
71 if (*argv[i] == '-') { 79 if (*argv[i] == '-') {
72 fprintf(stderr, "Error fbuilder: invalid program\n"); 80 fprintf(stderr, "Error: invalid program\n");
73 usage(); 81 usage();
74 exit(1); 82 exit(1);
75 } 83 }
@@ -79,10 +87,13 @@ printf("\n");
79 } 87 }
80 88
81 if (prog_index == 0) { 89 if (prog_index == 0) {
82 fprintf(stderr, "Error fbuilder: program and arguments required\n"); 90 fprintf(stderr, "Error : program and arguments required\n");
83 usage(); 91 usage();
84 if (prof_file) 92 if (prof_file) {
85 fclose(fp); 93 fclose(fp);
94 int rv = unlink(prof_file);
95 (void) rv;
96 }
86 exit(1); 97 exit(1);
87 } 98 }
88 99
diff --git a/src/fbuilder/utils.c b/src/fbuilder/utils.c
index 52493f470..f89e69679 100644
--- a/src/fbuilder/utils.c
+++ b/src/fbuilder/utils.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 572e9f601..e56d853c8 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,11 +19,15 @@
19 */ 19 */
20 20
21#include "../include/common.h" 21#include "../include/common.h"
22#include <fcntl.h>
23#include <ftw.h> 22#include <ftw.h>
24#include <errno.h> 23#include <errno.h>
25#include <pwd.h> 24#include <pwd.h>
26 25
26#include <fcntl.h>
27#ifndef O_PATH
28#define O_PATH 010000000
29#endif
30
27#if HAVE_SELINUX 31#if HAVE_SELINUX
28#include <sys/stat.h> 32#include <sys/stat.h>
29#include <sys/types.h> 33#include <sys/types.h>
@@ -55,7 +59,7 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
55 assert(path); 59 assert(path);
56 assert(inside_path); 60 assert(inside_path);
57#if HAVE_SELINUX 61#if HAVE_SELINUX
58 char procfs_path[64]; 62 char procfs_path[64];
59 char *fcon = NULL; 63 char *fcon = NULL;
60 int fd; 64 int fd;
61 struct stat st; 65 struct stat st;
@@ -69,20 +73,24 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
69 if (!label_hnd) 73 if (!label_hnd)
70 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); 74 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
71 75
76 if (!label_hnd)
77 errExit("selabel_open");
78
72 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 79 /* Open the file as O_PATH, to pin it while we determine and adjust the label */
73 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 80 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
74 if (fd < 0) 81 if (fd < 0)
75 return; 82 return;
76 if (fstat(fd, &st) < 0) 83 if (fstat(fd, &st) < 0)
77 goto close; 84 goto close;
78 85
79 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) { 86 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) {
80 sprintf(procfs_path, "/proc/self/fd/%i", fd); 87 sprintf(procfs_path, "/proc/self/fd/%i", fd);
81 if (arg_debug) 88 if (arg_debug)
82 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 89 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
83 90
84 setfilecon_raw(procfs_path, fcon); 91 if (setfilecon_raw(procfs_path, fcon) != 0 && arg_debug)
85 } 92 printf("Cannot relabel %s: %s\n", path, strerror(errno));
93 }
86 freecon(fcon); 94 freecon(fcon);
87 close: 95 close:
88 close(fd); 96 close(fd);
@@ -192,7 +200,8 @@ static char *proc_pid_to_self(const char *target) {
192 200
193 // check where /proc/self points to 201 // check where /proc/self points to
194 static const char proc_self[] = "/proc/self"; 202 static const char proc_self[] = "/proc/self";
195 if (!(proc_pid = realpath(proc_self, NULL))) 203 proc_pid = realpath(proc_self, NULL);
204 if (proc_pid == NULL)
196 goto done; 205 goto done;
197 206
198 // redirect /proc/PID/xxx -> /proc/self/XXX 207 // redirect /proc/PID/xxx -> /proc/self/XXX
@@ -340,7 +349,7 @@ static char *check(const char *src) {
340 349
341errexit: 350errexit:
342 free(rsrc); 351 free(rsrc);
343 fprintf(stderr, "Error fcopy: invalid file %s\n", src); 352 fprintf(stderr, "Error fcopy: invalid ownership for file %s\n", src);
344 exit(1); 353 exit(1);
345} 354}
346 355
@@ -463,18 +472,12 @@ int main(int argc, char **argv) {
463 size_t len = strlen(src); 472 size_t len = strlen(src);
464 while (len > 1 && src[len - 1] == '/') 473 while (len > 1 && src[len - 1] == '/')
465 src[--len] = '\0'; 474 src[--len] = '\0';
466 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != len) { 475 reject_meta_chars(src, 0);
467 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src);
468 exit(1);
469 }
470 476
471 len = strlen(dest); 477 len = strlen(dest);
472 while (len > 1 && dest[len - 1] == '/') 478 while (len > 1 && dest[len - 1] == '/')
473 dest[--len] = '\0'; 479 dest[--len] = '\0';
474 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != len) { 480 reject_meta_chars(dest, 0);
475 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest);
476 exit(1);
477 }
478 481
479 // the destination should be a directory; 482 // the destination should be a directory;
480 struct stat s; 483 struct stat s;
diff --git a/src/fids/Makefile.in b/src/fids/Makefile.in
new file mode 100644
index 000000000..5530bcee2
--- /dev/null
+++ b/src/fids/Makefile.in
@@ -0,0 +1,18 @@
1.PHONY: all
2all: fids
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST) ../include/common.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9#fseccomp: $(OBJS) ../lib/common.o ../lib/errno.o ../lib/syscall.o
10fids: $(OBJS)
11 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
12
13.PHONY: clean
14clean:; rm -fr *.o fids *.gcov *.gcda *.gcno *.plist
15
16.PHONY: distclean
17distclean: clean
18 rm -fr Makefile
diff --git a/src/fids/blake2b.c b/src/fids/blake2b.c
new file mode 100644
index 000000000..ec7cf8602
--- /dev/null
+++ b/src/fids/blake2b.c
@@ -0,0 +1,176 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/* A simple unkeyed BLAKE2b Implementation based on the official reference
22 * from https://github.com/BLAKE2/BLAKE2.
23 *
24 * The original code was released under CC0 1.0 Universal license (Creative Commons),
25 * a public domain license.
26 */
27
28#include "fids.h"
29
30// little-endian vs big-endian is irrelevant since the checksum is calculated and checked on the same computer.
31static inline uint64_t load64( const void *src ) {
32 uint64_t w;
33 memcpy( &w, src, sizeof( w ) );
34 return w;
35}
36
37// mixing function
38#define ROTR64(x, y) (((x) >> (y)) ^ ((x) << (64 - (y))))
39#define G(a, b, c, d, x, y) { \
40 v[a] = v[a] + v[b] + x; \
41 v[d] = ROTR64(v[d] ^ v[a], 32); \
42 v[c] = v[c] + v[d]; \
43 v[b] = ROTR64(v[b] ^ v[c], 24); \
44 v[a] = v[a] + v[b] + y; \
45 v[d] = ROTR64(v[d] ^ v[a], 16); \
46 v[c] = v[c] + v[d]; \
47 v[b] = ROTR64(v[b] ^ v[c], 63); }
48
49// init vector
50static const uint64_t iv[8] = {
51 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B,
52 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
53 0x510E527FADE682D1, 0x9B05688C2B3E6C1F,
54 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179
55};
56
57
58const uint8_t sigma[12][16] = {
59 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
60 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
61 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
62 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
63 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
64 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
65 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
66 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
67 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
68 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
69 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
70 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
71};
72
73// blake2b context
74typedef struct {
75 uint8_t b[128]; // input buffer
76 uint64_t h[8]; // chained state
77 uint64_t t[2]; // total number of bytes
78 size_t c; // pointer for b[]
79 size_t outlen; // digest size
80} CTX;
81
82// compress function
83static void compress(CTX *ctx, int last) {
84 uint64_t m[16];
85 uint64_t v[16];
86 size_t i;
87
88 for (i = 0; i < 16; i++)
89 m[i] = load64(&ctx->b[8 * i]);
90
91 for (i = 0; i < 8; i++) {
92 v[i] = ctx->h[i];
93 v[i + 8] = iv[i];
94 }
95
96 v[12] ^= ctx->t[0];
97 v[13] ^= ctx->t[1];
98 if (last)
99 v[14] = ~v[14];
100
101 for (i = 0; i < 12; i++) {
102 G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
103 G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
104 G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
105 G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
106 G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
107 G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
108 G( 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
109 G( 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
110 }
111
112 for( i = 0; i < 8; ++i )
113 ctx->h[i] ^= v[i] ^ v[i + 8];
114}
115
116static int init(CTX *ctx, size_t outlen) { // (keylen=0: no key)
117 size_t i;
118
119 if (outlen == 0 || outlen > 64)
120 return -1;
121
122 for (i = 0; i < 8; i++)
123 ctx->h[i] = iv[i];
124 ctx->h[0] ^= 0x01010000 ^ outlen;
125
126 ctx->t[0] = 0;
127 ctx->t[1] = 0;
128 ctx->c = 0;
129 ctx->outlen = outlen;
130
131 return 0;
132}
133
134static void update(CTX *ctx, const void *in, size_t inlen) {
135 size_t i;
136
137 for (i = 0; i < inlen; i++) {
138 if (ctx->c == 128) {
139 ctx->t[0] += ctx->c;
140 if (ctx->t[0] < ctx->c)
141 ctx->t[1]++;
142 compress(ctx, 0);
143 ctx->c = 0;
144 }
145 ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
146 }
147}
148
149static void final(CTX *ctx, void *out) {
150 size_t i;
151
152 ctx->t[0] += ctx->c;
153 if (ctx->t[0] < ctx->c)
154 ctx->t[1]++;
155
156 while (ctx->c < 128)
157 ctx->b[ctx->c++] = 0;
158 compress(ctx, 1);
159
160 for (i = 0; i < ctx->outlen; i++) {
161 ((uint8_t *) out)[i] =
162 (ctx->h[i >> 3] >> (8 * (i & 7))) & 0xFF;
163 }
164}
165
166// public function
167int blake2b(void *out, size_t outlen, const void *in, size_t inlen) {
168 CTX ctx;
169
170 if (init(&ctx, outlen))
171 return -1;
172 update(&ctx, in, inlen);
173 final(&ctx, out);
174
175 return 0;
176}
diff --git a/src/fids/config b/src/fids/config
new file mode 100644
index 000000000..c18c97260
--- /dev/null
+++ b/src/fids/config
@@ -0,0 +1,16 @@
1/bin
2/sbin
3/usr/bin
4/usr/sbin
5/usr/games
6/opt
7/usr/share/ca-certificates
8
9
10/home/netblue/.bashrc
11/home/netblue/.config/firejail
12/home/netblue/.config/autostart
13/home/netblue/Desktop/*.desktop
14/home/netblue/.ssh
15/home/netblue/.gnupg
16
diff --git a/src/fids/db.c b/src/fids/db.c
new file mode 100644
index 000000000..e8dfab1ac
--- /dev/null
+++ b/src/fids/db.c
@@ -0,0 +1,158 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include"fids.h"
21
22typedef struct db_t {
23 struct db_t *next;
24 char *fname;
25 char *checksum;
26 char *mode;
27 int checked;
28} DB;
29
30#define MAXBUF 4096
31static DB *database[HASH_MAX] = {NULL};
32
33// djb2 hash function by Dan Bernstein
34static unsigned hash(const char *str) {
35 unsigned long hash = 5381;
36 int c;
37
38 while ((c = *str++) != '\0')
39 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
40
41 return hash & (HASH_MAX - 1);
42}
43
44#if 0
45// for testing the hash table
46static void db_print(void) {
47 int i;
48 for (i = 0; i < HASH_MAX; i++) {
49 int cnt = 0;
50 DB *ptr = database[i];
51 while (ptr) {
52 cnt++;
53 ptr = ptr->next;
54 }
55 printf("%d ", cnt);
56 fflush(0);
57 }
58 printf("\n");
59}
60#endif
61
62static void db_add(const char *fname, const char *checksum, const char *mode) {
63 DB *ptr = malloc(sizeof(DB));
64 if (!ptr)
65 errExit("malloc");
66 ptr->fname = strdup(fname);
67 ptr->checksum = strdup(checksum);
68 ptr->mode = strdup(mode);
69 ptr->checked = 0;
70 if (!ptr->fname || !ptr->checksum || !ptr->mode)
71 errExit("strdup");
72
73 unsigned h = hash(fname);
74 ptr->next = database[h];
75 database[h] = ptr;
76}
77
78void db_check(const char *fname, const char *checksum, const char *mode) {
79 assert(fname);
80 assert(checksum);
81 assert(mode);
82
83 unsigned h =hash(fname);
84 DB *ptr = database[h];
85 while (ptr) {
86 if (strcmp(fname, ptr->fname) == 0) {
87 ptr->checked = 1;
88 break;
89 }
90 ptr = ptr->next;
91 }
92
93 if (ptr ) {
94 if (strcmp(checksum, ptr->checksum)) {
95 f_modified++;
96 fprintf(stderr, "\nWarning: modified %s\n", fname);
97 }
98 if (strcmp(mode, ptr->mode)) {
99 f_permissions++;
100 fprintf(stderr, "\nWarning: permissions %s: old %s, new %s\n",
101 fname, ptr->mode, mode);
102 }
103 }
104 else {
105 f_new++;
106 fprintf(stderr, "\nWarning: new file %s\n", fname);
107 }
108}
109
110void db_missing(void) {
111 int i;
112 for (i = 0; i < HASH_MAX; i++) {
113 DB *ptr = database[i];
114 while (ptr) {
115 if (!ptr->checked) {
116 f_removed++;
117 fprintf(stderr, "Warning: removed %s\n", ptr->fname);
118 }
119 ptr = ptr->next;
120 }
121 }
122}
123
124// return 0 if ok, 1 if error
125int db_init(void) {
126 char buf[MAXBUF];
127 while(fgets(buf, MAXBUF, stdin)) {
128 // split - tab separated
129
130 char *mode = buf;
131 char *ptr = strchr(buf, '\t');
132 if (!ptr)
133 goto errexit;
134 *ptr = '\0';
135
136 char *checksum = ptr + 1;
137 ptr = strchr(checksum, '\t');
138 if (!ptr)
139 goto errexit;
140 *ptr = '\0';
141
142 char *fname = ptr + 1;
143 ptr = strchr(fname, '\n');
144 if (!ptr)
145 goto errexit;
146 *ptr = '\0';
147
148 db_add(fname, checksum, mode);
149 }
150// db_print();
151
152 return 0;
153
154errexit:
155 fprintf(stderr, "Error fids: database corrupted\n");
156 exit(1);
157}
158
diff --git a/src/fids/db_exclude.c b/src/fids/db_exclude.c
new file mode 100644
index 000000000..cfb37219c
--- /dev/null
+++ b/src/fids/db_exclude.c
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include"fids.h"
21
22typedef struct db_exclude_t {
23 struct db_exclude_t *next;
24 char *fname;
25 int len;
26} DB_EXCLUDE;
27static DB_EXCLUDE *database = NULL;
28
29void db_exclude_add(const char *fname) {
30 assert(fname);
31
32 DB_EXCLUDE *ptr = malloc(sizeof(DB_EXCLUDE));
33 if (!ptr)
34 errExit("malloc");
35
36 ptr->fname = strdup(fname);
37 if (!ptr->fname)
38 errExit("strdup");
39 ptr->len = strlen(fname);
40 ptr->next = database;
41 database = ptr;
42}
43
44int db_exclude_check(const char *fname) {
45 assert(fname);
46
47 DB_EXCLUDE *ptr = database;
48 while (ptr != NULL) {
49 if (strncmp(fname, ptr->fname, ptr->len) == 0)
50 return 1;
51 ptr = ptr->next;
52 }
53
54 return 0;
55}
56
diff --git a/src/fids/fids.h b/src/fids/fids.h
new file mode 100644
index 000000000..93ae106a1
--- /dev/null
+++ b/src/fids/fids.h
@@ -0,0 +1,51 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#ifndef FIDS_H
21#define FIDS_H
22
23#include "../include/common.h"
24
25// main.c
26#define MAX_DIR_LEVEL 20 // max directory tree depth
27#define MAX_INCLUDE_LEVEL 10 // max include level for config files
28extern int f_scanned;
29extern int f_modified;
30extern int f_new;
31extern int f_removed;
32extern int f_permissions;
33
34// db.c
35#define HASH_MAX 2048 // power of 2
36int db_init(void);
37void db_check(const char *fname, const char *checksum, const char *mode);
38void db_missing(void);
39
40// db_exclude.c
41void db_exclude_add(const char *fname);
42int db_exclude_check(const char *fname);
43
44
45// blake2b.c
46//#define KEY_SIZE 128 // key size in bytes
47#define KEY_SIZE 256
48//#define KEY_SIZE 512
49int blake2b(void *out, size_t outlen, const void *in, size_t inlen);
50
51#endif
diff --git a/src/fids/main.c b/src/fids/main.c
new file mode 100644
index 000000000..e6be365d1
--- /dev/null
+++ b/src/fids/main.c
@@ -0,0 +1,378 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "fids.h"
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <sys/mman.h>
26#include <dirent.h>
27#include <glob.h>
28
29#define MAXBUF 4096
30
31static int dir_level = 1;
32static int include_level = 0;
33int arg_init = 0;
34int arg_check = 0;
35char *arg_homedir = NULL;
36char *arg_dbfile = NULL;
37
38int f_scanned = 0;
39int f_modified = 0;
40int f_new = 0;
41int f_removed = 0;
42int f_permissions = 0;
43
44
45
46static inline int is_dir(const char *fname) {
47 assert(fname);
48
49 struct stat s;
50 if (stat(fname, &s) == 0) {
51 if (S_ISDIR(s.st_mode))
52 return 1;
53 }
54 return 0;
55}
56
57static inline int is_link(const char *fname) {
58 assert(fname);
59
60 char c;
61 ssize_t rv = readlink(fname, &c, 1);
62 return (rv != -1);
63}
64
65// mode is an array of 10 chars or more
66static inline void file_mode(const char *fname, char *mode) {
67 assert(fname);
68 assert(mode);
69
70 struct stat s;
71 if (stat(fname, &s)) {
72 *mode = '\0';
73 return;
74 }
75
76 sprintf(mode, (s.st_mode & S_IRUSR) ? "r" : "-");
77 sprintf(mode + 1, (s.st_mode & S_IWUSR) ? "w" : "-");
78 sprintf(mode + 2, (s.st_mode & S_IXUSR) ? "x" : "-");
79 sprintf(mode + 3, (s.st_mode & S_IRGRP) ? "r" : "-");
80 sprintf(mode + 4, (s.st_mode & S_IWGRP) ? "w" : "-");
81 sprintf(mode + 5, (s.st_mode & S_IXGRP) ? "x" : "-");
82 sprintf(mode + 6, (s.st_mode & S_IROTH) ? "r" : "-");
83 sprintf(mode + 7, (s.st_mode & S_IWOTH) ? "w" : "-");
84 sprintf(mode + 8, (s.st_mode & S_IXOTH) ? "x" : "-");
85}
86
87
88static void file_checksum(const char *fname) {
89 assert(fname);
90
91 int fd = open(fname, O_RDONLY);
92 if (fd == -1)
93 return;
94
95 off_t size = lseek(fd, 0, SEEK_END);
96 if (size < 0) {
97 close(fd);
98 return;
99 }
100
101 char *content = "empty";
102 int mmapped = 0;
103 if (size == 0) {
104 // empty files don't mmap - use "empty" string as the file content
105 size = 6; // strlen("empty") + 1
106 }
107 else {
108 content = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
109 close(fd);
110 mmapped = 1;
111 }
112
113 unsigned char checksum[KEY_SIZE / 8];
114 blake2b(checksum, sizeof(checksum), content, size);
115 if (mmapped)
116 munmap(content, size);
117
118 // calculate blake2 checksum
119 char str_checksum[(KEY_SIZE / 8) * 2 + 1];
120 int long unsigned i;
121 char *ptr = str_checksum;
122 for (i = 0; i < sizeof(checksum); i++, ptr += 2)
123 sprintf(ptr, "%02x", (unsigned char ) checksum[i]);
124
125 // build permissions string
126 char mode[10];
127 file_mode(fname, mode);
128
129 if (arg_init)
130 printf("%s\t%s\t%s\n", mode, str_checksum, fname);
131 else if (arg_check)
132 db_check(fname, str_checksum, mode);
133 else
134 assert(0);
135
136 f_scanned++;
137 if (f_scanned % 500 == 0)
138 fprintf(stderr, "%d ", f_scanned);
139 fflush(0);
140}
141
142void list_directory(const char *fname) {
143 assert(fname);
144 if (dir_level > MAX_DIR_LEVEL) {
145 fprintf(stderr, "Warning fids: maximum depth level exceeded for %s\n", fname);
146 return;
147 }
148
149 if (db_exclude_check(fname))
150 return;
151
152 if (is_link(fname))
153 return;
154
155 if (!is_dir(fname)) {
156 file_checksum(fname);
157 return;
158 }
159
160 DIR *dir;
161 struct dirent *entry;
162
163 if (!(dir = opendir(fname)))
164 return;
165
166 dir_level++;
167 while ((entry = readdir(dir)) != NULL) {
168 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
169 continue;
170 char *path;
171 if (asprintf(&path, "%s/%s", fname, entry->d_name) == -1)
172 errExit("asprintf");
173 list_directory(path);
174 free(path);
175 }
176 closedir(dir);
177 dir_level--;
178}
179
180void globbing(const char *fname) {
181 assert(fname);
182
183 // filter top directory
184 if (strcmp(fname, "/") == 0)
185 return;
186
187 glob_t globbuf;
188 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
189 if (globerr) {
190 fprintf(stderr, "Error fids: failed to glob pattern %s\n", fname);
191 exit(1);
192 }
193
194 long unsigned i;
195 for (i = 0; i < globbuf.gl_pathc; i++) {
196 char *path = globbuf.gl_pathv[i];
197 assert(path);
198
199 list_directory(path);
200 }
201
202 globfree(&globbuf);
203}
204
205static void process_config(const char *fname) {
206 assert(fname);
207
208 if (++include_level >= MAX_INCLUDE_LEVEL) {
209 fprintf(stderr, "Error ids: maximum include level for config files exceeded\n");
210 exit(1);
211 }
212
213 fprintf(stderr, "Opening config file %s\n", fname);
214 int fd = open(fname, O_RDONLY|O_CLOEXEC);
215 if (fd < 0) {
216 if (include_level == 1) {
217 fprintf(stderr, "Error ids: cannot open config file %s\n", fname);
218 exit(1);
219 }
220 return;
221 }
222
223 // make sure the file is owned by root
224 struct stat s;
225 if (fstat(fd, &s)) {
226 fprintf(stderr, "Error ids: cannot stat config file %s\n", fname);
227 exit(1);
228 }
229 if (s.st_uid || s.st_gid) {
230 fprintf(stderr, "Error ids: config file not owned by root\n");
231 exit(1);
232 }
233
234 fprintf(stderr, "Loading config file %s\n", fname);
235 FILE *fp = fdopen(fd, "r");
236 if (!fp) {
237 fprintf(stderr, "Error fids: cannot open config file %s\n", fname);
238 exit(1);
239 }
240
241 char buf[MAXBUF];
242 int line = 0;
243 while (fgets(buf, MAXBUF, fp)) {
244 line++;
245
246 // trim \n
247 char *ptr = strchr(buf, '\n');
248 if (ptr)
249 *ptr = '\0';
250
251 // comments
252 ptr = strchr(buf, '#');
253 if (ptr)
254 *ptr = '\0';
255
256 // empty space
257 ptr = buf;
258 while (*ptr == ' ' || *ptr == '\t')
259 ptr++;
260 char *start = ptr;
261
262 // empty line
263 if (*start == '\0')
264 continue;
265
266 // trailing spaces
267 ptr = start + strlen(start);
268 ptr--;
269 while (*ptr == ' ' || *ptr == '\t')
270 *ptr-- = '\0';
271
272 // replace ${HOME}
273 if (strncmp(start, "include", 7) == 0) {
274 ptr = start + 7;
275 if ((*ptr != ' ' && *ptr != '\t') || *ptr == '\0') {
276 fprintf(stderr, "Error fids: invalid line %d in %s\n", line, fname);
277 exit(1);
278 }
279 while (*ptr == ' ' || *ptr == '\t')
280 ptr++;
281
282 if (*ptr == '/')
283 process_config(ptr);
284 else {
285 // assume the file is in /etc/firejail
286 char *tmp;
287 if (asprintf(&tmp, "/etc/firejail/%s", ptr) == -1)
288 errExit("asprintf");
289 process_config(tmp);
290 free(tmp);
291 }
292 }
293 else if (*start == '!') {
294 // exclude file or dir
295 start++;
296 if (strncmp(start, "${HOME}", 7))
297 db_exclude_add(start);
298 else {
299 char *fname;
300 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
301 errExit("asprintf");
302 db_exclude_add(fname);
303 free(fname);
304 }
305 }
306 else if (strncmp(start, "${HOME}", 7))
307 globbing(start);
308 else {
309 char *fname;
310 if (asprintf(&fname, "%s%s", arg_homedir, start + 7) == -1)
311 errExit("asprintf");
312 globbing(fname);
313 free(fname);
314 }
315 }
316
317 fclose(fp);
318 include_level--;
319}
320
321
322
323void usage(void) {
324 printf("Usage: fids [--help|-h|-?] --init|--check homedir\n");
325}
326
327int main(int argc, char **argv) {
328 int i;
329 for (i = 1; i < argc; i++) {
330 if (strcmp(argv[i], "-h") == 0 ||
331 strcmp(argv[i], "-?") == 0 ||
332 strcmp(argv[i], "--help") == 0) {
333 usage();
334 return 0;
335 }
336 else if (strcmp(argv[i], "--init") == 0)
337 arg_init = 1;
338 else if (strcmp(argv[i], "--check") == 0)
339 arg_check = 1;
340 else if (strncmp(argv[i], "--", 2) == 0) {
341 fprintf(stderr, "Error fids: invalid argument %s\n", argv[i]);
342 exit(1);
343 }
344 }
345
346 if (argc != 3) {
347 fprintf(stderr, "Error fids: invalid number of arguments\n");
348 exit(1);
349 }
350 arg_homedir = argv[2];
351
352 int op = arg_check + arg_init;
353 if (op == 0 || op == 2) {
354 fprintf(stderr, "Error fids: use either --init or --check\n");
355 exit(1);
356 }
357
358 if (arg_init) {
359 process_config(SYSCONFDIR"/ids.config");
360 fprintf(stderr, "\n%d files scanned\n", f_scanned);
361 fprintf(stderr, "IDS database initialized\n");
362 }
363 else if (arg_check) {
364 if (db_init()) {
365 fprintf(stderr, "Error: IDS database not initialized, please run \"firejail --ids-init\"\n");
366 exit(1);
367 }
368
369 process_config(SYSCONFDIR"/ids.config");
370 fprintf(stderr, "\n%d files scanned: modified %d, permissions %d, new %d, removed %d\n",
371 f_scanned, f_modified, f_permissions, f_new, f_removed);
372 db_missing();
373 }
374 else
375 assert(0);
376
377 return 0;
378}
diff --git a/src/firecfg/desktop_files.c b/src/firecfg/desktop_files.c
index 06b0a117f..408662907 100644
--- a/src/firecfg/desktop_files.c
+++ b/src/firecfg/desktop_files.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -24,11 +24,16 @@
24static int check_profile(const char *name, const char *homedir) { 24static int check_profile(const char *name, const char *homedir) {
25 // build profile name 25 // build profile name
26 char *profname1; 26 char *profname1;
27#ifndef HAVE_ONLY_SYSCFG_PROFILES
27 char *profname2; 28 char *profname2;
29#endif
28 if (asprintf(&profname1, "%s/%s.profile", SYSCONFDIR, name) == -1) 30 if (asprintf(&profname1, "%s/%s.profile", SYSCONFDIR, name) == -1)
29 errExit("asprintf"); 31 errExit("asprintf");
32
33#ifndef HAVE_ONLY_SYSCFG_PROFILES
30 if (asprintf(&profname2, "%s/.config/firejail/%s.profile", homedir, name) == -1) 34 if (asprintf(&profname2, "%s/.config/firejail/%s.profile", homedir, name) == -1)
31 errExit("asprintf"); 35 errExit("asprintf");
36#endif
32 37
33 int rv = 0; 38 int rv = 0;
34 if (access(profname1, R_OK) == 0) { 39 if (access(profname1, R_OK) == 0) {
@@ -36,14 +41,18 @@ static int check_profile(const char *name, const char *homedir) {
36 printf("found %s\n", profname1); 41 printf("found %s\n", profname1);
37 rv = 1; 42 rv = 1;
38 } 43 }
44#ifndef HAVE_ONLY_SYSCFG_PROFILES
39 else if (access(profname2, R_OK) == 0) { 45 else if (access(profname2, R_OK) == 0) {
40 if (arg_debug) 46 if (arg_debug)
41 printf("found %s\n", profname2); 47 printf("found %s\n", profname2);
42 rv = 1; 48 rv = 1;
43 } 49 }
50#endif
44 51
45 free(profname1); 52 free(profname1);
53#ifndef HAVE_ONLY_SYSCFG_PROFILES
46 free(profname2); 54 free(profname2);
55#endif
47 return rv; 56 return rv;
48} 57}
49 58
@@ -168,9 +177,9 @@ void fix_desktop_files(char *homedir) {
168 177
169 char *filename = entry->d_name; 178 char *filename = entry->d_name;
170 179
171 // skip links 180 // skip links - Discord on Arch #4235 seems to be a symlink to /opt directory
172 if (is_link(filename)) 181// if (is_link(filename))
173 continue; 182// continue;
174 183
175 // no profile in /etc/firejail, no desktop file fixing 184 // no profile in /etc/firejail, no desktop file fixing
176 if (!have_profile(filename, homedir)) 185 if (!have_profile(filename, homedir))
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 474904ebf..618093193 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -1,8 +1,8 @@
1# /usr/lib/firejail/firecfg.config - firecfg utility configuration file 1# /etc/firejail/firecfg.config - firecfg utility configuration file
2# This is the list of programs in alphabetical order handled by firecfg utility 2# This is the list of programs in alphabetical order handled by firecfg utility
3# 3#
4#qemu-system-x86_64
50ad 40ad
51password
62048-qt 62048-qt
7Books 7Books
8Builder 8Builder
@@ -38,13 +38,15 @@ abrowser
38akonadi_control 38akonadi_control
39akregator 39akregator
40alacarte 40alacarte
41alpine
42alpinef
41amarok 43amarok
42amule 44amule
43amuled 45amuled
44android-studio 46android-studio
45anydesk 47anydesk
46apostrophe
47apktool 48apktool
49apostrophe
48# ar - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 50# ar - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
49arch-audit 51arch-audit
50archaudit-report 52archaudit-report
@@ -92,6 +94,7 @@ bleachbit
92blender 94blender
93blender-2.8 95blender-2.8
94bless 96bless
97blobby
95blobwars 98blobwars
96bluefish 99bluefish
97bnox 100bnox
@@ -107,6 +110,7 @@ brave-browser-stable
107# bzcat - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 110# bzcat - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
108bzflag 111bzflag
109# bzip2 - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 112# bzip2 - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
113cachy-browser
110calibre 114calibre
111calligra 115calligra
112calligraauthor 116calligraauthor
@@ -136,23 +140,27 @@ clamdscan
136clamdtop 140clamdtop
137clamscan 141clamscan
138clamtk 142clamtk
139claws-mail
140clawsker 143clawsker
144claws-mail
141clementine 145clementine
142clion 146clion
143clipit 147clion-eap
144clipgrab 148clipgrab
149clipit
145cliqz 150cliqz
146clocks 151clocks
147cmus 152cmus
148code 153code
149code-oss 154code-oss
155codium
156cointop
150cola 157cola
151colorful 158colorful
152com.github.bleakgrey.tootle 159com.github.bleakgrey.tootle
153com.github.dahenson.agenda 160com.github.dahenson.agenda
154com.github.johnfactotum.Foliate 161com.github.johnfactotum.Foliate
155com.github.phase1geo.minder 162com.github.phase1geo.minder
163com.github.tchx84.Flatseal
156com.gitlab.newsflash 164com.gitlab.newsflash
157conkeror 165conkeror
158conky 166conky
@@ -167,12 +175,14 @@ cvlc
167cyberfox 175cyberfox
168darktable 176darktable
169dconf-editor 177dconf-editor
178ddgr
170ddgtk 179ddgtk
171deadbeef 180deadbeef
172deluge 181deluge
173desktopeditors 182desktopeditors
174devhelp 183devhelp
175dex2jar 184dex2jar
185d-feet
176dia 186dia
177dig 187dig
178digikam 188digikam
@@ -186,7 +196,6 @@ display-im6.q16
186dnox 196dnox
187dnscrypt-proxy 197dnscrypt-proxy
188dnsmasq 198dnsmasq
189dolphin
190dolphin-emu 199dolphin-emu
191dooble 200dooble
192dooble-qt4 201dooble-qt4
@@ -195,13 +204,12 @@ dragon
195drawio 204drawio
196drill 205drill
197dropbox 206dropbox
198d-feet
199easystroke 207easystroke
200ebook-viewer
201ebook-convert 208ebook-convert
202ebook-edit 209ebook-edit
203ebook-meta 210ebook-meta
204ebook-polish 211ebook-polish
212ebook-viewer
205electron-mail 213electron-mail
206electrum 214electrum
207element-desktop 215element-desktop
@@ -251,8 +259,8 @@ flacsplt
251flameshot 259flameshot
252flashpeak-slimjet 260flashpeak-slimjet
253flowblade 261flowblade
254font-manager
255fontforge 262fontforge
263font-manager
256fossamail 264fossamail
257four-in-a-row 265four-in-a-row
258fractal 266fractal
@@ -271,9 +279,12 @@ freetube
271freshclam 279freshclam
272frogatto 280frogatto
273frozen-bubble 281frozen-bubble
282ftp
283funnyboat
274gajim 284gajim
275gajim-history-manager 285gajim-history-manager
276galculator 286galculator
287gallery-dl
277gapplication 288gapplication
278gcalccmd 289gcalccmd
279gcloud 290gcloud
@@ -291,8 +302,8 @@ gimp-2.10
291gimp-2.8 302gimp-2.8
292gist 303gist
293gist-paste 304gist-paste
294gitg
295git-cola 305git-cola
306gitg
296github-desktop 307github-desktop
297gitter 308gitter
298# gjs -- https://github.com/netblue30/firejail/issues/3333#issuecomment-612601102 309# gjs -- https://github.com/netblue30/firejail/issues/3333#issuecomment-612601102
@@ -342,6 +353,7 @@ gnome-weather
342gnote 353gnote
343gnubik 354gnubik
344godot 355godot
356goldendict
345goobox 357goobox
346google-chrome 358google-chrome
347google-chrome-beta 359google-chrome-beta
@@ -350,6 +362,7 @@ google-chrome-unstable
350google-earth 362google-earth
351google-earth-pro 363google-earth-pro
352google-play-music-desktop-player 364google-play-music-desktop-player
365googler
353gpa 366gpa
354gpicview 367gpicview
355gpredict 368gpredict
@@ -357,10 +370,11 @@ gradio
357gramps 370gramps
358gravity-beams-and-evaporating-stars 371gravity-beams-and-evaporating-stars
359gthumb 372gthumb
360gtk-straw-viewer
361gtk-youtube-viewer
362gtk2-youtube-viewer 373gtk2-youtube-viewer
363gtk3-youtube-viewer 374gtk3-youtube-viewer
375gtk-pipe-viewer
376gtk-straw-viewer
377gtk-youtube-viewer
364guayadeque 378guayadeque
365gucharmap 379gucharmap
366gummi 380gummi
@@ -386,9 +400,11 @@ idea.sh
386imagej 400imagej
387img2txt 401img2txt
388impressive 402impressive
403imv
389inkscape 404inkscape
390inkview 405inkview
391inox 406inox
407io.github.lainsce.Notejot
392ipcalc 408ipcalc
393ipcalc-ng 409ipcalc-ng
394iridium 410iridium
@@ -443,13 +459,16 @@ kube
443kwrite 459kwrite
444leafpad 460leafpad
445# less - breaks man 461# less - breaks man
462librecad
446libreoffice 463libreoffice
447librewolf 464librewolf
448librewolf-nightly 465librewolf-nightly
466lifeograph
449liferea 467liferea
450lightsoff 468lightsoff
451lincity-ng 469lincity-ng
452links 470links
471links2
453linphone 472linphone
454lmms 473lmms
455lobase 474lobase
@@ -489,6 +508,7 @@ mathematica
489matrix-mirage 508matrix-mirage
490mattermost-desktop 509mattermost-desktop
491mcabber 510mcabber
511mcomix
492mediainfo 512mediainfo
493mediathekview 513mediathekview
494megaglest 514megaglest
@@ -499,6 +519,7 @@ mendeleydesktop
499menulibre 519menulibre
500meteo-qt 520meteo-qt
501microsoft-edge 521microsoft-edge
522microsoft-edge-beta
502microsoft-edge-dev 523microsoft-edge-dev
503midori 524midori
504min 525min
@@ -515,8 +536,8 @@ mp3splt-gtk
515mp3wrap 536mp3wrap
516mpDris2 537mpDris2
517mpg123 538mpg123
518mpg123.bin
519mpg123-alsa 539mpg123-alsa
540mpg123.bin
520mpg123-id3dump 541mpg123-id3dump
521mpg123-jack 542mpg123-jack
522mpg123-nas 543mpg123-nas
@@ -555,6 +576,7 @@ mypaint
555mypaint-ora-thumbnailer 576mypaint-ora-thumbnailer
556natron 577natron
557ncdu 578ncdu
579ncdu2
558neochat 580neochat
559neomutt 581neomutt
560netactview 582netactview
@@ -575,6 +597,7 @@ nitroshare-nmh
575nitroshare-send 597nitroshare-send
576nitroshare-ui 598nitroshare-ui
577nomacs 599nomacs
600notable
578nslookup 601nslookup
579nuclear 602nuclear
580nylas 603nylas
@@ -585,22 +608,26 @@ odt2txt
585oggsplt 608oggsplt
586okular 609okular
587onboard 610onboard
611onionshare
612onionshare-cli
588onionshare-gui 613onionshare-gui
589ooffice 614ooffice
590ooviewdoc 615ooviewdoc
591open-invaders
592openarena 616openarena
593openarena_ded 617openarena_ded
594opencity 618opencity
595openclonk 619openclonk
620open-invaders
596openmw 621openmw
597openmw-launcher 622openmw-launcher
598openoffice.org 623openoffice.org
599openshot 624openshot
600openshot-qt 625openshot-qt
626openstego
601openttd 627openttd
602opera 628opera
603opera-beta 629opera-beta
630opera-developer
604orage 631orage
605ostrichriders 632ostrichriders
606otter-browser 633otter-browser
@@ -626,6 +653,7 @@ pinball
626pingus 653pingus
627pinta 654pinta
628pioneer 655pioneer
656pipe-viewer
629pithos 657pithos
630pitivi 658pitivi
631pix 659pix
@@ -648,7 +676,9 @@ pybitmessage
648# pycharm-professional 676# pycharm-professional
649# pzstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 677# pzstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
650qbittorrent 678qbittorrent
679qcomicbook
651qemu-launcher 680qemu-launcher
681#qemu-system-x86_64
652qgis 682qgis
653qlipper 683qlipper
654qmmp 684qmmp
@@ -662,11 +692,14 @@ quaternion
662quiterss 692quiterss
663qupzilla 693qupzilla
664qutebrowser 694qutebrowser
695raincat
665rambox 696rambox
666redeclipse 697redeclipse
698rednotebook
667redshift 699redshift
668regextester 700regextester
669remmina 701remmina
702retroarch
670rhythmbox 703rhythmbox
671rhythmbox-client 704rhythmbox-client
672ricochet 705ricochet
@@ -675,6 +708,7 @@ riot-web
675ripperx 708ripperx
676ristretto 709ristretto
677rocketchat 710rocketchat
711rpcs3
678rtorrent 712rtorrent
679runenpass.sh 713runenpass.sh
680sayonara 714sayonara
@@ -683,6 +717,7 @@ scorched3d
683scorchwentbonkers 717scorchwentbonkers
684scribus 718scribus
685sdat2img 719sdat2img
720seafile-applet
686seahorse 721seahorse
687seahorse-adventures 722seahorse-adventures
688seahorse-daemon 723seahorse-daemon
@@ -710,8 +745,8 @@ smuxi-frontend-gnome
710snox 745snox
711soffice 746soffice
712sol 747sol
713sound-juicer
714soundconverter 748soundconverter
749sound-juicer
715spectacle 750spectacle
716spectral 751spectral
717spotify 752spotify
@@ -745,6 +780,7 @@ teamspeak3
745teeworlds 780teeworlds
746telegram 781telegram
747telegram-desktop 782telegram-desktop
783telnet
748terasology 784terasology
749textmaker18 785textmaker18
750textmaker18free 786textmaker18free
@@ -753,6 +789,7 @@ thunderbird-beta
753thunderbird-wayland 789thunderbird-wayland
754tilp 790tilp
755tor-browser 791tor-browser
792torbrowser
756tor-browser-ar 793tor-browser-ar
757tor-browser-ca 794tor-browser-ca
758tor-browser-cs 795tor-browser-cs
@@ -774,6 +811,7 @@ tor-browser-it
774tor-browser-ja 811tor-browser-ja
775tor-browser-ka 812tor-browser-ka
776tor-browser-ko 813tor-browser-ko
814torbrowser-launcher
777tor-browser-nb 815tor-browser-nb
778tor-browser-nl 816tor-browser-nl
779tor-browser-pl 817tor-browser-pl
@@ -784,7 +822,6 @@ tor-browser-tr
784tor-browser-vi 822tor-browser-vi
785tor-browser-zh-cn 823tor-browser-zh-cn
786tor-browser-zh-tw 824tor-browser-zh-tw
787torbrowser-launcher
788torcs 825torcs
789totem 826totem
790tracker 827tracker
@@ -844,6 +881,7 @@ weechat
844weechat-curses 881weechat-curses
845wesnoth 882wesnoth
846wget 883wget
884wget2
847whalebird 885whalebird
848whois 886whois
849widelands 887widelands
@@ -852,10 +890,10 @@ wire-desktop
852wireshark 890wireshark
853wireshark-gtk 891wireshark-gtk
854wireshark-qt 892wireshark-qt
893wordwarvi
855wpp 894wpp
856wps 895wps
857wpspdf 896wpspdf
858wordwarvi
859x2goclient 897x2goclient
860xbill 898xbill
861xcalc 899xcalc
@@ -868,6 +906,7 @@ xfce4-notes
868xfce4-screenshooter 906xfce4-screenshooter
869xiphos 907xiphos
870xlinks 908xlinks
909xlinks2
871xmms 910xmms
872xmr-stak 911xmr-stak
873xonotic 912xonotic
@@ -889,13 +928,15 @@ yelp
889youtube 928youtube
890youtube-dl 929youtube-dl
891youtube-dl-gui 930youtube-dl-gui
892youtube-viewer
893youtubemusic-nativefier 931youtubemusic-nativefier
932youtube-viewer
933yt-dlp
894ytmdesktop 934ytmdesktop
895zaproxy 935zaproxy
896zart 936zart
897zathura 937zathura
898zeal 938zeal
939zim
899zoom 940zoom
900# zpaq - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 941# zpaq - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
901# zstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 942# zstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
diff --git a/src/firecfg/firecfg.h b/src/firecfg/firecfg.h
index 15826cf37..f54bfd5b5 100644
--- a/src/firecfg/firecfg.h
+++ b/src/firecfg/firecfg.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firecfg/main.c b/src/firecfg/main.c
index 363000e15..2f346fecd 100644
--- a/src/firecfg/main.c
+++ b/src/firecfg/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -171,17 +171,17 @@ static void set_file(const char *name, const char *firejail_exec) {
171 free(fname); 171 free(fname);
172} 172}
173 173
174// parse /usr/lib/firejail/firecfg.cfg file 174// parse /etc/firejail/firecfg.config file
175static void set_links_firecfg(void) { 175static void set_links_firecfg(void) {
176 char *cfgfile; 176 char *cfgfile;
177 if (asprintf(&cfgfile, "%s/firejail/firecfg.config", LIBDIR) == -1) 177 if (asprintf(&cfgfile, "%s/firecfg.config", SYSCONFDIR) == -1)
178 errExit("asprintf"); 178 errExit("asprintf");
179 179
180 char *firejail_exec; 180 char *firejail_exec;
181 if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1) 181 if (asprintf(&firejail_exec, "%s/bin/firejail", PREFIX) == -1)
182 errExit("asprintf"); 182 errExit("asprintf");
183 183
184 // parse /usr/lib/firejail/firecfg.cfg file 184 // parse /etc/firejail/firecfg.config file
185 FILE *fp = fopen(cfgfile, "r"); 185 FILE *fp = fopen(cfgfile, "r");
186 if (!fp) { 186 if (!fp) {
187 perror("fopen"); 187 perror("fopen");
@@ -440,7 +440,7 @@ int main(int argc, char **argv) {
440 // clear all symlinks 440 // clear all symlinks
441 clean(); 441 clean();
442 442
443 // set new symlinks based on /usr/lib/firejail/firecfg.cfg 443 // set new symlinks based on /etc/firejail/firecfg.config
444 set_links_firecfg(); 444 set_links_firecfg();
445 445
446 if (getuid() == 0) { 446 if (getuid() == 0) {
diff --git a/src/firecfg/sound.c b/src/firecfg/sound.c
index e3fcdbd83..9d04c951b 100644
--- a/src/firecfg/sound.c
+++ b/src/firecfg/sound.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firecfg/util.c b/src/firecfg/util.c
index 14d90b549..4697e7dd9 100644
--- a/src/firecfg/util.c
+++ b/src/firecfg/util.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c
index 59758bf2d..479473572 100644
--- a/src/firejail/appimage.c
+++ b/src/firejail/appimage.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -21,6 +21,7 @@
21// sudo mount -o loop krita-3.0-x86_64.appimage mnt 21// sudo mount -o loop krita-3.0-x86_64.appimage mnt
22 22
23#include "firejail.h" 23#include "firejail.h"
24#include "../include/gcov_wrapper.h"
24#include <sys/types.h> 25#include <sys/types.h>
25#include <sys/stat.h> 26#include <sys/stat.h>
26#include <sys/mount.h> 27#include <sys/mount.h>
@@ -30,6 +31,7 @@
30 31
31static char *devloop = NULL; // device file 32static char *devloop = NULL; // device file
32static long unsigned size = 0; // offset into appimage file 33static long unsigned size = 0; // offset into appimage file
34#define MAXBUF 4096
33 35
34#ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h 36#ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h
35static void err_loop(void) { 37static void err_loop(void) {
@@ -38,6 +40,36 @@ static void err_loop(void) {
38} 40}
39#endif 41#endif
40 42
43// return 1 if found
44int appimage_find_profile(const char *archive) {
45 assert(archive);
46 assert(strlen(archive));
47
48 // try to match the name of the archive with the list of programs in /etc/firejail/firecfg.config
49 FILE *fp = fopen(SYSCONFDIR "/firecfg.config", "r");
50 if (!fp) {
51 fprintf(stderr, "Error: cannot find %s, firejail is not correctly installed\n", SYSCONFDIR "/firecfg.config");
52 exit(1);
53 }
54 char buf[MAXBUF];
55 while (fgets(buf, MAXBUF, fp)) {
56 if (*buf == '#')
57 continue;
58 char *ptr = strchr(buf, '\n');
59 if (ptr)
60 *ptr = '\0';
61 if (strcasestr(archive, buf)) {
62 fclose(fp);
63 return profile_find_firejail(buf, 1);
64 }
65 }
66
67 fclose(fp);
68 return 0;
69
70}
71
72
41void appimage_set(const char *appimage) { 73void appimage_set(const char *appimage) {
42 assert(appimage); 74 assert(appimage);
43 assert(devloop == NULL); // don't call this twice! 75 assert(devloop == NULL); // don't call this twice!
@@ -67,7 +99,7 @@ void appimage_set(const char *appimage) {
67 99
68 // find or allocate a free loop device to use 100 // find or allocate a free loop device to use
69 EUID_ROOT(); 101 EUID_ROOT();
70 int cfd = open("/dev/loop-control", O_RDWR); 102 int cfd = open("/dev/loop-control", O_RDWR|O_CLOEXEC);
71 if (cfd == -1) 103 if (cfd == -1)
72 err_loop(); 104 err_loop();
73 int devnr = ioctl(cfd, LOOP_CTL_GET_FREE); 105 int devnr = ioctl(cfd, LOOP_CTL_GET_FREE);
@@ -78,7 +110,7 @@ void appimage_set(const char *appimage) {
78 errExit("asprintf"); 110 errExit("asprintf");
79 111
80 // associate loop device with appimage 112 // associate loop device with appimage
81 int lfd = open(devloop, O_RDONLY); 113 int lfd = open(devloop, O_RDONLY|O_CLOEXEC);
82 if (lfd == -1) 114 if (lfd == -1)
83 err_loop(); 115 err_loop();
84 if (ioctl(lfd, LOOP_SET_FD, ffd) == -1) 116 if (ioctl(lfd, LOOP_SET_FD, ffd) == -1)
@@ -109,9 +141,8 @@ void appimage_set(const char *appimage) {
109 141
110 if (cfg.cwd) 142 if (cfg.cwd)
111 env_store_name_val("OWD", cfg.cwd, SETENV); 143 env_store_name_val("OWD", cfg.cwd, SETENV);
112#ifdef HAVE_GCOV 144
113 __gcov_flush(); 145 __gcov_flush();
114#endif
115#else 146#else
116 fprintf(stderr, "Error: /dev/loop-control interface is not supported by your kernel\n"); 147 fprintf(stderr, "Error: /dev/loop-control interface is not supported by your kernel\n");
117 exit(1); 148 exit(1);
@@ -146,7 +177,7 @@ void appimage_mount(void) {
146void appimage_clear(void) { 177void appimage_clear(void) {
147 EUID_ROOT(); 178 EUID_ROOT();
148 if (devloop) { 179 if (devloop) {
149 int lfd = open(devloop, O_RDONLY); 180 int lfd = open(devloop, O_RDONLY|O_CLOEXEC);
150 if (lfd != -1) { 181 if (lfd != -1) {
151 if (ioctl(lfd, LOOP_CLR_FD, 0) != -1) 182 if (ioctl(lfd, LOOP_CLR_FD, 0) != -1)
152 fmessage("AppImage detached\n"); 183 fmessage("AppImage detached\n");
diff --git a/src/firejail/appimage_size.c b/src/firejail/appimage_size.c
index 43ca501da..4f8c7a7aa 100644
--- a/src/firejail/appimage_size.c
+++ b/src/firejail/appimage_size.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firejail/arp.c b/src/firejail/arp.c
index 1e9641097..cbd80dee0 100644
--- a/src/firejail/arp.c
+++ b/src/firejail/arp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,6 +20,7 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/socket.h> 21#include <sys/socket.h>
22#include <sys/ioctl.h> 22#include <sys/ioctl.h>
23#include <sys/time.h>
23#include <linux/if_ether.h> //TCP/IP Protocol Suite for Linux 24#include <linux/if_ether.h> //TCP/IP Protocol Suite for Linux
24#include <net/if.h> 25#include <net/if.h>
25#include <netinet/in.h> 26#include <netinet/in.h>
@@ -188,9 +189,14 @@ int arp_check(const char *dev, uint32_t destaddr) {
188 FD_SET(sock, &fds); 189 FD_SET(sock, &fds);
189 int maxfd = sock; 190 int maxfd = sock;
190 struct timeval ts; 191 struct timeval ts;
191 ts.tv_sec = 0; // 0.5 seconds wait time 192 gettimeofday(&ts, NULL);
192 ts.tv_usec = 500000; 193 double timerend = ts.tv_sec + ts.tv_usec / 1000000.0 + 0.5;
193 while (1) { 194 while (1) {
195 gettimeofday(&ts, NULL);
196 double now = ts.tv_sec + ts.tv_usec / 1000000.0;
197 double timeout = timerend - now;
198 ts.tv_sec = timeout;
199 ts.tv_usec = (timeout - ts.tv_sec) * 1000000;
194 int nready = select(maxfd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts); 200 int nready = select(maxfd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts);
195 if (nready < 0) 201 if (nready < 0)
196 errExit("select"); 202 errExit("select");
@@ -201,8 +207,8 @@ int arp_check(const char *dev, uint32_t destaddr) {
201 } 207 }
202 if (sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr)) <= 0) 208 if (sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr)) <= 0)
203 errExit("send"); 209 errExit("send");
204 ts.tv_sec = 0; // 0.5 seconds wait time 210 gettimeofday(&ts, NULL);
205 ts.tv_usec = 500000; 211 timerend = ts.tv_sec + ts.tv_usec / 1000000.0 + 0.5;
206 fflush(0); 212 fflush(0);
207 } 213 }
208 else { 214 else {
@@ -277,7 +283,7 @@ static uint32_t arp_random(const char *dev, Bridge *br) {
277 int i = 0; 283 int i = 0;
278 for (i = 0; i < 10; i++) { 284 for (i = 0; i < 10; i++) {
279 dest = start + ((uint32_t) rand()) % range; 285 dest = start + ((uint32_t) rand()) % range;
280 if (dest == ifip) // do not allow the interface address 286 if (dest == ifip || dest == cfg.defaultgw) // do not allow the interface address or the default gateway
281 continue; // try again 287 continue; // try again
282 288
283 // if we've made it up to here, we have a valid address 289 // if we've made it up to here, we have a valid address
@@ -325,7 +331,7 @@ static uint32_t arp_sequential(const char *dev, Bridge *br) {
325 331
326 // loop through addresses and stop as soon as you find an unused one 332 // loop through addresses and stop as soon as you find an unused one
327 while (dest <= last) { 333 while (dest <= last) {
328 if (dest == ifip) { 334 if (dest == ifip || dest == cfg.defaultgw) {
329 dest++; 335 dest++;
330 continue; 336 continue;
331 } 337 }
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c
index 1c952c0bc..fa9d3a940 100644
--- a/src/firejail/bandwidth.c
+++ b/src/firejail/bandwidth.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,6 +22,7 @@
22#include <sys/types.h> 22#include <sys/types.h>
23#include <sys/stat.h> 23#include <sys/stat.h>
24#include <unistd.h> 24#include <unistd.h>
25#include <errno.h>
25#include <net/if.h> 26#include <net/if.h>
26#include "firejail.h" 27#include "firejail.h"
27 28
@@ -119,26 +120,19 @@ static void bandwidth_create_run_file(pid_t pid) {
119 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) 120 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1)
120 errExit("asprintf"); 121 errExit("asprintf");
121 122
122 // if the file already exists, do nothing
123 struct stat s;
124 if (stat(fname, &s) == 0) {
125 free(fname);
126 return;
127 }
128
129 // create an empty file and set mod and ownership 123 // create an empty file and set mod and ownership
130 /* coverity[toctou] */ 124 // if the file already exists, do nothing
131 FILE *fp = fopen(fname, "w"); 125 FILE *fp = fopen(fname, "wxe");
132 if (fp) { 126 free(fname);
133 SET_PERMS_STREAM(fp, 0, 0, 0644); 127 if (!fp) {
134 fclose(fp); 128 if (errno == EEXIST)
135 } 129 return;
136 else {
137 fprintf(stderr, "Error: cannot create bandwidth file\n"); 130 fprintf(stderr, "Error: cannot create bandwidth file\n");
138 exit(1); 131 exit(1);
139 } 132 }
140 133
141 free(fname); 134 SET_PERMS_STREAM(fp, 0, 0, 0644);
135 fclose(fp);
142} 136}
143 137
144 138
@@ -148,7 +142,7 @@ void network_set_run_file(pid_t pid) {
148 errExit("asprintf"); 142 errExit("asprintf");
149 143
150 // create an empty file and set mod and ownership 144 // create an empty file and set mod and ownership
151 FILE *fp = fopen(fname, "w"); 145 FILE *fp = fopen(fname, "we");
152 if (fp) { 146 if (fp) {
153 if (cfg.bridge0.configured) 147 if (cfg.bridge0.configured)
154 fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox); 148 fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox);
@@ -178,7 +172,7 @@ static void read_bandwidth_file(pid_t pid) {
178 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) 172 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1)
179 errExit("asprintf"); 173 errExit("asprintf");
180 174
181 FILE *fp = fopen(fname, "r"); 175 FILE *fp = fopen(fname, "re");
182 if (fp) { 176 if (fp) {
183 char buf[1024]; 177 char buf[1024];
184 while (fgets(buf, 1024,fp)) { 178 while (fgets(buf, 1024,fp)) {
@@ -214,7 +208,7 @@ static void write_bandwidth_file(pid_t pid) {
214 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) 208 if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1)
215 errExit("asprintf"); 209 errExit("asprintf");
216 210
217 FILE *fp = fopen(fname, "w"); 211 FILE *fp = fopen(fname, "we");
218 if (fp) { 212 if (fp) {
219 IFBW *ptr = ifbw; 213 IFBW *ptr = ifbw;
220 while (ptr) { 214 while (ptr) {
@@ -307,7 +301,7 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in
307 char *fname; 301 char *fname;
308 if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1) 302 if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1)
309 errExit("asprintf"); 303 errExit("asprintf");
310 FILE *fp = fopen(fname, "r"); 304 FILE *fp = fopen(fname, "re");
311 if (!fp) { 305 if (!fp) {
312 fprintf(stderr, "Error: cannot read network map file %s\n", fname); 306 fprintf(stderr, "Error: cannot read network map file %s\n", fname);
313 exit(1); 307 exit(1);
diff --git a/src/firejail/caps.c b/src/firejail/caps.c
index 597f9915b..c5c06c675 100644
--- a/src/firejail/caps.c
+++ b/src/firejail/caps.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -389,7 +389,7 @@ static uint64_t extract_caps(int pid) {
389 errExit("asprintf"); 389 errExit("asprintf");
390 390
391 EUID_ROOT(); // grsecurity 391 EUID_ROOT(); // grsecurity
392 FILE *fp = fopen(file, "r"); 392 FILE *fp = fopen(file, "re");
393 EUID_USER(); // grsecurity 393 EUID_USER(); // grsecurity
394 if (!fp) 394 if (!fp)
395 goto errexit; 395 goto errexit;
diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c
index 986b1157d..f1e16187f 100644
--- a/src/firejail/cgroup.c
+++ b/src/firejail/cgroup.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,7 +18,9 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/stat.h> 21#include "../include/gcov_wrapper.h"
22#include <sys/wait.h>
23#include <errno.h>
22 24
23#define MAXBUF 4096 25#define MAXBUF 4096
24 26
@@ -26,7 +28,7 @@ void save_cgroup(void) {
26 if (cfg.cgroup == NULL) 28 if (cfg.cgroup == NULL)
27 return; 29 return;
28 30
29 FILE *fp = fopen(RUN_CGROUP_CFG, "w"); 31 FILE *fp = fopen(RUN_CGROUP_CFG, "wxe");
30 if (fp) { 32 if (fp) {
31 fprintf(fp, "%s", cfg.cgroup); 33 fprintf(fp, "%s", cfg.cgroup);
32 fflush(0); 34 fflush(0);
@@ -48,7 +50,7 @@ void load_cgroup(const char *fname) {
48 if (!fname) 50 if (!fname)
49 return; 51 return;
50 52
51 FILE *fp = fopen(fname, "r"); 53 FILE *fp = fopen(fname, "re");
52 if (fp) { 54 if (fp) {
53 char buf[MAXBUF]; 55 char buf[MAXBUF];
54 if (fgets(buf, MAXBUF, fp)) { 56 if (fgets(buf, MAXBUF, fp)) {
@@ -68,52 +70,63 @@ errout:
68 fclose(fp); 70 fclose(fp);
69} 71}
70 72
73static int is_cgroup_path(const char *fname) {
74 // path starts with /sys/fs/cgroup
75 if (strncmp(fname, "/sys/fs/cgroup", 14) != 0)
76 return 0;
71 77
72void set_cgroup(const char *path) { 78 // no .. traversal
73 EUID_ASSERT(); 79 char *ptr = strstr(fname, "..");
80 if (ptr)
81 return 0;
74 82
75 invalid_filename(path, 0); // no globbing 83 return 1;
84}
76 85
77 // path starts with /sys/fs/cgroup 86void check_cgroup_file(const char *fname) {
78 if (strncmp(path, "/sys/fs/cgroup", 14) != 0) 87 assert(fname);
79 goto errout; 88 invalid_filename(fname, 0); // no globbing
80 89
81 // path ends in tasks 90 if (!is_cgroup_path(fname))
82 char *ptr = strstr(path, "tasks");
83 if (!ptr)
84 goto errout;
85 if (*(ptr + 5) != '\0')
86 goto errout; 91 goto errout;
87 92
88 // no .. traversal 93 const char *base = gnu_basename(fname);
89 ptr = strstr(path, ".."); 94 if (strcmp(base, "tasks") != 0 && // cgroup v1
90 if (ptr) 95 strcmp(base, "cgroup.procs") != 0)
91 goto errout; 96 goto errout;
92 97
93 // tasks file exists 98 if (access(fname, W_OK) == 0)
94 struct stat s; 99 return;
95 if (stat(path, &s) == -1)
96 goto errout;
97 100
98 // task file belongs to the user running the sandbox 101errout:
99 if (s.st_uid != getuid() && s.st_gid != getgid()) 102 fprintf(stderr, "Error: invalid cgroup\n");
100 goto errout2; 103 exit(1);
104}
105
106static void do_set_cgroup(const char *fname, pid_t pid) {
107 FILE *fp = fopen(fname, "ae");
108 if (!fp) {
109 fwarning("cannot open %s for writing: %s\n", fname, strerror(errno));
110 return;
111 }
101 112
102 // add the task to cgroup
103 /* coverity[toctou] */
104 FILE *fp = fopen(path, "a");
105 if (!fp)
106 goto errout;
107 pid_t pid = getpid();
108 int rv = fprintf(fp, "%d\n", pid); 113 int rv = fprintf(fp, "%d\n", pid);
109 (void) rv; 114 (void) rv;
110 fclose(fp); 115 fclose(fp);
111 return; 116}
112 117
113errout: 118void set_cgroup(const char *fname, pid_t pid) {
114 fprintf(stderr, "Error: invalid cgroup\n"); 119 pid_t child = fork();
115 exit(1); 120 if (child < 0)
116errout2: 121 errExit("fork");
117 fprintf(stderr, "Error: you don't have permissions to use this control group\n"); 122 if (child == 0) {
118 exit(1); 123 drop_privs(0);
124
125 do_set_cgroup(fname, pid);
126
127 __gcov_flush();
128
129 _exit(0);
130 }
131 waitpid(child, NULL, 0);
119} 132}
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index e1613b325..6fc70318b 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -35,6 +35,8 @@ char *xvfb_extra_params = "";
35char *netfilter_default = NULL; 35char *netfilter_default = NULL;
36unsigned long join_timeout = 5000000; // microseconds 36unsigned long join_timeout = 5000000; // microseconds
37char *config_seccomp_error_action_str = "EPERM"; 37char *config_seccomp_error_action_str = "EPERM";
38char *config_seccomp_filter_add = NULL;
39char **whitelist_reject_topdirs = NULL;
38 40
39int checkcfg(int val) { 41int checkcfg(int val) {
40 assert(val < CFG_MAX); 42 assert(val < CFG_MAX);
@@ -56,10 +58,11 @@ int checkcfg(int val) {
56 cfg_val[CFG_XPRA_ATTACH] = 0; 58 cfg_val[CFG_XPRA_ATTACH] = 0;
57 cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1; 59 cfg_val[CFG_SECCOMP_ERROR_ACTION] = -1;
58 cfg_val[CFG_BROWSER_ALLOW_DRM] = 0; 60 cfg_val[CFG_BROWSER_ALLOW_DRM] = 0;
61 cfg_val[CFG_ALLOW_TRAY] = 0;
59 62
60 // open configuration file 63 // open configuration file
61 const char *fname = SYSCONFDIR "/firejail.config"; 64 const char *fname = SYSCONFDIR "/firejail.config";
62 fp = fopen(fname, "r"); 65 fp = fopen(fname, "re");
63 if (!fp) { 66 if (!fp) {
64#ifdef HAVE_GLOBALCFG 67#ifdef HAVE_GLOBALCFG
65 fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname); 68 fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname);
@@ -102,22 +105,25 @@ int checkcfg(int val) {
102 PARSE_YESNO(CFG_USERNS, "userns") 105 PARSE_YESNO(CFG_USERNS, "userns")
103 PARSE_YESNO(CFG_CHROOT, "chroot") 106 PARSE_YESNO(CFG_CHROOT, "chroot")
104 PARSE_YESNO(CFG_FIREJAIL_PROMPT, "firejail-prompt") 107 PARSE_YESNO(CFG_FIREJAIL_PROMPT, "firejail-prompt")
105 PARSE_YESNO(CFG_FOLLOW_SYMLINK_AS_USER, "follow-symlink-as-user")
106 PARSE_YESNO(CFG_FORCE_NONEWPRIVS, "force-nonewprivs") 108 PARSE_YESNO(CFG_FORCE_NONEWPRIVS, "force-nonewprivs")
107 PARSE_YESNO(CFG_SECCOMP, "seccomp") 109 PARSE_YESNO(CFG_SECCOMP, "seccomp")
108 PARSE_YESNO(CFG_WHITELIST, "whitelist")
109 PARSE_YESNO(CFG_NETWORK, "network") 110 PARSE_YESNO(CFG_NETWORK, "network")
110 PARSE_YESNO(CFG_RESTRICTED_NETWORK, "restricted-network") 111 PARSE_YESNO(CFG_RESTRICTED_NETWORK, "restricted-network")
111 PARSE_YESNO(CFG_XEPHYR_WINDOW_TITLE, "xephyr-window-title") 112 PARSE_YESNO(CFG_XEPHYR_WINDOW_TITLE, "xephyr-window-title")
112 PARSE_YESNO(CFG_OVERLAYFS, "overlayfs") 113 PARSE_YESNO(CFG_OVERLAYFS, "overlayfs")
113 PARSE_YESNO(CFG_PRIVATE_HOME, "private-home") 114 PARSE_YESNO(CFG_PRIVATE_BIN, "private-bin")
115 PARSE_YESNO(CFG_PRIVATE_BIN_NO_LOCAL, "private-bin-no-local")
114 PARSE_YESNO(CFG_PRIVATE_CACHE, "private-cache") 116 PARSE_YESNO(CFG_PRIVATE_CACHE, "private-cache")
117 PARSE_YESNO(CFG_PRIVATE_ETC, "private-etc")
118 PARSE_YESNO(CFG_PRIVATE_HOME, "private-home")
115 PARSE_YESNO(CFG_PRIVATE_LIB, "private-lib") 119 PARSE_YESNO(CFG_PRIVATE_LIB, "private-lib")
116 PARSE_YESNO(CFG_PRIVATE_BIN_NO_LOCAL, "private-bin-no-local") 120 PARSE_YESNO(CFG_PRIVATE_OPT, "private-opt")
121 PARSE_YESNO(CFG_PRIVATE_SRV, "private-srv")
117 PARSE_YESNO(CFG_DISABLE_MNT, "disable-mnt") 122 PARSE_YESNO(CFG_DISABLE_MNT, "disable-mnt")
118 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach") 123 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach")
119 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f") 124 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f")
120 PARSE_YESNO(CFG_BROWSER_ALLOW_DRM, "browser-allow-drm") 125 PARSE_YESNO(CFG_BROWSER_ALLOW_DRM, "browser-allow-drm")
126 PARSE_YESNO(CFG_ALLOW_TRAY, "allow-tray")
121#undef PARSE_YESNO 127#undef PARSE_YESNO
122 128
123 // netfilter 129 // netfilter
@@ -130,8 +136,7 @@ int checkcfg(int val) {
130 *end = '\0'; 136 *end = '\0';
131 137
132 // is the file present? 138 // is the file present?
133 struct stat s; 139 if (access(fname, F_OK) == -1) {
134 if (stat(fname, &s) == -1) {
135 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname); 140 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname);
136 exit(1); 141 exit(1);
137 } 142 }
@@ -222,6 +227,10 @@ int checkcfg(int val) {
222 else if (strncmp(ptr, "join-timeout ", 13) == 0) 227 else if (strncmp(ptr, "join-timeout ", 13) == 0)
223 join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds 228 join_timeout = strtoul(ptr + 13, NULL, 10) * 1000000; // seconds to microseconds
224 229
230 // add rules to default seccomp filter
231 else if (strncmp(ptr, "seccomp-filter-add ", 19) == 0)
232 config_seccomp_filter_add = seccomp_check_list(ptr + 19);
233
225 // seccomp error action 234 // seccomp error action
226 else if (strncmp(ptr, "seccomp-error-action ", 21) == 0) { 235 else if (strncmp(ptr, "seccomp-error-action ", 21) == 0) {
227 if (strcmp(ptr + 21, "kill") == 0) 236 if (strcmp(ptr + 21, "kill") == 0)
@@ -238,6 +247,31 @@ int checkcfg(int val) {
238 errExit("strdup"); 247 errExit("strdup");
239 } 248 }
240 249
250 else if (strncmp(ptr, "whitelist-disable-topdir ", 25) == 0) {
251 char *str = strdup(ptr + 25);
252 if (!str)
253 errExit("strdup");
254
255 size_t cnt = 0;
256 size_t sz = 4;
257 whitelist_reject_topdirs = malloc(sz * sizeof(char *));
258 if (!whitelist_reject_topdirs)
259 errExit("malloc");
260
261 char *tok = strtok(str, ",");
262 while (tok) {
263 whitelist_reject_topdirs[cnt++] = tok;
264 if (cnt >= sz) {
265 sz *= 2;
266 whitelist_reject_topdirs = realloc(whitelist_reject_topdirs, sz * sizeof(char *));
267 if (!whitelist_reject_topdirs)
268 errExit("realloc");
269 }
270 tok = strtok(NULL, ",");
271 }
272 whitelist_reject_topdirs[cnt] = NULL;
273 }
274
241 else 275 else
242 goto errout; 276 goto errout;
243 277
@@ -269,7 +303,7 @@ errout:
269 303
270void print_compiletime_support(void) { 304void print_compiletime_support(void) {
271 printf("Compile time support:\n"); 305 printf("Compile time support:\n");
272 printf("\t- Always force nonewprivs support is %s\n", 306 printf("\t- always force nonewprivs support is %s\n",
273#ifdef HAVE_FORCE_NONEWPRIVS 307#ifdef HAVE_FORCE_NONEWPRIVS
274 "enabled" 308 "enabled"
275#else 309#else
@@ -309,14 +343,6 @@ void print_compiletime_support(void) {
309#endif 343#endif
310 ); 344 );
311 345
312 printf("\t- file and directory whitelisting support is %s\n",
313#ifdef HAVE_WHITELIST
314 "enabled"
315#else
316 "disabled"
317#endif
318 );
319
320 printf("\t- file transfer support is %s\n", 346 printf("\t- file transfer support is %s\n",
321#ifdef HAVE_FILE_TRANSFER 347#ifdef HAVE_FILE_TRANSFER
322 "enabled" 348 "enabled"
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
index d7e96cf4c..551948318 100644
--- a/src/firejail/chroot.c
+++ b/src/firejail/chroot.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,6 +20,7 @@
20 20
21#ifdef HAVE_CHROOT 21#ifdef HAVE_CHROOT
22#include "firejail.h" 22#include "firejail.h"
23#include "../include/gcov_wrapper.h"
23#include <sys/mount.h> 24#include <sys/mount.h>
24#include <sys/sendfile.h> 25#include <sys/sendfile.h>
25#include <errno.h> 26#include <errno.h>
@@ -29,7 +30,6 @@
29#define O_PATH 010000000 30#define O_PATH 010000000
30#endif 31#endif
31 32
32
33// exit if error 33// exit if error
34void fs_check_chroot_dir(void) { 34void fs_check_chroot_dir(void) {
35 EUID_ASSERT(); 35 EUID_ASSERT();
@@ -86,7 +86,7 @@ static void update_file(int parentfd, const char *relpath) {
86 if (arg_debug) 86 if (arg_debug)
87 printf("Updating chroot /%s\n", relpath); 87 printf("Updating chroot /%s\n", relpath);
88 unlinkat(parentfd, relpath, 0); 88 unlinkat(parentfd, relpath, 0);
89 int out = openat(parentfd, relpath, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 89 int out = openat(parentfd, relpath, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
90 if (out == -1) { 90 if (out == -1) {
91 close(in); 91 close(in);
92 goto errout; 92 goto errout;
@@ -131,9 +131,9 @@ void fs_chroot(const char *rootdir) {
131 assert(rootdir); 131 assert(rootdir);
132 132
133 // fails if there is any symlink or if rootdir is not a directory 133 // fails if there is any symlink or if rootdir is not a directory
134 int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 134 int parentfd = safer_openat(-1, rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
135 if (parentfd == -1) 135 if (parentfd == -1)
136 errExit("safe_fd"); 136 errExit("safer_openat");
137 // rootdir has to be owned by root and is not allowed to be generally writable, 137 // rootdir has to be owned by root and is not allowed to be generally writable,
138 // this also excludes /tmp and friends 138 // this also excludes /tmp and friends
139 struct stat s; 139 struct stat s;
@@ -163,12 +163,8 @@ void fs_chroot(const char *rootdir) {
163 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 163 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
164 if (fd == -1) 164 if (fd == -1)
165 errExit("open"); 165 errExit("open");
166 char *proc; 166 if (bind_mount_path_to_fd("/dev", fd))
167 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
168 errExit("asprintf");
169 if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
170 errExit("mounting /dev"); 167 errExit("mounting /dev");
171 free(proc);
172 close(fd); 168 close(fd);
173 169
174#ifdef HAVE_X11 170#ifdef HAVE_X11
@@ -192,11 +188,8 @@ void fs_chroot(const char *rootdir) {
192 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 188 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
193 if (fd == -1) 189 if (fd == -1)
194 errExit("open"); 190 errExit("open");
195 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 191 if (bind_mount_path_to_fd("/tmp/.X11-unix", fd))
196 errExit("asprintf");
197 if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
198 errExit("mounting /tmp/.X11-unix"); 192 errExit("mounting /tmp/.X11-unix");
199 free(proc);
200 close(fd); 193 close(fd);
201 } 194 }
202#endif // HAVE_X11 195#endif // HAVE_X11
@@ -215,29 +208,21 @@ void fs_chroot(const char *rootdir) {
215 208
216 if (arg_debug) 209 if (arg_debug)
217 printf("Mounting %s on chroot %s\n", orig_pulse, orig_pulse); 210 printf("Mounting %s on chroot %s\n", orig_pulse, orig_pulse);
218 int src = safe_fd(orig_pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 211 int src = safer_openat(-1, orig_pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
219 if (src == -1) { 212 if (src == -1) {
220 fprintf(stderr, "Error: cannot open %s\n", orig_pulse); 213 fprintf(stderr, "Error: cannot open %s\n", orig_pulse);
221 exit(1); 214 exit(1);
222 } 215 }
223 int dst = safe_fd(pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 216 int dst = safer_openat(-1, pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
224 if (dst == -1) { 217 if (dst == -1) {
225 fprintf(stderr, "Error: cannot open %s\n", pulse); 218 fprintf(stderr, "Error: cannot open %s\n", pulse);
226 exit(1); 219 exit(1);
227 } 220 }
228 free(pulse); 221 if (bind_mount_by_fd(src, dst))
229 222 errExit("mounting pulseaudio");
230 char *proc_src, *proc_dst;
231 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1)
232 errExit("asprintf");
233 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
234 errExit("asprintf");
235 if (mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL) < 0)
236 errExit("mount bind");
237 free(proc_src);
238 free(proc_dst);
239 close(src); 223 close(src);
240 close(dst); 224 close(dst);
225 free(pulse);
241 226
242 // update /etc/machine-id in chroot 227 // update /etc/machine-id in chroot
243 update_file(parentfd, "etc/machine-id"); 228 update_file(parentfd, "etc/machine-id");
@@ -256,11 +241,8 @@ void fs_chroot(const char *rootdir) {
256 fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 241 fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
257 if (fd == -1) 242 if (fd == -1)
258 errExit("open"); 243 errExit("open");
259 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 244 if (bind_mount_path_to_fd(RUN_FIREJAIL_LIB_DIR, fd))
260 errExit("asprintf");
261 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
262 errExit("mount bind"); 245 errExit("mount bind");
263 free(proc);
264 close(fd); 246 close(fd);
265 247
266 // create /run/firejail/mnt directory in chroot 248 // create /run/firejail/mnt directory in chroot
@@ -271,29 +253,22 @@ void fs_chroot(const char *rootdir) {
271 fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 253 fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
272 if (fd == -1) 254 if (fd == -1)
273 errExit("open"); 255 errExit("open");
274 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 256 if (bind_mount_path_to_fd(RUN_MNT_DIR, fd))
275 errExit("asprintf");
276 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
277 errExit("mount bind"); 257 errExit("mount bind");
278 free(proc);
279 close(fd); 258 close(fd);
280 259
281 // update chroot resolv.conf 260 // update chroot resolv.conf
282 update_file(parentfd, "etc/resolv.conf"); 261 update_file(parentfd, "etc/resolv.conf");
283 262
284#ifdef HAVE_GCOV
285 __gcov_flush(); 263 __gcov_flush();
286#endif 264
287 // create /run/firejail/mnt/oroot 265 // create /run/firejail/mnt/oroot
288 char *oroot = RUN_OVERLAY_ROOT; 266 char *oroot = RUN_OVERLAY_ROOT;
289 if (mkdir(oroot, 0755) == -1) 267 if (mkdir(oroot, 0755) == -1)
290 errExit("mkdir"); 268 errExit("mkdir");
291 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay 269 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
292 if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1) 270 if (bind_mount_fd_to_path(parentfd, oroot))
293 errExit("asprintf");
294 if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
295 errExit("mounting rootdir oroot"); 271 errExit("mounting rootdir oroot");
296 free(proc);
297 close(parentfd); 272 close(parentfd);
298 // chroot into the new directory 273 // chroot into the new directory
299 if (arg_debug) 274 if (arg_debug)
diff --git a/src/firejail/cmdline.c b/src/firejail/cmdline.c
index f902c4e1c..6f7739da0 100644
--- a/src/firejail/cmdline.c
+++ b/src/firejail/cmdline.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -26,7 +26,7 @@
26#include <assert.h> 26#include <assert.h>
27#include <errno.h> 27#include <errno.h>
28 28
29static int cmdline_length(int argc, char **argv, int index) { 29static int cmdline_length(int argc, char **argv, int index, bool want_extra_quotes) {
30 assert(index != -1); 30 assert(index != -1);
31 31
32 unsigned i,j; 32 unsigned i,j;
@@ -46,10 +46,11 @@ static int cmdline_length(int argc, char **argv, int index) {
46 len += 3; 46 len += 3;
47 in_quotes = false; 47 in_quotes = false;
48 } else { 48 } else {
49 if (!in_quotes) 49 if (!in_quotes && want_extra_quotes)
50 len++; 50 len++;
51 len++; 51 len++;
52 in_quotes = true; 52 if (want_extra_quotes)
53 in_quotes = true;
53 } 54 }
54 } 55 }
55 if (in_quotes) { 56 if (in_quotes) {
@@ -64,7 +65,7 @@ static int cmdline_length(int argc, char **argv, int index) {
64 return len; 65 return len;
65} 66}
66 67
67static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index) { 68static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index, bool want_extra_quotes) {
68 assert(index != -1); 69 assert(index != -1);
69 70
70 unsigned i,j; 71 unsigned i,j;
@@ -103,14 +104,15 @@ static void quote_cmdline(char *command_line, char *window_title, int len, int a
103 // anything other 104 // anything other
104 else 105 else
105 { 106 {
106 if (!in_quotes) { 107 if (!in_quotes && want_extra_quotes) {
107 // open quotes 108 // open quotes
108 ptr1[0] = '\''; 109 ptr1[0] = '\'';
109 ptr1++; 110 ptr1++;
110 } 111 }
111 ptr1[0] = argv[i + index][j]; 112 ptr1[0] = argv[i + index][j];
112 ptr1++; 113 ptr1++;
113 in_quotes = true; 114 if (want_extra_quotes)
115 in_quotes = true;
114 } 116 }
115 } 117 }
116 // close quotes 118 // close quotes
@@ -134,12 +136,12 @@ static void quote_cmdline(char *command_line, char *window_title, int len, int a
134 assert((unsigned) len == strlen(command_line)); 136 assert((unsigned) len == strlen(command_line));
135} 137}
136 138
137void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { 139void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes) {
138 // index == -1 could happen if we have --shell=none and no program was specified 140 // index == -1 could happen if we have --shell=none and no program was specified
139 // the program should exit with an error before entering this function 141 // the program should exit with an error before entering this function
140 assert(index != -1); 142 assert(index != -1);
141 143
142 int len = cmdline_length(argc, argv, index); 144 int len = cmdline_length(argc, argv, index, want_extra_quotes);
143 if (len > ARG_MAX) { 145 if (len > ARG_MAX) {
144 errno = E2BIG; 146 errno = E2BIG;
145 errExit("cmdline_length"); 147 errExit("cmdline_length");
@@ -152,7 +154,7 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar
152 if (!*window_title) 154 if (!*window_title)
153 errExit("malloc"); 155 errExit("malloc");
154 156
155 quote_cmdline(*command_line, *window_title, len, argc, argv, index); 157 quote_cmdline(*command_line, *window_title, len, argc, argv, index, want_extra_quotes);
156 158
157 if (arg_debug) 159 if (arg_debug)
158 printf("Building quoted command line: %s\n", *command_line); 160 printf("Building quoted command line: %s\n", *command_line);
@@ -161,17 +163,17 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar
161 assert(*window_title); 163 assert(*window_title);
162} 164}
163 165
164void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { 166void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes) {
165 // index == -1 could happen if we have --shell=none and no program was specified 167 // index == -1 could happen if we have --shell=none and no program was specified
166 // the program should exit with an error before entering this function 168 // the program should exit with an error before entering this function
167 assert(index != -1); 169 assert(index != -1);
168 170
169 char *apprun_path = RUN_FIREJAIL_APPIMAGE_DIR "/AppRun"; 171 char *apprun_path = RUN_FIREJAIL_APPIMAGE_DIR "/AppRun";
170 172
171 int len1 = cmdline_length(argc, argv, index); // length of argv w/o changes 173 int len1 = cmdline_length(argc, argv, index, want_extra_quotes); // length of argv w/o changes
172 int len2 = cmdline_length(1, &argv[index], 0); // apptest.AppImage 174 int len2 = cmdline_length(1, &argv[index], 0, want_extra_quotes); // apptest.AppImage
173 int len3 = cmdline_length(1, &apprun_path, 0); // /run/firejail/appimage/AppRun 175 int len3 = cmdline_length(1, &apprun_path, 0, want_extra_quotes); // /run/firejail/appimage/AppRun
174 int len4 = (len1 - len2 + len3) + 1; // apptest.AppImage is replaced by /path/to/AppRun 176 int len4 = (len1 - len2 + len3) + 1; // apptest.AppImage is replaced by /path/to/AppRun
175 177
176 if (len4 > ARG_MAX) { 178 if (len4 > ARG_MAX) {
177 errno = E2BIG; 179 errno = E2BIG;
@@ -187,7 +189,7 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
187 errExit("malloc"); 189 errExit("malloc");
188 190
189 // run default quote_cmdline 191 // run default quote_cmdline
190 quote_cmdline(command_line_tmp, *window_title, len1, argc, argv, index); 192 quote_cmdline(command_line_tmp, *window_title, len1, argc, argv, index, want_extra_quotes);
191 193
192 assert(command_line_tmp); 194 assert(command_line_tmp);
193 assert(*window_title); 195 assert(*window_title);
diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c
index 3427e8ade..1ec510456 100644
--- a/src/firejail/cpu.c
+++ b/src/firejail/cpu.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -75,7 +75,7 @@ void save_cpu(void) {
75 if (cfg.cpus == 0) 75 if (cfg.cpus == 0)
76 return; 76 return;
77 77
78 FILE *fp = fopen(RUN_CPU_CFG, "w"); 78 FILE *fp = fopen(RUN_CPU_CFG, "wxe");
79 if (fp) { 79 if (fp) {
80 fprintf(fp, "%x\n", cfg.cpus); 80 fprintf(fp, "%x\n", cfg.cpus);
81 SET_PERMS_STREAM(fp, 0, 0, 0600); 81 SET_PERMS_STREAM(fp, 0, 0, 0600);
@@ -91,7 +91,7 @@ void load_cpu(const char *fname) {
91 if (!fname) 91 if (!fname)
92 return; 92 return;
93 93
94 FILE *fp = fopen(fname, "r"); 94 FILE *fp = fopen(fname, "re");
95 if (fp) { 95 if (fp) {
96 unsigned tmp; 96 unsigned tmp;
97 int rv = fscanf(fp, "%x", &tmp); 97 int rv = fscanf(fp, "%x", &tmp);
@@ -139,7 +139,7 @@ static void print_cpu(int pid) {
139 } 139 }
140 140
141 EUID_ROOT(); // grsecurity 141 EUID_ROOT(); // grsecurity
142 FILE *fp = fopen(file, "r"); 142 FILE *fp = fopen(file, "re");
143 EUID_USER(); // grsecurity 143 EUID_USER(); // grsecurity
144 if (!fp) { 144 if (!fp) {
145 printf(" Error: cannot open %s\n", file); 145 printf(" Error: cannot open %s\n", file);
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c
index 658b84537..66738bd4b 100644
--- a/src/firejail/dbus.c
+++ b/src/firejail/dbus.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -180,7 +180,7 @@ static void dbus_check_bus_profile(char const *prefix, DbusPolicy *policy) {
180 } 180 }
181 } 181 }
182 182
183 if (num_matches > 0) { 183 if (num_matches > 0 && !arg_quiet) {
184 assert(first_match != NULL); 184 assert(first_match != NULL);
185 if (num_matches == 1) { 185 if (num_matches == 1) {
186 fprintf(stderr, "Ignoring \"%s\".\n", first_match); 186 fprintf(stderr, "Ignoring \"%s\".\n", first_match);
@@ -258,12 +258,8 @@ static char *find_user_socket_by_format(char *format) {
258 if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1) 258 if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1)
259 errExit("asprintf"); 259 errExit("asprintf");
260 struct stat s; 260 struct stat s;
261 if (stat(dbus_user_socket, &s) == -1) { 261 if (lstat(dbus_user_socket, &s) == -1)
262 if (errno == ENOENT) 262 goto fail;
263 goto fail;
264 return NULL;
265 errExit("stat");
266 }
267 if (!S_ISSOCK(s.st_mode)) 263 if (!S_ISSOCK(s.st_mode))
268 goto fail; 264 goto fail;
269 return dbus_user_socket; 265 return dbus_user_socket;
@@ -301,11 +297,12 @@ void dbus_proxy_start(void) {
301 if (dbus_proxy_pid == -1) 297 if (dbus_proxy_pid == -1)
302 errExit("fork"); 298 errExit("fork");
303 if (dbus_proxy_pid == 0) { 299 if (dbus_proxy_pid == 0) {
304 int i; 300 // close open files
305 for (i = STDERR_FILENO + 1; i < FIREJAIL_MAX_FD; i++) { 301 int keep[2];
306 if (i != status_pipe[1] && i != args_pipe[0]) 302 keep[0] = status_pipe[1];
307 close(i); // close open files 303 keep[1] = args_pipe[0];
308 } 304 close_all(keep, ARRAY_SIZE(keep));
305
309 if (arg_dbus_log_file != NULL) { 306 if (arg_dbus_log_file != NULL) {
310 int output_fd = creat(arg_dbus_log_file, 0666); 307 int output_fd = creat(arg_dbus_log_file, 0666);
311 if (output_fd < 0) 308 if (output_fd < 0)
@@ -416,7 +413,7 @@ void dbus_proxy_stop(void) {
416} 413}
417 414
418static void socket_overlay(char *socket_path, char *proxy_path) { 415static void socket_overlay(char *socket_path, char *proxy_path) {
419 int fd = safe_fd(proxy_path, O_PATH | O_NOFOLLOW | O_CLOEXEC); 416 int fd = safer_openat(-1, proxy_path, O_PATH | O_NOFOLLOW | O_CLOEXEC);
420 if (fd == -1) 417 if (fd == -1)
421 errExit("opening DBus proxy socket"); 418 errExit("opening DBus proxy socket");
422 struct stat s; 419 struct stat s;
@@ -426,12 +423,8 @@ static void socket_overlay(char *socket_path, char *proxy_path) {
426 errno = ENOTSOCK; 423 errno = ENOTSOCK;
427 errExit("mounting DBus proxy socket"); 424 errExit("mounting DBus proxy socket");
428 } 425 }
429 char *proxy_fd_path; 426 if (bind_mount_fd_to_path(fd, socket_path))
430 if (asprintf(&proxy_fd_path, "/proc/self/fd/%d", fd) == -1)
431 errExit("asprintf");
432 if (mount(proxy_path, socket_path, NULL, MS_BIND | MS_REC, NULL) == -1)
433 errExit("mount bind"); 427 errExit("mount bind");
434 free(proxy_fd_path);
435 close(fd); 428 close(fd);
436} 429}
437 430
@@ -478,7 +471,7 @@ void dbus_apply_policy(void) {
478 create_empty_dir_as_root(RUN_DBUS_DIR, 0755); 471 create_empty_dir_as_root(RUN_DBUS_DIR, 0755);
479 472
480 if (arg_dbus_user != DBUS_POLICY_ALLOW) { 473 if (arg_dbus_user != DBUS_POLICY_ALLOW) {
481 create_empty_file_as_root(RUN_DBUS_USER_SOCKET, 0700); 474 create_empty_file_as_root(RUN_DBUS_USER_SOCKET, 0600);
482 475
483 if (arg_dbus_user == DBUS_POLICY_FILTER) { 476 if (arg_dbus_user == DBUS_POLICY_FILTER) {
484 assert(dbus_user_proxy_socket != NULL); 477 assert(dbus_user_proxy_socket != NULL);
@@ -517,7 +510,7 @@ void dbus_apply_policy(void) {
517 } 510 }
518 511
519 if (arg_dbus_system != DBUS_POLICY_ALLOW) { 512 if (arg_dbus_system != DBUS_POLICY_ALLOW) {
520 create_empty_file_as_root(RUN_DBUS_SYSTEM_SOCKET, 0700); 513 create_empty_file_as_root(RUN_DBUS_SYSTEM_SOCKET, 0600);
521 514
522 if (arg_dbus_system == DBUS_POLICY_FILTER) { 515 if (arg_dbus_system == DBUS_POLICY_FILTER) {
523 assert(dbus_system_proxy_socket != NULL); 516 assert(dbus_system_proxy_socket != NULL);
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c
index bdbb338d5..fb66d74ff 100644
--- a/src/firejail/dhcp.c
+++ b/src/firejail/dhcp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -93,7 +93,7 @@ static pid_t dhcp_read_pidfile(const Dhclient *client) {
93 while (found == 0 && tries < 10) { 93 while (found == 0 && tries < 10) {
94 if (tries >= 1) 94 if (tries >= 1)
95 usleep(100000); 95 usleep(100000);
96 FILE *pidfile = fopen(client->pid_file, "r"); 96 FILE *pidfile = fopen(client->pid_file, "re");
97 if (pidfile) { 97 if (pidfile) {
98 long pid; 98 long pid;
99 if (fscanf(pidfile, "%ld", &pid) == 1) 99 if (fscanf(pidfile, "%ld", &pid) == 1)
@@ -153,19 +153,13 @@ void dhcp_start(void) {
153 if (!any_dhcp()) 153 if (!any_dhcp())
154 return; 154 return;
155 155
156 char *dhclient_path = RUN_MNT_DIR "/dhclient";; 156 char *dhclient_path = RUN_MNT_DIR "/dhclient";
157 struct stat s; 157 struct stat s;
158 if (stat(dhclient_path, &s) == -1) { 158 if (stat(dhclient_path, &s) == -1) {
159 dhclient_path = "/usr/sbin/dhclient"; 159 fprintf(stderr, "Error: %s was not found.\n", dhclient_path);
160 if (stat(dhclient_path, &s) == -1) { 160 exit(1);
161 fprintf(stderr, "Error: dhclient was not found.\n");
162 exit(1);
163 }
164 } 161 }
165 162
166 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", dhclient_path, RUN_MNT_DIR);
167 dhclient_path = RUN_MNT_DIR "/dhclient";
168
169 EUID_ROOT(); 163 EUID_ROOT();
170 if (mkdir(RUN_DHCLIENT_DIR, 0700)) 164 if (mkdir(RUN_DHCLIENT_DIR, 0700))
171 errExit("mkdir"); 165 errExit("mkdir");
diff --git a/src/firejail/env.c b/src/firejail/env.c
index 03818df0b..963288459 100644
--- a/src/firejail/env.c
+++ b/src/firejail/env.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,6 +22,7 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <unistd.h> 23#include <unistd.h>
24#include <dirent.h> 24#include <dirent.h>
25#include <limits.h>
25 26
26typedef struct env_t { 27typedef struct env_t {
27 struct env_t *next; 28 struct env_t *next;
@@ -59,12 +60,7 @@ void env_ibus_load(void) {
59 if (asprintf(&dirname, "%s/.config/ibus/bus", cfg.homedir) == -1) 60 if (asprintf(&dirname, "%s/.config/ibus/bus", cfg.homedir) == -1)
60 errExit("asprintf"); 61 errExit("asprintf");
61 62
62 struct stat s;
63 if (stat(dirname, &s) == -1)
64 return;
65
66 // find the file 63 // find the file
67 /* coverity[toctou] */
68 DIR *dir = opendir(dirname); 64 DIR *dir = opendir(dirname);
69 if (!dir) { 65 if (!dir) {
70 free(dirname); 66 free(dirname);
@@ -84,7 +80,7 @@ void env_ibus_load(void) {
84 char *fname; 80 char *fname;
85 if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) 81 if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1)
86 errExit("asprintf"); 82 errExit("asprintf");
87 FILE *fp = fopen(fname, "r"); 83 FILE *fp = fopen(fname, "re");
88 free(fname); 84 free(fname);
89 if (!fp) 85 if (!fp)
90 continue; 86 continue;
@@ -267,7 +263,7 @@ static const char * const env_whitelist[] = {
267 "LANG", 263 "LANG",
268 "LANGUAGE", 264 "LANGUAGE",
269 "LC_MESSAGES", 265 "LC_MESSAGES",
270 "PATH", 266 // "PATH",
271 "DISPLAY" // required by X11 267 "DISPLAY" // required by X11
272}; 268};
273 269
@@ -316,6 +312,10 @@ void env_apply_whitelist(void) {
316 errExit("clearenv"); 312 errExit("clearenv");
317 313
318 env_apply_list(env_whitelist, ARRAY_SIZE(env_whitelist)); 314 env_apply_list(env_whitelist, ARRAY_SIZE(env_whitelist));
315
316 // hardcoding PATH
317 if (setenv("PATH", "/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin", 1) < 0)
318 errExit("setenv");
319} 319}
320 320
321// Filter env variables for a sbox app 321// Filter env variables for a sbox app
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index e07035ae6..316518534 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,6 +22,7 @@
22#include "../include/common.h" 22#include "../include/common.h"
23#include "../include/euid_common.h" 23#include "../include/euid_common.h"
24#include "../include/rundefs.h" 24#include "../include/rundefs.h"
25#include <linux/limits.h> // Note: Plain limits.h may break ARG_MAX (see #4583)
25#include <stdarg.h> 26#include <stdarg.h>
26#include <sys/stat.h> 27#include <sys/stat.h>
27 28
@@ -45,6 +46,15 @@
45 assert(s.st_gid == gid);\ 46 assert(s.st_gid == gid);\
46 assert((s.st_mode & 07777) == (mode));\ 47 assert((s.st_mode & 07777) == (mode));\
47 } while (0) 48 } while (0)
49#define ASSERT_PERMS_AS_USER(file, uid, gid, mode) \
50 do { \
51 assert(file);\
52 struct stat s;\
53 if (stat_as_user(file, &s) == -1) errExit("stat");\
54 assert(s.st_uid == uid);\
55 assert(s.st_gid == gid);\
56 assert((s.st_mode & 07777) == (mode));\
57 } while (0)
48#define ASSERT_PERMS_FD(fd, uid, gid, mode) \ 58#define ASSERT_PERMS_FD(fd, uid, gid, mode) \
49 do { \ 59 do { \
50 struct stat s;\ 60 struct stat s;\
@@ -122,26 +132,22 @@ typedef struct interface_t {
122 uint8_t configured; 132 uint8_t configured;
123} Interface; 133} Interface;
124 134
135typedef struct topdir_t {
136 char *path;
137 int fd;
138} TopDir;
139
125typedef struct profile_entry_t { 140typedef struct profile_entry_t {
126 struct profile_entry_t *next; 141 struct profile_entry_t *next;
127 char *data; // command 142 char *data; // command
128 143
129 // whitelist command parameters 144 // whitelist command parameters
130 char *link; // link name - set if the file is a link 145 struct wparam_t {
131 enum { 146 char *file; // resolved file path
132 WLDIR_HOME = 1, // whitelist in home directory 147 char *link; // link path
133 WLDIR_TMP, // whitelist in /tmp directory 148 TopDir *top; // top level directory
134 WLDIR_MEDIA, // whitelist in /media directory 149 } *wparam;
135 WLDIR_MNT, // whitelist in /mnt directory 150
136 WLDIR_VAR, // whitelist in /var directory
137 WLDIR_DEV, // whitelist in /dev directory
138 WLDIR_OPT, // whitelist in /opt directory
139 WLDIR_SRV, // whitelist in /srv directory
140 WLDIR_ETC, // whitelist in /etc directory
141 WLDIR_SHARE, // whitelist in /usr/share directory
142 WLDIR_MODULE, // whitelist in /sys/module directory
143 WLDIR_RUN // whitelist in /run/user/$uid directory
144 } wldir;
145} ProfileEntry; 151} ProfileEntry;
146 152
147typedef struct config_t { 153typedef struct config_t {
@@ -151,8 +157,11 @@ typedef struct config_t {
151 157
152 // filesystem 158 // filesystem
153 ProfileEntry *profile; 159 ProfileEntry *profile;
160 ProfileEntry *profile_rebuild_etc; // blacklist files in /etc directory used by fs_rebuild_etc()
161
154#define MAX_PROFILE_IGNORE 32 162#define MAX_PROFILE_IGNORE 32
155 char *profile_ignore[MAX_PROFILE_IGNORE]; 163 char *profile_ignore[MAX_PROFILE_IGNORE];
164 char *keep_fd; // inherit file descriptors to sandbox
156 char *chrootdir; // chroot directory 165 char *chrootdir; // chroot directory
157 char *home_private; // private home directory 166 char *home_private; // private home directory
158 char *home_private_keep; // keep list for private home directory 167 char *home_private_keep; // keep list for private home directory
@@ -314,15 +323,16 @@ extern int arg_private_cwd; // private working directory
314extern int arg_scan; // arp-scan all interfaces 323extern int arg_scan; // arp-scan all interfaces
315extern int arg_whitelist; // whitelist command 324extern int arg_whitelist; // whitelist command
316extern int arg_nosound; // disable sound 325extern int arg_nosound; // disable sound
317extern int arg_noautopulse; // disable automatic ~/.config/pulse init
318extern int arg_novideo; //disable video devices in /dev 326extern int arg_novideo; //disable video devices in /dev
319extern int arg_no3d; // disable 3d hardware acceleration 327extern int arg_no3d; // disable 3d hardware acceleration
328extern int arg_noprinters; // disable printers
320extern int arg_quiet; // no output for scripting 329extern int arg_quiet; // no output for scripting
321extern int arg_join_network; // join only the network namespace 330extern int arg_join_network; // join only the network namespace
322extern int arg_join_filesystem; // join only the mount namespace 331extern int arg_join_filesystem; // join only the mount namespace
323extern int arg_nice; // nice value configured 332extern int arg_nice; // nice value configured
324extern int arg_ipc; // enable ipc namespace 333extern int arg_ipc; // enable ipc namespace
325extern int arg_writable_etc; // writable etc 334extern int arg_writable_etc; // writable etc
335extern int arg_keep_config_pulse; // disable automatic ~/.config/pulse init
326extern int arg_writable_var; // writable var 336extern int arg_writable_var; // writable var
327extern int arg_keep_var_tmp; // don't overwrite /var/tmp 337extern int arg_keep_var_tmp; // don't overwrite /var/tmp
328extern int arg_writable_run_user; // writable /run/user 338extern int arg_writable_run_user; // writable /run/user
@@ -333,7 +343,7 @@ extern int arg_allow_debuggers; // allow debuggers
333extern int arg_x11_block; // block X11 343extern int arg_x11_block; // block X11
334extern int arg_x11_xorg; // use X11 security extension 344extern int arg_x11_xorg; // use X11 security extension
335extern int arg_allusers; // all user home directories visible 345extern int arg_allusers; // all user home directories visible
336extern int arg_machineid; // preserve /etc/machine-id 346extern int arg_machineid; // spoof /etc/machine-id
337extern int arg_disable_mnt; // disable /mnt and /media 347extern int arg_disable_mnt; // disable /mnt and /media
338extern int arg_noprofile; // use default.profile if none other found/specified 348extern int arg_noprofile; // use default.profile if none other found/specified
339extern int arg_memory_deny_write_execute; // block writable and executable memory 349extern int arg_memory_deny_write_execute; // block writable and executable memory
@@ -342,6 +352,8 @@ extern int arg_nodvd; // --nodvd
342extern int arg_nou2f; // --nou2f 352extern int arg_nou2f; // --nou2f
343extern int arg_noinput; // --noinput 353extern int arg_noinput; // --noinput
344extern int arg_deterministic_exit_code; // always exit with first child's exit status 354extern int arg_deterministic_exit_code; // always exit with first child's exit status
355extern int arg_deterministic_shutdown; // shut down the sandbox if first child dies
356extern int arg_keep_fd_all; // inherit all file descriptors to sandbox
345 357
346typedef enum { 358typedef enum {
347 DBUS_POLICY_ALLOW, // Allow unrestricted access to the bus 359 DBUS_POLICY_ALLOW, // Allow unrestricted access to the bus
@@ -353,6 +365,7 @@ extern DbusPolicy arg_dbus_system; // --dbus-system
353extern int arg_dbus_log_user; 365extern int arg_dbus_log_user;
354extern int arg_dbus_log_system; 366extern int arg_dbus_log_system;
355extern const char *arg_dbus_log_file; 367extern const char *arg_dbus_log_file;
368extern int arg_tab;
356 369
357extern int login_shell; 370extern int login_shell;
358extern int parent_to_child_fds[2]; 371extern int parent_to_child_fds[2];
@@ -426,13 +439,15 @@ void fs_proc_sys_dev_boot(void);
426void disable_config(void); 439void disable_config(void);
427// build a basic read-only filesystem 440// build a basic read-only filesystem
428void fs_basic_fs(void); 441void fs_basic_fs(void);
429// mount overlayfs on top of / directory
430char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
431void fs_overlayfs(void);
432void fs_private_tmp(void); 442void fs_private_tmp(void);
433void fs_private_cache(void); 443void fs_private_cache(void);
434void fs_mnt(const int enforce); 444void fs_mnt(const int enforce);
435 445
446// fs_overlayfs.c
447char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
448void fs_overlayfs(void);
449int remove_overlay_directory(void);
450
436// chroot.c 451// chroot.c
437// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf 452// chroot into an existing directory; mount existing /dev and update /etc/resolv.conf
438void fs_check_chroot_dir(void); 453void fs_check_chroot_dir(void);
@@ -493,7 +508,9 @@ int macro_id(const char *name);
493void errLogExit(char* fmt, ...) __attribute__((noreturn)); 508void errLogExit(char* fmt, ...) __attribute__((noreturn));
494void fwarning(char* fmt, ...); 509void fwarning(char* fmt, ...);
495void fmessage(char* fmt, ...); 510void fmessage(char* fmt, ...);
496void drop_privs(int nogroups); 511long long unsigned parse_arg_size(char *str);
512int check_can_drop_all_groups();
513void drop_privs(int force_nogroups);
497int mkpath_as_root(const char* path); 514int mkpath_as_root(const char* path);
498void extract_command_name(int index, char **argv); 515void extract_command_name(int index, char **argv);
499void logsignal(int s); 516void logsignal(int s);
@@ -502,11 +519,15 @@ void logargs(int argc, char **argv) ;
502void logerr(const char *msg); 519void logerr(const char *msg);
503void set_nice(int inc); 520void set_nice(int inc);
504int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 521int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
505void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 522void copy_file_as_user(const char *srcname, const char *destname, mode_t mode);
506void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 523void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
507void touch_file_as_user(const char *fname, mode_t mode); 524void touch_file_as_user(const char *fname, mode_t mode);
508int is_dir(const char *fname); 525int is_dir(const char *fname);
509int is_link(const char *fname); 526int is_link(const char *fname);
527char *realpath_as_user(const char *fname);
528ssize_t readlink_as_user(const char *fname, char *buf, size_t sz);
529int stat_as_user(const char *fname, struct stat *s);
530int lstat_as_user(const char *fname, struct stat *s);
510void trim_trailing_slash_or_dot(char *path); 531void trim_trailing_slash_or_dot(char *path);
511char *line_remove_spaces(const char *buf); 532char *line_remove_spaces(const char *buf);
512char *split_comma(char *str); 533char *split_comma(char *str);
@@ -518,8 +539,7 @@ void update_map(char *mapping, char *map_file);
518void wait_for_other(int fd); 539void wait_for_other(int fd);
519void notify_other(int fd); 540void notify_other(int fd);
520uid_t pid_get_uid(pid_t pid); 541uid_t pid_get_uid(pid_t pid);
521uid_t get_group_id(const char *group); 542gid_t get_group_id(const char *groupname);
522int remove_overlay_directory(void);
523void flush_stdin(void); 543void flush_stdin(void);
524int create_empty_dir_as_user(const char *dir, mode_t mode); 544int create_empty_dir_as_user(const char *dir, mode_t mode);
525void create_empty_dir_as_root(const char *dir, mode_t mode); 545void create_empty_dir_as_root(const char *dir, mode_t mode);
@@ -529,12 +549,17 @@ void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid);
529unsigned extract_timeout(const char *str); 549unsigned extract_timeout(const char *str);
530void disable_file_or_dir(const char *fname); 550void disable_file_or_dir(const char *fname);
531void disable_file_path(const char *path, const char *file); 551void disable_file_path(const char *path, const char *file);
532int safe_fd(const char *path, int flags); 552int safer_openat(int dirfd, const char *path, int flags);
553int remount_by_fd(int dst, unsigned long mountflags);
554int bind_mount_by_fd(int src, int dst);
555int bind_mount_path_to_fd(const char *srcname, int dst);
556int bind_mount_fd_to_path(int src, const char *destname);
557void close_all(int *keep_list, size_t sz);
533int has_handler(pid_t pid, int signal); 558int has_handler(pid_t pid, int signal);
534void enter_network_namespace(pid_t pid); 559void enter_network_namespace(pid_t pid);
535int read_pid(const char *name, pid_t *pid); 560int read_pid(const char *name, pid_t *pid);
536pid_t require_pid(const char *name); 561pid_t require_pid(const char *name);
537void check_homedir(void); 562void check_homedir(const char *dir);
538 563
539// Get info regarding the last kernel mount operation from /proc/self/mountinfo 564// Get info regarding the last kernel mount operation from /proc/self/mountinfo
540// The return value points to a static area, and will be overwritten by subsequent calls. 565// The return value points to a static area, and will be overwritten by subsequent calls.
@@ -548,8 +573,8 @@ typedef struct {
548 573
549// mountinfo.c 574// mountinfo.c
550MountData *get_last_mount(void); 575MountData *get_last_mount(void);
551int get_mount_id(const char *path); 576int get_mount_id(int fd);
552char **build_mount_array(const int mount_id, const char *path); 577char **build_mount_array(const int mountid, const char *path);
553 578
554// fs_var.c 579// fs_var.c
555void fs_var_log(void); // mounting /var/log 580void fs_var_log(void); // mounting /var/log
@@ -606,13 +631,13 @@ void caps_print_filter(pid_t pid) __attribute__((noreturn));
606void caps_drop_dac_override(void); 631void caps_drop_dac_override(void);
607 632
608// fs_trace.c 633// fs_trace.c
609void fs_trace_preload(void); 634void fs_trace_touch_preload(void);
635void fs_trace_touch_or_store_preload(void);
610void fs_tracefile(void); 636void fs_tracefile(void);
611void fs_trace(void); 637void fs_trace(void);
612 638
613// fs_hostname.c 639// fs_hostname.c
614void fs_hostname(const char *hostname); 640void fs_hostname(const char *hostname);
615void fs_resolvconf(void);
616char *fs_check_hosts_file(const char *fname); 641char *fs_check_hosts_file(const char *fname);
617void fs_store_hosts_file(void); 642void fs_store_hosts_file(void);
618void fs_mount_hosts_file(void); 643void fs_mount_hosts_file(void);
@@ -630,12 +655,15 @@ void cpu_print_filter(pid_t pid) __attribute__((noreturn));
630// cgroup.c 655// cgroup.c
631void save_cgroup(void); 656void save_cgroup(void);
632void load_cgroup(const char *fname); 657void load_cgroup(const char *fname);
633void set_cgroup(const char *path); 658void check_cgroup_file(const char *fname);
659void set_cgroup(const char *fname, pid_t pid);
634 660
635// output.c 661// output.c
636void check_output(int argc, char **argv); 662void check_output(int argc, char **argv);
637 663
638// netfilter.c 664// netfilter.c
665void netfilter_netlock(pid_t pid);
666void netfilter_trace(pid_t pid);
639void check_netfilter_file(const char *fname); 667void check_netfilter_file(const char *fname);
640void netfilter(const char *fname); 668void netfilter(const char *fname);
641void netfilter6(const char *fname); 669void netfilter6(const char *fname);
@@ -655,6 +683,7 @@ void fs_machineid(void);
655void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list); 683void fs_private_dir_copy(const char *private_dir, const char *private_run_dir, const char *private_list);
656void fs_private_dir_mount(const char *private_dir, const char *private_run_dir); 684void fs_private_dir_mount(const char *private_dir, const char *private_run_dir);
657void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list); 685void fs_private_dir_list(const char *private_dir, const char *private_run_dir, const char *private_list);
686void fs_rebuild_etc(void);
658 687
659// no_sandbox.c 688// no_sandbox.c
660int check_namespace_virt(void); 689int check_namespace_virt(void);
@@ -682,6 +711,7 @@ void env_ibus_load(void);
682void fs_whitelist(void); 711void fs_whitelist(void);
683 712
684// pulseaudio.c 713// pulseaudio.c
714void pipewire_disable(void);
685void pulseaudio_init(void); 715void pulseaudio_init(void);
686void pulseaudio_disable(void); 716void pulseaudio_disable(void);
687 717
@@ -689,6 +719,8 @@ void pulseaudio_disable(void);
689void fs_private_bin_list(void); 719void fs_private_bin_list(void);
690 720
691// fs_lib.c 721// fs_lib.c
722int is_firejail_link(const char *fname);
723char *find_in_path(const char *program);
692void fs_private_lib(void); 724void fs_private_lib(void);
693 725
694// protocol.c 726// protocol.c
@@ -763,27 +795,30 @@ enum {
763 CFG_NETWORK, 795 CFG_NETWORK,
764 CFG_RESTRICTED_NETWORK, 796 CFG_RESTRICTED_NETWORK,
765 CFG_FORCE_NONEWPRIVS, 797 CFG_FORCE_NONEWPRIVS,
766 CFG_WHITELIST,
767 CFG_XEPHYR_WINDOW_TITLE, 798 CFG_XEPHYR_WINDOW_TITLE,
768 CFG_OVERLAYFS, 799 CFG_OVERLAYFS,
769 CFG_PRIVATE_HOME, 800 CFG_PRIVATE_BIN,
770 CFG_PRIVATE_BIN_NO_LOCAL, 801 CFG_PRIVATE_BIN_NO_LOCAL,
802 CFG_PRIVATE_CACHE,
803 CFG_PRIVATE_ETC,
804 CFG_PRIVATE_HOME,
805 CFG_PRIVATE_LIB,
806 CFG_PRIVATE_OPT,
807 CFG_PRIVATE_SRV,
771 CFG_FIREJAIL_PROMPT, 808 CFG_FIREJAIL_PROMPT,
772 CFG_FOLLOW_SYMLINK_AS_USER,
773 CFG_DISABLE_MNT, 809 CFG_DISABLE_MNT,
774 CFG_JOIN, 810 CFG_JOIN,
775 CFG_ARP_PROBES, 811 CFG_ARP_PROBES,
776 CFG_XPRA_ATTACH, 812 CFG_XPRA_ATTACH,
777 CFG_BROWSER_DISABLE_U2F, 813 CFG_BROWSER_DISABLE_U2F,
778 CFG_BROWSER_ALLOW_DRM, 814 CFG_BROWSER_ALLOW_DRM,
779 CFG_PRIVATE_LIB,
780 CFG_APPARMOR, 815 CFG_APPARMOR,
781 CFG_DBUS, 816 CFG_DBUS,
782 CFG_PRIVATE_CACHE,
783 CFG_CGROUP, 817 CFG_CGROUP,
784 CFG_NAME_CHANGE, 818 CFG_NAME_CHANGE,
785 CFG_SECCOMP_ERROR_ACTION, 819 CFG_SECCOMP_ERROR_ACTION,
786 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv 820 // CFG_FILE_COPY_LIMIT - file copy limit handled using setenv/getenv
821 CFG_ALLOW_TRAY,
787 CFG_MAX // this should always be the last entry 822 CFG_MAX // this should always be the last entry
788}; 823};
789extern char *xephyr_screen; 824extern char *xephyr_screen;
@@ -794,11 +829,14 @@ extern char *xvfb_extra_params;
794extern char *netfilter_default; 829extern char *netfilter_default;
795extern unsigned long join_timeout; 830extern unsigned long join_timeout;
796extern char *config_seccomp_error_action_str; 831extern char *config_seccomp_error_action_str;
832extern char *config_seccomp_filter_add;
833extern char **whitelist_reject_topdirs;
797 834
798int checkcfg(int val); 835int checkcfg(int val);
799void print_compiletime_support(void); 836void print_compiletime_support(void);
800 837
801// appimage.c 838// appimage.c
839int appimage_find_profile(const char *archive);
802void appimage_set(const char *appimage_path); 840void appimage_set(const char *appimage_path);
803void appimage_mount(void); 841void appimage_mount(void);
804void appimage_clear(void); 842void appimage_clear(void);
@@ -807,15 +845,14 @@ void appimage_clear(void);
807long unsigned int appimage2_size(int fd); 845long unsigned int appimage2_size(int fd);
808 846
809// cmdline.c 847// cmdline.c
810void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); 848void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes);
811void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); 849void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes);
812 850
813// sbox.c 851// sbox.c
814// programs 852// programs
815#define PATH_FNET_MAIN (LIBDIR "/firejail/fnet") // when called from main thread 853#define PATH_FNET_MAIN (LIBDIR "/firejail/fnet") // when called from main thread
816#define PATH_FNET (RUN_FIREJAIL_LIB_DIR "/fnet") // when called from sandbox thread 854#define PATH_FNET (RUN_FIREJAIL_LIB_DIR "/fnet") // when called from sandbox thread
817 855
818//#define PATH_FNETFILTER (LIBDIR "/firejail/fnetfilter")
819#define PATH_FNETFILTER (RUN_FIREJAIL_LIB_DIR "/fnetfilter") 856#define PATH_FNETFILTER (RUN_FIREJAIL_LIB_DIR "/fnetfilter")
820 857
821#define PATH_FIREMON (PREFIX "/bin/firemon") 858#define PATH_FIREMON (PREFIX "/bin/firemon")
@@ -828,17 +865,16 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
828// it is also run from inside the sandbox by --debug; in this case we do an access(filename, X_OK) test first 865// it is also run from inside the sandbox by --debug; in this case we do an access(filename, X_OK) test first
829#define PATH_FSEC_PRINT (LIBDIR "/firejail/fsec-print") 866#define PATH_FSEC_PRINT (LIBDIR "/firejail/fsec-print")
830 867
831//#define PATH_FSEC_OPTIMIZE (LIBDIR "/firejail/fsec-optimize")
832#define PATH_FSEC_OPTIMIZE (RUN_FIREJAIL_LIB_DIR "/fsec-optimize") 868#define PATH_FSEC_OPTIMIZE (RUN_FIREJAIL_LIB_DIR "/fsec-optimize")
833 869
834//#define PATH_FCOPY (LIBDIR "/firejail/fcopy")
835#define PATH_FCOPY (RUN_FIREJAIL_LIB_DIR "/fcopy") 870#define PATH_FCOPY (RUN_FIREJAIL_LIB_DIR "/fcopy")
836 871
837#define SBOX_STDIN_FILE "/run/firejail/mnt/sbox_stdin" 872#define SBOX_STDIN_FILE "/run/firejail/mnt/sbox_stdin"
838 873
839//#define PATH_FLDD (LIBDIR "/firejail/fldd")
840#define PATH_FLDD (RUN_FIREJAIL_LIB_DIR "/fldd") 874#define PATH_FLDD (RUN_FIREJAIL_LIB_DIR "/fldd")
841 875
876#define PATH_FIDS (LIBDIR "/firejail/fids")
877
842// bitmapped filters for sbox_run 878// bitmapped filters for sbox_run
843#define SBOX_ROOT (1 << 0) // run the sandbox as root 879#define SBOX_ROOT (1 << 0) // run the sandbox as root
844#define SBOX_USER (1 << 1) // run the sandbox as a regular user 880#define SBOX_USER (1 << 1) // run the sandbox as a regular user
@@ -850,7 +886,6 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
850#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon 886#define SBOX_CAPS_HIDEPID (1 << 7) // hidepid caps filter for running firemon
851#define SBOX_CAPS_NET_SERVICE (1 << 8) // caps filter for programs running network services 887#define SBOX_CAPS_NET_SERVICE (1 << 8) // caps filter for programs running network services
852#define SBOX_KEEP_FDS (1 << 9) // keep file descriptors open 888#define SBOX_KEEP_FDS (1 << 9) // keep file descriptors open
853#define FIREJAIL_MAX_FD 20 // getdtablesize() is overkill for a firejail process
854 889
855// run sbox 890// run sbox
856int sbox_run(unsigned filter, int num, ...); 891int sbox_run(unsigned filter, int num, ...);
@@ -883,4 +918,7 @@ void dhcp_start(void);
883// selinux.c 918// selinux.c
884void selinux_relabel_path(const char *path, const char *inside_path); 919void selinux_relabel_path(const char *path, const char *inside_path);
885 920
921// ids.c
922void run_ids(int argc, char **argv);
923
886#endif 924#endif
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index fc67a15f3..c03cd7a12 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,11 +18,9 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/gcov_wrapper.h"
21#include <sys/mount.h> 22#include <sys/mount.h>
22#include <sys/stat.h>
23#include <sys/statvfs.h> 23#include <sys/statvfs.h>
24#include <sys/wait.h>
25#include <linux/limits.h>
26#include <fnmatch.h> 24#include <fnmatch.h>
27#include <glob.h> 25#include <glob.h>
28#include <dirent.h> 26#include <dirent.h>
@@ -34,7 +32,7 @@
34#endif 32#endif
35 33
36#define MAX_BUF 4096 34#define MAX_BUF 4096
37#define EMPTY_STRING ("") 35
38// check noblacklist statements not matched by a proper blacklist in disable-*.inc files 36// check noblacklist statements not matched by a proper blacklist in disable-*.inc files
39//#define TEST_NO_BLACKLIST_MATCHING 37//#define TEST_NO_BLACKLIST_MATCHING
40 38
@@ -54,16 +52,10 @@ static char *opstr[] = {
54 [MOUNT_RDWR_NOCHECK] = "read-write", 52 [MOUNT_RDWR_NOCHECK] = "read-write",
55}; 53};
56 54
57typedef enum {
58 UNSUCCESSFUL,
59 SUCCESSFUL
60} LAST_DISABLE_OPERATION;
61LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL;
62
63static void disable_file(OPERATION op, const char *filename) { 55static void disable_file(OPERATION op, const char *filename) {
64 assert(filename); 56 assert(filename);
65 assert(op <OPERATION_MAX); 57 assert(op <OPERATION_MAX);
66 last_disable = UNSUCCESSFUL; 58 EUID_ASSERT();
67 59
68 // Resolve all symlinks 60 // Resolve all symlinks
69 char* fname = realpath(filename, NULL); 61 char* fname = realpath(filename, NULL);
@@ -71,20 +63,24 @@ static void disable_file(OPERATION op, const char *filename) {
71 return; 63 return;
72 } 64 }
73 if (fname == NULL && errno == EACCES) { 65 if (fname == NULL && errno == EACCES) {
74 if (arg_debug)
75 printf("Debug: no access to file %s, forcing mount\n", filename);
76 // realpath and stat functions will fail on FUSE filesystems 66 // realpath and stat functions will fail on FUSE filesystems
77 // they don't seem to like a uid of 0 67 // they don't seem to like a uid of 0
78 // force mounting 68 // force mounting
79 int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); 69 int fd = open(filename, O_PATH|O_CLOEXEC);
80 if (rv == 0) 70 if (fd < 0) {
81 last_disable = SUCCESSFUL; 71 if (arg_debug)
82 else { 72 printf("Warning (blacklisting): cannot open %s: %s\n", filename, strerror(errno));
83 rv = mount(RUN_RO_FILE, filename, "none", MS_BIND, "mode=400,gid=0"); 73 return;
84 if (rv == 0)
85 last_disable = SUCCESSFUL;
86 } 74 }
87 if (last_disable == SUCCESSFUL) { 75
76 EUID_ROOT();
77 int err = bind_mount_path_to_fd(RUN_RO_DIR, fd);
78 if (err != 0)
79 err = bind_mount_path_to_fd(RUN_RO_FILE, fd);
80 EUID_USER();
81 close(fd);
82
83 if (err == 0) {
88 if (arg_debug) 84 if (arg_debug)
89 printf("Disable %s\n", filename); 85 printf("Disable %s\n", filename);
90 if (op == BLACKLIST_FILE) 86 if (op == BLACKLIST_FILE)
@@ -92,31 +88,39 @@ static void disable_file(OPERATION op, const char *filename) {
92 else 88 else
93 fs_logger2("blacklist-nolog", filename); 89 fs_logger2("blacklist-nolog", filename);
94 } 90 }
95 else { 91 else if (arg_debug)
96 if (arg_debug) 92 printf("Warning (blacklisting): cannot mount on %s\n", filename);
97 printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename);
98 }
99 93
100 return; 94 return;
101 } 95 }
102 96
103 // if the file is not present, do nothing 97 assert(fname);
104 struct stat s; 98 // check for firejail executable
105 if (fname == NULL) 99 // we might have a file found in ${PATH} pointing to /usr/bin/firejail
100 // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird
101 // and expects Firefox to open in the same sandbox
102 if (strcmp(BINDIR "/firejail", fname) == 0) {
103 free(fname);
106 return; 104 return;
107 if (stat(fname, &s) == -1) { 105 }
106
107 // if the file is not present, do nothing
108 int fd = open(fname, O_PATH|O_CLOEXEC);
109 if (fd < 0) {
108 if (arg_debug) 110 if (arg_debug)
109 fwarning("%s does not exist, skipping...\n", fname); 111 printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
110 free(fname); 112 free(fname);
111 return; 113 return;
112 } 114 }
113 115
114 // check for firejail executable 116 struct stat s;
115 // we migth have a file found in ${PATH} pointing to /usr/bin/firejail 117 if (fstat(fd, &s) < 0) {
116 // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird 118 if (arg_debug)
117 // and expects Firefox to open in the same sandbox 119 printf("Warning (blacklisting): cannot stat %s: %s\n", fname, strerror(errno));
118 if (strcmp(BINDIR "/firejail", fname) == 0) 120 free(fname);
121 close(fd);
119 return; 122 return;
123 }
120 124
121 // modify the file 125 // modify the file
122 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { 126 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) {
@@ -141,44 +145,64 @@ static void disable_file(OPERATION op, const char *filename) {
141 printf(" - no logging\n"); 145 printf(" - no logging\n");
142 } 146 }
143 147
148 EUID_ROOT();
144 if (S_ISDIR(s.st_mode)) { 149 if (S_ISDIR(s.st_mode)) {
145 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 150 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
146 errExit("disable file"); 151 errExit("disable file");
147 } 152 }
148 else { 153 else {
149 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 154 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
150 errExit("disable file"); 155 errExit("disable file");
151 } 156 }
152 last_disable = SUCCESSFUL; 157 EUID_USER();
158
153 if (op == BLACKLIST_FILE) 159 if (op == BLACKLIST_FILE)
154 fs_logger2("blacklist", fname); 160 fs_logger2("blacklist", fname);
155 else 161 else
156 fs_logger2("blacklist-nolog", fname); 162 fs_logger2("blacklist-nolog", fname);
163
164 // files in /etc will be reprocessed during /etc rebuild
165 if (strncmp(fname, "/etc/", 5) == 0) {
166 ProfileEntry *prf = malloc(sizeof(ProfileEntry));
167 if (!prf)
168 errExit("malloc");
169 memset(prf, 0, sizeof(ProfileEntry));
170 prf->data = strdup(fname);
171 if (!prf->data)
172 errExit("strdup");
173 prf->next = cfg.profile_rebuild_etc;
174 cfg.profile_rebuild_etc = prf;
175 }
157 } 176 }
158 } 177 }
159 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { 178 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) {
160 fs_remount_rec(fname, op); 179 fs_remount_rec(fname, op);
161 // todo: last_disable = SUCCESSFUL;
162 } 180 }
163 else if (op == MOUNT_TMPFS) { 181 else if (op == MOUNT_TMPFS) {
164 if (S_ISDIR(s.st_mode)) { 182 if (!S_ISDIR(s.st_mode)) {
165 if (getuid()) { 183 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname);
166 if (strncmp(cfg.homedir, fname, strlen(cfg.homedir)) != 0 || 184 goto out;
167 fname[strlen(cfg.homedir)] != '/') { 185 }
168 fprintf(stderr, "Error: tmpfs outside $HOME is only available for root\n"); 186
169 exit(1); 187 uid_t uid = getuid();
170 } 188 if (uid != 0) {
189 // only user owned directories in user home
190 if (s.st_uid != uid ||
191 strncmp(cfg.homedir, fname, strlen(cfg.homedir)) != 0 ||
192 fname[strlen(cfg.homedir)] != '/') {
193 fwarning("you are not allowed to mount a tmpfs on %s\n", fname);
194 goto out;
171 } 195 }
172 fs_tmpfs(fname, getuid());
173 selinux_relabel_path(fname, fname);
174 last_disable = SUCCESSFUL;
175 } 196 }
176 else 197
177 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); 198 fs_tmpfs(fname, uid);
199 selinux_relabel_path(fname, fname);
178 } 200 }
179 else 201 else
180 assert(0); 202 assert(0);
181 203
204out:
205 close(fd);
182 free(fname); 206 free(fname);
183} 207}
184 208
@@ -191,6 +215,7 @@ static int *nbcheck = NULL;
191// Treat pattern as a shell glob pattern and blacklist matching files 215// Treat pattern as a shell glob pattern and blacklist matching files
192static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { 216static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) {
193 assert(pattern); 217 assert(pattern);
218 EUID_ASSERT();
194 219
195#ifdef TEST_NO_BLACKLIST_MATCHING 220#ifdef TEST_NO_BLACKLIST_MATCHING
196 if (nbcheck_start == 0) { 221 if (nbcheck_start == 0) {
@@ -253,6 +278,8 @@ static void globbing(OPERATION op, const char *pattern, const char *noblacklist[
253 278
254// blacklist files or directories by mounting empty files on top of them 279// blacklist files or directories by mounting empty files on top of them
255void fs_blacklist(void) { 280void fs_blacklist(void) {
281 EUID_ASSERT();
282
256 ProfileEntry *entry = cfg.profile; 283 ProfileEntry *entry = cfg.profile;
257 if (!entry) 284 if (!entry)
258 return; 285 return;
@@ -294,11 +321,13 @@ void fs_blacklist(void) {
294 if (arg_debug) 321 if (arg_debug)
295 printf("Mount-bind %s on top of %s\n", dname1, dname2); 322 printf("Mount-bind %s on top of %s\n", dname1, dname2);
296 // preserve dname2 mode and ownership 323 // preserve dname2 mode and ownership
324 // EUID_ROOT(); - option not accessible to non-root users
297 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) 325 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0)
298 errExit("mount bind"); 326 errExit("mount bind");
299 /* coverity[toctou] */ 327 /* coverity[toctou] */
300 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) 328 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode))
301 errExit("set_perms"); 329 errExit("set_perms");
330 // EUID_USER();
302 331
303 entry = entry->next; 332 entry = entry->next;
304 continue; 333 continue;
@@ -376,16 +405,12 @@ void fs_blacklist(void) {
376 op = MOUNT_TMPFS; 405 op = MOUNT_TMPFS;
377 } 406 }
378 else if (strncmp(entry->data, "mkdir ", 6) == 0) { 407 else if (strncmp(entry->data, "mkdir ", 6) == 0) {
379 EUID_USER();
380 fs_mkdir(entry->data + 6); 408 fs_mkdir(entry->data + 6);
381 EUID_ROOT();
382 entry = entry->next; 409 entry = entry->next;
383 continue; 410 continue;
384 } 411 }
385 else if (strncmp(entry->data, "mkfile ", 7) == 0) { 412 else if (strncmp(entry->data, "mkfile ", 7) == 0) {
386 EUID_USER();
387 fs_mkfile(entry->data + 7); 413 fs_mkfile(entry->data + 7);
388 EUID_ROOT();
389 entry = entry->next; 414 entry = entry->next;
390 continue; 415 continue;
391 } 416 }
@@ -449,11 +474,12 @@ void fs_blacklist(void) {
449 474
450// mount a writable tmpfs on directory; requires a resolved path 475// mount a writable tmpfs on directory; requires a resolved path
451void fs_tmpfs(const char *dir, unsigned check_owner) { 476void fs_tmpfs(const char *dir, unsigned check_owner) {
477 EUID_ASSERT();
452 assert(dir); 478 assert(dir);
453 if (arg_debug) 479 if (arg_debug)
454 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); 480 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no");
455 // get a file descriptor for dir, fails if there is any symlink 481 // get a file descriptor for dir, fails if there is any symlink
456 int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 482 int fd = safer_openat(-1, dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
457 if (fd == -1) 483 if (fd == -1)
458 errExit("while opening directory"); 484 errExit("while opening directory");
459 struct stat s; 485 struct stat s;
@@ -471,13 +497,15 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
471 struct statvfs buf; 497 struct statvfs buf;
472 if (fstatvfs(fd, &buf) == -1) 498 if (fstatvfs(fd, &buf) == -1)
473 errExit("fstatvfs"); 499 errExit("fstatvfs");
474 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND); 500 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND|MS_REMOUNT);
475 // mount via the symbolic link in /proc/self/fd 501 // mount via the symbolic link in /proc/self/fd
476 char *proc; 502 char *proc;
477 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 503 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
478 errExit("asprintf"); 504 errExit("asprintf");
505 EUID_ROOT();
479 if (mount("tmpfs", proc, "tmpfs", flags|MS_NOSUID|MS_NODEV, options) < 0) 506 if (mount("tmpfs", proc, "tmpfs", flags|MS_NOSUID|MS_NODEV, options) < 0)
480 errExit("mounting tmpfs"); 507 errExit("mounting tmpfs");
508 EUID_USER();
481 // check the last mount operation 509 // check the last mount operation
482 MountData *mdata = get_last_mount(); 510 MountData *mdata = get_last_mount();
483 if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, dir) != 0) 511 if (strcmp(mdata->fstype, "tmpfs") != 0 || strcmp(mdata->dir, dir) != 0)
@@ -490,38 +518,42 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
490 518
491// remount path, preserving other mount flags; requires a resolved path 519// remount path, preserving other mount flags; requires a resolved path
492static void fs_remount_simple(const char *path, OPERATION op) { 520static void fs_remount_simple(const char *path, OPERATION op) {
521 EUID_ASSERT();
493 assert(path); 522 assert(path);
494 523
495 // open path without following symbolic links 524 // open path without following symbolic links
496 int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 525 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
497 if (fd1 == -1) 526 if (fd < 0)
498 goto out; 527 goto out;
499 struct stat s1; 528
500 if (fstat(fd1, &s1) == -1) { 529 struct stat s;
530 if (fstat(fd, &s) < 0) {
501 // fstat can fail with EACCES if path is a FUSE mount, 531 // fstat can fail with EACCES if path is a FUSE mount,
502 // mounted without 'allow_root' or 'allow_other' 532 // mounted without 'allow_root' or 'allow_other'
503 if (errno != EACCES) 533 if (errno != EACCES)
504 errExit("fstat"); 534 errExit("fstat");
505 close(fd1); 535 close(fd);
506 goto out; 536 goto out;
507 } 537 }
508 // get mount flags 538 // get mount flags
509 struct statvfs buf; 539 struct statvfs buf;
510 if (fstatvfs(fd1, &buf) == -1) 540 if (fstatvfs(fd, &buf) < 0) {
511 errExit("fstatvfs"); 541 close(fd);
542 goto out;
543 }
512 unsigned long flags = buf.f_flag; 544 unsigned long flags = buf.f_flag;
513 545
514 // read-write option 546 // read-write option
515 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { 547 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) {
516 // nothing to do if there is no read-only flag 548 // nothing to do if there is no read-only flag
517 if ((flags & MS_RDONLY) == 0) { 549 if ((flags & MS_RDONLY) == 0) {
518 close(fd1); 550 close(fd);
519 return; 551 return;
520 } 552 }
521 // allow only user owned directories, except the user is root 553 // allow only user owned directories, except the user is root
522 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { 554 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s.st_uid != getuid()) {
523 fwarning("you are not allowed to change %s to read-write\n", path); 555 fwarning("you are not allowed to change %s to read-write\n", path);
524 close(fd1); 556 close(fd);
525 return; 557 return;
526 } 558 }
527 flags &= ~MS_RDONLY; 559 flags &= ~MS_RDONLY;
@@ -530,7 +562,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
530 else if (op == MOUNT_NOEXEC) { 562 else if (op == MOUNT_NOEXEC) {
531 // nothing to do if path is mounted noexec already 563 // nothing to do if path is mounted noexec already
532 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { 564 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) {
533 close(fd1); 565 close(fd);
534 return; 566 return;
535 } 567 }
536 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; 568 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID;
@@ -539,7 +571,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
539 else if (op == MOUNT_READONLY) { 571 else if (op == MOUNT_READONLY) {
540 // nothing to do if path is mounted read-only already 572 // nothing to do if path is mounted read-only already
541 if ((flags & MS_RDONLY) == MS_RDONLY) { 573 if ((flags & MS_RDONLY) == MS_RDONLY) {
542 close(fd1); 574 close(fd);
543 return; 575 return;
544 } 576 }
545 flags |= MS_RDONLY; 577 flags |= MS_RDONLY;
@@ -549,29 +581,37 @@ static void fs_remount_simple(const char *path, OPERATION op) {
549 581
550 if (arg_debug) 582 if (arg_debug)
551 printf("Mounting %s %s\n", opstr[op], path); 583 printf("Mounting %s %s\n", opstr[op], path);
584
585 // make path a mount point:
552 // mount --bind path path 586 // mount --bind path path
553 char *proc; 587 EUID_ROOT();
554 if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) 588 int err = bind_mount_by_fd(fd, fd);
555 errExit("asprintf"); 589 EUID_USER();
556 if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) 590 if (err) {
557 errExit("mount"); 591 close(fd);
558 free(proc); 592 goto out;
593 }
559 594
560 // mount --bind -o remount,ro path 595 // remount the mount point
561 // need to open path again without following symbolic links 596 // need to open path again
562 int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 597 int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
563 if (fd2 == -1) 598 close(fd); // earliest timepoint to close fd
564 errExit("open"); 599 if (fd2 < 0)
600 goto out;
601
602 // device and inode number should be the same
565 struct stat s2; 603 struct stat s2;
566 if (fstat(fd2, &s2) == -1) 604 if (fstat(fd2, &s2) < 0)
567 errExit("fstat"); 605 errExit("fstat");
568 // device and inode number should be the same 606 if (s.st_dev != s2.st_dev || s.st_ino != s2.st_ino)
569 if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino)
570 errLogExit("invalid %s mount", opstr[op]); 607 errLogExit("invalid %s mount", opstr[op]);
571 if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) 608
572 errExit("asprintf"); 609 EUID_ROOT();
573 if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) 610 err = remount_by_fd(fd2, flags);
574 errExit("mount"); 611 EUID_USER();
612 close(fd2);
613 if (err)
614 goto out;
575 615
576 // run a sanity check on /proc/self/mountinfo and confirm that target of the last 616 // run a sanity check on /proc/self/mountinfo and confirm that target of the last
577 // mount operation was path; if there are other mount points contained inside path, 617 // mount operation was path; if there are other mount points contained inside path,
@@ -582,10 +622,8 @@ static void fs_remount_simple(const char *path, OPERATION op) {
582 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 622 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
583 && strcmp(path, "/") != 0) // support read-only=/ 623 && strcmp(path, "/") != 0) // support read-only=/
584 errLogExit("invalid %s mount", opstr[op]); 624 errLogExit("invalid %s mount", opstr[op]);
625
585 fs_logger2(opstr[op], path); 626 fs_logger2(opstr[op], path);
586 free(proc);
587 close(fd1);
588 close(fd2);
589 return; 627 return;
590 628
591out: 629out:
@@ -593,38 +631,37 @@ out:
593} 631}
594 632
595// remount recursively; requires a resolved path 633// remount recursively; requires a resolved path
596static void fs_remount_rec(const char *dir, OPERATION op) { 634static void fs_remount_rec(const char *path, OPERATION op) {
597 assert(dir); 635 EUID_ASSERT();
598 struct stat s; 636 assert(op < OPERATION_MAX);
599 if (stat(dir, &s) != 0) 637 assert(path);
600 return; 638
601 if (!S_ISDIR(s.st_mode)) { 639 // no need to search /proc/self/mountinfo for submounts if not a directory
602 // no need to search in /proc/self/mountinfo for submounts if not a directory 640 int fd = open(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
603 fs_remount_simple(dir, op); 641 if (fd < 0) {
642 fs_remount_simple(path, op);
604 return; 643 return;
605 } 644 }
606 // get mount point of the directory 645
607 int mountid = get_mount_id(dir); 646 // get mount id of the directory
608 if (mountid == -1) 647 int mountid = get_mount_id(fd);
609 return; 648 close(fd);
610 if (mountid == -2) { 649 if (mountid < 0) {
611 // falling back to a simple remount on old kernels 650 // falling back to a simple remount
612 static int mount_warning = 0; 651 fwarning("%s %s not applied recursively\n", opstr[op], path);
613 if (!mount_warning) { 652 fs_remount_simple(path, op);
614 fwarning("read-only, read-write and noexec options are not applied recursively\n");
615 mount_warning = 1;
616 }
617 fs_remount_simple(dir, op);
618 return; 653 return;
619 } 654 }
655
620 // build array with all mount points that need to get remounted 656 // build array with all mount points that need to get remounted
621 char **arr = build_mount_array(mountid, dir); 657 char **arr = build_mount_array(mountid, path);
622 assert(arr); 658 if (!arr)
659 return;
623 // remount 660 // remount
624 char **tmp = arr; 661 int i;
625 while (*tmp) { 662 for (i = 0; arr[i]; i++) {
626 fs_remount_simple(*tmp, op); 663 fs_remount_simple(arr[i], op);
627 free(*tmp++); 664 free(arr[i]);
628 } 665 }
629 free(arr); 666 free(arr);
630} 667}
@@ -632,6 +669,14 @@ static void fs_remount_rec(const char *dir, OPERATION op) {
632// resolve a path and remount it 669// resolve a path and remount it
633void fs_remount(const char *path, OPERATION op, int rec) { 670void fs_remount(const char *path, OPERATION op, int rec) {
634 assert(path); 671 assert(path);
672
673 int called_as_root = 0;
674 if (geteuid() == 0)
675 called_as_root = 1;
676
677 if (called_as_root)
678 EUID_USER();
679
635 char *rpath = realpath(path, NULL); 680 char *rpath = realpath(path, NULL);
636 if (rpath) { 681 if (rpath) {
637 if (rec) 682 if (rec)
@@ -640,10 +685,14 @@ void fs_remount(const char *path, OPERATION op, int rec) {
640 fs_remount_simple(rpath, op); 685 fs_remount_simple(rpath, op);
641 free(rpath); 686 free(rpath);
642 } 687 }
688
689 if (called_as_root)
690 EUID_ROOT();
643} 691}
644 692
645// Disable /mnt, /media, /run/mount and /run/media access 693// Disable /mnt, /media, /run/mount and /run/media access
646void fs_mnt(const int enforce) { 694void fs_mnt(const int enforce) {
695 EUID_USER();
647 if (enforce) { 696 if (enforce) {
648 // disable-mnt set in firejail.config 697 // disable-mnt set in firejail.config
649 // overriding with noblacklist is not possible in this case 698 // overriding with noblacklist is not possible in this case
@@ -653,13 +702,12 @@ void fs_mnt(const int enforce) {
653 disable_file(BLACKLIST_FILE, "/run/media"); 702 disable_file(BLACKLIST_FILE, "/run/media");
654 } 703 }
655 else { 704 else {
656 EUID_USER();
657 profile_add("blacklist /mnt"); 705 profile_add("blacklist /mnt");
658 profile_add("blacklist /media"); 706 profile_add("blacklist /media");
659 profile_add("blacklist /run/mount"); 707 profile_add("blacklist /run/mount");
660 profile_add("blacklist /run/media"); 708 profile_add("blacklist /run/media");
661 EUID_ROOT();
662 } 709 }
710 EUID_ROOT();
663} 711}
664 712
665 713
@@ -674,7 +722,6 @@ void fs_proc_sys_dev_boot(void) {
674 errExit("mounting /proc/sys"); 722 errExit("mounting /proc/sys");
675 fs_logger("read-only /proc/sys"); 723 fs_logger("read-only /proc/sys");
676 724
677
678 /* Mount a version of /sys that describes the network namespace */ 725 /* Mount a version of /sys that describes the network namespace */
679 if (arg_debug) 726 if (arg_debug)
680 printf("Remounting /sys directory\n"); 727 printf("Remounting /sys directory\n");
@@ -689,13 +736,13 @@ void fs_proc_sys_dev_boot(void) {
689 else 736 else
690 fs_logger("remount /sys"); 737 fs_logger("remount /sys");
691 738
739 EUID_USER();
740
692 disable_file(BLACKLIST_FILE, "/sys/firmware"); 741 disable_file(BLACKLIST_FILE, "/sys/firmware");
693 disable_file(BLACKLIST_FILE, "/sys/hypervisor"); 742 disable_file(BLACKLIST_FILE, "/sys/hypervisor");
694 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option 743 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option
695 EUID_USER();
696 profile_add("blacklist /sys/fs"); 744 profile_add("blacklist /sys/fs");
697 profile_add("blacklist /sys/module"); 745 profile_add("blacklist /sys/module");
698 EUID_ROOT();
699 } 746 }
700 disable_file(BLACKLIST_FILE, "/sys/power"); 747 disable_file(BLACKLIST_FILE, "/sys/power");
701 disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); 748 disable_file(BLACKLIST_FILE, "/sys/kernel/debug");
@@ -739,12 +786,8 @@ void fs_proc_sys_dev_boot(void) {
739 // disable /dev/port 786 // disable /dev/port
740 disable_file(BLACKLIST_FILE, "/dev/port"); 787 disable_file(BLACKLIST_FILE, "/dev/port");
741 788
742
743
744 // disable various ipc sockets in /run/user 789 // disable various ipc sockets in /run/user
745 if (!arg_writable_run_user) { 790 if (!arg_writable_run_user) {
746 struct stat s;
747
748 char *fname; 791 char *fname;
749 if (asprintf(&fname, "/run/user/%d", getuid()) == -1) 792 if (asprintf(&fname, "/run/user/%d", getuid()) == -1)
750 errExit("asprintf"); 793 errExit("asprintf");
@@ -755,8 +798,7 @@ void fs_proc_sys_dev_boot(void) {
755 errExit("asprintf"); 798 errExit("asprintf");
756 if (create_empty_dir_as_user(fnamegpg, 0700)) 799 if (create_empty_dir_as_user(fnamegpg, 0700))
757 fs_logger2("create", fnamegpg); 800 fs_logger2("create", fnamegpg);
758 if (stat(fnamegpg, &s) == 0) 801 disable_file(BLACKLIST_FILE, fnamegpg);
759 disable_file(BLACKLIST_FILE, fnamegpg);
760 free(fnamegpg); 802 free(fnamegpg);
761 803
762 // disable /run/user/{uid}/systemd 804 // disable /run/user/{uid}/systemd
@@ -765,8 +807,7 @@ void fs_proc_sys_dev_boot(void) {
765 errExit("asprintf"); 807 errExit("asprintf");
766 if (create_empty_dir_as_user(fnamesysd, 0755)) 808 if (create_empty_dir_as_user(fnamesysd, 0755))
767 fs_logger2("create", fnamesysd); 809 fs_logger2("create", fnamesysd);
768 if (stat(fnamesysd, &s) == 0) 810 disable_file(BLACKLIST_FILE, fnamesysd);
769 disable_file(BLACKLIST_FILE, fnamesysd);
770 free(fnamesysd); 811 free(fnamesysd);
771 } 812 }
772 free(fname); 813 free(fname);
@@ -777,35 +818,32 @@ void fs_proc_sys_dev_boot(void) {
777 disable_file(BLACKLIST_FILE, "/dev/kmsg"); 818 disable_file(BLACKLIST_FILE, "/dev/kmsg");
778 disable_file(BLACKLIST_FILE, "/proc/kmsg"); 819 disable_file(BLACKLIST_FILE, "/proc/kmsg");
779 } 820 }
821
822 EUID_ROOT();
780} 823}
781 824
782// disable firejail configuration in ~/.config/firejail 825// disable firejail configuration in ~/.config/firejail
783void disable_config(void) { 826void disable_config(void) {
784 struct stat s; 827 EUID_USER();
785 828#ifndef HAVE_ONLY_SYSCFG_PROFILES
786 char *fname; 829 char *fname;
787 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) 830 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1)
788 errExit("asprintf"); 831 errExit("asprintf");
789 if (stat(fname, &s) == 0) 832 disable_file(BLACKLIST_FILE, fname);
790 disable_file(BLACKLIST_FILE, fname);
791 free(fname); 833 free(fname);
834#endif
792 835
793 // disable run time information 836 // disable run time information
794 if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) 837 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR);
795 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); 838 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR);
796 if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) 839 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR);
797 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); 840 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
798 if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) 841 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
799 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); 842 EUID_ROOT();
800 if (stat(RUN_FIREJAIL_PROFILE_DIR, &s) == 0)
801 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
802 if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0)
803 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
804} 843}
805 844
806 845
807// build a basic read-only filesystem 846// build a basic read-only filesystem
808// top level directories could be links, run no after-mount checks
809void fs_basic_fs(void) { 847void fs_basic_fs(void) {
810 uid_t uid = getuid(); 848 uid_t uid = getuid();
811 849
@@ -815,6 +853,7 @@ void fs_basic_fs(void) {
815 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 853 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
816 errExit("mounting /proc"); 854 errExit("mounting /proc");
817 855
856 EUID_USER();
818 if (arg_debug) 857 if (arg_debug)
819 printf("Basic read-only filesystem:\n"); 858 printf("Basic read-only filesystem:\n");
820 if (!arg_writable_etc) { 859 if (!arg_writable_etc) {
@@ -834,6 +873,7 @@ void fs_basic_fs(void) {
834 fs_remount("/lib64", MOUNT_READONLY, 1); 873 fs_remount("/lib64", MOUNT_READONLY, 1);
835 fs_remount("/lib32", MOUNT_READONLY, 1); 874 fs_remount("/lib32", MOUNT_READONLY, 1);
836 fs_remount("/libx32", MOUNT_READONLY, 1); 875 fs_remount("/libx32", MOUNT_READONLY, 1);
876 EUID_ROOT();
837 877
838 // update /var directory in order to support multiple sandboxes running on the same root directory 878 // update /var directory in order to support multiple sandboxes running on the same root directory
839 fs_var_lock(); 879 fs_var_lock();
@@ -858,369 +898,9 @@ void fs_basic_fs(void) {
858} 898}
859 899
860 900
861
862#ifdef HAVE_OVERLAYFS
863char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
864 assert(subdirname);
865 struct stat s;
866 char *dirname;
867
868 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1)
869 errExit("asprintf");
870 // check if ~/.firejail already exists
871 if (lstat(dirname, &s) == 0) {
872 if (!S_ISDIR(s.st_mode)) {
873 if (S_ISLNK(s.st_mode))
874 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
875 else
876 fprintf(stderr, "Error: %s is not a directory\n", dirname);
877 exit(1);
878 }
879 if (s.st_uid != getuid()) {
880 fprintf(stderr, "Error: %s is not owned by the current user\n", dirname);
881 exit(1);
882 }
883 }
884 else {
885 // create ~/.firejail directory
886 create_empty_dir_as_user(dirname, 0700);
887 if (stat(dirname, &s) == -1) {
888 fprintf(stderr, "Error: cannot create directory %s\n", dirname);
889 exit(1);
890 }
891 }
892 free(dirname);
893
894 // check overlay directory
895 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1)
896 errExit("asprintf");
897 if (lstat(dirname, &s) == 0) {
898 if (!S_ISDIR(s.st_mode)) {
899 if (S_ISLNK(s.st_mode))
900 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
901 else
902 fprintf(stderr, "Error: %s is not a directory\n", dirname);
903 exit(1);
904 }
905 if (s.st_uid != 0) {
906 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname);
907 exit(1);
908 }
909 if (allow_reuse == 0) {
910 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n");
911 exit(1);
912 }
913 }
914
915 return dirname;
916}
917
918
919
920// mount overlayfs on top of / directory
921// mounting an overlay and chrooting into it:
922//
923// Old Ubuntu kernel
924// # cd ~
925// # mkdir -p overlay/root
926// # mkdir -p overlay/diff
927// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
928// # chroot /root/overlay/root
929// to shutdown, first exit the chroot and then unmount the overlay
930// # exit
931// # umount /root/overlay/root
932//
933// Kernels 3.18+
934// # cd ~
935// # mkdir -p overlay/root
936// # mkdir -p overlay/diff
937// # mkdir -p overlay/work
938// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
939// # cat /etc/mtab | grep overlay
940// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
941// # chroot /root/overlay/root
942// to shutdown, first exit the chroot and then unmount the overlay
943// # exit
944// # umount /root/overlay/root
945
946
947// to do: fix the code below; also, it might work without /dev, but consider keeping /dev/shm; add locking mechanism for overlay-clean
948#include <sys/utsname.h>
949void fs_overlayfs(void) {
950 struct stat s;
951
952 // check kernel version
953 struct utsname u;
954 int rv = uname(&u);
955 if (rv != 0)
956 errExit("uname");
957 int major;
958 int minor;
959 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
960 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
961 exit(1);
962 }
963
964 if (arg_debug)
965 printf("Linux kernel version %d.%d\n", major, minor);
966 int oldkernel = 0;
967 if (major < 3) {
968 fprintf(stderr, "Error: minimum kernel version required 3.x\n");
969 exit(1);
970 }
971 if (major == 3 && minor < 18)
972 oldkernel = 1;
973
974 // mounting an overlayfs on top of / seems to be broken for kernels > 4.19
975 // we disable overlayfs for now, pending fixing
976 if (major >= 4 &&minor >= 19) {
977 fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n");
978 exit(1);
979 }
980
981 char *oroot = RUN_OVERLAY_ROOT;
982 mkdir_attr(oroot, 0755, 0, 0);
983
984 // set base for working and diff directories
985 char *basedir = RUN_MNT_DIR;
986 int basefd = -1;
987
988 if (arg_overlay_keep) {
989 basedir = cfg.overlay_dir;
990 assert(basedir);
991 // get a file descriptor for ~/.firejail, fails if there is any symlink
992 char *firejail;
993 if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1)
994 errExit("asprintf");
995 int fd = safe_fd(firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
996 if (fd == -1)
997 errExit("safe_fd");
998 free(firejail);
999 // create basedir if it doesn't exist
1000 // the new directory will be owned by root
1001 const char *dirname = gnu_basename(basedir);
1002 if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) {
1003 perror("mkdir");
1004 fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir);
1005 exit(1);
1006 }
1007 // open basedir
1008 basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1009 close(fd);
1010 }
1011 else {
1012 basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1013 }
1014 if (basefd == -1) {
1015 perror("open");
1016 fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir);
1017 exit(1);
1018 }
1019
1020 // confirm once more base is owned by root
1021 if (fstat(basefd, &s) == -1)
1022 errExit("fstat");
1023 if (s.st_uid != 0) {
1024 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir);
1025 exit(1);
1026 }
1027 // confirm permissions of base are 0755
1028 if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
1029 fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir);
1030 exit(1);
1031 }
1032
1033 // create diff and work directories inside base
1034 // no need to check arg_overlay_reuse
1035 char *odiff;
1036 if (asprintf(&odiff, "%s/odiff", basedir) == -1)
1037 errExit("asprintf");
1038 // the new directory will be owned by root
1039 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
1040 perror("mkdir");
1041 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
1042 exit(1);
1043 }
1044 ASSERT_PERMS(odiff, 0, 0, 0755);
1045
1046 char *owork;
1047 if (asprintf(&owork, "%s/owork", basedir) == -1)
1048 errExit("asprintf");
1049 // the new directory will be owned by root
1050 if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) {
1051 perror("mkdir");
1052 fprintf(stderr, "Error: cannot create overlay directory %s\n", owork);
1053 exit(1);
1054 }
1055 ASSERT_PERMS(owork, 0, 0, 0755);
1056
1057 // mount overlayfs
1058 if (arg_debug)
1059 printf("Mounting OverlayFS\n");
1060 char *option;
1061 if (oldkernel) { // old Ubuntu/OpenSUSE kernels
1062 if (arg_overlay_keep) {
1063 fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n");
1064 exit(1);
1065 }
1066 if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
1067 errExit("asprintf");
1068 if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
1069 errExit("mounting overlayfs");
1070 }
1071 else { // kernel 3.18 or newer
1072 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
1073 errExit("asprintf");
1074 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) {
1075 fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor);
1076 errExit("mounting overlayfs");
1077 }
1078
1079 //***************************
1080 // issue #263 start code
1081 // My setup has a separate mount point for /home. When the overlay is mounted,
1082 // the overlay does not contain the original /home contents.
1083 // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work
1084 // @dshmgh, Jan 2016
1085 {
1086 char *overlayhome;
1087 struct stat s;
1088 char *hroot;
1089 char *hdiff;
1090 char *hwork;
1091
1092 // dons add debug
1093 if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork);
1094
1095 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it?
1096 // must create var for oroot/cfg.homedir
1097 if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1)
1098 errExit("asprintf");
1099 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome);
1100
1101 // if no homedir in overlay -- create another overlay for /home
1102 if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) {
1103
1104 // no need to check arg_overlay_reuse
1105 if (asprintf(&hdiff, "%s/hdiff", basedir) == -1)
1106 errExit("asprintf");
1107 // the new directory will be owned by root
1108 if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) {
1109 perror("mkdir");
1110 fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff);
1111 exit(1);
1112 }
1113 ASSERT_PERMS(hdiff, 0, 0, 0755);
1114
1115 // no need to check arg_overlay_reuse
1116 if (asprintf(&hwork, "%s/hwork", basedir) == -1)
1117 errExit("asprintf");
1118 // the new directory will be owned by root
1119 if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) {
1120 perror("mkdir");
1121 fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork);
1122 exit(1);
1123 }
1124 ASSERT_PERMS(hwork, 0, 0, 0755);
1125
1126 // no homedir in overlay so now mount another overlay for /home
1127 if (asprintf(&hroot, "%s/home", oroot) == -1)
1128 errExit("asprintf");
1129 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1)
1130 errExit("asprintf");
1131 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0)
1132 errExit("mounting overlayfs for mounted home directory");
1133
1134 printf("OverlayFS for /home configured in %s directory\n", basedir);
1135 free(hroot);
1136 free(hdiff);
1137 free(hwork);
1138
1139 } // stat(overlayhome)
1140 free(overlayhome);
1141 }
1142 // issue #263 end code
1143 //***************************
1144 }
1145 fmessage("OverlayFS configured in %s directory\n", basedir);
1146 close(basefd);
1147
1148 // /dev, /run and /tmp are not covered by the overlay
1149 // mount-bind dev directory
1150 if (arg_debug)
1151 printf("Mounting /dev\n");
1152 char *dev;
1153 if (asprintf(&dev, "%s/dev", oroot) == -1)
1154 errExit("asprintf");
1155 if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
1156 errExit("mounting /dev");
1157 fs_logger("whitelist /dev");
1158
1159 // mount-bind run directory
1160 if (arg_debug)
1161 printf("Mounting /run\n");
1162 char *run;
1163 if (asprintf(&run, "%s/run", oroot) == -1)
1164 errExit("asprintf");
1165 if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0)
1166 errExit("mounting /run");
1167 fs_logger("whitelist /run");
1168
1169 // mount-bind tmp directory
1170 if (arg_debug)
1171 printf("Mounting /tmp\n");
1172 char *tmp;
1173 if (asprintf(&tmp, "%s/tmp", oroot) == -1)
1174 errExit("asprintf");
1175 if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0)
1176 errExit("mounting /tmp");
1177 fs_logger("whitelist /tmp");
1178
1179 // chroot in the new filesystem
1180#ifdef HAVE_GCOV
1181 __gcov_flush();
1182#endif
1183 if (chroot(oroot) == -1)
1184 errExit("chroot");
1185
1186 // mount a new proc filesystem
1187 if (arg_debug)
1188 printf("Mounting /proc filesystem representing the PID namespace\n");
1189 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
1190 errExit("mounting /proc");
1191
1192 // update /var directory in order to support multiple sandboxes running on the same root directory
1193// if (!arg_private_dev)
1194// fs_dev_shm();
1195 fs_var_lock();
1196 if (!arg_keep_var_tmp)
1197 fs_var_tmp();
1198 if (!arg_writable_var_log)
1199 fs_var_log();
1200 fs_var_lib();
1201 fs_var_cache();
1202 fs_var_utmp();
1203 fs_machineid();
1204
1205 // don't leak user information
1206 restrict_users();
1207
1208 // when starting as root, firejail config is not disabled;
1209 if (getuid() != 0)
1210 disable_config();
1211
1212 // cleanup and exit
1213 free(option);
1214 free(odiff);
1215 free(owork);
1216 free(dev);
1217 free(run);
1218 free(tmp);
1219}
1220#endif
1221
1222// this function is called from sandbox.c before blacklist/whitelist functions 901// this function is called from sandbox.c before blacklist/whitelist functions
1223void fs_private_tmp(void) { 902void fs_private_tmp(void) {
903 EUID_ASSERT();
1224 if (arg_debug) 904 if (arg_debug)
1225 printf("Generate private-tmp whitelist commands\n"); 905 printf("Generate private-tmp whitelist commands\n");
1226 906
@@ -1241,8 +921,10 @@ void fs_private_tmp(void) {
1241 921
1242 // whitelist x11 directory 922 // whitelist x11 directory
1243 profile_add("whitelist /tmp/.X11-unix"); 923 profile_add("whitelist /tmp/.X11-unix");
1244 // read-only x11 directory 924 profile_add("read-only /tmp/.X11-unix");
1245 profile_add("read-only /tmp/.X11-unix"); 925
926 // whitelist sndio directory
927 profile_add("whitelist /tmp/sndio");
1246 928
1247 // whitelist any pulse* file in /tmp directory 929 // whitelist any pulse* file in /tmp directory
1248 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user 930 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c
index 61398f12b..2b0b3003e 100644
--- a/src/firejail/fs_bin.c
+++ b/src/firejail/fs_bin.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -41,9 +41,9 @@ static char *paths[] = {
41 41
42// return 1 if found, 0 if not found 42// return 1 if found, 0 if not found
43static char *check_dir_or_file(const char *name) { 43static char *check_dir_or_file(const char *name) {
44 EUID_ASSERT();
44 assert(name); 45 assert(name);
45 struct stat s; 46 struct stat s;
46 char *fname = NULL;
47 47
48 int i = 0; 48 int i = 0;
49 while (paths[i]) { 49 while (paths[i]) {
@@ -54,50 +54,34 @@ static char *check_dir_or_file(const char *name) {
54 } 54 }
55 55
56 // check file 56 // check file
57 char *fname;
57 if (asprintf(&fname, "%s/%s", paths[i], name) == -1) 58 if (asprintf(&fname, "%s/%s", paths[i], name) == -1)
58 errExit("asprintf"); 59 errExit("asprintf");
59 if (arg_debug) 60 if (arg_debug)
60 printf("Checking %s/%s\n", paths[i], name); 61 printf("Checking %s/%s\n", paths[i], name);
61 if (stat(fname, &s) == 0 && !S_ISDIR(s.st_mode)) { // do not allow directories 62 if (stat(fname, &s) == 0 &&
62 // check symlink to firejail executable in /usr/local/bin 63 !S_ISDIR(s.st_mode) && // do not allow directories
63 if (strcmp(paths[i], "/usr/local/bin") == 0 && is_link(fname)) { 64 !is_firejail_link(fname)) { // skip symlinks to firejail executable, as created by firecfg
64 /* coverity[toctou] */ 65 free(fname);
65 char *actual_path = realpath(fname, NULL);
66 if (actual_path) {
67 char *ptr = strstr(actual_path, "/firejail");
68 if (ptr && strlen(ptr) == strlen("/firejail")) {
69 if (arg_debug)
70 printf("firejail exec symlink detected\n");
71 free(actual_path);
72 free(fname);
73 fname = NULL;
74 i++;
75 continue;
76 }
77 free(actual_path);
78 }
79
80 }
81 break; // file found 66 break; // file found
82 } 67 }
83 68
84 free(fname); 69 free(fname);
85 fname = NULL;
86 i++; 70 i++;
87 } 71 }
88 72
89 if (!fname) { 73 if (!paths[i]) {
90 if (arg_debug) 74 if (arg_debug)
91 fwarning("file %s not found\n", name); 75 fwarning("file %s not found\n", name);
92 return NULL; 76 return NULL;
93 } 77 }
94 78
95 free(fname);
96 return paths[i]; 79 return paths[i];
97} 80}
98 81
99// return 1 if the file is in paths[] 82// return 1 if the file is in paths[]
100static int valid_full_path_file(const char *name) { 83static int valid_full_path_file(const char *name) {
84 EUID_ASSERT();
101 assert(name); 85 assert(name);
102 86
103 if (*name != '/') 87 if (*name != '/')
@@ -149,6 +133,7 @@ static void report_duplication(const char *fname) {
149} 133}
150 134
151static void duplicate(char *fname) { 135static void duplicate(char *fname) {
136 EUID_ASSERT();
152 assert(fname); 137 assert(fname);
153 138
154 if (*fname == '~' || strstr(fname, "..")) { 139 if (*fname == '~' || strstr(fname, "..")) {
@@ -220,6 +205,7 @@ static void duplicate(char *fname) {
220} 205}
221 206
222static void globbing(char *fname) { 207static void globbing(char *fname) {
208 EUID_ASSERT();
223 assert(fname); 209 assert(fname);
224 210
225 // go directly to duplicate() if no globbing char is present - see man 7 glob 211 // go directly to duplicate() if no globbing char is present - see man 7 glob
@@ -256,6 +242,9 @@ static void globbing(char *fname) {
256 // testing for GLOB_NOCHECK - no pattern matched returns the original pattern 242 // testing for GLOB_NOCHECK - no pattern matched returns the original pattern
257 if (strcmp(globbuf.gl_pathv[j], pattern) == 0) 243 if (strcmp(globbuf.gl_pathv[j], pattern) == 0)
258 continue; 244 continue;
245 // skip symlinks to firejail executable, as created by firecfg
246 if (is_firejail_link(globbuf.gl_pathv[j]))
247 continue;
259 248
260 duplicate(globbuf.gl_pathv[j]); 249 duplicate(globbuf.gl_pathv[j]);
261 } 250 }
@@ -267,6 +256,7 @@ static void globbing(char *fname) {
267} 256}
268 257
269void fs_private_bin_list(void) { 258void fs_private_bin_list(void) {
259 EUID_ASSERT();
270 char *private_list = cfg.bin_private_keep; 260 char *private_list = cfg.bin_private_keep;
271 assert(private_list); 261 assert(private_list);
272 262
@@ -274,7 +264,9 @@ void fs_private_bin_list(void) {
274 timetrace_start(); 264 timetrace_start();
275 265
276 // create /run/firejail/mnt/bin directory 266 // create /run/firejail/mnt/bin directory
267 EUID_ROOT();
277 mkdir_attr(RUN_BIN_DIR, 0755, 0, 0); 268 mkdir_attr(RUN_BIN_DIR, 0755, 0, 0);
269 EUID_USER();
278 270
279 if (arg_debug) 271 if (arg_debug)
280 printf("Copying files in the new bin directory\n"); 272 printf("Copying files in the new bin directory\n");
@@ -293,9 +285,9 @@ void fs_private_bin_list(void) {
293 while ((ptr = strtok(NULL, ",")) != NULL) 285 while ((ptr = strtok(NULL, ",")) != NULL)
294 globbing(ptr); 286 globbing(ptr);
295 free(dlist); 287 free(dlist);
296 fs_logger_print();
297 288
298 // mount-bind 289 // mount-bind
290 EUID_ROOT();
299 int i = 0; 291 int i = 0;
300 while (paths[i]) { 292 while (paths[i]) {
301 struct stat s; 293 struct stat s;
@@ -309,6 +301,9 @@ void fs_private_bin_list(void) {
309 } 301 }
310 i++; 302 i++;
311 } 303 }
304 fs_logger_print();
305 EUID_USER();
306
312 selinux_relabel_path(RUN_BIN_DIR, "/bin"); 307 selinux_relabel_path(RUN_BIN_DIR, "/bin");
313 fmessage("%d %s installed in %0.2f ms\n", prog_cnt, (prog_cnt == 1)? "program": "programs", timetrace_end()); 308 fmessage("%d %s installed in %0.2f ms\n", prog_cnt, (prog_cnt == 1)? "program": "programs", timetrace_end());
314} 309}
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c
index 2f0067c93..a6fbbb89a 100644
--- a/src/firejail/fs_dev.c
+++ b/src/firejail/fs_dev.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,7 +20,6 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <glob.h> 23#include <glob.h>
25#include <dirent.h> 24#include <dirent.h>
26#include <fcntl.h> 25#include <fcntl.h>
@@ -122,7 +121,7 @@ static void deventry_mount(void) {
122 i++; 121 i++;
123 continue; 122 continue;
124 } 123 }
125 FILE *fp = fopen(dev[i].dev_fname, "w"); 124 FILE *fp = fopen(dev[i].dev_fname, "we");
126 if (fp) { 125 if (fp) {
127 fprintf(fp, "\n"); 126 fprintf(fp, "\n");
128 SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); 127 SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode);
@@ -187,8 +186,10 @@ static void mount_dev_shm(void) {
187static void process_dev_shm(void) { 186static void process_dev_shm(void) {
188 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...) 187 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...)
189 // looking for jack socket 188 // looking for jack socket
189 EUID_USER();
190 glob_t globbuf; 190 glob_t globbuf;
191 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf); 191 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf);
192 EUID_ROOT();
192 if (globerr && !arg_keep_dev_shm) { 193 if (globerr && !arg_keep_dev_shm) {
193 empty_dev_shm(); 194 empty_dev_shm();
194 return; 195 return;
@@ -218,7 +219,7 @@ void fs_private_dev(void){
218 struct stat s; 219 struct stat s;
219 if (stat("/dev/log", &s) == 0) { 220 if (stat("/dev/log", &s) == 0) {
220 have_devlog = 1; 221 have_devlog = 1;
221 FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); 222 FILE *fp = fopen(RUN_DEVLOG_FILE, "we");
222 if (!fp) 223 if (!fp)
223 have_devlog = 0; 224 have_devlog = 0;
224 else { 225 else {
@@ -239,7 +240,7 @@ void fs_private_dev(void){
239 240
240 // bring back /dev/log 241 // bring back /dev/log
241 if (have_devlog) { 242 if (have_devlog) {
242 FILE *fp = fopen("/dev/log", "w"); 243 FILE *fp = fopen("/dev/log", "we");
243 if (fp) { 244 if (fp) {
244 fprintf(fp, "\n"); 245 fprintf(fp, "\n");
245 fclose(fp); 246 fclose(fp);
@@ -328,8 +329,10 @@ void fs_dev_disable_sound(void) {
328 } 329 }
329 330
330 // disable all jack sockets in /dev/shm 331 // disable all jack sockets in /dev/shm
332 EUID_USER();
331 glob_t globbuf; 333 glob_t globbuf;
332 int globerr = glob("/dev/shm/jack*", GLOB_NOSORT, NULL, &globbuf); 334 int globerr = glob("/dev/shm/jack*", GLOB_NOSORT, NULL, &globbuf);
335 EUID_ROOT();
333 if (globerr) 336 if (globerr)
334 return; 337 return;
335 338
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c
index 8cb25a1ff..deaee31bb 100644
--- a/src/firejail/fs_etc.c
+++ b/src/firejail/fs_etc.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -24,6 +24,7 @@
24#include <sys/types.h> 24#include <sys/types.h>
25#include <time.h> 25#include <time.h>
26#include <unistd.h> 26#include <unistd.h>
27#include <dirent.h>
27 28
28// spoof /etc/machine_id 29// spoof /etc/machine_id
29void fs_machineid(void) { 30void fs_machineid(void) {
@@ -52,7 +53,7 @@ void fs_machineid(void) {
52 mid.u8[8] = (mid.u8[8] & 0x3F) | 0x80; 53 mid.u8[8] = (mid.u8[8] & 0x3F) | 0x80;
53 54
54 // write it in a file 55 // write it in a file
55 FILE *fp = fopen(RUN_MACHINEID, "w"); 56 FILE *fp = fopen(RUN_MACHINEID, "we");
56 if (!fp) 57 if (!fp)
57 errExit("fopen"); 58 errExit("fopen");
58 fprintf(fp, "%08x%08x%08x%08x\n", mid.u32[0], mid.u32[1], mid.u32[2], mid.u32[3]); 59 fprintf(fp, "%08x%08x%08x%08x\n", mid.u32[0], mid.u32[1], mid.u32[2], mid.u32[3]);
@@ -141,7 +142,7 @@ errexit:
141static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) { 142static void duplicate(const char *fname, const char *private_dir, const char *private_run_dir) {
142 assert(fname); 143 assert(fname);
143 144
144 if (*fname == '~' || *fname == '/' || strncmp(fname, "..", 2) == 0) { 145 if (*fname == '~' || *fname == '/' || strstr(fname, "..")) {
145 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname); 146 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", fname);
146 exit(1); 147 exit(1);
147 } 148 }
@@ -164,7 +165,14 @@ static void duplicate(const char *fname, const char *private_dir, const char *pr
164 errExit("asprintf"); 165 errExit("asprintf");
165 166
166 build_dirs(src, dst, strlen(private_dir), strlen(private_run_dir)); 167 build_dirs(src, dst, strlen(private_dir), strlen(private_run_dir));
167 sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FCOPY, src, dst); 168
169 // follow links! this will make a copy of the file or directory pointed by the symlink
170 // this will solve problems such as NixOS #4887
171 // don't follow links to dynamic directories such as /proc
172 if (strcmp(src, "/etc/mtab") == 0)
173 sbox_run(SBOX_ROOT | SBOX_SECCOMP, 3, PATH_FCOPY, src, dst);
174 else
175 sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", src, dst);
168 176
169 free(dst); 177 free(dst);
170 fs_logger2("clone", src); 178 fs_logger2("clone", src);
@@ -250,3 +258,128 @@ void fs_private_dir_list(const char *private_dir, const char *private_run_dir, c
250 fs_private_dir_mount(private_dir, private_run_dir); 258 fs_private_dir_mount(private_dir, private_run_dir);
251 fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end()); 259 fmessage("Private %s installed in %0.2f ms\n", private_dir, timetrace_end());
252} 260}
261
262void fs_rebuild_etc(void) {
263 int have_dhcp = 1;
264 if (cfg.dns1 == NULL && !any_dhcp())
265 have_dhcp = 0;
266
267 if (arg_debug)
268 printf("rebuilding /etc directory\n");
269 if (mkdir(RUN_DNS_ETC, 0755))
270 errExit("mkdir");
271 selinux_relabel_path(RUN_DNS_ETC, "/etc");
272 fs_logger("tmpfs /etc");
273
274 DIR *dir = opendir("/etc");
275 if (!dir)
276 errExit("opendir");
277
278 struct stat s;
279 struct dirent *entry;
280 while ((entry = readdir(dir))) {
281 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
282 continue;
283
284 // skip files in cfg.profile_rebuild_etc list
285 // these files are already blacklisted
286 {
287 ProfileEntry *prf = cfg.profile_rebuild_etc;
288 int found = 0;
289 while (prf) {
290 if (strcmp(entry->d_name, prf->data + 5) == 0) { // 5 is strlen("/etc/")
291 found = 1;
292 break;
293 }
294 prf = prf->next;
295 }
296 if (found)
297 continue;
298 }
299
300 // for resolv.conf we might have to create a brand new file later
301 if (have_dhcp &&
302 (strcmp(entry->d_name, "resolv.conf") == 0 ||
303 strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0))
304 continue;
305// printf("linking %s\n", entry->d_name);
306
307 char *src;
308 if (asprintf(&src, "/etc/%s", entry->d_name) == -1)
309 errExit("asprintf");
310 if (stat(src, &s) != 0) {
311 free(src);
312 continue;
313 }
314
315 char *dest;
316 if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1)
317 errExit("asprintf");
318
319 int symlink_done = 0;
320 if (is_link(src)) {
321 char *rp =realpath(src, NULL);
322 if (rp == NULL) {
323 free(src);
324 free(dest);
325 continue;
326 }
327 if (symlink(rp, dest))
328 errExit("symlink");
329 else
330 symlink_done = 1;
331 }
332 else if (S_ISDIR(s.st_mode))
333 create_empty_dir_as_root(dest, s.st_mode);
334 else
335 create_empty_file_as_root(dest, s.st_mode);
336
337 // bind-mount src on top of dest
338 if (!symlink_done) {
339 if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0)
340 errExit("mount bind mirroring /etc");
341 }
342 fs_logger2("clone", src);
343
344 free(src);
345 free(dest);
346 }
347 closedir(dir);
348
349 // mount bind our private etc directory on top of /etc
350 if (arg_debug)
351 printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC);
352 if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0)
353 errExit("mount bind mirroring /etc");
354 fs_logger("mount /etc");
355
356 if (have_dhcp == 0)
357 return;
358
359 if (arg_debug)
360 printf("Creating a new /etc/resolv.conf file\n");
361 FILE *fp = fopen("/etc/resolv.conf", "wxe");
362 if (!fp) {
363 fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n");
364 exit(1);
365 }
366
367 if (cfg.dns1) {
368 if (any_dhcp())
369 fwarning("network setup uses DHCP, nameservers will likely be overwritten\n");
370 fprintf(fp, "nameserver %s\n", cfg.dns1);
371 }
372 if (cfg.dns2)
373 fprintf(fp, "nameserver %s\n", cfg.dns2);
374 if (cfg.dns3)
375 fprintf(fp, "nameserver %s\n", cfg.dns3);
376 if (cfg.dns4)
377 fprintf(fp, "nameserver %s\n", cfg.dns4);
378
379 // mode and owner
380 SET_PERMS_STREAM(fp, 0, 0, 0644);
381
382 fclose(fp);
383
384 fs_logger("create /etc/resolv.conf");
385}
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 46f32d7ad..061461590 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,7 +19,6 @@
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <linux/limits.h>
23#include <dirent.h> 22#include <dirent.h>
24#include <errno.h> 23#include <errno.h>
25#include <sys/stat.h> 24#include <sys/stat.h>
@@ -34,24 +33,48 @@
34#define O_PATH 010000000 33#define O_PATH 010000000
35#endif 34#endif
36 35
37static void skel(const char *homedir, uid_t u, gid_t g) { 36static void disable_tab_completion(const char *homedir) {
37 if (arg_tab)
38 return;
39
38 char *fname; 40 char *fname;
41 if (asprintf(&fname, "%s/.inputrc", homedir) == -1)
42 errExit("asprintf");
43
44 // don't create a new one if we already have it
45 if (access(fname, F_OK)) {
46 FILE *fp = fopen(fname, "w");
47 if (!fp)
48 errExit("fopen");
49 fprintf(fp, "set disable-completion on\n");
50 fclose(fp);
51 if (chmod(fname, 0644))
52 errExit("chmod");
53 }
54 free(fname);
55}
56
57
58static void skel(const char *homedir) {
59 EUID_ASSERT();
60 char *fname;
61
62 disable_tab_completion(homedir);
39 63
40 // zsh 64 // zsh
41 if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { 65 if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) {
42 // copy skel files 66 // copy skel files
43 if (asprintf(&fname, "%s/.zshrc", homedir) == -1) 67 if (asprintf(&fname, "%s/.zshrc", homedir) == -1)
44 errExit("asprintf"); 68 errExit("asprintf");
45 struct stat s;
46 // don't copy it if we already have the file 69 // don't copy it if we already have the file
47 if (stat(fname, &s) == 0) 70 if (access(fname, F_OK) == 0)
48 return; 71 return;
49 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 72 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
50 fprintf(stderr, "Error: invalid %s file\n", fname); 73 fprintf(stderr, "Error: invalid %s file\n", fname);
51 exit(1); 74 exit(1);
52 } 75 }
53 if (stat("/etc/skel/.zshrc", &s) == 0) { 76 if (access("/etc/skel/.zshrc", R_OK) == 0) {
54 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user 77 copy_file_as_user("/etc/skel/.zshrc", fname, 0644); // regular user
55 fs_logger("clone /etc/skel/.zshrc"); 78 fs_logger("clone /etc/skel/.zshrc");
56 fs_logger2("clone", fname); 79 fs_logger2("clone", fname);
57 } 80 }
@@ -67,17 +90,15 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
67 // copy skel files 90 // copy skel files
68 if (asprintf(&fname, "%s/.cshrc", homedir) == -1) 91 if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
69 errExit("asprintf"); 92 errExit("asprintf");
70 struct stat s;
71
72 // don't copy it if we already have the file 93 // don't copy it if we already have the file
73 if (stat(fname, &s) == 0) 94 if (access(fname, F_OK) == 0)
74 return; 95 return;
75 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 96 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
76 fprintf(stderr, "Error: invalid %s file\n", fname); 97 fprintf(stderr, "Error: invalid %s file\n", fname);
77 exit(1); 98 exit(1);
78 } 99 }
79 if (stat("/etc/skel/.cshrc", &s) == 0) { 100 if (access("/etc/skel/.cshrc", R_OK) == 0) {
80 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user 101 copy_file_as_user("/etc/skel/.cshrc", fname, 0644); // regular user
81 fs_logger("clone /etc/skel/.cshrc"); 102 fs_logger("clone /etc/skel/.cshrc");
82 fs_logger2("clone", fname); 103 fs_logger2("clone", fname);
83 } 104 }
@@ -93,16 +114,15 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
93 // copy skel files 114 // copy skel files
94 if (asprintf(&fname, "%s/.bashrc", homedir) == -1) 115 if (asprintf(&fname, "%s/.bashrc", homedir) == -1)
95 errExit("asprintf"); 116 errExit("asprintf");
96 struct stat s;
97 // don't copy it if we already have the file 117 // don't copy it if we already have the file
98 if (stat(fname, &s) == 0) 118 if (access(fname, F_OK) == 0)
99 return; 119 return;
100 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 120 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
101 fprintf(stderr, "Error: invalid %s file\n", fname); 121 fprintf(stderr, "Error: invalid %s file\n", fname);
102 exit(1); 122 exit(1);
103 } 123 }
104 if (stat("/etc/skel/.bashrc", &s) == 0) { 124 if (access("/etc/skel/.bashrc", R_OK) == 0) {
105 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user 125 copy_file_as_user("/etc/skel/.bashrc", fname, 0644); // regular user
106 fs_logger("clone /etc/skel/.bashrc"); 126 fs_logger("clone /etc/skel/.bashrc");
107 fs_logger2("clone", fname); 127 fs_logger2("clone", fname);
108 } 128 }
@@ -112,6 +132,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
112} 132}
113 133
114static int store_xauthority(void) { 134static int store_xauthority(void) {
135 EUID_ASSERT();
115 if (arg_x11_block) 136 if (arg_x11_block)
116 return 0; 137 return 0;
117 138
@@ -122,15 +143,16 @@ static int store_xauthority(void) {
122 errExit("asprintf"); 143 errExit("asprintf");
123 144
124 struct stat s; 145 struct stat s;
125 if (stat(src, &s) == 0) { 146 if (lstat(src, &s) == 0) {
126 if (is_link(src)) { 147 if (S_ISLNK(s.st_mode)) {
127 fwarning("invalid .Xauthority file\n"); 148 fwarning("invalid .Xauthority file\n");
128 free(src); 149 free(src);
129 return 0; 150 return 0;
130 } 151 }
131 152
132 // create an empty file as root, and change ownership to user 153 // create an empty file as root, and change ownership to user
133 FILE *fp = fopen(dest, "w"); 154 EUID_ROOT();
155 FILE *fp = fopen(dest, "we");
134 if (fp) { 156 if (fp) {
135 fprintf(fp, "\n"); 157 fprintf(fp, "\n");
136 SET_PERMS_STREAM(fp, getuid(), getgid(), 0600); 158 SET_PERMS_STREAM(fp, getuid(), getgid(), 0600);
@@ -138,10 +160,11 @@ static int store_xauthority(void) {
138 } 160 }
139 else 161 else
140 errExit("fopen"); 162 errExit("fopen");
163 EUID_USER();
141 164
142 copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user 165 copy_file_as_user(src, dest, 0600); // regular user
143 fs_logger2("clone", dest);
144 selinux_relabel_path(dest, src); 166 selinux_relabel_path(dest, src);
167 fs_logger2("clone", dest);
145 free(src); 168 free(src);
146 return 1; // file copied 169 return 1; // file copied
147 } 170 }
@@ -151,6 +174,7 @@ static int store_xauthority(void) {
151} 174}
152 175
153static int store_asoundrc(void) { 176static int store_asoundrc(void) {
177 EUID_ASSERT();
154 if (arg_nosound) 178 if (arg_nosound)
155 return 0; 179 return 0;
156 180
@@ -161,11 +185,11 @@ static int store_asoundrc(void) {
161 errExit("asprintf"); 185 errExit("asprintf");
162 186
163 struct stat s; 187 struct stat s;
164 if (stat(src, &s) == 0) { 188 if (lstat(src, &s) == 0) {
165 if (is_link(src)) { 189 if (S_ISLNK(s.st_mode)) {
166 // make sure the real path of the file is inside the home directory 190 // make sure the real path of the file is inside the home directory
167 /* coverity[toctou] */ 191 /* coverity[toctou] */
168 char* rp = realpath(src, NULL); 192 char *rp = realpath(src, NULL);
169 if (!rp) { 193 if (!rp) {
170 fprintf(stderr, "Error: Cannot access %s\n", src); 194 fprintf(stderr, "Error: Cannot access %s\n", src);
171 exit(1); 195 exit(1);
@@ -178,7 +202,8 @@ static int store_asoundrc(void) {
178 } 202 }
179 203
180 // create an empty file as root, and change ownership to user 204 // create an empty file as root, and change ownership to user
181 FILE *fp = fopen(dest, "w"); 205 EUID_ROOT();
206 FILE *fp = fopen(dest, "we");
182 if (fp) { 207 if (fp) {
183 fprintf(fp, "\n"); 208 fprintf(fp, "\n");
184 SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); 209 SET_PERMS_STREAM(fp, getuid(), getgid(), 0644);
@@ -186,10 +211,11 @@ static int store_asoundrc(void) {
186 } 211 }
187 else 212 else
188 errExit("fopen"); 213 errExit("fopen");
214 EUID_USER();
189 215
190 copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user 216 copy_file_as_user(src, dest, 0644); // regular user
191 selinux_relabel_path(dest, src);
192 fs_logger2("clone", dest); 217 fs_logger2("clone", dest);
218 selinux_relabel_path(dest, src);
193 free(src); 219 free(src);
194 return 1; // file copied 220 return 1; // file copied
195 } 221 }
@@ -199,6 +225,7 @@ static int store_asoundrc(void) {
199} 225}
200 226
201static void copy_xauthority(void) { 227static void copy_xauthority(void) {
228 EUID_ASSERT();
202 // copy XAUTHORITY_FILE in the new home directory 229 // copy XAUTHORITY_FILE in the new home directory
203 char *src = RUN_XAUTHORITY_FILE ; 230 char *src = RUN_XAUTHORITY_FILE ;
204 char *dest; 231 char *dest;
@@ -211,16 +238,18 @@ static void copy_xauthority(void) {
211 exit(1); 238 exit(1);
212 } 239 }
213 240
214 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user 241 copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
215 selinux_relabel_path(dest, src);
216 fs_logger2("clone", dest); 242 fs_logger2("clone", dest);
243 selinux_relabel_path(dest, dest);
217 free(dest); 244 free(dest);
218 245
219 // delete the temporary file 246 EUID_ROOT();
220 unlink(src); 247 unlink(src); // delete the temporary file
248 EUID_USER();
221} 249}
222 250
223static void copy_asoundrc(void) { 251static void copy_asoundrc(void) {
252 EUID_ASSERT();
224 // copy ASOUNDRC_FILE in the new home directory 253 // copy ASOUNDRC_FILE in the new home directory
225 char *src = RUN_ASOUNDRC_FILE ; 254 char *src = RUN_ASOUNDRC_FILE ;
226 char *dest; 255 char *dest;
@@ -233,12 +262,14 @@ static void copy_asoundrc(void) {
233 exit(1); 262 exit(1);
234 } 263 }
235 264
236 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user 265 copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
237 fs_logger2("clone", dest); 266 fs_logger2("clone", dest);
267 selinux_relabel_path(dest, dest);
238 free(dest); 268 free(dest);
239 269
240 // delete the temporary file 270 EUID_ROOT();
241 unlink(src); 271 unlink(src); // delete the temporary file
272 EUID_USER();
242} 273}
243 274
244// private mode (--private=homedir): 275// private mode (--private=homedir):
@@ -251,21 +282,22 @@ void fs_private_homedir(void) {
251 char *private_homedir = cfg.home_private; 282 char *private_homedir = cfg.home_private;
252 assert(homedir); 283 assert(homedir);
253 assert(private_homedir); 284 assert(private_homedir);
285 EUID_ASSERT();
286
287 uid_t u = getuid();
288 // gid_t g = getgid();
254 289
255 int xflag = store_xauthority(); 290 int xflag = store_xauthority();
256 int aflag = store_asoundrc(); 291 int aflag = store_asoundrc();
257 292
258 uid_t u = getuid();
259 gid_t g = getgid();
260
261 // mount bind private_homedir on top of homedir 293 // mount bind private_homedir on top of homedir
262 if (arg_debug) 294 if (arg_debug)
263 printf("Mount-bind %s on top of %s\n", private_homedir, homedir); 295 printf("Mount-bind %s on top of %s\n", private_homedir, homedir);
264 // get file descriptors for homedir and private_homedir, fails if there is any symlink 296 // get file descriptors for homedir and private_homedir, fails if there is any symlink
265 int src = safe_fd(private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 297 int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
266 if (src == -1) 298 if (src == -1)
267 errExit("opening private directory"); 299 errExit("opening private directory");
268 int dst = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 300 int dst = safer_openat(-1, homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
269 if (dst == -1) 301 if (dst == -1)
270 errExit("opening home directory"); 302 errExit("opening home directory");
271 // both mount source and target should be owned by the user 303 // both mount source and target should be owned by the user
@@ -286,17 +318,11 @@ void fs_private_homedir(void) {
286 exit(1); 318 exit(1);
287 } 319 }
288 // mount via the links in /proc/self/fd 320 // mount via the links in /proc/self/fd
289 char *proc_src, *proc_dst; 321 EUID_ROOT();
290 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) 322 if (bind_mount_by_fd(src, dst))
291 errExit("asprintf");
292 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
293 errExit("asprintf");
294 if (mount(proc_src, proc_dst, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0)
295 errExit("mount bind"); 323 errExit("mount bind");
296 free(proc_src); 324 EUID_USER();
297 free(proc_dst); 325
298 close(src);
299 close(dst);
300 // check /proc/self/mountinfo to confirm the mount is ok 326 // check /proc/self/mountinfo to confirm the mount is ok
301 MountData *mptr = get_last_mount(); 327 MountData *mptr = get_last_mount();
302 size_t len = strlen(homedir); 328 size_t len = strlen(homedir);
@@ -304,6 +330,8 @@ void fs_private_homedir(void) {
304 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 330 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
305 errLogExit("invalid private mount"); 331 errLogExit("invalid private mount");
306 332
333 close(src);
334 close(dst);
307 fs_logger3("mount-bind", private_homedir, homedir); 335 fs_logger3("mount-bind", private_homedir, homedir);
308 fs_logger2("whitelist", homedir); 336 fs_logger2("whitelist", homedir);
309// preserve mode and ownership 337// preserve mode and ownership
@@ -312,6 +340,7 @@ void fs_private_homedir(void) {
312// if (chmod(homedir, s.st_mode) == -1) 340// if (chmod(homedir, s.st_mode) == -1)
313// errExit("mount-bind chmod"); 341// errExit("mount-bind chmod");
314 342
343 EUID_ROOT();
315 if (u != 0) { 344 if (u != 0) {
316 // mask /root 345 // mask /root
317 if (arg_debug) 346 if (arg_debug)
@@ -330,8 +359,9 @@ void fs_private_homedir(void) {
330 selinux_relabel_path("/home", "/home"); 359 selinux_relabel_path("/home", "/home");
331 fs_logger("tmpfs /home"); 360 fs_logger("tmpfs /home");
332 } 361 }
362 EUID_USER();
333 363
334 skel(homedir, u, g); 364 skel(homedir);
335 if (xflag) 365 if (xflag)
336 copy_xauthority(); 366 copy_xauthority();
337 if (aflag) 367 if (aflag)
@@ -346,12 +376,15 @@ void fs_private_homedir(void) {
346void fs_private(void) { 376void fs_private(void) {
347 char *homedir = cfg.homedir; 377 char *homedir = cfg.homedir;
348 assert(homedir); 378 assert(homedir);
379 EUID_ASSERT();
380
349 uid_t u = getuid(); 381 uid_t u = getuid();
350 gid_t g = getgid(); 382 gid_t g = getgid();
351 383
352 int xflag = store_xauthority(); 384 int xflag = store_xauthority();
353 int aflag = store_asoundrc(); 385 int aflag = store_asoundrc();
354 386
387 EUID_ROOT();
355 // mask /root 388 // mask /root
356 if (arg_debug) 389 if (arg_debug)
357 printf("Mounting a new /root directory\n"); 390 printf("Mounting a new /root directory\n");
@@ -369,12 +402,14 @@ void fs_private(void) {
369 selinux_relabel_path("/home", "/home"); 402 selinux_relabel_path("/home", "/home");
370 fs_logger("tmpfs /home"); 403 fs_logger("tmpfs /home");
371 } 404 }
405 EUID_USER();
372 406
373 if (u != 0) { 407 if (u != 0) {
374 if (!arg_allusers && strncmp(homedir, "/home/", 6) == 0) { 408 if (!arg_allusers && strncmp(homedir, "/home/", 6) == 0) {
375 // create new empty /home/user directory 409 // create new empty /home/user directory
376 if (arg_debug) 410 if (arg_debug)
377 printf("Create a new user directory\n"); 411 printf("Create a new user directory\n");
412 EUID_ROOT();
378 if (mkdir(homedir, S_IRWXU) == -1) { 413 if (mkdir(homedir, S_IRWXU) == -1) {
379 if (mkpath_as_root(homedir) == -1) 414 if (mkpath_as_root(homedir) == -1)
380 errExit("mkpath"); 415 errExit("mkpath");
@@ -383,7 +418,7 @@ void fs_private(void) {
383 } 418 }
384 if (chown(homedir, u, g) < 0) 419 if (chown(homedir, u, g) < 0)
385 errExit("chown"); 420 errExit("chown");
386 421 EUID_USER();
387 fs_logger2("mkdir", homedir); 422 fs_logger2("mkdir", homedir);
388 fs_logger2("tmpfs", homedir); 423 fs_logger2("tmpfs", homedir);
389 } 424 }
@@ -395,7 +430,7 @@ void fs_private(void) {
395 selinux_relabel_path(homedir, homedir); 430 selinux_relabel_path(homedir, homedir);
396 } 431 }
397 432
398 skel(homedir, u, g); 433 skel(homedir);
399 if (xflag) 434 if (xflag)
400 copy_xauthority(); 435 copy_xauthority();
401 if (aflag) 436 if (aflag)
@@ -420,24 +455,40 @@ void fs_check_private_dir(void) {
420} 455}
421 456
422// check new private working directory (--private-cwd= option) - exit if it fails 457// check new private working directory (--private-cwd= option) - exit if it fails
458// for testing:
459// $ firejail --private --private-cwd=. --noprofile ls
460// issue #4780: exposes full home directory, not the --private one
461// $ firejail --private-cwd=.. --noprofile ls -> error: full dir path required
462// $ firejail --private-cwd=/etc --noprofile ls -> OK
463// $ firejail --private-cwd=FULL-SYMLINK-PATH --noprofile ls -> error: no symlinks
464// $ firejail --private --private-cwd="${HOME}" --noprofile ls -al --> OK
465// $ firejail --private --private-cwd='${HOME}' --noprofile ls -al --> OK
466// $ firejail --private-cwd --> OK: should go in top of the home dir
467// profile with "private-cwd ${HOME}
423void fs_check_private_cwd(const char *dir) { 468void fs_check_private_cwd(const char *dir) {
424 EUID_ASSERT(); 469 EUID_ASSERT();
425 invalid_filename(dir, 0); // no globbing 470 invalid_filename(dir, 0); // no globbing
471 if (strcmp(dir, ".") == 0)
472 goto errout;
426 473
427 // Expand the working directory 474 // Expand the working directory
428 cfg.cwd = expand_macros(dir); 475 cfg.cwd = expand_macros(dir);
429 476
430 // realpath/is_dir not used because path may not exist outside of jail 477 // realpath/is_dir not used because path may not exist outside of jail
431 if (strstr(cfg.cwd, "..")) { 478 if (strstr(cfg.cwd, "..") || *cfg.cwd != '/')
432 fprintf(stderr, "Error: invalid private working directory\n"); 479 goto errout;
433 exit(1); 480
434 } 481 return;
482errout:
483 fprintf(stderr, "Error: invalid private working directory\n");
484 exit(1);
435} 485}
436 486
437//*********************************************************************************** 487//***********************************************************************************
438// --private-home 488// --private-home
439//*********************************************************************************** 489//***********************************************************************************
440static char *check_dir_or_file(const char *name) { 490static char *check_dir_or_file(const char *name) {
491 EUID_ASSERT();
441 assert(name); 492 assert(name);
442 493
443 // basic checks 494 // basic checks
@@ -498,6 +549,7 @@ errexit:
498} 549}
499 550
500static void duplicate(char *name) { 551static void duplicate(char *name) {
552 EUID_ASSERT();
501 char *fname = check_dir_or_file(name); 553 char *fname = check_dir_or_file(name);
502 554
503 if (arg_debug) 555 if (arg_debug)
@@ -535,28 +587,32 @@ static void duplicate(char *name) {
535// set skel files, 587// set skel files,
536// restore .Xauthority 588// restore .Xauthority
537void fs_private_home_list(void) { 589void fs_private_home_list(void) {
538 timetrace_start();
539
540 char *homedir = cfg.homedir; 590 char *homedir = cfg.homedir;
541 char *private_list = cfg.home_private_keep; 591 char *private_list = cfg.home_private_keep;
542 assert(homedir); 592 assert(homedir);
543 assert(private_list); 593 assert(private_list);
594 EUID_ASSERT();
544 595
545 int xflag = store_xauthority(); 596 timetrace_start();
546 int aflag = store_asoundrc();
547 597
548 uid_t uid = getuid(); 598 uid_t uid = getuid();
549 gid_t gid = getgid(); 599 gid_t gid = getgid();
550 600
601 int xflag = store_xauthority();
602 int aflag = store_asoundrc();
603
604 EUID_ROOT();
551 // create /run/firejail/mnt/home directory 605 // create /run/firejail/mnt/home directory
552 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); 606 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid);
553 selinux_relabel_path(RUN_HOME_DIR, homedir); 607 selinux_relabel_path(RUN_HOME_DIR, homedir);
554 fs_logger_print(); // save the current log
555 608
556 if (arg_debug) 609 // save the current log
557 printf("Copying files in the new home:\n"); 610 fs_logger_print();
611 EUID_USER();
558 612
559 // copy the list of files in the new home directory 613 // copy the list of files in the new home directory
614 if (arg_debug)
615 printf("Copying files in the new home:\n");
560 char *dlist = strdup(cfg.home_private_keep); 616 char *dlist = strdup(cfg.home_private_keep);
561 if (!dlist) 617 if (!dlist)
562 errExit("strdup"); 618 errExit("strdup");
@@ -576,7 +632,7 @@ void fs_private_home_list(void) {
576 if (arg_debug) 632 if (arg_debug)
577 printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); 633 printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir);
578 634
579 int fd = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 635 int fd = safer_openat(-1, homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
580 if (fd == -1) 636 if (fd == -1)
581 errExit("opening home directory"); 637 errExit("opening home directory");
582 // home directory should be owned by the user 638 // home directory should be owned by the user
@@ -589,24 +645,19 @@ void fs_private_home_list(void) {
589 exit(1); 645 exit(1);
590 } 646 }
591 // mount using the file descriptor 647 // mount using the file descriptor
592 char *proc; 648 EUID_ROOT();
593 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 649 if (bind_mount_path_to_fd(RUN_HOME_DIR, fd))
594 errExit("asprintf");
595 if (mount(RUN_HOME_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
596 errExit("mount bind"); 650 errExit("mount bind");
597 free(proc); 651 EUID_USER();
598 close(fd); 652 close(fd);
653
599 // check /proc/self/mountinfo to confirm the mount is ok 654 // check /proc/self/mountinfo to confirm the mount is ok
600 MountData *mptr = get_last_mount(); 655 MountData *mptr = get_last_mount();
601 if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 656 if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
602 errLogExit("invalid private-home mount"); 657 errLogExit("invalid private-home mount");
603 fs_logger2("tmpfs", homedir); 658 fs_logger2("tmpfs", homedir);
604 659
605 // mask RUN_HOME_DIR, it is writable and not noexec 660 EUID_ROOT();
606 if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
607 errExit("mounting tmpfs");
608 fs_logger2("tmpfs", RUN_HOME_DIR);
609
610 if (uid != 0) { 661 if (uid != 0) {
611 // mask /root 662 // mask /root
612 if (arg_debug) 663 if (arg_debug)
@@ -626,7 +677,12 @@ void fs_private_home_list(void) {
626 fs_logger("tmpfs /home"); 677 fs_logger("tmpfs /home");
627 } 678 }
628 679
629 skel(homedir, uid, gid); 680 // mask RUN_HOME_DIR, it is writable and not noexec
681 if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
682 errExit("mounting tmpfs");
683 EUID_USER();
684
685 skel(homedir);
630 if (xflag) 686 if (xflag)
631 copy_xauthority(); 687 copy_xauthority();
632 if (aflag) 688 if (aflag)
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c
index 8a3bb71ea..dca394865 100644
--- a/src/firejail/fs_hostname.c
+++ b/src/firejail/fs_hostname.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,7 +20,6 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <glob.h> 23#include <glob.h>
25#include <dirent.h> 24#include <dirent.h>
26#include <fcntl.h> 25#include <fcntl.h>
@@ -33,7 +32,7 @@ void fs_hostname(const char *hostname) {
33 if (arg_debug) 32 if (arg_debug)
34 printf("Creating a new /etc/hostname file\n"); 33 printf("Creating a new /etc/hostname file\n");
35 34
36 create_empty_file_as_root(RUN_HOSTNAME_FILE, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 35 create_empty_file_as_root(RUN_HOSTNAME_FILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
37 36
38 // bind-mount the file on top of /etc/hostname 37 // bind-mount the file on top of /etc/hostname
39 if (mount(RUN_HOSTNAME_FILE, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0) 38 if (mount(RUN_HOSTNAME_FILE, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0)
@@ -47,11 +46,11 @@ void fs_hostname(const char *hostname) {
47 printf("Creating a new /etc/hosts file\n"); 46 printf("Creating a new /etc/hosts file\n");
48 // copy /etc/host into our new file, and modify it on the fly 47 // copy /etc/host into our new file, and modify it on the fly
49 /* coverity[toctou] */ 48 /* coverity[toctou] */
50 FILE *fp1 = fopen("/etc/hosts", "r"); 49 FILE *fp1 = fopen("/etc/hosts", "re");
51 if (!fp1) 50 if (!fp1)
52 goto errexit; 51 goto errexit;
53 52
54 FILE *fp2 = fopen(RUN_HOSTS_FILE, "w"); 53 FILE *fp2 = fopen(RUN_HOSTS_FILE, "we");
55 if (!fp2) { 54 if (!fp2) {
56 fclose(fp1); 55 fclose(fp1);
57 goto errexit; 56 goto errexit;
@@ -75,7 +74,7 @@ void fs_hostname(const char *hostname) {
75 } 74 }
76 fclose(fp1); 75 fclose(fp1);
77 // mode and owner 76 // mode and owner
78 SET_PERMS_STREAM(fp2, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 77 SET_PERMS_STREAM(fp2, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
79 fclose(fp2); 78 fclose(fp2);
80 79
81 // bind-mount the file on top of /etc/hostname 80 // bind-mount the file on top of /etc/hostname
@@ -88,118 +87,11 @@ errexit:
88 exit(1); 87 exit(1);
89} 88}
90 89
91void fs_resolvconf(void) {
92 if (cfg.dns1 == NULL && !any_dhcp())
93 return;
94
95 if (arg_debug)
96 printf("mirroring /etc directory\n");
97 if (mkdir(RUN_DNS_ETC, 0755))
98 errExit("mkdir");
99 selinux_relabel_path(RUN_DNS_ETC, "/etc");
100 fs_logger("tmpfs /etc");
101
102 DIR *dir = opendir("/etc");
103 if (!dir)
104 errExit("opendir");
105
106 struct stat s;
107 struct dirent *entry;
108 while ((entry = readdir(dir))) {
109 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
110 continue;
111 // for resolv.conf we create a brand new file
112 if (strcmp(entry->d_name, "resolv.conf") == 0 ||
113 strcmp(entry->d_name, "resolv.conf.dhclient-new") == 0)
114 continue;
115// printf("linking %s\n", entry->d_name);
116
117 char *src;
118 if (asprintf(&src, "/etc/%s", entry->d_name) == -1)
119 errExit("asprintf");
120 if (stat(src, &s) != 0) {
121 free(src);
122 continue;
123 }
124
125 char *dest;
126 if (asprintf(&dest, "%s/%s", RUN_DNS_ETC, entry->d_name) == -1)
127 errExit("asprintf");
128
129 int symlink_done = 0;
130 if (is_link(src)) {
131 char *rp =realpath(src, NULL);
132 if (rp == NULL) {
133 free(src);
134 free(dest);
135 continue;
136 }
137 if (symlink(rp, dest))
138 errExit("symlink");
139 else
140 symlink_done = 1;
141 }
142 else if (S_ISDIR(s.st_mode))
143 create_empty_dir_as_root(dest, s.st_mode);
144 else
145 create_empty_file_as_root(dest, s.st_mode);
146
147 // bind-mount src on top of dest
148 if (!symlink_done) {
149 if (mount(src, dest, NULL, MS_BIND|MS_REC, NULL) < 0)
150 errExit("mount bind mirroring /etc");
151 }
152 fs_logger2("clone", src);
153
154 free(src);
155 free(dest);
156 }
157 closedir(dir);
158
159 // mount bind our private etc directory on top of /etc
160 if (arg_debug)
161 printf("Mount-bind %s on top of /etc\n", RUN_DNS_ETC);
162 if (mount(RUN_DNS_ETC, "/etc", NULL, MS_BIND|MS_REC, NULL) < 0)
163 errExit("mount bind mirroring /etc");
164 fs_logger("mount /etc");
165
166 if (arg_debug)
167 printf("Creating a new /etc/resolv.conf file\n");
168 FILE *fp = fopen("/etc/resolv.conf", "w");
169 if (!fp) {
170 fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n");
171 exit(1);
172 }
173
174 if (cfg.dns1) {
175 if (any_dhcp())
176 fwarning("network setup uses DHCP, nameservers will likely be overwritten\n");
177 fprintf(fp, "nameserver %s\n", cfg.dns1);
178 }
179 if (cfg.dns2)
180 fprintf(fp, "nameserver %s\n", cfg.dns2);
181 if (cfg.dns3)
182 fprintf(fp, "nameserver %s\n", cfg.dns3);
183 if (cfg.dns4)
184 fprintf(fp, "nameserver %s\n", cfg.dns4);
185
186 // mode and owner
187 SET_PERMS_STREAM(fp, 0, 0, 0644);
188
189 fclose(fp);
190
191 fs_logger("create /etc/resolv.conf");
192}
193
194char *fs_check_hosts_file(const char *fname) { 90char *fs_check_hosts_file(const char *fname) {
195 assert(fname); 91 assert(fname);
196 invalid_filename(fname, 0); // no globbing 92 invalid_filename(fname, 0); // no globbing
197 char *rv = expand_macros(fname); 93 char *rv = expand_macros(fname);
198 94
199 // no a link
200 if (is_link(rv))
201 goto errexit;
202
203 // the user has read access to the file 95 // the user has read access to the file
204 if (access(rv, R_OK)) 96 if (access(rv, R_OK))
205 goto errexit; 97 goto errexit;
@@ -222,9 +114,6 @@ void fs_mount_hosts_file(void) {
222 struct stat s; 114 struct stat s;
223 if (stat("/etc/hosts", &s) == -1) 115 if (stat("/etc/hosts", &s) == -1)
224 goto errexit; 116 goto errexit;
225 // not a link
226 if (is_link("/etc/hosts"))
227 goto errexit;
228 // owned by root 117 // owned by root
229 if (s.st_uid != 0) 118 if (s.st_uid != 0)
230 goto errexit; 119 goto errexit;
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c
index 85fb70854..848691a56 100644
--- a/src/firejail/fs_lib.c
+++ b/src/firejail/fs_lib.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -61,17 +61,31 @@ static int valid_full_path(const char *full_path) {
61 return 0; 61 return 0;
62} 62}
63 63
64// return 1 if symlink to firejail executable
65int is_firejail_link(const char *fname) {
66 EUID_ASSERT();
67
68 if (!is_link(fname))
69 return 0;
70
71 char *rp = realpath(fname, NULL);
72 if (!rp)
73 return 0;
74
75 int rv = 0;
76 const char *base = gnu_basename(rp);
77 if (strcmp(base, "firejail") == 0)
78 rv = 1;
79
80 free(rp);
81 return rv;
82}
83
64char *find_in_path(const char *program) { 84char *find_in_path(const char *program) {
65 EUID_ASSERT(); 85 EUID_ASSERT();
66 if (arg_debug) 86 if (arg_debug)
67 printf("Searching $PATH for %s\n", program); 87 printf("Searching $PATH for %s\n", program);
68 88
69 char self[MAXBUF];
70 ssize_t len = readlink("/proc/self/exe", self, MAXBUF - 1);
71 if (len < 0)
72 errExit("readlink");
73 self[len] = '\0';
74
75 const char *path = env_get("PATH"); 89 const char *path = env_get("PATH");
76 if (!path) 90 if (!path)
77 return NULL; 91 return NULL;
@@ -88,18 +102,12 @@ char *find_in_path(const char *program) {
88 if (arg_debug) 102 if (arg_debug)
89 printf("trying #%s#\n", fname); 103 printf("trying #%s#\n", fname);
90 struct stat s; 104 struct stat s;
91 if (stat(fname, &s) == 0) { 105 if (stat(fname, &s) == 0 &&
92 // but skip links created by firecfg 106 !is_firejail_link(fname)) { // skip links created by firecfg
93 char *rp = realpath(fname, NULL); 107 free(dup);
94 if (!rp) 108 return fname;
95 errExit("realpath");
96 if (strcmp(self, rp) != 0) {
97 free(rp);
98 free(dup);
99 return fname;
100 }
101 free(rp);
102 } 109 }
110
103 free(fname); 111 free(fname);
104 tok = strtok(NULL, ":"); 112 tok = strtok(NULL, ":");
105 } 113 }
@@ -178,8 +186,7 @@ void fslib_mount(const char *full_path) {
178 186
179 if (*full_path == '\0' || 187 if (*full_path == '\0' ||
180 !valid_full_path(full_path) || 188 !valid_full_path(full_path) ||
181 access(full_path, F_OK) != 0 || 189 stat_as_user(full_path, &s) != 0 ||
182 stat(full_path, &s) != 0 ||
183 s.st_uid != 0) 190 s.st_uid != 0)
184 return; 191 return;
185 192
@@ -196,6 +203,11 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
196 assert(full_path); 203 assert(full_path);
197 // if library/executable does not exist or the user does not have read access to it 204 // if library/executable does not exist or the user does not have read access to it
198 // print a warning and exit the function. 205 // print a warning and exit the function.
206 if (access(full_path, F_OK)) {
207 if (arg_debug || arg_debug_private_lib)
208 printf("Cannot find %s, skipping...\n", full_path);
209 return;
210 }
199 if (user && access(full_path, R_OK)) { 211 if (user && access(full_path, R_OK)) {
200 if (arg_debug || arg_debug_private_lib) 212 if (arg_debug || arg_debug_private_lib)
201 printf("Cannot read %s, skipping...\n", full_path); 213 printf("Cannot read %s, skipping...\n", full_path);
@@ -203,7 +215,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
203 } 215 }
204 216
205 if (arg_debug || arg_debug_private_lib) 217 if (arg_debug || arg_debug_private_lib)
206 printf(" fslib_mount_libs %s (parse as %s)\n", full_path, user ? "user" : "root"); 218 printf(" fslib_mount_libs %s\n", full_path);
207 // create an empty RUN_LIB_FILE and allow the user to write to it 219 // create an empty RUN_LIB_FILE and allow the user to write to it
208 unlink(RUN_LIB_FILE); // in case is there 220 unlink(RUN_LIB_FILE); // in case is there
209 create_empty_file_as_root(RUN_LIB_FILE, 0644); 221 create_empty_file_as_root(RUN_LIB_FILE, 0644);
@@ -212,7 +224,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
212 224
213 // run fldd to extract the list of files 225 // run fldd to extract the list of files
214 if (arg_debug || arg_debug_private_lib) 226 if (arg_debug || arg_debug_private_lib)
215 printf(" running fldd %s\n", full_path); 227 printf(" running fldd %s as %s\n", full_path, user ? "user" : "root");
216 unsigned mask; 228 unsigned mask;
217 if (user) 229 if (user)
218 mask = SBOX_USER; 230 mask = SBOX_USER;
@@ -221,7 +233,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
221 sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); 233 sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE);
222 234
223 // open the list of libraries and install them on by one 235 // open the list of libraries and install them on by one
224 FILE *fp = fopen(RUN_LIB_FILE, "r"); 236 FILE *fp = fopen(RUN_LIB_FILE, "re");
225 if (!fp) 237 if (!fp)
226 errExit("fopen"); 238 errExit("fopen");
227 239
@@ -246,7 +258,7 @@ static void load_library(const char *fname) {
246 258
247 // existing file owned by root 259 // existing file owned by root
248 struct stat s; 260 struct stat s;
249 if (!access(fname, F_OK) && stat(fname, &s) == 0 && s.st_uid == 0) { 261 if (stat_as_user(fname, &s) == 0 && s.st_uid == 0) {
250 // load directories, regular 64 bit libraries, and 64 bit executables 262 // load directories, regular 64 bit libraries, and 64 bit executables
251 if (S_ISDIR(s.st_mode)) 263 if (S_ISDIR(s.st_mode))
252 fslib_mount(fname); 264 fslib_mount(fname);
@@ -264,9 +276,9 @@ static void install_list_entry(const char *lib) {
264 assert(lib); 276 assert(lib);
265 277
266 // filename check 278 // filename check
267 int len = strlen(lib); 279 reject_meta_chars(lib, 1);
268 if (strcspn(lib, "\\&!?\"'<>%^(){}[];,") != (size_t)len || 280
269 strstr(lib, "..")) { 281 if (strstr(lib, "..")) {
270 fprintf(stderr, "Error: \"%s\" is an invalid library\n", lib); 282 fprintf(stderr, "Error: \"%s\" is an invalid library\n", lib);
271 exit(1); 283 exit(1);
272 } 284 }
@@ -286,19 +298,21 @@ static void install_list_entry(const char *lib) {
286#define DO_GLOBBING 298#define DO_GLOBBING
287#ifdef DO_GLOBBING 299#ifdef DO_GLOBBING
288 // globbing 300 // globbing
301 EUID_USER();
289 glob_t globbuf; 302 glob_t globbuf;
290 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); 303 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
291 if (globerr) { 304 if (globerr) {
292 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname); 305 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname);
293 exit(1); 306 exit(1);
294 } 307 }
308 EUID_ROOT();
295 size_t j; 309 size_t j;
296 for (j = 0; j < globbuf.gl_pathc; j++) { 310 for (j = 0; j < globbuf.gl_pathc; j++) {
297 assert(globbuf.gl_pathv[j]); 311 assert(globbuf.gl_pathv[j]);
298//printf("glob %s\n", globbuf.gl_pathv[j]); 312//printf("glob %s\n", globbuf.gl_pathv[j]);
299 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway 313 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway
300 314
301 // foobar/* includes foobar/. and foobar/.. 315 // foobar/* expands to foobar/. and foobar/..
302 const char *base = gnu_basename(globbuf.gl_pathv[j]); 316 const char *base = gnu_basename(globbuf.gl_pathv[j]);
303 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) 317 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0)
304 continue; 318 continue;
diff --git a/src/firejail/fs_lib2.c b/src/firejail/fs_lib2.c
index c69bf7c98..aefd38e3c 100644
--- a/src/firejail/fs_lib2.c
+++ b/src/firejail/fs_lib2.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -143,7 +143,7 @@ static void fdir(void) {
143 NULL, 143 NULL,
144 }; 144 };
145 145
146 // need to parse as root user, unprivileged users have no read permission on executables 146 // need to parse as root user, unprivileged users have no read permission on some of these binaries
147 int i; 147 int i;
148 for (i = 0; fbin[i]; i++) 148 for (i = 0; fbin[i]; i++)
149 fslib_mount_libs(fbin[i], 0); 149 fslib_mount_libs(fbin[i], 0);
@@ -153,7 +153,9 @@ void fslib_install_firejail(void) {
153 timetrace_start(); 153 timetrace_start();
154 // bring in firejail executable libraries, in case we are redirected here 154 // bring in firejail executable libraries, in case we are redirected here
155 // by a firejail symlink from /usr/local/bin/firejail 155 // by a firejail symlink from /usr/local/bin/firejail
156 fslib_mount_libs(PATH_FIREJAIL, 1); // parse as user 156 // fldd might have no read permission on the firejail executable
157 // parse as root in order to support these setups
158 fslib_mount_libs(PATH_FIREJAIL, 0);
157 159
158 // bring in firejail directory 160 // bring in firejail directory
159 fdir(); 161 fdir();
diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c
index 67ad4b52e..06f03dac5 100644
--- a/src/firejail/fs_logger.c
+++ b/src/firejail/fs_logger.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -92,7 +92,7 @@ void fs_logger_print(void) {
92 if (!head) 92 if (!head)
93 return; 93 return;
94 94
95 FILE *fp = fopen(RUN_FSLOGGER_FILE, "a"); 95 FILE *fp = fopen(RUN_FSLOGGER_FILE, "ae");
96 if (!fp) { 96 if (!fp) {
97 perror("fopen"); 97 perror("fopen");
98 return; 98 return;
@@ -123,15 +123,8 @@ void fs_logger_print_log(pid_t pid) {
123 // in case the pid is that of a firejail process, use the pid of the first child process 123 // in case the pid is that of a firejail process, use the pid of the first child process
124 pid = switch_to_child(pid); 124 pid = switch_to_child(pid);
125 125
126 // check privileges for non-root users 126 // exit if no permission to join the sandbox
127 uid_t uid = getuid(); 127 check_join_permission(pid);
128 if (uid != 0) {
129 uid_t sandbox_uid = pid_get_uid(pid);
130 if (uid != sandbox_uid) {
131 fprintf(stderr, "Error: permission denied\n");
132 exit(1);
133 }
134 }
135 128
136 // print RUN_FSLOGGER_FILE 129 // print RUN_FSLOGGER_FILE
137 char *fname; 130 char *fname;
@@ -139,24 +132,16 @@ void fs_logger_print_log(pid_t pid) {
139 errExit("asprintf"); 132 errExit("asprintf");
140 133
141 EUID_ROOT(); 134 EUID_ROOT();
142 struct stat s; 135 FILE *fp = fopen(fname, "re");
143 if (stat(fname, &s) == -1 || s.st_uid != 0) { 136 free(fname);
144 fprintf(stderr, "Error: Cannot access filesystem log\n");
145 exit(1);
146 }
147
148 /* coverity[toctou] */
149 FILE *fp = fopen(fname, "r");
150 if (!fp) { 137 if (!fp) {
151 fprintf(stderr, "Error: Cannot open filesystem log\n"); 138 fprintf(stderr, "Error: Cannot open filesystem log\n");
152 exit(1); 139 exit(1);
153 } 140 }
154
155 char buf[MAXBUF]; 141 char buf[MAXBUF];
156 while (fgets(buf, MAXBUF, fp)) 142 while (fgets(buf, MAXBUF, fp))
157 printf("%s", buf); 143 printf("%s", buf);
158 fclose(fp); 144 fclose(fp);
159 free(fname);
160 145
161 exit(0); 146 exit(0);
162} 147}
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c
index 8cfeea582..30dbd8e9b 100644
--- a/src/firejail/fs_mkdir.c
+++ b/src/firejail/fs_mkdir.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */ 19 */
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/gcov_wrapper.h"
21#include <sys/types.h> 22#include <sys/types.h>
22#include <sys/stat.h> 23#include <sys/stat.h>
23#include <unistd.h> 24#include <unistd.h>
@@ -25,7 +26,6 @@
25#include <sys/wait.h> 26#include <sys/wait.h>
26#include <string.h> 27#include <string.h>
27 28
28
29static void check(const char *fname) { 29static void check(const char *fname) {
30 // manufacture /run/user directory 30 // manufacture /run/user directory
31 char *runuser; 31 char *runuser;
@@ -95,9 +95,9 @@ void fs_mkdir(const char *name) {
95 95
96 // create directory 96 // create directory
97 mkdir_recursive(expanded); 97 mkdir_recursive(expanded);
98#ifdef HAVE_GCOV 98
99 __gcov_flush(); 99 __gcov_flush();
100#endif 100
101 _exit(0); 101 _exit(0);
102 } 102 }
103 // wait for the child to finish 103 // wait for the child to finish
diff --git a/src/firejail/fs_overlayfs.c b/src/firejail/fs_overlayfs.c
new file mode 100644
index 000000000..167a7e28b
--- /dev/null
+++ b/src/firejail/fs_overlayfs.c
@@ -0,0 +1,470 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20
21#ifdef HAVE_OVERLAYFS
22#include "firejail.h"
23#include "../include/gcov_wrapper.h"
24#include <sys/mount.h>
25#include <sys/wait.h>
26#include <ftw.h>
27#include <errno.h>
28
29#include <fcntl.h>
30#ifndef O_PATH
31#define O_PATH 010000000
32#endif
33
34
35char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
36 assert(subdirname);
37 EUID_ASSERT();
38 struct stat s;
39 char *dirname;
40
41 if (asprintf(&dirname, "%s/.firejail", cfg.homedir) == -1)
42 errExit("asprintf");
43 // check if ~/.firejail already exists
44 if (lstat(dirname, &s) == 0) {
45 if (!S_ISDIR(s.st_mode)) {
46 if (S_ISLNK(s.st_mode))
47 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
48 else
49 fprintf(stderr, "Error: %s is not a directory\n", dirname);
50 exit(1);
51 }
52 if (s.st_uid != getuid()) {
53 fprintf(stderr, "Error: %s is not owned by the current user\n", dirname);
54 exit(1);
55 }
56 }
57 else {
58 // create ~/.firejail directory
59 create_empty_dir_as_user(dirname, 0700);
60 if (stat(dirname, &s) == -1) {
61 fprintf(stderr, "Error: cannot create directory %s\n", dirname);
62 exit(1);
63 }
64 }
65 free(dirname);
66
67 // check overlay directory
68 if (asprintf(&dirname, "%s/.firejail/%s", cfg.homedir, subdirname) == -1)
69 errExit("asprintf");
70 if (lstat(dirname, &s) == 0) {
71 if (!S_ISDIR(s.st_mode)) {
72 if (S_ISLNK(s.st_mode))
73 fprintf(stderr, "Error: %s is a symbolic link\n", dirname);
74 else
75 fprintf(stderr, "Error: %s is not a directory\n", dirname);
76 exit(1);
77 }
78 if (s.st_uid != 0) {
79 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", dirname);
80 exit(1);
81 }
82 if (allow_reuse == 0) {
83 fprintf(stderr, "Error: overlay directory exists, but reuse is not allowed\n");
84 exit(1);
85 }
86 }
87
88 return dirname;
89}
90
91
92// mount overlayfs on top of / directory
93// mounting an overlay and chrooting into it:
94//
95// Old Ubuntu kernel
96// # cd ~
97// # mkdir -p overlay/root
98// # mkdir -p overlay/diff
99// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
100// # chroot /root/overlay/root
101// to shutdown, first exit the chroot and then unmount the overlay
102// # exit
103// # umount /root/overlay/root
104//
105// Kernels 3.18+
106// # cd ~
107// # mkdir -p overlay/root
108// # mkdir -p overlay/diff
109// # mkdir -p overlay/work
110// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
111// # cat /etc/mtab | grep overlay
112// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
113// # chroot /root/overlay/root
114// to shutdown, first exit the chroot and then unmount the overlay
115// # exit
116// # umount /root/overlay/root
117
118// to do: fix the code below
119#include <sys/utsname.h>
120void fs_overlayfs(void) {
121 struct stat s;
122
123 // check kernel version
124 struct utsname u;
125 int rv = uname(&u);
126 if (rv != 0)
127 errExit("uname");
128 int major;
129 int minor;
130 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
131 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
132 exit(1);
133 }
134
135 if (arg_debug)
136 printf("Linux kernel version %d.%d\n", major, minor);
137 int oldkernel = 0;
138 if (major < 3) {
139 fprintf(stderr, "Error: minimum kernel version required 3.x\n");
140 exit(1);
141 }
142 if (major == 3 && minor < 18)
143 oldkernel = 1;
144
145 // mounting an overlayfs on top of / seems to be broken for kernels > 4.19
146 // we disable overlayfs for now, pending fixing
147 if (major >= 4 &&minor >= 19) {
148 fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n");
149 exit(1);
150 }
151
152 char *oroot = RUN_OVERLAY_ROOT;
153 mkdir_attr(oroot, 0755, 0, 0);
154
155 // set base for working and diff directories
156 char *basedir = RUN_MNT_DIR;
157 int basefd = -1;
158
159 if (arg_overlay_keep) {
160 basedir = cfg.overlay_dir;
161 assert(basedir);
162 // get a file descriptor for ~/.firejail, fails if there is any symlink
163 char *firejail;
164 if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1)
165 errExit("asprintf");
166 int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
167 if (fd == -1)
168 errExit("safer_openat");
169 free(firejail);
170 // create basedir if it doesn't exist
171 // the new directory will be owned by root
172 const char *dirname = gnu_basename(basedir);
173 if (mkdirat(fd, dirname, 0755) == -1 && errno != EEXIST) {
174 perror("mkdir");
175 fprintf(stderr, "Error: cannot create overlay directory %s\n", basedir);
176 exit(1);
177 }
178 // open basedir
179 basefd = openat(fd, dirname, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
180 close(fd);
181 }
182 else {
183 basefd = open(basedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
184 }
185 if (basefd == -1) {
186 perror("open");
187 fprintf(stderr, "Error: cannot open overlay directory %s\n", basedir);
188 exit(1);
189 }
190
191 // confirm once more base is owned by root
192 if (fstat(basefd, &s) == -1)
193 errExit("fstat");
194 if (s.st_uid != 0) {
195 fprintf(stderr, "Error: overlay directory %s is not owned by the root user\n", basedir);
196 exit(1);
197 }
198 // confirm permissions of base are 0755
199 if (((S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) & s.st_mode) != (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
200 fprintf(stderr, "Error: invalid permissions on overlay directory %s\n", basedir);
201 exit(1);
202 }
203
204 // create diff and work directories inside base
205 // no need to check arg_overlay_reuse
206 char *odiff;
207 if (asprintf(&odiff, "%s/odiff", basedir) == -1)
208 errExit("asprintf");
209 // the new directory will be owned by root
210 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
211 perror("mkdir");
212 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
213 exit(1);
214 }
215 ASSERT_PERMS(odiff, 0, 0, 0755);
216
217 char *owork;
218 if (asprintf(&owork, "%s/owork", basedir) == -1)
219 errExit("asprintf");
220 // the new directory will be owned by root
221 if (mkdirat(basefd, "owork", 0755) == -1 && errno != EEXIST) {
222 perror("mkdir");
223 fprintf(stderr, "Error: cannot create overlay directory %s\n", owork);
224 exit(1);
225 }
226 ASSERT_PERMS(owork, 0, 0, 0755);
227
228 // mount overlayfs
229 if (arg_debug)
230 printf("Mounting OverlayFS\n");
231 char *option;
232 if (oldkernel) { // old Ubuntu/OpenSUSE kernels
233 if (arg_overlay_keep) {
234 fprintf(stderr, "Error: option --overlay= not available for kernels older than 3.18\n");
235 exit(1);
236 }
237 if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
238 errExit("asprintf");
239 if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
240 errExit("mounting overlayfs");
241 }
242 else { // kernel 3.18 or newer
243 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
244 errExit("asprintf");
245 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0) {
246 fprintf(stderr, "Debug: running on kernel version %d.%d\n", major, minor);
247 errExit("mounting overlayfs");
248 }
249
250 //***************************
251 // issue #263 start code
252 // My setup has a separate mount point for /home. When the overlay is mounted,
253 // the overlay does not contain the original /home contents.
254 // I added code to create a second overlay for /home if the overlay home dir is empty and this seems to work
255 // @dshmgh, Jan 2016
256 {
257 char *overlayhome;
258 struct stat s;
259 char *hroot;
260 char *hdiff;
261 char *hwork;
262
263 // dons add debug
264 if (arg_debug) printf ("DEBUG: chroot dirs are oroot %s odiff %s owork %s\n",oroot,odiff,owork);
265
266 // BEFORE NEXT, WE NEED TO TEST IF /home has any contents or do we need to mount it?
267 // must create var for oroot/cfg.homedir
268 if (asprintf(&overlayhome, "%s%s", oroot, cfg.homedir) == -1)
269 errExit("asprintf");
270 if (arg_debug) printf ("DEBUG: overlayhome var holds ##%s##\n", overlayhome);
271
272 // if no homedir in overlay -- create another overlay for /home
273 if (stat(cfg.homedir, &s) == 0 && stat(overlayhome, &s) == -1) {
274
275 // no need to check arg_overlay_reuse
276 if (asprintf(&hdiff, "%s/hdiff", basedir) == -1)
277 errExit("asprintf");
278 // the new directory will be owned by root
279 if (mkdirat(basefd, "hdiff", 0755) == -1 && errno != EEXIST) {
280 perror("mkdir");
281 fprintf(stderr, "Error: cannot create overlay directory %s\n", hdiff);
282 exit(1);
283 }
284 ASSERT_PERMS(hdiff, 0, 0, 0755);
285
286 // no need to check arg_overlay_reuse
287 if (asprintf(&hwork, "%s/hwork", basedir) == -1)
288 errExit("asprintf");
289 // the new directory will be owned by root
290 if (mkdirat(basefd, "hwork", 0755) == -1 && errno != EEXIST) {
291 perror("mkdir");
292 fprintf(stderr, "Error: cannot create overlay directory %s\n", hwork);
293 exit(1);
294 }
295 ASSERT_PERMS(hwork, 0, 0, 0755);
296
297 // no homedir in overlay so now mount another overlay for /home
298 if (asprintf(&hroot, "%s/home", oroot) == -1)
299 errExit("asprintf");
300 if (asprintf(&option, "lowerdir=/home,upperdir=%s,workdir=%s", hdiff, hwork) == -1)
301 errExit("asprintf");
302 if (mount("overlay", hroot, "overlay", MS_MGC_VAL, option) < 0)
303 errExit("mounting overlayfs for mounted home directory");
304
305 printf("OverlayFS for /home configured in %s directory\n", basedir);
306 free(hroot);
307 free(hdiff);
308 free(hwork);
309
310 } // stat(overlayhome)
311 free(overlayhome);
312 }
313 // issue #263 end code
314 //***************************
315 }
316 fmessage("OverlayFS configured in %s directory\n", basedir);
317 close(basefd);
318
319 // /dev, /run and /tmp are not covered by the overlay
320 // mount-bind dev directory
321 if (arg_debug)
322 printf("Mounting /dev\n");
323 char *dev;
324 if (asprintf(&dev, "%s/dev", oroot) == -1)
325 errExit("asprintf");
326 if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
327 errExit("mounting /dev");
328 fs_logger("whitelist /dev");
329
330 // mount-bind run directory
331 if (arg_debug)
332 printf("Mounting /run\n");
333 char *run;
334 if (asprintf(&run, "%s/run", oroot) == -1)
335 errExit("asprintf");
336 if (mount("/run", run, NULL, MS_BIND|MS_REC, NULL) < 0)
337 errExit("mounting /run");
338 fs_logger("whitelist /run");
339
340 // mount-bind tmp directory
341 if (arg_debug)
342 printf("Mounting /tmp\n");
343 char *tmp;
344 if (asprintf(&tmp, "%s/tmp", oroot) == -1)
345 errExit("asprintf");
346 if (mount("/tmp", tmp, NULL, MS_BIND|MS_REC, NULL) < 0)
347 errExit("mounting /tmp");
348 fs_logger("whitelist /tmp");
349
350 // chroot in the new filesystem
351 __gcov_flush();
352
353 if (chroot(oroot) == -1)
354 errExit("chroot");
355
356 // mount a new proc filesystem
357 if (arg_debug)
358 printf("Mounting /proc filesystem representing the PID namespace\n");
359 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
360 errExit("mounting /proc");
361
362 // update /var directory in order to support multiple sandboxes running on the same root directory
363// if (!arg_private_dev)
364// fs_dev_shm();
365 fs_var_lock();
366 if (!arg_keep_var_tmp)
367 fs_var_tmp();
368 if (!arg_writable_var_log)
369 fs_var_log();
370 fs_var_lib();
371 fs_var_cache();
372 fs_var_utmp();
373 fs_machineid();
374
375 // don't leak user information
376 restrict_users();
377
378 // when starting as root, firejail config is not disabled;
379 if (getuid() != 0)
380 disable_config();
381
382 // cleanup and exit
383 free(option);
384 free(odiff);
385 free(owork);
386 free(dev);
387 free(run);
388 free(tmp);
389}
390
391
392static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
393 (void) sb;
394 (void) typeflag;
395 (void) ftwbuf;
396 assert(fpath);
397
398 if (strcmp(fpath, ".") == 0) // rmdir would fail with EINVAL
399 return 0;
400
401 if (remove(fpath)) { // removes the link not the actual file
402 fprintf(stderr, "Error: cannot remove file: %s\n", strerror(errno));
403 exit(1);
404 }
405
406 return 0;
407}
408
409int remove_overlay_directory(void) {
410 EUID_ASSERT();
411 sleep(1);
412
413 char *path;
414 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
415 errExit("asprintf");
416
417 if (access(path, F_OK) == 0) {
418 pid_t child = fork();
419 if (child < 0)
420 errExit("fork");
421 if (child == 0) {
422 // open ~/.firejail
423 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
424 if (fd == -1) {
425 fprintf(stderr, "Error: cannot open %s\n", path);
426 exit(1);
427 }
428 struct stat s;
429 if (fstat(fd, &s) == -1)
430 errExit("fstat");
431 if (!S_ISDIR(s.st_mode)) {
432 if (S_ISLNK(s.st_mode))
433 fprintf(stderr, "Error: %s is a symbolic link\n", path);
434 else
435 fprintf(stderr, "Error: %s is not a directory\n", path);
436 exit(1);
437 }
438 if (s.st_uid != getuid()) {
439 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
440 exit(1);
441 }
442 // chdir to ~/.firejail
443 if (fchdir(fd) == -1)
444 errExit("fchdir");
445 close(fd);
446
447 EUID_ROOT();
448 // FTW_PHYS - do not follow symbolic links
449 if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1)
450 errExit("nftw");
451
452 EUID_USER();
453 // remove ~/.firejail
454 if (rmdir(path) == -1)
455 errExit("rmdir");
456
457 __gcov_flush();
458
459 _exit(0);
460 }
461 // wait for the child to finish
462 waitpid(child, NULL, 0);
463 // check if ~/.firejail was deleted
464 if (access(path, F_OK) == 0)
465 return 1;
466 }
467 return 0;
468}
469
470#endif // HAVE_OVERLAYFS
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c
index 8f939b5f5..4cecea9ce 100644
--- a/src/firejail/fs_trace.c
+++ b/src/firejail/fs_trace.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,26 +20,31 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <glob.h> 23#include <glob.h>
25#include <dirent.h> 24#include <dirent.h>
26#include <fcntl.h> 25#include <fcntl.h>
27#include <pwd.h> 26#include <pwd.h>
28 27
29void fs_trace_preload(void) { 28// create an empty /etc/ld.so.preload
29void fs_trace_touch_preload(void) {
30 create_empty_file_as_root("/etc/ld.so.preload", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
31}
32
33void fs_trace_touch_or_store_preload(void) {
30 struct stat s; 34 struct stat s;
31 35
32 // create an empty /etc/ld.so.preload 36 if (stat("/etc/ld.so.preload", &s) != 0) {
33 if (stat("/etc/ld.so.preload", &s)) { 37 fs_trace_touch_preload();
34 if (arg_debug) 38 return;
35 printf("Creating an empty /etc/ld.so.preload file\n"); 39 }
36 /* coverity[toctou] */ 40
37 FILE *fp = fopen("/etc/ld.so.preload", "w"); 41 if (s.st_size == 0)
38 if (!fp) 42 return;
39 errExit("fopen"); 43
40 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 44 // create a copy of /etc/ld.so.preload
41 fclose(fp); 45 if (copy_file("/etc/ld.so.preload", RUN_LDPRELOAD_FILE, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
42 fs_logger("touch /etc/ld.so.preload"); 46 fprintf(stderr, "Error: cannot copy /etc/ld.so.preload file\n");
47 exit(1);
43 } 48 }
44} 49}
45 50
@@ -48,7 +53,7 @@ void fs_tracefile(void) {
48 if (arg_debug) 53 if (arg_debug)
49 printf("Creating an empty trace log file: %s\n", arg_tracefile); 54 printf("Creating an empty trace log file: %s\n", arg_tracefile);
50 EUID_USER(); 55 EUID_USER();
51 int fd = open(arg_tracefile, O_CREAT|O_WRONLY|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 56 int fd = open(arg_tracefile, O_CREAT|O_WRONLY|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
52 if (fd == -1) { 57 if (fd == -1) {
53 perror("open"); 58 perror("open");
54 fprintf(stderr, "Error: cannot open trace log file %s for writing\n", arg_tracefile); 59 fprintf(stderr, "Error: cannot open trace log file %s for writing\n", arg_tracefile);
@@ -64,20 +69,16 @@ void fs_tracefile(void) {
64 if (ftruncate(fd, 0) == -1) 69 if (ftruncate(fd, 0) == -1)
65 errExit("ftruncate"); 70 errExit("ftruncate");
66 EUID_ROOT(); 71 EUID_ROOT();
67 FILE *fp = fopen(RUN_TRACE_FILE, "w"); 72 FILE *fp = fopen(RUN_TRACE_FILE, "we");
68 if (!fp) 73 if (!fp)
69 errExit("fopen " RUN_TRACE_FILE); 74 errExit("fopen " RUN_TRACE_FILE);
70 fclose(fp); 75 fclose(fp);
71 fs_logger2("touch ", arg_tracefile); 76 fs_logger2("touch", arg_tracefile);
72 // mount using the symbolic link in /proc/self/fd 77 // mount using the symbolic link in /proc/self/fd
73 if (arg_debug) 78 if (arg_debug)
74 printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE); 79 printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE);
75 char *proc; 80 if (bind_mount_fd_to_path(fd, RUN_TRACE_FILE))
76 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
77 errExit("asprintf");
78 if (mount(proc, RUN_TRACE_FILE, NULL, MS_BIND|MS_REC, NULL) < 0)
79 errExit("mount bind " RUN_TRACE_FILE); 81 errExit("mount bind " RUN_TRACE_FILE);
80 free(proc);
81 close(fd); 82 close(fd);
82 // now that RUN_TRACE_FILE is user-writable, mount it noexec 83 // now that RUN_TRACE_FILE is user-writable, mount it noexec
83 fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0); 84 fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0);
@@ -88,7 +89,7 @@ void fs_trace(void) {
88 if (arg_debug) 89 if (arg_debug)
89 printf("Create the new ld.so.preload file\n"); 90 printf("Create the new ld.so.preload file\n");
90 91
91 FILE *fp = fopen(RUN_LDPRELOAD_FILE, "w"); 92 FILE *fp = fopen(RUN_LDPRELOAD_FILE, "ae");
92 if (!fp) 93 if (!fp)
93 errExit("fopen"); 94 errExit("fopen");
94 const char *prefix = RUN_FIREJAIL_LIB_DIR; 95 const char *prefix = RUN_FIREJAIL_LIB_DIR;
@@ -105,7 +106,7 @@ void fs_trace(void) {
105 fmessage("Post-exec seccomp protector enabled\n"); 106 fmessage("Post-exec seccomp protector enabled\n");
106 } 107 }
107 108
108 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 109 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
109 fclose(fp); 110 fclose(fp);
110 111
111 // mount the new preload file 112 // mount the new preload file
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c
index f07581cd8..9523875d7 100644
--- a/src/firejail/fs_var.c
+++ b/src/firejail/fs_var.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,7 +20,6 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <glob.h> 23#include <glob.h>
25#include <dirent.h> 24#include <dirent.h>
26#include <fcntl.h> 25#include <fcntl.h>
@@ -127,17 +126,17 @@ void fs_var_log(void) {
127 126
128 // create an empty /var/log/wtmp file 127 // create an empty /var/log/wtmp file
129 /* coverity[toctou] */ 128 /* coverity[toctou] */
130 FILE *fp = fopen("/var/log/wtmp", "w"); 129 FILE *fp = fopen("/var/log/wtmp", "wxe");
131 if (fp) { 130 if (fp) {
132 SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); 131 SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
133 fclose(fp); 132 fclose(fp);
134 } 133 }
135 fs_logger("touch /var/log/wtmp"); 134 fs_logger("touch /var/log/wtmp");
136 135
137 // create an empty /var/log/btmp file 136 // create an empty /var/log/btmp file
138 fp = fopen("/var/log/btmp", "w"); 137 fp = fopen("/var/log/btmp", "wxe");
139 if (fp) { 138 if (fp) {
140 SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP); 139 SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
141 fclose(fp); 140 fclose(fp);
142 } 141 }
143 fs_logger("touch /var/log/btmp"); 142 fs_logger("touch /var/log/btmp");
@@ -158,8 +157,7 @@ void fs_var_lib(void) {
158 fs_logger("tmpfs /var/lib/dhcp"); 157 fs_logger("tmpfs /var/lib/dhcp");
159 158
160 // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file 159 // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file
161 FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w"); 160 FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "wxe");
162
163 if (fp) { 161 if (fp) {
164 fprintf(fp, "\n"); 162 fprintf(fp, "\n");
165 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 163 SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
@@ -287,7 +285,7 @@ void fs_var_utmp(void) {
287 if (stat(UTMP_FILE, &s) == 0) 285 if (stat(UTMP_FILE, &s) == 0)
288 utmp_group = s.st_gid; 286 utmp_group = s.st_gid;
289 else { 287 else {
290 fwarning("cannot find /var/run/utmp\n"); 288 fwarning("cannot find %s\n", UTMP_FILE);
291 return; 289 return;
292 } 290 }
293 291
@@ -296,7 +294,7 @@ void fs_var_utmp(void) {
296 printf("Create the new utmp file\n"); 294 printf("Create the new utmp file\n");
297 295
298 /* coverity[toctou] */ 296 /* coverity[toctou] */
299 FILE *fp = fopen(RUN_UTMP_FILE, "w"); 297 FILE *fp = fopen(RUN_UTMP_FILE, "we");
300 if (!fp) 298 if (!fp)
301 errExit("fopen"); 299 errExit("fopen");
302 300
@@ -315,7 +313,7 @@ void fs_var_utmp(void) {
315 // save new utmp file 313 // save new utmp file
316 int rv = fwrite(&u_boot, sizeof(u_boot), 1, fp); 314 int rv = fwrite(&u_boot, sizeof(u_boot), 1, fp);
317 (void) rv; 315 (void) rv;
318 SET_PERMS_STREAM(fp, 0, utmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); 316 SET_PERMS_STREAM(fp, 0, utmp_group, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
319 fclose(fp); 317 fclose(fp);
320 318
321 // mount the new utmp file 319 // mount the new utmp file
@@ -323,5 +321,9 @@ void fs_var_utmp(void) {
323 printf("Mount the new utmp file\n"); 321 printf("Mount the new utmp file\n");
324 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 322 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
325 errExit("mount bind utmp"); 323 errExit("mount bind utmp");
326 fs_logger("create /var/run/utmp"); 324 fs_logger2("create", UTMP_FILE);
325
326 // blacklist RUN_UTMP_FILE
327 if (mount(RUN_RO_FILE, RUN_UTMP_FILE, NULL, MS_BIND, "mode=400,gid=0") < 0)
328 errExit("mount bind");
327} 329}
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 698d47b69..3377b2592 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -16,63 +16,70 @@
16 * You should have received a copy of the GNU General Public License along 16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19 */
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/mount.h> 21#include <sys/mount.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <linux/limits.h>
24#include <fnmatch.h> 23#include <fnmatch.h>
25#include <glob.h> 24#include <glob.h>
26#include <dirent.h>
27#include <errno.h> 25#include <errno.h>
28 26
29#include <fcntl.h> 27#include <fcntl.h>
30#ifndef O_PATH 28#ifndef O_PATH
31# define O_PATH 010000000 29#define O_PATH 010000000
32#endif 30#endif
33 31
32#define TOP_MAX 64 // maximum number of top level directories
33
34// mountinfo functionality test; 34// mountinfo functionality test;
35// 1. enable TEST_MOUNTINFO definition 35// 1. enable TEST_MOUNTINFO definition
36// 2. run firejail --whitelist=/any/directory 36// 2. run firejail --whitelist=/any/directory
37//#define TEST_MOUNTINFO 37//#define TEST_MOUNTINFO
38 38
39#define EMPTY_STRING ("") 39static size_t homedir_len = 0; // cache length of homedir string
40static size_t homedir_len; // cache length of homedir string 40static size_t runuser_len = 0; // cache length of runuser string
41static size_t runuser_len; // cache length of runuser string 41static char *runuser = NULL;
42static char *runuser;
43 42
44 43
45static int mkpath(const char* path, mode_t mode) {
46 assert(path && *path);
47 mode |= 0111;
48 44
49 // create directories with uid/gid as root, or as current user if inside home or run/user/$uid directory 45static void whitelist_error(const char *path) {
50 int userprivs = 0; 46 assert(path);
51 if ((strncmp(path, cfg.homedir, homedir_len) == 0 && path[homedir_len] == '/') || 47
52 (strncmp(path, runuser, runuser_len) == 0 && path[runuser_len] == '/')) { 48 fprintf(stderr, "Error: invalid whitelist path %s\n", path);
53 EUID_USER(); 49 exit(1);
54 userprivs = 1; 50}
51
52static int whitelist_mkpath(const char *parentdir, const char *relpath, mode_t mode) {
53 // starting from top level directory
54 int parentfd = safer_openat(-1, parentdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
55 if (parentfd < 0)
56 errExit("open");
57
58 // top level directory mount id
59 int mountid = get_mount_id(parentfd);
60 if (mountid < 0) {
61 close(parentfd);
62 return -1;
55 } 63 }
56 64
57 // work on a copy of the path 65 // work on a copy of the path
58 char *dup = strdup(path); 66 char *dup = strdup(relpath);
59 if (!dup) 67 if (!dup)
60 errExit("strdup"); 68 errExit("strdup");
61 69
62 // don't create the last path element 70 // only create leading directories, don't create the file
63 char *p = strrchr(dup, '/'); 71 char *p = strrchr(dup, '/');
64 assert(p); 72 if (!p) { // nothing to do
73 free(dup);
74 return parentfd;
75 }
65 *p = '\0'; 76 *p = '\0';
66 77
67 int parentfd = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC);
68 if (parentfd == -1)
69 errExit("open");
70
71 // traverse the path, return -1 if a symlink is encountered 78 // traverse the path, return -1 if a symlink is encountered
72 int done = 0;
73 int fd = -1; 79 int fd = -1;
80 int done = 0;
74 char *tok = strtok(dup, "/"); 81 char *tok = strtok(dup, "/");
75 assert(tok); // path is no top level directory 82 assert(tok);
76 while (tok) { 83 while (tok) {
77 // create the directory if necessary 84 // create the directory if necessary
78 if (mkdirat(parentfd, tok, mode) == -1) { 85 if (mkdirat(parentfd, tok, mode) == -1) {
@@ -81,9 +88,6 @@ static int mkpath(const char* path, mode_t mode) {
81 perror("mkdir"); 88 perror("mkdir");
82 close(parentfd); 89 close(parentfd);
83 free(dup); 90 free(dup);
84 if (userprivs) {
85 EUID_ROOT();
86 }
87 return -1; 91 return -1;
88 } 92 }
89 } 93 }
@@ -96,9 +100,15 @@ static int mkpath(const char* path, mode_t mode) {
96 perror("open"); 100 perror("open");
97 close(parentfd); 101 close(parentfd);
98 free(dup); 102 free(dup);
99 if (userprivs) { 103 return -1;
100 EUID_ROOT(); 104 }
101 } 105 // different mount id indicates earlier whitelist mount
106 if (get_mount_id(fd) != mountid) {
107 if (arg_debug || arg_debug_whitelists)
108 printf("Debug %d: whitelisted already\n", __LINE__);
109 close(parentfd);
110 close(fd);
111 free(dup);
102 return -1; 112 return -1;
103 } 113 }
104 // move on to next path segment 114 // move on to next path segment
@@ -107,199 +117,110 @@ static int mkpath(const char* path, mode_t mode) {
107 tok = strtok(NULL, "/"); 117 tok = strtok(NULL, "/");
108 } 118 }
109 119
110 if (done) 120 if (done) {
111 fs_logger2("mkpath", path); 121 char *abspath;
122 if (asprintf(&abspath, "%s/%s", parentdir, relpath) < 0)
123 errExit("asprintf");
124 fs_logger2("mkpath", abspath);
125 free(abspath);
126 }
112 127
113 free(dup); 128 free(dup);
114 if (userprivs) {
115 EUID_ROOT();
116 }
117 return fd; 129 return fd;
118} 130}
119 131
120static void whitelist_path(ProfileEntry *entry) { 132static void whitelist_file(const TopDir * const top, const char *path) {
121 assert(entry); 133 EUID_ASSERT();
122 const char *path = entry->data + 10; 134 assert(top && path);
123 const char *fname;
124 char *wfile = NULL;
125
126 if (entry->wldir == WLDIR_HOME) {
127 if (strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/')
128 // either symlink pointing outside home directory
129 // or entire home directory, skip the mount
130 return;
131
132 fname = path + homedir_len + 1; // strlen("/home/user/")
133
134 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1)
135 errExit("asprintf");
136 }
137 else if (entry->wldir == WLDIR_TMP) {
138 fname = path + 5; // strlen("/tmp/")
139
140 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1)
141 errExit("asprintf");
142 }
143 else if (entry->wldir == WLDIR_MEDIA) {
144 fname = path + 7; // strlen("/media/")
145 135
146 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) 136 // check if path is inside top level directory
147 errExit("asprintf"); 137 size_t top_pathlen = strlen(top->path);
148 } 138 if (strncmp(top->path, path, top_pathlen) != 0 || path[top_pathlen] != '/')
149 else if (entry->wldir == WLDIR_MNT) { 139 return;
150 fname = path + 5; // strlen("/mnt/") 140 const char *relpath = path + top_pathlen + 1;
151 141
152 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) 142 // open mount source, using a file descriptor that refers to the
153 errExit("asprintf"); 143 // top level directory
154 } 144 // as the top level directory was opened before mounting the tmpfs
155 else if (entry->wldir == WLDIR_VAR) { 145 // we still have full access to all directory contents
156 if (strncmp(path, "/var/", 5) != 0) 146 // take care to not follow symbolic links (top->fd was obtained without
157 // symlink pointing outside /var, skip the mount 147 // following a link, too)
158 return; 148 int fd = safer_openat(top->fd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC);
159
160 fname = path + 5; // strlen("/var/")
161
162 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1)
163 errExit("asprintf");
164 }
165 else if (entry->wldir == WLDIR_DEV) {
166 if (strncmp(path, "/dev/", 5) != 0)
167 // symlink pointing outside /dev, skip the mount
168 return;
169
170 fname = path + 5; // strlen("/dev/")
171
172 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1)
173 errExit("asprintf");
174 }
175 else if (entry->wldir == WLDIR_OPT) {
176 fname = path + 5; // strlen("/opt/")
177
178 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1)
179 errExit("asprintf");
180 }
181 else if (entry->wldir == WLDIR_SRV) {
182 fname = path + 5; // strlen("/srv/")
183
184 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1)
185 errExit("asprintf");
186 }
187 else if (entry->wldir == WLDIR_ETC) {
188 if (strncmp(path, "/etc/", 5) != 0)
189 // symlink pointing outside /etc, skip the mount
190 return;
191
192 fname = path + 5; // strlen("/etc/")
193
194 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1)
195 errExit("asprintf");
196 }
197 else if (entry->wldir == WLDIR_SHARE) {
198 fname = path + 11; // strlen("/usr/share/")
199
200 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1)
201 errExit("asprintf");
202 }
203 else if (entry->wldir == WLDIR_MODULE) {
204 fname = path + 12; // strlen("/sys/module/")
205
206 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1)
207 errExit("asprintf");
208 }
209 else if (entry->wldir == WLDIR_RUN) {
210 fname = path + runuser_len + 1; // strlen("/run/user/$uid/")
211
212 if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_RUN_USER_DIR, fname) == -1)
213 errExit("asprintf");
214 }
215 assert(wfile);
216
217 if (arg_debug || arg_debug_whitelists)
218 printf("Whitelisting %s\n", path);
219
220 // confirm again the mount source exists and there is no symlink
221 struct stat wfilestat;
222 EUID_USER();
223 int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC);
224 EUID_ROOT();
225 if (fd == -1) { 149 if (fd == -1) {
226 if (arg_debug || arg_debug_whitelists) 150 if (arg_debug || arg_debug_whitelists)
227 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 151 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
228 free(wfile);
229 return; 152 return;
230 } 153 }
231 if (fstat(fd, &wfilestat) == -1) 154 struct stat s;
155 if (fstat(fd, &s) == -1)
232 errExit("fstat"); 156 errExit("fstat");
233 close(fd); 157 if (S_ISLNK(s.st_mode)) {
234 if (S_ISLNK(wfilestat.st_mode)) {
235 if (arg_debug || arg_debug_whitelists) 158 if (arg_debug || arg_debug_whitelists)
236 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 159 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
237 free(wfile); 160 close(fd);
238 return; 161 return;
239 } 162 }
240 163
241 // create path of the mount target if necessary 164 // now modify the tmpfs:
242 int fd2 = mkpath(path, 0755); 165 // create mount target as root, except if inside home or run/user/$UID directory
166 if (strcmp(top->path, cfg.homedir) != 0 &&
167 strcmp(top->path, runuser) != 0)
168 EUID_ROOT();
169
170 // create path of the mount target
171 int fd2 = whitelist_mkpath(top->path, relpath, 0755);
243 if (fd2 == -1) { 172 if (fd2 == -1) {
244 // something went wrong during path creation or a symlink was found;
245 // if there is a symlink somewhere in the path of the mount target,
246 // assume the file is whitelisted already
247 if (arg_debug || arg_debug_whitelists) 173 if (arg_debug || arg_debug_whitelists)
248 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 174 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
249 free(wfile); 175 close(fd);
176 EUID_USER();
250 return; 177 return;
251 } 178 }
252 179
253 // get file name of the mount target 180 // get file name of the mount target
254 const char *file = gnu_basename(path); 181 const char *file = gnu_basename(relpath);
255 182
256 // create the mount target if necessary and open it, a symlink is rejected 183 // create mount target itself if necessary
184 // and open it, a symlink is not allowed
257 int fd3 = -1; 185 int fd3 = -1;
258 if (S_ISDIR(wfilestat.st_mode)) { 186 if (S_ISDIR(s.st_mode)) {
259 // directory foo can exist already: 187 // directory bar can exist already:
260 // firejail --whitelist=/foo/bar --whitelist=/foo 188 // firejail --whitelist=/foo/bar/baz --whitelist=/foo/bar
261 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { 189 if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) {
262 if (arg_debug || arg_debug_whitelists) { 190 if (arg_debug || arg_debug_whitelists) {
263 perror("mkdir"); 191 perror("mkdir");
264 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); 192 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
265 } 193 }
194 close(fd);
266 close(fd2); 195 close(fd2);
267 free(wfile); 196 EUID_USER();
268 return; 197 return;
269 } 198 }
270 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 199 fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
271 } 200 }
272 else { 201 else
273 // create an empty file, fails with EEXIST if it is whitelisted already: 202 // create an empty file
274 // firejail --whitelist=/foo --whitelist=/foo/bar 203 // fails with EEXIST if it is whitelisted already
275 fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); 204 fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR);
276 }
277 205
278 if (fd3 == -1) { 206 if (fd3 == -1) {
279 if (arg_debug || arg_debug_whitelists) { 207 if (errno != EEXIST && (arg_debug || arg_debug_whitelists)) {
280 if (errno != EEXIST) { 208 perror("open");
281 perror("open"); 209 printf("Debug %d: skip whitelist %s\n", __LINE__, path);
282 printf("Debug %d: skip whitelisting of %s\n", __LINE__, path);
283 }
284 } 210 }
211 close(fd);
285 close(fd2); 212 close(fd2);
286 free(wfile); 213 EUID_USER();
287 return; 214 return;
288 } 215 }
289 close(fd2); 216 close(fd2);
290 217
291 fs_logger2("whitelist", path); 218 if (arg_debug || arg_debug_whitelists)
292 219 printf("Whitelisting %s\n", path);
293 // in order to make this mount resilient against symlink attacks, use 220 EUID_ROOT();
294 // a magic link in /proc/self/fd instead of mounting on path directly 221 if (bind_mount_by_fd(fd, fd3))
295 char *proc;
296 if (asprintf(&proc, "/proc/self/fd/%d", fd3) == -1)
297 errExit("asprintf");
298 if (mount(wfile, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
299 errExit("mount bind"); 222 errExit("mount bind");
300 free(proc); 223 EUID_USER();
301 close(fd3);
302
303 // check the last mount operation 224 // check the last mount operation
304 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found 225 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found
305#ifdef TEST_MOUNTINFO 226#ifdef TEST_MOUNTINFO
@@ -316,37 +237,54 @@ static void whitelist_path(ProfileEntry *entry) {
316 // - there should be more than one '/' char in dest string 237 // - there should be more than one '/' char in dest string
317 if (mptr->dir == strrchr(mptr->dir, '/')) 238 if (mptr->dir == strrchr(mptr->dir, '/'))
318 errLogExit("invalid whitelist mount"); 239 errLogExit("invalid whitelist mount");
319 // confirm the right file was mounted by comparing device and inode numbers 240 close(fd);
320 int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 241 close(fd3);
321 if (fd4 == -1) 242 fs_logger2("whitelist", path);
322 errExit("safe_fd");
323 struct stat s;
324 if (fstat(fd4, &s) == -1)
325 errExit("fstat");
326 if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino)
327 errLogExit("invalid whitelist mount");
328 close(fd4);
329
330 free(wfile);
331 return;
332} 243}
333 244
334static void whitelist_home(int topdir) { 245static void whitelist_symlink(const TopDir * const top, const char *link, const char *target) {
335 ProfileEntry entry; 246 EUID_ASSERT();
336 memset(&entry, 0, sizeof(entry)); 247 assert(top && link && target);
337 char *cmd; 248
338 if (asprintf(&cmd, "whitelist %s", cfg.homedir) == -1) 249 // confirm link is inside top level directory
339 errExit("asprintf"); 250 // this should never fail
340 entry.data = cmd; 251 size_t top_pathlen = strlen(top->path);
341 entry.wldir = topdir; 252 assert(strncmp(top->path, link, top_pathlen) == 0 && link[top_pathlen] == '/');
342 // creates path owned by root, except homedir is inside /run/user/$uid 253
343 // does nothing if homedir does not exist 254 const char *relpath = link + top_pathlen + 1;
344 whitelist_path(&entry); 255
345 free(cmd); 256 // create link as root, except if inside home or run/user/$UID directory
346} 257 if (strcmp(top->path, cfg.homedir) != 0 &&
258 strcmp(top->path, runuser) != 0)
259 EUID_ROOT();
347 260
261 int fd = whitelist_mkpath(top->path, relpath, 0755);
262 if (fd == -1) {
263 if (arg_debug || arg_debug_whitelists)
264 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link);
265 EUID_USER();
266 return;
267 }
268
269 // get file name of symlink
270 const char *file = gnu_basename(relpath);
271
272 // create the link
273 if (symlinkat(target, fd, file) == -1) {
274 if (arg_debug || arg_debug_whitelists) {
275 perror("symlink");
276 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link);
277 }
278 }
279 else if (arg_debug || arg_debug_whitelists)
280 printf("Created symbolic link %s -> %s\n", link, target);
281
282 close(fd);
283 EUID_USER();
284}
348 285
349static void globbing(const char *pattern) { 286static void globbing(const char *pattern) {
287 EUID_ASSERT();
350 assert(pattern); 288 assert(pattern);
351 289
352 // globbing 290 // globbing
@@ -363,6 +301,11 @@ static void globbing(const char *pattern) {
363 // testing for GLOB_NOCHECK - no pattern matched returns the original pattern 301 // testing for GLOB_NOCHECK - no pattern matched returns the original pattern
364 if (strcmp(globbuf.gl_pathv[i], pattern) == 0) 302 if (strcmp(globbuf.gl_pathv[i], pattern) == 0)
365 continue; 303 continue;
304 // foo/* expands to foo/. and foo/..
305 const char *base = gnu_basename(globbuf.gl_pathv[i]);
306 if (strcmp(base, ".") == 0 ||
307 strcmp(base, "..") == 0)
308 continue;
366 309
367 // build the new profile command 310 // build the new profile command
368 char *newcmd; 311 char *newcmd;
@@ -378,8 +321,237 @@ static void globbing(const char *pattern) {
378 globfree(&globbuf); 321 globfree(&globbuf);
379} 322}
380 323
324// mount tmpfs on all top level directories
325static void tmpfs_topdirs(const TopDir * const topdirs) {
326 int tmpfs_home = 0;
327 int tmpfs_runuser = 0;
328
329 int i;
330 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
331 // do nested top level directories last
332 // this way '--whitelist=nested_top_level_dir'
333 // yields the full, unmodified directory
334 // instead of the tmpfs
335 if (strcmp(topdirs[i].path, cfg.homedir) == 0) {
336 tmpfs_home = 1;
337 continue;
338 }
339 if (strcmp(topdirs[i].path, runuser) == 0) {
340 tmpfs_runuser = 1;
341 continue;
342 }
343
344 // special case /run
345 // open /run/firejail, so it can be restored right after mounting the tmpfs
346 int fd = -1;
347 if (strcmp(topdirs[i].path, "/run") == 0) {
348 fd = open(RUN_FIREJAIL_DIR, O_PATH|O_CLOEXEC);
349 if (fd == -1)
350 errExit("open");
351 }
352
353 // mount tmpfs
354 fs_tmpfs(topdirs[i].path, 0);
355 selinux_relabel_path(topdirs[i].path, topdirs[i].path);
356
357 // init tmpfs
358 if (strcmp(topdirs[i].path, "/run") == 0) {
359 // restore /run/firejail directory
360 EUID_ROOT();
361 mkdir_attr(RUN_FIREJAIL_DIR, 0755, 0, 0);
362 if (bind_mount_fd_to_path(fd, RUN_FIREJAIL_DIR))
363 errExit("mount bind");
364 EUID_USER();
365 close(fd);
366 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
367
368 // restore /run/user/$UID directory
369 whitelist_file(&topdirs[i], runuser);
370 }
371 else if (strcmp(topdirs[i].path, "/tmp") == 0) {
372 // fix pam-tmpdir (#2685)
373 const char *env = env_get("TMP");
374 if (env) {
375 // we allow TMP env set as /tmp/user/$UID and /tmp/$UID - see #4151
376 char *pamtmpdir1;
377 if (asprintf(&pamtmpdir1, "/tmp/user/%u", getuid()) == -1)
378 errExit("asprintf");
379 char *pamtmpdir2;
380 if (asprintf(&pamtmpdir2, "/tmp/%u", getuid()) == -1)
381 errExit("asprintf");
382 if (strcmp(env, pamtmpdir1) == 0) {
383 // create empty user-owned /tmp/user/$UID directory
384 EUID_ROOT();
385 mkdir_attr("/tmp/user", 0755, 0, 0);
386 selinux_relabel_path("/tmp/user", "/tmp/user");
387 fs_logger("mkdir /tmp/user");
388 mkdir_attr(pamtmpdir1, 0700, getuid(), 0);
389 selinux_relabel_path(pamtmpdir1, pamtmpdir1);
390 fs_logger2("mkdir", pamtmpdir1);
391 EUID_USER();
392 }
393 else if (strcmp(env, pamtmpdir2) == 0) {
394 // create empty user-owned /tmp/$UID directory
395 EUID_ROOT();
396 mkdir_attr(pamtmpdir2, 0700, getuid(), 0);
397 selinux_relabel_path(pamtmpdir2, pamtmpdir2);
398 fs_logger2("mkdir", pamtmpdir2);
399 EUID_USER();
400 }
401 free(pamtmpdir1);
402 free(pamtmpdir2);
403 }
404 }
405
406 // restore user home directory if it is masked by the tmpfs
407 // creates path owned by root
408 // does nothing if user home directory doesn't exist
409 whitelist_file(&topdirs[i], cfg.homedir);
410 }
411
412 // user home directory
413 if (tmpfs_home)
414 fs_private(); // checks owner if outside /home
415
416 // /run/user/$UID directory
417 if (tmpfs_runuser) {
418 fs_tmpfs(runuser, 0);
419 selinux_relabel_path(runuser, runuser);
420 }
421}
422
423static int reject_topdir(const char *dir) {
424 if (!whitelist_reject_topdirs)
425 return 0;
426
427 size_t i;
428 for (i = 0; whitelist_reject_topdirs[i]; i++) {
429 if (strcmp(dir, whitelist_reject_topdirs[i]) == 0)
430 return 1;
431 }
432 return 0;
433}
434
435// keep track of whitelist top level directories by adding them to an array
436// open each directory
437static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
438 EUID_ASSERT();
439 assert(dir && path);
440
441 // /proc and /sys are not allowed
442 if (strcmp(dir, "/") == 0 ||
443 strcmp(dir, "/proc") == 0 ||
444 strcmp(dir, "/sys") == 0)
445 whitelist_error(path);
446
447 // whitelisting home directory is disabled if --private option is present
448 if (arg_private && strcmp(dir, cfg.homedir) == 0) {
449 if (arg_debug || arg_debug_whitelists)
450 printf("Debug %d: skip %s - a private home dir is configured!\n", __LINE__, path);
451 return NULL;
452 }
453
454 // do nothing if directory doesn't exist
455 struct stat s;
456 if (lstat(dir, &s) != 0) {
457 if (arg_debug || arg_debug_whitelists)
458 printf("Cannot access whitelist top level directory %s: %s\n", dir, strerror(errno));
459 return NULL;
460 }
461 // do nothing if directory is a link
462 if (!S_ISDIR(s.st_mode)) {
463 if (S_ISLNK(s.st_mode)) {
464 fwarning("skipping whitelist %s because %s is a symbolic link\n", path, dir);
465 return NULL;
466 }
467 whitelist_error(path);
468 }
469 // do nothing if directory is disabled by administrator
470 if (reject_topdir(dir)) {
471 fmessage("Whitelist top level directory %s is disabled in Firejail configuration file\n", dir);
472 return NULL;
473 }
474
475 // add directory to array
476 if (arg_debug || arg_debug_whitelists)
477 printf("Adding whitelist top level directory %s\n", dir);
478 static int cnt = 0;
479 if (cnt >= TOP_MAX) {
480 fprintf(stderr, "Error: too many whitelist top level directories\n");
481 exit(1);
482 }
483 TopDir *rv = topdirs + cnt;
484 cnt++;
485
486 rv->path = strdup(dir);
487 if (!rv->path)
488 errExit("strdup");
489
490 // open the directory, don't follow symbolic links
491 rv->fd = safer_openat(-1, dir, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC);
492 if (rv->fd == -1) {
493 fprintf(stderr, "Error: cannot open %s\n", dir);
494 exit(1);
495 }
496
497 return rv;
498}
499
500static TopDir *have_topdir(const char *dir, TopDir *topdirs) {
501 assert(dir);
502
503 int i;
504 for (i = 0; i < TOP_MAX; i++) {
505 TopDir *rv = topdirs + i;
506 if (!rv->path)
507 break;
508 if (strcmp(dir, rv->path) == 0)
509 return rv;
510 }
511 return NULL;
512}
513
514static char *extract_topdir(const char *path) {
515 assert(path);
516
517 char *dup = strdup(path);
518 if (!dup)
519 errExit("strdup");
520
521 // user home directory can be anywhere; disconnect user home
522 // whitelisting from top level directory whitelisting
523 // by treating user home as separate whitelist top level directory
524 if (strncmp(dup, cfg.homedir, homedir_len) == 0 && dup[homedir_len] == '/')
525 dup[homedir_len] = '\0';
526 // /run/user/$UID is treated as top level directory
527 else if (strncmp(dup, runuser, runuser_len) == 0 && dup[runuser_len] == '/')
528 dup[runuser_len] = '\0';
529 // whitelisting in /sys is not allowed, but /sys/module is an exception
530 // and is treated as top level directory here
531 else if (strncmp(dup, "/sys/module", 11) == 0 && dup[11] == '/')
532 dup[11] = '\0';
533 // treat /usr subdirectories as top level directories
534 else if (strncmp(dup, "/usr/", 5) == 0) {
535 char *p = strchr(dup+5, '/');
536 if (!p)
537 whitelist_error(path);
538 *p = '\0';
539 }
540 // all other top level directories
541 else {
542 assert(dup[0] == '/');
543 char *p = strchr(dup+1, '/');
544 if (!p)
545 whitelist_error(path);
546 *p = '\0';
547 }
548
549 return dup;
550}
381 551
382void fs_whitelist(void) { 552void fs_whitelist(void) {
553 EUID_ASSERT();
554
383 ProfileEntry *entry = cfg.profile; 555 ProfileEntry *entry = cfg.profile;
384 if (!entry) 556 if (!entry)
385 return; 557 return;
@@ -389,29 +561,17 @@ void fs_whitelist(void) {
389 runuser_len = strlen(runuser); 561 runuser_len = strlen(runuser);
390 homedir_len = strlen(cfg.homedir); 562 homedir_len = strlen(cfg.homedir);
391 563
392 char *new_name = NULL;
393 int home_dir = 0; // /home/user directory flag
394 int tmp_dir = 0; // /tmp directory flag
395 int media_dir = 0; // /media directory flag
396 int mnt_dir = 0; // /mnt directory flag
397 int var_dir = 0; // /var directory flag
398 int dev_dir = 0; // /dev directory flag
399 int opt_dir = 0; // /opt directory flag
400 int srv_dir = 0; // /srv directory flag
401 int etc_dir = 0; // /etc directory flag
402 int share_dir = 0; // /usr/share directory flag
403 int module_dir = 0; // /sys/module directory flag
404 int run_dir = 0; // /run/user/$uid directory flag
405
406 size_t nowhitelist_c = 0; 564 size_t nowhitelist_c = 0;
407 size_t nowhitelist_m = 32; 565 size_t nowhitelist_m = 32;
408 char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist)); 566 char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist));
409 if (nowhitelist == NULL) 567 if (nowhitelist == NULL)
410 errExit("failed allocating memory for nowhitelist entries"); 568 errExit("calloc");
569
570 TopDir *topdirs = calloc(TOP_MAX, sizeof(*topdirs));
571 if (topdirs == NULL)
572 errExit("calloc");
411 573
412 // verify whitelist files, extract symbolic links, etc. 574 // verify whitelist files, extract symbolic links, etc.
413 EUID_USER();
414 struct stat s;
415 while (entry) { 575 while (entry) {
416 int nowhitelist_flag = 0; 576 int nowhitelist_flag = 0;
417 577
@@ -424,48 +584,76 @@ void fs_whitelist(void) {
424 entry = entry->next; 584 entry = entry->next;
425 continue; 585 continue;
426 } 586 }
427 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; 587 if (arg_debug || arg_debug_whitelists)
428 588 printf("Debug %d: %s\n", __LINE__, entry->data);
429 // replace ~/ or ${HOME} into /home/username or resolve macro 589
430 new_name = expand_macros(dataptr); 590 const char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
431 assert(new_name); 591
432 592 // replace ~ into /home/username or resolve macro
433 // mount empty home directory if resolving the macro was not successful 593 char *expanded = expand_macros(dataptr);
434 if (is_macro(new_name) && macro_id(new_name) > -1) { 594
435 // no warning if home does not exist (e.g. in a chroot) 595 // check if respolving the macro was successful
436 if (stat(cfg.homedir, &s) == 0 && !nowhitelist_flag && !arg_private) { 596 if (is_macro(expanded) && macro_id(expanded) > -1) {
437 home_dir = 1; 597 if (!nowhitelist_flag && (have_topdir(cfg.homedir, topdirs) || add_topdir(cfg.homedir, topdirs, expanded)) && !arg_quiet) {
438 if (!arg_quiet) { 598 fprintf(stderr, "***\n");
439 fprintf(stderr, "***\n"); 599 fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", expanded);
440 fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", new_name); 600 fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n");
441 fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n"); 601 fprintf(stderr, "***\n");
442 fprintf(stderr, "***\n");
443 }
444 } 602 }
445 entry->data = EMPTY_STRING;
446 entry = entry->next; 603 entry = entry->next;
447 free(new_name); 604 free(expanded);
448 continue; 605 continue;
449 } 606 }
450 607
451 // remove trailing slashes and single dots 608 if (arg_debug || arg_debug_whitelists)
452 if (!nowhitelist_flag) 609 printf("Debug %d: expanded: %s\n", __LINE__, expanded);
453 trim_trailing_slash_or_dot(new_name); 610
611 // path should be absolute at this point
612 if (expanded[0] != '/')
613 whitelist_error(expanded);
614
615 // sane pathname
616 char *new_name = clean_pathname(expanded);
617 free(expanded);
454 618
455 if (arg_debug || arg_debug_whitelists) 619 if (arg_debug || arg_debug_whitelists)
456 fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); 620 printf("Debug %d: new_name: %s\n", __LINE__, new_name);
621
622 if (strstr(new_name, ".."))
623 whitelist_error(new_name);
457 624
458 // valid path referenced to filesystem root 625 TopDir *current_top = NULL;
459 if (*new_name != '/') { 626 if (!nowhitelist_flag) {
627 // extract whitelist top level directory
628 char *dir = extract_topdir(new_name);
460 if (arg_debug || arg_debug_whitelists) 629 if (arg_debug || arg_debug_whitelists)
461 fprintf(stderr, "Debug %d: \n", __LINE__); 630 printf("Debug %d: dir: %s\n", __LINE__, dir);
462 goto errexit; 631
632 // check if this top level directory has been processed already
633 current_top = have_topdir(dir, topdirs);
634 if (!current_top) { // got new top level directory
635 current_top = add_topdir(dir, topdirs, new_name);
636 if (!current_top) { // skip this command, top level directory not valid
637 entry = entry->next;
638 free(new_name);
639 free(dir);
640 continue;
641 }
642 }
643 free(dir);
644 }
645
646 // /run/firejail directory is internal and not allowed
647 if (strncmp(new_name, RUN_FIREJAIL_DIR, strlen(RUN_FIREJAIL_DIR)) == 0) {
648 entry = entry->next;
649 free(new_name);
650 continue;
463 } 651 }
464 652
465 // extract the absolute path of the file 653 // extract resolved path of the file
466 // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission 654 // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission
467 // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr 655 // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr
468 char *fname; 656 char *fname = NULL;
469 if (strcmp(new_name, "/dev/fd") == 0) 657 if (strcmp(new_name, "/dev/fd") == 0)
470 fname = strdup("/proc/self/fd"); 658 fname = strdup("/proc/self/fd");
471 else if (strcmp(new_name, "/dev/stdin") == 0) 659 else if (strcmp(new_name, "/dev/stdin") == 0)
@@ -477,60 +665,34 @@ void fs_whitelist(void) {
477 else 665 else
478 fname = realpath(new_name, NULL); 666 fname = realpath(new_name, NULL);
479 667
480 // if this is not a real path, let's try globbing
481 // mark this entry as EMPTY_STRING and push the new paths at the end of profile entry list
482 // the new profile entries will be processed in this loop
483 // currently there is no globbing support for nowhitelist
484 if (!fname && !nowhitelist_flag)
485 globbing(new_name);
486
487 if (!fname) { 668 if (!fname) {
488 // file not found, blank the entry in the list and continue
489 if (arg_debug || arg_debug_whitelists) { 669 if (arg_debug || arg_debug_whitelists) {
490 printf("Removed whitelist/nowhitelist path: %s\n", entry->data); 670 printf("Removed path: %s\n", entry->data);
491 printf("\texpanded: %s\n", new_name); 671 printf("\tnew_name: %s\n", new_name);
492 printf("\treal path: (null)\n"); 672 printf("\trealpath: (null)\n");
493 printf("\t");fflush(0); 673 printf("\t%s\n", strerror(errno));
494 perror("realpath");
495 } 674 }
496 675
497 // if 1 the file was not found; mount an empty directory
498 if (!nowhitelist_flag) { 676 if (!nowhitelist_flag) {
499 if (strncmp(new_name, cfg.homedir, homedir_len) == 0 && new_name[homedir_len] == '/') { 677 // if this is not a real path, let's try globbing
500 if(!arg_private) 678 // push the new paths at the end of profile entry list
501 home_dir = 1; 679 // the new profile entries will be processed in this loop
502 } 680 // currently there is no globbing support for nowhitelist
503 else if (strncmp(new_name, "/tmp/", 5) == 0) 681 globbing(new_name);
504 tmp_dir = 1;
505 else if (strncmp(new_name, "/media/", 7) == 0)
506 media_dir = 1;
507 else if (strncmp(new_name, "/mnt/", 5) == 0)
508 mnt_dir = 1;
509 else if (strncmp(new_name, "/var/", 5) == 0)
510 var_dir = 1;
511 else if (strncmp(new_name, "/dev/", 5) == 0)
512 dev_dir = 1;
513 else if (strncmp(new_name, "/opt/", 5) == 0)
514 opt_dir = 1;
515 else if (strncmp(new_name, "/srv/", 5) == 0)
516 srv_dir = 1;
517 else if (strncmp(new_name, "/etc/", 5) == 0)
518 etc_dir = 1;
519 else if (strncmp(new_name, "/usr/share/", 11) == 0)
520 share_dir = 1;
521 else if (strncmp(new_name, "/sys/module/", 12) == 0)
522 module_dir = 1;
523 else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/')
524 run_dir = 1;
525 } 682 }
526 683
527 entry->data = EMPTY_STRING;
528 entry = entry->next; 684 entry = entry->next;
529 free(new_name); 685 free(new_name);
530 continue; 686 continue;
531 } 687 }
532 else if (arg_debug_whitelists) 688
533 printf("real path %s\n", fname); 689 // /run/firejail directory is internal and not allowed
690 if (strncmp(fname, RUN_FIREJAIL_DIR, strlen(RUN_FIREJAIL_DIR)) == 0) {
691 entry = entry->next;
692 free(new_name);
693 free(fname);
694 continue;
695 }
534 696
535 if (nowhitelist_flag) { 697 if (nowhitelist_flag) {
536 // store the path in nowhitelist array 698 // store the path in nowhitelist array
@@ -544,175 +706,12 @@ void fs_whitelist(void) {
544 errExit("failed increasing memory for nowhitelist entries"); 706 errExit("failed increasing memory for nowhitelist entries");
545 } 707 }
546 nowhitelist[nowhitelist_c++] = fname; 708 nowhitelist[nowhitelist_c++] = fname;
547 entry->data = EMPTY_STRING;
548 entry = entry->next; 709 entry = entry->next;
549 free(new_name); 710 free(new_name);
550 continue; 711 continue;
551 } 712 }
552
553 // check for supported directories
554 if (strncmp(new_name, cfg.homedir, homedir_len) == 0 && new_name[homedir_len] == '/') {
555 // whitelisting home directory is disabled if --private option is present
556 if (arg_private) {
557 if (arg_debug || arg_debug_whitelists)
558 printf("\"%s\" disabled by --private\n", entry->data);
559
560 entry->data = EMPTY_STRING;
561 entry = entry->next;
562 free(fname);
563 free(new_name);
564 continue;
565 }
566
567 entry->wldir = WLDIR_HOME;
568 home_dir = 1;
569 if (arg_debug || arg_debug_whitelists)
570 fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n",
571 __LINE__, fname, cfg.homedir);
572
573 // both path and absolute path are in user home,
574 // if not check if the symlink destination is owned by the user
575 if (strncmp(fname, cfg.homedir, homedir_len) != 0 || fname[homedir_len] != '/') {
576 if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) {
577 if (stat(fname, &s) == 0 && s.st_uid != getuid()) {
578 free(fname);
579 goto errexit;
580 }
581 }
582 }
583 }
584 else if (strncmp(new_name, "/tmp/", 5) == 0) {
585 entry->wldir = WLDIR_TMP;
586 tmp_dir = 1;
587
588 // both path and absolute path are under /tmp
589 if (strncmp(fname, "/tmp/", 5) != 0) {
590 free(fname);
591 goto errexit;
592 }
593 }
594 else if (strncmp(new_name, "/media/", 7) == 0) {
595 entry->wldir = WLDIR_MEDIA;
596 media_dir = 1;
597 // both path and absolute path are under /media
598 if (strncmp(fname, "/media/", 7) != 0) {
599 free(fname);
600 goto errexit;
601 }
602 }
603 else if (strncmp(new_name, "/mnt/", 5) == 0) {
604 entry->wldir = WLDIR_MNT;
605 mnt_dir = 1;
606 // both path and absolute path are under /mnt
607 if (strncmp(fname, "/mnt/", 5) != 0) {
608 free(fname);
609 goto errexit;
610 }
611 }
612 else if (strncmp(new_name, "/var/", 5) == 0) {
613 entry->wldir = WLDIR_VAR;
614 var_dir = 1;
615 // both path and absolute path are under /var
616 // exceptions: /var/tmp, /var/run and /var/lock
617 if (strcmp(new_name, "/var/run")== 0 && strcmp(fname, "/run") == 0);
618 else if (strcmp(new_name, "/var/lock")== 0 && strcmp(fname, "/run/lock") == 0);
619 else if (strcmp(new_name, "/var/tmp")== 0 && strcmp(fname, "/tmp") == 0);
620 else {
621 // both path and absolute path are under /var
622 if (strncmp(fname, "/var/", 5) != 0) {
623 free(fname);
624 goto errexit;
625 }
626 }
627 }
628 else if (strncmp(new_name, "/dev/", 5) == 0) {
629 entry->wldir = WLDIR_DEV;
630 dev_dir = 1;
631 // special handling for /dev/shm
632 // on some platforms (Debian wheezy, Ubuntu 14.04), it is a symlink to /run/shm
633 if (strcmp(new_name, "/dev/shm") == 0 && strcmp(fname, "/run/shm") == 0);
634 // special handling for /dev/log, which can be a symlink to /run/systemd/journal/dev-log
635 else if (strcmp(new_name, "/dev/log") == 0 && strcmp(fname, "/run/systemd/journal/dev-log") == 0);
636 // special processing for /proc/self/fd files
637 else if (strcmp(new_name, "/dev/fd") == 0 && strcmp(fname, "/proc/self/fd") == 0);
638 else if (strcmp(new_name, "/dev/stdin") == 0 && strcmp(fname, "/proc/self/fd/0") == 0);
639 else if (strcmp(new_name, "/dev/stdout") == 0 && strcmp(fname, "/proc/self/fd/1") == 0);
640 else if (strcmp(new_name, "/dev/stderr") == 0 && strcmp(fname, "/proc/self/fd/2") == 0);
641 else {
642 // both path and absolute path are under /dev
643 if (strncmp(fname, "/dev/", 5) != 0) {
644 free(fname);
645 goto errexit;
646 }
647 }
648 }
649 else if (strncmp(new_name, "/opt/", 5) == 0) {
650 entry->wldir = WLDIR_OPT;
651 opt_dir = 1;
652 // both path and absolute path are under /dev
653 if (strncmp(fname, "/opt/", 5) != 0) {
654 free(fname);
655 goto errexit;
656 }
657 }
658 else if (strncmp(new_name, "/srv/", 5) == 0) {
659 entry->wldir = WLDIR_SRV;
660 srv_dir = 1;
661 // both path and absolute path are under /srv
662 if (strncmp(fname, "/srv/", 5) != 0) {
663 free(fname);
664 goto errexit;
665 }
666 }
667 else if (strncmp(new_name, "/etc/", 5) == 0) {
668 entry->wldir = WLDIR_ETC;
669 etc_dir = 1;
670 // special handling for some of the symlinks
671 if (strcmp(new_name, "/etc/localtime") == 0);
672 else if (strcmp(new_name, "/etc/mtab") == 0);
673 else if (strcmp(new_name, "/etc/os-release") == 0);
674 // both path and absolute path are under /etc
675 else {
676 if (strncmp(fname, "/etc/", 5) != 0) {
677 free(fname);
678 goto errexit;
679 }
680 }
681 }
682 else if (strncmp(new_name, "/usr/share/", 11) == 0) {
683 entry->wldir = WLDIR_SHARE;
684 share_dir = 1;
685 // both path and absolute path are under /etc
686 if (strncmp(fname, "/usr/share/", 11) != 0) {
687 free(fname);
688 goto errexit;
689 }
690 }
691 else if (strncmp(new_name, "/sys/module/", 12) == 0) {
692 entry->wldir = WLDIR_MODULE;
693 module_dir = 1;
694 // both path and absolute path are under /sys/module
695 if (strncmp(fname, "/sys/module/", 12) != 0) {
696 free(fname);
697 goto errexit;
698 }
699 }
700 else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/') {
701 entry->wldir = WLDIR_RUN;
702 run_dir = 1;
703 // both path and absolute path are under /run/user/$uid
704 if (strncmp(fname, runuser, runuser_len) != 0 || fname[runuser_len] != '/') {
705 free(fname);
706 goto errexit;
707 }
708 }
709 else { 713 else {
710 free(fname); 714 // check if the path is in nowhitelist array
711 goto errexit;
712 }
713
714 // check if the path is in nowhitelist array
715 if (nowhitelist_flag == 0) {
716 size_t i; 715 size_t i;
717 int found = 0; 716 int found = 0;
718 for (i = 0; i < nowhitelist_c; i++) { 717 for (i = 0; i < nowhitelist_c; i++) {
@@ -726,494 +725,70 @@ void fs_whitelist(void) {
726 if (found) { 725 if (found) {
727 if (arg_debug || arg_debug_whitelists) 726 if (arg_debug || arg_debug_whitelists)
728 printf("Skip nowhitelisted path %s\n", fname); 727 printf("Skip nowhitelisted path %s\n", fname);
729 entry->data = EMPTY_STRING;
730 entry = entry->next; 728 entry = entry->next;
731 free(fname);
732 free(new_name); 729 free(new_name);
730 free(fname);
733 continue; 731 continue;
734 } 732 }
735 } 733 }
736 734
737 // mark symbolic links 735 // attach whitelist parameters to profile entry
738 if (is_link(new_name)) 736 entry->wparam = calloc(1, sizeof(struct wparam_t));
739 entry->link = new_name; 737 if (!entry->wparam)
740 else { 738 errExit("calloc");
741 free(new_name);
742 entry->link = NULL;
743 }
744
745 // change file name in entry->data
746 if (strcmp(fname, entry->data + 10) != 0) {
747 char *newdata;
748 if (asprintf(&newdata, "whitelist %s", fname) == -1)
749 errExit("asprintf");
750 entry->data = newdata;
751 if (arg_debug || arg_debug_whitelists)
752 printf("Replaced whitelist path: %s\n", entry->data);
753 }
754 free(fname);
755 entry = entry->next;
756 }
757
758 // release nowhitelist memory
759 assert(nowhitelist);
760 free(nowhitelist);
761
762 EUID_ROOT();
763 // /tmp mountpoint
764 if (tmp_dir) {
765 // check if /tmp directory exists
766 if (stat("/tmp", &s) == 0) {
767 // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR
768 mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0);
769 if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
770 errExit("mount bind");
771
772 // mount tmpfs on /tmp
773 if (arg_debug || arg_debug_whitelists)
774 printf("Mounting tmpfs on /tmp directory\n");
775 if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=1777,gid=0") < 0)
776 errExit("mounting tmpfs on /tmp");
777 selinux_relabel_path("/tmp", "/tmp");
778 fs_logger("tmpfs /tmp");
779
780 // pam-tmpdir - issue #2685
781 const char *env = env_get("TMP");
782 if (env) {
783 char *pamtmpdir;
784 if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1)
785 errExit("asprintf");
786 if (strcmp(env, pamtmpdir) == 0) {
787 // create empty user-owned /tmp/user/$uid directory
788 mkdir_attr("/tmp/user", 0711, 0, 0);
789 selinux_relabel_path("/tmp/user", "/tmp/user");
790 fs_logger("mkdir /tmp/user");
791 mkdir_attr(pamtmpdir, 0700, getuid(), 0);
792 selinux_relabel_path(pamtmpdir, pamtmpdir);
793 fs_logger2("mkdir", pamtmpdir);
794 }
795 free(pamtmpdir);
796 }
797
798 // autowhitelist home directory if it is masked by the tmpfs
799 if (strncmp(cfg.homedir, "/tmp/", 5) == 0)
800 whitelist_home(WLDIR_TMP);
801 }
802 else
803 tmp_dir = 0;
804 }
805
806 // /media mountpoint
807 if (media_dir) {
808 // some distros don't have a /media directory
809 if (stat("/media", &s) == 0) {
810 // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR
811 mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0);
812 if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
813 errExit("mount bind");
814
815 // mount tmpfs on /media
816 if (arg_debug || arg_debug_whitelists)
817 printf("Mounting tmpfs on /media directory\n");
818 if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
819 errExit("mounting tmpfs on /media");
820 selinux_relabel_path("/media", "/media");
821 fs_logger("tmpfs /media");
822
823 // autowhitelist home directory if it is masked by the tmpfs
824 if (strncmp(cfg.homedir, "/media/", 7) == 0)
825 whitelist_home(WLDIR_MEDIA);
826 }
827 else
828 media_dir = 0;
829 }
830
831 // /mnt mountpoint
832 if (mnt_dir) {
833 // check if /mnt directory exists
834 if (stat("/mnt", &s) == 0) {
835 // keep a copy of real /mnt directory in RUN_WHITELIST_MNT_DIR
836 mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0);
837 if (mount("/mnt", RUN_WHITELIST_MNT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
838 errExit("mount bind");
839
840 // mount tmpfs on /mnt
841 if (arg_debug || arg_debug_whitelists)
842 printf("Mounting tmpfs on /mnt directory\n");
843 if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
844 errExit("mounting tmpfs on /mnt");
845 selinux_relabel_path("/mnt", "/mnt");
846 fs_logger("tmpfs /mnt");
847
848 // autowhitelist home directory if it is masked by the tmpfs
849 if (strncmp(cfg.homedir, "/mnt/", 5) == 0)
850 whitelist_home(WLDIR_MNT);
851 }
852 else
853 mnt_dir = 0;
854 }
855
856 // /var mountpoint
857 if (var_dir) {
858 // check if /var directory exists
859 if (stat("/var", &s) == 0) {
860 // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR
861 mkdir_attr(RUN_WHITELIST_VAR_DIR, 0755, 0, 0);
862 if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
863 errExit("mount bind");
864 739
865 // mount tmpfs on /var 740 assert(current_top);
866 if (arg_debug || arg_debug_whitelists) 741 entry->wparam->top = current_top;
867 printf("Mounting tmpfs on /var directory\n"); 742 entry->wparam->file = fname;
868 if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
869 errExit("mounting tmpfs on /var");
870 selinux_relabel_path("/var", "/var");
871 fs_logger("tmpfs /var");
872
873 // autowhitelist home directory if it is masked by the tmpfs
874 if (strncmp(cfg.homedir, "/var/", 5) == 0)
875 whitelist_home(WLDIR_VAR);
876 }
877 else
878 var_dir = 0;
879 }
880
881 // /dev mountpoint
882 if (dev_dir) {
883 // check if /dev directory exists
884 if (stat("/dev", &s) == 0) {
885 // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR
886 mkdir_attr(RUN_WHITELIST_DEV_DIR, 0755, 0, 0);
887 if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0)
888 errExit("mount bind");
889
890 // mount tmpfs on /dev
891 if (arg_debug || arg_debug_whitelists)
892 printf("Mounting tmpfs on /dev directory\n");
893 if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
894 errExit("mounting tmpfs on /dev");
895 selinux_relabel_path("/dev", "/dev");
896 fs_logger("tmpfs /dev");
897
898 // autowhitelist home directory if it is masked by the tmpfs
899 if (strncmp(cfg.homedir, "/dev/", 5) == 0)
900 whitelist_home(WLDIR_DEV);
901 }
902 else
903 dev_dir = 0;
904 }
905
906 // /opt mountpoint
907 if (opt_dir) {
908 // check if /opt directory exists
909 if (stat("/opt", &s) == 0) {
910 // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR
911 mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0);
912 if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
913 errExit("mount bind");
914
915 // mount tmpfs on /opt
916 if (arg_debug || arg_debug_whitelists)
917 printf("Mounting tmpfs on /opt directory\n");
918 if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
919 errExit("mounting tmpfs on /opt");
920 selinux_relabel_path("/opt", "/opt");
921 fs_logger("tmpfs /opt");
922
923 // autowhitelist home directory if it is masked by the tmpfs
924 if (strncmp(cfg.homedir, "/opt/", 5) == 0)
925 whitelist_home(WLDIR_OPT);
926 }
927 else
928 opt_dir = 0;
929 }
930
931 // /srv mountpoint
932 if (srv_dir) {
933 // check if /srv directory exists
934 if (stat("/srv", &s) == 0) {
935 // keep a copy of real /srv directory in RUN_WHITELIST_SRV_DIR
936 mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0);
937 if (mount("/srv", RUN_WHITELIST_SRV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
938 errExit("mount bind");
939
940 // mount tmpfs on /srv
941 if (arg_debug || arg_debug_whitelists)
942 printf("Mounting tmpfs on /srv directory\n");
943 if (mount("tmpfs", "/srv", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
944 errExit("mounting tmpfs on /srv");
945 selinux_relabel_path("/srv", "/srv");
946 fs_logger("tmpfs /srv");
947
948 // autowhitelist home directory if it is masked by the tmpfs
949 if (strncmp(cfg.homedir, "/srv/", 5) == 0)
950 whitelist_home(WLDIR_SRV);
951 }
952 else
953 srv_dir = 0;
954 }
955
956 // /etc mountpoint
957 if (etc_dir) {
958 // check if /etc directory exists
959 if (stat("/etc", &s) == 0) {
960 // keep a copy of real /etc directory in RUN_WHITELIST_ETC_DIR
961 mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0);
962 if (mount("/etc", RUN_WHITELIST_ETC_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
963 errExit("mount bind");
964
965 // mount tmpfs on /etc
966 if (arg_debug || arg_debug_whitelists)
967 printf("Mounting tmpfs on /etc directory\n");
968 if (mount("tmpfs", "/etc", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
969 errExit("mounting tmpfs on /etc");
970 selinux_relabel_path("/etc", "/etc");
971 fs_logger("tmpfs /etc");
972
973 // autowhitelist home directory if it is masked by the tmpfs
974 if (strncmp(cfg.homedir, "/etc/", 5) == 0)
975 whitelist_home(WLDIR_ETC);
976 }
977 else
978 etc_dir = 0;
979 }
980
981 // /usr/share mountpoint
982 if (share_dir) {
983 // check if /usr/share directory exists
984 if (stat("/usr/share", &s) == 0) {
985 // keep a copy of real /usr/share directory in RUN_WHITELIST_ETC_DIR
986 mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0);
987 if (mount("/usr/share", RUN_WHITELIST_SHARE_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
988 errExit("mount bind");
989
990 // mount tmpfs on /srv
991 if (arg_debug || arg_debug_whitelists)
992 printf("Mounting tmpfs on /usr/share directory\n");
993 if (mount("tmpfs", "/usr/share", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
994 errExit("mounting tmpfs on /usr/share");
995 selinux_relabel_path("/usr/share", "/usr/share");
996 fs_logger("tmpfs /usr/share");
997
998 // autowhitelist home directory if it is masked by the tmpfs
999 if (strncmp(cfg.homedir, "/usr/share/", 11) == 0)
1000 whitelist_home(WLDIR_SHARE);
1001 }
1002 else
1003 share_dir = 0;
1004 }
1005
1006 // /sys/module mountpoint
1007 if (module_dir) {
1008 // check if /sys/module directory exists
1009 if (stat("/sys/module", &s) == 0) {
1010 // keep a copy of real /sys/module directory in RUN_WHITELIST_MODULE_DIR
1011 mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0);
1012 if (mount("/sys/module", RUN_WHITELIST_MODULE_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
1013 errExit("mount bind");
1014 743
1015 // mount tmpfs on /sys/module 744 // mark link
1016 if (arg_debug || arg_debug_whitelists) 745 if (is_link(new_name))
1017 printf("Mounting tmpfs on /sys/module directory\n"); 746 entry->wparam->link = new_name;
1018 if (mount("tmpfs", "/sys/module", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1019 errExit("mounting tmpfs on /sys/module");
1020 selinux_relabel_path("/sys/module", "/sys/module");
1021 fs_logger("tmpfs /sys/module");
1022 }
1023 else 747 else
1024 module_dir = 0; 748 free(new_name);
1025 }
1026
1027 // /run/user/$uid mountpoint
1028 if (run_dir) {
1029 // check if /run/user/$uid directory exists
1030 if (stat(runuser, &s) == 0) {
1031 // keep a copy of real /run/user/$uid directory in RUN_WHITELIST_RUN_USER_DIR
1032 mkdir_attr(RUN_WHITELIST_RUN_USER_DIR, 0700, getuid(), getgid());
1033 if (mount(runuser, RUN_WHITELIST_RUN_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
1034 errExit("mount bind");
1035 749
1036 // mount tmpfs on /run/user/$uid 750 entry = entry->next;
1037 if (arg_debug || arg_debug_whitelists)
1038 printf("Mounting tmpfs on %s directory\n", runuser);
1039 char *options;
1040 if (asprintf(&options, "mode=700,uid=%u,gid=%u", getuid(), getgid()) == -1)
1041 errExit("asprintf");
1042 if (mount("tmpfs", runuser, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, options) < 0)
1043 errExit("mounting tmpfs on /run/user/<uid>");
1044 selinux_relabel_path(runuser, runuser);
1045 free(options);
1046 fs_logger2("tmpfs", runuser);
1047
1048 // autowhitelist home directory if it is masked by the tmpfs
1049 if (strncmp(cfg.homedir, runuser, runuser_len) == 0 && cfg.homedir[runuser_len] == '/')
1050 whitelist_home(WLDIR_RUN);
1051 }
1052 else
1053 run_dir = 0;
1054 } 751 }
1055 752
1056 // home mountpoint 753 // mount tmpfs on all top level directories
1057 if (home_dir) { 754 tmpfs_topdirs(topdirs);
1058 // check if home directory exists
1059 if (stat(cfg.homedir, &s) == 0) {
1060 // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR
1061 mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid());
1062 int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1063 if (fd == -1)
1064 errExit("safe_fd");
1065 char *proc;
1066 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1067 errExit("asprintf");
1068 if (mount(proc, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0)
1069 errExit("mount bind");
1070 free(proc);
1071 close(fd);
1072
1073 // mount a tmpfs and initialize home directory
1074 fs_private();
1075 }
1076 else
1077 home_dir = 0;
1078 }
1079 755
1080 // go through profile rules again, and interpret whitelist commands 756 // go through profile rules again, and interpret whitelist commands
1081 entry = cfg.profile; 757 entry = cfg.profile;
1082 while (entry) { 758 while (entry) {
1083 // handle only whitelist commands 759 if (entry->wparam) {
1084 if (strncmp(entry->data, "whitelist ", 10)) { 760 char *file = entry->wparam->file;
1085 entry = entry->next; 761 char *link = entry->wparam->link;
1086 continue; 762 const TopDir * const current_top = entry->wparam->top;
1087 } 763
1088 764 // top level directories of link and file can differ
1089//printf("here %d#%s#\n", __LINE__, entry->data); 765 // will whitelist the file only if it is in same top level directory
1090 // whitelist the real file 766 whitelist_file(current_top, file);
1091 whitelist_path(entry); 767
1092 768 // create the link if any
1093 // create the link if any 769 if (link) {
1094 if (entry->link) { 770 whitelist_symlink(current_top, link, file);
1095 // if the link is already there, do not bother 771 free(link);
1096 if (lstat(entry->link, &s) != 0) {
1097 // create the path if necessary
1098 // entry->link has no trailing slashes or single dots
1099 int fd = mkpath(entry->link, 0755);
1100 if (fd == -1) {
1101 if (arg_debug || arg_debug_whitelists)
1102 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link);
1103 free(entry->link);
1104 entry->link = NULL;
1105 entry = entry->next;
1106 continue;
1107 }
1108 // get file name of symlink
1109 const char *file = gnu_basename(entry->link);
1110 // create the link
1111 int rv = symlinkat(entry->data + 10, fd, file);
1112 if (rv) {
1113 if (arg_debug || arg_debug_whitelists) {
1114 perror("symlink");
1115 printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link);
1116 }
1117 }
1118 else if (arg_debug || arg_debug_whitelists)
1119 printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10);
1120 close(fd);
1121 } 772 }
1122 free(entry->link); 773
1123 entry->link = NULL; 774 free(file);
775 free(entry->wparam);
776 entry->wparam = NULL;
1124 } 777 }
1125 778
1126 entry = entry->next; 779 entry = entry->next;
1127 } 780 }
1128 781
1129 // mask the real home directory, currently mounted on RUN_WHITELIST_HOME_DIR 782 // release resources
1130 if (home_dir) { 783 size_t i;
1131 if (mount("tmpfs", RUN_WHITELIST_HOME_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) 784 for (i = 0; i < nowhitelist_c; i++)
1132 errExit("mount tmpfs"); 785 free(nowhitelist[i]);
1133 fs_logger2("tmpfs", RUN_WHITELIST_HOME_USER_DIR); 786 free(nowhitelist);
1134 }
1135
1136 // mask the real /tmp directory, currently mounted on RUN_WHITELIST_TMP_DIR
1137 if (tmp_dir) {
1138 if (mount("tmpfs", RUN_WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1139 errExit("mount tmpfs");
1140 fs_logger2("tmpfs", RUN_WHITELIST_TMP_DIR);
1141 }
1142
1143 // mask the real /var directory, currently mounted on RUN_WHITELIST_VAR_DIR
1144 if (var_dir) {
1145 if (mount("tmpfs", RUN_WHITELIST_VAR_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1146 errExit("mount tmpfs");
1147 fs_logger2("tmpfs", RUN_WHITELIST_VAR_DIR);
1148 }
1149
1150 // mask the real /opt directory, currently mounted on RUN_WHITELIST_OPT_DIR
1151 if (opt_dir) {
1152 if (mount("tmpfs", RUN_WHITELIST_OPT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1153 errExit("mount tmpfs");
1154 fs_logger2("tmpfs", RUN_WHITELIST_OPT_DIR);
1155 }
1156
1157 // mask the real /dev directory, currently mounted on RUN_WHITELIST_DEV_DIR
1158 if (dev_dir) {
1159 if (mount("tmpfs", RUN_WHITELIST_DEV_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1160 errExit("mount tmpfs");
1161 fs_logger2("tmpfs", RUN_WHITELIST_DEV_DIR);
1162 }
1163
1164 // mask the real /media directory, currently mounted on RUN_WHITELIST_MEDIA_DIR
1165 if (media_dir) {
1166 if (mount("tmpfs", RUN_WHITELIST_MEDIA_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1167 errExit("mount tmpfs");
1168 fs_logger2("tmpfs", RUN_WHITELIST_MEDIA_DIR);
1169 }
1170
1171 // mask the real /mnt directory, currently mounted on RUN_WHITELIST_MNT_DIR
1172 if (mnt_dir) {
1173 if (mount("tmpfs", RUN_WHITELIST_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1174 errExit("mount tmpfs");
1175 fs_logger2("tmpfs", RUN_WHITELIST_MNT_DIR);
1176 }
1177
1178 // mask the real /srv directory, currently mounted on RUN_WHITELIST_SRV_DIR
1179 if (srv_dir) {
1180 if (mount("tmpfs", RUN_WHITELIST_SRV_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1181 errExit("mount tmpfs");
1182 fs_logger2("tmpfs", RUN_WHITELIST_SRV_DIR);
1183 }
1184
1185 // mask the real /etc directory, currently mounted on RUN_WHITELIST_ETC_DIR
1186 if (etc_dir) {
1187 if (mount("tmpfs", RUN_WHITELIST_ETC_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1188 errExit("mount tmpfs");
1189 fs_logger2("tmpfs", RUN_WHITELIST_ETC_DIR);
1190 }
1191
1192 // mask the real /usr/share directory, currently mounted on RUN_WHITELIST_SHARE_DIR
1193 if (share_dir) {
1194 if (mount("tmpfs", RUN_WHITELIST_SHARE_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1195 errExit("mount tmpfs");
1196 fs_logger2("tmpfs", RUN_WHITELIST_SHARE_DIR);
1197 }
1198
1199 // mask the real /sys/module directory, currently mounted on RUN_WHITELIST_MODULE_DIR
1200 if (module_dir) {
1201 if (mount("tmpfs", RUN_WHITELIST_MODULE_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0)
1202 errExit("mount tmpfs");
1203 fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR);
1204 }
1205 787
1206 // mask the real /run/user/$uid directory, currently mounted on RUN_WHITELIST_RUN_USER_DIR 788 for (i = 0; i < TOP_MAX && topdirs[i].path; i++) {
1207 if (run_dir) { 789 free(topdirs[i].path);
1208 if (mount("tmpfs", RUN_WHITELIST_RUN_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) 790 close(topdirs[i].fd);
1209 errExit("mount tmpfs");
1210 fs_logger2("tmpfs", RUN_WHITELIST_RUN_USER_DIR);
1211 } 791 }
1212 792 free(topdirs);
1213 free(runuser); 793 free(runuser);
1214 return;
1215
1216errexit:
1217 fprintf(stderr, "Error: invalid whitelist path %s\n", new_name);
1218 exit(1);
1219} 794}
diff --git a/src/firejail/ids.c b/src/firejail/ids.c
new file mode 100644
index 000000000..fdb78d6e6
--- /dev/null
+++ b/src/firejail/ids.c
@@ -0,0 +1,89 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20#include "firejail.h"
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
24
25
26static void ids_init(void) {
27 // store checksums as root in /var/lib/firejail/${USERNAME}.ids
28 char *fname;
29 if (asprintf(&fname, VARDIR"/%s.ids", cfg.username) == -1)
30 errExit("asprintf");
31
32 int rv = unlink(fname);
33 (void) rv;
34 int fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
35 if (fd < 0) {
36 fprintf(stderr, "Error: cannot create %s\n", fname);
37 exit(1);
38 }
39
40 // redirect output
41 close(STDOUT_FILENO);
42 if (dup(fd) != STDOUT_FILENO)
43 errExit("dup");
44 close(fd);
45
46 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 3, PATH_FIDS, "--init", cfg.homedir);
47}
48
49static void ids_check(void) {
50 // store checksums as root in /var/lib/firejail/${USERNAME}.ids
51 char *fname;
52 if (asprintf(&fname, VARDIR"/%s.ids", cfg.username) == -1)
53 errExit("asprintf");
54
55 int fd = open(fname, O_RDONLY);
56 if (fd < 0) {
57 fprintf(stderr, "Error: cannot open %s\n", fname);
58 exit(1);
59 }
60
61 // redirect input
62 close(STDIN_FILENO);
63 if (dup(fd) != STDIN_FILENO)
64 errExit("dup");
65 close(fd);
66
67 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP| SBOX_ALLOW_STDIN, 3, PATH_FIDS, "--check", cfg.homedir);
68}
69
70void run_ids(int argc, char **argv) {
71 if (argc != 2) {
72 fprintf(stderr, "Error: only one IDS command expected\n");
73 exit(1);
74 }
75
76 EUID_ROOT();
77 struct stat s;
78 if (stat(VARDIR, &s)) // /var/lib/firejail
79 create_empty_dir_as_root(VARDIR, 0700);
80
81 if (strcmp(argv[1], "--ids-init") == 0)
82 ids_init();
83 else if (strcmp(argv[1], "--ids-check") == 0)
84 ids_check();
85 else
86 fprintf(stderr, "Error: unrecognized IDS command\n");
87
88 exit(0);
89}
diff --git a/src/firejail/join.c b/src/firejail/join.c
index 1575a7469..5d73f71be 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -45,7 +45,7 @@ static unsigned display = 0;
45static void signal_handler(int sig){ 45static void signal_handler(int sig){
46 flush_stdin(); 46 flush_stdin();
47 47
48 exit(sig); 48 exit(128 + sig);
49} 49}
50 50
51static void install_handler(void) { 51static void install_handler(void) {
@@ -103,7 +103,7 @@ static void extract_x11_display(pid_t pid) {
103 if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1) 103 if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1)
104 errExit("asprintf"); 104 errExit("asprintf");
105 105
106 FILE *fp = fopen(fname, "r"); 106 FILE *fp = fopen(fname, "re");
107 free(fname); 107 free(fname);
108 if (!fp) 108 if (!fp)
109 return; 109 return;
@@ -147,7 +147,7 @@ static void extract_command(int argc, char **argv, int index) {
147 } 147 }
148 148
149 // build command 149 // build command
150 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index); 150 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index, true);
151} 151}
152 152
153static void extract_nogroups(pid_t pid) { 153static void extract_nogroups(pid_t pid) {
@@ -219,7 +219,7 @@ static void extract_caps(pid_t pid) {
219 perror("asprintf"); 219 perror("asprintf");
220 exit(1); 220 exit(1);
221 } 221 }
222 FILE *fp = fopen(file, "r"); 222 FILE *fp = fopen(file, "re");
223 if (!fp) 223 if (!fp)
224 goto errexit; 224 goto errexit;
225 225
@@ -266,7 +266,7 @@ static void extract_user_namespace(pid_t pid) {
266 char *uidmap; 266 char *uidmap;
267 if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1) 267 if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1)
268 errExit("asprintf"); 268 errExit("asprintf");
269 FILE *fp = fopen(uidmap, "r"); 269 FILE *fp = fopen(uidmap, "re");
270 if (!fp) { 270 if (!fp) {
271 free(uidmap); 271 free(uidmap);
272 return; 272 return;
@@ -315,6 +315,11 @@ static int open_shell(void) {
315 fprintf(stderr, "Error: cannot open shell %s\n", cfg.shell); 315 fprintf(stderr, "Error: cannot open shell %s\n", cfg.shell);
316 exit(1); 316 exit(1);
317 } 317 }
318
319 // pass file descriptor through to the final fexecve
320 if (asprintf(&cfg.keep_fd, "%s,%d", cfg.keep_fd ? cfg.keep_fd : "", fd) == -1)
321 errExit("asprintf");
322
318 return fd; 323 return fd;
319} 324}
320 325
@@ -431,7 +436,7 @@ void join(pid_t pid, int argc, char **argv, int index) {
431 436
432 // set cgroup 437 // set cgroup
433 if (cfg.cgroup) // not available for uid 0 438 if (cfg.cgroup) // not available for uid 0
434 set_cgroup(cfg.cgroup); 439 set_cgroup(cfg.cgroup, getpid());
435 440
436 // join namespaces 441 // join namespaces
437 if (arg_join_network) { 442 if (arg_join_network) {
@@ -536,7 +541,6 @@ void join(pid_t pid, int argc, char **argv, int index) {
536 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); 541 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
537 542
538#ifdef HAVE_APPARMOR 543#ifdef HAVE_APPARMOR
539 // add apparmor confinement after the execve
540 set_apparmor(); 544 set_apparmor();
541#endif 545#endif
542 546
@@ -552,10 +556,6 @@ void join(pid_t pid, int argc, char **argv, int index) {
552 if (cfg.cpus) // not available for uid 0 556 if (cfg.cpus) // not available for uid 0
553 set_cpu_affinity(); 557 set_cpu_affinity();
554 558
555 // set nice value
556 if (arg_nice)
557 set_nice(cfg.nice);
558
559 // add x11 display 559 // add x11 display
560 if (display) { 560 if (display) {
561 char *display_str; 561 char *display_str;
@@ -596,15 +596,17 @@ void join(pid_t pid, int argc, char **argv, int index) {
596 596
597 // end of signal-safe code 597 // end of signal-safe code
598 //***************************** 598 //*****************************
599 flush_stdin();
600 599
601 if (WIFEXITED(status)) { 600 if (WIFEXITED(status)) {
601 // if we had a proper exit, return that exit status
602 status = WEXITSTATUS(status); 602 status = WEXITSTATUS(status);
603 } else if (WIFSIGNALED(status)) { 603 } else if (WIFSIGNALED(status)) {
604 status = WTERMSIG(status); 604 // distinguish fatal signals by adding 128
605 status = 128 + WTERMSIG(status);
605 } else { 606 } else {
606 status = 0; 607 status = -1;
607 } 608 }
608 609
610 flush_stdin();
609 exit(status); 611 exit(status);
610} 612}
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index 63ef2309b..4156a7b25 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,6 +19,7 @@
19*/ 19*/
20 20
21#include "firejail.h" 21#include "firejail.h"
22#include "../include/gcov_wrapper.h"
22#include <sys/types.h> 23#include <sys/types.h>
23#include <sys/stat.h> 24#include <sys/stat.h>
24#include <sys/wait.h> 25#include <sys/wait.h>
@@ -45,7 +46,8 @@ static void print_file_or_dir(const char *path, const char *fname) {
45 struct stat s; 46 struct stat s;
46 if (stat(name, &s) == -1) { 47 if (stat(name, &s) == -1) {
47 if (lstat(name, &s) == -1) { 48 if (lstat(name, &s) == -1) {
48 printf("Error: cannot access %s\n", name); 49 printf("Error: cannot access %s\n", do_replace_cntrl_chars(name, '?'));
50 free(name);
49 return; 51 return;
50 } 52 }
51 } 53 }
@@ -150,12 +152,17 @@ static void print_file_or_dir(const char *path, const char *fname) {
150 if (allocated) 152 if (allocated)
151 free(groupname); 153 free(groupname);
152 154
155 // file size
153 char *sz; 156 char *sz;
154 if (asprintf(&sz, "%d", (int) s.st_size) == -1) 157 if (asprintf(&sz, "%d", (int) s.st_size) == -1)
155 errExit("asprintf"); 158 errExit("asprintf");
156 printf("%11.10s %s\n", sz, fname);
157 free(sz);
158 159
160 // file name
161 char *fname_print = replace_cntrl_chars(fname, '?');
162
163 printf("%11.10s %s\n", sz, fname_print);
164 free(sz);
165 free(fname_print);
159} 166}
160 167
161static void print_directory(const char *path) { 168static void print_directory(const char *path) {
@@ -191,13 +198,15 @@ void ls(const char *path) {
191 fprintf(stderr, "Error: cannot access %s\n", path); 198 fprintf(stderr, "Error: cannot access %s\n", path);
192 exit(1); 199 exit(1);
193 } 200 }
201
202 // debug doesn't filter control characters currently
194 if (arg_debug) 203 if (arg_debug)
195 printf("ls %s\n", rp); 204 printf("ls %s\n", rp);
196 205
197 // list directory contents 206 // list directory contents
198 struct stat s; 207 struct stat s;
199 if (stat(rp, &s) == -1) { 208 if (stat(rp, &s) == -1) {
200 fprintf(stderr, "Error: cannot access %s\n", rp); 209 fprintf(stderr, "Error: cannot access %s\n", do_replace_cntrl_chars(rp, '?'));
201 exit(1); 210 exit(1);
202 } 211 }
203 if (S_ISDIR(s.st_mode)) 212 if (S_ISDIR(s.st_mode))
@@ -221,7 +230,7 @@ void cat(const char *path) {
221 230
222 if (arg_debug) 231 if (arg_debug)
223 printf("cat %s\n", path); 232 printf("cat %s\n", path);
224 FILE *fp = fopen(path, "r"); 233 FILE *fp = fopen(path, "re");
225 if (!fp) { 234 if (!fp) {
226 fprintf(stderr, "Error: cannot read %s\n", path); 235 fprintf(stderr, "Error: cannot read %s\n", path);
227 exit(1); 236 exit(1);
@@ -236,13 +245,13 @@ void cat(const char *path) {
236 fprintf(stderr, "Error: %s is not a regular file\n", path); 245 fprintf(stderr, "Error: %s is not a regular file\n", path);
237 exit(1); 246 exit(1);
238 } 247 }
239 bool tty = isatty(STDOUT_FILENO); 248 int tty = isatty(STDOUT_FILENO);
240 249
241 int c; 250 int c;
242 while ((c = fgetc(fp)) != EOF) { 251 while ((c = fgetc(fp)) != EOF) {
243 // file is untrusted 252 // file is untrusted
244 // replace control characters when printing to a terminal 253 // replace control characters when printing to a terminal
245 if (tty && c != '\t' && c != '\n' && iscntrl((unsigned char) c)) 254 if (tty && iscntrl((unsigned char) c) && c != '\t' && c != '\n')
246 c = '?'; 255 c = '?';
247 fputc(c, stdout); 256 fputc(c, stdout);
248 } 257 }
@@ -304,7 +313,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
304 } 313 }
305 // create destination file if necessary 314 // create destination file if necessary
306 EUID_ASSERT(); 315 EUID_ASSERT();
307 int fd = open(dest_fname, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR | S_IWRITE); 316 int fd = open(dest_fname, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR | S_IWUSR);
308 if (fd == -1) { 317 if (fd == -1) {
309 fprintf(stderr, "Error: cannot open %s for writing\n", dest_fname); 318 fprintf(stderr, "Error: cannot open %s for writing\n", dest_fname);
310 exit(1); 319 exit(1);
@@ -324,7 +333,6 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
324 // redirection 333 // redirection
325 if (dup2(fd, STDOUT_FILENO) == -1) 334 if (dup2(fd, STDOUT_FILENO) == -1)
326 errExit("dup2"); 335 errExit("dup2");
327 assert(fd != STDOUT_FILENO);
328 close(fd); 336 close(fd);
329 op = SANDBOX_FS_CAT; 337 op = SANDBOX_FS_CAT;
330 } 338 }
@@ -349,9 +357,8 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
349 ls(fname1); 357 ls(fname1);
350 else 358 else
351 cat(fname1); 359 cat(fname1);
352#ifdef HAVE_GCOV 360
353 __gcov_flush(); 361 __gcov_flush();
354#endif
355 } 362 }
356 // get file from host and store it in the sandbox 363 // get file from host and store it in the sandbox
357 else if (op == SANDBOX_FS_PUT && path2) { 364 else if (op == SANDBOX_FS_PUT && path2) {
@@ -383,9 +390,9 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
383 // copy the file 390 // copy the file
384 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user 391 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user
385 _exit(1); 392 _exit(1);
386#ifdef HAVE_GCOV 393
387 __gcov_flush(); 394 __gcov_flush();
388#endif 395
389 _exit(0); 396 _exit(0);
390 } 397 }
391 398
@@ -415,9 +422,9 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
415 // copy the file 422 // copy the file
416 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user 423 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user
417 _exit(1); 424 _exit(1);
418#ifdef HAVE_GCOV 425
419 __gcov_flush(); 426 __gcov_flush();
420#endif 427
421 _exit(0); 428 _exit(0);
422 } 429 }
423 430
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
index 7f2f6dbf3..3f9460041 100644
--- a/src/firejail/macros.c
+++ b/src/firejail/macros.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,9 +22,11 @@
22#define MAXBUF 4098 22#define MAXBUF 4098
23 23
24typedef struct macro_t { 24typedef struct macro_t {
25 char *name; // macro name 25 char *name; // macro name
26 char *xdg; // xdg line in ~/.config/user-dirs.dirs 26 char *xdg; // xdg line in ~/.config/user-dirs.dirs
27#define MAX_TRANSLATIONS 3 // several translations in case ~/.config/user-dirs.dirs not found 27 // several translations in case ~/.config/user-dirs.dirs not found
28 // covered currently: English, Russian, French, Italian, Spanish, Portuguese, German
29#define MAX_TRANSLATIONS 7
28 char *translation[MAX_TRANSLATIONS]; 30 char *translation[MAX_TRANSLATIONS];
29} Macro; 31} Macro;
30 32
@@ -32,37 +34,37 @@ Macro macro[] = {
32 { 34 {
33 "${DOWNLOADS}", 35 "${DOWNLOADS}",
34 "XDG_DOWNLOAD_DIR=\"$HOME/", 36 "XDG_DOWNLOAD_DIR=\"$HOME/",
35 { "Downloads", "Загрузки", "Téléchargement" } 37 {"Downloads", "Загрузки", "Téléchargement", "Scaricati", "Descargas"}
36 }, 38 },
37 39
38 { 40 {
39 "${MUSIC}", 41 "${MUSIC}",
40 "XDG_MUSIC_DIR=\"$HOME/", 42 "XDG_MUSIC_DIR=\"$HOME/",
41 {"Music", "Музыка", "Musique"} 43 {"Music", "Музыка", "Musique", "Musica", "Música", "Musik"}
42 }, 44 },
43 45
44 { 46 {
45 "${VIDEOS}", 47 "${VIDEOS}",
46 "XDG_VIDEOS_DIR=\"$HOME/", 48 "XDG_VIDEOS_DIR=\"$HOME/",
47 {"Videos", "Видео", "Vidéos"} 49 {"Videos", "Видео", "Vidéos", "Video", "Vídeos"}
48 }, 50 },
49 51
50 { 52 {
51 "${PICTURES}", 53 "${PICTURES}",
52 "XDG_PICTURES_DIR=\"$HOME/", 54 "XDG_PICTURES_DIR=\"$HOME/",
53 {"Pictures", "Изображения", "Photos"} 55 {"Pictures", "Изображения", "Photos", "Immagini", "Imágenes", "Imagens", "Bilder"}
54 }, 56 },
55 57
56 { 58 {
57 "${DESKTOP}", 59 "${DESKTOP}",
58 "XDG_DESKTOP_DIR=\"$HOME/", 60 "XDG_DESKTOP_DIR=\"$HOME/",
59 {"Desktop", "Рабочий стол", "Bureau"} 61 {"Desktop", "Рабочий стол", "Bureau", "Scrivania", "Escritorio", "Área de trabalho", "Schreibtisch"}
60 }, 62 },
61 63
62 { 64 {
63 "${DOCUMENTS}", 65 "${DOCUMENTS}",
64 "XDG_DOCUMENTS_DIR=\"$HOME/", 66 "XDG_DOCUMENTS_DIR=\"$HOME/",
65 {"Documents", "Документы", "Documents"} 67 {"Documents", "Документы", "Documenti", "Documentos", "Dokumente"}
66 }, 68 },
67 69
68 { 0 } 70 { 0 }
@@ -99,7 +101,7 @@ static char *resolve_xdg(const char *var) {
99 101
100 if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1) 102 if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1)
101 errExit("asprintf"); 103 errExit("asprintf");
102 FILE *fp = fopen(fname, "r"); 104 FILE *fp = fopen(fname, "re");
103 if (!fp) { 105 if (!fp) {
104 free(fname); 106 free(fname);
105 return NULL; 107 return NULL;
@@ -149,11 +151,12 @@ static char *resolve_xdg(const char *var) {
149 151
150// returns mallocated memory 152// returns mallocated memory
151static char *resolve_hardcoded(char *entries[]) { 153static char *resolve_hardcoded(char *entries[]) {
154 EUID_ASSERT();
152 char *fname; 155 char *fname;
153 struct stat s; 156 struct stat s;
154 157
155 int i = 0; 158 int i = 0;
156 while (entries[i] != NULL) { 159 while (i < MAX_TRANSLATIONS && entries[i] != NULL) {
157 if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1) 160 if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1)
158 errExit("asprintf"); 161 errExit("asprintf");
159 162
@@ -262,28 +265,6 @@ char *expand_macros(const char *path) {
262 return rv; 265 return rv;
263} 266}
264 267
265// replace control characters with a '?'
266static char *fix_control_chars(const char *fname) {
267 assert(fname);
268
269 size_t len = strlen(fname);
270 char *rv = malloc(len + 1);
271 if (!rv)
272 errExit("malloc");
273
274 size_t i = 0;
275 while (fname[i] != '\0') {
276 if (iscntrl((unsigned char) fname[i]))
277 rv[i] = '?';
278 else
279 rv[i] = fname[i];
280 i++;
281 }
282 rv[i] = '\0';
283
284 return rv;
285}
286
287void invalid_filename(const char *fname, int globbing) { 268void invalid_filename(const char *fname, int globbing) {
288// EUID_ASSERT(); 269// EUID_ASSERT();
289 assert(fname); 270 assert(fname);
@@ -301,24 +282,5 @@ void invalid_filename(const char *fname, int globbing) {
301 return; 282 return;
302 } 283 }
303 284
304 size_t i = 0; 285 reject_meta_chars(ptr, globbing);
305 while (ptr[i] != '\0') {
306 if (iscntrl((unsigned char) ptr[i])) {
307 char *new = fix_control_chars(fname);
308 fprintf(stderr, "Error: \"%s\" is an invalid filename: no control characters allowed\n", new);
309 exit(1);
310 }
311 i++;
312 }
313
314 char *reject;
315 if (globbing)
316 reject = "\\&!\"'<>%^{};,"; // file globbing ('*?[]') is allowed
317 else
318 reject = "\\&!?\"'<>%^{};,*[]";
319 char *c = strpbrk(ptr, reject);
320 if (c) {
321 fprintf(stderr, "Error: \"%s\" is an invalid filename: rejected character: \"%c\"\n", fname, *c);
322 exit(1);
323 }
324} 286}
diff --git a/src/firejail/main.c b/src/firejail/main.c
index d6de6d997..4b01ea0a5 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,6 +20,7 @@
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/pid.h" 21#include "../include/pid.h"
22#include "../include/firejail_user.h" 22#include "../include/firejail_user.h"
23#include "../include/gcov_wrapper.h"
23#include "../include/syscall.h" 24#include "../include/syscall.h"
24#include "../include/seccomp.h" 25#include "../include/seccomp.h"
25#define _GNU_SOURCE 26#define _GNU_SOURCE
@@ -31,7 +32,8 @@
31#include <dirent.h> 32#include <dirent.h>
32#include <pwd.h> 33#include <pwd.h>
33#include <errno.h> 34#include <errno.h>
34//#include <limits.h> 35
36#include <limits.h>
35#include <sys/file.h> 37#include <sys/file.h>
36#include <sys/prctl.h> 38#include <sys/prctl.h>
37#include <signal.h> 39#include <signal.h>
@@ -116,15 +118,16 @@ int arg_private_cwd = 0; // private working directory
116int arg_scan = 0; // arp-scan all interfaces 118int arg_scan = 0; // arp-scan all interfaces
117int arg_whitelist = 0; // whitelist command 119int arg_whitelist = 0; // whitelist command
118int arg_nosound = 0; // disable sound 120int arg_nosound = 0; // disable sound
119int arg_noautopulse = 0; // disable automatic ~/.config/pulse init
120int arg_novideo = 0; //disable video devices in /dev 121int arg_novideo = 0; //disable video devices in /dev
121int arg_no3d; // disable 3d hardware acceleration 122int arg_no3d; // disable 3d hardware acceleration
123int arg_noprinters = 0; // disable printers
122int arg_quiet = 0; // no output for scripting 124int arg_quiet = 0; // no output for scripting
123int arg_join_network = 0; // join only the network namespace 125int arg_join_network = 0; // join only the network namespace
124int arg_join_filesystem = 0; // join only the mount namespace 126int arg_join_filesystem = 0; // join only the mount namespace
125int arg_nice = 0; // nice value configured 127int arg_nice = 0; // nice value configured
126int arg_ipc = 0; // enable ipc namespace 128int arg_ipc = 0; // enable ipc namespace
127int arg_writable_etc = 0; // writable etc 129int arg_writable_etc = 0; // writable etc
130int arg_keep_config_pulse = 0; // disable automatic ~/.config/pulse init
128int arg_writable_var = 0; // writable var 131int arg_writable_var = 0; // writable var
129int arg_keep_var_tmp = 0; // don't overwrite /var/tmp 132int arg_keep_var_tmp = 0; // don't overwrite /var/tmp
130int arg_writable_run_user = 0; // writable /run/user 133int arg_writable_run_user = 0; // writable /run/user
@@ -135,7 +138,7 @@ int arg_allow_debuggers = 0; // allow debuggers
135int arg_x11_block = 0; // block X11 138int arg_x11_block = 0; // block X11
136int arg_x11_xorg = 0; // use X11 security extension 139int arg_x11_xorg = 0; // use X11 security extension
137int arg_allusers = 0; // all user home directories visible 140int arg_allusers = 0; // all user home directories visible
138int arg_machineid = 0; // preserve /etc/machine-id 141int arg_machineid = 0; // spoof /etc/machine-id
139int arg_allow_private_blacklist = 0; // blacklist things in private directories 142int arg_allow_private_blacklist = 0; // blacklist things in private directories
140int arg_disable_mnt = 0; // disable /mnt and /media 143int arg_disable_mnt = 0; // disable /mnt and /media
141int arg_noprofile = 0; // use default.profile if none other found/specified 144int arg_noprofile = 0; // use default.profile if none other found/specified
@@ -145,11 +148,14 @@ int arg_nodvd = 0; // --nodvd
145int arg_nou2f = 0; // --nou2f 148int arg_nou2f = 0; // --nou2f
146int arg_noinput = 0; // --noinput 149int arg_noinput = 0; // --noinput
147int arg_deterministic_exit_code = 0; // always exit with first child's exit status 150int arg_deterministic_exit_code = 0; // always exit with first child's exit status
151int arg_deterministic_shutdown = 0; // shut down the sandbox if first child dies
152int arg_keep_fd_all = 0; // inherit all file descriptors to sandbox
148DbusPolicy arg_dbus_user = DBUS_POLICY_ALLOW; // --dbus-user 153DbusPolicy arg_dbus_user = DBUS_POLICY_ALLOW; // --dbus-user
149DbusPolicy arg_dbus_system = DBUS_POLICY_ALLOW; // --dbus-system 154DbusPolicy arg_dbus_system = DBUS_POLICY_ALLOW; // --dbus-system
150const char *arg_dbus_log_file = NULL; 155const char *arg_dbus_log_file = NULL;
151int arg_dbus_log_user = 0; 156int arg_dbus_log_user = 0;
152int arg_dbus_log_system = 0; 157int arg_dbus_log_system = 0;
158int arg_tab = 0;
153int login_shell = 0; 159int login_shell = 0;
154 160
155int parent_to_child_fds[2]; 161int parent_to_child_fds[2];
@@ -188,13 +194,15 @@ static void my_handler(int s) {
188 logsignal(s); 194 logsignal(s);
189 195
190 if (waitpid(child, NULL, WNOHANG) == 0) { 196 if (waitpid(child, NULL, WNOHANG) == 0) {
191 if (has_handler(child, s)) // signals are not delivered if there is no handler yet 197 // child is pid 1 of a pid namespace:
198 // signals are not delivered if there is no handler yet
199 if (has_handler(child, s))
192 kill(child, s); 200 kill(child, s);
193 else 201 else
194 kill(child, SIGKILL); 202 kill(child, SIGKILL);
195 waitpid(child, NULL, 0); 203 waitpid(child, NULL, 0);
196 } 204 }
197 myexit(s); 205 myexit(128 + s);
198} 206}
199 207
200static void install_handler(void) { 208static void install_handler(void) {
@@ -259,8 +267,8 @@ static void init_cfg(int argc, char **argv) {
259 fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); 267 fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username);
260 exit(1); 268 exit(1);
261 } 269 }
270 check_homedir(pw->pw_dir);
262 cfg.homedir = clean_pathname(pw->pw_dir); 271 cfg.homedir = clean_pathname(pw->pw_dir);
263 check_homedir();
264 272
265 // initialize random number generator 273 // initialize random number generator
266 sandbox_pid = getpid(); 274 sandbox_pid = getpid();
@@ -402,6 +410,23 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
402 } 410 }
403#endif 411#endif
404#ifdef HAVE_NETWORK 412#ifdef HAVE_NETWORK
413 else if (strcmp(argv[i], "--nettrace") == 0) {
414 if (checkcfg(CFG_NETWORK)) {
415 netfilter_trace(0);
416 }
417 else
418 exit_err_feature("networking");
419 exit(0);
420 }
421 else if (strncmp(argv[i], "--nettrace=", 11) == 0) {
422 if (checkcfg(CFG_NETWORK)) {
423 pid_t pid = require_pid(argv[i] + 11);
424 netfilter_trace(pid);
425 }
426 else
427 exit_err_feature("networking");
428 exit(0);
429 }
405 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { 430 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
406 if (checkcfg(CFG_NETWORK)) { 431 if (checkcfg(CFG_NETWORK)) {
407 logargs(argc, argv); 432 logargs(argc, argv);
@@ -535,7 +560,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
535 char *fname; 560 char *fname;
536 if (asprintf(&fname, RUN_FIREJAIL_PROFILE_DIR "/%d", pid) == -1) 561 if (asprintf(&fname, RUN_FIREJAIL_PROFILE_DIR "/%d", pid) == -1)
537 errExit("asprintf"); 562 errExit("asprintf");
538 FILE *fp = fopen(fname, "r"); 563 FILE *fp = fopen(fname, "re");
539 if (!fp) { 564 if (!fp) {
540 fprintf(stderr, "Error: sandbox %s not found\n", argv[i] + 16); 565 fprintf(stderr, "Error: sandbox %s not found\n", argv[i] + 16);
541 exit(1); 566 exit(1);
@@ -862,13 +887,12 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
862char *guess_shell(void) { 887char *guess_shell(void) {
863 const char *shell; 888 const char *shell;
864 char *retval; 889 char *retval;
865 struct stat s;
866 890
867 shell = env_get("SHELL"); 891 shell = env_get("SHELL");
868 if (shell) { 892 if (shell) {
869 invalid_filename(shell, 0); // no globbing 893 invalid_filename(shell, 0); // no globbing
870 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0 && 894 if (access(shell, X_OK) == 0 && !is_dir(shell) && strstr(shell, "..") == NULL &&
871 strcmp(shell, PATH_FIREJAIL) != 0) 895 strcmp(gnu_basename(shell), "firejail") != 0)
872 goto found; 896 goto found;
873 } 897 }
874 898
@@ -878,12 +902,15 @@ char *guess_shell(void) {
878 int i = 0; 902 int i = 0;
879 while (shells[i] != NULL) { 903 while (shells[i] != NULL) {
880 // access call checks as real UID/GID, not as effective UID/GID 904 // access call checks as real UID/GID, not as effective UID/GID
881 if (stat(shells[i], &s) == 0 && access(shells[i], X_OK) == 0) { 905 if (access(shells[i], X_OK) == 0) {
882 shell = shells[i]; 906 shell = shells[i];
883 break; 907 goto found;
884 } 908 }
885 i++; 909 i++;
886 } 910 }
911
912 return NULL;
913
887 found: 914 found:
888 retval = strdup(shell); 915 retval = strdup(shell);
889 if (!retval) 916 if (!retval)
@@ -929,12 +956,14 @@ static void run_builder(int argc, char **argv) {
929 if (setresuid(-1, getuid(), getuid()) != 0) 956 if (setresuid(-1, getuid(), getuid()) != 0)
930 errExit("setresuid"); 957 errExit("setresuid");
931 958
959 if (env_get("LD_PRELOAD") != NULL)
960 fprintf(stderr, "run_builder: LD_PRELOAD is: '%s'\n", env_get("LD_PRELOAD"));
932 assert(env_get("LD_PRELOAD") == NULL); 961 assert(env_get("LD_PRELOAD") == NULL);
933 assert(getenv("LD_PRELOAD") == NULL); 962 assert(getenv("LD_PRELOAD") == NULL);
934 umask(orig_umask); 963 umask(orig_umask);
935 964
936 // restore some environment variables 965 // restore original environment variables
937 env_apply_whitelist_sbox(); 966 env_apply_all();
938 967
939 argv[0] = LIBDIR "/firejail/fbuilder"; 968 argv[0] = LIBDIR "/firejail/fbuilder";
940 execvp(argv[0], argv); 969 execvp(argv[0], argv);
@@ -961,7 +990,7 @@ void filter_add_blacklist_override(int fd, int syscall, int arg, void *ptrarg, b
961static int check_postexec(const char *list) { 990static int check_postexec(const char *list) {
962 char *prelist, *postlist; 991 char *prelist, *postlist;
963 992
964 if (list) { 993 if (list && list[0]) {
965 syscalls_in_list(list, "@default-keep", -1, &prelist, &postlist, true); 994 syscalls_in_list(list, "@default-keep", -1, &prelist, &postlist, true);
966 if (postlist) 995 if (postlist)
967 return 1; 996 return 1;
@@ -980,125 +1009,64 @@ int main(int argc, char **argv, char **envp) {
980 int option_cgroup = 0; 1009 int option_cgroup = 0;
981 int custom_profile = 0; // custom profile loaded 1010 int custom_profile = 0; // custom profile loaded
982 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot) 1011 int arg_caps_cmdline = 0; // caps requested on command line (used to break out of --chroot)
1012 int arg_netlock = 0;
983 char **ptr; 1013 char **ptr;
984 1014
1015
985 // sanitize the umask 1016 // sanitize the umask
986 orig_umask = umask(022); 1017 orig_umask = umask(022);
987 1018
988 // check standard streams before printing anything
989 fix_std_streams();
990
991 // drop permissions by default and rise them when required 1019 // drop permissions by default and rise them when required
992 EUID_INIT(); 1020 EUID_INIT();
993 EUID_USER(); 1021 EUID_USER();
994 1022
1023 // check standard streams before opening any file
1024 fix_std_streams();
1025
995 // argument count should be larger than 0 1026 // argument count should be larger than 0
996 if (argc == 0 || !argv || strlen(argv[0]) == 0) { 1027 if (argc == 0 || !argv || strlen(argv[0]) == 0) {
997 fprintf(stderr, "Error: argv is invalid\n"); 1028 fprintf(stderr, "Error: argv is invalid\n");
998 exit(1); 1029 exit(1);
999 } else if (argc >= MAX_ARGS) { 1030 } else if (argc >= MAX_ARGS) {
1000 fprintf(stderr, "Error: too many arguments\n"); 1031 fprintf(stderr, "Error: too many arguments: argc (%d) >= MAX_ARGS (%d)\n", argc, MAX_ARGS);
1001 exit(1); 1032 exit(1);
1002 } 1033 }
1003 1034
1035 // sanity check for arguments
1036 for (i = 0; i < argc; i++) {
1037// if (*argv[i] == 0) { // see #4395 - bug reported by Debian
1038// fprintf(stderr, "Error: too short arguments: argv[%d] is empty\n", i);
1039// exit(1);
1040// }
1041 if (strlen(argv[i]) >= MAX_ARG_LEN) {
1042 fprintf(stderr, "Error: too long arguments: argv[%d] len (%zu) >= MAX_ARG_LEN (%d)\n", i, strlen(argv[i]), MAX_ARG_LEN);
1043 exit(1);
1044 }
1045 }
1046
1004 // Stash environment variables 1047 // Stash environment variables
1005 for (i = 0, ptr = envp; ptr && *ptr && i < MAX_ENVS; i++, ptr++) 1048 for (i = 0, ptr = envp; ptr && *ptr && i < MAX_ENVS; i++, ptr++)
1006 env_store(*ptr, SETENV); 1049 env_store(*ptr, SETENV);
1007 1050
1008 // sanity check for environment variables 1051 // sanity check for environment variables
1009 if (i >= MAX_ENVS) { 1052 if (i >= MAX_ENVS) {
1010 fprintf(stderr, "Error: too many environment variables\n"); 1053 fprintf(stderr, "Error: too many environment variables: >= MAX_ENVS (%d)\n", MAX_ENVS);
1011 exit(1); 1054 exit(1);
1012 } 1055 }
1013 1056
1014 // sanity check for arguments
1015 for (i = 0; i < argc; i++) {
1016 if (*argv[i] == 0) {
1017 fprintf(stderr, "Error: too short arguments\n");
1018 exit(1);
1019 }
1020 if (strlen(argv[i]) >= MAX_ARG_LEN) {
1021 fprintf(stderr, "Error: too long arguments\n");
1022 exit(1);
1023 }
1024 }
1025
1026 // Reapply a minimal set of environment variables 1057 // Reapply a minimal set of environment variables
1027 env_apply_whitelist(); 1058 env_apply_whitelist();
1028 1059
1029 // check if the user is allowed to use firejail 1060 // process --quiet
1030 init_cfg(argc, argv);
1031
1032 // get starting timestamp, process --quiet
1033 timetrace_start();
1034 const char *env_quiet = env_get("FIREJAIL_QUIET"); 1061 const char *env_quiet = env_get("FIREJAIL_QUIET");
1035 if (check_arg(argc, argv, "--quiet", 1) || (env_quiet && strcmp(env_quiet, "yes") == 0)) 1062 if (check_arg(argc, argv, "--quiet", 1) || (env_quiet && strcmp(env_quiet, "yes") == 0))
1036 arg_quiet = 1; 1063 arg_quiet = 1;
1037 1064
1038 // cleanup at exit 1065 // check if the user is allowed to use firejail
1039 EUID_ROOT(); 1066 init_cfg(argc, argv);
1040 atexit(clear_atexit);
1041
1042 // build /run/firejail directory structure
1043 preproc_build_firejail_dir();
1044 const char *container_name = env_get("container");
1045 if (!container_name || strcmp(container_name, "firejail")) {
1046 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1047 if (lockfd_directory != -1) {
1048 int rv = fchown(lockfd_directory, 0, 0);
1049 (void) rv;
1050 flock(lockfd_directory, LOCK_EX);
1051 }
1052 preproc_clean_run();
1053 flock(lockfd_directory, LOCK_UN);
1054 close(lockfd_directory);
1055 }
1056 EUID_USER();
1057
1058 // --ip=dhcp - we need access to /sbin and /usr/sbin directories in order to run ISC DHCP client (dhclient)
1059 // these paths are disabled in disable-common.inc
1060 if ((i = check_arg(argc, argv, "--ip", 0)) != 0) {
1061 if (strncmp(argv[i] + 4, "=dhcp", 5) == 0) {
1062 profile_add("noblacklist /sbin");
1063 profile_add("noblacklist /usr/sbin");
1064 }
1065 }
1066
1067 // for appimages we need to remove "include disable-shell.inc from the profile
1068 // a --profile command can show up before --appimage
1069 if (check_arg(argc, argv, "--appimage", 1))
1070 arg_appimage = 1;
1071
1072 // process allow-debuggers
1073 if (check_arg(argc, argv, "--allow-debuggers", 1)) {
1074 // check kernel version
1075 struct utsname u;
1076 int rv = uname(&u);
1077 if (rv != 0)
1078 errExit("uname");
1079 int major;
1080 int minor;
1081 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
1082 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
1083 exit(1);
1084 }
1085 if (major < 4 || (major == 4 && minor < 8)) {
1086 fprintf(stderr, "Error: --allow-debuggers is disabled on Linux kernels prior to 4.8. "
1087 "A bug in ptrace call allows a full bypass of the seccomp filter. "
1088 "Your current kernel version is %d.%d.\n", major, minor);
1089 exit(1);
1090 }
1091
1092 arg_allow_debuggers = 1;
1093 char *cmd = strdup("noblacklist ${PATH}/strace");
1094 if (!cmd)
1095 errExit("strdup");
1096 profile_add(cmd);
1097 }
1098 1067
1099 // profile builder 1068 // get starting timestamp
1100 if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename 1069 timetrace_start();
1101 run_builder(argc, argv); // this function will not return
1102 1070
1103 // check argv[0] symlink wrapper if this is not a login shell 1071 // check argv[0] symlink wrapper if this is not a login shell
1104 if (*argv[0] != '-') 1072 if (*argv[0] != '-')
@@ -1123,15 +1091,44 @@ int main(int argc, char **argv, char **envp) {
1123 __builtin_unreachable(); 1091 __builtin_unreachable();
1124 } 1092 }
1125 } 1093 }
1126 EUID_ASSERT();
1127 1094
1095 // profile builder
1096 if (check_arg(argc, argv, "--build", 0)) // supports both --build and --build=filename
1097 run_builder(argc, argv); // this function will not return
1098
1099 // intrusion detection system
1100 if (check_arg(argc, argv, "--ids-", 0)) // supports both --ids-init and --ids-check
1101 run_ids(argc, argv); // this function will not return
1128 1102
1129 // check firejail directories
1130 EUID_ROOT(); 1103 EUID_ROOT();
1131 delete_run_files(sandbox_pid); 1104#ifndef HAVE_SUID
1105 if (geteuid() != 0) {
1106 fprintf(stderr, "Error: Firejail needs to be SUID.\n");
1107 fprintf(stderr, "Assuming firejail is installed in /usr/bin, execute the following command as root:\n");
1108 fprintf(stderr, " chmod u+s /usr/bin/firejail\n");
1109 }
1110#endif
1111
1112 // build /run/firejail directory structure
1113 preproc_build_firejail_dir();
1114 const char *container_name = env_get("container");
1115 if (!container_name || strcmp(container_name, "firejail")) {
1116 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
1117 if (lockfd_directory != -1) {
1118 int rv = fchown(lockfd_directory, 0, 0);
1119 (void) rv;
1120 flock(lockfd_directory, LOCK_EX);
1121 }
1122 preproc_clean_run();
1123 flock(lockfd_directory, LOCK_UN);
1124 close(lockfd_directory);
1125 }
1126
1127 delete_run_files(getpid());
1128 atexit(clear_atexit);
1132 EUID_USER(); 1129 EUID_USER();
1133 1130
1134 //check if the parent is sshd daemon 1131 // check if the parent is sshd daemon
1135 int parent_sshd = 0; 1132 int parent_sshd = 0;
1136 { 1133 {
1137 pid_t ppid = getppid(); 1134 pid_t ppid = getppid();
@@ -1145,7 +1142,7 @@ int main(int argc, char **argv, char **envp) {
1145 1142
1146#ifdef DEBUG_RESTRICTED_SHELL 1143#ifdef DEBUG_RESTRICTED_SHELL
1147 {EUID_ROOT(); 1144 {EUID_ROOT();
1148 FILE *fp = fopen("/firelog", "w"); 1145 FILE *fp = fopen("/firelog", "we");
1149 if (fp) { 1146 if (fp) {
1150 int i; 1147 int i;
1151 fprintf(fp, "argc %d: ", argc); 1148 fprintf(fp, "argc %d: ", argc);
@@ -1164,7 +1161,7 @@ int main(int argc, char **argv, char **envp) {
1164 strncmp(argv[2], "scp ", 4) == 0) { 1161 strncmp(argv[2], "scp ", 4) == 0) {
1165#ifdef DEBUG_RESTRICTED_SHELL 1162#ifdef DEBUG_RESTRICTED_SHELL
1166 {EUID_ROOT(); 1163 {EUID_ROOT();
1167 FILE *fp = fopen("/firelog", "a"); 1164 FILE *fp = fopen("/firelog", "ae");
1168 if (fp) { 1165 if (fp) {
1169 fprintf(fp, "run without a sandbox\n"); 1166 fprintf(fp, "run without a sandbox\n");
1170 fclose(fp); 1167 fclose(fp);
@@ -1188,7 +1185,8 @@ int main(int argc, char **argv, char **envp) {
1188 } 1185 }
1189 EUID_ASSERT(); 1186 EUID_ASSERT();
1190 1187
1191 // is this a login shell, or a command passed by sshd, insert command line options from /etc/firejail/login.users 1188 // is this a login shell, or a command passed by sshd,
1189 // insert command line options from /etc/firejail/login.users
1192 if (*argv[0] == '-' || parent_sshd) { 1190 if (*argv[0] == '-' || parent_sshd) {
1193 if (argc == 1) 1191 if (argc == 1)
1194 login_shell = 1; 1192 login_shell = 1;
@@ -1197,7 +1195,7 @@ int main(int argc, char **argv, char **envp) {
1197 1195
1198#ifdef DEBUG_RESTRICTED_SHELL 1196#ifdef DEBUG_RESTRICTED_SHELL
1199 {EUID_ROOT(); 1197 {EUID_ROOT();
1200 FILE *fp = fopen("/firelog", "a"); 1198 FILE *fp = fopen("/firelog", "ae");
1201 if (fp) { 1199 if (fp) {
1202 fprintf(fp, "fullargc %d: ", fullargc); 1200 fprintf(fp, "fullargc %d: ", fullargc);
1203 int i; 1201 int i;
@@ -1219,7 +1217,7 @@ int main(int argc, char **argv, char **envp) {
1219 1217
1220#ifdef DEBUG_RESTRICTED_SHELL 1218#ifdef DEBUG_RESTRICTED_SHELL
1221 {EUID_ROOT(); 1219 {EUID_ROOT();
1222 FILE *fp = fopen("/firelog", "a"); 1220 FILE *fp = fopen("/firelog", "ae");
1223 if (fp) { 1221 if (fp) {
1224 fprintf(fp, "argc %d: ", argc); 1222 fprintf(fp, "argc %d: ", argc);
1225 int i; 1223 int i;
@@ -1240,6 +1238,47 @@ int main(int argc, char **argv, char **envp) {
1240#endif 1238#endif
1241 EUID_ASSERT(); 1239 EUID_ASSERT();
1242 1240
1241 // --ip=dhcp - we need access to /sbin and /usr/sbin directories in order to run ISC DHCP client (dhclient)
1242 // these paths are disabled in disable-common.inc
1243 if ((i = check_arg(argc, argv, "--ip", 0)) != 0) {
1244 if (strncmp(argv[i] + 4, "=dhcp", 5) == 0) {
1245 profile_add("noblacklist /sbin");
1246 profile_add("noblacklist /usr/sbin");
1247 }
1248 }
1249
1250 // process allow-debuggers
1251 if (check_arg(argc, argv, "--allow-debuggers", 1)) {
1252 // check kernel version
1253 struct utsname u;
1254 int rv = uname(&u);
1255 if (rv != 0)
1256 errExit("uname");
1257 int major;
1258 int minor;
1259 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
1260 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
1261 exit(1);
1262 }
1263 if (major < 4 || (major == 4 && minor < 8)) {
1264 fprintf(stderr, "Error: --allow-debuggers is disabled on Linux kernels prior to 4.8. "
1265 "A bug in ptrace call allows a full bypass of the seccomp filter. "
1266 "Your current kernel version is %d.%d.\n", major, minor);
1267 exit(1);
1268 }
1269
1270 arg_allow_debuggers = 1;
1271 char *cmd = strdup("noblacklist ${PATH}/strace");
1272 if (!cmd)
1273 errExit("strdup");
1274 profile_add(cmd);
1275 }
1276
1277 // for appimages we need to remove "include disable-shell.inc from the profile
1278 // a --profile command can show up before --appimage
1279 if (check_arg(argc, argv, "--appimage", 1))
1280 arg_appimage = 1;
1281
1243 // check for force-nonewprivs in /etc/firejail/firejail.config file 1282 // check for force-nonewprivs in /etc/firejail/firejail.config file
1244 if (checkcfg(CFG_FORCE_NONEWPRIVS)) 1283 if (checkcfg(CFG_FORCE_NONEWPRIVS))
1245 arg_nonewprivs = 1; 1284 arg_nonewprivs = 1;
@@ -1248,8 +1287,10 @@ int main(int argc, char **argv, char **envp) {
1248 for (i = 1; i < argc; i++) { 1287 for (i = 1; i < argc; i++) {
1249 run_cmd_and_exit(i, argc, argv); // will exit if the command is recognized 1288 run_cmd_and_exit(i, argc, argv); // will exit if the command is recognized
1250 1289
1251 if (strcmp(argv[i], "--debug") == 0 && !arg_quiet) 1290 if (strcmp(argv[i], "--debug") == 0) {
1252 arg_debug = 1; 1291 arg_debug = 1;
1292 arg_quiet = 0;
1293 }
1253 else if (strcmp(argv[i], "--debug-blacklists") == 0) 1294 else if (strcmp(argv[i], "--debug-blacklists") == 0)
1254 arg_debug_blacklists = 1; 1295 arg_debug_blacklists = 1;
1255 else if (strcmp(argv[i], "--debug-whitelists") == 0) 1296 else if (strcmp(argv[i], "--debug-whitelists") == 0)
@@ -1257,8 +1298,8 @@ int main(int argc, char **argv, char **envp) {
1257 else if (strcmp(argv[i], "--debug-private-lib") == 0) 1298 else if (strcmp(argv[i], "--debug-private-lib") == 0)
1258 arg_debug_private_lib = 1; 1299 arg_debug_private_lib = 1;
1259 else if (strcmp(argv[i], "--quiet") == 0) { 1300 else if (strcmp(argv[i], "--quiet") == 0) {
1260 arg_quiet = 1; 1301 if (!arg_debug)
1261 arg_debug = 0; 1302 arg_quiet = 1;
1262 } 1303 }
1263 else if (strcmp(argv[i], "--allow-debuggers") == 0) { 1304 else if (strcmp(argv[i], "--allow-debuggers") == 0) {
1264 // already handled 1305 // already handled
@@ -1480,8 +1521,11 @@ int main(int argc, char **argv, char **envp) {
1480 arg_rlimit_nproc = 1; 1521 arg_rlimit_nproc = 1;
1481 } 1522 }
1482 else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) { 1523 else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) {
1483 check_unsigned(argv[i] + 15, "Error: invalid rlimit"); 1524 cfg.rlimit_fsize = parse_arg_size(argv[i] + 15);
1484 sscanf(argv[i] + 15, "%llu", &cfg.rlimit_fsize); 1525 if (cfg.rlimit_fsize == 0) {
1526 perror("Error: invalid rlimit-fsize. Only use positive numbers and k, m or g suffix.");
1527 exit(1);
1528 }
1485 arg_rlimit_fsize = 1; 1529 arg_rlimit_fsize = 1;
1486 } 1530 }
1487 else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) { 1531 else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) {
@@ -1490,8 +1534,11 @@ int main(int argc, char **argv, char **envp) {
1490 arg_rlimit_sigpending = 1; 1534 arg_rlimit_sigpending = 1;
1491 } 1535 }
1492 else if (strncmp(argv[i], "--rlimit-as=", 12) == 0) { 1536 else if (strncmp(argv[i], "--rlimit-as=", 12) == 0) {
1493 check_unsigned(argv[i] + 12, "Error: invalid rlimit"); 1537 cfg.rlimit_as = parse_arg_size(argv[i] + 12);
1494 sscanf(argv[i] + 12, "%llu", &cfg.rlimit_as); 1538 if (cfg.rlimit_as == 0) {
1539 perror("Error: invalid rlimit-as. Only use positive numbers and k, m or g suffix.");
1540 exit(1);
1541 }
1495 arg_rlimit_as = 1; 1542 arg_rlimit_as = 1;
1496 } 1543 }
1497 else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) 1544 else if (strncmp(argv[i], "--ipc-namespace", 15) == 0)
@@ -1507,15 +1554,16 @@ int main(int argc, char **argv, char **envp) {
1507 else if (strncmp(argv[i], "--cgroup=", 9) == 0) { 1554 else if (strncmp(argv[i], "--cgroup=", 9) == 0) {
1508 if (checkcfg(CFG_CGROUP)) { 1555 if (checkcfg(CFG_CGROUP)) {
1509 if (option_cgroup) { 1556 if (option_cgroup) {
1510 fprintf(stderr, "Error: only a cgroup can be defined\n"); 1557 fprintf(stderr, "Error: only one cgroup can be defined\n");
1511 exit(1); 1558 exit(1);
1512 } 1559 }
1513
1514 option_cgroup = 1;
1515 cfg.cgroup = strdup(argv[i] + 9); 1560 cfg.cgroup = strdup(argv[i] + 9);
1516 if (!cfg.cgroup) 1561 if (!cfg.cgroup)
1517 errExit("strdup"); 1562 errExit("strdup");
1518 set_cgroup(cfg.cgroup); 1563
1564 check_cgroup_file(cfg.cgroup);
1565 set_cgroup(cfg.cgroup, getpid());
1566 option_cgroup = 1;
1519 } 1567 }
1520 else 1568 else
1521 exit_err_feature("cgroup"); 1569 exit_err_feature("cgroup");
@@ -1546,6 +1594,7 @@ int main(int argc, char **argv, char **envp) {
1546 profile_check_line(line, 0, NULL); // will exit if something wrong 1594 profile_check_line(line, 0, NULL); // will exit if something wrong
1547 profile_add(line); 1595 profile_add(line);
1548 } 1596 }
1597
1549 else if (strncmp(argv[i], "--blacklist=", 12) == 0) { 1598 else if (strncmp(argv[i], "--blacklist=", 12) == 0) {
1550 char *line; 1599 char *line;
1551 if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1) 1600 if (asprintf(&line, "blacklist %s", argv[i] + 12) == -1)
@@ -1562,19 +1611,13 @@ int main(int argc, char **argv, char **envp) {
1562 profile_check_line(line, 0, NULL); // will exit if something wrong 1611 profile_check_line(line, 0, NULL); // will exit if something wrong
1563 profile_add(line); 1612 profile_add(line);
1564 } 1613 }
1565
1566#ifdef HAVE_WHITELIST
1567 else if (strncmp(argv[i], "--whitelist=", 12) == 0) { 1614 else if (strncmp(argv[i], "--whitelist=", 12) == 0) {
1568 if (checkcfg(CFG_WHITELIST)) { 1615 char *line;
1569 char *line; 1616 if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1)
1570 if (asprintf(&line, "whitelist %s", argv[i] + 12) == -1) 1617 errExit("asprintf");
1571 errExit("asprintf");
1572 1618
1573 profile_check_line(line, 0, NULL); // will exit if something wrong 1619 profile_check_line(line, 0, NULL); // will exit if something wrong
1574 profile_add(line); 1620 profile_add(line);
1575 }
1576 else
1577 exit_err_feature("whitelist");
1578 } 1621 }
1579 else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) { 1622 else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) {
1580 char *line; 1623 char *line;
@@ -1584,7 +1627,7 @@ int main(int argc, char **argv, char **envp) {
1584 profile_check_line(line, 0, NULL); // will exit if something wrong 1627 profile_check_line(line, 0, NULL); // will exit if something wrong
1585 profile_add(line); 1628 profile_add(line);
1586 } 1629 }
1587#endif 1630
1588 else if (strncmp(argv[i], "--mkdir=", 8) == 0) { 1631 else if (strncmp(argv[i], "--mkdir=", 8) == 0) {
1589 char *line; 1632 char *line;
1590 if (asprintf(&line, "mkdir %s", argv[i] + 8) == -1) 1633 if (asprintf(&line, "mkdir %s", argv[i] + 8) == -1)
@@ -1824,6 +1867,8 @@ int main(int argc, char **argv, char **envp) {
1824 exit(1); 1867 exit(1);
1825 } 1868 }
1826 arg_noprofile = 1; 1869 arg_noprofile = 1;
1870 // force keep-config-pulse in order to keep ~/.config/pulse as is
1871 arg_keep_config_pulse = 1;
1827 } 1872 }
1828 else if (strncmp(argv[i], "--ignore=", 9) == 0) { 1873 else if (strncmp(argv[i], "--ignore=", 9) == 0) {
1829 if (custom_profile) { 1874 if (custom_profile) {
@@ -1832,6 +1877,14 @@ int main(int argc, char **argv, char **envp) {
1832 } 1877 }
1833 profile_add_ignore(argv[i] + 9); 1878 profile_add_ignore(argv[i] + 9);
1834 } 1879 }
1880 else if (strncmp(argv[i], "--keep-fd=", 10) == 0) {
1881 if (strcmp(argv[i] + 10, "all") == 0)
1882 arg_keep_fd_all = 1;
1883 else {
1884 const char *add = argv[i] + 10;
1885 profile_list_augment(&cfg.keep_fd, add);
1886 }
1887 }
1835#ifdef HAVE_CHROOT 1888#ifdef HAVE_CHROOT
1836 else if (strncmp(argv[i], "--chroot=", 9) == 0) { 1889 else if (strncmp(argv[i], "--chroot=", 9) == 0) {
1837 if (checkcfg(CFG_CHROOT)) { 1890 if (checkcfg(CFG_CHROOT)) {
@@ -1874,6 +1927,9 @@ int main(int argc, char **argv, char **envp) {
1874 } 1927 }
1875 arg_writable_etc = 1; 1928 arg_writable_etc = 1;
1876 } 1929 }
1930 else if (strcmp(argv[i], "--keep-config-pulse") == 0) {
1931 arg_keep_config_pulse = 1;
1932 }
1877 else if (strcmp(argv[i], "--writable-var") == 0) { 1933 else if (strcmp(argv[i], "--writable-var") == 0) {
1878 arg_writable_var = 1; 1934 arg_writable_var = 1;
1879 } 1935 }
@@ -1944,61 +2000,77 @@ int main(int argc, char **argv, char **envp) {
1944 arg_keep_dev_shm = 1; 2000 arg_keep_dev_shm = 1;
1945 } 2001 }
1946 else if (strncmp(argv[i], "--private-etc=", 14) == 0) { 2002 else if (strncmp(argv[i], "--private-etc=", 14) == 0) {
1947 if (arg_writable_etc) { 2003 if (checkcfg(CFG_PRIVATE_ETC)) {
1948 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); 2004 if (arg_writable_etc) {
1949 exit(1); 2005 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
1950 } 2006 exit(1);
2007 }
1951 2008
1952 // extract private etc list 2009 // extract private etc list
1953 if (*(argv[i] + 14) == '\0') { 2010 if (*(argv[i] + 14) == '\0') {
1954 fprintf(stderr, "Error: invalid private-etc option\n"); 2011 fprintf(stderr, "Error: invalid private-etc option\n");
1955 exit(1); 2012 exit(1);
2013 }
2014 if (cfg.etc_private_keep) {
2015 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, argv[i] + 14) < 0 )
2016 errExit("asprintf");
2017 } else
2018 cfg.etc_private_keep = argv[i] + 14;
2019 arg_private_etc = 1;
1956 } 2020 }
1957 if (cfg.etc_private_keep) { 2021 else
1958 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, argv[i] + 14) < 0 ) 2022 exit_err_feature("private-etc");
1959 errExit("asprintf");
1960 } else
1961 cfg.etc_private_keep = argv[i] + 14;
1962 arg_private_etc = 1;
1963 } 2023 }
1964 else if (strncmp(argv[i], "--private-opt=", 14) == 0) { 2024 else if (strncmp(argv[i], "--private-opt=", 14) == 0) {
1965 // extract private opt list 2025 if (checkcfg(CFG_PRIVATE_OPT)) {
1966 if (*(argv[i] + 14) == '\0') { 2026 // extract private opt list
1967 fprintf(stderr, "Error: invalid private-opt option\n"); 2027 if (*(argv[i] + 14) == '\0') {
1968 exit(1); 2028 fprintf(stderr, "Error: invalid private-opt option\n");
2029 exit(1);
2030 }
2031 if (cfg.opt_private_keep) {
2032 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, argv[i] + 14) < 0 )
2033 errExit("asprintf");
2034 } else
2035 cfg.opt_private_keep = argv[i] + 14;
2036 arg_private_opt = 1;
1969 } 2037 }
1970 if (cfg.opt_private_keep) { 2038 else
1971 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, argv[i] + 14) < 0 ) 2039 exit_err_feature("private-opt");
1972 errExit("asprintf");
1973 } else
1974 cfg.opt_private_keep = argv[i] + 14;
1975 arg_private_opt = 1;
1976 } 2040 }
1977 else if (strncmp(argv[i], "--private-srv=", 14) == 0) { 2041 else if (strncmp(argv[i], "--private-srv=", 14) == 0) {
1978 // extract private srv list 2042 if (checkcfg(CFG_PRIVATE_SRV)) {
1979 if (*(argv[i] + 14) == '\0') { 2043 // extract private srv list
1980 fprintf(stderr, "Error: invalid private-srv option\n"); 2044 if (*(argv[i] + 14) == '\0') {
1981 exit(1); 2045 fprintf(stderr, "Error: invalid private-srv option\n");
2046 exit(1);
2047 }
2048 if (cfg.srv_private_keep) {
2049 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, argv[i] + 14) < 0 )
2050 errExit("asprintf");
2051 } else
2052 cfg.srv_private_keep = argv[i] + 14;
2053 arg_private_srv = 1;
1982 } 2054 }
1983 if (cfg.srv_private_keep) { 2055 else
1984 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, argv[i] + 14) < 0 ) 2056 exit_err_feature("private-srv");
1985 errExit("asprintf");
1986 } else
1987 cfg.srv_private_keep = argv[i] + 14;
1988 arg_private_srv = 1;
1989 } 2057 }
1990 else if (strncmp(argv[i], "--private-bin=", 14) == 0) { 2058 else if (strncmp(argv[i], "--private-bin=", 14) == 0) {
1991 // extract private bin list 2059 if (checkcfg(CFG_PRIVATE_BIN)) {
1992 if (*(argv[i] + 14) == '\0') { 2060 // extract private bin list
1993 fprintf(stderr, "Error: invalid private-bin option\n"); 2061 if (*(argv[i] + 14) == '\0') {
1994 exit(1); 2062 fprintf(stderr, "Error: invalid private-bin option\n");
2063 exit(1);
2064 }
2065 if (cfg.bin_private_keep) {
2066 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, argv[i] + 14) < 0 )
2067 errExit("asprintf");
2068 } else
2069 cfg.bin_private_keep = argv[i] + 14;
2070 arg_private_bin = 1;
1995 } 2071 }
1996 if (cfg.bin_private_keep) { 2072 else
1997 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, argv[i] + 14) < 0 ) 2073 exit_err_feature("private-bin");
1998 errExit("asprintf");
1999 } else
2000 cfg.bin_private_keep = argv[i] + 14;
2001 arg_private_bin = 1;
2002 } 2074 }
2003 else if (strncmp(argv[i], "--private-lib", 13) == 0) { 2075 else if (strncmp(argv[i], "--private-lib", 13) == 0) {
2004 if (checkcfg(CFG_PRIVATE_LIB)) { 2076 if (checkcfg(CFG_PRIVATE_LIB)) {
@@ -2076,11 +2148,16 @@ int main(int argc, char **argv, char **envp) {
2076 else if (strcmp(argv[i], "--nosound") == 0) 2148 else if (strcmp(argv[i], "--nosound") == 0)
2077 arg_nosound = 1; 2149 arg_nosound = 1;
2078 else if (strcmp(argv[i], "--noautopulse") == 0) 2150 else if (strcmp(argv[i], "--noautopulse") == 0)
2079 arg_noautopulse = 1; 2151 arg_keep_config_pulse = 1;
2080 else if (strcmp(argv[i], "--novideo") == 0) 2152 else if (strcmp(argv[i], "--novideo") == 0)
2081 arg_novideo = 1; 2153 arg_novideo = 1;
2082 else if (strcmp(argv[i], "--no3d") == 0) 2154 else if (strcmp(argv[i], "--no3d") == 0)
2083 arg_no3d = 1; 2155 arg_no3d = 1;
2156 else if (strcmp(argv[i], "--noprinters") == 0) {
2157 arg_noprinters = 1;
2158 profile_add("blacklist /dev/lp*");
2159 profile_add("blacklist /run/cups/cups.sock");
2160 }
2084 else if (strcmp(argv[i], "--notv") == 0) 2161 else if (strcmp(argv[i], "--notv") == 0)
2085 arg_notv = 1; 2162 arg_notv = 1;
2086 else if (strcmp(argv[i], "--nodvd") == 0) 2163 else if (strcmp(argv[i], "--nodvd") == 0)
@@ -2253,6 +2330,21 @@ int main(int argc, char **argv, char **envp) {
2253 continue; 2330 continue;
2254 } 2331 }
2255#ifdef HAVE_NETWORK 2332#ifdef HAVE_NETWORK
2333 else if (strcmp(argv[i], "--netlock") == 0) {
2334 if (checkcfg(CFG_NETWORK))
2335 arg_netlock = 1;
2336 else
2337 exit_err_feature("networking");
2338 }
2339 else if (strncmp(argv[i], "--netlock=", 10) == 0) {
2340 if (checkcfg(CFG_NETWORK)) {
2341 pid_t pid = require_pid(argv[i] + 10);
2342 netfilter_netlock(pid);
2343 }
2344 else
2345 exit_err_feature("networking");
2346 exit(0);
2347 }
2256 else if (strncmp(argv[i], "--interface=", 12) == 0) { 2348 else if (strncmp(argv[i], "--interface=", 12) == 0) {
2257 if (checkcfg(CFG_NETWORK)) { 2349 if (checkcfg(CFG_NETWORK)) {
2258 // checks 2350 // checks
@@ -2558,7 +2650,7 @@ int main(int argc, char **argv, char **envp) {
2558 else if (cfg.dns4 == NULL) 2650 else if (cfg.dns4 == NULL)
2559 cfg.dns4 = dns; 2651 cfg.dns4 = dns;
2560 else { 2652 else {
2561 fwarning("Warning: up to 4 DNS servers can be specified, %s ignored\n", dns); 2653 fwarning("up to 4 DNS servers can be specified, %s ignored\n", dns);
2562 free(dns); 2654 free(dns);
2563 } 2655 }
2564 } 2656 }
@@ -2579,6 +2671,15 @@ int main(int argc, char **argv, char **envp) {
2579 if (checkcfg(CFG_NETWORK)) { 2671 if (checkcfg(CFG_NETWORK)) {
2580 arg_netfilter = 1; 2672 arg_netfilter = 1;
2581 arg_netfilter_file = argv[i] + 12; 2673 arg_netfilter_file = argv[i] + 12;
2674
2675 // expand tilde
2676 if (*arg_netfilter_file == '~') {
2677 char *tmp;
2678 if (asprintf(&tmp, "%s%s", cfg.homedir, arg_netfilter_file + 1) == -1)
2679 errExit("asprintf");
2680 arg_netfilter_file = tmp;
2681 }
2682
2582 check_netfilter_file(arg_netfilter_file); 2683 check_netfilter_file(arg_netfilter_file);
2583 } 2684 }
2584 else 2685 else
@@ -2589,6 +2690,15 @@ int main(int argc, char **argv, char **envp) {
2589 if (checkcfg(CFG_NETWORK)) { 2690 if (checkcfg(CFG_NETWORK)) {
2590 arg_netfilter6 = 1; 2691 arg_netfilter6 = 1;
2591 arg_netfilter6_file = argv[i] + 13; 2692 arg_netfilter6_file = argv[i] + 13;
2693
2694 // expand tilde
2695 if (*arg_netfilter6_file == '~') {
2696 char *tmp;
2697 if (asprintf(&tmp, "%s%s", cfg.homedir, arg_netfilter6_file + 1) == -1)
2698 errExit("asprintf");
2699 arg_netfilter6_file = tmp;
2700 }
2701
2592 check_netfilter_file(arg_netfilter6_file); 2702 check_netfilter_file(arg_netfilter6_file);
2593 } 2703 }
2594 else 2704 else
@@ -2609,8 +2719,9 @@ int main(int argc, char **argv, char **envp) {
2609 //************************************* 2719 //*************************************
2610 else if (strncmp(argv[i], "--timeout=", 10) == 0) 2720 else if (strncmp(argv[i], "--timeout=", 10) == 0)
2611 cfg.timeout = extract_timeout(argv[i] + 10); 2721 cfg.timeout = extract_timeout(argv[i] + 10);
2612 else if (strcmp(argv[i], "--appimage") == 0) 2722 else if (strcmp(argv[i], "--appimage") == 0) {
2613 arg_appimage = 1; 2723 // already handled
2724 }
2614 else if (strcmp(argv[i], "--shell=none") == 0) { 2725 else if (strcmp(argv[i], "--shell=none") == 0) {
2615 arg_shell_none = 1; 2726 arg_shell_none = 1;
2616 if (cfg.shell) { 2727 if (cfg.shell) {
@@ -2685,6 +2796,11 @@ int main(int argc, char **argv, char **envp) {
2685 else if (strcmp(argv[i], "--deterministic-exit-code") == 0) { 2796 else if (strcmp(argv[i], "--deterministic-exit-code") == 0) {
2686 arg_deterministic_exit_code = 1; 2797 arg_deterministic_exit_code = 1;
2687 } 2798 }
2799 else if (strcmp(argv[i], "--deterministic-shutdown") == 0) {
2800 arg_deterministic_shutdown = 1;
2801 }
2802 else if (strcmp(argv[i], "--tab") == 0)
2803 arg_tab = 1;
2688 else { 2804 else {
2689 // double dash - positional params to follow 2805 // double dash - positional params to follow
2690 if (strcmp(argv[i], "--") == 0) { 2806 if (strcmp(argv[i], "--") == 0) {
@@ -2786,6 +2902,11 @@ int main(int argc, char **argv, char **envp) {
2786 // build the sandbox command 2902 // build the sandbox command
2787 if (prog_index == -1 && cfg.shell) { 2903 if (prog_index == -1 && cfg.shell) {
2788 assert(cfg.command_line == NULL); // runs cfg.shell 2904 assert(cfg.command_line == NULL); // runs cfg.shell
2905 if (arg_appimage) {
2906 fprintf(stderr, "Error: no appimage archive specified\n");
2907 exit(1);
2908 }
2909
2789 cfg.window_title = cfg.shell; 2910 cfg.window_title = cfg.shell;
2790 cfg.command_name = cfg.shell; 2911 cfg.command_name = cfg.shell;
2791 } 2912 }
@@ -2793,10 +2914,11 @@ int main(int argc, char **argv, char **envp) {
2793 if (arg_debug) 2914 if (arg_debug)
2794 printf("Configuring appimage environment\n"); 2915 printf("Configuring appimage environment\n");
2795 appimage_set(cfg.command_name); 2916 appimage_set(cfg.command_name);
2796 build_appimage_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 2917 build_appimage_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, true);
2797 } 2918 }
2798 else { 2919 else {
2799 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 2920 // Only add extra quotes if we were not launched by sshd.
2921 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, !parent_sshd);
2800 } 2922 }
2801/* else { 2923/* else {
2802 fprintf(stderr, "Error: command must be specified when --shell=none used.\n"); 2924 fprintf(stderr, "Error: command must be specified when --shell=none used.\n");
@@ -2810,7 +2932,13 @@ int main(int argc, char **argv, char **envp) {
2810 2932
2811 // load the profile 2933 // load the profile
2812 if (!arg_noprofile && !custom_profile) { 2934 if (!arg_noprofile && !custom_profile) {
2813 custom_profile = profile_find_firejail(cfg.command_name, 1); 2935 if (arg_appimage) {
2936 custom_profile = appimage_find_profile(cfg.command_name);
2937 // disable shell=* for appimages
2938 arg_shell_none = 0;
2939 }
2940 else
2941 custom_profile = profile_find_firejail(cfg.command_name, 1);
2814 } 2942 }
2815 2943
2816 // use default.profile as the default 2944 // use default.profile as the default
@@ -2824,7 +2952,7 @@ int main(int argc, char **argv, char **envp) {
2824 custom_profile = profile_find_firejail(profile_name, 1); 2952 custom_profile = profile_find_firejail(profile_name, 1);
2825 2953
2826 if (!custom_profile) { 2954 if (!custom_profile) {
2827 fprintf(stderr, "Error: no default.profile installed\n"); 2955 fprintf(stderr, "Error: no %s installed\n", profile_name);
2828 exit(1); 2956 exit(1);
2829 } 2957 }
2830 2958
@@ -2840,6 +2968,15 @@ int main(int argc, char **argv, char **envp) {
2840 // check network configuration options - it will exit if anything went wrong 2968 // check network configuration options - it will exit if anything went wrong
2841 net_check_cfg(); 2969 net_check_cfg();
2842 2970
2971 // customization of default seccomp filter
2972 if (config_seccomp_filter_add) {
2973 if (arg_seccomp && !cfg.seccomp_list_keep && !cfg.seccomp_list_drop)
2974 profile_list_augment(&cfg.seccomp_list, config_seccomp_filter_add);
2975
2976 if (arg_seccomp32 && !cfg.seccomp_list_keep32 && !cfg.seccomp_list_drop32)
2977 profile_list_augment(&cfg.seccomp_list32, config_seccomp_filter_add);
2978 }
2979
2843 if (arg_seccomp) 2980 if (arg_seccomp)
2844 arg_seccomp_postexec = check_postexec(cfg.seccomp_list) || check_postexec(cfg.seccomp_list_drop); 2981 arg_seccomp_postexec = check_postexec(cfg.seccomp_list) || check_postexec(cfg.seccomp_list_drop);
2845 2982
@@ -2850,7 +2987,7 @@ int main(int argc, char **argv, char **envp) {
2850 // check and assign an IP address - for macvlan it will be done again in the sandbox! 2987 // check and assign an IP address - for macvlan it will be done again in the sandbox!
2851 if (any_bridge_configured()) { 2988 if (any_bridge_configured()) {
2852 EUID_ROOT(); 2989 EUID_ROOT();
2853 lockfd_network = open(RUN_NETWORK_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 2990 lockfd_network = open(RUN_NETWORK_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
2854 if (lockfd_network != -1) { 2991 if (lockfd_network != -1) {
2855 int rv = fchown(lockfd_network, 0, 0); 2992 int rv = fchown(lockfd_network, 0, 0);
2856 (void) rv; 2993 (void) rv;
@@ -2872,12 +3009,6 @@ int main(int argc, char **argv, char **envp) {
2872 } 3009 }
2873 EUID_ASSERT(); 3010 EUID_ASSERT();
2874 3011
2875 // create the parent-child communication pipe
2876 if (pipe(parent_to_child_fds) < 0)
2877 errExit("pipe");
2878 if (pipe(child_to_parent_fds) < 0)
2879 errExit("pipe");
2880
2881 if (arg_noroot && arg_overlay) { 3012 if (arg_noroot && arg_overlay) {
2882 fwarning("--overlay and --noroot are mutually exclusive, noroot disabled\n"); 3013 fwarning("--overlay and --noroot are mutually exclusive, noroot disabled\n");
2883 arg_noroot = 0; 3014 arg_noroot = 0;
@@ -2890,7 +3021,7 @@ int main(int argc, char **argv, char **envp) {
2890 3021
2891 // set name and x11 run files 3022 // set name and x11 run files
2892 EUID_ROOT(); 3023 EUID_ROOT();
2893 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 3024 lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
2894 if (lockfd_directory != -1) { 3025 if (lockfd_directory != -1) {
2895 int rv = fchown(lockfd_directory, 0, 0); 3026 int rv = fchown(lockfd_directory, 0, 0);
2896 (void) rv; 3027 (void) rv;
@@ -2919,6 +3050,12 @@ int main(int argc, char **argv, char **envp) {
2919 } 3050 }
2920#endif 3051#endif
2921 3052
3053 // create the parent-child communication pipe
3054 if (pipe2(parent_to_child_fds, O_CLOEXEC) < 0)
3055 errExit("pipe");
3056 if (pipe2(child_to_parent_fds, O_CLOEXEC) < 0)
3057 errExit("pipe");
3058
2922 // clone environment 3059 // clone environment
2923 int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; 3060 int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD;
2924 3061
@@ -2975,9 +3112,9 @@ int main(int argc, char **argv, char **envp) {
2975 network_main(child); 3112 network_main(child);
2976 if (arg_debug) 3113 if (arg_debug)
2977 printf("Host network configured\n"); 3114 printf("Host network configured\n");
2978#ifdef HAVE_GCOV 3115
2979 __gcov_flush(); 3116 __gcov_flush();
2980#endif 3117
2981 _exit(0); 3118 _exit(0);
2982 } 3119 }
2983 3120
@@ -2987,100 +3124,163 @@ int main(int argc, char **argv, char **envp) {
2987 } 3124 }
2988 EUID_ASSERT(); 3125 EUID_ASSERT();
2989 3126
2990 // close each end of the unused pipes 3127 // close each end of the unused pipes
2991 close(parent_to_child_fds[0]); 3128 close(parent_to_child_fds[0]);
2992 close(child_to_parent_fds[1]); 3129 close(child_to_parent_fds[1]);
2993 3130
2994 // notify child that base setup is complete 3131 // notify child that base setup is complete
2995 notify_other(parent_to_child_fds[1]); 3132 notify_other(parent_to_child_fds[1]);
2996 3133
2997 // wait for child to create new user namespace with CLONE_NEWUSER 3134 // wait for child to create new user namespace with CLONE_NEWUSER
2998 wait_for_other(child_to_parent_fds[0]); 3135 wait_for_other(child_to_parent_fds[0]);
2999 close(child_to_parent_fds[0]); 3136 close(child_to_parent_fds[0]);
3000 3137
3001 if (arg_noroot) { 3138 if (arg_noroot) {
3002 // update the UID and GID maps in the new child user namespace 3139 // update the UID and GID maps in the new child user namespace
3003 // uid 3140 // uid
3004 char *map_path; 3141 char *map_path;
3005 if (asprintf(&map_path, "/proc/%d/uid_map", child) == -1) 3142 if (asprintf(&map_path, "/proc/%d/uid_map", child) == -1)
3006 errExit("asprintf"); 3143 errExit("asprintf");
3007 3144
3008 char *map; 3145 char *map;
3009 uid_t uid = getuid(); 3146 uid_t uid = getuid();
3010 if (asprintf(&map, "%d %d 1", uid, uid) == -1) 3147 if (asprintf(&map, "%d %d 1", uid, uid) == -1)
3011 errExit("asprintf"); 3148 errExit("asprintf");
3012 EUID_ROOT(); 3149 EUID_ROOT();
3013 update_map(map, map_path); 3150 update_map(map, map_path);
3014 EUID_USER(); 3151 EUID_USER();
3015 free(map); 3152 free(map);
3016 free(map_path); 3153 free(map_path);
3017 3154
3018 // gid file 3155 // gid file
3019 if (asprintf(&map_path, "/proc/%d/gid_map", child) == -1) 3156 if (asprintf(&map_path, "/proc/%d/gid_map", child) == -1)
3020 errExit("asprintf"); 3157 errExit("asprintf");
3021 char gidmap[1024]; 3158 char gidmap[1024];
3022 char *ptr = gidmap; 3159 char *ptr = gidmap;
3023 *ptr = '\0'; 3160 *ptr = '\0';
3024 3161
3025 // add user group 3162 // add user group
3026 gid_t gid = getgid(); 3163 gid_t gid = getgid();
3027 sprintf(ptr, "%d %d 1\n", gid, gid); 3164 sprintf(ptr, "%d %d 1\n", gid, gid);
3028 ptr += strlen(ptr); 3165 ptr += strlen(ptr);
3029 3166
3030 if (!arg_nogroups) { 3167 gid_t g;
3031 // add firejail group 3168 if (!arg_nogroups || !check_can_drop_all_groups()) {
3032 gid_t g = get_group_id("firejail"); 3169 // add audio group
3033 if (g) { 3170 if (!arg_nosound) {
3034 sprintf(ptr, "%d %d 1\n", g, g); 3171 g = get_group_id("audio");
3035 ptr += strlen(ptr); 3172 if (g) {
3036 } 3173 sprintf(ptr, "%d %d 1\n", g, g);
3037 3174 ptr += strlen(ptr);
3038 // add tty group 3175 }
3039 g = get_group_id("tty"); 3176 }
3040 if (g) { 3177
3041 sprintf(ptr, "%d %d 1\n", g, g); 3178 // add video group
3042 ptr += strlen(ptr); 3179 if (!arg_novideo) {
3043 } 3180 g = get_group_id("video");
3044 3181 if (g) {
3045 // add audio group 3182 sprintf(ptr, "%d %d 1\n", g, g);
3046 g = get_group_id("audio"); 3183 ptr += strlen(ptr);
3047 if (g) { 3184 }
3048 sprintf(ptr, "%d %d 1\n", g, g); 3185 }
3049 ptr += strlen(ptr); 3186
3050 } 3187 // add render/vglusers group
3051 3188 if (!arg_no3d) {
3052 // add video group 3189 g = get_group_id("render");
3053 g = get_group_id("video"); 3190 if (g) {
3054 if (g) { 3191 sprintf(ptr, "%d %d 1\n", g, g);
3055 sprintf(ptr, "%d %d 1\n", g, g); 3192 ptr += strlen(ptr);
3056 ptr += strlen(ptr); 3193 }
3057 } 3194 g = get_group_id("vglusers");
3058 3195 if (g) {
3059 // add games group 3196 sprintf(ptr, "%d %d 1\n", g, g);
3060 g = get_group_id("games"); 3197 ptr += strlen(ptr);
3061 if (g) { 3198 }
3062 sprintf(ptr, "%d %d 1\n", g, g); 3199 }
3063 } 3200
3064 } 3201 // add lp group
3065 3202 if (!arg_noprinters) {
3066 EUID_ROOT(); 3203 g = get_group_id("lp");
3067 update_map(gidmap, map_path); 3204 if (g) {
3068 EUID_USER(); 3205 sprintf(ptr, "%d %d 1\n", g, g);
3069 free(map_path); 3206 ptr += strlen(ptr);
3070 } 3207 }
3208 }
3209
3210 // add cdrom/optical groups
3211 if (!arg_nodvd) {
3212 g = get_group_id("cdrom");
3213 if (g) {
3214 sprintf(ptr, "%d %d 1\n", g, g);
3215 ptr += strlen(ptr);
3216 }
3217 g = get_group_id("optical");
3218 if (g) {
3219 sprintf(ptr, "%d %d 1\n", g, g);
3220 ptr += strlen(ptr);
3221 }
3222 }
3223
3224 // add input group
3225 if (!arg_noinput) {
3226 g = get_group_id("input");
3227 if (g) {
3228 sprintf(ptr, "%d %d 1\n", g, g);
3229 ptr += strlen(ptr);
3230 }
3231 }
3232 }
3233
3234 if (!arg_nogroups) {
3235 // add firejail group
3236 g = get_group_id("firejail");
3237 if (g) {
3238 sprintf(ptr, "%d %d 1\n", g, g);
3239 ptr += strlen(ptr);
3240 }
3241
3242 // add tty group
3243 g = get_group_id("tty");
3244 if (g) {
3245 sprintf(ptr, "%d %d 1\n", g, g);
3246 ptr += strlen(ptr);
3247 }
3248
3249 // add games group
3250 g = get_group_id("games");
3251 if (g) {
3252 sprintf(ptr, "%d %d 1\n", g, g);
3253 }
3254 }
3255
3256 EUID_ROOT();
3257 update_map(gidmap, map_path);
3258 EUID_USER();
3259 free(map_path);
3260 }
3071 EUID_ASSERT(); 3261 EUID_ASSERT();
3072 3262
3073 // notify child that UID/GID mapping is complete 3263 // notify child that UID/GID mapping is complete
3074 notify_other(parent_to_child_fds[1]); 3264 notify_other(parent_to_child_fds[1]);
3075 close(parent_to_child_fds[1]); 3265 close(parent_to_child_fds[1]);
3076 3266
3077 EUID_ROOT(); 3267 EUID_ROOT();
3078 if (lockfd_network != -1) { 3268 if (lockfd_network != -1) {
3079 flock(lockfd_network, LOCK_UN); 3269 flock(lockfd_network, LOCK_UN);
3080 close(lockfd_network); 3270 close(lockfd_network);
3081 } 3271 }
3082 EUID_USER(); 3272 EUID_USER();
3083 3273
3274 // lock netfilter firewall
3275 if (arg_netlock) {
3276 char *cmd;
3277 if (asprintf(&cmd, "firejail --netlock=%d&", getpid()) == -1)
3278 errExit("asprintf");
3279 int rv = system(cmd);
3280 (void) rv;
3281 free(cmd);
3282 }
3283
3084 int status = 0; 3284 int status = 0;
3085 //***************************** 3285 //*****************************
3086 // following code is signal-safe 3286 // following code is signal-safe
@@ -3098,35 +3298,16 @@ int main(int argc, char **argv, char **envp) {
3098 // end of signal-safe code 3298 // end of signal-safe code
3099 //***************************** 3299 //*****************************
3100 3300
3101#if 0
3102// at this point the sandbox was closed and we are on our way out
3103// it would make sense to move this before waitpid above to free some memory
3104// crash for now as of issue #3662 from dhcp code
3105 // free globals
3106 if (cfg.profile) {
3107 ProfileEntry *prf = cfg.profile;
3108 while (prf != NULL) {
3109 ProfileEntry *next = prf->next;
3110printf("data #%s#\n", prf->data);
3111 if (prf->data)
3112 free(prf->data);
3113printf("link #%s#\n", prf->link);
3114 if (prf->link)
3115 free(prf->link);
3116 free(prf);
3117 prf = next;
3118 }
3119 }
3120#endif
3121 3301
3122 3302
3123 if (WIFEXITED(status)){ 3303 if (WIFEXITED(status)){
3124 myexit(WEXITSTATUS(status)); 3304 myexit(WEXITSTATUS(status));
3125 } else if (WIFSIGNALED(status)) { 3305 } else if (WIFSIGNALED(status)) {
3126 myexit(WTERMSIG(status)); 3306 // distinguish fatal signals by adding 128
3307 myexit(128 + WTERMSIG(status));
3127 } else { 3308 } else {
3128 myexit(0); 3309 myexit(1);
3129 } 3310 }
3130 3311
3131 return 0; 3312 return 1;
3132} 3313}
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c
index a700729d3..56c0bda30 100644
--- a/src/firejail/mountinfo.c
+++ b/src/firejail/mountinfo.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,10 +19,11 @@
19*/ 19*/
20 20
21#include "firejail.h" 21#include "firejail.h"
22#include <errno.h>
22 23
23#include <fcntl.h> 24#include <fcntl.h>
24#ifndef O_PATH 25#ifndef O_PATH
25# define O_PATH 010000000 26#define O_PATH 010000000
26#endif 27#endif
27 28
28#define MAX_BUF 4096 29#define MAX_BUF 4096
@@ -32,43 +33,38 @@ static MountData mdata;
32 33
33 34
34// Convert octal escape sequence to decimal value 35// Convert octal escape sequence to decimal value
35static int read_oct(const char *path) { 36static unsigned read_oct(char *s) {
36 int dec = 0; 37 assert(s[0] == '\\');
37 int digit, i; 38 s++;
38 // there are always exactly three octal digits 39
39 for (i = 1; i < 4; i++) { 40 int i;
40 digit = *(path + i); 41 for (i = 0; i < 3; i++)
41 if (digit < '0' || digit > '7') { 42 assert(s[i] >= '0' && s[i] <= '7');
42 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); 43
43 exit(1); 44 return ((s[0] - '0') << 6 |
44 } 45 (s[1] - '0') << 3 |
45 dec = (dec << 3) + (digit - '0'); 46 (s[2] - '0') << 0);
46 }
47 return dec;
48} 47}
49 48
50// Restore empty spaces in pathnames extracted from /proc/self/mountinfo 49// Restore empty spaces in pathnames extracted from /proc/self/mountinfo
51static void unmangle_path(char *path) { 50static void unmangle_path(char *path) {
52 char *p = strchr(path, '\\'); 51 char *r = strchr(path, '\\');
53 if (p && read_oct(p) == ' ') { 52 if (!r)
54 *p = ' '; 53 return;
55 int i = 3; 54
56 do { 55 char *w = r;
57 p++; 56 do {
58 if (*(p + i) == '\\' && read_oct(p + i) == ' ') { 57 while (*r == '\\') {
59 *p = ' '; 58 *w++ = read_oct(r);
60 i += 3; 59 r += 4;
61 } 60 }
62 else 61 *w++ = *r;
63 *p = *(p + i); 62 } while (*r++);
64 } while (*p);
65 }
66} 63}
67 64
68// Parse a line from /proc/self/mountinfo, 65// Parse a line from /proc/self/mountinfo,
69// the function does an exit(1) if anything goes wrong. 66// the function does an exit(1) if anything goes wrong.
70static void parse_line(char *line, MountData *output) { 67static void parse_line(char *line, MountData *output) {
71 assert(line && output);
72 memset(output, 0, sizeof(*output)); 68 memset(output, 0, sizeof(*output));
73 // extract mount id, filesystem name, directory and filesystem types 69 // extract mount id, filesystem name, directory and filesystem types
74 // examples: 70 // examples:
@@ -86,8 +82,6 @@ static void parse_line(char *line, MountData *output) {
86 char *ptr = strtok(line, " "); 82 char *ptr = strtok(line, " ");
87 if (!ptr) 83 if (!ptr)
88 goto errexit; 84 goto errexit;
89 if (ptr != line)
90 goto errexit;
91 output->mountid = atoi(ptr); 85 output->mountid = atoi(ptr);
92 int cnt = 1; 86 int cnt = 1;
93 87
@@ -108,10 +102,9 @@ static void parse_line(char *line, MountData *output) {
108 ptr = strtok(NULL, " "); 102 ptr = strtok(NULL, " ");
109 if (!ptr) 103 if (!ptr)
110 goto errexit; 104 goto errexit;
111 output->fstype = ptr++; 105 output->fstype = ptr;
112 106
113 107 if (output->mountid < 0 ||
114 if (output->mountid == 0 ||
115 output->fsname == NULL || 108 output->fsname == NULL ||
116 output->dir == NULL || 109 output->dir == NULL ||
117 output->fstype == NULL) 110 output->fstype == NULL)
@@ -151,108 +144,118 @@ MountData *get_last_mount(void) {
151 return &mdata; 144 return &mdata;
152} 145}
153 146
154// Extract the mount id from /proc/self/fdinfo and return it. 147// Returns mount id, or -1 if fd refers to a procfs or sysfs file
155int get_mount_id(const char *path) { 148static int get_mount_id_from_handle(int fd) {
156 assert(path); 149 char *proc;
150 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
151 errExit("asprintf");
152
153 struct file_handle *fh = malloc(sizeof *fh);
154 if (!fh)
155 errExit("malloc");
156 fh->handle_bytes = 0;
157
158 int rv = -1;
159 int tmp;
160 if (name_to_handle_at(-1, proc, fh, &tmp, AT_SYMLINK_FOLLOW) != -1) {
161 fprintf(stderr, "Error: unexpected result from name_to_handle_at\n");
162 exit(1);
163 }
164 if (errno == EOVERFLOW && fh->handle_bytes)
165 rv = tmp;
157 166
158 int fd = open(path, O_PATH|O_CLOEXEC); 167 free(proc);
159 if (fd == -1) 168 free(fh);
160 return -1; 169 return rv;
170}
161 171
162 char *fdinfo; 172// Returns mount id, or -1 on kernels < 3.15
163 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) 173static int get_mount_id_from_fdinfo(int fd) {
174 char *proc;
175 if (asprintf(&proc, "/proc/self/fdinfo/%d", fd) == -1)
164 errExit("asprintf"); 176 errExit("asprintf");
165 FILE *fp = fopen(fdinfo, "re");
166 free(fdinfo);
167 if (!fp)
168 goto errexit;
169 177
170 // read the file 178 int called_as_root = 0;
179 if (geteuid() == 0)
180 called_as_root = 1;
181
182 if (called_as_root == 0)
183 EUID_ROOT();
184
185 FILE *fp = fopen(proc, "re");
186 if (!fp) {
187 fprintf(stderr, "Error: cannot read proc file\n");
188 exit(1);
189 }
190
191 if (called_as_root == 0)
192 EUID_USER();
193
194 int rv = -1;
171 char buf[MAX_BUF]; 195 char buf[MAX_BUF];
172 if (fgets(buf, MAX_BUF, fp) == NULL) 196 while (fgets(buf, MAX_BUF, fp)) {
173 goto errexit; 197 if (sscanf(buf, "mnt_id: %d", &rv) == 1)
174 do { 198 break;
175 if (strncmp(buf, "mnt_id:", 7) == 0) { 199 }
176 char *ptr = buf + 7;
177 while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
178 ptr++;
179 }
180 if (*ptr == '\0')
181 goto errexit;
182 fclose(fp);
183 close(fd);
184 return atoi(ptr);
185 }
186 } while (fgets(buf, MAX_BUF, fp));
187 200
188 // fallback, kernels older than 3.15 don't expose the mount id in this place 201 free(proc);
189 fclose(fp); 202 fclose(fp);
190 close(fd); 203 return rv;
191 return -2; 204}
192 205
193errexit: 206int get_mount_id(int fd) {
194 fprintf(stderr, "Error: cannot read proc file\n"); 207 int rv = get_mount_id_from_handle(fd);
195 exit(1); 208 if (rv < 0)
209 rv = get_mount_id_from_fdinfo(fd);
210 return rv;
196} 211}
197 212
198// Check /proc/self/mountinfo if path contains any mounts points. 213// Check /proc/self/mountinfo if path contains any mounts points.
199// Returns an array that can be iterated over for recursive remounting. 214// Returns an array that can be iterated over for recursive remounting.
200char **build_mount_array(const int mount_id, const char *path) { 215char **build_mount_array(const int mountid, const char *path) {
201 assert(path); 216 assert(path);
202 217
203 // open /proc/self/mountinfo
204 FILE *fp = fopen("/proc/self/mountinfo", "re"); 218 FILE *fp = fopen("/proc/self/mountinfo", "re");
205 if (!fp) { 219 if (!fp) {
206 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); 220 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n");
207 exit(1); 221 exit(1);
208 } 222 }
209 223
210 // array to be returned 224 // try to find line with mount id
211 size_t cnt = 0; 225 int found = 0;
226 MountData mntp;
227 char line[MAX_BUF];
228 while (fgets(line, MAX_BUF, fp)) {
229 parse_line(line, &mntp);
230 if (mntp.mountid == mountid) {
231 found = 1;
232 break;
233 }
234 }
235
236 if (!found) {
237 fclose(fp);
238 return NULL;
239 }
240
241 // allocate array
212 size_t size = 32; 242 size_t size = 32;
213 char **rv = malloc(size * sizeof(*rv)); 243 char **rv = malloc(size * sizeof(*rv));
214 if (!rv) 244 if (!rv)
215 errExit("malloc"); 245 errExit("malloc");
216 246
217 // read /proc/self/mountinfo 247 // add directory itself
218 size_t pathlen = strlen(path); 248 size_t cnt = 0;
219 char buf[MAX_BUF]; 249 rv[cnt] = strdup(path);
220 MountData mntp; 250 if (rv[cnt] == NULL)
221 int found = 0; 251 errExit("strdup");
222 252
223 if (fgets(buf, MAX_BUF, fp) == NULL) { 253 // and add all following mountpoints contained in this directory
224 fprintf(stderr, "Error: cannot read /proc/self/mountinfo\n"); 254 size_t pathlen = strlen(path);
225 exit(1); 255 while (fgets(line, MAX_BUF, fp)) {
226 } 256 parse_line(line, &mntp);
227 do { 257 if (strncmp(mntp.dir, path, pathlen) == 0 && mntp.dir[pathlen] == '/') {
228 parse_line(buf, &mntp); 258 if (++cnt == size) {
229 // find mount point with mount id
230 if (!found) {
231 if (mntp.mountid == mount_id) {
232 // give up if mount id has been reassigned,
233 // don't remount blacklisted path
234 if (strncmp(mntp.dir, path, strlen(mntp.dir)) ||
235 strstr(mntp.fsname, "firejail.ro.dir") ||
236 strstr(mntp.fsname, "firejail.ro.file"))
237 break;
238
239 rv[cnt] = strdup(path);
240 if (rv[cnt] == NULL)
241 errExit("strdup");
242 cnt++;
243 found = 1;
244 continue;
245 }
246 continue;
247 }
248 // from here on add all mount points below path,
249 // don't remount blacklisted paths
250 if (strncmp(mntp.dir, path, pathlen) == 0 &&
251 mntp.dir[pathlen] == '/' &&
252 strstr(mntp.fsname, "firejail.ro.dir") == NULL &&
253 strstr(mntp.fsname, "firejail.ro.file") == NULL) {
254
255 if (cnt == size) {
256 size *= 2; 259 size *= 2;
257 rv = realloc(rv, size * sizeof(*rv)); 260 rv = realloc(rv, size * sizeof(*rv));
258 if (!rv) 261 if (!rv)
@@ -261,18 +264,17 @@ char **build_mount_array(const int mount_id, const char *path) {
261 rv[cnt] = strdup(mntp.dir); 264 rv[cnt] = strdup(mntp.dir);
262 if (rv[cnt] == NULL) 265 if (rv[cnt] == NULL)
263 errExit("strdup"); 266 errExit("strdup");
264 cnt++;
265 } 267 }
266 } while (fgets(buf, MAX_BUF, fp)); 268 }
269 fclose(fp);
267 270
268 if (cnt == size) { 271 // end of array
269 size++; 272 if (++cnt == size) {
273 ++size;
270 rv = realloc(rv, size * sizeof(*rv)); 274 rv = realloc(rv, size * sizeof(*rv));
271 if (!rv) 275 if (!rv)
272 errExit("realloc"); 276 errExit("realloc");
273 } 277 }
274 rv[cnt] = NULL; // end of the array 278 rv[cnt] = NULL;
275
276 fclose(fp);
277 return rv; 279 return rv;
278} 280}
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c
index fc79dddec..5b49fe19a 100644
--- a/src/firejail/netfilter.c
+++ b/src/firejail/netfilter.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -24,6 +24,96 @@
24#include <sys/wait.h> 24#include <sys/wait.h>
25#include <fcntl.h> 25#include <fcntl.h>
26 26
27void netfilter_netlock(pid_t pid) {
28 EUID_ASSERT();
29
30 // give the sandbox a chance to start up before entering the network namespace
31 sleep(1);
32 enter_network_namespace(pid);
33
34 char *flog;
35 if (asprintf(&flog, "/run/firejail/network/%d-netlock", getpid()) == -1)
36 errExit("asprintf");
37 FILE *fp = fopen(flog, "w");
38 if (!fp)
39 errExit("fopen");
40 fclose(fp);
41
42 // try to find a X terminal
43 char *terminal = NULL;
44 if (access("/usr/bin/xterm", X_OK) == 0)
45 terminal = "/usr/bin/xterm";
46 else if (access("/usr/bin/lxterminal", X_OK) == 0)
47 terminal = "/usr/bin/lxterminal";
48 else if (access("/usr/bin/xfce4-terminal", X_OK) == 0)
49 terminal = "/usr/bin/xfce4-terminal";
50 else if (access("/usr/bin/konsole", X_OK) == 0)
51 terminal = "/usr/bin/konsole";
52// problem: newer gnome-terminal versions don't support -e command line option???
53// same for mate-terminal
54
55 if (isatty(STDIN_FILENO))
56 terminal = NULL;
57
58 if (terminal) {
59 pid_t p = fork();
60 if (p == -1)
61 ; // run without terminal logger
62 else if (p == 0) { // child
63 drop_privs(0);
64
65 char *cmd;
66 if (asprintf(&cmd, "%s -e \"%s/firejail/fnettrace --tail --log=%s\"", terminal, LIBDIR, flog) == -1)
67 errExit("asprintf");
68 int rv = system(cmd);
69 (void) rv;
70 exit(0);
71 }
72 }
73
74 char *cmd;
75 if (asprintf(&cmd, "%s/firejail/fnettrace --netfilter --log=%s", LIBDIR, flog) == -1)
76 errExit("asprintf");
77 free(flog);
78
79 //************************
80 // build command
81 //************************
82 char *arg[4];
83 arg[0] = "/bin/sh";
84 arg[1] = "-c";
85 arg[2] = cmd;
86 arg[3] = NULL;
87 clearenv();
88 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
89 // it will never get here!!
90}
91
92void netfilter_trace(pid_t pid) {
93 EUID_ASSERT();
94
95 // a pid of 0 means the main system network namespace
96 if (pid)
97 enter_network_namespace(pid);
98
99 char *cmd;
100 if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1)
101 errExit("asprintf");
102
103 //************************
104 // build command
105 //************************
106 char *arg[4];
107 arg[0] = "/bin/sh";
108 arg[1] = "-c";
109 arg[2] = cmd;
110 arg[3] = NULL;
111
112 clearenv();
113 sbox_exec_v(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, arg);
114 // it will never get here!!
115}
116
27void check_netfilter_file(const char *fname) { 117void check_netfilter_file(const char *fname) {
28 EUID_ASSERT(); 118 EUID_ASSERT();
29 119
diff --git a/src/firejail/netns.c b/src/firejail/netns.c
index b5d6fb636..c72c009ae 100644
--- a/src/firejail/netns.c
+++ b/src/firejail/netns.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2020-2021 Firejail Authors 2 * Copyright (C) 2020-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firejail/network.c b/src/firejail/network.c
index f7142cefd..e631745fb 100644
--- a/src/firejail/network.c
+++ b/src/firejail/network.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -217,7 +217,7 @@ int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) {
217 217
218#define BUFSIZE 1024 218#define BUFSIZE 1024
219uint32_t network_get_defaultgw(void) { 219uint32_t network_get_defaultgw(void) {
220 FILE *fp = fopen("/proc/self/net/route", "r"); 220 FILE *fp = fopen("/proc/self/net/route", "re");
221 if (!fp) 221 if (!fp)
222 errExit("fopen"); 222 errExit("fopen");
223 223
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c
index ee3c00872..dd66ecc55 100644
--- a/src/firejail/network_main.c
+++ b/src/firejail/network_main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -292,7 +292,7 @@ void net_dns_print(pid_t pid) {
292 errExit("chdir"); 292 errExit("chdir");
293 293
294 // access /etc/resolv.conf 294 // access /etc/resolv.conf
295 FILE *fp = fopen("/etc/resolv.conf", "r"); 295 FILE *fp = fopen("/etc/resolv.conf", "re");
296 if (!fp) { 296 if (!fp) {
297 fprintf(stderr, "Error: cannot access /etc/resolv.conf\n"); 297 fprintf(stderr, "Error: cannot access /etc/resolv.conf\n");
298 exit(1); 298 exit(1);
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c
index 60a82821e..c57d397ef 100644
--- a/src/firejail/no_sandbox.c
+++ b/src/firejail/no_sandbox.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -20,6 +20,7 @@
20#include "firejail.h" 20#include "firejail.h"
21#include <sys/types.h> 21#include <sys/types.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <errno.h>
23#include <unistd.h> 24#include <unistd.h>
24#include <grp.h> 25#include <grp.h>
25 26
@@ -47,7 +48,8 @@ int check_namespace_virt(void) {
47 48
48 // check PID 1 container environment variable 49 // check PID 1 container environment variable
49 EUID_ROOT(); 50 EUID_ROOT();
50 FILE *fp = fopen("/proc/1/environ", "r"); 51 FILE *fp = fopen("/proc/1/environ", "re");
52 EUID_USER();
51 if (fp) { 53 if (fp) {
52 int c = 0; 54 int c = 0;
53 while (c != EOF) { 55 while (c != EOF) {
@@ -68,7 +70,6 @@ int check_namespace_virt(void) {
68 // found it 70 // found it
69 if (is_container(buf + 10)) { 71 if (is_container(buf + 10)) {
70 fclose(fp); 72 fclose(fp);
71 EUID_USER();
72 return 1; 73 return 1;
73 } 74 }
74 } 75 }
@@ -78,7 +79,6 @@ int check_namespace_virt(void) {
78 fclose(fp); 79 fclose(fp);
79 } 80 }
80 81
81 EUID_USER();
82 return 0; 82 return 0;
83} 83}
84 84
@@ -105,20 +105,15 @@ int check_kernel_procs(void) {
105 // look at the first 10 processes 105 // look at the first 10 processes
106 // if a kernel process is found, return 1 106 // if a kernel process is found, return 1
107 for (i = 1; i <= 10; i++) { 107 for (i = 1; i <= 10; i++) {
108 struct stat s;
109 char *fname; 108 char *fname;
110 if (asprintf(&fname, "/proc/%d/comm", i) == -1) 109 if (asprintf(&fname, "/proc/%d/comm", i) == -1)
111 errExit("asprintf"); 110 errExit("asprintf");
112 if (stat(fname, &s) == -1) {
113 free(fname);
114 continue;
115 }
116 111
117 // open file 112 // open file
118 /* coverity[toctou] */ 113 FILE *fp = fopen(fname, "re");
119 FILE *fp = fopen(fname, "r");
120 if (!fp) { 114 if (!fp) {
121 fwarning("cannot open %s\n", fname); 115 if (errno != ENOENT)
116 fwarning("cannot open %s\n", fname);
122 free(fname); 117 free(fname);
123 continue; 118 continue;
124 } 119 }
@@ -208,7 +203,7 @@ void run_no_sandbox(int argc, char **argv) {
208 // force --shell=none in order to not break firecfg symbolic links 203 // force --shell=none in order to not break firecfg symbolic links
209 arg_shell_none = 1; 204 arg_shell_none = 1;
210 205
211 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 206 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, true);
212 } 207 }
213 208
214 fwarning("an existing sandbox was detected. " 209 fwarning("an existing sandbox was detected. "
diff --git a/src/firejail/output.c b/src/firejail/output.c
index 835dff2db..f9df9f3d4 100644
--- a/src/firejail/output.c
+++ b/src/firejail/output.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -50,13 +50,21 @@ void check_output(int argc, char **argv) {
50 if (!outindex) 50 if (!outindex)
51 return; 51 return;
52 52
53
54 // check filename
55 drop_privs(0); 53 drop_privs(0);
56 char *outfile = argv[outindex]; 54 char *outfile = argv[outindex];
57 outfile += (enable_stderr)? 16:9; 55 outfile += (enable_stderr)? 16:9;
56
57 // check filename
58 invalid_filename(outfile, 0); // no globbing 58 invalid_filename(outfile, 0); // no globbing
59 59
60 // expand user home directory
61 if (outfile[0] == '~') {
62 char *full;
63 if (asprintf(&full, "%s%s", cfg.homedir, outfile + 1) == -1)
64 errExit("asprintf");
65 outfile = full;
66 }
67
60 // do not accept directories, links, and files with ".." 68 // do not accept directories, links, and files with ".."
61 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) { 69 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) {
62 fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n"); 70 fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n");
diff --git a/src/firejail/paths.c b/src/firejail/paths.c
index b800fa944..6d62c9004 100644
--- a/src/firejail/paths.c
+++ b/src/firejail/paths.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -136,7 +136,7 @@ int program_in_path(const char *program) {
136 // ('x' permission means something different for directories). 136 // ('x' permission means something different for directories).
137 // exec follows symlinks, so use stat, not lstat. 137 // exec follows symlinks, so use stat, not lstat.
138 struct stat st; 138 struct stat st;
139 if (stat(scratch, &st)) { 139 if (stat_as_user(scratch, &st)) {
140 perror(scratch); 140 perror(scratch);
141 exit(1); 141 exit(1);
142 } 142 }
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index 7f602545d..da50e9a82 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -164,7 +164,7 @@ void preproc_clean_run(void) {
164 int max_pids=32769; 164 int max_pids=32769;
165 int start_pid = 100; 165 int start_pid = 100;
166 // extract real max_pids 166 // extract real max_pids
167 FILE *fp = fopen("/proc/sys/kernel/pid_max", "r"); 167 FILE *fp = fopen("/proc/sys/kernel/pid_max", "re");
168 if (fp) { 168 if (fp) {
169 int val; 169 int val;
170 if (fscanf(fp, "%d", &val) == 1) { 170 if (fscanf(fp, "%d", &val) == 1) {
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 2ea32b665..5bc77263a 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,10 +18,12 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/gcov_wrapper.h"
21#include "../include/seccomp.h" 22#include "../include/seccomp.h"
22#include "../include/syscall.h" 23#include "../include/syscall.h"
23#include <dirent.h> 24#include <dirent.h>
24#include <sys/stat.h> 25#include <sys/stat.h>
26
25extern char *xephyr_screen; 27extern char *xephyr_screen;
26 28
27#define MAX_READ 8192 // line buffer for profile files 29#define MAX_READ 8192 // line buffer for profile files
@@ -70,6 +72,7 @@ static int profile_find(const char *name, const char *dir, int add_ext) {
70// search and read the profile specified by name from firejail directories 72// search and read the profile specified by name from firejail directories
71// return 1 if a profile was found 73// return 1 if a profile was found
72int profile_find_firejail(const char *name, int add_ext) { 74int profile_find_firejail(const char *name, int add_ext) {
75#ifndef HAVE_ONLY_SYSCFG_PROFILES
73 // look for a profile in ~/.config/firejail directory 76 // look for a profile in ~/.config/firejail directory
74 char *usercfgdir; 77 char *usercfgdir;
75 if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1) 78 if (asprintf(&usercfgdir, "%s/.config/firejail", cfg.homedir) == -1)
@@ -82,6 +85,9 @@ int profile_find_firejail(const char *name, int add_ext) {
82 rv = profile_find(name, SYSCONFDIR, add_ext); 85 rv = profile_find(name, SYSCONFDIR, add_ext);
83 86
84 return rv; 87 return rv;
88#else
89 return profile_find(name, SYSCONFDIR, add_ext);
90#endif
85} 91}
86 92
87//*************************************************** 93//***************************************************
@@ -173,6 +179,10 @@ static int check_allow_drm(void) {
173 return checkcfg(CFG_BROWSER_ALLOW_DRM) != 0; 179 return checkcfg(CFG_BROWSER_ALLOW_DRM) != 0;
174} 180}
175 181
182static int check_allow_tray(void) {
183 return checkcfg(CFG_ALLOW_TRAY) != 0;
184}
185
176Cond conditionals[] = { 186Cond conditionals[] = {
177 {"HAS_APPIMAGE", check_appimage}, 187 {"HAS_APPIMAGE", check_appimage},
178 {"HAS_NET", check_netoptions}, 188 {"HAS_NET", check_netoptions},
@@ -182,6 +192,7 @@ Cond conditionals[] = {
182 {"HAS_X11", check_x11}, 192 {"HAS_X11", check_x11},
183 {"BROWSER_DISABLE_U2F", check_disable_u2f}, 193 {"BROWSER_DISABLE_U2F", check_disable_u2f},
184 {"BROWSER_ALLOW_DRM", check_allow_drm}, 194 {"BROWSER_ALLOW_DRM", check_allow_drm},
195 {"ALLOW_TRAY", check_allow_tray},
185 { NULL, NULL } 196 { NULL, NULL }
186}; 197};
187 198
@@ -283,6 +294,15 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
283 return 0; 294 return 0;
284 } 295 }
285 296
297 if (strncmp(ptr, "keep-fd ", 8) == 0) {
298 if (strcmp(ptr + 8, "all") == 0)
299 arg_keep_fd_all = 1;
300 else {
301 const char *add = ptr + 8;
302 profile_list_augment(&cfg.keep_fd, add);
303 }
304 return 0;
305 }
286 if (strncmp(ptr, "xephyr-screen ", 14) == 0) { 306 if (strncmp(ptr, "xephyr-screen ", 14) == 0) {
287#ifdef HAVE_X11 307#ifdef HAVE_X11
288 if (checkcfg(CFG_X11)) { 308 if (checkcfg(CFG_X11)) {
@@ -373,6 +393,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
373#endif 393#endif
374 return 0; 394 return 0;
375 } 395 }
396 else if (strcmp(ptr, "tab") == 0) {
397 arg_tab = 1;
398 return 0;
399 }
376 else if (strcmp(ptr, "private-cwd") == 0) { 400 else if (strcmp(ptr, "private-cwd") == 0) {
377 cfg.cwd = NULL; 401 cfg.cwd = NULL;
378 arg_private_cwd = 1; 402 arg_private_cwd = 1;
@@ -409,13 +433,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
409 return 0; 433 return 0;
410 } 434 }
411 else if (strcmp(ptr, "nogroups") == 0) { 435 else if (strcmp(ptr, "nogroups") == 0) {
412 // nvidia cards require video group; disable nogroups 436 arg_nogroups = 1;
413 if (access("/dev/nvidiactl", R_OK) == 0 && arg_no3d == 0) {
414 fwarning("Warning: NVIDIA card detected, nogroups command disabled\n");
415 arg_nogroups = 0;
416 }
417 else
418 arg_nogroups = 1;
419 return 0; 437 return 0;
420 } 438 }
421 else if (strcmp(ptr, "nosound") == 0) { 439 else if (strcmp(ptr, "nosound") == 0) {
@@ -423,7 +441,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
423 return 0; 441 return 0;
424 } 442 }
425 else if (strcmp(ptr, "noautopulse") == 0) { 443 else if (strcmp(ptr, "noautopulse") == 0) {
426 arg_noautopulse = 1; 444 arg_keep_config_pulse = 1;
427 return 0; 445 return 0;
428 } 446 }
429 else if (strcmp(ptr, "notv") == 0) { 447 else if (strcmp(ptr, "notv") == 0) {
@@ -442,6 +460,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
442 arg_no3d = 1; 460 arg_no3d = 1;
443 return 0; 461 return 0;
444 } 462 }
463 else if (strcmp(ptr, "noprinters") == 0) {
464 arg_noprinters = 1;
465 profile_add("blacklist /dev/lp*");
466 profile_add("blacklist /run/cups/cups.sock");
467 return 0;
468 }
445 else if (strcmp(ptr, "noinput") == 0) { 469 else if (strcmp(ptr, "noinput") == 0) {
446 arg_noinput = 1; 470 arg_noinput = 1;
447 return 0; 471 return 0;
@@ -628,7 +652,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
628#endif 652#endif
629 return 0; 653 return 0;
630 } 654 }
631 else if (strncmp(ptr, "netns ", 6) == 0) { 655 else if (strncmp(ptr, "netns ", 6) == 0) {
632#ifdef HAVE_NETWORK 656#ifdef HAVE_NETWORK
633 if (checkcfg(CFG_NETWORK)) { 657 if (checkcfg(CFG_NETWORK)) {
634 arg_netns = ptr + 6; 658 arg_netns = ptr + 6;
@@ -979,10 +1003,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
979 warning_feature_disabled("seccomp"); 1003 warning_feature_disabled("seccomp");
980 return 0; 1004 return 0;
981 } 1005 }
982 if (strncmp(ptr, "seccomp.32.drop ", 13) == 0) { 1006 if (strncmp(ptr, "seccomp.32.drop ", 16) == 0) {
983 if (checkcfg(CFG_SECCOMP)) { 1007 if (checkcfg(CFG_SECCOMP)) {
984 arg_seccomp32 = 1; 1008 arg_seccomp32 = 1;
985 cfg.seccomp_list_drop32 = seccomp_check_list(ptr + 13); 1009 cfg.seccomp_list_drop32 = seccomp_check_list(ptr + 16);
986 } 1010 }
987 else 1011 else
988 warning_feature_disabled("seccomp"); 1012 warning_feature_disabled("seccomp");
@@ -999,10 +1023,10 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
999 warning_feature_disabled("seccomp"); 1023 warning_feature_disabled("seccomp");
1000 return 0; 1024 return 0;
1001 } 1025 }
1002 if (strncmp(ptr, "seccomp.32.keep ", 13) == 0) { 1026 if (strncmp(ptr, "seccomp.32.keep ", 16) == 0) {
1003 if (checkcfg(CFG_SECCOMP)) { 1027 if (checkcfg(CFG_SECCOMP)) {
1004 arg_seccomp32 = 1; 1028 arg_seccomp32 = 1;
1005 cfg.seccomp_list_keep32 = seccomp_check_list(ptr + 13); 1029 cfg.seccomp_list_keep32 = seccomp_check_list(ptr + 16);
1006 } 1030 }
1007 else 1031 else
1008 warning_feature_disabled("seccomp"); 1032 warning_feature_disabled("seccomp");
@@ -1099,7 +1123,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1099 else if (cfg.dns4 == NULL) 1123 else if (cfg.dns4 == NULL)
1100 cfg.dns4 = dns; 1124 cfg.dns4 = dns;
1101 else { 1125 else {
1102 fwarning("Warning: up to 4 DNS servers can be specified, %s ignored\n", dns); 1126 fwarning("up to 4 DNS servers can be specified, %s ignored\n", dns);
1103 free(dns); 1127 free(dns);
1104 } 1128 }
1105 return 0; 1129 return 0;
@@ -1122,8 +1146,14 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1122 1146
1123 // cgroup 1147 // cgroup
1124 if (strncmp(ptr, "cgroup ", 7) == 0) { 1148 if (strncmp(ptr, "cgroup ", 7) == 0) {
1125 if (checkcfg(CFG_CGROUP)) 1149 if (checkcfg(CFG_CGROUP)) {
1126 set_cgroup(ptr + 7); 1150 cfg.cgroup = strdup(ptr + 7);
1151 if (!cfg.cgroup)
1152 errExit("strdup");
1153
1154 check_cgroup_file(cfg.cgroup);
1155 set_cgroup(cfg.cgroup, getpid());
1156 }
1127 else 1157 else
1128 warning_feature_disabled("cgroup"); 1158 warning_feature_disabled("cgroup");
1129 return 0; 1159 return 0;
@@ -1143,6 +1173,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1143 arg_machineid = 1; 1173 arg_machineid = 1;
1144 return 0; 1174 return 0;
1145 } 1175 }
1176
1177 if (strcmp(ptr, "keep-config-pulse") == 0) {
1178 arg_keep_config_pulse = 1;
1179 return 0;
1180 }
1181
1146 // writable-var 1182 // writable-var
1147 if (strcmp(ptr, "writable-var") == 0) { 1183 if (strcmp(ptr, "writable-var") == 0) {
1148 arg_writable_var = 1; 1184 arg_writable_var = 1;
@@ -1269,56 +1305,69 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1269 1305
1270 // private /etc list of files and directories 1306 // private /etc list of files and directories
1271 if (strncmp(ptr, "private-etc ", 12) == 0) { 1307 if (strncmp(ptr, "private-etc ", 12) == 0) {
1272 if (arg_writable_etc) { 1308 if (checkcfg(CFG_PRIVATE_ETC)) {
1273 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); 1309 if (arg_writable_etc) {
1274 exit(1); 1310 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
1275 } 1311 exit(1);
1276 if (cfg.etc_private_keep) { 1312 }
1277 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 ) 1313 if (cfg.etc_private_keep) {
1278 errExit("asprintf"); 1314 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 )
1279 } else { 1315 errExit("asprintf");
1280 cfg.etc_private_keep = ptr + 12; 1316 } else {
1317 cfg.etc_private_keep = ptr + 12;
1318 }
1319 arg_private_etc = 1;
1281 } 1320 }
1282 arg_private_etc = 1; 1321 else
1283 1322 warning_feature_disabled("private-etc");
1284 return 0; 1323 return 0;
1285 } 1324 }
1286 1325
1287 // private /opt list of files and directories 1326 // private /opt list of files and directories
1288 if (strncmp(ptr, "private-opt ", 12) == 0) { 1327 if (strncmp(ptr, "private-opt ", 12) == 0) {
1289 if (cfg.opt_private_keep) { 1328 if (checkcfg(CFG_PRIVATE_OPT)) {
1290 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 ) 1329 if (cfg.opt_private_keep) {
1291 errExit("asprintf"); 1330 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 )
1292 } else { 1331 errExit("asprintf");
1293 cfg.opt_private_keep = ptr + 12; 1332 } else {
1333 cfg.opt_private_keep = ptr + 12;
1334 }
1335 arg_private_opt = 1;
1294 } 1336 }
1295 arg_private_opt = 1; 1337 else
1296 1338 warning_feature_disabled("private-opt");
1297 return 0; 1339 return 0;
1298 } 1340 }
1299 1341
1300 // private /srv list of files and directories 1342 // private /srv list of files and directories
1301 if (strncmp(ptr, "private-srv ", 12) == 0) { 1343 if (strncmp(ptr, "private-srv ", 12) == 0) {
1302 if (cfg.srv_private_keep) { 1344 if (checkcfg(CFG_PRIVATE_SRV)) {
1303 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 ) 1345 if (cfg.srv_private_keep) {
1304 errExit("asprintf"); 1346 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 )
1305 } else { 1347 errExit("asprintf");
1306 cfg.srv_private_keep = ptr + 12; 1348 } else {
1349 cfg.srv_private_keep = ptr + 12;
1350 }
1351 arg_private_srv = 1;
1307 } 1352 }
1308 arg_private_srv = 1; 1353 else
1309 1354 warning_feature_disabled("private-srv");
1310 return 0; 1355 return 0;
1311 } 1356 }
1312 1357
1313 // private /bin list of files 1358 // private /bin list of files
1314 if (strncmp(ptr, "private-bin ", 12) == 0) { 1359 if (strncmp(ptr, "private-bin ", 12) == 0) {
1315 if (cfg.bin_private_keep) { 1360 if (checkcfg(CFG_PRIVATE_BIN)) {
1316 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 ) 1361 if (cfg.bin_private_keep) {
1317 errExit("asprintf"); 1362 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 )
1318 } else { 1363 errExit("asprintf");
1319 cfg.bin_private_keep = ptr + 12; 1364 } else {
1365 cfg.bin_private_keep = ptr + 12;
1366 }
1367 arg_private_bin = 1;
1320 } 1368 }
1321 arg_private_bin = 1; 1369 else
1370 warning_feature_disabled("private-bin");
1322 return 0; 1371 return 0;
1323 } 1372 }
1324 1373
@@ -1486,8 +1535,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1486 arg_rlimit_nproc = 1; 1535 arg_rlimit_nproc = 1;
1487 } 1536 }
1488 else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) { 1537 else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
1489 check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: "); 1538 cfg.rlimit_fsize = parse_arg_size(ptr + 13);
1490 sscanf(ptr + 13, "%llu", &cfg.rlimit_fsize); 1539 if (cfg.rlimit_fsize == 0) {
1540 perror("Error: invalid rlimit-fsize in profile file. Only use positive numbers and k, m or g suffix.");
1541 exit(1);
1542 }
1491 arg_rlimit_fsize = 1; 1543 arg_rlimit_fsize = 1;
1492 } 1544 }
1493 else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) { 1545 else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
@@ -1496,8 +1548,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1496 arg_rlimit_sigpending = 1; 1548 arg_rlimit_sigpending = 1;
1497 } 1549 }
1498 else if (strncmp(ptr, "rlimit-as ", 10) == 0) { 1550 else if (strncmp(ptr, "rlimit-as ", 10) == 0) {
1499 check_unsigned(ptr + 10, "Error: invalid rlimit in profile file: "); 1551 cfg.rlimit_as = parse_arg_size(ptr + 10);
1500 sscanf(ptr + 10, "%llu", &cfg.rlimit_as); 1552 if (cfg.rlimit_as == 0) {
1553 perror("Error: invalid rlimit-as in profile file. Only use positive numbers and k, m or g suffix.");
1554 exit(1);
1555 }
1501 arg_rlimit_as = 1; 1556 arg_rlimit_as = 1;
1502 } 1557 }
1503 else { 1558 else {
@@ -1554,6 +1609,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1554 return 0; 1609 return 0;
1555 } 1610 }
1556 1611
1612 if (strcmp(ptr, "deterministic-shutdown") == 0) {
1613 arg_deterministic_shutdown = 1;
1614 return 0;
1615 }
1616
1557 // rest of filesystem 1617 // rest of filesystem
1558 if (strncmp(ptr, "blacklist ", 10) == 0) 1618 if (strncmp(ptr, "blacklist ", 10) == 0)
1559 ptr += 10; 1619 ptr += 10;
@@ -1562,22 +1622,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1562 else if (strncmp(ptr, "noblacklist ", 12) == 0) 1622 else if (strncmp(ptr, "noblacklist ", 12) == 0)
1563 ptr += 12; 1623 ptr += 12;
1564 else if (strncmp(ptr, "whitelist ", 10) == 0) { 1624 else if (strncmp(ptr, "whitelist ", 10) == 0) {
1565#ifdef HAVE_WHITELIST 1625 arg_whitelist = 1;
1566 if (checkcfg(CFG_WHITELIST)) { 1626 ptr += 10;
1567 arg_whitelist = 1;
1568 ptr += 10;
1569 }
1570 else {
1571 static int whitelist_warning_printed = 0;
1572 if (!whitelist_warning_printed) {
1573 warning_feature_disabled("whitelist");
1574 whitelist_warning_printed = 1;
1575 }
1576 return 0;
1577 }
1578#else
1579 return 0;
1580#endif
1581 } 1627 }
1582 else if (strncmp(ptr, "nowhitelist ", 12) == 0) 1628 else if (strncmp(ptr, "nowhitelist ", 12) == 0)
1583 ptr += 12; 1629 ptr += 12;
@@ -1691,7 +1737,7 @@ void profile_read(const char *fname) {
1691 } 1737 }
1692 1738
1693 // open profile file: 1739 // open profile file:
1694 FILE *fp = fopen(fname, "r"); 1740 FILE *fp = fopen(fname, "re");
1695 if (fp == NULL) { 1741 if (fp == NULL) {
1696 fprintf(stderr, "Error: cannot open profile file %s\n", fname); 1742 fprintf(stderr, "Error: cannot open profile file %s\n", fname);
1697 exit(1); 1743 exit(1);
@@ -1708,13 +1754,29 @@ void profile_read(const char *fname) {
1708 int lineno = 0; 1754 int lineno = 0;
1709 while (fgets(buf, MAX_READ, fp)) { 1755 while (fgets(buf, MAX_READ, fp)) {
1710 ++lineno; 1756 ++lineno;
1757
1758 // remove comments
1759 char *ptr = strchr(buf, '#');
1760 if (ptr)
1761 *ptr = '\0';
1762
1711 // remove empty space - ptr in allocated memory 1763 // remove empty space - ptr in allocated memory
1712 char *ptr = line_remove_spaces(buf); 1764 ptr = line_remove_spaces(buf);
1713 if (ptr == NULL) 1765 if (ptr == NULL)
1714 continue; 1766 continue;
1767 if (*ptr == '\0') {
1768 free(ptr);
1769 continue;
1770 }
1715 1771
1716 // comments 1772 if (strncmp(ptr, "whitelist-ro ", 13) == 0) {
1717 if (*ptr == '#' || *ptr == '\0') { 1773 char *whitelist, *readonly;
1774 if (asprintf(&whitelist, "whitelist %s", ptr + 13) == -1)
1775 errExit("asprintf");
1776 profile_add(whitelist);
1777 if (asprintf(&readonly, "read-only %s", ptr + 13) == -1)
1778 errExit("asprintf");
1779 profile_add(readonly);
1718 free(ptr); 1780 free(ptr);
1719 continue; 1781 continue;
1720 } 1782 }
@@ -1724,7 +1786,7 @@ void profile_read(const char *fname) {
1724 if (strcmp(ptr, "quiet") == 0) { 1786 if (strcmp(ptr, "quiet") == 0) {
1725 if (is_in_ignore_list(ptr)) 1787 if (is_in_ignore_list(ptr))
1726 arg_quiet = 0; 1788 arg_quiet = 0;
1727 else 1789 else if (!arg_debug)
1728 arg_quiet = 1; 1790 arg_quiet = 1;
1729 free(ptr); 1791 free(ptr);
1730 continue; 1792 continue;
@@ -1771,9 +1833,8 @@ void profile_read(const char *fname) {
1771// else { 1833// else {
1772// free(ptr); 1834// free(ptr);
1773// } 1835// }
1774#ifdef HAVE_GCOV 1836
1775 __gcov_flush(); 1837 __gcov_flush();
1776#endif
1777 } 1838 }
1778 fclose(fp); 1839 fclose(fp);
1779} 1840}
@@ -1884,7 +1945,7 @@ char *profile_list_compress(char *list)
1884 /* Include non-empty item */ 1945 /* Include non-empty item */
1885 if (!*item) 1946 if (!*item)
1886 in[i] = 0; 1947 in[i] = 0;
1887 /* Remove all allready included items */ 1948 /* Remove all already included items */
1888 for (k = 0; k < i; ++k) 1949 for (k = 0; k < i; ++k)
1889 in[k] = 0; 1950 in[k] = 0;
1890 break; 1951 break;
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c
index 926af7967..37e541f50 100644
--- a/src/firejail/protocol.c
+++ b/src/firejail/protocol.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -23,7 +23,7 @@
23 23
24void protocol_filter_save(void) { 24void protocol_filter_save(void) {
25 // save protocol filter configuration in PROTOCOL_CFG 25 // save protocol filter configuration in PROTOCOL_CFG
26 FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); 26 FILE *fp = fopen(RUN_PROTOCOL_CFG, "wxe");
27 if (!fp) 27 if (!fp)
28 errExit("fopen"); 28 errExit("fopen");
29 fprintf(fp, "%s\n", cfg.protocol); 29 fprintf(fp, "%s\n", cfg.protocol);
@@ -35,7 +35,7 @@ void protocol_filter_load(const char *fname) {
35 assert(fname); 35 assert(fname);
36 36
37 // read protocol filter configuration from PROTOCOL_CFG 37 // read protocol filter configuration from PROTOCOL_CFG
38 FILE *fp = fopen(fname, "r"); 38 FILE *fp = fopen(fname, "re");
39 if (!fp) 39 if (!fp)
40 return; 40 return;
41 41
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index 4b9203c36..320668bf9 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -25,6 +25,7 @@
25#include <dirent.h> 25#include <dirent.h>
26#include <errno.h> 26#include <errno.h>
27#include <sys/wait.h> 27#include <sys/wait.h>
28#include <glob.h>
28 29
29#include <fcntl.h> 30#include <fcntl.h>
30#ifndef O_PATH 31#ifndef O_PATH
@@ -33,6 +34,59 @@
33 34
34#define PULSE_CLIENT_SYSCONF "/etc/pulse/client.conf" 35#define PULSE_CLIENT_SYSCONF "/etc/pulse/client.conf"
35 36
37
38
39static void disable_rundir_pipewire(const char *path) {
40 assert(path);
41
42 // globbing for path/pipewire-*
43 char *pattern;
44 if (asprintf(&pattern, "%s/pipewire-*", path) == -1)
45 errExit("asprintf");
46
47 glob_t globbuf;
48 int globerr = glob(pattern, GLOB_NOCHECK | GLOB_NOSORT, NULL, &globbuf);
49 if (globerr) {
50 fprintf(stderr, "Error: failed to glob pattern %s\n", pattern);
51 exit(1);
52 }
53
54 size_t i;
55 for (i = 0; i < globbuf.gl_pathc; i++) {
56 char *dir = globbuf.gl_pathv[i];
57 assert(dir);
58
59 // don't disable symlinks - disable_file_or_dir will bind-mount an empty directory on top of it!
60 if (is_link(dir))
61 continue;
62 disable_file_or_dir(dir);
63 }
64 globfree(&globbuf);
65 free(pattern);
66}
67
68
69
70// disable pipewire socket
71void pipewire_disable(void) {
72 if (arg_debug)
73 printf("disable pipewire\n");
74 // blacklist user config directory
75 disable_file_path(cfg.homedir, ".config/pipewire");
76
77 // blacklist pipewire in XDG_RUNTIME_DIR
78 const char *name = env_get("XDG_RUNTIME_DIR");
79 if (name)
80 disable_rundir_pipewire(name);
81
82 // try the default location anyway
83 char *path;
84 if (asprintf(&path, "/run/user/%d", getuid()) == -1)
85 errExit("asprintf");
86 disable_rundir_pipewire(path);
87 free(path);
88}
89
36// disable pulseaudio socket 90// disable pulseaudio socket
37void pulseaudio_disable(void) { 91void pulseaudio_disable(void) {
38 if (arg_debug) 92 if (arg_debug)
@@ -75,38 +129,41 @@ void pulseaudio_disable(void) {
75 closedir(dir); 129 closedir(dir);
76} 130}
77 131
78static void pulseaudio_fallback(const char *path) {
79 assert(path);
80
81 fmessage("Cannot mount tmpfs on %s/.config/pulse\n", cfg.homedir);
82 env_store_name_val("PULSE_CLIENTCONFIG", path, SETENV);
83}
84
85// disable shm in pulseaudio (issue #69) 132// disable shm in pulseaudio (issue #69)
86void pulseaudio_init(void) { 133void pulseaudio_init(void) {
87 struct stat s;
88
89 // do we have pulseaudio in the system? 134 // do we have pulseaudio in the system?
90 if (stat(PULSE_CLIENT_SYSCONF, &s) == -1) { 135 if (access(PULSE_CLIENT_SYSCONF, R_OK)) {
91 if (arg_debug) 136 if (arg_debug)
92 printf("%s not found\n", PULSE_CLIENT_SYSCONF); 137 printf("Cannot read %s\n", PULSE_CLIENT_SYSCONF);
93 return; 138 return;
94 } 139 }
95 140
141 // create ~/.config/pulse directory if not present
142 char *homeusercfg = NULL;
143 if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1)
144 errExit("asprintf");
145 if (create_empty_dir_as_user(homeusercfg, 0700))
146 fs_logger2("create", homeusercfg);
147
148 free(homeusercfg);
149 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1)
150 errExit("asprintf");
151 if (create_empty_dir_as_user(homeusercfg, 0700))
152 fs_logger2("create", homeusercfg);
153
96 // create the new user pulseaudio directory 154 // create the new user pulseaudio directory
155 // that will be mounted over ~/.config/pulse
97 if (mkdir(RUN_PULSE_DIR, 0700) == -1) 156 if (mkdir(RUN_PULSE_DIR, 0700) == -1)
98 errExit("mkdir"); 157 errExit("mkdir");
99 selinux_relabel_path(RUN_PULSE_DIR, RUN_PULSE_DIR); 158 selinux_relabel_path(RUN_PULSE_DIR, homeusercfg);
100 // mount it nosuid, noexec, nodev
101 fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0); 159 fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0);
102
103 // create the new client.conf file 160 // create the new client.conf file
104 char *pulsecfg = NULL; 161 char *pulsecfg = NULL;
105 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) 162 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1)
106 errExit("asprintf"); 163 errExit("asprintf");
107 if (copy_file(PULSE_CLIENT_SYSCONF, pulsecfg, -1, -1, 0644)) // root needed 164 if (copy_file(PULSE_CLIENT_SYSCONF, pulsecfg, -1, -1, 0644)) // root needed
108 errExit("copy_file"); 165 errExit("copy_file");
109 FILE *fp = fopen(pulsecfg, "a"); 166 FILE *fp = fopen(pulsecfg, "ae");
110 if (!fp) 167 if (!fp)
111 errExit("fopen"); 168 errExit("fopen");
112 fprintf(fp, "%s", "\nenable-shm = no\n"); 169 fprintf(fp, "%s", "\nenable-shm = no\n");
@@ -116,37 +173,14 @@ void pulseaudio_init(void) {
116 if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) 173 if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700))
117 errExit("set_perms"); 174 errExit("set_perms");
118 175
119 // create ~/.config/pulse directory if not present
120 char *homeusercfg = NULL;
121 if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1)
122 errExit("asprintf");
123 if (create_empty_dir_as_user(homeusercfg, 0700))
124 fs_logger2("create", homeusercfg);
125
126 free(homeusercfg);
127 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1)
128 errExit("asprintf");
129 if (create_empty_dir_as_user(homeusercfg, 0700))
130 fs_logger2("create", homeusercfg);
131
132 // if ~/.config/pulse exists and there are no symbolic links, mount the new directory 176 // if ~/.config/pulse exists and there are no symbolic links, mount the new directory
133 // else set environment variable 177 // else set environment variable
134 int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 178 EUID_USER();
179 int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
180 EUID_ROOT();
135 if (fd == -1) { 181 if (fd == -1) {
136 pulseaudio_fallback(pulsecfg); 182 fwarning("not mounting tmpfs on %s\n", homeusercfg);
137 goto out; 183 env_store_name_val("PULSE_CLIENTCONFIG", pulsecfg, SETENV);
138 }
139 // confirm the actual mount destination is owned by the user
140 if (fstat(fd, &s) == -1) { // FUSE
141 if (errno != EACCES)
142 errExit("fstat");
143 close(fd);
144 pulseaudio_fallback(pulsecfg);
145 goto out;
146 }
147 if (s.st_uid != getuid()) {
148 close(fd);
149 pulseaudio_fallback(pulsecfg);
150 goto out; 184 goto out;
151 } 185 }
152 // preserve a read-only mount 186 // preserve a read-only mount
@@ -158,17 +192,13 @@ void pulseaudio_init(void) {
158 // mount via the link in /proc/self/fd 192 // mount via the link in /proc/self/fd
159 if (arg_debug) 193 if (arg_debug)
160 printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg); 194 printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg);
161 char *proc; 195 if (bind_mount_path_to_fd(RUN_PULSE_DIR, fd))
162 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
163 errExit("asprintf");
164 if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0)
165 errExit("mount pulseaudio"); 196 errExit("mount pulseaudio");
166 // check /proc/self/mountinfo to confirm the mount is ok 197 // check /proc/self/mountinfo to confirm the mount is ok
167 MountData *mptr = get_last_mount(); 198 MountData *mptr = get_last_mount();
168 if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 199 if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
169 errLogExit("invalid pulseaudio mount"); 200 errLogExit("invalid pulseaudio mount");
170 fs_logger2("tmpfs", homeusercfg); 201 fs_logger2("tmpfs", homeusercfg);
171 free(proc);
172 close(fd); 202 close(fd);
173 203
174 char *p; 204 char *p;
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c
index a0ca4c02c..447d7b663 100644
--- a/src/firejail/restrict_users.c
+++ b/src/firejail/restrict_users.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -21,7 +21,6 @@
21#include "../include/firejail_user.h" 21#include "../include/firejail_user.h"
22#include <sys/mount.h> 22#include <sys/mount.h>
23#include <sys/stat.h> 23#include <sys/stat.h>
24#include <linux/limits.h>
25#include <fnmatch.h> 24#include <fnmatch.h>
26#include <glob.h> 25#include <glob.h>
27#include <dirent.h> 26#include <dirent.h>
@@ -73,7 +72,7 @@ static void sanitize_home(void) {
73 if (arg_debug) 72 if (arg_debug)
74 printf("Cleaning /home directory\n"); 73 printf("Cleaning /home directory\n");
75 // open user home directory in order to keep it around 74 // open user home directory in order to keep it around
76 int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 75 int fd = safer_openat(-1, cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
77 if (fd == -1) 76 if (fd == -1)
78 goto errout; 77 goto errout;
79 if (fstat(fd, &s) == -1) { // FUSE 78 if (fstat(fd, &s) == -1) { // FUSE
@@ -104,12 +103,8 @@ static void sanitize_home(void) {
104 selinux_relabel_path(cfg.homedir, cfg.homedir); 103 selinux_relabel_path(cfg.homedir, cfg.homedir);
105 104
106 // bring back real user home directory 105 // bring back real user home directory
107 char *proc; 106 if (bind_mount_fd_to_path(fd, cfg.homedir))
108 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
109 errExit("asprintf");
110 if (mount(proc, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
111 errExit("mount bind"); 107 errExit("mount bind");
112 free(proc);
113 close(fd); 108 close(fd);
114 109
115 if (!arg_private) 110 if (!arg_private)
@@ -154,12 +149,8 @@ static void sanitize_run(void) {
154 selinux_relabel_path(runuser, runuser); 149 selinux_relabel_path(runuser, runuser);
155 150
156 // bring back real run/user/$UID directory 151 // bring back real run/user/$UID directory
157 char *proc; 152 if (bind_mount_fd_to_path(fd, runuser))
158 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
159 errExit("asprintf");
160 if (mount(proc, runuser, NULL, MS_BIND|MS_REC, NULL) < 0)
161 errExit("mount bind"); 153 errExit("mount bind");
162 free(proc);
163 close(fd); 154 close(fd);
164 155
165 fs_logger2("whitelist", runuser); 156 fs_logger2("whitelist", runuser);
@@ -183,10 +174,10 @@ static void sanitize_passwd(void) {
183 174
184 // open files 175 // open files
185 /* coverity[toctou] */ 176 /* coverity[toctou] */
186 fpin = fopen("/etc/passwd", "r"); 177 fpin = fopen("/etc/passwd", "re");
187 if (!fpin) 178 if (!fpin)
188 goto errout; 179 goto errout;
189 fpout = fopen(RUN_PASSWD_FILE, "w"); 180 fpout = fopen(RUN_PASSWD_FILE, "we");
190 if (!fpout) 181 if (!fpout)
191 goto errout; 182 goto errout;
192 183
@@ -246,6 +237,11 @@ static void sanitize_passwd(void) {
246 // mount-bind tne new password file 237 // mount-bind tne new password file
247 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) 238 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0)
248 errExit("mount"); 239 errExit("mount");
240
241 // blacklist RUN_PASSWD_FILE
242 if (mount(RUN_RO_FILE, RUN_PASSWD_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
243 errExit("mount");
244
249 fs_logger("create /etc/passwd"); 245 fs_logger("create /etc/passwd");
250 246
251 return; 247 return;
@@ -318,10 +314,10 @@ static void sanitize_group(void) {
318 314
319 // open files 315 // open files
320 /* coverity[toctou] */ 316 /* coverity[toctou] */
321 fpin = fopen("/etc/group", "r"); 317 fpin = fopen("/etc/group", "re");
322 if (!fpin) 318 if (!fpin)
323 goto errout; 319 goto errout;
324 fpout = fopen(RUN_GROUP_FILE, "w"); 320 fpout = fopen(RUN_GROUP_FILE, "we");
325 if (!fpout) 321 if (!fpout)
326 goto errout; 322 goto errout;
327 323
@@ -376,6 +372,11 @@ static void sanitize_group(void) {
376 // mount-bind tne new group file 372 // mount-bind tne new group file
377 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) 373 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0)
378 errExit("mount"); 374 errExit("mount");
375
376 // blacklist RUN_GROUP_FILE
377 if (mount(RUN_RO_FILE, RUN_GROUP_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
378 errExit("mount");
379
379 fs_logger("create /etc/group"); 380 fs_logger("create /etc/group");
380 381
381 return; 382 return;
diff --git a/src/firejail/restricted_shell.c b/src/firejail/restricted_shell.c
index ae453f4f1..c1340cae1 100644
--- a/src/firejail/restricted_shell.c
+++ b/src/firejail/restricted_shell.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -32,7 +32,7 @@ int restricted_shell(const char *user) {
32 char *fname; 32 char *fname;
33 if (asprintf(&fname, "%s/login.users", SYSCONFDIR) == -1) 33 if (asprintf(&fname, "%s/login.users", SYSCONFDIR) == -1)
34 errExit("asprintf"); 34 errExit("asprintf");
35 FILE *fp = fopen(fname, "r"); 35 FILE *fp = fopen(fname, "re");
36 free(fname); 36 free(fname);
37 if (fp == NULL) 37 if (fp == NULL)
38 return 0; 38 return 0;
@@ -96,7 +96,7 @@ int restricted_shell(const char *user) {
96 fullargv[i] = ptr; 96 fullargv[i] = ptr;
97#ifdef DEBUG_RESTRICTED_SHELL 97#ifdef DEBUG_RESTRICTED_SHELL
98 {EUID_ROOT(); 98 {EUID_ROOT();
99 FILE *fp = fopen("/firelog", "a"); 99 FILE *fp = fopen("/firelog", "ae");
100 if (fp) { 100 if (fp) {
101 fprintf(fp, "i %d ptr #%s#\n", i, fullargv[i]); 101 fprintf(fp, "i %d ptr #%s#\n", i, fullargv[i]);
102 fclose(fp); 102 fclose(fp);
diff --git a/src/firejail/rlimit.c b/src/firejail/rlimit.c
index 78f00bc63..b10d2c528 100644
--- a/src/firejail/rlimit.c
+++ b/src/firejail/rlimit.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firejail.h" 20#include "firejail.h"
21#include "../include/gcov_wrapper.h"
21#include <sys/time.h> 22#include <sys/time.h>
22#include <sys/resource.h> 23#include <sys/resource.h>
23 24
@@ -33,9 +34,9 @@ void set_rlimits(void) {
33 // set the new limit 34 // set the new limit
34 rl.rlim_cur = (rlim_t) cfg.rlimit_cpu; 35 rl.rlim_cur = (rlim_t) cfg.rlimit_cpu;
35 rl.rlim_max = (rlim_t) cfg.rlimit_cpu; 36 rl.rlim_max = (rlim_t) cfg.rlimit_cpu;
36#ifdef HAVE_GCOV 37
37 __gcov_dump(); 38 __gcov_dump();
38#endif 39
39 if (setrlimit(RLIMIT_CPU, &rl) == -1) 40 if (setrlimit(RLIMIT_CPU, &rl) == -1)
40 errExit("setrlimit"); 41 errExit("setrlimit");
41 if (arg_debug) 42 if (arg_debug)
@@ -50,9 +51,10 @@ void set_rlimits(void) {
50 // set the new limit 51 // set the new limit
51 rl.rlim_cur = (rlim_t) cfg.rlimit_nofile; 52 rl.rlim_cur = (rlim_t) cfg.rlimit_nofile;
52 rl.rlim_max = (rlim_t) cfg.rlimit_nofile; 53 rl.rlim_max = (rlim_t) cfg.rlimit_nofile;
53#ifdef HAVE_GCOV // gcov-instrumented programs might crash at this point 54
55 // gcov-instrumented programs might crash at this point
54 __gcov_dump(); 56 __gcov_dump();
55#endif 57
56 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 58 if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
57 errExit("setrlimit"); 59 errExit("setrlimit");
58 if (arg_debug) 60 if (arg_debug)
@@ -67,9 +69,9 @@ void set_rlimits(void) {
67 // set the new limit 69 // set the new limit
68 rl.rlim_cur = (rlim_t) cfg.rlimit_nproc; 70 rl.rlim_cur = (rlim_t) cfg.rlimit_nproc;
69 rl.rlim_max = (rlim_t) cfg.rlimit_nproc; 71 rl.rlim_max = (rlim_t) cfg.rlimit_nproc;
70#ifdef HAVE_GCOV 72
71 __gcov_dump(); 73 __gcov_dump();
72#endif 74
73 if (setrlimit(RLIMIT_NPROC, &rl) == -1) 75 if (setrlimit(RLIMIT_NPROC, &rl) == -1)
74 errExit("setrlimit"); 76 errExit("setrlimit");
75 if (arg_debug) 77 if (arg_debug)
@@ -84,9 +86,9 @@ void set_rlimits(void) {
84 // set the new limit 86 // set the new limit
85 rl.rlim_cur = (rlim_t) cfg.rlimit_fsize; 87 rl.rlim_cur = (rlim_t) cfg.rlimit_fsize;
86 rl.rlim_max = (rlim_t) cfg.rlimit_fsize; 88 rl.rlim_max = (rlim_t) cfg.rlimit_fsize;
87#ifdef HAVE_GCOV 89
88 __gcov_dump(); 90 __gcov_dump();
89#endif 91
90 if (setrlimit(RLIMIT_FSIZE, &rl) == -1) 92 if (setrlimit(RLIMIT_FSIZE, &rl) == -1)
91 errExit("setrlimit"); 93 errExit("setrlimit");
92 if (arg_debug) 94 if (arg_debug)
@@ -101,9 +103,9 @@ void set_rlimits(void) {
101 // set the new limit 103 // set the new limit
102 rl.rlim_cur = (rlim_t) cfg.rlimit_sigpending; 104 rl.rlim_cur = (rlim_t) cfg.rlimit_sigpending;
103 rl.rlim_max = (rlim_t) cfg.rlimit_sigpending; 105 rl.rlim_max = (rlim_t) cfg.rlimit_sigpending;
104#ifdef HAVE_GCOV 106
105 __gcov_dump(); 107 __gcov_dump();
106#endif 108
107 if (setrlimit(RLIMIT_SIGPENDING, &rl) == -1) 109 if (setrlimit(RLIMIT_SIGPENDING, &rl) == -1)
108 errExit("setrlimit"); 110 errExit("setrlimit");
109 if (arg_debug) 111 if (arg_debug)
@@ -118,9 +120,9 @@ void set_rlimits(void) {
118 // set the new limit 120 // set the new limit
119 rl.rlim_cur = (rlim_t) cfg.rlimit_as; 121 rl.rlim_cur = (rlim_t) cfg.rlimit_as;
120 rl.rlim_max = (rlim_t) cfg.rlimit_as; 122 rl.rlim_max = (rlim_t) cfg.rlimit_as;
121#ifdef HAVE_GCOV 123
122 __gcov_dump(); 124 __gcov_dump();
123#endif 125
124 if (setrlimit(RLIMIT_AS, &rl) == -1) 126 if (setrlimit(RLIMIT_AS, &rl) == -1)
125 errExit("setrlimit"); 127 errExit("setrlimit");
126 if (arg_debug) 128 if (arg_debug)
diff --git a/src/firejail/run_files.c b/src/firejail/run_files.c
index cd44f745f..c971a4f53 100644
--- a/src/firejail/run_files.c
+++ b/src/firejail/run_files.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -101,7 +101,7 @@ void set_name_run_file(pid_t pid) {
101 errExit("asprintf"); 101 errExit("asprintf");
102 102
103 // the file is deleted first 103 // the file is deleted first
104 FILE *fp = fopen(fname, "w"); 104 FILE *fp = fopen(fname, "we");
105 if (!fp) { 105 if (!fp) {
106 fprintf(stderr, "Error: cannot create %s\n", fname); 106 fprintf(stderr, "Error: cannot create %s\n", fname);
107 exit(1); 107 exit(1);
@@ -120,7 +120,7 @@ void set_x11_run_file(pid_t pid, int display) {
120 errExit("asprintf"); 120 errExit("asprintf");
121 121
122 // the file is deleted first 122 // the file is deleted first
123 FILE *fp = fopen(fname, "w"); 123 FILE *fp = fopen(fname, "we");
124 if (!fp) { 124 if (!fp) {
125 fprintf(stderr, "Error: cannot create %s\n", fname); 125 fprintf(stderr, "Error: cannot create %s\n", fname);
126 exit(1); 126 exit(1);
@@ -139,7 +139,7 @@ void set_profile_run_file(pid_t pid, const char *fname) {
139 139
140 EUID_ROOT(); 140 EUID_ROOT();
141 // the file is deleted first 141 // the file is deleted first
142 FILE *fp = fopen(runfile, "w"); 142 FILE *fp = fopen(runfile, "we");
143 if (!fp) { 143 if (!fp) {
144 fprintf(stderr, "Error: cannot create %s\n", runfile); 144 fprintf(stderr, "Error: cannot create %s\n", runfile);
145 exit(1); 145 exit(1);
diff --git a/src/firejail/run_symlink.c b/src/firejail/run_symlink.c
index 77fac5438..e2847aea6 100644
--- a/src/firejail/run_symlink.c
+++ b/src/firejail/run_symlink.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -22,7 +22,6 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <unistd.h> 23#include <unistd.h>
24 24
25extern char *find_in_path(const char *program);
26 25
27void run_symlink(int argc, char **argv, int run_as_is) { 26void run_symlink(int argc, char **argv, int run_as_is) {
28 EUID_ASSERT(); 27 EUID_ASSERT();
@@ -77,6 +76,8 @@ void run_symlink(int argc, char **argv, int run_as_is) {
77 a[i + 2] = argv[i + 1]; 76 a[i + 2] = argv[i + 1];
78 } 77 }
79 a[i + 2] = NULL; 78 a[i + 2] = NULL;
79 if (env_get("LD_PRELOAD") != NULL)
80 fprintf(stderr, "run_symlink: LD_PRELOAD is: '%s'\n", env_get("LD_PRELOAD"));
80 assert(env_get("LD_PRELOAD") == NULL); 81 assert(env_get("LD_PRELOAD") == NULL);
81 assert(getenv("LD_PRELOAD") == NULL); 82 assert(getenv("LD_PRELOAD") == NULL);
82 execvp(a[0], a); 83 execvp(a[0], a);
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 3af828ede..96407d081 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,6 +19,7 @@
19*/ 19*/
20 20
21#include "firejail.h" 21#include "firejail.h"
22#include "../include/gcov_wrapper.h"
22#include "../include/seccomp.h" 23#include "../include/seccomp.h"
23#include <sys/mman.h> 24#include <sys/mman.h>
24#include <sys/mount.h> 25#include <sys/mount.h>
@@ -49,7 +50,6 @@
49#include <sys/apparmor.h> 50#include <sys/apparmor.h>
50#endif 51#endif
51 52
52
53static int force_nonewprivs = 0; 53static int force_nonewprivs = 0;
54 54
55static int monitored_pid = 0; 55static int monitored_pid = 0;
@@ -67,7 +67,7 @@ static void sandbox_handler(int sig){
67 if (asprintf(&monfile, "/proc/%d/cmdline", monitored_pid) == -1) 67 if (asprintf(&monfile, "/proc/%d/cmdline", monitored_pid) == -1)
68 errExit("asprintf"); 68 errExit("asprintf");
69 while (monsec) { 69 while (monsec) {
70 FILE *fp = fopen(monfile, "r"); 70 FILE *fp = fopen(monfile, "re");
71 if (!fp) 71 if (!fp)
72 break; 72 break;
73 73
@@ -87,9 +87,9 @@ static void sandbox_handler(int sig){
87 87
88 // broadcast a SIGKILL 88 // broadcast a SIGKILL
89 kill(-1, SIGKILL); 89 kill(-1, SIGKILL);
90 flush_stdin();
91 90
92 exit(sig); 91 flush_stdin();
92 exit(128 + sig);
93} 93}
94 94
95static void install_handler(void) { 95static void install_handler(void) {
@@ -162,7 +162,7 @@ static void save_nogroups(void) {
162 if (arg_nogroups == 0) 162 if (arg_nogroups == 0)
163 return; 163 return;
164 164
165 FILE *fp = fopen(RUN_GROUPS_CFG, "w"); 165 FILE *fp = fopen(RUN_GROUPS_CFG, "wxe");
166 if (fp) { 166 if (fp) {
167 fprintf(fp, "\n"); 167 fprintf(fp, "\n");
168 SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644 168 SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644
@@ -204,7 +204,7 @@ static void save_umask(void) {
204} 204}
205 205
206static char *create_join_file(void) { 206static char *create_join_file(void) {
207 int fd = open(RUN_JOIN_FILE, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); 207 int fd = open(RUN_JOIN_FILE, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
208 if (fd == -1) 208 if (fd == -1)
209 errExit("open"); 209 errExit("open");
210 if (ftruncate(fd, 1) == -1) 210 if (ftruncate(fd, 1) == -1)
@@ -227,7 +227,7 @@ static void sandbox_if_up(Bridge *br) {
227 if (br->arg_ip_none == 1); // do nothing 227 if (br->arg_ip_none == 1); // do nothing
228 else if (br->arg_ip_none == 0 && br->macvlan == 0) { 228 else if (br->arg_ip_none == 0 && br->macvlan == 0) {
229 if (br->ipsandbox == br->ip) { 229 if (br->ipsandbox == br->ip) {
230 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); 230 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address, exiting...\n", PRINT_IP(br->ipsandbox), br->dev);
231 exit(1); 231 exit(1);
232 } 232 }
233 233
@@ -245,13 +245,17 @@ static void sandbox_if_up(Bridge *br) {
245 br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask); 245 br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask);
246 else { 246 else {
247 if (br->ipsandbox == br->ip) { 247 if (br->ipsandbox == br->ip) {
248 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); 248 fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address, exiting...\n", PRINT_IP(br->ipsandbox), br->dev);
249 exit(1);
250 }
251 if (br->ipsandbox == cfg.defaultgw) {
252 fprintf(stderr, "Error: %d.%d.%d.%d is the default gateway, exiting...\n", PRINT_IP(br->ipsandbox));
249 exit(1); 253 exit(1);
250 } 254 }
251 255
252 uint32_t rv = arp_check(dev, br->ipsandbox); 256 uint32_t rv = arp_check(dev, br->ipsandbox);
253 if (rv) { 257 if (rv) {
254 fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox)); 258 fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use, exiting...\n", PRINT_IP(br->ipsandbox));
255 exit(1); 259 exit(1);
256 } 260 }
257 } 261 }
@@ -352,6 +356,15 @@ static int monitor_application(pid_t app_pid) {
352 if (arg_debug) 356 if (arg_debug)
353 printf("Sandbox monitor: waitpid %d retval %d status %d\n", monitored_pid, rv, status); 357 printf("Sandbox monitor: waitpid %d retval %d status %d\n", monitored_pid, rv, status);
354 358
359 if (arg_deterministic_shutdown) {
360 if (arg_debug)
361 printf("Sandbox monitor: monitored process died, shut down the sandbox\n");
362 kill(-1, SIGTERM);
363 usleep(100000);
364 kill(-1, SIGKILL);
365 break;
366 }
367
355 DIR *dir; 368 DIR *dir;
356 if (!(dir = opendir("/proc"))) { 369 if (!(dir = opendir("/proc"))) {
357 // sleep 2 seconds and try again 370 // sleep 2 seconds and try again
@@ -373,18 +386,6 @@ static int monitor_application(pid_t app_pid) {
373 if ((pid_t) pid == dhclient4_pid || (pid_t) pid == dhclient6_pid) 386 if ((pid_t) pid == dhclient4_pid || (pid_t) pid == dhclient6_pid)
374 continue; 387 continue;
375 388
376 // todo: make this generic
377 // Dillo browser leaves a dpid process running, we need to shut it down
378 int found = 0;
379 if (strcmp(cfg.command_name, "dillo") == 0) {
380 char *pidname = pid_proc_comm(pid);
381 if (pidname && strcmp(pidname, "dpid") == 0)
382 found = 1;
383 free(pidname);
384 }
385 if (found)
386 break;
387
388 monitored_pid = pid; 389 monitored_pid = pid;
389 break; 390 break;
390 } 391 }
@@ -403,7 +404,6 @@ static void print_time(void) {
403 fmessage("Child process initialized in %.02f ms\n", delta); 404 fmessage("Child process initialized in %.02f ms\n", delta);
404} 405}
405 406
406
407// check execute permissions for the program 407// check execute permissions for the program
408// this is done typically by the shell 408// this is done typically by the shell
409// we are here because of --shell=none 409// we are here because of --shell=none
@@ -460,10 +460,42 @@ static int ok_to_run(const char *program) {
460 return 0; 460 return 0;
461} 461}
462 462
463static void close_file_descriptors(void) {
464 if (arg_keep_fd_all)
465 return;
466
467 if (arg_debug)
468 printf("Closing non-standard file descriptors\n");
469
470 if (!cfg.keep_fd) {
471 close_all(NULL, 0);
472 return;
473 }
474
475 size_t sz = 0;
476 int *keep = str_to_int_array(cfg.keep_fd, &sz);
477 if (!keep) {
478 fprintf(stderr, "Error: invalid keep-fd option\n");
479 exit(1);
480 }
481 close_all(keep, sz);
482 free(keep);
483}
484
485
463void start_application(int no_sandbox, int fd, char *set_sandbox_status) { 486void start_application(int no_sandbox, int fd, char *set_sandbox_status) {
464 // set environment 487 if (no_sandbox == 0) {
465 if (no_sandbox == 0) 488 close_file_descriptors();
489
490 // set nice and rlimits
491 if (arg_nice)
492 set_nice(cfg.nice);
493 set_rlimits();
494
466 env_defaults(); 495 env_defaults();
496 }
497
498 // set environment
467 env_apply_all(); 499 env_apply_all();
468 500
469 // restore original umask 501 // restore original umask
@@ -500,9 +532,8 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) {
500 exit(1); 532 exit(1);
501 } 533 }
502 534
503#ifdef HAVE_GCOV
504 __gcov_dump(); 535 __gcov_dump();
505#endif 536
506 seccomp_install_filters(); 537 seccomp_install_filters();
507 538
508 if (set_sandbox_status) 539 if (set_sandbox_status)
@@ -556,9 +587,8 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) {
556 if (!arg_command && !arg_quiet) 587 if (!arg_command && !arg_quiet)
557 print_time(); 588 print_time();
558 589
559#ifdef HAVE_GCOV
560 __gcov_dump(); 590 __gcov_dump();
561#endif 591
562 seccomp_install_filters(); 592 seccomp_install_filters();
563 593
564 if (set_sandbox_status) 594 if (set_sandbox_status)
@@ -796,7 +826,7 @@ int sandbox(void* sandbox_arg) {
796 826
797 // trace pre-install 827 // trace pre-install
798 if (need_preload) 828 if (need_preload)
799 fs_trace_preload(); 829 fs_trace_touch_or_store_preload();
800 830
801 // store hosts file 831 // store hosts file
802 if (cfg.hosts_file) 832 if (cfg.hosts_file)
@@ -812,8 +842,11 @@ int sandbox(void* sandbox_arg) {
812 //**************************** 842 //****************************
813 // trace pre-install, this time inside chroot 843 // trace pre-install, this time inside chroot
814 //**************************** 844 //****************************
815 if (need_preload) 845 if (need_preload) {
816 fs_trace_preload(); 846 int rv = unlink(RUN_LDPRELOAD_FILE);
847 (void) rv;
848 fs_trace_touch_or_store_preload();
849 }
817 } 850 }
818 else 851 else
819#endif 852#endif
@@ -833,6 +866,7 @@ int sandbox(void* sandbox_arg) {
833 // private mode 866 // private mode
834 //**************************** 867 //****************************
835 if (arg_private) { 868 if (arg_private) {
869 EUID_USER();
836 if (cfg.home_private) { // --private= 870 if (cfg.home_private) { // --private=
837 if (cfg.chrootdir) 871 if (cfg.chrootdir)
838 fwarning("private=directory feature is disabled in chroot\n"); 872 fwarning("private=directory feature is disabled in chroot\n");
@@ -851,6 +885,7 @@ int sandbox(void* sandbox_arg) {
851 } 885 }
852 else // --private 886 else // --private
853 fs_private(); 887 fs_private();
888 EUID_ROOT();
854 } 889 }
855 890
856 if (arg_private_dev) 891 if (arg_private_dev)
@@ -883,16 +918,16 @@ int sandbox(void* sandbox_arg) {
883 else if (arg_overlay) 918 else if (arg_overlay)
884 fwarning("private-bin feature is disabled in overlay\n"); 919 fwarning("private-bin feature is disabled in overlay\n");
885 else { 920 else {
921 EUID_USER();
886 // for --x11=xorg we need to add xauth command 922 // for --x11=xorg we need to add xauth command
887 if (arg_x11_xorg) { 923 if (arg_x11_xorg) {
888 EUID_USER();
889 char *tmp; 924 char *tmp;
890 if (asprintf(&tmp, "%s,xauth", cfg.bin_private_keep) == -1) 925 if (asprintf(&tmp, "%s,xauth", cfg.bin_private_keep) == -1)
891 errExit("asprintf"); 926 errExit("asprintf");
892 cfg.bin_private_keep = tmp; 927 cfg.bin_private_keep = tmp;
893 EUID_ROOT();
894 } 928 }
895 fs_private_bin_list(); 929 fs_private_bin_list();
930 EUID_ROOT();
896 } 931 }
897 } 932 }
898 933
@@ -988,7 +1023,7 @@ int sandbox(void* sandbox_arg) {
988 1023
989 // create /etc/ld.so.preload file again 1024 // create /etc/ld.so.preload file again
990 if (need_preload) 1025 if (need_preload)
991 fs_trace_preload(); 1026 fs_trace_touch_preload();
992 1027
993 // openSUSE configuration is split between /etc and /usr/etc 1028 // openSUSE configuration is split between /etc and /usr/etc
994 // process private-etc a second time 1029 // process private-etc a second time
@@ -1000,10 +1035,12 @@ int sandbox(void* sandbox_arg) {
1000 // apply the profile file 1035 // apply the profile file
1001 //**************************** 1036 //****************************
1002 // apply all whitelist commands ... 1037 // apply all whitelist commands ...
1038 EUID_USER();
1003 fs_whitelist(); 1039 fs_whitelist();
1004 1040
1005 // ... followed by blacklist commands 1041 // ... followed by blacklist commands
1006 fs_blacklist(); // mkdir and mkfile are processed all over again 1042 fs_blacklist(); // mkdir and mkfile are processed all over again
1043 EUID_ROOT();
1007 1044
1008 //**************************** 1045 //****************************
1009 // nosound/no3d/notv/novideo and fix for pulseaudio 7.0 1046 // nosound/no3d/notv/novideo and fix for pulseaudio 7.0
@@ -1012,10 +1049,13 @@ int sandbox(void* sandbox_arg) {
1012 // disable pulseaudio 1049 // disable pulseaudio
1013 pulseaudio_disable(); 1050 pulseaudio_disable();
1014 1051
1052 // disable pipewire
1053 pipewire_disable();
1054
1015 // disable /dev/snd 1055 // disable /dev/snd
1016 fs_dev_disable_sound(); 1056 fs_dev_disable_sound();
1017 } 1057 }
1018 else if (!arg_noautopulse) 1058 else if (!arg_keep_config_pulse)
1019 pulseaudio_init(); 1059 pulseaudio_init();
1020 1060
1021 if (arg_no3d) 1061 if (arg_no3d)
@@ -1039,7 +1079,7 @@ int sandbox(void* sandbox_arg) {
1039 //**************************** 1079 //****************************
1040 // set dns 1080 // set dns
1041 //**************************** 1081 //****************************
1042 fs_resolvconf(); 1082 fs_rebuild_etc();
1043 1083
1044 //**************************** 1084 //****************************
1045 // start dhcp client 1085 // start dhcp client
@@ -1052,6 +1092,11 @@ int sandbox(void* sandbox_arg) {
1052 EUID_USER(); 1092 EUID_USER();
1053 int cwd = 0; 1093 int cwd = 0;
1054 if (cfg.cwd) { 1094 if (cfg.cwd) {
1095 if (is_link(cfg.cwd)) {
1096 fprintf(stderr, "Error: unable to enter private working directory: %s\n", cfg.cwd);
1097 exit(1);
1098 }
1099
1055 if (chdir(cfg.cwd) == 0) 1100 if (chdir(cfg.cwd) == 0)
1056 cwd = 1; 1101 cwd = 1;
1057 else if (arg_private_cwd) { 1102 else if (arg_private_cwd) {
@@ -1219,7 +1264,7 @@ int sandbox(void* sandbox_arg) {
1219 //**************************************** 1264 //****************************************
1220 // drop privileges 1265 // drop privileges
1221 //**************************************** 1266 //****************************************
1222 drop_privs(arg_nogroups); 1267 drop_privs(0);
1223 1268
1224 // kill the sandbox in case the parent died 1269 // kill the sandbox in case the parent died
1225 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); 1270 prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
@@ -1239,28 +1284,25 @@ int sandbox(void* sandbox_arg) {
1239 1284
1240 if (app_pid == 0) { 1285 if (app_pid == 0) {
1241#ifdef HAVE_APPARMOR 1286#ifdef HAVE_APPARMOR
1242 // add apparmor confinement after the execve
1243 set_apparmor(); 1287 set_apparmor();
1244#endif 1288#endif
1245
1246 // set nice and rlimits
1247 if (arg_nice)
1248 set_nice(cfg.nice);
1249 set_rlimits();
1250
1251 start_application(0, -1, set_sandbox_status); 1289 start_application(0, -1, set_sandbox_status);
1252 } 1290 }
1253 1291
1254 munmap(set_sandbox_status, 1); 1292 munmap(set_sandbox_status, 1);
1255 1293
1256 int status = monitor_application(app_pid); // monitor application 1294 int status = monitor_application(app_pid); // monitor application
1257 flush_stdin();
1258 1295
1259 if (WIFEXITED(status)) { 1296 if (WIFEXITED(status)) {
1260 // if we had a proper exit, return that exit status 1297 // if we had a proper exit, return that exit status
1261 return WEXITSTATUS(status); 1298 status = WEXITSTATUS(status);
1299 } else if (WIFSIGNALED(status)) {
1300 // distinguish fatal signals by adding 128
1301 status = 128 + WTERMSIG(status);
1262 } else { 1302 } else {
1263 // something else went wrong! 1303 status = -1;
1264 return -1;
1265 } 1304 }
1305
1306 flush_stdin();
1307 return status;
1266} 1308}
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index f9c41f661..a37943940 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -23,6 +23,7 @@
23#include <unistd.h> 23#include <unistd.h>
24#include <net/if.h> 24#include <net/if.h>
25#include <stdarg.h> 25#include <stdarg.h>
26#include <sys/resource.h>
26#include <sys/wait.h> 27#include <sys/wait.h>
27#include "../include/seccomp.h" 28#include "../include/seccomp.h"
28 29
@@ -72,11 +73,8 @@ static int __attribute__((noreturn)) sbox_do_exec_v(unsigned filtermask, char *
72 } 73 }
73 74
74 // close all other file descriptors 75 // close all other file descriptors
75 if ((filtermask & SBOX_KEEP_FDS) == 0) { 76 if ((filtermask & SBOX_KEEP_FDS) == 0)
76 int i; 77 close_all(NULL, 0);
77 for (i = 3; i < FIREJAIL_MAX_FD; i++)
78 close(i); // close open files
79 }
80 78
81 umask(027); 79 umask(027);
82 80
@@ -206,6 +204,11 @@ static int __attribute__((noreturn)) sbox_do_exec_v(unsigned filtermask, char *
206 if (filtermask & SBOX_USER) 204 if (filtermask & SBOX_USER)
207 drop_privs(1); 205 drop_privs(1);
208 else if (filtermask & SBOX_ROOT) { 206 else if (filtermask & SBOX_ROOT) {
207 // https://seclists.org/oss-sec/2021/q4/43
208 struct rlimit tozero = { .rlim_cur = 0, .rlim_max = 0 };
209 if (setrlimit(RLIMIT_CORE, &tozero))
210 errExit("setrlimit");
211
209 // elevate privileges in order to get grsecurity working 212 // elevate privileges in order to get grsecurity working
210 if (setreuid(0, 0)) 213 if (setreuid(0, 0))
211 errExit("setreuid"); 214 errExit("setreuid");
@@ -248,7 +251,9 @@ int sbox_run(unsigned filtermask, int num, ...) {
248 va_start(valist, num); 251 va_start(valist, num);
249 252
250 // build argument list 253 // build argument list
251 char **arg = malloc((num + 1) * sizeof(char *)); 254 char **arg = calloc(num + 1, sizeof(char *));
255 if (!arg)
256 errExit("calloc");
252 int i; 257 int i;
253 for (i = 0; i < num; i++) 258 for (i = 0; i < num; i++)
254 arg[i] = va_arg(valist, char *); 259 arg[i] = va_arg(valist, char *);
@@ -263,7 +268,6 @@ int sbox_run(unsigned filtermask, int num, ...) {
263} 268}
264 269
265int sbox_run_v(unsigned filtermask, char * const arg[]) { 270int sbox_run_v(unsigned filtermask, char * const arg[]) {
266 EUID_ROOT();
267 assert(arg); 271 assert(arg);
268 272
269 if (arg_debug) { 273 if (arg_debug) {
@@ -283,6 +287,7 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
283 if (child < 0) 287 if (child < 0)
284 errExit("fork"); 288 errExit("fork");
285 if (child == 0) { 289 if (child == 0) {
290 EUID_ROOT();
286 sbox_do_exec_v(filtermask, arg); 291 sbox_do_exec_v(filtermask, arg);
287 } 292 }
288 293
@@ -290,8 +295,9 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
290 if (waitpid(child, &status, 0) == -1 ) { 295 if (waitpid(child, &status, 0) == -1 ) {
291 errExit("waitpid"); 296 errExit("waitpid");
292 } 297 }
293 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { 298 if (WIFSIGNALED(status) ||
294 fprintf(stderr, "Error: failed to run %s\n", arg[0]); 299 (WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
300 fprintf(stderr, "Error: failed to run %s, exiting...\n", arg[0]);
295 exit(1); 301 exit(1);
296 } 302 }
297 303
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c
index 785c29517..0cd6ac7ec 100644
--- a/src/firejail/seccomp.c
+++ b/src/firejail/seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -86,7 +86,7 @@ int seccomp_install_filters(void) {
86static void seccomp_save_file_list(const char *fname) { 86static void seccomp_save_file_list(const char *fname) {
87 assert(fname); 87 assert(fname);
88 88
89 FILE *fp = fopen(RUN_SECCOMP_LIST, "a+"); 89 FILE *fp = fopen(RUN_SECCOMP_LIST, "ae");
90 if (!fp) 90 if (!fp)
91 errExit("fopen"); 91 errExit("fopen");
92 92
@@ -99,7 +99,7 @@ static void seccomp_save_file_list(const char *fname) {
99#define MAXBUF 4096 99#define MAXBUF 4096
100static int load_file_list_flag = 0; 100static int load_file_list_flag = 0;
101void seccomp_load_file_list(void) { 101void seccomp_load_file_list(void) {
102 FILE *fp = fopen(RUN_SECCOMP_LIST, "r"); 102 FILE *fp = fopen(RUN_SECCOMP_LIST, "re");
103 if (!fp) 103 if (!fp)
104 return; // no seccomp configuration whatsoever 104 return; // no seccomp configuration whatsoever
105 105
@@ -122,7 +122,7 @@ int seccomp_load(const char *fname) {
122 assert(fname); 122 assert(fname);
123 123
124 // open filter file 124 // open filter file
125 int fd = open(fname, O_RDONLY); 125 int fd = open(fname, O_RDONLY|O_CLOEXEC);
126 if (fd == -1) 126 if (fd == -1)
127 goto errexit; 127 goto errexit;
128 128
@@ -208,7 +208,8 @@ int seccomp_filter_drop(bool native) {
208 // - seccomp 208 // - seccomp
209 if (cfg.seccomp_list_drop == NULL) { 209 if (cfg.seccomp_list_drop == NULL) {
210 // default seccomp if error action is not changed 210 // default seccomp if error action is not changed
211 if (cfg.seccomp_list == NULL && arg_seccomp_error_action == DEFAULT_SECCOMP_ERROR_ACTION) { 211 if ((cfg.seccomp_list == NULL || cfg.seccomp_list[0] == '\0')
212 && arg_seccomp_error_action == DEFAULT_SECCOMP_ERROR_ACTION) {
212 if (arg_seccomp_block_secondary) 213 if (arg_seccomp_block_secondary)
213 seccomp_filter_block_secondary(); 214 seccomp_filter_block_secondary();
214 else { 215 else {
@@ -261,7 +262,7 @@ int seccomp_filter_drop(bool native) {
261 } 262 }
262 263
263 // build the seccomp filter as a regular user 264 // build the seccomp filter as a regular user
264 if (list) 265 if (list && list[0])
265 if (arg_allow_debuggers) 266 if (arg_allow_debuggers)
266 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, 267 rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7,
267 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list, "allow-debuggers"); 268 PATH_FSECCOMP, command, "drop", filter, postexec_filter, list, "allow-debuggers");
@@ -434,11 +435,11 @@ void seccomp_print_filter(pid_t pid) {
434 if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_LIST) == -1) 435 if (asprintf(&fname, "/proc/%d/root%s", pid, RUN_SECCOMP_LIST) == -1)
435 errExit("asprintf"); 436 errExit("asprintf");
436 437
437 struct stat s; 438 int fd = open(fname, O_RDONLY|O_CLOEXEC);
438 if (stat(fname, &s) == -1) 439 if (fd < 0)
439 goto errexit; 440 goto errexit;
440 441
441 FILE *fp = fopen(fname, "r"); 442 FILE *fp = fdopen(fd, "r");
442 if (!fp) 443 if (!fp)
443 goto errexit; 444 goto errexit;
444 free(fname); 445 free(fname);
diff --git a/src/firejail/selinux.c b/src/firejail/selinux.c
index 06189d7f6..0348cef4b 100644
--- a/src/firejail/selinux.c
+++ b/src/firejail/selinux.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2020-2021 Firejail and systemd authors 2 * Copyright (C) 2020-2022 Firejail and systemd authors
3 * 3 *
4 * This file is part of firejail project, from systemd selinux-util.c 4 * This file is part of firejail project, from systemd selinux-util.c
5 * 5 *
@@ -19,10 +19,14 @@
19*/ 19*/
20#if HAVE_SELINUX 20#if HAVE_SELINUX
21#include "firejail.h" 21#include "firejail.h"
22
23#include <sys/types.h> 22#include <sys/types.h>
24#include <sys/stat.h> 23#include <sys/stat.h>
24#include <errno.h>
25
25#include <fcntl.h> 26#include <fcntl.h>
27#ifndef O_PATH
28#define O_PATH 010000000
29#endif
26 30
27#include <selinux/context.h> 31#include <selinux/context.h>
28#include <selinux/label.h> 32#include <selinux/label.h>
@@ -52,8 +56,19 @@ void selinux_relabel_path(const char *path, const char *inside_path)
52 if (!label_hnd) 56 if (!label_hnd)
53 errExit("selabel_open"); 57 errExit("selabel_open");
54 58
55 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 59 /* Open the file as O_PATH, to pin it while we determine and adjust the label
56 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 60 * Defeat symlink races by not allowing symbolic links */
61 int called_as_root = 0;
62 if (geteuid() == 0)
63 called_as_root = 1;
64 if (called_as_root)
65 EUID_USER();
66
67 fd = safer_openat(-1, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
68
69 if (called_as_root)
70 EUID_ROOT();
71
57 if (fd < 0) 72 if (fd < 0)
58 return; 73 return;
59 if (fstat(fd, &st) < 0) 74 if (fstat(fd, &st) < 0)
@@ -64,8 +79,16 @@ void selinux_relabel_path(const char *path, const char *inside_path)
64 if (arg_debug) 79 if (arg_debug)
65 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 80 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
66 81
67 setfilecon_raw(procfs_path, fcon); 82 if (!called_as_root)
83 EUID_ROOT();
84
85 if (setfilecon_raw(procfs_path, fcon) != 0 && arg_debug)
86 printf("Cannot relabel %s: %s\n", path, strerror(errno));
87
88 if (!called_as_root)
89 EUID_USER();
68 } 90 }
91
69 freecon(fcon); 92 freecon(fcon);
70 close: 93 close:
71 close(fd); 94 close(fd);
diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c
index 8fb03d0a6..44fdd58ab 100644
--- a/src/firejail/shutdown.c
+++ b/src/firejail/shutdown.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -36,8 +36,10 @@ void shut(pid_t pid) {
36 } 36 }
37 free(comm); 37 free(comm);
38 } 38 }
39 else 39 else {
40 errExit("/proc/PID/comm"); 40 fprintf(stderr, "Error: cannot find process %d\n", pid);
41 exit(1);
42 }
41 43
42 // check privileges for non-root users 44 // check privileges for non-root users
43 uid_t uid = getuid(); 45 uid_t uid = getuid();
@@ -64,7 +66,7 @@ void shut(pid_t pid) {
64 monsec--; 66 monsec--;
65 67
66 EUID_ROOT(); 68 EUID_ROOT();
67 FILE *fp = fopen(monfile, "r"); 69 FILE *fp = fopen(monfile, "re");
68 EUID_USER(); 70 EUID_USER();
69 if (!fp) { 71 if (!fp) {
70 killdone = 1; 72 killdone = 1;
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index 397150158..2dd913b5e 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -58,16 +58,18 @@ static char *usage_str =
58#ifdef HAVE_DBUSPROXY 58#ifdef HAVE_DBUSPROXY
59 " --dbus-log=file - set DBus log file location.\n" 59 " --dbus-log=file - set DBus log file location.\n"
60 " --dbus-system=filter|none - set system DBus access policy.\n" 60 " --dbus-system=filter|none - set system DBus access policy.\n"
61 " --dbus-system.broadcast=rule - allow signals on the system DBus according to rule.\n" 61 " --dbus-system.broadcast=rule - allow signals on the system DBus according\n"
62 "\tto rule.\n"
62 " --dbus-system.call=rule - allow calls on the system DBus according to rule.\n" 63 " --dbus-system.call=rule - allow calls on the system DBus according to rule.\n"
63 " --dbus-system.log - turn on logging for the system DBus." 64 " --dbus-system.log - turn on logging for the system DBus.\n"
64 " --dbus-system.own=name - allow ownership of name on the system DBus.\n" 65 " --dbus-system.own=name - allow ownership of name on the system DBus.\n"
65 " --dbus-system.see=name - allow seeing name on the system DBus.\n" 66 " --dbus-system.see=name - allow seeing name on the system DBus.\n"
66 " --dbus-system.talk=name - allow talking to name on the system DBus.\n" 67 " --dbus-system.talk=name - allow talking to name on the system DBus.\n"
67 " --dbus-user=filter|none - set session DBus access policy.\n" 68 " --dbus-user=filter|none - set session DBus access policy.\n"
68 " --dbus-user.broadcast=rule - allow signals on the session DBus according to rule.\n" 69 " --dbus-user.broadcast=rule - allow signals on the session DBus according\n"
70 "\tto rule.\n"
69 " --dbus-user.call=rule - allow calls on the session DBus according to rule.\n" 71 " --dbus-user.call=rule - allow calls on the session DBus according to rule.\n"
70 " --dbus-user.log - turn on logging for the user DBus." 72 " --dbus-user.log - turn on logging for the user DBus.\n"
71 " --dbus-user.own=name - allow ownership of name on the session DBus.\n" 73 " --dbus-user.own=name - allow ownership of name on the session DBus.\n"
72 " --dbus-user.see=name - allow seeing name on the session DBus.\n" 74 " --dbus-user.see=name - allow seeing name on the session DBus.\n"
73 " --dbus-user.talk=name - allow talking to name on the session DBus.\n" 75 " --dbus-user.talk=name - allow talking to name on the session DBus.\n"
@@ -80,13 +82,12 @@ static char *usage_str =
80 " --debug-protocols - print all recognized protocols.\n" 82 " --debug-protocols - print all recognized protocols.\n"
81 " --debug-syscalls - print all recognized system calls.\n" 83 " --debug-syscalls - print all recognized system calls.\n"
82 " --debug-syscalls32 - print all recognized 32 bit system calls.\n" 84 " --debug-syscalls32 - print all recognized 32 bit system calls.\n"
83#ifdef HAVE_WHITELIST
84 " --debug-whitelists - debug whitelisting.\n" 85 " --debug-whitelists - debug whitelisting.\n"
85#endif
86#ifdef HAVE_NETWORK 86#ifdef HAVE_NETWORK
87 " --defaultgw=address - configure default gateway.\n" 87 " --defaultgw=address - configure default gateway.\n"
88#endif 88#endif
89 " --deterministic-exit-code - always exit with first child's status code.\n" 89 " --deterministic-exit-code - always exit with first child's status code.\n"
90 " --deterministic-shutdown - terminate orphan processes.\n"
90 " --dns=address - set DNS server.\n" 91 " --dns=address - set DNS server.\n"
91 " --dns.print=name|pid - print DNS configuration.\n" 92 " --dns.print=name|pid - print DNS configuration.\n"
92 " --env=name=value - set environment variable.\n" 93 " --env=name=value - set environment variable.\n"
@@ -97,6 +98,8 @@ static char *usage_str =
97 " --help, -? - this help screen.\n" 98 " --help, -? - this help screen.\n"
98 " --hostname=name - set sandbox hostname.\n" 99 " --hostname=name - set sandbox hostname.\n"
99 " --hosts-file=file - use file as /etc/hosts.\n" 100 " --hosts-file=file - use file as /etc/hosts.\n"
101 " --ids-check - verify file system.\n"
102 " --ids-init - initialize IDS database.\n"
100 " --ignore=command - ignore command in profile files.\n" 103 " --ignore=command - ignore command in profile files.\n"
101#ifdef HAVE_NETWORK 104#ifdef HAVE_NETWORK
102 " --interface=name - move interface in sandbox.\n" 105 " --interface=name - move interface in sandbox.\n"
@@ -114,7 +117,9 @@ static char *usage_str =
114 " --join-network=name|pid - join the network namespace.\n" 117 " --join-network=name|pid - join the network namespace.\n"
115#endif 118#endif
116 " --join-or-start=name|pid - join the sandbox or start a new one.\n" 119 " --join-or-start=name|pid - join the sandbox or start a new one.\n"
117 " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" 120 " --keep-config-pulse - disable automatic ~/.config/pulse init.\n"
121 " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n"
122 " --keep-fd - inherit open file descriptors to sandbox.\n"
118 " --keep-var-tmp - /var/tmp directory is untouched.\n" 123 " --keep-var-tmp - /var/tmp directory is untouched.\n"
119 " --list - list all sandboxes.\n" 124 " --list - list all sandboxes.\n"
120#ifdef HAVE_FILE_TRANSFER 125#ifdef HAVE_FILE_TRANSFER
@@ -123,7 +128,7 @@ static char *usage_str =
123#ifdef HAVE_NETWORK 128#ifdef HAVE_NETWORK
124 " --mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n" 129 " --mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n"
125#endif 130#endif
126 " --machine-id - preserve /etc/machine-id\n" 131 " --machine-id - spoof /etc/machine-id with a random id\n"
127 " --memory-deny-write-execute - seccomp filter to block attempts to create\n" 132 " --memory-deny-write-execute - seccomp filter to block attempts to create\n"
128 "\tmemory mappings that are both writable and executable.\n" 133 "\tmemory mappings that are both writable and executable.\n"
129 " --mkdir=dirname - create a directory.\n" 134 " --mkdir=dirname - create a directory.\n"
@@ -142,10 +147,12 @@ static char *usage_str =
142 " --netfilter.print=name|pid - print the firewall.\n" 147 " --netfilter.print=name|pid - print the firewall.\n"
143 " --netfilter6=filename - enable IPv6 firewall.\n" 148 " --netfilter6=filename - enable IPv6 firewall.\n"
144 " --netfilter6.print=name|pid - print the IPv6 firewall.\n" 149 " --netfilter6.print=name|pid - print the IPv6 firewall.\n"
145 " --netmask=address - define a network mask when dealing with unconfigured" 150 " --netlock - enable the network locking feature\n"
146 "\tparrent interfaces.\n" 151 " --netmask=address - define a network mask when dealing with unconfigured\n"
152 "\tparent interfaces.\n"
147 " --netns=name - Run the program in a named, persistent network namespace.\n" 153 " --netns=name - Run the program in a named, persistent network namespace.\n"
148 " --netstats - monitor network statistics.\n" 154 " --netstats - monitor network statistics.\n"
155 " --nettrace - monitor TCP and UDP traffic coming into the sandbox.\n"
149#endif 156#endif
150 " --nice=value - set nice value.\n" 157 " --nice=value - set nice value.\n"
151 " --no3d - disable 3D hardware acceleration.\n" 158 " --no3d - disable 3D hardware acceleration.\n"
@@ -154,7 +161,9 @@ static char *usage_str =
154 " --nodvd - disable DVD and audio CD devices.\n" 161 " --nodvd - disable DVD and audio CD devices.\n"
155 " --noexec=filename - remount the file or directory noexec nosuid and nodev.\n" 162 " --noexec=filename - remount the file or directory noexec nosuid and nodev.\n"
156 " --nogroups - disable supplementary groups.\n" 163 " --nogroups - disable supplementary groups.\n"
164 " --noinput - disable input devices.\n"
157 " --nonewprivs - sets the NO_NEW_PRIVS prctl.\n" 165 " --nonewprivs - sets the NO_NEW_PRIVS prctl.\n"
166 " --noprinters - disable printers.\n"
158 " --noprofile - do not use a security profile.\n" 167 " --noprofile - do not use a security profile.\n"
159#ifdef HAVE_USERNS 168#ifdef HAVE_USERNS
160 " --noroot - install a user namespace with only the current user.\n" 169 " --noroot - install a user namespace with only the current user.\n"
@@ -236,6 +245,8 @@ static char *usage_str =
236 " --shell=none - run the program directly without a user shell.\n" 245 " --shell=none - run the program directly without a user shell.\n"
237 " --shell=program - set default user shell.\n" 246 " --shell=program - set default user shell.\n"
238 " --shutdown=name|pid - shutdown the sandbox identified by name or PID.\n" 247 " --shutdown=name|pid - shutdown the sandbox identified by name or PID.\n"
248 " --tab - enable shell tab completion in sandboxes using private or\n"
249 "\twhitelisted home directories.\n"
239 " --timeout=hh:mm:ss - kill the sandbox automatically after the time\n" 250 " --timeout=hh:mm:ss - kill the sandbox automatically after the time\n"
240 "\thas elapsed.\n" 251 "\thas elapsed.\n"
241 " --tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n" 252 " --tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n"
@@ -250,9 +261,7 @@ static char *usage_str =
250#ifdef HAVE_NETWORK 261#ifdef HAVE_NETWORK
251 " --veth-name=name - use this name for the interface connected to the bridge.\n" 262 " --veth-name=name - use this name for the interface connected to the bridge.\n"
252#endif 263#endif
253#ifdef HAVE_WHITELIST
254 " --whitelist=filename - whitelist directory or file.\n" 264 " --whitelist=filename - whitelist directory or file.\n"
255#endif
256 " --writable-etc - /etc directory is mounted read-write.\n" 265 " --writable-etc - /etc directory is mounted read-write.\n"
257 " --writable-run-user - allow access to /run/user/$UID/systemd and\n" 266 " --writable-run-user - allow access to /run/user/$UID/systemd and\n"
258 "\t/run/user/$UID/gnupg.\n" 267 "\t/run/user/$UID/gnupg.\n"
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 2ad85acd6..109105630 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -19,8 +19,7 @@
19 */ 19 */
20#define _XOPEN_SOURCE 500 20#define _XOPEN_SOURCE 500
21#include "firejail.h" 21#include "firejail.h"
22#include <ftw.h> 22#include "../include/gcov_wrapper.h"
23#include <sys/stat.h>
24#include <sys/mount.h> 23#include <sys/mount.h>
25#include <syslog.h> 24#include <syslog.h>
26#include <errno.h> 25#include <errno.h>
@@ -46,6 +45,44 @@
46#define EMPTY_STRING ("") 45#define EMPTY_STRING ("")
47 46
48 47
48long long unsigned parse_arg_size(char *str) {
49 long long unsigned result = 0;
50 int len = strlen(str);
51 sscanf(str, "%llu", &result);
52
53 char suffix = *(str + len - 1);
54 if (!isdigit(suffix) && (suffix == 'k' || suffix == 'm' || suffix == 'g')) {
55 len -= 1;
56 }
57
58 /* checks for is value valid positive number */
59 for (int i = 0; i < len; i++) {
60 if (!isdigit(*(str+i))) {
61 return 0;
62 }
63 }
64
65 if (isdigit(suffix))
66 return result;
67
68 switch (suffix) {
69 case 'k':
70 result *= 1024;
71 break;
72 case 'm':
73 result *= 1024 * 1024;
74 break;
75 case 'g':
76 result *= 1024 * 1024 * 1024;
77 break;
78 default:
79 result = 0;
80 break;
81 }
82
83 return result;
84}
85
49// send the error to /var/log/auth.log and exit after a small delay 86// send the error to /var/log/auth.log and exit after a small delay
50void errLogExit(char* fmt, ...) { 87void errLogExit(char* fmt, ...) {
51 va_list args; 88 va_list args;
@@ -66,6 +103,72 @@ void errLogExit(char* fmt, ...) {
66 exit(1); 103 exit(1);
67} 104}
68 105
106// Returns whether all supplementary groups can be safely dropped
107int check_can_drop_all_groups() {
108 static int can_drop_all_groups = -1;
109
110 // Avoid needlessly checking (and printing) things twice
111 if (can_drop_all_groups != -1)
112 goto out;
113
114 // nvidia cards require video group; ignore nogroups
115 if (access("/dev/nvidiactl", R_OK) == 0 && arg_no3d == 0) {
116 fwarning("NVIDIA card detected, nogroups command ignored\n");
117 can_drop_all_groups = 0;
118 goto out;
119 }
120
121 /* When we are not sure that the system has working seat-based ACLs
122 * (e.g.: probably yes on (e)udev + (e)logind, probably not on eudev +
123 * seatd), supplementary groups (e.g.: audio and input) might be needed
124 * to avoid breakage (e.g.: audio or gamepads not working). See #4600
125 * and #4603.
126 */
127 if (access("/run/systemd/seats/", F_OK) != 0) {
128 // TODO: wrc causes this to be printed even with (e)logind (see #4930)
129 //fwarning("logind not detected, nogroups command ignored\n");
130 can_drop_all_groups = 0;
131 goto out;
132 }
133
134 if (arg_debug)
135 fprintf(stderr, "nogroups command not ignored\n");
136 can_drop_all_groups = 1;
137
138out:
139 return can_drop_all_groups;
140}
141
142static int find_group(gid_t group, const gid_t *groups, int ngroups) {
143 int i;
144 for (i = 0; i < ngroups; i++) {
145 if (group == groups[i])
146 return i;
147 }
148
149 return -1;
150}
151
152// Gets group from "groupname" and adds it to "new_groups" if it exists on
153// "groups". Always returns the current value of new_ngroups.
154static int copy_group_ifcont(const char *groupname,
155 const gid_t *groups, int ngroups,
156 gid_t *new_groups, int *new_ngroups, int new_sz) {
157 if (*new_ngroups >= new_sz) {
158 errno = ERANGE;
159 goto out;
160 }
161
162 gid_t g = get_group_id(groupname);
163 if (g && find_group(g, groups, ngroups) >= 0) {
164 new_groups[*new_ngroups] = g;
165 (*new_ngroups)++;
166 }
167
168out:
169 return *new_ngroups;
170}
171
69static void clean_supplementary_groups(gid_t gid) { 172static void clean_supplementary_groups(gid_t gid) {
70 assert(cfg.username); 173 assert(cfg.username);
71 gid_t groups[MAX_GROUPS]; 174 gid_t groups[MAX_GROUPS];
@@ -74,35 +177,60 @@ static void clean_supplementary_groups(gid_t gid) {
74 if (rv == -1) 177 if (rv == -1)
75 goto clean_all; 178 goto clean_all;
76 179
180 if (arg_nogroups && check_can_drop_all_groups())
181 goto clean_all;
182
77 // clean supplementary group list 183 // clean supplementary group list
78 // allow only firejail, tty, audio, video, games
79 gid_t new_groups[MAX_GROUPS]; 184 gid_t new_groups[MAX_GROUPS];
80 int new_ngroups = 0; 185 int new_ngroups = 0;
81 char *allowed[] = { 186 char *allowed[] = {
82 "firejail", 187 "firejail",
83 "tty", 188 "tty",
84 "audio",
85 "video",
86 "games", 189 "games",
87 NULL 190 NULL
88 }; 191 };
89 192
90 int i = 0; 193 int i = 0;
91 while (allowed[i]) { 194 while (allowed[i]) {
92 gid_t g = get_group_id(allowed[i]); 195 copy_group_ifcont(allowed[i], groups, ngroups,
93 if (g) { 196 new_groups, &new_ngroups, MAX_GROUPS);
94 int j;
95 for (j = 0; j < ngroups; j++) {
96 if (g == groups[j]) {
97 new_groups[new_ngroups] = g;
98 new_ngroups++;
99 break;
100 }
101 }
102 }
103 i++; 197 i++;
104 } 198 }
105 199
200 if (!arg_nosound) {
201 copy_group_ifcont("audio", groups, ngroups,
202 new_groups, &new_ngroups, MAX_GROUPS);
203 }
204
205 if (!arg_novideo) {
206 copy_group_ifcont("video", groups, ngroups,
207 new_groups, &new_ngroups, MAX_GROUPS);
208 }
209
210 if (!arg_no3d) {
211 copy_group_ifcont("render", groups, ngroups,
212 new_groups, &new_ngroups, MAX_GROUPS);
213 copy_group_ifcont("vglusers", groups, ngroups,
214 new_groups, &new_ngroups, MAX_GROUPS);
215 }
216
217 if (!arg_noprinters) {
218 copy_group_ifcont("lp", groups, ngroups,
219 new_groups, &new_ngroups, MAX_GROUPS);
220 }
221
222 if (!arg_nodvd) {
223 copy_group_ifcont("cdrom", groups, ngroups,
224 new_groups, &new_ngroups, MAX_GROUPS);
225 copy_group_ifcont("optical", groups, ngroups,
226 new_groups, &new_ngroups, MAX_GROUPS);
227 }
228
229 if (!arg_noinput) {
230 copy_group_ifcont("input", groups, ngroups,
231 new_groups, &new_ngroups, MAX_GROUPS);
232 }
233
106 if (new_ngroups) { 234 if (new_ngroups) {
107 rv = setgroups(new_ngroups, new_groups); 235 rv = setgroups(new_ngroups, new_groups);
108 if (rv) 236 if (rv)
@@ -128,21 +256,22 @@ clean_all:
128 256
129 257
130// drop privileges 258// drop privileges
131// - for root group or if nogroups is set, supplementary groups are not configured 259// - for root group or if force_nogroups is set, supplementary groups are not configured
132void drop_privs(int nogroups) { 260void drop_privs(int force_nogroups) {
133 gid_t gid = getgid(); 261 gid_t gid = getgid();
134 if (arg_debug) 262 if (arg_debug)
135 printf("Drop privileges: pid %d, uid %d, gid %d, nogroups %d\n", getpid(), getuid(), gid, nogroups); 263 printf("Drop privileges: pid %d, uid %d, gid %d, force_nogroups %d\n",
264 getpid(), getuid(), gid, force_nogroups);
136 265
137 // configure supplementary groups 266 // configure supplementary groups
138 EUID_ROOT(); 267 EUID_ROOT();
139 if (gid == 0 || nogroups) { 268 if (gid == 0 || force_nogroups) {
140 if (setgroups(0, NULL) < 0) 269 if (setgroups(0, NULL) < 0)
141 errExit("setgroups"); 270 errExit("setgroups");
142 if (arg_debug) 271 if (arg_debug)
143 printf("No supplementary groups\n"); 272 printf("No supplementary groups\n");
144 } 273 }
145 else if (arg_noroot) 274 else if (arg_noroot || arg_nogroups)
146 clean_supplementary_groups(gid); 275 clean_supplementary_groups(gid);
147 276
148 // set uid/gid 277 // set uid/gid
@@ -298,14 +427,14 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
298 assert(destname); 427 assert(destname);
299 428
300 // open source 429 // open source
301 int src = open(srcname, O_RDONLY); 430 int src = open(srcname, O_RDONLY|O_CLOEXEC);
302 if (src < 0) { 431 if (src < 0) {
303 fwarning("cannot open source file %s, file not copied\n", srcname); 432 fwarning("cannot open source file %s, file not copied\n", srcname);
304 return -1; 433 return -1;
305 } 434 }
306 435
307 // open destination 436 // open destination
308 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 437 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
309 if (dst < 0) { 438 if (dst < 0) {
310 fwarning("cannot open destination file %s, file not copied\n", destname); 439 fwarning("cannot open destination file %s, file not copied\n", destname);
311 close(src); 440 close(src);
@@ -325,7 +454,7 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
325} 454}
326 455
327// return -1 if error, 0 if no error 456// return -1 if error, 0 if no error
328void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { 457void copy_file_as_user(const char *srcname, const char *destname, mode_t mode) {
329 pid_t child = fork(); 458 pid_t child = fork();
330 if (child < 0) 459 if (child < 0)
331 errExit("fork"); 460 errExit("fork");
@@ -333,13 +462,13 @@ void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid
333 // drop privileges 462 // drop privileges
334 drop_privs(0); 463 drop_privs(0);
335 464
336 // copy, set permissions and ownership 465 // copy, set permissions
337 int rv = copy_file(srcname, destname, uid, gid, mode); // already a regular user 466 int rv = copy_file(srcname, destname, -1, -1, mode); // already a regular user
338 if (rv) 467 if (rv)
339 fwarning("cannot copy %s\n", srcname); 468 fwarning("cannot copy %s\n", srcname);
340#ifdef HAVE_GCOV 469
341 __gcov_flush(); 470 __gcov_flush();
342#endif 471
343 _exit(0); 472 _exit(0);
344 } 473 }
345 // wait for the child to finish 474 // wait for the child to finish
@@ -348,7 +477,7 @@ void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid
348 477
349void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { 478void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
350 // open destination 479 // open destination
351 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 480 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
352 if (dst < 0) { 481 if (dst < 0) {
353 fwarning("cannot open destination file %s, file not copied\n", destname); 482 fwarning("cannot open destination file %s, file not copied\n", destname);
354 return; 483 return;
@@ -361,7 +490,7 @@ void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_
361 // drop privileges 490 // drop privileges
362 drop_privs(0); 491 drop_privs(0);
363 492
364 int src = open(srcname, O_RDONLY); 493 int src = open(srcname, O_RDONLY|O_CLOEXEC);
365 if (src < 0) { 494 if (src < 0) {
366 fwarning("cannot open source file %s, file not copied\n", srcname); 495 fwarning("cannot open source file %s, file not copied\n", srcname);
367 } else { 496 } else {
@@ -371,9 +500,9 @@ void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_
371 close(src); 500 close(src);
372 } 501 }
373 close(dst); 502 close(dst);
374#ifdef HAVE_GCOV 503
375 __gcov_flush(); 504 __gcov_flush();
376#endif 505
377 _exit(0); 506 _exit(0);
378 } 507 }
379 // wait for the child to finish 508 // wait for the child to finish
@@ -394,17 +523,17 @@ void touch_file_as_user(const char *fname, mode_t mode) {
394 // drop privileges 523 // drop privileges
395 drop_privs(0); 524 drop_privs(0);
396 525
397 FILE *fp = fopen(fname, "wx"); 526 int fd = open(fname, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR);
398 if (fp) { 527 if (fd > -1) {
399 fprintf(fp, "\n"); 528 int err = fchmod(fd, mode);
400 SET_PERMS_STREAM(fp, -1, -1, mode); 529 (void) err;
401 fclose(fp); 530 close(fd);
402 } 531 }
403 else 532 else
404 fwarning("cannot create %s\n", fname); 533 fwarning("cannot create %s\n", fname);
405#ifdef HAVE_GCOV 534
406 __gcov_flush(); 535 __gcov_flush();
407#endif 536
408 _exit(0); 537 _exit(0);
409 } 538 }
410 // wait for the child to finish 539 // wait for the child to finish
@@ -421,14 +550,14 @@ int is_dir(const char *fname) {
421 int rv; 550 int rv;
422 struct stat s; 551 struct stat s;
423 if (fname[strlen(fname) - 1] == '/') 552 if (fname[strlen(fname) - 1] == '/')
424 rv = stat(fname, &s); 553 rv = stat_as_user(fname, &s);
425 else { 554 else {
426 char *tmp; 555 char *tmp;
427 if (asprintf(&tmp, "%s/", fname) == -1) { 556 if (asprintf(&tmp, "%s/", fname) == -1) {
428 fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__); 557 fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__);
429 errExit("asprintf"); 558 errExit("asprintf");
430 } 559 }
431 rv = stat(tmp, &s); 560 rv = stat_as_user(tmp, &s);
432 free(tmp); 561 free(tmp);
433 } 562 }
434 563
@@ -447,18 +576,91 @@ int is_link(const char *fname) {
447 if (*fname == '\0') 576 if (*fname == '\0')
448 return 0; 577 return 0;
449 578
450 char *dup = strdup(fname); 579 // remove trailing '/' if any
451 if (!dup) 580 char *tmp = strdup(fname);
581 if (!tmp)
452 errExit("strdup"); 582 errExit("strdup");
453 trim_trailing_slash_or_dot(dup); 583 trim_trailing_slash_or_dot(tmp);
454 584
455 char c; 585 char c;
456 ssize_t rv = readlink(dup, &c, 1); 586 ssize_t rv = readlink_as_user(tmp, &c, 1);
587 free(tmp);
457 588
458 free(dup);
459 return (rv != -1); 589 return (rv != -1);
460} 590}
461 591
592char *realpath_as_user(const char *fname) {
593 assert(fname);
594
595 int called_as_root = 0;
596 if (geteuid() == 0)
597 called_as_root = 1;
598
599 if (called_as_root)
600 EUID_USER();
601
602 char *rv = realpath(fname, NULL);
603
604 if (called_as_root)
605 EUID_ROOT();
606
607 return rv;
608}
609
610ssize_t readlink_as_user(const char *fname, char *buf, size_t sz) {
611 assert(fname && buf && sz);
612
613 int called_as_root = 0;
614 if (geteuid() == 0)
615 called_as_root = 1;
616
617 if (called_as_root)
618 EUID_USER();
619
620 ssize_t rv = readlink(fname, buf, sz);
621
622 if (called_as_root)
623 EUID_ROOT();
624
625 return rv;
626}
627
628int stat_as_user(const char *fname, struct stat *s) {
629 assert(fname);
630
631 int called_as_root = 0;
632 if (geteuid() == 0)
633 called_as_root = 1;
634
635 if (called_as_root)
636 EUID_USER();
637
638 int rv = stat(fname, s);
639
640 if (called_as_root)
641 EUID_ROOT();
642
643 return rv;
644}
645
646int lstat_as_user(const char *fname, struct stat *s) {
647 assert(fname);
648
649 int called_as_root = 0;
650 if (geteuid() == 0)
651 called_as_root = 1;
652
653 if (called_as_root)
654 EUID_USER();
655
656 int rv = lstat(fname, s);
657
658 if (called_as_root)
659 EUID_ROOT();
660
661 return rv;
662}
663
462// remove all slashes and single dots from the end of a path 664// remove all slashes and single dots from the end of a path
463// for example /foo/bar///././. -> /foo/bar 665// for example /foo/bar///././. -> /foo/bar
464void trim_trailing_slash_or_dot(char *path) { 666void trim_trailing_slash_or_dot(char *path) {
@@ -544,11 +746,13 @@ char *split_comma(char *str) {
544} 746}
545 747
546 748
547// remove consecutive and trailing slashes 749// simplify absolute path by removing
548// and return allocated memory 750// 1) consecutive and trailing slashes, and
549// e.g. /home//user/ -> /home/user 751// 2) segments with a single dot
752// for example /foo//./bar/ -> /foo/bar
550char *clean_pathname(const char *path) { 753char *clean_pathname(const char *path) {
551 assert(path); 754 assert(path && path[0] == '/');
755
552 size_t len = strlen(path); 756 size_t len = strlen(path);
553 char *rv = malloc(len + 1); 757 char *rv = malloc(len + 1);
554 if (!rv) 758 if (!rv)
@@ -557,15 +761,23 @@ char *clean_pathname(const char *path) {
557 size_t i = 0; 761 size_t i = 0;
558 size_t j = 0; 762 size_t j = 0;
559 while (path[i]) { 763 while (path[i]) {
560 while (path[i] == '/' && path[i+1] == '/') 764 if (path[i] == '/') {
561 i++; 765 while (path[i+1] == '/' ||
766 (path[i+1] == '.' && path[i+2] == '/'))
767 i++;
768 }
769
562 rv[j++] = path[i++]; 770 rv[j++] = path[i++];
563 } 771 }
564 rv[j] = '\0'; 772 rv[j] = '\0';
565 773
774 // remove a trailing dot
775 if (j > 1 && rv[j - 1] == '.' && rv[j - 2] == '/')
776 rv[--j] = '\0';
777
566 // remove a trailing slash 778 // remove a trailing slash
567 if (j > 1 && rv[j - 1] == '/') 779 if (j > 1 && rv[j - 1] == '/')
568 rv[j - 1] = '\0'; 780 rv[--j] = '\0';
569 781
570 return rv; 782 return rv;
571} 783}
@@ -616,7 +828,7 @@ int find_child(pid_t parent, pid_t *child) {
616 perror("asprintf"); 828 perror("asprintf");
617 exit(1); 829 exit(1);
618 } 830 }
619 FILE *fp = fopen(file, "r"); 831 FILE *fp = fopen(file, "re");
620 if (!fp) { 832 if (!fp) {
621 free(file); 833 free(file);
622 continue; 834 continue;
@@ -637,9 +849,11 @@ int find_child(pid_t parent, pid_t *child) {
637 if (parent == atoi(ptr)) { 849 if (parent == atoi(ptr)) {
638 // we don't want /usr/bin/xdg-dbus-proxy! 850 // we don't want /usr/bin/xdg-dbus-proxy!
639 char *cmdline = pid_proc_cmdline(pid); 851 char *cmdline = pid_proc_cmdline(pid);
640 if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0) 852 if (cmdline) {
641 *child = pid; 853 if (strncmp(cmdline, XDG_DBUS_PROXY_PATH, strlen(XDG_DBUS_PROXY_PATH)) != 0)
642 free(cmdline); 854 *child = pid;
855 free(cmdline);
856 }
643 } 857 }
644 break; // stop reading the file 858 break; // stop reading the file
645 } 859 }
@@ -722,7 +936,7 @@ void update_map(char *mapping, char *map_file) {
722 if (mapping[j] == ',') 936 if (mapping[j] == ',')
723 mapping[j] = '\n'; 937 mapping[j] = '\n';
724 938
725 fd = open(map_file, O_RDWR); 939 fd = open(map_file, O_RDWR|O_CLOEXEC);
726 if (fd == -1) { 940 if (fd == -1) {
727 fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno)); 941 fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno));
728 exit(EXIT_FAILURE); 942 exit(EXIT_FAILURE);
@@ -742,9 +956,9 @@ void wait_for_other(int fd) {
742 // wait for the parent to be initialized 956 // wait for the parent to be initialized
743 //**************************** 957 //****************************
744 char childstr[BUFLEN + 1]; 958 char childstr[BUFLEN + 1];
745 int newfd = dup(fd); 959 int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
746 if (newfd == -1) 960 if (newfd == -1)
747 errExit("dup"); 961 errExit("fcntl");
748 FILE* stream; 962 FILE* stream;
749 stream = fdopen(newfd, "r"); 963 stream = fdopen(newfd, "r");
750 *childstr = '\0'; 964 *childstr = '\0';
@@ -791,9 +1005,9 @@ void wait_for_other(int fd) {
791 1005
792void notify_other(int fd) { 1006void notify_other(int fd) {
793 FILE* stream; 1007 FILE* stream;
794 int newfd = dup(fd); 1008 int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
795 if (newfd == -1) 1009 if (newfd == -1)
796 errExit("dup"); 1010 errExit("fcntl");
797 stream = fdopen(newfd, "w"); 1011 stream = fdopen(newfd, "w");
798 fprintf(stream, "arg_noroot=%d\n", arg_noroot); 1012 fprintf(stream, "arg_noroot=%d\n", arg_noroot);
799 fflush(stream); 1013 fflush(stream);
@@ -811,7 +1025,7 @@ uid_t pid_get_uid(pid_t pid) {
811 exit(1); 1025 exit(1);
812 } 1026 }
813 EUID_ROOT(); // grsecurity fix 1027 EUID_ROOT(); // grsecurity fix
814 FILE *fp = fopen(file, "r"); 1028 FILE *fp = fopen(file, "re");
815 if (!fp) { 1029 if (!fp) {
816 free(file); 1030 free(file);
817 fprintf(stderr, "Error: cannot open /proc file\n"); 1031 fprintf(stderr, "Error: cannot open /proc file\n");
@@ -845,12 +1059,9 @@ uid_t pid_get_uid(pid_t pid) {
845} 1059}
846 1060
847 1061
848 1062gid_t get_group_id(const char *groupname) {
849
850uid_t get_group_id(const char *group) {
851 // find tty group id
852 gid_t gid = 0; 1063 gid_t gid = 0;
853 struct group *g = getgrnam(group); 1064 struct group *g = getgrnam(groupname);
854 if (g) 1065 if (g)
855 gid = g->gr_gid; 1066 gid = g->gr_gid;
856 1067
@@ -858,84 +1069,6 @@ uid_t get_group_id(const char *group) {
858} 1069}
859 1070
860 1071
861static int remove_callback(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
862 (void) sb;
863 (void) typeflag;
864 (void) ftwbuf;
865 assert(fpath);
866
867 if (strcmp(fpath, ".") == 0)
868 return 0;
869
870 if (remove(fpath)) { // removes the link not the actual file
871 perror("remove");
872 fprintf(stderr, "Error: cannot remove file from user .firejail directory: %s\n", fpath);
873 exit(1);
874 }
875
876 return 0;
877}
878
879
880int remove_overlay_directory(void) {
881 EUID_ASSERT();
882 struct stat s;
883 sleep(1);
884
885 char *path;
886 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
887 errExit("asprintf");
888
889 if (lstat(path, &s) == 0) {
890 // deal with obvious problems such as symlinks and root ownership
891 if (!S_ISDIR(s.st_mode)) {
892 if (S_ISLNK(s.st_mode))
893 fprintf(stderr, "Error: %s is a symbolic link\n", path);
894 else
895 fprintf(stderr, "Error: %s is not a directory\n", path);
896 exit(1);
897 }
898 if (s.st_uid != getuid()) {
899 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
900 exit(1);
901 }
902
903 pid_t child = fork();
904 if (child < 0)
905 errExit("fork");
906 if (child == 0) {
907 // open ~/.firejail, fails if there is any symlink
908 int fd = safe_fd(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
909 if (fd == -1)
910 errExit("safe_fd");
911 // chdir to ~/.firejail
912 if (fchdir(fd) == -1)
913 errExit("fchdir");
914 close(fd);
915
916 EUID_ROOT();
917 // FTW_PHYS - do not follow symbolic links
918 if (nftw(".", remove_callback, 64, FTW_DEPTH | FTW_PHYS) == -1)
919 errExit("nftw");
920
921 EUID_USER();
922 // remove ~/.firejail
923 if (rmdir(path) == -1)
924 errExit("rmdir");
925#ifdef HAVE_GCOV
926 __gcov_flush();
927#endif
928 _exit(0);
929 }
930 // wait for the child to finish
931 waitpid(child, NULL, 0);
932 // check if ~/.firejail was deleted
933 if (stat(path, &s) == 0)
934 return 1;
935 }
936 return 0;
937}
938
939// flush stdin if it is connected to a tty and has input 1072// flush stdin if it is connected to a tty and has input
940void flush_stdin(void) { 1073void flush_stdin(void) {
941 if (!isatty(STDIN_FILENO)) 1074 if (!isatty(STDIN_FILENO))
@@ -963,33 +1096,34 @@ void flush_stdin(void) {
963int create_empty_dir_as_user(const char *dir, mode_t mode) { 1096int create_empty_dir_as_user(const char *dir, mode_t mode) {
964 assert(dir); 1097 assert(dir);
965 mode &= 07777; 1098 mode &= 07777;
966 struct stat s;
967 1099
968 if (stat(dir, &s)) { 1100 if (access(dir, F_OK) == 0)
1101 return 0;
1102
1103 pid_t child = fork();
1104 if (child < 0)
1105 errExit("fork");
1106 if (child == 0) {
1107 // drop privileges
1108 drop_privs(0);
1109
969 if (arg_debug) 1110 if (arg_debug)
970 printf("Creating empty %s directory\n", dir); 1111 printf("Creating empty %s directory\n", dir);
971 pid_t child = fork(); 1112 if (mkdir(dir, mode) == 0) {
972 if (child < 0) 1113 int err = chmod(dir, mode);
973 errExit("fork"); 1114 (void) err;
974 if (child == 0) {
975 // drop privileges
976 drop_privs(0);
977
978 if (mkdir(dir, mode) == 0) {
979 if (chmod(dir, mode) == -1)
980 {;} // do nothing
981 }
982 else if (arg_debug)
983 printf("Directory %s not created: %s\n", dir, strerror(errno));
984#ifdef HAVE_GCOV
985 __gcov_flush();
986#endif
987 _exit(0);
988 } 1115 }
989 waitpid(child, NULL, 0); 1116 else if (arg_debug)
990 if (stat(dir, &s) == 0) 1117 printf("Directory %s not created: %s\n", dir, strerror(errno));
991 return 1; 1118
1119 __gcov_flush();
1120
1121 _exit(0);
992 } 1122 }
1123 waitpid(child, NULL, 0);
1124
1125 if (access(dir, F_OK) == 0)
1126 return 1;
993 return 0; 1127 return 0;
994} 1128}
995 1129
@@ -1020,9 +1154,10 @@ void create_empty_file_as_root(const char *fname, mode_t mode) {
1020 if (stat(fname, &s)) { 1154 if (stat(fname, &s)) {
1021 if (arg_debug) 1155 if (arg_debug)
1022 printf("Creating empty %s file\n", fname); 1156 printf("Creating empty %s file\n", fname);
1023
1024 /* coverity[toctou] */ 1157 /* coverity[toctou] */
1025 FILE *fp = fopen(fname, "w"); 1158 // don't fail if file already exists. This can be the case in a race
1159 // condition, when two jails launch at the same time. Compare to #1013
1160 FILE *fp = fopen(fname, "we");
1026 if (!fp) 1161 if (!fp)
1027 errExit("fopen"); 1162 errExit("fopen");
1028 SET_PERMS_STREAM(fp, 0, 0, mode); 1163 SET_PERMS_STREAM(fp, 0, 0, mode);
@@ -1097,20 +1232,35 @@ unsigned extract_timeout(const char *str) {
1097} 1232}
1098 1233
1099void disable_file_or_dir(const char *fname) { 1234void disable_file_or_dir(const char *fname) {
1235 assert(geteuid() == 0);
1236 assert(fname);
1237
1238 EUID_USER();
1239 int fd = open(fname, O_PATH|O_CLOEXEC);
1240 EUID_ROOT();
1241 if (fd < 0)
1242 return;
1243
1100 struct stat s; 1244 struct stat s;
1101 if (stat(fname, &s) != -1) { 1245 if (fstat(fd, &s) < 0) { // FUSE
1102 if (arg_debug) 1246 if (errno != EACCES)
1103 printf("blacklist %s\n", fname); 1247 errExit("fstat");
1104 if (is_dir(fname)) { 1248 close(fd);
1105 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1249 return;
1106 errExit("disable directory"); 1250 }
1107 } 1251
1108 else { 1252 if (arg_debug)
1109 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1253 printf("blacklist %s\n", fname);
1110 errExit("disable file"); 1254 if (S_ISDIR(s.st_mode)) {
1111 } 1255 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
1112 fs_logger2("blacklist", fname); 1256 errExit("disable directory");
1113 } 1257 }
1258 else {
1259 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
1260 errExit("disable file");
1261 }
1262 close(fd);
1263 fs_logger2("blacklist", fname);
1114} 1264}
1115 1265
1116void disable_file_path(const char *path, const char *file) { 1266void disable_file_path(const char *path, const char *file) {
@@ -1126,13 +1276,13 @@ void disable_file_path(const char *path, const char *file) {
1126} 1276}
1127 1277
1128// open an existing file without following any symbolic link 1278// open an existing file without following any symbolic link
1129int safe_fd(const char *path, int flags) { 1279// relative paths are interpreted relative to dirfd
1280// ignore dirfd if path is absolute
1281// https://web.archive.org/web/20180419120236/https://blogs.gnome.org/jamesh/2018/04/19/secure-mounts
1282int safer_openat(int dirfd, const char *path, int flags) {
1283 assert(path && path[0]);
1130 flags |= O_NOFOLLOW; 1284 flags |= O_NOFOLLOW;
1131 assert(path); 1285
1132 if (*path != '/' || strstr(path, "..")) {
1133 fprintf(stderr, "Error: invalid path %s\n", path);
1134 exit(1);
1135 }
1136 int fd = -1; 1286 int fd = -1;
1137 1287
1138#ifdef __NR_openat2 // kernel 5.6 or better 1288#ifdef __NR_openat2 // kernel 5.6 or better
@@ -1140,7 +1290,7 @@ int safe_fd(const char *path, int flags) {
1140 memset(&oh, 0, sizeof(oh)); 1290 memset(&oh, 0, sizeof(oh));
1141 oh.flags = flags; 1291 oh.flags = flags;
1142 oh.resolve = RESOLVE_NO_SYMLINKS; 1292 oh.resolve = RESOLVE_NO_SYMLINKS;
1143 fd = syscall(__NR_openat2, -1, path, &oh, sizeof(struct open_how)); 1293 fd = syscall(__NR_openat2, dirfd, path, &oh, sizeof(struct open_how));
1144 if (fd != -1 || errno != ENOSYS) 1294 if (fd != -1 || errno != ENOSYS)
1145 return fd; 1295 return fd;
1146#endif 1296#endif
@@ -1151,18 +1301,23 @@ int safe_fd(const char *path, int flags) {
1151 if (!dup) 1301 if (!dup)
1152 errExit("strdup"); 1302 errExit("strdup");
1153 char *tok = strtok(dup, "/"); 1303 char *tok = strtok(dup, "/");
1154 if (!tok) { // root directory 1304 if (!tok) { // nothing to do, path is the root directory
1155 free(dup); 1305 free(dup);
1156 return open("/", flags); 1306 return openat(dirfd, path, flags);
1157 } 1307 }
1158 char *last_tok = EMPTY_STRING; 1308 char *last_tok = EMPTY_STRING;
1159 int parentfd = open("/", O_PATH|O_CLOEXEC); 1309
1310 int parentfd;
1311 if (path[0] == '/')
1312 parentfd = open("/", O_PATH|O_CLOEXEC);
1313 else
1314 parentfd = fcntl(dirfd, F_DUPFD_CLOEXEC, 0);
1160 if (parentfd == -1) 1315 if (parentfd == -1)
1161 errExit("open"); 1316 errExit("open/fcntl");
1162 1317
1163 while(1) { 1318 while (1) {
1164 // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link 1319 // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link
1165 // if token is a single dot, the previous directory is reopened 1320 // if token is a single dot, the directory referred to by parentfd is reopened
1166 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1321 fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1167 if (fd == -1) { 1322 if (fd == -1) {
1168 // if the following token is NULL, the current token is the final path component 1323 // if the following token is NULL, the current token is the final path component
@@ -1192,6 +1347,106 @@ int safe_fd(const char *path, int flags) {
1192 return fd; 1347 return fd;
1193} 1348}
1194 1349
1350int remount_by_fd(int dst, unsigned long mountflags) {
1351 char *proc;
1352 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1353 errExit("asprintf");
1354
1355 int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL);
1356 if (rv < 0 && arg_debug)
1357 printf("Failed mount: %s\n", strerror(errno));
1358
1359 free(proc);
1360 return rv;
1361}
1362
1363int bind_mount_by_fd(int src, int dst) {
1364 char *proc_src, *proc_dst;
1365 if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 ||
1366 asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0)
1367 errExit("asprintf");
1368
1369 int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL);
1370 if (rv < 0 && arg_debug)
1371 printf("Failed mount: %s\n", strerror(errno));
1372
1373 free(proc_src);
1374 free(proc_dst);
1375 return rv;
1376}
1377
1378int bind_mount_fd_to_path(int src, const char *destname) {
1379 char *proc;
1380 if (asprintf(&proc, "/proc/self/fd/%d", src) < 0)
1381 errExit("asprintf");
1382
1383 int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL);
1384 if (rv < 0 && arg_debug)
1385 printf("Failed mount: %s\n", strerror(errno));
1386
1387 free(proc);
1388 return rv;
1389}
1390
1391int bind_mount_path_to_fd(const char *srcname, int dst) {
1392 char *proc;
1393 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1394 errExit("asprintf");
1395
1396 int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL);
1397 if (rv < 0 && arg_debug)
1398 printf("Failed mount: %s\n", strerror(errno));
1399
1400 free(proc);
1401 return rv;
1402}
1403
1404void close_all(int *keep_list, size_t sz) {
1405 DIR *dir;
1406 if (!(dir = opendir("/proc/self/fd"))) {
1407 // sleep 2 seconds and try again
1408 sleep(2);
1409 if (!(dir = opendir("/proc/self/fd"))) {
1410 fprintf(stderr, "Error: cannot open /proc/self/fd directory\n");
1411 exit(1);
1412 }
1413 }
1414 struct dirent *entry;
1415 while ((entry = readdir(dir)) != NULL) {
1416 if (strcmp(entry->d_name, ".") == 0 ||
1417 strcmp(entry->d_name, "..") == 0)
1418 continue;
1419
1420 int fd = atoi(entry->d_name);
1421
1422 // don't close standard streams
1423 if (fd == STDIN_FILENO ||
1424 fd == STDOUT_FILENO ||
1425 fd == STDERR_FILENO)
1426 continue;
1427
1428 if (fd == dirfd(dir))
1429 continue; // just postponed
1430
1431 // dont't close file descriptors in keep list
1432 int keep = 0;
1433 if (keep_list) {
1434 size_t i;
1435 for (i = 0; i < sz; i++) {
1436 if (keep_list[i] == fd) {
1437 keep = 1;
1438 break;
1439 }
1440 }
1441 }
1442 if (keep)
1443 continue;
1444
1445 close(fd);
1446 }
1447 closedir(dir);
1448}
1449
1195int has_handler(pid_t pid, int signal) { 1450int has_handler(pid_t pid, int signal) {
1196 if (signal > 0 && signal <= SIGRTMAX) { 1451 if (signal > 0 && signal <= SIGRTMAX) {
1197 char *fname; 1452 char *fname;
@@ -1293,25 +1548,22 @@ pid_t require_pid(const char *name) {
1293// return 1 if there is a link somewhere in path of directory 1548// return 1 if there is a link somewhere in path of directory
1294static int has_link(const char *dir) { 1549static int has_link(const char *dir) {
1295 assert(dir); 1550 assert(dir);
1296 int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1551 int fd = safer_openat(-1, dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
1297 if (fd == -1) { 1552 if (fd != -1)
1298 if ((errno == ELOOP || errno == ENOTDIR) && is_dir(dir))
1299 return 1;
1300 }
1301 else
1302 close(fd); 1553 close(fd);
1554 else if (errno == ELOOP || (errno == ENOTDIR && is_dir(dir)))
1555 return 1;
1303 return 0; 1556 return 0;
1304} 1557}
1305 1558
1306void check_homedir(void) { 1559void check_homedir(const char *dir) {
1307 assert(cfg.homedir); 1560 assert(dir);
1308 if (cfg.homedir[0] != '/') { 1561 if (dir[0] != '/') {
1309 fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir); 1562 fprintf(stderr, "Error: invalid user directory \"%s\"\n", dir);
1310 exit(1); 1563 exit(1);
1311 } 1564 }
1312 // symlinks are rejected in many places 1565 // symlinks are rejected in many places
1313 if (has_link(cfg.homedir)) { 1566 if (has_link(dir))
1314 fprintf(stderr, "No full support for symbolic links in path of user directory.\n" 1567 fmessage("No full support for symbolic links in path of user directory.\n"
1315 "Please provide resolved path in password database (/etc/passwd).\n\n"); 1568 "Please provide resolved path in password database (/etc/passwd).\n\n");
1316 }
1317} 1569}
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index 1dabf272e..f173b6672 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -84,7 +84,7 @@ int x11_display(void) {
84static int x11_abstract_sockets_present(void) { 84static int x11_abstract_sockets_present(void) {
85 85
86 EUID_ROOT(); // grsecurity fix 86 EUID_ROOT(); // grsecurity fix
87 FILE *fp = fopen("/proc/net/unix", "r"); 87 FILE *fp = fopen("/proc/net/unix", "re");
88 if (!fp) 88 if (!fp)
89 errExit("fopen"); 89 errExit("fopen");
90 EUID_USER(); 90 EUID_USER();
@@ -204,7 +204,6 @@ static int random_display_number(void) {
204void x11_start_xvfb(int argc, char **argv) { 204void x11_start_xvfb(int argc, char **argv) {
205 EUID_ASSERT(); 205 EUID_ASSERT();
206 int i; 206 int i;
207 struct stat s;
208 pid_t jail = 0; 207 pid_t jail = 0;
209 pid_t server = 0; 208 pid_t server = 0;
210 209
@@ -348,7 +347,7 @@ void x11_start_xvfb(int argc, char **argv) {
348 // wait for x11 server to start 347 // wait for x11 server to start
349 while (++n < 10) { 348 while (++n < 10) {
350 sleep(1); 349 sleep(1);
351 if (stat(fname, &s) == 0) 350 if (access(fname, F_OK) == 0)
352 break; 351 break;
353 }; 352 };
354 353
@@ -427,7 +426,6 @@ static char *extract_setting(int argc, char **argv, const char *argument) {
427void x11_start_xephyr(int argc, char **argv) { 426void x11_start_xephyr(int argc, char **argv) {
428 EUID_ASSERT(); 427 EUID_ASSERT();
429 int i; 428 int i;
430 struct stat s;
431 pid_t jail = 0; 429 pid_t jail = 0;
432 pid_t server = 0; 430 pid_t server = 0;
433 431
@@ -586,7 +584,7 @@ void x11_start_xephyr(int argc, char **argv) {
586 // wait for x11 server to start 584 // wait for x11 server to start
587 while (++n < 10) { 585 while (++n < 10) {
588 sleep(1); 586 sleep(1);
589 if (stat(fname, &s) == 0) 587 if (access(fname, F_OK) == 0)
590 break; 588 break;
591 }; 589 };
592 590
@@ -701,7 +699,6 @@ static char * get_title_arg_str() {
701static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { 699static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) {
702 EUID_ASSERT(); 700 EUID_ASSERT();
703 int i; 701 int i;
704 struct stat s;
705 pid_t client = 0; 702 pid_t client = 0;
706 pid_t server = 0; 703 pid_t server = 0;
707 704
@@ -818,7 +815,7 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
818 // wait for x11 server to start 815 // wait for x11 server to start
819 while (++n < 10) { 816 while (++n < 10) {
820 sleep(1); 817 sleep(1);
821 if (stat(fname, &s) == 0) 818 if (access(fname, F_OK) == 0)
822 break; 819 break;
823 } 820 }
824 821
@@ -1207,14 +1204,13 @@ void x11_xorg(void) {
1207 fmessage("Generating a new .Xauthority file\n"); 1204 fmessage("Generating a new .Xauthority file\n");
1208 mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid()); 1205 mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid());
1209 // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR 1206 // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR
1207 EUID_USER();
1210 char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX"; 1208 char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX";
1211 int fd = mkstemp(tmpfname); 1209 int fd = mkstemp(tmpfname);
1212 if (fd == -1) { 1210 if (fd == -1) {
1213 fprintf(stderr, "Error: cannot create .Xauthority file\n"); 1211 fprintf(stderr, "Error: cannot create .Xauthority file\n");
1214 exit(1); 1212 exit(1);
1215 } 1213 }
1216 if (fchown(fd, getuid(), getgid()) == -1)
1217 errExit("chown");
1218 close(fd); 1214 close(fd);
1219 1215
1220 // run xauth 1216 // run xauth
@@ -1224,24 +1220,22 @@ void x11_xorg(void) {
1224 else 1220 else
1225 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname, 1221 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname,
1226 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); 1222 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted");
1227 // remove xauth copy
1228 unlink(RUN_XAUTH_FILE);
1229 1223
1230 // ensure there is already a file ~/.Xauthority, so that bind-mount below will work. 1224 // ensure there is already a file ~/.Xauthority, so that bind-mount below will work.
1231 char *dest; 1225 char *dest;
1232 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) 1226 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
1233 errExit("asprintf"); 1227 errExit("asprintf");
1234 if (lstat(dest, &s) == -1) { 1228 if (access(dest, F_OK) == -1) {
1235 touch_file_as_user(dest, 0600); 1229 touch_file_as_user(dest, 0600);
1236 if (stat(dest, &s) == -1) { 1230 if (access(dest, F_OK) == -1) {
1237 fprintf(stderr, "Error: cannot create %s\n", dest); 1231 fprintf(stderr, "Error: cannot create %s\n", dest);
1238 exit(1); 1232 exit(1);
1239 } 1233 }
1240 } 1234 }
1241 // get a file descriptor for ~/.Xauthority 1235 // get a file descriptor for ~/.Xauthority
1242 int dst = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); 1236 int dst = safer_openat(-1, dest, O_PATH|O_NOFOLLOW|O_CLOEXEC);
1243 if (dst == -1) 1237 if (dst == -1)
1244 errExit("safe_fd"); 1238 errExit("safer_openat");
1245 // check if the actual mount destination is a user owned regular file 1239 // check if the actual mount destination is a user owned regular file
1246 if (fstat(dst, &s) == -1) 1240 if (fstat(dst, &s) == -1)
1247 errExit("fstat"); 1241 errExit("fstat");
@@ -1263,9 +1257,9 @@ void x11_xorg(void) {
1263 fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_NOEXEC, 0); 1257 fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_NOEXEC, 0);
1264 1258
1265 // get a file descriptor for the new Xauthority file 1259 // get a file descriptor for the new Xauthority file
1266 int src = safe_fd(tmpfname, O_PATH|O_NOFOLLOW|O_CLOEXEC); 1260 int src = safer_openat(-1, tmpfname, O_PATH|O_NOFOLLOW|O_CLOEXEC);
1267 if (src == -1) 1261 if (src == -1)
1268 errExit("safe_fd"); 1262 errExit("safer_openat");
1269 if (fstat(src, &s) == -1) 1263 if (fstat(src, &s) == -1)
1270 errExit("fstat"); 1264 errExit("fstat");
1271 if (!S_ISREG(s.st_mode)) { 1265 if (!S_ISREG(s.st_mode)) {
@@ -1276,21 +1270,16 @@ void x11_xorg(void) {
1276 // mount via the link in /proc/self/fd 1270 // mount via the link in /proc/self/fd
1277 if (arg_debug) 1271 if (arg_debug)
1278 printf("Mounting %s on %s\n", tmpfname, dest); 1272 printf("Mounting %s on %s\n", tmpfname, dest);
1279 char *proc_src, *proc_dst; 1273 EUID_ROOT();
1280 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) 1274 if (bind_mount_by_fd(src, dst)) {
1281 errExit("asprintf");
1282 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
1283 errExit("asprintf");
1284 if (mount(proc_src, proc_dst, NULL, MS_BIND, NULL) == -1) {
1285 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); 1275 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n");
1286 exit(1); 1276 exit(1);
1287 } 1277 }
1278 EUID_USER();
1288 // check /proc/self/mountinfo to confirm the mount is ok 1279 // check /proc/self/mountinfo to confirm the mount is ok
1289 MountData *mptr = get_last_mount(); 1280 MountData *mptr = get_last_mount();
1290 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 1281 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
1291 errLogExit("invalid .Xauthority mount"); 1282 errLogExit("invalid .Xauthority mount");
1292 free(proc_src);
1293 free(proc_dst);
1294 close(src); 1283 close(src);
1295 close(dst); 1284 close(dst);
1296 1285
@@ -1301,8 +1290,11 @@ void x11_xorg(void) {
1301 if (envar) { 1290 if (envar) {
1302 char *rp = realpath(envar, NULL); 1291 char *rp = realpath(envar, NULL);
1303 if (rp) { 1292 if (rp) {
1304 if (strcmp(rp, dest) != 0) 1293 if (strcmp(rp, dest) != 0) {
1294 EUID_ROOT();
1305 disable_file_or_dir(rp); 1295 disable_file_or_dir(rp);
1296 EUID_USER();
1297 }
1306 free(rp); 1298 free(rp);
1307 } 1299 }
1308 } 1300 }
@@ -1311,9 +1303,13 @@ void x11_xorg(void) {
1311 free(dest); 1303 free(dest);
1312 1304
1313 // mask RUN_XAUTHORITY_SEC_DIR 1305 // mask RUN_XAUTHORITY_SEC_DIR
1306 EUID_ROOT();
1314 if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) 1307 if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
1315 errExit("mounting tmpfs"); 1308 errExit("mounting tmpfs");
1316 fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR); 1309 fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR);
1310
1311 // cleanup
1312 unlink(RUN_XAUTH_FILE);
1317#endif 1313#endif
1318} 1314}
1319 1315
@@ -1327,7 +1323,7 @@ void fs_x11(void) {
1327 struct stat s1, s2; 1323 struct stat s1, s2;
1328 if (stat("/tmp", &s1) != 0 || lstat("/tmp/.X11-unix", &s2) != 0) 1324 if (stat("/tmp", &s1) != 0 || lstat("/tmp/.X11-unix", &s2) != 0)
1329 return; 1325 return;
1330 if ((s1.st_mode & S_ISVTX) == 0) { 1326 if ((s1.st_mode & S_ISVTX) != S_ISVTX) {
1331 fwarning("cannot mask X11 sockets: sticky bit not set on /tmp directory\n"); 1327 fwarning("cannot mask X11 sockets: sticky bit not set on /tmp directory\n");
1332 return; 1328 return;
1333 } 1329 }
@@ -1335,68 +1331,46 @@ void fs_x11(void) {
1335 fwarning("cannot mask X11 sockets: /tmp/.X11-unix not owned by root user\n"); 1331 fwarning("cannot mask X11 sockets: /tmp/.X11-unix not owned by root user\n");
1336 return; 1332 return;
1337 } 1333 }
1334
1335 // the mount source is under control of the user, so be careful and
1336 // mount without following symbolic links, using a file descriptor
1338 char *x11file; 1337 char *x11file;
1339 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) 1338 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
1340 errExit("asprintf"); 1339 errExit("asprintf");
1341 struct stat x11stat; 1340 int src = open(x11file, O_PATH|O_NOFOLLOW|O_CLOEXEC);
1342 if (lstat(x11file, &x11stat) != 0 || !S_ISSOCK(x11stat.st_mode)) { 1341 if (src < 0) {
1342 free(x11file);
1343 return;
1344 }
1345 struct stat s3;
1346 if (fstat(src, &s3) < 0)
1347 errExit("fstat");
1348 if (!S_ISSOCK(s3.st_mode)) {
1349 close(src);
1343 free(x11file); 1350 free(x11file);
1344 return; 1351 return;
1345 } 1352 }
1346 1353
1347 if (arg_debug || arg_debug_whitelists) 1354 if (arg_debug || arg_debug_whitelists)
1348 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file); 1355 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
1349
1350 // Move the real /tmp/.X11-unix to a scratch location
1351 // so we can still access x11file after we mount a
1352 // tmpfs over /tmp/.X11-unix.
1353 if (mkdir(RUN_WHITELIST_X11_DIR, 0700) == -1)
1354 errExit("mkdir");
1355 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
1356 errExit("mount bind");
1357
1358 // This directory must be mode 1777 1356 // This directory must be mode 1777
1359 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", 1357 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
1360 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME, 1358 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME,
1361 "mode=1777,uid=0,gid=0") < 0) 1359 "mode=1777,uid=0,gid=0") < 0)
1362 errExit("mounting tmpfs on /tmp/.X11-unix"); 1360 errExit("mounting tmpfs on /tmp/.X11-unix");
1361 selinux_relabel_path("/tmp/.X11-unix", "/tmp/.X11-unix");
1363 fs_logger("tmpfs /tmp/.X11-unix"); 1362 fs_logger("tmpfs /tmp/.X11-unix");
1364 1363
1365 // create an empty root-owned file which will have the desired socket bind-mounted over it 1364 // create an empty root-owned file which will have the desired socket bind-mounted over it
1366 int fd = open(x11file, O_RDONLY|O_CREAT|O_EXCL, S_IRUSR | S_IWUSR); 1365 int dst = open(x11file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR);
1367 if (fd < 0) 1366 if (dst < 0)
1368 errExit(x11file); 1367 errExit("open");
1369 close(fd);
1370 1368
1371 // the mount source is under control of the user, so be careful and 1369 if (bind_mount_by_fd(src, dst))
1372 // mount without following symbolic links, using a file descriptor
1373 char *wx11file;
1374 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
1375 errExit("asprintf");
1376 fd = safe_fd(wx11file, O_PATH|O_NOFOLLOW|O_CLOEXEC);
1377 if (fd == -1)
1378 errExit("opening X11 socket");
1379 // confirm once more we are mounting a socket
1380 if (fstat(fd, &x11stat) == -1)
1381 errExit("fstat");
1382 if (!S_ISSOCK(x11stat.st_mode)) {
1383 errno = ENOTSOCK;
1384 errExit("mounting X11 socket");
1385 }
1386 char *proc;
1387 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
1388 errExit("asprintf");
1389 if (mount(proc, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
1390 errExit("mount bind"); 1370 errExit("mount bind");
1371 close(src);
1372 close(dst);
1391 fs_logger2("whitelist", x11file); 1373 fs_logger2("whitelist", x11file);
1392 close(fd);
1393 free(proc);
1394
1395 // block access to RUN_WHITELIST_X11_DIR
1396 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
1397 errExit("mount");
1398 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
1399 free(wx11file);
1400 free(x11file); 1374 free(x11file);
1401#endif 1375#endif
1402} 1376}
diff --git a/src/firemon/apparmor.c b/src/firemon/apparmor.c
index eb810a9e7..7103ab7af 100644
--- a/src/firemon/apparmor.c
+++ b/src/firemon/apparmor.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/arp.c b/src/firemon/arp.c
index 1a69a67b1..1a01da016 100644
--- a/src/firemon/arp.c
+++ b/src/firemon/arp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/caps.c b/src/firemon/caps.c
index c0f305a5d..045cd1968 100644
--- a/src/firemon/caps.c
+++ b/src/firemon/caps.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/cgroup.c b/src/firemon/cgroup.c
index 97ba591a6..7ef76fa46 100644
--- a/src/firemon/cgroup.c
+++ b/src/firemon/cgroup.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/cpu.c b/src/firemon/cpu.c
index 91b455941..31e4eb7fd 100644
--- a/src/firemon/cpu.c
+++ b/src/firemon/cpu.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/firemon.c b/src/firemon/firemon.c
index 37870747d..91406d6a7 100644
--- a/src/firemon/firemon.c
+++ b/src/firemon/firemon.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -52,7 +52,7 @@ static void my_handler(int s){
52 52
53 if (terminal_set) 53 if (terminal_set)
54 tcsetattr(0, TCSANOW, &tlocal); 54 tcsetattr(0, TCSANOW, &tlocal);
55 exit(0); 55 _exit(0);
56} 56}
57 57
58// find the second child process for the specified pid 58// find the second child process for the specified pid
diff --git a/src/firemon/firemon.h b/src/firemon/firemon.h
index 5252ad34f..2fa294e8d 100644
--- a/src/firemon/firemon.h
+++ b/src/firemon/firemon.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/interface.c b/src/firemon/interface.c
index e04b6f431..f57616ed7 100644
--- a/src/firemon/interface.c
+++ b/src/firemon/interface.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firemon.h" 20#include "firemon.h"
21#include "../include/gcov_wrapper.h"
21#include <sys/types.h> 22#include <sys/types.h>
22#include <sys/wait.h> 23#include <sys/wait.h>
23#include <netdb.h> 24#include <netdb.h>
@@ -145,9 +146,9 @@ static void print_sandbox(pid_t pid) {
145 if (rv) 146 if (rv)
146 return; 147 return;
147 net_ifprint(); 148 net_ifprint();
148#ifdef HAVE_GCOV 149
149 __gcov_flush(); 150 __gcov_flush();
150#endif 151
151 _exit(0); 152 _exit(0);
152 } 153 }
153 154
diff --git a/src/firemon/list.c b/src/firemon/list.c
index 51099a75c..d066c7a5f 100644
--- a/src/firemon/list.c
+++ b/src/firemon/list.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/netstats.c b/src/firemon/netstats.c
index 850959eb3..0a1b7e0c4 100644
--- a/src/firemon/netstats.c
+++ b/src/firemon/netstats.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firemon.h" 20#include "firemon.h"
21#include "../include/gcov_wrapper.h"
21#include <termios.h> 22#include <termios.h>
22#include <sys/ioctl.h> 23#include <sys/ioctl.h>
23#include <sys/types.h> 24#include <sys/types.h>
@@ -242,8 +243,7 @@ void netstats(void) {
242 print_proc(i, itv, col); 243 print_proc(i, itv, col);
243 } 244 }
244 } 245 }
245#ifdef HAVE_GCOV 246
246 __gcov_flush(); 247 __gcov_flush();
247#endif
248 } 248 }
249} 249}
diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c
index 8085d2d29..ccc1ba1c6 100644
--- a/src/firemon/procevent.c
+++ b/src/firemon/procevent.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firemon.h" 20#include "firemon.h"
21#include "../include/gcov_wrapper.h"
21#include <sys/socket.h> 22#include <sys/socket.h>
22#include <linux/connector.h> 23#include <linux/connector.h>
23#include <linux/netlink.h> 24#include <linux/netlink.h>
@@ -230,9 +231,7 @@ static void __attribute__((noreturn)) procevent_monitor(const int sock, pid_t my
230 tv.tv_usec = 0; 231 tv.tv_usec = 0;
231 232
232 while (1) { 233 while (1) {
233#ifdef HAVE_GCOV
234 __gcov_flush(); 234 __gcov_flush();
235#endif
236 235
237#define BUFFSIZE 4096 236#define BUFFSIZE 4096
238 char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE]; 237 char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE];
diff --git a/src/firemon/route.c b/src/firemon/route.c
index 9cf5054b2..86f4d85ae 100644
--- a/src/firemon/route.c
+++ b/src/firemon/route.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/seccomp.c b/src/firemon/seccomp.c
index 04111b6c0..ba0017eff 100644
--- a/src/firemon/seccomp.c
+++ b/src/firemon/seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/top.c b/src/firemon/top.c
index a25e3c0d8..2bfa63380 100644
--- a/src/firemon/top.c
+++ b/src/firemon/top.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "firemon.h" 20#include "firemon.h"
21#include "../include/gcov_wrapper.h"
21#include <termios.h> 22#include <termios.h>
22#include <sys/ioctl.h> 23#include <sys/ioctl.h>
23#include <sys/types.h> 24#include <sys/types.h>
@@ -326,8 +327,7 @@ void top(void) {
326 } 327 }
327 } 328 }
328 head_print(col, row); 329 head_print(col, row);
329#ifdef HAVE_GCOV 330
330 __gcov_flush(); 331 __gcov_flush();
331#endif
332 } 332 }
333} 333}
diff --git a/src/firemon/tree.c b/src/firemon/tree.c
index 899214b9f..7ad413772 100644
--- a/src/firemon/tree.c
+++ b/src/firemon/tree.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/firemon/usage.c b/src/firemon/usage.c
index baaef3111..c6a664790 100644
--- a/src/firemon/usage.c
+++ b/src/firemon/usage.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -38,12 +38,12 @@ static char *help_str =
38 "\t--name=name - print information only about named sandbox.\n\n" 38 "\t--name=name - print information only about named sandbox.\n\n"
39 "\t--netstats - monitor network statistics for sandboxes creating a new\n" 39 "\t--netstats - monitor network statistics for sandboxes creating a new\n"
40 "\t\tnetwork namespace.\n\n" 40 "\t\tnetwork namespace.\n\n"
41 "\t--nowrap - enable line wrapping in terminals.\n\n"
42 "\t--route - print route table for each sandbox.\n\n" 41 "\t--route - print route table for each sandbox.\n\n"
43 "\t--seccomp - print seccomp configuration for each sandbox.\n\n" 42 "\t--seccomp - print seccomp configuration for each sandbox.\n\n"
44 "\t--tree - print a tree of all sandboxed processes.\n\n" 43 "\t--tree - print a tree of all sandboxed processes.\n\n"
45 "\t--top - monitor the most CPU-intensive sandboxes.\n\n" 44 "\t--top - monitor the most CPU-intensive sandboxes.\n\n"
46 "\t--version - print program version and exit.\n\n" 45 "\t--version - print program version and exit.\n\n"
46 "\t--wrap - enable line wrapping in terminals.\n\n"
47 "\t--x11 - print X11 display number.\n\n" 47 "\t--x11 - print X11 display number.\n\n"
48 48
49 "Without any options, firemon monitors all fork, exec, id change, and exit\n" 49 "Without any options, firemon monitors all fork, exec, id change, and exit\n"
diff --git a/src/firemon/x11.c b/src/firemon/x11.c
index 97e24b2d2..16ee0a2d6 100644
--- a/src/firemon/x11.c
+++ b/src/firemon/x11.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fldd/main.c b/src/fldd/main.c
index 9d91557c1..898e0f36a 100644
--- a/src/fldd/main.c
+++ b/src/fldd/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -261,12 +261,21 @@ static void walk_directory(const char *dirname) {
261 261
262 // check directory 262 // check directory
263 // entry->d_type field is supported in glibc since version 2.19 (Feb 2014) 263 // entry->d_type field is supported in glibc since version 2.19 (Feb 2014)
264 // we'll use stat to check for directories 264 // we'll use stat to check for directories using the real path
265 // (sometimes the path is a double symlink to a real file and stat would fail)
266 char *rpath = realpath(path, NULL);
267 if (!rpath) {
268 free(path);
269 continue;
270 }
271 free(path);
272
265 struct stat s; 273 struct stat s;
266 if (stat(path, &s) == -1) 274 if (stat(rpath, &s) == -1)
267 errExit("stat"); 275 errExit("stat");
268 if (S_ISDIR(s.st_mode)) 276 if (S_ISDIR(s.st_mode))
269 walk_directory(path); 277 walk_directory(rpath);
278 free(rpath);
270 } 279 }
271 closedir(dir); 280 closedir(dir);
272 } 281 }
diff --git a/src/fnet/arp.c b/src/fnet/arp.c
index 59798d32d..ed110c271 100644
--- a/src/fnet/arp.c
+++ b/src/fnet/arp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h
index c0154b53e..41db5aa1b 100644
--- a/src/fnet/fnet.h
+++ b/src/fnet/fnet.h
@@ -1,5 +1,5 @@
1 /* 1 /*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fnet/interface.c b/src/fnet/interface.c
index 91d91360d..072dbf381 100644
--- a/src/fnet/interface.c
+++ b/src/fnet/interface.c
@@ -1,5 +1,5 @@
1 /* 1 /*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fnet/main.c b/src/fnet/main.c
index df8f7226c..d39fcfc84 100644
--- a/src/fnet/main.c
+++ b/src/fnet/main.c
@@ -1,5 +1,5 @@
1 /* 1 /*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fnet/veth.c b/src/fnet/veth.c
index e09b1b1c5..bd6e33583 100644
--- a/src/fnet/veth.c
+++ b/src/fnet/veth.c
@@ -26,7 +26,7 @@
26 * 26 *
27 */ 27 */
28 /* 28 /*
29 * Copyright (C) 2014-2021 Firejail Authors 29 * Copyright (C) 2014-2022 Firejail Authors
30 * 30 *
31 * This file is part of firejail project 31 * This file is part of firejail project
32 * 32 *
diff --git a/src/fnetfilter/main.c b/src/fnetfilter/main.c
index 979f082d0..a89e12933 100644
--- a/src/fnetfilter/main.c
+++ b/src/fnetfilter/main.c
@@ -1,5 +1,5 @@
1 /* 1 /*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -187,10 +187,9 @@ printf("\n");
187 char *command = (argc == 3)? argv[1]: NULL; 187 char *command = (argc == 3)? argv[1]: NULL;
188//printf("command %s\n", command); 188//printf("command %s\n", command);
189//printf("destfile %s\n", destfile); 189//printf("destfile %s\n", destfile);
190
190 // destfile is a real filename 191 // destfile is a real filename
191 int len = strlen(destfile); 192 reject_meta_chars(destfile, 0);
192 if (strcspn(destfile, "\\&!?\"'<>%^(){};,*[]") != (size_t)len)
193 err_exit_cannot_open_file(destfile);
194 193
195 // handle default config (command = NULL, destfile) 194 // handle default config (command = NULL, destfile)
196 if (command == NULL) { 195 if (command == NULL) {
diff --git a/src/fnettrace/Makefile.in b/src/fnettrace/Makefile.in
new file mode 100644
index 000000000..755ddcc3a
--- /dev/null
+++ b/src/fnettrace/Makefile.in
@@ -0,0 +1,17 @@
1.PHONY: all
2all: fnettrace
3
4include ../common.mk
5
6%.o : %.c $(H_FILE_LIST)
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8
9fnettrace: $(OBJS)
10 $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS)
11
12.PHONY: clean
13clean:; rm -fr *.o fnettrace *.gcov *.gcda *.gcno *.plist
14
15.PHONY: distclean
16distclean: clean
17 rm -fr Makefile
diff --git a/src/fnettrace/fnettrace.h b/src/fnettrace/fnettrace.h
new file mode 100644
index 000000000..b30a9f10d
--- /dev/null
+++ b/src/fnettrace/fnettrace.h
@@ -0,0 +1,73 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#ifndef FNETTRACE_H
21#define FNETTRACE_H
22
23#include "../include/common.h"
24#include <unistd.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <time.h>
30#include <stdarg.h>
31#include <fcntl.h>
32#include <sys/mman.h>
33
34
35//#define DEBUG 1
36
37#define NETLOCK_INTERVAL 60 // seconds
38#define DISPLAY_INTERVAL 2 // seconds
39#define DISPLAY_TTL 4 // display intervals (4 * 2 seconds)
40#define DISPLAY_BW_UNITS 20 // length of the bandwidth bar
41
42
43static inline void ansi_topleft(void) {
44 char str[] = {0x1b, '[', '1', ';', '1', 'H', '\0'};
45 printf("%s", str);
46 fflush(0);
47}
48
49static inline void ansi_clrscr(void) {
50 ansi_topleft();
51 char str[] = {0x1b, '[', '0', 'J', '\0'};
52 printf("%s", str);
53 fflush(0);
54}
55
56static inline uint8_t hash(uint32_t ip) {
57 uint8_t *ptr = (uint8_t *) &ip;
58 // simple byte xor
59 return *ptr ^ *(ptr + 1) ^ *(ptr + 2) ^ *(ptr + 3);
60}
61
62// main.c
63void logprintf(char* fmt, ...);
64
65// hostnames.c
66extern int geoip_calls;
67void load_hostnames(const char *fname);
68char* retrieve_hostname(uint32_t ip);
69
70// tail.c
71void tail(const char *logfile);
72
73#endif \ No newline at end of file
diff --git a/src/fnettrace/hostnames.c b/src/fnettrace/hostnames.c
new file mode 100644
index 000000000..dd92070bf
--- /dev/null
+++ b/src/fnettrace/hostnames.c
@@ -0,0 +1,124 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "fnettrace.h"
21#include "radix.h"
22#define MAXBUF 1024
23
24int geoip_calls = 0;
25static int geoip_not_found = 0;
26static char buf[MAXBUF];
27
28char *retrieve_hostname(uint32_t ip) {
29 if (geoip_not_found)
30 return NULL;
31 geoip_calls++;
32
33 char *rv = NULL;
34 char *cmd;
35 if (asprintf(&cmd, "/usr/bin/geoiplookup %d.%d.%d.%d", PRINT_IP(ip)) == -1)
36 errExit("asprintf");
37
38 FILE *fp = popen(cmd, "r");
39 if (fp) {
40 char *ptr;
41 if (fgets(buf, MAXBUF, fp)) {
42 ptr = strchr(buf, '\n');
43 if (ptr)
44 *ptr = '\0';
45 if (strncmp(buf, "GeoIP Country Edition:", 22) == 0) {
46 ptr = buf + 22;
47 if (*ptr == ' ' && *(ptr + 3) == ',' && *(ptr + 4) == ' ') {
48 rv = ptr + 5;
49 rv = radix_add(ip, 0xffffffff, rv);
50 }
51 }
52 }
53 fclose(fp);
54 return rv;
55 }
56 else
57 geoip_not_found = 1;
58
59 free(cmd);
60
61 return NULL;
62}
63
64void load_hostnames(const char *fname) {
65 assert(fname);
66 FILE *fp = fopen(fname, "r");
67 if (!fp) {
68 fprintf(stderr, "Warning: cannot find %s file\n", fname);
69 return;
70 }
71
72 char buf[MAXBUF];
73 int line = 0;
74 while (fgets(buf, MAXBUF, fp)) {
75 line++;
76
77 // skip empty spaces
78 char *start = buf;
79 while (*start == ' ' || *start == '\t')
80 start++;
81 // comments
82 if (*start == '#')
83 continue;
84 char *end = strchr(start, '#');
85 if (end)
86 *end = '\0';
87
88 // end
89 end = strchr(start, '\n');
90 if (end)
91 *end = '\0';
92 end = start + strlen(start);
93 if (end == start) // empty line
94 continue;
95
96 // line format: 1.2.3.4/32 name_without_empty_spaces
97 // a single empty space between address and name
98 end = strchr(start, ' ');
99 if (!end)
100 goto errexit;
101 *end = '\0';
102 end++;
103 if (*end == '\0')
104 goto errexit;
105
106 uint32_t ip;
107 uint32_t mask;
108 if (atocidr(start, &ip, &mask)) {
109 fprintf(stderr, "Error: invalid CIDR address\n");
110 goto errexit;
111 }
112
113 radix_add(ip, mask, end);
114 }
115
116 fclose(fp);
117 return;
118
119
120errexit:
121 fprintf(stderr, "Error: invalid line %d in file %s\n", line, fname);
122 exit(1);
123}
124
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c
new file mode 100644
index 000000000..31d49d839
--- /dev/null
+++ b/src/fnettrace/main.c
@@ -0,0 +1,665 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "fnettrace.h"
21#include "radix.h"
22#include <sys/ioctl.h>
23#define MAX_BUF_SIZE (64 * 1024)
24
25static int arg_netfilter = 0;
26static int arg_tail = 0;
27static char *arg_log = NULL;
28
29typedef struct hnode_t {
30 struct hnode_t *hnext; // used for hash table and unused linked list
31 struct hnode_t *dnext; // used to display stremas on the screen
32 uint32_t ip_src;
33 uint32_t bytes; // number of bytes received in the last display interval
34 uint16_t port_src;
35 uint8_t protocol;
36 // the firewall is build based on source address, and in the linked list
37 // we have elements with the same address but different ports
38 uint8_t ip_instance;
39 char *hostname;
40 int ttl;
41} HNode;
42
43// hash table
44#define HMAX 256
45HNode *htable[HMAX] = {NULL};
46// display linked list
47HNode *dlist = NULL;
48
49
50// speed up malloc/free
51#define HNODE_MAX_MALLOC 16
52static HNode *hnode_unused = NULL;
53HNode *hmalloc(void) {
54 if (hnode_unused == NULL) {
55 hnode_unused = malloc(sizeof(HNode) * HNODE_MAX_MALLOC);
56 if (!hnode_unused)
57 errExit("malloc");
58 memset(hnode_unused, 0, sizeof(HNode) * HNODE_MAX_MALLOC);
59 HNode *ptr = hnode_unused;
60 int i;
61 for ( i = 1; i < HNODE_MAX_MALLOC; i++, ptr++)
62 ptr->hnext = hnode_unused + i;
63 }
64
65 HNode *rv = hnode_unused;
66 hnode_unused = hnode_unused->hnext;
67 return rv;
68}
69
70void hfree(HNode *ptr) {
71 assert(ptr);
72 memset(ptr, 0, sizeof(HNode));
73 ptr->hnext = hnode_unused;
74 hnode_unused = ptr;
75}
76
77
78static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) {
79 uint8_t h = hash(ip_src);
80
81 // find
82 int ip_instance = 0;
83 HNode *ptr = htable[h];
84 while (ptr) {
85 if (ptr->ip_src == ip_src) {
86 ip_instance++;
87 if (ptr->port_src == port_src && ptr->protocol == protocol) {
88 ptr->bytes += bytes;
89 return;
90 }
91 }
92 ptr = ptr->hnext;
93 }
94
95#ifdef DEBUG
96 printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src));
97#endif
98 HNode *hnew = hmalloc();
99 assert(hnew);
100 hnew->hostname = NULL;
101 hnew->ip_src = ip_src;
102 hnew->port_src = port_src;
103 hnew->protocol = protocol;
104 hnew->hnext = NULL;
105 hnew->bytes = bytes;
106 hnew->ip_instance = ip_instance + 1;
107 hnew->ttl = DISPLAY_TTL;
108 if (htable[h] == NULL)
109 htable[h] = hnew;
110 else {
111 hnew->hnext = htable[h];
112 htable[h] = hnew;
113 }
114
115 // add to the end of list
116 hnew->dnext = NULL;
117 if (dlist == NULL)
118 dlist = hnew;
119 else {
120 ptr = dlist;
121 while (ptr->dnext != NULL)
122 ptr = ptr->dnext;
123 ptr->dnext = hnew;
124 }
125
126 if (arg_netfilter)
127 logprintf(" %d.%d.%d.%d ", PRINT_IP(hnew->ip_src));
128}
129
130static void hnode_free(HNode *elem) {
131 assert(elem);
132#ifdef DEBUG
133 printf("free %d.%d.%d.%d\n", PRINT_IP(elem->ip_src));
134#endif
135
136 uint8_t h = hash(elem->ip_src);
137 HNode *ptr = htable[h];
138 assert(ptr);
139
140 HNode *prev = NULL;
141 while (ptr != elem) {
142 prev = ptr;
143 ptr = ptr->hnext;
144 }
145 if (prev == NULL)
146 htable[h] = elem->hnext;
147 else
148 prev->hnext = elem->hnext;
149 hfree(elem);
150}
151
152#ifdef DEBUG
153static void debug_dlist(void) {
154 HNode *ptr = dlist;
155 while (ptr) {
156 printf("dlist %d.%d.%d.%d:%d\n", PRINT_IP(ptr->ip_src), ptr->port_src);
157 ptr = ptr->dnext;
158 }
159}
160static void debug_hnode(void) {
161 int i;
162 for (i = 0; i < HMAX; i++) {
163 HNode *ptr = htable[i];
164 while (ptr) {
165 printf("hnode (%d) %d.%d.%d.%d:%d\n", i, PRINT_IP(ptr->ip_src), ptr->port_src);
166 ptr = ptr->hnext;
167 }
168 }
169}
170#endif
171
172static char *bw_line[DISPLAY_BW_UNITS + 1] = { NULL };
173
174static char *print_bw(unsigned units) {
175 if (units > DISPLAY_BW_UNITS)
176 units = DISPLAY_BW_UNITS ;
177
178 if (bw_line[units] == NULL) {
179 char *ptr = malloc(DISPLAY_BW_UNITS + 2);
180 if (!ptr)
181 errExit("malloc");
182 bw_line[units] = ptr;
183
184 unsigned i;
185 for (i = 0; i < DISPLAY_BW_UNITS; i++, ptr++)
186 sprintf(ptr, "%s", (i < units)? "*": " ");
187 sprintf(ptr, "%s", " ");
188 }
189
190 return bw_line[units];
191}
192
193#define LINE_MAX 200
194static inline void adjust_line(char *str, int len, int cols) {
195 if (len > LINE_MAX) // functions such as snprintf truncate the string, and return the length of the untruncated string
196 len = LINE_MAX;
197 if (cols > 4 && len > cols) {
198 str[cols] = '\0';
199 str[cols- 1] = '\n';
200 }
201}
202
203#define BWMAX_CNT 8
204static unsigned adjust_bandwidth(unsigned bw) {
205 static unsigned array[BWMAX_CNT] = {0};
206 static int instance = 0;
207
208 array[instance] = bw;
209 int i;
210 unsigned sum = 0;
211 unsigned max = 0;
212 for ( i = 0; i < BWMAX_CNT; i++) {
213 sum += array[i];
214 max = (max > array[i])? max: array[i];
215 }
216 sum /= BWMAX_CNT;
217
218 if (++instance >= BWMAX_CNT)
219 instance = 0;
220
221 return (max < (sum / 2))? sum: max;
222}
223
224static void hnode_print(unsigned bw) {
225 assert(!arg_netfilter);
226 bw = (bw < 1024 * DISPLAY_INTERVAL)? 1024 * DISPLAY_INTERVAL: bw;
227#ifdef DEBUG
228 printf("*********************\n");
229 debug_dlist();
230 printf("-----------------------------\n");
231 debug_hnode();
232 printf("*********************\n");
233#else
234 ansi_clrscr();
235#endif
236
237 // get terminal size
238 struct winsize sz;
239 int cols = 80;
240 if (isatty(STDIN_FILENO)) {
241 if (!ioctl(0, TIOCGWINSZ, &sz))
242 cols = sz.ws_col;
243 }
244 if (cols > LINE_MAX)
245 cols = LINE_MAX;
246 char line[LINE_MAX + 1];
247
248 // print stats line
249 bw = adjust_bandwidth(bw);
250 char stats[31];
251 if (bw > (1024 * 1024 * DISPLAY_INTERVAL))
252 sprintf(stats, "%u MB/s ", bw / (1024 * 1024 * DISPLAY_INTERVAL));
253 else
254 sprintf(stats, "%u KB/s ", bw / (1024 * DISPLAY_INTERVAL));
255 int len = snprintf(line, LINE_MAX, "%32s geoip %d, IP database %d\n", stats, geoip_calls, radix_nodes);
256 adjust_line(line, len, cols);
257 printf("%s", line);
258
259 HNode *ptr = dlist;
260 HNode *prev = NULL;
261 while (ptr) {
262 HNode *next = ptr->dnext;
263 if (--ptr->ttl > 0) {
264 char bytes[11];
265 if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 1024 * 2)) // > 2 MB/second
266 snprintf(bytes, 11, "%u MB/s",
267 (unsigned) (ptr->bytes / (DISPLAY_INTERVAL * 1024* 1024)));
268 else if (ptr->bytes > (DISPLAY_INTERVAL * 1024 * 2)) // > 2 KB/second
269 snprintf(bytes, 11, "%u KB/s",
270 (unsigned) (ptr->bytes / (DISPLAY_INTERVAL * 1024)));
271 else
272 snprintf(bytes, 11, "%u B/s ", (unsigned) (ptr->bytes / DISPLAY_INTERVAL));
273
274 if (!ptr->hostname)
275 ptr->hostname = radix_longest_prefix_match(ptr->ip_src);
276 if (!ptr->hostname)
277 ptr->hostname = retrieve_hostname(ptr->ip_src);
278 if (!ptr->hostname)
279 ptr->hostname = " ";
280
281 unsigned bwunit = bw / DISPLAY_BW_UNITS;
282 char *bwline;
283 if (bwunit == 0)
284 bwline = print_bw(0);
285 else
286 bwline = print_bw(ptr->bytes / bwunit);
287
288 char *protocol = "";
289 if (ptr->port_src == 80)
290 protocol = "(HTTP)";
291 else if (ptr->port_src == 853)
292 protocol = "(DoT)";
293 else if (ptr->protocol == 0x11)
294 protocol = "(UDP)";
295/*
296 else (ptr->port_src == 443)
297 protocol = "TLS";
298 else if (ptr->port_src == 53)
299 protocol = "DNS";
300*/
301
302 len = snprintf(line, LINE_MAX, "%10s %s %d.%d.%d.%d:%u%s %s\n",
303 bytes, bwline, PRINT_IP(ptr->ip_src), ptr->port_src, protocol, ptr->hostname);
304 adjust_line(line, len, cols);
305 printf("%s", line);
306
307 if (ptr->bytes)
308 ptr->ttl = DISPLAY_TTL;
309 ptr->bytes = 0;
310 prev = ptr;
311 }
312 else {
313 // free the element
314 if (prev == NULL)
315 dlist = next;
316 else
317 prev->dnext = next;
318 hnode_free(ptr);
319 }
320
321 ptr = next;
322 }
323
324#ifdef DEBUG
325 {
326 int cnt = 0;
327 HNode *ptr = hnode_unused;
328 while (ptr) {
329 cnt++;
330 ptr = ptr->hnext;
331 }
332 printf("hnode unused %d\n", cnt);
333 }
334#endif
335}
336
337static void run_trace(void) {
338 if (arg_netfilter)
339 logprintf("accumulating traffic for %d seconds\n", NETLOCK_INTERVAL);
340
341 // trace only rx ipv4 tcp and upd
342 int s1 = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
343 int s2 = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
344 if (s1 < 0 || s2 < 0)
345 errExit("socket");
346
347 unsigned start = time(NULL);
348 unsigned last_print_traces = 0;
349 unsigned last_print_remaining = 0;
350 unsigned char buf[MAX_BUF_SIZE];
351 unsigned bw = 0; // bandwidth calculations
352 while (1) {
353 unsigned end = time(NULL);
354 if (arg_netfilter && end - start >= NETLOCK_INTERVAL)
355 break;
356 if (end % DISPLAY_INTERVAL == 1 && last_print_traces != end) { // first print after 1 second
357 if (!arg_netfilter)
358 hnode_print(bw);
359 last_print_traces = end;
360 bw = 0;
361 }
362 if (arg_netfilter && last_print_remaining != end) {
363 logprintf(".");
364 fflush(0);
365 last_print_remaining = end;
366 }
367
368 fd_set rfds;
369 FD_ZERO(&rfds);
370 FD_SET(s1, &rfds);
371 FD_SET(s2, &rfds);
372 int maxfd = (s1 > s2) ? s1 : s2;
373 maxfd++;
374 struct timeval tv;
375 tv.tv_sec = 1;
376 tv.tv_usec = 0;
377 int rv = select(maxfd, &rfds, NULL, NULL, &tv);
378 if (rv < 0)
379 errExit("select");
380 else if (rv == 0)
381 continue;
382
383 int sock = (FD_ISSET(s1, &rfds)) ? s1 : s2;
384
385 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
386 if (bytes >= 20) { // size of IP header
387#ifdef DEBUG
388 {
389 uint32_t ip_src;
390 memcpy(&ip_src, buf + 12, 4);
391 ip_src = ntohl(ip_src);
392
393 uint32_t ip_dst;
394 memcpy(&ip_dst, buf + 16, 4);
395 ip_dst = ntohl(ip_dst);
396 printf("%d.%d.%d.%d -> %d.%d.%d.%d, %u bytes\n", PRINT_IP(ip_src), PRINT_IP(ip_dst), bytes);
397 }
398#endif
399 // filter out loopback traffic
400 if (buf[12] != 127 && buf[16] != 127) {
401 bw += bytes + 14; // assume a 14 byte Ethernet layer
402
403 uint32_t ip_src;
404 memcpy(&ip_src, buf + 12, 4);
405 ip_src = ntohl(ip_src);
406
407 uint8_t hlen = (buf[0] & 0x0f) * 4;
408 uint16_t port_src;
409 memcpy(&port_src, buf + hlen, 2);
410 port_src = ntohs(port_src);
411
412 hnode_add(ip_src, buf[9], port_src, bytes + 14);
413 }
414 }
415 }
416
417 close(s1);
418 close(s2);
419}
420
421static char *filter_start =
422 "*filter\n"
423 ":INPUT DROP [0:0]\n"
424 ":FORWARD DROP [0:0]\n"
425 ":OUTPUT DROP [0:0]\n";
426
427// return 1 if error
428static int print_filter(FILE *fp) {
429 if (dlist == NULL)
430 return 1;
431 fprintf(fp, "%s\n", filter_start);
432 fprintf(fp, "-A INPUT -s 127.0.0.0/8 -j ACCEPT\n");
433 fprintf(fp, "-A OUTPUT -d 127.0.0.0/8 -j ACCEPT\n");
434 fprintf(fp, "\n");
435
436 int i;
437 for (i = 0; i < HMAX; i++) {
438 HNode *ptr = htable[i];
439 while (ptr) {
440 // filter rules are targeting ip address, the port number is disregarded,
441 // so we look only at the first instance of an address
442 if (ptr->ip_instance == 1) {
443 char *protocol = (ptr->protocol == 6)? "tcp": "udp";
444 fprintf(fp, "-A INPUT -s %d.%d.%d.%d -p %s -j ACCEPT\n",
445 PRINT_IP(ptr->ip_src),
446 protocol);
447 fprintf(fp, "-A OUTPUT -d %d.%d.%d.%d -p %s -j ACCEPT\n",
448 PRINT_IP(ptr->ip_src),
449 protocol);
450 fprintf(fp, "\n");
451 }
452 ptr = ptr->hnext;
453 }
454 }
455 fprintf(fp, "COMMIT\n");
456
457 return 0;
458}
459
460static char *flush_rules[] = {
461 "-P INPUT ACCEPT",
462// "-P FORWARD DENY",
463 "-P OUTPUT ACCEPT",
464 "-F",
465 "-X",
466// "-t nat -F",
467// "-t nat -X",
468// "-t mangle -F",
469// "-t mangle -X",
470// "iptables -t raw -F",
471// "-t raw -X",
472 NULL
473};
474
475static void deploy_netfilter(void) {
476 int rv;
477 char *cmd;
478 int i;
479
480 if (dlist == NULL) {
481 logprintf("Sorry, no network traffic was detected. The firewall was not configured.\n");
482 return;
483 }
484 // find iptables command
485 char *iptables = NULL;
486 char *iptables_restore = NULL;
487 if (access("/sbin/iptables", X_OK) == 0) {
488 iptables = "/sbin/iptables";
489 iptables_restore = "/sbin/iptables-restore";
490 }
491 else if (access("/usr/sbin/iptables", X_OK) == 0) {
492 iptables = "/usr/sbin/iptables";
493 iptables_restore = "/usr/sbin/iptables-restore";
494 }
495 if (iptables == NULL || iptables_restore == NULL) {
496 fprintf(stderr, "Error: iptables command not found, netfilter not configured\n");
497 exit(1);
498 }
499
500 // flush all netfilter rules
501 i = 0;
502 while (flush_rules[i]) {
503 char *cmd;
504 if (asprintf(&cmd, "%s %s", iptables, flush_rules[i]) == -1)
505 errExit("asprintf");
506 int rv = system(cmd);
507 (void) rv;
508 free(cmd);
509 i++;
510 }
511
512 // create temporary file
513 char fname[] = "/tmp/firejail-XXXXXX";
514 int fd = mkstemp(fname);
515 if (fd == -1) {
516 fprintf(stderr, "Error: cannot create temporary configuration file\n");
517 exit(1);
518 }
519
520 FILE* fp = fdopen(fd, "w");
521 if (!fp) {
522 rv = unlink(fname);
523 (void) rv;
524 fprintf(stderr, "Error: cannot create temporary configuration file\n");
525 exit(1);
526 }
527 print_filter(fp);
528 fclose(fp);
529
530 logprintf("\n\n");
531 logprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
532 if (asprintf(&cmd, "cat %s >> %s", fname, arg_log) == -1)
533 errExit("asprintf");
534 rv = system(cmd);
535 (void) rv;
536 free(cmd);
537
538 if (asprintf(&cmd, "cat %s", fname) == -1)
539 errExit("asprintf");
540 rv = system(cmd);
541 (void) rv;
542 free(cmd);
543 logprintf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
544
545 // configuring
546 if (asprintf(&cmd, "%s %s", iptables_restore, fname) == -1)
547 errExit("asprintf");
548 rv = system(cmd);
549 if (rv)
550 fprintf(stdout, "Warning: possible netfilter problem!");
551 free(cmd);
552
553 rv = unlink(fname);
554 (void) rv;
555 logprintf("\nfirewall deployed\n");
556}
557
558void logprintf(char* fmt, ...) {
559 if (!arg_log)
560 return;
561
562 FILE *fp = fopen(arg_log, "a");
563 if (fp) { // disregard if error
564 va_list args;
565 va_start(args,fmt);
566 vfprintf(fp, fmt, args);
567 va_end(args);
568 fclose(fp);
569 }
570
571 va_list args;
572 va_start(args,fmt);
573 vfprintf(stdout, fmt, args);
574 va_end(args);
575}
576
577static void usage(void) {
578 printf("Usage: fnettrace [OPTIONS]\n");
579 printf("Options:\n");
580 printf(" --help, -? - this help screen\n");
581 printf(" --log=filename - netlocker logfile\n");
582 printf(" --netfilter - build the firewall rules and commit them.\n");
583 printf(" --tail - \"tail -f\" functionality\n");
584 printf("Examples:\n");
585 printf(" # fnettrace - traffic trace\n");
586 printf(" # fnettrace --netfilter --log=logfile - netlocker, dump output in logfile\n");
587 printf(" # fnettrace --tail --log=logifile - similar to \"tail -f logfile\"\n");
588 printf("\n");
589}
590
591int main(int argc, char **argv) {
592 int i;
593
594#ifdef DEBUG
595 // radix test
596 radix_add(0x09000000, 0xff000000, "IBM");
597 radix_add(0x09090909, 0xffffffff, "Quad9 DNS");
598 radix_add(0x09000000, 0xff000000, "IBM");
599 printf("This test should print \"IBM, Quad9 DNS, IBM\"\n");
600 char *name = radix_longest_prefix_match(0x09040404);
601 printf("%s, ", name);
602 name = radix_longest_prefix_match(0x09090909);
603 printf("%s, ", name);
604 name = radix_longest_prefix_match(0x09322209);
605 printf("%s\n", name);
606#endif
607
608 for (i = 1; i < argc; i++) {
609 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
610 usage();
611 return 0;
612 }
613 else if (strcmp(argv[i], "--netfilter") == 0)
614 arg_netfilter = 1;
615 else if (strcmp(argv[i], "--tail") == 0)
616 arg_tail = 1;
617 else if (strncmp(argv[i], "--log=", 6) == 0)
618 arg_log = argv[i] + 6;
619 else {
620 fprintf(stderr, "Error: invalid argument\n");
621 return 1;
622 }
623 }
624
625 // tail
626 if (arg_tail) {
627 if (!arg_log) {
628 fprintf(stderr, "Error: no log file\n");
629 usage();
630 exit(1);
631 }
632
633 tail(arg_log);
634 sleep(5);
635 exit(0);
636 }
637
638 if (getuid() != 0) {
639 fprintf(stderr, "Error: you need to be root to run this program\n");
640 return 1;
641 }
642
643 ansi_clrscr();
644 if (arg_netfilter)
645 logprintf("starting network lockdown\n");
646 else {
647 char *fname = LIBDIR "/firejail/static-ip-map";
648 load_hostnames(fname);
649 }
650
651 run_trace();
652 if (arg_netfilter) {
653 // TCP path MTU discovery will not work properly since the firewall drops all ICMP packets
654 // Instead, we use iPacketization Layer PMTUD (RFC 4821) support in Linux kernel
655 int rv = system("echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing");
656 (void) rv;
657
658 deploy_netfilter();
659 sleep(3);
660 if (arg_log)
661 unlink(arg_log);
662 }
663
664 return 0;
665}
diff --git a/src/fnettrace/radix.c b/src/fnettrace/radix.c
new file mode 100644
index 000000000..c9493717d
--- /dev/null
+++ b/src/fnettrace/radix.c
@@ -0,0 +1,155 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <stdint.h>
24#include <assert.h>
25#include "radix.h"
26#include "fnettrace.h"
27
28typedef struct rnode_t {
29 struct rnode_t *zero;
30 struct rnode_t *one;
31 char *name;
32} RNode;
33
34RNode *head = 0;
35int radix_nodes = 0;
36
37// get rid of the malloc overhead
38#define RNODE_MAX_MALLOC 128
39static RNode *rnode_unused = NULL;
40static int rnode_malloc_cnt = 0;
41static RNode *rmalloc(void) {
42 if (rnode_unused == NULL || rnode_malloc_cnt >= RNODE_MAX_MALLOC) {
43 rnode_unused = malloc(sizeof(RNode) * RNODE_MAX_MALLOC);
44 if (!rnode_unused)
45 errExit("malloc");
46 memset(rnode_unused, 0, sizeof(RNode) * RNODE_MAX_MALLOC);
47 rnode_malloc_cnt = 0;
48 }
49
50 rnode_malloc_cnt++;
51 return rnode_unused + rnode_malloc_cnt - 1;
52}
53
54
55static inline char *duplicate_name(const char *name) {
56 assert(name);
57
58 if (strcmp(name, "United States") == 0)
59 return "United States";
60 else if (strcmp(name, "Amazon") == 0)
61 return "Amazon";
62 return strdup(name);
63}
64
65static inline RNode *addOne(RNode *ptr, char *name) {
66 assert(ptr);
67 if (ptr->one)
68 return ptr->one;
69 RNode *node = rmalloc();
70 assert(node);
71 if (name) {
72 node->name = duplicate_name(name);
73 if (!node->name)
74 errExit("duplicate name");
75 }
76
77 ptr->one = node;
78 return node;
79}
80
81static inline RNode *addZero(RNode *ptr, char *name) {
82 assert(ptr);
83 if (ptr->zero)
84 return ptr->zero;
85 RNode *node = rmalloc();
86 assert(node);
87 if (name) {
88 node->name = duplicate_name(name);
89 if (!node->name)
90 errExit("duplicate name");
91 }
92
93 ptr->zero = node;
94 return node;
95}
96
97
98// add to radix tree
99char *radix_add(uint32_t ip, uint32_t mask, char *name) {
100 assert(name);
101 uint32_t m = 0x80000000;
102 uint32_t lastm = 0;
103 if (head == 0) {
104 head = malloc(sizeof(RNode));
105 memset(head, 0, sizeof(RNode));
106 }
107 RNode *ptr = head;
108 radix_nodes++;
109
110 int i;
111 for (i = 0; i < 32; i++, m >>= 1) {
112 if (!(m & mask))
113 break;
114
115 lastm |= m;
116 int valid = (lastm == mask)? 1: 0;
117 if (m & ip)
118 ptr = addOne(ptr, (valid)? name: NULL);
119 else
120 ptr = addZero(ptr, (valid)? name: NULL);
121 }
122 assert(ptr);
123 if (!ptr->name) {
124 ptr->name = duplicate_name(name);
125 if (!ptr->name)
126 errExit("duplicate_name");
127 }
128
129 return ptr->name;
130}
131
132// find last match
133char *radix_longest_prefix_match(uint32_t ip) {
134 if (!head)
135 return NULL;
136
137 uint32_t m = 0x80000000;
138 RNode *ptr = head;
139 RNode *rv = NULL;
140
141 int i;
142 for (i = 0; i < 32; i++, m >>= 1) {
143 if (m & ip)
144 ptr = ptr->one;
145 else
146 ptr = ptr->zero;
147 if (!ptr)
148 break;
149 if (ptr->name)
150 rv = ptr;
151 }
152
153 return (rv)? rv->name: NULL;
154}
155
diff --git a/src/fnettrace/radix.h b/src/fnettrace/radix.h
new file mode 100644
index 000000000..c22c5c547
--- /dev/null
+++ b/src/fnettrace/radix.h
@@ -0,0 +1,27 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#ifndef RADIX_H
21#define RADIX_H
22
23extern int radix_nodes;
24char *radix_longest_prefix_match(uint32_t ip);
25char *radix_add(uint32_t ip, uint32_t mask, char *name);
26
27#endif \ No newline at end of file
diff --git a/src/fnettrace/static-ip-map b/src/fnettrace/static-ip-map
new file mode 100644
index 000000000..e24ecf218
--- /dev/null
+++ b/src/fnettrace/static-ip-map
@@ -0,0 +1,4044 @@
1#
2# Copyright (C) 2014-2022 Firejail Authors
3#
4# This file is part of firejail project
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20#
21# Static Internet Map
22#
23# Unfortunately we cannot do a hostname lookup. This will leak a lot of
24# information about what network resources we access.
25# A static map, helped out by geoip package available on all Linux distros,
26# will have to do it for now!
27#
28# Format:
29# CIDR-IPv4-address-range hostname
30# a single space between address and hostname
31# use '#' for comments
32# example: 9.9.9.0/24 Quad9 DNS
33#
34#
35
36# local network addresses
37192.168.0.0/16 local network
3810.0.0.0/8 local network
39172.16.0.0/16 local network
40
41# huge address ranges
426.0.0.0/8 US Army
437.0.0.0/8 US Army
449.0.0.0/8 IBM
4511.0.0.0/8 US Army
4617.0.0.0/8 Apple
4719.0.0.0/8 Ford
4821.0.0.0/8 US Army
4922.0.0.0/8 US Army
5026.0.0.0/8 US Army
5128.0.0.0/8 US Army
5229.0.0.0/8 US Army
5330.0.0.0/8 US Army
5433.0.0.0/8 US Army
5548.0.0.0/8 Prudential US
5655.0.0.0/8 US Army
5756.0.0.0/8 US Postal Service
58214.0.0.0/8 US Army
59215.0.0.0/8 US Army
60
61# whois/DNS
621.1.1.0/24 Cloudflare DNS
631.0.0.0/24 Cloudflare DNS
648.8.4.0/24 Google DNS
658.8.8.0/24 Google DNS
669.9.9.0/24 Quad9 DNS
6745.90.28.0/22 NextDNS
68149.112.112.0/24 Quad9 DNS
69149.112.120.0/21 CIRA DNS Canada
70176.103.128.0/19 Adguard DNS
71185.228.168.0/24 Cleanbrowsing DNS
72193.0.0.0/21 whois.ripe.net Netherlands
73199.5.26.0/24 whois.arin.net US
74199.15.80.0/21 whois.publicinterestregistry.net Canada
75199.15.88.0/24 whois.publicinterestregistry.net Canada
76199.71.0.0/24 whois.arin.net US
77199.212.0.0/24 whois.arin.net US
78200.3.12.0/22 whois.lacnic.net Uruguay
79201.159.220.0/22 whois.lacnic.net Ecuador
80
81# some popular websites
8231.13.24.0/21 Facebook
8331.13.64.0/18 Facebook
8464.63.0.0/18 Twitter
8569.171.224.0/19 Facebook
86104.244.40.0/21 Twitter
87129.134.0.0/16 Facebook
88140.82.112.0/20 GitHub
89157.240.0.0/16 Facebook
90185.199.108.0/22 GitHub
91188.64.224.0/21 Twitter
92192.0.64.0/18 Wordpress
93199.16.156.0/22 Twitter
94199.59.148.0/22 Twitter
95208.80.152.0/22 Wikipedia
96
97# Akamai
9823.0.0.0/12 Akamai
9923.32.0.0/11 Akamai
10023.64.0.0/14 Akamai
10123.72.0.0/13 Akamai
10223.192.0.0/11 Akamai
10372.246.0.0/15 Akamai
10496.6.0.0/15 Akamai
10596.16.0.0/15 Akamai
106104.64.0.0/10 Akamai
107184.24.0.0/13 Akamai
108184.50.0.0/15 Akamai
109184.84.0.0/14 Akamai
110
111# Fastly
11223.235.32.0/20 Fastly
11343.249.72.0/22 Fastly
114103.244.50.0/24 Fastly
115103.245.222.0/23 Fastly
116103.245.224.0/24 Fastly
117104.156.80.0/20 Fastly
118146.75.0.0/16 Fastly
119151.101.0.0/16 Fastly
120157.52.64.0/18 Fastly
121167.82.0.0/17 Fastly
122167.82.128.0/20 Fastly
123167.82.160.0/20 Fastly
124167.82.224.0/20 Fastly
125172.111.64.0/18 Fastly
126185.31.16.0/22 Fastly
127199.27.72.0/21 Fastly
128199.232.0.0/16 Fastly
129
130# MCI/Verizon
13172.21.80.0/20 MCI
132108.29.0.0/16 MCI
133108.30.0.0/16 MCI
134108.31.0.0/16 MCI
135108.3.128.0/17 MCI
136108.32.0.0/17 MCI
137108.32.128.0/17 MCI
138108.33.254.0/24 MCI
139108.33.255.0/24 MCI
140108.34.128.0/17 MCI
141108.34.16.0/20 MCI
142108.34.32.0/19 MCI
143108.34.64.0/18 MCI
144108.35.0.0/16 MCI
145108.36.0.0/16 MCI
146108.3.64.0/18 MCI
147108.37.0.0/16 MCI
148108.39.0.0/17 MCI
149108.39.128.0/17 MCI
150108.40.0.0/17 MCI
151108.4.0.0/17 MCI
152108.41.0.0/16 MCI
153108.4.128.0/19 MCI
154108.4.160.0/19 MCI
155108.4.192.0/18 MCI
156108.44.0.0/18 MCI
157108.44.128.0/17 MCI
158108.44.64.0/18 MCI
159108.45.0.0/16 MCI
160108.46.0.0/16 MCI
161192.229.128.0/17 MCI
162
163# Microsoft
16440.76.0.0/14 Microsoft
16540.96.0.0/12 Microsoft
16640.112.0.0/13 Microsoft
16740.124.0.0/16 Microsoft
16840.74.0.0/15 Microsoft
16940.80.0.0/12 Microsoft
17040.120.0.0/14 Microsoft
17140.125.0.0/17 Microsoft
17252.145.0.0/16 Microsoft
17352.148.0.0/14 Microsoft
17452.152.0.0/13 Microsoft
17552.146.0.0/15 Microsoft
17652.160.0.0/11 Microsoft
177
178# Yahoo
17963.250.192.0/19 Yahoo
18066.196.64.0/18 Yahoo
18167.195.0.0/16 Yahoo
18269.147.64.0/18 Yahoo
18376.13.0.0/16 Yahoo
18498.136.0.0/14 Yahoo
185206.190.32.0/19 Yahoo
186209.73.160.0/19 Yahoo
187209.191.64.0/18 Yahoo
188216.115.96.0/20 Yahoo
189
190# Google
191# from https://support.google.com/a/answer/10026322?hl=en
192# last update January 5, 2022
1938.34.208.0/20 Google
1948.35.192.0/20 Google
19523.236.48.0/20 Google
19623.251.128.0/19 Google
19734.64.0.0/10 Google
19834.128.0.0/10 Google
19935.184.0.0/13 Google
20035.192.0.0/14 Google
20135.196.0.0/15 Google
20235.198.0.0/16 Google
20335.199.0.0/17 Google
20435.199.128.0/18 Google
20535.200.0.0/13 Google
20635.208.0.0/12 Google
20735.224.0.0/12 Google
20835.240.0.0/13 Google
20964.15.112.0/20 Google
21064.233.160.0/19 Google
21166.102.0.0/20 Google
21266.249.64.0/19 Google
21370.32.128.0/19 Google
21472.14.192.0/18 Google
21574.114.24.0/21 Google
21674.125.0.0/16 Google
217104.154.0.0/15 Google
218104.196.0.0/14 Google
219104.237.160.0/19 Google
220107.167.160.0/19 Google
221107.178.192.0/18 Google
222108.59.80.0/20 Google
223108.170.192.0/18 Google
224108.177.0.0/17 Google
225130.211.0.0/16 Google
226136.112.0.0/12 Google
227142.250.0.0/15 Google
228146.148.0.0/17 Google
229162.216.148.0/22 Google
230162.222.176.0/21 Google
231172.110.32.0/21 Google
232172.217.0.0/16 Google
233172.253.0.0/16 Google
234173.194.0.0/16 Google
235173.255.112.0/20 Google
236192.158.28.0/22 Google
237192.178.0.0/15 Google
238193.186.4.0/24 Google
239199.36.154.0/23 Google
240199.36.156.0/24 Google
241199.192.112.0/22 Google
242199.223.232.0/21 Google
243207.223.160.0/20 Google
244208.65.152.0/22 Google
245208.68.108.0/22 Google
246208.81.188.0/22 Google
247208.117.224.0/19 Google
248209.85.128.0/17 Google
249216.58.192.0/19 Google
250216.73.80.0/20 Google
251216.239.32.0/19 Google
252
253
254#Cloudflare
255# from https://www.cloudflare.com/ips/
256# update April 8, 2021
257103.21.244.0/22 Cloudflare
258103.22.200.0/22 Cloudflare
259103.31.4.0/22 Cloudflare
260104.16.0.0/13 Cloudflare
261104.24.0.0/14 Cloudflare
262108.162.192.0/18 Cloudflare
263131.0.72.0/22 Cloudflare
264141.101.64.0/18 Cloudflare
265162.158.0.0/15 Cloudflare
266172.64.0.0/13 Cloudflare
267173.245.48.0/20 Cloudflare
268188.114.96.0/20 Cloudflare
269190.93.240.0/20 Cloudflare
270197.234.240.0/22 Cloudflare
271198.41.128.0/17 Cloudflare
272
273# Amazon
274# from https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html
275# update January 6, 2022
2763.0.0.0/15 Amazon
2773.2.0.0/24 Amazon
2783.2.2.0/24 Amazon
2793.2.3.0/24 Amazon
2803.2.8.0/21 Amazon
2813.3.0.0/23 Amazon
2823.3.5.0/24 Amazon
2833.3.6.0/23 Amazon
2843.3.8.0/21 Amazon
2853.3.16.0/21 Amazon
2863.3.24.0/22 Amazon
2873.3.28.0/22 Amazon
2883.4.0.0/24 Amazon
2893.4.1.0/24 Amazon
2903.4.2.0/24 Amazon
2913.4.3.0/24 Amazon
2923.4.4.0/24 Amazon
2933.4.6.0/24 Amazon
2943.4.7.0/24 Amazon
2953.4.16.0/21 Amazon
2963.4.24.0/21 Amazon
2973.5.0.0/19 Amazon
2983.5.32.0/22 Amazon
2993.5.36.0/22 Amazon
3003.5.40.0/22 Amazon
3013.5.44.0/22 Amazon
3023.5.48.0/22 Amazon
3033.5.52.0/22 Amazon
3043.5.64.0/21 Amazon
3053.5.72.0/23 Amazon
3063.5.76.0/22 Amazon
3073.5.80.0/21 Amazon
3083.5.128.0/22 Amazon
3093.5.132.0/23 Amazon
3103.5.134.0/23 Amazon
3113.5.136.0/22 Amazon
3123.5.140.0/22 Amazon
3133.5.144.0/23 Amazon
3143.5.146.0/23 Amazon
3153.5.148.0/22 Amazon
3163.5.152.0/21 Amazon
3173.5.160.0/22 Amazon
3183.5.164.0/22 Amazon
3193.5.168.0/23 Amazon
3203.5.208.0/22 Amazon
3213.5.212.0/23 Amazon
3223.5.216.0/22 Amazon
3233.5.220.0/22 Amazon
3243.5.224.0/22 Amazon
3253.5.228.0/22 Amazon
3263.5.232.0/22 Amazon
3273.5.236.0/22 Amazon
3283.5.240.0/22 Amazon
3293.5.244.0/22 Amazon
3303.5.248.0/22 Amazon
3313.5.252.0/22 Amazon
3323.6.0.0/15 Amazon
3333.8.0.0/14 Amazon
3343.12.0.0/16 Amazon
3353.13.0.0/16 Amazon
3363.14.0.0/15 Amazon
3373.16.0.0/14 Amazon
3383.20.0.0/14 Amazon
3393.24.0.0/14 Amazon
3403.28.0.0/15 Amazon
3413.30.0.0/15 Amazon
3423.32.0.0/16 Amazon
3433.33.34.0/24 Amazon
3443.33.35.0/24 Amazon
3453.33.128.0/17 Amazon
3463.34.0.0/15 Amazon
3473.36.0.0/14 Amazon
3483.48.0.0/12 Amazon
3493.64.0.0/12 Amazon
3503.80.0.0/12 Amazon
3513.96.0.0/15 Amazon
3523.98.0.0/15 Amazon
3533.100.0.0/16 Amazon
3543.101.0.0/16 Amazon
3553.104.0.0/14 Amazon
3563.108.0.0/14 Amazon
3573.112.0.0/14 Amazon
3583.116.0.0/14 Amazon
3593.120.0.0/14 Amazon
3603.124.0.0/14 Amazon
3613.128.0.0/15 Amazon
3623.130.0.0/16 Amazon
3633.131.0.0/16 Amazon
3643.132.0.0/14 Amazon
3653.136.0.0/13 Amazon
3663.144.0.0/13 Amazon
3673.152.0.0/13 Amazon
3683.208.0.0/12 Amazon
3693.224.0.0/12 Amazon
3703.240.0.0/13 Amazon
3713.248.0.0/13 Amazon
37213.32.0.0/15 Amazon
37313.34.0.128/27 Amazon
37413.34.0.160/27 Amazon
37513.34.1.0/27 Amazon
37613.34.1.32/27 Amazon
37713.34.3.128/27 Amazon
37813.34.3.160/27 Amazon
37913.34.3.192/27 Amazon
38013.34.3.224/27 Amazon
38113.34.4.64/27 Amazon
38213.34.4.96/27 Amazon
38313.34.5.12/32 Amazon
38413.34.5.13/32 Amazon
38513.34.5.14/32 Amazon
38613.34.5.15/32 Amazon
38713.34.5.16/32 Amazon
38813.34.5.17/32 Amazon
38913.34.5.44/32 Amazon
39013.34.5.45/32 Amazon
39113.34.5.46/32 Amazon
39213.34.5.47/32 Amazon
39313.34.5.48/32 Amazon
39413.34.5.49/32 Amazon
39513.34.5.78/32 Amazon
39613.34.5.79/32 Amazon
39713.34.5.80/32 Amazon
39813.34.5.81/32 Amazon
39913.34.5.110/32 Amazon
40013.34.5.111/32 Amazon
40113.34.5.112/32 Amazon
40213.34.5.113/32 Amazon
40313.34.5.128/27 Amazon
40413.34.5.160/27 Amazon
40513.34.5.192/27 Amazon
40613.34.5.224/27 Amazon
40713.34.6.192/27 Amazon
40813.34.6.224/27 Amazon
40913.34.7.64/27 Amazon
41013.34.7.96/27 Amazon
41113.34.8.64/27 Amazon
41213.34.8.96/27 Amazon
41313.34.9.0/27 Amazon
41413.34.9.32/27 Amazon
41513.34.10.128/27 Amazon
41613.34.10.160/27 Amazon
41713.34.11.0/27 Amazon
41813.34.11.32/27 Amazon
41913.34.11.128/27 Amazon
42013.34.11.160/27 Amazon
42113.34.12.64/27 Amazon
42213.34.12.96/27 Amazon
42313.34.12.192/27 Amazon
42413.34.12.242/32 Amazon
42513.34.12.243/32 Amazon
42613.34.12.244/32 Amazon
42713.34.12.245/32 Amazon
42813.34.13.18/32 Amazon
42913.34.13.19/32 Amazon
43013.34.13.20/32 Amazon
43113.34.13.21/32 Amazon
43213.34.13.50/32 Amazon
43313.34.13.51/32 Amazon
43413.34.13.52/32 Amazon
43513.34.13.53/32 Amazon
43613.34.14.128/27 Amazon
43713.34.14.160/27 Amazon
43813.34.14.192/27 Amazon
43913.34.14.224/27 Amazon
44013.34.15.0/27 Amazon
44113.34.15.32/27 Amazon
44213.34.16.64/27 Amazon
44313.34.16.96/27 Amazon
44413.34.16.192/27 Amazon
44513.34.17.24/29 Amazon
44613.34.17.64/27 Amazon
44713.34.17.96/27 Amazon
44813.34.18.192/27 Amazon
44913.34.18.224/27 Amazon
45013.34.19.192/27 Amazon
45113.34.19.224/27 Amazon
45213.34.20.0/27 Amazon
45313.34.20.32/27 Amazon
45413.34.20.64/27 Amazon
45513.34.20.96/27 Amazon
45613.34.21.64/27 Amazon
45713.34.21.96/27 Amazon
45813.34.22.88/29 Amazon
45913.34.22.160/27 Amazon
46013.34.22.192/27 Amazon
46113.34.22.224/27 Amazon
46213.34.23.0/27 Amazon
46313.34.23.32/27 Amazon
46413.34.23.64/27 Amazon
46513.34.23.96/27 Amazon
46613.34.23.128/27 Amazon
46713.34.23.160/27 Amazon
46813.34.23.192/27 Amazon
46913.34.23.224/27 Amazon
47013.34.24.64/27 Amazon
47113.34.24.96/27 Amazon
47213.34.24.128/27 Amazon
47313.34.24.160/27 Amazon
47413.34.24.192/27 Amazon
47513.34.25.64/27 Amazon
47613.34.25.96/27 Amazon
47713.34.25.128/27 Amazon
47813.34.25.160/27 Amazon
47913.34.25.192/27 Amazon
48013.34.25.248/29 Amazon
48113.34.26.0/27 Amazon
48213.34.26.32/27 Amazon
48313.34.26.64/27 Amazon
48413.34.26.96/27 Amazon
48513.34.26.128/27 Amazon
48613.34.26.160/27 Amazon
48713.34.26.192/27 Amazon
48813.34.26.224/27 Amazon
48913.34.27.16/32 Amazon
49013.34.27.17/32 Amazon
49113.34.27.32/27 Amazon
49213.34.27.64/27 Amazon
49313.34.27.96/27 Amazon
49413.34.27.128/27 Amazon
49513.34.28.0/27 Amazon
49613.34.28.32/27 Amazon
49713.34.28.64/27 Amazon
49813.34.28.96/27 Amazon
49913.34.28.128/27 Amazon
50013.34.28.160/27 Amazon
50113.34.28.192/27 Amazon
50213.34.28.224/27 Amazon
50313.34.29.0/27 Amazon
50413.34.29.32/27 Amazon
50513.34.29.64/27 Amazon
50613.34.29.96/27 Amazon
50713.34.29.128/27 Amazon
50813.34.29.160/27 Amazon
50913.34.29.192/27 Amazon
51013.34.29.224/27 Amazon
51113.34.30.0/27 Amazon
51213.34.30.32/27 Amazon
51313.34.30.64/27 Amazon
51413.34.30.96/27 Amazon
51513.34.30.128/27 Amazon
51613.34.30.160/27 Amazon
51713.34.30.192/27 Amazon
51813.34.30.224/27 Amazon
51913.34.31.0/27 Amazon
52013.34.31.32/27 Amazon
52113.34.31.64/27 Amazon
52213.34.31.96/27 Amazon
52313.34.31.128/27 Amazon
52413.34.31.160/27 Amazon
52513.34.31.192/27 Amazon
52613.34.31.224/27 Amazon
52713.34.32.0/27 Amazon
52813.34.32.32/27 Amazon
52913.34.32.64/27 Amazon
53013.34.32.96/27 Amazon
53113.34.32.128/27 Amazon
53213.34.32.160/27 Amazon
53313.34.33.0/27 Amazon
53413.34.33.32/27 Amazon
53513.34.33.64/27 Amazon
53613.34.33.96/27 Amazon
53713.34.33.128/27 Amazon
53813.34.33.160/27 Amazon
53913.34.33.192/27 Amazon
54013.34.33.224/27 Amazon
54113.34.34.0/27 Amazon
54213.34.34.32/27 Amazon
54313.34.34.64/27 Amazon
54413.34.34.96/27 Amazon
54513.34.34.128/27 Amazon
54613.34.34.160/27 Amazon
54713.34.34.192/27 Amazon
54813.34.34.224/27 Amazon
54913.34.35.0/27 Amazon
55013.34.35.32/27 Amazon
55113.34.35.64/27 Amazon
55213.34.35.96/27 Amazon
55313.34.35.128/27 Amazon
55413.34.35.160/27 Amazon
55513.34.35.192/27 Amazon
55613.34.35.224/27 Amazon
55713.34.36.0/27 Amazon
55813.34.36.32/27 Amazon
55913.34.36.64/27 Amazon
56013.34.36.96/27 Amazon
56113.34.36.128/27 Amazon
56213.34.36.160/27 Amazon
56313.34.36.192/27 Amazon
56413.34.36.224/27 Amazon
56513.34.37.0/27 Amazon
56613.34.37.32/27 Amazon
56713.34.37.64/27 Amazon
56813.34.37.96/27 Amazon
56913.34.37.128/27 Amazon
57013.34.37.160/27 Amazon
57113.34.37.192/27 Amazon
57213.34.37.224/27 Amazon
57313.34.38.0/27 Amazon
57413.34.38.32/27 Amazon
57513.34.38.64/27 Amazon
57613.34.38.96/27 Amazon
57713.34.38.128/27 Amazon
57813.34.38.160/27 Amazon
57913.34.39.0/27 Amazon
58013.34.39.32/27 Amazon
58113.34.39.64/27 Amazon
58213.34.39.96/27 Amazon
58313.34.39.128/27 Amazon
58413.34.39.160/27 Amazon
58513.34.39.192/27 Amazon
58613.34.39.224/27 Amazon
58713.34.40.0/27 Amazon
58813.34.40.32/27 Amazon
58913.34.40.64/27 Amazon
59013.34.40.96/27 Amazon
59113.34.40.128/27 Amazon
59213.34.40.160/27 Amazon
59313.34.40.192/27 Amazon
59413.34.40.224/27 Amazon
59513.34.41.0/27 Amazon
59613.34.41.32/27 Amazon
59713.34.41.64/27 Amazon
59813.34.41.96/27 Amazon
59913.34.41.128/27 Amazon
60013.34.41.160/27 Amazon
60113.34.41.192/27 Amazon
60213.34.41.224/27 Amazon
60313.34.42.0/27 Amazon
60413.34.42.32/27 Amazon
60513.34.42.64/27 Amazon
60613.34.42.96/27 Amazon
60713.34.42.128/27 Amazon
60813.34.42.160/27 Amazon
60913.34.42.192/27 Amazon
61013.34.42.224/27 Amazon
61113.34.43.0/27 Amazon
61213.34.43.32/27 Amazon
61313.34.43.64/27 Amazon
61413.34.43.96/27 Amazon
61513.34.43.128/27 Amazon
61613.34.43.160/27 Amazon
61713.34.43.192/27 Amazon
61813.34.43.224/27 Amazon
61913.34.44.0/27 Amazon
62013.34.44.32/27 Amazon
62113.34.44.64/27 Amazon
62213.34.44.96/27 Amazon
62313.34.44.128/27 Amazon
62413.34.44.160/27 Amazon
62513.34.44.192/27 Amazon
62613.34.44.224/27 Amazon
62713.34.45.0/27 Amazon
62813.34.45.32/27 Amazon
62913.34.45.64/27 Amazon
63013.34.45.96/27 Amazon
63113.34.45.128/27 Amazon
63213.34.45.160/27 Amazon
63313.34.45.192/27 Amazon
63413.34.45.224/27 Amazon
63513.34.46.0/27 Amazon
63613.34.46.32/27 Amazon
63713.34.46.64/27 Amazon
63813.34.46.96/27 Amazon
63913.34.46.128/27 Amazon
64013.34.46.160/27 Amazon
64113.34.46.192/27 Amazon
64213.34.46.224/27 Amazon
64313.34.47.0/27 Amazon
64413.34.47.32/27 Amazon
64513.34.47.64/27 Amazon
64613.34.47.96/27 Amazon
64713.34.47.128/27 Amazon
64813.34.47.160/27 Amazon
64913.34.47.192/27 Amazon
65013.34.47.224/27 Amazon
65113.34.48.0/27 Amazon
65213.34.48.32/27 Amazon
65313.34.48.64/27 Amazon
65413.34.48.96/27 Amazon
65513.34.48.128/27 Amazon
65613.34.48.160/27 Amazon
65713.34.48.192/27 Amazon
65813.34.48.224/27 Amazon
65913.34.49.0/27 Amazon
66013.34.49.32/27 Amazon
66113.34.49.64/27 Amazon
66213.34.49.96/27 Amazon
66313.34.49.128/27 Amazon
66413.34.49.160/27 Amazon
66513.34.49.192/27 Amazon
66613.34.49.224/27 Amazon
66713.34.50.0/27 Amazon
66813.34.50.32/27 Amazon
66913.34.50.64/27 Amazon
67013.34.50.96/27 Amazon
67113.34.50.128/27 Amazon
67213.34.50.160/27 Amazon
67313.34.50.192/27 Amazon
67413.34.50.224/27 Amazon
67513.34.51.0/27 Amazon
67613.34.51.32/27 Amazon
67713.34.51.64/27 Amazon
67813.34.51.96/27 Amazon
67913.34.51.128/27 Amazon
68013.34.51.160/27 Amazon
68113.34.51.192/27 Amazon
68213.34.51.224/27 Amazon
68313.34.52.0/27 Amazon
68413.34.52.32/27 Amazon
68513.34.52.64/27 Amazon
68613.34.52.96/27 Amazon
68713.34.52.128/27 Amazon
68813.34.52.160/27 Amazon
68913.34.52.192/27 Amazon
69013.34.52.224/27 Amazon
69113.34.53.0/27 Amazon
69213.34.53.32/27 Amazon
69313.34.53.64/27 Amazon
69413.34.53.96/27 Amazon
69513.34.53.128/27 Amazon
69613.34.53.160/27 Amazon
69713.34.53.192/27 Amazon
69813.34.53.224/27 Amazon
69913.34.54.0/27 Amazon
70013.34.54.32/27 Amazon
70113.34.54.64/27 Amazon
70213.34.54.96/27 Amazon
70313.34.54.128/27 Amazon
70413.34.54.160/27 Amazon
70513.34.54.192/27 Amazon
70613.34.54.224/27 Amazon
70713.34.55.0/27 Amazon
70813.34.55.32/27 Amazon
70913.34.55.64/27 Amazon
71013.34.55.96/27 Amazon
71113.34.55.128/27 Amazon
71213.34.55.160/27 Amazon
71313.34.55.192/27 Amazon
71413.34.55.224/27 Amazon
71513.34.56.0/27 Amazon
71613.34.56.32/27 Amazon
71713.34.56.64/27 Amazon
71813.34.56.96/27 Amazon
71913.34.56.128/27 Amazon
72013.34.56.160/27 Amazon
72113.34.56.192/27 Amazon
72213.34.56.224/27 Amazon
72313.34.57.0/27 Amazon
72413.34.57.32/27 Amazon
72513.34.57.64/27 Amazon
72613.34.57.96/27 Amazon
72713.34.57.128/27 Amazon
72813.34.57.160/27 Amazon
72913.34.57.192/27 Amazon
73013.34.57.224/27 Amazon
73113.34.58.0/27 Amazon
73213.34.58.32/27 Amazon
73313.34.58.64/27 Amazon
73413.34.58.96/27 Amazon
73513.34.58.128/27 Amazon
73613.34.58.160/27 Amazon
73713.34.58.192/27 Amazon
73813.34.58.224/27 Amazon
73913.34.59.0/27 Amazon
74013.34.59.32/27 Amazon
74113.34.59.64/27 Amazon
74213.34.59.96/27 Amazon
74313.34.59.128/27 Amazon
74413.34.59.160/27 Amazon
74513.34.59.192/27 Amazon
74613.34.59.224/27 Amazon
74713.34.60.0/27 Amazon
74813.34.60.32/27 Amazon
74913.34.60.64/27 Amazon
75013.34.60.96/27 Amazon
75113.34.60.128/27 Amazon
75213.34.60.160/27 Amazon
75313.34.60.192/27 Amazon
75413.34.60.224/27 Amazon
75513.34.61.0/27 Amazon
75613.34.61.32/27 Amazon
75713.34.61.64/27 Amazon
75813.34.61.96/27 Amazon
75913.34.61.128/27 Amazon
76013.34.61.160/27 Amazon
76113.34.61.192/27 Amazon
76213.34.61.224/27 Amazon
76313.34.62.0/27 Amazon
76413.34.62.32/27 Amazon
76513.34.62.128/27 Amazon
76613.34.62.160/27 Amazon
76713.34.62.192/27 Amazon
76813.34.62.224/27 Amazon
76913.34.63.0/27 Amazon
77013.34.63.32/27 Amazon
77113.34.63.64/27 Amazon
77213.34.63.96/27 Amazon
77313.34.63.128/27 Amazon
77413.34.63.160/27 Amazon
77513.35.0.0/16 Amazon
77613.36.0.0/14 Amazon
77713.40.0.0/14 Amazon
77813.44.0.0/14 Amazon
77913.48.0.0/15 Amazon
78013.50.0.0/16 Amazon
78113.51.0.0/16 Amazon
78213.52.0.0/16 Amazon
78313.53.0.0/16 Amazon
78413.54.0.0/15 Amazon
78513.56.0.0/16 Amazon
78613.57.0.0/16 Amazon
78713.58.0.0/15 Amazon
78813.112.0.0/14 Amazon
78913.124.0.0/16 Amazon
79013.125.0.0/16 Amazon
79113.126.0.0/15 Amazon
79213.200.0.0/13 Amazon
79313.208.0.0/16 Amazon
79413.209.0.0/16 Amazon
79513.210.0.0/15 Amazon
79613.212.0.0/15 Amazon
79713.214.0.0/15 Amazon
79813.224.0.0/14 Amazon
79913.228.0.0/15 Amazon
80013.230.0.0/15 Amazon
80113.232.0.0/14 Amazon
80213.236.0.0/14 Amazon
80313.244.0.0/15 Amazon
80413.246.0.0/16 Amazon
80513.247.0.0/16 Amazon
80613.248.0.0/20 Amazon
80713.248.16.0/21 Amazon
80813.248.24.0/22 Amazon
80913.248.28.0/22 Amazon
81013.248.32.0/20 Amazon
81113.248.48.0/21 Amazon
81213.248.56.0/22 Amazon
81313.248.60.0/22 Amazon
81413.248.64.0/24 Amazon
81513.248.65.0/24 Amazon
81613.248.66.0/24 Amazon
81713.248.67.0/24 Amazon
81813.248.68.0/24 Amazon
81913.248.69.0/24 Amazon
82013.248.70.0/24 Amazon
82113.248.71.0/24 Amazon
82213.248.96.0/24 Amazon
82313.248.97.0/24 Amazon
82413.248.98.0/24 Amazon
82513.248.99.0/24 Amazon
82613.248.100.0/24 Amazon
82713.248.101.0/24 Amazon
82813.248.102.0/24 Amazon
82913.248.103.0/24 Amazon
83013.248.104.0/24 Amazon
83113.248.105.0/24 Amazon
83213.248.106.0/24 Amazon
83313.248.107.0/24 Amazon
83413.248.108.0/24 Amazon
83513.248.109.0/24 Amazon
83613.248.111.0/24 Amazon
83713.248.112.0/24 Amazon
83813.248.113.0/24 Amazon
83913.248.114.0/24 Amazon
84013.248.115.0/24 Amazon
84113.248.116.0/24 Amazon
84213.248.117.0/24 Amazon
84313.248.118.0/24 Amazon
84413.248.119.0/24 Amazon
84513.248.120.0/24 Amazon
84613.248.121.0/24 Amazon
84713.248.122.0/24 Amazon
84813.248.123.0/24 Amazon
84913.248.124.0/24 Amazon
85013.248.125.0/24 Amazon
85113.248.126.0/24 Amazon
85213.248.127.0/24 Amazon
85313.248.128.0/17 Amazon
85413.249.0.0/16 Amazon
85513.250.0.0/15 Amazon
85615.152.0.0/16 Amazon
85715.156.0.0/15 Amazon
85815.158.0.0/16 Amazon
85915.160.0.0/16 Amazon
86015.161.0.0/16 Amazon
86115.164.0.0/15 Amazon
86215.168.0.0/16 Amazon
86315.177.0.0/18 Amazon
86415.177.64.0/23 Amazon
86515.177.66.0/23 Amazon
86615.177.68.0/23 Amazon
86715.177.70.0/23 Amazon
86815.177.72.0/24 Amazon
86915.177.73.0/24 Amazon
87015.177.74.0/24 Amazon
87115.177.75.0/24 Amazon
87215.177.76.0/24 Amazon
87315.177.77.0/24 Amazon
87415.177.78.0/24 Amazon
87515.177.79.0/24 Amazon
87615.177.80.0/24 Amazon
87715.177.81.0/24 Amazon
87815.177.82.0/24 Amazon
87915.177.83.0/24 Amazon
88015.177.84.0/24 Amazon
88115.177.85.0/24 Amazon
88215.177.86.0/24 Amazon
88315.177.87.0/24 Amazon
88415.177.88.0/24 Amazon
88515.177.89.0/24 Amazon
88615.177.90.0/24 Amazon
88715.177.91.0/24 Amazon
88815.177.92.0/24 Amazon
88915.181.0.0/20 Amazon
89015.181.16.0/20 Amazon
89115.181.32.0/21 Amazon
89215.181.40.0/21 Amazon
89315.181.48.0/20 Amazon
89415.181.64.0/20 Amazon
89515.181.80.0/20 Amazon
89615.181.96.0/20 Amazon
89715.181.112.0/22 Amazon
89815.181.116.0/22 Amazon
89915.181.120.0/21 Amazon
90015.181.128.0/20 Amazon
90115.181.144.0/20 Amazon
90215.181.160.0/20 Amazon
90315.181.176.0/20 Amazon
90415.181.192.0/19 Amazon
90515.181.224.0/21 Amazon
90615.181.232.0/21 Amazon
90715.181.240.0/24 Amazon
90815.181.241.0/24 Amazon
90915.181.242.0/24 Amazon
91015.181.243.0/24 Amazon
91115.181.244.0/24 Amazon
91215.181.245.0/24 Amazon
91315.181.246.0/24 Amazon
91415.181.247.0/24 Amazon
91515.181.248.0/24 Amazon
91615.181.249.0/24 Amazon
91715.181.250.0/24 Amazon
91815.181.251.0/24 Amazon
91915.181.252.0/24 Amazon
92015.181.253.0/24 Amazon
92115.181.254.0/24 Amazon
92215.184.0.0/16 Amazon
92315.185.0.0/16 Amazon
92415.188.0.0/16 Amazon
92515.191.0.0/16 Amazon
92615.193.0.0/19 Amazon
92715.197.0.0/23 Amazon
92815.197.2.0/24 Amazon
92915.197.3.0/24 Amazon
93015.197.4.0/22 Amazon
93115.197.8.0/22 Amazon
93215.197.12.0/22 Amazon
93315.197.16.0/23 Amazon
93415.197.18.0/23 Amazon
93515.197.20.0/22 Amazon
93615.197.24.0/22 Amazon
93715.197.28.0/23 Amazon
93815.197.30.0/23 Amazon
93915.197.32.0/23 Amazon
94015.197.128.0/17 Amazon
94115.200.0.0/16 Amazon
94215.205.0.0/16 Amazon
94315.206.0.0/15 Amazon
94415.220.0.0/20 Amazon
94515.220.16.0/20 Amazon
94615.220.220.0/23 Amazon
94715.220.222.0/23 Amazon
94815.220.224.0/23 Amazon
94915.220.226.0/24 Amazon
95015.220.250.0/23 Amazon
95115.220.252.0/22 Amazon
95215.221.0.0/24 Amazon
95315.221.1.0/24 Amazon
95415.221.2.0/24 Amazon
95515.221.3.0/24 Amazon
95615.221.4.0/23 Amazon
95715.221.6.0/24 Amazon
95815.221.7.0/24 Amazon
95915.221.8.0/21 Amazon
96015.221.16.0/22 Amazon
96115.221.20.0/22 Amazon
96215.221.24.0/21 Amazon
96315.221.33.0/24 Amazon
96415.221.34.0/24 Amazon
96515.221.35.0/24 Amazon
96615.221.36.0/22 Amazon
96715.221.40.0/21 Amazon
96815.221.48.0/24 Amazon
96915.221.49.0/24 Amazon
97015.221.50.0/24 Amazon
97115.221.51.0/24 Amazon
97215.221.52.0/24 Amazon
97315.221.53.0/24 Amazon
97415.222.0.0/15 Amazon
97515.228.0.0/15 Amazon
97615.230.0.4/32 Amazon
97715.230.0.5/32 Amazon
97815.230.0.6/31 Amazon
97915.230.0.12/31 Amazon
98015.230.0.14/32 Amazon
98115.230.4.19/32 Amazon
98215.230.4.152/31 Amazon
98315.230.4.154/31 Amazon
98415.230.4.156/31 Amazon
98515.230.4.158/31 Amazon
98615.230.4.160/31 Amazon
98715.230.4.162/31 Amazon
98815.230.4.176/28 Amazon
98915.230.5.0/24 Amazon
99015.230.6.0/24 Amazon
99115.230.14.12/32 Amazon
99215.230.14.18/31 Amazon
99315.230.14.20/31 Amazon
99415.230.14.252/31 Amazon
99515.230.16.0/32 Amazon
99615.230.16.12/32 Amazon
99715.230.16.17/32 Amazon
99815.230.16.18/31 Amazon
99915.230.16.20/31 Amazon
100015.230.16.252/31 Amazon
100115.230.18.0/24 Amazon
100215.230.21.0/24 Amazon
100315.230.22.0/24 Amazon
100415.230.23.0/24 Amazon
100515.230.24.0/22 Amazon
100615.230.28.0/24 Amazon
100715.230.29.0/24 Amazon
100815.230.30.0/24 Amazon
100915.230.31.0/24 Amazon
101015.230.32.0/24 Amazon
101115.230.35.0/24 Amazon
101215.230.36.0/23 Amazon
101315.230.38.0/24 Amazon
101415.230.39.0/31 Amazon
101515.230.39.2/31 Amazon
101615.230.39.4/31 Amazon
101715.230.39.6/31 Amazon
101815.230.39.8/31 Amazon
101915.230.39.10/31 Amazon
102015.230.39.12/31 Amazon
102115.230.39.14/31 Amazon
102215.230.39.16/31 Amazon
102315.230.39.18/31 Amazon
102415.230.39.20/31 Amazon
102515.230.39.22/31 Amazon
102615.230.39.24/31 Amazon
102715.230.39.26/31 Amazon
102815.230.39.28/31 Amazon
102915.230.39.30/31 Amazon
103015.230.39.32/31 Amazon
103115.230.39.34/31 Amazon
103215.230.39.36/31 Amazon
103315.230.39.38/31 Amazon
103415.230.39.40/31 Amazon
103515.230.39.42/31 Amazon
103615.230.39.44/31 Amazon
103715.230.39.46/31 Amazon
103815.230.39.48/31 Amazon
103915.230.39.50/31 Amazon
104015.230.39.52/31 Amazon
104115.230.39.54/31 Amazon
104215.230.39.56/31 Amazon
104315.230.39.58/31 Amazon
104415.230.39.60/31 Amazon
104515.230.39.62/31 Amazon
104615.230.39.64/31 Amazon
104715.230.39.66/31 Amazon
104815.230.39.68/31 Amazon
104915.230.39.70/31 Amazon
105015.230.39.72/31 Amazon
105115.230.39.74/31 Amazon
105215.230.39.76/31 Amazon
105315.230.39.78/31 Amazon
105415.230.39.80/31 Amazon
105515.230.39.82/31 Amazon
105615.230.39.84/31 Amazon
105715.230.39.86/31 Amazon
105815.230.39.88/31 Amazon
105915.230.39.90/31 Amazon
106015.230.39.92/31 Amazon
106115.230.39.94/31 Amazon
106215.230.39.96/31 Amazon
106315.230.39.98/31 Amazon
106415.230.39.100/31 Amazon
106515.230.39.102/31 Amazon
106615.230.39.104/31 Amazon
106715.230.39.106/31 Amazon
106815.230.39.108/31 Amazon
106915.230.39.110/31 Amazon
107015.230.39.112/31 Amazon
107115.230.39.114/31 Amazon
107215.230.39.116/31 Amazon
107315.230.39.118/31 Amazon
107415.230.39.120/31 Amazon
107515.230.39.122/31 Amazon
107615.230.39.124/31 Amazon
107715.230.39.126/31 Amazon
107815.230.39.128/31 Amazon
107915.230.39.130/31 Amazon
108015.230.39.132/31 Amazon
108115.230.39.134/31 Amazon
108215.230.39.136/31 Amazon
108315.230.39.138/31 Amazon
108415.230.39.140/31 Amazon
108515.230.39.142/31 Amazon
108615.230.39.144/31 Amazon
108715.230.39.146/31 Amazon
108815.230.39.148/31 Amazon
108915.230.39.150/31 Amazon
109015.230.39.152/31 Amazon
109115.230.39.154/31 Amazon
109215.230.39.156/31 Amazon
109315.230.39.158/31 Amazon
109415.230.39.160/31 Amazon
109515.230.39.162/31 Amazon
109615.230.39.164/31 Amazon
109715.230.39.166/31 Amazon
109815.230.39.168/31 Amazon
109915.230.39.170/31 Amazon
110015.230.39.172/31 Amazon
110115.230.39.174/31 Amazon
110215.230.39.176/31 Amazon
110315.230.39.178/31 Amazon
110415.230.39.180/31 Amazon
110515.230.39.182/31 Amazon
110615.230.39.184/31 Amazon
110715.230.39.186/31 Amazon
110815.230.39.188/31 Amazon
110915.230.39.190/31 Amazon
111015.230.39.192/31 Amazon
111115.230.39.194/31 Amazon
111215.230.39.196/31 Amazon
111315.230.39.198/31 Amazon
111415.230.39.200/31 Amazon
111515.230.39.202/31 Amazon
111615.230.39.204/31 Amazon
111715.230.39.206/31 Amazon
111815.230.39.208/31 Amazon
111915.230.39.210/31 Amazon
112015.230.39.212/31 Amazon
112115.230.39.214/31 Amazon
112215.230.39.216/31 Amazon
112315.230.39.218/31 Amazon
112415.230.39.220/31 Amazon
112515.230.39.222/31 Amazon
112615.230.39.224/31 Amazon
112715.230.39.226/31 Amazon
112815.230.39.228/31 Amazon
112915.230.39.230/31 Amazon
113015.230.39.232/31 Amazon
113115.230.39.234/31 Amazon
113215.230.39.236/31 Amazon
113315.230.39.238/31 Amazon
113415.230.39.240/31 Amazon
113515.230.39.242/31 Amazon
113615.230.39.244/31 Amazon
113715.230.39.246/31 Amazon
113815.230.39.248/31 Amazon
113915.230.39.250/31 Amazon
114015.230.39.252/31 Amazon
114115.230.39.254/31 Amazon
114215.230.40.0/24 Amazon
114315.230.41.0/24 Amazon
114415.230.42.0/24 Amazon
114515.230.43.0/24 Amazon
114615.230.49.0/24 Amazon
114715.230.50.0/24 Amazon
114815.230.51.0/24 Amazon
114915.230.52.0/24 Amazon
115015.230.53.0/24 Amazon
115115.230.54.0/24 Amazon
115215.230.55.0/24 Amazon
115315.230.56.0/24 Amazon
115415.230.57.0/24 Amazon
115515.230.58.0/24 Amazon
115615.230.59.0/24 Amazon
115715.230.60.0/24 Amazon
115815.230.61.0/24 Amazon
115915.230.64.192/26 Amazon
116015.230.65.0/26 Amazon
116115.230.65.64/26 Amazon
116215.230.65.128/25 Amazon
116315.230.66.0/25 Amazon
116415.230.66.128/25 Amazon
116515.230.67.0/26 Amazon
116615.230.67.64/26 Amazon
116715.230.67.128/26 Amazon
116815.230.67.192/26 Amazon
116915.230.68.0/26 Amazon
117015.230.68.64/26 Amazon
117115.230.68.128/26 Amazon
117215.230.68.192/26 Amazon
117315.230.69.0/26 Amazon
117415.230.69.64/26 Amazon
117515.230.69.128/26 Amazon
117615.230.69.192/26 Amazon
117715.230.70.0/26 Amazon
117815.230.70.64/26 Amazon
117915.230.70.128/26 Amazon
118015.230.70.192/26 Amazon
118115.230.71.0/26 Amazon
118215.230.71.64/26 Amazon
118315.230.71.128/26 Amazon
118415.230.71.192/26 Amazon
118515.230.72.0/26 Amazon
118615.230.72.64/26 Amazon
118715.230.72.128/26 Amazon
118815.230.72.192/26 Amazon
118915.230.73.0/26 Amazon
119015.230.73.64/26 Amazon
119115.230.73.128/26 Amazon
119215.230.73.192/26 Amazon
119315.230.74.0/26 Amazon
119415.230.74.64/26 Amazon
119515.230.74.128/26 Amazon
119615.230.74.192/26 Amazon
119715.230.75.0/26 Amazon
119815.230.75.64/26 Amazon
119915.230.75.128/26 Amazon
120015.230.75.192/26 Amazon
120115.230.76.0/26 Amazon
120215.230.76.64/26 Amazon
120315.230.76.128/26 Amazon
120415.230.76.192/26 Amazon
120515.230.77.0/26 Amazon
120615.230.77.64/26 Amazon
120715.230.77.128/26 Amazon
120815.230.77.192/26 Amazon
120915.230.78.0/26 Amazon
121015.230.78.64/26 Amazon
121115.230.78.128/26 Amazon
121215.230.78.192/26 Amazon
121315.230.79.0/26 Amazon
121415.230.79.64/26 Amazon
121515.230.79.128/26 Amazon
121615.230.80.0/24 Amazon
121715.230.81.0/24 Amazon
121815.230.82.0/24 Amazon
121915.230.83.0/24 Amazon
122015.230.84.0/24 Amazon
122115.230.85.0/24 Amazon
122215.230.86.0/24 Amazon
122315.230.87.0/24 Amazon
122415.230.88.0/24 Amazon
122515.230.89.0/24 Amazon
122615.230.90.0/24 Amazon
122715.230.91.0/24 Amazon
122815.230.92.0/24 Amazon
122915.230.93.0/24 Amazon
123015.230.94.0/24 Amazon
123115.230.129.0/24 Amazon
123215.230.130.0/24 Amazon
123315.230.131.0/32 Amazon
123415.230.131.1/32 Amazon
123515.230.131.2/32 Amazon
123615.230.131.3/32 Amazon
123715.230.131.4/32 Amazon
123815.230.131.5/32 Amazon
123915.230.131.6/32 Amazon
124015.230.131.7/32 Amazon
124115.230.131.8/32 Amazon
124215.230.131.9/32 Amazon
124315.230.131.10/31 Amazon
124415.230.131.12/31 Amazon
124515.230.131.14/32 Amazon
124615.230.131.15/32 Amazon
124715.230.131.16/28 Amazon
124815.230.131.32/28 Amazon
124915.230.131.48/28 Amazon
125015.230.131.64/28 Amazon
125115.230.131.80/28 Amazon
125215.230.131.96/28 Amazon
125315.230.131.112/28 Amazon
125415.230.131.128/28 Amazon
125515.230.131.144/28 Amazon
125615.230.131.160/31 Amazon
125715.230.131.162/31 Amazon
125815.230.131.164/31 Amazon
125915.230.131.166/31 Amazon
126015.230.131.168/31 Amazon
126115.230.131.170/31 Amazon
126215.230.131.172/31 Amazon
126315.230.131.174/31 Amazon
126415.230.132.0/24 Amazon
126515.230.133.0/28 Amazon
126615.230.133.16/32 Amazon
126715.230.133.17/32 Amazon
126815.230.133.18/31 Amazon
126915.230.133.20/31 Amazon
127015.230.133.22/31 Amazon
127115.230.133.24/32 Amazon
127215.230.133.26/31 Amazon
127315.230.133.28/31 Amazon
127415.230.134.0/24 Amazon
127515.230.135.0/24 Amazon
127615.230.136.0/24 Amazon
127715.230.137.0/24 Amazon
127815.230.138.0/24 Amazon
127915.230.140.0/24 Amazon
128015.230.141.0/24 Amazon
128115.230.142.0/24 Amazon
128215.230.143.0/24 Amazon
128315.230.144.0/24 Amazon
128415.230.145.0/24 Amazon
128515.230.148.0/24 Amazon
128615.230.149.0/31 Amazon
128715.230.149.2/31 Amazon
128815.230.149.4/31 Amazon
128915.230.149.8/31 Amazon
129015.230.149.10/32 Amazon
129115.230.149.11/32 Amazon
129215.230.150.0/23 Amazon
129315.230.152.0/24 Amazon
129415.230.153.0/24 Amazon
129515.230.154.0/23 Amazon
129615.230.156.0/24 Amazon
129715.230.157.0/24 Amazon
129815.230.158.0/23 Amazon
129915.230.160.0/24 Amazon
130015.230.161.0/24 Amazon
130115.230.162.0/24 Amazon
130215.230.163.0/24 Amazon
130315.230.164.0/24 Amazon
130415.230.165.0/24 Amazon
130515.230.166.0/24 Amazon
130615.230.170.0/23 Amazon
130715.230.173.0/24 Amazon
130815.230.174.0/24 Amazon
130915.230.176.0/24 Amazon
131015.230.177.0/31 Amazon
131115.230.177.2/31 Amazon
131215.230.178.0/24 Amazon
131315.230.179.0/29 Amazon
131415.230.179.8/29 Amazon
131515.230.179.16/29 Amazon
131615.230.181.0/24 Amazon
131715.230.182.0/24 Amazon
131815.230.183.0/24 Amazon
131915.230.184.0/24 Amazon
132015.230.185.0/24 Amazon
132115.230.186.0/24 Amazon
132215.230.188.0/25 Amazon
132315.230.188.128/25 Amazon
132415.230.189.0/25 Amazon
132515.230.189.128/25 Amazon
132615.230.190.0/25 Amazon
132715.230.190.128/25 Amazon
132815.230.192.0/24 Amazon
132915.230.193.0/24 Amazon
133015.230.195.0/24 Amazon
133115.230.196.0/24 Amazon
133215.230.197.0/24 Amazon
133315.230.198.0/24 Amazon
133415.230.199.0/28 Amazon
133515.230.200.0/24 Amazon
133615.230.201.0/24 Amazon
133715.230.202.0/30 Amazon
133815.230.203.0/24 Amazon
133915.230.204.0/32 Amazon
134015.230.204.1/32 Amazon
134115.230.204.2/32 Amazon
134215.230.204.3/32 Amazon
134315.230.205.0/24 Amazon
134415.230.206.0/24 Amazon
134515.230.207.0/24 Amazon
134615.231.0.0/16 Amazon
134715.236.0.0/15 Amazon
134815.248.8.0/22 Amazon
134915.248.16.0/22 Amazon
135015.248.20.0/22 Amazon
135115.248.24.0/22 Amazon
135215.248.28.0/22 Amazon
135315.248.32.0/22 Amazon
135415.248.36.0/22 Amazon
135515.248.40.0/22 Amazon
135615.251.0.0/32 Amazon
135715.251.0.1/32 Amazon
135815.251.0.2/32 Amazon
135915.251.0.3/32 Amazon
136015.251.0.4/32 Amazon
136115.251.0.5/32 Amazon
136215.251.0.6/32 Amazon
136315.251.0.7/32 Amazon
136415.251.0.8/32 Amazon
136515.251.0.9/32 Amazon
136615.251.0.10/32 Amazon
136715.251.0.11/32 Amazon
136815.251.0.12/32 Amazon
136915.251.0.13/32 Amazon
137015.251.0.14/32 Amazon
137115.251.0.15/32 Amazon
137215.253.0.0/16 Amazon
137315.254.0.0/16 Amazon
137416.12.0.0/23 Amazon
137516.12.2.0/24 Amazon
137616.12.4.0/23 Amazon
137716.12.6.0/23 Amazon
137816.12.8.0/24 Amazon
137916.16.0.0/16 Amazon
138016.50.0.0/15 Amazon
138116.62.0.0/15 Amazon
138216.162.0.0/15 Amazon
138316.168.0.0/15 Amazon
138416.170.0.0/15 Amazon
138518.60.0.0/15 Amazon
138618.64.0.0/14 Amazon
138718.100.0.0/15 Amazon
138818.102.0.0/16 Amazon
138918.116.0.0/14 Amazon
139018.130.0.0/16 Amazon
139118.132.0.0/14 Amazon
139218.136.0.0/16 Amazon
139318.138.0.0/15 Amazon
139418.140.0.0/15 Amazon
139518.142.0.0/15 Amazon
139618.144.0.0/15 Amazon
139718.148.0.0/14 Amazon
139818.153.0.0/16 Amazon
139918.156.0.0/14 Amazon
140018.162.0.0/16 Amazon
140118.163.0.0/16 Amazon
140218.166.0.0/15 Amazon
140318.168.0.0/14 Amazon
140418.175.0.0/16 Amazon
140518.176.0.0/15 Amazon
140618.178.0.0/16 Amazon
140718.179.0.0/16 Amazon
140818.180.0.0/15 Amazon
140918.182.0.0/16 Amazon
141018.183.0.0/16 Amazon
141118.184.0.0/15 Amazon
141218.186.0.0/15 Amazon
141318.188.0.0/16 Amazon
141418.189.0.0/16 Amazon
141518.190.0.0/16 Amazon
141618.191.0.0/16 Amazon
141718.192.0.0/15 Amazon
141818.194.0.0/15 Amazon
141918.196.0.0/15 Amazon
142018.198.0.0/15 Amazon
142118.200.0.0/16 Amazon
142218.201.0.0/16 Amazon
142318.202.0.0/15 Amazon
142418.204.0.0/14 Amazon
142518.208.0.0/13 Amazon
142618.216.0.0/14 Amazon
142718.220.0.0/14 Amazon
142818.224.0.0/14 Amazon
142918.228.0.0/16 Amazon
143018.229.0.0/16 Amazon
143118.230.0.0/16 Amazon
143218.231.0.0/16 Amazon
143318.232.0.0/14 Amazon
143418.236.0.0/15 Amazon
143518.246.0.0/16 Amazon
143618.252.0.0/16 Amazon
143718.253.0.0/16 Amazon
143818.254.0.0/16 Amazon
143923.20.0.0/14 Amazon
144027.0.0.0/22 Amazon
144134.192.0.0/12 Amazon
144234.208.0.0/12 Amazon
144334.224.0.0/12 Amazon
144434.240.0.0/13 Amazon
144534.248.0.0/13 Amazon
144635.71.64.0/22 Amazon
144735.71.96.0/24 Amazon
144835.71.97.0/24 Amazon
144935.71.128.0/17 Amazon
145035.72.0.0/13 Amazon
145135.80.0.0/12 Amazon
145235.96.0.0/12 Amazon
145335.152.0.0/16 Amazon
145435.153.0.0/16 Amazon
145535.154.0.0/16 Amazon
145635.155.0.0/16 Amazon
145735.156.0.0/14 Amazon
145835.160.0.0/13 Amazon
145935.168.0.0/13 Amazon
146035.176.0.0/15 Amazon
146135.178.0.0/15 Amazon
146235.180.0.0/16 Amazon
146335.181.0.0/16 Amazon
146435.182.0.0/15 Amazon
146536.103.232.0/25 Amazon
146636.103.232.128/26 Amazon
146743.192.0.0/15 Amazon
146843.194.0.0/16 Amazon
146943.195.0.0/16 Amazon
147043.196.0.0/15 Amazon
147143.198.0.0/15 Amazon
147243.200.0.0/14 Amazon
147343.204.0.0/15 Amazon
147443.206.0.0/15 Amazon
147543.224.76.0/30 Amazon
147643.224.76.4/30 Amazon
147743.224.76.8/30 Amazon
147843.224.76.12/30 Amazon
147943.224.76.16/30 Amazon
148043.224.76.20/30 Amazon
148143.224.76.24/30 Amazon
148243.224.76.28/30 Amazon
148343.224.76.32/30 Amazon
148443.224.76.36/30 Amazon
148543.224.76.40/30 Amazon
148643.224.76.44/30 Amazon
148743.224.76.48/30 Amazon
148843.224.76.52/30 Amazon
148943.224.76.56/30 Amazon
149043.224.76.60/30 Amazon
149143.224.76.64/30 Amazon
149243.224.76.68/30 Amazon
149343.224.76.72/30 Amazon
149443.224.76.76/30 Amazon
149543.224.76.80/30 Amazon
149643.224.76.84/30 Amazon
149743.224.76.88/30 Amazon
149843.224.76.92/30 Amazon
149943.224.76.96/30 Amazon
150043.224.76.100/30 Amazon
150143.224.76.104/30 Amazon
150243.224.76.108/30 Amazon
150343.224.76.112/30 Amazon
150443.224.76.116/30 Amazon
150543.224.76.120/30 Amazon
150643.224.76.124/30 Amazon
150743.224.76.128/30 Amazon
150843.224.76.132/30 Amazon
150943.224.76.136/30 Amazon
151043.224.76.140/30 Amazon
151143.224.76.144/30 Amazon
151243.224.76.148/30 Amazon
151343.224.76.152/30 Amazon
151443.224.76.156/30 Amazon
151543.224.76.160/30 Amazon
151643.224.76.164/30 Amazon
151743.224.76.168/30 Amazon
151843.224.76.172/30 Amazon
151943.224.76.176/30 Amazon
152043.224.76.180/30 Amazon
152143.224.76.184/30 Amazon
152243.224.76.188/30 Amazon
152343.224.76.192/30 Amazon
152443.224.76.196/30 Amazon
152543.224.76.200/30 Amazon
152643.224.76.204/30 Amazon
152743.224.76.208/30 Amazon
152843.224.76.212/30 Amazon
152943.224.76.216/30 Amazon
153043.224.76.220/30 Amazon
153143.224.76.224/30 Amazon
153243.224.76.228/30 Amazon
153343.224.76.232/30 Amazon
153443.224.76.236/30 Amazon
153543.224.76.240/30 Amazon
153643.224.76.244/30 Amazon
153743.224.76.248/30 Amazon
153843.224.77.0/29 Amazon
153943.224.77.8/29 Amazon
154043.224.77.24/30 Amazon
154143.224.77.28/30 Amazon
154243.224.77.32/30 Amazon
154343.224.77.36/30 Amazon
154443.224.77.40/30 Amazon
154543.224.77.44/30 Amazon
154643.224.77.76/30 Amazon
154743.224.77.80/30 Amazon
154843.224.77.84/30 Amazon
154943.224.77.88/30 Amazon
155043.224.77.92/30 Amazon
155143.224.77.96/30 Amazon
155243.224.77.100/30 Amazon
155343.224.77.104/30 Amazon
155443.224.77.108/30 Amazon
155543.224.77.112/30 Amazon
155643.224.77.116/30 Amazon
155743.224.77.120/30 Amazon
155843.224.77.124/30 Amazon
155943.224.77.128/30 Amazon
156043.224.77.132/30 Amazon
156143.224.77.136/30 Amazon
156243.224.77.140/30 Amazon
156343.224.77.144/30 Amazon
156443.224.77.148/30 Amazon
156543.224.77.152/30 Amazon
156643.224.77.156/30 Amazon
156743.224.77.168/30 Amazon
156843.224.77.172/30 Amazon
156943.224.77.176/30 Amazon
157043.224.77.180/30 Amazon
157143.224.77.184/30 Amazon
157243.224.77.188/30 Amazon
157343.224.77.192/30 Amazon
157443.224.77.208/30 Amazon
157543.224.77.212/30 Amazon
157643.224.79.26/31 Amazon
157743.224.79.28/31 Amazon
157843.224.79.30/31 Amazon
157943.224.79.32/31 Amazon
158043.224.79.34/31 Amazon
158143.224.79.36/31 Amazon
158243.224.79.38/31 Amazon
158343.224.79.40/31 Amazon
158443.224.79.42/31 Amazon
158543.224.79.44/31 Amazon
158643.224.79.46/31 Amazon
158743.224.79.48/31 Amazon
158843.224.79.50/31 Amazon
158943.224.79.52/31 Amazon
159043.224.79.54/31 Amazon
159143.224.79.56/31 Amazon
159243.224.79.58/31 Amazon
159343.224.79.60/31 Amazon
159443.224.79.62/31 Amazon
159543.224.79.64/31 Amazon
159643.224.79.66/31 Amazon
159743.224.79.68/31 Amazon
159843.224.79.70/31 Amazon
159943.224.79.72/31 Amazon
160043.224.79.74/31 Amazon
160143.224.79.76/31 Amazon
160243.224.79.78/31 Amazon
160343.224.79.80/31 Amazon
160443.224.79.82/31 Amazon
160543.224.79.84/31 Amazon
160643.224.79.90/31 Amazon
160743.224.79.92/31 Amazon
160843.224.79.94/31 Amazon
160943.224.79.96/31 Amazon
161043.224.79.98/31 Amazon
161143.224.79.100/31 Amazon
161243.224.79.102/31 Amazon
161343.224.79.104/31 Amazon
161443.224.79.106/31 Amazon
161543.224.79.108/31 Amazon
161643.224.79.110/31 Amazon
161743.224.79.112/31 Amazon
161843.224.79.114/31 Amazon
161943.224.79.116/31 Amazon
162043.224.79.118/31 Amazon
162143.224.79.120/31 Amazon
162243.224.79.122/31 Amazon
162343.224.79.124/31 Amazon
162443.224.79.126/31 Amazon
162543.224.79.128/31 Amazon
162643.224.79.130/31 Amazon
162743.224.79.136/31 Amazon
162843.224.79.138/31 Amazon
162943.224.79.140/31 Amazon
163043.224.79.142/31 Amazon
163143.224.79.144/31 Amazon
163243.224.79.154/31 Amazon
163343.224.79.156/31 Amazon
163443.224.79.158/31 Amazon
163543.224.79.160/31 Amazon
163643.224.79.162/31 Amazon
163743.224.79.164/31 Amazon
163843.224.79.166/31 Amazon
163943.224.79.168/31 Amazon
164043.224.79.174/31 Amazon
164143.224.79.176/31 Amazon
164243.224.79.178/31 Amazon
164343.224.79.180/31 Amazon
164443.224.79.182/31 Amazon
164543.224.79.184/31 Amazon
164643.224.79.186/31 Amazon
164743.224.79.188/31 Amazon
164843.224.79.190/31 Amazon
164943.224.79.192/31 Amazon
165043.224.79.194/31 Amazon
165143.224.79.196/31 Amazon
165243.224.79.198/31 Amazon
165343.224.79.200/31 Amazon
165443.224.79.202/31 Amazon
165543.224.79.204/31 Amazon
165643.224.79.206/31 Amazon
165743.224.79.208/31 Amazon
165843.224.79.210/31 Amazon
165943.224.79.212/31 Amazon
166043.224.79.214/31 Amazon
166143.224.79.216/31 Amazon
166243.224.79.218/31 Amazon
166343.224.79.220/31 Amazon
166443.224.79.222/31 Amazon
166543.224.79.224/31 Amazon
166643.224.79.226/31 Amazon
166743.224.79.228/31 Amazon
166843.224.79.230/31 Amazon
166943.224.79.232/31 Amazon
167043.224.79.234/31 Amazon
167143.224.79.236/31 Amazon
167243.224.79.238/31 Amazon
167343.224.79.240/31 Amazon
167443.224.79.242/31 Amazon
167543.224.79.244/31 Amazon
167643.224.79.246/31 Amazon
167743.224.79.248/31 Amazon
167843.224.79.250/31 Amazon
167943.224.79.252/31 Amazon
168043.224.79.254/31 Amazon
168143.249.45.0/24 Amazon
168243.249.46.0/24 Amazon
168343.249.47.0/24 Amazon
168443.250.192.0/24 Amazon
168543.250.193.0/24 Amazon
168644.192.0.0/11 Amazon
168744.224.0.0/11 Amazon
168846.51.128.0/18 Amazon
168946.51.192.0/20 Amazon
169046.51.208.0/22 Amazon
169146.51.216.0/21 Amazon
169246.51.224.0/19 Amazon
169346.137.0.0/17 Amazon
169446.137.128.0/18 Amazon
169546.137.192.0/19 Amazon
169646.137.224.0/19 Amazon
169750.16.0.0/15 Amazon
169850.18.0.0/16 Amazon
169950.19.0.0/16 Amazon
170050.112.0.0/16 Amazon
170151.20.0.0/14 Amazon
170252.0.0.0/15 Amazon
170352.2.0.0/15 Amazon
170452.4.0.0/14 Amazon
170552.8.0.0/16 Amazon
170652.9.0.0/16 Amazon
170752.10.0.0/15 Amazon
170852.12.0.0/15 Amazon
170952.14.0.0/16 Amazon
171052.15.0.0/16 Amazon
171152.16.0.0/15 Amazon
171252.18.0.0/15 Amazon
171352.20.0.0/14 Amazon
171452.24.0.0/14 Amazon
171552.28.0.0/16 Amazon
171652.29.0.0/16 Amazon
171752.30.0.0/15 Amazon
171852.32.0.0/14 Amazon
171952.36.0.0/14 Amazon
172052.40.0.0/14 Amazon
172152.44.0.0/15 Amazon
172252.46.0.0/18 Amazon
172352.46.64.0/20 Amazon
172452.46.80.0/21 Amazon
172552.46.88.0/22 Amazon
172652.46.92.0/22 Amazon
172752.46.96.0/19 Amazon
172852.46.128.0/19 Amazon
172952.46.164.0/23 Amazon
173052.46.166.0/23 Amazon
173152.46.168.0/23 Amazon
173252.46.170.0/23 Amazon
173352.46.172.0/22 Amazon
173452.46.176.0/22 Amazon
173552.46.180.0/22 Amazon
173652.46.184.0/22 Amazon
173752.46.188.24/30 Amazon
173852.46.188.28/30 Amazon
173952.46.188.36/30 Amazon
174052.46.188.40/30 Amazon
174152.46.188.44/30 Amazon
174252.46.188.48/30 Amazon
174352.46.188.52/30 Amazon
174452.46.188.56/30 Amazon
174552.46.188.60/30 Amazon
174652.46.188.64/30 Amazon
174752.46.188.68/30 Amazon
174852.46.188.72/30 Amazon
174952.46.188.76/30 Amazon
175052.46.188.80/30 Amazon
175152.46.188.84/30 Amazon
175252.46.188.88/30 Amazon
175352.46.188.92/30 Amazon
175452.46.188.96/30 Amazon
175552.46.188.108/30 Amazon
175652.46.188.120/30 Amazon
175752.46.188.132/30 Amazon
175852.46.188.136/30 Amazon
175952.46.188.140/30 Amazon
176052.46.188.144/30 Amazon
176152.46.188.148/30 Amazon
176252.46.188.152/30 Amazon
176352.46.188.156/30 Amazon
176452.46.188.160/30 Amazon
176552.46.188.164/30 Amazon
176652.46.188.168/30 Amazon
176752.46.188.172/30 Amazon
176852.46.188.176/30 Amazon
176952.46.188.180/30 Amazon
177052.46.188.184/30 Amazon
177152.46.188.188/30 Amazon
177252.46.188.192/30 Amazon
177352.46.188.204/30 Amazon
177452.46.188.208/30 Amazon
177552.46.188.216/30 Amazon
177652.46.188.224/30 Amazon
177752.46.188.228/30 Amazon
177852.46.188.232/30 Amazon
177952.46.188.236/30 Amazon
178052.46.188.240/30 Amazon
178152.46.188.244/30 Amazon
178252.46.188.248/30 Amazon
178352.46.188.252/30 Amazon
178452.46.189.0/30 Amazon
178552.46.189.4/30 Amazon
178652.46.189.8/30 Amazon
178752.46.189.12/30 Amazon
178852.46.189.16/30 Amazon
178952.46.189.32/30 Amazon
179052.46.189.36/30 Amazon
179152.46.189.40/30 Amazon
179252.46.189.44/30 Amazon
179352.46.189.48/30 Amazon
179452.46.189.52/30 Amazon
179552.46.189.56/30 Amazon
179652.46.189.60/30 Amazon
179752.46.189.64/30 Amazon
179852.46.189.68/30 Amazon
179952.46.189.72/30 Amazon
180052.46.189.76/30 Amazon
180152.46.189.80/30 Amazon
180252.46.189.84/30 Amazon
180352.46.189.88/30 Amazon
180452.46.189.92/30 Amazon
180552.46.189.96/30 Amazon
180652.46.189.100/30 Amazon
180752.46.189.104/30 Amazon
180852.46.189.108/30 Amazon
180952.46.189.112/30 Amazon
181052.46.189.124/30 Amazon
181152.46.189.128/30 Amazon
181252.46.189.132/30 Amazon
181352.46.189.136/30 Amazon
181452.46.189.140/30 Amazon
181552.46.189.156/30 Amazon
181652.46.189.160/30 Amazon
181752.46.189.168/30 Amazon
181852.46.189.172/30 Amazon
181952.46.189.176/30 Amazon
182052.46.189.180/30 Amazon
182152.46.189.192/30 Amazon
182252.46.189.196/30 Amazon
182352.46.189.200/30 Amazon
182452.46.189.204/30 Amazon
182552.46.189.216/30 Amazon
182652.46.189.220/30 Amazon
182752.46.189.224/30 Amazon
182852.46.189.228/30 Amazon
182952.46.189.240/30 Amazon
183052.46.189.244/30 Amazon
183152.46.189.248/30 Amazon
183252.46.189.252/30 Amazon
183352.46.190.0/30 Amazon
183452.46.190.4/30 Amazon
183552.46.190.8/30 Amazon
183652.46.190.12/30 Amazon
183752.46.190.32/30 Amazon
183852.46.190.36/30 Amazon
183952.46.190.40/30 Amazon
184052.46.190.44/30 Amazon
184152.46.190.52/30 Amazon
184252.46.190.56/30 Amazon
184352.46.190.60/30 Amazon
184452.46.190.64/30 Amazon
184552.46.190.68/30 Amazon
184652.46.190.72/30 Amazon
184752.46.190.76/30 Amazon
184852.46.190.92/30 Amazon
184952.46.190.96/30 Amazon
185052.46.190.100/30 Amazon
185152.46.190.104/30 Amazon
185252.46.190.108/30 Amazon
185352.46.190.120/30 Amazon
185452.46.190.124/30 Amazon
185552.46.190.144/30 Amazon
185652.46.190.148/30 Amazon
185752.46.190.152/30 Amazon
185852.46.190.164/30 Amazon
185952.46.190.168/30 Amazon
186052.46.190.180/31 Amazon
186152.46.190.182/31 Amazon
186252.46.190.188/31 Amazon
186352.46.190.190/31 Amazon
186452.46.190.192/31 Amazon
186552.46.190.202/31 Amazon
186652.46.190.204/31 Amazon
186752.46.190.206/31 Amazon
186852.46.190.208/31 Amazon
186952.46.190.210/31 Amazon
187052.46.190.212/31 Amazon
187152.46.190.214/31 Amazon
187252.46.190.216/31 Amazon
187352.46.190.222/31 Amazon
187452.46.190.224/31 Amazon
187552.46.190.226/31 Amazon
187652.46.190.228/31 Amazon
187752.46.190.230/31 Amazon
187852.46.190.232/31 Amazon
187952.46.190.234/31 Amazon
188052.46.190.236/31 Amazon
188152.46.190.238/31 Amazon
188252.46.190.240/31 Amazon
188352.46.190.242/31 Amazon
188452.46.190.244/31 Amazon
188552.46.190.254/31 Amazon
188652.46.191.0/31 Amazon
188752.46.191.2/31 Amazon
188852.46.191.4/31 Amazon
188952.46.191.6/31 Amazon
189052.46.191.8/31 Amazon
189152.46.191.10/31 Amazon
189252.46.191.12/31 Amazon
189352.46.191.18/31 Amazon
189452.46.191.20/31 Amazon
189552.46.191.22/31 Amazon
189652.46.191.24/31 Amazon
189752.46.191.26/31 Amazon
189852.46.191.28/31 Amazon
189952.46.191.34/31 Amazon
190052.46.191.36/31 Amazon
190152.46.191.42/31 Amazon
190252.46.191.44/31 Amazon
190352.46.191.46/31 Amazon
190452.46.191.48/31 Amazon
190552.46.191.52/31 Amazon
190652.46.191.54/31 Amazon
190752.46.191.60/31 Amazon
190852.46.191.62/31 Amazon
190952.46.191.64/31 Amazon
191052.46.191.66/31 Amazon
191152.46.191.68/31 Amazon
191252.46.191.70/31 Amazon
191352.46.191.72/31 Amazon
191452.46.191.76/31 Amazon
191552.46.191.78/31 Amazon
191652.46.191.80/31 Amazon
191752.46.191.82/31 Amazon
191852.46.191.84/31 Amazon
191952.46.191.86/31 Amazon
192052.46.191.88/31 Amazon
192152.46.191.90/31 Amazon
192252.46.191.92/31 Amazon
192352.46.191.94/31 Amazon
192452.46.191.96/31 Amazon
192552.46.191.98/31 Amazon
192652.46.191.100/31 Amazon
192752.46.191.102/31 Amazon
192852.46.191.104/31 Amazon
192952.46.191.106/31 Amazon
193052.46.191.108/31 Amazon
193152.46.191.110/31 Amazon
193252.46.191.120/31 Amazon
193352.46.191.122/31 Amazon
193452.46.191.124/31 Amazon
193552.46.191.126/31 Amazon
193652.46.191.128/31 Amazon
193752.46.191.130/31 Amazon
193852.46.191.132/31 Amazon
193952.46.191.134/31 Amazon
194052.46.191.136/31 Amazon
194152.46.191.140/31 Amazon
194252.46.191.142/31 Amazon
194352.46.191.144/31 Amazon
194452.46.191.148/31 Amazon
194552.46.191.150/31 Amazon
194652.46.191.152/31 Amazon
194752.46.191.156/31 Amazon
194852.46.191.158/31 Amazon
194952.46.191.164/31 Amazon
195052.46.191.166/31 Amazon
195152.46.191.168/31 Amazon
195252.46.191.170/31 Amazon
195352.46.191.172/31 Amazon
195452.46.191.174/31 Amazon
195552.46.191.176/31 Amazon
195652.46.191.178/31 Amazon
195752.46.191.180/31 Amazon
195852.46.191.182/31 Amazon
195952.46.191.184/31 Amazon
196052.46.191.186/31 Amazon
196152.46.191.188/31 Amazon
196252.46.191.190/31 Amazon
196352.46.191.192/31 Amazon
196452.46.191.194/31 Amazon
196552.46.191.200/31 Amazon
196652.46.191.202/31 Amazon
196752.46.191.210/31 Amazon
196852.46.191.212/31 Amazon
196952.46.191.214/31 Amazon
197052.46.191.216/31 Amazon
197152.46.191.218/31 Amazon
197252.46.191.220/31 Amazon
197352.46.191.222/31 Amazon
197452.46.191.224/31 Amazon
197552.46.191.226/31 Amazon
197652.46.191.228/31 Amazon
197752.46.191.230/31 Amazon
197852.46.191.232/31 Amazon
197952.46.191.234/31 Amazon
198052.46.191.236/31 Amazon
198152.46.191.238/31 Amazon
198252.46.191.240/31 Amazon
198352.46.192.0/20 Amazon
198452.46.208.0/21 Amazon
198552.46.216.0/22 Amazon
198652.46.220.0/22 Amazon
198752.46.224.0/20 Amazon
198852.46.240.0/22 Amazon
198952.46.249.0/24 Amazon
199052.46.250.0/23 Amazon
199152.46.252.0/22 Amazon
199252.47.0.0/16 Amazon
199352.48.0.0/14 Amazon
199452.52.0.0/15 Amazon
199552.54.0.0/15 Amazon
199652.56.0.0/16 Amazon
199752.57.0.0/16 Amazon
199852.58.0.0/15 Amazon
199952.60.0.0/16 Amazon
200052.61.0.0/16 Amazon
200152.62.0.0/15 Amazon
200252.64.0.0/17 Amazon
200352.64.128.0/17 Amazon
200452.65.0.0/16 Amazon
200552.66.0.0/16 Amazon
200652.67.0.0/16 Amazon
200752.68.0.0/15 Amazon
200852.70.0.0/15 Amazon
200952.72.0.0/15 Amazon
201052.74.0.0/16 Amazon
201152.75.0.0/16 Amazon
201252.76.0.0/17 Amazon
201352.76.128.0/17 Amazon
201452.77.0.0/16 Amazon
201552.78.0.0/16 Amazon
201652.79.0.0/16 Amazon
201752.80.0.0/16 Amazon
201852.81.0.0/16 Amazon
201952.82.0.0/17 Amazon
202052.82.128.0/19 Amazon
202152.82.160.0/22 Amazon
202252.82.164.0/22 Amazon
202352.82.168.0/24 Amazon
202452.82.169.0/28 Amazon
202552.82.169.16/28 Amazon
202652.82.176.0/22 Amazon
202752.82.180.0/22 Amazon
202852.82.184.0/23 Amazon
202952.82.187.0/24 Amazon
203052.82.188.0/22 Amazon
203152.82.192.0/18 Amazon
203252.83.0.0/16 Amazon
203352.84.0.0/15 Amazon
203452.86.0.0/15 Amazon
203552.88.0.0/15 Amazon
203652.90.0.0/15 Amazon
203752.92.0.0/17 Amazon
203852.92.128.0/17 Amazon
203952.93.0.0/24 Amazon
204052.93.1.0/24 Amazon
204152.93.2.0/24 Amazon
204252.93.3.0/24 Amazon
204352.93.4.0/24 Amazon
204452.93.5.0/24 Amazon
204552.93.8.0/22 Amazon
204652.93.12.12/32 Amazon
204752.93.12.13/32 Amazon
204852.93.14.18/32 Amazon
204952.93.14.19/32 Amazon
205052.93.16.0/24 Amazon
205152.93.17.0/24 Amazon
205252.93.18.178/32 Amazon
205352.93.18.179/32 Amazon
205452.93.19.236/32 Amazon
205552.93.19.237/32 Amazon
205652.93.20.0/24 Amazon
205752.93.21.14/32 Amazon
205852.93.21.15/32 Amazon
205952.93.32.176/32 Amazon
206052.93.32.179/32 Amazon
206152.93.32.180/32 Amazon
206252.93.34.40/32 Amazon
206352.93.34.42/32 Amazon
206452.93.34.56/32 Amazon
206552.93.34.57/32 Amazon
206652.93.34.120/31 Amazon
206752.93.34.122/31 Amazon
206852.93.34.124/31 Amazon
206952.93.34.126/31 Amazon
207052.93.35.212/32 Amazon
207152.93.35.213/32 Amazon
207252.93.37.222/32 Amazon
207352.93.37.223/32 Amazon
207452.93.38.0/24 Amazon
207552.93.43.0/24 Amazon
207652.93.48.0/24 Amazon
207752.93.50.128/32 Amazon
207852.93.50.129/32 Amazon
207952.93.50.130/32 Amazon
208052.93.50.131/32 Amazon
208152.93.50.132/31 Amazon
208252.93.50.134/31 Amazon
208352.93.50.136/31 Amazon
208452.93.50.138/31 Amazon
208552.93.50.140/31 Amazon
208652.93.50.142/31 Amazon
208752.93.50.144/31 Amazon
208852.93.50.146/31 Amazon
208952.93.50.148/31 Amazon
209052.93.50.150/31 Amazon
209152.93.50.152/31 Amazon
209252.93.50.154/31 Amazon
209352.93.50.156/31 Amazon
209452.93.50.158/31 Amazon
209552.93.50.160/31 Amazon
209652.93.50.162/31 Amazon
209752.93.50.164/31 Amazon
209852.93.50.166/31 Amazon
209952.93.50.168/31 Amazon
210052.93.50.170/31 Amazon
210152.93.50.172/31 Amazon
210252.93.50.174/31 Amazon
210352.93.50.176/31 Amazon
210452.93.50.178/31 Amazon
210552.93.50.180/31 Amazon
210652.93.50.182/31 Amazon
210752.93.50.184/31 Amazon
210852.93.50.186/31 Amazon
210952.93.50.188/31 Amazon
211052.93.50.190/31 Amazon
211152.93.50.192/31 Amazon
211252.93.50.194/31 Amazon
211352.93.51.28/32 Amazon
211452.93.51.29/32 Amazon
211552.93.55.144/31 Amazon
211652.93.55.146/31 Amazon
211752.93.55.148/31 Amazon
211852.93.55.152/31 Amazon
211952.93.55.154/31 Amazon
212052.93.55.156/31 Amazon
212152.93.55.158/31 Amazon
212252.93.55.160/31 Amazon
212352.93.55.162/31 Amazon
212452.93.55.164/31 Amazon
212552.93.55.166/31 Amazon
212652.93.56.0/24 Amazon
212752.93.57.0/24 Amazon
212852.93.58.32/28 Amazon
212952.93.59.0/24 Amazon
213052.93.60.0/24 Amazon
213152.93.62.0/24 Amazon
213252.93.63.0/24 Amazon
213352.93.64.0/24 Amazon
213452.93.66.0/24 Amazon
213552.93.67.0/24 Amazon
213652.93.69.0/24 Amazon
213752.93.71.37/32 Amazon
213852.93.73.0/26 Amazon
213952.93.75.0/24 Amazon
214052.93.76.0/24 Amazon
214152.93.78.0/24 Amazon
214252.93.80.0/24 Amazon
214352.93.81.0/24 Amazon
214452.93.87.96/27 Amazon
214552.93.91.96/32 Amazon
214652.93.91.97/32 Amazon
214752.93.91.98/32 Amazon
214852.93.91.99/32 Amazon
214952.93.91.100/32 Amazon
215052.93.91.101/32 Amazon
215152.93.91.102/32 Amazon
215252.93.91.103/32 Amazon
215352.93.91.104/32 Amazon
215452.93.91.105/32 Amazon
215552.93.91.106/32 Amazon
215652.93.91.107/32 Amazon
215752.93.91.108/32 Amazon
215852.93.91.109/32 Amazon
215952.93.91.110/32 Amazon
216052.93.91.111/32 Amazon
216152.93.91.112/32 Amazon
216252.93.91.113/32 Amazon
216352.93.91.114/32 Amazon
216452.93.91.115/32 Amazon
216552.93.92.64/31 Amazon
216652.93.92.66/31 Amazon
216752.93.92.68/31 Amazon
216852.93.92.70/31 Amazon
216952.93.92.72/31 Amazon
217052.93.92.74/31 Amazon
217152.93.96.0/24 Amazon
217252.93.97.0/24 Amazon
217352.93.98.0/24 Amazon
217452.93.99.0/24 Amazon
217552.93.112.0/24 Amazon
217652.93.120.176/32 Amazon
217752.93.120.177/32 Amazon
217852.93.120.178/32 Amazon
217952.93.120.179/32 Amazon
218052.93.121.187/32 Amazon
218152.93.121.188/32 Amazon
218252.93.121.189/32 Amazon
218352.93.121.190/32 Amazon
218452.93.121.195/32 Amazon
218552.93.121.196/32 Amazon
218652.93.121.197/32 Amazon
218752.93.121.198/32 Amazon
218852.93.122.131/32 Amazon
218952.93.122.202/32 Amazon
219052.93.122.203/32 Amazon
219152.93.122.218/32 Amazon
219252.93.122.255/32 Amazon
219352.93.123.6/32 Amazon
219452.93.123.11/32 Amazon
219552.93.123.98/32 Amazon
219652.93.123.99/32 Amazon
219752.93.123.136/32 Amazon
219852.93.123.255/32 Amazon
219952.93.124.14/32 Amazon
220052.93.124.15/32 Amazon
220152.93.124.96/32 Amazon
220252.93.124.97/32 Amazon
220352.93.124.210/32 Amazon
220452.93.124.211/32 Amazon
220552.93.124.212/32 Amazon
220652.93.124.213/32 Amazon
220752.93.125.42/32 Amazon
220852.93.125.43/32 Amazon
220952.93.126.76/32 Amazon
221052.93.126.122/32 Amazon
221152.93.126.123/32 Amazon
221252.93.126.132/32 Amazon
221352.93.126.133/32 Amazon
221452.93.126.134/32 Amazon
221552.93.126.135/32 Amazon
221652.93.126.136/32 Amazon
221752.93.126.137/32 Amazon
221852.93.126.138/32 Amazon
221952.93.126.139/32 Amazon
222052.93.126.144/32 Amazon
222152.93.126.145/32 Amazon
222252.93.126.146/32 Amazon
222352.93.126.147/32 Amazon
222452.93.126.198/32 Amazon
222552.93.126.199/32 Amazon
222652.93.126.204/32 Amazon
222752.93.126.205/32 Amazon
222852.93.126.206/32 Amazon
222952.93.126.207/32 Amazon
223052.93.126.212/32 Amazon
223152.93.126.213/32 Amazon
223252.93.126.214/32 Amazon
223352.93.126.215/32 Amazon
223452.93.126.234/32 Amazon
223552.93.126.235/32 Amazon
223652.93.126.244/32 Amazon
223752.93.126.245/32 Amazon
223852.93.126.250/32 Amazon
223952.93.126.251/32 Amazon
224052.93.127.17/32 Amazon
224152.93.127.18/32 Amazon
224252.93.127.19/32 Amazon
224352.93.127.24/32 Amazon
224452.93.127.25/32 Amazon
224552.93.127.26/32 Amazon
224652.93.127.27/32 Amazon
224752.93.127.68/32 Amazon
224852.93.127.69/32 Amazon
224952.93.127.70/32 Amazon
225052.93.127.71/32 Amazon
225152.93.127.92/32 Amazon
225252.93.127.93/32 Amazon
225352.93.127.94/32 Amazon
225452.93.127.95/32 Amazon
225552.93.127.96/32 Amazon
225652.93.127.97/32 Amazon
225752.93.127.98/32 Amazon
225852.93.127.99/32 Amazon
225952.93.127.100/32 Amazon
226052.93.127.101/32 Amazon
226152.93.127.102/32 Amazon
226252.93.127.103/32 Amazon
226352.93.127.104/32 Amazon
226452.93.127.105/32 Amazon
226552.93.127.106/32 Amazon
226652.93.127.107/32 Amazon
226752.93.127.108/32 Amazon
226852.93.127.109/32 Amazon
226952.93.127.110/32 Amazon
227052.93.127.111/32 Amazon
227152.93.127.112/32 Amazon
227252.93.127.113/32 Amazon
227352.93.127.114/32 Amazon
227452.93.127.115/32 Amazon
227552.93.127.116/32 Amazon
227652.93.127.117/32 Amazon
227752.93.127.118/32 Amazon
227852.93.127.119/32 Amazon
227952.93.127.120/32 Amazon
228052.93.127.121/32 Amazon
228152.93.127.122/32 Amazon
228252.93.127.123/32 Amazon
228352.93.127.124/32 Amazon
228452.93.127.125/32 Amazon
228552.93.127.126/32 Amazon
228652.93.127.127/32 Amazon
228752.93.127.128/32 Amazon
228852.93.127.129/32 Amazon
228952.93.127.130/32 Amazon
229052.93.127.131/32 Amazon
229152.93.127.132/32 Amazon
229252.93.127.133/32 Amazon
229352.93.127.138/32 Amazon
229452.93.127.139/32 Amazon
229552.93.127.146/32 Amazon
229652.93.127.147/32 Amazon
229752.93.127.148/32 Amazon
229852.93.127.149/32 Amazon
229952.93.127.152/32 Amazon
230052.93.127.153/32 Amazon
230152.93.127.154/32 Amazon
230252.93.127.155/32 Amazon
230352.93.127.156/32 Amazon
230452.93.127.157/32 Amazon
230552.93.127.158/32 Amazon
230652.93.127.159/32 Amazon
230752.93.127.160/32 Amazon
230852.93.127.161/32 Amazon
230952.93.127.162/32 Amazon
231052.93.127.163/32 Amazon
231152.93.127.164/32 Amazon
231252.93.127.165/32 Amazon
231352.93.127.166/32 Amazon
231452.93.127.167/32 Amazon
231552.93.127.168/32 Amazon
231652.93.127.169/32 Amazon
231752.93.127.172/32 Amazon
231852.93.127.173/32 Amazon
231952.93.127.174/32 Amazon
232052.93.127.175/32 Amazon
232152.93.127.176/32 Amazon
232252.93.127.177/32 Amazon
232352.93.127.178/32 Amazon
232452.93.127.179/32 Amazon
232552.93.127.180/32 Amazon
232652.93.127.181/32 Amazon
232752.93.127.182/32 Amazon
232852.93.127.183/32 Amazon
232952.93.127.184/32 Amazon
233052.93.127.185/32 Amazon
233152.93.127.194/32 Amazon
233252.93.127.195/32 Amazon
233352.93.127.196/32 Amazon
233452.93.127.197/32 Amazon
233552.93.127.198/32 Amazon
233652.93.127.199/32 Amazon
233752.93.127.200/32 Amazon
233852.93.127.201/32 Amazon
233952.93.127.202/32 Amazon
234052.93.127.203/32 Amazon
234152.93.127.204/32 Amazon
234252.93.127.205/32 Amazon
234352.93.127.206/32 Amazon
234452.93.127.207/32 Amazon
234552.93.127.216/32 Amazon
234652.93.127.217/32 Amazon
234752.93.127.218/32 Amazon
234852.93.127.219/32 Amazon
234952.93.127.220/32 Amazon
235052.93.127.221/32 Amazon
235152.93.127.232/32 Amazon
235252.93.127.237/32 Amazon
235352.93.127.238/32 Amazon
235452.93.127.239/32 Amazon
235552.93.127.244/32 Amazon
235652.93.127.245/32 Amazon
235752.93.127.246/32 Amazon
235852.93.127.247/32 Amazon
235952.93.127.248/32 Amazon
236052.93.127.249/32 Amazon
236152.93.127.250/32 Amazon
236252.93.127.251/32 Amazon
236352.93.127.252/32 Amazon
236452.93.127.253/32 Amazon
236552.93.127.254/32 Amazon
236652.93.127.255/32 Amazon
236752.93.129.95/32 Amazon
236852.93.131.217/32 Amazon
236952.93.133.127/32 Amazon
237052.93.133.129/32 Amazon
237152.93.133.131/32 Amazon
237252.93.133.133/32 Amazon
237352.93.133.153/32 Amazon
237452.93.133.155/32 Amazon
237552.93.133.175/32 Amazon
237652.93.133.177/32 Amazon
237752.93.133.179/32 Amazon
237852.93.133.181/32 Amazon
237952.93.134.181/32 Amazon
238052.93.135.195/32 Amazon
238152.93.137.0/24 Amazon
238252.93.138.252/32 Amazon
238352.93.138.253/32 Amazon
238452.93.139.252/32 Amazon
238552.93.139.253/32 Amazon
238652.93.141.212/32 Amazon
238752.93.141.213/32 Amazon
238852.93.141.214/31 Amazon
238952.93.141.216/31 Amazon
239052.93.141.218/31 Amazon
239152.93.141.220/31 Amazon
239252.93.141.222/31 Amazon
239352.93.141.224/31 Amazon
239452.93.141.226/31 Amazon
239552.93.141.228/31 Amazon
239652.93.141.230/31 Amazon
239752.93.141.232/31 Amazon
239852.93.141.234/31 Amazon
239952.93.141.236/31 Amazon
240052.93.141.238/31 Amazon
240152.93.141.240/31 Amazon
240252.93.141.242/31 Amazon
240352.93.141.244/31 Amazon
240452.93.146.5/32 Amazon
240552.93.149.0/24 Amazon
240652.93.150.0/24 Amazon
240752.93.151.0/24 Amazon
240852.93.153.80/32 Amazon
240952.93.153.148/32 Amazon
241052.93.153.149/32 Amazon
241152.93.153.168/32 Amazon
241252.93.153.169/32 Amazon
241352.93.153.170/32 Amazon
241452.93.153.171/32 Amazon
241552.93.153.172/32 Amazon
241652.93.153.173/32 Amazon
241752.93.153.174/32 Amazon
241852.93.153.175/32 Amazon
241952.93.153.176/32 Amazon
242052.93.153.177/32 Amazon
242152.93.153.178/32 Amazon
242252.93.153.179/32 Amazon
242352.93.156.0/22 Amazon
242452.93.178.128/32 Amazon
242552.93.178.129/32 Amazon
242652.93.178.130/32 Amazon
242752.93.178.131/32 Amazon
242852.93.178.132/32 Amazon
242952.93.178.133/32 Amazon
243052.93.178.134/32 Amazon
243152.93.178.135/32 Amazon
243252.93.178.136/32 Amazon
243352.93.178.137/32 Amazon
243452.93.178.138/32 Amazon
243552.93.178.139/32 Amazon
243652.93.178.140/32 Amazon
243752.93.178.141/32 Amazon
243852.93.178.142/32 Amazon
243952.93.178.143/32 Amazon
244052.93.178.144/32 Amazon
244152.93.178.145/32 Amazon
244252.93.178.146/32 Amazon
244352.93.178.147/32 Amazon
244452.93.178.148/32 Amazon
244552.93.178.149/32 Amazon
244652.93.178.150/32 Amazon
244752.93.178.151/32 Amazon
244852.93.178.152/32 Amazon
244952.93.178.153/32 Amazon
245052.93.178.154/32 Amazon
245152.93.178.155/32 Amazon
245252.93.178.156/32 Amazon
245352.93.178.157/32 Amazon
245452.93.178.158/32 Amazon
245552.93.178.159/32 Amazon
245652.93.178.160/32 Amazon
245752.93.178.161/32 Amazon
245852.93.178.162/32 Amazon
245952.93.178.163/32 Amazon
246052.93.178.164/32 Amazon
246152.93.178.165/32 Amazon
246252.93.178.166/32 Amazon
246352.93.178.167/32 Amazon
246452.93.178.168/32 Amazon
246552.93.178.169/32 Amazon
246652.93.178.170/32 Amazon
246752.93.178.171/32 Amazon
246852.93.178.172/32 Amazon
246952.93.178.173/32 Amazon
247052.93.178.174/32 Amazon
247152.93.178.175/32 Amazon
247252.93.178.176/32 Amazon
247352.93.178.177/32 Amazon
247452.93.178.178/32 Amazon
247552.93.178.179/32 Amazon
247652.93.178.180/32 Amazon
247752.93.178.181/32 Amazon
247852.93.178.182/32 Amazon
247952.93.178.183/32 Amazon
248052.93.178.184/32 Amazon
248152.93.178.185/32 Amazon
248252.93.178.186/32 Amazon
248352.93.178.187/32 Amazon
248452.93.178.188/32 Amazon
248552.93.178.189/32 Amazon
248652.93.178.190/32 Amazon
248752.93.178.191/32 Amazon
248852.93.178.192/32 Amazon
248952.93.178.193/32 Amazon
249052.93.178.194/32 Amazon
249152.93.178.195/32 Amazon
249252.93.178.196/32 Amazon
249352.93.178.197/32 Amazon
249452.93.178.198/32 Amazon
249552.93.178.199/32 Amazon
249652.93.178.200/32 Amazon
249752.93.178.201/32 Amazon
249852.93.178.202/32 Amazon
249952.93.178.203/32 Amazon
250052.93.178.204/32 Amazon
250152.93.178.205/32 Amazon
250252.93.178.206/32 Amazon
250352.93.178.207/32 Amazon
250452.93.178.208/32 Amazon
250552.93.178.209/32 Amazon
250652.93.178.210/32 Amazon
250752.93.178.211/32 Amazon
250852.93.178.212/32 Amazon
250952.93.178.213/32 Amazon
251052.93.178.214/32 Amazon
251152.93.178.215/32 Amazon
251252.93.178.216/32 Amazon
251352.93.178.217/32 Amazon
251452.93.178.218/32 Amazon
251552.93.178.219/32 Amazon
251652.93.178.220/32 Amazon
251752.93.178.221/32 Amazon
251852.93.178.222/32 Amazon
251952.93.178.223/32 Amazon
252052.93.178.224/32 Amazon
252152.93.178.225/32 Amazon
252252.93.178.226/32 Amazon
252352.93.178.227/32 Amazon
252452.93.178.228/32 Amazon
252552.93.178.229/32 Amazon
252652.93.178.230/32 Amazon
252752.93.178.231/32 Amazon
252852.93.178.232/32 Amazon
252952.93.178.233/32 Amazon
253052.93.178.234/32 Amazon
253152.93.178.235/32 Amazon
253252.93.193.192/32 Amazon
253352.93.193.193/32 Amazon
253452.93.193.194/32 Amazon
253552.93.193.195/32 Amazon
253652.93.193.196/32 Amazon
253752.93.193.197/32 Amazon
253852.93.193.198/32 Amazon
253952.93.193.199/32 Amazon
254052.93.193.200/32 Amazon
254152.93.193.201/32 Amazon
254252.93.193.202/32 Amazon
254352.93.193.203/32 Amazon
254452.93.198.0/25 Amazon
254552.93.229.148/32 Amazon
254652.93.229.149/32 Amazon
254752.93.236.0/24 Amazon
254852.93.237.0/24 Amazon
254952.93.240.146/31 Amazon
255052.93.240.148/31 Amazon
255152.93.240.150/31 Amazon
255252.93.240.152/31 Amazon
255352.93.240.154/31 Amazon
255452.93.240.156/31 Amazon
255552.93.240.158/31 Amazon
255652.93.240.160/31 Amazon
255752.93.240.162/31 Amazon
255852.93.240.164/31 Amazon
255952.93.240.166/31 Amazon
256052.93.240.168/31 Amazon
256152.93.240.170/31 Amazon
256252.93.240.172/31 Amazon
256352.93.240.174/31 Amazon
256452.93.240.176/31 Amazon
256552.93.240.178/31 Amazon
256652.93.240.180/31 Amazon
256752.93.240.182/31 Amazon
256852.93.240.184/31 Amazon
256952.93.240.186/31 Amazon
257052.93.240.188/31 Amazon
257152.93.240.190/31 Amazon
257252.93.240.192/31 Amazon
257352.93.240.194/31 Amazon
257452.93.240.196/31 Amazon
257552.93.240.198/31 Amazon
257652.93.240.200/31 Amazon
257752.93.240.202/31 Amazon
257852.93.240.204/31 Amazon
257952.93.245.0/24 Amazon
258052.93.247.0/25 Amazon
258152.93.248.0/24 Amazon
258252.93.249.0/24 Amazon
258352.93.250.0/23 Amazon
258452.93.254.0/24 Amazon
258552.94.0.0/22 Amazon
258652.94.4.0/24 Amazon
258752.94.5.0/24 Amazon
258852.94.6.0/24 Amazon
258952.94.7.0/24 Amazon
259052.94.8.0/24 Amazon
259152.94.9.0/24 Amazon
259252.94.10.0/24 Amazon
259352.94.11.0/24 Amazon
259452.94.12.0/24 Amazon
259552.94.13.0/24 Amazon
259652.94.14.0/24 Amazon
259752.94.15.0/24 Amazon
259852.94.16.0/24 Amazon
259952.94.17.0/24 Amazon
260052.94.18.0/24 Amazon
260152.94.19.0/24 Amazon
260252.94.20.0/24 Amazon
260352.94.22.0/24 Amazon
260452.94.23.0/24 Amazon
260552.94.24.0/23 Amazon
260652.94.26.0/23 Amazon
260752.94.28.0/23 Amazon
260852.94.30.0/24 Amazon
260952.94.32.0/20 Amazon
261052.94.48.0/20 Amazon
261152.94.64.0/22 Amazon
261252.94.68.0/24 Amazon
261352.94.69.0/24 Amazon
261452.94.72.0/22 Amazon
261552.94.76.0/22 Amazon
261652.94.80.0/20 Amazon
261752.94.96.0/20 Amazon
261852.94.112.0/22 Amazon
261952.94.116.0/22 Amazon
262052.94.120.0/22 Amazon
262152.94.124.0/22 Amazon
262252.94.128.0/22 Amazon
262352.94.132.0/22 Amazon
262452.94.136.0/21 Amazon
262552.94.148.0/22 Amazon
262652.94.152.3/32 Amazon
262752.94.152.9/32 Amazon
262852.94.152.11/32 Amazon
262952.94.152.12/32 Amazon
263052.94.152.44/32 Amazon
263152.94.152.60/32 Amazon
263252.94.152.61/32 Amazon
263352.94.152.62/32 Amazon
263452.94.152.63/32 Amazon
263552.94.152.64/32 Amazon
263652.94.152.65/32 Amazon
263752.94.152.66/32 Amazon
263852.94.152.67/32 Amazon
263952.94.152.68/32 Amazon
264052.94.152.69/32 Amazon
264152.94.160.0/20 Amazon
264252.94.176.0/20 Amazon
264352.94.192.0/22 Amazon
264452.94.196.0/24 Amazon
264552.94.197.0/24 Amazon
264652.94.198.0/28 Amazon
264752.94.198.16/28 Amazon
264852.94.198.32/28 Amazon
264952.94.198.48/28 Amazon
265052.94.198.64/28 Amazon
265152.94.198.80/28 Amazon
265252.94.198.96/28 Amazon
265352.94.198.112/28 Amazon
265452.94.198.128/28 Amazon
265552.94.198.144/28 Amazon
265652.94.199.0/24 Amazon
265752.94.200.0/24 Amazon
265852.94.201.0/26 Amazon
265952.94.204.0/23 Amazon
266052.94.206.0/23 Amazon
266152.94.208.0/21 Amazon
266252.94.216.0/21 Amazon
266352.94.224.0/20 Amazon
266452.94.240.0/22 Amazon
266552.94.244.0/22 Amazon
266652.94.248.0/28 Amazon
266752.94.248.16/28 Amazon
266852.94.248.32/28 Amazon
266952.94.248.48/28 Amazon
267052.94.248.64/28 Amazon
267152.94.248.80/28 Amazon
267252.94.248.96/28 Amazon
267352.94.248.112/28 Amazon
267452.94.248.128/28 Amazon
267552.94.248.144/28 Amazon
267652.94.248.160/28 Amazon
267752.94.248.176/28 Amazon
267852.94.248.192/28 Amazon
267952.94.248.208/28 Amazon
268052.94.248.224/28 Amazon
268152.94.249.32/28 Amazon
268252.94.249.48/28 Amazon
268352.94.249.64/28 Amazon
268452.94.249.80/28 Amazon
268552.94.249.96/28 Amazon
268652.94.249.112/28 Amazon
268752.94.249.128/28 Amazon
268852.94.249.144/28 Amazon
268952.94.249.160/28 Amazon
269052.94.249.176/28 Amazon
269152.94.249.192/28 Amazon
269252.94.249.208/28 Amazon
269352.94.249.224/28 Amazon
269452.94.249.240/28 Amazon
269552.94.250.0/28 Amazon
269652.94.250.16/28 Amazon
269752.94.252.0/23 Amazon
269852.94.254.0/23 Amazon
269952.95.0.0/20 Amazon
270052.95.16.0/21 Amazon
270152.95.24.0/22 Amazon
270252.95.28.0/24 Amazon
270352.95.29.0/26 Amazon
270452.95.30.0/23 Amazon
270552.95.34.0/24 Amazon
270652.95.35.0/24 Amazon
270752.95.36.0/22 Amazon
270852.95.40.0/24 Amazon
270952.95.41.0/24 Amazon
271052.95.42.0/24 Amazon
271152.95.48.0/22 Amazon
271252.95.52.0/22 Amazon
271352.95.56.0/22 Amazon
271452.95.60.0/24 Amazon
271552.95.61.0/24 Amazon
271652.95.62.0/24 Amazon
271752.95.63.0/24 Amazon
271852.95.64.0/20 Amazon
271952.95.80.0/20 Amazon
272052.95.96.0/22 Amazon
272152.95.100.0/22 Amazon
272252.95.104.0/22 Amazon
272352.95.108.0/23 Amazon
272452.95.110.0/24 Amazon
272552.95.111.0/24 Amazon
272652.95.112.0/20 Amazon
272752.95.128.0/21 Amazon
272852.95.136.0/23 Amazon
272952.95.138.0/24 Amazon
273052.95.139.0/24 Amazon
273152.95.140.0/23 Amazon
273252.95.142.0/23 Amazon
273352.95.144.0/24 Amazon
273452.95.145.0/24 Amazon
273552.95.146.0/23 Amazon
273652.95.148.0/23 Amazon
273752.95.150.0/24 Amazon
273852.95.151.0/24 Amazon
273952.95.152.0/23 Amazon
274052.95.154.0/23 Amazon
274152.95.156.0/24 Amazon
274252.95.157.0/24 Amazon
274352.95.158.0/23 Amazon
274452.95.160.0/23 Amazon
274552.95.162.0/24 Amazon
274652.95.163.0/24 Amazon
274752.95.164.0/23 Amazon
274852.95.166.0/23 Amazon
274952.95.168.0/24 Amazon
275052.95.169.0/24 Amazon
275152.95.170.0/23 Amazon
275252.95.172.0/23 Amazon
275352.95.174.0/24 Amazon
275452.95.175.0/24 Amazon
275552.95.176.0/24 Amazon
275652.95.177.0/24 Amazon
275752.95.178.0/23 Amazon
275852.95.180.0/24 Amazon
275952.95.181.0/24 Amazon
276052.95.182.0/23 Amazon
276152.95.184.0/23 Amazon
276252.95.186.0/24 Amazon
276352.95.187.0/24 Amazon
276452.95.188.0/23 Amazon
276552.95.190.0/24 Amazon
276652.95.192.0/20 Amazon
276752.95.208.0/22 Amazon
276852.95.212.0/22 Amazon
276952.95.216.0/22 Amazon
277052.95.224.0/24 Amazon
277152.95.225.0/24 Amazon
277252.95.226.0/24 Amazon
277352.95.227.0/24 Amazon
277452.95.228.0/24 Amazon
277552.95.229.0/24 Amazon
277652.95.230.0/24 Amazon
277752.95.235.0/24 Amazon
277852.95.239.0/24 Amazon
277952.95.240.0/24 Amazon
278052.95.241.0/24 Amazon
278152.95.242.0/24 Amazon
278252.95.243.0/24 Amazon
278352.95.244.0/24 Amazon
278452.95.245.0/24 Amazon
278552.95.246.0/24 Amazon
278652.95.247.0/24 Amazon
278752.95.248.0/24 Amazon
278852.95.249.0/24 Amazon
278952.95.250.0/24 Amazon
279052.95.251.0/24 Amazon
279152.95.252.0/24 Amazon
279252.95.253.0/24 Amazon
279352.95.254.0/24 Amazon
279452.95.255.0/28 Amazon
279552.95.255.16/28 Amazon
279652.95.255.32/28 Amazon
279752.95.255.48/28 Amazon
279852.95.255.64/28 Amazon
279952.95.255.80/28 Amazon
280052.95.255.96/28 Amazon
280152.95.255.112/28 Amazon
280252.95.255.128/28 Amazon
280352.95.255.144/28 Amazon
280452.119.128.0/20 Amazon
280552.119.144.0/21 Amazon
280652.119.152.0/22 Amazon
280752.119.156.0/22 Amazon
280852.119.160.0/20 Amazon
280952.119.176.0/21 Amazon
281052.119.184.0/22 Amazon
281152.119.188.0/22 Amazon
281252.119.192.0/22 Amazon
281352.119.196.0/22 Amazon
281452.119.205.0/24 Amazon
281552.119.206.0/23 Amazon
281652.119.208.0/23 Amazon
281752.119.210.0/23 Amazon
281852.119.212.0/23 Amazon
281952.119.214.0/23 Amazon
282052.119.216.0/21 Amazon
282152.119.224.0/21 Amazon
282252.119.232.0/21 Amazon
282352.119.240.0/21 Amazon
282452.119.248.0/24 Amazon
282552.119.249.0/24 Amazon
282652.119.252.0/22 Amazon
282752.124.128.0/17 Amazon
282852.144.133.32/27 Amazon
282952.144.192.0/26 Amazon
283052.144.192.64/26 Amazon
283152.144.192.128/26 Amazon
283252.144.192.192/26 Amazon
283352.144.193.0/26 Amazon
283452.144.193.64/26 Amazon
283552.144.193.128/26 Amazon
283652.144.194.0/26 Amazon
283752.144.194.64/26 Amazon
283852.144.194.128/26 Amazon
283952.144.194.192/26 Amazon
284052.144.195.0/26 Amazon
284152.144.196.192/26 Amazon
284252.144.197.128/26 Amazon
284352.144.197.192/26 Amazon
284452.144.199.128/26 Amazon
284552.144.200.64/26 Amazon
284652.144.200.128/26 Amazon
284752.144.201.64/26 Amazon
284852.144.201.128/26 Amazon
284952.144.205.0/26 Amazon
285052.144.208.0/31 Amazon
285152.144.208.2/31 Amazon
285252.144.208.64/26 Amazon
285352.144.208.128/26 Amazon
285452.144.208.192/26 Amazon
285552.144.209.0/26 Amazon
285652.144.209.64/26 Amazon
285752.144.209.128/26 Amazon
285852.144.209.192/26 Amazon
285952.144.210.0/26 Amazon
286052.144.210.64/26 Amazon
286152.144.210.128/26 Amazon
286252.144.210.192/26 Amazon
286352.144.211.0/26 Amazon
286452.144.211.64/26 Amazon
286552.144.211.128/26 Amazon
286652.144.211.192/31 Amazon
286752.144.211.194/31 Amazon
286852.144.211.196/31 Amazon
286952.144.211.198/31 Amazon
287052.144.211.200/31 Amazon
287152.144.211.202/31 Amazon
287252.144.212.64/26 Amazon
287352.144.212.192/26 Amazon
287452.144.213.64/26 Amazon
287552.144.214.128/26 Amazon
287652.144.215.0/31 Amazon
287752.144.215.2/31 Amazon
287852.144.215.192/31 Amazon
287952.144.215.194/31 Amazon
288052.144.215.196/31 Amazon
288152.144.215.198/31 Amazon
288252.144.215.200/31 Amazon
288352.144.215.202/31 Amazon
288452.144.216.0/31 Amazon
288552.144.216.2/31 Amazon
288652.144.216.4/31 Amazon
288752.144.216.6/31 Amazon
288852.144.216.8/31 Amazon
288952.144.216.10/31 Amazon
289052.144.218.0/26 Amazon
289152.144.218.64/26 Amazon
289252.144.223.64/26 Amazon
289352.144.223.128/26 Amazon
289452.144.224.64/26 Amazon
289552.144.224.128/26 Amazon
289652.144.224.192/26 Amazon
289752.144.225.0/26 Amazon
289852.144.225.64/26 Amazon
289952.144.225.128/26 Amazon
290052.144.227.64/26 Amazon
290152.144.227.192/26 Amazon
290252.144.228.0/31 Amazon
290352.144.228.2/31 Amazon
290452.144.228.64/26 Amazon
290552.144.228.128/26 Amazon
290652.144.228.192/26 Amazon
290752.144.229.0/26 Amazon
290852.144.229.64/26 Amazon
290952.144.230.0/26 Amazon
291052.144.231.64/26 Amazon
291152.144.233.64/31 Amazon
291252.144.233.66/31 Amazon
291352.144.233.68/31 Amazon
291452.144.233.70/31 Amazon
291552.144.233.128/31 Amazon
291652.144.233.130/31 Amazon
291752.144.233.132/31 Amazon
291852.144.233.134/31 Amazon
291952.144.233.192/26 Amazon
292052.192.0.0/15 Amazon
292152.194.0.0/15 Amazon
292252.196.0.0/14 Amazon
292352.200.0.0/13 Amazon
292452.208.0.0/13 Amazon
292552.216.0.0/15 Amazon
292652.218.0.0/17 Amazon
292752.218.128.0/17 Amazon
292852.219.0.0/20 Amazon
292952.219.16.0/22 Amazon
293052.219.24.0/21 Amazon
293152.219.32.0/21 Amazon
293252.219.40.0/22 Amazon
293352.219.44.0/22 Amazon
293452.219.56.0/22 Amazon
293552.219.60.0/23 Amazon
293652.219.62.0/23 Amazon
293752.219.64.0/22 Amazon
293852.219.68.0/22 Amazon
293952.219.72.0/22 Amazon
294052.219.80.0/20 Amazon
294152.219.96.0/20 Amazon
294252.219.112.0/21 Amazon
294352.219.120.0/22 Amazon
294452.219.124.0/22 Amazon
294552.219.128.0/22 Amazon
294652.219.132.0/22 Amazon
294752.219.136.0/22 Amazon
294852.219.140.0/24 Amazon
294952.219.141.0/24 Amazon
295052.219.142.0/24 Amazon
295152.219.143.0/24 Amazon
295252.219.144.0/22 Amazon
295352.219.148.0/23 Amazon
295452.219.152.0/22 Amazon
295552.219.156.0/22 Amazon
295652.219.160.0/23 Amazon
295752.219.164.0/22 Amazon
295852.219.168.0/24 Amazon
295952.219.169.0/24 Amazon
296052.219.170.0/23 Amazon
296152.219.172.0/22 Amazon
296252.219.176.0/22 Amazon
296352.219.180.0/22 Amazon
296452.219.184.0/21 Amazon
296552.219.192.0/23 Amazon
296652.219.194.0/24 Amazon
296752.219.195.0/24 Amazon
296852.219.196.0/22 Amazon
296952.219.200.0/24 Amazon
297052.219.202.0/23 Amazon
297152.219.204.0/22 Amazon
297252.220.0.0/15 Amazon
297352.222.0.0/17 Amazon
297452.222.128.0/17 Amazon
297552.223.0.0/17 Amazon
297654.64.0.0/15 Amazon
297754.66.0.0/16 Amazon
297854.67.0.0/16 Amazon
297954.68.0.0/14 Amazon
298054.72.0.0/15 Amazon
298154.74.0.0/15 Amazon
298254.76.0.0/15 Amazon
298354.78.0.0/16 Amazon
298454.79.0.0/16 Amazon
298554.80.0.0/13 Amazon
298654.88.0.0/14 Amazon
298754.92.0.0/17 Amazon
298854.92.128.0/17 Amazon
298954.93.0.0/16 Amazon
299054.94.0.0/16 Amazon
299154.95.0.0/16 Amazon
299254.144.0.0/14 Amazon
299354.148.0.0/15 Amazon
299454.150.0.0/16 Amazon
299554.151.0.0/17 Amazon
299654.151.128.0/17 Amazon
299754.152.0.0/16 Amazon
299854.153.0.0/17 Amazon
299954.153.128.0/17 Amazon
300054.154.0.0/16 Amazon
300154.155.0.0/16 Amazon
300254.156.0.0/14 Amazon
300354.160.0.0/13 Amazon
300454.168.0.0/16 Amazon
300554.169.0.0/16 Amazon
300654.170.0.0/15 Amazon
300754.172.0.0/15 Amazon
300854.174.0.0/15 Amazon
300954.176.0.0/15 Amazon
301054.178.0.0/16 Amazon
301154.179.0.0/16 Amazon
301254.180.0.0/15 Amazon
301354.182.0.0/16 Amazon
301454.183.0.0/16 Amazon
301554.184.0.0/13 Amazon
301654.192.0.0/16 Amazon
301754.193.0.0/16 Amazon
301854.194.0.0/15 Amazon
301954.196.0.0/15 Amazon
302054.198.0.0/16 Amazon
302154.199.0.0/16 Amazon
302254.200.0.0/15 Amazon
302354.202.0.0/15 Amazon
302454.204.0.0/15 Amazon
302554.206.0.0/16 Amazon
302654.207.0.0/16 Amazon
302754.208.0.0/15 Amazon
302854.210.0.0/15 Amazon
302954.212.0.0/15 Amazon
303054.214.0.0/16 Amazon
303154.215.0.0/16 Amazon
303254.216.0.0/15 Amazon
303354.218.0.0/16 Amazon
303454.219.0.0/16 Amazon
303554.220.0.0/16 Amazon
303654.221.0.0/16 Amazon
303754.222.0.0/19 Amazon
303854.222.32.0/22 Amazon
303954.222.36.0/22 Amazon
304054.222.48.0/22 Amazon
304154.222.52.0/22 Amazon
304254.222.57.0/24 Amazon
304354.222.58.0/28 Amazon
304454.222.58.32/28 Amazon
304554.222.58.48/28 Amazon
304654.222.59.0/24 Amazon
304754.222.64.0/23 Amazon
304854.222.66.0/23 Amazon
304954.222.68.0/23 Amazon
305054.222.70.0/24 Amazon
305154.222.71.0/24 Amazon
305254.222.76.0/22 Amazon
305354.222.80.0/21 Amazon
305454.222.128.0/17 Amazon
305554.223.0.0/16 Amazon
305654.224.0.0/15 Amazon
305754.226.0.0/15 Amazon
305854.228.0.0/16 Amazon
305954.229.0.0/16 Amazon
306054.230.0.0/17 Amazon
306154.230.128.0/18 Amazon
306254.230.192.0/21 Amazon
306354.230.200.0/21 Amazon
306454.230.208.0/20 Amazon
306554.230.224.0/19 Amazon
306654.231.0.0/16 Amazon
306754.232.0.0/16 Amazon
306854.233.0.0/18 Amazon
306954.233.64.0/18 Amazon
307054.233.128.0/17 Amazon
307154.234.0.0/15 Amazon
307254.236.0.0/15 Amazon
307354.238.0.0/16 Amazon
307454.239.0.0/28 Amazon
307554.239.0.16/28 Amazon
307654.239.0.32/28 Amazon
307754.239.0.48/28 Amazon
307854.239.0.64/28 Amazon
307954.239.0.80/28 Amazon
308054.239.0.96/28 Amazon
308154.239.0.112/28 Amazon
308254.239.0.128/28 Amazon
308354.239.0.144/28 Amazon
308454.239.0.160/28 Amazon
308554.239.0.176/28 Amazon
308654.239.0.192/28 Amazon
308754.239.0.208/28 Amazon
308854.239.0.224/28 Amazon
308954.239.0.240/28 Amazon
309054.239.1.0/28 Amazon
309154.239.1.16/28 Amazon
309254.239.1.32/28 Amazon
309354.239.1.48/28 Amazon
309454.239.1.64/28 Amazon
309554.239.1.80/28 Amazon
309654.239.1.96/28 Amazon
309754.239.1.112/28 Amazon
309854.239.1.128/28 Amazon
309954.239.1.144/28 Amazon
310054.239.1.160/28 Amazon
310154.239.1.176/28 Amazon
310254.239.1.192/28 Amazon
310354.239.1.208/28 Amazon
310454.239.1.224/28 Amazon
310554.239.2.0/23 Amazon
310654.239.4.0/22 Amazon
310754.239.8.0/21 Amazon
310854.239.16.0/20 Amazon
310954.239.32.0/21 Amazon
311054.239.40.152/29 Amazon
311154.239.48.0/22 Amazon
311254.239.52.0/23 Amazon
311354.239.54.0/23 Amazon
311454.239.56.0/21 Amazon
311554.239.64.0/21 Amazon
311654.239.96.0/24 Amazon
311754.239.98.0/24 Amazon
311854.239.99.0/24 Amazon
311954.239.100.0/23 Amazon
312054.239.102.162/31 Amazon
312154.239.102.232/31 Amazon
312254.239.102.234/31 Amazon
312354.239.102.236/31 Amazon
312454.239.104.0/23 Amazon
312554.239.106.0/23 Amazon
312654.239.108.0/22 Amazon
312754.239.112.0/24 Amazon
312854.239.113.0/24 Amazon
312954.239.115.0/25 Amazon
313054.239.116.0/22 Amazon
313154.239.120.0/21 Amazon
313254.239.128.0/18 Amazon
313354.239.192.0/19 Amazon
313454.240.17.0/24 Amazon
313554.240.128.0/18 Amazon
313654.240.192.0/22 Amazon
313754.240.196.0/24 Amazon
313854.240.197.0/24 Amazon
313954.240.198.0/24 Amazon
314054.240.199.0/24 Amazon
314154.240.200.0/24 Amazon
314254.240.202.0/24 Amazon
314354.240.203.0/24 Amazon
314454.240.204.0/22 Amazon
314554.240.208.0/22 Amazon
314654.240.212.0/22 Amazon
314754.240.216.0/22 Amazon
314854.240.220.0/22 Amazon
314954.240.225.0/24 Amazon
315054.240.226.0/24 Amazon
315154.240.227.0/24 Amazon
315254.240.228.0/23 Amazon
315354.240.230.0/23 Amazon
315454.240.232.0/22 Amazon
315554.240.236.1/32 Amazon
315654.240.236.2/32 Amazon
315754.240.236.5/32 Amazon
315854.240.236.6/32 Amazon
315954.240.236.9/32 Amazon
316054.240.236.10/32 Amazon
316154.240.236.13/32 Amazon
316254.240.236.14/32 Amazon
316354.240.236.17/32 Amazon
316454.240.236.18/32 Amazon
316554.240.236.21/32 Amazon
316654.240.236.22/32 Amazon
316754.240.236.25/32 Amazon
316854.240.236.26/32 Amazon
316954.240.236.29/32 Amazon
317054.240.236.30/32 Amazon
317154.240.236.33/32 Amazon
317254.240.236.34/32 Amazon
317354.240.236.37/32 Amazon
317454.240.236.38/32 Amazon
317554.240.236.41/32 Amazon
317654.240.236.42/32 Amazon
317754.240.236.45/32 Amazon
317854.240.236.46/32 Amazon
317954.240.236.49/32 Amazon
318054.240.236.50/32 Amazon
318154.240.236.53/32 Amazon
318254.240.236.54/32 Amazon
318354.240.236.57/32 Amazon
318454.240.236.58/32 Amazon
318554.240.236.61/32 Amazon
318654.240.236.62/32 Amazon
318754.240.236.65/32 Amazon
318854.240.236.66/32 Amazon
318954.240.236.69/32 Amazon
319054.240.236.70/32 Amazon
319154.240.236.73/32 Amazon
319254.240.236.74/32 Amazon
319354.240.236.77/32 Amazon
319454.240.236.78/32 Amazon
319554.240.236.81/32 Amazon
319654.240.236.82/32 Amazon
319754.240.236.85/32 Amazon
319854.240.236.86/32 Amazon
319954.240.236.89/32 Amazon
320054.240.236.90/32 Amazon
320154.240.236.93/32 Amazon
320254.240.236.94/32 Amazon
320354.240.241.0/24 Amazon
320454.240.244.0/22 Amazon
320554.240.248.0/21 Amazon
320654.241.0.0/16 Amazon
320754.242.0.0/15 Amazon
320854.244.0.0/16 Amazon
320954.245.0.0/16 Amazon
321054.246.0.0/16 Amazon
321154.247.0.0/16 Amazon
321254.248.0.0/15 Amazon
321354.250.0.0/16 Amazon
321454.251.0.0/16 Amazon
321554.252.0.0/16 Amazon
321654.253.0.0/16 Amazon
321754.254.0.0/16 Amazon
321854.255.0.0/16 Amazon
321958.254.138.0/25 Amazon
322058.254.138.128/26 Amazon
322163.32.0.0/14 Amazon
322263.246.112.0/24 Amazon
322363.246.113.0/24 Amazon
322463.246.114.0/23 Amazon
322563.246.119.0/24 Amazon
322664.187.128.0/20 Amazon
322764.252.64.0/18 Amazon
322864.252.128.0/18 Amazon
322965.0.0.0/14 Amazon
323065.8.0.0/16 Amazon
323165.9.0.0/17 Amazon
323265.9.128.0/18 Amazon
323367.202.0.0/18 Amazon
323467.220.224.0/20 Amazon
323567.220.240.0/20 Amazon
323668.66.112.0/20 Amazon
323768.79.0.0/18 Amazon
323869.107.3.176/29 Amazon
323969.107.3.184/29 Amazon
324069.107.6.112/29 Amazon
324169.107.6.120/29 Amazon
324269.107.6.160/29 Amazon
324369.107.6.168/29 Amazon
324469.107.6.200/29 Amazon
324569.107.6.208/29 Amazon
324669.107.6.216/29 Amazon
324769.107.6.224/29 Amazon
324869.107.7.0/29 Amazon
324969.107.7.8/29 Amazon
325069.107.7.16/29 Amazon
325169.107.7.32/29 Amazon
325269.107.7.40/29 Amazon
325369.107.7.48/29 Amazon
325469.107.7.56/29 Amazon
325569.107.7.64/29 Amazon
325669.107.7.72/29 Amazon
325769.107.7.80/29 Amazon
325869.107.7.88/29 Amazon
325969.107.7.96/29 Amazon
326069.107.7.104/29 Amazon
326169.107.7.112/29 Amazon
326269.107.7.120/29 Amazon
326369.107.7.128/29 Amazon
326469.107.7.136/29 Amazon
326569.230.192.0/18 Amazon
326669.231.128.0/18 Amazon
326769.234.192.0/18 Amazon
326869.235.128.0/18 Amazon
326970.132.0.0/18 Amazon
327070.224.192.0/18 Amazon
327170.232.64.0/20 Amazon
327270.232.80.0/21 Amazon
327370.232.88.0/22 Amazon
327470.232.92.0/22 Amazon
327570.232.96.0/20 Amazon
327670.232.112.0/21 Amazon
327770.232.120.0/22 Amazon
327870.232.124.0/22 Amazon
327971.131.192.0/18 Amazon
328071.132.0.0/18 Amazon
328171.137.0.0/22 Amazon
328271.137.4.0/24 Amazon
328371.137.8.0/22 Amazon
328471.152.0.0/17 Amazon
328572.21.192.0/19 Amazon
328672.41.0.0/20 Amazon
328772.44.32.0/19 Amazon
328875.2.0.0/17 Amazon
328975.101.128.0/17 Amazon
329076.223.0.0/17 Amazon
329179.125.0.0/17 Amazon
329287.238.80.0/21 Amazon
329396.127.0.0/17 Amazon
329499.77.0.0/20 Amazon
329599.77.16.0/21 Amazon
329699.77.24.0/22 Amazon
329799.77.28.0/22 Amazon
329899.77.32.0/20 Amazon
329999.77.48.0/21 Amazon
330099.77.56.0/21 Amazon
330199.77.128.0/18 Amazon
330299.77.247.0/24 Amazon
330399.77.250.0/24 Amazon
330499.77.253.0/24 Amazon
330599.77.254.0/24 Amazon
330699.78.128.0/20 Amazon
330799.78.144.0/21 Amazon
330899.78.152.0/22 Amazon
330999.78.156.0/22 Amazon
331099.78.160.0/21 Amazon
331199.78.168.0/23 Amazon
331299.78.170.0/23 Amazon
331399.78.172.0/24 Amazon
331499.78.176.0/21 Amazon
331599.78.184.0/22 Amazon
331699.78.188.0/22 Amazon
331799.78.192.0/22 Amazon
331899.78.196.0/22 Amazon
331999.78.208.0/22 Amazon
332099.78.212.0/22 Amazon
332199.78.216.0/22 Amazon
332299.78.220.0/22 Amazon
332399.78.228.0/22 Amazon
332499.78.232.0/21 Amazon
332599.78.240.0/20 Amazon
332699.79.0.0/16 Amazon
332799.80.0.0/15 Amazon
332899.82.128.0/20 Amazon
332999.82.144.0/21 Amazon
333099.82.152.0/22 Amazon
333199.82.156.0/22 Amazon
333299.82.160.0/24 Amazon
333399.82.161.0/24 Amazon
333499.82.162.0/24 Amazon
333599.82.163.0/24 Amazon
333699.82.164.0/24 Amazon
333799.82.165.0/24 Amazon
333899.82.166.0/24 Amazon
333999.82.167.0/24 Amazon
334099.82.168.0/24 Amazon
334199.82.169.0/24 Amazon
334299.82.170.0/24 Amazon
334399.82.171.0/24 Amazon
334499.82.172.0/24 Amazon
334599.82.173.0/24 Amazon
334699.82.174.0/24 Amazon
334799.82.175.0/24 Amazon
334899.82.176.0/21 Amazon
334999.82.184.0/22 Amazon
335099.82.188.0/22 Amazon
335199.83.64.0/21 Amazon
335299.83.72.0/22 Amazon
335399.83.76.0/22 Amazon
335499.83.80.0/22 Amazon
335599.83.84.0/22 Amazon
335699.83.88.0/21 Amazon
335799.83.96.0/24 Amazon
335899.83.97.0/24 Amazon
335999.83.98.0/24 Amazon
336099.83.99.0/24 Amazon
336199.83.100.0/24 Amazon
336299.83.101.0/24 Amazon
336399.83.112.0/21 Amazon
336499.83.120.0/22 Amazon
336599.83.128.0/17 Amazon
336699.84.0.0/16 Amazon
336799.86.0.0/16 Amazon
336899.87.0.0/22 Amazon
336999.87.4.0/22 Amazon
337099.87.8.0/21 Amazon
337199.87.16.0/20 Amazon
337299.87.32.0/22 Amazon
337399.150.0.0/21 Amazon
337499.150.8.0/21 Amazon
337599.150.16.0/21 Amazon
337699.150.24.0/21 Amazon
337799.150.32.0/21 Amazon
337899.150.40.0/21 Amazon
337999.150.48.0/21 Amazon
338099.150.56.0/21 Amazon
338199.150.64.0/21 Amazon
338299.150.72.0/21 Amazon
338399.150.80.0/21 Amazon
338499.150.88.0/21 Amazon
338599.150.96.0/21 Amazon
338699.150.104.0/21 Amazon
338799.150.112.0/21 Amazon
338899.150.120.0/21 Amazon
338999.151.64.0/21 Amazon
339099.151.72.0/21 Amazon
339199.151.80.0/21 Amazon
339299.151.88.0/21 Amazon
339399.151.96.0/21 Amazon
339499.151.104.0/21 Amazon
339599.151.112.0/21 Amazon
339699.151.120.0/21 Amazon
339799.151.128.0/21 Amazon
339899.151.136.0/21 Amazon
339999.151.144.0/21 Amazon
3400100.20.0.0/14 Amazon
3401100.24.0.0/13 Amazon
3402103.4.8.0/21 Amazon
3403103.8.172.0/22 Amazon
3404103.246.148.0/23 Amazon
3405103.246.150.0/23 Amazon
3406104.255.56.11/32 Amazon
3407104.255.56.12/32 Amazon
3408104.255.59.81/32 Amazon
3409104.255.59.82/32 Amazon
3410104.255.59.83/32 Amazon
3411104.255.59.85/32 Amazon
3412104.255.59.86/32 Amazon
3413104.255.59.87/32 Amazon
3414104.255.59.88/32 Amazon
3415104.255.59.91/32 Amazon
3416104.255.59.101/32 Amazon
3417104.255.59.102/32 Amazon
3418104.255.59.103/32 Amazon
3419104.255.59.104/32 Amazon
3420104.255.59.105/32 Amazon
3421104.255.59.106/32 Amazon
3422104.255.59.114/32 Amazon
3423104.255.59.115/32 Amazon
3424104.255.59.118/32 Amazon
3425104.255.59.119/32 Amazon
3426104.255.59.122/32 Amazon
3427104.255.59.130/32 Amazon
3428104.255.59.131/32 Amazon
3429104.255.59.132/32 Amazon
3430104.255.59.133/32 Amazon
3431104.255.59.134/32 Amazon
3432104.255.59.135/32 Amazon
3433104.255.59.136/32 Amazon
3434104.255.59.137/32 Amazon
3435104.255.59.138/32 Amazon
3436104.255.59.139/32 Amazon
3437107.20.0.0/14 Amazon
3438107.176.0.0/15 Amazon
3439108.128.0.0/13 Amazon
3440108.136.0.0/15 Amazon
3441108.138.0.0/15 Amazon
3442108.156.0.0/14 Amazon
3443108.166.224.0/21 Amazon
3444108.166.232.0/21 Amazon
3445108.166.240.0/21 Amazon
3446108.166.248.0/21 Amazon
3447108.175.48.0/22 Amazon
3448108.175.52.0/22 Amazon
3449108.175.56.0/22 Amazon
3450108.175.60.0/22 Amazon
3451116.129.226.0/25 Amazon
3452116.129.226.128/26 Amazon
3453118.193.97.64/26 Amazon
3454118.193.97.128/25 Amazon
3455119.147.182.0/25 Amazon
3456119.147.182.128/26 Amazon
3457120.52.12.64/26 Amazon
3458120.52.22.96/27 Amazon
3459120.52.39.128/27 Amazon
3460120.52.153.192/26 Amazon
3461120.232.236.0/25 Amazon
3462120.232.236.128/26 Amazon
3463120.253.240.192/26 Amazon
3464120.253.241.160/27 Amazon
3465120.253.245.128/26 Amazon
3466120.253.245.192/27 Amazon
3467122.248.192.0/18 Amazon
3468130.176.0.0/17 Amazon
3469130.176.128.0/18 Amazon
3470130.176.192.0/19 Amazon
3471130.176.224.0/20 Amazon
3472130.176.254.0/24 Amazon
3473130.176.255.0/24 Amazon
3474140.179.0.0/16 Amazon
3475142.4.160.0/29 Amazon
3476142.4.160.8/29 Amazon
3477142.4.160.16/29 Amazon
3478142.4.160.24/29 Amazon
3479142.4.160.32/29 Amazon
3480142.4.160.40/29 Amazon
3481142.4.160.48/29 Amazon
3482142.4.160.56/29 Amazon
3483142.4.160.64/29 Amazon
3484142.4.160.72/29 Amazon
3485142.4.160.80/29 Amazon
3486142.4.160.88/29 Amazon
3487142.4.160.96/29 Amazon
3488142.4.160.104/29 Amazon
3489142.4.160.112/29 Amazon
3490143.204.0.0/16 Amazon
3491144.220.0.0/16 Amazon
3492150.222.0.16/32 Amazon
3493150.222.0.17/32 Amazon
3494150.222.0.18/32 Amazon
3495150.222.0.19/32 Amazon
3496150.222.2.0/24 Amazon
3497150.222.3.176/32 Amazon
3498150.222.3.177/32 Amazon
3499150.222.3.178/32 Amazon
3500150.222.3.179/32 Amazon
3501150.222.3.180/32 Amazon
3502150.222.3.181/32 Amazon
3503150.222.3.182/32 Amazon
3504150.222.3.183/32 Amazon
3505150.222.3.184/32 Amazon
3506150.222.3.185/32 Amazon
3507150.222.3.186/32 Amazon
3508150.222.3.187/32 Amazon
3509150.222.3.188/32 Amazon
3510150.222.3.189/32 Amazon
3511150.222.3.190/32 Amazon
3512150.222.3.191/32 Amazon
3513150.222.3.192/31 Amazon
3514150.222.3.194/31 Amazon
3515150.222.3.196/31 Amazon
3516150.222.3.198/31 Amazon
3517150.222.3.200/31 Amazon
3518150.222.3.202/31 Amazon
3519150.222.3.204/31 Amazon
3520150.222.3.206/31 Amazon
3521150.222.3.208/31 Amazon
3522150.222.3.210/31 Amazon
3523150.222.3.212/31 Amazon
3524150.222.3.214/31 Amazon
3525150.222.3.216/31 Amazon
3526150.222.3.218/31 Amazon
3527150.222.3.220/31 Amazon
3528150.222.3.222/31 Amazon
3529150.222.3.224/31 Amazon
3530150.222.3.226/31 Amazon
3531150.222.3.228/31 Amazon
3532150.222.3.230/31 Amazon
3533150.222.3.232/31 Amazon
3534150.222.3.234/31 Amazon
3535150.222.3.236/31 Amazon
3536150.222.3.238/31 Amazon
3537150.222.3.240/31 Amazon
3538150.222.3.242/31 Amazon
3539150.222.3.244/31 Amazon
3540150.222.3.246/31 Amazon
3541150.222.3.248/31 Amazon
3542150.222.3.250/31 Amazon
3543150.222.3.252/31 Amazon
3544150.222.3.254/31 Amazon
3545150.222.5.0/24 Amazon
3546150.222.6.0/24 Amazon
3547150.222.7.0/24 Amazon
3548150.222.10.0/24 Amazon
3549150.222.11.0/31 Amazon
3550150.222.11.74/31 Amazon
3551150.222.11.76/31 Amazon
3552150.222.11.78/31 Amazon
3553150.222.11.80/31 Amazon
3554150.222.11.84/31 Amazon
3555150.222.11.86/31 Amazon
3556150.222.11.88/31 Amazon
3557150.222.11.90/31 Amazon
3558150.222.11.92/31 Amazon
3559150.222.11.94/31 Amazon
3560150.222.11.96/31 Amazon
3561150.222.12.0/24 Amazon
3562150.222.13.0/24 Amazon
3563150.222.14.72/31 Amazon
3564150.222.15.124/32 Amazon
3565150.222.15.125/32 Amazon
3566150.222.15.126/32 Amazon
3567150.222.15.127/32 Amazon
3568150.222.15.128/31 Amazon
3569150.222.15.130/31 Amazon
3570150.222.28.17/32 Amazon
3571150.222.28.18/31 Amazon
3572150.222.28.104/32 Amazon
3573150.222.28.105/32 Amazon
3574150.222.28.106/31 Amazon
3575150.222.28.108/31 Amazon
3576150.222.28.110/31 Amazon
3577150.222.28.112/31 Amazon
3578150.222.28.114/31 Amazon
3579150.222.28.116/31 Amazon
3580150.222.28.118/31 Amazon
3581150.222.28.120/31 Amazon
3582150.222.28.122/31 Amazon
3583150.222.28.124/31 Amazon
3584150.222.28.126/31 Amazon
3585150.222.28.128/31 Amazon
3586150.222.28.130/31 Amazon
3587150.222.28.132/31 Amazon
3588150.222.28.134/31 Amazon
3589150.222.28.136/31 Amazon
3590150.222.28.138/31 Amazon
3591150.222.28.140/31 Amazon
3592150.222.28.142/31 Amazon
3593150.222.66.0/24 Amazon
3594150.222.67.0/24 Amazon
3595150.222.69.0/24 Amazon
3596150.222.70.0/24 Amazon
3597150.222.71.0/24 Amazon
3598150.222.72.0/24 Amazon
3599150.222.73.0/24 Amazon
3600150.222.74.0/24 Amazon
3601150.222.75.0/24 Amazon
3602150.222.76.0/24 Amazon
3603150.222.77.0/24 Amazon
3604150.222.78.0/24 Amazon
3605150.222.79.0/24 Amazon
3606150.222.80.0/24 Amazon
3607150.222.81.0/24 Amazon
3608150.222.82.0/24 Amazon
3609150.222.83.0/24 Amazon
3610150.222.84.0/24 Amazon
3611150.222.85.0/24 Amazon
3612150.222.87.0/24 Amazon
3613150.222.88.0/24 Amazon
3614150.222.89.0/24 Amazon
3615150.222.90.0/24 Amazon
3616150.222.91.0/24 Amazon
3617150.222.92.0/22 Amazon
3618150.222.96.0/24 Amazon
3619150.222.97.0/24 Amazon
3620150.222.98.0/24 Amazon
3621150.222.99.0/24 Amazon
3622150.222.100.0/24 Amazon
3623150.222.101.0/24 Amazon
3624150.222.102.0/24 Amazon
3625150.222.104.0/24 Amazon
3626150.222.105.0/24 Amazon
3627150.222.106.0/24 Amazon
3628150.222.108.0/24 Amazon
3629150.222.109.0/24 Amazon
3630150.222.110.0/24 Amazon
3631150.222.112.0/24 Amazon
3632150.222.113.0/24 Amazon
3633150.222.114.0/24 Amazon
3634150.222.115.0/24 Amazon
3635150.222.116.0/24 Amazon
3636150.222.117.0/24 Amazon
3637150.222.118.0/24 Amazon
3638150.222.119.0/24 Amazon
3639150.222.120.20/31 Amazon
3640150.222.120.62/31 Amazon
3641150.222.120.224/31 Amazon
3642150.222.120.226/31 Amazon
3643150.222.120.228/31 Amazon
3644150.222.120.230/31 Amazon
3645150.222.120.232/31 Amazon
3646150.222.120.234/31 Amazon
3647150.222.120.240/31 Amazon
3648150.222.120.242/31 Amazon
3649150.222.120.244/31 Amazon
3650150.222.120.246/31 Amazon
3651150.222.120.248/31 Amazon
3652150.222.120.250/31 Amazon
3653150.222.120.252/32 Amazon
3654150.222.120.255/32 Amazon
3655150.222.121.0/24 Amazon
3656150.222.122.92/31 Amazon
3657150.222.122.94/31 Amazon
3658150.222.122.96/31 Amazon
3659150.222.122.98/31 Amazon
3660150.222.122.100/31 Amazon
3661150.222.122.102/31 Amazon
3662150.222.122.104/31 Amazon
3663150.222.122.106/31 Amazon
3664150.222.122.108/31 Amazon
3665150.222.122.110/31 Amazon
3666150.222.122.112/31 Amazon
3667150.222.122.114/31 Amazon
3668150.222.122.116/31 Amazon
3669150.222.129.19/32 Amazon
3670150.222.129.20/31 Amazon
3671150.222.129.62/31 Amazon
3672150.222.129.64/31 Amazon
3673150.222.129.66/31 Amazon
3674150.222.129.69/32 Amazon
3675150.222.129.110/31 Amazon
3676150.222.129.112/31 Amazon
3677150.222.129.114/31 Amazon
3678150.222.129.116/31 Amazon
3679150.222.129.118/31 Amazon
3680150.222.129.120/31 Amazon
3681150.222.129.122/31 Amazon
3682150.222.129.124/31 Amazon
3683150.222.129.126/31 Amazon
3684150.222.129.128/31 Amazon
3685150.222.129.130/31 Amazon
3686150.222.129.132/31 Amazon
3687150.222.129.134/31 Amazon
3688150.222.129.136/31 Amazon
3689150.222.129.138/31 Amazon
3690150.222.129.140/31 Amazon
3691150.222.129.142/31 Amazon
3692150.222.129.144/31 Amazon
3693150.222.129.146/31 Amazon
3694150.222.129.152/31 Amazon
3695150.222.129.154/31 Amazon
3696150.222.129.156/31 Amazon
3697150.222.129.158/31 Amazon
3698150.222.129.240/31 Amazon
3699150.222.129.242/31 Amazon
3700150.222.129.244/31 Amazon
3701150.222.129.246/31 Amazon
3702150.222.129.248/31 Amazon
3703150.222.129.250/31 Amazon
3704150.222.129.252/32 Amazon
3705150.222.129.255/32 Amazon
3706150.222.133.0/24 Amazon
3707150.222.134.0/24 Amazon
3708150.222.135.0/24 Amazon
3709150.222.136.0/24 Amazon
3710150.222.138.0/24 Amazon
3711150.222.139.116/30 Amazon
3712150.222.139.120/30 Amazon
3713150.222.139.124/30 Amazon
3714150.222.140.0/24 Amazon
3715150.222.141.0/24 Amazon
3716150.222.142.0/24 Amazon
3717150.222.143.0/24 Amazon
3718150.222.164.208/31 Amazon
3719150.222.164.210/32 Amazon
3720150.222.164.211/32 Amazon
3721150.222.164.220/31 Amazon
3722150.222.164.222/32 Amazon
3723150.222.176.0/22 Amazon
3724150.222.180.0/24 Amazon
3725150.222.196.0/24 Amazon
3726150.222.199.0/25 Amazon
3727150.222.202.0/24 Amazon
3728150.222.203.0/24 Amazon
3729150.222.204.0/24 Amazon
3730150.222.205.0/24 Amazon
3731150.222.206.0/24 Amazon
3732150.222.207.0/24 Amazon
3733150.222.208.64/32 Amazon
3734150.222.208.65/32 Amazon
3735150.222.208.66/31 Amazon
3736150.222.208.68/31 Amazon
3737150.222.208.70/31 Amazon
3738150.222.208.72/31 Amazon
3739150.222.208.74/31 Amazon
3740150.222.208.76/31 Amazon
3741150.222.208.78/31 Amazon
3742150.222.208.80/31 Amazon
3743150.222.208.82/31 Amazon
3744150.222.208.84/31 Amazon
3745150.222.208.86/31 Amazon
3746150.222.208.88/31 Amazon
3747150.222.208.90/31 Amazon
3748150.222.208.92/31 Amazon
3749150.222.208.94/31 Amazon
3750150.222.208.96/31 Amazon
3751150.222.210.0/24 Amazon
3752150.222.212.0/24 Amazon
3753150.222.213.40/32 Amazon
3754150.222.213.41/32 Amazon
3755150.222.214.0/24 Amazon
3756150.222.215.0/24 Amazon
3757150.222.217.17/32 Amazon
3758150.222.217.226/31 Amazon
3759150.222.217.228/30 Amazon
3760150.222.217.232/31 Amazon
3761150.222.217.234/31 Amazon
3762150.222.217.248/31 Amazon
3763150.222.217.250/31 Amazon
3764150.222.218.0/24 Amazon
3765150.222.219.0/24 Amazon
3766150.222.220.0/24 Amazon
3767150.222.221.0/24 Amazon
3768150.222.222.0/24 Amazon
3769150.222.223.0/24 Amazon
3770150.222.224.0/24 Amazon
3771150.222.226.0/24 Amazon
3772150.222.227.0/24 Amazon
3773150.222.228.0/24 Amazon
3774150.222.229.0/24 Amazon
3775150.222.230.92/32 Amazon
3776150.222.230.93/32 Amazon
3777150.222.230.94/31 Amazon
3778150.222.230.96/31 Amazon
3779150.222.230.98/31 Amazon
3780150.222.230.100/31 Amazon
3781150.222.230.102/31 Amazon
3782150.222.230.104/31 Amazon
3783150.222.230.106/31 Amazon
3784150.222.230.108/31 Amazon
3785150.222.230.110/31 Amazon
3786150.222.230.112/31 Amazon
3787150.222.230.114/31 Amazon
3788150.222.230.116/31 Amazon
3789150.222.230.118/31 Amazon
3790150.222.230.120/31 Amazon
3791150.222.230.122/31 Amazon
3792150.222.230.124/31 Amazon
3793150.222.231.0/24 Amazon
3794150.222.232.51/32 Amazon
3795150.222.232.88/32 Amazon
3796150.222.232.94/31 Amazon
3797150.222.232.96/28 Amazon
3798150.222.232.112/31 Amazon
3799150.222.232.114/31 Amazon
3800150.222.232.116/31 Amazon
3801150.222.232.118/31 Amazon
3802150.222.232.120/31 Amazon
3803150.222.233.0/24 Amazon
3804150.222.234.0/32 Amazon
3805150.222.234.1/32 Amazon
3806150.222.234.2/32 Amazon
3807150.222.234.3/32 Amazon
3808150.222.234.4/32 Amazon
3809150.222.234.5/32 Amazon
3810150.222.234.6/31 Amazon
3811150.222.234.8/31 Amazon
3812150.222.234.10/31 Amazon
3813150.222.234.12/31 Amazon
3814150.222.234.14/31 Amazon
3815150.222.234.16/31 Amazon
3816150.222.234.18/31 Amazon
3817150.222.234.20/31 Amazon
3818150.222.234.22/31 Amazon
3819150.222.234.24/31 Amazon
3820150.222.234.26/31 Amazon
3821150.222.234.28/31 Amazon
3822150.222.234.30/31 Amazon
3823150.222.234.32/31 Amazon
3824150.222.234.34/31 Amazon
3825150.222.234.36/31 Amazon
3826150.222.234.38/31 Amazon
3827150.222.234.40/31 Amazon
3828150.222.234.42/31 Amazon
3829150.222.234.44/31 Amazon
3830150.222.234.46/31 Amazon
3831150.222.234.48/31 Amazon
3832150.222.234.50/31 Amazon
3833150.222.234.52/31 Amazon
3834150.222.234.54/31 Amazon
3835150.222.234.56/31 Amazon
3836150.222.234.58/31 Amazon
3837150.222.234.60/31 Amazon
3838150.222.234.62/31 Amazon
3839150.222.234.64/31 Amazon
3840150.222.234.66/31 Amazon
3841150.222.234.68/31 Amazon
3842150.222.234.70/31 Amazon
3843150.222.234.72/31 Amazon
3844150.222.234.74/31 Amazon
3845150.222.234.76/31 Amazon
3846150.222.234.78/31 Amazon
3847150.222.234.80/31 Amazon
3848150.222.234.82/31 Amazon
3849150.222.234.84/31 Amazon
3850150.222.234.86/31 Amazon
3851150.222.234.96/31 Amazon
3852150.222.234.98/31 Amazon
3853150.222.234.100/31 Amazon
3854150.222.234.102/32 Amazon
3855150.222.234.103/32 Amazon
3856150.222.234.104/31 Amazon
3857150.222.234.106/31 Amazon
3858150.222.234.108/31 Amazon
3859150.222.234.110/31 Amazon
3860150.222.234.112/31 Amazon
3861150.222.234.114/31 Amazon
3862150.222.234.116/31 Amazon
3863150.222.234.118/31 Amazon
3864150.222.234.120/31 Amazon
3865150.222.234.122/31 Amazon
3866150.222.234.124/31 Amazon
3867150.222.234.126/31 Amazon
3868150.222.234.128/31 Amazon
3869150.222.234.130/31 Amazon
3870150.222.234.132/31 Amazon
3871150.222.234.134/31 Amazon
3872150.222.234.136/31 Amazon
3873150.222.234.138/31 Amazon
3874150.222.234.140/31 Amazon
3875150.222.234.142/31 Amazon
3876150.222.235.0/24 Amazon
3877150.222.236.0/24 Amazon
3878150.222.237.0/24 Amazon
3879150.222.239.0/24 Amazon
3880150.222.240.131/32 Amazon
3881150.222.240.135/32 Amazon
3882150.222.240.137/32 Amazon
3883150.222.240.161/32 Amazon
3884150.222.240.207/32 Amazon
3885150.222.240.237/32 Amazon
3886150.222.240.245/32 Amazon
3887150.222.240.247/32 Amazon
3888150.222.240.249/32 Amazon
3889150.222.240.251/32 Amazon
3890150.222.242.84/31 Amazon
3891150.222.242.97/32 Amazon
3892150.222.242.99/32 Amazon
3893150.222.242.214/31 Amazon
3894150.222.242.227/32 Amazon
3895150.222.242.229/32 Amazon
3896150.222.242.231/32 Amazon
3897150.222.242.233/32 Amazon
3898150.222.243.9/32 Amazon
3899150.222.243.11/32 Amazon
3900150.222.243.13/32 Amazon
3901150.222.243.15/32 Amazon
3902150.222.243.17/32 Amazon
3903150.222.243.19/32 Amazon
3904150.222.243.33/32 Amazon
3905150.222.243.35/32 Amazon
3906150.222.243.37/32 Amazon
3907150.222.243.39/32 Amazon
3908150.222.243.41/32 Amazon
3909150.222.243.43/32 Amazon
3910150.222.243.45/32 Amazon
3911150.222.243.47/32 Amazon
3912150.222.243.51/32 Amazon
3913150.222.243.53/32 Amazon
3914150.222.243.55/32 Amazon
3915150.222.243.57/32 Amazon
3916150.222.243.59/32 Amazon
3917150.222.243.177/32 Amazon
3918150.222.244.35/32 Amazon
3919150.222.244.37/32 Amazon
3920150.222.245.122/31 Amazon
3921150.222.252.244/31 Amazon
3922150.222.252.246/31 Amazon
3923150.222.252.248/31 Amazon
3924150.222.252.250/31 Amazon
3925157.175.0.0/16 Amazon
3926157.241.0.0/16 Amazon
3927160.1.0.0/16 Amazon
3928161.188.128.0/23 Amazon
3929161.188.130.0/23 Amazon
3930161.188.132.0/23 Amazon
3931161.188.134.0/23 Amazon
3932161.188.136.0/23 Amazon
3933161.188.138.0/23 Amazon
3934161.188.140.0/23 Amazon
3935161.188.142.0/23 Amazon
3936161.188.144.0/23 Amazon
3937161.188.146.0/23 Amazon
3938161.188.148.0/23 Amazon
3939161.188.150.0/23 Amazon
3940161.188.152.0/23 Amazon
3941161.188.154.0/23 Amazon
3942161.188.156.0/23 Amazon
3943161.188.158.0/23 Amazon
3944161.188.160.0/23 Amazon
3945161.189.0.0/16 Amazon
3946162.213.232.0/24 Amazon
3947162.213.233.0/24 Amazon
3948162.213.234.0/23 Amazon
3949162.222.148.0/22 Amazon
3950162.250.236.0/24 Amazon
3951162.250.237.0/24 Amazon
3952162.250.238.0/23 Amazon
3953172.96.97.0/24 Amazon
3954172.96.98.0/24 Amazon
3955172.96.110.0/24 Amazon
3956174.129.0.0/16 Amazon
3957175.41.128.0/18 Amazon
3958175.41.192.0/18 Amazon
3959176.32.64.0/19 Amazon
3960176.32.96.0/21 Amazon
3961176.32.104.0/21 Amazon
3962176.32.112.0/21 Amazon
3963176.32.120.0/22 Amazon
3964176.32.124.128/25 Amazon
3965176.32.125.0/25 Amazon
3966176.32.125.128/26 Amazon
3967176.32.125.192/27 Amazon
3968176.32.125.224/31 Amazon
3969176.32.125.226/31 Amazon
3970176.32.125.228/31 Amazon
3971176.32.125.230/31 Amazon
3972176.32.125.232/31 Amazon
3973176.32.125.234/31 Amazon
3974176.32.125.236/31 Amazon
3975176.32.125.238/31 Amazon
3976176.32.125.240/31 Amazon
3977176.32.125.242/31 Amazon
3978176.32.125.244/31 Amazon
3979176.32.125.246/31 Amazon
3980176.32.125.248/31 Amazon
3981176.32.125.250/31 Amazon
3982176.32.125.252/31 Amazon
3983176.32.125.254/31 Amazon
3984176.34.0.0/19 Amazon
3985176.34.32.0/19 Amazon
3986176.34.64.0/18 Amazon
3987176.34.128.0/17 Amazon
3988177.71.128.0/17 Amazon
3989177.72.240.0/21 Amazon
3990178.236.0.0/20 Amazon
3991180.163.57.0/25 Amazon
3992180.163.57.128/26 Amazon
3993184.72.0.0/18 Amazon
3994184.72.64.0/18 Amazon
3995184.72.128.0/17 Amazon
3996184.73.0.0/16 Amazon
3997184.169.128.0/17 Amazon
3998185.48.120.0/22 Amazon
3999185.143.16.0/24 Amazon
4000195.17.0.0/24 Amazon
4001198.99.2.0/24 Amazon
4002199.127.232.0/22 Amazon
4003203.83.220.0/22 Amazon
4004204.45.0.0/16 Amazon
4005204.236.128.0/18 Amazon
4006204.236.192.0/18 Amazon
4007204.246.160.0/22 Amazon
4008204.246.164.0/22 Amazon
4009204.246.168.0/22 Amazon
4010204.246.172.0/24 Amazon
4011204.246.173.0/24 Amazon
4012204.246.174.0/23 Amazon
4013204.246.176.0/20 Amazon
4014205.251.192.0/21 Amazon
4015205.251.200.0/21 Amazon
4016205.251.208.0/20 Amazon
4017205.251.224.0/22 Amazon
4018205.251.228.0/22 Amazon
4019205.251.232.0/22 Amazon
4020205.251.236.0/22 Amazon
4021205.251.240.0/22 Amazon
4022205.251.244.0/23 Amazon
4023205.251.246.0/24 Amazon
4024205.251.247.0/24 Amazon
4025205.251.248.0/24 Amazon
4026205.251.249.0/24 Amazon
4027205.251.250.0/23 Amazon
4028205.251.252.0/23 Amazon
4029205.251.254.0/24 Amazon
4030207.171.160.0/20 Amazon
4031207.171.176.0/20 Amazon
4032208.86.88.0/23 Amazon
4033208.86.90.0/23 Amazon
4034208.110.48.0/20 Amazon
4035209.54.176.0/21 Amazon
4036209.54.184.0/21 Amazon
4037216.137.32.0/19 Amazon
4038216.182.224.0/21 Amazon
4039216.182.232.0/22 Amazon
4040216.182.236.0/23 Amazon
4041216.182.238.0/23 Amazon
4042223.71.11.0/27 Amazon
4043223.71.71.96/27 Amazon
4044223.71.71.128/25 Amazon
diff --git a/src/fnettrace/tail.c b/src/fnettrace/tail.c
new file mode 100644
index 000000000..a910788d6
--- /dev/null
+++ b/src/fnettrace/tail.c
@@ -0,0 +1,63 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "fnettrace.h"
21
22void tail(const char *logfile) {
23 assert(logfile);
24
25 // wait for no more than 5 seconds for the logfile to appear in the filesystem
26 int cnt = 5;
27 while (access(logfile, R_OK) && cnt > 0)
28 cnt--;
29 if (cnt == 0)
30 exit(1);
31
32 off_t last_size = 0;
33
34 while (1) {
35 int fd = open(logfile, O_RDONLY);
36 if (fd == -1)
37 return;
38
39 off_t size = lseek(fd, 0, SEEK_END);
40 if (size < 0) {
41 close(fd);
42 return;
43 }
44
45 char *content = NULL;
46 int mmapped = 0;
47 if (size && size != last_size) {
48 content = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
49 close(fd);
50 if (content != MAP_FAILED)
51 mmapped = 1;
52 }
53
54 if (mmapped) {
55 printf("%.*s", (int) (size - last_size), content + last_size);
56 fflush(0);
57 munmap(content, size);
58 last_size = size;
59 }
60
61 sleep(1);
62 }
63}
diff --git a/src/fsec-optimize/fsec_optimize.h b/src/fsec-optimize/fsec_optimize.h
index fc9dd7db8..2a77f69aa 100644
--- a/src/fsec-optimize/fsec_optimize.h
+++ b/src/fsec-optimize/fsec_optimize.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fsec-optimize/main.c b/src/fsec-optimize/main.c
index 84bf2d4f9..ec3420e16 100644
--- a/src/fsec-optimize/main.c
+++ b/src/fsec-optimize/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fsec-optimize/optimizer.c b/src/fsec-optimize/optimizer.c
index 4c02de59d..20333a8a8 100644
--- a/src/fsec-optimize/optimizer.c
+++ b/src/fsec-optimize/optimizer.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fsec-print/fsec_print.h b/src/fsec-print/fsec_print.h
index 75a82c11a..a754e2295 100644
--- a/src/fsec-print/fsec_print.h
+++ b/src/fsec-print/fsec_print.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fsec-print/main.c b/src/fsec-print/main.c
index 5bca93d50..039377999 100644
--- a/src/fsec-print/main.c
+++ b/src/fsec-print/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fsec-print/print.c b/src/fsec-print/print.c
index 143a7a53e..f6af20f04 100644
--- a/src/fsec-print/print.c
+++ b/src/fsec-print/print.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h
index 97eac9ed8..65337da2a 100644
--- a/src/fseccomp/fseccomp.h
+++ b/src/fseccomp/fseccomp.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c
index 326c29a44..48665ab71 100644
--- a/src/fseccomp/main.c
+++ b/src/fseccomp/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/protocol.c b/src/fseccomp/protocol.c
index 48dda61dd..25742c173 100644
--- a/src/fseccomp/protocol.c
+++ b/src/fseccomp/protocol.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c
index 99e671799..49b789755 100644
--- a/src/fseccomp/seccomp.c
+++ b/src/fseccomp/seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c
index 846c7f335..ee18ca74f 100644
--- a/src/fseccomp/seccomp_file.c
+++ b/src/fseccomp/seccomp_file.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c
index 540892026..d4ccd96b2 100644
--- a/src/fseccomp/seccomp_secondary.c
+++ b/src/fseccomp/seccomp_secondary.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/fshaper/fshaper.sh b/src/fshaper/fshaper.sh
index f9a6c4f06..a8379612d 100755
--- a/src/fshaper/fshaper.sh
+++ b/src/fshaper/fshaper.sh
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# This file is part of Firejail project 2# This file is part of Firejail project
3# Copyright (C) 2014-2021 Firejail Authors 3# Copyright (C) 2014-2022 Firejail Authors
4# License GPL v2 4# License GPL v2
5 5
6TCFILE="" 6TCFILE=""
diff --git a/src/ftee/ftee.h b/src/ftee/ftee.h
index a556efb75..458308a4c 100644
--- a/src/ftee/ftee.h
+++ b/src/ftee/ftee.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/ftee/main.c b/src/ftee/main.c
index 4d447f2c4..d408566fa 100644
--- a/src/ftee/main.c
+++ b/src/ftee/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/common.h b/src/include/common.h
index 5bcbaad88..c9640435a 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -73,6 +73,25 @@ static inline int atoip(const char *str, uint32_t *ip) {
73 return 0; 73 return 0;
74} 74}
75 75
76// read an IPv4 address in CIDR format, for example 192.168.1.0/24
77static inline int atocidr(const char *str, uint32_t *ip, uint32_t *mask) {
78 unsigned a, b, c, d, e;
79
80 // extract ip
81 int rv = sscanf(str, "%u.%u.%u.%u/%u", &a, &b, &c, &d, &e);
82 if (rv != 5 || a > 255 || b > 255 || c > 255 || d > 255 || e > 32)
83 return 1;
84 *ip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d;
85
86 // extract mask
87 uint32_t tmp;
88 unsigned i;
89 for (i = 0, *mask = 0, tmp = 0x80000000; i < e; i++, tmp >>= 1) {
90 *mask |= tmp;
91 }
92 return 0;
93}
94
76// verify an ip address is in the network range given by ifip and mask 95// verify an ip address is in the network range given by ifip and mask
77static inline char *in_netrange(uint32_t ip, uint32_t ifip, uint32_t ifmask) { 96static inline char *in_netrange(uint32_t ip, uint32_t ifip, uint32_t ifmask) {
78 if ((ip & ifmask) != (ifip & ifmask)) 97 if ((ip & ifmask) != (ifip & ifmask))
@@ -121,6 +140,12 @@ char *pid_proc_comm(const pid_t pid);
121char *pid_proc_cmdline(const pid_t pid); 140char *pid_proc_cmdline(const pid_t pid);
122int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid); 141int pid_proc_cmdline_x11_xpra_xephyr(const pid_t pid);
123int pid_hidepid(void); 142int pid_hidepid(void);
143char *do_replace_cntrl_chars(char *str, char c);
144char *replace_cntrl_chars(const char *str, char c);
145int has_cntrl_chars(const char *str);
146void reject_cntrl_chars(const char *fname);
147void reject_meta_chars(const char *fname, int globbing);
124void warn_dumpable(void); 148void warn_dumpable(void);
125const char *gnu_basename(const char *path); 149const char *gnu_basename(const char *path);
150int *str_to_int_array(const char *str, size_t *sz);
126#endif 151#endif
diff --git a/src/include/euid_common.h b/src/include/euid_common.h
index 8d8dd95f6..f40cbb9de 100644
--- a/src/include/euid_common.h
+++ b/src/include/euid_common.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/firejail_user.h b/src/include/firejail_user.h
index cf17fa0cf..6cf895db8 100644
--- a/src/include/firejail_user.h
+++ b/src/include/firejail_user.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/gcov_wrapper.h b/src/include/gcov_wrapper.h
new file mode 100644
index 000000000..144181ca0
--- /dev/null
+++ b/src/include/gcov_wrapper.h
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#ifndef GCOV_WRAPPER_H
22#define GCOV_WRAPPER_H
23
24#ifdef HAS_GCOV
25#include <gcov.h>
26
27/*
28 * __gcov_flush was removed on gcc 11.1.0 (as it's no longer needed), but it
29 * appears to be the safe/"correct" way to do things on previous versions (as
30 * it ensured proper locking, which is now done elsewhere). Thus, keep using
31 * it in the code and ensure that it exists, in order to support gcc <11.1.0
32 * and gcc >=11.1.0, respectively.
33 */
34#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1)
35static void __gcov_flush(void) {
36 __gcov_dump();
37 __gcov_reset();
38}
39#endif
40#else
41#define __gcov_dump() ((void)0)
42#define __gcov_reset() ((void)0)
43#define __gcov_flush() ((void)0)
44#endif /* HAS_GCOV */
45
46#endif /* GCOV_WRAPPER_H */
diff --git a/src/include/ldd_utils.h b/src/include/ldd_utils.h
index ffd6e189f..e9dac1171 100644
--- a/src/include/ldd_utils.h
+++ b/src/include/ldd_utils.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/pid.h b/src/include/pid.h
index 17e51f660..7e235b713 100644
--- a/src/include/pid.h
+++ b/src/include/pid.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/rundefs.h b/src/include/rundefs.h
index d14f6782f..4ba3e27f4 100644
--- a/src/include/rundefs.h
+++ b/src/include/rundefs.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -79,24 +79,8 @@
79#define PATH_SECCOMP_MDWX_32 LIBDIR "/firejail/seccomp.mdwx.32" 79#define PATH_SECCOMP_MDWX_32 LIBDIR "/firejail/seccomp.mdwx.32"
80#define PATH_SECCOMP_BLOCK_SECONDARY LIBDIR "/firejail/seccomp.block_secondary" // secondary arch blocking filter built during make 80#define PATH_SECCOMP_BLOCK_SECONDARY LIBDIR "/firejail/seccomp.block_secondary" // secondary arch blocking filter built during make
81 81
82
83#define RUN_DEV_DIR RUN_MNT_DIR "/dev" 82#define RUN_DEV_DIR RUN_MNT_DIR "/dev"
84#define RUN_DEVLOG_FILE RUN_MNT_DIR "/devlog" 83#define RUN_DEVLOG_FILE RUN_MNT_DIR "/devlog"
85
86#define RUN_WHITELIST_X11_DIR RUN_MNT_DIR "/orig-x11"
87#define RUN_WHITELIST_HOME_USER_DIR RUN_MNT_DIR "/orig-home-user" // home directory whitelisting
88#define RUN_WHITELIST_RUN_USER_DIR RUN_MNT_DIR "/orig-run-user" // run directory whitelisting
89#define RUN_WHITELIST_TMP_DIR RUN_MNT_DIR "/orig-tmp"
90#define RUN_WHITELIST_MEDIA_DIR RUN_MNT_DIR "/orig-media"
91#define RUN_WHITELIST_MNT_DIR RUN_MNT_DIR "/orig-mnt"
92#define RUN_WHITELIST_VAR_DIR RUN_MNT_DIR "/orig-var"
93#define RUN_WHITELIST_DEV_DIR RUN_MNT_DIR "/orig-dev"
94#define RUN_WHITELIST_OPT_DIR RUN_MNT_DIR "/orig-opt"
95#define RUN_WHITELIST_SRV_DIR RUN_MNT_DIR "/orig-srv"
96#define RUN_WHITELIST_ETC_DIR RUN_MNT_DIR "/orig-etc"
97#define RUN_WHITELIST_SHARE_DIR RUN_MNT_DIR "/orig-share"
98#define RUN_WHITELIST_MODULE_DIR RUN_MNT_DIR "/orig-module"
99
100#define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" // private options 84#define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" // private options
101#define RUN_XAUTH_FILE RUN_MNT_DIR "/xauth" // x11=xorg 85#define RUN_XAUTH_FILE RUN_MNT_DIR "/xauth" // x11=xorg
102#define RUN_XAUTHORITY_SEC_DIR RUN_MNT_DIR "/.sec.Xauthority" // x11=xorg 86#define RUN_XAUTHORITY_SEC_DIR RUN_MNT_DIR "/.sec.Xauthority" // x11=xorg
diff --git a/src/include/seccomp.h b/src/include/seccomp.h
index 43bb73a04..9dbe25bfa 100644
--- a/src/include/seccomp.h
+++ b/src/include/seccomp.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/include/syscall.h b/src/include/syscall.h
index 015dd01b9..68be16a04 100644
--- a/src/include/syscall.h
+++ b/src/include/syscall.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/jailtest/Makefile.in b/src/jailcheck/Makefile.in
index 6306d24ec..d218c1f90 100644
--- a/src/jailtest/Makefile.in
+++ b/src/jailcheck/Makefile.in
@@ -1,16 +1,16 @@
1.PHONY: all 1.PHONY: all
2all: jailtest 2all: jailcheck
3 3
4include ../common.mk 4include ../common.mk
5 5
6%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h 6%.o : %.c $(H_FILE_LIST) ../include/common.h ../include/pid.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8 8
9jailtest: $(OBJS) 9jailcheck: $(OBJS)
10 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS) 10 $(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS) $(EXTRA_LDFLAGS)
11 11
12.PHONY: clean 12.PHONY: clean
13clean:; rm -fr *.o jailtest *.gcov *.gcda *.gcno *.plist 13clean:; rm -fr *.o jailcheck *.gcov *.gcda *.gcno *.plist
14 14
15.PHONY: distclean 15.PHONY: distclean
16distclean: clean 16distclean: clean
diff --git a/src/jailtest/access.c b/src/jailcheck/access.c
index 4e737dc7a..3e99b0b52 100644
--- a/src/jailtest/access.c
+++ b/src/jailcheck/access.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include <dirent.h> 21#include <dirent.h>
22#include <sys/wait.h> 22#include <sys/wait.h>
23 23
@@ -36,7 +36,7 @@ void access_setup(const char *directory) {
36 assert(user_home_dir); 36 assert(user_home_dir);
37 37
38 if (files_cnt >= MAX_TEST_FILES) { 38 if (files_cnt >= MAX_TEST_FILES) {
39 fprintf(stderr, "Error: maximum number of test directories exceded\n"); 39 fprintf(stderr, "Error: maximum number of test directories exceeded\n");
40 exit(1); 40 exit(1);
41 } 41 }
42 42
@@ -74,7 +74,7 @@ void access_setup(const char *directory) {
74 74
75 // create a test file 75 // create a test file
76 char *test_file; 76 char *test_file;
77 if (asprintf(&test_file, "%s/jailtest-access-%d", path, getpid()) == -1) 77 if (asprintf(&test_file, "%s/jailcheck-access-%d", path, getpid()) == -1)
78 errExit("asprintf"); 78 errExit("asprintf");
79 79
80 FILE *fp = fopen(test_file, "w"); 80 FILE *fp = fopen(test_file, "w");
diff --git a/src/jailtest/apparmor.c b/src/jailcheck/apparmor.c
index 9ddfea3de..521ce047e 100644
--- a/src/jailtest/apparmor.c
+++ b/src/jailcheck/apparmor.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21 21
22#ifdef HAVE_APPARMOR 22#ifdef HAVE_APPARMOR
23#include <sys/apparmor.h> 23#include <sys/apparmor.h>
diff --git a/src/jailtest/jailtest.h b/src/jailcheck/jailcheck.h
index 0c4883061..2d25ee8ce 100644
--- a/src/jailtest/jailtest.h
+++ b/src/jailcheck/jailcheck.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,8 +17,8 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#ifndef JAILTEST_H 20#ifndef JAILCHECK_H
21#define JAILTEST_H 21#define JAILCHECK_H
22 22
23#include "../include/common.h" 23#include "../include/common.h"
24 24
@@ -53,10 +53,12 @@ void apparmor_test(pid_t pid);
53// seccomp.c 53// seccomp.c
54void seccomp_test(pid_t pid); 54void seccomp_test(pid_t pid);
55 55
56// network.c
57void network_test(void);
56// utils.c 58// utils.c
57char *get_sudo_user(void); 59char *get_sudo_user(void);
58char *get_homedir(const char *user, uid_t *uid, gid_t *gid); 60char *get_homedir(const char *user, uid_t *uid, gid_t *gid);
59int find_child(pid_t pid); 61int find_child(pid_t pid);
60pid_t switch_to_child(pid_t pid); 62pid_t switch_to_child(pid_t pid);
61 63
62#endif \ No newline at end of file 64#endif
diff --git a/src/jailtest/main.c b/src/jailcheck/main.c
index 3369dca39..04fc3a6af 100644
--- a/src/jailtest/main.c
+++ b/src/jailcheck/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include "../include/firejail_user.h" 21#include "../include/firejail_user.h"
22#include "../include/pid.h" 22#include "../include/pid.h"
23#include <sys/wait.h> 23#include <sys/wait.h>
@@ -30,7 +30,7 @@ char *user_run_dir = NULL;
30int arg_debug = 0; 30int arg_debug = 0;
31 31
32static char *usage_str = 32static char *usage_str =
33 "Usage: jailtest [options] directory [directory]\n\n" 33 "Usage: jailcheck [options] directory [directory]\n\n"
34 "Options:\n" 34 "Options:\n"
35 " --debug - print debug messages.\n" 35 " --debug - print debug messages.\n"
36 " --help, -? - this help screen.\n" 36 " --help, -? - this help screen.\n"
@@ -157,6 +157,7 @@ int main(int argc, char **argv) {
157 seccomp_test(pid); 157 seccomp_test(pid);
158 fflush(0); 158 fflush(0);
159 159
160 // filesystem tests
160 pid_t child = fork(); 161 pid_t child = fork();
161 if (child == -1) 162 if (child == -1)
162 errExit("fork"); 163 errExit("fork");
@@ -185,6 +186,28 @@ int main(int argc, char **argv) {
185 } 186 }
186 int status; 187 int status;
187 wait(&status); 188 wait(&status);
189
190 // network test
191 child = fork();
192 if (child == -1)
193 errExit("fork");
194 if (child == 0) {
195 int rv = join_namespace(pid, "net");
196 if (rv == 0)
197 network_test();
198 else {
199 printf(" Error: I cannot join the process network stack\n");
200 exit(1);
201 }
202
203 // drop privileges in order not to trigger cleanup()
204 if (setgid(user_gid) != 0)
205 errExit("setgid");
206 if (setuid(user_uid) != 0)
207 errExit("setuid");
208 return 0;
209 }
210 wait(&status);
188 } 211 }
189 } 212 }
190 213
diff --git a/src/jailcheck/network.c b/src/jailcheck/network.c
new file mode 100644
index 000000000..8f70c6ff0
--- /dev/null
+++ b/src/jailcheck/network.c
@@ -0,0 +1,57 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "jailcheck.h"
21#include <netdb.h>
22#include <arpa/inet.h>
23#include <ifaddrs.h>
24#include <net/if.h>
25#include <linux/connector.h>
26#include <linux/netlink.h>
27#include <linux/if_link.h>
28#include <linux/sockios.h>
29#include <sys/ioctl.h>
30
31
32void network_test(void) {
33 // I am root running in a network namespace
34 struct ifaddrs *ifaddr, *ifa;
35 int found = 0;
36
37 // walk through the linked list
38 if (getifaddrs(&ifaddr) == -1)
39 errExit("getifaddrs");
40 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
41 if (strcmp(ifa->ifa_name, "lo") == 0)
42 continue;
43
44 found = 1;
45 break;
46 }
47
48 freeifaddrs(ifaddr);
49
50 if (found)
51 printf(" Networking: enabled\n");
52 else
53 printf(" Networking: disabled\n");
54}
55
56
57
diff --git a/src/jailtest/noexec.c b/src/jailcheck/noexec.c
index 4347b7eef..4cf5dabde 100644
--- a/src/jailtest/noexec.c
+++ b/src/jailcheck/noexec.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include <sys/wait.h> 21#include <sys/wait.h>
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <fcntl.h> 23#include <fcntl.h>
@@ -67,7 +67,7 @@ void noexec_test(const char *path) {
67 return; 67 return;
68 68
69 char *fname; 69 char *fname;
70 if (asprintf(&fname, "%s/jailtest-noexec-%d", path, getpid()) == -1) 70 if (asprintf(&fname, "%s/jailcheck-noexec-%d", path, getpid()) == -1)
71 errExit("asprintf"); 71 errExit("asprintf");
72 72
73 pid_t child = fork(); 73 pid_t child = fork();
@@ -110,4 +110,4 @@ void noexec_test(const char *path) {
110 wait(&status); 110 wait(&status);
111 int rv = unlink(fname); 111 int rv = unlink(fname);
112 (void) rv; 112 (void) rv;
113} \ No newline at end of file 113}
diff --git a/src/jailtest/seccomp.c b/src/jailcheck/seccomp.c
index 2cecb4b4d..ac8064f0b 100644
--- a/src/jailtest/seccomp.c
+++ b/src/jailcheck/seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#define MAXBUF 4096 21#define MAXBUF 4096
22 22
23void seccomp_test(pid_t pid) { 23void seccomp_test(pid_t pid) {
diff --git a/src/jailtest/sysfiles.c b/src/jailcheck/sysfiles.c
index 7e4709453..0df95d496 100644
--- a/src/jailtest/sysfiles.c
+++ b/src/jailcheck/sysfiles.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include <dirent.h> 21#include <dirent.h>
22#include <sys/wait.h> 22#include <sys/wait.h>
23 23
@@ -34,7 +34,7 @@ void sysfiles_setup(const char *file) {
34 assert(file); 34 assert(file);
35 35
36 if (files_cnt >= MAX_TEST_FILES) { 36 if (files_cnt >= MAX_TEST_FILES) {
37 fprintf(stderr, "Error: maximum number of system test files exceded\n"); 37 fprintf(stderr, "Error: maximum number of system test files exceeded\n");
38 exit(1); 38 exit(1);
39 } 39 }
40 40
diff --git a/src/jailtest/utils.c b/src/jailcheck/utils.c
index 41c21b753..65431e2e1 100644
--- a/src/jailtest/utils.c
+++ b/src/jailcheck/utils.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include "../include/pid.h" 21#include "../include/pid.h"
22#include <errno.h> 22#include <errno.h>
23#include <pwd.h> 23#include <pwd.h>
diff --git a/src/jailtest/virtual.c b/src/jailcheck/virtual.c
index fcdcf9720..93172d65c 100644
--- a/src/jailtest/virtual.c
+++ b/src/jailcheck/virtual.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,7 +17,7 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include "jailtest.h" 20#include "jailcheck.h"
21#include <dirent.h> 21#include <dirent.h>
22#include <sys/wait.h> 22#include <sys/wait.h>
23 23
@@ -43,7 +43,7 @@ void virtual_setup(const char *directory) {
43 43
44 // create a test file 44 // create a test file
45 char *test_file; 45 char *test_file;
46 if (asprintf(&test_file, "%s/jailtest-private-%d", directory, getpid()) == -1) 46 if (asprintf(&test_file, "%s/jailcheck-private-%d", directory, getpid()) == -1)
47 errExit("asprintf"); 47 errExit("asprintf");
48 48
49 FILE *fp = fopen(test_file, "w"); 49 FILE *fp = fopen(test_file, "w");
diff --git a/src/lib/common.c b/src/lib/common.c
index f1bd7a6fe..8e84fab26 100644
--- a/src/lib/common.c
+++ b/src/lib/common.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -31,6 +31,7 @@
31#include <dirent.h> 31#include <dirent.h>
32#include <string.h> 32#include <string.h>
33#include <time.h> 33#include <time.h>
34#include <limits.h>
34#include "../include/common.h" 35#include "../include/common.h"
35#define BUFLEN 4096 36#define BUFLEN 4096
36 37
@@ -320,6 +321,115 @@ const char *gnu_basename(const char *path) {
320 return last_slash+1; 321 return last_slash+1;
321} 322}
322 323
324char *do_replace_cntrl_chars(char *str, char c) {
325 if (str) {
326 size_t i;
327 for (i = 0; str[i]; i++) {
328 if (iscntrl((unsigned char) str[i]))
329 str[i] = c;
330 }
331 }
332 return str;
333}
334
335char *replace_cntrl_chars(const char *str, char c) {
336 assert(str);
337
338 char *rv = strdup(str);
339 if (!rv)
340 errExit("strdup");
341
342 do_replace_cntrl_chars(rv, c);
343 return rv;
344}
345
346int has_cntrl_chars(const char *str) {
347 assert(str);
348
349 size_t i;
350 for (i = 0; str[i]; i++) {
351 if (iscntrl((unsigned char) str[i]))
352 return 1;
353 }
354 return 0;
355}
356
357void reject_cntrl_chars(const char *fname) {
358 assert(fname);
359
360 if (has_cntrl_chars(fname)) {
361 char *fname_print = replace_cntrl_chars(fname, '?');
362
363 fprintf(stderr, "Error: \"%s\" is an invalid filename: no control characters are allowed\n", fname_print);
364 exit(1);
365 }
366}
367
368void reject_meta_chars(const char *fname, int globbing) {
369 assert(fname);
370
371 reject_cntrl_chars(fname);
372
373 const char *reject = "\\&!?\"<>%^{};,*[]";
374 if (globbing)
375 reject = "\\&!\"<>%^{};,"; // file globbing ('*?[]') is allowed
376
377 const char *c = strpbrk(fname, reject);
378 if (c) {
379 fprintf(stderr, "Error: \"%s\" is an invalid filename: rejected character: \"%c\"\n", fname, *c);
380 exit(1);
381 }
382}
383
384// takes string with comma separated int values, returns int array
385int *str_to_int_array(const char *str, size_t *sz) {
386 assert(str && sz);
387
388 size_t curr_sz = 0;
389 size_t arr_sz = 16;
390 int *rv = malloc(arr_sz * sizeof(int));
391 if (!rv)
392 errExit("malloc");
393
394 char *dup = strdup(str);
395 if (!dup)
396 errExit("strdup");
397 char *tok = strtok(dup, ",");
398 if (!tok) {
399 free(dup);
400 free(rv);
401 goto errout;
402 }
403
404 while (tok) {
405 char *end;
406 long val = strtol(tok, &end, 10);
407 if (end == tok || *end != '\0' || val < INT_MIN || val > INT_MAX) {
408 free(dup);
409 free(rv);
410 goto errout;
411 }
412
413 if (curr_sz == arr_sz) {
414 arr_sz *= 2;
415 rv = realloc(rv, arr_sz * sizeof(int));
416 if (!rv)
417 errExit("realloc");
418 }
419 rv[curr_sz++] = val;
420
421 tok = strtok(NULL, ",");
422 }
423 free(dup);
424
425 *sz = curr_sz;
426 return rv;
427
428errout:
429 *sz = 0;
430 return NULL;
431}
432
323//************************** 433//**************************
324// time trace based on getticks function 434// time trace based on getticks function
325//************************** 435//**************************
diff --git a/src/lib/errno.c b/src/lib/errno.c
index 9edb44c22..b666c5646 100644
--- a/src/lib/errno.c
+++ b/src/lib/errno.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/lib/firejail_user.c b/src/lib/firejail_user.c
index d6a3c71ab..bf338ee43 100644
--- a/src/lib/firejail_user.c
+++ b/src/lib/firejail_user.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/lib/ldd_utils.c b/src/lib/ldd_utils.c
index cd60d74e4..bc4f7cf9c 100644
--- a/src/lib/ldd_utils.c
+++ b/src/lib/ldd_utils.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -50,7 +50,7 @@ int is_lib_64(const char *exe) {
50 unsigned char buf[EI_NIDENT]; 50 unsigned char buf[EI_NIDENT];
51 ssize_t len = 0; 51 ssize_t len = 0;
52 while (len < EI_NIDENT) { 52 while (len < EI_NIDENT) {
53 ssize_t sz = read(fd, buf, EI_NIDENT); 53 ssize_t sz = read(fd, buf + len, EI_NIDENT - len);
54 if (sz <= 0) 54 if (sz <= 0)
55 goto doexit; 55 goto doexit;
56 len += sz; 56 len += sz;
diff --git a/src/lib/pid.c b/src/lib/pid.c
index ca62aaa42..ad6403f65 100644
--- a/src/lib/pid.c
+++ b/src/lib/pid.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/lib/syscall.c b/src/lib/syscall.c
index b3131ac17..a17f6423a 100644
--- a/src/lib/syscall.c
+++ b/src/lib/syscall.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -253,9 +253,6 @@ static const SyscallGroupList sysgroups[] = {
253#ifdef SYS_fanotify_init 253#ifdef SYS_fanotify_init
254 "fanotify_init," 254 "fanotify_init,"
255#endif 255#endif
256#ifdef SYS_kcmp
257 "kcmp,"
258#endif
259#ifdef SYS_add_key 256#ifdef SYS_add_key
260 "add_key," 257 "add_key,"
261#endif 258#endif
@@ -1681,14 +1678,14 @@ void syscalls_in_list(const char *list, const char *slist, int fd, char **prelis
1681 sl.postlist = NULL; 1678 sl.postlist = NULL;
1682 syscall_check_list(list, syscall_in_list, 0, 0, &sl, native); 1679 syscall_check_list(list, syscall_in_list, 0, 0, &sl, native);
1683 if (!arg_quiet) { 1680 if (!arg_quiet) {
1684 printf("Seccomp list in: %s,", list); 1681 fprintf(stderr, "Seccomp list in: %s,", list);
1685 if (sl.slist) 1682 if (sl.slist)
1686 printf(" check list: %s,", sl.slist); 1683 fprintf(stderr, " check list: %s,", sl.slist);
1687 if (sl.prelist) 1684 if (sl.prelist)
1688 printf(" prelist: %s,", sl.prelist); 1685 fprintf(stderr, " prelist: %s,", sl.prelist);
1689 if (sl.postlist) 1686 if (sl.postlist)
1690 printf(" postlist: %s", sl.postlist); 1687 fprintf(stderr, " postlist: %s", sl.postlist);
1691 printf("\n"); 1688 fprintf(stderr, "\n");
1692 } 1689 }
1693 *prelist = sl.prelist; 1690 *prelist = sl.prelist;
1694 *postlist = sl.postlist; 1691 *postlist = sl.postlist;
diff --git a/src/libpostexecseccomp/libpostexecseccomp.c b/src/libpostexecseccomp/libpostexecseccomp.c
index 1d1eb283b..e2339547e 100644
--- a/src/libpostexecseccomp/libpostexecseccomp.c
+++ b/src/libpostexecseccomp/libpostexecseccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/libtrace/libtrace.c b/src/libtrace/libtrace.c
index d88512b0a..c0832cbde 100644
--- a/src/libtrace/libtrace.c
+++ b/src/libtrace/libtrace.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -18,12 +18,12 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#define _GNU_SOURCE 20#define _GNU_SOURCE
21#include <errno.h>
21#include <stdio.h> 22#include <stdio.h>
22#include <stdlib.h> 23#include <stdlib.h>
23#include <string.h> 24#include <string.h>
24#include <dlfcn.h> 25#include <dlfcn.h>
25#include <sys/types.h> 26#include <sys/types.h>
26#include <limits.h>
27#include <unistd.h> 27#include <unistd.h>
28#include <sys/socket.h> 28#include <sys/socket.h>
29#include <netinet/in.h> 29#include <netinet/in.h>
@@ -706,10 +706,14 @@ __attribute__((constructor))
706static void log_exec(int argc, char** argv) { 706static void log_exec(int argc, char** argv) {
707 (void) argc; 707 (void) argc;
708 (void) argv; 708 (void) argv;
709 static char buf[PATH_MAX + 1]; 709 char *buf = realpath("/proc/self/exe", NULL);
710 int rv = readlink("/proc/self/exe", buf, PATH_MAX); 710 if (buf == NULL) {
711 if (rv != -1) { 711 if (errno == ENOMEM) {
712 buf[rv] = '\0'; // readlink does not add a '\0' at the end 712 tprintf(ftty, "realpath: %s\n", strerror(errno));
713 exit(1);
714 }
715 } else {
713 tprintf(ftty, "%u:%s:exec %s:0\n", mypid, myname, buf); 716 tprintf(ftty, "%u:%s:exec %s:0\n", mypid, myname, buf);
717 free(buf);
714 } 718 }
715} 719}
diff --git a/src/libtracelog/libtracelog.c b/src/libtracelog/libtracelog.c
index b946cc889..760ac7612 100644
--- a/src/libtracelog/libtracelog.c
+++ b/src/libtracelog/libtracelog.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/man/Makefile.in b/src/man/Makefile.in
index 3711d5cec..fbd2d795e 100644
--- a/src/man/Makefile.in
+++ b/src/man/Makefile.in
@@ -1,5 +1,5 @@
1.PHONY: all 1.PHONY: all
2all: firecfg.man firejail.man firejail-login.man firejail-users.man firejail-profile.man firemon.man jailtest.man 2all: firecfg.man firejail.man firejail-login.man firejail-users.man firejail-profile.man firemon.man jailcheck.man
3 3
4include ../common.mk 4include ../common.mk
5 5
diff --git a/src/man/firecfg.txt b/src/man/firecfg.txt
index dbb9397c6..189e9cc8d 100644
--- a/src/man/firecfg.txt
+++ b/src/man/firecfg.txt
@@ -27,7 +27,7 @@ desktop managers are supported in this moment
27To set it up, run "sudo firecfg" after installing Firejail software. 27To set it up, run "sudo firecfg" after installing Firejail software.
28The same command should also be run after 28The same command should also be run after
29installing new programs. If the program is supported by Firejail, the symbolic link in /usr/local/bin 29installing new programs. If the program is supported by Firejail, the symbolic link in /usr/local/bin
30will be created. For a full list of programs supported by default run "cat /usr/lib/firejail/firecfg.config". 30will be created. For a full list of programs supported by default run "cat /etc/firejail/firecfg.config".
31 31
32For user-driven manual integration, see \fBDESKTOP INTEGRATION\fR section in \fBman 1 firejail\fR. 32For user-driven manual integration, see \fBDESKTOP INTEGRATION\fR section in \fBman 1 firejail\fR.
33.SH DEFAULT ACTIONS 33.SH DEFAULT ACTIONS
@@ -135,4 +135,4 @@ Homepage: https://firejail.wordpress.com
135.BR firejail-profile (5), 135.BR firejail-profile (5),
136.BR firejail-login (5), 136.BR firejail-login (5),
137.BR firejail-users (5), 137.BR firejail-users (5),
138.BR jailtest (1) 138.BR jailcheck (1)
diff --git a/src/man/firejail-login.txt b/src/man/firejail-login.txt
index 1b8a4931c..05afd55b5 100644
--- a/src/man/firejail-login.txt
+++ b/src/man/firejail-login.txt
@@ -39,4 +39,4 @@ Homepage: https://firejail.wordpress.com
39.BR firecfg (1), 39.BR firecfg (1),
40.BR firejail-profile (5), 40.BR firejail-profile (5),
41.BR firejail-users (5), 41.BR firejail-users (5),
42.BR jailtest (1) 42.BR jailcheck (1)
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index 9d11add06..e962e18da 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -1,18 +1,84 @@
1.TH FIREJAIL-PROFILE 5 "MONTH YEAR" "VERSION" "firejail profiles man page" 1.TH FIREJAIL-PROFILE 5 "MONTH YEAR" "VERSION" "firejail profiles man page"
2.SH NAME 2.SH NAME
3profile \- Security profile file syntax for Firejail 3profile \- Security profile file syntax, and information about building new application profiles.
4 4
5.SH USAGE 5.SH SYNOPSIS
6
7Using a specific profile:
8.PP
9.RS
6.TP 10.TP
7firejail \-\-profile=filename.profile 11\fBfirejail \-\-profile=filename.profile
12.br
13
14.br
15Example:
16.br
17$ firejail --profile=/etc/firejail/kdenlive.profile --appimage kdenlive.appimage
18.br
19
20.br
21.TP
22\fBfirejail \-\-profile=profile_name
23.br
24
25.br
26Example:
27.br
28$ firejail --profile=kdenlive --appimage kdenlive.appimage
29.br
30
31.br
32.RE
33.PP
34
35
36
37Building a profile manually:
38.PP
39.RS
40Start with the template in /usr/share/doc/firejail/profile.template and modify it in a text editor.
41To integrate the program in your desktop environment copy the profile file in ~/.config/firejail
42directory and run "sudo firecfg".
8.RE 43.RE
9firejail \-\-profile=profile_name 44.PP
45
46Aliases and redirections:
47.PP
48.RS
49In some cases the same profile can be used for several applications.
50One such example is LibreOffice.
51Build a regular profile for the main application, and for the rest use
52/usr/share/doc/firejail/redirect_alias-profile.template.
53.RE
54.PP
55
56Running the profile builder:
57.PP
58.RS
59.TP
60\fBfirejail \-\-build=appname.profile appname
61.br
62
63.br
64Example:
65.br
66$ firejail --build=blobby.profile blobby
67.br
68
69.br
70Run the program in "firejail \-\-build" and try to exercise as many program features as possible.
71The profile is extracted and saved in the current directory. Open it in a text editor and add or remove
72sandboxing options as necessary. Test again after modifying the profile. To integrate the program
73in your desktop environment copy the profile file in ~/.config/firejail directory and run "sudo firecfg".
74.RE
75.PP
10 76
11.SH DESCRIPTION 77.SH DESCRIPTION
12Several command line options can be passed to the program using 78Several command line options can be passed to the program using
13profile files. Firejail chooses the profile file as follows: 79profile files. Firejail chooses the profile file as follows:
14 80
15\fB1.\fR If a profile file is provided by the user with \-\-profile option, the profile file is loaded. If a profile name is given, it is searched for first in the ~/.config/firejail directory and if not found then in /etc/firejail directory. Profile names do not include the .profile suffix. 81\fB1.\fR If a profile file is provided by the user with \-\-profile option, the profile file is loaded. If a profile name is given, it is searched for first in the ~/.config/firejail directory and if not found then in /etc/firejail directory. Profile names do not include the .profile suffix.
16Example: 82Example:
17.PP 83.PP
18.RS 84.RS
@@ -94,6 +160,11 @@ Example: "blacklist ~/My Virtual Machines"
94 160
95.TP 161.TP
96\fB# this is a comment 162\fB# this is a comment
163Example:
164
165# disable networking
166.br
167net none # this command creates an empty network namespace
97 168
98.TP 169.TP
99\fB?CONDITIONAL: profile line 170\fB?CONDITIONAL: profile line
@@ -103,7 +174,7 @@ Example: "?HAS_APPIMAGE: whitelist ${HOME}/special/appimage/dir"
103 174
104This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line. 175This example will load the whitelist profile line only if the \-\-appimage option has been specified on the command line.
105 176
106Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS, HAS_NOSOUND, HAS_PRIVATE and HAS_X11. The conditionals BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM 177Currently the only conditionals supported this way are HAS_APPIMAGE, HAS_NET, HAS_NODBUS, HAS_NOSOUND, HAS_PRIVATE and HAS_X11. The conditionals ALLOW_TRAY, BROWSER_DISABLE_U2F and BROWSER_ALLOW_DRM
107can be enabled or disabled globally in Firejail's configuration file. 178can be enabled or disabled globally in Firejail's configuration file.
108 179
109The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines. 180The profile line may be any profile line that you would normally use in a profile \fBexcept\fR for "quiet" and "include" lines.
@@ -205,6 +276,10 @@ Mount-bind file1 on top of file2. This option is only available when running as
205\fBdisable-mnt 276\fBdisable-mnt
206Disable /mnt, /media, /run/mount and /run/media access. 277Disable /mnt, /media, /run/mount and /run/media access.
207.TP 278.TP
279\fBkeep-config-pulse
280Disable automatic ~/.config/pulse init, for complex setups such as remote
281pulse servers or non-standard socket paths.
282.TP
208\fBkeep-dev-shm 283\fBkeep-dev-shm
209/dev/shm directory is untouched (even with private-dev). 284/dev/shm directory is untouched (even with private-dev).
210.TP 285.TP
@@ -249,16 +324,16 @@ Remount the file or the directory noexec, nodev and nosuid.
249#ifdef HAVE_OVERLAYFS 324#ifdef HAVE_OVERLAYFS
250.TP 325.TP
251\fBoverlay 326\fBoverlay
252Mount a filesystem overlay on top of the current filesystem. 327Mount a filesystem overlay on top of the current filesystem.
253The overlay is stored in $HOME/.firejail/<PID> directory. 328The overlay is stored in $HOME/.firejail/<PID> directory.
254.TP 329.TP
255\fBoverlay-named name 330\fBoverlay-named name
256Mount a filesystem overlay on top of the current filesystem. 331Mount a filesystem overlay on top of the current filesystem.
257The overlay is stored in $HOME/.firejail/name directory. 332The overlay is stored in $HOME/.firejail/name directory.
258.TP 333.TP
259\fBoverlay-tmpfs 334\fBoverlay-tmpfs
260Mount a filesystem overlay on top of the current filesystem. 335Mount a filesystem overlay on top of the current filesystem.
261All filesystem modifications are discarded when the sandbox is closed. 336All filesystem modifications are discarded when the sandbox is closed.
262#endif 337#endif
263.TP 338.TP
264\fBprivate 339\fBprivate
@@ -274,6 +349,7 @@ Build a new /bin in a temporary filesystem, and copy the programs in the list.
274The files in the list must be expressed as relative to the /bin, 349The files in the list must be expressed as relative to the /bin,
275/sbin, /usr/bin, /usr/sbin, or /usr/local/bin directories. 350/sbin, /usr/bin, /usr/sbin, or /usr/local/bin directories.
276The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin. 351The same directory is also bind-mounted over /sbin, /usr/bin and /usr/sbin.
352Multiple private-bin commands are allowed and they accumulate.
277.TP 353.TP
278\fBprivate-cache 354\fBprivate-cache
279Mount an empty temporary filesystem on top of the .cache directory in user home. All 355Mount an empty temporary filesystem on top of the .cache directory in user home. All
@@ -283,7 +359,7 @@ modifications are discarded when the sandbox is closed.
283Set working directory inside jail to the home directory, and failing that, the root directory. 359Set working directory inside jail to the home directory, and failing that, the root directory.
284.TP 360.TP
285\fBprivate-cwd directory 361\fBprivate-cwd directory
286Set working directory inside the jail. 362Set working directory inside the jail. Full directory path is required. Symbolic links are not allowed.
287.TP 363.TP
288\fBprivate-dev 364\fBprivate-dev
289Create a new /dev directory. Only disc, dri, dvb, hidraw, null, full, zero, tty, pts, ptmx, 365Create a new /dev directory. Only disc, dri, dvb, hidraw, null, full, zero, tty, pts, ptmx,
@@ -299,6 +375,7 @@ the /etc directory, and must not contain the / character
299(e.g., /etc/foo must be expressed as foo, but /etc/foo/bar -- 375(e.g., /etc/foo must be expressed as foo, but /etc/foo/bar --
300expressed as foo/bar -- is disallowed). 376expressed as foo/bar -- is disallowed).
301All modifications are discarded when the sandbox is closed. 377All modifications are discarded when the sandbox is closed.
378Multiple private-etc commands are allowed and they accumulate.
302#ifdef HAVE_PRIVATE_HOME 379#ifdef HAVE_PRIVATE_HOME
303.TP 380.TP
304\fBprivate-home file,directory 381\fBprivate-home file,directory
@@ -345,7 +422,7 @@ Make directory or file read-only.
345Make directory or file read-write. 422Make directory or file read-write.
346.TP 423.TP
347\fBtmpfs directory 424\fBtmpfs directory
348Mount an empty tmpfs filesystem on top of directory. This option is available only when running the sandbox as root. 425Mount an empty tmpfs filesystem on top of directory. Directories outside user home or not owned by the user are not allowed. Sandboxes running as root are exempt from these restrictions.
349.TP 426.TP
350\fBtracelog 427\fBtracelog
351Blacklist violations logged to syslog. 428Blacklist violations logged to syslog.
@@ -353,13 +430,19 @@ Blacklist violations logged to syslog.
353\fBwhitelist file_or_directory 430\fBwhitelist file_or_directory
354Whitelist directory or file. A temporary file system is mounted on the top directory, and the 431Whitelist directory or file. A temporary file system is mounted on the top directory, and the
355whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, 432whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent,
356everything else is discarded when the sandbox is closed. The top directory could be 433everything else is discarded when the sandbox is closed. The top directory can be
357user home, /dev, /etc, /media, /mnt, /opt, /srv, /sys/module, /usr/share, /var, and /tmp. 434all directories in / (except /proc and /sys), /sys/module, /run/user/$UID, $HOME and
435all directories in /usr.
358.br 436.br
359 437
360.br 438.br
361Symbolic link handling: with the exception of user home, both the link and the real file should be in 439Symbolic link handling: with the exception of user home, both the link and the real file should be in
362the same top directory. For user home, both the link and the real file should be owned by the user. 440the same top directory. For user home, both the link and the real file should be owned by the user.
441
442.TP
443\fBwhitelist-ro file_or_directory
444Equivalent to "whitelist file_or_directory" followed by "read-only file_or_directory"
445
363.TP 446.TP
364\fBwritable-etc 447\fBwritable-etc
365Mount /etc directory read-write. 448Mount /etc directory read-write.
@@ -408,17 +491,21 @@ Sets the NO_NEW_PRIVS prctl. This ensures that child processes
408cannot acquire new privileges using execve(2); in particular, 491cannot acquire new privileges using execve(2); in particular,
409this means that calling a suid binary (or one with file capabilities) 492this means that calling a suid binary (or one with file capabilities)
410does not result in an increase of privilege. 493does not result in an increase of privilege.
494.TP
495\fBnoprinters
496Disable printers.
411#ifdef HAVE_USERNS 497#ifdef HAVE_USERNS
412.TP 498.TP
413\fBnoroot 499\fBnoroot
414Use this command to enable an user namespace. The namespace has only one user, the current user. 500Use this command to enable an user namespace. The namespace has only one user, the current user.
415There is no root account (uid 0) defined in the namespace. 501There is no root account (uid 0) defined in the namespace.
416#endif 502#endif
417.TP 503.TP
418\fBprotocol protocol1,protocol2,protocol3 504\fBprotocol protocol1,protocol2,protocol3
419Enable protocol filter. The filter is based on seccomp and checks the 505Enable protocol filter. The filter is based on seccomp and checks the
420first argument to socket system call. Recognized values: \fBunix\fR, 506first argument to socket system call. Recognized values: \fBunix\fR,
421\fBinet\fR, \fBinet6\fR, \fBnetlink\fR, \fBpacket\fR and \fBbluetooth\fR. 507\fBinet\fR, \fBinet6\fR, \fBnetlink\fR, \fBpacket\fR, and \fBbluetooth\fR.
508Multiple protocol commands are allowed.
422.TP 509.TP
423\fBseccomp 510\fBseccomp
424Enable seccomp filter and blacklist the syscalls in the default list. See man 1 firejail for more details. 511Enable seccomp filter and blacklist the syscalls in the default list. See man 1 firejail for more details.
@@ -530,7 +617,7 @@ Allow the application to see but not talk to the name org.freedesktop.Notificati
530Allow the application to call methods of the interface org.freedesktop.Notifications of the object exposed at the path /org/freedesktop/Notifications by the client owning the bus name org.freedesktop.Notifications on the system DBus. 617Allow the application to call methods of the interface org.freedesktop.Notifications of the object exposed at the path /org/freedesktop/Notifications by the client owning the bus name org.freedesktop.Notifications on the system DBus.
531.TP 618.TP
532\fBdbus-system.broadcast org.freedesktop.Notifications=org.freedesktop.Notifications.*@/org/freedesktop/Notifications 619\fBdbus-system.broadcast org.freedesktop.Notifications=org.freedesktop.Notifications.*@/org/freedesktop/Notifications
533Allow the application to receive broadcast signals from the the interface org.freedesktop.Notifications of the object exposed at the path /org/freedesktop/Notifications by the client owning the bus name org.freedesktop.Notifications on the system DBus. 620Allow the application to receive broadcast signals from the interface org.freedesktop.Notifications of the object exposed at the path /org/freedesktop/Notifications by the client owning the bus name org.freedesktop.Notifications on the system DBus.
534.TP 621.TP
535\fBdbus-user filter 622\fBdbus-user filter
536Enable filtered access to the session DBus. Filters can be specified with the dbus-user.talk and dbus-user.own commands. 623Enable filtered access to the session DBus. Filters can be specified with the dbus-user.talk and dbus-user.own commands.
@@ -640,6 +727,11 @@ env CFLAGS="-W -Wall -Werror"
640.TP 727.TP
641\fBipc-namespace 728\fBipc-namespace
642Enable IPC namespace. 729Enable IPC namespace.
730
731.TP
732\fBkeep-fd
733Inherit open file descriptors to sandbox.
734
643.TP 735.TP
644\fBname sandboxname 736\fBname sandboxname
645Set sandbox name. Example: 737Set sandbox name. Example:
@@ -652,9 +744,8 @@ name browser
652\fBno3d 744\fBno3d
653Disable 3D hardware acceleration. 745Disable 3D hardware acceleration.
654.TP 746.TP
655\fBnoautopulse 747\fBnoautopulse \fR(deprecated)
656Disable automatic ~/.config/pulse init, for complex setups such as remote 748See keep-config-pulse.
657pulse servers or non-standard socket paths.
658.TP 749.TP
659\fBnodvd 750\fBnodvd
660Disable DVD and audio CD devices. 751Disable DVD and audio CD devices.
@@ -662,6 +753,9 @@ Disable DVD and audio CD devices.
662\fBnogroups 753\fBnogroups
663Disable supplementary user groups 754Disable supplementary user groups
664.TP 755.TP
756\fBnoinput
757Disable input devices.
758.TP
665\fBnosound 759\fBnosound
666Disable sound system. 760Disable sound system.
667.TP 761.TP
@@ -674,8 +768,8 @@ Disable U2F devices.
674\fBnovideo 768\fBnovideo
675Disable video capture devices. 769Disable video capture devices.
676.TP 770.TP
677\fBnoinput 771\fBmachine-id
678Disable input devices. 772Spoof id number in /etc/machine-id file - a new random id is generated inside the sandbox.
679.TP 773.TP
680\fBshell none 774\fBshell none
681Run the program directly, without a shell. 775Run the program directly, without a shell.
@@ -795,8 +889,8 @@ a DHCP client and releasing the lease manually.
795 889
796.TP 890.TP
797\fBiprange address,address 891\fBiprange address,address
798Assign an IP address in the provided range to the last network 892Assign an IP address in the provided range to the last network
799interface defined by a net command. A default gateway is assigned by default. 893interface defined by a net command. A default gateway is assigned by default.
800.br 894.br
801 895
802.br 896.br
@@ -814,10 +908,6 @@ iprange 192.168.1.150,192.168.1.160
814Assign MAC addresses to the last network interface defined by a net command. 908Assign MAC addresses to the last network interface defined by a net command.
815 909
816.TP 910.TP
817\fBmachine-id
818Spoof id number in /etc/machine-id file - a new random id is generated inside the sandbox.
819
820.TP
821\fBmtu number 911\fBmtu number
822Assign a MTU value to the last network interface defined by a net command. 912Assign a MTU value to the last network interface defined by a net command.
823 913
@@ -880,18 +970,37 @@ be created and configured using "ip netns".
880Use this name for the interface connected to the bridge for --net=bridge_interface commands, 970Use this name for the interface connected to the bridge for --net=bridge_interface commands,
881instead of the default one. 971instead of the default one.
882#endif 972#endif
973
883.SH Other 974.SH Other
884.TP 975.TP
885\fBdeterministic-exit-code 976\fBdeterministic-exit-code
886Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic. 977Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic.
887 978
888.TP 979.TP
980\fBdeterministic-shutdown
981Always shut down the sandbox after the first child has terminated. The default behavior is to keep the sandbox alive as long as it contains running processes.
982
983.TP
889\fBjoin-or-start sandboxname 984\fBjoin-or-start sandboxname
890Join the sandbox identified by name or start a new one. 985Join the sandbox identified by name or start a new one.
891Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname". 986Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname".
892 987
893.SH FILES 988.SH FILES
894/etc/firejail/filename.profile, $HOME/.config/firejail/filename.profile 989.TP
990\fB/etc/firejail/appname.profile
991Global Firejail configuration consisting mainly of profiles for each application supported by default.
992
993.TP
994\fB$HOME/.config/firejail/appname.profile
995User application profiles, will take precedence over the global profiles.
996
997.TP
998\fB/usr/share/doc/firejail/profile.template
999Template for building new profiles.
1000
1001.TP
1002\fB/usr/share/doc/firejail/redirect_alias-profile.template
1003Template for aliasing/redirecting profiles.
895 1004
896.SH LICENSE 1005.SH LICENSE
897Firejail 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. 1006Firejail 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.
@@ -903,7 +1012,7 @@ Homepage: https://firejail.wordpress.com
903.BR firecfg (1), 1012.BR firecfg (1),
904.BR firejail-login (5), 1013.BR firejail-login (5),
905.BR firejail-users (5), 1014.BR firejail-users (5),
906.BR jailtest (1) 1015.BR jailcheck (1)
907 1016
908.UR https://github.com/netblue30/firejail/wiki/Creating-Profiles 1017.UR https://github.com/netblue30/firejail/wiki/Creating-Profiles
909.UE 1018.UE
diff --git a/src/man/firejail-users.txt b/src/man/firejail-users.txt
index c5a9c1848..e3cce7ed5 100644
--- a/src/man/firejail-users.txt
+++ b/src/man/firejail-users.txt
@@ -59,4 +59,4 @@ Homepage: https://firejail.wordpress.com
59.BR firecfg (1), 59.BR firecfg (1),
60.BR firejail-profile (5), 60.BR firejail-profile (5),
61.BR firejail-login (5), 61.BR firejail-login (5),
62.BR jailtest (1) 62.BR jailcheck (1)
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index 23ec23fb1..f9deaeaa4 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -45,7 +45,7 @@ firejail {\-? | \-\-debug-caps | \-\-debug-errnos | \-\-debug-syscalls | \-\-deb
45#ifdef HAVE_LTS 45#ifdef HAVE_LTS
46This is Firejail long-term support (LTS), an enterprise focused version of the software, 46This is Firejail long-term support (LTS), an enterprise focused version of the software,
47LTS is usually supported for two or three years. 47LTS is usually supported for two or three years.
48During this time only bugs and the occasional documentation problems are fixed. 48During this time only bugs and the occasional documentation problems are fixed.
49The attack surface of the SUID executable was greatly reduced by removing some of the features. 49The attack surface of the SUID executable was greatly reduced by removing some of the features.
50.br 50.br
51 51
@@ -109,7 +109,7 @@ ptrace system call allows a full bypass of the seccomp filter.
109.br 109.br
110Example: 110Example:
111.br 111.br
112$ firejail --allow-debuggers --profile=/etc/firejail/firefox.profile strace -f firefox 112$ firejail --allow-debuggers --profile=/etc/firejail/firefox.profile strace -f firefox
113.TP 113.TP
114\fB\-\-allusers 114\fB\-\-allusers
115All directories under /home are visible inside the sandbox. By default, only current user home directory is visible. 115All directories under /home are visible inside the sandbox. By default, only current user home directory is visible.
@@ -147,12 +147,12 @@ private-bin and private-lib are disabled by default when running appimages.
147.br 147.br
148Example: 148Example:
149.br 149.br
150$ firejail --appimage krita-3.0-x86_64.appimage 150$ firejail --appimage --profile=krita krita-3.0-x86_64.appimage
151.br 151.br
152$ firejail --appimage --private krita-3.0-x86_64.appimage 152$ firejail --appimage --private --profile=krita krita-3.0-x86_64.appimage
153.br 153.br
154#ifdef HAVE_X11 154#ifdef HAVE_X11
155$ firejail --appimage --net=none --x11 krita-3.0-x86_64.appimage 155$ firejail --appimage --net=none --x11 --profile=krita krita-3.0-x86_64.appimage
156#endif 156#endif
157.TP 157.TP
158#ifdef HAVE_NETWORK 158#ifdef HAVE_NETWORK
@@ -185,10 +185,7 @@ $ firejail "\-\-blacklist=/home/username/My Virtual Machines"
185$ firejail \-\-blacklist=/home/username/My\\ Virtual\\ Machines 185$ firejail \-\-blacklist=/home/username/My\\ Virtual\\ Machines
186.TP 186.TP
187\fB\-\-build 187\fB\-\-build
188The command builds a whitelisted profile. The profile is printed on the screen. If /usr/bin/strace is installed on the system, it also 188The command builds a whitelisted profile. The profile is printed on the screen. The program is run in a very relaxed sandbox, with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported.
189builds a whitelisted seccomp profile. The program is run in a very relaxed sandbox,
190with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported
191in order to allow strace to run. Chromium and Chromium-based browsers will not work.
192.br 189.br
193 190
194.br 191.br
@@ -197,10 +194,8 @@ Example:
197$ firejail --build vlc ~/Videos/test.mp4 194$ firejail --build vlc ~/Videos/test.mp4
198.TP 195.TP
199\fB\-\-build=profile-file 196\fB\-\-build=profile-file
200The command builds a whitelisted profile, and saves it in profile-file. If /usr/bin/strace is installed on the system, it also 197The command builds a whitelisted profile, and saves it in profile-file. The program is run in a very relaxed sandbox,
201builds a whitelisted seccomp profile. The program is run in a very relaxed sandbox, 198with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported.
202with only --caps.drop=all and --nonewprivs. Programs that raise user privileges are not supported
203in order to allow strace to run. Chromium and Chromium-based browsers will not work.
204.br 199.br
205 200
206.br 201.br
@@ -290,8 +285,8 @@ $ firejail \-\-caps.print=3272
290Print content of file from sandbox container, see FILE TRANSFER section for more details. 285Print content of file from sandbox container, see FILE TRANSFER section for more details.
291#endif 286#endif
292.TP 287.TP
293\fB\-\-cgroup=tasks-file 288\fB\-\-cgroup=file
294Place the sandbox in the specified control group. tasks-file is the full path of cgroup tasks file. 289Place the sandbox in the specified control group. file is the full path of a tasks or cgroup.procs file.
295.br 290.br
296 291
297.br 292.br
@@ -310,6 +305,11 @@ regular user, nonewprivs and a default capabilities filter are enabled.
310Example: 305Example:
311.br 306.br
312$ firejail \-\-chroot=/media/ubuntu warzone2100 307$ firejail \-\-chroot=/media/ubuntu warzone2100
308.br
309
310.br
311For automatic mounting of X11 and PulseAudio sockets set environment variables
312FIREJAIL_CHROOT_X11 and FIREJAIL_CHROOT_PULSE.
313#endif 313#endif
314.TP 314.TP
315\fB\-\-cpu=cpu-number,cpu-number,cpu-number 315\fB\-\-cpu=cpu-number,cpu-number,cpu-number
@@ -701,6 +701,12 @@ $ firejail \-\-net=eth0 \-\-defaultgw=10.10.20.1 firefox
701\fB\-\-deterministic-exit-code 701\fB\-\-deterministic-exit-code
702Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic. 702Always exit firejail with the first child's exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic.
703.br 703.br
704
705.TP
706\fB\-\-deterministic-shutdown
707Always shut down the sandbox after the first child has terminated. The default behavior is to keep the sandbox alive as long as it contains running processes.
708.br
709
704.TP 710.TP
705\fB\-\-disable-mnt 711\fB\-\-disable-mnt
706Blacklist /mnt, /media, /run/mount and /run/media access. 712Blacklist /mnt, /media, /run/mount and /run/media access.
@@ -810,6 +816,26 @@ Example:
810$ firejail \-\-hosts-file=~/myhosts firefox 816$ firejail \-\-hosts-file=~/myhosts firefox
811 817
812.TP 818.TP
819\fB\-\-ids-check
820Check file hashes previously generated by \-\-ids-check. See INTRUSION DETECTION SYSTEM section for more details.
821.br
822
823.br
824Example:
825.br
826$ firejail \-\-ids-check
827
828.TP
829\fB\-\-ids-init
830Initialize file hashes. See INTRUSION DETECTION SYSTEM section for more details.
831.br
832
833.br
834Example:
835.br
836$ firejail \-\-ids-init
837
838.TP
813\fB\-\-ignore=command 839\fB\-\-ignore=command
814Ignore command in profile file. 840Ignore command in profile file.
815.br 841.br
@@ -947,7 +973,7 @@ $ firejail \-\-net=eth0 \-\-\iprange=192.168.1.100,192.168.1.150
947 973
948.TP 974.TP
949\fB\-\-ipc-namespace 975\fB\-\-ipc-namespace
950Enable a new IPC namespace if the sandbox was started as a regular user. IPC namespace is enabled by default 976Enable a new IPC namespace if the sandbox was started as a regular user. IPC namespace is enabled by default
951for sandboxes started as root. 977for sandboxes started as root.
952.br 978.br
953 979
@@ -1014,7 +1040,7 @@ $ sudo firejail --join-network=browser /sbin/iptables -vL
1014.br 1040.br
1015 1041
1016.br 1042.br
1017# verify IP addresses 1043# verify IP addresses
1018.br 1044.br
1019$ sudo firejail --join-network=browser ip addr 1045$ sudo firejail --join-network=browser ip addr
1020.br 1046.br
@@ -1052,6 +1078,17 @@ Same as "firejail --join=name" if sandbox with specified name exists, otherwise
1052Note that in contrary to other join options there is respective profile option. 1078Note that in contrary to other join options there is respective profile option.
1053 1079
1054.TP 1080.TP
1081\fB\-\-keep-config-pulse
1082Disable automatic ~/.config/pulse init, for complex setups such as remote
1083pulse servers or non-standard socket paths.
1084.br
1085
1086.br
1087Example:
1088.br
1089$ firejail \-\-keep-config-pulse firefox
1090
1091.TP
1055\fB\-\-keep-dev-shm 1092\fB\-\-keep-dev-shm
1056/dev/shm directory is untouched (even with --private-dev) 1093/dev/shm directory is untouched (even with --private-dev)
1057.br 1094.br
@@ -1062,6 +1099,26 @@ Example:
1062$ firejail --keep-dev-shm --private-dev 1099$ firejail --keep-dev-shm --private-dev
1063 1100
1064.TP 1101.TP
1102\fB\-\-keep-fd=all
1103Inherit all open file descriptors to the sandbox. By default only file descriptors 0, 1 and 2 are inherited to the sandbox, and all other file descriptors are closed.
1104.br
1105
1106.br
1107Example:
1108.br
1109$ firejail --keep-fd=all
1110
1111.TP
1112\fB\-\-keep-fd=file_descriptor
1113Don't close specified open file descriptors. By default only file descriptors 0, 1 and 2 are inherited to the sandbox, and all other file descriptors are closed.
1114.br
1115
1116.br
1117Example:
1118.br
1119$ firejail --keep-fd=3,4,5
1120
1121.TP
1065\fB\-\-keep-var-tmp 1122\fB\-\-keep-var-tmp
1066/var/tmp directory is untouched. 1123/var/tmp directory is untouched.
1067.br 1124.br
@@ -1214,7 +1271,7 @@ $ firejail \-\-net=br0 \-\-net=br1
1214.TP 1271.TP
1215\fB\-\-net=ethernet_interface|wireless_interface 1272\fB\-\-net=ethernet_interface|wireless_interface
1216Enable a new network namespace and connect it 1273Enable a new network namespace and connect it
1217to this ethernet interface using the standard Linux macvlan|ipvaln 1274to this ethernet interface using the standard Linux macvlan|ipvlan
1218driver. Unless specified with option \-\-ip and \-\-defaultgw, an 1275driver. Unless specified with option \-\-ip and \-\-defaultgw, an
1219IP address and a default gateway will be assigned automatically 1276IP address and a default gateway will be assigned automatically
1220to the sandbox. The IP address is verified using ARP before 1277to the sandbox. The IP address is verified using ARP before
@@ -1401,6 +1458,30 @@ $ firejail --name=browser --net=eth0 --netfilter firefox &
1401$ firejail --netfilter6.print=browser 1458$ firejail --netfilter6.print=browser
1402 1459
1403.TP 1460.TP
1461\fB\-\-netlock
1462Several type of programs (email clients, multiplayer games etc.) talk to a very small
1463number of IP addresses. But the best example is tor browser. It only talks to a guard node,
1464and there are two or three more on standby in case the main one fails.
1465During startup, the browser contacts all of them, after that it keeps talking to the main
1466one... for weeks!
1467
1468Use the network locking feature to build and deploy a custom network firewall in your sandbox.
1469The firewall allows only the traffic to the IP addresses detected during the program
1470startup. Traffic to any other address is quietly dropped. By default the network monitoring
1471time is one minute.
1472
1473A network namespace (\-\-net=eth0) is required for this feature to work. Example:
1474.br
1475
1476.br
1477$ firejail --net=eth0 --netlock \\
1478.br
1479--private=~/tor-browser_en-US ./start-tor-browser.desktop
1480.br
1481
1482.br
1483
1484.TP
1404\fB\-\-netmask=address 1485\fB\-\-netmask=address
1405Use this option when you want to assign an IP address in a new namespace and 1486Use this option when you want to assign an IP address in a new namespace and
1406the parent interface specified by --net is not configured. An IP address and 1487the parent interface specified by --net is not configured. An IP address and
@@ -1437,6 +1518,40 @@ PID User RX(KB/s) TX(KB/s) Command
14371294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox 15181294 netblue 53.355 1.473 firejail \-\-net=eth0 firefox
1438.br 1519.br
14397383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission 15207383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission
1521.TP
1522\fB\-\-nettrace[=name|pid]
1523Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes
1524created with \-\-net are supported.
1525.br
1526
1527.br
1528Without a name/pid, Firejail will monitor the main system network namespace.
1529.br
1530
1531.br
1532 $ firejail --nettrace=browser
1533.br
1534
1535.br
1536 95 KB/s geoip 457, IP database 4436
1537.br
1538 52 KB/s *********** 64.222.84.207:443 United States
1539.br
1540 33 KB/s ******* 89.147.74.105:63930 Hungary
1541.br
1542 0 B/s 45.90.28.0:443 NextDNS
1543.br
1544 0 B/s 94.70.122.176:52309(UDP) Greece
1545.br
1546 339 B/s 104.26.7.35:443 Cloudflare
1547.br
1548
1549.br
1550If /usr/bin/geoiplookup is installed (geoip-bin package in Debian),
1551the country the IP address originates from is added to the trace.
1552We also use the static IP map in /etc/firejail/hostnames
1553to print the domain names for some of the more common websites and cloud platforms.
1554No external services are contacted for reverse IP lookup.
1440#endif 1555#endif
1441.TP 1556.TP
1442\fB\-\-nice=value 1557\fB\-\-nice=value
@@ -1460,15 +1575,8 @@ Example:
1460$ firejail --no3d firefox 1575$ firejail --no3d firefox
1461 1576
1462.TP 1577.TP
1463\fB\-\-noautopulse 1578\fB\-\-noautopulse \fR(deprecated)
1464Disable automatic ~/.config/pulse init, for complex setups such as remote 1579See --keep-config-pulse.
1465pulse servers or non-standard socket paths.
1466.br
1467
1468.br
1469Example:
1470.br
1471$ firejail \-\-noautopulse firefox
1472 1580
1473.TP 1581.TP
1474\fB\-\-noblacklist=dirname_or_filename 1582\fB\-\-noblacklist=dirname_or_filename
@@ -1576,6 +1684,10 @@ does not result in an increase of privilege. This option
1576is enabled by default if seccomp filter is activated. 1684is enabled by default if seccomp filter is activated.
1577 1685
1578.TP 1686.TP
1687\fB\-\-noprinters
1688Disable printers.
1689
1690.TP
1579\fB\-\-noprofile 1691\fB\-\-noprofile
1580Do not use a security profile. 1692Do not use a security profile.
1581.br 1693.br
@@ -1797,8 +1909,9 @@ The files in the list must be expressed as relative to the /bin,
1797/sbin, /usr/bin, /usr/sbin, or /usr/local/bin directories. 1909/sbin, /usr/bin, /usr/sbin, or /usr/local/bin directories.
1798If no listed files are found, /bin directory will be empty. 1910If no listed files are found, /bin directory will be empty.
1799The same directory is also bind-mounted over /sbin, /usr/bin, /usr/sbin and /usr/local/bin. 1911The same directory is also bind-mounted over /sbin, /usr/bin, /usr/sbin and /usr/local/bin.
1800All modifications are discarded when the sandbox is closed. File globbing is supported, 1912All modifications are discarded when the sandbox is closed.
1801see \fBFILE GLOBBING\fR section for more details. 1913Multiple private-bin commands are allowed and they accumulate.
1914File globbing is supported, see \fBFILE GLOBBING\fR section for more details.
1802.br 1915.br
1803 1916
1804.br 1917.br
@@ -1828,7 +1941,6 @@ $ firejail \-\-private-cache openbox
1828.TP 1941.TP
1829\fB\-\-private-cwd 1942\fB\-\-private-cwd
1830Set working directory inside jail to the home directory, and failing that, the root directory. 1943Set working directory inside jail to the home directory, and failing that, the root directory.
1831.br
1832Does not impact working directory of profile include paths. 1944Does not impact working directory of profile include paths.
1833.br 1945.br
1834 1946
@@ -1849,7 +1961,7 @@ $ pwd
1849.TP 1961.TP
1850\fB\-\-private-cwd=directory 1962\fB\-\-private-cwd=directory
1851Set working directory inside the jail. 1963Set working directory inside the jail.
1852.br 1964Full directory path is required. Symbolic links are not allowed.
1853Does not impact working directory of profile include paths. 1965Does not impact working directory of profile include paths.
1854.br 1966.br
1855 1967
@@ -1892,11 +2004,10 @@ $
1892Build a new /etc in a temporary 2004Build a new /etc in a temporary
1893filesystem, and copy the files and directories in the list. 2005filesystem, and copy the files and directories in the list.
1894The files and directories in the list must be expressed as relative to 2006The files and directories in the list must be expressed as relative to
1895the /etc directory, and must not contain the / character 2007the /etc directory (e.g., /etc/foo must be expressed as foo).
1896(e.g., /etc/foo must be expressed as foo, but /etc/foo/bar --
1897expressed as foo/bar -- is disallowed).
1898If no listed file is found, /etc directory will be empty. 2008If no listed file is found, /etc directory will be empty.
1899All modifications are discarded when the sandbox is closed. 2009All modifications are discarded when the sandbox is closed.
2010Multiple private-etc commands are allowed and they accumulate.
1900.br 2011.br
1901 2012
1902.br 2013.br
@@ -1997,7 +2108,7 @@ Build a new /srv in a temporary
1997filesystem, and copy the files and directories in the list. 2108filesystem, and copy the files and directories in the list.
1998The files and directories in the list must be expressed as relative to 2109The files and directories in the list must be expressed as relative to
1999the /srv directory, and must not contain the / character 2110the /srv directory, and must not contain the / character
2000(e.g., /opt/srv must be expressed as foo, but /srv/foo/bar -- 2111(e.g., /srv/foo must be expressed as foo, but /srv/foo/bar --
2001expressed as srv/bar -- is disallowed). 2112expressed as srv/bar -- is disallowed).
2002If no listed file is found, /srv directory will be empty. 2113If no listed file is found, /srv directory will be empty.
2003All modifications are discarded when the sandbox is closed. 2114All modifications are discarded when the sandbox is closed.
@@ -2055,7 +2166,7 @@ $ firejail \-\-profile.print=browser
2055.TP 2166.TP
2056\fB\-\-protocol=protocol,protocol,protocol 2167\fB\-\-protocol=protocol,protocol,protocol
2057Enable protocol filter. The filter is based on seccomp and checks the first argument to socket system call. 2168Enable protocol filter. The filter is based on seccomp and checks the first argument to socket system call.
2058Recognized values: unix, inet, inet6, netlink, packet and bluetooth. This option is not supported for i386 architecture. 2169Recognized values: unix, inet, inet6, netlink, packet, and bluetooth. This option is not supported for i386 architecture.
2059.br 2170.br
2060 2171
2061.br 2172.br
@@ -2127,11 +2238,12 @@ $ firejail --read-only=~/test --read-write=~/test/a
2127.TP 2238.TP
2128\fB\-\-rlimit-as=number 2239\fB\-\-rlimit-as=number
2129Set the maximum size of the process's virtual memory (address space) in bytes. 2240Set the maximum size of the process's virtual memory (address space) in bytes.
2241Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024).
2130 2242
2131.TP 2243.TP
2132\fB\-\-rlimit-cpu=number 2244\fB\-\-rlimit-cpu=number
2133Set the maximum limit, in seconds, for the amount of CPU time each 2245Set the maximum limit, in seconds, for the amount of CPU time each
2134sandboxed process can consume. When the limit is reached, the processes are killed. 2246sandboxed process can consume. When the limit is reached, the processes are killed.
2135 2247
2136The CPU limit is a limit on CPU seconds rather than elapsed time. CPU seconds is basically how many seconds 2248The CPU limit is a limit on CPU seconds rather than elapsed time. CPU seconds is basically how many seconds
2137the CPU has been in use and does not necessarily directly relate to the elapsed time. Linux kernel keeps 2249the CPU has been in use and does not necessarily directly relate to the elapsed time. Linux kernel keeps
@@ -2140,6 +2252,7 @@ track of CPU seconds for each process independently.
2140.TP 2252.TP
2141\fB\-\-rlimit-fsize=number 2253\fB\-\-rlimit-fsize=number
2142Set the maximum file size that can be created by a process. 2254Set the maximum file size that can be created by a process.
2255Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024).
2143.TP 2256.TP
2144\fB\-\-rlimit-nofile=number 2257\fB\-\-rlimit-nofile=number
2145Set the maximum number of files that can be opened by a process. 2258Set the maximum number of files that can be opened by a process.
@@ -2174,7 +2287,7 @@ $ firejail \-\-net=eth0 \-\-scan
2174.TP 2287.TP
2175\fB\-\-seccomp 2288\fB\-\-seccomp
2176Enable seccomp filter and blacklist the syscalls in the default list, 2289Enable seccomp filter and blacklist the syscalls in the default list,
2177which is @default-nodebuggers unless allow-debuggers is specified, 2290which is @default-nodebuggers unless \-\-allow-debuggers is specified,
2178then it is @default. 2291then it is @default.
2179 2292
2180.br 2293.br
@@ -2185,18 +2298,18 @@ system call groups are defined: @aio, @basic-io, @chown, @clock,
2185@network-io, @obsolete, @privileged, @process, @raw-io, @reboot, 2298@network-io, @obsolete, @privileged, @process, @raw-io, @reboot,
2186@resources, @setuid, @swap, @sync, @system-service and @timer. 2299@resources, @setuid, @swap, @sync, @system-service and @timer.
2187More information about groups can be found in /usr/share/doc/firejail/syscalls.txt 2300More information about groups can be found in /usr/share/doc/firejail/syscalls.txt
2301.br
2188 2302
2189In addition, a system call can be specified by its number instead of 2303.br
2190name with prefix $, so for example $165 would be equal to mount on i386. 2304The default list can be customized, see \-\-seccomp= for a description.
2191Exceptions can be allowed with prefix !. 2305It can be customized also globally in /etc/firejail/firejail.config file.
2306.br
2192 2307
2193.br 2308.br
2194System architecture is strictly imposed only if flag 2309System architecture is strictly imposed only if flag
2195\-\-seccomp.block-secondary is used. The filter is applied at run time 2310\-\-seccomp.block-secondary is used. The filter is applied at run time
2196only if the correct architecture was detected. For the case of I386 2311only if the correct architecture was detected. For the case of I386
2197and AMD64 both 32-bit and 64-bit filters are installed. On a 64 bit 2312and AMD64 both 32-bit and 64-bit filters are installed.
2198architecture, an additional filter for 32 bit system calls can be
2199installed with \-\-seccomp.32.
2200.br 2313.br
2201 2314
2202.br 2315.br
@@ -2207,11 +2320,14 @@ Firejail will print seccomp violations to the audit log if the kernel was compil
2207Example: 2320Example:
2208.br 2321.br
2209$ firejail \-\-seccomp 2322$ firejail \-\-seccomp
2323
2324
2210.TP 2325.TP
2211\fB\-\-seccomp=syscall,@group,!syscall2 2326\fB\-\-seccomp=syscall,@group,!syscall2
2212Enable seccomp filter, whitelist "syscall2", but blacklist the default 2327Enable seccomp filter, blacklist the default list and the syscalls or syscall groups
2213list and the syscalls or syscall groups specified by the 2328specified by the command, but don't blacklist "syscall2". On a 64 bit
2214command. 2329architecture, an additional filter for 32 bit system calls can be
2330installed with \-\-seccomp.32.
2215.br 2331.br
2216 2332
2217.br 2333.br
@@ -2221,6 +2337,13 @@ $ firejail \-\-seccomp=utime,utimensat,utimes firefox
2221.br 2337.br
2222$ firejail \-\-seccomp=@clock,mkdir,unlinkat transmission-gtk 2338$ firejail \-\-seccomp=@clock,mkdir,unlinkat transmission-gtk
2223.br 2339.br
2340$ firejail '\-\-seccomp=@ipc,!pipe,!pipe2' audacious
2341.br
2342
2343.br
2344Syscalls can be specified by their number if prefix $ is added,
2345so for example $165 would be equal to mount on i386.
2346.br
2224 2347
2225.br 2348.br
2226Instead of dropping the syscall by returning EPERM, another error 2349Instead of dropping the syscall by returning EPERM, another error
@@ -2233,6 +2356,7 @@ by using \fBsyscall:kill\fR syntax, or the attempt may be logged with
2233 2356
2234.br 2357.br
2235Example: 2358Example:
2359.br
2236$ firejail \-\-seccomp=unlinkat:ENOENT,utimensat,utimes 2360$ firejail \-\-seccomp=unlinkat:ENOENT,utimensat,utimes
2237.br 2361.br
2238Parent pid 10662, child pid 10663 2362Parent pid 10662, child pid 10663
@@ -2241,9 +2365,13 @@ Child process initialized
2241.br 2365.br
2242$ touch testfile 2366$ touch testfile
2243.br 2367.br
2368$ ls testfile
2369.br
2370testfile
2371.br
2244$ rm testfile 2372$ rm testfile
2245.br 2373.br
2246rm: cannot remove `testfile': Operation not permitted 2374rm: cannot remove `testfile': No such file or directory
2247.br 2375.br
2248 2376
2249.br 2377.br
@@ -2256,7 +2384,7 @@ filters.
2256.br 2384.br
2257Example: 2385Example:
2258.br 2386.br
2259$ firejail \-\-noprofile \-\-shell=none \-\-seccomp=execve bash 2387$ firejail \-\-noprofile \-\-shell=none \-\-seccomp=execve sh
2260.br 2388.br
2261Parent pid 32751, child pid 32752 2389Parent pid 32751, child pid 32752
2262.br 2390.br
@@ -2268,8 +2396,7 @@ Child process initialized in 46.44 ms
2268.br 2396.br
2269$ ls 2397$ ls
2270.br 2398.br
2271Bad system call 2399Operation not permitted
2272.br
2273 2400
2274.TP 2401.TP
2275\fB\-\-seccomp.block-secondary 2402\fB\-\-seccomp.block-secondary
@@ -2313,15 +2440,15 @@ Child process initialized
2313.br 2440.br
2314$ touch testfile 2441$ touch testfile
2315.br 2442.br
2443$ ls testfile
2444.br
2445testfile
2446.br
2316$ rm testfile 2447$ rm testfile
2317.br 2448.br
2318rm: cannot remove `testfile': Operation not permitted 2449rm: cannot remove `testfile': No such file or directory
2319.br 2450.br
2320 2451
2321
2322
2323
2324
2325.TP 2452.TP
2326\fB\-\-seccomp.keep=syscall,@group,!syscall2 2453\fB\-\-seccomp.keep=syscall,@group,!syscall2
2327Enable seccomp filter, blacklist all syscall not listed and "syscall2". 2454Enable seccomp filter, blacklist all syscall not listed and "syscall2".
@@ -2556,6 +2683,13 @@ $ firejail \-\-list
2556.br 2683.br
2557$ firejail \-\-shutdown=3272 2684$ firejail \-\-shutdown=3272
2558.TP 2685.TP
2686\fB\-\-tab
2687Enable shell tab completion in sandboxes using private or whitelisted home directories.
2688.br
2689
2690.br
2691$ firejail \-\-private --tab
2692.TP
2559\fB\-\-timeout=hh:mm:ss 2693\fB\-\-timeout=hh:mm:ss
2560Kill the sandbox automatically after the time has elapsed. The time is specified in hours/minutes/seconds format. 2694Kill the sandbox automatically after the time has elapsed. The time is specified in hours/minutes/seconds format.
2561.br 2695.br
@@ -2564,14 +2698,13 @@ Kill the sandbox automatically after the time has elapsed. The time is specified
2564$ firejail \-\-timeout=01:30:00 firefox 2698$ firejail \-\-timeout=01:30:00 firefox
2565.TP 2699.TP
2566\fB\-\-tmpfs=dirname 2700\fB\-\-tmpfs=dirname
2567Mount a writable tmpfs filesystem on directory dirname. This option is available only when running the sandbox as root. 2701Mount a writable tmpfs filesystem on directory dirname. Directories outside user home or not owned by the user are not allowed. Sandboxes running as root are exempt from these restrictions. File globbing is supported, see \fBFILE GLOBBING\fR section for more details.
2568File globbing is supported, see \fBFILE GLOBBING\fR section for more details.
2569.br 2702.br
2570 2703
2571.br 2704.br
2572Example: 2705Example:
2573.br 2706.br
2574# firejail \-\-tmpfs=/var 2707$ firejail \-\-tmpfs=~/.local/share
2575.TP 2708.TP
2576\fB\-\-top 2709\fB\-\-top
2577Monitor the most CPU-intensive sandboxes, see \fBMONITORING\fR section for more details. 2710Monitor the most CPU-intensive sandboxes, see \fBMONITORING\fR section for more details.
@@ -2721,8 +2854,9 @@ $ firejail \-\-net=br0 --veth-name=if0
2721\fB\-\-whitelist=dirname_or_filename 2854\fB\-\-whitelist=dirname_or_filename
2722Whitelist directory or file. A temporary file system is mounted on the top directory, and the 2855Whitelist directory or file. A temporary file system is mounted on the top directory, and the
2723whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent, 2856whitelisted files are mount-binded inside. Modifications to whitelisted files are persistent,
2724everything else is discarded when the sandbox is closed. The top directory could be 2857everything else is discarded when the sandbox is closed. The top directory can be
2725user home, /dev, /etc, /media, /mnt, /opt, /run/user/$UID, /srv, /sys/module, /tmp, /usr/share and /var. 2858all directories in / (except /proc and /sys), /sys/module, /run/user/$UID, $HOME and
2859all directories in /usr.
2726.br 2860.br
2727 2861
2728.br 2862.br
@@ -2848,7 +2982,7 @@ and it is installed by default on most Linux distributions. It provides support
2848connection model. Untrusted clients are restricted in certain ways to prevent them from reading window 2982connection model. Untrusted clients are restricted in certain ways to prevent them from reading window
2849contents of other clients, stealing input events, etc. 2983contents of other clients, stealing input events, etc.
2850 2984
2851The untrusted mode has several limitations. A lot of regular programs assume they are a trusted X11 clients 2985The untrusted mode has several limitations. A lot of regular programs assume they are a trusted X11 clients
2852and will crash or lock up when run in untrusted mode. Chromium browser and xterm are two examples. 2986and will crash or lock up when run in untrusted mode. Chromium browser and xterm are two examples.
2853Firefox and transmission-gtk seem to be working fine. 2987Firefox and transmission-gtk seem to be working fine.
2854A network namespace is not required for this option. 2988A network namespace is not required for this option.
@@ -3179,6 +3313,65 @@ $ firejail \-\-put=mybrowser xpra-clipboard.png ~/Downloads/xpra-clipboard.png
3179$ firejail \-\-cat=mybrowser ~/.bashrc 3313$ firejail \-\-cat=mybrowser ~/.bashrc
3180.br 3314.br
3181#endif 3315#endif
3316
3317.SH INTRUSION DETECTION SYSTEM (IDS)
3318The host-based intrusion detection system tracks down and audits user and system file modifications.
3319The feature is configured using /etc/firejail/ids.config file, the checksums are stored in /var/lib/firejail/USERNAME.ids,
3320where USERNAME is the name of the current user. We use BLAKE2 cryptographic function for hashing.
3321
3322As a regular user, initialize the database:
3323.br
3324
3325.br
3326$ firejail --ids-init
3327.br
3328Opening config file /etc/firejail/ids.config
3329.br
3330Loading config file /etc/firejail/ids.config
3331.br
3332Opening config file /etc/firejail/ids.config.local
3333.br
3334500 1000 1500 2000
3335.br
33362466 files scanned
3337.br
3338IDS database initialized
3339.br
3340
3341.br
3342The default configuration targets several system executables in directories such as /bin, /sbin, /usr/bin, /usr/sbin, and several critical config files in user home directory
3343such as ~/.bashrc, ~/.xinitrc, and ~/.config/autostart. Several system config files in /etc directory are also hashed.
3344.br
3345
3346.br
3347Run --ids-check to audit the system:
3348.br
3349
3350.br
3351$ firejail --ids-check
3352.br
3353Opening config file /etc/firejail/ids.config
3354.br
3355Loading config file /etc/firejail/ids.config
3356.br
3357Opening config file /etc/firejail/ids.config.local
3358.br
3359500 1000 1500
3360.br
3361Warning: modified /home/netblue/.bashrc
3362.br
33632000
3364.br
33652466 files scanned: modified 1, permissions 0, new 0, removed 0
3366.br
3367
3368.br
3369The program will print the files that have been modified since the database was created, or the files with different access permissions.
3370New files and deleted files are also flagged.
3371
3372Currently while scanning the file system, symbolic links are not followed, and files the user doesn't have read access to are silently dropped.
3373The program can also be run as root (sudo firejail --ids-init/--ids-check).
3374
3182.SH MONITORING 3375.SH MONITORING
3183Option \-\-list prints a list of all sandboxes. The format 3376Option \-\-list prints a list of all sandboxes. The format
3184for each process entry is as follows: 3377for each process entry is as follows:
@@ -3239,7 +3432,7 @@ The owner of the sandbox.
3239.SH RESTRICTED SHELL 3432.SH RESTRICTED SHELL
3240To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in 3433To configure a restricted shell, replace /bin/bash with /usr/bin/firejail in
3241/etc/passwd file for each user that needs to be restricted. Alternatively, 3434/etc/passwd file for each user that needs to be restricted. Alternatively,
3242you can specify /usr/bin/firejail in adduser command: 3435you can specify /usr/bin/firejail in adduser command:
3243 3436
3244adduser \-\-shell /usr/bin/firejail username 3437adduser \-\-shell /usr/bin/firejail username
3245 3438
@@ -3249,7 +3442,7 @@ Additional arguments passed to firejail executable upon login are declared in /e
3249Several command line options can be passed to the program using 3442Several command line options can be passed to the program using
3250profile files. Firejail chooses the profile file as follows: 3443profile files. Firejail chooses the profile file as follows:
3251 3444
32521. If a profile file is provided by the user with --profile=FILE option, the profile FILE is loaded. If a profile name is given, it is searched for first in the ~/.config/firejail directory and if not found then in /etc/firejail directory. Profile names do not include the .profile suffix. If there is a file with the same name as the given profile name, it will be used instead of doing the profile search. To force a profile search, prefix the profile name with a colon (:), eg. --profile=:PROFILE_NAME. 34451. If a profile file is provided by the user with --profile=FILE option, the profile FILE is loaded. If a profile name is given, it is searched for first in the ~/.config/firejail directory and if not found then in /etc/firejail directory. Profile names do not include the .profile suffix. If there is a file with the same name as the given profile name, it will be used instead of doing the profile search. To force a profile search, prefix the profile name with a colon (:), eg. --profile=:PROFILE_NAME.
3253Example: 3446Example:
3254.PP 3447.PP
3255.RS 3448.RS
@@ -3365,7 +3558,7 @@ Homepage: https://firejail.wordpress.com
3365.BR firejail-profile (5), 3558.BR firejail-profile (5),
3366.BR firejail-login (5), 3559.BR firejail-login (5),
3367.BR firejail-users (5), 3560.BR firejail-users (5),
3368.BR jailtest (1) 3561.BR jailcheck (1)
3369 3562
3370.UR https://github.com/netblue30/firejail/wiki 3563.UR https://github.com/netblue30/firejail/wiki
3371.UE , 3564.UE ,
diff --git a/src/man/firemon.txt b/src/man/firemon.txt
index 64f15a1f0..c4e6e15b3 100644
--- a/src/man/firemon.txt
+++ b/src/man/firemon.txt
@@ -56,7 +56,7 @@ Print route table for each sandbox.
56Print seccomp configuration for each sandbox. 56Print seccomp configuration for each sandbox.
57.TP 57.TP
58\fB\-\-top 58\fB\-\-top
59Monitor the most CPU-intensive sandboxes. This command is similar to 59Monitor the most CPU-intensive sandboxes. This command is similar to
60the regular UNIX top command, however it applies only to sandboxes. 60the regular UNIX top command, however it applies only to sandboxes.
61.TP 61.TP
62\fB\-\-tree 62\fB\-\-tree
@@ -120,4 +120,4 @@ Homepage: https://firejail.wordpress.com
120.BR firejail-profile (5), 120.BR firejail-profile (5),
121.BR firejail-login (5), 121.BR firejail-login (5),
122.BR firejail-users (5), 122.BR firejail-users (5),
123.BR jailtest (1) 123.BR jailcheck (1)
diff --git a/src/man/jailtest.txt b/src/man/jailcheck.txt
index b52fc5eed..483f47fb9 100644
--- a/src/man/jailtest.txt
+++ b/src/man/jailcheck.txt
@@ -1,29 +1,30 @@
1.TH JAILTEST 1 "MONTH YEAR" "VERSION" "JAILTEST man page" 1.TH JAILCHECK 1 "MONTH YEAR" "VERSION" "JAILCHECK man page"
2.SH NAME 2.SH NAME
3jailtest \- Simple utility program to test running sandboxes 3jailcheck \- Simple utility program to test running sandboxes
4.SH SYNOPSIS 4.SH SYNOPSIS
5sudo jailtest [OPTIONS] [directory] 5sudo jailcheck [OPTIONS] [directory]
6.SH DESCRIPTION 6.SH DESCRIPTION
7WORK IN PROGRESS! 7jailcheck attaches itself to all sandboxes started by the user and performs some basic tests
8jailtest attaches itself to all sandboxes started by the user and performs some basic tests
9on the sandbox filesystem: 8on the sandbox filesystem:
10.TP 9.TP
11\fB1. Virtual directories 10\fB1. Virtual directories
12jailtest extracts a list with the main virtual directories installed by the sandbox. 11jailcheck extracts a list with the main virtual directories installed by the sandbox.
13These directories are build by firejail at startup using --private* and --whitelist commands. 12These directories are build by firejail at startup using --private* and --whitelist commands.
14.TP 13.TP
15\fB2. Noexec test 14\fB2. Noexec test
16jailtest inserts executable programs in /home/username, /tmp, and /var/tmp directories 15jailcheck inserts executable programs in /home/username, /tmp, and /var/tmp directories
17and tries to run them from inside the sandbox, thus testing if the directory is executable or not. 16and tries to run them from inside the sandbox, thus testing if the directory is executable or not.
18.TP 17.TP
19\fB3. Read access test 18\fB3. Read access test
20jailtest creates test files in the directories specified by the user and tries to read 19jailcheck creates test files in the directories specified by the user and tries to read
21them from inside the sandbox. 20them from inside the sandbox.
22.TP 21.TP
23\fB4. AppArmor test 22\fB4. AppArmor test
24.TP 23.TP
25\fB5. Seccomp test 24\fB5. Seccomp test
26.TP 25.TP
26\fB6. Networking test
27.TP
27The program is started as root using sudo. 28The program is started as root using sudo.
28 29
29.SH OPTIONS 30.SH OPTIONS
@@ -49,7 +50,7 @@ It is followed by relevant sandbox information, such as the virtual directories
49 50
50.SH EXAMPLE 51.SH EXAMPLE
51 52
52$ sudo jailtest 53$ sudo jailcheck
53.br 54.br
542014:netblue::firejail /usr/bin/gimp 552014:netblue::firejail /usr/bin/gimp
55.br 56.br
@@ -57,6 +58,8 @@ $ sudo jailtest
57.br 58.br
58 Warning: I can run programs in /home/netblue 59 Warning: I can run programs in /home/netblue
59.br 60.br
61 Networking: disabled
62.br
60 63
61.br 64.br
622055:netblue::firejail /usr/bin/ssh -X netblue@x.y.z.net 652055:netblue::firejail /usr/bin/ssh -X netblue@x.y.z.net
@@ -65,12 +68,16 @@ $ sudo jailtest
65.br 68.br
66 Warning: I can read ~/.ssh 69 Warning: I can read ~/.ssh
67.br 70.br
71 Networking: enabled
72.br
68 73
69.br 74.br
702186:netblue:libreoffice:firejail --appimage /opt/LibreOffice-fresh.appimage 752186:netblue:libreoffice:firejail --appimage /opt/LibreOffice-fresh.appimage
71.br 76.br
72 Virtual dirs: /tmp, /var/tmp, /dev, 77 Virtual dirs: /tmp, /var/tmp, /dev,
73.br 78.br
79 Networking: enabled
80.br
74 81
75.br 82.br
7626090:netblue::/usr/bin/firejail /opt/firefox/firefox 8326090:netblue::/usr/bin/firejail /opt/firefox/firefox
@@ -79,6 +86,8 @@ $ sudo jailtest
79.br 86.br
80 /run/user/1000, 87 /run/user/1000,
81.br 88.br
89 Networking: enabled
90.br
82 91
83.br 92.br
8426160:netblue:tor:firejail --private=~/tor-browser_en-US ./start-tor 9326160:netblue:tor:firejail --private=~/tor-browser_en-US ./start-tor
@@ -91,6 +100,8 @@ $ sudo jailtest
91.br 100.br
92 Warning: I can run programs in /home/netblue 101 Warning: I can run programs in /home/netblue
93.br 102.br
103 Networking: enabled
104.br
94 105
95 106
96.SH LICENSE 107.SH LICENSE
diff --git a/src/man/preproc.awk b/src/man/preproc.awk
index 1ce5c82de..d3a2b71c9 100755
--- a/src/man/preproc.awk
+++ b/src/man/preproc.awk
@@ -1,6 +1,6 @@
1#!/usr/bin/gawk -E 1#!/usr/bin/gawk -E
2 2
3# Copyright (c) 2019-2021 rusty-snake 3# Copyright (c) 2019-2022 rusty-snake
4# 4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy 5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal 6# of this software and associated documentation files (the "Software"), to deal
diff --git a/src/profstats/Makefile.in b/src/profstats/Makefile.in
index e025f5939..fa1b4f200 100644
--- a/src/profstats/Makefile.in
+++ b/src/profstats/Makefile.in
@@ -3,7 +3,7 @@ all: profstats
3 3
4include ../common.mk 4include ../common.mk
5 5
6%.o : %.c $(H_FILE_LIST) 6%.o : %.c $(H_FILE_LIST) ../include/common.h
7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@ 7 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(INCLUDE) -c $< -o $@
8 8
9profstats: $(OBJS) 9profstats: $(OBJS)
diff --git a/src/profstats/main.c b/src/profstats/main.c
index 5035280b1..595a94c11 100644
--- a/src/profstats/main.c
+++ b/src/profstats/main.c
@@ -1,5 +1,5 @@
1 /* 1 /*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -17,10 +17,8 @@
17 * with this program; if not, write to the Free Software Foundation, Inc., 17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/ 19*/
20#include <stdio.h> 20
21#include <stdlib.h> 21#include "../include/common.h"
22#include <string.h>
23#include <assert.h>
24 22
25#define MAXBUF 2048 23#define MAXBUF 2048
26// stats 24// stats
@@ -46,6 +44,7 @@ static int cnt_whitelistusrshare = 0; // include whitelist-usr-share-common.inc
46static int cnt_ssh = 0; 44static int cnt_ssh = 0;
47static int cnt_mdwx = 0; 45static int cnt_mdwx = 0;
48static int cnt_whitelisthome = 0; 46static int cnt_whitelisthome = 0;
47static int cnt_noroot = 0;
49 48
50static int level = 0; 49static int level = 0;
51static int arg_debug = 0; 50static int arg_debug = 0;
@@ -65,27 +64,31 @@ static int arg_mdwx = 0;
65static int arg_dbus_system_none = 0; 64static int arg_dbus_system_none = 0;
66static int arg_dbus_user_none = 0; 65static int arg_dbus_user_none = 0;
67static int arg_whitelisthome = 0; 66static int arg_whitelisthome = 0;
68 67static int arg_noroot = 0;
68static int arg_print_blacklist = 0;
69static int arg_print_whitelist = 0;
69 70
70static char *profile = NULL; 71static char *profile = NULL;
71 72
72
73static void usage(void) { 73static void usage(void) {
74 printf("proftool - print profile statistics\n"); 74 printf("profstats - print profile statistics\n");
75 printf("Usage: proftool [options] file[s]\n"); 75 printf("Usage: profstats [options] file[s]\n");
76 printf("Options:\n"); 76 printf("Options:\n");
77 printf(" --apparmor - print profiles without apparmor\n"); 77 printf(" --apparmor - print profiles without apparmor\n");
78 printf(" --caps - print profiles without caps\n"); 78 printf(" --caps - print profiles without caps\n");
79 printf(" --dbus-system-none - profiles without \"dbus-system none\"\n"); 79 printf(" --dbus-system-none - print profiles without \"dbus-system none\"\n");
80 printf(" --dbus-user-none - profiles without \"dbus-user none\"\n"); 80 printf(" --dbus-user-none - print profiles without \"dbus-user none\"\n");
81 printf(" --ssh - print profiles without \"include disable-common.inc\"\n"); 81 printf(" --ssh - print profiles without \"include disable-common.inc\"\n");
82 printf(" --noexec - print profiles without \"include disable-exec.inc\"\n"); 82 printf(" --noexec - print profiles without \"include disable-exec.inc\"\n");
83 printf(" --noroot - print profiles without \"noroot\"\n");
83 printf(" --private-bin - print profiles without private-bin\n"); 84 printf(" --private-bin - print profiles without private-bin\n");
84 printf(" --private-dev - print profiles without private-dev\n"); 85 printf(" --private-dev - print profiles without private-dev\n");
85 printf(" --private-etc - print profiles without private-etc\n"); 86 printf(" --private-etc - print profiles without private-etc\n");
86 printf(" --private-tmp - print profiles without private-tmp\n"); 87 printf(" --private-tmp - print profiles without private-tmp\n");
88 printf(" --print-blacklist - print all --blacklist for a profile\n");
89 printf(" --print-whitelist - print all --private and --whitelist for a profile\n");
87 printf(" --seccomp - print profiles without seccomp\n"); 90 printf(" --seccomp - print profiles without seccomp\n");
88 printf(" --memory-deny-write-execute - profile without \"memory-deny-write-execute\"\n"); 91 printf(" --memory-deny-write-execute - print profiles without \"memory-deny-write-execute\"\n");
89 printf(" --whitelist-home - print profiles whitelisting home directory\n"); 92 printf(" --whitelist-home - print profiles whitelisting home directory\n");
90 printf(" --whitelist-var - print profiles without \"include whitelist-var-common.inc\"\n"); 93 printf(" --whitelist-var - print profiles without \"include whitelist-var-common.inc\"\n");
91 printf(" --whitelist-runuser - print profiles without \"include whitelist-runuser-common.inc\" or \"blacklist ${RUNUSER}\"\n"); 94 printf(" --whitelist-runuser - print profiles without \"include whitelist-runuser-common.inc\" or \"blacklist ${RUNUSER}\"\n");
@@ -94,8 +97,9 @@ static void usage(void) {
94 printf("\n"); 97 printf("\n");
95} 98}
96 99
97void process_file(const char *fname) { 100static void process_file(char *fname) {
98 assert(fname); 101 assert(fname);
102 char *tmpfname = NULL;
99 103
100 if (arg_debug) 104 if (arg_debug)
101 printf("processing #%s#\n", fname); 105 printf("processing #%s#\n", fname);
@@ -104,9 +108,19 @@ void process_file(const char *fname) {
104 108
105 FILE *fp = fopen(fname, "r"); 109 FILE *fp = fopen(fname, "r");
106 if (!fp) { 110 if (!fp) {
107 fprintf(stderr, "Warning: cannot open %s, while processing %s\n", fname, profile); 111 // the file was not found in the current directory
108 level--; 112 // look for it in /etc/firejail directory
109 return; 113 if (asprintf(&tmpfname, "%s/%s", SYSCONFDIR, fname) == -1)
114 errExit("asprintf");
115
116 fp = fopen(tmpfname, "r");
117 if (!fp) {
118 fprintf(stderr, "Warning: cannot open %s or %s, while processing %s\n", fname, tmpfname, profile);
119 free(tmpfname);
120 level--;
121 return;
122 }
123 fname = tmpfname;
110 } 124 }
111 125
112 int have_include_local = 0; 126 int have_include_local = 0;
@@ -122,12 +136,26 @@ void process_file(const char *fname) {
122 if (*ptr == '\n' || *ptr == '#') 136 if (*ptr == '\n' || *ptr == '#')
123 continue; 137 continue;
124 138
139 if (arg_print_blacklist) {
140 if (strncmp(ptr, "blacklist", 9) == 0 ||
141 strncmp(ptr, "noblacklist", 11) == 0)
142 printf("%s: %s\n", fname, ptr);
143 }
144 else if (arg_print_whitelist) {
145 if (strncmp(ptr, "whitelist", 9) == 0 ||
146 strncmp(ptr, "nowhitelist", 11) == 0 ||
147 strncmp(ptr, "private", 7) == 0)
148 printf("%s: %s\n", fname, ptr);
149 }
150
125 if (strncmp(ptr, "seccomp", 7) == 0) 151 if (strncmp(ptr, "seccomp", 7) == 0)
126 cnt_seccomp++; 152 cnt_seccomp++;
127 else if (strncmp(ptr, "caps", 4) == 0) 153 else if (strncmp(ptr, "caps", 4) == 0)
128 cnt_caps++; 154 cnt_caps++;
129 else if (strncmp(ptr, "include disable-exec.inc", 24) == 0) 155 else if (strncmp(ptr, "include disable-exec.inc", 24) == 0)
130 cnt_noexec++; 156 cnt_noexec++;
157 else if (strncmp(ptr, "noroot", 6) == 0)
158 cnt_noroot++;
131 else if (strncmp(ptr, "include whitelist-var-common.inc", 32) == 0) 159 else if (strncmp(ptr, "include whitelist-var-common.inc", 32) == 0)
132 cnt_whitelistvar++; 160 cnt_whitelistvar++;
133 else if (strncmp(ptr, "include whitelist-runuser-common.inc", 36) == 0 || 161 else if (strncmp(ptr, "include whitelist-runuser-common.inc", 36) == 0 ||
@@ -185,6 +213,8 @@ void process_file(const char *fname) {
185 if (!have_include_local) 213 if (!have_include_local)
186 printf("No include .local found in %s\n", fname); 214 printf("No include .local found in %s\n", fname);
187 level--; 215 level--;
216 if (tmpfname)
217 free(tmpfname);
188} 218}
189 219
190int main(int argc, char **argv) { 220int main(int argc, char **argv) {
@@ -212,6 +242,8 @@ int main(int argc, char **argv) {
212 arg_mdwx = 1; 242 arg_mdwx = 1;
213 else if (strcmp(argv[i], "--noexec") == 0) 243 else if (strcmp(argv[i], "--noexec") == 0)
214 arg_noexec = 1; 244 arg_noexec = 1;
245 else if (strcmp(argv[i], "--noroot") == 0)
246 arg_noroot = 1;
215 else if (strcmp(argv[i], "--private-bin") == 0) 247 else if (strcmp(argv[i], "--private-bin") == 0)
216 arg_privatebin = 1; 248 arg_privatebin = 1;
217 else if (strcmp(argv[i], "--private-dev") == 0) 249 else if (strcmp(argv[i], "--private-dev") == 0)
@@ -220,6 +252,10 @@ int main(int argc, char **argv) {
220 arg_privatetmp = 1; 252 arg_privatetmp = 1;
221 else if (strcmp(argv[i], "--private-etc") == 0) 253 else if (strcmp(argv[i], "--private-etc") == 0)
222 arg_privateetc = 1; 254 arg_privateetc = 1;
255 else if (strcmp(argv[i], "--print-blacklist") == 0)
256 arg_print_blacklist = 1;
257 else if (strcmp(argv[i], "--print-whitelist") == 0)
258 arg_print_whitelist = 1;
223 else if (strcmp(argv[i], "--whitelist-home") == 0) 259 else if (strcmp(argv[i], "--whitelist-home") == 0)
224 arg_whitelisthome = 1; 260 arg_whitelisthome = 1;
225 else if (strcmp(argv[i], "--whitelist-var") == 0) 261 else if (strcmp(argv[i], "--whitelist-var") == 0)
@@ -256,6 +292,7 @@ int main(int argc, char **argv) {
256 int caps = cnt_caps; 292 int caps = cnt_caps;
257 int apparmor = cnt_apparmor; 293 int apparmor = cnt_apparmor;
258 int noexec = cnt_noexec; 294 int noexec = cnt_noexec;
295 int noroot = cnt_noroot;
259 int privatebin = cnt_privatebin; 296 int privatebin = cnt_privatebin;
260 int privatetmp = cnt_privatetmp; 297 int privatetmp = cnt_privatetmp;
261 int privatedev = cnt_privatedev; 298 int privatedev = cnt_privatedev;
@@ -313,6 +350,8 @@ int main(int argc, char **argv) {
313 printf("No seccomp found in %s\n", argv[i]); 350 printf("No seccomp found in %s\n", argv[i]);
314 if (arg_noexec && noexec == cnt_noexec) 351 if (arg_noexec && noexec == cnt_noexec)
315 printf("No include disable-exec.inc found in %s\n", argv[i]); 352 printf("No include disable-exec.inc found in %s\n", argv[i]);
353 if (arg_noroot && noroot == cnt_noroot)
354 printf("No noroot found in %s\n", argv[i]);
316 if (arg_privatedev && privatedev == cnt_privatedev) 355 if (arg_privatedev && privatedev == cnt_privatedev)
317 printf("No private-dev found in %s\n", argv[i]); 356 printf("No private-dev found in %s\n", argv[i]);
318 if (arg_privatebin && privatebin == cnt_privatebin) 357 if (arg_privatebin && privatebin == cnt_privatebin)
@@ -337,6 +376,9 @@ int main(int argc, char **argv) {
337 assert(level == 0); 376 assert(level == 0);
338 } 377 }
339 378
379 if (arg_print_blacklist || arg_print_whitelist)
380 return 0;
381
340 printf("\n"); 382 printf("\n");
341 printf("Stats:\n"); 383 printf("Stats:\n");
342 printf(" profiles\t\t\t%d\n", cnt_profiles); 384 printf(" profiles\t\t\t%d\n", cnt_profiles);
@@ -346,6 +388,7 @@ int main(int argc, char **argv) {
346 printf(" seccomp\t\t\t%d\n", cnt_seccomp); 388 printf(" seccomp\t\t\t%d\n", cnt_seccomp);
347 printf(" capabilities\t\t%d\n", cnt_caps); 389 printf(" capabilities\t\t%d\n", cnt_caps);
348 printf(" noexec\t\t\t%d (include disable-exec.inc)\n", cnt_noexec); 390 printf(" noexec\t\t\t%d (include disable-exec.inc)\n", cnt_noexec);
391 printf(" noroot\t\t\t%d\n", cnt_noroot);
349 printf(" memory-deny-write-execute\t%d\n", cnt_mdwx); 392 printf(" memory-deny-write-execute\t%d\n", cnt_mdwx);
350 printf(" apparmor\t\t\t%d\n", cnt_apparmor); 393 printf(" apparmor\t\t\t%d\n", cnt_apparmor);
351 printf(" private-bin\t\t\t%d\n", cnt_privatebin); 394 printf(" private-bin\t\t\t%d\n", cnt_privatebin);
diff --git a/src/tools/check-caps.sh b/src/tools/check-caps.sh
index b7026b1cd..62c3b9066 100755
--- a/src/tools/check-caps.sh
+++ b/src/tools/check-caps.sh
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# This file is part of Firejail project 2# This file is part of Firejail project
3# Copyright (C) 2014-2021 Firejail Authors 3# Copyright (C) 2014-2022 Firejail Authors
4# License GPL v2 4# License GPL v2
5 5
6if [ $# -eq 0 ] 6if [ $# -eq 0 ]
diff --git a/src/tools/extract_caps.c b/src/tools/extract_caps.c
index 8da9c452b..5e5b3cdc6 100644
--- a/src/tools/extract_caps.c
+++ b/src/tools/extract_caps.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/tools/extract_errnos.sh b/src/tools/extract_errnos.sh
index 34c416b04..bb430b3e1 100644
--- a/src/tools/extract_errnos.sh
+++ b/src/tools/extract_errnos.sh
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# This file is part of Firejail project 2# This file is part of Firejail project
3# Copyright (C) 2014-2021 Firejail Authors 3# Copyright (C) 2014-2022 Firejail Authors
4# License GPL v2 4# License GPL v2
5 5
6echo -e "#include <errno.h>\n#include <attr/xattr.h>" | \ 6echo -e "#include <errno.h>\n#include <attr/xattr.h>" | \
diff --git a/src/tools/extract_seccomp.c b/src/tools/extract_seccomp.c
index b5f92d2df..6b644796b 100644
--- a/src/tools/extract_seccomp.c
+++ b/src/tools/extract_seccomp.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/tools/extract_syscalls.c b/src/tools/extract_syscalls.c
index 9159b6576..f77a84123 100644
--- a/src/tools/extract_syscalls.c
+++ b/src/tools/extract_syscalls.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/tools/mkcoverit.sh b/src/tools/mkcoverit.sh
index 86d798a11..c7a57cd21 100755
--- a/src/tools/mkcoverit.sh
+++ b/src/tools/mkcoverit.sh
@@ -1,6 +1,6 @@
1#!/bin/bash 1#!/bin/bash
2# This file is part of Firejail project 2# This file is part of Firejail project
3# Copyright (C) 2014-2021 Firejail Authors 3# Copyright (C) 2014-2022 Firejail Authors
4# License GPL v2 4# License GPL v2
5 5
6# unpack firejail archive 6# unpack firejail archive
diff --git a/src/tools/testuid.c b/src/tools/testuid.c
index a18d57d5e..1bc617522 100644
--- a/src/tools/testuid.c
+++ b/src/tools/testuid.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/tools/ttytest.c b/src/tools/ttytest.c
index 0f72753bc..9e40d289a 100644
--- a/src/tools/ttytest.c
+++ b/src/tools/ttytest.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/tools/unixsocket.c b/src/tools/unixsocket.c
index c4ecabca7..bd638269d 100644
--- a/src/tools/unixsocket.c
+++ b/src/tools/unixsocket.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2021 Firejail Authors 2 * Copyright (C) 2014-2022 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in
index fd27bb35f..f7cd3cdff 100644
--- a/src/zsh_completion/_firejail.in
+++ b/src/zsh_completion/_firejail.in
@@ -62,6 +62,9 @@ _firejail_args=(
62 '--tree[print a tree of all sandboxed processes]' 62 '--tree[print a tree of all sandboxed processes]'
63 '--version[print program version and exit]' 63 '--version[print program version and exit]'
64 64
65 '--ids-check[verify file system]'
66 '--ids-init[initialize IDS database]'
67
65 '--debug[print sandbox debug messages]' 68 '--debug[print sandbox debug messages]'
66 '--debug-blacklists[debug blacklisting]' 69 '--debug-blacklists[debug blacklisting]'
67 '--debug-caps[print all recognized capabilities]' 70 '--debug-caps[print all recognized capabilities]'
@@ -91,6 +94,7 @@ _firejail_args=(
91 '--cgroup=-[place the sandbox in the specified control group]: :' 94 '--cgroup=-[place the sandbox in the specified control group]: :'
92 '--cpu=-[set cpu affinity]: :->cpus' 95 '--cpu=-[set cpu affinity]: :->cpus'
93 "--deterministic-exit-code[always exit with first child's status code]" 96 "--deterministic-exit-code[always exit with first child's status code]"
97 '--deterministic-shutdown[terminate orphan processes]'
94 '*--dns=-[set DNS server]: :' 98 '*--dns=-[set DNS server]: :'
95 '*--env=-[set environment variable]: :' 99 '*--env=-[set environment variable]: :'
96 '--hostname=-[set sandbox hostname]: :' 100 '--hostname=-[set sandbox hostname]: :'
@@ -98,9 +102,11 @@ _firejail_args=(
98 '*--ignore=-[ignore command in profile files]: :' 102 '*--ignore=-[ignore command in profile files]: :'
99 '--ipc-namespace[enable a new IPC namespace]' 103 '--ipc-namespace[enable a new IPC namespace]'
100 '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails' 104 '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails'
105 '--keep-config-pulse[disable automatic ~/.config/pulse init]'
101 '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' 106 '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]'
107 '--keep-fd[inherit open file descriptors to sandbox]'
102 '--keep-var-tmp[/var/tmp directory is untouched]' 108 '--keep-var-tmp[/var/tmp directory is untouched]'
103 '--machine-id[preserve /etc/machine-id]' 109 '--machine-id[spoof /etc/machine-id with a random id]'
104 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' 110 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]'
105 '*--mkdir=-[create a directory]:' 111 '*--mkdir=-[create a directory]:'
106 '*--mkfile=-[create a file]:' 112 '*--mkfile=-[create a file]:'
@@ -116,7 +122,9 @@ _firejail_args=(
116 '--nodvd[disable DVD and audio CD devices]' 122 '--nodvd[disable DVD and audio CD devices]'
117 '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files' 123 '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files'
118 '--nogroups[disable supplementary groups]' 124 '--nogroups[disable supplementary groups]'
125 '--noinput[disable input devices]'
119 '--nonewprivs[sets the NO_NEW_PRIVS prctl]' 126 '--nonewprivs[sets the NO_NEW_PRIVS prctl]'
127 '--noprinters[disable printers]'
120 '--nosound[disable sound system]' 128 '--nosound[disable sound system]'
121 '--nou2f[disable U2F devices]' 129 '--nou2f[disable U2F devices]'
122 '--novideo[disable video devices]' 130 '--novideo[disable video devices]'
@@ -213,7 +221,7 @@ _firejail_args=(
213 '--netfilter.print=-[print the firewall name|pid]: :_all_firejails' 221 '--netfilter.print=-[print the firewall name|pid]: :_all_firejails'
214 '--netfilter6=-[enable IPv6 firewall]: :' 222 '--netfilter6=-[enable IPv6 firewall]: :'
215 '--netfilter6.print=-[print the IPv6 firewall name|pid]: :_all_firejails' 223 '--netfilter6.print=-[print the IPv6 firewall name|pid]: :_all_firejails'
216 '--netmask=-[define a network mask when dealing with unconfigured parrent interfaces]: :' 224 '--netmask=-[define a network mask when dealing with unconfigured parent interfaces]: :'
217 '--netns=-[Run the program in a named, persistent network namespace]: :' 225 '--netns=-[Run the program in a named, persistent network namespace]: :'
218 '--netstats[monitor network statistics]' 226 '--netstats[monitor network statistics]'
219 '--interface=-[move interface in sandbox]: :' 227 '--interface=-[move interface in sandbox]: :'
@@ -249,10 +257,8 @@ _firejail_args=(
249 '*--tmpfs=-[mount a tmpfs filesystem on directory dirname]: :_files -/' 257 '*--tmpfs=-[mount a tmpfs filesystem on directory dirname]: :_files -/'
250#endif 258#endif
251 259
252#ifdef HAVE_WHITELIST
253 '*--nowhitelist=-[disable whitelist for file or directory]: :_files' 260 '*--nowhitelist=-[disable whitelist for file or directory]: :_files'
254 '*--whitelist=-[whitelist directory or file]: :_files' 261 '*--whitelist=-[whitelist directory or file]: :_files'
255#endif
256 262
257#ifdef HAVE_X11 263#ifdef HAVE_X11
258 '--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]' 264 '--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]'