aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in2
-rw-r--r--README3
-rw-r--r--README.md15
-rw-r--r--RELNOTES2
-rw-r--r--etc/firejail.config22
-rw-r--r--etc/inc/disable-programs.inc12
-rw-r--r--etc/profile-a-l/alpine.profile104
-rw-r--r--etc/profile-a-l/alpinef.profile14
-rw-r--r--etc/profile-a-l/cargo.profile1
-rw-r--r--etc/profile-a-l/chromium-common.profile3
-rw-r--r--etc/profile-a-l/ddgr.profile13
-rw-r--r--etc/profile-a-l/firefox.profile3
-rw-r--r--etc/profile-a-l/googler-common.profile62
-rw-r--r--etc/profile-a-l/googler.profile13
-rw-r--r--etc/profile-a-l/librewolf.profile3
-rw-r--r--etc/profile-m-z/mcomix.profile74
-rw-r--r--etc/profile-m-z/minecraft-launcher.profile1
-rw-r--r--etc/profile-m-z/qcomicbook.profile68
-rw-r--r--etc/profile-m-z/rtin.profile8
-rw-r--r--etc/profile-m-z/tin.profile69
-rw-r--r--etc/profile-m-z/tuxguitar.profile6
-rw-r--r--etc/profile-m-z/w3m.profile24
-rw-r--r--etc/profile-m-z/weechat.profile1
-rw-r--r--etc/profile-m-z/zathura.profile3
-rw-r--r--etc/templates/profile.template2
-rw-r--r--platform/rpm/firejail.spec2
-rw-r--r--src/fcopy/main.c17
-rw-r--r--src/firecfg/firecfg.config6
-rw-r--r--src/firejail/appimage.c35
-rw-r--r--src/firejail/checkcfg.c11
-rw-r--r--src/firejail/chroot.c43
-rw-r--r--src/firejail/cmdline.c32
-rw-r--r--src/firejail/dbus.c18
-rw-r--r--src/firejail/dhcp.c12
-rw-r--r--src/firejail/firejail.h36
-rw-r--r--src/firejail/fs.c253
-rw-r--r--src/firejail/fs_dev.c2
-rw-r--r--src/firejail/fs_home.c150
-rw-r--r--src/firejail/fs_lib.c13
-rw-r--r--src/firejail/fs_mkdir.c3
-rw-r--r--src/firejail/fs_trace.c6
-rw-r--r--src/firejail/fs_var.c4
-rw-r--r--src/firejail/fs_whitelist.c40
-rw-r--r--src/firejail/join.c2
-rw-r--r--src/firejail/ls.c4
-rw-r--r--src/firejail/macros.c1
-rw-r--r--src/firejail/main.c154
-rw-r--r--src/firejail/mountinfo.c5
-rw-r--r--src/firejail/no_sandbox.c2
-rw-r--r--src/firejail/paths.c2
-rw-r--r--src/firejail/profile.c94
-rw-r--r--src/firejail/pulseaudio.c68
-rw-r--r--src/firejail/restrict_users.c22
-rw-r--r--src/firejail/rlimit.c4
-rw-r--r--src/firejail/sandbox.c5
-rw-r--r--src/firejail/sbox.c2
-rw-r--r--src/firejail/selinux.c10
-rw-r--r--src/firejail/util.c298
-rw-r--r--src/firejail/x11.c55
-rw-r--r--src/firemon/interface.c4
-rw-r--r--src/firemon/netstats.c4
-rw-r--r--src/firemon/procevent.c4
-rw-r--r--src/firemon/top.c4
-rw-r--r--src/include/gcov_wrapper.h40
-rw-r--r--src/jailcheck/access.c2
-rw-r--r--src/jailcheck/jailcheck.h2
-rw-r--r--src/jailcheck/main.c23
-rw-r--r--src/jailcheck/network.c56
-rw-r--r--src/jailcheck/sysfiles.c2
-rw-r--r--src/lib/ldd_utils.c2
-rw-r--r--src/man/firejail-profile.txt2
-rw-r--r--src/man/firejail.txt7
-rw-r--r--src/man/jailcheck.txt12
-rwxr-xr-xtest/environment/rlimit-bad-profile.exp2
-rwxr-xr-xtest/environment/rlimit-bad.exp2
-rwxr-xr-xtest/fs/fscheck-tmpfs.exp2
76 files changed, 1536 insertions, 573 deletions
diff --git a/Makefile.in b/Makefile.in
index 6be62cb6e..17bd76464 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -31,7 +31,7 @@ SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfil
31MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) 31MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS)
32MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so 32MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so
33COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion 33COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion
34MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.5 34MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1
35SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp 35SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp
36SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 36SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32
37ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) 37ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS)
diff --git a/README b/README
index 7310d22da..8284ce825 100644
--- a/README
+++ b/README
@@ -500,6 +500,7 @@ Jean-Philippe Eisenbarth (https://github.com/jpeisenbarth)
500 - fixed spotify.profile 500 - fixed spotify.profile
501Jeff Squyres (https://github.com/jsquyres) 501Jeff Squyres (https://github.com/jsquyres)
502 - various manpage fixes 502 - various manpage fixes
503 - cmdline.c: optionally quote the resulting command line
503Jericho (https://github.com/attritionorg) 504Jericho (https://github.com/attritionorg)
504 - spelling 505 - spelling
505Jesse Smith (https://github.com/slicer69) 506Jesse Smith (https://github.com/slicer69)
@@ -570,6 +571,8 @@ kortewegdevries (https://github.com/kortewegdevries)
570 - whitelisting evolution, kmail 571 - whitelisting evolution, kmail
571Kristóf Marussy (https://github.com/kris7t) 572Kristóf Marussy (https://github.com/kris7t)
572 - dns support 573 - dns support
574kuesji koesnu (https://github.com/kuesji)
575 - unit suffixes for rlimit-fsize and rlimit-as
573Kunal Mehta (https://github.com/legoktm) 576Kunal Mehta (https://github.com/legoktm)
574 - converted all links to https in manpages 577 - converted all links to https in manpages
575laniakea64 (https://github.com/laniakea64) 578laniakea64 (https://github.com/laniakea64)
diff --git a/README.md b/README.md
index 1c1823cb6..c235759e9 100644
--- a/README.md
+++ b/README.md
@@ -126,18 +126,18 @@ $ cd firejail
126$ ./configure && make && sudo make install-strip 126$ ./configure && make && sudo make install-strip
127````` 127`````
128On Debian/Ubuntu you will need to install git and gcc compiler. AppArmor 128On Debian/Ubuntu you will need to install git and gcc compiler. AppArmor
129development libraries and pkg-config are required when using --apparmor 129development libraries and pkg-config are required when using `--apparmor`
130./configure option: 130./configure option:
131````` 131`````
132$ sudo apt-get install git build-essential libapparmor-dev pkg-config gawk 132$ sudo apt-get install git build-essential libapparmor-dev pkg-config gawk
133````` 133`````
134For --selinux option, add libselinux1-dev (libselinux-devel for Fedora). 134For `--selinux` option, add libselinux1-dev (libselinux-devel for Fedora).
135 135
136Detailed information on using firejail from git is available on the [wiki](https://github.com/netblue30/firejail/wiki/Using-firejail-from-git). 136Detailed information on using firejail from git is available on the [wiki](https://github.com/netblue30/firejail/wiki/Using-firejail-from-git).
137 137
138## Running the sandbox 138## Running the sandbox
139 139
140To start the sandbox, prefix your command with firejail: 140To start the sandbox, prefix your command with `firejail`:
141 141
142````` 142`````
143$ firejail firefox # starting Mozilla Firefox 143$ firejail firefox # starting Mozilla Firefox
@@ -145,7 +145,7 @@ $ firejail transmission-gtk # starting Transmission BitTorrent
145$ firejail vlc # starting VideoLAN Client 145$ firejail vlc # starting VideoLAN Client
146$ sudo firejail /etc/init.d/nginx start 146$ sudo firejail /etc/init.d/nginx start
147````` 147`````
148Run "firejail --list" in a terminal to list all active sandboxes. Example: 148Run `firejail --list` in a terminal to list all active sandboxes. Example:
149````` 149`````
150$ firejail --list 150$ firejail --list
1511617:netblue:/usr/bin/firejail /usr/bin/firefox-esr 1511617:netblue:/usr/bin/firejail /usr/bin/firefox-esr
@@ -188,9 +188,7 @@ Use this issue to request new profiles: [#1139](https://github.com/netblue30/fir
188You can also use this tool to get a list of syscalls needed by a program: [contrib/syscalls.sh](contrib/syscalls.sh). 188You can also use this tool to get a list of syscalls needed by a program: [contrib/syscalls.sh](contrib/syscalls.sh).
189 189
190We also keep a list of profile fixes for previous released versions in [etc-fixes](https://github.com/netblue30/firejail/tree/master/etc-fixes) directory. 190We also keep a list of profile fixes for previous released versions in [etc-fixes](https://github.com/netblue30/firejail/tree/master/etc-fixes) directory.
191`````
192 191
193`````
194## Latest released version: 0.9.64 192## Latest released version: 0.9.64
195 193
196## Current development version: 0.9.65 194## Current development version: 0.9.65
@@ -334,5 +332,6 @@ avidemux, calligragemini, vmware-player, vmware-workstation, gget, com.github.ph
334pcsxr, PPSSPPSDL, openmw, openmw-launcher, jami-gnome, PCSX2, bcompare, b2sum, cksum, md5sum, sha1sum, sha224sum, 332pcsxr, PPSSPPSDL, openmw, openmw-launcher, jami-gnome, PCSX2, bcompare, b2sum, cksum, md5sum, sha1sum, sha224sum,
335sha256sum, sha384sum, sha512sum, sum, librewold-nightly, Quodlibet, tmux, sway, alienarena, alienarena-wrapper, 333sha256sum, sha384sum, sha512sum, sum, librewold-nightly, Quodlibet, tmux, sway, alienarena, alienarena-wrapper,
336ballbuster, ballbuster-wrapper, colorful, colorful-wrapper, gl-117, gl-117-wrapper, glaxium, glaxium-wrapper, 334ballbuster, ballbuster-wrapper, colorful, colorful-wrapper, gl-117, gl-117-wrapper, glaxium, glaxium-wrapper,
337pinball, pinball-wrapper, etr-wrapper, neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, firedragon 335pinball, pinball-wrapper, etr-wrapper, neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, firedragon,
338neochat, node, nvm, cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer, links2, xlinks2 336neochat, node, nvm, cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer, links2, xlinks2, googler, ddgr,
337tin
diff --git a/RELNOTES b/RELNOTES
index b6b5f0f7e..6a629476d 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -38,7 +38,7 @@ firejail (0.9.65) baseline; urgency=low
38 * glaxium-wrapper, pinball, pinball-wrapper, etr-wrapper, firedragon 38 * glaxium-wrapper, pinball, pinball-wrapper, etr-wrapper, firedragon
39 * neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, neochat, 39 * neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, neochat,
40 * cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer 40 * cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer
41 * links2, xlinks2 41 * links2, xlinks2, googler, ddgr, tin
42 -- netblue30 <netblue30@yahoo.com> Wed, 2 Jun 2021 09:00:00 -0500 42 -- netblue30 <netblue30@yahoo.com> Wed, 2 Jun 2021 09:00:00 -0500
43 43
44firejail (0.9.64.4) baseline; urgency=low 44firejail (0.9.64.4) baseline; urgency=low
diff --git a/etc/firejail.config b/etc/firejail.config
index 4b59f8955..43db49422 100644
--- a/etc/firejail.config
+++ b/etc/firejail.config
@@ -35,11 +35,6 @@
35# cannot be overridden by --noblacklist or --ignore. 35# cannot be overridden by --noblacklist or --ignore.
36# disable-mnt no 36# disable-mnt no
37 37
38# Set the limit for file copy in several --private-* options. The size is set
39# in megabytes. By default we allow up to 500MB.
40# Note: the files are copied in RAM.
41# file-copy-limit 500
42
43# Enable or disable file transfer support, default enabled. 38# Enable or disable file transfer support, default enabled.
44# file-transfer yes 39# file-transfer yes
45 40
@@ -77,18 +72,35 @@
77# Enable or disable overlayfs features, default enabled. 72# Enable or disable overlayfs features, default enabled.
78# overlayfs yes 73# overlayfs yes
79 74
75# Set the limit for file copy in several --private-* options. The size is set
76# in megabytes. By default we allow up to 500MB.
77# Note: the files are copied in RAM.
78# file-copy-limit 500
79
80# Enable or disable private-bin feature, default enabled.
81# private-bin yes
82
80# Remove /usr/local directories from private-bin list, default disabled. 83# Remove /usr/local directories from private-bin list, default disabled.
81# private-bin-no-local no 84# private-bin-no-local no
82 85
83# Enable or disable private-cache feature, default enabled 86# Enable or disable private-cache feature, default enabled
84# private-cache yes 87# private-cache yes
85 88
89# Enable or disable private-etc feature, default enabled.
90# private-etc yes
91
86# Enable or disable private-home feature, default enabled 92# Enable or disable private-home feature, default enabled
87# private-home yes 93# private-home yes
88 94
89# Enable or disable private-lib feature, default enabled 95# Enable or disable private-lib feature, default enabled
90# private-lib yes 96# private-lib yes
91 97
98# Enable or disable private-opt feature, default enabled.
99# private-opt yes
100
101# Enable or disable private-srv feature, default enabled.
102# private-srv yes
103
92# Enable --quiet as default every time the sandbox is started. Default disabled. 104# Enable --quiet as default every time the sandbox is started. Default disabled.
93# quiet-by-default no 105# quiet-by-default no
94 106
diff --git a/etc/inc/disable-programs.inc b/etc/inc/disable-programs.inc
index 18d1978fc..0e575e5eb 100644
--- a/etc/inc/disable-programs.inc
+++ b/etc/inc/disable-programs.inc
@@ -39,6 +39,8 @@ blacklist ${HOME}/.WebStorm*
39blacklist ${HOME}/.Wolfram Research 39blacklist ${HOME}/.Wolfram Research
40blacklist ${HOME}/.ZAP 40blacklist ${HOME}/.ZAP
41blacklist ${HOME}/.abook 41blacklist ${HOME}/.abook
42blacklist ${HOME}/.addressbook
43blacklist ${HOME}/.alpine-smime
42blacklist ${HOME}/.aMule 44blacklist ${HOME}/.aMule
43blacklist ${HOME}/.android 45blacklist ${HOME}/.android
44blacklist ${HOME}/.anydesk 46blacklist ${HOME}/.anydesk
@@ -810,6 +812,7 @@ blacklist ${HOME}/.netactview
810blacklist ${HOME}/.neverball 812blacklist ${HOME}/.neverball
811blacklist ${HOME}/.newsbeuter 813blacklist ${HOME}/.newsbeuter
812blacklist ${HOME}/.newsboat 814blacklist ${HOME}/.newsboat
815blacklist ${HOME}/.newsrc
813blacklist ${HOME}/.nicotine 816blacklist ${HOME}/.nicotine
814blacklist ${HOME}/.node-gyp 817blacklist ${HOME}/.node-gyp
815blacklist ${HOME}/.npm 818blacklist ${HOME}/.npm
@@ -830,6 +833,14 @@ blacklist ${HOME}/.paradoxinteractive
830blacklist ${HOME}/.parallelrealities/blobwars 833blacklist ${HOME}/.parallelrealities/blobwars
831blacklist ${HOME}/.pcsxr 834blacklist ${HOME}/.pcsxr
832blacklist ${HOME}/.penguin-command 835blacklist ${HOME}/.penguin-command
836blacklist ${HOME}/.pine-crash
837blacklist ${HOME}/.pine-debug1
838blacklist ${HOME}/.pine-debug2
839blacklist ${HOME}/.pine-debug3
840blacklist ${HOME}/.pine-debug4
841blacklist ${HOME}/.pine-interrupted-mail
842blacklist ${HOME}/.pinerc
843blacklist ${HOME}/.pinercex
833blacklist ${HOME}/.pingus 844blacklist ${HOME}/.pingus
834blacklist ${HOME}/.pioneer 845blacklist ${HOME}/.pioneer
835blacklist ${HOME}/.purple 846blacklist ${HOME}/.purple
@@ -867,6 +878,7 @@ blacklist ${HOME}/.teeworlds
867blacklist ${HOME}/.texlive20* 878blacklist ${HOME}/.texlive20*
868blacklist ${HOME}/.thunderbird 879blacklist ${HOME}/.thunderbird
869blacklist ${HOME}/.tilp 880blacklist ${HOME}/.tilp
881blacklist ${HOME}/.tin
870blacklist ${HOME}/.tooling 882blacklist ${HOME}/.tooling
871blacklist ${HOME}/.tor-browser* 883blacklist ${HOME}/.tor-browser*
872blacklist ${HOME}/.torcs 884blacklist ${HOME}/.torcs
diff --git a/etc/profile-a-l/alpine.profile b/etc/profile-a-l/alpine.profile
new file mode 100644
index 000000000..0b5cf0df0
--- /dev/null
+++ b/etc/profile-a-l/alpine.profile
@@ -0,0 +1,104 @@
1# Firejail profile for alpine
2# Description: Text-based email and newsgroups reader
3# This file is overwritten after every install/update
4quiet
5# Persistent local customizations
6include alpine.local
7# Persistent global definitions
8include globals.local
9
10# Workaround for bug https://github.com/netblue30/firejail/issues/2747
11# firejail --private-bin=sh --include='${CFG}/allow-bin-sh.inc' --profile=alpine sh -c '(alpine)'
12
13noblacklist /var/mail
14noblacklist /var/spool/mail
15noblacklist ${DOCUMENTS}
16noblacklist ${HOME}/.addressbook
17noblacklist ${HOME}/.alpine-smime
18noblacklist ${HOME}/.mailcap
19noblacklist ${HOME}/.mh_profile
20noblacklist ${HOME}/.mime.types
21noblacklist ${HOME}/.newsrc
22noblacklist ${HOME}/.pine-crash
23noblacklist ${HOME}/.pine-debug1
24noblacklist ${HOME}/.pine-debug2
25noblacklist ${HOME}/.pine-debug3
26noblacklist ${HOME}/.pine-debug4
27noblacklist ${HOME}/.pine-interrupted-mail
28noblacklist ${HOME}/.pinerc
29noblacklist ${HOME}/.pinercex
30noblacklist ${HOME}/.signature
31noblacklist ${HOME}/mail
32
33blacklist /tmp/.X11-unix
34blacklist ${RUNUSER}/wayland-*
35
36include disable-common.inc
37include disable-devel.inc
38include disable-exec.inc
39include disable-interpreters.inc
40include disable-passwdmgr.inc
41include disable-programs.inc
42include disable-shell.inc
43include disable-xdg.inc
44
45#whitelist ${DOCUMENTS}
46#whitelist ${DOWNLOADS}
47#whitelist ${HOME}/.addressbook
48#whitelist ${HOME}/.alpine-smime
49#whitelist ${HOME}/.mailcap
50#whitelist ${HOME}/.mh_profile
51#whitelist ${HOME}/.mime.types
52#whitelist ${HOME}/.newsrc
53#whitelist ${HOME}/.pine-crash
54#whitelist ${HOME}/.pine-interrupted-mail
55#whitelist ${HOME}/.pinerc
56#whitelist ${HOME}/.pinercex
57#whitelist ${HOME}/.pine-debug1
58#whitelist ${HOME}/.pine-debug2
59#whitelist ${HOME}/.pine-debug3
60#whitelist ${HOME}/.pine-debug4
61#whitelist ${HOME}/.signature
62#whitelist ${HOME}/mail
63whitelist /var/mail
64whitelist /var/spool/mail
65#include whitelist-common.inc
66include whitelist-runuser-common.inc
67include whitelist-usr-share-common.inc
68include whitelist-var-common.inc
69
70apparmor
71caps.drop all
72ipc-namespace
73machine-id
74netfilter
75no3d
76nodvd
77nogroups
78noinput
79nonewprivs
80noroot
81nosound
82notv
83nou2f
84novideo
85protocol unix,inet,inet6
86seccomp
87seccomp.block-secondary
88shell none
89tracelog
90
91disable-mnt
92private-bin alpine
93private-cache
94private-dev
95private-etc alternatives,c-client.cf,ca-certificates,crypto-policies,host.conf,hostname,hosts,krb5.keytab,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,mailcap,mime.types,nsswitch.conf,passwd,pine.conf,pinerc.fixed,pki,protocols,resolv.conf,rpc,services,ssl,terminfo,xdg
96private-tmp
97writable-run-user
98writable-var
99
100dbus-user none
101dbus-system none
102
103memory-deny-write-execute
104read-only ${HOME}/.signature
diff --git a/etc/profile-a-l/alpinef.profile b/etc/profile-a-l/alpinef.profile
new file mode 100644
index 000000000..97b97fe5f
--- /dev/null
+++ b/etc/profile-a-l/alpinef.profile
@@ -0,0 +1,14 @@
1# Firejail profile for alpinef
2# Description: Text-based email and newsgroups reader using function keys
3# This file is overwritten after every install/update
4quiet
5# Persistent local customizations
6include alpinef.local
7# Persistent global definitions
8# added by included profile
9#include globals.local
10
11private-bin alpinef
12
13# Redirect
14include alpine.profile
diff --git a/etc/profile-a-l/cargo.profile b/etc/profile-a-l/cargo.profile
index 043fd6718..7cf04c550 100644
--- a/etc/profile-a-l/cargo.profile
+++ b/etc/profile-a-l/cargo.profile
@@ -34,6 +34,7 @@ include disable-xdg.inc
34#whitelist ${HOME}/.cargo 34#whitelist ${HOME}/.cargo
35#whitelist ${HOME}/.rustup 35#whitelist ${HOME}/.rustup
36#include whitelist-common.inc 36#include whitelist-common.inc
37whitelist /usr/share/pkgconfig
37include whitelist-runuser-common.inc 38include whitelist-runuser-common.inc
38include whitelist-usr-share-common.inc 39include whitelist-usr-share-common.inc
39include whitelist-var-common.inc 40include whitelist-var-common.inc
diff --git a/etc/profile-a-l/chromium-common.profile b/etc/profile-a-l/chromium-common.profile
index f7493aa82..b0e0254d4 100644
--- a/etc/profile-a-l/chromium-common.profile
+++ b/etc/profile-a-l/chromium-common.profile
@@ -37,8 +37,9 @@ include whitelist-var-common.inc
37# Add the next line to your chromium-common.local if your kernel allows unprivileged userns clone. 37# Add the next line to your chromium-common.local if your kernel allows unprivileged userns clone.
38#include chromium-common-hardened.inc.profile 38#include chromium-common-hardened.inc.profile
39 39
40# Add the next line to your chromium-common.local to allow screen sharing under wayland. 40# Add the next two lines to your chromium-common.local to allow screen sharing under wayland.
41#whitelist ${RUNUSER}/pipewire-0 41#whitelist ${RUNUSER}/pipewire-0
42#whitelist /usr/share/pipewire/client.conf
42 43
43apparmor 44apparmor
44caps.keep sys_admin,sys_chroot 45caps.keep sys_admin,sys_chroot
diff --git a/etc/profile-a-l/ddgr.profile b/etc/profile-a-l/ddgr.profile
new file mode 100644
index 000000000..b1d41ddf7
--- /dev/null
+++ b/etc/profile-a-l/ddgr.profile
@@ -0,0 +1,13 @@
1# Firejail profile for ddgr
2# Description: Search DuckDuckGo from your terminal
3# This file is overwritten after every install/update
4quiet
5# Persistent local customizations
6include ddgr.local
7# Persistent global definitions
8include globals.local
9
10private-bin ddgr
11
12# Redirect
13include googler-common.profile
diff --git a/etc/profile-a-l/firefox.profile b/etc/profile-a-l/firefox.profile
index 7874c882f..3ad67734d 100644
--- a/etc/profile-a-l/firefox.profile
+++ b/etc/profile-a-l/firefox.profile
@@ -56,8 +56,9 @@ dbus-user.own org.mpris.MediaPlayer2.firefox.*
56#dbus-user.own org.mpris.MediaPlayer2.plasma-browser-integration 56#dbus-user.own org.mpris.MediaPlayer2.plasma-browser-integration
57#dbus-user.talk org.kde.JobViewServer 57#dbus-user.talk org.kde.JobViewServer
58#dbus-user.talk org.kde.kuiserver 58#dbus-user.talk org.kde.kuiserver
59# Add the next two lines to your firefox.local to allow screen sharing under wayland. 59# Add the next three lines to your firefox.local to allow screen sharing under wayland.
60#whitelist ${RUNUSER}/pipewire-0 60#whitelist ${RUNUSER}/pipewire-0
61#whitelist /usr/share/pipewire/client.conf
61#dbus-user.talk org.freedesktop.portal.* 62#dbus-user.talk org.freedesktop.portal.*
62# Add the next line to your firefox.local if screen sharing sharing still does not work 63# Add the next line to your firefox.local if screen sharing sharing still does not work
63# with the above lines (might depend on the portal implementation). 64# with the above lines (might depend on the portal implementation).
diff --git a/etc/profile-a-l/googler-common.profile b/etc/profile-a-l/googler-common.profile
new file mode 100644
index 000000000..2d0bce52b
--- /dev/null
+++ b/etc/profile-a-l/googler-common.profile
@@ -0,0 +1,62 @@
1# Firejail profile for googler clones
2# Description: common profile for googler clones
3# This file is overwritten after every install/update
4# Persistent local customizations
5include googler-common.local
6# Persistent global definitions
7# added by caller profile
8#include globals.local
9
10blacklist /tmp/.X11-unix
11blacklist ${RUNUSER}
12
13noblacklist ${HOME}/.w3m
14
15# Allow /bin/sh (blacklisted by disable-shell.inc)
16include allow-bin-sh.inc
17# Allow python (blacklisted by disable-interpreters.inc)
18include allow-python3.inc
19
20include disable-common.inc
21include disable-devel.inc
22include disable-exec.inc
23include disable-interpreters.inc
24include disable-passwdmgr.inc
25include disable-programs.inc
26include disable-shell.inc
27include disable-xdg.inc
28
29whitelist ${HOME}/.w3m
30include whitelist-usr-share-common.inc
31include whitelist-var-common.inc
32
33apparmor
34caps.drop all
35ipc-namespace
36machine-id
37netfilter
38no3d
39nodvd
40nogroups
41noinput
42nonewprivs
43noroot
44nosound
45notv
46nou2f
47novideo
48protocol unix,inet,inet6
49seccomp
50seccomp.block-secondary
51shell none
52tracelog
53
54disable-mnt
55private-bin env,python3*,sh,w3m
56private-cache
57private-dev
58private-etc ca-certificates,crypto-policies,host.conf,hostname,hosts,nsswitch.conf,pki,protocols,resolv.conf,rpc,services,ssl
59private-tmp
60
61dbus-user none
62dbus-system none
diff --git a/etc/profile-a-l/googler.profile b/etc/profile-a-l/googler.profile
new file mode 100644
index 000000000..9d67006f6
--- /dev/null
+++ b/etc/profile-a-l/googler.profile
@@ -0,0 +1,13 @@
1# Firejail profile for googler
2# Description: Search Google from your terminal
3# This file is overwritten after every install/update
4quiet
5# Persistent local customizations
6include googler.local
7# Persistent global definitions
8include globals.local
9
10private-bin googler
11
12# Redirect
13include googler-common.profile
diff --git a/etc/profile-a-l/librewolf.profile b/etc/profile-a-l/librewolf.profile
index 8e3e58f19..da047357a 100644
--- a/etc/profile-a-l/librewolf.profile
+++ b/etc/profile-a-l/librewolf.profile
@@ -44,8 +44,9 @@ dbus-user filter
44#dbus-user.own org.mpris.MediaPlayer2.plasma-browser-integration 44#dbus-user.own org.mpris.MediaPlayer2.plasma-browser-integration
45#dbus-user.talk org.kde.JobViewServer 45#dbus-user.talk org.kde.JobViewServer
46#dbus-user.talk org.kde.kuiserver 46#dbus-user.talk org.kde.kuiserver
47# Add the next lines to your librewolf.local to allow screensharing under Wayland. 47# Add the next three lines to your librewolf.local to allow screensharing under Wayland.
48#whitelist ${RUNUSER}/pipewire-0 48#whitelist ${RUNUSER}/pipewire-0
49#whitelist /usr/share/pipewire/client.conf
49#dbus-user.talk org.freedesktop.portal.* 50#dbus-user.talk org.freedesktop.portal.*
50# Also add the next line to your librewolf.local if screensharing does not work with 51# Also add the next line to your librewolf.local if screensharing does not work with
51# the above lines (depends on the portal implementation). 52# the above lines (depends on the portal implementation).
diff --git a/etc/profile-m-z/mcomix.profile b/etc/profile-m-z/mcomix.profile
new file mode 100644
index 000000000..fcd1e24e5
--- /dev/null
+++ b/etc/profile-m-z/mcomix.profile
@@ -0,0 +1,74 @@
1# Firejail profile for mcomix
2# Description: A comic book and manga viewer in python
3# This file is overwritten after every install/update
4# Persistent local customizations
5include mcomix.local
6# Persistent global definitions
7include globals.local
8
9noblacklist ${HOME}/.config/mcomix
10noblacklist ${HOME}/.local/share/mcomix
11noblacklist ${DOCUMENTS}
12
13# Allow /bin/sh (blacklisted by disable-shell.inc)
14include allow-bin-sh.inc
15
16# Allow python (blacklisted by disable-interpreters.inc)
17# mcomix <= 1.2 uses python2
18include allow-python2.inc
19include allow-python3.inc
20
21include disable-common.inc
22include disable-devel.inc
23include disable-exec.inc
24include disable-interpreters.inc
25include disable-passwdmgr.inc
26include disable-programs.inc
27include disable-shell.inc
28include disable-write-mnt.inc
29include disable-xdg.inc
30
31mkdir ${HOME}/.config/mcomix
32mkdir ${HOME}/.local/share/mcomix
33whitelist /usr/share/mcomix
34include whitelist-usr-share-common.inc
35include whitelist-var-common.inc
36include whitelist-runuser-common.inc
37
38apparmor
39caps.drop all
40machine-id
41net none
42nodvd
43nogroups
44noinput
45nonewprivs
46noroot
47nosound
48notv
49nou2f
50novideo
51protocol unix
52seccomp
53seccomp.block-secondary
54shell none
55tracelog
56
57# mcomix <= 1.2 uses python2
58private-bin 7z,lha,mcomix,mutool,python*,rar,sh,unrar,unzip
59private-cache
60private-dev
61# mcomix <= 1.2 uses gtk-2.0
62private-etc alternatives,dconf,fonts,gconf,gtk-2.0,gtk-3.0,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,machine-id,mime.types,pango,passwd,X11,xdg
63private-tmp
64
65dbus-user none
66dbus-system none
67
68read-only ${HOME}
69read-write ${HOME}/.config/mcomix
70read-write ${HOME}/.local/share/mcomix
71#to allow ${HOME}/.local/share/recently-used.xbel
72read-write ${HOME}/.local/share
73# used by mcomix <= 1.2, tip, make a symbolic link to .cache/thumbnails
74read-write ${HOME}/.thumbnails
diff --git a/etc/profile-m-z/minecraft-launcher.profile b/etc/profile-m-z/minecraft-launcher.profile
index 2536d0b38..1028e374a 100644
--- a/etc/profile-m-z/minecraft-launcher.profile
+++ b/etc/profile-m-z/minecraft-launcher.profile
@@ -31,7 +31,6 @@ include whitelist-runuser-common.inc
31include whitelist-usr-share-common.inc 31include whitelist-usr-share-common.inc
32include whitelist-var-common.inc 32include whitelist-var-common.inc
33 33
34apparmor
35caps.drop all 34caps.drop all
36netfilter 35netfilter
37nodvd 36nodvd
diff --git a/etc/profile-m-z/qcomicbook.profile b/etc/profile-m-z/qcomicbook.profile
new file mode 100644
index 000000000..0e52d7fc4
--- /dev/null
+++ b/etc/profile-m-z/qcomicbook.profile
@@ -0,0 +1,68 @@
1# Firejail profile for qcomicbook
2# Description: A comic book and manga viewer in QT
3# This file is overwritten after every install/update
4# Persistent local customizations
5include qcomicbook.local
6# Persistent global definitions
7include globals.local
8
9noblacklist ${HOME}/.cache/PawelStolowski
10noblacklist ${HOME}/.config/PawelStolowski
11noblacklist ${HOME}/.local/share/PawelStolowski
12noblacklist ${DOCUMENTS}
13
14# Allow /bin/sh (blacklisted by disable-shell.inc)
15include allow-bin-sh.inc
16
17include disable-common.inc
18include disable-devel.inc
19include disable-exec.inc
20include disable-interpreters.inc
21include disable-passwdmgr.inc
22include disable-programs.inc
23include disable-shell.inc
24include disable-write-mnt.inc
25include disable-xdg.inc
26
27mkdir ${HOME}/.cache/PawelStolowski
28mkdir ${HOME}/.config/PawelStolowski
29mkdir ${HOME}/.local/share/PawelStolowski
30whitelist /usr/share/qcomicbook
31include whitelist-runuser-common.inc
32include whitelist-usr-share-common.inc
33include whitelist-var-common.inc
34
35apparmor
36caps.drop all
37machine-id
38net none
39nodvd
40nogroups
41noinput
42nonewprivs
43noroot
44nosound
45notv
46nou2f
47novideo
48protocol unix
49seccomp
50seccomp.block-secondary
51shell none
52tracelog
53
54private-bin 7z,7zr,qcomicbook,rar,sh,tar,unace,unrar,unzip
55private-cache
56private-dev
57private-etc alternatives,fonts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,machine-id,mime.types,pango,passwd,Trolltech.conf,X11,xdg
58private-tmp
59
60dbus-user none
61dbus-system none
62
63read-only ${HOME}
64read-write ${HOME}/.cache/PawelStolowski
65read-write ${HOME}/.config/PawelStolowski
66read-write ${HOME}/.local/share/PawelStolowski
67#to allow ${HOME}/.local/share/recently-used.xbel
68read-write ${HOME}/.local/share
diff --git a/etc/profile-m-z/rtin.profile b/etc/profile-m-z/rtin.profile
new file mode 100644
index 000000000..cd84ce05e
--- /dev/null
+++ b/etc/profile-m-z/rtin.profile
@@ -0,0 +1,8 @@
1# Firejail profile for rtin
2# Description: ncurses-based Usenet newsreader
3# symlink to tin, same as `tin -r`
4# This file is overwritten after every install/update
5# Persistent local customizations
6include rtin.local
7
8include tin.profile
diff --git a/etc/profile-m-z/tin.profile b/etc/profile-m-z/tin.profile
new file mode 100644
index 000000000..e0ed3090a
--- /dev/null
+++ b/etc/profile-m-z/tin.profile
@@ -0,0 +1,69 @@
1# Firejail profile for tin
2# Description: ncurses-based Usenet newsreader
3# This file is overwritten after every install/update
4# Persistent local customizations
5include tin.local
6# Persistent global definitions
7include globals.local
8
9noblacklist ${HOME}/.newsrc
10noblacklist ${HOME}/.tin
11
12blacklist /tmp/.X11-unix
13blacklist ${RUNUSER}
14blacklist /usr/libexec
15
16include disable-common.inc
17include disable-devel.inc
18include disable-exec.inc
19include disable-interpreters.inc
20include disable-passwdmgr.inc
21include disable-programs.inc
22include disable-shell.inc
23include disable-xdg.inc
24
25mkdir ${HOME}/.tin
26mkfile ${HOME}/.newsrc
27# Note: files/directories directly in ${HOME} can't be whitelisted, as
28# tin saves .newsrc by renaming a temporary file, which is not possible for
29# bind-mounted files.
30#whitelist ${HOME}/.newsrc
31#whitelist ${HOME}/.tin
32#include whitelist-common.inc
33include whitelist-runuser-common.inc
34include whitelist-usr-share-common.inc
35include whitelist-var-common.inc
36
37apparmor
38caps.drop all
39ipc-namespace
40machine-id
41netfilter
42no3d
43nodvd
44nogroups
45noinput
46nonewprivs
47noroot
48nosound
49notv
50nou2f
51novideo
52protocol inet,inet6
53seccomp
54seccomp.block-secondary
55shell none
56tracelog
57
58disable-mnt
59private-bin rtin,tin
60private-cache
61private-dev
62private-etc passwd,resolv.conf,terminfo,tin
63private-lib terminfo
64private-tmp
65
66dbus-user none
67dbus-system none
68
69memory-deny-write-execute
diff --git a/etc/profile-m-z/tuxguitar.profile b/etc/profile-m-z/tuxguitar.profile
index d0bcbe79f..3cd496412 100644
--- a/etc/profile-m-z/tuxguitar.profile
+++ b/etc/profile-m-z/tuxguitar.profile
@@ -6,6 +6,9 @@ include tuxguitar.local
6# Persistent global definitions 6# Persistent global definitions
7include globals.local 7include globals.local
8 8
9# tuxguitar fails to launch
10ignore noexec ${HOME}
11
9noblacklist ${HOME}/.tuxguitar* 12noblacklist ${HOME}/.tuxguitar*
10noblacklist ${DOCUMENTS} 13noblacklist ${DOCUMENTS}
11noblacklist ${MUSIC} 14noblacklist ${MUSIC}
@@ -41,6 +44,3 @@ tracelog
41 44
42private-dev 45private-dev
43private-tmp 46private-tmp
44
45# noexec ${HOME} - tuxguitar may fail to launch
46noexec /tmp
diff --git a/etc/profile-m-z/w3m.profile b/etc/profile-m-z/w3m.profile
index 131213ed2..69b2c6c59 100644
--- a/etc/profile-m-z/w3m.profile
+++ b/etc/profile-m-z/w3m.profile
@@ -17,18 +17,32 @@ noblacklist ${HOME}/.w3m
17blacklist /tmp/.X11-unix 17blacklist /tmp/.X11-unix
18blacklist ${RUNUSER}/wayland-* 18blacklist ${RUNUSER}/wayland-*
19 19
20# Allow /bin/sh (blacklisted by disable-shell.inc)
21include allow-bin-sh.inc
22
23# Allow perl (blacklisted by disable-interpreters.inc)
20include allow-perl.inc 24include allow-perl.inc
21 25
22include disable-common.inc 26include disable-common.inc
23include disable-devel.inc 27include disable-devel.inc
28include disable-exec.inc
24include disable-interpreters.inc 29include disable-interpreters.inc
25include disable-passwdmgr.inc 30include disable-passwdmgr.inc
26include disable-programs.inc 31include disable-programs.inc
32include disable-shell.inc
27include disable-xdg.inc 33include disable-xdg.inc
28 34
35mkdir ${HOME}/.w3m
36whitelist /usr/share/w3m
37whitelist ${DOWNLOADS}
38whitelist ${HOME}/.w3m
29include whitelist-runuser-common.inc 39include whitelist-runuser-common.inc
40include whitelist-usr-share-common.inc
41include whitelist-var-common.inc
30 42
31caps.drop all 43caps.drop all
44ipc-namespace
45machine-id
32netfilter 46netfilter
33no3d 47no3d
34nodvd 48nodvd
@@ -45,8 +59,14 @@ seccomp
45shell none 59shell none
46tracelog 60tracelog
47 61
48# private-bin w3m 62disable-mnt
63private-bin perl,sh,w3m
49private-cache 64private-cache
50private-dev 65private-dev
51private-etc alternatives,ca-certificates,crypto-policies,pki,resolv.conf,ssl 66private-etc alternatives,ca-certificates,crypto-policies,mailcap,nsswitch.conf,pki,resolv.conf,ssl
52private-tmp 67private-tmp
68
69dbus-user none
70dbus-system none
71
72memory-deny-write-execute
diff --git a/etc/profile-m-z/weechat.profile b/etc/profile-m-z/weechat.profile
index 3a93d2ec7..76935212f 100644
--- a/etc/profile-m-z/weechat.profile
+++ b/etc/profile-m-z/weechat.profile
@@ -11,6 +11,7 @@ noblacklist ${HOME}/.weechat
11include disable-common.inc 11include disable-common.inc
12include disable-programs.inc 12include disable-programs.inc
13 13
14whitelist /usr/share/weechat
14include whitelist-usr-share-common.inc 15include whitelist-usr-share-common.inc
15include whitelist-var-common.inc 16include whitelist-var-common.inc
16 17
diff --git a/etc/profile-m-z/zathura.profile b/etc/profile-m-z/zathura.profile
index a39729685..d0e68c980 100644
--- a/etc/profile-m-z/zathura.profile
+++ b/etc/profile-m-z/zathura.profile
@@ -17,12 +17,14 @@ include disable-interpreters.inc
17include disable-passwdmgr.inc 17include disable-passwdmgr.inc
18include disable-programs.inc 18include disable-programs.inc
19include disable-shell.inc 19include disable-shell.inc
20include disable-write-mnt.inc
20include disable-xdg.inc 21include disable-xdg.inc
21 22
22mkdir ${HOME}/.config/zathura 23mkdir ${HOME}/.config/zathura
23mkdir ${HOME}/.local/share/zathura 24mkdir ${HOME}/.local/share/zathura
24whitelist /usr/share/doc 25whitelist /usr/share/doc
25whitelist /usr/share/zathura 26whitelist /usr/share/zathura
27include whitelist-runuser-common.inc
26include whitelist-usr-share-common.inc 28include whitelist-usr-share-common.inc
27include whitelist-var-common.inc 29include whitelist-var-common.inc
28 30
@@ -41,6 +43,7 @@ nou2f
41novideo 43novideo
42protocol unix 44protocol unix
43seccomp 45seccomp
46seccomp.block-secondary
44shell none 47shell none
45tracelog 48tracelog
46 49
diff --git a/etc/templates/profile.template b/etc/templates/profile.template
index 61e9c9fd8..18e4e8bce 100644
--- a/etc/templates/profile.template
+++ b/etc/templates/profile.template
@@ -192,7 +192,7 @@ include globals.local
192# GUI: fonts,pango,X11 192# GUI: fonts,pango,X11
193# GTK: dconf,gconf,gtk-2.0,gtk-3.0 193# GTK: dconf,gconf,gtk-2.0,gtk-3.0
194# KDE: kde4rc,kde5rc 194# KDE: kde4rc,kde5rc
195# Networking: ca-certificates,crypto-policies,host.conf,hostname,hosts,nsswitch.conf,pki,protocols,resolv.conf,services,rpc,ssl 195# Networking: ca-certificates,crypto-policies,host.conf,hostname,hosts,nsswitch.conf,pki,protocols,resolv.conf,rpc,services,ssl
196# Extra: gai.conf,proxychains.conf 196# Extra: gai.conf,proxychains.conf
197# Qt: Trolltech.conf 197# Qt: Trolltech.conf
198##private-lib LIBS 198##private-lib LIBS
diff --git a/platform/rpm/firejail.spec b/platform/rpm/firejail.spec
index acdc8d561..86cd6006e 100644
--- a/platform/rpm/firejail.spec
+++ b/platform/rpm/firejail.spec
@@ -45,8 +45,8 @@ rm -rf %{buildroot}
45%{_mandir}/man1/__NAME__.1.gz 45%{_mandir}/man1/__NAME__.1.gz
46%{_mandir}/man1/firecfg.1.gz 46%{_mandir}/man1/firecfg.1.gz
47%{_mandir}/man1/firemon.1.gz 47%{_mandir}/man1/firemon.1.gz
48%{_mandir}/man1/jailcheck.1.gz
48%{_mandir}/man5/__NAME__-login.5.gz 49%{_mandir}/man5/__NAME__-login.5.gz
49%{_mandir}/man5/__NAME__-profile.5.gz 50%{_mandir}/man5/__NAME__-profile.5.gz
50%{_mandir}/man5/__NAME__-users.5.gz 51%{_mandir}/man5/__NAME__-users.5.gz
51%{_mandir}/man5/jailcheck.5.gz
52%config(noreplace) %{_sysconfdir}/__NAME__ 52%config(noreplace) %{_sysconfdir}/__NAME__
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index 869549821..31810de9a 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -19,11 +19,15 @@
19 */ 19 */
20 20
21#include "../include/common.h" 21#include "../include/common.h"
22#include <fcntl.h>
23#include <ftw.h> 22#include <ftw.h>
24#include <errno.h> 23#include <errno.h>
25#include <pwd.h> 24#include <pwd.h>
26 25
26#include <fcntl.h>
27#ifndef O_PATH
28#define O_PATH 010000000
29#endif
30
27#if HAVE_SELINUX 31#if HAVE_SELINUX
28#include <sys/stat.h> 32#include <sys/stat.h>
29#include <sys/types.h> 33#include <sys/types.h>
@@ -55,7 +59,7 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
55 assert(path); 59 assert(path);
56 assert(inside_path); 60 assert(inside_path);
57#if HAVE_SELINUX 61#if HAVE_SELINUX
58 char procfs_path[64]; 62 char procfs_path[64];
59 char *fcon = NULL; 63 char *fcon = NULL;
60 int fd; 64 int fd;
61 struct stat st; 65 struct stat st;
@@ -69,20 +73,23 @@ static void selinux_relabel_path(const char *path, const char *inside_path) {
69 if (!label_hnd) 73 if (!label_hnd)
70 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); 74 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
71 75
76 if (!label_hnd)
77 errExit("selabel_open");
78
72 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 79 /* Open the file as O_PATH, to pin it while we determine and adjust the label */
73 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 80 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
74 if (fd < 0) 81 if (fd < 0)
75 return; 82 return;
76 if (fstat(fd, &st) < 0) 83 if (fstat(fd, &st) < 0)
77 goto close; 84 goto close;
78 85
79 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) { 86 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) == 0) {
80 sprintf(procfs_path, "/proc/self/fd/%i", fd); 87 sprintf(procfs_path, "/proc/self/fd/%i", fd);
81 if (arg_debug) 88 if (arg_debug)
82 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon); 89 printf("Relabeling %s as %s (%s)\n", path, inside_path, fcon);
83 90
84 setfilecon_raw(procfs_path, fcon); 91 setfilecon_raw(procfs_path, fcon);
85 } 92 }
86 freecon(fcon); 93 freecon(fcon);
87 close: 94 close:
88 close(fd); 95 close(fd);
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index 245e6a4a0..e58fe39ec 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -38,6 +38,8 @@ abrowser
38akonadi_control 38akonadi_control
39akregator 39akregator
40alacarte 40alacarte
41alpine
42alpinef
41amarok 43amarok
42amule 44amule
43amuled 45amuled
@@ -167,6 +169,7 @@ cvlc
167cyberfox 169cyberfox
168darktable 170darktable
169dconf-editor 171dconf-editor
172ddgr
170ddgtk 173ddgtk
171deadbeef 174deadbeef
172deluge 175deluge
@@ -350,6 +353,7 @@ google-chrome-unstable
350google-earth 353google-earth
351google-earth-pro 354google-earth-pro
352google-play-music-desktop-player 355google-play-music-desktop-player
356googler
353gpa 357gpa
354gpicview 358gpicview
355gpredict 359gpredict
@@ -492,6 +496,7 @@ mathematica
492matrix-mirage 496matrix-mirage
493mattermost-desktop 497mattermost-desktop
494mcabber 498mcabber
499mcomix
495mediainfo 500mediainfo
496mediathekview 501mediathekview
497megaglest 502megaglest
@@ -652,6 +657,7 @@ pybitmessage
652# pycharm-professional 657# pycharm-professional
653# pzstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095) 658# pzstd - disable until we fix CLI archivers for makepkg on Arch (see discussion in #3095)
654qbittorrent 659qbittorrent
660qcomicbook
655qemu-launcher 661qemu-launcher
656qgis 662qgis
657qlipper 663qlipper
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c
index 6b9fed765..056640eec 100644
--- a/src/firejail/appimage.c
+++ b/src/firejail/appimage.c
@@ -28,8 +28,13 @@
28#include <linux/loop.h> 28#include <linux/loop.h>
29#include <errno.h> 29#include <errno.h>
30 30
31#ifdef HAVE_GCOV
32#include "../include/gcov_wrapper.h"
33#endif
34
31static char *devloop = NULL; // device file 35static char *devloop = NULL; // device file
32static long unsigned size = 0; // offset into appimage file 36static long unsigned size = 0; // offset into appimage file
37#define MAXBUF 4096
33 38
34#ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h 39#ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h
35static void err_loop(void) { 40static void err_loop(void) {
@@ -38,6 +43,36 @@ static void err_loop(void) {
38} 43}
39#endif 44#endif
40 45
46// return 1 if found
47int appimage_find_profile(const char *archive) {
48 assert(archive);
49 assert(strlen(archive));
50
51 // try to match the name of the archive with the list of programs in /usr/lib/firejail/firecfg.config
52 FILE *fp = fopen(LIBDIR "/firejail/firecfg.config", "r");
53 if (!fp) {
54 fprintf(stderr, "Error: cannot find %s, firejail is not correctly installed\n", LIBDIR "/firejail/firecfg.config");
55 exit(1);
56 }
57 char buf[MAXBUF];
58 while (fgets(buf, MAXBUF, fp)) {
59 if (*buf == '#')
60 continue;
61 char *ptr = strchr(buf, '\n');
62 if (ptr)
63 *ptr = '\0';
64 if (strcasestr(archive, buf)) {
65 fclose(fp);
66 return profile_find_firejail(buf, 1);
67 }
68 }
69
70 fclose(fp);
71 return 0;
72
73}
74
75
41void appimage_set(const char *appimage) { 76void appimage_set(const char *appimage) {
42 assert(appimage); 77 assert(appimage);
43 assert(devloop == NULL); // don't call this twice! 78 assert(devloop == NULL); // don't call this twice!
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index 12b5fc683..1e9f4b641 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -111,10 +111,14 @@ int checkcfg(int val) {
111 PARSE_YESNO(CFG_RESTRICTED_NETWORK, "restricted-network") 111 PARSE_YESNO(CFG_RESTRICTED_NETWORK, "restricted-network")
112 PARSE_YESNO(CFG_XEPHYR_WINDOW_TITLE, "xephyr-window-title") 112 PARSE_YESNO(CFG_XEPHYR_WINDOW_TITLE, "xephyr-window-title")
113 PARSE_YESNO(CFG_OVERLAYFS, "overlayfs") 113 PARSE_YESNO(CFG_OVERLAYFS, "overlayfs")
114 PARSE_YESNO(CFG_PRIVATE_HOME, "private-home") 114 PARSE_YESNO(CFG_PRIVATE_BIN, "private-bin")
115 PARSE_YESNO(CFG_PRIVATE_BIN_NO_LOCAL, "private-bin-no-local")
115 PARSE_YESNO(CFG_PRIVATE_CACHE, "private-cache") 116 PARSE_YESNO(CFG_PRIVATE_CACHE, "private-cache")
117 PARSE_YESNO(CFG_PRIVATE_ETC, "private-etc")
118 PARSE_YESNO(CFG_PRIVATE_HOME, "private-home")
116 PARSE_YESNO(CFG_PRIVATE_LIB, "private-lib") 119 PARSE_YESNO(CFG_PRIVATE_LIB, "private-lib")
117 PARSE_YESNO(CFG_PRIVATE_BIN_NO_LOCAL, "private-bin-no-local") 120 PARSE_YESNO(CFG_PRIVATE_OPT, "private-opt")
121 PARSE_YESNO(CFG_PRIVATE_SRV, "private-srv")
118 PARSE_YESNO(CFG_DISABLE_MNT, "disable-mnt") 122 PARSE_YESNO(CFG_DISABLE_MNT, "disable-mnt")
119 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach") 123 PARSE_YESNO(CFG_XPRA_ATTACH, "xpra-attach")
120 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f") 124 PARSE_YESNO(CFG_BROWSER_DISABLE_U2F, "browser-disable-u2f")
@@ -131,8 +135,7 @@ int checkcfg(int val) {
131 *end = '\0'; 135 *end = '\0';
132 136
133 // is the file present? 137 // is the file present?
134 struct stat s; 138 if (access(fname, F_OK) == -1) {
135 if (stat(fname, &s) == -1) {
136 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname); 139 fprintf(stderr, "Error: netfilter-default file %s not available\n", fname);
137 exit(1); 140 exit(1);
138 } 141 }
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c
index 757ffb1f7..0d4baa618 100644
--- a/src/firejail/chroot.c
+++ b/src/firejail/chroot.c
@@ -29,6 +29,9 @@
29#define O_PATH 010000000 29#define O_PATH 010000000
30#endif 30#endif
31 31
32#ifdef HAVE_GCOV
33#include "../include/gcov_wrapper.h"
34#endif
32 35
33// exit if error 36// exit if error
34void fs_check_chroot_dir(void) { 37void fs_check_chroot_dir(void) {
@@ -163,12 +166,8 @@ void fs_chroot(const char *rootdir) {
163 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 166 int fd = openat(parentfd, "dev", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
164 if (fd == -1) 167 if (fd == -1)
165 errExit("open"); 168 errExit("open");
166 char *proc; 169 if (bind_mount_path_to_fd("/dev", fd))
167 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
168 errExit("asprintf");
169 if (mount("/dev", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
170 errExit("mounting /dev"); 170 errExit("mounting /dev");
171 free(proc);
172 close(fd); 171 close(fd);
173 172
174#ifdef HAVE_X11 173#ifdef HAVE_X11
@@ -192,11 +191,8 @@ void fs_chroot(const char *rootdir) {
192 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 191 fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
193 if (fd == -1) 192 if (fd == -1)
194 errExit("open"); 193 errExit("open");
195 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 194 if (bind_mount_path_to_fd("/tmp/.X11-unix", fd))
196 errExit("asprintf");
197 if (mount("/tmp/.X11-unix", proc, NULL, MS_BIND|MS_REC, NULL) < 0)
198 errExit("mounting /tmp/.X11-unix"); 195 errExit("mounting /tmp/.X11-unix");
199 free(proc);
200 close(fd); 196 close(fd);
201 } 197 }
202#endif // HAVE_X11 198#endif // HAVE_X11
@@ -225,19 +221,11 @@ void fs_chroot(const char *rootdir) {
225 fprintf(stderr, "Error: cannot open %s\n", pulse); 221 fprintf(stderr, "Error: cannot open %s\n", pulse);
226 exit(1); 222 exit(1);
227 } 223 }
228 free(pulse); 224 if (bind_mount_by_fd(src, dst))
229 225 errExit("mounting pulseaudio");
230 char *proc_src, *proc_dst;
231 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1)
232 errExit("asprintf");
233 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
234 errExit("asprintf");
235 if (mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL) < 0)
236 errExit("mount bind");
237 free(proc_src);
238 free(proc_dst);
239 close(src); 226 close(src);
240 close(dst); 227 close(dst);
228 free(pulse);
241 229
242 // update /etc/machine-id in chroot 230 // update /etc/machine-id in chroot
243 update_file(parentfd, "etc/machine-id"); 231 update_file(parentfd, "etc/machine-id");
@@ -256,11 +244,8 @@ void fs_chroot(const char *rootdir) {
256 fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 244 fd = openat(parentfd, &RUN_FIREJAIL_LIB_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
257 if (fd == -1) 245 if (fd == -1)
258 errExit("open"); 246 errExit("open");
259 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 247 if (bind_mount_path_to_fd(RUN_FIREJAIL_LIB_DIR, fd))
260 errExit("asprintf");
261 if (mount(RUN_FIREJAIL_LIB_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
262 errExit("mount bind"); 248 errExit("mount bind");
263 free(proc);
264 close(fd); 249 close(fd);
265 250
266 // create /run/firejail/mnt directory in chroot 251 // create /run/firejail/mnt directory in chroot
@@ -271,11 +256,8 @@ void fs_chroot(const char *rootdir) {
271 fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 256 fd = openat(parentfd, &RUN_MNT_DIR[1], O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
272 if (fd == -1) 257 if (fd == -1)
273 errExit("open"); 258 errExit("open");
274 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 259 if (bind_mount_path_to_fd(RUN_MNT_DIR, fd))
275 errExit("asprintf");
276 if (mount(RUN_MNT_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
277 errExit("mount bind"); 260 errExit("mount bind");
278 free(proc);
279 close(fd); 261 close(fd);
280 262
281 // update chroot resolv.conf 263 // update chroot resolv.conf
@@ -289,11 +271,8 @@ void fs_chroot(const char *rootdir) {
289 if (mkdir(oroot, 0755) == -1) 271 if (mkdir(oroot, 0755) == -1)
290 errExit("mkdir"); 272 errExit("mkdir");
291 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay 273 // mount the chroot dir on top of /run/firejail/mnt/oroot in order to reuse the apparmor rules for overlay
292 if (asprintf(&proc, "/proc/self/fd/%d", parentfd) == -1) 274 if (bind_mount_fd_to_path(parentfd, oroot))
293 errExit("asprintf");
294 if (mount(proc, oroot, NULL, MS_BIND|MS_REC, NULL) < 0)
295 errExit("mounting rootdir oroot"); 275 errExit("mounting rootdir oroot");
296 free(proc);
297 close(parentfd); 276 close(parentfd);
298 // chroot into the new directory 277 // chroot into the new directory
299 if (arg_debug) 278 if (arg_debug)
diff --git a/src/firejail/cmdline.c b/src/firejail/cmdline.c
index f902c4e1c..2fa68a55d 100644
--- a/src/firejail/cmdline.c
+++ b/src/firejail/cmdline.c
@@ -26,7 +26,7 @@
26#include <assert.h> 26#include <assert.h>
27#include <errno.h> 27#include <errno.h>
28 28
29static int cmdline_length(int argc, char **argv, int index) { 29static int cmdline_length(int argc, char **argv, int index, bool want_extra_quotes) {
30 assert(index != -1); 30 assert(index != -1);
31 31
32 unsigned i,j; 32 unsigned i,j;
@@ -46,10 +46,11 @@ static int cmdline_length(int argc, char **argv, int index) {
46 len += 3; 46 len += 3;
47 in_quotes = false; 47 in_quotes = false;
48 } else { 48 } else {
49 if (!in_quotes) 49 if (!in_quotes && want_extra_quotes)
50 len++; 50 len++;
51 len++; 51 len++;
52 in_quotes = true; 52 if (want_extra_quotes)
53 in_quotes = true;
53 } 54 }
54 } 55 }
55 if (in_quotes) { 56 if (in_quotes) {
@@ -64,7 +65,7 @@ static int cmdline_length(int argc, char **argv, int index) {
64 return len; 65 return len;
65} 66}
66 67
67static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index) { 68static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index, bool want_extra_quotes) {
68 assert(index != -1); 69 assert(index != -1);
69 70
70 unsigned i,j; 71 unsigned i,j;
@@ -103,14 +104,15 @@ static void quote_cmdline(char *command_line, char *window_title, int len, int a
103 // anything other 104 // anything other
104 else 105 else
105 { 106 {
106 if (!in_quotes) { 107 if (!in_quotes && want_extra_quotes) {
107 // open quotes 108 // open quotes
108 ptr1[0] = '\''; 109 ptr1[0] = '\'';
109 ptr1++; 110 ptr1++;
110 } 111 }
111 ptr1[0] = argv[i + index][j]; 112 ptr1[0] = argv[i + index][j];
112 ptr1++; 113 ptr1++;
113 in_quotes = true; 114 if (want_extra_quotes)
115 in_quotes = true;
114 } 116 }
115 } 117 }
116 // close quotes 118 // close quotes
@@ -134,12 +136,12 @@ static void quote_cmdline(char *command_line, char *window_title, int len, int a
134 assert((unsigned) len == strlen(command_line)); 136 assert((unsigned) len == strlen(command_line));
135} 137}
136 138
137void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { 139void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes) {
138 // index == -1 could happen if we have --shell=none and no program was specified 140 // index == -1 could happen if we have --shell=none and no program was specified
139 // the program should exit with an error before entering this function 141 // the program should exit with an error before entering this function
140 assert(index != -1); 142 assert(index != -1);
141 143
142 int len = cmdline_length(argc, argv, index); 144 int len = cmdline_length(argc, argv, index, want_extra_quotes);
143 if (len > ARG_MAX) { 145 if (len > ARG_MAX) {
144 errno = E2BIG; 146 errno = E2BIG;
145 errExit("cmdline_length"); 147 errExit("cmdline_length");
@@ -152,7 +154,7 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar
152 if (!*window_title) 154 if (!*window_title)
153 errExit("malloc"); 155 errExit("malloc");
154 156
155 quote_cmdline(*command_line, *window_title, len, argc, argv, index); 157 quote_cmdline(*command_line, *window_title, len, argc, argv, index, want_extra_quotes);
156 158
157 if (arg_debug) 159 if (arg_debug)
158 printf("Building quoted command line: %s\n", *command_line); 160 printf("Building quoted command line: %s\n", *command_line);
@@ -161,17 +163,17 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar
161 assert(*window_title); 163 assert(*window_title);
162} 164}
163 165
164void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { 166void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes) {
165 // index == -1 could happen if we have --shell=none and no program was specified 167 // index == -1 could happen if we have --shell=none and no program was specified
166 // the program should exit with an error before entering this function 168 // the program should exit with an error before entering this function
167 assert(index != -1); 169 assert(index != -1);
168 170
169 char *apprun_path = RUN_FIREJAIL_APPIMAGE_DIR "/AppRun"; 171 char *apprun_path = RUN_FIREJAIL_APPIMAGE_DIR "/AppRun";
170 172
171 int len1 = cmdline_length(argc, argv, index); // length of argv w/o changes 173 int len1 = cmdline_length(argc, argv, index, want_extra_quotes); // length of argv w/o changes
172 int len2 = cmdline_length(1, &argv[index], 0); // apptest.AppImage 174 int len2 = cmdline_length(1, &argv[index], 0, want_extra_quotes); // apptest.AppImage
173 int len3 = cmdline_length(1, &apprun_path, 0); // /run/firejail/appimage/AppRun 175 int len3 = cmdline_length(1, &apprun_path, 0, want_extra_quotes); // /run/firejail/appimage/AppRun
174 int len4 = (len1 - len2 + len3) + 1; // apptest.AppImage is replaced by /path/to/AppRun 176 int len4 = (len1 - len2 + len3) + 1; // apptest.AppImage is replaced by /path/to/AppRun
175 177
176 if (len4 > ARG_MAX) { 178 if (len4 > ARG_MAX) {
177 errno = E2BIG; 179 errno = E2BIG;
@@ -187,7 +189,7 @@ void build_appimage_cmdline(char **command_line, char **window_title, int argc,
187 errExit("malloc"); 189 errExit("malloc");
188 190
189 // run default quote_cmdline 191 // run default quote_cmdline
190 quote_cmdline(command_line_tmp, *window_title, len1, argc, argv, index); 192 quote_cmdline(command_line_tmp, *window_title, len1, argc, argv, index, want_extra_quotes);
191 193
192 assert(command_line_tmp); 194 assert(command_line_tmp);
193 assert(*window_title); 195 assert(*window_title);
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c
index b8aa2c974..9a4cb2e6b 100644
--- a/src/firejail/dbus.c
+++ b/src/firejail/dbus.c
@@ -258,12 +258,8 @@ static char *find_user_socket_by_format(char *format) {
258 if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1) 258 if (asprintf(&dbus_user_socket, format, (int) getuid()) == -1)
259 errExit("asprintf"); 259 errExit("asprintf");
260 struct stat s; 260 struct stat s;
261 if (stat(dbus_user_socket, &s) == -1) { 261 if (lstat(dbus_user_socket, &s) == -1)
262 if (errno == ENOENT) 262 goto fail;
263 goto fail;
264 return NULL;
265 errExit("stat");
266 }
267 if (!S_ISSOCK(s.st_mode)) 263 if (!S_ISSOCK(s.st_mode))
268 goto fail; 264 goto fail;
269 return dbus_user_socket; 265 return dbus_user_socket;
@@ -426,12 +422,8 @@ static void socket_overlay(char *socket_path, char *proxy_path) {
426 errno = ENOTSOCK; 422 errno = ENOTSOCK;
427 errExit("mounting DBus proxy socket"); 423 errExit("mounting DBus proxy socket");
428 } 424 }
429 char *proxy_fd_path; 425 if (bind_mount_fd_to_path(fd, socket_path))
430 if (asprintf(&proxy_fd_path, "/proc/self/fd/%d", fd) == -1)
431 errExit("asprintf");
432 if (mount(proxy_path, socket_path, NULL, MS_BIND | MS_REC, NULL) == -1)
433 errExit("mount bind"); 426 errExit("mount bind");
434 free(proxy_fd_path);
435 close(fd); 427 close(fd);
436} 428}
437 429
@@ -478,7 +470,7 @@ void dbus_apply_policy(void) {
478 create_empty_dir_as_root(RUN_DBUS_DIR, 0755); 470 create_empty_dir_as_root(RUN_DBUS_DIR, 0755);
479 471
480 if (arg_dbus_user != DBUS_POLICY_ALLOW) { 472 if (arg_dbus_user != DBUS_POLICY_ALLOW) {
481 create_empty_file_as_root(RUN_DBUS_USER_SOCKET, 0700); 473 create_empty_file_as_root(RUN_DBUS_USER_SOCKET, 0600);
482 474
483 if (arg_dbus_user == DBUS_POLICY_FILTER) { 475 if (arg_dbus_user == DBUS_POLICY_FILTER) {
484 assert(dbus_user_proxy_socket != NULL); 476 assert(dbus_user_proxy_socket != NULL);
@@ -517,7 +509,7 @@ void dbus_apply_policy(void) {
517 } 509 }
518 510
519 if (arg_dbus_system != DBUS_POLICY_ALLOW) { 511 if (arg_dbus_system != DBUS_POLICY_ALLOW) {
520 create_empty_file_as_root(RUN_DBUS_SYSTEM_SOCKET, 0700); 512 create_empty_file_as_root(RUN_DBUS_SYSTEM_SOCKET, 0600);
521 513
522 if (arg_dbus_system == DBUS_POLICY_FILTER) { 514 if (arg_dbus_system == DBUS_POLICY_FILTER) {
523 assert(dbus_system_proxy_socket != NULL); 515 assert(dbus_system_proxy_socket != NULL);
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c
index 5bcdcad37..ec482e2ea 100644
--- a/src/firejail/dhcp.c
+++ b/src/firejail/dhcp.c
@@ -153,19 +153,13 @@ void dhcp_start(void) {
153 if (!any_dhcp()) 153 if (!any_dhcp())
154 return; 154 return;
155 155
156 char *dhclient_path = RUN_MNT_DIR "/dhclient";; 156 char *dhclient_path = RUN_MNT_DIR "/dhclient";
157 struct stat s; 157 struct stat s;
158 if (stat(dhclient_path, &s) == -1) { 158 if (stat(dhclient_path, &s) == -1) {
159 dhclient_path = "/usr/sbin/dhclient"; 159 fprintf(stderr, "Error: %s was not found.\n", dhclient_path);
160 if (stat(dhclient_path, &s) == -1) { 160 exit(1);
161 fprintf(stderr, "Error: dhclient was not found.\n");
162 exit(1);
163 }
164 } 161 }
165 162
166 sbox_run(SBOX_ROOT| SBOX_SECCOMP, 4, PATH_FCOPY, "--follow-link", dhclient_path, RUN_MNT_DIR);
167 dhclient_path = RUN_MNT_DIR "/dhclient";
168
169 EUID_ROOT(); 163 EUID_ROOT();
170 if (mkdir(RUN_DHCLIENT_DIR, 0700)) 164 if (mkdir(RUN_DHCLIENT_DIR, 0700))
171 errExit("mkdir"); 165 errExit("mkdir");
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 60d178f1e..9971d30b6 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -45,6 +45,15 @@
45 assert(s.st_gid == gid);\ 45 assert(s.st_gid == gid);\
46 assert((s.st_mode & 07777) == (mode));\ 46 assert((s.st_mode & 07777) == (mode));\
47 } while (0) 47 } while (0)
48#define ASSERT_PERMS_AS_USER(file, uid, gid, mode) \
49 do { \
50 assert(file);\
51 struct stat s;\
52 if (stat_as_user(file, &s) == -1) errExit("stat");\
53 assert(s.st_uid == uid);\
54 assert(s.st_gid == gid);\
55 assert((s.st_mode & 07777) == (mode));\
56 } while (0)
48#define ASSERT_PERMS_FD(fd, uid, gid, mode) \ 57#define ASSERT_PERMS_FD(fd, uid, gid, mode) \
49 do { \ 58 do { \
50 struct stat s;\ 59 struct stat s;\
@@ -489,6 +498,7 @@ int macro_id(const char *name);
489void errLogExit(char* fmt, ...) __attribute__((noreturn)); 498void errLogExit(char* fmt, ...) __attribute__((noreturn));
490void fwarning(char* fmt, ...); 499void fwarning(char* fmt, ...);
491void fmessage(char* fmt, ...); 500void fmessage(char* fmt, ...);
501long long unsigned parse_arg_size(char *str);
492void drop_privs(int nogroups); 502void drop_privs(int nogroups);
493int mkpath_as_root(const char* path); 503int mkpath_as_root(const char* path);
494void extract_command_name(int index, char **argv); 504void extract_command_name(int index, char **argv);
@@ -498,11 +508,14 @@ void logargs(int argc, char **argv) ;
498void logerr(const char *msg); 508void logerr(const char *msg);
499void set_nice(int inc); 509void set_nice(int inc);
500int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 510int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
501void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 511void copy_file_as_user(const char *srcname, const char *destname, mode_t mode);
502void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 512void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
503void touch_file_as_user(const char *fname, mode_t mode); 513void touch_file_as_user(const char *fname, mode_t mode);
504int is_dir(const char *fname); 514int is_dir(const char *fname);
505int is_link(const char *fname); 515int is_link(const char *fname);
516char *realpath_as_user(const char *fname);
517int stat_as_user(const char *fname, struct stat *s);
518int lstat_as_user(const char *fname, struct stat *s);
506void trim_trailing_slash_or_dot(char *path); 519void trim_trailing_slash_or_dot(char *path);
507char *line_remove_spaces(const char *buf); 520char *line_remove_spaces(const char *buf);
508char *split_comma(char *str); 521char *split_comma(char *str);
@@ -526,11 +539,15 @@ unsigned extract_timeout(const char *str);
526void disable_file_or_dir(const char *fname); 539void disable_file_or_dir(const char *fname);
527void disable_file_path(const char *path, const char *file); 540void disable_file_path(const char *path, const char *file);
528int safer_openat(int dirfd, const char *path, int flags); 541int safer_openat(int dirfd, const char *path, int flags);
542int remount_by_fd(int dst, unsigned long mountflags);
543int bind_mount_by_fd(int src, int dst);
544int bind_mount_path_to_fd(const char *srcname, int dst);
545int bind_mount_fd_to_path(int src, const char *destname);
529int has_handler(pid_t pid, int signal); 546int has_handler(pid_t pid, int signal);
530void enter_network_namespace(pid_t pid); 547void enter_network_namespace(pid_t pid);
531int read_pid(const char *name, pid_t *pid); 548int read_pid(const char *name, pid_t *pid);
532pid_t require_pid(const char *name); 549pid_t require_pid(const char *name);
533void check_homedir(void); 550void check_homedir(const char *dir);
534 551
535// Get info regarding the last kernel mount operation from /proc/self/mountinfo 552// Get info regarding the last kernel mount operation from /proc/self/mountinfo
536// The return value points to a static area, and will be overwritten by subsequent calls. 553// The return value points to a static area, and will be overwritten by subsequent calls.
@@ -762,8 +779,14 @@ enum {
762 CFG_WHITELIST, 779 CFG_WHITELIST,
763 CFG_XEPHYR_WINDOW_TITLE, 780 CFG_XEPHYR_WINDOW_TITLE,
764 CFG_OVERLAYFS, 781 CFG_OVERLAYFS,
765 CFG_PRIVATE_HOME, 782 CFG_PRIVATE_BIN,
766 CFG_PRIVATE_BIN_NO_LOCAL, 783 CFG_PRIVATE_BIN_NO_LOCAL,
784 CFG_PRIVATE_CACHE,
785 CFG_PRIVATE_ETC,
786 CFG_PRIVATE_HOME,
787 CFG_PRIVATE_LIB,
788 CFG_PRIVATE_OPT,
789 CFG_PRIVATE_SRV,
767 CFG_FIREJAIL_PROMPT, 790 CFG_FIREJAIL_PROMPT,
768 CFG_DISABLE_MNT, 791 CFG_DISABLE_MNT,
769 CFG_JOIN, 792 CFG_JOIN,
@@ -771,10 +794,8 @@ enum {
771 CFG_XPRA_ATTACH, 794 CFG_XPRA_ATTACH,
772 CFG_BROWSER_DISABLE_U2F, 795 CFG_BROWSER_DISABLE_U2F,
773 CFG_BROWSER_ALLOW_DRM, 796 CFG_BROWSER_ALLOW_DRM,
774 CFG_PRIVATE_LIB,
775 CFG_APPARMOR, 797 CFG_APPARMOR,
776 CFG_DBUS, 798 CFG_DBUS,
777 CFG_PRIVATE_CACHE,
778 CFG_CGROUP, 799 CFG_CGROUP,
779 CFG_NAME_CHANGE, 800 CFG_NAME_CHANGE,
780 CFG_SECCOMP_ERROR_ACTION, 801 CFG_SECCOMP_ERROR_ACTION,
@@ -796,6 +817,7 @@ int checkcfg(int val);
796void print_compiletime_support(void); 817void print_compiletime_support(void);
797 818
798// appimage.c 819// appimage.c
820int appimage_find_profile(const char *archive);
799void appimage_set(const char *appimage_path); 821void appimage_set(const char *appimage_path);
800void appimage_mount(void); 822void appimage_mount(void);
801void appimage_clear(void); 823void appimage_clear(void);
@@ -804,8 +826,8 @@ void appimage_clear(void);
804long unsigned int appimage2_size(int fd); 826long unsigned int appimage2_size(int fd);
805 827
806// cmdline.c 828// cmdline.c
807void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); 829void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes);
808void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); 830void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes);
809 831
810// sbox.c 832// sbox.c
811// programs 833// programs
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index 09de11de9..806fa9249 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -33,6 +33,10 @@
33#define O_PATH 010000000 33#define O_PATH 010000000
34#endif 34#endif
35 35
36#ifdef HAVE_GCOV
37#include "../include/gcov_wrapper.h"
38#endif
39
36#define MAX_BUF 4096 40#define MAX_BUF 4096
37#define EMPTY_STRING ("") 41#define EMPTY_STRING ("")
38// check noblacklist statements not matched by a proper blacklist in disable-*.inc files 42// check noblacklist statements not matched by a proper blacklist in disable-*.inc files
@@ -54,16 +58,10 @@ static char *opstr[] = {
54 [MOUNT_RDWR_NOCHECK] = "read-write", 58 [MOUNT_RDWR_NOCHECK] = "read-write",
55}; 59};
56 60
57typedef enum {
58 UNSUCCESSFUL,
59 SUCCESSFUL
60} LAST_DISABLE_OPERATION;
61LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL;
62
63static void disable_file(OPERATION op, const char *filename) { 61static void disable_file(OPERATION op, const char *filename) {
64 assert(filename); 62 assert(filename);
65 assert(op <OPERATION_MAX); 63 assert(op <OPERATION_MAX);
66 last_disable = UNSUCCESSFUL; 64 EUID_ASSERT();
67 65
68 // Resolve all symlinks 66 // Resolve all symlinks
69 char* fname = realpath(filename, NULL); 67 char* fname = realpath(filename, NULL);
@@ -71,20 +69,24 @@ static void disable_file(OPERATION op, const char *filename) {
71 return; 69 return;
72 } 70 }
73 if (fname == NULL && errno == EACCES) { 71 if (fname == NULL && errno == EACCES) {
74 if (arg_debug)
75 printf("Debug: no access to file %s, forcing mount\n", filename);
76 // realpath and stat functions will fail on FUSE filesystems 72 // realpath and stat functions will fail on FUSE filesystems
77 // they don't seem to like a uid of 0 73 // they don't seem to like a uid of 0
78 // force mounting 74 // force mounting
79 int rv = mount(RUN_RO_DIR, filename, "none", MS_BIND, "mode=400,gid=0"); 75 int fd = open(filename, O_PATH|O_CLOEXEC);
80 if (rv == 0) 76 if (fd < 0) {
81 last_disable = SUCCESSFUL; 77 if (arg_debug)
82 else { 78 printf("Warning (blacklisting): cannot open %s: %s\n", filename, strerror(errno));
83 rv = mount(RUN_RO_FILE, filename, "none", MS_BIND, "mode=400,gid=0"); 79 return;
84 if (rv == 0)
85 last_disable = SUCCESSFUL;
86 } 80 }
87 if (last_disable == SUCCESSFUL) { 81
82 EUID_ROOT();
83 int err = bind_mount_path_to_fd(RUN_RO_DIR, fd);
84 if (err != 0)
85 err = bind_mount_path_to_fd(RUN_RO_FILE, fd);
86 EUID_USER();
87 close(fd);
88
89 if (err == 0) {
88 if (arg_debug) 90 if (arg_debug)
89 printf("Disable %s\n", filename); 91 printf("Disable %s\n", filename);
90 if (op == BLACKLIST_FILE) 92 if (op == BLACKLIST_FILE)
@@ -92,21 +94,18 @@ static void disable_file(OPERATION op, const char *filename) {
92 else 94 else
93 fs_logger2("blacklist-nolog", filename); 95 fs_logger2("blacklist-nolog", filename);
94 } 96 }
95 else { 97 else if (arg_debug)
96 if (arg_debug) 98 printf("Warning (blacklisting): cannot mount on %s\n", filename);
97 printf("Warning (blacklisting): %s is an invalid file, skipping...\n", filename);
98 }
99 99
100 return; 100 return;
101 } 101 }
102 102
103 // if the file is not present, do nothing 103 // if the file is not present, do nothing
104 assert(fname);
104 struct stat s; 105 struct stat s;
105 if (fname == NULL) 106 if (stat(fname, &s) < 0) {
106 return;
107 if (stat(fname, &s) == -1) {
108 if (arg_debug) 107 if (arg_debug)
109 fwarning("%s does not exist, skipping...\n", fname); 108 printf("Warning (blacklisting): cannot access %s: %s\n", fname, strerror(errno));
110 free(fname); 109 free(fname);
111 return; 110 return;
112 } 111 }
@@ -115,8 +114,10 @@ static void disable_file(OPERATION op, const char *filename) {
115 // we migth have a file found in ${PATH} pointing to /usr/bin/firejail 114 // we migth have a file found in ${PATH} pointing to /usr/bin/firejail
116 // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird 115 // blacklisting it here will end up breaking situations like user clicks on a link in Thunderbird
117 // and expects Firefox to open in the same sandbox 116 // and expects Firefox to open in the same sandbox
118 if (strcmp(BINDIR "/firejail", fname) == 0) 117 if (strcmp(BINDIR "/firejail", fname) == 0) {
118 free(fname);
119 return; 119 return;
120 }
120 121
121 // modify the file 122 // modify the file
122 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) { 123 if (op == BLACKLIST_FILE || op == BLACKLIST_NOLOG) {
@@ -141,15 +142,25 @@ static void disable_file(OPERATION op, const char *filename) {
141 printf(" - no logging\n"); 142 printf(" - no logging\n");
142 } 143 }
143 144
145 int fd = open(fname, O_PATH|O_CLOEXEC);
146 if (fd < 0) {
147 if (arg_debug)
148 printf("Warning (blacklisting): cannot open %s: %s\n", fname, strerror(errno));
149 free(fname);
150 return;
151 }
152 EUID_ROOT();
144 if (S_ISDIR(s.st_mode)) { 153 if (S_ISDIR(s.st_mode)) {
145 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 154 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
146 errExit("disable file"); 155 errExit("disable file");
147 } 156 }
148 else { 157 else {
149 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 158 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
150 errExit("disable file"); 159 errExit("disable file");
151 } 160 }
152 last_disable = SUCCESSFUL; 161 EUID_USER();
162 close(fd);
163
153 if (op == BLACKLIST_FILE) 164 if (op == BLACKLIST_FILE)
154 fs_logger2("blacklist", fname); 165 fs_logger2("blacklist", fname);
155 else 166 else
@@ -158,23 +169,30 @@ static void disable_file(OPERATION op, const char *filename) {
158 } 169 }
159 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) { 170 else if (op == MOUNT_READONLY || op == MOUNT_RDWR || op == MOUNT_NOEXEC) {
160 fs_remount_rec(fname, op); 171 fs_remount_rec(fname, op);
161 // todo: last_disable = SUCCESSFUL;
162 } 172 }
163 else if (op == MOUNT_TMPFS) { 173 else if (op == MOUNT_TMPFS) {
164 if (S_ISDIR(s.st_mode)) { 174 if (!S_ISDIR(s.st_mode)) {
165 if (getuid()) { 175 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname);
166 if (strncmp(cfg.homedir, fname, strlen(cfg.homedir)) != 0 || 176 free(fname);
167 fname[strlen(cfg.homedir)] != '/') { 177 return;
168 fprintf(stderr, "Error: tmpfs outside $HOME is only available for root\n"); 178 }
169 exit(1); 179
170 } 180 uid_t uid = getuid();
181 if (uid != 0) {
182 // only user owned directories in user home
183 if (s.st_uid != uid ||
184 strncmp(cfg.homedir, fname, strlen(cfg.homedir)) != 0 ||
185 fname[strlen(cfg.homedir)] != '/') {
186 fwarning("you are not allowed to mount a tmpfs on %s\n", fname);
187 free(fname);
188 return;
171 } 189 }
172 fs_tmpfs(fname, getuid());
173 selinux_relabel_path(fname, fname);
174 last_disable = SUCCESSFUL;
175 } 190 }
176 else 191
177 fwarning("%s is not a directory; cannot mount a tmpfs on top of it.\n", fname); 192 fs_tmpfs(fname, uid);
193 EUID_USER(); // fs_tmpfs returns with EUID 0
194
195 selinux_relabel_path(fname, fname);
178 } 196 }
179 else 197 else
180 assert(0); 198 assert(0);
@@ -191,6 +209,7 @@ static int *nbcheck = NULL;
191// Treat pattern as a shell glob pattern and blacklist matching files 209// Treat pattern as a shell glob pattern and blacklist matching files
192static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { 210static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) {
193 assert(pattern); 211 assert(pattern);
212 EUID_ASSERT();
194 213
195#ifdef TEST_NO_BLACKLIST_MATCHING 214#ifdef TEST_NO_BLACKLIST_MATCHING
196 if (nbcheck_start == 0) { 215 if (nbcheck_start == 0) {
@@ -264,6 +283,7 @@ void fs_blacklist(void) {
264 if (noblacklist == NULL) 283 if (noblacklist == NULL)
265 errExit("failed allocating memory for noblacklist entries"); 284 errExit("failed allocating memory for noblacklist entries");
266 285
286 EUID_USER();
267 while (entry) { 287 while (entry) {
268 OPERATION op = OPERATION_MAX; 288 OPERATION op = OPERATION_MAX;
269 char *ptr; 289 char *ptr;
@@ -294,11 +314,13 @@ void fs_blacklist(void) {
294 if (arg_debug) 314 if (arg_debug)
295 printf("Mount-bind %s on top of %s\n", dname1, dname2); 315 printf("Mount-bind %s on top of %s\n", dname1, dname2);
296 // preserve dname2 mode and ownership 316 // preserve dname2 mode and ownership
317 // EUID_ROOT(); - option not accessible to non-root users
297 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) 318 if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0)
298 errExit("mount bind"); 319 errExit("mount bind");
299 /* coverity[toctou] */ 320 /* coverity[toctou] */
300 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode)) 321 if (set_perms(dname2, s.st_uid, s.st_gid,s.st_mode))
301 errExit("set_perms"); 322 errExit("set_perms");
323 // EUID_USER();
302 324
303 entry = entry->next; 325 entry = entry->next;
304 continue; 326 continue;
@@ -376,16 +398,12 @@ void fs_blacklist(void) {
376 op = MOUNT_TMPFS; 398 op = MOUNT_TMPFS;
377 } 399 }
378 else if (strncmp(entry->data, "mkdir ", 6) == 0) { 400 else if (strncmp(entry->data, "mkdir ", 6) == 0) {
379 EUID_USER();
380 fs_mkdir(entry->data + 6); 401 fs_mkdir(entry->data + 6);
381 EUID_ROOT();
382 entry = entry->next; 402 entry = entry->next;
383 continue; 403 continue;
384 } 404 }
385 else if (strncmp(entry->data, "mkfile ", 7) == 0) { 405 else if (strncmp(entry->data, "mkfile ", 7) == 0) {
386 EUID_USER();
387 fs_mkfile(entry->data + 7); 406 fs_mkfile(entry->data + 7);
388 EUID_ROOT();
389 entry = entry->next; 407 entry = entry->next;
390 continue; 408 continue;
391 } 409 }
@@ -441,6 +459,8 @@ void fs_blacklist(void) {
441 for (i = 0; i < noblacklist_c; i++) 459 for (i = 0; i < noblacklist_c; i++)
442 free(noblacklist[i]); 460 free(noblacklist[i]);
443 free(noblacklist); 461 free(noblacklist);
462
463 EUID_ROOT();
444} 464}
445 465
446//*********************************************** 466//***********************************************
@@ -449,6 +469,7 @@ void fs_blacklist(void) {
449 469
450// mount a writable tmpfs on directory; requires a resolved path 470// mount a writable tmpfs on directory; requires a resolved path
451void fs_tmpfs(const char *dir, unsigned check_owner) { 471void fs_tmpfs(const char *dir, unsigned check_owner) {
472 EUID_USER();
452 assert(dir); 473 assert(dir);
453 if (arg_debug) 474 if (arg_debug)
454 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); 475 printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no");
@@ -473,6 +494,7 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
473 errExit("fstatvfs"); 494 errExit("fstatvfs");
474 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND); 495 unsigned long flags = buf.f_flag & ~(MS_RDONLY|MS_BIND);
475 // mount via the symbolic link in /proc/self/fd 496 // mount via the symbolic link in /proc/self/fd
497 EUID_ROOT();
476 char *proc; 498 char *proc;
477 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 499 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
478 errExit("asprintf"); 500 errExit("asprintf");
@@ -490,38 +512,42 @@ void fs_tmpfs(const char *dir, unsigned check_owner) {
490 512
491// remount path, preserving other mount flags; requires a resolved path 513// remount path, preserving other mount flags; requires a resolved path
492static void fs_remount_simple(const char *path, OPERATION op) { 514static void fs_remount_simple(const char *path, OPERATION op) {
515 EUID_ASSERT();
493 assert(path); 516 assert(path);
494 517
495 // open path without following symbolic links 518 // open path without following symbolic links
496 int fd1 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 519 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
497 if (fd1 == -1) 520 if (fd < 0)
498 goto out; 521 goto out;
499 struct stat s1; 522
500 if (fstat(fd1, &s1) == -1) { 523 struct stat s;
524 if (fstat(fd, &s) < 0) {
501 // fstat can fail with EACCES if path is a FUSE mount, 525 // fstat can fail with EACCES if path is a FUSE mount,
502 // mounted without 'allow_root' or 'allow_other' 526 // mounted without 'allow_root' or 'allow_other'
503 if (errno != EACCES) 527 if (errno != EACCES)
504 errExit("fstat"); 528 errExit("fstat");
505 close(fd1); 529 close(fd);
506 goto out; 530 goto out;
507 } 531 }
508 // get mount flags 532 // get mount flags
509 struct statvfs buf; 533 struct statvfs buf;
510 if (fstatvfs(fd1, &buf) == -1) 534 if (fstatvfs(fd, &buf) < 0) {
511 errExit("fstatvfs"); 535 close(fd);
536 goto out;
537 }
512 unsigned long flags = buf.f_flag; 538 unsigned long flags = buf.f_flag;
513 539
514 // read-write option 540 // read-write option
515 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { 541 if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) {
516 // nothing to do if there is no read-only flag 542 // nothing to do if there is no read-only flag
517 if ((flags & MS_RDONLY) == 0) { 543 if ((flags & MS_RDONLY) == 0) {
518 close(fd1); 544 close(fd);
519 return; 545 return;
520 } 546 }
521 // allow only user owned directories, except the user is root 547 // allow only user owned directories, except the user is root
522 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { 548 if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s.st_uid != getuid()) {
523 fwarning("you are not allowed to change %s to read-write\n", path); 549 fwarning("you are not allowed to change %s to read-write\n", path);
524 close(fd1); 550 close(fd);
525 return; 551 return;
526 } 552 }
527 flags &= ~MS_RDONLY; 553 flags &= ~MS_RDONLY;
@@ -530,7 +556,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
530 else if (op == MOUNT_NOEXEC) { 556 else if (op == MOUNT_NOEXEC) {
531 // nothing to do if path is mounted noexec already 557 // nothing to do if path is mounted noexec already
532 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { 558 if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) {
533 close(fd1); 559 close(fd);
534 return; 560 return;
535 } 561 }
536 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; 562 flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID;
@@ -539,7 +565,7 @@ static void fs_remount_simple(const char *path, OPERATION op) {
539 else if (op == MOUNT_READONLY) { 565 else if (op == MOUNT_READONLY) {
540 // nothing to do if path is mounted read-only already 566 // nothing to do if path is mounted read-only already
541 if ((flags & MS_RDONLY) == MS_RDONLY) { 567 if ((flags & MS_RDONLY) == MS_RDONLY) {
542 close(fd1); 568 close(fd);
543 return; 569 return;
544 } 570 }
545 flags |= MS_RDONLY; 571 flags |= MS_RDONLY;
@@ -549,29 +575,37 @@ static void fs_remount_simple(const char *path, OPERATION op) {
549 575
550 if (arg_debug) 576 if (arg_debug)
551 printf("Mounting %s %s\n", opstr[op], path); 577 printf("Mounting %s %s\n", opstr[op], path);
578
579 // make path a mount point:
552 // mount --bind path path 580 // mount --bind path path
553 char *proc; 581 EUID_ROOT();
554 if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) 582 int err = bind_mount_by_fd(fd, fd);
555 errExit("asprintf"); 583 EUID_USER();
556 if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) 584 if (err) {
557 errExit("mount"); 585 close(fd);
558 free(proc); 586 goto out;
587 }
559 588
560 // mount --bind -o remount,ro path 589 // remount the mount point
561 // need to open path again without following symbolic links 590 // need to open path again
562 int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); 591 int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
563 if (fd2 == -1) 592 close(fd); // earliest timepoint to close fd
564 errExit("open"); 593 if (fd2 < 0)
594 goto out;
595
596 // device and inode number should be the same
565 struct stat s2; 597 struct stat s2;
566 if (fstat(fd2, &s2) == -1) 598 if (fstat(fd2, &s2) < 0)
567 errExit("fstat"); 599 errExit("fstat");
568 // device and inode number should be the same 600 if (s.st_dev != s2.st_dev || s.st_ino != s2.st_ino)
569 if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino)
570 errLogExit("invalid %s mount", opstr[op]); 601 errLogExit("invalid %s mount", opstr[op]);
571 if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) 602
572 errExit("asprintf"); 603 EUID_ROOT();
573 if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) 604 err = remount_by_fd(fd2, flags);
574 errExit("mount"); 605 EUID_USER();
606 close(fd2);
607 if (err)
608 goto out;
575 609
576 // run a sanity check on /proc/self/mountinfo and confirm that target of the last 610 // run a sanity check on /proc/self/mountinfo and confirm that target of the last
577 // mount operation was path; if there are other mount points contained inside path, 611 // mount operation was path; if there are other mount points contained inside path,
@@ -582,10 +616,8 @@ static void fs_remount_simple(const char *path, OPERATION op) {
582 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 616 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
583 && strcmp(path, "/") != 0) // support read-only=/ 617 && strcmp(path, "/") != 0) // support read-only=/
584 errLogExit("invalid %s mount", opstr[op]); 618 errLogExit("invalid %s mount", opstr[op]);
619
585 fs_logger2(opstr[op], path); 620 fs_logger2(opstr[op], path);
586 free(proc);
587 close(fd1);
588 close(fd2);
589 return; 621 return;
590 622
591out: 623out:
@@ -594,7 +626,9 @@ out:
594 626
595// remount recursively; requires a resolved path 627// remount recursively; requires a resolved path
596static void fs_remount_rec(const char *dir, OPERATION op) { 628static void fs_remount_rec(const char *dir, OPERATION op) {
629 EUID_ASSERT();
597 assert(dir); 630 assert(dir);
631
598 struct stat s; 632 struct stat s;
599 if (stat(dir, &s) != 0) 633 if (stat(dir, &s) != 0)
600 return; 634 return;
@@ -632,6 +666,14 @@ static void fs_remount_rec(const char *dir, OPERATION op) {
632// resolve a path and remount it 666// resolve a path and remount it
633void fs_remount(const char *path, OPERATION op, int rec) { 667void fs_remount(const char *path, OPERATION op, int rec) {
634 assert(path); 668 assert(path);
669
670 int called_as_root = 0;
671 if (geteuid() == 0)
672 called_as_root = 1;
673
674 if (called_as_root)
675 EUID_USER();
676
635 char *rpath = realpath(path, NULL); 677 char *rpath = realpath(path, NULL);
636 if (rpath) { 678 if (rpath) {
637 if (rec) 679 if (rec)
@@ -640,10 +682,14 @@ void fs_remount(const char *path, OPERATION op, int rec) {
640 fs_remount_simple(rpath, op); 682 fs_remount_simple(rpath, op);
641 free(rpath); 683 free(rpath);
642 } 684 }
685
686 if (called_as_root)
687 EUID_ROOT();
643} 688}
644 689
645// Disable /mnt, /media, /run/mount and /run/media access 690// Disable /mnt, /media, /run/mount and /run/media access
646void fs_mnt(const int enforce) { 691void fs_mnt(const int enforce) {
692 EUID_USER();
647 if (enforce) { 693 if (enforce) {
648 // disable-mnt set in firejail.config 694 // disable-mnt set in firejail.config
649 // overriding with noblacklist is not possible in this case 695 // overriding with noblacklist is not possible in this case
@@ -653,13 +699,12 @@ void fs_mnt(const int enforce) {
653 disable_file(BLACKLIST_FILE, "/run/media"); 699 disable_file(BLACKLIST_FILE, "/run/media");
654 } 700 }
655 else { 701 else {
656 EUID_USER();
657 profile_add("blacklist /mnt"); 702 profile_add("blacklist /mnt");
658 profile_add("blacklist /media"); 703 profile_add("blacklist /media");
659 profile_add("blacklist /run/mount"); 704 profile_add("blacklist /run/mount");
660 profile_add("blacklist /run/media"); 705 profile_add("blacklist /run/media");
661 EUID_ROOT();
662 } 706 }
707 EUID_ROOT();
663} 708}
664 709
665 710
@@ -674,7 +719,6 @@ void fs_proc_sys_dev_boot(void) {
674 errExit("mounting /proc/sys"); 719 errExit("mounting /proc/sys");
675 fs_logger("read-only /proc/sys"); 720 fs_logger("read-only /proc/sys");
676 721
677
678 /* Mount a version of /sys that describes the network namespace */ 722 /* Mount a version of /sys that describes the network namespace */
679 if (arg_debug) 723 if (arg_debug)
680 printf("Remounting /sys directory\n"); 724 printf("Remounting /sys directory\n");
@@ -689,13 +733,13 @@ void fs_proc_sys_dev_boot(void) {
689 else 733 else
690 fs_logger("remount /sys"); 734 fs_logger("remount /sys");
691 735
736 EUID_USER();
737
692 disable_file(BLACKLIST_FILE, "/sys/firmware"); 738 disable_file(BLACKLIST_FILE, "/sys/firmware");
693 disable_file(BLACKLIST_FILE, "/sys/hypervisor"); 739 disable_file(BLACKLIST_FILE, "/sys/hypervisor");
694 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option 740 { // allow user access to some directories in /sys/ by specifying 'noblacklist' option
695 EUID_USER();
696 profile_add("blacklist /sys/fs"); 741 profile_add("blacklist /sys/fs");
697 profile_add("blacklist /sys/module"); 742 profile_add("blacklist /sys/module");
698 EUID_ROOT();
699 } 743 }
700 disable_file(BLACKLIST_FILE, "/sys/power"); 744 disable_file(BLACKLIST_FILE, "/sys/power");
701 disable_file(BLACKLIST_FILE, "/sys/kernel/debug"); 745 disable_file(BLACKLIST_FILE, "/sys/kernel/debug");
@@ -739,12 +783,8 @@ void fs_proc_sys_dev_boot(void) {
739 // disable /dev/port 783 // disable /dev/port
740 disable_file(BLACKLIST_FILE, "/dev/port"); 784 disable_file(BLACKLIST_FILE, "/dev/port");
741 785
742
743
744 // disable various ipc sockets in /run/user 786 // disable various ipc sockets in /run/user
745 if (!arg_writable_run_user) { 787 if (!arg_writable_run_user) {
746 struct stat s;
747
748 char *fname; 788 char *fname;
749 if (asprintf(&fname, "/run/user/%d", getuid()) == -1) 789 if (asprintf(&fname, "/run/user/%d", getuid()) == -1)
750 errExit("asprintf"); 790 errExit("asprintf");
@@ -755,8 +795,7 @@ void fs_proc_sys_dev_boot(void) {
755 errExit("asprintf"); 795 errExit("asprintf");
756 if (create_empty_dir_as_user(fnamegpg, 0700)) 796 if (create_empty_dir_as_user(fnamegpg, 0700))
757 fs_logger2("create", fnamegpg); 797 fs_logger2("create", fnamegpg);
758 if (stat(fnamegpg, &s) == 0) 798 disable_file(BLACKLIST_FILE, fnamegpg);
759 disable_file(BLACKLIST_FILE, fnamegpg);
760 free(fnamegpg); 799 free(fnamegpg);
761 800
762 // disable /run/user/{uid}/systemd 801 // disable /run/user/{uid}/systemd
@@ -765,8 +804,7 @@ void fs_proc_sys_dev_boot(void) {
765 errExit("asprintf"); 804 errExit("asprintf");
766 if (create_empty_dir_as_user(fnamesysd, 0755)) 805 if (create_empty_dir_as_user(fnamesysd, 0755))
767 fs_logger2("create", fnamesysd); 806 fs_logger2("create", fnamesysd);
768 if (stat(fnamesysd, &s) == 0) 807 disable_file(BLACKLIST_FILE, fnamesysd);
769 disable_file(BLACKLIST_FILE, fnamesysd);
770 free(fnamesysd); 808 free(fnamesysd);
771 } 809 }
772 free(fname); 810 free(fname);
@@ -777,35 +815,30 @@ void fs_proc_sys_dev_boot(void) {
777 disable_file(BLACKLIST_FILE, "/dev/kmsg"); 815 disable_file(BLACKLIST_FILE, "/dev/kmsg");
778 disable_file(BLACKLIST_FILE, "/proc/kmsg"); 816 disable_file(BLACKLIST_FILE, "/proc/kmsg");
779 } 817 }
818
819 EUID_ROOT();
780} 820}
781 821
782// disable firejail configuration in ~/.config/firejail 822// disable firejail configuration in ~/.config/firejail
783void disable_config(void) { 823void disable_config(void) {
784 struct stat s; 824 EUID_USER();
785
786 char *fname; 825 char *fname;
787 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1) 826 if (asprintf(&fname, "%s/.config/firejail", cfg.homedir) == -1)
788 errExit("asprintf"); 827 errExit("asprintf");
789 if (stat(fname, &s) == 0) 828 disable_file(BLACKLIST_FILE, fname);
790 disable_file(BLACKLIST_FILE, fname);
791 free(fname); 829 free(fname);
792 830
793 // disable run time information 831 // disable run time information
794 if (stat(RUN_FIREJAIL_NETWORK_DIR, &s) == 0) 832 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR);
795 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NETWORK_DIR); 833 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR);
796 if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s) == 0) 834 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR);
797 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_BANDWIDTH_DIR); 835 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
798 if (stat(RUN_FIREJAIL_NAME_DIR, &s) == 0) 836 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
799 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_NAME_DIR); 837 EUID_ROOT();
800 if (stat(RUN_FIREJAIL_PROFILE_DIR, &s) == 0)
801 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_PROFILE_DIR);
802 if (stat(RUN_FIREJAIL_X11_DIR, &s) == 0)
803 disable_file(BLACKLIST_FILE, RUN_FIREJAIL_X11_DIR);
804} 838}
805 839
806 840
807// build a basic read-only filesystem 841// build a basic read-only filesystem
808// top level directories could be links, run no after-mount checks
809void fs_basic_fs(void) { 842void fs_basic_fs(void) {
810 uid_t uid = getuid(); 843 uid_t uid = getuid();
811 844
@@ -815,6 +848,7 @@ void fs_basic_fs(void) {
815 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 848 if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
816 errExit("mounting /proc"); 849 errExit("mounting /proc");
817 850
851 EUID_USER();
818 if (arg_debug) 852 if (arg_debug)
819 printf("Basic read-only filesystem:\n"); 853 printf("Basic read-only filesystem:\n");
820 if (!arg_writable_etc) { 854 if (!arg_writable_etc) {
@@ -834,6 +868,7 @@ void fs_basic_fs(void) {
834 fs_remount("/lib64", MOUNT_READONLY, 1); 868 fs_remount("/lib64", MOUNT_READONLY, 1);
835 fs_remount("/lib32", MOUNT_READONLY, 1); 869 fs_remount("/lib32", MOUNT_READONLY, 1);
836 fs_remount("/libx32", MOUNT_READONLY, 1); 870 fs_remount("/libx32", MOUNT_READONLY, 1);
871 EUID_ROOT();
837 872
838 // update /var directory in order to support multiple sandboxes running on the same root directory 873 // update /var directory in order to support multiple sandboxes running on the same root directory
839 fs_var_lock(); 874 fs_var_lock();
@@ -862,6 +897,7 @@ void fs_basic_fs(void) {
862#ifdef HAVE_OVERLAYFS 897#ifdef HAVE_OVERLAYFS
863char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { 898char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) {
864 assert(subdirname); 899 assert(subdirname);
900 EUID_ASSERT();
865 struct stat s; 901 struct stat s;
866 char *dirname; 902 char *dirname;
867 903
@@ -1221,6 +1257,7 @@ void fs_overlayfs(void) {
1221 1257
1222// this function is called from sandbox.c before blacklist/whitelist functions 1258// this function is called from sandbox.c before blacklist/whitelist functions
1223void fs_private_tmp(void) { 1259void fs_private_tmp(void) {
1260 EUID_ASSERT();
1224 if (arg_debug) 1261 if (arg_debug)
1225 printf("Generate private-tmp whitelist commands\n"); 1262 printf("Generate private-tmp whitelist commands\n");
1226 1263
@@ -1241,8 +1278,8 @@ void fs_private_tmp(void) {
1241 1278
1242 // whitelist x11 directory 1279 // whitelist x11 directory
1243 profile_add("whitelist /tmp/.X11-unix"); 1280 profile_add("whitelist /tmp/.X11-unix");
1244 // read-only x11 directory 1281 // read-only x11 directory
1245 profile_add("read-only /tmp/.X11-unix"); 1282 profile_add("read-only /tmp/.X11-unix");
1246 1283
1247 // whitelist any pulse* file in /tmp directory 1284 // whitelist any pulse* file in /tmp directory
1248 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user 1285 // some distros use PulseAudio sockets under /tmp instead of the socket in /urn/user
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c
index 8c2870a4d..8cc3ecc62 100644
--- a/src/firejail/fs_dev.c
+++ b/src/firejail/fs_dev.c
@@ -187,8 +187,10 @@ static void mount_dev_shm(void) {
187static void process_dev_shm(void) { 187static void process_dev_shm(void) {
188 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...) 188 // Jack audio keeps an Unix socket under (/dev/shm/jack_default_1000_0 or /dev/shm/jack/...)
189 // looking for jack socket 189 // looking for jack socket
190 EUID_USER();
190 glob_t globbuf; 191 glob_t globbuf;
191 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf); 192 int globerr = glob(RUN_DEV_DIR "/shm/jack*", GLOB_NOSORT, NULL, &globbuf);
193 EUID_ROOT();
192 if (globerr && !arg_keep_dev_shm) { 194 if (globerr && !arg_keep_dev_shm) {
193 empty_dev_shm(); 195 empty_dev_shm();
194 return; 196 return;
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 4bcefa443..0ed476063 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -34,24 +34,24 @@
34#define O_PATH 010000000 34#define O_PATH 010000000
35#endif 35#endif
36 36
37static void skel(const char *homedir, uid_t u, gid_t g) { 37static void skel(const char *homedir) {
38 char *fname; 38 EUID_ASSERT();
39 39
40 // zsh 40 // zsh
41 if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) { 41 if (!arg_shell_none && (strcmp(cfg.shell,"/usr/bin/zsh") == 0 || strcmp(cfg.shell,"/bin/zsh") == 0)) {
42 // copy skel files 42 // copy skel files
43 char *fname;
43 if (asprintf(&fname, "%s/.zshrc", homedir) == -1) 44 if (asprintf(&fname, "%s/.zshrc", homedir) == -1)
44 errExit("asprintf"); 45 errExit("asprintf");
45 struct stat s;
46 // don't copy it if we already have the file 46 // don't copy it if we already have the file
47 if (stat(fname, &s) == 0) 47 if (access(fname, F_OK) == 0)
48 return; 48 return;
49 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 49 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
50 fprintf(stderr, "Error: invalid %s file\n", fname); 50 fprintf(stderr, "Error: invalid %s file\n", fname);
51 exit(1); 51 exit(1);
52 } 52 }
53 if (stat("/etc/skel/.zshrc", &s) == 0) { 53 if (access("/etc/skel/.zshrc", R_OK) == 0) {
54 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user 54 copy_file_as_user("/etc/skel/.zshrc", fname, 0644); // regular user
55 fs_logger("clone /etc/skel/.zshrc"); 55 fs_logger("clone /etc/skel/.zshrc");
56 fs_logger2("clone", fname); 56 fs_logger2("clone", fname);
57 } 57 }
@@ -65,19 +65,18 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
65 // csh 65 // csh
66 else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) { 66 else if (!arg_shell_none && strcmp(cfg.shell,"/bin/csh") == 0) {
67 // copy skel files 67 // copy skel files
68 char *fname;
68 if (asprintf(&fname, "%s/.cshrc", homedir) == -1) 69 if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
69 errExit("asprintf"); 70 errExit("asprintf");
70 struct stat s;
71
72 // don't copy it if we already have the file 71 // don't copy it if we already have the file
73 if (stat(fname, &s) == 0) 72 if (access(fname, F_OK) == 0)
74 return; 73 return;
75 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 74 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
76 fprintf(stderr, "Error: invalid %s file\n", fname); 75 fprintf(stderr, "Error: invalid %s file\n", fname);
77 exit(1); 76 exit(1);
78 } 77 }
79 if (stat("/etc/skel/.cshrc", &s) == 0) { 78 if (access("/etc/skel/.cshrc", R_OK) == 0) {
80 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user 79 copy_file_as_user("/etc/skel/.cshrc", fname, 0644); // regular user
81 fs_logger("clone /etc/skel/.cshrc"); 80 fs_logger("clone /etc/skel/.cshrc");
82 fs_logger2("clone", fname); 81 fs_logger2("clone", fname);
83 } 82 }
@@ -91,18 +90,18 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
91 // bash etc. 90 // bash etc.
92 else { 91 else {
93 // copy skel files 92 // copy skel files
93 char *fname;
94 if (asprintf(&fname, "%s/.bashrc", homedir) == -1) 94 if (asprintf(&fname, "%s/.bashrc", homedir) == -1)
95 errExit("asprintf"); 95 errExit("asprintf");
96 struct stat s;
97 // don't copy it if we already have the file 96 // don't copy it if we already have the file
98 if (stat(fname, &s) == 0) 97 if (access(fname, F_OK) == 0)
99 return; 98 return;
100 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat 99 if (is_link(fname)) { // access(3) on dangling symlinks fails, try again using lstat
101 fprintf(stderr, "Error: invalid %s file\n", fname); 100 fprintf(stderr, "Error: invalid %s file\n", fname);
102 exit(1); 101 exit(1);
103 } 102 }
104 if (stat("/etc/skel/.bashrc", &s) == 0) { 103 if (access("/etc/skel/.bashrc", R_OK) == 0) {
105 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user 104 copy_file_as_user("/etc/skel/.bashrc", fname, 0644); // regular user
106 fs_logger("clone /etc/skel/.bashrc"); 105 fs_logger("clone /etc/skel/.bashrc");
107 fs_logger2("clone", fname); 106 fs_logger2("clone", fname);
108 } 107 }
@@ -112,6 +111,7 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
112} 111}
113 112
114static int store_xauthority(void) { 113static int store_xauthority(void) {
114 EUID_ASSERT();
115 if (arg_x11_block) 115 if (arg_x11_block)
116 return 0; 116 return 0;
117 117
@@ -122,14 +122,15 @@ static int store_xauthority(void) {
122 errExit("asprintf"); 122 errExit("asprintf");
123 123
124 struct stat s; 124 struct stat s;
125 if (stat(src, &s) == 0) { 125 if (lstat(src, &s) == 0) {
126 if (is_link(src)) { 126 if (S_ISLNK(s.st_mode)) {
127 fwarning("invalid .Xauthority file\n"); 127 fwarning("invalid .Xauthority file\n");
128 free(src); 128 free(src);
129 return 0; 129 return 0;
130 } 130 }
131 131
132 // create an empty file as root, and change ownership to user 132 // create an empty file as root, and change ownership to user
133 EUID_ROOT();
133 FILE *fp = fopen(dest, "we"); 134 FILE *fp = fopen(dest, "we");
134 if (fp) { 135 if (fp) {
135 fprintf(fp, "\n"); 136 fprintf(fp, "\n");
@@ -138,10 +139,11 @@ static int store_xauthority(void) {
138 } 139 }
139 else 140 else
140 errExit("fopen"); 141 errExit("fopen");
142 EUID_USER();
141 143
142 copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user 144 copy_file_as_user(src, dest, 0600); // regular user
143 fs_logger2("clone", dest);
144 selinux_relabel_path(dest, src); 145 selinux_relabel_path(dest, src);
146 fs_logger2("clone", dest);
145 free(src); 147 free(src);
146 return 1; // file copied 148 return 1; // file copied
147 } 149 }
@@ -151,6 +153,7 @@ static int store_xauthority(void) {
151} 153}
152 154
153static int store_asoundrc(void) { 155static int store_asoundrc(void) {
156 EUID_ASSERT();
154 if (arg_nosound) 157 if (arg_nosound)
155 return 0; 158 return 0;
156 159
@@ -161,11 +164,11 @@ static int store_asoundrc(void) {
161 errExit("asprintf"); 164 errExit("asprintf");
162 165
163 struct stat s; 166 struct stat s;
164 if (stat(src, &s) == 0) { 167 if (lstat(src, &s) == 0) {
165 if (is_link(src)) { 168 if (S_ISLNK(s.st_mode)) {
166 // make sure the real path of the file is inside the home directory 169 // make sure the real path of the file is inside the home directory
167 /* coverity[toctou] */ 170 /* coverity[toctou] */
168 char* rp = realpath(src, NULL); 171 char *rp = realpath(src, NULL);
169 if (!rp) { 172 if (!rp) {
170 fprintf(stderr, "Error: Cannot access %s\n", src); 173 fprintf(stderr, "Error: Cannot access %s\n", src);
171 exit(1); 174 exit(1);
@@ -178,6 +181,7 @@ static int store_asoundrc(void) {
178 } 181 }
179 182
180 // create an empty file as root, and change ownership to user 183 // create an empty file as root, and change ownership to user
184 EUID_ROOT();
181 FILE *fp = fopen(dest, "we"); 185 FILE *fp = fopen(dest, "we");
182 if (fp) { 186 if (fp) {
183 fprintf(fp, "\n"); 187 fprintf(fp, "\n");
@@ -186,10 +190,11 @@ static int store_asoundrc(void) {
186 } 190 }
187 else 191 else
188 errExit("fopen"); 192 errExit("fopen");
193 EUID_USER();
189 194
190 copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user 195 copy_file_as_user(src, dest, 0644); // regular user
191 selinux_relabel_path(dest, src);
192 fs_logger2("clone", dest); 196 fs_logger2("clone", dest);
197 selinux_relabel_path(dest, src);
193 free(src); 198 free(src);
194 return 1; // file copied 199 return 1; // file copied
195 } 200 }
@@ -199,6 +204,7 @@ static int store_asoundrc(void) {
199} 204}
200 205
201static void copy_xauthority(void) { 206static void copy_xauthority(void) {
207 EUID_ASSERT();
202 // copy XAUTHORITY_FILE in the new home directory 208 // copy XAUTHORITY_FILE in the new home directory
203 char *src = RUN_XAUTHORITY_FILE ; 209 char *src = RUN_XAUTHORITY_FILE ;
204 char *dest; 210 char *dest;
@@ -211,16 +217,18 @@ static void copy_xauthority(void) {
211 exit(1); 217 exit(1);
212 } 218 }
213 219
214 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user 220 copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
215 selinux_relabel_path(dest, src);
216 fs_logger2("clone", dest); 221 fs_logger2("clone", dest);
222 selinux_relabel_path(dest, dest);
217 free(dest); 223 free(dest);
218 224
219 // delete the temporary file 225 EUID_ROOT();
220 unlink(src); 226 unlink(src); // delete the temporary file
227 EUID_USER();
221} 228}
222 229
223static void copy_asoundrc(void) { 230static void copy_asoundrc(void) {
231 EUID_ASSERT();
224 // copy ASOUNDRC_FILE in the new home directory 232 // copy ASOUNDRC_FILE in the new home directory
225 char *src = RUN_ASOUNDRC_FILE ; 233 char *src = RUN_ASOUNDRC_FILE ;
226 char *dest; 234 char *dest;
@@ -233,12 +241,14 @@ static void copy_asoundrc(void) {
233 exit(1); 241 exit(1);
234 } 242 }
235 243
236 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user 244 copy_file_as_user(src, dest, S_IRUSR | S_IWUSR); // regular user
237 fs_logger2("clone", dest); 245 fs_logger2("clone", dest);
246 selinux_relabel_path(dest, dest);
238 free(dest); 247 free(dest);
239 248
240 // delete the temporary file 249 EUID_ROOT();
241 unlink(src); 250 unlink(src); // delete the temporary file
251 EUID_USER();
242} 252}
243 253
244// private mode (--private=homedir): 254// private mode (--private=homedir):
@@ -251,13 +261,14 @@ void fs_private_homedir(void) {
251 char *private_homedir = cfg.home_private; 261 char *private_homedir = cfg.home_private;
252 assert(homedir); 262 assert(homedir);
253 assert(private_homedir); 263 assert(private_homedir);
264 EUID_ASSERT();
265
266 uid_t u = getuid();
267 // gid_t g = getgid();
254 268
255 int xflag = store_xauthority(); 269 int xflag = store_xauthority();
256 int aflag = store_asoundrc(); 270 int aflag = store_asoundrc();
257 271
258 uid_t u = getuid();
259 gid_t g = getgid();
260
261 // mount bind private_homedir on top of homedir 272 // mount bind private_homedir on top of homedir
262 if (arg_debug) 273 if (arg_debug)
263 printf("Mount-bind %s on top of %s\n", private_homedir, homedir); 274 printf("Mount-bind %s on top of %s\n", private_homedir, homedir);
@@ -286,17 +297,11 @@ void fs_private_homedir(void) {
286 exit(1); 297 exit(1);
287 } 298 }
288 // mount via the links in /proc/self/fd 299 // mount via the links in /proc/self/fd
289 char *proc_src, *proc_dst; 300 EUID_ROOT();
290 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) 301 if (bind_mount_by_fd(src, dst))
291 errExit("asprintf");
292 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
293 errExit("asprintf");
294 if (mount(proc_src, proc_dst, NULL, MS_NOSUID | MS_NODEV | MS_BIND | MS_REC, NULL) < 0)
295 errExit("mount bind"); 302 errExit("mount bind");
296 free(proc_src); 303 EUID_USER();
297 free(proc_dst); 304
298 close(src);
299 close(dst);
300 // check /proc/self/mountinfo to confirm the mount is ok 305 // check /proc/self/mountinfo to confirm the mount is ok
301 MountData *mptr = get_last_mount(); 306 MountData *mptr = get_last_mount();
302 size_t len = strlen(homedir); 307 size_t len = strlen(homedir);
@@ -304,6 +309,8 @@ void fs_private_homedir(void) {
304 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/')) 309 (*(mptr->dir + len) != '\0' && *(mptr->dir + len) != '/'))
305 errLogExit("invalid private mount"); 310 errLogExit("invalid private mount");
306 311
312 close(src);
313 close(dst);
307 fs_logger3("mount-bind", private_homedir, homedir); 314 fs_logger3("mount-bind", private_homedir, homedir);
308 fs_logger2("whitelist", homedir); 315 fs_logger2("whitelist", homedir);
309// preserve mode and ownership 316// preserve mode and ownership
@@ -312,6 +319,7 @@ void fs_private_homedir(void) {
312// if (chmod(homedir, s.st_mode) == -1) 319// if (chmod(homedir, s.st_mode) == -1)
313// errExit("mount-bind chmod"); 320// errExit("mount-bind chmod");
314 321
322 EUID_ROOT();
315 if (u != 0) { 323 if (u != 0) {
316 // mask /root 324 // mask /root
317 if (arg_debug) 325 if (arg_debug)
@@ -330,8 +338,9 @@ void fs_private_homedir(void) {
330 selinux_relabel_path("/home", "/home"); 338 selinux_relabel_path("/home", "/home");
331 fs_logger("tmpfs /home"); 339 fs_logger("tmpfs /home");
332 } 340 }
341 EUID_USER();
333 342
334 skel(homedir, u, g); 343 skel(homedir);
335 if (xflag) 344 if (xflag)
336 copy_xauthority(); 345 copy_xauthority();
337 if (aflag) 346 if (aflag)
@@ -346,12 +355,15 @@ void fs_private_homedir(void) {
346void fs_private(void) { 355void fs_private(void) {
347 char *homedir = cfg.homedir; 356 char *homedir = cfg.homedir;
348 assert(homedir); 357 assert(homedir);
358 EUID_ASSERT();
359
349 uid_t u = getuid(); 360 uid_t u = getuid();
350 gid_t g = getgid(); 361 gid_t g = getgid();
351 362
352 int xflag = store_xauthority(); 363 int xflag = store_xauthority();
353 int aflag = store_asoundrc(); 364 int aflag = store_asoundrc();
354 365
366 EUID_ROOT();
355 // mask /root 367 // mask /root
356 if (arg_debug) 368 if (arg_debug)
357 printf("Mounting a new /root directory\n"); 369 printf("Mounting a new /root directory\n");
@@ -394,8 +406,9 @@ void fs_private(void) {
394 406
395 selinux_relabel_path(homedir, homedir); 407 selinux_relabel_path(homedir, homedir);
396 } 408 }
409 EUID_USER();
397 410
398 skel(homedir, u, g); 411 skel(homedir);
399 if (xflag) 412 if (xflag)
400 copy_xauthority(); 413 copy_xauthority();
401 if (aflag) 414 if (aflag)
@@ -438,6 +451,7 @@ void fs_check_private_cwd(const char *dir) {
438// --private-home 451// --private-home
439//*********************************************************************************** 452//***********************************************************************************
440static char *check_dir_or_file(const char *name) { 453static char *check_dir_or_file(const char *name) {
454 EUID_ASSERT();
441 assert(name); 455 assert(name);
442 456
443 // basic checks 457 // basic checks
@@ -498,6 +512,7 @@ errexit:
498} 512}
499 513
500static void duplicate(char *name) { 514static void duplicate(char *name) {
515 EUID_ASSERT();
501 char *fname = check_dir_or_file(name); 516 char *fname = check_dir_or_file(name);
502 517
503 if (arg_debug) 518 if (arg_debug)
@@ -535,28 +550,31 @@ static void duplicate(char *name) {
535// set skel files, 550// set skel files,
536// restore .Xauthority 551// restore .Xauthority
537void fs_private_home_list(void) { 552void fs_private_home_list(void) {
538 timetrace_start();
539
540 char *homedir = cfg.homedir; 553 char *homedir = cfg.homedir;
541 char *private_list = cfg.home_private_keep; 554 char *private_list = cfg.home_private_keep;
542 assert(homedir); 555 assert(homedir);
543 assert(private_list); 556 assert(private_list);
557 EUID_ASSERT();
544 558
545 int xflag = store_xauthority(); 559 timetrace_start();
546 int aflag = store_asoundrc();
547 560
548 uid_t uid = getuid(); 561 uid_t uid = getuid();
549 gid_t gid = getgid(); 562 gid_t gid = getgid();
550 563
564 int xflag = store_xauthority();
565 int aflag = store_asoundrc();
566
551 // create /run/firejail/mnt/home directory 567 // create /run/firejail/mnt/home directory
568 EUID_ROOT();
552 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid); 569 mkdir_attr(RUN_HOME_DIR, 0755, uid, gid);
553 selinux_relabel_path(RUN_HOME_DIR, homedir); 570 selinux_relabel_path(RUN_HOME_DIR, homedir);
571
554 fs_logger_print(); // save the current log 572 fs_logger_print(); // save the current log
573 EUID_USER();
555 574
575 // copy the list of files in the new home directory
556 if (arg_debug) 576 if (arg_debug)
557 printf("Copying files in the new home:\n"); 577 printf("Copying files in the new home:\n");
558
559 // copy the list of files in the new home directory
560 char *dlist = strdup(cfg.home_private_keep); 578 char *dlist = strdup(cfg.home_private_keep);
561 if (!dlist) 579 if (!dlist)
562 errExit("strdup"); 580 errExit("strdup");
@@ -589,24 +607,19 @@ void fs_private_home_list(void) {
589 exit(1); 607 exit(1);
590 } 608 }
591 // mount using the file descriptor 609 // mount using the file descriptor
592 char *proc; 610 EUID_ROOT();
593 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) 611 if (bind_mount_path_to_fd(RUN_HOME_DIR, fd))
594 errExit("asprintf");
595 if (mount(RUN_HOME_DIR, proc, NULL, MS_BIND|MS_REC, NULL) < 0)
596 errExit("mount bind"); 612 errExit("mount bind");
597 free(proc); 613 EUID_USER();
598 close(fd); 614 close(fd);
615
599 // check /proc/self/mountinfo to confirm the mount is ok 616 // check /proc/self/mountinfo to confirm the mount is ok
600 MountData *mptr = get_last_mount(); 617 MountData *mptr = get_last_mount();
601 if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 618 if (strcmp(mptr->dir, homedir) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
602 errLogExit("invalid private-home mount"); 619 errLogExit("invalid private-home mount");
603 fs_logger2("tmpfs", homedir); 620 fs_logger2("tmpfs", homedir);
604 621
605 // mask RUN_HOME_DIR, it is writable and not noexec 622 EUID_ROOT();
606 if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
607 errExit("mounting tmpfs");
608 fs_logger2("tmpfs", RUN_HOME_DIR);
609
610 if (uid != 0) { 623 if (uid != 0) {
611 // mask /root 624 // mask /root
612 if (arg_debug) 625 if (arg_debug)
@@ -626,7 +639,12 @@ void fs_private_home_list(void) {
626 fs_logger("tmpfs /home"); 639 fs_logger("tmpfs /home");
627 } 640 }
628 641
629 skel(homedir, uid, gid); 642 // mask RUN_HOME_DIR, it is writable and not noexec
643 if (mount("tmpfs", RUN_HOME_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
644 errExit("mounting tmpfs");
645 EUID_USER();
646
647 skel(homedir);
630 if (xflag) 648 if (xflag)
631 copy_xauthority(); 649 copy_xauthority();
632 if (aflag) 650 if (aflag)
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c
index 5df356d04..9d7a17cf3 100644
--- a/src/firejail/fs_lib.c
+++ b/src/firejail/fs_lib.c
@@ -178,8 +178,7 @@ void fslib_mount(const char *full_path) {
178 178
179 if (*full_path == '\0' || 179 if (*full_path == '\0' ||
180 !valid_full_path(full_path) || 180 !valid_full_path(full_path) ||
181 access(full_path, F_OK) != 0 || 181 stat_as_user(full_path, &s) != 0 ||
182 stat(full_path, &s) != 0 ||
183 s.st_uid != 0) 182 s.st_uid != 0)
184 return; 183 return;
185 184
@@ -203,7 +202,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
203 } 202 }
204 203
205 if (arg_debug || arg_debug_private_lib) 204 if (arg_debug || arg_debug_private_lib)
206 printf(" fslib_mount_libs %s (parse as %s)\n", full_path, user ? "user" : "root"); 205 printf(" fslib_mount_libs %s\n", full_path);
207 // create an empty RUN_LIB_FILE and allow the user to write to it 206 // create an empty RUN_LIB_FILE and allow the user to write to it
208 unlink(RUN_LIB_FILE); // in case is there 207 unlink(RUN_LIB_FILE); // in case is there
209 create_empty_file_as_root(RUN_LIB_FILE, 0644); 208 create_empty_file_as_root(RUN_LIB_FILE, 0644);
@@ -212,7 +211,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) {
212 211
213 // run fldd to extract the list of files 212 // run fldd to extract the list of files
214 if (arg_debug || arg_debug_private_lib) 213 if (arg_debug || arg_debug_private_lib)
215 printf(" running fldd %s\n", full_path); 214 printf(" running fldd %s as %s\n", full_path, user ? "user" : "root");
216 unsigned mask; 215 unsigned mask;
217 if (user) 216 if (user)
218 mask = SBOX_USER; 217 mask = SBOX_USER;
@@ -246,7 +245,7 @@ static void load_library(const char *fname) {
246 245
247 // existing file owned by root 246 // existing file owned by root
248 struct stat s; 247 struct stat s;
249 if (!access(fname, F_OK) && stat(fname, &s) == 0 && s.st_uid == 0) { 248 if (stat_as_user(fname, &s) == 0 && s.st_uid == 0) {
250 // load directories, regular 64 bit libraries, and 64 bit executables 249 // load directories, regular 64 bit libraries, and 64 bit executables
251 if (S_ISDIR(s.st_mode)) 250 if (S_ISDIR(s.st_mode))
252 fslib_mount(fname); 251 fslib_mount(fname);
@@ -286,19 +285,21 @@ static void install_list_entry(const char *lib) {
286#define DO_GLOBBING 285#define DO_GLOBBING
287#ifdef DO_GLOBBING 286#ifdef DO_GLOBBING
288 // globbing 287 // globbing
288 EUID_USER();
289 glob_t globbuf; 289 glob_t globbuf;
290 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf); 290 int globerr = glob(fname, GLOB_NOCHECK | GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf);
291 if (globerr) { 291 if (globerr) {
292 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname); 292 fprintf(stderr, "Error: failed to glob private-lib pattern %s\n", fname);
293 exit(1); 293 exit(1);
294 } 294 }
295 EUID_ROOT();
295 size_t j; 296 size_t j;
296 for (j = 0; j < globbuf.gl_pathc; j++) { 297 for (j = 0; j < globbuf.gl_pathc; j++) {
297 assert(globbuf.gl_pathv[j]); 298 assert(globbuf.gl_pathv[j]);
298//printf("glob %s\n", globbuf.gl_pathv[j]); 299//printf("glob %s\n", globbuf.gl_pathv[j]);
299 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway 300 // GLOB_NOCHECK - no pattern matched returns the original pattern; try to load it anyway
300 301
301 // foobar/* includes foobar/. and foobar/.. 302 // foobar/* expands to foobar/. and foobar/..
302 const char *base = gnu_basename(globbuf.gl_pathv[j]); 303 const char *base = gnu_basename(globbuf.gl_pathv[j]);
303 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0) 304 if (strcmp(base, ".") == 0 || strcmp(base, "..") == 0)
304 continue; 305 continue;
diff --git a/src/firejail/fs_mkdir.c b/src/firejail/fs_mkdir.c
index 8cfeea582..0195435f9 100644
--- a/src/firejail/fs_mkdir.c
+++ b/src/firejail/fs_mkdir.c
@@ -25,6 +25,9 @@
25#include <sys/wait.h> 25#include <sys/wait.h>
26#include <string.h> 26#include <string.h>
27 27
28#ifdef HAVE_GCOV
29#include "../include/gcov_wrapper.h"
30#endif
28 31
29static void check(const char *fname) { 32static void check(const char *fname) {
30 // manufacture /run/user directory 33 // manufacture /run/user directory
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c
index 1fc38361e..475a391ec 100644
--- a/src/firejail/fs_trace.c
+++ b/src/firejail/fs_trace.c
@@ -71,12 +71,8 @@ void fs_tracefile(void) {
71 // mount using the symbolic link in /proc/self/fd 71 // mount using the symbolic link in /proc/self/fd
72 if (arg_debug) 72 if (arg_debug)
73 printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE); 73 printf("Bind mount %s to %s\n", arg_tracefile, RUN_TRACE_FILE);
74 char *proc; 74 if (bind_mount_fd_to_path(fd, RUN_TRACE_FILE))
75 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
76 errExit("asprintf");
77 if (mount(proc, RUN_TRACE_FILE, NULL, MS_BIND|MS_REC, NULL) < 0)
78 errExit("mount bind " RUN_TRACE_FILE); 75 errExit("mount bind " RUN_TRACE_FILE);
79 free(proc);
80 close(fd); 76 close(fd);
81 // now that RUN_TRACE_FILE is user-writable, mount it noexec 77 // now that RUN_TRACE_FILE is user-writable, mount it noexec
82 fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0); 78 fs_remount(RUN_TRACE_FILE, MOUNT_NOEXEC, 0);
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c
index bae3d6df0..20e262d80 100644
--- a/src/firejail/fs_var.c
+++ b/src/firejail/fs_var.c
@@ -323,4 +323,8 @@ void fs_var_utmp(void) {
323 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) 323 if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
324 errExit("mount bind utmp"); 324 errExit("mount bind utmp");
325 fs_logger2("create", UTMP_FILE); 325 fs_logger2("create", UTMP_FILE);
326
327 // blacklist RUN_UTMP_FILE
328 if (mount(RUN_RO_FILE, RUN_UTMP_FILE, NULL, MS_BIND, "mode=400,gid=0") < 0)
329 errExit("mount bind");
326} 330}
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c
index 9a7a1bac7..943f275de 100644
--- a/src/firejail/fs_whitelist.c
+++ b/src/firejail/fs_whitelist.c
@@ -195,15 +195,7 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
195 195
196 if (arg_debug || arg_debug_whitelists) 196 if (arg_debug || arg_debug_whitelists)
197 printf("Whitelisting %s\n", path); 197 printf("Whitelisting %s\n", path);
198 198 if (bind_mount_by_fd(fd, fd3))
199 // in order to make this mount resilient against symlink attacks, use
200 // magic links in /proc/self/fd instead of mounting the paths directly
201 char *proc_src, *proc_dst;
202 if (asprintf(&proc_src, "/proc/self/fd/%d", fd) == -1)
203 errExit("asprintf");
204 if (asprintf(&proc_dst, "/proc/self/fd/%d", fd3) == -1)
205 errExit("asprintf");
206 if (mount(proc_src, proc_dst, NULL, MS_BIND | MS_REC, NULL) < 0)
207 errExit("mount bind"); 199 errExit("mount bind");
208 // check the last mount operation 200 // check the last mount operation
209 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found 201 MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found
@@ -221,8 +213,6 @@ static void whitelist_file(int dirfd, const char *relpath, const char *path) {
221 // - there should be more than one '/' char in dest string 213 // - there should be more than one '/' char in dest string
222 if (mptr->dir == strrchr(mptr->dir, '/')) 214 if (mptr->dir == strrchr(mptr->dir, '/'))
223 errLogExit("invalid whitelist mount"); 215 errLogExit("invalid whitelist mount");
224 free(proc_src);
225 free(proc_dst);
226 close(fd); 216 close(fd);
227 close(fd3); 217 close(fd3);
228 fs_logger2("whitelist", path); 218 fs_logger2("whitelist", path);
@@ -267,6 +257,7 @@ static void whitelist_symlink(const char *link, const char *target) {
267} 257}
268 258
269static void globbing(const char *pattern) { 259static void globbing(const char *pattern) {
260 EUID_ASSERT();
270 assert(pattern); 261 assert(pattern);
271 262
272 // globbing 263 // globbing
@@ -304,7 +295,6 @@ static void globbing(const char *pattern) {
304} 295}
305 296
306// mount tmpfs on all top level directories 297// mount tmpfs on all top level directories
307// home directories *inside* /run/user/$UID are not fully supported
308static void tmpfs_topdirs(const TopDir *topdirs) { 298static void tmpfs_topdirs(const TopDir *topdirs) {
309 int tmpfs_home = 0; 299 int tmpfs_home = 0;
310 int tmpfs_runuser = 0; 300 int tmpfs_runuser = 0;
@@ -335,18 +325,15 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
335 325
336 // mount tmpfs 326 // mount tmpfs
337 fs_tmpfs(topdirs[i].path, 0); 327 fs_tmpfs(topdirs[i].path, 0);
328 selinux_relabel_path(topdirs[i].path, topdirs[i].path);
338 329
339 // init tmpfs 330 // init tmpfs
340 if (strcmp(topdirs[i].path, "/run") == 0) { 331 if (strcmp(topdirs[i].path, "/run") == 0) {
341 // restore /run/firejail directory 332 // restore /run/firejail directory
342 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) 333 if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1)
343 errExit("mkdir"); 334 errExit("mkdir");
344 char *proc; 335 if (bind_mount_fd_to_path(fd, RUN_FIREJAIL_DIR))
345 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
346 errExit("asprintf");
347 if (mount(proc, RUN_FIREJAIL_DIR, NULL, MS_BIND | MS_REC, NULL) < 0)
348 errExit("mount bind"); 336 errExit("mount bind");
349 free(proc);
350 close(fd); 337 close(fd);
351 fs_logger2("whitelist", RUN_FIREJAIL_DIR); 338 fs_logger2("whitelist", RUN_FIREJAIL_DIR);
352 339
@@ -384,14 +371,14 @@ static void tmpfs_topdirs(const TopDir *topdirs) {
384 const char *rel = cfg.homedir + topdir_len + 1; 371 const char *rel = cfg.homedir + topdir_len + 1;
385 whitelist_file(topdirs[i].fd, rel, cfg.homedir); 372 whitelist_file(topdirs[i].fd, rel, cfg.homedir);
386 } 373 }
387
388 selinux_relabel_path(topdirs[i].path, topdirs[i].path);
389 } 374 }
390 375
391 // user home directory 376 // user home directory
392 if (tmpfs_home) 377 if (tmpfs_home) {
393 // checks owner if outside /home 378 EUID_USER();
394 fs_private(); 379 fs_private(); // checks owner if outside /home
380 EUID_ROOT();
381 }
395 382
396 // /run/user/$UID directory 383 // /run/user/$UID directory
397 if (tmpfs_runuser) { 384 if (tmpfs_runuser) {
@@ -467,9 +454,9 @@ static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) {
467 errExit("strdup"); 454 errExit("strdup");
468 455
469 // open the directory, don't follow symbolic links 456 // open the directory, don't follow symbolic links
470 rv->fd = safer_openat(-1, rv->path, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC); 457 rv->fd = safer_openat(-1, dir, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC);
471 if (rv->fd == -1) { 458 if (rv->fd == -1) {
472 fprintf(stderr, "Error: cannot open %s\n", rv->path); 459 fprintf(stderr, "Error: cannot open %s\n", dir);
473 exit(1); 460 exit(1);
474 } 461 }
475 462
@@ -750,10 +737,11 @@ void fs_whitelist(void) {
750 } 737 }
751 738
752 // create the link if any 739 // create the link if any
753 if (link) 740 if (link) {
754 whitelist_symlink(link, file); 741 whitelist_symlink(link, file);
742 free(link);
743 }
755 744
756 free(link);
757 free(file); 745 free(file);
758 free(entry->wparam); 746 free(entry->wparam);
759 entry->wparam = NULL; 747 entry->wparam = NULL;
diff --git a/src/firejail/join.c b/src/firejail/join.c
index bab4b830f..394bbb528 100644
--- a/src/firejail/join.c
+++ b/src/firejail/join.c
@@ -147,7 +147,7 @@ static void extract_command(int argc, char **argv, int index) {
147 } 147 }
148 148
149 // build command 149 // build command
150 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index); 150 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, index, true);
151} 151}
152 152
153static void extract_nogroups(pid_t pid) { 153static void extract_nogroups(pid_t pid) {
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index 796c42290..ae7e89741 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -31,6 +31,10 @@
31//#include <stdio.h> 31//#include <stdio.h>
32//#include <stdlib.h> 32//#include <stdlib.h>
33 33
34#ifdef HAVE_GCOV
35#include "../include/gcov_wrapper.h"
36#endif
37
34// uid/gid cache 38// uid/gid cache
35static uid_t c_uid = 0; 39static uid_t c_uid = 0;
36static char *c_uid_name = NULL; 40static char *c_uid_name = NULL;
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
index bcac1feb4..cd29d8f85 100644
--- a/src/firejail/macros.c
+++ b/src/firejail/macros.c
@@ -149,6 +149,7 @@ static char *resolve_xdg(const char *var) {
149 149
150// returns mallocated memory 150// returns mallocated memory
151static char *resolve_hardcoded(char *entries[]) { 151static char *resolve_hardcoded(char *entries[]) {
152 EUID_ASSERT();
152 char *fname; 153 char *fname;
153 struct stat s; 154 struct stat s;
154 155
diff --git a/src/firejail/main.c b/src/firejail/main.c
index d46a56627..7b32f6f07 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -44,6 +44,10 @@
44#define O_PATH 010000000 44#define O_PATH 010000000
45#endif 45#endif
46 46
47#ifdef HAVE_GCOV
48#include "../include/gcov_wrapper.h"
49#endif
50
47#ifdef __ia64__ 51#ifdef __ia64__
48/* clone(2) has a different interface on ia64, as it needs to know 52/* clone(2) has a different interface on ia64, as it needs to know
49 the size of the stack */ 53 the size of the stack */
@@ -259,8 +263,8 @@ static void init_cfg(int argc, char **argv) {
259 fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username); 263 fprintf(stderr, "Error: user %s doesn't have a user directory assigned\n", cfg.username);
260 exit(1); 264 exit(1);
261 } 265 }
266 check_homedir(pw->pw_dir);
262 cfg.homedir = clean_pathname(pw->pw_dir); 267 cfg.homedir = clean_pathname(pw->pw_dir);
263 check_homedir();
264 268
265 // initialize random number generator 269 // initialize random number generator
266 sandbox_pid = getpid(); 270 sandbox_pid = getpid();
@@ -862,12 +866,11 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
862char *guess_shell(void) { 866char *guess_shell(void) {
863 const char *shell; 867 const char *shell;
864 char *retval; 868 char *retval;
865 struct stat s;
866 869
867 shell = env_get("SHELL"); 870 shell = env_get("SHELL");
868 if (shell) { 871 if (shell) {
869 invalid_filename(shell, 0); // no globbing 872 invalid_filename(shell, 0); // no globbing
870 if (!is_dir(shell) && strstr(shell, "..") == NULL && stat(shell, &s) == 0 && access(shell, X_OK) == 0 && 873 if (access(shell, X_OK) == 0 && !is_dir(shell) && strstr(shell, "..") == NULL &&
871 strcmp(shell, PATH_FIREJAIL) != 0) 874 strcmp(shell, PATH_FIREJAIL) != 0)
872 goto found; 875 goto found;
873 } 876 }
@@ -878,12 +881,15 @@ char *guess_shell(void) {
878 int i = 0; 881 int i = 0;
879 while (shells[i] != NULL) { 882 while (shells[i] != NULL) {
880 // access call checks as real UID/GID, not as effective UID/GID 883 // access call checks as real UID/GID, not as effective UID/GID
881 if (stat(shells[i], &s) == 0 && access(shells[i], X_OK) == 0) { 884 if (access(shells[i], X_OK) == 0) {
882 shell = shells[i]; 885 shell = shells[i];
883 break; 886 goto found;
884 } 887 }
885 i++; 888 i++;
886 } 889 }
890
891 return NULL;
892
887 found: 893 found:
888 retval = strdup(shell); 894 retval = strdup(shell);
889 if (!retval) 895 if (!retval)
@@ -1490,8 +1496,11 @@ int main(int argc, char **argv, char **envp) {
1490 arg_rlimit_nproc = 1; 1496 arg_rlimit_nproc = 1;
1491 } 1497 }
1492 else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) { 1498 else if (strncmp(argv[i], "--rlimit-fsize=", 15) == 0) {
1493 check_unsigned(argv[i] + 15, "Error: invalid rlimit"); 1499 cfg.rlimit_fsize = parse_arg_size(argv[i] + 15);
1494 sscanf(argv[i] + 15, "%llu", &cfg.rlimit_fsize); 1500 if (cfg.rlimit_fsize == 0) {
1501 perror("Error: invalid rlimit-fsize. Only use positive numbers and k, m or g suffix.");
1502 exit(1);
1503 }
1495 arg_rlimit_fsize = 1; 1504 arg_rlimit_fsize = 1;
1496 } 1505 }
1497 else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) { 1506 else if (strncmp(argv[i], "--rlimit-sigpending=", 20) == 0) {
@@ -1500,8 +1509,11 @@ int main(int argc, char **argv, char **envp) {
1500 arg_rlimit_sigpending = 1; 1509 arg_rlimit_sigpending = 1;
1501 } 1510 }
1502 else if (strncmp(argv[i], "--rlimit-as=", 12) == 0) { 1511 else if (strncmp(argv[i], "--rlimit-as=", 12) == 0) {
1503 check_unsigned(argv[i] + 12, "Error: invalid rlimit"); 1512 cfg.rlimit_as = parse_arg_size(argv[i] + 12);
1504 sscanf(argv[i] + 12, "%llu", &cfg.rlimit_as); 1513 if (cfg.rlimit_as == 0) {
1514 perror("Error: invalid rlimit-as. Only use positive numbers and k, m or g suffix.");
1515 exit(1);
1516 }
1505 arg_rlimit_as = 1; 1517 arg_rlimit_as = 1;
1506 } 1518 }
1507 else if (strncmp(argv[i], "--ipc-namespace", 15) == 0) 1519 else if (strncmp(argv[i], "--ipc-namespace", 15) == 0)
@@ -1959,61 +1971,77 @@ int main(int argc, char **argv, char **envp) {
1959 arg_keep_dev_shm = 1; 1971 arg_keep_dev_shm = 1;
1960 } 1972 }
1961 else if (strncmp(argv[i], "--private-etc=", 14) == 0) { 1973 else if (strncmp(argv[i], "--private-etc=", 14) == 0) {
1962 if (arg_writable_etc) { 1974 if (checkcfg(CFG_PRIVATE_ETC)) {
1963 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); 1975 if (arg_writable_etc) {
1964 exit(1); 1976 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
1965 } 1977 exit(1);
1978 }
1966 1979
1967 // extract private etc list 1980 // extract private etc list
1968 if (*(argv[i] + 14) == '\0') { 1981 if (*(argv[i] + 14) == '\0') {
1969 fprintf(stderr, "Error: invalid private-etc option\n"); 1982 fprintf(stderr, "Error: invalid private-etc option\n");
1970 exit(1); 1983 exit(1);
1984 }
1985 if (cfg.etc_private_keep) {
1986 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, argv[i] + 14) < 0 )
1987 errExit("asprintf");
1988 } else
1989 cfg.etc_private_keep = argv[i] + 14;
1990 arg_private_etc = 1;
1971 } 1991 }
1972 if (cfg.etc_private_keep) { 1992 else
1973 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, argv[i] + 14) < 0 ) 1993 exit_err_feature("private-etc");
1974 errExit("asprintf");
1975 } else
1976 cfg.etc_private_keep = argv[i] + 14;
1977 arg_private_etc = 1;
1978 } 1994 }
1979 else if (strncmp(argv[i], "--private-opt=", 14) == 0) { 1995 else if (strncmp(argv[i], "--private-opt=", 14) == 0) {
1980 // extract private opt list 1996 if (checkcfg(CFG_PRIVATE_OPT)) {
1981 if (*(argv[i] + 14) == '\0') { 1997 // extract private opt list
1982 fprintf(stderr, "Error: invalid private-opt option\n"); 1998 if (*(argv[i] + 14) == '\0') {
1983 exit(1); 1999 fprintf(stderr, "Error: invalid private-opt option\n");
2000 exit(1);
2001 }
2002 if (cfg.opt_private_keep) {
2003 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, argv[i] + 14) < 0 )
2004 errExit("asprintf");
2005 } else
2006 cfg.opt_private_keep = argv[i] + 14;
2007 arg_private_opt = 1;
1984 } 2008 }
1985 if (cfg.opt_private_keep) { 2009 else
1986 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, argv[i] + 14) < 0 ) 2010 exit_err_feature("private-opt");
1987 errExit("asprintf");
1988 } else
1989 cfg.opt_private_keep = argv[i] + 14;
1990 arg_private_opt = 1;
1991 } 2011 }
1992 else if (strncmp(argv[i], "--private-srv=", 14) == 0) { 2012 else if (strncmp(argv[i], "--private-srv=", 14) == 0) {
1993 // extract private srv list 2013 if (checkcfg(CFG_PRIVATE_SRV)) {
1994 if (*(argv[i] + 14) == '\0') { 2014 // extract private srv list
1995 fprintf(stderr, "Error: invalid private-srv option\n"); 2015 if (*(argv[i] + 14) == '\0') {
1996 exit(1); 2016 fprintf(stderr, "Error: invalid private-srv option\n");
2017 exit(1);
2018 }
2019 if (cfg.srv_private_keep) {
2020 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, argv[i] + 14) < 0 )
2021 errExit("asprintf");
2022 } else
2023 cfg.srv_private_keep = argv[i] + 14;
2024 arg_private_srv = 1;
1997 } 2025 }
1998 if (cfg.srv_private_keep) { 2026 else
1999 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, argv[i] + 14) < 0 ) 2027 exit_err_feature("private-srv");
2000 errExit("asprintf");
2001 } else
2002 cfg.srv_private_keep = argv[i] + 14;
2003 arg_private_srv = 1;
2004 } 2028 }
2005 else if (strncmp(argv[i], "--private-bin=", 14) == 0) { 2029 else if (strncmp(argv[i], "--private-bin=", 14) == 0) {
2006 // extract private bin list 2030 if (checkcfg(CFG_PRIVATE_BIN)) {
2007 if (*(argv[i] + 14) == '\0') { 2031 // extract private bin list
2008 fprintf(stderr, "Error: invalid private-bin option\n"); 2032 if (*(argv[i] + 14) == '\0') {
2009 exit(1); 2033 fprintf(stderr, "Error: invalid private-bin option\n");
2034 exit(1);
2035 }
2036 if (cfg.bin_private_keep) {
2037 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, argv[i] + 14) < 0 )
2038 errExit("asprintf");
2039 } else
2040 cfg.bin_private_keep = argv[i] + 14;
2041 arg_private_bin = 1;
2010 } 2042 }
2011 if (cfg.bin_private_keep) { 2043 else
2012 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, argv[i] + 14) < 0 ) 2044 exit_err_feature("private-bin");
2013 errExit("asprintf");
2014 } else
2015 cfg.bin_private_keep = argv[i] + 14;
2016 arg_private_bin = 1;
2017 } 2045 }
2018 else if (strncmp(argv[i], "--private-lib", 13) == 0) { 2046 else if (strncmp(argv[i], "--private-lib", 13) == 0) {
2019 if (checkcfg(CFG_PRIVATE_LIB)) { 2047 if (checkcfg(CFG_PRIVATE_LIB)) {
@@ -2801,6 +2829,11 @@ int main(int argc, char **argv, char **envp) {
2801 // build the sandbox command 2829 // build the sandbox command
2802 if (prog_index == -1 && cfg.shell) { 2830 if (prog_index == -1 && cfg.shell) {
2803 assert(cfg.command_line == NULL); // runs cfg.shell 2831 assert(cfg.command_line == NULL); // runs cfg.shell
2832 if (arg_appimage) {
2833 fprintf(stderr, "Error: no appimage archive specified\n");
2834 exit(1);
2835 }
2836
2804 cfg.window_title = cfg.shell; 2837 cfg.window_title = cfg.shell;
2805 cfg.command_name = cfg.shell; 2838 cfg.command_name = cfg.shell;
2806 } 2839 }
@@ -2808,10 +2841,11 @@ int main(int argc, char **argv, char **envp) {
2808 if (arg_debug) 2841 if (arg_debug)
2809 printf("Configuring appimage environment\n"); 2842 printf("Configuring appimage environment\n");
2810 appimage_set(cfg.command_name); 2843 appimage_set(cfg.command_name);
2811 build_appimage_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 2844 build_appimage_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, true);
2812 } 2845 }
2813 else { 2846 else {
2814 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 2847 // Only add extra quotes if we were not launched by sshd.
2848 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, !parent_sshd);
2815 } 2849 }
2816/* else { 2850/* else {
2817 fprintf(stderr, "Error: command must be specified when --shell=none used.\n"); 2851 fprintf(stderr, "Error: command must be specified when --shell=none used.\n");
@@ -2825,7 +2859,13 @@ int main(int argc, char **argv, char **envp) {
2825 2859
2826 // load the profile 2860 // load the profile
2827 if (!arg_noprofile && !custom_profile) { 2861 if (!arg_noprofile && !custom_profile) {
2828 custom_profile = profile_find_firejail(cfg.command_name, 1); 2862 if (arg_appimage) {
2863 custom_profile = appimage_find_profile(cfg.command_name);
2864 // disable shell=* for appimages
2865 arg_shell_none = 0;
2866 }
2867 else
2868 custom_profile = profile_find_firejail(cfg.command_name, 1);
2829 } 2869 }
2830 2870
2831 // use default.profile as the default 2871 // use default.profile as the default
@@ -2839,7 +2879,7 @@ int main(int argc, char **argv, char **envp) {
2839 custom_profile = profile_find_firejail(profile_name, 1); 2879 custom_profile = profile_find_firejail(profile_name, 1);
2840 2880
2841 if (!custom_profile) { 2881 if (!custom_profile) {
2842 fprintf(stderr, "Error: no default.profile installed\n"); 2882 fprintf(stderr, "Error: no %s installed\n", profile_name);
2843 exit(1); 2883 exit(1);
2844 } 2884 }
2845 2885
diff --git a/src/firejail/mountinfo.c b/src/firejail/mountinfo.c
index a700729d3..64a94bd84 100644
--- a/src/firejail/mountinfo.c
+++ b/src/firejail/mountinfo.c
@@ -22,7 +22,7 @@
22 22
23#include <fcntl.h> 23#include <fcntl.h>
24#ifndef O_PATH 24#ifndef O_PATH
25# define O_PATH 010000000 25#define O_PATH 010000000
26#endif 26#endif
27 27
28#define MAX_BUF 4096 28#define MAX_BUF 4096
@@ -153,6 +153,7 @@ MountData *get_last_mount(void) {
153 153
154// Extract the mount id from /proc/self/fdinfo and return it. 154// Extract the mount id from /proc/self/fdinfo and return it.
155int get_mount_id(const char *path) { 155int get_mount_id(const char *path) {
156 EUID_ASSERT();
156 assert(path); 157 assert(path);
157 158
158 int fd = open(path, O_PATH|O_CLOEXEC); 159 int fd = open(path, O_PATH|O_CLOEXEC);
@@ -162,7 +163,9 @@ int get_mount_id(const char *path) {
162 char *fdinfo; 163 char *fdinfo;
163 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1) 164 if (asprintf(&fdinfo, "/proc/self/fdinfo/%d", fd) == -1)
164 errExit("asprintf"); 165 errExit("asprintf");
166 EUID_ROOT();
165 FILE *fp = fopen(fdinfo, "re"); 167 FILE *fp = fopen(fdinfo, "re");
168 EUID_USER();
166 free(fdinfo); 169 free(fdinfo);
167 if (!fp) 170 if (!fp)
168 goto errexit; 171 goto errexit;
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c
index 0e153c47b..665bef73d 100644
--- a/src/firejail/no_sandbox.c
+++ b/src/firejail/no_sandbox.c
@@ -204,7 +204,7 @@ void run_no_sandbox(int argc, char **argv) {
204 // force --shell=none in order to not break firecfg symbolic links 204 // force --shell=none in order to not break firecfg symbolic links
205 arg_shell_none = 1; 205 arg_shell_none = 1;
206 206
207 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index); 207 build_cmdline(&cfg.command_line, &cfg.window_title, argc, argv, prog_index, true);
208 } 208 }
209 209
210 fwarning("an existing sandbox was detected. " 210 fwarning("an existing sandbox was detected. "
diff --git a/src/firejail/paths.c b/src/firejail/paths.c
index b800fa944..d58a9d272 100644
--- a/src/firejail/paths.c
+++ b/src/firejail/paths.c
@@ -136,7 +136,7 @@ int program_in_path(const char *program) {
136 // ('x' permission means something different for directories). 136 // ('x' permission means something different for directories).
137 // exec follows symlinks, so use stat, not lstat. 137 // exec follows symlinks, so use stat, not lstat.
138 struct stat st; 138 struct stat st;
139 if (stat(scratch, &st)) { 139 if (stat_as_user(scratch, &st)) {
140 perror(scratch); 140 perror(scratch);
141 exit(1); 141 exit(1);
142 } 142 }
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index af28cd488..7b21eb387 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -22,6 +22,11 @@
22#include "../include/syscall.h" 22#include "../include/syscall.h"
23#include <dirent.h> 23#include <dirent.h>
24#include <sys/stat.h> 24#include <sys/stat.h>
25
26#ifdef HAVE_GCOV
27#include "../include/gcov_wrapper.h"
28#endif
29
25extern char *xephyr_screen; 30extern char *xephyr_screen;
26 31
27#define MAX_READ 8192 // line buffer for profile files 32#define MAX_READ 8192 // line buffer for profile files
@@ -1275,56 +1280,69 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1275 1280
1276 // private /etc list of files and directories 1281 // private /etc list of files and directories
1277 if (strncmp(ptr, "private-etc ", 12) == 0) { 1282 if (strncmp(ptr, "private-etc ", 12) == 0) {
1278 if (arg_writable_etc) { 1283 if (checkcfg(CFG_PRIVATE_ETC)) {
1279 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); 1284 if (arg_writable_etc) {
1280 exit(1); 1285 fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n");
1281 } 1286 exit(1);
1282 if (cfg.etc_private_keep) { 1287 }
1283 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 ) 1288 if (cfg.etc_private_keep) {
1284 errExit("asprintf"); 1289 if ( asprintf(&cfg.etc_private_keep, "%s,%s", cfg.etc_private_keep, ptr + 12) < 0 )
1285 } else { 1290 errExit("asprintf");
1286 cfg.etc_private_keep = ptr + 12; 1291 } else {
1292 cfg.etc_private_keep = ptr + 12;
1293 }
1294 arg_private_etc = 1;
1287 } 1295 }
1288 arg_private_etc = 1; 1296 else
1289 1297 warning_feature_disabled("private-etc");
1290 return 0; 1298 return 0;
1291 } 1299 }
1292 1300
1293 // private /opt list of files and directories 1301 // private /opt list of files and directories
1294 if (strncmp(ptr, "private-opt ", 12) == 0) { 1302 if (strncmp(ptr, "private-opt ", 12) == 0) {
1295 if (cfg.opt_private_keep) { 1303 if (checkcfg(CFG_PRIVATE_OPT)) {
1296 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 ) 1304 if (cfg.opt_private_keep) {
1297 errExit("asprintf"); 1305 if ( asprintf(&cfg.opt_private_keep, "%s,%s", cfg.opt_private_keep, ptr + 12) < 0 )
1298 } else { 1306 errExit("asprintf");
1299 cfg.opt_private_keep = ptr + 12; 1307 } else {
1308 cfg.opt_private_keep = ptr + 12;
1309 }
1310 arg_private_opt = 1;
1300 } 1311 }
1301 arg_private_opt = 1; 1312 else
1302 1313 warning_feature_disabled("private-opt");
1303 return 0; 1314 return 0;
1304 } 1315 }
1305 1316
1306 // private /srv list of files and directories 1317 // private /srv list of files and directories
1307 if (strncmp(ptr, "private-srv ", 12) == 0) { 1318 if (strncmp(ptr, "private-srv ", 12) == 0) {
1308 if (cfg.srv_private_keep) { 1319 if (checkcfg(CFG_PRIVATE_SRV)) {
1309 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 ) 1320 if (cfg.srv_private_keep) {
1310 errExit("asprintf"); 1321 if ( asprintf(&cfg.srv_private_keep, "%s,%s", cfg.srv_private_keep, ptr + 12) < 0 )
1311 } else { 1322 errExit("asprintf");
1312 cfg.srv_private_keep = ptr + 12; 1323 } else {
1324 cfg.srv_private_keep = ptr + 12;
1325 }
1326 arg_private_srv = 1;
1313 } 1327 }
1314 arg_private_srv = 1; 1328 else
1315 1329 warning_feature_disabled("private-srv");
1316 return 0; 1330 return 0;
1317 } 1331 }
1318 1332
1319 // private /bin list of files 1333 // private /bin list of files
1320 if (strncmp(ptr, "private-bin ", 12) == 0) { 1334 if (strncmp(ptr, "private-bin ", 12) == 0) {
1321 if (cfg.bin_private_keep) { 1335 if (checkcfg(CFG_PRIVATE_BIN)) {
1322 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 ) 1336 if (cfg.bin_private_keep) {
1323 errExit("asprintf"); 1337 if ( asprintf(&cfg.bin_private_keep, "%s,%s", cfg.bin_private_keep, ptr + 12) < 0 )
1324 } else { 1338 errExit("asprintf");
1325 cfg.bin_private_keep = ptr + 12; 1339 } else {
1340 cfg.bin_private_keep = ptr + 12;
1341 }
1342 arg_private_bin = 1;
1326 } 1343 }
1327 arg_private_bin = 1; 1344 else
1345 warning_feature_disabled("private-bin");
1328 return 0; 1346 return 0;
1329 } 1347 }
1330 1348
@@ -1492,8 +1510,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1492 arg_rlimit_nproc = 1; 1510 arg_rlimit_nproc = 1;
1493 } 1511 }
1494 else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) { 1512 else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
1495 check_unsigned(ptr + 13, "Error: invalid rlimit in profile file: "); 1513 cfg.rlimit_fsize = parse_arg_size(ptr + 13);
1496 sscanf(ptr + 13, "%llu", &cfg.rlimit_fsize); 1514 if (cfg.rlimit_fsize == 0) {
1515 perror("Error: invalid rlimit-fsize in profile file. Only use positive numbers and k, m or g suffix.");
1516 exit(1);
1517 }
1497 arg_rlimit_fsize = 1; 1518 arg_rlimit_fsize = 1;
1498 } 1519 }
1499 else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) { 1520 else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
@@ -1502,8 +1523,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1502 arg_rlimit_sigpending = 1; 1523 arg_rlimit_sigpending = 1;
1503 } 1524 }
1504 else if (strncmp(ptr, "rlimit-as ", 10) == 0) { 1525 else if (strncmp(ptr, "rlimit-as ", 10) == 0) {
1505 check_unsigned(ptr + 10, "Error: invalid rlimit in profile file: "); 1526 cfg.rlimit_as = parse_arg_size(ptr + 10);
1506 sscanf(ptr + 10, "%llu", &cfg.rlimit_as); 1527 if (cfg.rlimit_as == 0) {
1528 perror("Error: invalid rlimit-as in profile file. Only use positive numbers and k, m or g suffix.");
1529 exit(1);
1530 }
1507 arg_rlimit_as = 1; 1531 arg_rlimit_as = 1;
1508 } 1532 }
1509 else { 1533 else {
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index 1b01a71c6..f8d4c2f3c 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -75,31 +75,34 @@ void pulseaudio_disable(void) {
75 closedir(dir); 75 closedir(dir);
76} 76}
77 77
78static void pulseaudio_fallback(const char *path) {
79 assert(path);
80
81 fmessage("Cannot mount tmpfs on %s/.config/pulse\n", cfg.homedir);
82 env_store_name_val("PULSE_CLIENTCONFIG", path, SETENV);
83}
84
85// disable shm in pulseaudio (issue #69) 78// disable shm in pulseaudio (issue #69)
86void pulseaudio_init(void) { 79void pulseaudio_init(void) {
87 struct stat s;
88
89 // do we have pulseaudio in the system? 80 // do we have pulseaudio in the system?
90 if (stat(PULSE_CLIENT_SYSCONF, &s) == -1) { 81 if (access(PULSE_CLIENT_SYSCONF, R_OK)) {
91 if (arg_debug) 82 if (arg_debug)
92 printf("%s not found\n", PULSE_CLIENT_SYSCONF); 83 printf("Cannot read %s\n", PULSE_CLIENT_SYSCONF);
93 return; 84 return;
94 } 85 }
95 86
87 // create ~/.config/pulse directory if not present
88 char *homeusercfg = NULL;
89 if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1)
90 errExit("asprintf");
91 if (create_empty_dir_as_user(homeusercfg, 0700))
92 fs_logger2("create", homeusercfg);
93
94 free(homeusercfg);
95 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1)
96 errExit("asprintf");
97 if (create_empty_dir_as_user(homeusercfg, 0700))
98 fs_logger2("create", homeusercfg);
99
96 // create the new user pulseaudio directory 100 // create the new user pulseaudio directory
101 // that will be mounted over ~/.config/pulse
97 if (mkdir(RUN_PULSE_DIR, 0700) == -1) 102 if (mkdir(RUN_PULSE_DIR, 0700) == -1)
98 errExit("mkdir"); 103 errExit("mkdir");
99 selinux_relabel_path(RUN_PULSE_DIR, RUN_PULSE_DIR); 104 selinux_relabel_path(RUN_PULSE_DIR, homeusercfg);
100 // mount it nosuid, noexec, nodev
101 fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0); 105 fs_remount(RUN_PULSE_DIR, MOUNT_NOEXEC, 0);
102
103 // create the new client.conf file 106 // create the new client.conf file
104 char *pulsecfg = NULL; 107 char *pulsecfg = NULL;
105 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) 108 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1)
@@ -116,37 +119,14 @@ void pulseaudio_init(void) {
116 if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) 119 if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700))
117 errExit("set_perms"); 120 errExit("set_perms");
118 121
119 // create ~/.config/pulse directory if not present
120 char *homeusercfg = NULL;
121 if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1)
122 errExit("asprintf");
123 if (create_empty_dir_as_user(homeusercfg, 0700))
124 fs_logger2("create", homeusercfg);
125
126 free(homeusercfg);
127 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1)
128 errExit("asprintf");
129 if (create_empty_dir_as_user(homeusercfg, 0700))
130 fs_logger2("create", homeusercfg);
131
132 // if ~/.config/pulse exists and there are no symbolic links, mount the new directory 122 // if ~/.config/pulse exists and there are no symbolic links, mount the new directory
133 // else set environment variable 123 // else set environment variable
124 EUID_USER();
134 int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 125 int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
126 EUID_ROOT();
135 if (fd == -1) { 127 if (fd == -1) {
136 pulseaudio_fallback(pulsecfg); 128 fwarning("not mounting tmpfs on %s\n", homeusercfg);
137 goto out; 129 env_store_name_val("PULSE_CLIENTCONFIG", pulsecfg, SETENV);
138 }
139 // confirm the actual mount destination is owned by the user
140 if (fstat(fd, &s) == -1) { // FUSE
141 if (errno != EACCES)
142 errExit("fstat");
143 close(fd);
144 pulseaudio_fallback(pulsecfg);
145 goto out;
146 }
147 if (s.st_uid != getuid()) {
148 close(fd);
149 pulseaudio_fallback(pulsecfg);
150 goto out; 130 goto out;
151 } 131 }
152 // preserve a read-only mount 132 // preserve a read-only mount
@@ -158,17 +138,13 @@ void pulseaudio_init(void) {
158 // mount via the link in /proc/self/fd 138 // mount via the link in /proc/self/fd
159 if (arg_debug) 139 if (arg_debug)
160 printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg); 140 printf("Mounting %s on %s\n", RUN_PULSE_DIR, homeusercfg);
161 char *proc; 141 if (bind_mount_path_to_fd(RUN_PULSE_DIR, fd))
162 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
163 errExit("asprintf");
164 if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0)
165 errExit("mount pulseaudio"); 142 errExit("mount pulseaudio");
166 // check /proc/self/mountinfo to confirm the mount is ok 143 // check /proc/self/mountinfo to confirm the mount is ok
167 MountData *mptr = get_last_mount(); 144 MountData *mptr = get_last_mount();
168 if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 145 if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
169 errLogExit("invalid pulseaudio mount"); 146 errLogExit("invalid pulseaudio mount");
170 fs_logger2("tmpfs", homeusercfg); 147 fs_logger2("tmpfs", homeusercfg);
171 free(proc);
172 close(fd); 148 close(fd);
173 149
174 char *p; 150 char *p;
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c
index 53e395b89..6f17231a4 100644
--- a/src/firejail/restrict_users.c
+++ b/src/firejail/restrict_users.c
@@ -104,12 +104,8 @@ static void sanitize_home(void) {
104 selinux_relabel_path(cfg.homedir, cfg.homedir); 104 selinux_relabel_path(cfg.homedir, cfg.homedir);
105 105
106 // bring back real user home directory 106 // bring back real user home directory
107 char *proc; 107 if (bind_mount_fd_to_path(fd, cfg.homedir))
108 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
109 errExit("asprintf");
110 if (mount(proc, cfg.homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
111 errExit("mount bind"); 108 errExit("mount bind");
112 free(proc);
113 close(fd); 109 close(fd);
114 110
115 if (!arg_private) 111 if (!arg_private)
@@ -154,12 +150,8 @@ static void sanitize_run(void) {
154 selinux_relabel_path(runuser, runuser); 150 selinux_relabel_path(runuser, runuser);
155 151
156 // bring back real run/user/$UID directory 152 // bring back real run/user/$UID directory
157 char *proc; 153 if (bind_mount_fd_to_path(fd, runuser))
158 if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1)
159 errExit("asprintf");
160 if (mount(proc, runuser, NULL, MS_BIND|MS_REC, NULL) < 0)
161 errExit("mount bind"); 154 errExit("mount bind");
162 free(proc);
163 close(fd); 155 close(fd);
164 156
165 fs_logger2("whitelist", runuser); 157 fs_logger2("whitelist", runuser);
@@ -246,6 +238,11 @@ static void sanitize_passwd(void) {
246 // mount-bind tne new password file 238 // mount-bind tne new password file
247 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) 239 if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0)
248 errExit("mount"); 240 errExit("mount");
241
242 // blacklist RUN_PASSWD_FILE
243 if (mount(RUN_RO_FILE, RUN_PASSWD_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
244 errExit("mount");
245
249 fs_logger("create /etc/passwd"); 246 fs_logger("create /etc/passwd");
250 247
251 return; 248 return;
@@ -376,6 +373,11 @@ static void sanitize_group(void) {
376 // mount-bind tne new group file 373 // mount-bind tne new group file
377 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) 374 if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0)
378 errExit("mount"); 375 errExit("mount");
376
377 // blacklist RUN_GROUP_FILE
378 if (mount(RUN_RO_FILE, RUN_GROUP_FILE, "none", MS_BIND, "mode=400,gid=0") < 0)
379 errExit("mount");
380
379 fs_logger("create /etc/group"); 381 fs_logger("create /etc/group");
380 382
381 return; 383 return;
diff --git a/src/firejail/rlimit.c b/src/firejail/rlimit.c
index 78f00bc63..2666486fa 100644
--- a/src/firejail/rlimit.c
+++ b/src/firejail/rlimit.c
@@ -21,6 +21,10 @@
21#include <sys/time.h> 21#include <sys/time.h>
22#include <sys/resource.h> 22#include <sys/resource.h>
23 23
24#ifdef HAVE_GCOV
25#include "../include/gcov_wrapper.h"
26#endif
27
24void set_rlimits(void) { 28void set_rlimits(void) {
25 EUID_ASSERT(); 29 EUID_ASSERT();
26 // resource limits 30 // resource limits
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index a6bcec02c..99abce57f 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -49,6 +49,9 @@
49#include <sys/apparmor.h> 49#include <sys/apparmor.h>
50#endif 50#endif
51 51
52#ifdef HAVE_GCOV
53#include "../include/gcov_wrapper.h"
54#endif
52 55
53static int force_nonewprivs = 0; 56static int force_nonewprivs = 0;
54 57
@@ -837,6 +840,7 @@ int sandbox(void* sandbox_arg) {
837 // private mode 840 // private mode
838 //**************************** 841 //****************************
839 if (arg_private) { 842 if (arg_private) {
843 EUID_USER();
840 if (cfg.home_private) { // --private= 844 if (cfg.home_private) { // --private=
841 if (cfg.chrootdir) 845 if (cfg.chrootdir)
842 fwarning("private=directory feature is disabled in chroot\n"); 846 fwarning("private=directory feature is disabled in chroot\n");
@@ -855,6 +859,7 @@ int sandbox(void* sandbox_arg) {
855 } 859 }
856 else // --private 860 else // --private
857 fs_private(); 861 fs_private();
862 EUID_ROOT();
858 } 863 }
859 864
860 if (arg_private_dev) 865 if (arg_private_dev)
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c
index d2c0bcc19..37111324a 100644
--- a/src/firejail/sbox.c
+++ b/src/firejail/sbox.c
@@ -265,7 +265,6 @@ int sbox_run(unsigned filtermask, int num, ...) {
265} 265}
266 266
267int sbox_run_v(unsigned filtermask, char * const arg[]) { 267int sbox_run_v(unsigned filtermask, char * const arg[]) {
268 EUID_ROOT();
269 assert(arg); 268 assert(arg);
270 269
271 if (arg_debug) { 270 if (arg_debug) {
@@ -285,6 +284,7 @@ int sbox_run_v(unsigned filtermask, char * const arg[]) {
285 if (child < 0) 284 if (child < 0)
286 errExit("fork"); 285 errExit("fork");
287 if (child == 0) { 286 if (child == 0) {
287 EUID_ROOT();
288 sbox_do_exec_v(filtermask, arg); 288 sbox_do_exec_v(filtermask, arg);
289 } 289 }
290 290
diff --git a/src/firejail/selinux.c b/src/firejail/selinux.c
index 06189d7f6..6969e7a3d 100644
--- a/src/firejail/selinux.c
+++ b/src/firejail/selinux.c
@@ -19,10 +19,13 @@
19*/ 19*/
20#if HAVE_SELINUX 20#if HAVE_SELINUX
21#include "firejail.h" 21#include "firejail.h"
22
23#include <sys/types.h> 22#include <sys/types.h>
24#include <sys/stat.h> 23#include <sys/stat.h>
24
25#include <fcntl.h> 25#include <fcntl.h>
26#ifndef O_PATH
27#define O_PATH 010000000
28#endif
26 29
27#include <selinux/context.h> 30#include <selinux/context.h>
28#include <selinux/label.h> 31#include <selinux/label.h>
@@ -52,8 +55,9 @@ void selinux_relabel_path(const char *path, const char *inside_path)
52 if (!label_hnd) 55 if (!label_hnd)
53 errExit("selabel_open"); 56 errExit("selabel_open");
54 57
55 /* Open the file as O_PATH, to pin it while we determine and adjust the label */ 58 /* Open the file as O_PATH, to pin it while we determine and adjust the label
56 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); 59 * Defeat symlink races by not allowing symbolic links */
60 fd = safer_openat(-1, path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
57 if (fd < 0) 61 if (fd < 0)
58 return; 62 return;
59 if (fstat(fd, &st) < 0) 63 if (fstat(fd, &st) < 0)
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 6a7318c4b..55998e6c3 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -31,6 +31,9 @@
31#include <sys/wait.h> 31#include <sys/wait.h>
32#include <limits.h> 32#include <limits.h>
33 33
34#include <string.h>
35#include <ctype.h>
36
34#include <fcntl.h> 37#include <fcntl.h>
35#ifndef O_PATH 38#ifndef O_PATH
36#define O_PATH 010000000 39#define O_PATH 010000000
@@ -41,11 +44,53 @@
41#include <linux/openat2.h> 44#include <linux/openat2.h>
42#endif 45#endif
43 46
47#ifdef HAVE_GCOV
48#include "../include/gcov_wrapper.h"
49#endif
50
44#define MAX_GROUPS 1024 51#define MAX_GROUPS 1024
45#define MAXBUF 4098 52#define MAXBUF 4098
46#define EMPTY_STRING ("") 53#define EMPTY_STRING ("")
47 54
48 55
56long long unsigned parse_arg_size(char *str) {
57 long long unsigned result = 0;
58 int len = strlen(str);
59 sscanf(str, "%llu", &result);
60
61 char suffix = *(str + len - 1);
62 if (!isdigit(suffix) && (suffix == 'k' || suffix == 'm' || suffix == 'g')) {
63 len -= 1;
64 }
65
66 /* checks for is value valid positive number */
67 for (int i = 0; i < len; i++) {
68 if (!isdigit(*(str+i))) {
69 return 0;
70 }
71 }
72
73 if (isdigit(suffix))
74 return result;
75
76 switch (suffix) {
77 case 'k':
78 result *= 1024;
79 break;
80 case 'm':
81 result *= 1024 * 1024;
82 break;
83 case 'g':
84 result *= 1024 * 1024 * 1024;
85 break;
86 default:
87 result = 0;
88 break;
89 }
90
91 return result;
92}
93
49// send the error to /var/log/auth.log and exit after a small delay 94// send the error to /var/log/auth.log and exit after a small delay
50void errLogExit(char* fmt, ...) { 95void errLogExit(char* fmt, ...) {
51 va_list args; 96 va_list args;
@@ -325,7 +370,7 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
325} 370}
326 371
327// return -1 if error, 0 if no error 372// return -1 if error, 0 if no error
328void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { 373void copy_file_as_user(const char *srcname, const char *destname, mode_t mode) {
329 pid_t child = fork(); 374 pid_t child = fork();
330 if (child < 0) 375 if (child < 0)
331 errExit("fork"); 376 errExit("fork");
@@ -333,8 +378,8 @@ void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid
333 // drop privileges 378 // drop privileges
334 drop_privs(0); 379 drop_privs(0);
335 380
336 // copy, set permissions and ownership 381 // copy, set permissions
337 int rv = copy_file(srcname, destname, uid, gid, mode); // already a regular user 382 int rv = copy_file(srcname, destname, -1, -1, mode); // already a regular user
338 if (rv) 383 if (rv)
339 fwarning("cannot copy %s\n", srcname); 384 fwarning("cannot copy %s\n", srcname);
340#ifdef HAVE_GCOV 385#ifdef HAVE_GCOV
@@ -394,11 +439,11 @@ void touch_file_as_user(const char *fname, mode_t mode) {
394 // drop privileges 439 // drop privileges
395 drop_privs(0); 440 drop_privs(0);
396 441
397 FILE *fp = fopen(fname, "wx"); 442 int fd = open(fname, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR);
398 if (fp) { 443 if (fd > -1) {
399 fprintf(fp, "\n"); 444 int err = fchmod(fd, mode);
400 SET_PERMS_STREAM(fp, -1, -1, mode); 445 (void) err;
401 fclose(fp); 446 close(fd);
402 } 447 }
403 else 448 else
404 fwarning("cannot create %s\n", fname); 449 fwarning("cannot create %s\n", fname);
@@ -417,6 +462,13 @@ int is_dir(const char *fname) {
417 if (*fname == '\0') 462 if (*fname == '\0')
418 return 0; 463 return 0;
419 464
465 int called_as_root = 0;
466 if (geteuid() == 0)
467 called_as_root = 1;
468
469 if (called_as_root)
470 EUID_USER();
471
420 // if fname doesn't end in '/', add one 472 // if fname doesn't end in '/', add one
421 int rv; 473 int rv;
422 struct stat s; 474 struct stat s;
@@ -432,6 +484,9 @@ int is_dir(const char *fname) {
432 free(tmp); 484 free(tmp);
433 } 485 }
434 486
487 if (called_as_root)
488 EUID_ROOT();
489
435 if (rv == -1) 490 if (rv == -1)
436 return 0; 491 return 0;
437 492
@@ -447,18 +502,83 @@ int is_link(const char *fname) {
447 if (*fname == '\0') 502 if (*fname == '\0')
448 return 0; 503 return 0;
449 504
450 char *dup = strdup(fname); 505 int called_as_root = 0;
451 if (!dup) 506 if (geteuid() == 0)
507 called_as_root = 1;
508
509 if (called_as_root)
510 EUID_USER();
511
512 // remove trailing '/' if any
513 char *tmp = strdup(fname);
514 if (!tmp)
452 errExit("strdup"); 515 errExit("strdup");
453 trim_trailing_slash_or_dot(dup); 516 trim_trailing_slash_or_dot(tmp);
454 517
455 char c; 518 char c;
456 ssize_t rv = readlink(dup, &c, 1); 519 ssize_t rv = readlink(tmp, &c, 1);
520 free(tmp);
521
522 if (called_as_root)
523 EUID_ROOT();
457 524
458 free(dup);
459 return (rv != -1); 525 return (rv != -1);
460} 526}
461 527
528char *realpath_as_user(const char *fname) {
529 assert(fname);
530
531 int called_as_root = 0;
532 if (geteuid() == 0)
533 called_as_root = 1;
534
535 if (called_as_root)
536 EUID_USER();
537
538 char *rv = realpath(fname, NULL);
539
540 if (called_as_root)
541 EUID_ROOT();
542
543 return rv;
544}
545
546int stat_as_user(const char *fname, struct stat *s) {
547 assert(fname);
548
549 int called_as_root = 0;
550 if (geteuid() == 0)
551 called_as_root = 1;
552
553 if (called_as_root)
554 EUID_USER();
555
556 int rv = stat(fname, s);
557
558 if (called_as_root)
559 EUID_ROOT();
560
561 return rv;
562}
563
564int lstat_as_user(const char *fname, struct stat *s) {
565 assert(fname);
566
567 int called_as_root = 0;
568 if (geteuid() == 0)
569 called_as_root = 1;
570
571 if (called_as_root)
572 EUID_USER();
573
574 int rv = lstat(fname, s);
575
576 if (called_as_root)
577 EUID_ROOT();
578
579 return rv;
580}
581
462// remove all slashes and single dots from the end of a path 582// remove all slashes and single dots from the end of a path
463// for example /foo/bar///././. -> /foo/bar 583// for example /foo/bar///././. -> /foo/bar
464void trim_trailing_slash_or_dot(char *path) { 584void trim_trailing_slash_or_dot(char *path) {
@@ -891,35 +1011,37 @@ static int remove_callback(const char *fpath, const struct stat *sb, int typefla
891 1011
892int remove_overlay_directory(void) { 1012int remove_overlay_directory(void) {
893 EUID_ASSERT(); 1013 EUID_ASSERT();
894 struct stat s;
895 sleep(1); 1014 sleep(1);
896 1015
897 char *path; 1016 char *path;
898 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1) 1017 if (asprintf(&path, "%s/.firejail", cfg.homedir) == -1)
899 errExit("asprintf"); 1018 errExit("asprintf");
900 1019
901 if (lstat(path, &s) == 0) { 1020 if (access(path, F_OK) == 0) {
902 // deal with obvious problems such as symlinks and root ownership
903 if (!S_ISDIR(s.st_mode)) {
904 if (S_ISLNK(s.st_mode))
905 fprintf(stderr, "Error: %s is a symbolic link\n", path);
906 else
907 fprintf(stderr, "Error: %s is not a directory\n", path);
908 exit(1);
909 }
910 if (s.st_uid != getuid()) {
911 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
912 exit(1);
913 }
914
915 pid_t child = fork(); 1021 pid_t child = fork();
916 if (child < 0) 1022 if (child < 0)
917 errExit("fork"); 1023 errExit("fork");
918 if (child == 0) { 1024 if (child == 0) {
919 // open ~/.firejail, fails if there is any symlink 1025 // open ~/.firejail
920 int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 1026 int fd = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC);
921 if (fd == -1) 1027 if (fd == -1) {
922 errExit("safer_openat"); 1028 fprintf(stderr, "Error: cannot open %s\n", path);
1029 exit(1);
1030 }
1031 struct stat s;
1032 if (fstat(fd, &s) == -1)
1033 errExit("fstat");
1034 if (!S_ISDIR(s.st_mode)) {
1035 if (S_ISLNK(s.st_mode))
1036 fprintf(stderr, "Error: %s is a symbolic link\n", path);
1037 else
1038 fprintf(stderr, "Error: %s is not a directory\n", path);
1039 exit(1);
1040 }
1041 if (s.st_uid != getuid()) {
1042 fprintf(stderr, "Error: %s is not owned by the current user\n", path);
1043 exit(1);
1044 }
923 // chdir to ~/.firejail 1045 // chdir to ~/.firejail
924 if (fchdir(fd) == -1) 1046 if (fchdir(fd) == -1)
925 errExit("fchdir"); 1047 errExit("fchdir");
@@ -942,7 +1064,7 @@ int remove_overlay_directory(void) {
942 // wait for the child to finish 1064 // wait for the child to finish
943 waitpid(child, NULL, 0); 1065 waitpid(child, NULL, 0);
944 // check if ~/.firejail was deleted 1066 // check if ~/.firejail was deleted
945 if (stat(path, &s) == 0) 1067 if (access(path, F_OK) == 0)
946 return 1; 1068 return 1;
947 } 1069 }
948 return 0; 1070 return 0;
@@ -975,9 +1097,8 @@ void flush_stdin(void) {
975int create_empty_dir_as_user(const char *dir, mode_t mode) { 1097int create_empty_dir_as_user(const char *dir, mode_t mode) {
976 assert(dir); 1098 assert(dir);
977 mode &= 07777; 1099 mode &= 07777;
978 struct stat s;
979 1100
980 if (stat(dir, &s)) { 1101 if (access(dir, F_OK) != 0) {
981 if (arg_debug) 1102 if (arg_debug)
982 printf("Creating empty %s directory\n", dir); 1103 printf("Creating empty %s directory\n", dir);
983 pid_t child = fork(); 1104 pid_t child = fork();
@@ -988,8 +1109,8 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
988 drop_privs(0); 1109 drop_privs(0);
989 1110
990 if (mkdir(dir, mode) == 0) { 1111 if (mkdir(dir, mode) == 0) {
991 if (chmod(dir, mode) == -1) 1112 int err = chmod(dir, mode);
992 {;} // do nothing 1113 (void) err;
993 } 1114 }
994 else if (arg_debug) 1115 else if (arg_debug)
995 printf("Directory %s not created: %s\n", dir, strerror(errno)); 1116 printf("Directory %s not created: %s\n", dir, strerror(errno));
@@ -999,7 +1120,7 @@ int create_empty_dir_as_user(const char *dir, mode_t mode) {
999 _exit(0); 1120 _exit(0);
1000 } 1121 }
1001 waitpid(child, NULL, 0); 1122 waitpid(child, NULL, 0);
1002 if (stat(dir, &s) == 0) 1123 if (access(dir, F_OK) == 0)
1003 return 1; 1124 return 1;
1004 } 1125 }
1005 return 0; 1126 return 0;
@@ -1110,20 +1231,35 @@ unsigned extract_timeout(const char *str) {
1110} 1231}
1111 1232
1112void disable_file_or_dir(const char *fname) { 1233void disable_file_or_dir(const char *fname) {
1234 assert(geteuid() == 0);
1235 assert(fname);
1236
1237 EUID_USER();
1238 int fd = open(fname, O_PATH|O_CLOEXEC);
1239 EUID_ROOT();
1240 if (fd < 0)
1241 return;
1242
1113 struct stat s; 1243 struct stat s;
1114 if (stat(fname, &s) != -1) { 1244 if (fstat(fd, &s) < 0) { // FUSE
1115 if (arg_debug) 1245 if (errno != EACCES)
1116 printf("blacklist %s\n", fname); 1246 errExit("fstat");
1117 if (is_dir(fname)) { 1247 close(fd);
1118 if (mount(RUN_RO_DIR, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1248 return;
1119 errExit("disable directory"); 1249 }
1120 } 1250
1121 else { 1251 if (arg_debug)
1122 if (mount(RUN_RO_FILE, fname, "none", MS_BIND, "mode=400,gid=0") < 0) 1252 printf("blacklist %s\n", fname);
1123 errExit("disable file"); 1253 if (S_ISDIR(s.st_mode)) {
1124 } 1254 if (bind_mount_path_to_fd(RUN_RO_DIR, fd) < 0)
1125 fs_logger2("blacklist", fname); 1255 errExit("disable directory");
1256 }
1257 else {
1258 if (bind_mount_path_to_fd(RUN_RO_FILE, fd) < 0)
1259 errExit("disable file");
1126 } 1260 }
1261 close(fd);
1262 fs_logger2("blacklist", fname);
1127} 1263}
1128 1264
1129void disable_file_path(const char *path, const char *file) { 1265void disable_file_path(const char *path, const char *file) {
@@ -1210,6 +1346,60 @@ int safer_openat(int dirfd, const char *path, int flags) {
1210 return fd; 1346 return fd;
1211} 1347}
1212 1348
1349int remount_by_fd(int dst, unsigned long mountflags) {
1350 char *proc;
1351 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1352 errExit("asprintf");
1353
1354 int rv = mount(NULL, proc, NULL, mountflags|MS_BIND|MS_REMOUNT, NULL);
1355 if (rv < 0 && arg_debug)
1356 printf("Failed mount: %s\n", strerror(errno));
1357
1358 free(proc);
1359 return rv;
1360}
1361
1362int bind_mount_by_fd(int src, int dst) {
1363 char *proc_src, *proc_dst;
1364 if (asprintf(&proc_src, "/proc/self/fd/%d", src) < 0 ||
1365 asprintf(&proc_dst, "/proc/self/fd/%d", dst) < 0)
1366 errExit("asprintf");
1367
1368 int rv = mount(proc_src, proc_dst, NULL, MS_BIND|MS_REC, NULL);
1369 if (rv < 0 && arg_debug)
1370 printf("Failed mount: %s\n", strerror(errno));
1371
1372 free(proc_src);
1373 free(proc_dst);
1374 return rv;
1375}
1376
1377int bind_mount_fd_to_path(int src, const char *destname) {
1378 char *proc;
1379 if (asprintf(&proc, "/proc/self/fd/%d", src) < 0)
1380 errExit("asprintf");
1381
1382 int rv = mount(proc, destname, NULL, MS_BIND|MS_REC, NULL);
1383 if (rv < 0 && arg_debug)
1384 printf("Failed mount: %s\n", strerror(errno));
1385
1386 free(proc);
1387 return rv;
1388}
1389
1390int bind_mount_path_to_fd(const char *srcname, int dst) {
1391 char *proc;
1392 if (asprintf(&proc, "/proc/self/fd/%d", dst) < 0)
1393 errExit("asprintf");
1394
1395 int rv = mount(srcname, proc, NULL, MS_BIND|MS_REC, NULL);
1396 if (rv < 0 && arg_debug)
1397 printf("Failed mount: %s\n", strerror(errno));
1398
1399 free(proc);
1400 return rv;
1401}
1402
1213int has_handler(pid_t pid, int signal) { 1403int has_handler(pid_t pid, int signal) {
1214 if (signal > 0 && signal <= SIGRTMAX) { 1404 if (signal > 0 && signal <= SIGRTMAX) {
1215 char *fname; 1405 char *fname;
@@ -1319,14 +1509,14 @@ static int has_link(const char *dir) {
1319 return 0; 1509 return 0;
1320} 1510}
1321 1511
1322void check_homedir(void) { 1512void check_homedir(const char *dir) {
1323 assert(cfg.homedir); 1513 assert(dir);
1324 if (cfg.homedir[0] != '/') { 1514 if (dir[0] != '/') {
1325 fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir); 1515 fprintf(stderr, "Error: invalid user directory \"%s\"\n", cfg.homedir);
1326 exit(1); 1516 exit(1);
1327 } 1517 }
1328 // symlinks are rejected in many places 1518 // symlinks are rejected in many places
1329 if (has_link(cfg.homedir)) { 1519 if (has_link(dir)) {
1330 fprintf(stderr, "No full support for symbolic links in path of user directory.\n" 1520 fprintf(stderr, "No full support for symbolic links in path of user directory.\n"
1331 "Please provide resolved path in password database (/etc/passwd).\n\n"); 1521 "Please provide resolved path in password database (/etc/passwd).\n\n");
1332 } 1522 }
diff --git a/src/firejail/x11.c b/src/firejail/x11.c
index f4f093138..896aa2fd3 100644
--- a/src/firejail/x11.c
+++ b/src/firejail/x11.c
@@ -204,7 +204,6 @@ static int random_display_number(void) {
204void x11_start_xvfb(int argc, char **argv) { 204void x11_start_xvfb(int argc, char **argv) {
205 EUID_ASSERT(); 205 EUID_ASSERT();
206 int i; 206 int i;
207 struct stat s;
208 pid_t jail = 0; 207 pid_t jail = 0;
209 pid_t server = 0; 208 pid_t server = 0;
210 209
@@ -348,7 +347,7 @@ void x11_start_xvfb(int argc, char **argv) {
348 // wait for x11 server to start 347 // wait for x11 server to start
349 while (++n < 10) { 348 while (++n < 10) {
350 sleep(1); 349 sleep(1);
351 if (stat(fname, &s) == 0) 350 if (access(fname, F_OK) == 0)
352 break; 351 break;
353 }; 352 };
354 353
@@ -427,7 +426,6 @@ static char *extract_setting(int argc, char **argv, const char *argument) {
427void x11_start_xephyr(int argc, char **argv) { 426void x11_start_xephyr(int argc, char **argv) {
428 EUID_ASSERT(); 427 EUID_ASSERT();
429 int i; 428 int i;
430 struct stat s;
431 pid_t jail = 0; 429 pid_t jail = 0;
432 pid_t server = 0; 430 pid_t server = 0;
433 431
@@ -586,7 +584,7 @@ void x11_start_xephyr(int argc, char **argv) {
586 // wait for x11 server to start 584 // wait for x11 server to start
587 while (++n < 10) { 585 while (++n < 10) {
588 sleep(1); 586 sleep(1);
589 if (stat(fname, &s) == 0) 587 if (access(fname, F_OK) == 0)
590 break; 588 break;
591 }; 589 };
592 590
@@ -701,7 +699,6 @@ static char * get_title_arg_str() {
701static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { 699static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) {
702 EUID_ASSERT(); 700 EUID_ASSERT();
703 int i; 701 int i;
704 struct stat s;
705 pid_t client = 0; 702 pid_t client = 0;
706 pid_t server = 0; 703 pid_t server = 0;
707 704
@@ -818,7 +815,7 @@ static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv,
818 // wait for x11 server to start 815 // wait for x11 server to start
819 while (++n < 10) { 816 while (++n < 10) {
820 sleep(1); 817 sleep(1);
821 if (stat(fname, &s) == 0) 818 if (access(fname, F_OK) == 0)
822 break; 819 break;
823 } 820 }
824 821
@@ -1207,14 +1204,13 @@ void x11_xorg(void) {
1207 fmessage("Generating a new .Xauthority file\n"); 1204 fmessage("Generating a new .Xauthority file\n");
1208 mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid()); 1205 mkdir_attr(RUN_XAUTHORITY_SEC_DIR, 0700, getuid(), getgid());
1209 // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR 1206 // create new Xauthority file in RUN_XAUTHORITY_SEC_DIR
1207 EUID_USER();
1210 char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX"; 1208 char tmpfname[] = RUN_XAUTHORITY_SEC_DIR "/.Xauth-XXXXXX";
1211 int fd = mkstemp(tmpfname); 1209 int fd = mkstemp(tmpfname);
1212 if (fd == -1) { 1210 if (fd == -1) {
1213 fprintf(stderr, "Error: cannot create .Xauthority file\n"); 1211 fprintf(stderr, "Error: cannot create .Xauthority file\n");
1214 exit(1); 1212 exit(1);
1215 } 1213 }
1216 if (fchown(fd, getuid(), getgid()) == -1)
1217 errExit("chown");
1218 close(fd); 1214 close(fd);
1219 1215
1220 // run xauth 1216 // run xauth
@@ -1224,16 +1220,14 @@ void x11_xorg(void) {
1224 else 1220 else
1225 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname, 1221 sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 7, RUN_XAUTH_FILE, "-f", tmpfname,
1226 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted"); 1222 "generate", display, "MIT-MAGIC-COOKIE-1", "untrusted");
1227 // remove xauth copy
1228 unlink(RUN_XAUTH_FILE);
1229 1223
1230 // ensure there is already a file ~/.Xauthority, so that bind-mount below will work. 1224 // ensure there is already a file ~/.Xauthority, so that bind-mount below will work.
1231 char *dest; 1225 char *dest;
1232 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) 1226 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
1233 errExit("asprintf"); 1227 errExit("asprintf");
1234 if (lstat(dest, &s) == -1) { 1228 if (access(dest, F_OK) == -1) {
1235 touch_file_as_user(dest, 0600); 1229 touch_file_as_user(dest, 0600);
1236 if (stat(dest, &s) == -1) { 1230 if (access(dest, F_OK) == -1) {
1237 fprintf(stderr, "Error: cannot create %s\n", dest); 1231 fprintf(stderr, "Error: cannot create %s\n", dest);
1238 exit(1); 1232 exit(1);
1239 } 1233 }
@@ -1276,21 +1270,16 @@ void x11_xorg(void) {
1276 // mount via the link in /proc/self/fd 1270 // mount via the link in /proc/self/fd
1277 if (arg_debug) 1271 if (arg_debug)
1278 printf("Mounting %s on %s\n", tmpfname, dest); 1272 printf("Mounting %s on %s\n", tmpfname, dest);
1279 char *proc_src, *proc_dst; 1273 EUID_ROOT();
1280 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1) 1274 if (bind_mount_by_fd(src, dst)) {
1281 errExit("asprintf");
1282 if (asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
1283 errExit("asprintf");
1284 if (mount(proc_src, proc_dst, NULL, MS_BIND, NULL) == -1) {
1285 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n"); 1275 fprintf(stderr, "Error: cannot mount the new .Xauthority file\n");
1286 exit(1); 1276 exit(1);
1287 } 1277 }
1278 EUID_USER();
1288 // check /proc/self/mountinfo to confirm the mount is ok 1279 // check /proc/self/mountinfo to confirm the mount is ok
1289 MountData *mptr = get_last_mount(); 1280 MountData *mptr = get_last_mount();
1290 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) 1281 if (strcmp(mptr->dir, dest) != 0 || strcmp(mptr->fstype, "tmpfs") != 0)
1291 errLogExit("invalid .Xauthority mount"); 1282 errLogExit("invalid .Xauthority mount");
1292 free(proc_src);
1293 free(proc_dst);
1294 close(src); 1283 close(src);
1295 close(dst); 1284 close(dst);
1296 1285
@@ -1301,8 +1290,11 @@ void x11_xorg(void) {
1301 if (envar) { 1290 if (envar) {
1302 char *rp = realpath(envar, NULL); 1291 char *rp = realpath(envar, NULL);
1303 if (rp) { 1292 if (rp) {
1304 if (strcmp(rp, dest) != 0) 1293 if (strcmp(rp, dest) != 0) {
1294 EUID_ROOT();
1305 disable_file_or_dir(rp); 1295 disable_file_or_dir(rp);
1296 EUID_USER();
1297 }
1306 free(rp); 1298 free(rp);
1307 } 1299 }
1308 } 1300 }
@@ -1311,9 +1303,13 @@ void x11_xorg(void) {
1311 free(dest); 1303 free(dest);
1312 1304
1313 // mask RUN_XAUTHORITY_SEC_DIR 1305 // mask RUN_XAUTHORITY_SEC_DIR
1306 EUID_ROOT();
1314 if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0) 1307 if (mount("tmpfs", RUN_XAUTHORITY_SEC_DIR, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755,gid=0") < 0)
1315 errExit("mounting tmpfs"); 1308 errExit("mounting tmpfs");
1316 fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR); 1309 fs_logger2("tmpfs", RUN_XAUTHORITY_SEC_DIR);
1310
1311 // cleanup
1312 unlink(RUN_XAUTH_FILE);
1317#endif 1313#endif
1318} 1314}
1319 1315
@@ -1336,6 +1332,8 @@ void fs_x11(void) {
1336 return; 1332 return;
1337 } 1333 }
1338 1334
1335 // the mount source is under control of the user, so be careful and
1336 // mount without following symbolic links, using a file descriptor
1339 char *x11file; 1337 char *x11file;
1340 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1) 1338 if (asprintf(&x11file, "/tmp/.X11-unix/X%d", display) == -1)
1341 errExit("asprintf"); 1339 errExit("asprintf");
@@ -1344,10 +1342,10 @@ void fs_x11(void) {
1344 free(x11file); 1342 free(x11file);
1345 return; 1343 return;
1346 } 1344 }
1347 struct stat x11stat; 1345 struct stat s3;
1348 if (fstat(src, &x11stat) < 0) 1346 if (fstat(src, &s3) < 0)
1349 errExit("fstat"); 1347 errExit("fstat");
1350 if (!S_ISSOCK(x11stat.st_mode)) { 1348 if (!S_ISSOCK(s3.st_mode)) {
1351 close(src); 1349 close(src);
1352 free(x11file); 1350 free(x11file);
1353 return; 1351 return;
@@ -1360,6 +1358,7 @@ void fs_x11(void) {
1360 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME, 1358 MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_STRICTATIME,
1361 "mode=1777,uid=0,gid=0") < 0) 1359 "mode=1777,uid=0,gid=0") < 0)
1362 errExit("mounting tmpfs on /tmp/.X11-unix"); 1360 errExit("mounting tmpfs on /tmp/.X11-unix");
1361 selinux_relabel_path("/tmp/.X11-unix", "/tmp/.X11-unix");
1363 fs_logger("tmpfs /tmp/.X11-unix"); 1362 fs_logger("tmpfs /tmp/.X11-unix");
1364 1363
1365 // create an empty root-owned file which will have the desired socket bind-mounted over it 1364 // create an empty root-owned file which will have the desired socket bind-mounted over it
@@ -1367,14 +1366,8 @@ void fs_x11(void) {
1367 if (dst < 0) 1366 if (dst < 0)
1368 errExit("open"); 1367 errExit("open");
1369 1368
1370 char *proc_src, *proc_dst; 1369 if (bind_mount_by_fd(src, dst))
1371 if (asprintf(&proc_src, "/proc/self/fd/%d", src) == -1 ||
1372 asprintf(&proc_dst, "/proc/self/fd/%d", dst) == -1)
1373 errExit("asprintf");
1374 if (mount(proc_src, proc_dst, NULL, MS_BIND | MS_REC, NULL) < 0)
1375 errExit("mount bind"); 1370 errExit("mount bind");
1376 free(proc_src);
1377 free(proc_dst);
1378 close(src); 1371 close(src);
1379 close(dst); 1372 close(dst);
1380 fs_logger2("whitelist", x11file); 1373 fs_logger2("whitelist", x11file);
diff --git a/src/firemon/interface.c b/src/firemon/interface.c
index e04b6f431..372cdee41 100644
--- a/src/firemon/interface.c
+++ b/src/firemon/interface.c
@@ -33,6 +33,10 @@
33//#include <net/route.h> 33//#include <net/route.h>
34//#include <linux/if_bridge.h> 34//#include <linux/if_bridge.h>
35 35
36#ifdef HAVE_GCOV
37#include "../include/gcov_wrapper.h"
38#endif
39
36// print IP addresses for all interfaces 40// print IP addresses for all interfaces
37static void net_ifprint(void) { 41static void net_ifprint(void) {
38 uint32_t ip; 42 uint32_t ip;
diff --git a/src/firemon/netstats.c b/src/firemon/netstats.c
index 850959eb3..bc951aa26 100644
--- a/src/firemon/netstats.c
+++ b/src/firemon/netstats.c
@@ -24,6 +24,10 @@
24#include <sys/stat.h> 24#include <sys/stat.h>
25#include <unistd.h> 25#include <unistd.h>
26 26
27#ifdef HAVE_GCOV
28#include "../include/gcov_wrapper.h"
29#endif
30
27#define MAXBUF 4096 31#define MAXBUF 4096
28 32
29// ip -s link: device stats 33// ip -s link: device stats
diff --git a/src/firemon/procevent.c b/src/firemon/procevent.c
index 8085d2d29..79f487582 100644
--- a/src/firemon/procevent.c
+++ b/src/firemon/procevent.c
@@ -30,6 +30,10 @@
30#include <fcntl.h> 30#include <fcntl.h>
31#include <sys/uio.h> 31#include <sys/uio.h>
32 32
33#ifdef HAVE_GCOV
34#include "../include/gcov_wrapper.h"
35#endif
36
33#define PIDS_BUFLEN 4096 37#define PIDS_BUFLEN 4096
34#define SERVER_PORT 889 // 889-899 is left unassigned by IANA 38#define SERVER_PORT 889 // 889-899 is left unassigned by IANA
35 39
diff --git a/src/firemon/top.c b/src/firemon/top.c
index a25e3c0d8..d0f911e60 100644
--- a/src/firemon/top.c
+++ b/src/firemon/top.c
@@ -24,6 +24,10 @@
24#include <sys/stat.h> 24#include <sys/stat.h>
25#include <unistd.h> 25#include <unistd.h>
26 26
27#ifdef HAVE_GCOV
28#include "../include/gcov_wrapper.h"
29#endif
30
27static unsigned pgs_rss = 0; 31static unsigned pgs_rss = 0;
28static unsigned pgs_shared = 0; 32static unsigned pgs_shared = 0;
29static unsigned clocktick = 0; 33static unsigned clocktick = 0;
diff --git a/src/include/gcov_wrapper.h b/src/include/gcov_wrapper.h
new file mode 100644
index 000000000..2f409309d
--- /dev/null
+++ b/src/include/gcov_wrapper.h
@@ -0,0 +1,40 @@
1/*
2 * Copyright (C) 2021 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#ifndef GCOV_WRAPPER_H
22#define GCOV_WRAPPER_H
23
24#include <gcov.h>
25
26/*
27 * __gcov_flush was removed on gcc 11.1.0 (as it's no longer needed), but it
28 * appears to be the safe/"correct" way to do things on previous versions (as
29 * it ensured proper locking, which is now done elsewhere). Thus, keep using
30 * it in the code and ensure that it exists, in order to support gcc <11.1.0
31 * and gcc >=11.1.0, respectively.
32 */
33#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1)
34static void __gcov_flush(void) {
35 __gcov_dump();
36 __gcov_reset();
37}
38#endif
39
40#endif /* GCOV_WRAPPER_H */
diff --git a/src/jailcheck/access.c b/src/jailcheck/access.c
index c18d64a82..3c2f46495 100644
--- a/src/jailcheck/access.c
+++ b/src/jailcheck/access.c
@@ -36,7 +36,7 @@ void access_setup(const char *directory) {
36 assert(user_home_dir); 36 assert(user_home_dir);
37 37
38 if (files_cnt >= MAX_TEST_FILES) { 38 if (files_cnt >= MAX_TEST_FILES) {
39 fprintf(stderr, "Error: maximum number of test directories exceded\n"); 39 fprintf(stderr, "Error: maximum number of test directories exceeded\n");
40 exit(1); 40 exit(1);
41 } 41 }
42 42
diff --git a/src/jailcheck/jailcheck.h b/src/jailcheck/jailcheck.h
index 32be1c978..be3104da3 100644
--- a/src/jailcheck/jailcheck.h
+++ b/src/jailcheck/jailcheck.h
@@ -53,6 +53,8 @@ void apparmor_test(pid_t pid);
53// seccomp.c 53// seccomp.c
54void seccomp_test(pid_t pid); 54void seccomp_test(pid_t pid);
55 55
56// network.c
57void network_test(void);
56// utils.c 58// utils.c
57char *get_sudo_user(void); 59char *get_sudo_user(void);
58char *get_homedir(const char *user, uid_t *uid, gid_t *gid); 60char *get_homedir(const char *user, uid_t *uid, gid_t *gid);
diff --git a/src/jailcheck/main.c b/src/jailcheck/main.c
index 4d642bf96..812ac5808 100644
--- a/src/jailcheck/main.c
+++ b/src/jailcheck/main.c
@@ -157,6 +157,7 @@ int main(int argc, char **argv) {
157 seccomp_test(pid); 157 seccomp_test(pid);
158 fflush(0); 158 fflush(0);
159 159
160 // filesystem tests
160 pid_t child = fork(); 161 pid_t child = fork();
161 if (child == -1) 162 if (child == -1)
162 errExit("fork"); 163 errExit("fork");
@@ -185,6 +186,28 @@ int main(int argc, char **argv) {
185 } 186 }
186 int status; 187 int status;
187 wait(&status); 188 wait(&status);
189
190 // network test
191 child = fork();
192 if (child == -1)
193 errExit("fork");
194 if (child == 0) {
195 int rv = join_namespace(pid, "net");
196 if (rv == 0)
197 network_test();
198 else {
199 printf(" Error: I cannot join the process network stack\n");
200 exit(1);
201 }
202
203 // drop privileges in order not to trigger cleanup()
204 if (setgid(user_gid) != 0)
205 errExit("setgid");
206 if (setuid(user_uid) != 0)
207 errExit("setuid");
208 return 0;
209 }
210 wait(&status);
188 } 211 }
189 } 212 }
190 213
diff --git a/src/jailcheck/network.c b/src/jailcheck/network.c
new file mode 100644
index 000000000..636344e77
--- /dev/null
+++ b/src/jailcheck/network.c
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2014-2021 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "jailcheck.h"
21#include <netdb.h>
22#include <arpa/inet.h>
23#include <ifaddrs.h>
24#include <net/if.h>
25#include <linux/connector.h>
26#include <linux/netlink.h>
27#include <linux/if_link.h>
28#include <linux/sockios.h>
29#include <sys/ioctl.h>
30
31
32void network_test(void) {
33 // I am root running in a network namespace
34 struct ifaddrs *ifaddr, *ifa;
35 int found = 0;
36
37 // walk through the linked list
38 if (getifaddrs(&ifaddr) == -1)
39 errExit("getifaddrs");
40 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
41 if (strcmp(ifa->ifa_name, "lo") == 0)
42 continue;
43 found = 1;
44 break;
45 }
46
47 freeifaddrs(ifaddr);
48
49 if (found)
50 printf(" Networking: enabled\n");
51 else
52 printf(" Networking: disabled\n");
53}
54
55
56
diff --git a/src/jailcheck/sysfiles.c b/src/jailcheck/sysfiles.c
index caeb580af..9a0d6350e 100644
--- a/src/jailcheck/sysfiles.c
+++ b/src/jailcheck/sysfiles.c
@@ -34,7 +34,7 @@ void sysfiles_setup(const char *file) {
34 assert(file); 34 assert(file);
35 35
36 if (files_cnt >= MAX_TEST_FILES) { 36 if (files_cnt >= MAX_TEST_FILES) {
37 fprintf(stderr, "Error: maximum number of system test files exceded\n"); 37 fprintf(stderr, "Error: maximum number of system test files exceeded\n");
38 exit(1); 38 exit(1);
39 } 39 }
40 40
diff --git a/src/lib/ldd_utils.c b/src/lib/ldd_utils.c
index cd60d74e4..c5dde85b0 100644
--- a/src/lib/ldd_utils.c
+++ b/src/lib/ldd_utils.c
@@ -50,7 +50,7 @@ int is_lib_64(const char *exe) {
50 unsigned char buf[EI_NIDENT]; 50 unsigned char buf[EI_NIDENT];
51 ssize_t len = 0; 51 ssize_t len = 0;
52 while (len < EI_NIDENT) { 52 while (len < EI_NIDENT) {
53 ssize_t sz = read(fd, buf, EI_NIDENT); 53 ssize_t sz = read(fd, buf + len, EI_NIDENT - len);
54 if (sz <= 0) 54 if (sz <= 0)
55 goto doexit; 55 goto doexit;
56 len += sz; 56 len += sz;
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt
index 6f3bef7f2..db58e0910 100644
--- a/src/man/firejail-profile.txt
+++ b/src/man/firejail-profile.txt
@@ -420,7 +420,7 @@ Make directory or file read-only.
420Make directory or file read-write. 420Make directory or file read-write.
421.TP 421.TP
422\fBtmpfs directory 422\fBtmpfs directory
423Mount an empty tmpfs filesystem on top of directory. This option is available only when running the sandbox as root. 423Mount an empty tmpfs filesystem on top of directory. Directories outside user home or not owned by the user are not allowed. Sandboxes running as root are exempt from these restrictions.
424.TP 424.TP
425\fBtracelog 425\fBtracelog
426Blacklist violations logged to syslog. 426Blacklist violations logged to syslog.
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index d4c2a5bc8..0462705c0 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -2129,6 +2129,7 @@ $ firejail --read-only=~/test --read-write=~/test/a
2129.TP 2129.TP
2130\fB\-\-rlimit-as=number 2130\fB\-\-rlimit-as=number
2131Set the maximum size of the process's virtual memory (address space) in bytes. 2131Set the maximum size of the process's virtual memory (address space) in bytes.
2132Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024).
2132 2133
2133.TP 2134.TP
2134\fB\-\-rlimit-cpu=number 2135\fB\-\-rlimit-cpu=number
@@ -2142,6 +2143,7 @@ track of CPU seconds for each process independently.
2142.TP 2143.TP
2143\fB\-\-rlimit-fsize=number 2144\fB\-\-rlimit-fsize=number
2144Set the maximum file size that can be created by a process. 2145Set the maximum file size that can be created by a process.
2146Use k(ilobyte), m(egabyte) or g(igabyte) for size suffix (base 1024).
2145.TP 2147.TP
2146\fB\-\-rlimit-nofile=number 2148\fB\-\-rlimit-nofile=number
2147Set the maximum number of files that can be opened by a process. 2149Set the maximum number of files that can be opened by a process.
@@ -2579,14 +2581,13 @@ Kill the sandbox automatically after the time has elapsed. The time is specified
2579$ firejail \-\-timeout=01:30:00 firefox 2581$ firejail \-\-timeout=01:30:00 firefox
2580.TP 2582.TP
2581\fB\-\-tmpfs=dirname 2583\fB\-\-tmpfs=dirname
2582Mount a writable tmpfs filesystem on directory dirname. This option is available only when running the sandbox as root. 2584Mount a writable tmpfs filesystem on directory dirname. Directories outside user home or not owned by the user are not allowed. Sandboxes running as root are exempt from these restrictions. File globbing is supported, see \fBFILE GLOBBING\fR section for more details.
2583File globbing is supported, see \fBFILE GLOBBING\fR section for more details.
2584.br 2585.br
2585 2586
2586.br 2587.br
2587Example: 2588Example:
2588.br 2589.br
2589# firejail \-\-tmpfs=/var 2590$ firejail \-\-tmpfs=~/.local/share
2590.TP 2591.TP
2591\fB\-\-top 2592\fB\-\-top
2592Monitor the most CPU-intensive sandboxes, see \fBMONITORING\fR section for more details. 2593Monitor the most CPU-intensive sandboxes, see \fBMONITORING\fR section for more details.
diff --git a/src/man/jailcheck.txt b/src/man/jailcheck.txt
index c80e305cc..483f47fb9 100644
--- a/src/man/jailcheck.txt
+++ b/src/man/jailcheck.txt
@@ -23,6 +23,8 @@ them from inside the sandbox.
23.TP 23.TP
24\fB5. Seccomp test 24\fB5. Seccomp test
25.TP 25.TP
26\fB6. Networking test
27.TP
26The program is started as root using sudo. 28The program is started as root using sudo.
27 29
28.SH OPTIONS 30.SH OPTIONS
@@ -56,6 +58,8 @@ $ sudo jailcheck
56.br 58.br
57 Warning: I can run programs in /home/netblue 59 Warning: I can run programs in /home/netblue
58.br 60.br
61 Networking: disabled
62.br
59 63
60.br 64.br
612055:netblue::firejail /usr/bin/ssh -X netblue@x.y.z.net 652055:netblue::firejail /usr/bin/ssh -X netblue@x.y.z.net
@@ -64,12 +68,16 @@ $ sudo jailcheck
64.br 68.br
65 Warning: I can read ~/.ssh 69 Warning: I can read ~/.ssh
66.br 70.br
71 Networking: enabled
72.br
67 73
68.br 74.br
692186:netblue:libreoffice:firejail --appimage /opt/LibreOffice-fresh.appimage 752186:netblue:libreoffice:firejail --appimage /opt/LibreOffice-fresh.appimage
70.br 76.br
71 Virtual dirs: /tmp, /var/tmp, /dev, 77 Virtual dirs: /tmp, /var/tmp, /dev,
72.br 78.br
79 Networking: enabled
80.br
73 81
74.br 82.br
7526090:netblue::/usr/bin/firejail /opt/firefox/firefox 8326090:netblue::/usr/bin/firejail /opt/firefox/firefox
@@ -78,6 +86,8 @@ $ sudo jailcheck
78.br 86.br
79 /run/user/1000, 87 /run/user/1000,
80.br 88.br
89 Networking: enabled
90.br
81 91
82.br 92.br
8326160:netblue:tor:firejail --private=~/tor-browser_en-US ./start-tor 9326160:netblue:tor:firejail --private=~/tor-browser_en-US ./start-tor
@@ -90,6 +100,8 @@ $ sudo jailcheck
90.br 100.br
91 Warning: I can run programs in /home/netblue 101 Warning: I can run programs in /home/netblue
92.br 102.br
103 Networking: enabled
104.br
93 105
94 106
95.SH LICENSE 107.SH LICENSE
diff --git a/test/environment/rlimit-bad-profile.exp b/test/environment/rlimit-bad-profile.exp
index b838f83f4..b1572afb6 100755
--- a/test/environment/rlimit-bad-profile.exp
+++ b/test/environment/rlimit-bad-profile.exp
@@ -11,7 +11,7 @@ match_max 100000
11send -- "firejail --profile=rlimit-bad1.profile\r" 11send -- "firejail --profile=rlimit-bad1.profile\r"
12expect { 12expect {
13 timeout {puts "TESTING ERROR 4\n";exit} 13 timeout {puts "TESTING ERROR 4\n";exit}
14 "invalid rlimit" 14 "invalid rlimit-fsize in profile file. Only use positive numbers and k, m or g suffix."
15} 15}
16after 100 16after 100
17 17
diff --git a/test/environment/rlimit-bad.exp b/test/environment/rlimit-bad.exp
index 3a82ded9b..c05e14b97 100755
--- a/test/environment/rlimit-bad.exp
+++ b/test/environment/rlimit-bad.exp
@@ -10,7 +10,7 @@ match_max 100000
10send -- "firejail --rlimit-fsize=-1024\r" 10send -- "firejail --rlimit-fsize=-1024\r"
11expect { 11expect {
12 timeout {puts "TESTING ERROR 0\n";exit} 12 timeout {puts "TESTING ERROR 0\n";exit}
13 "invalid rlimit" 13 "invalid rlimit-fsize. Only use positive numbers and k, m or g suffix."
14} 14}
15after 100 15after 100
16 16
diff --git a/test/fs/fscheck-tmpfs.exp b/test/fs/fscheck-tmpfs.exp
index 8dd08aa72..78b6efb76 100755
--- a/test/fs/fscheck-tmpfs.exp
+++ b/test/fs/fscheck-tmpfs.exp
@@ -41,7 +41,7 @@ after 500
41send -- "firejail --noprofile --tmpfs=/tmp/fjtest-dir\r" 41send -- "firejail --noprofile --tmpfs=/tmp/fjtest-dir\r"
42expect { 42expect {
43 timeout {puts "TESTING ERROR 5\n";exit} 43 timeout {puts "TESTING ERROR 5\n";exit}
44 "Error" 44 "Warning: you are not allowed to mount a tmpfs"
45} 45}
46after 500 46after 500
47 47