diff options
75 files changed, 1495 insertions, 571 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 | |||
31 | MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) | 31 | MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) |
32 | MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so | 32 | MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so |
33 | COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion | 33 | COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion |
34 | MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.5 | 34 | MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 jailcheck.1 |
35 | SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp | 35 | SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp |
36 | SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 | 36 | SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 |
37 | ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) | 37 | ALL_ITEMS = $(APPS) $(SBOX_APPS) $(SBOX_APPS_NON_DUMPABLE) $(MYLIBS) |
@@ -500,6 +500,7 @@ Jean-Philippe Eisenbarth (https://github.com/jpeisenbarth) | |||
500 | - fixed spotify.profile | 500 | - fixed spotify.profile |
501 | Jeff Squyres (https://github.com/jsquyres) | 501 | Jeff Squyres (https://github.com/jsquyres) |
502 | - various manpage fixes | 502 | - various manpage fixes |
503 | - cmdline.c: optionally quote the resulting command line | ||
503 | Jericho (https://github.com/attritionorg) | 504 | Jericho (https://github.com/attritionorg) |
504 | - spelling | 505 | - spelling |
505 | Jesse Smith (https://github.com/slicer69) | 506 | Jesse Smith (https://github.com/slicer69) |
@@ -570,6 +571,8 @@ kortewegdevries (https://github.com/kortewegdevries) | |||
570 | - whitelisting evolution, kmail | 571 | - whitelisting evolution, kmail |
571 | Kristóf Marussy (https://github.com/kris7t) | 572 | Kristóf Marussy (https://github.com/kris7t) |
572 | - dns support | 573 | - dns support |
574 | kuesji koesnu (https://github.com/kuesji) | ||
575 | - unit suffixes for rlimit-fsize and rlimit-as | ||
573 | Kunal Mehta (https://github.com/legoktm) | 576 | Kunal Mehta (https://github.com/legoktm) |
574 | - converted all links to https in manpages | 577 | - converted all links to https in manpages |
575 | laniakea64 (https://github.com/laniakea64) | 578 | laniakea64 (https://github.com/laniakea64) |
@@ -126,18 +126,18 @@ $ cd firejail | |||
126 | $ ./configure && make && sudo make install-strip | 126 | $ ./configure && make && sudo make install-strip |
127 | ````` | 127 | ````` |
128 | On Debian/Ubuntu you will need to install git and gcc compiler. AppArmor | 128 | On Debian/Ubuntu you will need to install git and gcc compiler. AppArmor |
129 | development libraries and pkg-config are required when using --apparmor | 129 | development 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 | ````` |
134 | For --selinux option, add libselinux1-dev (libselinux-devel for Fedora). | 134 | For `--selinux` option, add libselinux1-dev (libselinux-devel for Fedora). |
135 | 135 | ||
136 | Detailed information on using firejail from git is available on the [wiki](https://github.com/netblue30/firejail/wiki/Using-firejail-from-git). | 136 | Detailed 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 | ||
140 | To start the sandbox, prefix your command with “firejail”: | 140 | To 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 | ````` |
148 | Run "firejail --list" in a terminal to list all active sandboxes. Example: | 148 | Run `firejail --list` in a terminal to list all active sandboxes. Example: |
149 | ````` | 149 | ````` |
150 | $ firejail --list | 150 | $ firejail --list |
151 | 1617:netblue:/usr/bin/firejail /usr/bin/firefox-esr | 151 | 1617: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 | |||
188 | You can also use this tool to get a list of syscalls needed by a program: [contrib/syscalls.sh](contrib/syscalls.sh). | 188 | You can also use this tool to get a list of syscalls needed by a program: [contrib/syscalls.sh](contrib/syscalls.sh). |
189 | 189 | ||
190 | We also keep a list of profile fixes for previous released versions in [etc-fixes](https://github.com/netblue30/firejail/tree/master/etc-fixes) directory. | 190 | We 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 | |||
334 | pcsxr, PPSSPPSDL, openmw, openmw-launcher, jami-gnome, PCSX2, bcompare, b2sum, cksum, md5sum, sha1sum, sha224sum, | 332 | pcsxr, PPSSPPSDL, openmw, openmw-launcher, jami-gnome, PCSX2, bcompare, b2sum, cksum, md5sum, sha1sum, sha224sum, |
335 | sha256sum, sha384sum, sha512sum, sum, librewold-nightly, Quodlibet, tmux, sway, alienarena, alienarena-wrapper, | 333 | sha256sum, sha384sum, sha512sum, sum, librewold-nightly, Quodlibet, tmux, sway, alienarena, alienarena-wrapper, |
336 | ballbuster, ballbuster-wrapper, colorful, colorful-wrapper, gl-117, gl-117-wrapper, glaxium, glaxium-wrapper, | 334 | ballbuster, ballbuster-wrapper, colorful, colorful-wrapper, gl-117, gl-117-wrapper, glaxium, glaxium-wrapper, |
337 | pinball, pinball-wrapper, etr-wrapper, neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, firedragon | 335 | pinball, pinball-wrapper, etr-wrapper, neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, firedragon, |
338 | neochat, node, nvm, cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer, links2, xlinks2 | 336 | neochat, node, nvm, cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer, links2, xlinks2, googler, ddgr, |
337 | tin | ||
@@ -37,7 +37,7 @@ firejail (0.9.65) baseline; urgency=low | |||
37 | * glaxium-wrapper, pinball, pinball-wrapper, etr-wrapper, firedragon | 37 | * glaxium-wrapper, pinball, pinball-wrapper, etr-wrapper, firedragon |
38 | * neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, neochat, | 38 | * neverball-wrapper, neverputt-wrapper, supertuxkart-wrapper, neochat, |
39 | * cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer | 39 | * cargo, LibreCAD, blobby, funnyboat, pipe-viewer, gtk-pipe-viewer |
40 | * links2, xlinks2 | 40 | * links2, xlinks2, googler, ddgr, tin |
41 | -- netblue30 <netblue30@yahoo.com> Wed, 2 Jun 2021 09:00:00 -0500 | 41 | -- netblue30 <netblue30@yahoo.com> Wed, 2 Jun 2021 09:00:00 -0500 |
42 | 42 | ||
43 | firejail (0.9.64.4) baseline; urgency=low | 43 | firejail (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* | |||
39 | blacklist ${HOME}/.Wolfram Research | 39 | blacklist ${HOME}/.Wolfram Research |
40 | blacklist ${HOME}/.ZAP | 40 | blacklist ${HOME}/.ZAP |
41 | blacklist ${HOME}/.abook | 41 | blacklist ${HOME}/.abook |
42 | blacklist ${HOME}/.addressbook | ||
43 | blacklist ${HOME}/.alpine-smime | ||
42 | blacklist ${HOME}/.aMule | 44 | blacklist ${HOME}/.aMule |
43 | blacklist ${HOME}/.android | 45 | blacklist ${HOME}/.android |
44 | blacklist ${HOME}/.anydesk | 46 | blacklist ${HOME}/.anydesk |
@@ -810,6 +812,7 @@ blacklist ${HOME}/.netactview | |||
810 | blacklist ${HOME}/.neverball | 812 | blacklist ${HOME}/.neverball |
811 | blacklist ${HOME}/.newsbeuter | 813 | blacklist ${HOME}/.newsbeuter |
812 | blacklist ${HOME}/.newsboat | 814 | blacklist ${HOME}/.newsboat |
815 | blacklist ${HOME}/.newsrc | ||
813 | blacklist ${HOME}/.nicotine | 816 | blacklist ${HOME}/.nicotine |
814 | blacklist ${HOME}/.node-gyp | 817 | blacklist ${HOME}/.node-gyp |
815 | blacklist ${HOME}/.npm | 818 | blacklist ${HOME}/.npm |
@@ -830,6 +833,14 @@ blacklist ${HOME}/.paradoxinteractive | |||
830 | blacklist ${HOME}/.parallelrealities/blobwars | 833 | blacklist ${HOME}/.parallelrealities/blobwars |
831 | blacklist ${HOME}/.pcsxr | 834 | blacklist ${HOME}/.pcsxr |
832 | blacklist ${HOME}/.penguin-command | 835 | blacklist ${HOME}/.penguin-command |
836 | blacklist ${HOME}/.pine-crash | ||
837 | blacklist ${HOME}/.pine-debug1 | ||
838 | blacklist ${HOME}/.pine-debug2 | ||
839 | blacklist ${HOME}/.pine-debug3 | ||
840 | blacklist ${HOME}/.pine-debug4 | ||
841 | blacklist ${HOME}/.pine-interrupted-mail | ||
842 | blacklist ${HOME}/.pinerc | ||
843 | blacklist ${HOME}/.pinercex | ||
833 | blacklist ${HOME}/.pingus | 844 | blacklist ${HOME}/.pingus |
834 | blacklist ${HOME}/.pioneer | 845 | blacklist ${HOME}/.pioneer |
835 | blacklist ${HOME}/.purple | 846 | blacklist ${HOME}/.purple |
@@ -867,6 +878,7 @@ blacklist ${HOME}/.teeworlds | |||
867 | blacklist ${HOME}/.texlive20* | 878 | blacklist ${HOME}/.texlive20* |
868 | blacklist ${HOME}/.thunderbird | 879 | blacklist ${HOME}/.thunderbird |
869 | blacklist ${HOME}/.tilp | 880 | blacklist ${HOME}/.tilp |
881 | blacklist ${HOME}/.tin | ||
870 | blacklist ${HOME}/.tooling | 882 | blacklist ${HOME}/.tooling |
871 | blacklist ${HOME}/.tor-browser* | 883 | blacklist ${HOME}/.tor-browser* |
872 | blacklist ${HOME}/.torcs | 884 | blacklist ${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 | ||
4 | quiet | ||
5 | # Persistent local customizations | ||
6 | include alpine.local | ||
7 | # Persistent global definitions | ||
8 | include 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 | |||
13 | noblacklist /var/mail | ||
14 | noblacklist /var/spool/mail | ||
15 | noblacklist ${DOCUMENTS} | ||
16 | noblacklist ${HOME}/.addressbook | ||
17 | noblacklist ${HOME}/.alpine-smime | ||
18 | noblacklist ${HOME}/.mailcap | ||
19 | noblacklist ${HOME}/.mh_profile | ||
20 | noblacklist ${HOME}/.mime.types | ||
21 | noblacklist ${HOME}/.newsrc | ||
22 | noblacklist ${HOME}/.pine-crash | ||
23 | noblacklist ${HOME}/.pine-debug1 | ||
24 | noblacklist ${HOME}/.pine-debug2 | ||
25 | noblacklist ${HOME}/.pine-debug3 | ||
26 | noblacklist ${HOME}/.pine-debug4 | ||
27 | noblacklist ${HOME}/.pine-interrupted-mail | ||
28 | noblacklist ${HOME}/.pinerc | ||
29 | noblacklist ${HOME}/.pinercex | ||
30 | noblacklist ${HOME}/.signature | ||
31 | noblacklist ${HOME}/mail | ||
32 | |||
33 | blacklist /tmp/.X11-unix | ||
34 | blacklist ${RUNUSER}/wayland-* | ||
35 | |||
36 | include disable-common.inc | ||
37 | include disable-devel.inc | ||
38 | include disable-exec.inc | ||
39 | include disable-interpreters.inc | ||
40 | include disable-passwdmgr.inc | ||
41 | include disable-programs.inc | ||
42 | include disable-shell.inc | ||
43 | include 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 | ||
63 | whitelist /var/mail | ||
64 | whitelist /var/spool/mail | ||
65 | #include whitelist-common.inc | ||
66 | include whitelist-runuser-common.inc | ||
67 | include whitelist-usr-share-common.inc | ||
68 | include whitelist-var-common.inc | ||
69 | |||
70 | apparmor | ||
71 | caps.drop all | ||
72 | ipc-namespace | ||
73 | machine-id | ||
74 | netfilter | ||
75 | no3d | ||
76 | nodvd | ||
77 | nogroups | ||
78 | noinput | ||
79 | nonewprivs | ||
80 | noroot | ||
81 | nosound | ||
82 | notv | ||
83 | nou2f | ||
84 | novideo | ||
85 | protocol unix,inet,inet6 | ||
86 | seccomp | ||
87 | seccomp.block-secondary | ||
88 | shell none | ||
89 | tracelog | ||
90 | |||
91 | disable-mnt | ||
92 | private-bin alpine | ||
93 | private-cache | ||
94 | private-dev | ||
95 | private-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 | ||
96 | private-tmp | ||
97 | writable-run-user | ||
98 | writable-var | ||
99 | |||
100 | dbus-user none | ||
101 | dbus-system none | ||
102 | |||
103 | memory-deny-write-execute | ||
104 | read-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 | ||
4 | quiet | ||
5 | # Persistent local customizations | ||
6 | include alpinef.local | ||
7 | # Persistent global definitions | ||
8 | # added by included profile | ||
9 | #include globals.local | ||
10 | |||
11 | private-bin alpinef | ||
12 | |||
13 | # Redirect | ||
14 | include 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 |
37 | whitelist /usr/share/pkgconfig | ||
37 | include whitelist-runuser-common.inc | 38 | include whitelist-runuser-common.inc |
38 | include whitelist-usr-share-common.inc | 39 | include whitelist-usr-share-common.inc |
39 | include whitelist-var-common.inc | 40 | include 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 | ||
43 | apparmor | 44 | apparmor |
44 | caps.keep sys_admin,sys_chroot | 45 | caps.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 | ||
4 | quiet | ||
5 | # Persistent local customizations | ||
6 | include ddgr.local | ||
7 | # Persistent global definitions | ||
8 | include globals.local | ||
9 | |||
10 | private-bin ddgr | ||
11 | |||
12 | # Redirect | ||
13 | include 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 | ||
5 | include googler-common.local | ||
6 | # Persistent global definitions | ||
7 | # added by caller profile | ||
8 | #include globals.local | ||
9 | |||
10 | blacklist /tmp/.X11-unix | ||
11 | blacklist ${RUNUSER} | ||
12 | |||
13 | noblacklist ${HOME}/.w3m | ||
14 | |||
15 | # Allow /bin/sh (blacklisted by disable-shell.inc) | ||
16 | include allow-bin-sh.inc | ||
17 | # Allow python (blacklisted by disable-interpreters.inc) | ||
18 | include allow-python3.inc | ||
19 | |||
20 | include disable-common.inc | ||
21 | include disable-devel.inc | ||
22 | include disable-exec.inc | ||
23 | include disable-interpreters.inc | ||
24 | include disable-passwdmgr.inc | ||
25 | include disable-programs.inc | ||
26 | include disable-shell.inc | ||
27 | include disable-xdg.inc | ||
28 | |||
29 | whitelist ${HOME}/.w3m | ||
30 | include whitelist-usr-share-common.inc | ||
31 | include whitelist-var-common.inc | ||
32 | |||
33 | apparmor | ||
34 | caps.drop all | ||
35 | ipc-namespace | ||
36 | machine-id | ||
37 | netfilter | ||
38 | no3d | ||
39 | nodvd | ||
40 | nogroups | ||
41 | noinput | ||
42 | nonewprivs | ||
43 | noroot | ||
44 | nosound | ||
45 | notv | ||
46 | nou2f | ||
47 | novideo | ||
48 | protocol unix,inet,inet6 | ||
49 | seccomp | ||
50 | seccomp.block-secondary | ||
51 | shell none | ||
52 | tracelog | ||
53 | |||
54 | disable-mnt | ||
55 | private-bin env,python3*,sh,w3m | ||
56 | private-cache | ||
57 | private-dev | ||
58 | private-etc ca-certificates,crypto-policies,host.conf,hostname,hosts,nsswitch.conf,pki,protocols,resolv.conf,rpc,services,ssl | ||
59 | private-tmp | ||
60 | |||
61 | dbus-user none | ||
62 | dbus-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 | ||
4 | quiet | ||
5 | # Persistent local customizations | ||
6 | include googler.local | ||
7 | # Persistent global definitions | ||
8 | include globals.local | ||
9 | |||
10 | private-bin googler | ||
11 | |||
12 | # Redirect | ||
13 | include 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 | ||
5 | include mcomix.local | ||
6 | # Persistent global definitions | ||
7 | include globals.local | ||
8 | |||
9 | noblacklist ${HOME}/.config/mcomix | ||
10 | noblacklist ${HOME}/.local/share/mcomix | ||
11 | noblacklist ${DOCUMENTS} | ||
12 | |||
13 | # Allow /bin/sh (blacklisted by disable-shell.inc) | ||
14 | include allow-bin-sh.inc | ||
15 | |||
16 | # Allow python (blacklisted by disable-interpreters.inc) | ||
17 | # mcomix <= 1.2 uses python2 | ||
18 | include allow-python2.inc | ||
19 | include allow-python3.inc | ||
20 | |||
21 | include disable-common.inc | ||
22 | include disable-devel.inc | ||
23 | include disable-exec.inc | ||
24 | include disable-interpreters.inc | ||
25 | include disable-passwdmgr.inc | ||
26 | include disable-programs.inc | ||
27 | include disable-shell.inc | ||
28 | include disable-write-mnt.inc | ||
29 | include disable-xdg.inc | ||
30 | |||
31 | mkdir ${HOME}/.config/mcomix | ||
32 | mkdir ${HOME}/.local/share/mcomix | ||
33 | whitelist /usr/share/mcomix | ||
34 | include whitelist-usr-share-common.inc | ||
35 | include whitelist-var-common.inc | ||
36 | include whitelist-runuser-common.inc | ||
37 | |||
38 | apparmor | ||
39 | caps.drop all | ||
40 | machine-id | ||
41 | net none | ||
42 | nodvd | ||
43 | nogroups | ||
44 | noinput | ||
45 | nonewprivs | ||
46 | noroot | ||
47 | nosound | ||
48 | notv | ||
49 | nou2f | ||
50 | novideo | ||
51 | protocol unix | ||
52 | seccomp | ||
53 | seccomp.block-secondary | ||
54 | shell none | ||
55 | tracelog | ||
56 | |||
57 | # mcomix <= 1.2 uses python2 | ||
58 | private-bin 7z,lha,mcomix,mutool,python*,rar,sh,unrar,unzip | ||
59 | private-cache | ||
60 | private-dev | ||
61 | # mcomix <= 1.2 uses gtk-2.0 | ||
62 | private-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 | ||
63 | private-tmp | ||
64 | |||
65 | dbus-user none | ||
66 | dbus-system none | ||
67 | |||
68 | read-only ${HOME} | ||
69 | read-write ${HOME}/.config/mcomix | ||
70 | read-write ${HOME}/.local/share/mcomix | ||
71 | #to allow ${HOME}/.local/share/recently-used.xbel | ||
72 | read-write ${HOME}/.local/share | ||
73 | # used by mcomix <= 1.2, tip, make a symbolic link to .cache/thumbnails | ||
74 | read-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 | |||
31 | include whitelist-usr-share-common.inc | 31 | include whitelist-usr-share-common.inc |
32 | include whitelist-var-common.inc | 32 | include whitelist-var-common.inc |
33 | 33 | ||
34 | apparmor | ||
35 | caps.drop all | 34 | caps.drop all |
36 | netfilter | 35 | netfilter |
37 | nodvd | 36 | nodvd |
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 | ||
5 | include qcomicbook.local | ||
6 | # Persistent global definitions | ||
7 | include globals.local | ||
8 | |||
9 | noblacklist ${HOME}/.cache/PawelStolowski | ||
10 | noblacklist ${HOME}/.config/PawelStolowski | ||
11 | noblacklist ${HOME}/.local/share/PawelStolowski | ||
12 | noblacklist ${DOCUMENTS} | ||
13 | |||
14 | # Allow /bin/sh (blacklisted by disable-shell.inc) | ||
15 | include allow-bin-sh.inc | ||
16 | |||
17 | include disable-common.inc | ||
18 | include disable-devel.inc | ||
19 | include disable-exec.inc | ||
20 | include disable-interpreters.inc | ||
21 | include disable-passwdmgr.inc | ||
22 | include disable-programs.inc | ||
23 | include disable-shell.inc | ||
24 | include disable-write-mnt.inc | ||
25 | include disable-xdg.inc | ||
26 | |||
27 | mkdir ${HOME}/.cache/PawelStolowski | ||
28 | mkdir ${HOME}/.config/PawelStolowski | ||
29 | mkdir ${HOME}/.local/share/PawelStolowski | ||
30 | whitelist /usr/share/qcomicbook | ||
31 | include whitelist-runuser-common.inc | ||
32 | include whitelist-usr-share-common.inc | ||
33 | include whitelist-var-common.inc | ||
34 | |||
35 | apparmor | ||
36 | caps.drop all | ||
37 | machine-id | ||
38 | net none | ||
39 | nodvd | ||
40 | nogroups | ||
41 | noinput | ||
42 | nonewprivs | ||
43 | noroot | ||
44 | nosound | ||
45 | notv | ||
46 | nou2f | ||
47 | novideo | ||
48 | protocol unix | ||
49 | seccomp | ||
50 | seccomp.block-secondary | ||
51 | shell none | ||
52 | tracelog | ||
53 | |||
54 | private-bin 7z,7zr,qcomicbook,rar,sh,tar,unace,unrar,unzip | ||
55 | private-cache | ||
56 | private-dev | ||
57 | private-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 | ||
58 | private-tmp | ||
59 | |||
60 | dbus-user none | ||
61 | dbus-system none | ||
62 | |||
63 | read-only ${HOME} | ||
64 | read-write ${HOME}/.cache/PawelStolowski | ||
65 | read-write ${HOME}/.config/PawelStolowski | ||
66 | read-write ${HOME}/.local/share/PawelStolowski | ||
67 | #to allow ${HOME}/.local/share/recently-used.xbel | ||
68 | read-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 | ||
6 | include rtin.local | ||
7 | |||
8 | include 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 | ||
5 | include tin.local | ||
6 | # Persistent global definitions | ||
7 | include globals.local | ||
8 | |||
9 | noblacklist ${HOME}/.newsrc | ||
10 | noblacklist ${HOME}/.tin | ||
11 | |||
12 | blacklist /tmp/.X11-unix | ||
13 | blacklist ${RUNUSER} | ||
14 | blacklist /usr/libexec | ||
15 | |||
16 | include disable-common.inc | ||
17 | include disable-devel.inc | ||
18 | include disable-exec.inc | ||
19 | include disable-interpreters.inc | ||
20 | include disable-passwdmgr.inc | ||
21 | include disable-programs.inc | ||
22 | include disable-shell.inc | ||
23 | include disable-xdg.inc | ||
24 | |||
25 | mkdir ${HOME}/.tin | ||
26 | mkfile ${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 | ||
33 | include whitelist-runuser-common.inc | ||
34 | include whitelist-usr-share-common.inc | ||
35 | include whitelist-var-common.inc | ||
36 | |||
37 | apparmor | ||
38 | caps.drop all | ||
39 | ipc-namespace | ||
40 | machine-id | ||
41 | netfilter | ||
42 | no3d | ||
43 | nodvd | ||
44 | nogroups | ||
45 | noinput | ||
46 | nonewprivs | ||
47 | noroot | ||
48 | nosound | ||
49 | notv | ||
50 | nou2f | ||
51 | novideo | ||
52 | protocol inet,inet6 | ||
53 | seccomp | ||
54 | seccomp.block-secondary | ||
55 | shell none | ||
56 | tracelog | ||
57 | |||
58 | disable-mnt | ||
59 | private-bin rtin,tin | ||
60 | private-cache | ||
61 | private-dev | ||
62 | private-etc passwd,resolv.conf,terminfo,tin | ||
63 | private-lib terminfo | ||
64 | private-tmp | ||
65 | |||
66 | dbus-user none | ||
67 | dbus-system none | ||
68 | |||
69 | memory-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 |
7 | include globals.local | 7 | include globals.local |
8 | 8 | ||
9 | # tuxguitar fails to launch | ||
10 | ignore noexec ${HOME} | ||
11 | |||
9 | noblacklist ${HOME}/.tuxguitar* | 12 | noblacklist ${HOME}/.tuxguitar* |
10 | noblacklist ${DOCUMENTS} | 13 | noblacklist ${DOCUMENTS} |
11 | noblacklist ${MUSIC} | 14 | noblacklist ${MUSIC} |
@@ -41,6 +44,3 @@ tracelog | |||
41 | 44 | ||
42 | private-dev | 45 | private-dev |
43 | private-tmp | 46 | private-tmp |
44 | |||
45 | # noexec ${HOME} - tuxguitar may fail to launch | ||
46 | noexec /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 | |||
17 | blacklist /tmp/.X11-unix | 17 | blacklist /tmp/.X11-unix |
18 | blacklist ${RUNUSER}/wayland-* | 18 | blacklist ${RUNUSER}/wayland-* |
19 | 19 | ||
20 | # Allow /bin/sh (blacklisted by disable-shell.inc) | ||
21 | include allow-bin-sh.inc | ||
22 | |||
23 | # Allow perl (blacklisted by disable-interpreters.inc) | ||
20 | include allow-perl.inc | 24 | include allow-perl.inc |
21 | 25 | ||
22 | include disable-common.inc | 26 | include disable-common.inc |
23 | include disable-devel.inc | 27 | include disable-devel.inc |
28 | include disable-exec.inc | ||
24 | include disable-interpreters.inc | 29 | include disable-interpreters.inc |
25 | include disable-passwdmgr.inc | 30 | include disable-passwdmgr.inc |
26 | include disable-programs.inc | 31 | include disable-programs.inc |
32 | include disable-shell.inc | ||
27 | include disable-xdg.inc | 33 | include disable-xdg.inc |
28 | 34 | ||
35 | mkdir ${HOME}/.w3m | ||
36 | whitelist /usr/share/w3m | ||
37 | whitelist ${DOWNLOADS} | ||
38 | whitelist ${HOME}/.w3m | ||
29 | include whitelist-runuser-common.inc | 39 | include whitelist-runuser-common.inc |
40 | include whitelist-usr-share-common.inc | ||
41 | include whitelist-var-common.inc | ||
30 | 42 | ||
31 | caps.drop all | 43 | caps.drop all |
44 | ipc-namespace | ||
45 | machine-id | ||
32 | netfilter | 46 | netfilter |
33 | no3d | 47 | no3d |
34 | nodvd | 48 | nodvd |
@@ -45,8 +59,14 @@ seccomp | |||
45 | shell none | 59 | shell none |
46 | tracelog | 60 | tracelog |
47 | 61 | ||
48 | # private-bin w3m | 62 | disable-mnt |
63 | private-bin perl,sh,w3m | ||
49 | private-cache | 64 | private-cache |
50 | private-dev | 65 | private-dev |
51 | private-etc alternatives,ca-certificates,crypto-policies,pki,resolv.conf,ssl | 66 | private-etc alternatives,ca-certificates,crypto-policies,mailcap,nsswitch.conf,pki,resolv.conf,ssl |
52 | private-tmp | 67 | private-tmp |
68 | |||
69 | dbus-user none | ||
70 | dbus-system none | ||
71 | |||
72 | memory-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 | |||
11 | include disable-common.inc | 11 | include disable-common.inc |
12 | include disable-programs.inc | 12 | include disable-programs.inc |
13 | 13 | ||
14 | whitelist /usr/share/weechat | ||
14 | include whitelist-usr-share-common.inc | 15 | include whitelist-usr-share-common.inc |
15 | include whitelist-var-common.inc | 16 | include 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 | |||
17 | include disable-passwdmgr.inc | 17 | include disable-passwdmgr.inc |
18 | include disable-programs.inc | 18 | include disable-programs.inc |
19 | include disable-shell.inc | 19 | include disable-shell.inc |
20 | include disable-write-mnt.inc | ||
20 | include disable-xdg.inc | 21 | include disable-xdg.inc |
21 | 22 | ||
22 | mkdir ${HOME}/.config/zathura | 23 | mkdir ${HOME}/.config/zathura |
23 | mkdir ${HOME}/.local/share/zathura | 24 | mkdir ${HOME}/.local/share/zathura |
24 | whitelist /usr/share/doc | 25 | whitelist /usr/share/doc |
25 | whitelist /usr/share/zathura | 26 | whitelist /usr/share/zathura |
27 | include whitelist-runuser-common.inc | ||
26 | include whitelist-usr-share-common.inc | 28 | include whitelist-usr-share-common.inc |
27 | include whitelist-var-common.inc | 29 | include whitelist-var-common.inc |
28 | 30 | ||
@@ -41,6 +43,7 @@ nou2f | |||
41 | novideo | 43 | novideo |
42 | protocol unix | 44 | protocol unix |
43 | seccomp | 45 | seccomp |
46 | seccomp.block-secondary | ||
44 | shell none | 47 | shell none |
45 | tracelog | 48 | tracelog |
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 | |||
38 | akonadi_control | 38 | akonadi_control |
39 | akregator | 39 | akregator |
40 | alacarte | 40 | alacarte |
41 | alpine | ||
42 | alpinef | ||
41 | amarok | 43 | amarok |
42 | amule | 44 | amule |
43 | amuled | 45 | amuled |
@@ -167,6 +169,7 @@ cvlc | |||
167 | cyberfox | 169 | cyberfox |
168 | darktable | 170 | darktable |
169 | dconf-editor | 171 | dconf-editor |
172 | ddgr | ||
170 | ddgtk | 173 | ddgtk |
171 | deadbeef | 174 | deadbeef |
172 | deluge | 175 | deluge |
@@ -350,6 +353,7 @@ google-chrome-unstable | |||
350 | google-earth | 353 | google-earth |
351 | google-earth-pro | 354 | google-earth-pro |
352 | google-play-music-desktop-player | 355 | google-play-music-desktop-player |
356 | googler | ||
353 | gpa | 357 | gpa |
354 | gpicview | 358 | gpicview |
355 | gpredict | 359 | gpredict |
@@ -492,6 +496,7 @@ mathematica | |||
492 | matrix-mirage | 496 | matrix-mirage |
493 | mattermost-desktop | 497 | mattermost-desktop |
494 | mcabber | 498 | mcabber |
499 | mcomix | ||
495 | mediainfo | 500 | mediainfo |
496 | mediathekview | 501 | mediathekview |
497 | megaglest | 502 | megaglest |
@@ -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) |
654 | qbittorrent | 659 | qbittorrent |
660 | qcomicbook | ||
655 | qemu-launcher | 661 | qemu-launcher |
656 | qgis | 662 | qgis |
657 | qlipper | 663 | qlipper |
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 6b9fed765..a96415985 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 <gcov.h> | ||
33 | #endif | ||
34 | |||
31 | static char *devloop = NULL; // device file | 35 | static char *devloop = NULL; // device file |
32 | static long unsigned size = 0; // offset into appimage file | 36 | static 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 |
35 | static void err_loop(void) { | 40 | static 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 | ||
47 | int 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 | |||
41 | void appimage_set(const char *appimage) { | 76 | void 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..edc31cdea 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 <gcov.h> | ||
34 | #endif | ||
32 | 35 | ||
33 | // exit if error | 36 | // exit if error |
34 | void fs_check_chroot_dir(void) { | 37 | void 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 | ||
29 | static int cmdline_length(int argc, char **argv, int index) { | 29 | static 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 | ||
67 | static void quote_cmdline(char *command_line, char *window_title, int len, int argc, char **argv, int index) { | 68 | static 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 | ||
137 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { | 139 | void 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 | ||
164 | void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index) { | 166 | void 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); | |||
489 | void errLogExit(char* fmt, ...) __attribute__((noreturn)); | 498 | void errLogExit(char* fmt, ...) __attribute__((noreturn)); |
490 | void fwarning(char* fmt, ...); | 499 | void fwarning(char* fmt, ...); |
491 | void fmessage(char* fmt, ...); | 500 | void fmessage(char* fmt, ...); |
501 | long long unsigned parse_arg_size(char *str); | ||
492 | void drop_privs(int nogroups); | 502 | void drop_privs(int nogroups); |
493 | int mkpath_as_root(const char* path); | 503 | int mkpath_as_root(const char* path); |
494 | void extract_command_name(int index, char **argv); | 504 | void extract_command_name(int index, char **argv); |
@@ -498,11 +508,14 @@ void logargs(int argc, char **argv) ; | |||
498 | void logerr(const char *msg); | 508 | void logerr(const char *msg); |
499 | void set_nice(int inc); | 509 | void set_nice(int inc); |
500 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 510 | int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
501 | void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 511 | void copy_file_as_user(const char *srcname, const char *destname, mode_t mode); |
502 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); | 512 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); |
503 | void touch_file_as_user(const char *fname, mode_t mode); | 513 | void touch_file_as_user(const char *fname, mode_t mode); |
504 | int is_dir(const char *fname); | 514 | int is_dir(const char *fname); |
505 | int is_link(const char *fname); | 515 | int is_link(const char *fname); |
516 | char *realpath_as_user(const char *fname); | ||
517 | int stat_as_user(const char *fname, struct stat *s); | ||
518 | int lstat_as_user(const char *fname, struct stat *s); | ||
506 | void trim_trailing_slash_or_dot(char *path); | 519 | void trim_trailing_slash_or_dot(char *path); |
507 | char *line_remove_spaces(const char *buf); | 520 | char *line_remove_spaces(const char *buf); |
508 | char *split_comma(char *str); | 521 | char *split_comma(char *str); |
@@ -526,11 +539,15 @@ unsigned extract_timeout(const char *str); | |||
526 | void disable_file_or_dir(const char *fname); | 539 | void disable_file_or_dir(const char *fname); |
527 | void disable_file_path(const char *path, const char *file); | 540 | void disable_file_path(const char *path, const char *file); |
528 | int safer_openat(int dirfd, const char *path, int flags); | 541 | int safer_openat(int dirfd, const char *path, int flags); |
542 | int remount_by_fd(int dst, unsigned long mountflags); | ||
543 | int bind_mount_by_fd(int src, int dst); | ||
544 | int bind_mount_path_to_fd(const char *srcname, int dst); | ||
545 | int bind_mount_fd_to_path(int src, const char *destname); | ||
529 | int has_handler(pid_t pid, int signal); | 546 | int has_handler(pid_t pid, int signal); |
530 | void enter_network_namespace(pid_t pid); | 547 | void enter_network_namespace(pid_t pid); |
531 | int read_pid(const char *name, pid_t *pid); | 548 | int read_pid(const char *name, pid_t *pid); |
532 | pid_t require_pid(const char *name); | 549 | pid_t require_pid(const char *name); |
533 | void check_homedir(void); | 550 | void 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); | |||
796 | void print_compiletime_support(void); | 817 | void print_compiletime_support(void); |
797 | 818 | ||
798 | // appimage.c | 819 | // appimage.c |
820 | int appimage_find_profile(const char *archive); | ||
799 | void appimage_set(const char *appimage_path); | 821 | void appimage_set(const char *appimage_path); |
800 | void appimage_mount(void); | 822 | void appimage_mount(void); |
801 | void appimage_clear(void); | 823 | void appimage_clear(void); |
@@ -804,8 +826,8 @@ void appimage_clear(void); | |||
804 | long unsigned int appimage2_size(int fd); | 826 | long unsigned int appimage2_size(int fd); |
805 | 827 | ||
806 | // cmdline.c | 828 | // cmdline.c |
807 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); | 829 | void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index, bool want_extra_quotes); |
808 | void build_appimage_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); | 830 | void 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..4ae7dbfa4 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 <gcov.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 | ||
57 | typedef enum { | ||
58 | UNSUCCESSFUL, | ||
59 | SUCCESSFUL | ||
60 | } LAST_DISABLE_OPERATION; | ||
61 | LAST_DISABLE_OPERATION last_disable = UNSUCCESSFUL; | ||
62 | |||
63 | static void disable_file(OPERATION op, const char *filename) { | 61 | static 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 |
192 | static void globbing(OPERATION op, const char *pattern, const char *noblacklist[], size_t noblacklist_len) { | 210 | static 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 |
451 | void fs_tmpfs(const char *dir, unsigned check_owner) { | 471 | void 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 |
492 | static void fs_remount_simple(const char *path, OPERATION op) { | 514 | static 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 | ||
591 | out: | 623 | out: |
@@ -594,7 +626,9 @@ out: | |||
594 | 626 | ||
595 | // remount recursively; requires a resolved path | 627 | // remount recursively; requires a resolved path |
596 | static void fs_remount_rec(const char *dir, OPERATION op) { | 628 | static 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 |
633 | void fs_remount(const char *path, OPERATION op, int rec) { | 667 | void 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 |
646 | void fs_mnt(const int enforce) { | 691 | void 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 |
783 | void disable_config(void) { | 823 | void 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 | ||
809 | void fs_basic_fs(void) { | 842 | void 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 |
863 | char *fs_check_overlay_dir(const char *subdirname, int allow_reuse) { | 898 | char *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 |
1223 | void fs_private_tmp(void) { | 1259 | void 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) { | |||
187 | static void process_dev_shm(void) { | 187 | static 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..1c4f9e07e 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 | ||
37 | static void skel(const char *homedir, uid_t u, gid_t g) { | 37 | static 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 | ||
114 | static int store_xauthority(void) { | 113 | static 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 | ||
153 | static int store_asoundrc(void) { | 155 | static 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 | ||
201 | static void copy_xauthority(void) { | 206 | static 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 | ||
223 | static void copy_asoundrc(void) { | 230 | static 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) { | |||
346 | void fs_private(void) { | 355 | void 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 | //*********************************************************************************** |
440 | static char *check_dir_or_file(const char *name) { | 453 | static 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 | ||
500 | static void duplicate(char *name) { | 514 | static 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 |
537 | void fs_private_home_list(void) { | 552 | void 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); | ||
598 | close(fd); | 613 | close(fd); |
614 | EUID_USER(); | ||
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..bbc2aa938 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 <gcov.h> | ||
30 | #endif | ||
28 | 31 | ||
29 | static void check(const char *fname) { | 32 | static 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..7588f69b7 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 | ||
269 | static void globbing(const char *pattern) { | 259 | static 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 | ||
308 | static void tmpfs_topdirs(const TopDir *topdirs) { | 298 | static 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,15 @@ 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 | // checks owner if outside /home |
379 | EUID_USER(); | ||
394 | fs_private(); | 380 | fs_private(); |
381 | EUID_ROOT(); | ||
382 | } | ||
395 | 383 | ||
396 | // /run/user/$UID directory | 384 | // /run/user/$UID directory |
397 | if (tmpfs_runuser) { | 385 | if (tmpfs_runuser) { |
@@ -467,9 +455,9 @@ static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) { | |||
467 | errExit("strdup"); | 455 | errExit("strdup"); |
468 | 456 | ||
469 | // open the directory, don't follow symbolic links | 457 | // open the directory, don't follow symbolic links |
470 | rv->fd = safer_openat(-1, rv->path, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC); | 458 | rv->fd = safer_openat(-1, dir, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC); |
471 | if (rv->fd == -1) { | 459 | if (rv->fd == -1) { |
472 | fprintf(stderr, "Error: cannot open %s\n", rv->path); | 460 | fprintf(stderr, "Error: cannot open %s\n", dir); |
473 | exit(1); | 461 | exit(1); |
474 | } | 462 | } |
475 | 463 | ||
@@ -750,10 +738,11 @@ void fs_whitelist(void) { | |||
750 | } | 738 | } |
751 | 739 | ||
752 | // create the link if any | 740 | // create the link if any |
753 | if (link) | 741 | if (link) { |
754 | whitelist_symlink(link, file); | 742 | whitelist_symlink(link, file); |
743 | free(link); | ||
744 | } | ||
755 | 745 | ||
756 | free(link); | ||
757 | free(file); | 746 | free(file); |
758 | free(entry->wparam); | 747 | free(entry->wparam); |
759 | entry->wparam = NULL; | 748 | 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 | ||
153 | static void extract_nogroups(pid_t pid) { | 153 | static void extract_nogroups(pid_t pid) { |
diff --git a/src/firejail/ls.c b/src/firejail/ls.c index 796c42290..6ee557648 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 <gcov.h> | ||
36 | #endif | ||
37 | |||
34 | // uid/gid cache | 38 | // uid/gid cache |
35 | static uid_t c_uid = 0; | 39 | static uid_t c_uid = 0; |
36 | static char *c_uid_name = NULL; | 40 | static 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 |
151 | static char *resolve_hardcoded(char *entries[]) { | 151 | static 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..374afed11 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 <gcov.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) { | |||
862 | char *guess_shell(void) { | 866 | char *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. |
155 | int get_mount_id(const char *path) { | 155 | int 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..5b1478918 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 <gcov.h> | ||
28 | #endif | ||
29 | |||
25 | extern char *xephyr_screen; | 30 | extern 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 | ||
78 | static 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) |
86 | void pulseaudio_init(void) { | 79 | void 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..dd6fec972 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 <gcov.h> | ||
26 | #endif | ||
27 | |||
24 | void set_rlimits(void) { | 28 | void 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..95be3335f 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 <gcov.h> | ||
54 | #endif | ||
52 | 55 | ||
53 | static int force_nonewprivs = 0; | 56 | static 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 | ||
267 | int sbox_run_v(unsigned filtermask, char * const arg[]) { | 267 | int 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..8fcaf3f7b 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 <gcov.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 | ||
56 | long 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 |
50 | void errLogExit(char* fmt, ...) { | 95 | void 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 |
328 | void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { | 373 | void 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 | ||
528 | char *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 | |||
546 | int 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 | |||
564 | int 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 |
464 | void trim_trailing_slash_or_dot(char *path) { | 584 | void 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 | ||
892 | int remove_overlay_directory(void) { | 1012 | int 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) { | |||
975 | int create_empty_dir_as_user(const char *dir, mode_t mode) { | 1097 | int 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 | ||
1112 | void disable_file_or_dir(const char *fname) { | 1233 | void 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 | ||
1129 | void disable_file_path(const char *path, const char *file) { | 1265 | void 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 | ||
1349 | int 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 | |||
1362 | int 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 | |||
1377 | int 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 | |||
1390 | int 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 | |||
1213 | int has_handler(pid_t pid, int signal) { | 1403 | int 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 | ||
1322 | void check_homedir(void) { | 1512 | void 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) { | |||
204 | void x11_start_xvfb(int argc, char **argv) { | 204 | void 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) { | |||
427 | void x11_start_xephyr(int argc, char **argv) { | 426 | void 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() { | |||
701 | static void __attribute__((noreturn)) x11_start_xpra_old(int argc, char **argv, int display, char *display_str) { | 699 | static 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..b93d4a5a2 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 <gcov.h> | ||
38 | #endif | ||
39 | |||
36 | // print IP addresses for all interfaces | 40 | // print IP addresses for all interfaces |
37 | static void net_ifprint(void) { | 41 | static 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..23d228e26 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 <gcov.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..4e809681e 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 <gcov.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..9d6f34991 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 <gcov.h> | ||
29 | #endif | ||
30 | |||
27 | static unsigned pgs_rss = 0; | 31 | static unsigned pgs_rss = 0; |
28 | static unsigned pgs_shared = 0; | 32 | static unsigned pgs_shared = 0; |
29 | static unsigned clocktick = 0; | 33 | static unsigned clocktick = 0; |
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 |
54 | void seccomp_test(pid_t pid); | 54 | void seccomp_test(pid_t pid); |
55 | 55 | ||
56 | // network.c | ||
57 | void network_test(void); | ||
56 | // utils.c | 58 | // utils.c |
57 | char *get_sudo_user(void); | 59 | char *get_sudo_user(void); |
58 | char *get_homedir(const char *user, uid_t *uid, gid_t *gid); | 60 | char *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 | |||
32 | void 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. | |||
420 | Make directory or file read-write. | 420 | Make directory or file read-write. |
421 | .TP | 421 | .TP |
422 | \fBtmpfs directory | 422 | \fBtmpfs directory |
423 | Mount an empty tmpfs filesystem on top of directory. This option is available only when running the sandbox as root. | 423 | Mount 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 |
426 | Blacklist violations logged to syslog. | 426 | Blacklist 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 |
2131 | Set the maximum size of the process's virtual memory (address space) in bytes. | 2131 | Set the maximum size of the process's virtual memory (address space) in bytes. |
2132 | Use 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 |
2144 | Set the maximum file size that can be created by a process. | 2145 | Set the maximum file size that can be created by a process. |
2146 | Use 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 |
2147 | Set the maximum number of files that can be opened by a process. | 2149 | Set 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 |
2582 | Mount a writable tmpfs filesystem on directory dirname. This option is available only when running the sandbox as root. | 2584 | Mount 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. |
2583 | File globbing is supported, see \fBFILE GLOBBING\fR section for more details. | ||
2584 | .br | 2585 | .br |
2585 | 2586 | ||
2586 | .br | 2587 | .br |
2587 | Example: | 2588 | Example: |
2588 | .br | 2589 | .br |
2589 | # firejail \-\-tmpfs=/var | 2590 | $ firejail \-\-tmpfs=~/.local/share |
2590 | .TP | 2591 | .TP |
2591 | \fB\-\-top | 2592 | \fB\-\-top |
2592 | Monitor the most CPU-intensive sandboxes, see \fBMONITORING\fR section for more details. | 2593 | Monitor 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 | ||
26 | The program is started as root using sudo. | 28 | The 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 |
61 | 2055:netblue::firejail /usr/bin/ssh -X netblue@x.y.z.net | 65 | 2055: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 |
69 | 2186:netblue:libreoffice:firejail --appimage /opt/LibreOffice-fresh.appimage | 75 | 2186: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 |
75 | 26090:netblue::/usr/bin/firejail /opt/firefox/firefox | 83 | 26090: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 |
83 | 26160:netblue:tor:firejail --private=~/tor-browser_en-US ./start-tor | 93 | 26160: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 | |||
11 | send -- "firejail --profile=rlimit-bad1.profile\r" | 11 | send -- "firejail --profile=rlimit-bad1.profile\r" |
12 | expect { | 12 | expect { |
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 | } |
16 | after 100 | 16 | after 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 | |||
10 | send -- "firejail --rlimit-fsize=-1024\r" | 10 | send -- "firejail --rlimit-fsize=-1024\r" |
11 | expect { | 11 | expect { |
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 | } |
15 | after 100 | 15 | after 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 | |||
41 | send -- "firejail --noprofile --tmpfs=/tmp/fjtest-dir\r" | 41 | send -- "firejail --noprofile --tmpfs=/tmp/fjtest-dir\r" |
42 | expect { | 42 | expect { |
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 | } |
46 | after 500 | 46 | after 500 |
47 | 47 | ||