aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--README.md42
-rw-r--r--RELNOTES12
-rw-r--r--etc/disable-programs.inc4
-rw-r--r--etc/engrampa.profile1
-rw-r--r--etc/firefox.profile4
-rw-r--r--etc/firejail.config11
-rw-r--r--etc/geeqie.profile27
-rw-r--r--etc/vlc.profile2
-rw-r--r--platform/debian/conffiles2
-rw-r--r--src/bash_completion/firejail.bash_completion8
-rw-r--r--src/faudit/seccomp.c2
-rw-r--r--src/firecfg/firecfg.config1
-rw-r--r--src/firejail/caps.c6
-rw-r--r--src/firejail/checkcfg.c60
-rw-r--r--src/firejail/firejail.h8
-rw-r--r--src/firejail/fs.c23
-rw-r--r--src/firejail/fs_bin.c5
-rw-r--r--src/firejail/fs_mkdir.c10
-rw-r--r--src/firejail/fs_whitelist.c146
-rw-r--r--src/firejail/main.c20
-rw-r--r--src/firejail/no_sandbox.c2
-rw-r--r--src/firejail/output.c66
-rw-r--r--src/firejail/profile.c43
-rw-r--r--src/firejail/sandbox.c20
-rw-r--r--src/firejail/usage.c2
-rw-r--r--src/firejail/x11.c664
-rw-r--r--src/firemon/arp.c2
-rw-r--r--src/firemon/caps.c6
-rw-r--r--src/firemon/cgroup.c2
-rw-r--r--src/firemon/cpu.c6
-rw-r--r--src/firemon/interface.c2
-rw-r--r--src/firemon/list.c2
-rw-r--r--src/firemon/procevent.c21
-rw-r--r--src/firemon/route.c2
-rw-r--r--src/firemon/seccomp.c6
-rw-r--r--src/firemon/usage.c1
-rw-r--r--src/firemon/x11.c2
-rw-r--r--src/man/firejail-profile.txt19
-rw-r--r--src/man/firejail.txt74
-rw-r--r--src/man/firemon.txt3
-rwxr-xr-xtest/apps-x11-xorg/firefox.exp4
-rwxr-xr-xtest/apps-x11-xorg/icedove.exp4
-rwxr-xr-xtest/apps-x11-xorg/transmission-gtk.exp4
-rwxr-xr-xtest/environment/nice.exp41
-rwxr-xr-xtest/environment/output.exp22
-rwxr-xr-xtest/filters/debug.exp45
-rwxr-xr-xtest/filters/filters.sh3
-rwxr-xr-xtest/fs/whitelist.exp2
-rwxr-xr-xtest/root/checkcfg.exp106
-rwxr-xr-xtest/root/git.exp51
-rwxr-xr-xtest/root/root.sh6
-rwxr-xr-xtest/utils/join.exp8
53 files changed, 1246 insertions, 391 deletions
diff --git a/README b/README
index 71e329792..17d5f1b3e 100644
--- a/README
+++ b/README
@@ -126,6 +126,7 @@ Zack Weinberg (https://github.com/zackw)
126 - rework fcopy, --follow-link support in fcopy 126 - rework fcopy, --follow-link support in fcopy
127 - follow link support in --private-bin 127 - follow link support in --private-bin
128 - wait_for_other function rewrite 128 - wait_for_other function rewrite
129 - xvfb X11 server support
129Austin S. Hemmelgarn (https://github.com/Ferroin) 130Austin S. Hemmelgarn (https://github.com/Ferroin)
130 - unbound profile update 131 - unbound profile update
131Igor Bukanov (https://github.com/ibukanov) 132Igor Bukanov (https://github.com/ibukanov)
@@ -154,6 +155,7 @@ SYN-cook (https://github.com/SYN-cook)
154 - disable-common.inc fixes 155 - disable-common.inc fixes
155 - blacklist GNOME keyring and Konqueror 156 - blacklist GNOME keyring and Konqueror
156 - fixed Keepass(x) profiles 157 - fixed Keepass(x) profiles
158 - Engrampa profile
157thewisenerd (https://github.com/thewisenerd) 159thewisenerd (https://github.com/thewisenerd)
158 - appimage: pass commandline arguments 160 - appimage: pass commandline arguments
159KOLANICH (https://github.com/KOLANICH) 161KOLANICH (https://github.com/KOLANICH)
diff --git a/README.md b/README.md
index 687877c73..62cb0daaf 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,46 @@ Added AppImage type 2 support, and support for passing command line arguments to
145 145
146 $ firejail --git-uninstall 146 $ firejail --git-uninstall
147 147
148 --x11=xvfb
149 Start Xvfb X11 server and attach the sandbox to this server.
150 Xvfb, short for X virtual framebuffer, performs all graphical
151 operations in memory without showing any screen output. Xvfb is
152 mainly used for remote access and software testing on headless
153 servers.
154
155 On Debian platforms Xvfb is installed with the command sudo apt-
156 get install xvfb. This feature is not available when running as
157 root.
158
159 Example: remote VNC access
160
161 On the server we start a sandbox using Xvfb and openbox window
162 manager. The default size of Xvfb screen is 800x600 - it can be
163 changed in /etc/firejail/firejail.config (xvfb-screen). Some
164 sort of networking (--net) is required in order to isolate the
165 abstract sockets used by other X servers.
166
167 $ firejail --net=none --x11=xvfb openbox
168
169 *** Attaching to Xvfb display 792 ***
170
171 Reading profile /etc/firejail/openbox.profile
172 Reading profile /etc/firejail/disable-common.inc
173 Reading profile /etc/firejail/disable-common.local
174 Parent pid 5400, child pid 5401
175
176 On the server we also start a VNC server and attach it to the
177 display handled by our Xvfb server (792).
178
179 $ x11vnc -display :792
180
181 On the client machine we start a VNC viewer and use it to con‐
182 nect to our server:
183
184 $ vncviewer
185
186 --nowhitelist=dirname_or_filename
187 Disable whitelist for this directory or file.
148 188
149````` 189`````
150## New Profiles 190## New Profiles
@@ -155,4 +195,4 @@ goobox, gpa, gpg, gpg-agent, highlight, img2txt, k3b, kate, lynx, mediainfo, nau
155simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget, 195simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget,
156xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5, 196xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5,
157PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla, FossaMail, Uzbl browser, xmms, iridium browser, 197PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla, FossaMail, Uzbl browser, xmms, iridium browser,
158Kino, Thunar 198Kino, Thunar, Geeqie, Engrampa
diff --git a/RELNOTES b/RELNOTES
index 54078875b..122d5cfbe 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -13,8 +13,6 @@ firejail (0.9.45) baseline; urgency=low
13 * security: split seccomp filter code configuration in a separate executable 13 * security: split seccomp filter code configuration in a separate executable
14 * security: split file copying in private option in a separate executable 14 * security: split file copying in private option in a separate executable
15 * security: root exploit found by Sebastian Krahmer (CVE-2017-5180) 15 * security: root exploit found by Sebastian Krahmer (CVE-2017-5180)
16 * security: ~/.pki directory whitelisted and later blacklisted. This affects
17 most browsers, and disables the custom certificates installed by the user.
18 * feature: disable gnupg and systemd directories under /run/user 16 * feature: disable gnupg and systemd directories under /run/user
19 * feature: test coverage (gcov) support 17 * feature: test coverage (gcov) support
20 * feature: allow root user access to /dev/shm (--noblacklist=/dev/shm) 18 * feature: allow root user access to /dev/shm (--noblacklist=/dev/shm)
@@ -31,12 +29,20 @@ firejail (0.9.45) baseline; urgency=low
31 * feature: added a number o Python scripts for handling sandboxes 29 * feature: added a number o Python scripts for handling sandboxes
32 * feature: allow local customization using .local files under /etc/firejail 30 * feature: allow local customization using .local files under /etc/firejail
33 * feature: follow-symlink-as-user runtime config option in /etc/firejail/firejail.config 31 * feature: follow-symlink-as-user runtime config option in /etc/firejail/firejail.config
32 * feature: follow-symlink-private-bin option in /etc/firejail/firejail.config
33 * feature: xvfb X11 server support (--x11=xvfb)
34 * feature: allow /tmp directory in mkdir and mkfile profile commands
35 * feature: implemented --noblacklist command, profile support
36 * feature: config support to disable access to /mnt and /media (disable-mnt)
37 * feature: allow tmpfs for regular users for files in home directory
38 * feature: mount a tmpfs on top of ~/.cache directory by default
39 * feature: config support to disable tmpfs mounting on ~/.cache (cache-tmpfs)
34 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire, 40 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire,
35 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma, 41 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma,
36 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator, 42 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator,
37 * new profiles: Gnome Contacts, JD-GUI, Lollypop, MultiMC5, PDFSam, Pithos, 43 * new profiles: Gnome Contacts, JD-GUI, Lollypop, MultiMC5, PDFSam, Pithos,
38 * new profies: Xonotic, wireshark, keepassx2, QupZilla, FossaMail, 44 * new profies: Xonotic, wireshark, keepassx2, QupZilla, FossaMail,
39 * new profiles: Uzbl browser, iridium browser, Thunar 45 * new profiles: Uzbl browser, iridium browser, Thunar, Geeqie, Engrampa
40 * bugfixes 46 * bugfixes
41 -- netblue30 <netblue30@yahoo.com> Sun, 23 Oct 2016 08:00:00 -0500 47 -- netblue30 <netblue30@yahoo.com> Sun, 23 Oct 2016 08:00:00 -0500
42 48
diff --git a/etc/disable-programs.inc b/etc/disable-programs.inc
index 00473de95..a5c7502db 100644
--- a/etc/disable-programs.inc
+++ b/etc/disable-programs.inc
@@ -30,6 +30,7 @@ blacklist ${HOME}/.cache/darktable
30blacklist ${HOME}/.cache/epiphany 30blacklist ${HOME}/.cache/epiphany
31blacklist ${HOME}/.cache/evolution 31blacklist ${HOME}/.cache/evolution
32blacklist ${HOME}/.cache/gajim 32blacklist ${HOME}/.cache/gajim
33blacklist ${HOME}/.cache/geeqie
33blacklist ${HOME}/.cache/google-chrome 34blacklist ${HOME}/.cache/google-chrome
34blacklist ${HOME}/.cache/google-chrome-beta 35blacklist ${HOME}/.cache/google-chrome-beta
35blacklist ${HOME}/.cache/google-chrome-unstable 36blacklist ${HOME}/.cache/google-chrome-unstable
@@ -103,6 +104,7 @@ blacklist ${HOME}/.config/evolution
103blacklist ${HOME}/.config/filezilla 104blacklist ${HOME}/.config/filezilla
104blacklist ${HOME}/.config/flowblade 105blacklist ${HOME}/.config/flowblade
105blacklist ${HOME}/.config/gajim 106blacklist ${HOME}/.config/gajim
107blacklist ${HOME}/.config/geeqie
106blacklist ${HOME}/.config/gedit 108blacklist ${HOME}/.config/gedit
107blacklist ${HOME}/.config/google-chrome 109blacklist ${HOME}/.config/google-chrome
108blacklist ${HOME}/.config/google-chrome-beta 110blacklist ${HOME}/.config/google-chrome-beta
@@ -221,6 +223,7 @@ blacklist ${HOME}/.local/share/epiphany
221blacklist ${HOME}/.local/share/evolution 223blacklist ${HOME}/.local/share/evolution
222blacklist ${HOME}/.local/share/feral-interactive 224blacklist ${HOME}/.local/share/feral-interactive
223blacklist ${HOME}/.local/share/gajim 225blacklist ${HOME}/.local/share/gajim
226blacklist ${HOME}/.local/share/geeqie
224blacklist ${HOME}/.local/share/gnome-2048 227blacklist ${HOME}/.local/share/gnome-2048
225blacklist ${HOME}/.local/share/gnome-chess 228blacklist ${HOME}/.local/share/gnome-chess
226blacklist ${HOME}/.local/share/gnome-music 229blacklist ${HOME}/.local/share/gnome-music
@@ -284,6 +287,7 @@ blacklist ${HOME}/.weechat
284blacklist ${HOME}/.wine 287blacklist ${HOME}/.wine
285blacklist ${HOME}/.wine64 288blacklist ${HOME}/.wine64
286blacklist ${HOME}/.xiphos 289blacklist ${HOME}/.xiphos
290blacklist ${HOME}/.xmms
287blacklist ${HOME}/.xonotic 291blacklist ${HOME}/.xonotic
288blacklist ${HOME}/.xpdfrc 292blacklist ${HOME}/.xpdfrc
289blacklist ${HOME}/.zoom 293blacklist ${HOME}/.zoom
diff --git a/etc/engrampa.profile b/etc/engrampa.profile
index b30b53085..da4872ca0 100644
--- a/etc/engrampa.profile
+++ b/etc/engrampa.profile
@@ -13,7 +13,6 @@ nogroups
13nonewprivs 13nonewprivs
14noroot 14noroot
15nosound 15nosound
16no3d
17protocol unix 16protocol unix
18seccomp 17seccomp
19netfilter 18netfilter
diff --git a/etc/firefox.profile b/etc/firefox.profile
index e323cac91..e2cfb9138 100644
--- a/etc/firefox.profile
+++ b/etc/firefox.profile
@@ -54,5 +54,5 @@ include /etc/firejail/whitelist-common.inc
54# experimental features 54# experimental features
55#private-bin firefox,which,sh,dbus-launch,dbus-send,env 55#private-bin firefox,which,sh,dbus-launch,dbus-send,env
56#private-etc passwd,group,hostname,hosts,localtime,nsswitch.conf,resolv.conf,xdg,gtk-2.0,gtk-3.0,X11,pango,fonts,firefox,mime.types,mailcap,asound.conf,pulse 56#private-etc passwd,group,hostname,hosts,localtime,nsswitch.conf,resolv.conf,xdg,gtk-2.0,gtk-3.0,X11,pango,fonts,firefox,mime.types,mailcap,asound.conf,pulse
57private-dev 57private-dev
58private-tmp 58#private-tmp
diff --git a/etc/firejail.config b/etc/firejail.config
index 766802a7d..0887e05b5 100644
--- a/etc/firejail.config
+++ b/etc/firejail.config
@@ -6,6 +6,9 @@
6# Enable or disable bind support, default enabled. 6# Enable or disable bind support, default enabled.
7# bind yes 7# bind yes
8 8
9# Enable mounting a tmpfs on top of ~/.cache directory, default enabled.
10# cache-tmpfs yes
11
9# Enable or disable chroot support, default enabled. 12# Enable or disable chroot support, default enabled.
10# chroot yes 13# chroot yes
11 14
@@ -14,6 +17,10 @@
14# and it will harden the rest of the chroot tree. 17# and it will harden the rest of the chroot tree.
15# chroot-desktop yes 18# chroot-desktop yes
16 19
20# Disable /mnt, /media, /run/mount and /run/media access. By default access
21# to these directories is enabled.
22# disable-mnt no
23
17# Enable or disable file transfer support, default enabled. 24# Enable or disable file transfer support, default enabled.
18# file-transfer yes 25# file-transfer yes
19 26
@@ -26,6 +33,10 @@
26# Enabled by default 33# Enabled by default
27# follow-symlink-as-user yes 34# follow-symlink-as-user yes
28 35
36# Follow symlink for private-bin command.
37# Disabled by default
38# follow-symlink-private-bin no
39
29# Force use of nonewprivs. This mitigates the possibility of 40# Force use of nonewprivs. This mitigates the possibility of
30# a user abusing firejail's features to trick a privileged (suid 41# a user abusing firejail's features to trick a privileged (suid
31# or file capabilities) process into loading code or configuration 42# or file capabilities) process into loading code or configuration
diff --git a/etc/geeqie.profile b/etc/geeqie.profile
new file mode 100644
index 000000000..57f942a50
--- /dev/null
+++ b/etc/geeqie.profile
@@ -0,0 +1,27 @@
1# This file is overwritten during software install.
2# Persistent customizations should go in a .local file.
3include /etc/firejail/geeqie.local
4
5# Firejail profile for Geeqie
6noblacklist ~/.cache/geeqie
7noblacklist ~/.config/geeqie
8noblacklist ~/.local/share/geeqie
9include /etc/firejail/disable-common.inc
10include /etc/firejail/disable-programs.inc
11include /etc/firejail/disable-devel.inc
12include /etc/firejail/disable-passwdmgr.inc
13
14caps.drop all
15nogroups
16nonewprivs
17noroot
18protocol unix
19seccomp
20nosound
21
22private-dev
23
24#Experimental:
25shell none
26#private-bin geeqie
27#private-etc X11
diff --git a/etc/vlc.profile b/etc/vlc.profile
index 9d1cdb4c8..0c96f0108 100644
--- a/etc/vlc.profile
+++ b/etc/vlc.profile
@@ -20,5 +20,5 @@ seccomp
20shell none 20shell none
21 21
22private-bin vlc,cvlc,nvlc,rvlc,qvlc,svlc 22private-bin vlc,cvlc,nvlc,rvlc,qvlc,svlc
23private-dev 23# private-dev
24private-tmp 24private-tmp
diff --git a/platform/debian/conffiles b/platform/debian/conffiles
index edaf1781b..c4868981b 100644
--- a/platform/debian/conffiles
+++ b/platform/debian/conffiles
@@ -249,3 +249,5 @@
249/etc/firejail/kino.profile 249/etc/firejail/kino.profile
250/etc/firejail/Thunar.profile 250/etc/firejail/Thunar.profile
251/etc/firejail/thunar.profile 251/etc/firejail/thunar.profile
252/etc/firejail/geeqie.profile
253/etc/firejail/engrampa.profile
diff --git a/src/bash_completion/firejail.bash_completion b/src/bash_completion/firejail.bash_completion
index 0f71c74dc..38548c42b 100644
--- a/src/bash_completion/firejail.bash_completion
+++ b/src/bash_completion/firejail.bash_completion
@@ -43,10 +43,18 @@ _firejail()
43 _filedir 43 _filedir
44 return 0 44 return 0
45 ;; 45 ;;
46 --noblacklist)
47 _filedir
48 return 0
49 ;;
46 --whitelist) 50 --whitelist)
47 _filedir 51 _filedir
48 return 0 52 return 0
49 ;; 53 ;;
54 --nowhitelist)
55 _filedir
56 return 0
57 ;;
50 --read-only) 58 --read-only)
51 _filedir 59 _filedir
52 return 0 60 return 0
diff --git a/src/faudit/seccomp.c b/src/faudit/seccomp.c
index fe814598b..1c188aa45 100644
--- a/src/faudit/seccomp.c
+++ b/src/faudit/seccomp.c
@@ -54,7 +54,7 @@ void seccomp_test(void) {
54 printf("BAD: seccomp disabled. Use \"firejail --seccomp\" to enable it.\n"); 54 printf("BAD: seccomp disabled. Use \"firejail --seccomp\" to enable it.\n");
55 } 55 }
56 else if (seccomp_status == 1) 56 else if (seccomp_status == 1)
57 printf("GOOD: seccomp strict mode - only read, write, _exit, and sigreturn are allowd.\n"); 57 printf("GOOD: seccomp strict mode - only read, write, _exit, and sigreturn are allowed.\n");
58 else if (seccomp_status == 2) { 58 else if (seccomp_status == 2) {
59 printf("GOOD: seccomp BPF enabled.\n"); 59 printf("GOOD: seccomp BPF enabled.\n");
60 60
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 7c959cd04..3c1ecc573 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -113,6 +113,7 @@ dolphin
113dragon 113dragon
114exiftool 114exiftool
115feh 115feh
116geeqie
116gjs 117gjs
117gnome-books 118gnome-books
118gnome-clocks 119gnome-clocks
diff --git a/src/firejail/caps.c b/src/firejail/caps.c
index 521187e3a..30693f7a0 100644
--- a/src/firejail/caps.c
+++ b/src/firejail/caps.c
@@ -183,8 +183,10 @@ static int caps_find_name(const char *name) {
183// return 1 if error, 0 if OK 183// return 1 if error, 0 if OK
184void caps_check_list(const char *clist, void (*callback)(int)) { 184void caps_check_list(const char *clist, void (*callback)(int)) {
185 // don't allow empty lists 185 // don't allow empty lists
186 if (clist == NULL || *clist == '\0') 186 if (clist == NULL || *clist == '\0') {
187 goto errexit; 187 fprintf(stderr, "Error: empty capabilities list\n");
188 exit(1);
189 }
188 190
189 // work on a copy of the string 191 // work on a copy of the string
190 char *str = strdup(clist); 192 char *str = strdup(clist);
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index dff892ea3..476ecbe10 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -27,6 +27,8 @@ static int initialized = 0;
27static int cfg_val[CFG_MAX]; 27static int cfg_val[CFG_MAX];
28char *xephyr_screen = "800x600"; 28char *xephyr_screen = "800x600";
29char *xephyr_extra_params = ""; 29char *xephyr_extra_params = "";
30char *xvfb_screen = "800x600x24";
31char *xvfb_extra_params = "";
30char *netfilter_default = NULL; 32char *netfilter_default = NULL;
31 33
32int checkcfg(int val) { 34int checkcfg(int val) {
@@ -41,9 +43,11 @@ int checkcfg(int val) {
41 for (i = 0; i < CFG_MAX; i++) 43 for (i = 0; i < CFG_MAX; i++)
42 cfg_val[i] = 1; // most of them are enabled by default 44 cfg_val[i] = 1; // most of them are enabled by default
43 cfg_val[CFG_RESTRICTED_NETWORK] = 0; // disabled by default 45 cfg_val[CFG_RESTRICTED_NETWORK] = 0; // disabled by default
44 cfg_val[CFG_FORCE_NONEWPRIVS] = 0; // disabled by default 46 cfg_val[CFG_FORCE_NONEWPRIVS] = 0;
45 cfg_val[CFG_PRIVATE_BIN_NO_LOCAL] = 0; // disabled by default 47 cfg_val[CFG_PRIVATE_BIN_NO_LOCAL] = 0;
46 cfg_val[CFG_FIREJAIL_PROMPT] = 0; // disabled by default 48 cfg_val[CFG_FIREJAIL_PROMPT] = 0;
49 cfg_val[CFG_FOLLOW_SYMLINK_PRIVATE_BIN] = 0;
50 cfg_val[CFG_DISABLE_MNT] = 0;
47 51
48 // open configuration file 52 // open configuration file
49 const char *fname = SYSCONFDIR "/firejail.config"; 53 const char *fname = SYSCONFDIR "/firejail.config";
@@ -70,8 +74,17 @@ int checkcfg(int val) {
70 if (!ptr) 74 if (!ptr)
71 continue; 75 continue;
72 76
77 // mount tmpfs on top of ~/.cache directory
78 if (strncmp(ptr, "cache-tmpfs ", 12) == 0) {
79 if (strcmp(ptr + 12, "yes") == 0)
80 cfg_val[CFG_CACHE_TMPFS] = 1;
81 else if (strcmp(ptr + 12, "no") == 0)
82 cfg_val[CFG_CACHE_TMPFS] = 0;
83 else
84 goto errout;
85 }
73 // file transfer 86 // file transfer
74 if (strncmp(ptr, "file-transfer ", 14) == 0) { 87 else if (strncmp(ptr, "file-transfer ", 14) == 0) {
75 if (strcmp(ptr + 14, "yes") == 0) 88 if (strcmp(ptr + 14, "yes") == 0)
76 cfg_val[CFG_FILE_TRANSFER] = 1; 89 cfg_val[CFG_FILE_TRANSFER] = 1;
77 else if (strcmp(ptr + 14, "no") == 0) 90 else if (strcmp(ptr + 14, "no") == 0)
@@ -133,6 +146,15 @@ int checkcfg(int val) {
133 else 146 else
134 goto errout; 147 goto errout;
135 } 148 }
149 // follow symlink in private-bin command
150 else if (strncmp(ptr, "follow-symlink-private-bin ", 27) == 0) {
151 if (strcmp(ptr + 27, "yes") == 0)
152 cfg_val[CFG_FOLLOW_SYMLINK_PRIVATE_BIN] = 1;
153 else if (strcmp(ptr + 27, "no") == 0)
154 cfg_val[CFG_FOLLOW_SYMLINK_PRIVATE_BIN] = 0;
155 else
156 goto errout;
157 }
136 // nonewprivs 158 // nonewprivs
137 else if (strncmp(ptr, "force-nonewprivs ", 17) == 0) { 159 else if (strncmp(ptr, "force-nonewprivs ", 17) == 0) {
138 if (strcmp(ptr + 17, "yes") == 0) 160 if (strcmp(ptr + 17, "yes") == 0)
@@ -234,6 +256,28 @@ int checkcfg(int val) {
234 errExit("strdup"); 256 errExit("strdup");
235 } 257 }
236 258
259 // Xvfb screen size
260 else if (strncmp(ptr, "xvfb-screen ", 12) == 0) {
261 // expecting three numbers separated by x's
262 unsigned int n1;
263 unsigned int n2;
264 unsigned int n3;
265 int rv = sscanf(ptr + 12, "%ux%ux%u", &n1, &n2, &n3);
266 if (rv != 3)
267 goto errout;
268 if (asprintf(&xvfb_screen, "%ux%ux%u", n1, n2, n3) == -1)
269 errExit("asprintf");
270 }
271
272 // Xvfb extra parameters
273 else if (strncmp(ptr, "xvfb-extra-params ", 18) == 0) {
274 if (*xvfb_extra_params != '\0')
275 goto errout;
276 xvfb_extra_params = strdup(ptr + 18);
277 if (!xvfb_extra_params)
278 errExit("strdup");
279 }
280
237 // quiet by default 281 // quiet by default
238 else if (strncmp(ptr, "quiet-by-default ", 17) == 0) { 282 else if (strncmp(ptr, "quiet-by-default ", 17) == 0) {
239 if (strcmp(ptr + 17, "yes") == 0) 283 if (strcmp(ptr + 17, "yes") == 0)
@@ -280,6 +324,14 @@ int checkcfg(int val) {
280 else 324 else
281 goto errout; 325 goto errout;
282 } 326 }
327 else if (strncmp(ptr, "disable-mnt ", 12) == 0) {
328 if (strcmp(ptr + 12, "yes") == 0)
329 cfg_val[CFG_DISABLE_MNT] = 1;
330 else if (strcmp(ptr + 12, "no") == 0)
331 cfg_val[CFG_DISABLE_MNT] = 0;
332 else
333 goto errout;
334 }
283 else 335 else
284 goto errout; 336 goto errout;
285 337
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index fbf83abb3..fa6ba5c6a 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -419,6 +419,7 @@ void profile_read(const char *fname);
419int profile_check_line(char *ptr, int lineno, const char *fname); 419int profile_check_line(char *ptr, int lineno, const char *fname);
420// add a profile entry in cfg.profile list; use str to populate the list 420// add a profile entry in cfg.profile list; use str to populate the list
421void profile_add(char *str); 421void profile_add(char *str);
422void fs_mnt(void);
422 423
423// list.c 424// list.c
424void list(void); 425void list(void);
@@ -644,13 +645,13 @@ void fs_mkdir(const char *name);
644void fs_mkfile(const char *name); 645void fs_mkfile(const char *name);
645 646
646// x11.c 647// x11.c
647extern int mask_x11_abstract_socket;
648void fs_x11(void); 648void fs_x11(void);
649int x11_display(void); 649int x11_display(void);
650void x11_start(int argc, char **argv); 650void x11_start(int argc, char **argv);
651void x11_start_xpra(int argc, char **argv); 651void x11_start_xpra(int argc, char **argv);
652void x11_start_xephyr(int argc, char **argv); 652void x11_start_xephyr(int argc, char **argv);
653void x11_block(void); 653void x11_block(void);
654void x11_start_xvfb(int argc, char **argv);
654 655
655// ls.c 656// ls.c
656enum { 657enum {
@@ -681,10 +682,15 @@ enum {
681 CFG_PRIVATE_BIN_NO_LOCAL, 682 CFG_PRIVATE_BIN_NO_LOCAL,
682 CFG_FIREJAIL_PROMPT, 683 CFG_FIREJAIL_PROMPT,
683 CFG_FOLLOW_SYMLINK_AS_USER, 684 CFG_FOLLOW_SYMLINK_AS_USER,
685 CFG_FOLLOW_SYMLINK_PRIVATE_BIN,
686 CFG_DISABLE_MNT,
687 CFG_CACHE_TMPFS,
684 CFG_MAX // this should always be the last entry 688 CFG_MAX // this should always be the last entry
685}; 689};
686extern char *xephyr_screen; 690extern char *xephyr_screen;
687extern char *xephyr_extra_params; 691extern char *xephyr_extra_params;
692extern char *xvfb_screen;
693extern char *xvfb_extra_params;
688extern char *netfilter_default; 694extern char *netfilter_default;
689int checkcfg(int val); 695int checkcfg(int val);
690void print_compiletime_support(void); 696void print_compiletime_support(void);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index c386f70cf..3413febcb 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -255,7 +255,9 @@ void fs_blacklist(void) {
255 char *ptr; 255 char *ptr;
256 256
257 // whitelist commands handled by fs_whitelist() 257 // whitelist commands handled by fs_whitelist()
258 if (strncmp(entry->data, "whitelist ", 10) == 0 || *entry->data == '\0') { 258 if (strncmp(entry->data, "whitelist ", 10) == 0 ||
259 strncmp(entry->data, "nowhitelist ", 12) == 0 ||
260 *entry->data == '\0') {
259 entry = entry->next; 261 entry = entry->next;
260 continue; 262 continue;
261 } 263 }
@@ -469,7 +471,24 @@ void fs_noexec(const char *dir) {
469 } 471 }
470} 472}
471 473
474// Disable /mnt, /media, /run/mount and /run/media access
475void fs_mnt(void) {
476 disable_file(BLACKLIST_FILE, "/mnt");
477 disable_file(BLACKLIST_FILE, "/media");
478 disable_file(BLACKLIST_FILE, "/run/mount");
479 disable_file(BLACKLIST_FILE, "//run/media");
480}
481
472 482
483void fs_cache(void) {
484 if (arg_debug)
485 printf("Deploy ~/.cache tmpfs\n");
486 char *cache;
487 if (asprintf(&cache, "%s/.cache", cfg.homedir) == -1)
488 errExit("asprintf");
489 disable_file(MOUNT_TMPFS, cache);
490 free(cache);
491}
473 492
474// mount /proc and /sys directories 493// mount /proc and /sys directories
475void fs_proc_sys_dev_boot(void) { 494void fs_proc_sys_dev_boot(void) {
@@ -1084,7 +1103,6 @@ void fs_check_chroot_dir(const char *rootdir) {
1084 1103
1085 // check x11 socket directory 1104 // check x11 socket directory
1086 if (getenv("FIREJAIL_X11")) { 1105 if (getenv("FIREJAIL_X11")) {
1087 mask_x11_abstract_socket = 1;
1088 char *name; 1106 char *name;
1089 if (asprintf(&name, "%s/tmp/.X11-unix", rootdir) == -1) 1107 if (asprintf(&name, "%s/tmp/.X11-unix", rootdir) == -1)
1090 errExit("asprintf"); 1108 errExit("asprintf");
@@ -1117,7 +1135,6 @@ void fs_chroot(const char *rootdir) {
1117 1135
1118 // x11 1136 // x11
1119 if (getenv("FIREJAIL_X11")) { 1137 if (getenv("FIREJAIL_X11")) {
1120 mask_x11_abstract_socket = 1;
1121 char *newx11; 1138 char *newx11;
1122 if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1) 1139 if (asprintf(&newx11, "%s/tmp/.X11-unix", rootdir) == -1)
1123 errExit("asprintf"); 1140 errExit("asprintf");
diff --git a/src/firejail/fs_bin.c b/src/firejail/fs_bin.c
index 3473fca4c..73edd2ef9 100644
--- a/src/firejail/fs_bin.c
+++ b/src/firejail/fs_bin.c
@@ -111,7 +111,10 @@ static void duplicate(char *fname) {
111 errExit("asprintf"); 111 errExit("asprintf");
112 112
113 // copy the file 113 // copy the file
114 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, RUN_BIN_DIR); 114 if (checkcfg(CFG_FOLLOW_SYMLINK_PRIVATE_BIN))
115 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", full_path, RUN_BIN_DIR);
116 else
117 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 3, PATH_FCOPY, full_path, RUN_BIN_DIR);
115 fs_logger2("clone", fname); 118 fs_logger2("clone", fname);
116 free(full_path); 119 free(full_path);
117} 120}
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c
index a0bda7443..f90b7df60 100644
--- a/src/firejail/fs_mkdir.c
+++ b/src/firejail/fs_mkdir.c
@@ -61,8 +61,9 @@ void fs_mkdir(const char *name) {
61 // check directory name 61 // check directory name
62 invalid_filename(name); 62 invalid_filename(name);
63 char *expanded = expand_home(name, cfg.homedir); 63 char *expanded = expand_home(name, cfg.homedir);
64 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0) { 64 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 &&
65 fprintf(stderr, "Error: only directories in user home are supported by mkdir\n"); 65 strncmp(expanded, "/tmp", 4) != 0) {
66 fprintf(stderr, "Error: only directories in user home or /tmp are supported by mkdir\n");
66 exit(1); 67 exit(1);
67 } 68 }
68 69
@@ -100,8 +101,9 @@ void fs_mkfile(const char *name) {
100 // check file name 101 // check file name
101 invalid_filename(name); 102 invalid_filename(name);
102 char *expanded = expand_home(name, cfg.homedir); 103 char *expanded = expand_home(name, cfg.homedir);
103 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0) { 104 if (strncmp(expanded, cfg.homedir, strlen(cfg.homedir)) != 0 &&
104 fprintf(stderr, "Error: only files in user home are supported by mkfile\n"); 105 strncmp(expanded, "/tmp", 4) != 0) {
106 fprintf(stderr, "Error: only files in user home or /tmp are supported by mkfile\n");
105 exit(1); 107 exit(1);
106 } 108 }
107 109
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 1794e4b35..7ad5ffeb8 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -35,7 +35,7 @@ static char *dentry[] = {
35}; 35};
36 36
37#define MAXBUF 4098 37#define MAXBUF 4098
38static char *resolve_downloads(void) { 38static char *resolve_downloads(int nowhitelist_flag) {
39 char *fname; 39 char *fname;
40 struct stat s; 40 struct stat s;
41 41
@@ -50,8 +50,14 @@ static char *resolve_downloads(void) {
50 printf("Downloads directory resolved as \"%s\"\n", fname); 50 printf("Downloads directory resolved as \"%s\"\n", fname);
51 51
52 char *rv; 52 char *rv;
53 if (asprintf(&rv, "whitelist ~/%s", dentry[i]) == -1) 53 if (nowhitelist_flag) {
54 errExit("asprintf"); 54 if (asprintf(&rv, "nowhitelist ~/%s", dentry[i]) == -1)
55 errExit("asprintf");
56 }
57 else {
58 if (asprintf(&rv, "whitelist ~/%s", dentry[i]) == -1)
59 errExit("asprintf");
60 }
55 free(fname); 61 free(fname);
56 return rv; 62 return rv;
57 } 63 }
@@ -101,8 +107,14 @@ static char *resolve_downloads(void) {
101 } 107 }
102 108
103 char *rv; 109 char *rv;
104 if (asprintf(&rv, "whitelist ~/%s", ptr + 24) == -1) 110 if (nowhitelist_flag) {
105 errExit("asprintf"); 111 if (asprintf(&rv, "nowhitelist ~/%s", ptr + 24) == -1)
112 errExit("asprintf");
113 }
114 else {
115 if (asprintf(&rv, "whitelist ~/%s", ptr + 24) == -1)
116 errExit("asprintf");
117 }
106 return rv; 118 return rv;
107 } 119 }
108 else 120 else
@@ -309,38 +321,54 @@ void fs_whitelist(void) {
309 int var_dir = 0; // /var directory flag 321 int var_dir = 0; // /var directory flag
310 int dev_dir = 0; // /dev directory flag 322 int dev_dir = 0; // /dev directory flag
311 int opt_dir = 0; // /opt directory flag 323 int opt_dir = 0; // /opt directory flag
312 int srv_dir = 0; // /srv directory flag 324 int srv_dir = 0; // /srv directory flag
325
326 size_t nowhitelist_c = 0;
327 size_t nowhitelist_m = 32;
328 char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist));
329 if (nowhitelist == NULL)
330 errExit("failed allocating memory for nowhitelist entries");
331
313 // verify whitelist files, extract symbolic links, etc. 332 // verify whitelist files, extract symbolic links, etc.
314 while (entry) { 333 while (entry) {
315 // handle only whitelist commands 334 int nowhitelist_flag = 0;
316 if (strncmp(entry->data, "whitelist ", 10)) { 335
336 // handle only whitelist and nowhitelist commands
337 if (strncmp(entry->data, "whitelist ", 10) == 0)
338 nowhitelist_flag = 0;
339 else if (strncmp(entry->data, "nowhitelist ", 12) == 0)
340 nowhitelist_flag = 1;
341 else {
317 entry = entry->next; 342 entry = entry->next;
318 continue; 343 continue;
319 } 344 }
345 char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
320 346
321 // resolve ${DOWNLOADS} 347 // resolve ${DOWNLOADS}
322 if (strcmp(entry->data + 10, "${DOWNLOADS}") == 0) { 348 if (strcmp(dataptr, "${DOWNLOADS}") == 0) {
323 char *tmp = resolve_downloads(); 349 char *tmp = resolve_downloads(nowhitelist_flag);
324 if (tmp) 350 if (tmp) {
325 entry->data = tmp; 351 entry->data = tmp;
352 dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10;
353 }
326 else { 354 else {
355 if (!nowhitelist_flag) {
356 fprintf(stderr, "***\n");
357 fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n");
358 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
359 fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n");
360 fprintf(stderr, "***\n");
361 }
327 *entry->data = '\0'; 362 *entry->data = '\0';
328 fprintf(stderr, "***\n");
329 fprintf(stderr, "*** Warning: cannot whitelist Downloads directory\n");
330 fprintf(stderr, "*** \tAny file saved will be lost when the sandbox is closed.\n");
331 fprintf(stderr, "*** \tPlease create a proper Downloads directory for your application.\n");
332 fprintf(stderr, "***\n");
333 continue; 363 continue;
334 } 364 }
335 } 365 }
336 366
337 // replace ~/ or ${HOME} into /home/username 367 // replace ~/ or ${HOME} into /home/username
338// if (new_name) 368 new_name = expand_home(dataptr, cfg.homedir);
339// free(new_name);
340 new_name = expand_home(entry->data + 10, cfg.homedir);
341 assert(new_name); 369 assert(new_name);
342 if (arg_debug) 370 if (arg_debug)
343 fprintf(stderr, "Debug %d: new_name #%s#\n", __LINE__, new_name); 371 fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist");
344 372
345 // valid path referenced to filesystem root 373 // valid path referenced to filesystem root
346 if (*new_name != '/') { 374 if (*new_name != '/') {
@@ -356,37 +384,56 @@ void fs_whitelist(void) {
356 if (!fname) { 384 if (!fname) {
357 // file not found, blank the entry in the list and continue 385 // file not found, blank the entry in the list and continue
358 if (arg_debug || arg_debug_whitelists) { 386 if (arg_debug || arg_debug_whitelists) {
359 printf("Removed whitelist path: %s\n", entry->data); 387 printf("Removed whitelist/nowhitelist path: %s\n", entry->data);
360 printf("\texpanded: %s\n", new_name); 388 printf("\texpanded: %s\n", new_name);
361 printf("\treal path: (null)\n"); 389 printf("\treal path: (null)\n");
362 printf("\t");fflush(0); 390 printf("\t");fflush(0);
363 perror("realpath"); 391 perror("realpath");
364 } 392 }
365 *entry->data = '\0';
366 393
367 // if 1 the file was not found; mount an empty directory 394 // if 1 the file was not found; mount an empty directory
368 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { 395 if (!nowhitelist_flag) {
369 if(!arg_private) 396 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) {
370 home_dir = 1; 397 if(!arg_private)
398 home_dir = 1;
399 }
400 else if (strncmp(new_name, "/tmp/", 5) == 0)
401 tmp_dir = 1;
402 else if (strncmp(new_name, "/media/", 7) == 0)
403 media_dir = 1;
404 else if (strncmp(new_name, "/mnt/", 5) == 0)
405 mnt_dir = 1;
406 else if (strncmp(new_name, "/var/", 5) == 0)
407 var_dir = 1;
408 else if (strncmp(new_name, "/dev/", 5) == 0)
409 dev_dir = 1;
410 else if (strncmp(new_name, "/opt/", 5) == 0)
411 opt_dir = 1;
412 else if (strncmp(new_name, "/srv/", 5) == 0)
413 opt_dir = 1;
371 } 414 }
372 else if (strncmp(new_name, "/tmp/", 5) == 0)
373 tmp_dir = 1;
374 else if (strncmp(new_name, "/media/", 7) == 0)
375 media_dir = 1;
376 else if (strncmp(new_name, "/mnt/", 5) == 0)
377 mnt_dir = 1;
378 else if (strncmp(new_name, "/var/", 5) == 0)
379 var_dir = 1;
380 else if (strncmp(new_name, "/dev/", 5) == 0)
381 dev_dir = 1;
382 else if (strncmp(new_name, "/opt/", 5) == 0)
383 opt_dir = 1;
384 else if (strncmp(new_name, "/srv/", 5) == 0)
385 opt_dir = 1;
386 415
416 *entry->data = '\0';
417 continue;
418 }
419
420 if (nowhitelist_flag) {
421 // store the path in nowhitelist array
422 if (arg_debug || arg_debug_whitelists)
423 printf("Storing nowhitelist %s\n", fname);
424
425 if (nowhitelist_c >= nowhitelist_m) {
426 nowhitelist_m *= 2;
427 nowhitelist = realloc(nowhitelist, sizeof(*nowhitelist) * nowhitelist_m);
428 if (nowhitelist == NULL)
429 errExit("failed increasing memory for nowhitelist entries");
430 }
431 nowhitelist[nowhitelist_c++] = fname;
432 *entry->data = 0;
387 continue; 433 continue;
388 } 434 }
389 435
436
390 // check for supported directories 437 // check for supported directories
391 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) { 438 if (strncmp(new_name, cfg.homedir, strlen(cfg.homedir)) == 0) {
392 // whitelisting home directory is disabled if --private option is present 439 // whitelisting home directory is disabled if --private option is present
@@ -479,6 +526,27 @@ void fs_whitelist(void) {
479 goto errexit; 526 goto errexit;
480 } 527 }
481 528
529 // check if the path is in nowhitelist array
530 if (nowhitelist_flag == 0) {
531 size_t i;
532 int found = 0;
533 for (i = 0; i < nowhitelist_c; i++) {
534 if (nowhitelist[i] == NULL)
535 break;
536 if (strcmp(nowhitelist[i], fname) == 0) {
537 found = 1;
538 break;
539 }
540 }
541 if (found) {
542 if (arg_debug || arg_debug_whitelists)
543 printf("Skip nowhitelisted path %s\n", fname);
544 *entry->data = 0;
545 free(fname);
546 continue;
547 }
548 }
549
482 // mark symbolic links 550 // mark symbolic links
483 if (is_link(new_name)) 551 if (is_link(new_name))
484 entry->link = new_name; 552 entry->link = new_name;
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 310795abf..aead29957 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -349,6 +349,14 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
349 else 349 else
350 exit_err_feature("x11"); 350 exit_err_feature("x11");
351 } 351 }
352 else if (strcmp(argv[i], "--x11=xvfb") == 0) {
353 if (checkcfg(CFG_X11)) {
354 x11_start_xvfb(argc, argv);
355 exit(0);
356 }
357 else
358 exit_err_feature("x11");
359 }
352#endif 360#endif
353#ifdef HAVE_NETWORK 361#ifdef HAVE_NETWORK
354 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { 362 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
@@ -1261,6 +1269,14 @@ int main(int argc, char **argv) {
1261 else 1269 else
1262 exit_err_feature("whitelist"); 1270 exit_err_feature("whitelist");
1263 } 1271 }
1272 else if (strncmp(argv[i], "--nowhitelist=", 14) == 0) {
1273 char *line;
1274 if (asprintf(&line, "nowhitelist %s", argv[i] + 14) == -1)
1275 errExit("asprintf");
1276
1277 profile_check_line(line, 0, NULL); // will exit if something wrong
1278 profile_add(line);
1279 }
1264#endif 1280#endif
1265 1281
1266 else if (strncmp(argv[i], "--read-only=", 12) == 0) { 1282 else if (strncmp(argv[i], "--read-only=", 12) == 0) {
@@ -2182,10 +2198,6 @@ int main(int argc, char **argv) {
2182 fprintf(stderr, "Warning: --trace and --tracelog are mutually exclusive; --tracelog disabled\n"); 2198 fprintf(stderr, "Warning: --trace and --tracelog are mutually exclusive; --tracelog disabled\n");
2183 } 2199 }
2184 2200
2185 // disable x11 abstract socket
2186 if (getenv("FIREJAIL_X11"))
2187 mask_x11_abstract_socket = 1;
2188
2189 // check user namespace (--noroot) options 2201 // check user namespace (--noroot) options
2190 if (arg_noroot) { 2202 if (arg_noroot) {
2191 if (arg_overlay) { 2203 if (arg_overlay) {
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c
index 1828405db..7cca6b291 100644
--- a/src/firejail/no_sandbox.c
+++ b/src/firejail/no_sandbox.c
@@ -171,7 +171,7 @@ void run_no_sandbox(int argc, char **argv) {
171 strcmp(argv[i], "--zsh") == 0 || 171 strcmp(argv[i], "--zsh") == 0 ||
172 strcmp(argv[i], "--shell=none") == 0 || 172 strcmp(argv[i], "--shell=none") == 0 ||
173 strncmp(argv[i], "--shell=", 8) == 0) 173 strncmp(argv[i], "--shell=", 8) == 0)
174 fprintf(stderr, "Warning: shell-related command line options are disregarded - using SHELL environment variable"); 174 fprintf(stderr, "Warning: shell-related command line options are disregarded - using SHELL environment variable\n");
175 } 175 }
176 176
177 // use $SHELL to get shell used in sandbox 177 // use $SHELL to get shell used in sandbox
diff --git a/src/firejail/output.c b/src/firejail/output.c
index 4872c57ba..cea4f4e28 100644
--- a/src/firejail/output.c
+++ b/src/firejail/output.c
@@ -26,50 +26,45 @@ void check_output(int argc, char **argv) {
26 EUID_ASSERT(); 26 EUID_ASSERT();
27 27
28 int i; 28 int i;
29 char *outfile = NULL; 29 int outindex = 0;
30 30
31 int found = 0;
32 for (i = 1; i < argc; i++) { 31 for (i = 1; i < argc; i++) {
33 if (strncmp(argv[i], "--output=", 9) == 0) { 32 if (strncmp(argv[i], "--output=", 9) == 0) {
34 found = 1; 33 outindex = i;
35 invalid_filename(argv[i] + 9);
36 outfile = argv[i] + 9;
37
38 // do not accept directories, links, and files with ".."
39 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) {
40 fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n");
41 exit(1);
42 }
43
44 struct stat s;
45 if (stat(outfile, &s) == 0) {
46 // check permissions
47 if (s.st_uid != getuid() || s.st_gid != getgid()) {
48 fprintf(stderr, "Error: the output file needs to be owned by the current user.\n");
49 exit(1);
50 }
51
52 // check hard links
53 if (s.st_nlink != 1) {
54 fprintf(stderr, "Error: no hard links allowed.\n");
55 exit(1);
56 }
57 }
58
59 /* coverity[toctou] */
60 FILE *fp = fopen(outfile, "a");
61 if (!fp) {
62 fprintf(stderr, "Error: cannot open output file %s\n", outfile);
63 exit(1);
64 }
65 fclose(fp);
66 break; 34 break;
67 } 35 }
68 } 36 }
69 if (!found) 37 if (!outindex)
70 return; 38 return;
71 39
72 40
41 // check filename
42 drop_privs(0);
43 char *outfile = NULL;
44 invalid_filename(argv[outindex] + 9);
45 outfile = argv[outindex] + 9;
46
47 // do not accept directories, links, and files with ".."
48 if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) {
49 fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n");
50 exit(1);
51 }
52
53 struct stat s;
54 if (stat(outfile, &s) == 0) {
55 // check permissions
56 if (s.st_uid != getuid() || s.st_gid != getgid()) {
57 fprintf(stderr, "Error: the output file needs to be owned by the current user.\n");
58 exit(1);
59 }
60
61 // check hard links
62 if (s.st_nlink != 1) {
63 fprintf(stderr, "Error: no hard links allowed.\n");
64 exit(1);
65 }
66 }
67
73 // build the new command line 68 // build the new command line
74 int len = 0; 69 int len = 0;
75 for (i = 0; i < argc; i++) { 70 for (i = 0; i < argc; i++) {
@@ -90,7 +85,6 @@ void check_output(int argc, char **argv) {
90 sprintf(ptr, "2>&1 | %s/firejail/ftee %s", LIBDIR, outfile); 85 sprintf(ptr, "2>&1 | %s/firejail/ftee %s", LIBDIR, outfile);
91 86
92 // run command 87 // run command
93 drop_privs(0);
94 char *a[4]; 88 char *a[4];
95 a[0] = "/bin/bash"; 89 a[0] = "/bin/bash";
96 a[1] = "-c"; 90 a[1] = "-c";
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index 5684a2d95..00dd87dad 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -696,7 +696,6 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
696 if (checkcfg(CFG_X11)) { 696 if (checkcfg(CFG_X11)) {
697 char *x11env = getenv("FIREJAIL_X11"); 697 char *x11env = getenv("FIREJAIL_X11");
698 if (x11env && strcmp(x11env, "yes") == 0) { 698 if (x11env && strcmp(x11env, "yes") == 0) {
699 mask_x11_abstract_socket = 1;
700 return 0; 699 return 0;
701 } 700 }
702 else { 701 else {
@@ -720,12 +719,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
720#endif 719#endif
721 return 0; 720 return 0;
722 } 721 }
722
723 if (strcmp(ptr, "x11 xpra") == 0) { 723 if (strcmp(ptr, "x11 xpra") == 0) {
724#ifdef HAVE_X11 724#ifdef HAVE_X11
725 if (checkcfg(CFG_X11)) { 725 if (checkcfg(CFG_X11)) {
726 char *x11env = getenv("FIREJAIL_X11"); 726 char *x11env = getenv("FIREJAIL_X11");
727 if (x11env && strcmp(x11env, "yes") == 0) { 727 if (x11env && strcmp(x11env, "yes") == 0) {
728 mask_x11_abstract_socket = 1;
729 return 0; 728 return 0;
730 } 729 }
731 else { 730 else {
@@ -740,12 +739,30 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
740 return 0; 739 return 0;
741 } 740 }
742 741
742 if (strcmp(ptr, "x11 xvfb") == 0) {
743#ifdef HAVE_X11
744 if (checkcfg(CFG_X11)) {
745 char *x11env = getenv("FIREJAIL_X11");
746 if (x11env && strcmp(x11env, "yes") == 0) {
747 return 0;
748 }
749 else {
750 // start x11
751 x11_start_xvfb(cfg.original_argc, cfg.original_argv);
752 exit(0);
753 }
754 }
755 else
756 warning_feature_disabled("x11");
757#endif
758 return 0;
759 }
760
743 if (strcmp(ptr, "x11") == 0) { 761 if (strcmp(ptr, "x11") == 0) {
744#ifdef HAVE_X11 762#ifdef HAVE_X11
745 if (checkcfg(CFG_X11)) { 763 if (checkcfg(CFG_X11)) {
746 char *x11env = getenv("FIREJAIL_X11"); 764 char *x11env = getenv("FIREJAIL_X11");
747 if (x11env && strcmp(x11env, "yes") == 0) { 765 if (x11env && strcmp(x11env, "yes") == 0) {
748 mask_x11_abstract_socket = 1;
749 return 0; 766 return 0;
750 } 767 }
751 else { 768 else {
@@ -943,6 +960,8 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
943 return 0; 960 return 0;
944#endif 961#endif
945 } 962 }
963 else if (strncmp(ptr, "nowhitelist ", 12) == 0)
964 ptr += 12;
946 else if (strncmp(ptr, "read-only ", 10) == 0) 965 else if (strncmp(ptr, "read-only ", 10) == 0)
947 ptr += 10; 966 ptr += 10;
948 else if (strncmp(ptr, "read-write ", 11) == 0) 967 else if (strncmp(ptr, "read-write ", 11) == 0)
@@ -951,8 +970,19 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
951 ptr += 7; 970 ptr += 7;
952 else if (strncmp(ptr, "tmpfs ", 6) == 0) { 971 else if (strncmp(ptr, "tmpfs ", 6) == 0) {
953 if (getuid() != 0) { 972 if (getuid() != 0) {
954 fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n"); 973 // allow a non-root user to mount tmpfs in user home directory, links are not allowed
955 exit(1); 974 invalid_filename(ptr + 6);
975 char *newfname = expand_home(ptr + 6, cfg.homedir);
976 assert(newfname);
977 if (is_link(newfname)) {
978 fprintf(stderr, "Error: for regular user, tmpfs is not available for symbolic links\n");
979 exit(1);
980 }
981 if (strncmp(newfname, cfg.homedir, strlen(cfg.homedir)) != 0) {
982 fprintf(stderr, "Error: for regular user, tmpfs is available only for files in user home directory\n");
983 exit(1);
984 }
985 free(newfname);
956 } 986 }
957 ptr += 6; 987 ptr += 6;
958 } 988 }
@@ -1025,7 +1055,8 @@ void profile_read(const char *fname) {
1025 } 1055 }
1026 if (access(fname, R_OK)) { 1056 if (access(fname, R_OK)) {
1027 // if the file ends in ".local", do not exit 1057 // if the file ends in ".local", do not exit
1028 char *ptr = strstr(fname, ".local"); 1058 const char *base = gnu_basename(fname);
1059 char *ptr = strstr(base, ".local");
1029 if (ptr && strlen(ptr) == 6) 1060 if (ptr && strlen(ptr) == 6)
1030 return; 1061 return;
1031 1062
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 84ee5ee11..f26f8b06a 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -208,6 +208,11 @@ static int monitor_application(pid_t app_pid) {
208 while(rv != monitored_pid); 208 while(rv != monitored_pid);
209 if (arg_debug) 209 if (arg_debug)
210 printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status); 210 printf("Sandbox monitor: waitpid %u retval %d status %d\n", monitored_pid, rv, status);
211 if (rv == -1) { // we can get here if we have processes joining the sandbox (ECHILD)
212 if (arg_debug)
213 perror("waitpid");
214 sleep(1);
215 }
211 216
212 // if /proc is not remounted, we cannot check /proc directory, 217 // if /proc is not remounted, we cannot check /proc directory,
213 // for now we just get out of here 218 // for now we just get out of here
@@ -759,12 +764,25 @@ int sandbox(void* sandbox_arg) {
759 netns_mounts(arg_netns); 764 netns_mounts(arg_netns);
760 765
761 //**************************** 766 //****************************
762 // update /proc, /sys, /dev, /boot directorymy 767 // update /proc, /sys, /dev, /boot directory
763 //**************************** 768 //****************************
764 if (checkcfg(CFG_REMOUNT_PROC_SYS)) 769 if (checkcfg(CFG_REMOUNT_PROC_SYS))
765 fs_proc_sys_dev_boot(); 770 fs_proc_sys_dev_boot();
766 771
767 //**************************** 772 //****************************
773 // handle /mnt and /media
774 //****************************
775 if (checkcfg(CFG_DISABLE_MNT))
776 fs_mnt();
777
778 //****************************
779 // deploy a tmpfs on ~/.cache directory
780 //****************************
781 if (checkcfg(CFG_CACHE_TMPFS))
782 fs_cache();
783
784
785 //****************************
768 // apply the profile file 786 // apply the profile file
769 //**************************** 787 //****************************
770 // apply all whitelist commands ... 788 // apply all whitelist commands ...
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index ae3993aec..9c91b4630 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -127,6 +127,7 @@ void usage(void) {
127 printf(" --noroot - install a user namespace with only the current user.\n"); 127 printf(" --noroot - install a user namespace with only the current user.\n");
128#endif 128#endif
129 printf(" --nonewprivs - sets the NO_NEW_PRIVS prctl.\n"); 129 printf(" --nonewprivs - sets the NO_NEW_PRIVS prctl.\n");
130 printf(" --nowhitelist=filename - disable whitelist for file or directory .\n");
130 printf(" --output=logfile - stdout logging and log rotation.\n"); 131 printf(" --output=logfile - stdout logging and log rotation.\n");
131 printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n"); 132 printf(" --overlay - mount a filesystem overlay on top of the current filesystem.\n");
132 printf(" --overlay-named=name - mount a filesystem overlay on top of the current\n"); 133 printf(" --overlay-named=name - mount a filesystem overlay on top of the current\n");
@@ -205,6 +206,7 @@ void usage(void) {
205 printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n"); 206 printf(" --x11=xephyr - enable Xephyr X11 server. The window size is 800x600.\n");
206 printf(" --x11=xorg - enable X11 security extension.\n"); 207 printf(" --x11=xorg - enable X11 security extension.\n");
207 printf(" --x11=xpra - enable Xpra X11 server.\n"); 208 printf(" --x11=xpra - enable Xpra X11 server.\n");
209 printf(" --x11=xvfb - enable Xvfb X11 server.\n");
208 printf(" --zsh - use /usr/bin/zsh as default shell.\n"); 210 printf(" --zsh - use /usr/bin/zsh as default shell.\n");
209 printf("\n"); 211 printf("\n");
210 printf("Examples:\n"); 212 printf("Examples:\n");
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index 5bbc327a6..b72b46f0d 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -31,46 +31,45 @@
31#include <sys/wait.h> 31#include <sys/wait.h>
32#include <errno.h> 32#include <errno.h>
33#include <limits.h> 33#include <limits.h>
34int mask_x11_abstract_socket = 0;
35
36 34
37// Parse the DISPLAY environment variable and return a display number. 35// Parse the DISPLAY environment variable and return a display number.
38// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd. 36// Returns -1 if DISPLAY is not set, or is set to anything other than :ddd.
39int x11_display(void) { 37int x11_display(void) {
40 const char *display_str = getenv("DISPLAY"); 38 const char *display_str = getenv("DISPLAY");
41 char *endp; 39 char *endp;
42 unsigned long display; 40 unsigned long display;
43 41
44 if (!display_str) { 42 if (!display_str) {
45 if (arg_debug) 43 if (arg_debug)
46 fputs("DISPLAY is not set\n", stderr); 44 fputs("DISPLAY is not set\n", stderr);
47 return -1; 45 return -1;
48 } 46 }
49 47
50 if (display_str[0] != ':' || display_str[1] < '0' || display_str[1] > '9') { 48 if (display_str[0] != ':' || display_str[1] < '0' || display_str[1] > '9') {
51 if (arg_debug) 49 if (arg_debug)
52 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); 50 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str);
53 return -1; 51 return -1;
54 } 52 }
55 53
56 errno = 0; 54 errno = 0;
57 display = strtoul(display_str+1, &endp, 10); 55 display = strtoul(display_str+1, &endp, 10);
58 if (endp == display_str+1 || (*endp != '\0' && *endp != '.')) { // handling DISPLAY=:0 and also :0.0 56 // handling DISPLAY=:0 and also :0.0
59 if (arg_debug) 57 if (endp == display_str+1 || (*endp != '\0' && *endp != '.')) {
60 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str); 58 if (arg_debug)
61 return -1; 59 fprintf(stderr, "unsupported DISPLAY form '%s'\n", display_str);
62 } 60 return -1;
63 if (errno || display > (unsigned long)INT_MAX) { 61 }
64 if (arg_debug) 62 if (errno || display > (unsigned long)INT_MAX) {
65 fprintf(stderr, "display number %s is outside the valid range\n", 63 if (arg_debug)
66 display_str+1); 64 fprintf(stderr, "display number %s is outside the valid range\n",
67 return -1; 65 display_str+1);
68 } 66 return -1;
69 67 }
70 if (arg_debug) 68
71 fprintf(stderr, "DISPLAY=%s parsed as %lu\n", display_str, display); 69 if (arg_debug)
72 70 fprintf(stderr, "DISPLAY=%s parsed as %lu\n", display_str, display);
73 return (int)display; 71
72 return (int)display;
74} 73}
75 74
76 75
@@ -78,34 +77,34 @@ int x11_display(void) {
78// check for X11 abstract sockets 77// check for X11 abstract sockets
79static int x11_abstract_sockets_present(void) { 78static int x11_abstract_sockets_present(void) {
80 79
81 EUID_ROOT(); // grsecurity fix 80 EUID_ROOT(); // grsecurity fix
82 FILE *fp = fopen("/proc/net/unix", "r"); 81 FILE *fp = fopen("/proc/net/unix", "r");
83 if (!fp) 82 if (!fp)
84 errExit("fopen"); 83 errExit("fopen");
85 EUID_USER(); 84 EUID_USER();
86 85
87 char *linebuf = 0; 86 char *linebuf = 0;
88 size_t bufsz = 0; 87 size_t bufsz = 0;
89 int found = 0; 88 int found = 0;
90 errno = 0; 89 errno = 0;
91 90
92 for (;;) { 91 for (;;) {
93 if (getline(&linebuf, &bufsz, fp) == -1) { 92 if (getline(&linebuf, &bufsz, fp) == -1) {
94 if (errno) 93 if (errno)
95 errExit("getline"); 94 errExit("getline");
96 break; 95 break;
97 } 96 }
98 // The last space-separated field in 'linebuf' is the 97 // The last space-separated field in 'linebuf' is the
99 // pathname of the socket. Abstract sockets' pathnames 98 // pathname of the socket. Abstract sockets' pathnames
100 // all begin with '@/', normal ones begin with '/'. 99 // all begin with '@/', normal ones begin with '/'.
101 char *p = strrchr(linebuf, ' '); 100 char *p = strrchr(linebuf, ' ');
102 if (!p) { 101 if (!p) {
103 fputs("error parsing /proc/net/unix\n", stderr); 102 fputs("error parsing /proc/net/unix\n", stderr);
104 exit(1); 103 exit(1);
105 } 104 }
106 if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) { 105 if (strncmp(p+1, "@/tmp/.X11-unix/", 16) == 0) {
107 found = 1; 106 found = 1;
108 break; 107 break;
109 } 108 }
110 } 109 }
111 110
@@ -114,94 +113,289 @@ static int x11_abstract_sockets_present(void) {
114 return found; 113 return found;
115} 114}
116 115
116
117// Choose a random, unallocated display number. This has an inherent 117// Choose a random, unallocated display number. This has an inherent
118// and unavoidable TOCTOU race, since we cannot create either the 118// and unavoidable TOCTOU race, since we cannot create either the
119// socket or a lockfile ourselves. 119// socket or a lockfile ourselves.
120static int random_display_number(void) { 120static int random_display_number(void) {
121 int display; 121 int display;
122 int found = 0; 122 int found = 0;
123 int i; 123 int i;
124 124
125 struct sockaddr_un sa; 125 struct sockaddr_un sa;
126 // The -1 here is because we need space to inject a 126 // The -1 here is because we need space to inject a
127 // leading nul byte. 127 // leading nul byte.
128 int sun_pathmax = (int)(sizeof sa.sun_path - 1); 128 int sun_pathmax = (int)(sizeof sa.sun_path - 1);
129 assert((size_t)sun_pathmax == sizeof sa.sun_path - 1); 129 assert((size_t)sun_pathmax == sizeof sa.sun_path - 1);
130 int sun_pathlen; 130 int sun_pathlen;
131 131
132 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 132 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
133 if (sockfd == -1) 133 if (sockfd == -1)
134 errExit("socket"); 134 errExit("socket");
135 135
136 for (i = 0; i < 100; i++) { 136 for (i = 0; i < 100; i++) {
137 // We try display numbers in the range 21 through 1000. 137 // We try display numbers in the range 21 through 1000.
138 // Normal X servers typically use displays in the 0-10 range; 138 // Normal X servers typically use displays in the 0-10 range;
139 // ssh's X11 forwarding uses 10-20, and login screens 139 // ssh's X11 forwarding uses 10-20, and login screens
140 // (e.g. gdm3) may use displays above 1000. 140 // (e.g. gdm3) may use displays above 1000.
141 display = rand() % 979 + 21; 141 display = rand() % 979 + 21;
142 142
143 // The display number might be claimed by a server listening 143 // The display number might be claimed by a server listening
144 // in _either_ the normal or the abstract namespace; they 144 // in _either_ the normal or the abstract namespace; they
145 // don't necessarily do both. The easiest way to check is 145 // don't necessarily do both. The easiest way to check is
146 // to try to connect, both ways. 146 // to try to connect, both ways.
147 memset(&sa, 0, sizeof sa); 147 memset(&sa, 0, sizeof sa);
148 sa.sun_family = AF_UNIX; 148 sa.sun_family = AF_UNIX;
149 sun_pathlen = snprintf(sa.sun_path, sun_pathmax, 149 sun_pathlen = snprintf(sa.sun_path, sun_pathmax,
150 "/tmp/.X11-unix/X%d", display); 150 "/tmp/.X11-unix/X%d", display);
151 if (sun_pathlen >= sun_pathmax) { 151 if (sun_pathlen >= sun_pathmax) {
152 fprintf(stderr, "sun_path too small for display :%d" 152 fprintf(stderr, "sun_path too small for display :%d"
153 " (only %d bytes usable)\n", display, sun_pathmax); 153 " (only %d bytes usable)\n", display, sun_pathmax);
154 exit(1); 154 exit(1);
155 } 155 }
156 156
157 if (connect(sockfd, (struct sockaddr *)&sa, 157 if (connect(sockfd, (struct sockaddr *)&sa,
158 offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) { 158 offsetof(struct sockaddr_un, sun_path) + sun_pathlen + 1) == 0) {
159 close(sockfd); 159 close(sockfd);
160 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 160 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
161 if (sockfd == -1) 161 if (sockfd == -1)
162 errExit("socket"); 162 errExit("socket");
163 continue; 163 continue;
164 } 164 }
165 if (errno != ECONNREFUSED && errno != ENOENT) 165 if (errno != ECONNREFUSED && errno != ENOENT)
166 errExit("connect"); 166 errExit("connect");
167 167
168 // Name not claimed in the normal namespace; now try it 168 // Name not claimed in the normal namespace; now try it
169 // in the abstract namespace. Note that abstract-namespace 169 // in the abstract namespace. Note that abstract-namespace
170 // names are NOT nul-terminated; they extend to the length 170 // names are NOT nul-terminated; they extend to the length
171 // specified as the third argument to 'connect'. 171 // specified as the third argument to 'connect'.
172 memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1); 172 memmove(sa.sun_path + 1, sa.sun_path, sun_pathlen + 1);
173 sa.sun_path[0] = '\0'; 173 sa.sun_path[0] = '\0';
174 if (connect(sockfd, (struct sockaddr *)&sa, 174 if (connect(sockfd, (struct sockaddr *)&sa,
175 offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) { 175 offsetof(struct sockaddr_un, sun_path) + 1 + sun_pathlen) == 0) {
176 close(sockfd); 176 close(sockfd);
177 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 177 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
178 if (sockfd == -1) 178 if (sockfd == -1)
179 errExit("socket"); 179 errExit("socket");
180 continue; 180 continue;
181 } 181 }
182 if (errno != ECONNREFUSED && errno != ENOENT) 182 if (errno != ECONNREFUSED && errno != ENOENT)
183 errExit("connect"); 183 errExit("connect");
184 184
185 // This display number is unclaimed. Of course, it could 185 // This display number is unclaimed. Of course, it could
186 // be claimed before we get around to doing it... 186 // be claimed before we get around to doing it...
187 found = 1; 187 found = 1;
188 break; 188 break;
189 } 189 }
190 close(sockfd); 190 close(sockfd);
191 191
192 if (!found) { 192 if (!found) {
193 fputs("Error: cannot find an unallocated X11 display number, " 193 fputs("Error: cannot find an unallocated X11 display number, "
194 "exiting...\n", stderr); 194 "exiting...\n", stderr);
195 exit(1); 195 exit(1);
196 } 196 }
197 return display; 197 return display;
198} 198}
199#endif 199#endif
200 200
201#ifdef HAVE_X11
202void x11_start_xvfb(int argc, char **argv) {
203 EUID_ASSERT();
204 int i;
205 struct stat s;
206 pid_t jail = 0;
207 pid_t server = 0;
201 208
209 setenv("FIREJAIL_X11", "yes", 1);
210
211 // mever try to run X servers as root!!!
212 if (getuid() == 0) {
213 fprintf(stderr, "Error: X11 sandboxing is not available when running as root\n");
214 exit(1);
215 }
216 drop_privs(0);
217
218 // check xephyr
219 if (!program_in_path("Xvfb")) {
220 fprintf(stderr, "\nError: Xvfb program was not found in /usr/bin directory, please install it:\n");
221 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xvfb\n");
222 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xvfb\n");
223 exit(0);
224 }
225
226 int display = random_display_number();
227 char *display_str;
228 if (asprintf(&display_str, ":%d", display) == -1)
229 errExit("asprintf");
230
231 assert(xvfb_screen);
232
233 char *server_argv[256] = { // rest initialyzed to NULL
234 "Xvfb", display_str, "-screen", "0", xvfb_screen
235 };
236 unsigned pos = 0;
237 while (server_argv[pos] != NULL) pos++;
238 assert(xvfb_extra_params); // should be "" if empty
239
240 // parse xvfb_extra_params
241 // very basic quoting support
242 char *temp = strdup(xephyr_extra_params);
243 if (*xephyr_extra_params != '\0') {
244 if (!temp)
245 errExit("strdup");
246 bool dquote = false;
247 bool squote = false;
248 for (i = 0; i < (int) strlen(xvfb_extra_params); i++) {
249 if (temp[i] == '\"') {
250 dquote = !dquote;
251 // replace closing quote by \0
252 if (dquote) temp[i] = '\0';
253 }
254 if (temp[i] == '\'') {
255 squote = !squote;
256 // replace closing quote by \0
257 if (squote) temp[i] = '\0';
258 }
259 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0';
260 if (dquote && squote) {
261 fprintf(stderr, "Error: mixed quoting found while parsing xvfb_extra_params\n");
262 exit(1);
263 }
264 }
265 if (dquote) {
266 fprintf(stderr, "Error: unclosed quote found while parsing xephyr_extra_params\n");
267 exit(1);
268 }
269
270 for (i = 0; i < (int) strlen(xvfb_extra_params)-1; i++) {
271 if (pos >= (sizeof(server_argv)/sizeof(*server_argv)) - 2) {
272 fprintf(stderr, "Error: arg count limit exceeded while parsing xvfb_extra_params\n");
273 exit(1);
274 }
275 if (temp[i] == '\0' && (temp[i+1] == '\"' || temp[i+1] == '\'')) server_argv[pos++] = temp + i + 2;
276 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1;
277 }
278 }
279
280 server_argv[pos++] = NULL;
281
282 assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun
283 assert(server_argv[pos-1] == NULL); // last element is null
284
285 if (arg_debug) {
286 size_t i = 0;
287 printf("xvfb server:");
288 while (server_argv[i]!=NULL) {
289 printf(" \"%s\"", server_argv[i]);
290 i++;
291 }
292 putchar('\n');
293 }
294
295 // remove --x11 arg
296 char *jail_argv[argc+2];
297 int j = 0;
298 for (i = 0; i < argc; i++) {
299 if (strncmp(argv[i], "--x11", 5) == 0)
300 continue;
301 jail_argv[j] = argv[i];
302 j++;
303 }
304 jail_argv[j] = NULL;
305
306 assert(j < argc+2); // no overrun
307
308 if (arg_debug) {
309 size_t i = 0;
310 printf("xvfb client:");
311 while (jail_argv[i]!=NULL) {
312 printf(" \"%s\"", jail_argv[i]);
313 i++;
314 }
315 putchar('\n');
316 }
317
318 server = fork();
319 if (server < 0)
320 errExit("fork");
321 if (server == 0) {
322 if (arg_debug)
323 printf("Starting xvfb...\n");
324
325 // running without privileges - see drop_privs call above
326 assert(getenv("LD_PRELOAD") == NULL);
327 execvp(server_argv[0], server_argv);
328 perror("execvp");
329 _exit(1);
330 }
331
332 if (arg_debug)
333 printf("xephyr server pid %d\n", server);
334
335 // check X11 socket
336 char *fname;
337 if (asprintf(&fname, "/tmp/.X11-unix/X%d", display) == -1)
338 errExit("asprintf");
339 int n = 0;
340 // wait for x11 server to start
341 while (++n < 10) {
342 sleep(1);
343 if (stat(fname, &s) == 0)
344 break;
345 };
346
347 if (n == 10) {
348 fprintf(stderr, "Error: failed to start xephyr\n");
349 exit(1);
350 }
351 free(fname);
352
353 if (arg_debug) {
354 printf("X11 sockets: "); fflush(0);
355 int rv = system("ls /tmp/.X11-unix");
356 (void) rv;
357 }
358
359 setenv("DISPLAY", display_str, 1);
360 // run attach command
361 jail = fork();
362 if (jail < 0)
363 errExit("fork");
364 if (jail == 0) {
365 if (!arg_quiet)
366 printf("\n*** Attaching to Xvfb display %d ***\n\n", display);
367
368 // running without privileges - see drop_privs call above
369 assert(getenv("LD_PRELOAD") == NULL);
370 execvp(jail_argv[0], jail_argv);
371 perror("execvp");
372 _exit(1);
373 }
374
375 // cleanup
376 free(display_str);
377 free(temp);
378
379 // wait for either server or jail termination
380 pid_t pid = wait(NULL);
381
382 // see which process terminated and kill other
383 if (pid == server) {
384 kill(jail, SIGTERM);
385 }
386 else if (pid == jail) {
387 kill(server, SIGTERM);
388 }
389
390 // without this closing Xephyr window may mess your terminal:
391 // "monitoring" process will release terminal before
392 // jail process ends and releases terminal
393 wait(NULL); // fulneral
394
395 exit(0);
396}
202 397
203 398
204#ifdef HAVE_X11
205//$ Xephyr -ac -br -noreset -screen 800x600 :22 & 399//$ Xephyr -ac -br -noreset -screen 800x600 :22 &
206//$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox 400//$ DISPLAY=:22 firejail --net=eth0 --blacklist=/tmp/.X11-unix/x0 firefox
207void x11_start_xephyr(int argc, char **argv) { 401void x11_start_xephyr(int argc, char **argv) {
@@ -210,7 +404,7 @@ void x11_start_xephyr(int argc, char **argv) {
210 struct stat s; 404 struct stat s;
211 pid_t jail = 0; 405 pid_t jail = 0;
212 pid_t server = 0; 406 pid_t server = 0;
213 407
214 setenv("FIREJAIL_X11", "yes", 1); 408 setenv("FIREJAIL_X11", "yes", 1);
215 409
216 // unfortunately, xephyr does a number of weird things when started by root user!!! 410 // unfortunately, xephyr does a number of weird things when started by root user!!!
@@ -227,14 +421,16 @@ void x11_start_xephyr(int argc, char **argv) {
227 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n"); 421 fprintf(stderr, " Arch: sudo pacman -S xorg-server-xephyr\n");
228 exit(0); 422 exit(0);
229 } 423 }
230 424
231 int display = random_display_number(); 425 int display = random_display_number();
232 char *display_str; 426 char *display_str;
233 if (asprintf(&display_str, ":%d", display) == -1) 427 if (asprintf(&display_str, ":%d", display) == -1)
234 errExit("asprintf"); 428 errExit("asprintf");
235 429
236 assert(xephyr_screen); 430 assert(xephyr_screen);
237 char *server_argv[256] = { "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen }; // rest initialyzed to NULL 431 char *server_argv[256] = { // rest initialyzed to NULL
432 "Xephyr", "-ac", "-br", "-noreset", "-screen", xephyr_screen
433 };
238 unsigned pos = 0; 434 unsigned pos = 0;
239 while (server_argv[pos] != NULL) pos++; 435 while (server_argv[pos] != NULL) pos++;
240 if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) { 436 if (checkcfg(CFG_XEPHYR_WINDOW_TITLE)) {
@@ -242,7 +438,7 @@ void x11_start_xephyr(int argc, char **argv) {
242 server_argv[pos++] = "firejail x11 sandbox"; 438 server_argv[pos++] = "firejail x11 sandbox";
243 } 439 }
244 440
245 assert(xephyr_extra_params); // should be "" if empty 441 assert(xephyr_extra_params); // should be "" if empty
246 442
247 // parse xephyr_extra_params 443 // parse xephyr_extra_params
248 // very basic quoting support 444 // very basic quoting support
@@ -255,11 +451,13 @@ void x11_start_xephyr(int argc, char **argv) {
255 for (i = 0; i < (int) strlen(xephyr_extra_params); i++) { 451 for (i = 0; i < (int) strlen(xephyr_extra_params); i++) {
256 if (temp[i] == '\"') { 452 if (temp[i] == '\"') {
257 dquote = !dquote; 453 dquote = !dquote;
258 if (dquote) temp[i] = '\0'; // replace closing quote by \0 454 // replace closing quote by \0
455 if (dquote) temp[i] = '\0';
259 } 456 }
260 if (temp[i] == '\'') { 457 if (temp[i] == '\'') {
261 squote = !squote; 458 squote = !squote;
262 if (squote) temp[i] = '\0'; // replace closing quote by \0 459 // replace closing quote by \0
460 if (squote) temp[i] = '\0';
263 } 461 }
264 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0'; 462 if (!dquote && !squote && temp[i] == ' ') temp[i] = '\0';
265 if (dquote && squote) { 463 if (dquote && squote) {
@@ -281,13 +479,14 @@ void x11_start_xephyr(int argc, char **argv) {
281 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1; 479 else if (temp[i] == '\0' && temp[i+1] != '\0') server_argv[pos++] = temp + i + 1;
282 } 480 }
283 } 481 }
284 482
285 server_argv[pos++] = display_str; 483 server_argv[pos++] = display_str;
286 server_argv[pos++] = NULL; 484 server_argv[pos++] = NULL;
287 485
288 assert(pos < (sizeof(server_argv)/sizeof(*server_argv))); // no overrun 486 // no overrun
289 assert(server_argv[pos-1] == NULL); // last element is null 487 assert(pos < (sizeof(server_argv)/sizeof(*server_argv)));
290 488 assert(server_argv[pos-1] == NULL); // last element is null
489
291 if (arg_debug) { 490 if (arg_debug) {
292 size_t i = 0; 491 size_t i = 0;
293 printf("xephyr server:"); 492 printf("xephyr server:");
@@ -302,18 +501,14 @@ void x11_start_xephyr(int argc, char **argv) {
302 char *jail_argv[argc+2]; 501 char *jail_argv[argc+2];
303 int j = 0; 502 int j = 0;
304 for (i = 0; i < argc; i++) { 503 for (i = 0; i < argc; i++) {
305 if (strcmp(argv[i], "--x11") == 0) 504 if (strncmp(argv[i], "--x11", 5) == 0)
306 continue;
307 if (strcmp(argv[i], "--x11=xpra") == 0)
308 continue;
309 if (strcmp(argv[i], "--x11=xephyr") == 0)
310 continue; 505 continue;
311 jail_argv[j] = argv[i]; 506 jail_argv[j] = argv[i];
312 j++; 507 j++;
313 } 508 }
314 jail_argv[j] = NULL; 509 jail_argv[j] = NULL;
315 510
316 assert(j < argc+2); // no overrun 511 assert(j < argc+2); // no overrun
317 512
318 if (arg_debug) { 513 if (arg_debug) {
319 size_t i = 0; 514 size_t i = 0;
@@ -324,7 +519,7 @@ void x11_start_xephyr(int argc, char **argv) {
324 } 519 }
325 putchar('\n'); 520 putchar('\n');
326 } 521 }
327 522
328 server = fork(); 523 server = fork();
329 if (server < 0) 524 if (server < 0)
330 errExit("fork"); 525 errExit("fork");
@@ -333,7 +528,7 @@ void x11_start_xephyr(int argc, char **argv) {
333 printf("Starting xephyr...\n"); 528 printf("Starting xephyr...\n");
334 529
335 // running without privileges - see drop_privs call above 530 // running without privileges - see drop_privs call above
336 assert(getenv("LD_PRELOAD") == NULL); 531 assert(getenv("LD_PRELOAD") == NULL);
337 execvp(server_argv[0], server_argv); 532 execvp(server_argv[0], server_argv);
338 perror("execvp"); 533 perror("execvp");
339 _exit(1); 534 _exit(1);
@@ -353,17 +548,17 @@ void x11_start_xephyr(int argc, char **argv) {
353 if (stat(fname, &s) == 0) 548 if (stat(fname, &s) == 0)
354 break; 549 break;
355 }; 550 };
356 551
357 if (n == 10) { 552 if (n == 10) {
358 fprintf(stderr, "Error: failed to start xephyr\n"); 553 fprintf(stderr, "Error: failed to start xephyr\n");
359 exit(1); 554 exit(1);
360 } 555 }
361 free(fname); 556 free(fname);
362 557
363 if (arg_debug) { 558 if (arg_debug) {
364 printf("X11 sockets: "); fflush(0); 559 printf("X11 sockets: "); fflush(0);
365 int rv = system("ls /tmp/.X11-unix"); 560 int rv = system("ls /tmp/.X11-unix");
366 (void) rv; 561 (void) rv;
367 } 562 }
368 563
369 setenv("DISPLAY", display_str, 1); 564 setenv("DISPLAY", display_str, 1);
@@ -376,7 +571,7 @@ void x11_start_xephyr(int argc, char **argv) {
376 printf("\n*** Attaching to Xephyr display %d ***\n\n", display); 571 printf("\n*** Attaching to Xephyr display %d ***\n\n", display);
377 572
378 // running without privileges - see drop_privs call above 573 // running without privileges - see drop_privs call above
379 assert(getenv("LD_PRELOAD") == NULL); 574 assert(getenv("LD_PRELOAD") == NULL);
380 execvp(jail_argv[0], jail_argv); 575 execvp(jail_argv[0], jail_argv);
381 perror("execvp"); 576 perror("execvp");
382 _exit(1); 577 _exit(1);
@@ -392,25 +587,27 @@ void x11_start_xephyr(int argc, char **argv) {
392 // see which process terminated and kill other 587 // see which process terminated and kill other
393 if (pid == server) { 588 if (pid == server) {
394 kill(jail, SIGTERM); 589 kill(jail, SIGTERM);
395 } else if (pid == jail) { 590 }
591 else if (pid == jail) {
396 kill(server, SIGTERM); 592 kill(server, SIGTERM);
397 } 593 }
398 594
399 // without this closing Xephyr window may mess your terminal: 595 // without this closing Xephyr window may mess your terminal:
400 // "monitoring" process will release terminal before 596 // "monitoring" process will release terminal before
401 // jail process ends and releases terminal 597 // jail process ends and releases terminal
402 wait(NULL); // fulneral 598 wait(NULL); // fulneral
403 599
404 exit(0); 600 exit(0);
405} 601}
406 602
603
407void x11_start_xpra(int argc, char **argv) { 604void x11_start_xpra(int argc, char **argv) {
408 EUID_ASSERT(); 605 EUID_ASSERT();
409 int i; 606 int i;
410 struct stat s; 607 struct stat s;
411 pid_t client = 0; 608 pid_t client = 0;
412 pid_t server = 0; 609 pid_t server = 0;
413 610
414 setenv("FIREJAIL_X11", "yes", 1); 611 setenv("FIREJAIL_X11", "yes", 1);
415 612
416 // unfortunately, xpra does a number of weird things when started by root user!!! 613 // unfortunately, xpra does a number of weird things when started by root user!!!
@@ -426,7 +623,7 @@ void x11_start_xpra(int argc, char **argv) {
426 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n"); 623 fprintf(stderr, " Debian/Ubuntu/Mint: sudo apt-get install xpra\n");
427 exit(0); 624 exit(0);
428 } 625 }
429 626
430 int display = random_display_number(); 627 int display = random_display_number();
431 char *display_str; 628 char *display_str;
432 if (asprintf(&display_str, ":%d", display) == -1) 629 if (asprintf(&display_str, ":%d", display) == -1)
@@ -455,9 +652,9 @@ void x11_start_xpra(int argc, char **argv) {
455 dup2(fd_null,1); 652 dup2(fd_null,1);
456 dup2(fd_null,2); 653 dup2(fd_null,2);
457 } 654 }
458 655
459 // running without privileges - see drop_privs call above 656 // running without privileges - see drop_privs call above
460 assert(getenv("LD_PRELOAD") == NULL); 657 assert(getenv("LD_PRELOAD") == NULL);
461 execvp(server_argv[0], server_argv); 658 execvp(server_argv[0], server_argv);
462 perror("execvp"); 659 perror("execvp");
463 _exit(1); 660 _exit(1);
@@ -477,18 +674,18 @@ void x11_start_xpra(int argc, char **argv) {
477 if (stat(fname, &s) == 0) 674 if (stat(fname, &s) == 0)
478 break; 675 break;
479 } 676 }
480 677
481 if (n == 10) { 678 if (n == 10) {
482 fprintf(stderr, "Error: failed to start xpra\n"); 679 fprintf(stderr, "Error: failed to start xpra\n");
483 exit(1); 680 exit(1);
484 } 681 }
485 free(fname); 682 free(fname);
486 683
487 if (arg_debug) { 684 if (arg_debug) {
488 printf("X11 sockets: "); fflush(0); 685 printf("X11 sockets: "); fflush(0);
489 int rv = system("ls /tmp/.X11-unix"); 686 int rv = system("ls /tmp/.X11-unix");
490 (void) rv; 687 (void) rv;
491 } 688 }
492 689
493 // build attach command 690 // build attach command
494 char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL }; 691 char *attach_argv[] = { "xpra", "--title=\"firejail x11 sandbox\"", "attach", display_str, NULL };
@@ -508,7 +705,7 @@ void x11_start_xpra(int argc, char **argv) {
508 printf("\n*** Attaching to xpra display %d ***\n\n", display); 705 printf("\n*** Attaching to xpra display %d ***\n\n", display);
509 706
510 // running without privileges - see drop_privs call above 707 // running without privileges - see drop_privs call above
511 assert(getenv("LD_PRELOAD") == NULL); 708 assert(getenv("LD_PRELOAD") == NULL);
512 execvp(attach_argv[0], attach_argv); 709 execvp(attach_argv[0], attach_argv);
513 perror("execvp"); 710 perror("execvp");
514 _exit(1); 711 _exit(1);
@@ -520,11 +717,7 @@ void x11_start_xpra(int argc, char **argv) {
520 char *firejail_argv[argc+2]; 717 char *firejail_argv[argc+2];
521 int pos = 0; 718 int pos = 0;
522 for (i = 0; i < argc; i++) { 719 for (i = 0; i < argc; i++) {
523 if (strcmp(argv[i], "--x11") == 0) 720 if (strncmp(argv[i], "--x11", 5) == 0)
524 continue;
525 if (strcmp(argv[i], "--x11=xpra") == 0)
526 continue;
527 if (strcmp(argv[i], "--x11=xephyr") == 0)
528 continue; 721 continue;
529 firejail_argv[pos] = argv[i]; 722 firejail_argv[pos] = argv[i];
530 pos++; 723 pos++;
@@ -540,8 +733,8 @@ void x11_start_xpra(int argc, char **argv) {
540 errExit("fork"); 733 errExit("fork");
541 if (jail == 0) { 734 if (jail == 0) {
542 // running without privileges - see drop_privs call above 735 // running without privileges - see drop_privs call above
543 assert(getenv("LD_PRELOAD") == NULL); 736 assert(getenv("LD_PRELOAD") == NULL);
544 if (firejail_argv[0]) // shut up llvm scan-build 737 if (firejail_argv[0]) // shut up llvm scan-build
545 execvp(firejail_argv[0], firejail_argv); 738 execvp(firejail_argv[0], firejail_argv);
546 perror("execvp"); 739 perror("execvp");
547 exit(1); 740 exit(1);
@@ -550,8 +743,8 @@ void x11_start_xpra(int argc, char **argv) {
550 if (!arg_quiet) 743 if (!arg_quiet)
551 printf("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail); 744 printf("Xpra server pid %d, xpra client pid %d, jail %d\n", server, client, jail);
552 745
553 sleep(1); // let jail start 746 sleep(1); // adding a delay in order to let the server start
554 747
555 // wait for jail or server to end 748 // wait for jail or server to end
556 while (1) { 749 while (1) {
557 pid_t pid = wait(NULL); 750 pid_t pid = wait(NULL);
@@ -568,7 +761,7 @@ void x11_start_xpra(int argc, char **argv) {
568 dup2(fd_null,2); 761 dup2(fd_null,2);
569 } 762 }
570 // running without privileges - see drop_privs call above 763 // running without privileges - see drop_privs call above
571 assert(getenv("LD_PRELOAD") == NULL); 764 assert(getenv("LD_PRELOAD") == NULL);
572 execvp(stop_argv[0], stop_argv); 765 execvp(stop_argv[0], stop_argv);
573 perror("execvp"); 766 perror("execvp");
574 _exit(1); 767 _exit(1);
@@ -588,7 +781,7 @@ void x11_start_xpra(int argc, char **argv) {
588 else 781 else
589 printf("xpra server successfully stopped in %d secs\n", n); 782 printf("xpra server successfully stopped in %d secs\n", n);
590 } 783 }
591 784
592 // kill xpra server and xpra client 785 // kill xpra server and xpra client
593 kill(client, SIGTERM); 786 kill(client, SIGTERM);
594 kill(server, SIGTERM); 787 kill(server, SIGTERM);
@@ -604,6 +797,7 @@ void x11_start_xpra(int argc, char **argv) {
604 } 797 }
605} 798}
606 799
800
607void x11_start(int argc, char **argv) { 801void x11_start(int argc, char **argv) {
608 EUID_ASSERT(); 802 EUID_ASSERT();
609 803
@@ -628,7 +822,7 @@ void x11_start(int argc, char **argv) {
628#endif 822#endif
629 823
630// Porting notes: 824// Porting notes:
631// 825//
632// 1. merge #1100 from zackw: 826// 1. merge #1100 from zackw:
633// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8 827// Attempting to run xauth -f directly on a file in /run/firejail/mnt/ directory fails on Debian 8
634// with this message: 828// with this message:
@@ -653,7 +847,7 @@ void x11_xorg(void) {
653 struct stat s; 847 struct stat s;
654 if (stat("/usr/bin/xauth", &s) == -1) { 848 if (stat("/usr/bin/xauth", &s) == -1) {
655 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n" 849 fprintf(stderr, "Error: xauth utility not found in PATH. Please install it:\n"
656 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n"); 850 " Debian/Ubuntu/Mint: sudo apt-get install xauth\n");
657 exit(1); 851 exit(1);
658 } 852 }
659 if (s.st_uid != 0 && s.st_gid != 0) { 853 if (s.st_uid != 0 && s.st_gid != 0) {
@@ -695,8 +889,8 @@ void x11_xorg(void) {
695 __gcov_flush(); 889 __gcov_flush();
696#endif 890#endif
697 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname, 891 execlp("/usr/bin/xauth", "/usr/bin/xauth", "-v", "-f", tmpfname,
698 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL); 892 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted", NULL);
699 893
700 _exit(127); 894 _exit(127);
701 } 895 }
702 896
@@ -705,16 +899,19 @@ void x11_xorg(void) {
705 if (waitpid(child, &status, 0) != child) 899 if (waitpid(child, &status, 0) != child)
706 errExit("waitpid"); 900 errExit("waitpid");
707 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 901 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
708 /* success */ 902 /* success */
709 } else if (WIFEXITED(status)) { 903 }
904 else if (WIFEXITED(status)) {
710 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n", 905 fprintf(stderr, "Failed to create untrusted X cookie: xauth: exit %d\n",
711 WEXITSTATUS(status)); 906 WEXITSTATUS(status));
712 exit(1); 907 exit(1);
713 } else if (WIFSIGNALED(status)) { 908 }
909 else if (WIFSIGNALED(status)) {
714 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n", 910 fprintf(stderr, "Failed to create untrusted X cookie: xauth: %s\n",
715 strsignal(WTERMSIG(status))); 911 strsignal(WTERMSIG(status)));
716 exit(1); 912 exit(1);
717 } else { 913 }
914 else {
718 fprintf(stderr, "Failed to create untrusted X cookie: " 915 fprintf(stderr, "Failed to create untrusted X cookie: "
719 "xauth: un-decodable exit status %04x\n", status); 916 "xauth: un-decodable exit status %04x\n", status);
720 exit(1); 917 exit(1);
@@ -728,10 +925,11 @@ void x11_xorg(void) {
728 } 925 }
729 if (set_perms(tmpfname, getuid(), getgid(), 0600)) 926 if (set_perms(tmpfname, getuid(), getgid(), 0600))
730 errExit("set_perms"); 927 errExit("set_perms");
731 928
732 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted 929 // move the temporary file in RUN_XAUTHORITY_SEC_FILE in order to have it deleted
733 // automatically when the sandbox is closed (rename doesn't work) 930 // automatically when the sandbox is closed (rename doesn't work)
734 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) { // root needed 931 // root needed
932 if (copy_file(tmpfname, RUN_XAUTHORITY_SEC_FILE, getuid(), getgid(), 0600)) {
735 fprintf(stderr, "Error: cannot create the new .Xauthority file\n"); 933 fprintf(stderr, "Error: cannot create the new .Xauthority file\n");
736 exit(1); 934 exit(1);
737 } 935 }
@@ -740,7 +938,6 @@ void x11_xorg(void) {
740 /* coverity[toctou] */ 938 /* coverity[toctou] */
741 unlink(tmpfname); 939 unlink(tmpfname);
742 umount("/tmp"); 940 umount("/tmp");
743
744 941
745 // Ensure there is already a file in the usual location, so that bind-mount below will work. 942 // Ensure there is already a file in the usual location, so that bind-mount below will work.
746 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable 943 // todo: fix TOCTOU races, currently managed by imposing /usr/bin/xauth as executable
@@ -765,9 +962,10 @@ void x11_xorg(void) {
765 if (set_perms(dest, getuid(), getgid(), 0600)) 962 if (set_perms(dest, getuid(), getgid(), 0600))
766 errExit("set_perms"); 963 errExit("set_perms");
767 free(dest); 964 free(dest);
768#endif 965#endif
769} 966}
770 967
968
771void fs_x11(void) { 969void fs_x11(void) {
772#ifdef HAVE_X11 970#ifdef HAVE_X11
773 int display = x11_display(); 971 int display = x11_display();
@@ -786,63 +984,62 @@ void fs_x11(void) {
786 if (arg_debug || arg_debug_whitelists) 984 if (arg_debug || arg_debug_whitelists)
787 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file); 985 fprintf(stderr, "Masking all X11 sockets except %s\n", x11file);
788 986
789 // Move the real /tmp/.X11-unix to a scratch location 987 // Move the real /tmp/.X11-unix to a scratch location
790 // so we can still access x11file after we mount a 988 // so we can still access x11file after we mount a
791 // tmpfs over /tmp/.X11-unix. 989 // tmpfs over /tmp/.X11-unix.
792 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700); 990 int rv = mkdir(RUN_WHITELIST_X11_DIR, 0700);
793 if (rv == -1) 991 if (rv == -1)
794 errExit("mkdir"); 992 errExit("mkdir");
795 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700)) 993 if (set_perms(RUN_WHITELIST_X11_DIR, 0, 0, 0700))
796 errExit("set_perms"); 994 errExit("set_perms");
797 995
798 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0) 996 if (mount("/tmp/.X11-unix", RUN_WHITELIST_X11_DIR, 0, MS_BIND|MS_REC, 0) < 0)
799 errExit("mount bind"); 997 errExit("mount bind");
800 998
801 // This directory must be mode 1777, or Xlib will barf. 999 // This directory must be mode 1777, or Xlib will barf.
802 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs", 1000 if (mount("tmpfs", "/tmp/.X11-unix", "tmpfs",
803 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC, 1001 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME | MS_REC,
804 "mode=1777,uid=0,gid=0") < 0) 1002 "mode=1777,uid=0,gid=0") < 0)
805 errExit("mounting tmpfs on /tmp/.X11-unix"); 1003 errExit("mounting tmpfs on /tmp/.X11-unix");
806 fs_logger("tmpfs /tmp/.X11-unix"); 1004 fs_logger("tmpfs /tmp/.X11-unix");
807 1005
808 // create an empty file which will have the desired socket bind-mounted over it 1006 // create an empty file which will have the desired socket bind-mounted over it
809 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT); 1007 int fd = open(x11file, O_RDWR|O_CREAT|O_EXCL, x11stat.st_mode & ~S_IFMT);
810 if (fd < 0) 1008 if (fd < 0)
811 errExit(x11file); 1009 errExit(x11file);
812 if (fchown(fd, x11stat.st_uid, x11stat.st_gid)) 1010 if (fchown(fd, x11stat.st_uid, x11stat.st_gid))
813 errExit("fchown"); 1011 errExit("fchown");
814 close(fd); 1012 close(fd);
815 1013
816 // do the mount 1014 // do the mount
817 char *wx11file; 1015 char *wx11file;
818 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) 1016 if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1)
819 errExit("asprintf"); 1017 errExit("asprintf");
820 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0) 1018 if (mount(wx11file, x11file, NULL, MS_BIND|MS_REC, NULL) < 0)
821 errExit("mount bind"); 1019 errExit("mount bind");
822 fs_logger2("whitelist", x11file); 1020 fs_logger2("whitelist", x11file);
823 1021
824 free(x11file); 1022 free(x11file);
825 free(wx11file); 1023 free(wx11file);
826 1024
827 // block access to RUN_WHITELIST_X11_DIR 1025 // block access to RUN_WHITELIST_X11_DIR
828 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0) 1026 if (mount(RUN_RO_DIR, RUN_WHITELIST_X11_DIR, 0, MS_BIND, 0) < 0)
829 errExit("mount"); 1027 errExit("mount");
830 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR); 1028 fs_logger2("blacklist", RUN_WHITELIST_X11_DIR);
831#endif 1029#endif
832} 1030}
833 1031
1032
834void x11_block(void) { 1033void x11_block(void) {
835#ifdef HAVE_X11 1034#ifdef HAVE_X11
836 mask_x11_abstract_socket = 1;
837
838 // check abstract socket presence and network namespace options 1035 // check abstract socket presence and network namespace options
839 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured) 1036 if ((!arg_nonetwork && !cfg.bridge0.configured && !cfg.interface0.configured)
840 && x11_abstract_sockets_present()) { 1037 && x11_abstract_sockets_present()) {
841 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n" 1038 fprintf(stderr, "ERROR: --x11=none specified, but abstract X11 socket still accessible.\n"
842 "Additional setup required. To block abstract X11 socket you can either:\n" 1039 "Additional setup required. To block abstract X11 socket you can either:\n"
843 " * use network namespace in firejail (--net=none, --net=...)\n" 1040 " * use network namespace in firejail (--net=none, --net=...)\n"
844 " * add \"-nolisten local\" to xserver options\n" 1041 " * add \"-nolisten local\" to xserver options\n"
845 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n"); 1042 " (eg. to your display manager config, or /etc/X11/xinit/xserverrc)\n");
846 exit(1); 1043 exit(1);
847 } 1044 }
848 1045
@@ -867,4 +1064,3 @@ void x11_block(void) {
867 env_store("XAUTHORITY", RMENV); 1064 env_store("XAUTHORITY", RMENV);
868#endif 1065#endif
869} 1066}
870
diff --git a/src/firemon/arp.c b/src/firemon/arp.c
index cef48fb0d..d30983e4a 100644
--- a/src/firemon/arp.c
+++ b/src/firemon/arp.c
@@ -80,7 +80,7 @@ void arp(pid_t pid, int print_procs) {
80 for (i = 0; i < max_pids; i++) { 80 for (i = 0; i < max_pids; i++) {
81 if (pids[i].level == 1) { 81 if (pids[i].level == 1) {
82 if (print_procs || pid == 0) 82 if (print_procs || pid == 0)
83 pid_print_list(i, 0); 83 pid_print_list(i, arg_nowrap);
84 int child = find_child(i); 84 int child = find_child(i);
85 if (child != -1) { 85 if (child != -1) {
86 char *fname; 86 char *fname;
diff --git a/src/firemon/caps.c b/src/firemon/caps.c
index 8837c9ee7..a13b784a2 100644
--- a/src/firemon/caps.c
+++ b/src/firemon/caps.c
@@ -38,9 +38,7 @@ static void print_caps(int pid) {
38 if (strncmp(buf, "CapBnd:", 7) == 0) { 38 if (strncmp(buf, "CapBnd:", 7) == 0) {
39 printf(" %s", buf); 39 printf(" %s", buf);
40 fflush(0); 40 fflush(0);
41 free(file); 41 break;
42 fclose(fp);
43 return;
44 } 42 }
45 } 43 }
46 fclose(fp); 44 fclose(fp);
@@ -55,7 +53,7 @@ void caps(pid_t pid, int print_procs) {
55 for (i = 0; i < max_pids; i++) { 53 for (i = 0; i < max_pids; i++) {
56 if (pids[i].level == 1) { 54 if (pids[i].level == 1) {
57 if (print_procs || pid == 0) 55 if (print_procs || pid == 0)
58 pid_print_list(i, 0); 56 pid_print_list(i, arg_nowrap);
59 int child = find_child(i); 57 int child = find_child(i);
60 if (child != -1) 58 if (child != -1)
61 print_caps(child); 59 print_caps(child);
diff --git a/src/firemon/cgroup.c b/src/firemon/cgroup.c
index bbb28f619..48427210b 100644
--- a/src/firemon/cgroup.c
+++ b/src/firemon/cgroup.c
@@ -52,7 +52,7 @@ void cgroup(pid_t pid, int print_procs) {
52 for (i = 0; i < max_pids; i++) { 52 for (i = 0; i < max_pids; i++) {
53 if (pids[i].level == 1) { 53 if (pids[i].level == 1) {
54 if (print_procs || pid == 0) 54 if (print_procs || pid == 0)
55 pid_print_list(i, 0); 55 pid_print_list(i, arg_nowrap);
56 int child = find_child(i); 56 int child = find_child(i);
57 if (child != -1) 57 if (child != -1)
58 print_cgroup(child); 58 print_cgroup(child);
diff --git a/src/firemon/cpu.c b/src/firemon/cpu.c
index 47c935686..2a6979573 100644
--- a/src/firemon/cpu.c
+++ b/src/firemon/cpu.c
@@ -39,9 +39,7 @@ static void print_cpu(int pid) {
39 if (strncmp(buf, "Cpus_allowed_list:", 18) == 0) { 39 if (strncmp(buf, "Cpus_allowed_list:", 18) == 0) {
40 printf(" %s", buf); 40 printf(" %s", buf);
41 fflush(0); 41 fflush(0);
42 free(file); 42 break;
43 fclose(fp);
44 return;
45 } 43 }
46 } 44 }
47 fclose(fp); 45 fclose(fp);
@@ -56,7 +54,7 @@ void cpu(pid_t pid, int print_procs) {
56 for (i = 0; i < max_pids; i++) { 54 for (i = 0; i < max_pids; i++) {
57 if (pids[i].level == 1) { 55 if (pids[i].level == 1) {
58 if (print_procs || pid == 0) 56 if (print_procs || pid == 0)
59 pid_print_list(i, 0); 57 pid_print_list(i, arg_nowrap);
60 int child = find_child(i); 58 int child = find_child(i);
61 if (child != -1) 59 if (child != -1)
62 print_cpu(child); 60 print_cpu(child);
diff --git a/src/firemon/interface.c b/src/firemon/interface.c
index ba3c9fceb..77dd1f277 100644
--- a/src/firemon/interface.c
+++ b/src/firemon/interface.c
@@ -163,7 +163,7 @@ void interface(pid_t pid, int print_procs) {
163 for (i = 0; i < max_pids; i++) { 163 for (i = 0; i < max_pids; i++) {
164 if (pids[i].level == 1) { 164 if (pids[i].level == 1) {
165 if (print_procs || pid == 0) 165 if (print_procs || pid == 0)
166 pid_print_list(i, 0); 166 pid_print_list(i, arg_nowrap);
167 int child = find_child(i); 167 int child = find_child(i);
168 if (child != -1) { 168 if (child != -1) {
169 print_sandbox(child); 169 print_sandbox(child);
diff --git a/src/firemon/list.c b/src/firemon/list.c
index 1df737e8c..2152df31f 100644
--- a/src/firemon/list.c
+++ b/src/firemon/list.c
@@ -26,7 +26,7 @@ void list(void) {
26 int i; 26 int i;
27 for (i = 0; i < max_pids; i++) { 27 for (i = 0; i < max_pids; i++) {
28 if (pids[i].level == 1) 28 if (pids[i].level == 1)
29 pid_print_list(i, 0); 29 pid_print_list(i, arg_nowrap);
30 } 30 }
31} 31}
32 32
diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c
index ebcb7a72c..378bdefe9 100644
--- a/src/firemon/procevent.c
+++ b/src/firemon/procevent.c
@@ -150,10 +150,8 @@ doexit:
150static int procevent_netlink_setup(void) { 150static int procevent_netlink_setup(void) {
151 // open socket for process event connector 151 // open socket for process event connector
152 int sock; 152 int sock;
153 if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) < 0) { 153 if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) < 0)
154 fprintf(stderr, "Error: cannot open netlink socket\n"); 154 goto errexit;
155 exit(1);
156 }
157 155
158 // bind socket 156 // bind socket
159 struct sockaddr_nl addr; 157 struct sockaddr_nl addr;
@@ -161,10 +159,8 @@ static int procevent_netlink_setup(void) {
161 addr.nl_pid = getpid(); 159 addr.nl_pid = getpid();
162 addr.nl_family = AF_NETLINK; 160 addr.nl_family = AF_NETLINK;
163 addr.nl_groups = CN_IDX_PROC; 161 addr.nl_groups = CN_IDX_PROC;
164 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 162 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
165 fprintf(stderr, "Error: cannot bind to netlink socket\n"); 163 goto errexit;
166 exit(1);
167 }
168 164
169 // send monitoring message 165 // send monitoring message
170 struct nlmsghdr nlmsghdr; 166 struct nlmsghdr nlmsghdr;
@@ -189,12 +185,13 @@ static int procevent_netlink_setup(void) {
189 iov[2].iov_base = &op; 185 iov[2].iov_base = &op;
190 iov[2].iov_len = sizeof(op); 186 iov[2].iov_len = sizeof(op);
191 187
192 if (writev(sock, iov, 3) == -1) { 188 if (writev(sock, iov, 3) == -1)
193 fprintf(stderr, "Error: cannot write to netlink socket\n"); 189 goto errexit;
194 exit(1);
195 }
196 190
197 return sock; 191 return sock;
192errexit:
193 fprintf(stderr, "Error: netlink socket problem\n");
194 exit(1);
198} 195}
199 196
200 197
diff --git a/src/firemon/route.c b/src/firemon/route.c
index dff594431..145daa152 100644
--- a/src/firemon/route.c
+++ b/src/firemon/route.c
@@ -189,7 +189,7 @@ void route(pid_t pid, int print_procs) {
189 for (i = 0; i < max_pids; i++) { 189 for (i = 0; i < max_pids; i++) {
190 if (pids[i].level == 1) { 190 if (pids[i].level == 1) {
191 if (print_procs || pid == 0) 191 if (print_procs || pid == 0)
192 pid_print_list(i, 0); 192 pid_print_list(i, arg_nowrap);
193 int child = find_child(i); 193 int child = find_child(i);
194 if (child != -1) { 194 if (child != -1) {
195 char *fname; 195 char *fname;
diff --git a/src/firemon/seccomp.c b/src/firemon/seccomp.c
index d50692b37..e530fa1c3 100644
--- a/src/firemon/seccomp.c
+++ b/src/firemon/seccomp.c
@@ -37,9 +37,7 @@ static void print_seccomp(int pid) {
37 if (strncmp(buf, "Seccomp:", 8) == 0) { 37 if (strncmp(buf, "Seccomp:", 8) == 0) {
38 printf(" %s", buf); 38 printf(" %s", buf);
39 fflush(0); 39 fflush(0);
40 fclose(fp); 40 break;
41 free(file);
42 return;
43 } 41 }
44 } 42 }
45 fclose(fp); 43 fclose(fp);
@@ -54,7 +52,7 @@ void seccomp(pid_t pid, int print_procs) {
54 for (i = 0; i < max_pids; i++) { 52 for (i = 0; i < max_pids; i++) {
55 if (pids[i].level == 1) { 53 if (pids[i].level == 1) {
56 if (print_procs || pid == 0) 54 if (print_procs || pid == 0)
57 pid_print_list(i, 0); 55 pid_print_list(i, arg_nowrap);
58 int child = find_child(i); 56 int child = find_child(i);
59 if (child != -1) 57 if (child != -1)
60 print_seccomp(child); 58 print_seccomp(child);
diff --git a/src/firemon/usage.c b/src/firemon/usage.c
index 1768237b3..20f2c071b 100644
--- a/src/firemon/usage.c
+++ b/src/firemon/usage.c
@@ -37,6 +37,7 @@ void usage(void) {
37 printf("\t--name=name - print information only about named sandbox.\n\n"); 37 printf("\t--name=name - print information only about named sandbox.\n\n");
38 printf("\t--netstats - monitor network statistics for sandboxes creating a new\n"); 38 printf("\t--netstats - monitor network statistics for sandboxes creating a new\n");
39 printf("\t\tnetwork namespace.\n\n"); 39 printf("\t\tnetwork namespace.\n\n");
40 printf("\t--nowrap - enable line wrapping in terminals.\n\n");
40 printf("\t--route - print route table for each sandbox.\n\n"); 41 printf("\t--route - print route table for each sandbox.\n\n");
41 printf("\t--seccomp - print seccomp configuration for each sandbox.\n\n"); 42 printf("\t--seccomp - print seccomp configuration for each sandbox.\n\n");
42 printf("\t--tree - print a tree of all sandboxed processes.\n\n"); 43 printf("\t--tree - print a tree of all sandboxed processes.\n\n");
diff --git a/src/firemon/x11.c b/src/firemon/x11.c
index 97cfffe64..c923c8ef8 100644
--- a/src/firemon/x11.c
+++ b/src/firemon/x11.c
@@ -30,7 +30,7 @@ void x11(pid_t pid, int print_procs) {
30 for (i = 0; i < max_pids; i++) { 30 for (i = 0; i < max_pids; i++) {
31 if (pids[i].level == 1) { 31 if (pids[i].level == 1) {
32 if (print_procs || pid == 0) 32 if (print_procs || pid == 0)
33 pid_print_list(i, 0); 33 pid_print_list(i, arg_nowrap);
34 34
35 char *x11file; 35 char *x11file;
36 // todo: use macro from src/firejail/firejail.h for /run/firejail/x11 directory 36 // todo: use macro from src/firejail/firejail.h for /run/firejail/x11 directory
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index aa1aec567..76cd4d4fa 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -103,6 +103,12 @@ If the file name matches file_name, the file will not be blacklisted in any blac
103Example: "noblacklist ${HOME}/.mozilla" 103Example: "noblacklist ${HOME}/.mozilla"
104 104
105.TP 105.TP
106\fBnowhitelist file_name
107If the file name matches file_name, the file will not be whitelisted in any whitelist commands that follow.
108
109Example: "nowhitelist ~/.config"
110
111.TP
106\fBignore 112\fBignore
107Ignore command. 113Ignore command.
108 114
@@ -156,7 +162,7 @@ Mount-bind directory1 on top of directory2. This option is only available when r
156Mount-bind file1 on top of file2. This option is only available when running as root. 162Mount-bind file1 on top of file2. This option is only available when running as root.
157.TP 163.TP
158\fBmkdir directory 164\fBmkdir directory
159Create a directory in user home before the sandbox is started. 165Create a directory in user home or under /tmp before the sandbox is started.
160The directory is created if it doesn't already exist. 166The directory is created if it doesn't already exist.
161.br 167.br
162 168
@@ -177,8 +183,8 @@ mkdir ~/.cache/mozilla/firefox
177whitelist ~/.cache/mozilla/firefox 183whitelist ~/.cache/mozilla/firefox
178.TP 184.TP
179\fBmkfile file 185\fBmkfile file
180Similar to mkdir, this command creates a file in user home before the sandbox is started. 186Similar to mkdir, this command creates a file in user home or under /tmp before the sandbox is started.
181The file is created if it doesn't already exist, but it's target directory has to exist. 187The file is created if it doesn't already exist.
182.TP 188.TP
183\fBnoexec file_or_directory 189\fBnoexec file_or_directory
184Remount the file or the directory noexec, nodev and nosuid. 190Remount the file or the directory noexec, nodev and nosuid.
@@ -310,13 +316,16 @@ Remove DISPLAY and XAUTHORITY environment variables.
310Stop with error message if X11 abstract socket will be accessible in jail. 316Stop with error message if X11 abstract socket will be accessible in jail.
311.TP 317.TP
312\fBx11 xephyr 318\fBx11 xephyr
313Enable X11 sandboxing with xephyr. 319Enable X11 sandboxing with Xephyr server.
314.TP 320.TP
315\fBx11 xorg 321\fBx11 xorg
316Enable X11 sandboxing with X11 security extension. 322Enable X11 sandboxing with X11 security extension.
317.TP 323.TP
318\fBx11 xpra 324\fBx11 xpra
319Enable X11 sandboxing with xpra. 325Enable X11 sandboxing with Xpra server.
326.TP
327\fBx11 xvfb
328Enable X11 sandboxing with Xvfb server.
320 329
321.SH Resource limits, CPU affinity, Control Groups 330.SH Resource limits, CPU affinity, Control Groups
322These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. 331These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox.
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index f978661dc..f603daecb 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -1067,6 +1067,10 @@ Example:
1067$ firejail \-\-nosound firefox 1067$ firejail \-\-nosound firefox
1068 1068
1069.TP 1069.TP
1070\fB\-\-nowhitelist=dirname_or_filename
1071Disable whitelist for this directory or file.
1072
1073.TP
1070\fB\-\-output=logfile 1074\fB\-\-output=logfile
1071stdout logging and log rotation. Copy stdout and stderr to logfile, and keep the size of the file under 500KB using log 1075stdout logging and log rotation. Copy stdout and stderr to logfile, and keep the size of the file under 500KB using log
1072rotation. Five files with prefixes .1 to .5 are used in rotation. 1076rotation. Five files with prefixes .1 to .5 are used in rotation.
@@ -1772,17 +1776,17 @@ $ sudo firejail --writable-var-log
1772 1776
1773.TP 1777.TP
1774\fB\-\-x11 1778\fB\-\-x11
1775Sandbox the application using Xpra, Xephyr or Xorg security extension. 1779Sandbox the application using Xpra, Xephyr, Xvfb or Xorg security extension.
1776The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing 1780The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing
1777clients running outside the sandbox. 1781clients running outside the sandbox.
1778Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. 1782Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr.
1779If all fails, Firejail will not attempt to use X11 security extension. 1783If all fails, Firejail will not attempt to use Xvfb or X11 security extension.
1780.br 1784.br
1781 1785
1782.br 1786.br
1783Xpra and Xephyr modes require a network namespace to be instantiated in order to disable 1787Xpra, Xephyr and Xvfb modes require a network namespace to be instantiated in order to disable
1784X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket 1788X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket
1785by adding "-nolisten local" on Xorg command line. 1789by adding "-nolisten local" on Xorg command line at system level.
1786.br 1790.br
1787 1791
1788.br 1792.br
@@ -1859,6 +1863,68 @@ Example:
1859.br 1863.br
1860$ firejail \-\-x11=xpra --net=eth0 firefox 1864$ firejail \-\-x11=xpra --net=eth0 firefox
1861 1865
1866
1867.TP
1868\fB\-\-x11=xvfb
1869Start Xvfb X11 server and attach the sandbox to this server.
1870Xvfb, short for X virtual framebuffer, performs all graphical operations in memory
1871without showing any screen output. Xvfb is mainly used for remote access and software
1872testing on headless servers.
1873.br
1874
1875.br
1876On Debian platforms Xvfb is installed with the command \fBsudo apt-get install xvfb\fR.
1877This feature is not available when running as root.
1878.br
1879
1880.br
1881Example: remote VNC access
1882.br
1883
1884.br
1885On the server we start a sandbox using Xvfb and openbox
1886window manager. The default size of Xvfb screen is 800x600 - it can be changed
1887in /etc/firejail/firejail.config (xvfb-screen). Some sort of networking (--net) is required
1888in order to isolate the abstract sockets used by other X servers.
1889.br
1890
1891.br
1892$ firejail --net=none --x11=xvfb openbox
1893.br
1894
1895.br
1896*** Attaching to Xvfb display 792 ***
1897.br
1898
1899.br
1900Reading profile /etc/firejail/openbox.profile
1901.br
1902Reading profile /etc/firejail/disable-common.inc
1903.br
1904Reading profile /etc/firejail/disable-common.local
1905.br
1906Parent pid 5400, child pid 5401
1907.br
1908
1909.br
1910On the server we also start a VNC server and attach it to the display handled by our
1911Xvfb server (792).
1912.br
1913
1914.br
1915$ x11vnc -display :792
1916.br
1917
1918.br
1919On the client machine we start a VNC viewer and use it to connect to our server:
1920.br
1921
1922.br
1923$ vncviewer
1924.br
1925
1926
1927
1862.TP 1928.TP
1863\fB\-\-zsh 1929\fB\-\-zsh
1864Use /usr/bin/zsh as default user shell. 1930Use /usr/bin/zsh as default user shell.
diff --git a/src/man/firemon.txt b/src/man/firemon.txt
index bd84401af..ecb626fc6 100644
--- a/src/man/firemon.txt
+++ b/src/man/firemon.txt
@@ -37,6 +37,9 @@ Print information only about named sandbox.
37\fB\-\-netstats 37\fB\-\-netstats
38Monitor network statistics for sandboxes creating a new network namespace. 38Monitor network statistics for sandboxes creating a new network namespace.
39.TP 39.TP
40\fB\-\-nowrap
41Enable line wrapping in terminals. By default the lines are trimmed.
42.TP
40\fB\-\-route 43\fB\-\-route
41Print route table for each sandbox. 44Print route table for each sandbox.
42.TP 45.TP
diff --git a/test/apps-x11-xorg/firefox.exp b/test/apps-x11-xorg/firefox.exp
index 4da9e5a16..4fd17caa6 100755
--- a/test/apps-x11-xorg/firefox.exp
+++ b/test/apps-x11-xorg/firefox.exp
@@ -41,7 +41,7 @@ expect {
41sleep 2 41sleep 2
42 42
43spawn $env(SHELL) 43spawn $env(SHELL)
44send -- "firemon --seccomp\r" 44send -- "firemon --seccomp --nowrap\r"
45expect { 45expect {
46 timeout {puts "TESTING ERROR 5\n";exit} 46 timeout {puts "TESTING ERROR 5\n";exit}
47 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit} 47 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit}
@@ -61,7 +61,7 @@ expect {
61 "name=blablabla" 61 "name=blablabla"
62} 62}
63sleep 1 63sleep 1
64send -- "firemon --caps\r" 64send -- "firemon --caps --nowrap\r"
65expect { 65expect {
66 timeout {puts "TESTING ERROR 6\n";exit} 66 timeout {puts "TESTING ERROR 6\n";exit}
67 " firefox" {puts "firefox detected\n";} 67 " firefox" {puts "firefox detected\n";}
diff --git a/test/apps-x11-xorg/icedove.exp b/test/apps-x11-xorg/icedove.exp
index ce1d38222..8f6722cd7 100755
--- a/test/apps-x11-xorg/icedove.exp
+++ b/test/apps-x11-xorg/icedove.exp
@@ -38,7 +38,7 @@ expect {
38sleep 2 38sleep 2
39 39
40spawn $env(SHELL) 40spawn $env(SHELL)
41send -- "firemon --seccomp\r" 41send -- "firemon --seccomp --nowrap\r"
42expect { 42expect {
43 timeout {puts "TESTING ERROR 5\n";exit} 43 timeout {puts "TESTING ERROR 5\n";exit}
44 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit} 44 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit}
@@ -57,7 +57,7 @@ expect {
57 "name=blablabla" 57 "name=blablabla"
58} 58}
59sleep 2 59sleep 2
60send -- "firemon --caps\r" 60send -- "firemon --caps --nowrap\r"
61expect { 61expect {
62 timeout {puts "TESTING ERROR 6\n";exit} 62 timeout {puts "TESTING ERROR 6\n";exit}
63 ":firejail" 63 ":firejail"
diff --git a/test/apps-x11-xorg/transmission-gtk.exp b/test/apps-x11-xorg/transmission-gtk.exp
index c6d9ba13a..3eb537c1b 100755
--- a/test/apps-x11-xorg/transmission-gtk.exp
+++ b/test/apps-x11-xorg/transmission-gtk.exp
@@ -38,7 +38,7 @@ expect {
38sleep 2 38sleep 2
39 39
40spawn $env(SHELL) 40spawn $env(SHELL)
41send -- "firemon --seccomp\r" 41send -- "firemon --seccomp --nowrap\r"
42expect { 42expect {
43 timeout {puts "TESTING ERROR 5\n";exit} 43 timeout {puts "TESTING ERROR 5\n";exit}
44 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit} 44 "need to be root" {puts "/proc mounted as hidepid, exiting...\n"; exit}
@@ -57,7 +57,7 @@ expect {
57 "name=blablabla" 57 "name=blablabla"
58} 58}
59sleep 1 59sleep 1
60send -- "firemon --caps\r" 60send -- "firemon --caps --nowrap\r"
61expect { 61expect {
62 timeout {puts "TESTING ERROR 6\n";exit} 62 timeout {puts "TESTING ERROR 6\n";exit}
63 ":firejail" 63 ":firejail"
diff --git a/test/environment/nice.exp b/test/environment/nice.exp
index 2c00d1485..50e789c9e 100755
--- a/test/environment/nice.exp
+++ b/test/environment/nice.exp
@@ -77,7 +77,48 @@ expect {
77 "top" 77 "top"
78} 78}
79 79
80sleep 1
81send -- "exit\r"
82after 100
83
84
85# negative nice value should result in nice=0
86send -- "firejail --nice=-5\r"
87expect {
88 timeout {puts "TESTING ERROR 17\n";exit}
89 "Child process initialized"
90}
91sleep 1
92
93send -- "top -b -n 1\r"
94expect {
95 timeout {puts "TESTING ERROR 18\n";exit}
96 $env(USER)
97}
98expect {
99 timeout {puts "TESTING ERROR 19\n";exit}
100 "0"
101}
102expect {
103 timeout {puts "TESTING ERROR 20\n";exit}
104 "bash"
105}
106expect {
107 timeout {puts "TESTING ERROR 21\n";exit}
108 $env(USER)
109}
110expect {
111 timeout {puts "TESTING ERROR 22\n";exit}
112 "0"
113}
114expect {
115 timeout {puts "TESTING ERROR 23\n";exit}
116 "top"
117}
80 118
119sleep 1
120send -- "exit\r"
121after 100
81 122
82puts "\nall done\n" 123puts "\nall done\n"
83 124
diff --git a/test/environment/output.exp b/test/environment/output.exp
index 10c325832..d175ddae2 100755
--- a/test/environment/output.exp
+++ b/test/environment/output.exp
@@ -61,5 +61,27 @@ expect {
61} 61}
62after 100 62after 100
63send -- "rm -f logfile*\r" 63send -- "rm -f logfile*\r"
64
65
66send -- "firejail --output=../logfile -- ./output.sh\r"
67expect {
68 timeout {puts "TESTING ERROR 8\n";exit}
69 "invalid output file"
70}
71after 100
72
73send -- "firejail --output=/etc -- ./output.sh\r"
74expect {
75 timeout {puts "TESTING ERROR 9\n";exit}
76 "invalid output file"
77}
78after 100
79
80send -- "firejail --output=/etc/firejail/zoom.profile -- ./output.sh\r"
81expect {
82 timeout {puts "TESTING ERROR 10\n";exit}
83 "the output file needs to be owned by the current user"
84}
85
64after 100 86after 100
65puts "\nall done\n" 87puts "\nall done\n"
diff --git a/test/filters/debug.exp b/test/filters/debug.exp
new file mode 100755
index 000000000..493022c05
--- /dev/null
+++ b/test/filters/debug.exp
@@ -0,0 +1,45 @@
1#!/usr/bin/expect -f
2# This file is part of Firejail project
3# Copyright (C) 2014-2017 Firejail Authors
4# License GPL v2
5
6set timeout 10
7spawn $env(SHELL)
8match_max 100000
9
10send -- "firejail --debug-syscalls\r"
11expect {
12 timeout {puts "TESTING ERROR 0\n";exit}
13 "set_mempolicy"
14}
15after 100
16
17send -- "firejail --debug-syscalls\r"
18expect {
19 timeout {puts "TESTING ERROR 1\n";exit}
20 "setitimer"
21}
22after 100
23
24send -- "firejail --debug-errnos\r"
25expect {
26 timeout {puts "TESTING ERROR 2\n";exit}
27 "EBADMSG"
28}
29after 100
30
31send -- "firejail --debug-errnos\r"
32expect {
33 timeout {puts "TESTING ERROR 3\n";exit}
34 "ELIBMAX"
35}
36after 100
37
38send -- "firejail --debug-protocols\r"
39expect {
40 timeout {puts "TESTING ERROR 4\n";exit}
41 "unix, inet, inet6, netlink, packet"
42}
43after 100
44
45puts "all done\n"
diff --git a/test/filters/filters.sh b/test/filters/filters.sh
index 73e0e4d5c..4996e6d66 100755
--- a/test/filters/filters.sh
+++ b/test/filters/filters.sh
@@ -6,6 +6,9 @@
6export MALLOC_CHECK_=3 6export MALLOC_CHECK_=3
7export MALLOC_PERTURB_=$(($RANDOM % 255 + 1)) 7export MALLOC_PERTURB_=$(($RANDOM % 255 + 1))
8 8
9echo "TESTING: debug options (test/filters/debug.exp)"
10./debug.exp
11
9echo "TESTING: noroot (test/filters/noroot.exp)" 12echo "TESTING: noroot (test/filters/noroot.exp)"
10./noroot.exp 13./noroot.exp
11 14
diff --git a/test/fs/whitelist.exp b/test/fs/whitelist.exp
index 20492c739..8ebad48f0 100755
--- a/test/fs/whitelist.exp
+++ b/test/fs/whitelist.exp
@@ -161,7 +161,7 @@ expect {
161} 161}
162expect { 162expect {
163 timeout {puts "TESTING ERROR 31\n";exit} 163 timeout {puts "TESTING ERROR 31\n";exit}
164 "exiting" 164 "cannot sync with peer"
165} 165}
166sleep 1 166sleep 1
167 167
diff --git a/test/root/checkcfg.exp b/test/root/checkcfg.exp
new file mode 100755
index 000000000..e1ec6cf79
--- /dev/null
+++ b/test/root/checkcfg.exp
@@ -0,0 +1,106 @@
1#!/usr/bin/expect -f
2# This file is part of Firejail project
3# Copyright (C) 2014-2017 Firejail Authors
4# License GPL v2
5
6set timeout 10
7cd /home
8spawn $env(SHELL)
9match_max 100000
10
11send -- "firejail --noprofile --overlay\r"
12expect {
13 timeout {puts "TESTING ERROR 0\n";exit}
14 "Child process initialized"
15}
16sleep 2
17
18send -- "rm /etc/firejail/firejail.config\r"
19after 100
20
21send -- "firejail\r"
22expect {
23 timeout {puts "TESTING ERROR 1\n";exit}
24 "firejail.config not found"
25}
26
27# seccomp
28send -- "echo \"seccomp no\" > /etc/firejail/firejail.config\r"
29after 100
30send -- "firejail --noprofile --seccomp --force\r"
31expect {
32 timeout {puts "TESTING ERROR 2\n";exit}
33 "seccomp feature is disabled in Firejail configuration file\r"
34}
35send -- "exit\r"
36after 100
37
38# whitelist
39send -- "echo \"whitelist no\" > /etc/firejail/firejail.config\r"
40after 100
41send -- "firejail --noprofile --whitelist=~/.config --force\r"
42expect {
43 timeout {puts "TESTING ERROR 3\n";exit}
44 "whitelist feature is disabled in Firejail configuration file\r"
45}
46
47# network
48send -- "echo \"network no\" > /etc/firejail/firejail.config\r"
49after 100
50send -- "firejail --noprofile --net=eth0 --force\r"
51expect {
52 timeout {puts "TESTING ERROR 4\n";exit}
53 "networking feature is disabled in Firejail configuration file\r"
54}
55
56# bind
57send -- "echo \"bind no\" > /etc/firejail/firejail.config\r"
58after 100
59send -- "firejail --noprofile --bind=/tmp,/var/tmp --force\r"
60expect {
61 timeout {puts "TESTING ERROR 5\n";exit}
62 "bind feature is disabled in Firejail configuration file\r"
63}
64
65# overlay
66send -- "echo \"overlayfs no\" > /etc/firejail/firejail.config\r"
67after 100
68send -- "firejail --noprofile --overlay --force\r"
69expect {
70 timeout {puts "TESTING ERROR 6\n";exit}
71 "overlayfs feature is disabled in Firejail configuration file\r"
72}
73
74# private-home
75send -- "echo \"private-home no\" > /etc/firejail/firejail.config\r"
76after 100
77send -- "firejail --noprofile --private-home=/tmp --force\r"
78expect {
79 timeout {puts "TESTING ERROR 7\n";exit}
80 "private-home feature is disabled in Firejail configuration file\r"
81}
82
83# chroot
84send -- "echo \"chroot no\" > /etc/firejail/firejail.config\r"
85after 100
86send -- "firejail --noprofile --chroot=/tmp --force\r"
87expect {
88 timeout {puts "TESTING ERROR 8\n";exit}
89 "chroot feature is disabled in Firejail configuration file\r"
90}
91
92# userns
93send -- "echo \"userns no\" > /etc/firejail/firejail.config\r"
94after 100
95send -- "firejail --noprofile --noroot --force\r"
96expect {
97 timeout {puts "TESTING ERROR 9\n";exit}
98 "noroot feature is disabled in Firejail configuration file\r"
99}
100
101send -- "exit\r"
102after 100
103
104
105after 100
106puts "\nall done\n"
diff --git a/test/root/git.exp b/test/root/git.exp
new file mode 100755
index 000000000..c5ddeee89
--- /dev/null
+++ b/test/root/git.exp
@@ -0,0 +1,51 @@
1#!/usr/bin/expect -f
2
3set timeout 10
4spawn $env(SHELL)
5match_max 100000
6
7
8
9send -- "firejail --version\r"
10expect {
11 timeout {puts "TESTING ERROR 1\n";exit}
12 "git install support is disabled" { puts "TESTING: git support not available in current build\n"; exit}
13 "git install support is enabled" { puts "git support available\n"}
14}
15
16set timeout 120
17send -- "firejail --git-install\r"
18expect {
19 timeout {puts "TESTING ERROR 2\n";exit}
20 "Cloning into"
21}
22expect {
23 timeout {puts "TESTING ERROR 3\n";exit}
24 "Configuration options"
25}
26expect {
27 timeout {puts "TESTING ERROR 4\n";exit}
28 "src/fseccomp/fseccomp default seccomp"
29}
30expect {
31 timeout {puts "TESTING ERROR 5\n";exit}
32 "Mainline git Firejail version was installed in"
33}
34after 100
35
36send -- "firejail --git-uninstall\r"
37expect {
38 timeout {puts "TESTING ERROR 6\n";exit}
39 "Cloning into"
40}
41expect {
42 timeout {puts "TESTING ERROR 7\n";exit}
43 "Configuration options"
44}
45expect {
46 timeout {puts "TESTING ERROR 8\n";exit}
47 "Firejail mainline git version uninstalled from"
48}
49after 100
50
51puts "\nall done\n"
diff --git a/test/root/root.sh b/test/root/root.sh
index 9764b3804..e23499d2a 100755
--- a/test/root/root.sh
+++ b/test/root/root.sh
@@ -62,6 +62,9 @@ echo "TESTING: fs whitelist mnt, opt, media (test/root/whitelist-mnt.exp)"
62echo "TESTING: join (test/root/join.exp)" 62echo "TESTING: join (test/root/join.exp)"
63./join.exp 63./join.exp
64 64
65echo "TESTING: git-install (test/root/git.exp)"
66./git.exp
67
65#******************************** 68#********************************
66# seccomp 69# seccomp
67#******************************** 70#********************************
@@ -77,6 +80,9 @@ echo "TESTING: seccomp chown (test/root/seccomp-chown.exp)"
77#******************************** 80#********************************
78# command line options 81# command line options
79#******************************** 82#********************************
83echo "TESTING: firejail configuration (test/root/checkcfg.exp)"
84./checkcfg.exp
85
80echo "TESTING: tmpfs (test/root/option_tmpfs.exp)" 86echo "TESTING: tmpfs (test/root/option_tmpfs.exp)"
81./option_tmpfs.exp 87./option_tmpfs.exp
82 88
diff --git a/test/utils/join.exp b/test/utils/join.exp
index b74b0b17a..d5c421676 100755
--- a/test/utils/join.exp
+++ b/test/utils/join.exp
@@ -16,6 +16,14 @@ expect {
16sleep 2 16sleep 2
17 17
18spawn $env(SHELL) 18spawn $env(SHELL)
19send -- "firejail --shell=none --join=jointesting\r"
20expect {
21 timeout {puts "TESTING ERROR 1\n";exit}
22 "shell=none set, but no command specified"
23}
24after 100
25
26
19send -- "firejail --join=jointesting\r" 27send -- "firejail --join=jointesting\r"
20expect { 28expect {
21 timeout {puts "TESTING ERROR 1\n";exit} 29 timeout {puts "TESTING ERROR 1\n";exit}