diff options
53 files changed, 1246 insertions, 391 deletions
@@ -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 | ||
129 | Austin S. Hemmelgarn (https://github.com/Ferroin) | 130 | Austin S. Hemmelgarn (https://github.com/Ferroin) |
130 | - unbound profile update | 131 | - unbound profile update |
131 | Igor Bukanov (https://github.com/ibukanov) | 132 | Igor 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 | ||
157 | thewisenerd (https://github.com/thewisenerd) | 159 | thewisenerd (https://github.com/thewisenerd) |
158 | - appimage: pass commandline arguments | 160 | - appimage: pass commandline arguments |
159 | KOLANICH (https://github.com/KOLANICH) | 161 | KOLANICH (https://github.com/KOLANICH) |
@@ -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 | |||
155 | simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget, | 195 | simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget, |
156 | xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5, | 196 | xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5, |
157 | PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla, FossaMail, Uzbl browser, xmms, iridium browser, | 197 | PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla, FossaMail, Uzbl browser, xmms, iridium browser, |
158 | Kino, Thunar | 198 | Kino, Thunar, Geeqie, Engrampa |
@@ -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 | |||
30 | blacklist ${HOME}/.cache/epiphany | 30 | blacklist ${HOME}/.cache/epiphany |
31 | blacklist ${HOME}/.cache/evolution | 31 | blacklist ${HOME}/.cache/evolution |
32 | blacklist ${HOME}/.cache/gajim | 32 | blacklist ${HOME}/.cache/gajim |
33 | blacklist ${HOME}/.cache/geeqie | ||
33 | blacklist ${HOME}/.cache/google-chrome | 34 | blacklist ${HOME}/.cache/google-chrome |
34 | blacklist ${HOME}/.cache/google-chrome-beta | 35 | blacklist ${HOME}/.cache/google-chrome-beta |
35 | blacklist ${HOME}/.cache/google-chrome-unstable | 36 | blacklist ${HOME}/.cache/google-chrome-unstable |
@@ -103,6 +104,7 @@ blacklist ${HOME}/.config/evolution | |||
103 | blacklist ${HOME}/.config/filezilla | 104 | blacklist ${HOME}/.config/filezilla |
104 | blacklist ${HOME}/.config/flowblade | 105 | blacklist ${HOME}/.config/flowblade |
105 | blacklist ${HOME}/.config/gajim | 106 | blacklist ${HOME}/.config/gajim |
107 | blacklist ${HOME}/.config/geeqie | ||
106 | blacklist ${HOME}/.config/gedit | 108 | blacklist ${HOME}/.config/gedit |
107 | blacklist ${HOME}/.config/google-chrome | 109 | blacklist ${HOME}/.config/google-chrome |
108 | blacklist ${HOME}/.config/google-chrome-beta | 110 | blacklist ${HOME}/.config/google-chrome-beta |
@@ -221,6 +223,7 @@ blacklist ${HOME}/.local/share/epiphany | |||
221 | blacklist ${HOME}/.local/share/evolution | 223 | blacklist ${HOME}/.local/share/evolution |
222 | blacklist ${HOME}/.local/share/feral-interactive | 224 | blacklist ${HOME}/.local/share/feral-interactive |
223 | blacklist ${HOME}/.local/share/gajim | 225 | blacklist ${HOME}/.local/share/gajim |
226 | blacklist ${HOME}/.local/share/geeqie | ||
224 | blacklist ${HOME}/.local/share/gnome-2048 | 227 | blacklist ${HOME}/.local/share/gnome-2048 |
225 | blacklist ${HOME}/.local/share/gnome-chess | 228 | blacklist ${HOME}/.local/share/gnome-chess |
226 | blacklist ${HOME}/.local/share/gnome-music | 229 | blacklist ${HOME}/.local/share/gnome-music |
@@ -284,6 +287,7 @@ blacklist ${HOME}/.weechat | |||
284 | blacklist ${HOME}/.wine | 287 | blacklist ${HOME}/.wine |
285 | blacklist ${HOME}/.wine64 | 288 | blacklist ${HOME}/.wine64 |
286 | blacklist ${HOME}/.xiphos | 289 | blacklist ${HOME}/.xiphos |
290 | blacklist ${HOME}/.xmms | ||
287 | blacklist ${HOME}/.xonotic | 291 | blacklist ${HOME}/.xonotic |
288 | blacklist ${HOME}/.xpdfrc | 292 | blacklist ${HOME}/.xpdfrc |
289 | blacklist ${HOME}/.zoom | 293 | blacklist ${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 | |||
13 | nonewprivs | 13 | nonewprivs |
14 | noroot | 14 | noroot |
15 | nosound | 15 | nosound |
16 | no3d | ||
17 | protocol unix | 16 | protocol unix |
18 | seccomp | 17 | seccomp |
19 | netfilter | 18 | netfilter |
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 |
57 | private-dev | 57 | private-dev |
58 | private-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. | ||
3 | include /etc/firejail/geeqie.local | ||
4 | |||
5 | # Firejail profile for Geeqie | ||
6 | noblacklist ~/.cache/geeqie | ||
7 | noblacklist ~/.config/geeqie | ||
8 | noblacklist ~/.local/share/geeqie | ||
9 | include /etc/firejail/disable-common.inc | ||
10 | include /etc/firejail/disable-programs.inc | ||
11 | include /etc/firejail/disable-devel.inc | ||
12 | include /etc/firejail/disable-passwdmgr.inc | ||
13 | |||
14 | caps.drop all | ||
15 | nogroups | ||
16 | nonewprivs | ||
17 | noroot | ||
18 | protocol unix | ||
19 | seccomp | ||
20 | nosound | ||
21 | |||
22 | private-dev | ||
23 | |||
24 | #Experimental: | ||
25 | shell 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 | |||
20 | shell none | 20 | shell none |
21 | 21 | ||
22 | private-bin vlc,cvlc,nvlc,rvlc,qvlc,svlc | 22 | private-bin vlc,cvlc,nvlc,rvlc,qvlc,svlc |
23 | private-dev | 23 | # private-dev |
24 | private-tmp | 24 | private-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 | |||
113 | dragon | 113 | dragon |
114 | exiftool | 114 | exiftool |
115 | feh | 115 | feh |
116 | geeqie | ||
116 | gjs | 117 | gjs |
117 | gnome-books | 118 | gnome-books |
118 | gnome-clocks | 119 | gnome-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 |
184 | void caps_check_list(const char *clist, void (*callback)(int)) { | 184 | void 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; | |||
27 | static int cfg_val[CFG_MAX]; | 27 | static int cfg_val[CFG_MAX]; |
28 | char *xephyr_screen = "800x600"; | 28 | char *xephyr_screen = "800x600"; |
29 | char *xephyr_extra_params = ""; | 29 | char *xephyr_extra_params = ""; |
30 | char *xvfb_screen = "800x600x24"; | ||
31 | char *xvfb_extra_params = ""; | ||
30 | char *netfilter_default = NULL; | 32 | char *netfilter_default = NULL; |
31 | 33 | ||
32 | int checkcfg(int val) { | 34 | int 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); | |||
419 | int profile_check_line(char *ptr, int lineno, const char *fname); | 419 | int 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 |
421 | void profile_add(char *str); | 421 | void profile_add(char *str); |
422 | void fs_mnt(void); | ||
422 | 423 | ||
423 | // list.c | 424 | // list.c |
424 | void list(void); | 425 | void list(void); |
@@ -644,13 +645,13 @@ void fs_mkdir(const char *name); | |||
644 | void fs_mkfile(const char *name); | 645 | void fs_mkfile(const char *name); |
645 | 646 | ||
646 | // x11.c | 647 | // x11.c |
647 | extern int mask_x11_abstract_socket; | ||
648 | void fs_x11(void); | 648 | void fs_x11(void); |
649 | int x11_display(void); | 649 | int x11_display(void); |
650 | void x11_start(int argc, char **argv); | 650 | void x11_start(int argc, char **argv); |
651 | void x11_start_xpra(int argc, char **argv); | 651 | void x11_start_xpra(int argc, char **argv); |
652 | void x11_start_xephyr(int argc, char **argv); | 652 | void x11_start_xephyr(int argc, char **argv); |
653 | void x11_block(void); | 653 | void x11_block(void); |
654 | void x11_start_xvfb(int argc, char **argv); | ||
654 | 655 | ||
655 | // ls.c | 656 | // ls.c |
656 | enum { | 657 | enum { |
@@ -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 | }; |
686 | extern char *xephyr_screen; | 690 | extern char *xephyr_screen; |
687 | extern char *xephyr_extra_params; | 691 | extern char *xephyr_extra_params; |
692 | extern char *xvfb_screen; | ||
693 | extern char *xvfb_extra_params; | ||
688 | extern char *netfilter_default; | 694 | extern char *netfilter_default; |
689 | int checkcfg(int val); | 695 | int checkcfg(int val); |
690 | void print_compiletime_support(void); | 696 | void 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 | ||
475 | void 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 | ||
483 | void 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 |
475 | void fs_proc_sys_dev_boot(void) { | 494 | void 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 |
38 | static char *resolve_downloads(void) { | 38 | static 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> |
34 | int 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. |
39 | int x11_display(void) { | 37 | int 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 |
79 | static int x11_abstract_sockets_present(void) { | 78 | static 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. |
120 | static int random_display_number(void) { | 120 | static 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 | ||
202 | void 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 |
207 | void x11_start_xephyr(int argc, char **argv) { | 401 | void 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 | |||
407 | void x11_start_xpra(int argc, char **argv) { | 604 | void 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 | |||
607 | void x11_start(int argc, char **argv) { | 801 | void 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 | |||
771 | void fs_x11(void) { | 969 | void 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 | |||
834 | void x11_block(void) { | 1033 | void 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: | |||
150 | static int procevent_netlink_setup(void) { | 150 | static 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; |
192 | errexit: | ||
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 | |||
103 | Example: "noblacklist ${HOME}/.mozilla" | 103 | Example: "noblacklist ${HOME}/.mozilla" |
104 | 104 | ||
105 | .TP | 105 | .TP |
106 | \fBnowhitelist file_name | ||
107 | If the file name matches file_name, the file will not be whitelisted in any whitelist commands that follow. | ||
108 | |||
109 | Example: "nowhitelist ~/.config" | ||
110 | |||
111 | .TP | ||
106 | \fBignore | 112 | \fBignore |
107 | Ignore command. | 113 | Ignore command. |
108 | 114 | ||
@@ -156,7 +162,7 @@ Mount-bind directory1 on top of directory2. This option is only available when r | |||
156 | Mount-bind file1 on top of file2. This option is only available when running as root. | 162 | Mount-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 |
159 | Create a directory in user home before the sandbox is started. | 165 | Create a directory in user home or under /tmp before the sandbox is started. |
160 | The directory is created if it doesn't already exist. | 166 | The directory is created if it doesn't already exist. |
161 | .br | 167 | .br |
162 | 168 | ||
@@ -177,8 +183,8 @@ mkdir ~/.cache/mozilla/firefox | |||
177 | whitelist ~/.cache/mozilla/firefox | 183 | whitelist ~/.cache/mozilla/firefox |
178 | .TP | 184 | .TP |
179 | \fBmkfile file | 185 | \fBmkfile file |
180 | Similar to mkdir, this command creates a file in user home before the sandbox is started. | 186 | Similar to mkdir, this command creates a file in user home or under /tmp before the sandbox is started. |
181 | The file is created if it doesn't already exist, but it's target directory has to exist. | 187 | The file is created if it doesn't already exist. |
182 | .TP | 188 | .TP |
183 | \fBnoexec file_or_directory | 189 | \fBnoexec file_or_directory |
184 | Remount the file or the directory noexec, nodev and nosuid. | 190 | Remount the file or the directory noexec, nodev and nosuid. |
@@ -310,13 +316,16 @@ Remove DISPLAY and XAUTHORITY environment variables. | |||
310 | Stop with error message if X11 abstract socket will be accessible in jail. | 316 | Stop with error message if X11 abstract socket will be accessible in jail. |
311 | .TP | 317 | .TP |
312 | \fBx11 xephyr | 318 | \fBx11 xephyr |
313 | Enable X11 sandboxing with xephyr. | 319 | Enable X11 sandboxing with Xephyr server. |
314 | .TP | 320 | .TP |
315 | \fBx11 xorg | 321 | \fBx11 xorg |
316 | Enable X11 sandboxing with X11 security extension. | 322 | Enable X11 sandboxing with X11 security extension. |
317 | .TP | 323 | .TP |
318 | \fBx11 xpra | 324 | \fBx11 xpra |
319 | Enable X11 sandboxing with xpra. | 325 | Enable X11 sandboxing with Xpra server. |
326 | .TP | ||
327 | \fBx11 xvfb | ||
328 | Enable X11 sandboxing with Xvfb server. | ||
320 | 329 | ||
321 | .SH Resource limits, CPU affinity, Control Groups | 330 | .SH Resource limits, CPU affinity, Control Groups |
322 | These profile entries define the limits on system resources (rlimits) for the processes inside the sandbox. | 331 | These 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 | ||
1071 | Disable whitelist for this directory or file. | ||
1072 | |||
1073 | .TP | ||
1070 | \fB\-\-output=logfile | 1074 | \fB\-\-output=logfile |
1071 | stdout logging and log rotation. Copy stdout and stderr to logfile, and keep the size of the file under 500KB using log | 1075 | stdout logging and log rotation. Copy stdout and stderr to logfile, and keep the size of the file under 500KB using log |
1072 | rotation. Five files with prefixes .1 to .5 are used in rotation. | 1076 | rotation. 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 |
1775 | Sandbox the application using Xpra, Xephyr or Xorg security extension. | 1779 | Sandbox the application using Xpra, Xephyr, Xvfb or Xorg security extension. |
1776 | The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing | 1780 | The sandbox will prevents screenshot and keylogger applications started inside the sandbox from accessing |
1777 | clients running outside the sandbox. | 1781 | clients running outside the sandbox. |
1778 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. | 1782 | Firejail will try first Xpra, and if Xpra is not installed on the system, it will try to find Xephyr. |
1779 | If all fails, Firejail will not attempt to use X11 security extension. | 1783 | If all fails, Firejail will not attempt to use Xvfb or X11 security extension. |
1780 | .br | 1784 | .br |
1781 | 1785 | ||
1782 | .br | 1786 | .br |
1783 | Xpra and Xephyr modes require a network namespace to be instantiated in order to disable | 1787 | Xpra, Xephyr and Xvfb modes require a network namespace to be instantiated in order to disable |
1784 | X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket | 1788 | X11 abstract Unix socket. If this is not possible, the user can disable the abstract socket |
1785 | by adding "-nolisten local" on Xorg command line. | 1789 | by 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 | ||
1869 | Start Xvfb X11 server and attach the sandbox to this server. | ||
1870 | Xvfb, short for X virtual framebuffer, performs all graphical operations in memory | ||
1871 | without showing any screen output. Xvfb is mainly used for remote access and software | ||
1872 | testing on headless servers. | ||
1873 | .br | ||
1874 | |||
1875 | .br | ||
1876 | On Debian platforms Xvfb is installed with the command \fBsudo apt-get install xvfb\fR. | ||
1877 | This feature is not available when running as root. | ||
1878 | .br | ||
1879 | |||
1880 | .br | ||
1881 | Example: remote VNC access | ||
1882 | .br | ||
1883 | |||
1884 | .br | ||
1885 | On the server we start a sandbox using Xvfb and openbox | ||
1886 | window manager. The default size of Xvfb screen is 800x600 - it can be changed | ||
1887 | in /etc/firejail/firejail.config (xvfb-screen). Some sort of networking (--net) is required | ||
1888 | in 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 | ||
1900 | Reading profile /etc/firejail/openbox.profile | ||
1901 | .br | ||
1902 | Reading profile /etc/firejail/disable-common.inc | ||
1903 | .br | ||
1904 | Reading profile /etc/firejail/disable-common.local | ||
1905 | .br | ||
1906 | Parent pid 5400, child pid 5401 | ||
1907 | .br | ||
1908 | |||
1909 | .br | ||
1910 | On the server we also start a VNC server and attach it to the display handled by our | ||
1911 | Xvfb server (792). | ||
1912 | .br | ||
1913 | |||
1914 | .br | ||
1915 | $ x11vnc -display :792 | ||
1916 | .br | ||
1917 | |||
1918 | .br | ||
1919 | On 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 |
1864 | Use /usr/bin/zsh as default user shell. | 1930 | Use /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 |
38 | Monitor network statistics for sandboxes creating a new network namespace. | 38 | Monitor network statistics for sandboxes creating a new network namespace. |
39 | .TP | 39 | .TP |
40 | \fB\-\-nowrap | ||
41 | Enable line wrapping in terminals. By default the lines are trimmed. | ||
42 | .TP | ||
40 | \fB\-\-route | 43 | \fB\-\-route |
41 | Print route table for each sandbox. | 44 | Print 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 { | |||
41 | sleep 2 | 41 | sleep 2 |
42 | 42 | ||
43 | spawn $env(SHELL) | 43 | spawn $env(SHELL) |
44 | send -- "firemon --seccomp\r" | 44 | send -- "firemon --seccomp --nowrap\r" |
45 | expect { | 45 | expect { |
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 | } |
63 | sleep 1 | 63 | sleep 1 |
64 | send -- "firemon --caps\r" | 64 | send -- "firemon --caps --nowrap\r" |
65 | expect { | 65 | expect { |
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 { | |||
38 | sleep 2 | 38 | sleep 2 |
39 | 39 | ||
40 | spawn $env(SHELL) | 40 | spawn $env(SHELL) |
41 | send -- "firemon --seccomp\r" | 41 | send -- "firemon --seccomp --nowrap\r" |
42 | expect { | 42 | expect { |
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 | } |
59 | sleep 2 | 59 | sleep 2 |
60 | send -- "firemon --caps\r" | 60 | send -- "firemon --caps --nowrap\r" |
61 | expect { | 61 | expect { |
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 { | |||
38 | sleep 2 | 38 | sleep 2 |
39 | 39 | ||
40 | spawn $env(SHELL) | 40 | spawn $env(SHELL) |
41 | send -- "firemon --seccomp\r" | 41 | send -- "firemon --seccomp --nowrap\r" |
42 | expect { | 42 | expect { |
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 | } |
59 | sleep 1 | 59 | sleep 1 |
60 | send -- "firemon --caps\r" | 60 | send -- "firemon --caps --nowrap\r" |
61 | expect { | 61 | expect { |
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 | ||
80 | sleep 1 | ||
81 | send -- "exit\r" | ||
82 | after 100 | ||
83 | |||
84 | |||
85 | # negative nice value should result in nice=0 | ||
86 | send -- "firejail --nice=-5\r" | ||
87 | expect { | ||
88 | timeout {puts "TESTING ERROR 17\n";exit} | ||
89 | "Child process initialized" | ||
90 | } | ||
91 | sleep 1 | ||
92 | |||
93 | send -- "top -b -n 1\r" | ||
94 | expect { | ||
95 | timeout {puts "TESTING ERROR 18\n";exit} | ||
96 | $env(USER) | ||
97 | } | ||
98 | expect { | ||
99 | timeout {puts "TESTING ERROR 19\n";exit} | ||
100 | "0" | ||
101 | } | ||
102 | expect { | ||
103 | timeout {puts "TESTING ERROR 20\n";exit} | ||
104 | "bash" | ||
105 | } | ||
106 | expect { | ||
107 | timeout {puts "TESTING ERROR 21\n";exit} | ||
108 | $env(USER) | ||
109 | } | ||
110 | expect { | ||
111 | timeout {puts "TESTING ERROR 22\n";exit} | ||
112 | "0" | ||
113 | } | ||
114 | expect { | ||
115 | timeout {puts "TESTING ERROR 23\n";exit} | ||
116 | "top" | ||
117 | } | ||
80 | 118 | ||
119 | sleep 1 | ||
120 | send -- "exit\r" | ||
121 | after 100 | ||
81 | 122 | ||
82 | puts "\nall done\n" | 123 | puts "\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 | } |
62 | after 100 | 62 | after 100 |
63 | send -- "rm -f logfile*\r" | 63 | send -- "rm -f logfile*\r" |
64 | |||
65 | |||
66 | send -- "firejail --output=../logfile -- ./output.sh\r" | ||
67 | expect { | ||
68 | timeout {puts "TESTING ERROR 8\n";exit} | ||
69 | "invalid output file" | ||
70 | } | ||
71 | after 100 | ||
72 | |||
73 | send -- "firejail --output=/etc -- ./output.sh\r" | ||
74 | expect { | ||
75 | timeout {puts "TESTING ERROR 9\n";exit} | ||
76 | "invalid output file" | ||
77 | } | ||
78 | after 100 | ||
79 | |||
80 | send -- "firejail --output=/etc/firejail/zoom.profile -- ./output.sh\r" | ||
81 | expect { | ||
82 | timeout {puts "TESTING ERROR 10\n";exit} | ||
83 | "the output file needs to be owned by the current user" | ||
84 | } | ||
85 | |||
64 | after 100 | 86 | after 100 |
65 | puts "\nall done\n" | 87 | puts "\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 | |||
6 | set timeout 10 | ||
7 | spawn $env(SHELL) | ||
8 | match_max 100000 | ||
9 | |||
10 | send -- "firejail --debug-syscalls\r" | ||
11 | expect { | ||
12 | timeout {puts "TESTING ERROR 0\n";exit} | ||
13 | "set_mempolicy" | ||
14 | } | ||
15 | after 100 | ||
16 | |||
17 | send -- "firejail --debug-syscalls\r" | ||
18 | expect { | ||
19 | timeout {puts "TESTING ERROR 1\n";exit} | ||
20 | "setitimer" | ||
21 | } | ||
22 | after 100 | ||
23 | |||
24 | send -- "firejail --debug-errnos\r" | ||
25 | expect { | ||
26 | timeout {puts "TESTING ERROR 2\n";exit} | ||
27 | "EBADMSG" | ||
28 | } | ||
29 | after 100 | ||
30 | |||
31 | send -- "firejail --debug-errnos\r" | ||
32 | expect { | ||
33 | timeout {puts "TESTING ERROR 3\n";exit} | ||
34 | "ELIBMAX" | ||
35 | } | ||
36 | after 100 | ||
37 | |||
38 | send -- "firejail --debug-protocols\r" | ||
39 | expect { | ||
40 | timeout {puts "TESTING ERROR 4\n";exit} | ||
41 | "unix, inet, inet6, netlink, packet" | ||
42 | } | ||
43 | after 100 | ||
44 | |||
45 | puts "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 @@ | |||
6 | export MALLOC_CHECK_=3 | 6 | export MALLOC_CHECK_=3 |
7 | export MALLOC_PERTURB_=$(($RANDOM % 255 + 1)) | 7 | export MALLOC_PERTURB_=$(($RANDOM % 255 + 1)) |
8 | 8 | ||
9 | echo "TESTING: debug options (test/filters/debug.exp)" | ||
10 | ./debug.exp | ||
11 | |||
9 | echo "TESTING: noroot (test/filters/noroot.exp)" | 12 | echo "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 | } |
162 | expect { | 162 | expect { |
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 | } |
166 | sleep 1 | 166 | sleep 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 | |||
6 | set timeout 10 | ||
7 | cd /home | ||
8 | spawn $env(SHELL) | ||
9 | match_max 100000 | ||
10 | |||
11 | send -- "firejail --noprofile --overlay\r" | ||
12 | expect { | ||
13 | timeout {puts "TESTING ERROR 0\n";exit} | ||
14 | "Child process initialized" | ||
15 | } | ||
16 | sleep 2 | ||
17 | |||
18 | send -- "rm /etc/firejail/firejail.config\r" | ||
19 | after 100 | ||
20 | |||
21 | send -- "firejail\r" | ||
22 | expect { | ||
23 | timeout {puts "TESTING ERROR 1\n";exit} | ||
24 | "firejail.config not found" | ||
25 | } | ||
26 | |||
27 | # seccomp | ||
28 | send -- "echo \"seccomp no\" > /etc/firejail/firejail.config\r" | ||
29 | after 100 | ||
30 | send -- "firejail --noprofile --seccomp --force\r" | ||
31 | expect { | ||
32 | timeout {puts "TESTING ERROR 2\n";exit} | ||
33 | "seccomp feature is disabled in Firejail configuration file\r" | ||
34 | } | ||
35 | send -- "exit\r" | ||
36 | after 100 | ||
37 | |||
38 | # whitelist | ||
39 | send -- "echo \"whitelist no\" > /etc/firejail/firejail.config\r" | ||
40 | after 100 | ||
41 | send -- "firejail --noprofile --whitelist=~/.config --force\r" | ||
42 | expect { | ||
43 | timeout {puts "TESTING ERROR 3\n";exit} | ||
44 | "whitelist feature is disabled in Firejail configuration file\r" | ||
45 | } | ||
46 | |||
47 | # network | ||
48 | send -- "echo \"network no\" > /etc/firejail/firejail.config\r" | ||
49 | after 100 | ||
50 | send -- "firejail --noprofile --net=eth0 --force\r" | ||
51 | expect { | ||
52 | timeout {puts "TESTING ERROR 4\n";exit} | ||
53 | "networking feature is disabled in Firejail configuration file\r" | ||
54 | } | ||
55 | |||
56 | # bind | ||
57 | send -- "echo \"bind no\" > /etc/firejail/firejail.config\r" | ||
58 | after 100 | ||
59 | send -- "firejail --noprofile --bind=/tmp,/var/tmp --force\r" | ||
60 | expect { | ||
61 | timeout {puts "TESTING ERROR 5\n";exit} | ||
62 | "bind feature is disabled in Firejail configuration file\r" | ||
63 | } | ||
64 | |||
65 | # overlay | ||
66 | send -- "echo \"overlayfs no\" > /etc/firejail/firejail.config\r" | ||
67 | after 100 | ||
68 | send -- "firejail --noprofile --overlay --force\r" | ||
69 | expect { | ||
70 | timeout {puts "TESTING ERROR 6\n";exit} | ||
71 | "overlayfs feature is disabled in Firejail configuration file\r" | ||
72 | } | ||
73 | |||
74 | # private-home | ||
75 | send -- "echo \"private-home no\" > /etc/firejail/firejail.config\r" | ||
76 | after 100 | ||
77 | send -- "firejail --noprofile --private-home=/tmp --force\r" | ||
78 | expect { | ||
79 | timeout {puts "TESTING ERROR 7\n";exit} | ||
80 | "private-home feature is disabled in Firejail configuration file\r" | ||
81 | } | ||
82 | |||
83 | # chroot | ||
84 | send -- "echo \"chroot no\" > /etc/firejail/firejail.config\r" | ||
85 | after 100 | ||
86 | send -- "firejail --noprofile --chroot=/tmp --force\r" | ||
87 | expect { | ||
88 | timeout {puts "TESTING ERROR 8\n";exit} | ||
89 | "chroot feature is disabled in Firejail configuration file\r" | ||
90 | } | ||
91 | |||
92 | # userns | ||
93 | send -- "echo \"userns no\" > /etc/firejail/firejail.config\r" | ||
94 | after 100 | ||
95 | send -- "firejail --noprofile --noroot --force\r" | ||
96 | expect { | ||
97 | timeout {puts "TESTING ERROR 9\n";exit} | ||
98 | "noroot feature is disabled in Firejail configuration file\r" | ||
99 | } | ||
100 | |||
101 | send -- "exit\r" | ||
102 | after 100 | ||
103 | |||
104 | |||
105 | after 100 | ||
106 | puts "\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 | |||
3 | set timeout 10 | ||
4 | spawn $env(SHELL) | ||
5 | match_max 100000 | ||
6 | |||
7 | |||
8 | |||
9 | send -- "firejail --version\r" | ||
10 | expect { | ||
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 | |||
16 | set timeout 120 | ||
17 | send -- "firejail --git-install\r" | ||
18 | expect { | ||
19 | timeout {puts "TESTING ERROR 2\n";exit} | ||
20 | "Cloning into" | ||
21 | } | ||
22 | expect { | ||
23 | timeout {puts "TESTING ERROR 3\n";exit} | ||
24 | "Configuration options" | ||
25 | } | ||
26 | expect { | ||
27 | timeout {puts "TESTING ERROR 4\n";exit} | ||
28 | "src/fseccomp/fseccomp default seccomp" | ||
29 | } | ||
30 | expect { | ||
31 | timeout {puts "TESTING ERROR 5\n";exit} | ||
32 | "Mainline git Firejail version was installed in" | ||
33 | } | ||
34 | after 100 | ||
35 | |||
36 | send -- "firejail --git-uninstall\r" | ||
37 | expect { | ||
38 | timeout {puts "TESTING ERROR 6\n";exit} | ||
39 | "Cloning into" | ||
40 | } | ||
41 | expect { | ||
42 | timeout {puts "TESTING ERROR 7\n";exit} | ||
43 | "Configuration options" | ||
44 | } | ||
45 | expect { | ||
46 | timeout {puts "TESTING ERROR 8\n";exit} | ||
47 | "Firejail mainline git version uninstalled from" | ||
48 | } | ||
49 | after 100 | ||
50 | |||
51 | puts "\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)" | |||
62 | echo "TESTING: join (test/root/join.exp)" | 62 | echo "TESTING: join (test/root/join.exp)" |
63 | ./join.exp | 63 | ./join.exp |
64 | 64 | ||
65 | echo "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 | #******************************** |
83 | echo "TESTING: firejail configuration (test/root/checkcfg.exp)" | ||
84 | ./checkcfg.exp | ||
85 | |||
80 | echo "TESTING: tmpfs (test/root/option_tmpfs.exp)" | 86 | echo "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 { | |||
16 | sleep 2 | 16 | sleep 2 |
17 | 17 | ||
18 | spawn $env(SHELL) | 18 | spawn $env(SHELL) |
19 | send -- "firejail --shell=none --join=jointesting\r" | ||
20 | expect { | ||
21 | timeout {puts "TESTING ERROR 1\n";exit} | ||
22 | "shell=none set, but no command specified" | ||
23 | } | ||
24 | after 100 | ||
25 | |||
26 | |||
19 | send -- "firejail --join=jointesting\r" | 27 | send -- "firejail --join=jointesting\r" |
20 | expect { | 28 | expect { |
21 | timeout {puts "TESTING ERROR 1\n";exit} | 29 | timeout {puts "TESTING ERROR 1\n";exit} |