diff options
80 files changed, 1043 insertions, 1254 deletions
diff --git a/.github/workflows/build-extra.yml b/.github/workflows/build-extra.yml index 29f14788d..fd1f23954 100644 --- a/.github/workflows/build-extra.yml +++ b/.github/workflows/build-extra.yml | |||
@@ -32,19 +32,19 @@ jobs: | |||
32 | steps: | 32 | steps: |
33 | - uses: actions/checkout@v2 | 33 | - uses: actions/checkout@v2 |
34 | - name: configure | 34 | - name: configure |
35 | run: CC=clang-10 ./configure --enable-fatal-warnings | 35 | run: CC=clang-11 ./configure --enable-fatal-warnings |
36 | - name: make | 36 | - name: make |
37 | run: make | 37 | run: make |
38 | scan-build: | 38 | scan-build: |
39 | runs-on: ubuntu-20.04 | 39 | runs-on: ubuntu-20.04 |
40 | steps: | 40 | steps: |
41 | - uses: actions/checkout@v2 | 41 | - uses: actions/checkout@v2 |
42 | - name: install clang-tools-10 | 42 | - name: install clang-tools-11 |
43 | run: sudo apt-get install clang-tools-10 | 43 | run: sudo apt-get install clang-tools-11 |
44 | - name: configure | 44 | - name: configure |
45 | run: CC=clang-10 ./configure --enable-fatal-warnings | 45 | run: CC=clang-11 ./configure --enable-fatal-warnings |
46 | - name: scan-build | 46 | - name: scan-build |
47 | run: NO_EXTRA_CFLAGS="yes" scan-build-10 --status-bugs make | 47 | run: NO_EXTRA_CFLAGS="yes" scan-build-11 --status-bugs make |
48 | cppcheck: | 48 | cppcheck: |
49 | runs-on: ubuntu-20.04 | 49 | runs-on: ubuntu-20.04 |
50 | steps: | 50 | steps: |
diff --git a/contrib/vim/syntax/firejail.vim b/contrib/vim/syntax/firejail.vim index 65eb690ac..8775ae71d 100644 --- a/contrib/vim/syntax/firejail.vim +++ b/contrib/vim/syntax/firejail.vim | |||
@@ -49,7 +49,7 @@ syn match fjVar /\v\$\{(CFG|DESKTOP|DOCUMENTS|DOWNLOADS|HOME|MUSIC|PATH|PICTURES | |||
49 | " Generate list with: { rg -o 'strn?cmp\(ptr, "([^"]+) "' -r '$1' src/firejail/profile.c; echo private-lib; } | grep -vEx '(include|ignore|caps\.drop|caps\.keep|protocol|seccomp|seccomp\.drop|seccomp\.keep|env|rmenv|net|ip)' | sort -u | tr $'\n' '|' # private-lib is special-cased in the code and doesn't match the regex; grep-ed patterns are handled later with 'syn match nextgroup=' directives (except for include which is special-cased as a fjCommandNoCond keyword) | 49 | " Generate list with: { rg -o 'strn?cmp\(ptr, "([^"]+) "' -r '$1' src/firejail/profile.c; echo private-lib; } | grep -vEx '(include|ignore|caps\.drop|caps\.keep|protocol|seccomp|seccomp\.drop|seccomp\.keep|env|rmenv|net|ip)' | sort -u | tr $'\n' '|' # private-lib is special-cased in the code and doesn't match the regex; grep-ed patterns are handled later with 'syn match nextgroup=' directives (except for include which is special-cased as a fjCommandNoCond keyword) |
50 | syn match fjCommand /\v(bind|blacklist|blacklist-nolog|cgroup|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained | 50 | syn match fjCommand /\v(bind|blacklist|blacklist-nolog|cgroup|cpu|defaultgw|dns|hostname|hosts-file|ip6|iprange|join-or-start|mac|mkdir|mkfile|mtu|name|netfilter|netfilter6|netmask|nice|noblacklist|noexec|nowhitelist|overlay-named|private|private-bin|private-etc|private-home|private-lib|private-opt|private-srv|read-only|read-write|rlimit-as|rlimit-cpu|rlimit-fsize|rlimit-nofile|rlimit-nproc|rlimit-sigpending|timeout|tmpfs|veth-name|whitelist|xephyr-screen) / skipwhite contained |
51 | " Generate list with: rg -o 'strn?cmp\(ptr, "([^ "]*[^ ])"' -r '$1' src/firejail/profile.c | grep -vEx '(include|rlimit|quiet)' | sed -e 's/\./\\./' | sort -u | tr $'\n' '|' # include/rlimit are false positives, quiet is special-cased below | 51 | " Generate list with: rg -o 'strn?cmp\(ptr, "([^ "]*[^ ])"' -r '$1' src/firejail/profile.c | grep -vEx '(include|rlimit|quiet)' | sed -e 's/\./\\./' | sort -u | tr $'\n' '|' # include/rlimit are false positives, quiet is special-cased below |
52 | syn match fjCommand /\v(allusers|apparmor|caps|disable-mnt|ipc-namespace|keep-dev-shm|keep-var-tmp|machine-id|memory-deny-write-execute|netfilter|no3d|noautopulse|nodbus|nodvd|nogroups|nonewprivs|noroot|nosound|notv|nou2f|novideo|overlay|overlay-tmpfs|private|private-cache|private-dev|private-lib|private-tmp|seccomp|seccomp\.block-secondary|tracelog|writable-etc|writable-run-user|writable-var|writable-var-log|x11)$/ contained | 52 | syn match fjCommand /\v(allusers|apparmor|caps|disable-mnt|ipc-namespace|keep-config-pulse|keep-dev-shm|keep-var-tmp|machine-id|memory-deny-write-execute|netfilter|no3d|noautopulse|nodbus|nodvd|nogroups|noinput|nonewprivs|noroot|nosound|notv|nou2f|novideo|overlay|overlay-tmpfs|private|private-cache|private-dev|private-lib|private-tmp|seccomp|seccomp\.block-secondary|tracelog|writable-etc|writable-run-user|writable-var|writable-var-log|x11)$/ contained |
53 | syn match fjCommand /ignore / nextgroup=fjCommand,fjCommandNoCond skipwhite contained | 53 | syn match fjCommand /ignore / nextgroup=fjCommand,fjCommandNoCond skipwhite contained |
54 | syn match fjCommand /caps\.drop / nextgroup=fjCapability,fjAll skipwhite contained | 54 | syn match fjCommand /caps\.drop / nextgroup=fjCapability,fjAll skipwhite contained |
55 | syn match fjCommand /caps\.keep / nextgroup=fjCapability skipwhite contained | 55 | syn match fjCommand /caps\.keep / nextgroup=fjCapability skipwhite contained |
diff --git a/etc/firejail.config b/etc/firejail.config index 731e744dd..9dd33b5ed 100644 --- a/etc/firejail.config +++ b/etc/firejail.config | |||
@@ -116,6 +116,10 @@ | |||
116 | # Enable or disable whitelisting support, default enabled. | 116 | # Enable or disable whitelisting support, default enabled. |
117 | # whitelist yes | 117 | # whitelist yes |
118 | 118 | ||
119 | # Disable whitelist top level directories, in addition to those | ||
120 | # that are disabled out of the box. None by default; this is an example. | ||
121 | # whitelist-disable-topdir /etc,/usr/etc | ||
122 | |||
119 | # Enable or disable X11 sandboxing support, default enabled. | 123 | # Enable or disable X11 sandboxing support, default enabled. |
120 | # x11 yes | 124 | # x11 yes |
121 | 125 | ||
diff --git a/etc/inc/disable-common.inc b/etc/inc/disable-common.inc index a6dbb7403..2dc53d311 100644 --- a/etc/inc/disable-common.inc +++ b/etc/inc/disable-common.inc | |||
@@ -343,6 +343,7 @@ read-only ${HOME}/bin | |||
343 | read-only ${HOME}/.bin | 343 | read-only ${HOME}/.bin |
344 | read-only ${HOME}/.local/bin | 344 | read-only ${HOME}/.local/bin |
345 | read-only ${HOME}/.cargo/bin | 345 | read-only ${HOME}/.cargo/bin |
346 | read-only ${HOME}/.rustup | ||
346 | 347 | ||
347 | # Write-protection for desktop entries | 348 | # Write-protection for desktop entries |
348 | read-only ${HOME}/.config/menus | 349 | read-only ${HOME}/.config/menus |
@@ -367,6 +368,7 @@ blacklist ${HOME}/*.key | |||
367 | blacklist ${HOME}/.Private | 368 | blacklist ${HOME}/.Private |
368 | blacklist ${HOME}/.caff | 369 | blacklist ${HOME}/.caff |
369 | blacklist ${HOME}/.cargo/credentials | 370 | blacklist ${HOME}/.cargo/credentials |
371 | blacklist ${HOME}/.cargo/credentials.toml | ||
370 | blacklist ${HOME}/.cert | 372 | blacklist ${HOME}/.cert |
371 | blacklist ${HOME}/.config/keybase | 373 | blacklist ${HOME}/.config/keybase |
372 | blacklist ${HOME}/.davfs2/secrets | 374 | blacklist ${HOME}/.davfs2/secrets |
diff --git a/etc/inc/whitelist-runuser-common.inc b/etc/inc/whitelist-runuser-common.inc index 0a1030b34..48309ffe3 100644 --- a/etc/inc/whitelist-runuser-common.inc +++ b/etc/inc/whitelist-runuser-common.inc | |||
@@ -13,3 +13,4 @@ whitelist ${RUNUSER}/pulse/native | |||
13 | whitelist ${RUNUSER}/wayland-0 | 13 | whitelist ${RUNUSER}/wayland-0 |
14 | whitelist ${RUNUSER}/wayland-1 | 14 | whitelist ${RUNUSER}/wayland-1 |
15 | whitelist ${RUNUSER}/xauth_* | 15 | whitelist ${RUNUSER}/xauth_* |
16 | whitelist ${RUNUSER}/[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]-[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] | ||
diff --git a/etc/profile-a-l/agetpkg.profile b/etc/profile-a-l/agetpkg.profile index 7e3946454..34f59769e 100644 --- a/etc/profile-a-l/agetpkg.profile +++ b/etc/profile-a-l/agetpkg.profile | |||
@@ -32,7 +32,6 @@ caps.drop all | |||
32 | hostname agetpkg | 32 | hostname agetpkg |
33 | ipc-namespace | 33 | ipc-namespace |
34 | machine-id | 34 | machine-id |
35 | noautopulse | ||
36 | netfilter | 35 | netfilter |
37 | no3d | 36 | no3d |
38 | nodvd | 37 | nodvd |
diff --git a/etc/profile-a-l/amarok.profile b/etc/profile-a-l/amarok.profile index a15d3628d..a7caddc4c 100644 --- a/etc/profile-a-l/amarok.profile +++ b/etc/profile-a-l/amarok.profile | |||
@@ -35,14 +35,14 @@ private-dev | |||
35 | # private-etc alternatives,asound.conf,ca-certificates,crypto-policies,machine-id,pki,pulse,ssl | 35 | # private-etc alternatives,asound.conf,ca-certificates,crypto-policies,machine-id,pki,pulse,ssl |
36 | private-tmp | 36 | private-tmp |
37 | 37 | ||
38 | # If you ain't on kde-plasma you need to uncomment the following | ||
39 | dbus-user filter | 38 | dbus-user filter |
40 | dbus-user.own org.kde.amarok | 39 | dbus-user.own org.kde.amarok |
41 | #dbus-user.own org.kde.kded | ||
42 | #dbus-user.own org.kde.klauncher | ||
43 | dbus-user.own org.mpris.amarok | 40 | dbus-user.own org.mpris.amarok |
44 | dbus-user.own org.mpris.MediaPlayer2.amarok | 41 | dbus-user.own org.mpris.MediaPlayer2.amarok |
45 | dbus-user.talk org.freedesktop.Notifications | 42 | dbus-user.talk org.freedesktop.Notifications |
46 | #dbus-user.talk org.kde.knotify | ||
47 | dbus-user.talk org.kde.StatusNotifierWatcher | 43 | dbus-user.talk org.kde.StatusNotifierWatcher |
44 | # If you're not on kde-plasma add the next lines to your amarok.local. | ||
45 | #dbus-user.own org.kde.kded | ||
46 | #dbus-user.own org.kde.klauncher | ||
47 | #dbus-user.talk org.kde.knotify | ||
48 | dbus-system none | 48 | dbus-system none |
diff --git a/etc/profile-a-l/cargo.profile b/etc/profile-a-l/cargo.profile new file mode 100644 index 000000000..043fd6718 --- /dev/null +++ b/etc/profile-a-l/cargo.profile | |||
@@ -0,0 +1,72 @@ | |||
1 | # Firejail profile for cargo | ||
2 | # Description: The Rust package manager | ||
3 | # This file is overwritten after every install/update | ||
4 | quiet | ||
5 | # Persistent local customizations | ||
6 | include cargo.local | ||
7 | # Persistent global definitions | ||
8 | include globals.local | ||
9 | |||
10 | ignore noexec ${HOME} | ||
11 | ignore noexec /tmp | ||
12 | |||
13 | blacklist /tmp/.X11-unix | ||
14 | blacklist ${RUNUSER} | ||
15 | |||
16 | noblacklist ${HOME}/.cargo/credentials | ||
17 | noblacklist ${HOME}/.cargo/credentials.toml | ||
18 | |||
19 | # Allows files commonly used by IDEs | ||
20 | include allow-common-devel.inc | ||
21 | |||
22 | # Allow ssh (blacklisted by disable-common.inc) | ||
23 | #include allow-ssh.inc | ||
24 | |||
25 | include disable-common.inc | ||
26 | include disable-exec.inc | ||
27 | include disable-interpreters.inc | ||
28 | include disable-passwdmgr.inc | ||
29 | include disable-programs.inc | ||
30 | include disable-xdg.inc | ||
31 | |||
32 | #mkdir ${HOME}/.cargo | ||
33 | #whitelist ${HOME}/YOUR_CARGO_PROJECTS | ||
34 | #whitelist ${HOME}/.cargo | ||
35 | #whitelist ${HOME}/.rustup | ||
36 | #include whitelist-common.inc | ||
37 | include whitelist-runuser-common.inc | ||
38 | include whitelist-usr-share-common.inc | ||
39 | include whitelist-var-common.inc | ||
40 | |||
41 | caps.drop all | ||
42 | ipc-namespace | ||
43 | machine-id | ||
44 | netfilter | ||
45 | no3d | ||
46 | nodvd | ||
47 | nogroups | ||
48 | noinput | ||
49 | nonewprivs | ||
50 | noroot | ||
51 | nosound | ||
52 | notv | ||
53 | nou2f | ||
54 | novideo | ||
55 | protocol unix,inet,inet6 | ||
56 | seccomp | ||
57 | seccomp.block-secondary | ||
58 | shell none | ||
59 | tracelog | ||
60 | |||
61 | disable-mnt | ||
62 | #private-bin cargo,rustc | ||
63 | private-cache | ||
64 | private-dev | ||
65 | private-etc alternatives,ca-certificates,crypto-policies,group,host.conf,hostname,hosts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,localtime,magic,magic.mgc,nsswitch.conf,passwd,pki,protocols,resolv.conf,rpc,services,ssl | ||
66 | private-tmp | ||
67 | |||
68 | dbus-user none | ||
69 | dbus-system none | ||
70 | |||
71 | memory-deny-write-execute | ||
72 | read-write ${HOME}/.cargo/bin | ||
diff --git a/etc/profile-a-l/chromium-common.profile b/etc/profile-a-l/chromium-common.profile index 134f4665c..f7493aa82 100644 --- a/etc/profile-a-l/chromium-common.profile +++ b/etc/profile-a-l/chromium-common.profile | |||
@@ -45,6 +45,7 @@ caps.keep sys_admin,sys_chroot | |||
45 | netfilter | 45 | netfilter |
46 | nodvd | 46 | nodvd |
47 | nogroups | 47 | nogroups |
48 | noinput | ||
48 | notv | 49 | notv |
49 | ?BROWSER_DISABLE_U2F: nou2f | 50 | ?BROWSER_DISABLE_U2F: nou2f |
50 | shell none | 51 | shell none |
diff --git a/etc/profile-a-l/cin.profile b/etc/profile-a-l/cin.profile index 542d6600d..e1f9523c4 100644 --- a/etc/profile-a-l/cin.profile +++ b/etc/profile-a-l/cin.profile | |||
@@ -26,7 +26,7 @@ nou2f | |||
26 | noroot | 26 | noroot |
27 | protocol unix | 27 | protocol unix |
28 | 28 | ||
29 | # if an 1-1.2% gap per thread hurts you, comment seccomp | 29 | # If a 1-1.2% gap per thread hurts you, add 'ignore seccomp' to your cin.local. |
30 | seccomp | 30 | seccomp |
31 | shell none | 31 | shell none |
32 | 32 | ||
diff --git a/etc/profile-a-l/default.profile b/etc/profile-a-l/default.profile index 2ecf1a45d..5bdf5df7f 100644 --- a/etc/profile-a-l/default.profile +++ b/etc/profile-a-l/default.profile | |||
@@ -32,12 +32,13 @@ netfilter | |||
32 | # no3d | 32 | # no3d |
33 | # nodvd | 33 | # nodvd |
34 | # nogroups | 34 | # nogroups |
35 | noinput | ||
35 | nonewprivs | 36 | nonewprivs |
36 | noroot | 37 | noroot |
37 | # nosound | 38 | # nosound |
38 | # notv | 39 | notv |
39 | # nou2f | 40 | # nou2f |
40 | # novideo | 41 | novideo |
41 | protocol unix,inet,inet6 | 42 | protocol unix,inet,inet6 |
42 | seccomp | 43 | seccomp |
43 | # shell none | 44 | # shell none |
diff --git a/etc/profile-a-l/dino.profile b/etc/profile-a-l/dino.profile index 968c0b114..c3174b35f 100644 --- a/etc/profile-a-l/dino.profile +++ b/etc/profile-a-l/dino.profile | |||
@@ -20,22 +20,24 @@ mkdir ${HOME}/.local/share/dino | |||
20 | whitelist ${HOME}/.local/share/dino | 20 | whitelist ${HOME}/.local/share/dino |
21 | whitelist ${DOWNLOADS} | 21 | whitelist ${DOWNLOADS} |
22 | include whitelist-common.inc | 22 | include whitelist-common.inc |
23 | include whitelist-runuser-common.inc | ||
24 | include whitelist-usr-share-common.inc | ||
25 | include whitelist-var-common.inc | ||
23 | 26 | ||
24 | caps.drop all | 27 | caps.drop all |
25 | netfilter | 28 | netfilter |
26 | no3d | ||
27 | nodvd | 29 | nodvd |
28 | nogroups | 30 | nogroups |
29 | noinput | 31 | noinput |
30 | nonewprivs | 32 | nonewprivs |
31 | noroot | 33 | noroot |
32 | nosound | ||
33 | notv | 34 | notv |
34 | nou2f | 35 | nou2f |
35 | novideo | ||
36 | protocol unix,inet,inet6 | 36 | protocol unix,inet,inet6 |
37 | seccomp | 37 | seccomp |
38 | seccomp.block-secondary | ||
38 | shell none | 39 | shell none |
40 | tracelog | ||
39 | 41 | ||
40 | disable-mnt | 42 | disable-mnt |
41 | private-bin dino | 43 | private-bin dino |
@@ -43,3 +45,4 @@ private-dev | |||
43 | # private-etc alternatives,ca-certificates,crypto-policies,fonts,pki,ssl -- breaks server connection | 45 | # private-etc alternatives,ca-certificates,crypto-policies,fonts,pki,ssl -- breaks server connection |
44 | private-tmp | 46 | private-tmp |
45 | 47 | ||
48 | dbus-system none | ||
diff --git a/etc/profile-a-l/discord-common.profile b/etc/profile-a-l/discord-common.profile index b83e626d9..19e7bd9ab 100644 --- a/etc/profile-a-l/discord-common.profile +++ b/etc/profile-a-l/discord-common.profile | |||
@@ -26,5 +26,7 @@ whitelist ${HOME}/.local/share/betterdiscordctl | |||
26 | private-bin bash,cut,echo,egrep,fish,grep,head,sed,sh,tclsh,tr,xdg-mime,xdg-open,zsh | 26 | private-bin bash,cut,echo,egrep,fish,grep,head,sed,sh,tclsh,tr,xdg-mime,xdg-open,zsh |
27 | private-etc alternatives,ca-certificates,crypto-policies,fonts,group,ld.so.cache,localtime,login.defs,machine-id,password,pki,pulse,resolv.conf,ssl | 27 | private-etc alternatives,ca-certificates,crypto-policies,fonts,group,ld.so.cache,localtime,login.defs,machine-id,password,pki,pulse,resolv.conf,ssl |
28 | 28 | ||
29 | join-or-start discord | ||
30 | |||
29 | # Redirect | 31 | # Redirect |
30 | include electron.profile | 32 | include electron.profile |
diff --git a/etc/profile-a-l/enpass.profile b/etc/profile-a-l/enpass.profile index feae5abb3..c4123b4c2 100644 --- a/etc/profile-a-l/enpass.profile +++ b/etc/profile-a-l/enpass.profile | |||
@@ -32,10 +32,10 @@ whitelist ${DOCUMENTS} | |||
32 | include whitelist-common.inc | 32 | include whitelist-common.inc |
33 | include whitelist-var-common.inc | 33 | include whitelist-var-common.inc |
34 | 34 | ||
35 | # machine-id and nosound break audio notification functionality | 35 | # machine-id and nosound break audio notification functionality. |
36 | # comment both if you need that functionality or put 'ignore machine-id' | 36 | # Add the next lines to your enpass.local if you need that functionality. |
37 | # and 'ignore nosound' in your enpass.local | 37 | #ignore machine-id |
38 | 38 | #ignore nosound | |
39 | caps.drop all | 39 | caps.drop all |
40 | machine-id | 40 | machine-id |
41 | netfilter | 41 | netfilter |
diff --git a/etc/profile-a-l/eog.profile b/etc/profile-a-l/eog.profile index aabef65fc..5892374bd 100644 --- a/etc/profile-a-l/eog.profile +++ b/etc/profile-a-l/eog.profile | |||
@@ -10,11 +10,13 @@ noblacklist ${HOME}/.config/eog | |||
10 | 10 | ||
11 | whitelist /usr/share/eog | 11 | whitelist /usr/share/eog |
12 | 12 | ||
13 | # private-bin, private-etc and private-lib break 'Open With' / 'Open in file manager' | 13 | # private-bin, private-etc and private-lib break 'Open With' / 'Open in file manager'. |
14 | # comment those if you need that functionality | 14 | # Add the next lines to your eog.local if you need that functionality. |
15 | # or put 'ignore private-bin', 'ignore private-etc' and 'ignore private-lib' in your eog.local | 15 | #ignore private-bin |
16 | private-bin eog | 16 | #ignore private-etc |
17 | #ignore private-lib | ||
17 | 18 | ||
19 | private-bin eog | ||
18 | 20 | ||
19 | # broken on Debian 10 (buster) running LXDE got the folowing error: | 21 | # broken on Debian 10 (buster) running LXDE got the folowing error: |
20 | # Failed to register: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: org.freedesktop.DBus.Error.ServiceUnknown | 22 | # Failed to register: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: org.freedesktop.DBus.Error.ServiceUnknown |
diff --git a/etc/profile-a-l/eom.profile b/etc/profile-a-l/eom.profile index 5bfeb8c8f..7143a8e03 100644 --- a/etc/profile-a-l/eom.profile +++ b/etc/profile-a-l/eom.profile | |||
@@ -10,9 +10,12 @@ noblacklist ${HOME}/.config/mate/eom | |||
10 | 10 | ||
11 | whitelist /usr/share/eom | 11 | whitelist /usr/share/eom |
12 | 12 | ||
13 | # private-bin, private-etc and private-lib break 'Open With' / 'Open in file manager' | 13 | # private-bin, private-etc and private-lib break 'Open With' / 'Open in file manager'. |
14 | # comment those if you need that functionality | 14 | # Add the next lines to your eom.local if you need that functionality. |
15 | # or put 'ignore private-bin', 'ignore private-etc' and 'ignore private-lib' in your eom.local | 15 | #ignore private-bin |
16 | #ignore private-etc | ||
17 | #ignore private-lib | ||
18 | |||
16 | private-bin eom | 19 | private-bin eom |
17 | 20 | ||
18 | # Redirect | 21 | # Redirect |
diff --git a/etc/profile-a-l/ephemeral.profile b/etc/profile-a-l/ephemeral.profile index 029f613c6..131d68951 100644 --- a/etc/profile-a-l/ephemeral.profile +++ b/etc/profile-a-l/ephemeral.profile | |||
@@ -41,6 +41,7 @@ caps.drop all | |||
41 | netfilter | 41 | netfilter |
42 | nodvd | 42 | nodvd |
43 | nogroups | 43 | nogroups |
44 | noinput | ||
44 | nonewprivs | 45 | nonewprivs |
45 | # noroot breaks GTK_USE_PORTAL=1 usage, see https://github.com/netblue30/firejail/issues/2506. | 46 | # noroot breaks GTK_USE_PORTAL=1 usage, see https://github.com/netblue30/firejail/issues/2506. |
46 | noroot | 47 | noroot |
diff --git a/etc/profile-a-l/firefox-common.profile b/etc/profile-a-l/firefox-common.profile index b0ead7590..8b74ed979 100644 --- a/etc/profile-a-l/firefox-common.profile +++ b/etc/profile-a-l/firefox-common.profile | |||
@@ -37,6 +37,7 @@ caps.drop all | |||
37 | netfilter | 37 | netfilter |
38 | nodvd | 38 | nodvd |
39 | nogroups | 39 | nogroups |
40 | noinput | ||
40 | nonewprivs | 41 | nonewprivs |
41 | # noroot breaks GTK_USE_PORTAL=1 usage, see https://github.com/netblue30/firejail/issues/2506. | 42 | # noroot breaks GTK_USE_PORTAL=1 usage, see https://github.com/netblue30/firejail/issues/2506. |
42 | noroot | 43 | noroot |
diff --git a/etc/profile-a-l/libreoffice.profile b/etc/profile-a-l/libreoffice.profile index 0041f2540..e4440eac0 100644 --- a/etc/profile-a-l/libreoffice.profile +++ b/etc/profile-a-l/libreoffice.profile | |||
@@ -9,8 +9,8 @@ include globals.local | |||
9 | noblacklist /usr/local/sbin | 9 | noblacklist /usr/local/sbin |
10 | noblacklist ${HOME}/.config/libreoffice | 10 | noblacklist ${HOME}/.config/libreoffice |
11 | 11 | ||
12 | # libreoffice uses java for some certain operations | 12 | # libreoffice uses java for some functionality. |
13 | # comment if you don't care about java functionality | 13 | # Add 'ignore include allow-java.inc' to your libreoffice.local if you don't need that functionality. |
14 | # Allow java (blacklisted by disable-devel.inc) | 14 | # Allow java (blacklisted by disable-devel.inc) |
15 | include allow-java.inc | 15 | include allow-java.inc |
16 | 16 | ||
@@ -22,26 +22,28 @@ include disable-programs.inc | |||
22 | 22 | ||
23 | include whitelist-var-common.inc | 23 | include whitelist-var-common.inc |
24 | 24 | ||
25 | # ubuntu 18.04 comes with its own apparmor profile, but it is not in enforce mode. | 25 | # Debian 10/Ubuntu 18.04 come with their own apparmor profile, but it is not in enforce mode. |
26 | # comment the next line to use the ubuntu profile instead of firejail's apparmor profile | 26 | # Add the next lines to your libreoffice.local to use the Ubuntu profile instead of firejail's apparmor profile. |
27 | #ignore apparmor | ||
28 | #ignore nonewprivs | ||
29 | #ignore protocol | ||
30 | #ignore seccomp | ||
31 | #ignore tracelog | ||
32 | |||
27 | apparmor | 33 | apparmor |
28 | caps.drop all | 34 | caps.drop all |
29 | netfilter | 35 | netfilter |
30 | nodvd | 36 | nodvd |
31 | nogroups | 37 | nogroups |
32 | noinput | 38 | noinput |
33 | # comment nonewprivs when using the ubuntu 18.04/debian 10 apparmor profile | ||
34 | nonewprivs | 39 | nonewprivs |
35 | noroot | 40 | noroot |
36 | notv | 41 | notv |
37 | nou2f | 42 | nou2f |
38 | novideo | 43 | novideo |
39 | # comment the protocol line when using the ubuntu 18.04/debian 10 apparmor profile | ||
40 | protocol unix,inet,inet6 | 44 | protocol unix,inet,inet6 |
41 | # comment seccomp when using the ubuntu 18.04/debian 10 apparmor profile | ||
42 | seccomp | 45 | seccomp |
43 | shell none | 46 | shell none |
44 | # comment tracelog when using the ubuntu 18.04/debian 10 apparmor profile | ||
45 | tracelog | 47 | tracelog |
46 | 48 | ||
47 | #private-bin libreoffice,sh,uname,dirname,grep,sed,basename,ls | 49 | #private-bin libreoffice,sh,uname,dirname,grep,sed,basename,ls |
diff --git a/etc/profile-a-l/librewolf.profile b/etc/profile-a-l/librewolf.profile index 0934e1271..8e3e58f19 100644 --- a/etc/profile-a-l/librewolf.profile +++ b/etc/profile-a-l/librewolf.profile | |||
@@ -18,8 +18,8 @@ whitelist ${HOME}/.librewolf | |||
18 | #noblacklist ${HOME}/.mozilla | 18 | #noblacklist ${HOME}/.mozilla |
19 | #whitelist ${HOME}/.mozilla | 19 | #whitelist ${HOME}/.mozilla |
20 | 20 | ||
21 | # Uncomment or put in your librewolf.local one of the following whitelist to enable KeePassXC Plugin | 21 | # To enable KeePassXC Plugin add one of the following lines to your librewolf.local. |
22 | # NOTE: start KeePassXC before Librewolf and keep it open to allow communication between them | 22 | # NOTE: start KeePassXC before Librewolf and keep it open to allow communication between them. |
23 | #whitelist ${RUNUSER}/kpxc_server | 23 | #whitelist ${RUNUSER}/kpxc_server |
24 | #whitelist ${RUNUSER}/org.keepassxc.KeePassXC.BrowserServer | 24 | #whitelist ${RUNUSER}/org.keepassxc.KeePassXC.BrowserServer |
25 | 25 | ||
@@ -31,25 +31,24 @@ include whitelist-usr-share-common.inc | |||
31 | 31 | ||
32 | # Add the next line to your librewolf.local to enable private-bin (Arch Linux). | 32 | # Add the next line to your librewolf.local to enable private-bin (Arch Linux). |
33 | #private-bin dbus-launch,dbus-send,librewolf,sh | 33 | #private-bin dbus-launch,dbus-send,librewolf,sh |
34 | # Add the next line to your librewolf.local to enable private-etc. Note | 34 | # Add the next line to your librewolf.local to enable private-etc. |
35 | # that private-etc must first be enabled in firefox-common.local. | 35 | # NOTE: private-etc must first be enabled in firefox-common.local. |
36 | #private-etc librewolf | 36 | #private-etc librewolf |
37 | 37 | ||
38 | dbus-user filter | 38 | dbus-user filter |
39 | # Uncomment or put in your librewolf.local to enable native notifications. | 39 | # Add the next line to your librewolf.local to enable native notifications. |
40 | #dbus-user.talk org.freedesktop.Notifications | 40 | #dbus-user.talk org.freedesktop.Notifications |
41 | # Uncomment or put in your librewolf.local to allow to inhibit screensavers | 41 | # Add the next line to your librewolf.local to allow inhibiting screensavers. |
42 | #dbus-user.talk org.freedesktop.ScreenSaver | 42 | #dbus-user.talk org.freedesktop.ScreenSaver |
43 | # Uncomment or put in your librewolf.local for plasma browser integration | 43 | # Add the next lines to your librewolf.local for plasma browser integration. |
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 | # Uncomment or put in your librewolf.local to allow screen sharing under wayland. | 47 | # Add the next lines to your librewolf.local to allow screensharing under Wayland. |
48 | #whitelist ${RUNUSER}/pipewire-0 | 48 | #whitelist ${RUNUSER}/pipewire-0 |
49 | #dbus-user.talk org.freedesktop.portal.* | 49 | #dbus-user.talk org.freedesktop.portal.* |
50 | # Also uncomment or put in your librewolf.local if screen sharing sharing still | 50 | # Also add the next line to your librewolf.local if screensharing does not work with |
51 | # does not work with the above lines (might depend on the portal | 51 | # the above lines (depends on the portal implementation). |
52 | # implementation) | ||
53 | #ignore noroot | 52 | #ignore noroot |
54 | ignore dbus-user none | 53 | ignore dbus-user none |
55 | 54 | ||
diff --git a/etc/profile-a-l/lutris.profile b/etc/profile-a-l/lutris.profile index d750e5fcd..80a3aba86 100644 --- a/etc/profile-a-l/lutris.profile +++ b/etc/profile-a-l/lutris.profile | |||
@@ -14,6 +14,10 @@ noblacklist ${HOME}/.config/lutris | |||
14 | noblacklist ${HOME}/.local/share/lutris | 14 | noblacklist ${HOME}/.local/share/lutris |
15 | # noblacklist ${HOME}/.wine | 15 | # noblacklist ${HOME}/.wine |
16 | noblacklist /tmp/.wine-* | 16 | noblacklist /tmp/.wine-* |
17 | # Don't block access to /sbin and /usr/sbin to allow using ldconfig. Otherwise | ||
18 | # Lutris won't even start. | ||
19 | noblacklist /sbin | ||
20 | noblacklist /usr/sbin | ||
17 | 21 | ||
18 | ignore noexec ${HOME} | 22 | ignore noexec ${HOME} |
19 | 23 | ||
@@ -70,5 +74,7 @@ shell none | |||
70 | #private-dev | 74 | #private-dev |
71 | private-tmp | 75 | private-tmp |
72 | 76 | ||
73 | dbus-user none | 77 | dbus-user filter |
78 | dbus-user.own net.lutris.Lutris | ||
79 | dbus-user.talk com.feralinteractive.GameMode | ||
74 | dbus-system none | 80 | dbus-system none |
diff --git a/etc/profile-m-z/minecraft-launcher.profile b/etc/profile-m-z/minecraft-launcher.profile index cdea91b8f..2536d0b38 100644 --- a/etc/profile-m-z/minecraft-launcher.profile +++ b/etc/profile-m-z/minecraft-launcher.profile | |||
@@ -6,7 +6,8 @@ include minecraft-launcher.local | |||
6 | # Persistent global definitions | 6 | # Persistent global definitions |
7 | include globals.local | 7 | include globals.local |
8 | 8 | ||
9 | # On some distros executable may be in '/opt/minecraft-launcher/', if so, run 'firejail /opt/minecraft-launcher/minecraft-launcher' to start it. | 9 | # Some distros put the executable in /opt/minecraft-launcher. |
10 | # Run 'firejail /opt/minecraft-launcher/minecraft-launcher' to start it. | ||
10 | 11 | ||
11 | ignore noexec ${HOME} | 12 | ignore noexec ${HOME} |
12 | 13 | ||
@@ -50,7 +51,8 @@ disable-mnt | |||
50 | private-bin java,java-config,minecraft-launcher | 51 | private-bin java,java-config,minecraft-launcher |
51 | private-cache | 52 | private-cache |
52 | private-dev | 53 | private-dev |
53 | # If multiplayer or realms break add your own java folder from /etc or comment the line below. | 54 | # If multiplayer or realms break, add 'private-etc <your-own-java-folder-from-/etc>' |
55 | # or 'ignore private-etc' to your minecraft-launcher.local. | ||
54 | private-etc alternatives,asound.conf,ati,ca-certificates,crypto-policies,drirc,fonts,group,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,java-10-openjdk,java-11-openjdk,java-12-openjdk,java-13-openjdk,java-14-openjdk,java-7-openjdk,java-8-openjdk,java-9-openjdk,java-openjdk,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,localtime,login.defs,machine-id,mime.types,nvidia,passwd,pki,pulse,resolv.conf,selinux,services,ssl,timezone,X11,xdg | 56 | private-etc alternatives,asound.conf,ati,ca-certificates,crypto-policies,drirc,fonts,group,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,java-10-openjdk,java-11-openjdk,java-12-openjdk,java-13-openjdk,java-14-openjdk,java-7-openjdk,java-8-openjdk,java-9-openjdk,java-openjdk,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,localtime,login.defs,machine-id,mime.types,nvidia,passwd,pki,pulse,resolv.conf,selinux,services,ssl,timezone,X11,xdg |
55 | private-opt minecraft-launcher | 57 | private-opt minecraft-launcher |
56 | private-tmp | 58 | private-tmp |
diff --git a/etc/profile-m-z/nano.profile b/etc/profile-m-z/nano.profile index 45d5f59dd..4698c2287 100644 --- a/etc/profile-m-z/nano.profile +++ b/etc/profile-m-z/nano.profile | |||
@@ -47,8 +47,12 @@ x11 none | |||
47 | private-bin nano,rnano | 47 | private-bin nano,rnano |
48 | private-cache | 48 | private-cache |
49 | private-dev | 49 | private-dev |
50 | # Comment the next line if you want to edit files in /etc directly | 50 | # Add the next lines to your nano.local if you want to edit files in /etc directly. |
51 | #ignore private-etc | ||
52 | #writable-etc | ||
51 | private-etc alternatives,nanorc | 53 | private-etc alternatives,nanorc |
54 | # Add the next line to your nano.local if you want to edit files in /var directly. | ||
55 | #writable-var | ||
52 | 56 | ||
53 | dbus-user none | 57 | dbus-user none |
54 | dbus-system none | 58 | dbus-system none |
diff --git a/etc/profile-m-z/ostrichriders.profile b/etc/profile-m-z/ostrichriders.profile index e0be078a7..310b90919 100644 --- a/etc/profile-m-z/ostrichriders.profile +++ b/etc/profile-m-z/ostrichriders.profile | |||
@@ -29,6 +29,7 @@ ipc-namespace | |||
29 | net none | 29 | net none |
30 | nodvd | 30 | nodvd |
31 | nogroups | 31 | nogroups |
32 | # Add 'ignore noinput' to your ostrichriders.local if you need controller support. | ||
32 | noinput | 33 | noinput |
33 | nonewprivs | 34 | nonewprivs |
34 | noroot | 35 | noroot |
@@ -43,7 +44,6 @@ tracelog | |||
43 | disable-mnt | 44 | disable-mnt |
44 | private-bin ostrichriders | 45 | private-bin ostrichriders |
45 | private-cache | 46 | private-cache |
46 | # comment the following line if you need controller support | ||
47 | private-dev | 47 | private-dev |
48 | private-tmp | 48 | private-tmp |
49 | 49 | ||
diff --git a/etc/profile-m-z/otter-browser.profile b/etc/profile-m-z/otter-browser.profile index aa26ddd4e..20a4e25ed 100644 --- a/etc/profile-m-z/otter-browser.profile +++ b/etc/profile-m-z/otter-browser.profile | |||
@@ -41,6 +41,7 @@ caps.drop all | |||
41 | netfilter | 41 | netfilter |
42 | nodvd | 42 | nodvd |
43 | nogroups | 43 | nogroups |
44 | noinput | ||
44 | nonewprivs | 45 | nonewprivs |
45 | noroot | 46 | noroot |
46 | notv | 47 | notv |
diff --git a/etc/profile-m-z/spotify.profile b/etc/profile-m-z/spotify.profile index f679be9e7..01bc2bc05 100644 --- a/etc/profile-m-z/spotify.profile +++ b/etc/profile-m-z/spotify.profile | |||
@@ -44,7 +44,7 @@ tracelog | |||
44 | disable-mnt | 44 | disable-mnt |
45 | private-bin bash,cat,dirname,find,grep,head,rm,sh,spotify,tclsh,touch,zenity | 45 | private-bin bash,cat,dirname,find,grep,head,rm,sh,spotify,tclsh,touch,zenity |
46 | private-dev | 46 | private-dev |
47 | # Comment the next line or put 'ignore private-etc' in your spotify.local if want to see the albums covers or if you want to use the radio | 47 | # If you want to see album covers or want to use the radio, add 'ignore private-etc' to your spotify.local. |
48 | private-etc alternatives,ca-certificates,crypto-policies,fonts,group,host.conf,hosts,ld.so.cache,machine-id,nsswitch.conf,pki,pulse,resolv.conf,ssl | 48 | private-etc alternatives,ca-certificates,crypto-policies,fonts,group,host.conf,hosts,ld.so.cache,machine-id,nsswitch.conf,pki,pulse,resolv.conf,ssl |
49 | private-opt spotify | 49 | private-opt spotify |
50 | private-srv none | 50 | private-srv none |
diff --git a/etc/profile-m-z/steam.profile b/etc/profile-m-z/steam.profile index 369255324..06d08f3a2 100644 --- a/etc/profile-m-z/steam.profile +++ b/etc/profile-m-z/steam.profile | |||
@@ -119,7 +119,7 @@ whitelist ${HOME}/.steampid | |||
119 | include whitelist-common.inc | 119 | include whitelist-common.inc |
120 | include whitelist-var-common.inc | 120 | include whitelist-var-common.inc |
121 | 121 | ||
122 | # Note: The following were intentionally left out as they are alternative | 122 | # NOTE: The following were intentionally left out as they are alternative |
123 | # (i.e.: unnecessary and/or legacy) paths whose existence may potentially | 123 | # (i.e.: unnecessary and/or legacy) paths whose existence may potentially |
124 | # clobber other paths (see #4225). If you use any, either add the entry to | 124 | # clobber other paths (see #4225). If you use any, either add the entry to |
125 | # steam.local or move the contents to a path listed above (or open an issue if | 125 | # steam.local or move the contents to a path listed above (or open an issue if |
@@ -131,34 +131,36 @@ caps.drop all | |||
131 | #ipc-namespace | 131 | #ipc-namespace |
132 | netfilter | 132 | netfilter |
133 | nodvd | 133 | nodvd |
134 | # nVidia users may need to comment / ignore nogroups and noroot | ||
135 | nogroups | 134 | nogroups |
136 | nonewprivs | 135 | nonewprivs |
136 | # If you use nVidia you might need to add 'ignore noroot' to your steam.local. | ||
137 | noroot | 137 | noroot |
138 | notv | 138 | notv |
139 | nou2f | 139 | nou2f |
140 | # novideo should be commented for VR | 140 | # For VR support add 'ignore novideo' to your steam.local. |
141 | novideo | 141 | novideo |
142 | protocol unix,inet,inet6,netlink | 142 | protocol unix,inet,inet6,netlink |
143 | # seccomp sometimes causes issues (see #2951, #3267), | 143 | # seccomp sometimes causes issues (see #2951, #3267). |
144 | # comment it or add 'ignore seccomp' to steam.local if so. | 144 | # Add 'ignore seccomp' to your steam.local if you experience this. |
145 | seccomp !ptrace | 145 | seccomp !ptrace |
146 | shell none | 146 | shell none |
147 | # tracelog breaks integrated browser | 147 | # tracelog breaks integrated browser |
148 | #tracelog | 148 | #tracelog |
149 | 149 | ||
150 | # private-bin is disabled while in testing, but has been tested working with multiple games | 150 | # private-bin is disabled while in testing, but is known to work with multiple games. |
151 | # Add the next line to your steam.local to enable private-bin. | ||
151 | #private-bin awk,basename,bash,bsdtar,bzip2,cat,chmod,cksum,cmp,comm,compress,cp,curl,cut,date,dbus-launch,dbus-send,desktop-file-edit,desktop-file-install,desktop-file-validate,dirname,echo,env,expr,file,find,getopt,grep,gtar,gzip,head,hostname,id,lbzip2,ldconfig,ldd,ln,ls,lsb_release,lsof,lspci,lz4,lzip,lzma,lzop,md5sum,mkdir,mktemp,mv,netstat,ps,pulseaudio,python*,readlink,realpath,rm,sed,sh,sha1sum,sha256sum,sha512sum,sleep,sort,steam,steamdeps,steam-native,steam-runtime,sum,tail,tar,tclsh,test,touch,tr,umask,uname,update-desktop-database,wc,wget,which,whoami,xterm,xz,zenity | 152 | #private-bin awk,basename,bash,bsdtar,bzip2,cat,chmod,cksum,cmp,comm,compress,cp,curl,cut,date,dbus-launch,dbus-send,desktop-file-edit,desktop-file-install,desktop-file-validate,dirname,echo,env,expr,file,find,getopt,grep,gtar,gzip,head,hostname,id,lbzip2,ldconfig,ldd,ln,ls,lsb_release,lsof,lspci,lz4,lzip,lzma,lzop,md5sum,mkdir,mktemp,mv,netstat,ps,pulseaudio,python*,readlink,realpath,rm,sed,sh,sha1sum,sha256sum,sha512sum,sleep,sort,steam,steamdeps,steam-native,steam-runtime,sum,tail,tar,tclsh,test,touch,tr,umask,uname,update-desktop-database,wc,wget,which,whoami,xterm,xz,zenity |
152 | # extra programs are available which might be needed for select games | 153 | # Extra programs are available which might be needed for select games. |
154 | # Add the next line to your steam.local to enable support for these programs. | ||
153 | #private-bin java,java-config,mono | 155 | #private-bin java,java-config,mono |
154 | # picture viewers are needed for viewing screenshots | 156 | # To view screenshots add the next line to your steam.local. |
155 | #private-bin eog,eom,gthumb,pix,viewnior,xviewer | 157 | #private-bin eog,eom,gthumb,pix,viewnior,xviewer |
156 | 158 | ||
157 | private-dev | 159 | private-dev |
158 | # private-etc breaks a small selection of games on some systems, comment to support those | 160 | # private-etc breaks a small selection of games on some systems. Add 'ignore private-etc' |
161 | # to your steam.local to support those. | ||
159 | private-etc alsa,alternatives,asound.conf,bumblebee,ca-certificates,crypto-policies,dbus-1,drirc,fonts,group,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,localtime,lsb-release,machine-id,mime.types,nvidia,os-release,passwd,pki,pulse,resolv.conf,services,ssl | 162 | private-etc alsa,alternatives,asound.conf,bumblebee,ca-certificates,crypto-policies,dbus-1,drirc,fonts,group,gtk-2.0,gtk-3.0,host.conf,hostname,hosts,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,localtime,lsb-release,machine-id,mime.types,nvidia,os-release,passwd,pki,pulse,resolv.conf,services,ssl |
160 | private-tmp | 163 | private-tmp |
161 | 164 | ||
162 | # breaks appindicator support | ||
163 | # dbus-user none | 165 | # dbus-user none |
164 | # dbus-system none | 166 | # dbus-system none |
diff --git a/etc/profile-m-z/sysprof.profile b/etc/profile-m-z/sysprof.profile index 2473988e4..b52b25b96 100644 --- a/etc/profile-m-z/sysprof.profile +++ b/etc/profile-m-z/sysprof.profile | |||
@@ -15,8 +15,15 @@ include disable-passwdmgr.inc | |||
15 | include disable-programs.inc | 15 | include disable-programs.inc |
16 | include disable-xdg.inc | 16 | include disable-xdg.inc |
17 | 17 | ||
18 | # help menu functionality (yelp) - comment or add this block prepended with 'ignore' | 18 | # Add the next lines to your sysprof.local if you don't need (yelp) help menu functionality. |
19 | # to your sysprof.local if you don't need the help functionality | 19 | #ignore noblacklist ${HOME}/.config/yelp |
20 | #ignore mkdir ${HOME}/.config/yelp | ||
21 | #nowhitelist ${HOME}/.config/yelp | ||
22 | #nowhitelist /usr/share/help/C/sysprof | ||
23 | #nowhitelist /usr/share/yelp | ||
24 | #nowhitelist /usr/share/yelp-tools | ||
25 | #nowhitelist /usr/share/yelp-xsl | ||
26 | |||
20 | noblacklist ${HOME}/.config/yelp | 27 | noblacklist ${HOME}/.config/yelp |
21 | mkdir ${HOME}/.config/yelp | 28 | mkdir ${HOME}/.config/yelp |
22 | whitelist ${HOME}/.config/yelp | 29 | whitelist ${HOME}/.config/yelp |
@@ -41,7 +48,8 @@ nodvd | |||
41 | nogroups | 48 | nogroups |
42 | noinput | 49 | noinput |
43 | nonewprivs | 50 | nonewprivs |
44 | # Ubuntu 16.04 version needs root privileges - comment or put 'ignore noroot' in sysprof.local if you run Xenial | 51 | # Some older Debian/Ubuntu sysprof versions need root privileges. |
52 | # Add 'ignore noroot' to your sysprof.local if you run one of these. | ||
45 | noroot | 53 | noroot |
46 | nosound | 54 | nosound |
47 | notv | 55 | notv |
@@ -57,7 +65,7 @@ disable-mnt | |||
57 | private-cache | 65 | private-cache |
58 | private-dev | 66 | private-dev |
59 | private-etc alternatives,fonts,ld.so.cache,machine-id,ssl | 67 | private-etc alternatives,fonts,ld.so.cache,machine-id,ssl |
60 | # private-lib breaks help menu | 68 | # private-lib - breaks help menu |
61 | #private-lib gdk-pixbuf-2.*,gio,gtk3,gvfs/libgvfscommon.so,libgconf-2.so.*,librsvg-2.so.*,libsysprof-2.so,libsysprof-ui-2.so | 69 | #private-lib gdk-pixbuf-2.*,gio,gtk3,gvfs/libgvfscommon.so,libgconf-2.so.*,librsvg-2.so.*,libsysprof-2.so,libsysprof-ui-2.so |
62 | private-tmp | 70 | private-tmp |
63 | 71 | ||
diff --git a/etc/templates/profile.template b/etc/templates/profile.template index a7144a29f..fcc7fe949 100644 --- a/etc/templates/profile.template +++ b/etc/templates/profile.template | |||
@@ -64,7 +64,7 @@ include globals.local | |||
64 | #blacklist /tmp/.X11-unix | 64 | #blacklist /tmp/.X11-unix |
65 | # Disable Wayland | 65 | # Disable Wayland |
66 | #blacklist ${RUNUSER}/wayland-* | 66 | #blacklist ${RUNUSER}/wayland-* |
67 | # Disable RUNUSER (cli only) | 67 | # Disable RUNUSER (cli only; supersedes Disable Wayland) |
68 | #blacklist ${RUNUSER} | 68 | #blacklist ${RUNUSER} |
69 | 69 | ||
70 | # It is common practice to add files/dirs containing program-specific configuration | 70 | # It is common practice to add files/dirs containing program-specific configuration |
@@ -109,6 +109,8 @@ include globals.local | |||
109 | # Allow ssh (blacklisted by disable-common.inc) | 109 | # Allow ssh (blacklisted by disable-common.inc) |
110 | #include allow-ssh.inc | 110 | #include allow-ssh.inc |
111 | 111 | ||
112 | # disable-*.inc includes | ||
113 | # remove disable-write-mnt.inc if you set disable-mnt | ||
112 | #include disable-common.inc | 114 | #include disable-common.inc |
113 | #include disable-devel.inc | 115 | #include disable-devel.inc |
114 | #include disable-exec.inc | 116 | #include disable-exec.inc |
@@ -220,3 +222,4 @@ include globals.local | |||
220 | #memory-deny-write-execute | 222 | #memory-deny-write-execute |
221 | ##noexec PATH | 223 | ##noexec PATH |
222 | ##read-only ${HOME} | 224 | ##read-only ${HOME} |
225 | ##read-write ${HOME} | ||
diff --git a/src/fbuilder/build_profile.c b/src/fbuilder/build_profile.c index fb53f70a6..1726b4dbb 100644 --- a/src/fbuilder/build_profile.c +++ b/src/fbuilder/build_profile.c | |||
@@ -145,9 +145,9 @@ void build_profile(int argc, char **argv, int index, FILE *fp) { | |||
145 | fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n"); | 145 | fprintf(fp, "# program name) in ~/.config/firejail directory. Firejail will find it\n"); |
146 | fprintf(fp, "# automatically every time you sandbox your application.\n#\n"); | 146 | fprintf(fp, "# automatically every time you sandbox your application.\n#\n"); |
147 | fprintf(fp, "# Run \"firejail application\" to test it. In the file there are\n"); | 147 | fprintf(fp, "# Run \"firejail application\" to test it. In the file there are\n"); |
148 | fprintf(fp, "# some other commands you can try. Enable them by removing the \"#\".\n"); | 148 | fprintf(fp, "# some other commands you can try. Enable them by removing the \"#\".\n\n"); |
149 | 149 | ||
150 | fprintf(fp, "\n# Firejail profile for %s\n", argv[index]); | 150 | fprintf(fp, "# Firejail profile for %s\n", argv[index]); |
151 | fprintf(fp, "# Persistent local customizations\n"); | 151 | fprintf(fp, "# Persistent local customizations\n"); |
152 | fprintf(fp, "#include %s.local\n", argv[index]); | 152 | fprintf(fp, "#include %s.local\n", argv[index]); |
153 | fprintf(fp, "# Persistent global definitions\n"); | 153 | fprintf(fp, "# Persistent global definitions\n"); |
@@ -164,6 +164,7 @@ void build_profile(int argc, char **argv, int index, FILE *fp) { | |||
164 | fprintf(fp, "#include disable-interpreters.inc\n"); | 164 | fprintf(fp, "#include disable-interpreters.inc\n"); |
165 | fprintf(fp, "include disable-passwdmgr.inc\n"); | 165 | fprintf(fp, "include disable-passwdmgr.inc\n"); |
166 | fprintf(fp, "include disable-programs.inc\n"); | 166 | fprintf(fp, "include disable-programs.inc\n"); |
167 | fprintf(fp, "#include disable-shell.inc\n"); | ||
167 | fprintf(fp, "#include disable-xdg.inc\n"); | 168 | fprintf(fp, "#include disable-xdg.inc\n"); |
168 | fprintf(fp, "\n"); | 169 | fprintf(fp, "\n"); |
169 | 170 | ||
@@ -171,29 +172,27 @@ void build_profile(int argc, char **argv, int index, FILE *fp) { | |||
171 | fprintf(fp, "### If something goes wrong, this section is the first one to comment out.\n"); | 172 | fprintf(fp, "### If something goes wrong, this section is the first one to comment out.\n"); |
172 | fprintf(fp, "### Instead, you'll have to relay on the basic blacklisting above.\n"); | 173 | fprintf(fp, "### Instead, you'll have to relay on the basic blacklisting above.\n"); |
173 | build_home(trace_output, fp); | 174 | build_home(trace_output, fp); |
175 | fprintf(fp, "\n"); | ||
174 | 176 | ||
175 | fprintf(fp, "\n### The Rest of the Filesystem ###\n"); | 177 | fprintf(fp, "### Filesystem Whitelisting ###\n"); |
176 | build_share(trace_output, fp); | 178 | build_share(trace_output, fp); |
179 | //todo: include whitelist-runuser-common.inc | ||
177 | build_var(trace_output, fp); | 180 | build_var(trace_output, fp); |
178 | build_bin(trace_output, fp); | 181 | fprintf(fp, "\n"); |
179 | build_dev(trace_output, fp); | ||
180 | fprintf(fp, "#nodvd\n"); | ||
181 | fprintf(fp, "#noinput\n"); | ||
182 | fprintf(fp, "#notv\n"); | ||
183 | fprintf(fp, "#nou2f\n"); | ||
184 | fprintf(fp, "#novideo\n"); | ||
185 | build_etc(trace_output, fp); | ||
186 | build_tmp(trace_output, fp); | ||
187 | 182 | ||
188 | fprintf(fp, "\n### Security Filters ###\n"); | ||
189 | fprintf(fp, "#apparmor\n"); | 183 | fprintf(fp, "#apparmor\n"); |
190 | fprintf(fp, "caps.drop all\n"); | 184 | fprintf(fp, "caps.drop all\n"); |
185 | fprintf(fp, "ipc-namespace\n"); | ||
191 | fprintf(fp, "netfilter\n"); | 186 | fprintf(fp, "netfilter\n"); |
187 | fprintf(fp, "#nodvd\n"); | ||
192 | fprintf(fp, "#nogroups\n"); | 188 | fprintf(fp, "#nogroups\n"); |
193 | fprintf(fp, "#noroot\n"); | 189 | fprintf(fp, "#noinput\n"); |
194 | fprintf(fp, "nonewprivs\n"); | 190 | fprintf(fp, "nonewprivs\n"); |
191 | fprintf(fp, "noroot\n"); | ||
192 | fprintf(fp, "#notv\n"); | ||
193 | fprintf(fp, "#nou2f\n"); | ||
194 | fprintf(fp, "#novideo\n"); | ||
195 | build_protocol(trace_output, fp); | 195 | build_protocol(trace_output, fp); |
196 | |||
197 | fprintf(fp, "seccomp\n"); | 196 | fprintf(fp, "seccomp\n"); |
198 | if (!have_strace) { | 197 | if (!have_strace) { |
199 | fprintf(fp, "### If you install strace on your system, Firejail will also create a\n"); | 198 | fprintf(fp, "### If you install strace on your system, Firejail will also create a\n"); |
@@ -203,8 +202,21 @@ void build_profile(int argc, char **argv, int index, FILE *fp) { | |||
203 | fprintf(fp, "### Yama security module prevents creation of a whitelisted seccomp filter\n"); | 202 | fprintf(fp, "### Yama security module prevents creation of a whitelisted seccomp filter\n"); |
204 | else | 203 | else |
205 | build_seccomp(strace_output, fp); | 204 | build_seccomp(strace_output, fp); |
206 | fprintf(fp, "#shell none\n"); | 205 | fprintf(fp, "shell none\n"); |
207 | fprintf(fp, "#tracelog\n"); | 206 | fprintf(fp, "#tracelog\n"); |
207 | fprintf(fp, "\n"); | ||
208 | |||
209 | fprintf(fp, "#disable-mnt\n"); | ||
210 | build_bin(trace_output, fp); | ||
211 | fprintf(fp, "#private-lib\n"); | ||
212 | build_dev(trace_output, fp); | ||
213 | build_etc(trace_output, fp); | ||
214 | build_tmp(trace_output, fp); | ||
215 | fprintf(fp, "\n"); | ||
216 | |||
217 | fprintf(fp, "#dbus-user none\n"); | ||
218 | fprintf(fp, "#dbus-system none\n"); | ||
219 | fprintf(fp, "#memory-deny-write-execute\n"); | ||
208 | 220 | ||
209 | if (!arg_debug) { | 221 | if (!arg_debug) { |
210 | unlink(trace_output); | 222 | unlink(trace_output); |
diff --git a/src/fbuilder/main.c b/src/fbuilder/main.c index f4917aefc..35ec49519 100644 --- a/src/fbuilder/main.c +++ b/src/fbuilder/main.c | |||
@@ -58,10 +58,16 @@ printf("\n"); | |||
58 | exit(1); | 58 | exit(1); |
59 | } | 59 | } |
60 | 60 | ||
61 | // don't run if the file exists | ||
62 | if (access(argv[i] + 8, F_OK) == 0) { | ||
63 | fprintf(stderr, "Error: the profile file already exists. Please use a different file name.\n"); | ||
64 | exit(1); | ||
65 | } | ||
66 | |||
61 | // check file access | 67 | // check file access |
62 | fp = fopen(argv[i] + 8, "w"); | 68 | fp = fopen(argv[i] + 8, "w"); |
63 | if (!fp) { | 69 | if (!fp) { |
64 | fprintf(stderr, "Error fbuild: cannot open profile file.\n"); | 70 | fprintf(stderr, "Error: cannot open profile file.\n"); |
65 | exit(1); | 71 | exit(1); |
66 | } | 72 | } |
67 | prof_file = 1; | 73 | prof_file = 1; |
@@ -69,7 +75,7 @@ printf("\n"); | |||
69 | } | 75 | } |
70 | else { | 76 | else { |
71 | if (*argv[i] == '-') { | 77 | if (*argv[i] == '-') { |
72 | fprintf(stderr, "Error fbuilder: invalid program\n"); | 78 | fprintf(stderr, "Error: invalid program\n"); |
73 | usage(); | 79 | usage(); |
74 | exit(1); | 80 | exit(1); |
75 | } | 81 | } |
@@ -79,7 +85,7 @@ printf("\n"); | |||
79 | } | 85 | } |
80 | 86 | ||
81 | if (prog_index == 0) { | 87 | if (prog_index == 0) { |
82 | fprintf(stderr, "Error fbuilder: program and arguments required\n"); | 88 | fprintf(stderr, "Error : program and arguments required\n"); |
83 | usage(); | 89 | usage(); |
84 | if (prof_file) | 90 | if (prof_file) |
85 | fclose(fp); | 91 | fclose(fp); |
diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 59758bf2d..6b9fed765 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c | |||
@@ -67,7 +67,7 @@ void appimage_set(const char *appimage) { | |||
67 | 67 | ||
68 | // find or allocate a free loop device to use | 68 | // find or allocate a free loop device to use |
69 | EUID_ROOT(); | 69 | EUID_ROOT(); |
70 | int cfd = open("/dev/loop-control", O_RDWR); | 70 | int cfd = open("/dev/loop-control", O_RDWR|O_CLOEXEC); |
71 | if (cfd == -1) | 71 | if (cfd == -1) |
72 | err_loop(); | 72 | err_loop(); |
73 | int devnr = ioctl(cfd, LOOP_CTL_GET_FREE); | 73 | int devnr = ioctl(cfd, LOOP_CTL_GET_FREE); |
@@ -78,7 +78,7 @@ void appimage_set(const char *appimage) { | |||
78 | errExit("asprintf"); | 78 | errExit("asprintf"); |
79 | 79 | ||
80 | // associate loop device with appimage | 80 | // associate loop device with appimage |
81 | int lfd = open(devloop, O_RDONLY); | 81 | int lfd = open(devloop, O_RDONLY|O_CLOEXEC); |
82 | if (lfd == -1) | 82 | if (lfd == -1) |
83 | err_loop(); | 83 | err_loop(); |
84 | if (ioctl(lfd, LOOP_SET_FD, ffd) == -1) | 84 | if (ioctl(lfd, LOOP_SET_FD, ffd) == -1) |
@@ -146,7 +146,7 @@ void appimage_mount(void) { | |||
146 | void appimage_clear(void) { | 146 | void appimage_clear(void) { |
147 | EUID_ROOT(); | 147 | EUID_ROOT(); |
148 | if (devloop) { | 148 | if (devloop) { |
149 | int lfd = open(devloop, O_RDONLY); | 149 | int lfd = open(devloop, O_RDONLY|O_CLOEXEC); |
150 | if (lfd != -1) { | 150 | if (lfd != -1) { |
151 | if (ioctl(lfd, LOOP_CLR_FD, 0) != -1) | 151 | if (ioctl(lfd, LOOP_CLR_FD, 0) != -1) |
152 | fmessage("AppImage detached\n"); | 152 | fmessage("AppImage detached\n"); |
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c index 1c952c0bc..a085f2c27 100644 --- a/src/firejail/bandwidth.c +++ b/src/firejail/bandwidth.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <sys/types.h> | 22 | #include <sys/types.h> |
23 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
24 | #include <unistd.h> | 24 | #include <unistd.h> |
25 | #include <errno.h> | ||
25 | #include <net/if.h> | 26 | #include <net/if.h> |
26 | #include "firejail.h" | 27 | #include "firejail.h" |
27 | 28 | ||
@@ -119,26 +120,19 @@ static void bandwidth_create_run_file(pid_t pid) { | |||
119 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) | 120 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) |
120 | errExit("asprintf"); | 121 | errExit("asprintf"); |
121 | 122 | ||
122 | // if the file already exists, do nothing | ||
123 | struct stat s; | ||
124 | if (stat(fname, &s) == 0) { | ||
125 | free(fname); | ||
126 | return; | ||
127 | } | ||
128 | |||
129 | // create an empty file and set mod and ownership | 123 | // create an empty file and set mod and ownership |
130 | /* coverity[toctou] */ | 124 | // if the file already exists, do nothing |
131 | FILE *fp = fopen(fname, "w"); | 125 | FILE *fp = fopen(fname, "wxe"); |
132 | if (fp) { | 126 | free(fname); |
133 | SET_PERMS_STREAM(fp, 0, 0, 0644); | 127 | if (!fp) { |
134 | fclose(fp); | 128 | if (errno == EEXIST) |
135 | } | 129 | return; |
136 | else { | ||
137 | fprintf(stderr, "Error: cannot create bandwidth file\n"); | 130 | fprintf(stderr, "Error: cannot create bandwidth file\n"); |
138 | exit(1); | 131 | exit(1); |
139 | } | 132 | } |
140 | 133 | ||
141 | free(fname); | 134 | SET_PERMS_STREAM(fp, 0, 0, 0644); |
135 | fclose(fp); | ||
142 | } | 136 | } |
143 | 137 | ||
144 | 138 | ||
@@ -148,7 +142,7 @@ void network_set_run_file(pid_t pid) { | |||
148 | errExit("asprintf"); | 142 | errExit("asprintf"); |
149 | 143 | ||
150 | // create an empty file and set mod and ownership | 144 | // create an empty file and set mod and ownership |
151 | FILE *fp = fopen(fname, "w"); | 145 | FILE *fp = fopen(fname, "we"); |
152 | if (fp) { | 146 | if (fp) { |
153 | if (cfg.bridge0.configured) | 147 | if (cfg.bridge0.configured) |
154 | fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox); | 148 | fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox); |
@@ -178,7 +172,7 @@ static void read_bandwidth_file(pid_t pid) { | |||
178 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) | 172 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) |
179 | errExit("asprintf"); | 173 | errExit("asprintf"); |
180 | 174 | ||
181 | FILE *fp = fopen(fname, "r"); | 175 | FILE *fp = fopen(fname, "re"); |
182 | if (fp) { | 176 | if (fp) { |
183 | char buf[1024]; | 177 | char buf[1024]; |
184 | while (fgets(buf, 1024,fp)) { | 178 | while (fgets(buf, 1024,fp)) { |
@@ -214,7 +208,7 @@ static void write_bandwidth_file(pid_t pid) { | |||
214 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) | 208 | if (asprintf(&fname, "%s/%d-bandwidth", RUN_FIREJAIL_BANDWIDTH_DIR, (int) pid) == -1) |
215 | errExit("asprintf"); | 209 | errExit("asprintf"); |
216 | 210 | ||
217 | FILE *fp = fopen(fname, "w"); | 211 | FILE *fp = fopen(fname, "we"); |
218 | if (fp) { | 212 | if (fp) { |
219 | IFBW *ptr = ifbw; | 213 | IFBW *ptr = ifbw; |
220 | while (ptr) { | 214 | while (ptr) { |
@@ -307,7 +301,7 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in | |||
307 | char *fname; | 301 | char *fname; |
308 | if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1) | 302 | if (asprintf(&fname, "%s/%d-netmap", RUN_FIREJAIL_NETWORK_DIR, (int) pid) == -1) |
309 | errExit("asprintf"); | 303 | errExit("asprintf"); |
310 | FILE *fp = fopen(fname, "r"); | 304 | FILE *fp = fopen(fname, "re"); |
311 | if (!fp) { | 305 | if (!fp) { |
312 | fprintf(stderr, "Error: cannot read network map file %s\n", fname); | 306 | fprintf(stderr, "Error: cannot read network map file %s\n", fname); |
313 | exit(1); | 307 | exit(1); |
diff --git a/src/firejail/caps.c b/src/firejail/caps.c index 597f9915b..5e02b99c2 100644 --- a/src/firejail/caps.c +++ b/src/firejail/caps.c | |||
@@ -389,7 +389,7 @@ static uint64_t extract_caps(int pid) { | |||
389 | errExit("asprintf"); | 389 | errExit("asprintf"); |
390 | 390 | ||
391 | EUID_ROOT(); // grsecurity | 391 | EUID_ROOT(); // grsecurity |
392 | FILE *fp = fopen(file, "r"); | 392 | FILE *fp = fopen(file, "re"); |
393 | EUID_USER(); // grsecurity | 393 | EUID_USER(); // grsecurity |
394 | if (!fp) | 394 | if (!fp) |
395 | goto errexit; | 395 | goto errexit; |
diff --git a/src/firejail/cgroup.c b/src/firejail/cgroup.c index 986b1157d..e7ffbca36 100644 --- a/src/firejail/cgroup.c +++ b/src/firejail/cgroup.c | |||
@@ -26,7 +26,7 @@ void save_cgroup(void) { | |||
26 | if (cfg.cgroup == NULL) | 26 | if (cfg.cgroup == NULL) |
27 | return; | 27 | return; |
28 | 28 | ||
29 | FILE *fp = fopen(RUN_CGROUP_CFG, "w"); | 29 | FILE *fp = fopen(RUN_CGROUP_CFG, "wxe"); |
30 | if (fp) { | 30 | if (fp) { |
31 | fprintf(fp, "%s", cfg.cgroup); | 31 | fprintf(fp, "%s", cfg.cgroup); |
32 | fflush(0); | 32 | fflush(0); |
@@ -48,7 +48,7 @@ void load_cgroup(const char *fname) { | |||
48 | if (!fname) | 48 | if (!fname) |
49 | return; | 49 | return; |
50 | 50 | ||
51 | FILE *fp = fopen(fname, "r"); | 51 | FILE *fp = fopen(fname, "re"); |
52 | if (fp) { | 52 | if (fp) { |
53 | char buf[MAXBUF]; | 53 | char buf[MAXBUF]; |
54 | if (fgets(buf, MAXBUF, fp)) { | 54 | if (fgets(buf, MAXBUF, fp)) { |
@@ -91,19 +91,19 @@ void set_cgroup(const char *path) { | |||
91 | goto errout; | 91 | goto errout; |
92 | 92 | ||
93 | // tasks file exists | 93 | // tasks file exists |
94 | struct stat s; | 94 | FILE *fp = fopen(path, "ae"); |
95 | if (stat(path, &s) == -1) | 95 | if (!fp) |
96 | goto errout; | 96 | goto errout; |
97 | |||
98 | // task file belongs to the user running the sandbox | 97 | // task file belongs to the user running the sandbox |
98 | int fd = fileno(fp); | ||
99 | if (fd == -1) | ||
100 | errExit("fileno"); | ||
101 | struct stat s; | ||
102 | if (fstat(fd, &s) == -1) | ||
103 | errExit("fstat"); | ||
99 | if (s.st_uid != getuid() && s.st_gid != getgid()) | 104 | if (s.st_uid != getuid() && s.st_gid != getgid()) |
100 | goto errout2; | 105 | goto errout2; |
101 | |||
102 | // add the task to cgroup | 106 | // add the task to cgroup |
103 | /* coverity[toctou] */ | ||
104 | FILE *fp = fopen(path, "a"); | ||
105 | if (!fp) | ||
106 | goto errout; | ||
107 | pid_t pid = getpid(); | 107 | pid_t pid = getpid(); |
108 | int rv = fprintf(fp, "%d\n", pid); | 108 | int rv = fprintf(fp, "%d\n", pid); |
109 | (void) rv; | 109 | (void) rv; |
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c index e1613b325..614b144e5 100644 --- a/src/firejail/checkcfg.c +++ b/src/firejail/checkcfg.c | |||
@@ -35,6 +35,7 @@ char *xvfb_extra_params = ""; | |||
35 | char *netfilter_default = NULL; | 35 | char *netfilter_default = NULL; |
36 | unsigned long join_timeout = 5000000; // microseconds | 36 | unsigned long join_timeout = 5000000; // microseconds |
37 | char *config_seccomp_error_action_str = "EPERM"; | 37 | char *config_seccomp_error_action_str = "EPERM"; |
38 | char **whitelist_reject_topdirs = NULL; | ||
38 | 39 | ||
39 | int checkcfg(int val) { | 40 | int checkcfg(int val) { |
40 | assert(val < CFG_MAX); | 41 | assert(val < CFG_MAX); |
@@ -59,7 +60,7 @@ int checkcfg(int val) { | |||
59 | 60 | ||
60 | // open configuration file | 61 | // open configuration file |
61 | const char *fname = SYSCONFDIR "/firejail.config"; | 62 | const char *fname = SYSCONFDIR "/firejail.config"; |
62 | fp = fopen(fname, "r"); | 63 | fp = fopen(fname, "re"); |
63 | if (!fp) { | 64 | if (!fp) { |
64 | #ifdef HAVE_GLOBALCFG | 65 | #ifdef HAVE_GLOBALCFG |
65 | fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname); | 66 | fprintf(stderr, "Error: Firejail configuration file %s not found\n", fname); |
@@ -238,6 +239,31 @@ int checkcfg(int val) { | |||
238 | errExit("strdup"); | 239 | errExit("strdup"); |
239 | } | 240 | } |
240 | 241 | ||
242 | else if (strncmp(ptr, "whitelist-disable-topdir ", 25) == 0) { | ||
243 | char *str = strdup(ptr + 25); | ||
244 | if (!str) | ||
245 | errExit("strdup"); | ||
246 | |||
247 | size_t cnt = 0; | ||
248 | size_t sz = 4; | ||
249 | whitelist_reject_topdirs = malloc(sz * sizeof(char *)); | ||
250 | if (!whitelist_reject_topdirs) | ||
251 | errExit("malloc"); | ||
252 | |||
253 | char *tok = strtok(str, ","); | ||
254 | while (tok) { | ||
255 | whitelist_reject_topdirs[cnt++] = tok; | ||
256 | if (cnt >= sz) { | ||
257 | sz *= 2; | ||
258 | whitelist_reject_topdirs = realloc(whitelist_reject_topdirs, sz * sizeof(char *)); | ||
259 | if (!whitelist_reject_topdirs) | ||
260 | errExit("realloc"); | ||
261 | } | ||
262 | tok = strtok(NULL, ","); | ||
263 | } | ||
264 | whitelist_reject_topdirs[cnt] = NULL; | ||
265 | } | ||
266 | |||
241 | else | 267 | else |
242 | goto errout; | 268 | goto errout; |
243 | 269 | ||
diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c index d7e96cf4c..757ffb1f7 100644 --- a/src/firejail/chroot.c +++ b/src/firejail/chroot.c | |||
@@ -131,9 +131,9 @@ void fs_chroot(const char *rootdir) { | |||
131 | assert(rootdir); | 131 | assert(rootdir); |
132 | 132 | ||
133 | // fails if there is any symlink or if rootdir is not a directory | 133 | // fails if there is any symlink or if rootdir is not a directory |
134 | int parentfd = safe_fd(rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 134 | int parentfd = safer_openat(-1, rootdir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
135 | if (parentfd == -1) | 135 | if (parentfd == -1) |
136 | errExit("safe_fd"); | 136 | errExit("safer_openat"); |
137 | // rootdir has to be owned by root and is not allowed to be generally writable, | 137 | // rootdir has to be owned by root and is not allowed to be generally writable, |
138 | // this also excludes /tmp and friends | 138 | // this also excludes /tmp and friends |
139 | struct stat s; | 139 | struct stat s; |
@@ -215,12 +215,12 @@ void fs_chroot(const char *rootdir) { | |||
215 | 215 | ||
216 | if (arg_debug) | 216 | if (arg_debug) |
217 | printf("Mounting %s on chroot %s\n", orig_pulse, orig_pulse); | 217 | printf("Mounting %s on chroot %s\n", orig_pulse, orig_pulse); |
218 | int src = safe_fd(orig_pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 218 | int src = safer_openat(-1, orig_pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
219 | if (src == -1) { | 219 | if (src == -1) { |
220 | fprintf(stderr, "Error: cannot open %s\n", orig_pulse); | 220 | fprintf(stderr, "Error: cannot open %s\n", orig_pulse); |
221 | exit(1); | 221 | exit(1); |
222 | } | 222 | } |
223 | int dst = safe_fd(pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 223 | int dst = safer_openat(-1, pulse, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
224 | if (dst == -1) { | 224 | if (dst == -1) { |
225 | fprintf(stderr, "Error: cannot open %s\n", pulse); | 225 | fprintf(stderr, "Error: cannot open %s\n", pulse); |
226 | exit(1); | 226 | exit(1); |
diff --git a/src/firejail/cpu.c b/src/firejail/cpu.c index 3427e8ade..fe7258fb0 100644 --- a/src/firejail/cpu.c +++ b/src/firejail/cpu.c | |||
@@ -75,7 +75,7 @@ void save_cpu(void) { | |||
75 | if (cfg.cpus == 0) | 75 | if (cfg.cpus == 0) |
76 | return; | 76 | return; |
77 | 77 | ||
78 | FILE *fp = fopen(RUN_CPU_CFG, "w"); | 78 | FILE *fp = fopen(RUN_CPU_CFG, "wxe"); |
79 | if (fp) { | 79 | if (fp) { |
80 | fprintf(fp, "%x\n", cfg.cpus); | 80 | fprintf(fp, "%x\n", cfg.cpus); |
81 | SET_PERMS_STREAM(fp, 0, 0, 0600); | 81 | SET_PERMS_STREAM(fp, 0, 0, 0600); |
@@ -91,7 +91,7 @@ void load_cpu(const char *fname) { | |||
91 | if (!fname) | 91 | if (!fname) |
92 | return; | 92 | return; |
93 | 93 | ||
94 | FILE *fp = fopen(fname, "r"); | 94 | FILE *fp = fopen(fname, "re"); |
95 | if (fp) { | 95 | if (fp) { |
96 | unsigned tmp; | 96 | unsigned tmp; |
97 | int rv = fscanf(fp, "%x", &tmp); | 97 | int rv = fscanf(fp, "%x", &tmp); |
@@ -139,7 +139,7 @@ static void print_cpu(int pid) { | |||
139 | } | 139 | } |
140 | 140 | ||
141 | EUID_ROOT(); // grsecurity | 141 | EUID_ROOT(); // grsecurity |
142 | FILE *fp = fopen(file, "r"); | 142 | FILE *fp = fopen(file, "re"); |
143 | EUID_USER(); // grsecurity | 143 | EUID_USER(); // grsecurity |
144 | if (!fp) { | 144 | if (!fp) { |
145 | printf(" Error: cannot open %s\n", file); | 145 | printf(" Error: cannot open %s\n", file); |
diff --git a/src/firejail/dbus.c b/src/firejail/dbus.c index 658b84537..b8aa2c974 100644 --- a/src/firejail/dbus.c +++ b/src/firejail/dbus.c | |||
@@ -416,7 +416,7 @@ void dbus_proxy_stop(void) { | |||
416 | } | 416 | } |
417 | 417 | ||
418 | static void socket_overlay(char *socket_path, char *proxy_path) { | 418 | static void socket_overlay(char *socket_path, char *proxy_path) { |
419 | int fd = safe_fd(proxy_path, O_PATH | O_NOFOLLOW | O_CLOEXEC); | 419 | int fd = safer_openat(-1, proxy_path, O_PATH | O_NOFOLLOW | O_CLOEXEC); |
420 | if (fd == -1) | 420 | if (fd == -1) |
421 | errExit("opening DBus proxy socket"); | 421 | errExit("opening DBus proxy socket"); |
422 | struct stat s; | 422 | struct stat s; |
diff --git a/src/firejail/dhcp.c b/src/firejail/dhcp.c index bdbb338d5..5bcdcad37 100644 --- a/src/firejail/dhcp.c +++ b/src/firejail/dhcp.c | |||
@@ -93,7 +93,7 @@ static pid_t dhcp_read_pidfile(const Dhclient *client) { | |||
93 | while (found == 0 && tries < 10) { | 93 | while (found == 0 && tries < 10) { |
94 | if (tries >= 1) | 94 | if (tries >= 1) |
95 | usleep(100000); | 95 | usleep(100000); |
96 | FILE *pidfile = fopen(client->pid_file, "r"); | 96 | FILE *pidfile = fopen(client->pid_file, "re"); |
97 | if (pidfile) { | 97 | if (pidfile) { |
98 | long pid; | 98 | long pid; |
99 | if (fscanf(pidfile, "%ld", &pid) == 1) | 99 | if (fscanf(pidfile, "%ld", &pid) == 1) |
diff --git a/src/firejail/env.c b/src/firejail/env.c index 03818df0b..f5e9dd980 100644 --- a/src/firejail/env.c +++ b/src/firejail/env.c | |||
@@ -59,12 +59,7 @@ void env_ibus_load(void) { | |||
59 | if (asprintf(&dirname, "%s/.config/ibus/bus", cfg.homedir) == -1) | 59 | if (asprintf(&dirname, "%s/.config/ibus/bus", cfg.homedir) == -1) |
60 | errExit("asprintf"); | 60 | errExit("asprintf"); |
61 | 61 | ||
62 | struct stat s; | ||
63 | if (stat(dirname, &s) == -1) | ||
64 | return; | ||
65 | |||
66 | // find the file | 62 | // find the file |
67 | /* coverity[toctou] */ | ||
68 | DIR *dir = opendir(dirname); | 63 | DIR *dir = opendir(dirname); |
69 | if (!dir) { | 64 | if (!dir) { |
70 | free(dirname); | 65 | free(dirname); |
@@ -84,7 +79,7 @@ void env_ibus_load(void) { | |||
84 | char *fname; | 79 | char *fname; |
85 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) | 80 | if (asprintf(&fname, "%s/%s", dirname, entry->d_name) == -1) |
86 | errExit("asprintf"); | 81 | errExit("asprintf"); |
87 | FILE *fp = fopen(fname, "r"); | 82 | FILE *fp = fopen(fname, "re"); |
88 | free(fname); | 83 | free(fname); |
89 | if (!fp) | 84 | if (!fp) |
90 | continue; | 85 | continue; |
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index e07035ae6..1c1ad4e97 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -122,26 +122,22 @@ typedef struct interface_t { | |||
122 | uint8_t configured; | 122 | uint8_t configured; |
123 | } Interface; | 123 | } Interface; |
124 | 124 | ||
125 | typedef struct topdir_t { | ||
126 | char *path; | ||
127 | int fd; | ||
128 | } TopDir; | ||
129 | |||
125 | typedef struct profile_entry_t { | 130 | typedef struct profile_entry_t { |
126 | struct profile_entry_t *next; | 131 | struct profile_entry_t *next; |
127 | char *data; // command | 132 | char *data; // command |
128 | 133 | ||
129 | // whitelist command parameters | 134 | // whitelist command parameters |
130 | char *link; // link name - set if the file is a link | 135 | struct wparam_t { |
131 | enum { | 136 | char *file; // resolved file path |
132 | WLDIR_HOME = 1, // whitelist in home directory | 137 | char *link; // link path |
133 | WLDIR_TMP, // whitelist in /tmp directory | 138 | TopDir *top; // top level directory |
134 | WLDIR_MEDIA, // whitelist in /media directory | 139 | } *wparam; |
135 | WLDIR_MNT, // whitelist in /mnt directory | 140 | |
136 | WLDIR_VAR, // whitelist in /var directory | ||
137 | WLDIR_DEV, // whitelist in /dev directory | ||
138 | WLDIR_OPT, // whitelist in /opt directory | ||
139 | WLDIR_SRV, // whitelist in /srv directory | ||
140 | WLDIR_ETC, // whitelist in /etc directory | ||
141 | WLDIR_SHARE, // whitelist in /usr/share directory | ||
142 | WLDIR_MODULE, // whitelist in /sys/module directory | ||
143 | WLDIR_RUN // whitelist in /run/user/$uid directory | ||
144 | } wldir; | ||
145 | } ProfileEntry; | 141 | } ProfileEntry; |
146 | 142 | ||
147 | typedef struct config_t { | 143 | typedef struct config_t { |
@@ -314,7 +310,6 @@ extern int arg_private_cwd; // private working directory | |||
314 | extern int arg_scan; // arp-scan all interfaces | 310 | extern int arg_scan; // arp-scan all interfaces |
315 | extern int arg_whitelist; // whitelist command | 311 | extern int arg_whitelist; // whitelist command |
316 | extern int arg_nosound; // disable sound | 312 | extern int arg_nosound; // disable sound |
317 | extern int arg_noautopulse; // disable automatic ~/.config/pulse init | ||
318 | extern int arg_novideo; //disable video devices in /dev | 313 | extern int arg_novideo; //disable video devices in /dev |
319 | extern int arg_no3d; // disable 3d hardware acceleration | 314 | extern int arg_no3d; // disable 3d hardware acceleration |
320 | extern int arg_quiet; // no output for scripting | 315 | extern int arg_quiet; // no output for scripting |
@@ -323,6 +318,7 @@ extern int arg_join_filesystem; // join only the mount namespace | |||
323 | extern int arg_nice; // nice value configured | 318 | extern int arg_nice; // nice value configured |
324 | extern int arg_ipc; // enable ipc namespace | 319 | extern int arg_ipc; // enable ipc namespace |
325 | extern int arg_writable_etc; // writable etc | 320 | extern int arg_writable_etc; // writable etc |
321 | extern int arg_keep_config_pulse; // disable automatic ~/.config/pulse init | ||
326 | extern int arg_writable_var; // writable var | 322 | extern int arg_writable_var; // writable var |
327 | extern int arg_keep_var_tmp; // don't overwrite /var/tmp | 323 | extern int arg_keep_var_tmp; // don't overwrite /var/tmp |
328 | extern int arg_writable_run_user; // writable /run/user | 324 | extern int arg_writable_run_user; // writable /run/user |
@@ -529,7 +525,7 @@ void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid); | |||
529 | unsigned extract_timeout(const char *str); | 525 | unsigned extract_timeout(const char *str); |
530 | void disable_file_or_dir(const char *fname); | 526 | void disable_file_or_dir(const char *fname); |
531 | void disable_file_path(const char *path, const char *file); | 527 | void disable_file_path(const char *path, const char *file); |
532 | int safe_fd(const char *path, int flags); | 528 | int safer_openat(int dirfd, const char *path, int flags); |
533 | int has_handler(pid_t pid, int signal); | 529 | int has_handler(pid_t pid, int signal); |
534 | void enter_network_namespace(pid_t pid); | 530 | void enter_network_namespace(pid_t pid); |
535 | int read_pid(const char *name, pid_t *pid); | 531 | int read_pid(const char *name, pid_t *pid); |
@@ -794,6 +790,7 @@ extern char *xvfb_extra_params; | |||
794 | extern char *netfilter_default; | 790 | extern char *netfilter_default; |
795 | extern unsigned long join_timeout; | 791 | extern unsigned long join_timeout; |
796 | extern char *config_seccomp_error_action_str; | 792 | extern char *config_seccomp_error_action_str; |
793 | extern char **whitelist_reject_topdirs; | ||
797 | 794 | ||
798 | int checkcfg(int val); | 795 | int checkcfg(int val); |
799 | void print_compiletime_support(void); | 796 | void print_compiletime_support(void); |
diff --git a/src/firejail/fs.c b/src/firejail/fs.c index fc67a15f3..09de11de9 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c | |||
@@ -453,7 +453,7 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { | |||
453 | if (arg_debug) | 453 | if (arg_debug) |
454 | printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); | 454 | printf("Mounting tmpfs on %s, check owner: %s\n", dir, (check_owner)? "yes": "no"); |
455 | // get a file descriptor for dir, fails if there is any symlink | 455 | // get a file descriptor for dir, fails if there is any symlink |
456 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 456 | int fd = safer_openat(-1, dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
457 | if (fd == -1) | 457 | if (fd == -1) |
458 | errExit("while opening directory"); | 458 | errExit("while opening directory"); |
459 | struct stat s; | 459 | struct stat s; |
@@ -493,7 +493,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
493 | assert(path); | 493 | assert(path); |
494 | 494 | ||
495 | // open path without following symbolic links | 495 | // open path without following symbolic links |
496 | int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 496 | int fd1 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
497 | if (fd1 == -1) | 497 | if (fd1 == -1) |
498 | goto out; | 498 | goto out; |
499 | struct stat s1; | 499 | struct stat s1; |
@@ -559,7 +559,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { | |||
559 | 559 | ||
560 | // mount --bind -o remount,ro path | 560 | // mount --bind -o remount,ro path |
561 | // need to open path again without following symbolic links | 561 | // need to open path again without following symbolic links |
562 | int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 562 | int fd2 = safer_openat(-1, path, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
563 | if (fd2 == -1) | 563 | if (fd2 == -1) |
564 | errExit("open"); | 564 | errExit("open"); |
565 | struct stat s2; | 565 | struct stat s2; |
@@ -992,9 +992,9 @@ void fs_overlayfs(void) { | |||
992 | char *firejail; | 992 | char *firejail; |
993 | if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1) | 993 | if (asprintf(&firejail, "%s/.firejail", cfg.homedir) == -1) |
994 | errExit("asprintf"); | 994 | errExit("asprintf"); |
995 | int fd = safe_fd(firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 995 | int fd = safer_openat(-1, firejail, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
996 | if (fd == -1) | 996 | if (fd == -1) |
997 | errExit("safe_fd"); | 997 | errExit("safer_openat"); |
998 | free(firejail); | 998 | free(firejail); |
999 | // create basedir if it doesn't exist | 999 | // create basedir if it doesn't exist |
1000 | // the new directory will be owned by root | 1000 | // the new directory will be owned by root |
diff --git a/src/firejail/fs_dev.c b/src/firejail/fs_dev.c index 2f0067c93..8c2870a4d 100644 --- a/src/firejail/fs_dev.c +++ b/src/firejail/fs_dev.c | |||
@@ -122,7 +122,7 @@ static void deventry_mount(void) { | |||
122 | i++; | 122 | i++; |
123 | continue; | 123 | continue; |
124 | } | 124 | } |
125 | FILE *fp = fopen(dev[i].dev_fname, "w"); | 125 | FILE *fp = fopen(dev[i].dev_fname, "we"); |
126 | if (fp) { | 126 | if (fp) { |
127 | fprintf(fp, "\n"); | 127 | fprintf(fp, "\n"); |
128 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); | 128 | SET_PERMS_STREAM(fp, s.st_uid, s.st_gid, s.st_mode); |
@@ -218,7 +218,7 @@ void fs_private_dev(void){ | |||
218 | struct stat s; | 218 | struct stat s; |
219 | if (stat("/dev/log", &s) == 0) { | 219 | if (stat("/dev/log", &s) == 0) { |
220 | have_devlog = 1; | 220 | have_devlog = 1; |
221 | FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); | 221 | FILE *fp = fopen(RUN_DEVLOG_FILE, "we"); |
222 | if (!fp) | 222 | if (!fp) |
223 | have_devlog = 0; | 223 | have_devlog = 0; |
224 | else { | 224 | else { |
@@ -239,7 +239,7 @@ void fs_private_dev(void){ | |||
239 | 239 | ||
240 | // bring back /dev/log | 240 | // bring back /dev/log |
241 | if (have_devlog) { | 241 | if (have_devlog) { |
242 | FILE *fp = fopen("/dev/log", "w"); | 242 | FILE *fp = fopen("/dev/log", "we"); |
243 | if (fp) { | 243 | if (fp) { |
244 | fprintf(fp, "\n"); | 244 | fprintf(fp, "\n"); |
245 | fclose(fp); | 245 | fclose(fp); |
diff --git a/src/firejail/fs_etc.c b/src/firejail/fs_etc.c index 8cb25a1ff..b0e1e1bf1 100644 --- a/src/firejail/fs_etc.c +++ b/src/firejail/fs_etc.c | |||
@@ -52,7 +52,7 @@ void fs_machineid(void) { | |||
52 | mid.u8[8] = (mid.u8[8] & 0x3F) | 0x80; | 52 | mid.u8[8] = (mid.u8[8] & 0x3F) | 0x80; |
53 | 53 | ||
54 | // write it in a file | 54 | // write it in a file |
55 | FILE *fp = fopen(RUN_MACHINEID, "w"); | 55 | FILE *fp = fopen(RUN_MACHINEID, "we"); |
56 | if (!fp) | 56 | if (!fp) |
57 | errExit("fopen"); | 57 | errExit("fopen"); |
58 | fprintf(fp, "%08x%08x%08x%08x\n", mid.u32[0], mid.u32[1], mid.u32[2], mid.u32[3]); | 58 | fprintf(fp, "%08x%08x%08x%08x\n", mid.u32[0], mid.u32[1], mid.u32[2], mid.u32[3]); |
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c index 46f32d7ad..4bcefa443 100644 --- a/src/firejail/fs_home.c +++ b/src/firejail/fs_home.c | |||
@@ -130,7 +130,7 @@ static int store_xauthority(void) { | |||
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 | FILE *fp = fopen(dest, "w"); | 133 | FILE *fp = fopen(dest, "we"); |
134 | if (fp) { | 134 | if (fp) { |
135 | fprintf(fp, "\n"); | 135 | fprintf(fp, "\n"); |
136 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0600); | 136 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0600); |
@@ -178,7 +178,7 @@ static int store_asoundrc(void) { | |||
178 | } | 178 | } |
179 | 179 | ||
180 | // create an empty file as root, and change ownership to user | 180 | // create an empty file as root, and change ownership to user |
181 | FILE *fp = fopen(dest, "w"); | 181 | FILE *fp = fopen(dest, "we"); |
182 | if (fp) { | 182 | if (fp) { |
183 | fprintf(fp, "\n"); | 183 | fprintf(fp, "\n"); |
184 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); | 184 | SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); |
@@ -262,10 +262,10 @@ void fs_private_homedir(void) { | |||
262 | if (arg_debug) | 262 | if (arg_debug) |
263 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); | 263 | printf("Mount-bind %s on top of %s\n", private_homedir, homedir); |
264 | // get file descriptors for homedir and private_homedir, fails if there is any symlink | 264 | // get file descriptors for homedir and private_homedir, fails if there is any symlink |
265 | int src = safe_fd(private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 265 | int src = safer_openat(-1, private_homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
266 | if (src == -1) | 266 | if (src == -1) |
267 | errExit("opening private directory"); | 267 | errExit("opening private directory"); |
268 | int dst = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 268 | int dst = safer_openat(-1, homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
269 | if (dst == -1) | 269 | if (dst == -1) |
270 | errExit("opening home directory"); | 270 | errExit("opening home directory"); |
271 | // both mount source and target should be owned by the user | 271 | // both mount source and target should be owned by the user |
@@ -576,7 +576,7 @@ void fs_private_home_list(void) { | |||
576 | if (arg_debug) | 576 | if (arg_debug) |
577 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); | 577 | printf("Mount-bind %s on top of %s\n", RUN_HOME_DIR, homedir); |
578 | 578 | ||
579 | int fd = safe_fd(homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 579 | int fd = safer_openat(-1, homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
580 | if (fd == -1) | 580 | if (fd == -1) |
581 | errExit("opening home directory"); | 581 | errExit("opening home directory"); |
582 | // home directory should be owned by the user | 582 | // home directory should be owned by the user |
diff --git a/src/firejail/fs_hostname.c b/src/firejail/fs_hostname.c index 8a3bb71ea..80046f7ae 100644 --- a/src/firejail/fs_hostname.c +++ b/src/firejail/fs_hostname.c | |||
@@ -47,11 +47,11 @@ void fs_hostname(const char *hostname) { | |||
47 | printf("Creating a new /etc/hosts file\n"); | 47 | printf("Creating a new /etc/hosts file\n"); |
48 | // copy /etc/host into our new file, and modify it on the fly | 48 | // copy /etc/host into our new file, and modify it on the fly |
49 | /* coverity[toctou] */ | 49 | /* coverity[toctou] */ |
50 | FILE *fp1 = fopen("/etc/hosts", "r"); | 50 | FILE *fp1 = fopen("/etc/hosts", "re"); |
51 | if (!fp1) | 51 | if (!fp1) |
52 | goto errexit; | 52 | goto errexit; |
53 | 53 | ||
54 | FILE *fp2 = fopen(RUN_HOSTS_FILE, "w"); | 54 | FILE *fp2 = fopen(RUN_HOSTS_FILE, "we"); |
55 | if (!fp2) { | 55 | if (!fp2) { |
56 | fclose(fp1); | 56 | fclose(fp1); |
57 | goto errexit; | 57 | goto errexit; |
@@ -165,7 +165,7 @@ void fs_resolvconf(void) { | |||
165 | 165 | ||
166 | if (arg_debug) | 166 | if (arg_debug) |
167 | printf("Creating a new /etc/resolv.conf file\n"); | 167 | printf("Creating a new /etc/resolv.conf file\n"); |
168 | FILE *fp = fopen("/etc/resolv.conf", "w"); | 168 | FILE *fp = fopen("/etc/resolv.conf", "wxe"); |
169 | if (!fp) { | 169 | if (!fp) { |
170 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); | 170 | fprintf(stderr, "Error: cannot create /etc/resolv.conf file\n"); |
171 | exit(1); | 171 | exit(1); |
diff --git a/src/firejail/fs_lib.c b/src/firejail/fs_lib.c index 85fb70854..5df356d04 100644 --- a/src/firejail/fs_lib.c +++ b/src/firejail/fs_lib.c | |||
@@ -221,7 +221,7 @@ void fslib_mount_libs(const char *full_path, unsigned user) { | |||
221 | sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); | 221 | sbox_run(mask | SBOX_SECCOMP | SBOX_CAPS_NONE, 3, PATH_FLDD, full_path, RUN_LIB_FILE); |
222 | 222 | ||
223 | // open the list of libraries and install them on by one | 223 | // open the list of libraries and install them on by one |
224 | FILE *fp = fopen(RUN_LIB_FILE, "r"); | 224 | FILE *fp = fopen(RUN_LIB_FILE, "re"); |
225 | if (!fp) | 225 | if (!fp) |
226 | errExit("fopen"); | 226 | errExit("fopen"); |
227 | 227 | ||
diff --git a/src/firejail/fs_logger.c b/src/firejail/fs_logger.c index 67ad4b52e..604e297b1 100644 --- a/src/firejail/fs_logger.c +++ b/src/firejail/fs_logger.c | |||
@@ -92,7 +92,7 @@ void fs_logger_print(void) { | |||
92 | if (!head) | 92 | if (!head) |
93 | return; | 93 | return; |
94 | 94 | ||
95 | FILE *fp = fopen(RUN_FSLOGGER_FILE, "a"); | 95 | FILE *fp = fopen(RUN_FSLOGGER_FILE, "ae"); |
96 | if (!fp) { | 96 | if (!fp) { |
97 | perror("fopen"); | 97 | perror("fopen"); |
98 | return; | 98 | return; |
@@ -123,15 +123,8 @@ void fs_logger_print_log(pid_t pid) { | |||
123 | // in case the pid is that of a firejail process, use the pid of the first child process | 123 | // in case the pid is that of a firejail process, use the pid of the first child process |
124 | pid = switch_to_child(pid); | 124 | pid = switch_to_child(pid); |
125 | 125 | ||
126 | // check privileges for non-root users | 126 | // exit if no permission to join the sandbox |
127 | uid_t uid = getuid(); | 127 | check_join_permission(pid); |
128 | if (uid != 0) { | ||
129 | uid_t sandbox_uid = pid_get_uid(pid); | ||
130 | if (uid != sandbox_uid) { | ||
131 | fprintf(stderr, "Error: permission denied\n"); | ||
132 | exit(1); | ||
133 | } | ||
134 | } | ||
135 | 128 | ||
136 | // print RUN_FSLOGGER_FILE | 129 | // print RUN_FSLOGGER_FILE |
137 | char *fname; | 130 | char *fname; |
@@ -139,24 +132,16 @@ void fs_logger_print_log(pid_t pid) { | |||
139 | errExit("asprintf"); | 132 | errExit("asprintf"); |
140 | 133 | ||
141 | EUID_ROOT(); | 134 | EUID_ROOT(); |
142 | struct stat s; | 135 | FILE *fp = fopen(fname, "re"); |
143 | if (stat(fname, &s) == -1 || s.st_uid != 0) { | 136 | free(fname); |
144 | fprintf(stderr, "Error: Cannot access filesystem log\n"); | ||
145 | exit(1); | ||
146 | } | ||
147 | |||
148 | /* coverity[toctou] */ | ||
149 | FILE *fp = fopen(fname, "r"); | ||
150 | if (!fp) { | 137 | if (!fp) { |
151 | fprintf(stderr, "Error: Cannot open filesystem log\n"); | 138 | fprintf(stderr, "Error: Cannot open filesystem log\n"); |
152 | exit(1); | 139 | exit(1); |
153 | } | 140 | } |
154 | |||
155 | char buf[MAXBUF]; | 141 | char buf[MAXBUF]; |
156 | while (fgets(buf, MAXBUF, fp)) | 142 | while (fgets(buf, MAXBUF, fp)) |
157 | printf("%s", buf); | 143 | printf("%s", buf); |
158 | fclose(fp); | 144 | fclose(fp); |
159 | free(fname); | ||
160 | 145 | ||
161 | exit(0); | 146 | exit(0); |
162 | } | 147 | } |
diff --git a/src/firejail/fs_trace.c b/src/firejail/fs_trace.c index 8f939b5f5..1fc38361e 100644 --- a/src/firejail/fs_trace.c +++ b/src/firejail/fs_trace.c | |||
@@ -33,8 +33,7 @@ void fs_trace_preload(void) { | |||
33 | if (stat("/etc/ld.so.preload", &s)) { | 33 | if (stat("/etc/ld.so.preload", &s)) { |
34 | if (arg_debug) | 34 | if (arg_debug) |
35 | printf("Creating an empty /etc/ld.so.preload file\n"); | 35 | printf("Creating an empty /etc/ld.so.preload file\n"); |
36 | /* coverity[toctou] */ | 36 | FILE *fp = fopen("/etc/ld.so.preload", "wxe"); |
37 | FILE *fp = fopen("/etc/ld.so.preload", "w"); | ||
38 | if (!fp) | 37 | if (!fp) |
39 | errExit("fopen"); | 38 | errExit("fopen"); |
40 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); | 39 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH); |
@@ -64,11 +63,11 @@ void fs_tracefile(void) { | |||
64 | if (ftruncate(fd, 0) == -1) | 63 | if (ftruncate(fd, 0) == -1) |
65 | errExit("ftruncate"); | 64 | errExit("ftruncate"); |
66 | EUID_ROOT(); | 65 | EUID_ROOT(); |
67 | FILE *fp = fopen(RUN_TRACE_FILE, "w"); | 66 | FILE *fp = fopen(RUN_TRACE_FILE, "we"); |
68 | if (!fp) | 67 | if (!fp) |
69 | errExit("fopen " RUN_TRACE_FILE); | 68 | errExit("fopen " RUN_TRACE_FILE); |
70 | fclose(fp); | 69 | fclose(fp); |
71 | fs_logger2("touch ", arg_tracefile); | 70 | fs_logger2("touch", arg_tracefile); |
72 | // mount using the symbolic link in /proc/self/fd | 71 | // mount using the symbolic link in /proc/self/fd |
73 | if (arg_debug) | 72 | if (arg_debug) |
74 | 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); |
@@ -88,7 +87,7 @@ void fs_trace(void) { | |||
88 | if (arg_debug) | 87 | if (arg_debug) |
89 | printf("Create the new ld.so.preload file\n"); | 88 | printf("Create the new ld.so.preload file\n"); |
90 | 89 | ||
91 | FILE *fp = fopen(RUN_LDPRELOAD_FILE, "w"); | 90 | FILE *fp = fopen(RUN_LDPRELOAD_FILE, "we"); |
92 | if (!fp) | 91 | if (!fp) |
93 | errExit("fopen"); | 92 | errExit("fopen"); |
94 | const char *prefix = RUN_FIREJAIL_LIB_DIR; | 93 | const char *prefix = RUN_FIREJAIL_LIB_DIR; |
diff --git a/src/firejail/fs_var.c b/src/firejail/fs_var.c index f07581cd8..bae3d6df0 100644 --- a/src/firejail/fs_var.c +++ b/src/firejail/fs_var.c | |||
@@ -127,7 +127,7 @@ void fs_var_log(void) { | |||
127 | 127 | ||
128 | // create an empty /var/log/wtmp file | 128 | // create an empty /var/log/wtmp file |
129 | /* coverity[toctou] */ | 129 | /* coverity[toctou] */ |
130 | FILE *fp = fopen("/var/log/wtmp", "w"); | 130 | FILE *fp = fopen("/var/log/wtmp", "wxe"); |
131 | if (fp) { | 131 | if (fp) { |
132 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); | 132 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH); |
133 | fclose(fp); | 133 | fclose(fp); |
@@ -135,7 +135,7 @@ void fs_var_log(void) { | |||
135 | fs_logger("touch /var/log/wtmp"); | 135 | fs_logger("touch /var/log/wtmp"); |
136 | 136 | ||
137 | // create an empty /var/log/btmp file | 137 | // create an empty /var/log/btmp file |
138 | fp = fopen("/var/log/btmp", "w"); | 138 | fp = fopen("/var/log/btmp", "wxe"); |
139 | if (fp) { | 139 | if (fp) { |
140 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP); | 140 | SET_PERMS_STREAM(fp, 0, wtmp_group, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP); |
141 | fclose(fp); | 141 | fclose(fp); |
@@ -158,8 +158,7 @@ void fs_var_lib(void) { | |||
158 | fs_logger("tmpfs /var/lib/dhcp"); | 158 | fs_logger("tmpfs /var/lib/dhcp"); |
159 | 159 | ||
160 | // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file | 160 | // isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file |
161 | FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w"); | 161 | FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "wxe"); |
162 | |||
163 | if (fp) { | 162 | if (fp) { |
164 | fprintf(fp, "\n"); | 163 | fprintf(fp, "\n"); |
165 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); | 164 | SET_PERMS_STREAM(fp, 0, 0, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); |
@@ -287,7 +286,7 @@ void fs_var_utmp(void) { | |||
287 | if (stat(UTMP_FILE, &s) == 0) | 286 | if (stat(UTMP_FILE, &s) == 0) |
288 | utmp_group = s.st_gid; | 287 | utmp_group = s.st_gid; |
289 | else { | 288 | else { |
290 | fwarning("cannot find /var/run/utmp\n"); | 289 | fwarning("cannot find %s\n", UTMP_FILE); |
291 | return; | 290 | return; |
292 | } | 291 | } |
293 | 292 | ||
@@ -296,7 +295,7 @@ void fs_var_utmp(void) { | |||
296 | printf("Create the new utmp file\n"); | 295 | printf("Create the new utmp file\n"); |
297 | 296 | ||
298 | /* coverity[toctou] */ | 297 | /* coverity[toctou] */ |
299 | FILE *fp = fopen(RUN_UTMP_FILE, "w"); | 298 | FILE *fp = fopen(RUN_UTMP_FILE, "we"); |
300 | if (!fp) | 299 | if (!fp) |
301 | errExit("fopen"); | 300 | errExit("fopen"); |
302 | 301 | ||
@@ -323,5 +322,5 @@ void fs_var_utmp(void) { | |||
323 | printf("Mount the new utmp file\n"); | 322 | printf("Mount the new utmp file\n"); |
324 | if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) | 323 | if (mount(RUN_UTMP_FILE, UTMP_FILE, NULL, MS_BIND|MS_NOSUID|MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0) |
325 | errExit("mount bind utmp"); | 324 | errExit("mount bind utmp"); |
326 | fs_logger("create /var/run/utmp"); | 325 | fs_logger2("create", UTMP_FILE); |
327 | } | 326 | } |
diff --git a/src/firejail/fs_whitelist.c b/src/firejail/fs_whitelist.c index 698d47b69..c7dbe6496 100644 --- a/src/firejail/fs_whitelist.c +++ b/src/firejail/fs_whitelist.c | |||
@@ -16,50 +16,46 @@ | |||
16 | * You should have received a copy of the GNU General Public License along | 16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/mount.h> | 21 | #include <sys/mount.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <linux/limits.h> | ||
24 | #include <fnmatch.h> | 23 | #include <fnmatch.h> |
25 | #include <glob.h> | 24 | #include <glob.h> |
26 | #include <dirent.h> | ||
27 | #include <errno.h> | 25 | #include <errno.h> |
28 | 26 | ||
29 | #include <fcntl.h> | 27 | #include <fcntl.h> |
30 | #ifndef O_PATH | 28 | #ifndef O_PATH |
31 | # define O_PATH 010000000 | 29 | #define O_PATH 010000000 |
32 | #endif | 30 | #endif |
33 | 31 | ||
32 | #define TOP_MAX 64 // maximum number of top level directories | ||
33 | |||
34 | // mountinfo functionality test; | 34 | // mountinfo functionality test; |
35 | // 1. enable TEST_MOUNTINFO definition | 35 | // 1. enable TEST_MOUNTINFO definition |
36 | // 2. run firejail --whitelist=/any/directory | 36 | // 2. run firejail --whitelist=/any/directory |
37 | //#define TEST_MOUNTINFO | 37 | //#define TEST_MOUNTINFO |
38 | 38 | ||
39 | #define EMPTY_STRING ("") | 39 | static size_t homedir_len = 0; // cache length of homedir string |
40 | static size_t homedir_len; // cache length of homedir string | 40 | static size_t runuser_len = 0; // cache length of runuser string |
41 | static size_t runuser_len; // cache length of runuser string | 41 | static char *runuser = NULL; |
42 | static char *runuser; | ||
43 | 42 | ||
44 | 43 | ||
45 | static int mkpath(const char* path, mode_t mode) { | ||
46 | assert(path && *path); | ||
47 | mode |= 0111; | ||
48 | 44 | ||
49 | // create directories with uid/gid as root, or as current user if inside home or run/user/$uid directory | 45 | static void whitelist_error(const char *path) { |
50 | int userprivs = 0; | 46 | assert(path); |
51 | if ((strncmp(path, cfg.homedir, homedir_len) == 0 && path[homedir_len] == '/') || | 47 | |
52 | (strncmp(path, runuser, runuser_len) == 0 && path[runuser_len] == '/')) { | 48 | fprintf(stderr, "Error: invalid whitelist path %s\n", path); |
53 | EUID_USER(); | 49 | exit(1); |
54 | userprivs = 1; | 50 | } |
55 | } | ||
56 | 51 | ||
52 | static int whitelist_mkpath(const char* path, mode_t mode) { | ||
57 | // work on a copy of the path | 53 | // work on a copy of the path |
58 | char *dup = strdup(path); | 54 | char *dup = strdup(path); |
59 | if (!dup) | 55 | if (!dup) |
60 | errExit("strdup"); | 56 | errExit("strdup"); |
61 | 57 | ||
62 | // don't create the last path element | 58 | // only create leading directories, don't create the file |
63 | char *p = strrchr(dup, '/'); | 59 | char *p = strrchr(dup, '/'); |
64 | assert(p); | 60 | assert(p); |
65 | *p = '\0'; | 61 | *p = '\0'; |
@@ -69,10 +65,10 @@ static int mkpath(const char* path, mode_t mode) { | |||
69 | errExit("open"); | 65 | errExit("open"); |
70 | 66 | ||
71 | // traverse the path, return -1 if a symlink is encountered | 67 | // traverse the path, return -1 if a symlink is encountered |
72 | int done = 0; | ||
73 | int fd = -1; | 68 | int fd = -1; |
69 | int done = 0; | ||
74 | char *tok = strtok(dup, "/"); | 70 | char *tok = strtok(dup, "/"); |
75 | assert(tok); // path is no top level directory | 71 | assert(tok); |
76 | while (tok) { | 72 | while (tok) { |
77 | // create the directory if necessary | 73 | // create the directory if necessary |
78 | if (mkdirat(parentfd, tok, mode) == -1) { | 74 | if (mkdirat(parentfd, tok, mode) == -1) { |
@@ -81,9 +77,6 @@ static int mkpath(const char* path, mode_t mode) { | |||
81 | perror("mkdir"); | 77 | perror("mkdir"); |
82 | close(parentfd); | 78 | close(parentfd); |
83 | free(dup); | 79 | free(dup); |
84 | if (userprivs) { | ||
85 | EUID_ROOT(); | ||
86 | } | ||
87 | return -1; | 80 | return -1; |
88 | } | 81 | } |
89 | } | 82 | } |
@@ -96,9 +89,6 @@ static int mkpath(const char* path, mode_t mode) { | |||
96 | perror("open"); | 89 | perror("open"); |
97 | close(parentfd); | 90 | close(parentfd); |
98 | free(dup); | 91 | free(dup); |
99 | if (userprivs) { | ||
100 | EUID_ROOT(); | ||
101 | } | ||
102 | return -1; | 92 | return -1; |
103 | } | 93 | } |
104 | // move on to next path segment | 94 | // move on to next path segment |
@@ -111,195 +101,111 @@ static int mkpath(const char* path, mode_t mode) { | |||
111 | fs_logger2("mkpath", path); | 101 | fs_logger2("mkpath", path); |
112 | 102 | ||
113 | free(dup); | 103 | free(dup); |
114 | if (userprivs) { | ||
115 | EUID_ROOT(); | ||
116 | } | ||
117 | return fd; | 104 | return fd; |
118 | } | 105 | } |
119 | 106 | ||
120 | static void whitelist_path(ProfileEntry *entry) { | 107 | static void whitelist_file(int dirfd, const char *topdir, const char *relpath, const char *path) { |
121 | assert(entry); | 108 | assert(topdir && relpath && path); |
122 | const char *path = entry->data + 10; | ||
123 | const char *fname; | ||
124 | char *wfile = NULL; | ||
125 | |||
126 | if (entry->wldir == WLDIR_HOME) { | ||
127 | if (strncmp(path, cfg.homedir, homedir_len) != 0 || path[homedir_len] != '/') | ||
128 | // either symlink pointing outside home directory | ||
129 | // or entire home directory, skip the mount | ||
130 | return; | ||
131 | |||
132 | fname = path + homedir_len + 1; // strlen("/home/user/") | ||
133 | |||
134 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_HOME_USER_DIR, fname) == -1) | ||
135 | errExit("asprintf"); | ||
136 | } | ||
137 | else if (entry->wldir == WLDIR_TMP) { | ||
138 | fname = path + 5; // strlen("/tmp/") | ||
139 | |||
140 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_TMP_DIR, fname) == -1) | ||
141 | errExit("asprintf"); | ||
142 | } | ||
143 | else if (entry->wldir == WLDIR_MEDIA) { | ||
144 | fname = path + 7; // strlen("/media/") | ||
145 | |||
146 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MEDIA_DIR, fname) == -1) | ||
147 | errExit("asprintf"); | ||
148 | } | ||
149 | else if (entry->wldir == WLDIR_MNT) { | ||
150 | fname = path + 5; // strlen("/mnt/") | ||
151 | |||
152 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MNT_DIR, fname) == -1) | ||
153 | errExit("asprintf"); | ||
154 | } | ||
155 | else if (entry->wldir == WLDIR_VAR) { | ||
156 | if (strncmp(path, "/var/", 5) != 0) | ||
157 | // symlink pointing outside /var, skip the mount | ||
158 | return; | ||
159 | |||
160 | fname = path + 5; // strlen("/var/") | ||
161 | |||
162 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_VAR_DIR, fname) == -1) | ||
163 | errExit("asprintf"); | ||
164 | } | ||
165 | else if (entry->wldir == WLDIR_DEV) { | ||
166 | if (strncmp(path, "/dev/", 5) != 0) | ||
167 | // symlink pointing outside /dev, skip the mount | ||
168 | return; | ||
169 | |||
170 | fname = path + 5; // strlen("/dev/") | ||
171 | |||
172 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_DEV_DIR, fname) == -1) | ||
173 | errExit("asprintf"); | ||
174 | } | ||
175 | else if (entry->wldir == WLDIR_OPT) { | ||
176 | fname = path + 5; // strlen("/opt/") | ||
177 | |||
178 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_OPT_DIR, fname) == -1) | ||
179 | errExit("asprintf"); | ||
180 | } | ||
181 | else if (entry->wldir == WLDIR_SRV) { | ||
182 | fname = path + 5; // strlen("/srv/") | ||
183 | |||
184 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SRV_DIR, fname) == -1) | ||
185 | errExit("asprintf"); | ||
186 | } | ||
187 | else if (entry->wldir == WLDIR_ETC) { | ||
188 | if (strncmp(path, "/etc/", 5) != 0) | ||
189 | // symlink pointing outside /etc, skip the mount | ||
190 | return; | ||
191 | |||
192 | fname = path + 5; // strlen("/etc/") | ||
193 | |||
194 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_ETC_DIR, fname) == -1) | ||
195 | errExit("asprintf"); | ||
196 | } | ||
197 | else if (entry->wldir == WLDIR_SHARE) { | ||
198 | fname = path + 11; // strlen("/usr/share/") | ||
199 | |||
200 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_SHARE_DIR, fname) == -1) | ||
201 | errExit("asprintf"); | ||
202 | } | ||
203 | else if (entry->wldir == WLDIR_MODULE) { | ||
204 | fname = path + 12; // strlen("/sys/module/") | ||
205 | |||
206 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_MODULE_DIR, fname) == -1) | ||
207 | errExit("asprintf"); | ||
208 | } | ||
209 | else if (entry->wldir == WLDIR_RUN) { | ||
210 | fname = path + runuser_len + 1; // strlen("/run/user/$uid/") | ||
211 | |||
212 | if (asprintf(&wfile, "%s/%s", RUN_WHITELIST_RUN_USER_DIR, fname) == -1) | ||
213 | errExit("asprintf"); | ||
214 | } | ||
215 | assert(wfile); | ||
216 | 109 | ||
217 | if (arg_debug || arg_debug_whitelists) | 110 | if (arg_debug || arg_debug_whitelists) |
218 | printf("Whitelisting %s\n", path); | 111 | printf("Debug %d: dirfd: %d; topdir: %s; relpath: %s; path: %s\n", __LINE__, dirfd, topdir, relpath, path); |
219 | 112 | ||
220 | // confirm again the mount source exists and there is no symlink | 113 | // open mount source, using a file descriptor that refers to the |
221 | struct stat wfilestat; | 114 | // top level directory |
222 | EUID_USER(); | 115 | // as the top level directory was opened before mounting the tmpfs |
223 | int fd = safe_fd(wfile, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 116 | // we still have full access to all directory contents |
224 | EUID_ROOT(); | 117 | // take care to not follow symbolic links |
118 | int fd = safer_openat(dirfd, relpath, O_PATH|O_NOFOLLOW|O_CLOEXEC); | ||
225 | if (fd == -1) { | 119 | if (fd == -1) { |
226 | if (arg_debug || arg_debug_whitelists) | 120 | if (arg_debug || arg_debug_whitelists) |
227 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | 121 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
228 | free(wfile); | ||
229 | return; | 122 | return; |
230 | } | 123 | } |
231 | if (fstat(fd, &wfilestat) == -1) | 124 | struct stat s; |
125 | if (fstat(fd, &s) == -1) | ||
232 | errExit("fstat"); | 126 | errExit("fstat"); |
233 | close(fd); | 127 | if (S_ISLNK(s.st_mode)) { |
234 | if (S_ISLNK(wfilestat.st_mode)) { | ||
235 | if (arg_debug || arg_debug_whitelists) | 128 | if (arg_debug || arg_debug_whitelists) |
236 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | 129 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
237 | free(wfile); | 130 | close(fd); |
238 | return; | 131 | return; |
239 | } | 132 | } |
240 | 133 | ||
241 | // create path of the mount target if necessary | 134 | // create mount target as root, except if inside home or run/user/$UID directory |
242 | int fd2 = mkpath(path, 0755); | 135 | int userprivs = 0; |
136 | if (strcmp(topdir, cfg.homedir) == 0 || strcmp(topdir, runuser) == 0) { | ||
137 | EUID_USER(); | ||
138 | userprivs = 1; | ||
139 | } | ||
140 | |||
141 | // create path of the mount target | ||
142 | int fd2 = whitelist_mkpath(path, 0755); | ||
243 | if (fd2 == -1) { | 143 | if (fd2 == -1) { |
244 | // something went wrong during path creation or a symlink was found; | 144 | // something went wrong during path creation or a symlink was found; |
245 | // if there is a symlink somewhere in the path of the mount target, | 145 | // if there is a symlink somewhere in the path of the mount target, |
246 | // assume the file is whitelisted already | 146 | // assume the file is whitelisted already |
247 | if (arg_debug || arg_debug_whitelists) | 147 | if (arg_debug || arg_debug_whitelists) |
248 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | 148 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
249 | free(wfile); | 149 | close(fd); |
150 | if (userprivs) | ||
151 | EUID_ROOT(); | ||
250 | return; | 152 | return; |
251 | } | 153 | } |
252 | 154 | ||
253 | // get file name of the mount target | 155 | // get file name of the mount target |
254 | const char *file = gnu_basename(path); | 156 | const char *file = gnu_basename(path); |
255 | 157 | ||
256 | // create the mount target if necessary and open it, a symlink is rejected | 158 | // create mount target itself and open it, a symlink is rejected |
257 | int fd3 = -1; | 159 | int fd3 = -1; |
258 | if (S_ISDIR(wfilestat.st_mode)) { | 160 | if (S_ISDIR(s.st_mode)) { |
259 | // directory foo can exist already: | 161 | // directory foo can exist already: |
260 | // firejail --whitelist=/foo/bar --whitelist=/foo | 162 | // firejail --whitelist=~/foo/bar --whitelist=~/foo |
261 | if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { | 163 | if (mkdirat(fd2, file, 0755) == -1 && errno != EEXIST) { |
262 | if (arg_debug || arg_debug_whitelists) { | 164 | if (arg_debug || arg_debug_whitelists) { |
263 | perror("mkdir"); | 165 | perror("mkdir"); |
264 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | 166 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
265 | } | 167 | } |
168 | close(fd); | ||
266 | close(fd2); | 169 | close(fd2); |
267 | free(wfile); | 170 | if (userprivs) |
171 | EUID_ROOT(); | ||
268 | return; | 172 | return; |
269 | } | 173 | } |
270 | fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 174 | fd3 = openat(fd2, file, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
271 | } | 175 | } |
272 | else { | 176 | else |
273 | // create an empty file, fails with EEXIST if it is whitelisted already: | 177 | // create an empty file, fails with EEXIST if it is whitelisted already: |
274 | // firejail --whitelist=/foo --whitelist=/foo/bar | 178 | // firejail --whitelist=/foo --whitelist=/foo/bar |
275 | fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); | 179 | fd3 = openat(fd2, file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR|S_IWUSR); |
276 | } | ||
277 | 180 | ||
278 | if (fd3 == -1) { | 181 | if (fd3 == -1) { |
279 | if (arg_debug || arg_debug_whitelists) { | 182 | if (errno != EEXIST && (arg_debug || arg_debug_whitelists)) { |
280 | if (errno != EEXIST) { | 183 | perror("open"); |
281 | perror("open"); | 184 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); |
282 | printf("Debug %d: skip whitelisting of %s\n", __LINE__, path); | ||
283 | } | ||
284 | } | 185 | } |
186 | close(fd); | ||
285 | close(fd2); | 187 | close(fd2); |
286 | free(wfile); | 188 | if (userprivs) |
189 | EUID_ROOT(); | ||
287 | return; | 190 | return; |
288 | } | 191 | } |
192 | |||
289 | close(fd2); | 193 | close(fd2); |
194 | if (userprivs) | ||
195 | EUID_ROOT(); | ||
290 | 196 | ||
291 | fs_logger2("whitelist", path); | 197 | if (arg_debug || arg_debug_whitelists) |
198 | printf("Whitelisting %s\n", path); | ||
292 | 199 | ||
293 | // in order to make this mount resilient against symlink attacks, use | 200 | // in order to make this mount resilient against symlink attacks, use |
294 | // a magic link in /proc/self/fd instead of mounting on path directly | 201 | // magic links in /proc/self/fd instead of mounting the paths directly |
295 | char *proc; | 202 | char *proc_src, *proc_dst; |
296 | if (asprintf(&proc, "/proc/self/fd/%d", fd3) == -1) | 203 | if (asprintf(&proc_src, "/proc/self/fd/%d", fd) == -1) |
204 | errExit("asprintf"); | ||
205 | if (asprintf(&proc_dst, "/proc/self/fd/%d", fd3) == -1) | ||
297 | errExit("asprintf"); | 206 | errExit("asprintf"); |
298 | if (mount(wfile, proc, NULL, MS_BIND|MS_REC, NULL) < 0) | 207 | if (mount(proc_src, proc_dst, NULL, MS_BIND | MS_REC, NULL) < 0) |
299 | errExit("mount bind"); | 208 | errExit("mount bind"); |
300 | free(proc); | ||
301 | close(fd3); | ||
302 | |||
303 | // check the last mount operation | 209 | // check the last mount operation |
304 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found | 210 | MountData *mptr = get_last_mount(); // will do exit(1) if the mount cannot be found |
305 | #ifdef TEST_MOUNTINFO | 211 | #ifdef TEST_MOUNTINFO |
@@ -316,35 +222,52 @@ static void whitelist_path(ProfileEntry *entry) { | |||
316 | // - there should be more than one '/' char in dest string | 222 | // - there should be more than one '/' char in dest string |
317 | if (mptr->dir == strrchr(mptr->dir, '/')) | 223 | if (mptr->dir == strrchr(mptr->dir, '/')) |
318 | errLogExit("invalid whitelist mount"); | 224 | errLogExit("invalid whitelist mount"); |
319 | // confirm the right file was mounted by comparing device and inode numbers | 225 | free(proc_src); |
320 | int fd4 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 226 | free(proc_dst); |
321 | if (fd4 == -1) | 227 | close(fd); |
322 | errExit("safe_fd"); | 228 | close(fd3); |
323 | struct stat s; | 229 | fs_logger2("whitelist", path); |
324 | if (fstat(fd4, &s) == -1) | ||
325 | errExit("fstat"); | ||
326 | if (s.st_dev != wfilestat.st_dev || s.st_ino != wfilestat.st_ino) | ||
327 | errLogExit("invalid whitelist mount"); | ||
328 | close(fd4); | ||
329 | |||
330 | free(wfile); | ||
331 | return; | ||
332 | } | 230 | } |
333 | 231 | ||
334 | static void whitelist_home(int topdir) { | 232 | static void whitelist_symlink(const char *topdir, const char *link, const char *target) { |
335 | ProfileEntry entry; | 233 | assert(topdir && link && target); |
336 | memset(&entry, 0, sizeof(entry)); | 234 | |
337 | char *cmd; | 235 | if (arg_debug || arg_debug_whitelists) |
338 | if (asprintf(&cmd, "whitelist %s", cfg.homedir) == -1) | 236 | printf("Debug %d: topdir: %s; link: %s; target: %s\n", __LINE__, topdir, link, target); |
339 | errExit("asprintf"); | 237 | |
340 | entry.data = cmd; | 238 | // create files as root, except if inside home or run/user/$UID directory |
341 | entry.wldir = topdir; | 239 | int userprivs = 0; |
342 | // creates path owned by root, except homedir is inside /run/user/$uid | 240 | if (strcmp(topdir, cfg.homedir) == 0 || strcmp(topdir, runuser) == 0) { |
343 | // does nothing if homedir does not exist | 241 | EUID_USER(); |
344 | whitelist_path(&entry); | 242 | userprivs = 1; |
345 | free(cmd); | 243 | } |
346 | } | 244 | |
245 | int fd = whitelist_mkpath(link, 0755); | ||
246 | if (fd == -1) { | ||
247 | if (arg_debug || arg_debug_whitelists) | ||
248 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link); | ||
249 | if (userprivs) | ||
250 | EUID_ROOT(); | ||
251 | return; | ||
252 | } | ||
253 | |||
254 | // get file name of symlink | ||
255 | const char *file = gnu_basename(link); | ||
256 | |||
257 | // create the link | ||
258 | if (symlinkat(target, fd, file) == -1) { | ||
259 | if (arg_debug || arg_debug_whitelists) { | ||
260 | perror("symlink"); | ||
261 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, link); | ||
262 | } | ||
263 | } | ||
264 | else if (arg_debug || arg_debug_whitelists) | ||
265 | printf("Created symbolic link %s -> %s\n", link, target); | ||
347 | 266 | ||
267 | close(fd); | ||
268 | if (userprivs) | ||
269 | EUID_ROOT(); | ||
270 | } | ||
348 | 271 | ||
349 | static void globbing(const char *pattern) { | 272 | static void globbing(const char *pattern) { |
350 | assert(pattern); | 273 | assert(pattern); |
@@ -363,6 +286,11 @@ static void globbing(const char *pattern) { | |||
363 | // testing for GLOB_NOCHECK - no pattern matched returns the original pattern | 286 | // testing for GLOB_NOCHECK - no pattern matched returns the original pattern |
364 | if (strcmp(globbuf.gl_pathv[i], pattern) == 0) | 287 | if (strcmp(globbuf.gl_pathv[i], pattern) == 0) |
365 | continue; | 288 | continue; |
289 | // foo/* expands to foo/. and foo/.. | ||
290 | const char *base = gnu_basename(globbuf.gl_pathv[i]); | ||
291 | if (strcmp(base, ".") == 0 || | ||
292 | strcmp(base, "..") == 0) | ||
293 | continue; | ||
366 | 294 | ||
367 | // build the new profile command | 295 | // build the new profile command |
368 | char *newcmd; | 296 | char *newcmd; |
@@ -378,6 +306,219 @@ static void globbing(const char *pattern) { | |||
378 | globfree(&globbuf); | 306 | globfree(&globbuf); |
379 | } | 307 | } |
380 | 308 | ||
309 | // mount tmpfs on all top level directories | ||
310 | static void tmpfs_topdirs(const TopDir *topdirs) { | ||
311 | int tmpfs_home = 0; | ||
312 | int tmpfs_runuser = 0; | ||
313 | |||
314 | int i; | ||
315 | for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { | ||
316 | // do user home and /run/user/$UID last | ||
317 | if (strcmp(topdirs[i].path, cfg.homedir) == 0) { | ||
318 | tmpfs_home = 1; | ||
319 | continue; | ||
320 | } | ||
321 | if (strcmp(topdirs[i].path, runuser) == 0) { | ||
322 | tmpfs_runuser = 1; | ||
323 | continue; | ||
324 | } | ||
325 | |||
326 | // special case /run | ||
327 | // open /run/firejail, so it can be restored right after mounting the tmpfs | ||
328 | int fd = -1; | ||
329 | if (strcmp(topdirs[i].path, "/run") == 0) { | ||
330 | fd = open(RUN_FIREJAIL_DIR, O_PATH|O_CLOEXEC); | ||
331 | if (fd == -1) | ||
332 | errExit("open"); | ||
333 | } | ||
334 | |||
335 | // mount tmpfs | ||
336 | fs_tmpfs(topdirs[i].path, 0); | ||
337 | |||
338 | // init tmpfs | ||
339 | if (strcmp(topdirs[i].path, "/run") == 0) { | ||
340 | // restore /run/firejail directory | ||
341 | if (mkdir(RUN_FIREJAIL_DIR, 0755) == -1) | ||
342 | errExit("mkdir"); | ||
343 | char *proc; | ||
344 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
345 | errExit("asprintf"); | ||
346 | if (mount(proc, RUN_FIREJAIL_DIR, NULL, MS_BIND | MS_REC, NULL) < 0) | ||
347 | errExit("mount bind"); | ||
348 | free(proc); | ||
349 | close(fd); | ||
350 | fs_logger2("whitelist", RUN_FIREJAIL_DIR); | ||
351 | |||
352 | // restore /run/user/$UID directory | ||
353 | // get path relative to /run | ||
354 | const char *rel = runuser + 5; | ||
355 | whitelist_file(topdirs[i].fd, topdirs[i].path, rel, runuser); | ||
356 | } | ||
357 | else if (strcmp(topdirs[i].path, "/tmp") == 0) { | ||
358 | // fix pam-tmpdir (#2685) | ||
359 | const char *env = env_get("TMP"); | ||
360 | if (env) { | ||
361 | char *pamtmpdir; | ||
362 | if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1) | ||
363 | errExit("asprintf"); | ||
364 | if (strcmp(env, pamtmpdir) == 0) { | ||
365 | // create empty user-owned /tmp/user/$UID directory | ||
366 | mkdir_attr("/tmp/user", 0711, 0, 0); | ||
367 | selinux_relabel_path("/tmp/user", "/tmp/user"); | ||
368 | fs_logger("mkdir /tmp/user"); | ||
369 | mkdir_attr(pamtmpdir, 0700, getuid(), 0); | ||
370 | selinux_relabel_path(pamtmpdir, pamtmpdir); | ||
371 | fs_logger2("mkdir", pamtmpdir); | ||
372 | } | ||
373 | free(pamtmpdir); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | // restore user home directory if it is masked by the tmpfs | ||
378 | // creates path owned by root | ||
379 | size_t topdir_len = strlen(topdirs[i].path); | ||
380 | if (strncmp(topdirs[i].path, cfg.homedir, topdir_len) == 0 && cfg.homedir[topdir_len] == '/') { | ||
381 | // get path relative to top level directory | ||
382 | const char *rel = cfg.homedir + topdir_len + 1; | ||
383 | whitelist_file(topdirs[i].fd, topdirs[i].path, rel, cfg.homedir); | ||
384 | } | ||
385 | |||
386 | selinux_relabel_path(topdirs[i].path, topdirs[i].path); | ||
387 | } | ||
388 | |||
389 | // user home directory | ||
390 | if (tmpfs_home) | ||
391 | fs_private(); // checks owner if outside /home | ||
392 | |||
393 | // /run/user/$UID directory | ||
394 | if (tmpfs_runuser) { | ||
395 | fs_tmpfs(runuser, 0); | ||
396 | selinux_relabel_path(runuser, runuser); | ||
397 | } | ||
398 | } | ||
399 | |||
400 | static int reject_topdir(const char *dir) { | ||
401 | if (!whitelist_reject_topdirs) | ||
402 | return 0; | ||
403 | |||
404 | size_t i; | ||
405 | for (i = 0; whitelist_reject_topdirs[i]; i++) { | ||
406 | if (strcmp(dir, whitelist_reject_topdirs[i]) == 0) | ||
407 | return 1; | ||
408 | } | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | // keep track of whitelist top level directories by adding them to an array | ||
413 | // open each directory | ||
414 | static TopDir *add_topdir(const char *dir, TopDir *topdirs, const char *path) { | ||
415 | assert(dir && path); | ||
416 | |||
417 | // /proc and /sys are not allowed | ||
418 | if (strcmp(dir, "/") == 0 || | ||
419 | strcmp(dir, "/proc") == 0 || | ||
420 | strcmp(dir, "/sys") == 0) | ||
421 | whitelist_error(path); | ||
422 | |||
423 | // do nothing if directory doesn't exist | ||
424 | struct stat s; | ||
425 | if (lstat(dir, &s) != 0) { | ||
426 | if (arg_debug || arg_debug_whitelists) | ||
427 | printf("Cannot access whitelist top level directory %s: %s\n", dir, strerror(errno)); | ||
428 | return NULL; | ||
429 | } | ||
430 | // do nothing if directory is a link | ||
431 | if (!S_ISDIR(s.st_mode)) { | ||
432 | if (S_ISLNK(s.st_mode)) { | ||
433 | fwarning("skipping whitelist %s because %s is a symbolic link\n", path, dir); | ||
434 | return NULL; | ||
435 | } | ||
436 | whitelist_error(path); | ||
437 | } | ||
438 | // do nothing if directory is disabled by administrator | ||
439 | if (reject_topdir(dir)) { | ||
440 | fwarning("skipping whitelist %s because\n" | ||
441 | "whitelist top level directory is disabled in Firejail configuration file\n", path); | ||
442 | return NULL; | ||
443 | } | ||
444 | |||
445 | // add directory to array | ||
446 | if (arg_debug || arg_debug_whitelists) | ||
447 | printf("Adding whitelist top level directory %s\n", dir); | ||
448 | static int cnt = 0; | ||
449 | if (cnt >= TOP_MAX) { | ||
450 | fprintf(stderr, "Error: too many whitelist top level directories\n"); | ||
451 | exit(1); | ||
452 | } | ||
453 | TopDir *rv = topdirs + cnt; | ||
454 | cnt++; | ||
455 | |||
456 | char *dup = strdup(dir); | ||
457 | if (!dup) | ||
458 | errExit("strdup"); | ||
459 | rv->path = dup; | ||
460 | |||
461 | // open the directory, don't follow symbolic links | ||
462 | rv->fd = safer_openat(-1, dup, O_PATH|O_NOFOLLOW|O_DIRECTORY|O_CLOEXEC); | ||
463 | if (rv->fd == -1) { | ||
464 | fprintf(stderr, "Error: cannot open %s\n", dup); | ||
465 | exit(1); | ||
466 | } | ||
467 | |||
468 | return rv; | ||
469 | } | ||
470 | |||
471 | static TopDir *have_topdir(const char *dir, TopDir *topdirs) { | ||
472 | assert(dir); | ||
473 | |||
474 | int i; | ||
475 | for (i = 0; i < TOP_MAX; i++) { | ||
476 | TopDir *rv = topdirs + i; | ||
477 | if (!rv->path) | ||
478 | break; | ||
479 | if (strcmp(dir, rv->path) == 0) | ||
480 | return rv; | ||
481 | } | ||
482 | return NULL; | ||
483 | } | ||
484 | |||
485 | static char *extract_topdir(const char *path) { | ||
486 | assert(path); | ||
487 | |||
488 | char *dup = strdup(path); | ||
489 | if (!dup) | ||
490 | errExit("strdup"); | ||
491 | |||
492 | // user home directory can be anywhere; disconnect user home | ||
493 | // whitelisting from top level directory whitelisting | ||
494 | // by treating user home as separate whitelist top level directory | ||
495 | if (strncmp(dup, cfg.homedir, homedir_len) == 0 && dup[homedir_len] == '/') | ||
496 | dup[homedir_len] = '\0'; | ||
497 | // /run/user/$UID is treated as top level directory | ||
498 | else if (strncmp(dup, runuser, runuser_len) == 0 && dup[runuser_len] == '/') | ||
499 | dup[runuser_len] = '\0'; | ||
500 | // whitelisting in /sys is not allowed, but /sys/module is an exception | ||
501 | // and is treated as top level directory here | ||
502 | else if (strncmp(dup, "/sys/module", 11) == 0 && dup[11] == '/') | ||
503 | dup[11] = '\0'; | ||
504 | // treat /usr subdirectories as top level directories | ||
505 | else if (strncmp(dup, "/usr/", 5) == 0) { | ||
506 | char *p = strchr(dup+5, '/'); | ||
507 | if (!p) | ||
508 | whitelist_error(path); | ||
509 | *p = '\0'; | ||
510 | } | ||
511 | // all other top level directories | ||
512 | else { | ||
513 | assert(dup[0] == '/'); | ||
514 | char *p = strchr(dup+1, '/'); | ||
515 | if (!p) | ||
516 | whitelist_error(path); | ||
517 | *p = '\0'; | ||
518 | } | ||
519 | |||
520 | return dup; | ||
521 | } | ||
381 | 522 | ||
382 | void fs_whitelist(void) { | 523 | void fs_whitelist(void) { |
383 | ProfileEntry *entry = cfg.profile; | 524 | ProfileEntry *entry = cfg.profile; |
@@ -389,29 +530,18 @@ void fs_whitelist(void) { | |||
389 | runuser_len = strlen(runuser); | 530 | runuser_len = strlen(runuser); |
390 | homedir_len = strlen(cfg.homedir); | 531 | homedir_len = strlen(cfg.homedir); |
391 | 532 | ||
392 | char *new_name = NULL; | ||
393 | int home_dir = 0; // /home/user directory flag | ||
394 | int tmp_dir = 0; // /tmp directory flag | ||
395 | int media_dir = 0; // /media directory flag | ||
396 | int mnt_dir = 0; // /mnt directory flag | ||
397 | int var_dir = 0; // /var directory flag | ||
398 | int dev_dir = 0; // /dev directory flag | ||
399 | int opt_dir = 0; // /opt directory flag | ||
400 | int srv_dir = 0; // /srv directory flag | ||
401 | int etc_dir = 0; // /etc directory flag | ||
402 | int share_dir = 0; // /usr/share directory flag | ||
403 | int module_dir = 0; // /sys/module directory flag | ||
404 | int run_dir = 0; // /run/user/$uid directory flag | ||
405 | |||
406 | size_t nowhitelist_c = 0; | 533 | size_t nowhitelist_c = 0; |
407 | size_t nowhitelist_m = 32; | 534 | size_t nowhitelist_m = 32; |
408 | char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist)); | 535 | char **nowhitelist = calloc(nowhitelist_m, sizeof(*nowhitelist)); |
409 | if (nowhitelist == NULL) | 536 | if (nowhitelist == NULL) |
410 | errExit("failed allocating memory for nowhitelist entries"); | 537 | errExit("calloc"); |
538 | |||
539 | TopDir *topdirs = calloc(TOP_MAX, sizeof(*topdirs)); | ||
540 | if (topdirs == NULL) | ||
541 | errExit("calloc"); | ||
411 | 542 | ||
412 | // verify whitelist files, extract symbolic links, etc. | 543 | // verify whitelist files, extract symbolic links, etc. |
413 | EUID_USER(); | 544 | EUID_USER(); |
414 | struct stat s; | ||
415 | while (entry) { | 545 | while (entry) { |
416 | int nowhitelist_flag = 0; | 546 | int nowhitelist_flag = 0; |
417 | 547 | ||
@@ -424,48 +554,69 @@ void fs_whitelist(void) { | |||
424 | entry = entry->next; | 554 | entry = entry->next; |
425 | continue; | 555 | continue; |
426 | } | 556 | } |
427 | char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; | 557 | if (arg_debug || arg_debug_whitelists) |
428 | 558 | printf("Debug %d: %s\n", __LINE__, entry->data); | |
429 | // replace ~/ or ${HOME} into /home/username or resolve macro | 559 | |
430 | new_name = expand_macros(dataptr); | 560 | const char *dataptr = (nowhitelist_flag)? entry->data + 12: entry->data + 10; |
431 | assert(new_name); | 561 | |
432 | 562 | // replace ~ into /home/username or resolve macro | |
433 | // mount empty home directory if resolving the macro was not successful | 563 | char *expanded = expand_macros(dataptr); |
434 | if (is_macro(new_name) && macro_id(new_name) > -1) { | 564 | |
435 | // no warning if home does not exist (e.g. in a chroot) | 565 | // check if respolving the macro was successful |
436 | if (stat(cfg.homedir, &s) == 0 && !nowhitelist_flag && !arg_private) { | 566 | if (is_macro(expanded) && macro_id(expanded) > -1) { |
437 | home_dir = 1; | 567 | if (!nowhitelist_flag && (have_topdir(cfg.homedir, topdirs) || add_topdir(cfg.homedir, topdirs, expanded)) && !arg_quiet) { |
438 | if (!arg_quiet) { | 568 | fprintf(stderr, "***\n"); |
439 | fprintf(stderr, "***\n"); | 569 | fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", expanded); |
440 | fprintf(stderr, "*** Warning: cannot whitelist %s directory\n", new_name); | 570 | fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n"); |
441 | fprintf(stderr, "*** Any file saved in this directory will be lost when the sandbox is closed.\n"); | 571 | fprintf(stderr, "***\n"); |
442 | fprintf(stderr, "***\n"); | ||
443 | } | ||
444 | } | 572 | } |
445 | entry->data = EMPTY_STRING; | ||
446 | entry = entry->next; | 573 | entry = entry->next; |
447 | free(new_name); | 574 | free(expanded); |
448 | continue; | 575 | continue; |
449 | } | 576 | } |
450 | 577 | ||
451 | // remove trailing slashes and single dots | 578 | if (arg_debug || arg_debug_whitelists) |
452 | if (!nowhitelist_flag) | 579 | printf("Debug %d: expanded: %s\n", __LINE__, expanded); |
453 | trim_trailing_slash_or_dot(new_name); | 580 | |
581 | // path should be absolute at this point | ||
582 | if (expanded[0] != '/') | ||
583 | whitelist_error(expanded); | ||
584 | |||
585 | // sane pathname | ||
586 | char *new_name = clean_pathname(expanded); | ||
587 | free(expanded); | ||
454 | 588 | ||
455 | if (arg_debug || arg_debug_whitelists) | 589 | if (arg_debug || arg_debug_whitelists) |
456 | fprintf(stderr, "Debug %d: new_name #%s#, %s\n", __LINE__, new_name, (nowhitelist_flag)? "nowhitelist": "whitelist"); | 590 | printf("Debug %d: new_name: %s\n", __LINE__, new_name); |
591 | |||
592 | if (strstr(new_name, "..")) | ||
593 | whitelist_error(new_name); | ||
457 | 594 | ||
458 | // valid path referenced to filesystem root | 595 | TopDir *current_top = NULL; |
459 | if (*new_name != '/') { | 596 | if (!nowhitelist_flag) { |
597 | // extract whitelist top level directory | ||
598 | char *dir = extract_topdir(new_name); | ||
460 | if (arg_debug || arg_debug_whitelists) | 599 | if (arg_debug || arg_debug_whitelists) |
461 | fprintf(stderr, "Debug %d: \n", __LINE__); | 600 | printf("Debug %d: dir: %s\n", __LINE__, dir); |
462 | goto errexit; | 601 | |
602 | // check if this top level directory has been processed already | ||
603 | current_top = have_topdir(dir, topdirs); | ||
604 | if (!current_top) { // got new top level directory | ||
605 | current_top = add_topdir(dir, topdirs, new_name); | ||
606 | if (!current_top) { // skip this command, top level directory not valid | ||
607 | entry = entry->next; | ||
608 | free(new_name); | ||
609 | free(dir); | ||
610 | continue; | ||
611 | } | ||
612 | } | ||
613 | free(dir); | ||
463 | } | 614 | } |
464 | 615 | ||
465 | // extract the absolute path of the file | 616 | // extract resolved path of the file |
466 | // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission | 617 | // realpath function will fail with ENOENT if the file is not found or with EACCES if user has no permission |
467 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr | 618 | // special processing for /dev/fd, /dev/stdin, /dev/stdout and /dev/stderr |
468 | char *fname; | 619 | char *fname = NULL; |
469 | if (strcmp(new_name, "/dev/fd") == 0) | 620 | if (strcmp(new_name, "/dev/fd") == 0) |
470 | fname = strdup("/proc/self/fd"); | 621 | fname = strdup("/proc/self/fd"); |
471 | else if (strcmp(new_name, "/dev/stdin") == 0) | 622 | else if (strcmp(new_name, "/dev/stdin") == 0) |
@@ -477,60 +628,26 @@ void fs_whitelist(void) { | |||
477 | else | 628 | else |
478 | fname = realpath(new_name, NULL); | 629 | fname = realpath(new_name, NULL); |
479 | 630 | ||
480 | // if this is not a real path, let's try globbing | ||
481 | // mark this entry as EMPTY_STRING and push the new paths at the end of profile entry list | ||
482 | // the new profile entries will be processed in this loop | ||
483 | // currently there is no globbing support for nowhitelist | ||
484 | if (!fname && !nowhitelist_flag) | ||
485 | globbing(new_name); | ||
486 | |||
487 | if (!fname) { | 631 | if (!fname) { |
488 | // file not found, blank the entry in the list and continue | ||
489 | if (arg_debug || arg_debug_whitelists) { | 632 | if (arg_debug || arg_debug_whitelists) { |
490 | printf("Removed whitelist/nowhitelist path: %s\n", entry->data); | 633 | printf("Removed path: %s\n", entry->data); |
491 | printf("\texpanded: %s\n", new_name); | 634 | printf("\texpanded: %s\n", new_name); |
492 | printf("\treal path: (null)\n"); | 635 | printf("\trealpath: (null)\n"); |
493 | printf("\t");fflush(0); | 636 | printf("\t%s\n", strerror(errno)); |
494 | perror("realpath"); | ||
495 | } | 637 | } |
496 | 638 | ||
497 | // if 1 the file was not found; mount an empty directory | ||
498 | if (!nowhitelist_flag) { | 639 | if (!nowhitelist_flag) { |
499 | if (strncmp(new_name, cfg.homedir, homedir_len) == 0 && new_name[homedir_len] == '/') { | 640 | // if this is not a real path, let's try globbing |
500 | if(!arg_private) | 641 | // push the new paths at the end of profile entry list |
501 | home_dir = 1; | 642 | // the new profile entries will be processed in this loop |
502 | } | 643 | // currently there is no globbing support for nowhitelist |
503 | else if (strncmp(new_name, "/tmp/", 5) == 0) | 644 | globbing(new_name); |
504 | tmp_dir = 1; | ||
505 | else if (strncmp(new_name, "/media/", 7) == 0) | ||
506 | media_dir = 1; | ||
507 | else if (strncmp(new_name, "/mnt/", 5) == 0) | ||
508 | mnt_dir = 1; | ||
509 | else if (strncmp(new_name, "/var/", 5) == 0) | ||
510 | var_dir = 1; | ||
511 | else if (strncmp(new_name, "/dev/", 5) == 0) | ||
512 | dev_dir = 1; | ||
513 | else if (strncmp(new_name, "/opt/", 5) == 0) | ||
514 | opt_dir = 1; | ||
515 | else if (strncmp(new_name, "/srv/", 5) == 0) | ||
516 | srv_dir = 1; | ||
517 | else if (strncmp(new_name, "/etc/", 5) == 0) | ||
518 | etc_dir = 1; | ||
519 | else if (strncmp(new_name, "/usr/share/", 11) == 0) | ||
520 | share_dir = 1; | ||
521 | else if (strncmp(new_name, "/sys/module/", 12) == 0) | ||
522 | module_dir = 1; | ||
523 | else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/') | ||
524 | run_dir = 1; | ||
525 | } | 645 | } |
526 | 646 | ||
527 | entry->data = EMPTY_STRING; | ||
528 | entry = entry->next; | 647 | entry = entry->next; |
529 | free(new_name); | 648 | free(new_name); |
530 | continue; | 649 | continue; |
531 | } | 650 | } |
532 | else if (arg_debug_whitelists) | ||
533 | printf("real path %s\n", fname); | ||
534 | 651 | ||
535 | if (nowhitelist_flag) { | 652 | if (nowhitelist_flag) { |
536 | // store the path in nowhitelist array | 653 | // store the path in nowhitelist array |
@@ -544,175 +661,12 @@ void fs_whitelist(void) { | |||
544 | errExit("failed increasing memory for nowhitelist entries"); | 661 | errExit("failed increasing memory for nowhitelist entries"); |
545 | } | 662 | } |
546 | nowhitelist[nowhitelist_c++] = fname; | 663 | nowhitelist[nowhitelist_c++] = fname; |
547 | entry->data = EMPTY_STRING; | ||
548 | entry = entry->next; | 664 | entry = entry->next; |
549 | free(new_name); | 665 | free(new_name); |
550 | continue; | 666 | continue; |
551 | } | 667 | } |
552 | |||
553 | // check for supported directories | ||
554 | if (strncmp(new_name, cfg.homedir, homedir_len) == 0 && new_name[homedir_len] == '/') { | ||
555 | // whitelisting home directory is disabled if --private option is present | ||
556 | if (arg_private) { | ||
557 | if (arg_debug || arg_debug_whitelists) | ||
558 | printf("\"%s\" disabled by --private\n", entry->data); | ||
559 | |||
560 | entry->data = EMPTY_STRING; | ||
561 | entry = entry->next; | ||
562 | free(fname); | ||
563 | free(new_name); | ||
564 | continue; | ||
565 | } | ||
566 | |||
567 | entry->wldir = WLDIR_HOME; | ||
568 | home_dir = 1; | ||
569 | if (arg_debug || arg_debug_whitelists) | ||
570 | fprintf(stderr, "Debug %d: fname #%s#, cfg.homedir #%s#\n", | ||
571 | __LINE__, fname, cfg.homedir); | ||
572 | |||
573 | // both path and absolute path are in user home, | ||
574 | // if not check if the symlink destination is owned by the user | ||
575 | if (strncmp(fname, cfg.homedir, homedir_len) != 0 || fname[homedir_len] != '/') { | ||
576 | if (checkcfg(CFG_FOLLOW_SYMLINK_AS_USER)) { | ||
577 | if (stat(fname, &s) == 0 && s.st_uid != getuid()) { | ||
578 | free(fname); | ||
579 | goto errexit; | ||
580 | } | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | else if (strncmp(new_name, "/tmp/", 5) == 0) { | ||
585 | entry->wldir = WLDIR_TMP; | ||
586 | tmp_dir = 1; | ||
587 | |||
588 | // both path and absolute path are under /tmp | ||
589 | if (strncmp(fname, "/tmp/", 5) != 0) { | ||
590 | free(fname); | ||
591 | goto errexit; | ||
592 | } | ||
593 | } | ||
594 | else if (strncmp(new_name, "/media/", 7) == 0) { | ||
595 | entry->wldir = WLDIR_MEDIA; | ||
596 | media_dir = 1; | ||
597 | // both path and absolute path are under /media | ||
598 | if (strncmp(fname, "/media/", 7) != 0) { | ||
599 | free(fname); | ||
600 | goto errexit; | ||
601 | } | ||
602 | } | ||
603 | else if (strncmp(new_name, "/mnt/", 5) == 0) { | ||
604 | entry->wldir = WLDIR_MNT; | ||
605 | mnt_dir = 1; | ||
606 | // both path and absolute path are under /mnt | ||
607 | if (strncmp(fname, "/mnt/", 5) != 0) { | ||
608 | free(fname); | ||
609 | goto errexit; | ||
610 | } | ||
611 | } | ||
612 | else if (strncmp(new_name, "/var/", 5) == 0) { | ||
613 | entry->wldir = WLDIR_VAR; | ||
614 | var_dir = 1; | ||
615 | // both path and absolute path are under /var | ||
616 | // exceptions: /var/tmp, /var/run and /var/lock | ||
617 | if (strcmp(new_name, "/var/run")== 0 && strcmp(fname, "/run") == 0); | ||
618 | else if (strcmp(new_name, "/var/lock")== 0 && strcmp(fname, "/run/lock") == 0); | ||
619 | else if (strcmp(new_name, "/var/tmp")== 0 && strcmp(fname, "/tmp") == 0); | ||
620 | else { | ||
621 | // both path and absolute path are under /var | ||
622 | if (strncmp(fname, "/var/", 5) != 0) { | ||
623 | free(fname); | ||
624 | goto errexit; | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | else if (strncmp(new_name, "/dev/", 5) == 0) { | ||
629 | entry->wldir = WLDIR_DEV; | ||
630 | dev_dir = 1; | ||
631 | // special handling for /dev/shm | ||
632 | // on some platforms (Debian wheezy, Ubuntu 14.04), it is a symlink to /run/shm | ||
633 | if (strcmp(new_name, "/dev/shm") == 0 && strcmp(fname, "/run/shm") == 0); | ||
634 | // special handling for /dev/log, which can be a symlink to /run/systemd/journal/dev-log | ||
635 | else if (strcmp(new_name, "/dev/log") == 0 && strcmp(fname, "/run/systemd/journal/dev-log") == 0); | ||
636 | // special processing for /proc/self/fd files | ||
637 | else if (strcmp(new_name, "/dev/fd") == 0 && strcmp(fname, "/proc/self/fd") == 0); | ||
638 | else if (strcmp(new_name, "/dev/stdin") == 0 && strcmp(fname, "/proc/self/fd/0") == 0); | ||
639 | else if (strcmp(new_name, "/dev/stdout") == 0 && strcmp(fname, "/proc/self/fd/1") == 0); | ||
640 | else if (strcmp(new_name, "/dev/stderr") == 0 && strcmp(fname, "/proc/self/fd/2") == 0); | ||
641 | else { | ||
642 | // both path and absolute path are under /dev | ||
643 | if (strncmp(fname, "/dev/", 5) != 0) { | ||
644 | free(fname); | ||
645 | goto errexit; | ||
646 | } | ||
647 | } | ||
648 | } | ||
649 | else if (strncmp(new_name, "/opt/", 5) == 0) { | ||
650 | entry->wldir = WLDIR_OPT; | ||
651 | opt_dir = 1; | ||
652 | // both path and absolute path are under /dev | ||
653 | if (strncmp(fname, "/opt/", 5) != 0) { | ||
654 | free(fname); | ||
655 | goto errexit; | ||
656 | } | ||
657 | } | ||
658 | else if (strncmp(new_name, "/srv/", 5) == 0) { | ||
659 | entry->wldir = WLDIR_SRV; | ||
660 | srv_dir = 1; | ||
661 | // both path and absolute path are under /srv | ||
662 | if (strncmp(fname, "/srv/", 5) != 0) { | ||
663 | free(fname); | ||
664 | goto errexit; | ||
665 | } | ||
666 | } | ||
667 | else if (strncmp(new_name, "/etc/", 5) == 0) { | ||
668 | entry->wldir = WLDIR_ETC; | ||
669 | etc_dir = 1; | ||
670 | // special handling for some of the symlinks | ||
671 | if (strcmp(new_name, "/etc/localtime") == 0); | ||
672 | else if (strcmp(new_name, "/etc/mtab") == 0); | ||
673 | else if (strcmp(new_name, "/etc/os-release") == 0); | ||
674 | // both path and absolute path are under /etc | ||
675 | else { | ||
676 | if (strncmp(fname, "/etc/", 5) != 0) { | ||
677 | free(fname); | ||
678 | goto errexit; | ||
679 | } | ||
680 | } | ||
681 | } | ||
682 | else if (strncmp(new_name, "/usr/share/", 11) == 0) { | ||
683 | entry->wldir = WLDIR_SHARE; | ||
684 | share_dir = 1; | ||
685 | // both path and absolute path are under /etc | ||
686 | if (strncmp(fname, "/usr/share/", 11) != 0) { | ||
687 | free(fname); | ||
688 | goto errexit; | ||
689 | } | ||
690 | } | ||
691 | else if (strncmp(new_name, "/sys/module/", 12) == 0) { | ||
692 | entry->wldir = WLDIR_MODULE; | ||
693 | module_dir = 1; | ||
694 | // both path and absolute path are under /sys/module | ||
695 | if (strncmp(fname, "/sys/module/", 12) != 0) { | ||
696 | free(fname); | ||
697 | goto errexit; | ||
698 | } | ||
699 | } | ||
700 | else if (strncmp(new_name, runuser, runuser_len) == 0 && new_name[runuser_len] == '/') { | ||
701 | entry->wldir = WLDIR_RUN; | ||
702 | run_dir = 1; | ||
703 | // both path and absolute path are under /run/user/$uid | ||
704 | if (strncmp(fname, runuser, runuser_len) != 0 || fname[runuser_len] != '/') { | ||
705 | free(fname); | ||
706 | goto errexit; | ||
707 | } | ||
708 | } | ||
709 | else { | 668 | else { |
710 | free(fname); | 669 | // check if the path is in nowhitelist array |
711 | goto errexit; | ||
712 | } | ||
713 | |||
714 | // check if the path is in nowhitelist array | ||
715 | if (nowhitelist_flag == 0) { | ||
716 | size_t i; | 670 | size_t i; |
717 | int found = 0; | 671 | int found = 0; |
718 | for (i = 0; i < nowhitelist_c; i++) { | 672 | for (i = 0; i < nowhitelist_c; i++) { |
@@ -726,494 +680,76 @@ void fs_whitelist(void) { | |||
726 | if (found) { | 680 | if (found) { |
727 | if (arg_debug || arg_debug_whitelists) | 681 | if (arg_debug || arg_debug_whitelists) |
728 | printf("Skip nowhitelisted path %s\n", fname); | 682 | printf("Skip nowhitelisted path %s\n", fname); |
729 | entry->data = EMPTY_STRING; | ||
730 | entry = entry->next; | 683 | entry = entry->next; |
731 | free(fname); | ||
732 | free(new_name); | 684 | free(new_name); |
685 | free(fname); | ||
733 | continue; | 686 | continue; |
734 | } | 687 | } |
735 | } | 688 | } |
736 | 689 | ||
737 | // mark symbolic links | 690 | // attach whitelist parameters to profile entry |
691 | entry->wparam = calloc(1, sizeof(struct wparam_t)); | ||
692 | if (!entry->wparam) | ||
693 | errExit("calloc"); | ||
694 | |||
695 | assert(current_top); | ||
696 | entry->wparam->top = current_top; | ||
697 | entry->wparam->file = fname; | ||
698 | |||
699 | // mark link | ||
738 | if (is_link(new_name)) | 700 | if (is_link(new_name)) |
739 | entry->link = new_name; | 701 | entry->wparam->link = new_name; |
740 | else { | 702 | else |
741 | free(new_name); | 703 | free(new_name); |
742 | entry->link = NULL; | ||
743 | } | ||
744 | 704 | ||
745 | // change file name in entry->data | ||
746 | if (strcmp(fname, entry->data + 10) != 0) { | ||
747 | char *newdata; | ||
748 | if (asprintf(&newdata, "whitelist %s", fname) == -1) | ||
749 | errExit("asprintf"); | ||
750 | entry->data = newdata; | ||
751 | if (arg_debug || arg_debug_whitelists) | ||
752 | printf("Replaced whitelist path: %s\n", entry->data); | ||
753 | } | ||
754 | free(fname); | ||
755 | entry = entry->next; | 705 | entry = entry->next; |
756 | } | 706 | } |
757 | 707 | ||
758 | // release nowhitelist memory | 708 | // release nowhitelist memory |
759 | assert(nowhitelist); | ||
760 | free(nowhitelist); | 709 | free(nowhitelist); |
761 | 710 | ||
711 | // mount tmpfs on all top level directories | ||
762 | EUID_ROOT(); | 712 | EUID_ROOT(); |
763 | // /tmp mountpoint | 713 | tmpfs_topdirs(topdirs); |
764 | if (tmp_dir) { | ||
765 | // check if /tmp directory exists | ||
766 | if (stat("/tmp", &s) == 0) { | ||
767 | // keep a copy of real /tmp directory in RUN_WHITELIST_TMP_DIR | ||
768 | mkdir_attr(RUN_WHITELIST_TMP_DIR, 1777, 0, 0); | ||
769 | if (mount("/tmp", RUN_WHITELIST_TMP_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
770 | errExit("mount bind"); | ||
771 | |||
772 | // mount tmpfs on /tmp | ||
773 | if (arg_debug || arg_debug_whitelists) | ||
774 | printf("Mounting tmpfs on /tmp directory\n"); | ||
775 | if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=1777,gid=0") < 0) | ||
776 | errExit("mounting tmpfs on /tmp"); | ||
777 | selinux_relabel_path("/tmp", "/tmp"); | ||
778 | fs_logger("tmpfs /tmp"); | ||
779 | |||
780 | // pam-tmpdir - issue #2685 | ||
781 | const char *env = env_get("TMP"); | ||
782 | if (env) { | ||
783 | char *pamtmpdir; | ||
784 | if (asprintf(&pamtmpdir, "/tmp/user/%u", getuid()) == -1) | ||
785 | errExit("asprintf"); | ||
786 | if (strcmp(env, pamtmpdir) == 0) { | ||
787 | // create empty user-owned /tmp/user/$uid directory | ||
788 | mkdir_attr("/tmp/user", 0711, 0, 0); | ||
789 | selinux_relabel_path("/tmp/user", "/tmp/user"); | ||
790 | fs_logger("mkdir /tmp/user"); | ||
791 | mkdir_attr(pamtmpdir, 0700, getuid(), 0); | ||
792 | selinux_relabel_path(pamtmpdir, pamtmpdir); | ||
793 | fs_logger2("mkdir", pamtmpdir); | ||
794 | } | ||
795 | free(pamtmpdir); | ||
796 | } | ||
797 | |||
798 | // autowhitelist home directory if it is masked by the tmpfs | ||
799 | if (strncmp(cfg.homedir, "/tmp/", 5) == 0) | ||
800 | whitelist_home(WLDIR_TMP); | ||
801 | } | ||
802 | else | ||
803 | tmp_dir = 0; | ||
804 | } | ||
805 | |||
806 | // /media mountpoint | ||
807 | if (media_dir) { | ||
808 | // some distros don't have a /media directory | ||
809 | if (stat("/media", &s) == 0) { | ||
810 | // keep a copy of real /media directory in RUN_WHITELIST_MEDIA_DIR | ||
811 | mkdir_attr(RUN_WHITELIST_MEDIA_DIR, 0755, 0, 0); | ||
812 | if (mount("/media", RUN_WHITELIST_MEDIA_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
813 | errExit("mount bind"); | ||
814 | |||
815 | // mount tmpfs on /media | ||
816 | if (arg_debug || arg_debug_whitelists) | ||
817 | printf("Mounting tmpfs on /media directory\n"); | ||
818 | if (mount("tmpfs", "/media", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
819 | errExit("mounting tmpfs on /media"); | ||
820 | selinux_relabel_path("/media", "/media"); | ||
821 | fs_logger("tmpfs /media"); | ||
822 | |||
823 | // autowhitelist home directory if it is masked by the tmpfs | ||
824 | if (strncmp(cfg.homedir, "/media/", 7) == 0) | ||
825 | whitelist_home(WLDIR_MEDIA); | ||
826 | } | ||
827 | else | ||
828 | media_dir = 0; | ||
829 | } | ||
830 | |||
831 | // /mnt mountpoint | ||
832 | if (mnt_dir) { | ||
833 | // check if /mnt directory exists | ||
834 | if (stat("/mnt", &s) == 0) { | ||
835 | // keep a copy of real /mnt directory in RUN_WHITELIST_MNT_DIR | ||
836 | mkdir_attr(RUN_WHITELIST_MNT_DIR, 0755, 0, 0); | ||
837 | if (mount("/mnt", RUN_WHITELIST_MNT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
838 | errExit("mount bind"); | ||
839 | |||
840 | // mount tmpfs on /mnt | ||
841 | if (arg_debug || arg_debug_whitelists) | ||
842 | printf("Mounting tmpfs on /mnt directory\n"); | ||
843 | if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
844 | errExit("mounting tmpfs on /mnt"); | ||
845 | selinux_relabel_path("/mnt", "/mnt"); | ||
846 | fs_logger("tmpfs /mnt"); | ||
847 | |||
848 | // autowhitelist home directory if it is masked by the tmpfs | ||
849 | if (strncmp(cfg.homedir, "/mnt/", 5) == 0) | ||
850 | whitelist_home(WLDIR_MNT); | ||
851 | } | ||
852 | else | ||
853 | mnt_dir = 0; | ||
854 | } | ||
855 | |||
856 | // /var mountpoint | ||
857 | if (var_dir) { | ||
858 | // check if /var directory exists | ||
859 | if (stat("/var", &s) == 0) { | ||
860 | // keep a copy of real /var directory in RUN_WHITELIST_VAR_DIR | ||
861 | mkdir_attr(RUN_WHITELIST_VAR_DIR, 0755, 0, 0); | ||
862 | if (mount("/var", RUN_WHITELIST_VAR_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
863 | errExit("mount bind"); | ||
864 | |||
865 | // mount tmpfs on /var | ||
866 | if (arg_debug || arg_debug_whitelists) | ||
867 | printf("Mounting tmpfs on /var directory\n"); | ||
868 | if (mount("tmpfs", "/var", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
869 | errExit("mounting tmpfs on /var"); | ||
870 | selinux_relabel_path("/var", "/var"); | ||
871 | fs_logger("tmpfs /var"); | ||
872 | |||
873 | // autowhitelist home directory if it is masked by the tmpfs | ||
874 | if (strncmp(cfg.homedir, "/var/", 5) == 0) | ||
875 | whitelist_home(WLDIR_VAR); | ||
876 | } | ||
877 | else | ||
878 | var_dir = 0; | ||
879 | } | ||
880 | |||
881 | // /dev mountpoint | ||
882 | if (dev_dir) { | ||
883 | // check if /dev directory exists | ||
884 | if (stat("/dev", &s) == 0) { | ||
885 | // keep a copy of real /dev directory in RUN_WHITELIST_DEV_DIR | ||
886 | mkdir_attr(RUN_WHITELIST_DEV_DIR, 0755, 0, 0); | ||
887 | if (mount("/dev", RUN_WHITELIST_DEV_DIR, NULL, MS_BIND|MS_REC, "mode=755,gid=0") < 0) | ||
888 | errExit("mount bind"); | ||
889 | |||
890 | // mount tmpfs on /dev | ||
891 | if (arg_debug || arg_debug_whitelists) | ||
892 | printf("Mounting tmpfs on /dev directory\n"); | ||
893 | if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
894 | errExit("mounting tmpfs on /dev"); | ||
895 | selinux_relabel_path("/dev", "/dev"); | ||
896 | fs_logger("tmpfs /dev"); | ||
897 | |||
898 | // autowhitelist home directory if it is masked by the tmpfs | ||
899 | if (strncmp(cfg.homedir, "/dev/", 5) == 0) | ||
900 | whitelist_home(WLDIR_DEV); | ||
901 | } | ||
902 | else | ||
903 | dev_dir = 0; | ||
904 | } | ||
905 | |||
906 | // /opt mountpoint | ||
907 | if (opt_dir) { | ||
908 | // check if /opt directory exists | ||
909 | if (stat("/opt", &s) == 0) { | ||
910 | // keep a copy of real /opt directory in RUN_WHITELIST_OPT_DIR | ||
911 | mkdir_attr(RUN_WHITELIST_OPT_DIR, 0755, 0, 0); | ||
912 | if (mount("/opt", RUN_WHITELIST_OPT_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
913 | errExit("mount bind"); | ||
914 | |||
915 | // mount tmpfs on /opt | ||
916 | if (arg_debug || arg_debug_whitelists) | ||
917 | printf("Mounting tmpfs on /opt directory\n"); | ||
918 | if (mount("tmpfs", "/opt", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
919 | errExit("mounting tmpfs on /opt"); | ||
920 | selinux_relabel_path("/opt", "/opt"); | ||
921 | fs_logger("tmpfs /opt"); | ||
922 | |||
923 | // autowhitelist home directory if it is masked by the tmpfs | ||
924 | if (strncmp(cfg.homedir, "/opt/", 5) == 0) | ||
925 | whitelist_home(WLDIR_OPT); | ||
926 | } | ||
927 | else | ||
928 | opt_dir = 0; | ||
929 | } | ||
930 | |||
931 | // /srv mountpoint | ||
932 | if (srv_dir) { | ||
933 | // check if /srv directory exists | ||
934 | if (stat("/srv", &s) == 0) { | ||
935 | // keep a copy of real /srv directory in RUN_WHITELIST_SRV_DIR | ||
936 | mkdir_attr(RUN_WHITELIST_SRV_DIR, 0755, 0, 0); | ||
937 | if (mount("/srv", RUN_WHITELIST_SRV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
938 | errExit("mount bind"); | ||
939 | |||
940 | // mount tmpfs on /srv | ||
941 | if (arg_debug || arg_debug_whitelists) | ||
942 | printf("Mounting tmpfs on /srv directory\n"); | ||
943 | if (mount("tmpfs", "/srv", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
944 | errExit("mounting tmpfs on /srv"); | ||
945 | selinux_relabel_path("/srv", "/srv"); | ||
946 | fs_logger("tmpfs /srv"); | ||
947 | |||
948 | // autowhitelist home directory if it is masked by the tmpfs | ||
949 | if (strncmp(cfg.homedir, "/srv/", 5) == 0) | ||
950 | whitelist_home(WLDIR_SRV); | ||
951 | } | ||
952 | else | ||
953 | srv_dir = 0; | ||
954 | } | ||
955 | |||
956 | // /etc mountpoint | ||
957 | if (etc_dir) { | ||
958 | // check if /etc directory exists | ||
959 | if (stat("/etc", &s) == 0) { | ||
960 | // keep a copy of real /etc directory in RUN_WHITELIST_ETC_DIR | ||
961 | mkdir_attr(RUN_WHITELIST_ETC_DIR, 0755, 0, 0); | ||
962 | if (mount("/etc", RUN_WHITELIST_ETC_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
963 | errExit("mount bind"); | ||
964 | |||
965 | // mount tmpfs on /etc | ||
966 | if (arg_debug || arg_debug_whitelists) | ||
967 | printf("Mounting tmpfs on /etc directory\n"); | ||
968 | if (mount("tmpfs", "/etc", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
969 | errExit("mounting tmpfs on /etc"); | ||
970 | selinux_relabel_path("/etc", "/etc"); | ||
971 | fs_logger("tmpfs /etc"); | ||
972 | |||
973 | // autowhitelist home directory if it is masked by the tmpfs | ||
974 | if (strncmp(cfg.homedir, "/etc/", 5) == 0) | ||
975 | whitelist_home(WLDIR_ETC); | ||
976 | } | ||
977 | else | ||
978 | etc_dir = 0; | ||
979 | } | ||
980 | |||
981 | // /usr/share mountpoint | ||
982 | if (share_dir) { | ||
983 | // check if /usr/share directory exists | ||
984 | if (stat("/usr/share", &s) == 0) { | ||
985 | // keep a copy of real /usr/share directory in RUN_WHITELIST_ETC_DIR | ||
986 | mkdir_attr(RUN_WHITELIST_SHARE_DIR, 0755, 0, 0); | ||
987 | if (mount("/usr/share", RUN_WHITELIST_SHARE_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
988 | errExit("mount bind"); | ||
989 | |||
990 | // mount tmpfs on /srv | ||
991 | if (arg_debug || arg_debug_whitelists) | ||
992 | printf("Mounting tmpfs on /usr/share directory\n"); | ||
993 | if (mount("tmpfs", "/usr/share", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
994 | errExit("mounting tmpfs on /usr/share"); | ||
995 | selinux_relabel_path("/usr/share", "/usr/share"); | ||
996 | fs_logger("tmpfs /usr/share"); | ||
997 | |||
998 | // autowhitelist home directory if it is masked by the tmpfs | ||
999 | if (strncmp(cfg.homedir, "/usr/share/", 11) == 0) | ||
1000 | whitelist_home(WLDIR_SHARE); | ||
1001 | } | ||
1002 | else | ||
1003 | share_dir = 0; | ||
1004 | } | ||
1005 | |||
1006 | // /sys/module mountpoint | ||
1007 | if (module_dir) { | ||
1008 | // check if /sys/module directory exists | ||
1009 | if (stat("/sys/module", &s) == 0) { | ||
1010 | // keep a copy of real /sys/module directory in RUN_WHITELIST_MODULE_DIR | ||
1011 | mkdir_attr(RUN_WHITELIST_MODULE_DIR, 0755, 0, 0); | ||
1012 | if (mount("/sys/module", RUN_WHITELIST_MODULE_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1013 | errExit("mount bind"); | ||
1014 | |||
1015 | // mount tmpfs on /sys/module | ||
1016 | if (arg_debug || arg_debug_whitelists) | ||
1017 | printf("Mounting tmpfs on /sys/module directory\n"); | ||
1018 | if (mount("tmpfs", "/sys/module", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1019 | errExit("mounting tmpfs on /sys/module"); | ||
1020 | selinux_relabel_path("/sys/module", "/sys/module"); | ||
1021 | fs_logger("tmpfs /sys/module"); | ||
1022 | } | ||
1023 | else | ||
1024 | module_dir = 0; | ||
1025 | } | ||
1026 | |||
1027 | // /run/user/$uid mountpoint | ||
1028 | if (run_dir) { | ||
1029 | // check if /run/user/$uid directory exists | ||
1030 | if (stat(runuser, &s) == 0) { | ||
1031 | // keep a copy of real /run/user/$uid directory in RUN_WHITELIST_RUN_USER_DIR | ||
1032 | mkdir_attr(RUN_WHITELIST_RUN_USER_DIR, 0700, getuid(), getgid()); | ||
1033 | if (mount(runuser, RUN_WHITELIST_RUN_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1034 | errExit("mount bind"); | ||
1035 | |||
1036 | // mount tmpfs on /run/user/$uid | ||
1037 | if (arg_debug || arg_debug_whitelists) | ||
1038 | printf("Mounting tmpfs on %s directory\n", runuser); | ||
1039 | char *options; | ||
1040 | if (asprintf(&options, "mode=700,uid=%u,gid=%u", getuid(), getgid()) == -1) | ||
1041 | errExit("asprintf"); | ||
1042 | if (mount("tmpfs", runuser, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME, options) < 0) | ||
1043 | errExit("mounting tmpfs on /run/user/<uid>"); | ||
1044 | selinux_relabel_path(runuser, runuser); | ||
1045 | free(options); | ||
1046 | fs_logger2("tmpfs", runuser); | ||
1047 | |||
1048 | // autowhitelist home directory if it is masked by the tmpfs | ||
1049 | if (strncmp(cfg.homedir, runuser, runuser_len) == 0 && cfg.homedir[runuser_len] == '/') | ||
1050 | whitelist_home(WLDIR_RUN); | ||
1051 | } | ||
1052 | else | ||
1053 | run_dir = 0; | ||
1054 | } | ||
1055 | |||
1056 | // home mountpoint | ||
1057 | if (home_dir) { | ||
1058 | // check if home directory exists | ||
1059 | if (stat(cfg.homedir, &s) == 0) { | ||
1060 | // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR | ||
1061 | mkdir_attr(RUN_WHITELIST_HOME_USER_DIR, 0755, getuid(), getgid()); | ||
1062 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
1063 | if (fd == -1) | ||
1064 | errExit("safe_fd"); | ||
1065 | char *proc; | ||
1066 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
1067 | errExit("asprintf"); | ||
1068 | if (mount(proc, RUN_WHITELIST_HOME_USER_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) | ||
1069 | errExit("mount bind"); | ||
1070 | free(proc); | ||
1071 | close(fd); | ||
1072 | |||
1073 | // mount a tmpfs and initialize home directory | ||
1074 | fs_private(); | ||
1075 | } | ||
1076 | else | ||
1077 | home_dir = 0; | ||
1078 | } | ||
1079 | 714 | ||
1080 | // go through profile rules again, and interpret whitelist commands | 715 | // go through profile rules again, and interpret whitelist commands |
1081 | entry = cfg.profile; | 716 | entry = cfg.profile; |
1082 | while (entry) { | 717 | while (entry) { |
1083 | // handle only whitelist commands | 718 | if (entry->wparam) { |
1084 | if (strncmp(entry->data, "whitelist ", 10)) { | 719 | char *file = entry->wparam->file; |
1085 | entry = entry->next; | 720 | char *link = entry->wparam->link; |
1086 | continue; | 721 | const char *topdir = entry->wparam->top->path; |
1087 | } | 722 | size_t topdir_len = strlen(topdir); |
1088 | 723 | int dirfd = entry->wparam->top->fd; | |
1089 | //printf("here %d#%s#\n", __LINE__, entry->data); | 724 | |
1090 | // whitelist the real file | 725 | // top level directories of link and file can differ |
1091 | whitelist_path(entry); | 726 | // whitelist the file only if it is in same top level directory |
1092 | 727 | if (strncmp(file, topdir, topdir_len) == 0 && file[topdir_len] == '/') { | |
1093 | // create the link if any | 728 | // get path relative to top level directory |
1094 | if (entry->link) { | 729 | const char *rel = file + topdir_len + 1; |
1095 | // if the link is already there, do not bother | 730 | whitelist_file(dirfd, topdir, rel, file); |
1096 | if (lstat(entry->link, &s) != 0) { | ||
1097 | // create the path if necessary | ||
1098 | // entry->link has no trailing slashes or single dots | ||
1099 | int fd = mkpath(entry->link, 0755); | ||
1100 | if (fd == -1) { | ||
1101 | if (arg_debug || arg_debug_whitelists) | ||
1102 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); | ||
1103 | free(entry->link); | ||
1104 | entry->link = NULL; | ||
1105 | entry = entry->next; | ||
1106 | continue; | ||
1107 | } | ||
1108 | // get file name of symlink | ||
1109 | const char *file = gnu_basename(entry->link); | ||
1110 | // create the link | ||
1111 | int rv = symlinkat(entry->data + 10, fd, file); | ||
1112 | if (rv) { | ||
1113 | if (arg_debug || arg_debug_whitelists) { | ||
1114 | perror("symlink"); | ||
1115 | printf("Debug %d: cannot create symbolic link %s\n", __LINE__, entry->link); | ||
1116 | } | ||
1117 | } | ||
1118 | else if (arg_debug || arg_debug_whitelists) | ||
1119 | printf("Created symbolic link %s -> %s\n", entry->link, entry->data + 10); | ||
1120 | close(fd); | ||
1121 | } | 731 | } |
1122 | free(entry->link); | ||
1123 | entry->link = NULL; | ||
1124 | } | ||
1125 | |||
1126 | entry = entry->next; | ||
1127 | } | ||
1128 | |||
1129 | // mask the real home directory, currently mounted on RUN_WHITELIST_HOME_DIR | ||
1130 | if (home_dir) { | ||
1131 | if (mount("tmpfs", RUN_WHITELIST_HOME_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1132 | errExit("mount tmpfs"); | ||
1133 | fs_logger2("tmpfs", RUN_WHITELIST_HOME_USER_DIR); | ||
1134 | } | ||
1135 | |||
1136 | // mask the real /tmp directory, currently mounted on RUN_WHITELIST_TMP_DIR | ||
1137 | if (tmp_dir) { | ||
1138 | if (mount("tmpfs", RUN_WHITELIST_TMP_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1139 | errExit("mount tmpfs"); | ||
1140 | fs_logger2("tmpfs", RUN_WHITELIST_TMP_DIR); | ||
1141 | } | ||
1142 | |||
1143 | // mask the real /var directory, currently mounted on RUN_WHITELIST_VAR_DIR | ||
1144 | if (var_dir) { | ||
1145 | if (mount("tmpfs", RUN_WHITELIST_VAR_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1146 | errExit("mount tmpfs"); | ||
1147 | fs_logger2("tmpfs", RUN_WHITELIST_VAR_DIR); | ||
1148 | } | ||
1149 | |||
1150 | // mask the real /opt directory, currently mounted on RUN_WHITELIST_OPT_DIR | ||
1151 | if (opt_dir) { | ||
1152 | if (mount("tmpfs", RUN_WHITELIST_OPT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1153 | errExit("mount tmpfs"); | ||
1154 | fs_logger2("tmpfs", RUN_WHITELIST_OPT_DIR); | ||
1155 | } | ||
1156 | |||
1157 | // mask the real /dev directory, currently mounted on RUN_WHITELIST_DEV_DIR | ||
1158 | if (dev_dir) { | ||
1159 | if (mount("tmpfs", RUN_WHITELIST_DEV_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1160 | errExit("mount tmpfs"); | ||
1161 | fs_logger2("tmpfs", RUN_WHITELIST_DEV_DIR); | ||
1162 | } | ||
1163 | |||
1164 | // mask the real /media directory, currently mounted on RUN_WHITELIST_MEDIA_DIR | ||
1165 | if (media_dir) { | ||
1166 | if (mount("tmpfs", RUN_WHITELIST_MEDIA_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1167 | errExit("mount tmpfs"); | ||
1168 | fs_logger2("tmpfs", RUN_WHITELIST_MEDIA_DIR); | ||
1169 | } | ||
1170 | 732 | ||
1171 | // mask the real /mnt directory, currently mounted on RUN_WHITELIST_MNT_DIR | 733 | // create the link if any |
1172 | if (mnt_dir) { | 734 | if (link) |
1173 | if (mount("tmpfs", RUN_WHITELIST_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 735 | whitelist_symlink(topdir, link, file); |
1174 | errExit("mount tmpfs"); | ||
1175 | fs_logger2("tmpfs", RUN_WHITELIST_MNT_DIR); | ||
1176 | } | ||
1177 | |||
1178 | // mask the real /srv directory, currently mounted on RUN_WHITELIST_SRV_DIR | ||
1179 | if (srv_dir) { | ||
1180 | if (mount("tmpfs", RUN_WHITELIST_SRV_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1181 | errExit("mount tmpfs"); | ||
1182 | fs_logger2("tmpfs", RUN_WHITELIST_SRV_DIR); | ||
1183 | } | ||
1184 | |||
1185 | // mask the real /etc directory, currently mounted on RUN_WHITELIST_ETC_DIR | ||
1186 | if (etc_dir) { | ||
1187 | if (mount("tmpfs", RUN_WHITELIST_ETC_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1188 | errExit("mount tmpfs"); | ||
1189 | fs_logger2("tmpfs", RUN_WHITELIST_ETC_DIR); | ||
1190 | } | ||
1191 | |||
1192 | // mask the real /usr/share directory, currently mounted on RUN_WHITELIST_SHARE_DIR | ||
1193 | if (share_dir) { | ||
1194 | if (mount("tmpfs", RUN_WHITELIST_SHARE_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1195 | errExit("mount tmpfs"); | ||
1196 | fs_logger2("tmpfs", RUN_WHITELIST_SHARE_DIR); | ||
1197 | } | ||
1198 | 736 | ||
1199 | // mask the real /sys/module directory, currently mounted on RUN_WHITELIST_MODULE_DIR | 737 | free(link); |
1200 | if (module_dir) { | 738 | free(file); |
1201 | if (mount("tmpfs", RUN_WHITELIST_MODULE_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | 739 | free(entry->wparam); |
1202 | errExit("mount tmpfs"); | 740 | entry->wparam = NULL; |
1203 | fs_logger2("tmpfs", RUN_WHITELIST_MODULE_DIR); | 741 | } |
1204 | } | ||
1205 | 742 | ||
1206 | // mask the real /run/user/$uid directory, currently mounted on RUN_WHITELIST_RUN_USER_DIR | 743 | entry = entry->next; |
1207 | if (run_dir) { | ||
1208 | if (mount("tmpfs", RUN_WHITELIST_RUN_USER_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,gid=0") < 0) | ||
1209 | errExit("mount tmpfs"); | ||
1210 | fs_logger2("tmpfs", RUN_WHITELIST_RUN_USER_DIR); | ||
1211 | } | 744 | } |
1212 | 745 | ||
746 | // release resources | ||
1213 | free(runuser); | 747 | free(runuser); |
1214 | return; | ||
1215 | 748 | ||
1216 | errexit: | 749 | size_t i; |
1217 | fprintf(stderr, "Error: invalid whitelist path %s\n", new_name); | 750 | for (i = 0; i < TOP_MAX && topdirs[i].path; i++) { |
1218 | exit(1); | 751 | free(topdirs[i].path); |
752 | close(topdirs[i].fd); | ||
753 | } | ||
754 | free(topdirs); | ||
1219 | } | 755 | } |
diff --git a/src/firejail/join.c b/src/firejail/join.c index 1575a7469..bab4b830f 100644 --- a/src/firejail/join.c +++ b/src/firejail/join.c | |||
@@ -103,7 +103,7 @@ static void extract_x11_display(pid_t pid) { | |||
103 | if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1) | 103 | if (asprintf(&fname, "%s/%d", RUN_FIREJAIL_X11_DIR, pid) == -1) |
104 | errExit("asprintf"); | 104 | errExit("asprintf"); |
105 | 105 | ||
106 | FILE *fp = fopen(fname, "r"); | 106 | FILE *fp = fopen(fname, "re"); |
107 | free(fname); | 107 | free(fname); |
108 | if (!fp) | 108 | if (!fp) |
109 | return; | 109 | return; |
@@ -219,7 +219,7 @@ static void extract_caps(pid_t pid) { | |||
219 | perror("asprintf"); | 219 | perror("asprintf"); |
220 | exit(1); | 220 | exit(1); |
221 | } | 221 | } |
222 | FILE *fp = fopen(file, "r"); | 222 | FILE *fp = fopen(file, "re"); |
223 | if (!fp) | 223 | if (!fp) |
224 | goto errexit; | 224 | goto errexit; |
225 | 225 | ||
@@ -266,7 +266,7 @@ static void extract_user_namespace(pid_t pid) { | |||
266 | char *uidmap; | 266 | char *uidmap; |
267 | if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1) | 267 | if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1) |
268 | errExit("asprintf"); | 268 | errExit("asprintf"); |
269 | FILE *fp = fopen(uidmap, "r"); | 269 | FILE *fp = fopen(uidmap, "re"); |
270 | if (!fp) { | 270 | if (!fp) { |
271 | free(uidmap); | 271 | free(uidmap); |
272 | return; | 272 | return; |
diff --git a/src/firejail/ls.c b/src/firejail/ls.c index 63ef2309b..796c42290 100644 --- a/src/firejail/ls.c +++ b/src/firejail/ls.c | |||
@@ -221,7 +221,7 @@ void cat(const char *path) { | |||
221 | 221 | ||
222 | if (arg_debug) | 222 | if (arg_debug) |
223 | printf("cat %s\n", path); | 223 | printf("cat %s\n", path); |
224 | FILE *fp = fopen(path, "r"); | 224 | FILE *fp = fopen(path, "re"); |
225 | if (!fp) { | 225 | if (!fp) { |
226 | fprintf(stderr, "Error: cannot read %s\n", path); | 226 | fprintf(stderr, "Error: cannot read %s\n", path); |
227 | exit(1); | 227 | exit(1); |
diff --git a/src/firejail/macros.c b/src/firejail/macros.c index 7f2f6dbf3..bcac1feb4 100644 --- a/src/firejail/macros.c +++ b/src/firejail/macros.c | |||
@@ -99,7 +99,7 @@ static char *resolve_xdg(const char *var) { | |||
99 | 99 | ||
100 | if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1) | 100 | if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1) |
101 | errExit("asprintf"); | 101 | errExit("asprintf"); |
102 | FILE *fp = fopen(fname, "r"); | 102 | FILE *fp = fopen(fname, "re"); |
103 | if (!fp) { | 103 | if (!fp) { |
104 | free(fname); | 104 | free(fname); |
105 | return NULL; | 105 | return NULL; |
diff --git a/src/firejail/main.c b/src/firejail/main.c index f5797a2d8..7cfa58078 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -116,7 +116,6 @@ int arg_private_cwd = 0; // private working directory | |||
116 | int arg_scan = 0; // arp-scan all interfaces | 116 | int arg_scan = 0; // arp-scan all interfaces |
117 | int arg_whitelist = 0; // whitelist command | 117 | int arg_whitelist = 0; // whitelist command |
118 | int arg_nosound = 0; // disable sound | 118 | int arg_nosound = 0; // disable sound |
119 | int arg_noautopulse = 0; // disable automatic ~/.config/pulse init | ||
120 | int arg_novideo = 0; //disable video devices in /dev | 119 | int arg_novideo = 0; //disable video devices in /dev |
121 | int arg_no3d; // disable 3d hardware acceleration | 120 | int arg_no3d; // disable 3d hardware acceleration |
122 | int arg_quiet = 0; // no output for scripting | 121 | int arg_quiet = 0; // no output for scripting |
@@ -125,6 +124,7 @@ int arg_join_filesystem = 0; // join only the mount namespace | |||
125 | int arg_nice = 0; // nice value configured | 124 | int arg_nice = 0; // nice value configured |
126 | int arg_ipc = 0; // enable ipc namespace | 125 | int arg_ipc = 0; // enable ipc namespace |
127 | int arg_writable_etc = 0; // writable etc | 126 | int arg_writable_etc = 0; // writable etc |
127 | int arg_keep_config_pulse = 0; // disable automatic ~/.config/pulse init | ||
128 | int arg_writable_var = 0; // writable var | 128 | int arg_writable_var = 0; // writable var |
129 | int arg_keep_var_tmp = 0; // don't overwrite /var/tmp | 129 | int arg_keep_var_tmp = 0; // don't overwrite /var/tmp |
130 | int arg_writable_run_user = 0; // writable /run/user | 130 | int arg_writable_run_user = 0; // writable /run/user |
@@ -535,7 +535,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { | |||
535 | char *fname; | 535 | char *fname; |
536 | if (asprintf(&fname, RUN_FIREJAIL_PROFILE_DIR "/%d", pid) == -1) | 536 | if (asprintf(&fname, RUN_FIREJAIL_PROFILE_DIR "/%d", pid) == -1) |
537 | errExit("asprintf"); | 537 | errExit("asprintf"); |
538 | FILE *fp = fopen(fname, "r"); | 538 | FILE *fp = fopen(fname, "re"); |
539 | if (!fp) { | 539 | if (!fp) { |
540 | fprintf(stderr, "Error: sandbox %s not found\n", argv[i] + 16); | 540 | fprintf(stderr, "Error: sandbox %s not found\n", argv[i] + 16); |
541 | exit(1); | 541 | exit(1); |
@@ -1051,7 +1051,7 @@ int main(int argc, char **argv, char **envp) { | |||
1051 | preproc_build_firejail_dir(); | 1051 | preproc_build_firejail_dir(); |
1052 | const char *container_name = env_get("container"); | 1052 | const char *container_name = env_get("container"); |
1053 | if (!container_name || strcmp(container_name, "firejail")) { | 1053 | if (!container_name || strcmp(container_name, "firejail")) { |
1054 | lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | 1054 | lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); |
1055 | if (lockfd_directory != -1) { | 1055 | if (lockfd_directory != -1) { |
1056 | int rv = fchown(lockfd_directory, 0, 0); | 1056 | int rv = fchown(lockfd_directory, 0, 0); |
1057 | (void) rv; | 1057 | (void) rv; |
@@ -1153,7 +1153,7 @@ int main(int argc, char **argv, char **envp) { | |||
1153 | 1153 | ||
1154 | #ifdef DEBUG_RESTRICTED_SHELL | 1154 | #ifdef DEBUG_RESTRICTED_SHELL |
1155 | {EUID_ROOT(); | 1155 | {EUID_ROOT(); |
1156 | FILE *fp = fopen("/firelog", "w"); | 1156 | FILE *fp = fopen("/firelog", "we"); |
1157 | if (fp) { | 1157 | if (fp) { |
1158 | int i; | 1158 | int i; |
1159 | fprintf(fp, "argc %d: ", argc); | 1159 | fprintf(fp, "argc %d: ", argc); |
@@ -1172,7 +1172,7 @@ int main(int argc, char **argv, char **envp) { | |||
1172 | strncmp(argv[2], "scp ", 4) == 0) { | 1172 | strncmp(argv[2], "scp ", 4) == 0) { |
1173 | #ifdef DEBUG_RESTRICTED_SHELL | 1173 | #ifdef DEBUG_RESTRICTED_SHELL |
1174 | {EUID_ROOT(); | 1174 | {EUID_ROOT(); |
1175 | FILE *fp = fopen("/firelog", "a"); | 1175 | FILE *fp = fopen("/firelog", "ae"); |
1176 | if (fp) { | 1176 | if (fp) { |
1177 | fprintf(fp, "run without a sandbox\n"); | 1177 | fprintf(fp, "run without a sandbox\n"); |
1178 | fclose(fp); | 1178 | fclose(fp); |
@@ -1205,7 +1205,7 @@ int main(int argc, char **argv, char **envp) { | |||
1205 | 1205 | ||
1206 | #ifdef DEBUG_RESTRICTED_SHELL | 1206 | #ifdef DEBUG_RESTRICTED_SHELL |
1207 | {EUID_ROOT(); | 1207 | {EUID_ROOT(); |
1208 | FILE *fp = fopen("/firelog", "a"); | 1208 | FILE *fp = fopen("/firelog", "ae"); |
1209 | if (fp) { | 1209 | if (fp) { |
1210 | fprintf(fp, "fullargc %d: ", fullargc); | 1210 | fprintf(fp, "fullargc %d: ", fullargc); |
1211 | int i; | 1211 | int i; |
@@ -1227,7 +1227,7 @@ int main(int argc, char **argv, char **envp) { | |||
1227 | 1227 | ||
1228 | #ifdef DEBUG_RESTRICTED_SHELL | 1228 | #ifdef DEBUG_RESTRICTED_SHELL |
1229 | {EUID_ROOT(); | 1229 | {EUID_ROOT(); |
1230 | FILE *fp = fopen("/firelog", "a"); | 1230 | FILE *fp = fopen("/firelog", "ae"); |
1231 | if (fp) { | 1231 | if (fp) { |
1232 | fprintf(fp, "argc %d: ", argc); | 1232 | fprintf(fp, "argc %d: ", argc); |
1233 | int i; | 1233 | int i; |
@@ -1832,6 +1832,8 @@ int main(int argc, char **argv, char **envp) { | |||
1832 | exit(1); | 1832 | exit(1); |
1833 | } | 1833 | } |
1834 | arg_noprofile = 1; | 1834 | arg_noprofile = 1; |
1835 | // force keep-config-pulse in order to keep ~/.config/pulse as is | ||
1836 | arg_keep_config_pulse = 1; | ||
1835 | } | 1837 | } |
1836 | else if (strncmp(argv[i], "--ignore=", 9) == 0) { | 1838 | else if (strncmp(argv[i], "--ignore=", 9) == 0) { |
1837 | if (custom_profile) { | 1839 | if (custom_profile) { |
@@ -1882,6 +1884,9 @@ int main(int argc, char **argv, char **envp) { | |||
1882 | } | 1884 | } |
1883 | arg_writable_etc = 1; | 1885 | arg_writable_etc = 1; |
1884 | } | 1886 | } |
1887 | else if (strcmp(argv[i], "--keep-config-pulse") == 0) { | ||
1888 | arg_keep_config_pulse = 1; | ||
1889 | } | ||
1885 | else if (strcmp(argv[i], "--writable-var") == 0) { | 1890 | else if (strcmp(argv[i], "--writable-var") == 0) { |
1886 | arg_writable_var = 1; | 1891 | arg_writable_var = 1; |
1887 | } | 1892 | } |
@@ -2084,7 +2089,7 @@ int main(int argc, char **argv, char **envp) { | |||
2084 | else if (strcmp(argv[i], "--nosound") == 0) | 2089 | else if (strcmp(argv[i], "--nosound") == 0) |
2085 | arg_nosound = 1; | 2090 | arg_nosound = 1; |
2086 | else if (strcmp(argv[i], "--noautopulse") == 0) | 2091 | else if (strcmp(argv[i], "--noautopulse") == 0) |
2087 | arg_noautopulse = 1; | 2092 | arg_keep_config_pulse = 1; |
2088 | else if (strcmp(argv[i], "--novideo") == 0) | 2093 | else if (strcmp(argv[i], "--novideo") == 0) |
2089 | arg_novideo = 1; | 2094 | arg_novideo = 1; |
2090 | else if (strcmp(argv[i], "--no3d") == 0) | 2095 | else if (strcmp(argv[i], "--no3d") == 0) |
@@ -2858,7 +2863,7 @@ int main(int argc, char **argv, char **envp) { | |||
2858 | // check and assign an IP address - for macvlan it will be done again in the sandbox! | 2863 | // check and assign an IP address - for macvlan it will be done again in the sandbox! |
2859 | if (any_bridge_configured()) { | 2864 | if (any_bridge_configured()) { |
2860 | EUID_ROOT(); | 2865 | EUID_ROOT(); |
2861 | lockfd_network = open(RUN_NETWORK_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | 2866 | lockfd_network = open(RUN_NETWORK_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); |
2862 | if (lockfd_network != -1) { | 2867 | if (lockfd_network != -1) { |
2863 | int rv = fchown(lockfd_network, 0, 0); | 2868 | int rv = fchown(lockfd_network, 0, 0); |
2864 | (void) rv; | 2869 | (void) rv; |
@@ -2880,12 +2885,6 @@ int main(int argc, char **argv, char **envp) { | |||
2880 | } | 2885 | } |
2881 | EUID_ASSERT(); | 2886 | EUID_ASSERT(); |
2882 | 2887 | ||
2883 | // create the parent-child communication pipe | ||
2884 | if (pipe(parent_to_child_fds) < 0) | ||
2885 | errExit("pipe"); | ||
2886 | if (pipe(child_to_parent_fds) < 0) | ||
2887 | errExit("pipe"); | ||
2888 | |||
2889 | if (arg_noroot && arg_overlay) { | 2888 | if (arg_noroot && arg_overlay) { |
2890 | fwarning("--overlay and --noroot are mutually exclusive, noroot disabled\n"); | 2889 | fwarning("--overlay and --noroot are mutually exclusive, noroot disabled\n"); |
2891 | arg_noroot = 0; | 2890 | arg_noroot = 0; |
@@ -2898,7 +2897,7 @@ int main(int argc, char **argv, char **envp) { | |||
2898 | 2897 | ||
2899 | // set name and x11 run files | 2898 | // set name and x11 run files |
2900 | EUID_ROOT(); | 2899 | EUID_ROOT(); |
2901 | lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | 2900 | lockfd_directory = open(RUN_DIRECTORY_LOCK_FILE, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); |
2902 | if (lockfd_directory != -1) { | 2901 | if (lockfd_directory != -1) { |
2903 | int rv = fchown(lockfd_directory, 0, 0); | 2902 | int rv = fchown(lockfd_directory, 0, 0); |
2904 | (void) rv; | 2903 | (void) rv; |
@@ -2927,6 +2926,12 @@ int main(int argc, char **argv, char **envp) { | |||
2927 | } | 2926 | } |
2928 | #endif | 2927 | #endif |
2929 | 2928 | ||
2929 | // create the parent-child communication pipe | ||
2930 | if (pipe2(parent_to_child_fds, O_CLOEXEC) < 0) | ||
2931 | errExit("pipe"); | ||
2932 | if (pipe2(child_to_parent_fds, O_CLOEXEC) < 0) | ||
2933 | errExit("pipe"); | ||
2934 | |||
2930 | // clone environment | 2935 | // clone environment |
2931 | int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; | 2936 | int flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD; |
2932 | 2937 | ||
diff --git a/src/firejail/network.c b/src/firejail/network.c index f7142cefd..289e164c6 100644 --- a/src/firejail/network.c +++ b/src/firejail/network.c | |||
@@ -217,7 +217,7 @@ int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { | |||
217 | 217 | ||
218 | #define BUFSIZE 1024 | 218 | #define BUFSIZE 1024 |
219 | uint32_t network_get_defaultgw(void) { | 219 | uint32_t network_get_defaultgw(void) { |
220 | FILE *fp = fopen("/proc/self/net/route", "r"); | 220 | FILE *fp = fopen("/proc/self/net/route", "re"); |
221 | if (!fp) | 221 | if (!fp) |
222 | errExit("fopen"); | 222 | errExit("fopen"); |
223 | 223 | ||
diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index ee3c00872..d3e75bbed 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c | |||
@@ -292,7 +292,7 @@ void net_dns_print(pid_t pid) { | |||
292 | errExit("chdir"); | 292 | errExit("chdir"); |
293 | 293 | ||
294 | // access /etc/resolv.conf | 294 | // access /etc/resolv.conf |
295 | FILE *fp = fopen("/etc/resolv.conf", "r"); | 295 | FILE *fp = fopen("/etc/resolv.conf", "re"); |
296 | if (!fp) { | 296 | if (!fp) { |
297 | fprintf(stderr, "Error: cannot access /etc/resolv.conf\n"); | 297 | fprintf(stderr, "Error: cannot access /etc/resolv.conf\n"); |
298 | exit(1); | 298 | exit(1); |
diff --git a/src/firejail/no_sandbox.c b/src/firejail/no_sandbox.c index 60a82821e..0e153c47b 100644 --- a/src/firejail/no_sandbox.c +++ b/src/firejail/no_sandbox.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "firejail.h" | 20 | #include "firejail.h" |
21 | #include <sys/types.h> | 21 | #include <sys/types.h> |
22 | #include <sys/stat.h> | 22 | #include <sys/stat.h> |
23 | #include <errno.h> | ||
23 | #include <unistd.h> | 24 | #include <unistd.h> |
24 | #include <grp.h> | 25 | #include <grp.h> |
25 | 26 | ||
@@ -47,7 +48,7 @@ int check_namespace_virt(void) { | |||
47 | 48 | ||
48 | // check PID 1 container environment variable | 49 | // check PID 1 container environment variable |
49 | EUID_ROOT(); | 50 | EUID_ROOT(); |
50 | FILE *fp = fopen("/proc/1/environ", "r"); | 51 | FILE *fp = fopen("/proc/1/environ", "re"); |
51 | if (fp) { | 52 | if (fp) { |
52 | int c = 0; | 53 | int c = 0; |
53 | while (c != EOF) { | 54 | while (c != EOF) { |
@@ -105,20 +106,15 @@ int check_kernel_procs(void) { | |||
105 | // look at the first 10 processes | 106 | // look at the first 10 processes |
106 | // if a kernel process is found, return 1 | 107 | // if a kernel process is found, return 1 |
107 | for (i = 1; i <= 10; i++) { | 108 | for (i = 1; i <= 10; i++) { |
108 | struct stat s; | ||
109 | char *fname; | 109 | char *fname; |
110 | if (asprintf(&fname, "/proc/%d/comm", i) == -1) | 110 | if (asprintf(&fname, "/proc/%d/comm", i) == -1) |
111 | errExit("asprintf"); | 111 | errExit("asprintf"); |
112 | if (stat(fname, &s) == -1) { | ||
113 | free(fname); | ||
114 | continue; | ||
115 | } | ||
116 | 112 | ||
117 | // open file | 113 | // open file |
118 | /* coverity[toctou] */ | 114 | FILE *fp = fopen(fname, "re"); |
119 | FILE *fp = fopen(fname, "r"); | ||
120 | if (!fp) { | 115 | if (!fp) { |
121 | fwarning("cannot open %s\n", fname); | 116 | if (errno != ENOENT) |
117 | fwarning("cannot open %s\n", fname); | ||
122 | free(fname); | 118 | free(fname); |
123 | continue; | 119 | continue; |
124 | } | 120 | } |
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c index 7f602545d..1aafd1ca2 100644 --- a/src/firejail/preproc.c +++ b/src/firejail/preproc.c | |||
@@ -164,7 +164,7 @@ void preproc_clean_run(void) { | |||
164 | int max_pids=32769; | 164 | int max_pids=32769; |
165 | int start_pid = 100; | 165 | int start_pid = 100; |
166 | // extract real max_pids | 166 | // extract real max_pids |
167 | FILE *fp = fopen("/proc/sys/kernel/pid_max", "r"); | 167 | FILE *fp = fopen("/proc/sys/kernel/pid_max", "re"); |
168 | if (fp) { | 168 | if (fp) { |
169 | int val; | 169 | int val; |
170 | if (fscanf(fp, "%d", &val) == 1) { | 170 | if (fscanf(fp, "%d", &val) == 1) { |
diff --git a/src/firejail/profile.c b/src/firejail/profile.c index 2ea32b665..dd4506ac1 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -423,7 +423,7 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
423 | return 0; | 423 | return 0; |
424 | } | 424 | } |
425 | else if (strcmp(ptr, "noautopulse") == 0) { | 425 | else if (strcmp(ptr, "noautopulse") == 0) { |
426 | arg_noautopulse = 1; | 426 | arg_keep_config_pulse = 1; |
427 | return 0; | 427 | return 0; |
428 | } | 428 | } |
429 | else if (strcmp(ptr, "notv") == 0) { | 429 | else if (strcmp(ptr, "notv") == 0) { |
@@ -1143,6 +1143,12 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1143 | arg_machineid = 1; | 1143 | arg_machineid = 1; |
1144 | return 0; | 1144 | return 0; |
1145 | } | 1145 | } |
1146 | |||
1147 | if (strcmp(ptr, "keep-config-pulse") == 0) { | ||
1148 | arg_keep_config_pulse = 1; | ||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1146 | // writable-var | 1152 | // writable-var |
1147 | if (strcmp(ptr, "writable-var") == 0) { | 1153 | if (strcmp(ptr, "writable-var") == 0) { |
1148 | arg_writable_var = 1; | 1154 | arg_writable_var = 1; |
@@ -1691,7 +1697,7 @@ void profile_read(const char *fname) { | |||
1691 | } | 1697 | } |
1692 | 1698 | ||
1693 | // open profile file: | 1699 | // open profile file: |
1694 | FILE *fp = fopen(fname, "r"); | 1700 | FILE *fp = fopen(fname, "re"); |
1695 | if (fp == NULL) { | 1701 | if (fp == NULL) { |
1696 | fprintf(stderr, "Error: cannot open profile file %s\n", fname); | 1702 | fprintf(stderr, "Error: cannot open profile file %s\n", fname); |
1697 | exit(1); | 1703 | exit(1); |
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 926af7967..f21f8c96e 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c | |||
@@ -23,7 +23,7 @@ | |||
23 | 23 | ||
24 | void protocol_filter_save(void) { | 24 | void protocol_filter_save(void) { |
25 | // save protocol filter configuration in PROTOCOL_CFG | 25 | // save protocol filter configuration in PROTOCOL_CFG |
26 | FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); | 26 | FILE *fp = fopen(RUN_PROTOCOL_CFG, "wxe"); |
27 | if (!fp) | 27 | if (!fp) |
28 | errExit("fopen"); | 28 | errExit("fopen"); |
29 | fprintf(fp, "%s\n", cfg.protocol); | 29 | fprintf(fp, "%s\n", cfg.protocol); |
@@ -35,7 +35,7 @@ void protocol_filter_load(const char *fname) { | |||
35 | assert(fname); | 35 | assert(fname); |
36 | 36 | ||
37 | // read protocol filter configuration from PROTOCOL_CFG | 37 | // read protocol filter configuration from PROTOCOL_CFG |
38 | FILE *fp = fopen(fname, "r"); | 38 | FILE *fp = fopen(fname, "re"); |
39 | if (!fp) | 39 | if (!fp) |
40 | return; | 40 | return; |
41 | 41 | ||
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 4b9203c36..1b01a71c6 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -106,7 +106,7 @@ void pulseaudio_init(void) { | |||
106 | errExit("asprintf"); | 106 | errExit("asprintf"); |
107 | if (copy_file(PULSE_CLIENT_SYSCONF, pulsecfg, -1, -1, 0644)) // root needed | 107 | if (copy_file(PULSE_CLIENT_SYSCONF, pulsecfg, -1, -1, 0644)) // root needed |
108 | errExit("copy_file"); | 108 | errExit("copy_file"); |
109 | FILE *fp = fopen(pulsecfg, "a"); | 109 | FILE *fp = fopen(pulsecfg, "ae"); |
110 | if (!fp) | 110 | if (!fp) |
111 | errExit("fopen"); | 111 | errExit("fopen"); |
112 | fprintf(fp, "%s", "\nenable-shm = no\n"); | 112 | fprintf(fp, "%s", "\nenable-shm = no\n"); |
@@ -131,7 +131,7 @@ void pulseaudio_init(void) { | |||
131 | 131 | ||
132 | // if ~/.config/pulse exists and there are no symbolic links, mount the new directory | 132 | // if ~/.config/pulse exists and there are no symbolic links, mount the new directory |
133 | // else set environment variable | 133 | // else set environment variable |
134 | int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 134 | int fd = safer_openat(-1, homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
135 | if (fd == -1) { | 135 | if (fd == -1) { |
136 | pulseaudio_fallback(pulsecfg); | 136 | pulseaudio_fallback(pulsecfg); |
137 | goto out; | 137 | goto out; |
diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index a0ca4c02c..53e395b89 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c | |||
@@ -73,7 +73,7 @@ static void sanitize_home(void) { | |||
73 | if (arg_debug) | 73 | if (arg_debug) |
74 | printf("Cleaning /home directory\n"); | 74 | printf("Cleaning /home directory\n"); |
75 | // open user home directory in order to keep it around | 75 | // open user home directory in order to keep it around |
76 | int fd = safe_fd(cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 76 | int fd = safer_openat(-1, cfg.homedir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
77 | if (fd == -1) | 77 | if (fd == -1) |
78 | goto errout; | 78 | goto errout; |
79 | if (fstat(fd, &s) == -1) { // FUSE | 79 | if (fstat(fd, &s) == -1) { // FUSE |
@@ -183,10 +183,10 @@ static void sanitize_passwd(void) { | |||
183 | 183 | ||
184 | // open files | 184 | // open files |
185 | /* coverity[toctou] */ | 185 | /* coverity[toctou] */ |
186 | fpin = fopen("/etc/passwd", "r"); | 186 | fpin = fopen("/etc/passwd", "re"); |
187 | if (!fpin) | 187 | if (!fpin) |
188 | goto errout; | 188 | goto errout; |
189 | fpout = fopen(RUN_PASSWD_FILE, "w"); | 189 | fpout = fopen(RUN_PASSWD_FILE, "we"); |
190 | if (!fpout) | 190 | if (!fpout) |
191 | goto errout; | 191 | goto errout; |
192 | 192 | ||
@@ -318,10 +318,10 @@ static void sanitize_group(void) { | |||
318 | 318 | ||
319 | // open files | 319 | // open files |
320 | /* coverity[toctou] */ | 320 | /* coverity[toctou] */ |
321 | fpin = fopen("/etc/group", "r"); | 321 | fpin = fopen("/etc/group", "re"); |
322 | if (!fpin) | 322 | if (!fpin) |
323 | goto errout; | 323 | goto errout; |
324 | fpout = fopen(RUN_GROUP_FILE, "w"); | 324 | fpout = fopen(RUN_GROUP_FILE, "we"); |
325 | if (!fpout) | 325 | if (!fpout) |
326 | goto errout; | 326 | goto errout; |
327 | 327 | ||
diff --git a/src/firejail/restricted_shell.c b/src/firejail/restricted_shell.c index ae453f4f1..ed66903b5 100644 --- a/src/firejail/restricted_shell.c +++ b/src/firejail/restricted_shell.c | |||
@@ -32,7 +32,7 @@ int restricted_shell(const char *user) { | |||
32 | char *fname; | 32 | char *fname; |
33 | if (asprintf(&fname, "%s/login.users", SYSCONFDIR) == -1) | 33 | if (asprintf(&fname, "%s/login.users", SYSCONFDIR) == -1) |
34 | errExit("asprintf"); | 34 | errExit("asprintf"); |
35 | FILE *fp = fopen(fname, "r"); | 35 | FILE *fp = fopen(fname, "re"); |
36 | free(fname); | 36 | free(fname); |
37 | if (fp == NULL) | 37 | if (fp == NULL) |
38 | return 0; | 38 | return 0; |
@@ -96,7 +96,7 @@ int restricted_shell(const char *user) { | |||
96 | fullargv[i] = ptr; | 96 | fullargv[i] = ptr; |
97 | #ifdef DEBUG_RESTRICTED_SHELL | 97 | #ifdef DEBUG_RESTRICTED_SHELL |
98 | {EUID_ROOT(); | 98 | {EUID_ROOT(); |
99 | FILE *fp = fopen("/firelog", "a"); | 99 | FILE *fp = fopen("/firelog", "ae"); |
100 | if (fp) { | 100 | if (fp) { |
101 | fprintf(fp, "i %d ptr #%s#\n", i, fullargv[i]); | 101 | fprintf(fp, "i %d ptr #%s#\n", i, fullargv[i]); |
102 | fclose(fp); | 102 | fclose(fp); |
diff --git a/src/firejail/run_files.c b/src/firejail/run_files.c index cd44f745f..c28c3e01b 100644 --- a/src/firejail/run_files.c +++ b/src/firejail/run_files.c | |||
@@ -101,7 +101,7 @@ void set_name_run_file(pid_t pid) { | |||
101 | errExit("asprintf"); | 101 | errExit("asprintf"); |
102 | 102 | ||
103 | // the file is deleted first | 103 | // the file is deleted first |
104 | FILE *fp = fopen(fname, "w"); | 104 | FILE *fp = fopen(fname, "we"); |
105 | if (!fp) { | 105 | if (!fp) { |
106 | fprintf(stderr, "Error: cannot create %s\n", fname); | 106 | fprintf(stderr, "Error: cannot create %s\n", fname); |
107 | exit(1); | 107 | exit(1); |
@@ -120,7 +120,7 @@ void set_x11_run_file(pid_t pid, int display) { | |||
120 | errExit("asprintf"); | 120 | errExit("asprintf"); |
121 | 121 | ||
122 | // the file is deleted first | 122 | // the file is deleted first |
123 | FILE *fp = fopen(fname, "w"); | 123 | FILE *fp = fopen(fname, "we"); |
124 | if (!fp) { | 124 | if (!fp) { |
125 | fprintf(stderr, "Error: cannot create %s\n", fname); | 125 | fprintf(stderr, "Error: cannot create %s\n", fname); |
126 | exit(1); | 126 | exit(1); |
@@ -139,7 +139,7 @@ void set_profile_run_file(pid_t pid, const char *fname) { | |||
139 | 139 | ||
140 | EUID_ROOT(); | 140 | EUID_ROOT(); |
141 | // the file is deleted first | 141 | // the file is deleted first |
142 | FILE *fp = fopen(runfile, "w"); | 142 | FILE *fp = fopen(runfile, "we"); |
143 | if (!fp) { | 143 | if (!fp) { |
144 | fprintf(stderr, "Error: cannot create %s\n", runfile); | 144 | fprintf(stderr, "Error: cannot create %s\n", runfile); |
145 | exit(1); | 145 | exit(1); |
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 3af828ede..08f0f32c9 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c | |||
@@ -67,7 +67,7 @@ static void sandbox_handler(int sig){ | |||
67 | if (asprintf(&monfile, "/proc/%d/cmdline", monitored_pid) == -1) | 67 | if (asprintf(&monfile, "/proc/%d/cmdline", monitored_pid) == -1) |
68 | errExit("asprintf"); | 68 | errExit("asprintf"); |
69 | while (monsec) { | 69 | while (monsec) { |
70 | FILE *fp = fopen(monfile, "r"); | 70 | FILE *fp = fopen(monfile, "re"); |
71 | if (!fp) | 71 | if (!fp) |
72 | break; | 72 | break; |
73 | 73 | ||
@@ -162,7 +162,7 @@ static void save_nogroups(void) { | |||
162 | if (arg_nogroups == 0) | 162 | if (arg_nogroups == 0) |
163 | return; | 163 | return; |
164 | 164 | ||
165 | FILE *fp = fopen(RUN_GROUPS_CFG, "w"); | 165 | FILE *fp = fopen(RUN_GROUPS_CFG, "wxe"); |
166 | if (fp) { | 166 | if (fp) { |
167 | fprintf(fp, "\n"); | 167 | fprintf(fp, "\n"); |
168 | SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644 | 168 | SET_PERMS_STREAM(fp, 0, 0, 0644); // assume mode 0644 |
@@ -1015,7 +1015,7 @@ int sandbox(void* sandbox_arg) { | |||
1015 | // disable /dev/snd | 1015 | // disable /dev/snd |
1016 | fs_dev_disable_sound(); | 1016 | fs_dev_disable_sound(); |
1017 | } | 1017 | } |
1018 | else if (!arg_noautopulse) | 1018 | else if (!arg_keep_config_pulse) |
1019 | pulseaudio_init(); | 1019 | pulseaudio_init(); |
1020 | 1020 | ||
1021 | if (arg_no3d) | 1021 | if (arg_no3d) |
diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c index f9c41f661..4a8dd1bf7 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c | |||
@@ -248,7 +248,9 @@ int sbox_run(unsigned filtermask, int num, ...) { | |||
248 | va_start(valist, num); | 248 | va_start(valist, num); |
249 | 249 | ||
250 | // build argument list | 250 | // build argument list |
251 | char **arg = malloc((num + 1) * sizeof(char *)); | 251 | char **arg = calloc(num + 1, sizeof(char *)); |
252 | if (!arg) | ||
253 | errExit("calloc"); | ||
252 | int i; | 254 | int i; |
253 | for (i = 0; i < num; i++) | 255 | for (i = 0; i < num; i++) |
254 | arg[i] = va_arg(valist, char *); | 256 | arg[i] = va_arg(valist, char *); |
diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 785c29517..9670fe816 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c | |||
@@ -86,7 +86,7 @@ int seccomp_install_filters(void) { | |||
86 | static void seccomp_save_file_list(const char *fname) { | 86 | static void seccomp_save_file_list(const char *fname) { |
87 | assert(fname); | 87 | assert(fname); |
88 | 88 | ||
89 | FILE *fp = fopen(RUN_SECCOMP_LIST, "a+"); | 89 | FILE *fp = fopen(RUN_SECCOMP_LIST, "ae"); |
90 | if (!fp) | 90 | if (!fp) |
91 | errExit("fopen"); | 91 | errExit("fopen"); |
92 | 92 | ||
@@ -99,7 +99,7 @@ static void seccomp_save_file_list(const char *fname) { | |||
99 | #define MAXBUF 4096 | 99 | #define MAXBUF 4096 |
100 | static int load_file_list_flag = 0; | 100 | static int load_file_list_flag = 0; |
101 | void seccomp_load_file_list(void) { | 101 | void seccomp_load_file_list(void) { |
102 | FILE *fp = fopen(RUN_SECCOMP_LIST, "r"); | 102 | FILE *fp = fopen(RUN_SECCOMP_LIST, "re"); |
103 | if (!fp) | 103 | if (!fp) |
104 | return; // no seccomp configuration whatsoever | 104 | return; // no seccomp configuration whatsoever |
105 | 105 | ||
@@ -122,7 +122,7 @@ int seccomp_load(const char *fname) { | |||
122 | assert(fname); | 122 | assert(fname); |
123 | 123 | ||
124 | // open filter file | 124 | // open filter file |
125 | int fd = open(fname, O_RDONLY); | 125 | int fd = open(fname, O_RDONLY|O_CLOEXEC); |
126 | if (fd == -1) | 126 | if (fd == -1) |
127 | goto errexit; | 127 | goto errexit; |
128 | 128 | ||
@@ -438,7 +438,7 @@ void seccomp_print_filter(pid_t pid) { | |||
438 | if (stat(fname, &s) == -1) | 438 | if (stat(fname, &s) == -1) |
439 | goto errexit; | 439 | goto errexit; |
440 | 440 | ||
441 | FILE *fp = fopen(fname, "r"); | 441 | FILE *fp = fopen(fname, "re"); |
442 | if (!fp) | 442 | if (!fp) |
443 | goto errexit; | 443 | goto errexit; |
444 | free(fname); | 444 | free(fname); |
diff --git a/src/firejail/shutdown.c b/src/firejail/shutdown.c index 8fb03d0a6..fbfe1765b 100644 --- a/src/firejail/shutdown.c +++ b/src/firejail/shutdown.c | |||
@@ -64,7 +64,7 @@ void shut(pid_t pid) { | |||
64 | monsec--; | 64 | monsec--; |
65 | 65 | ||
66 | EUID_ROOT(); | 66 | EUID_ROOT(); |
67 | FILE *fp = fopen(monfile, "r"); | 67 | FILE *fp = fopen(monfile, "re"); |
68 | EUID_USER(); | 68 | EUID_USER(); |
69 | if (!fp) { | 69 | if (!fp) { |
70 | killdone = 1; | 70 | killdone = 1; |
diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 397150158..888a6ffed 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c | |||
@@ -114,7 +114,8 @@ static char *usage_str = | |||
114 | " --join-network=name|pid - join the network namespace.\n" | 114 | " --join-network=name|pid - join the network namespace.\n" |
115 | #endif | 115 | #endif |
116 | " --join-or-start=name|pid - join the sandbox or start a new one.\n" | 116 | " --join-or-start=name|pid - join the sandbox or start a new one.\n" |
117 | " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" | 117 | " --keep-config-pulse - disable automatic ~/.config/pulse init.\n" |
118 | " --keep-dev-shm - /dev/shm directory is untouched (even with --private-dev).\n" | ||
118 | " --keep-var-tmp - /var/tmp directory is untouched.\n" | 119 | " --keep-var-tmp - /var/tmp directory is untouched.\n" |
119 | " --list - list all sandboxes.\n" | 120 | " --list - list all sandboxes.\n" |
120 | #ifdef HAVE_FILE_TRANSFER | 121 | #ifdef HAVE_FILE_TRANSFER |
@@ -154,6 +155,7 @@ static char *usage_str = | |||
154 | " --nodvd - disable DVD and audio CD devices.\n" | 155 | " --nodvd - disable DVD and audio CD devices.\n" |
155 | " --noexec=filename - remount the file or directory noexec nosuid and nodev.\n" | 156 | " --noexec=filename - remount the file or directory noexec nosuid and nodev.\n" |
156 | " --nogroups - disable supplementary groups.\n" | 157 | " --nogroups - disable supplementary groups.\n" |
158 | " --noinput - disable input devices.\n" | ||
157 | " --nonewprivs - sets the NO_NEW_PRIVS prctl.\n" | 159 | " --nonewprivs - sets the NO_NEW_PRIVS prctl.\n" |
158 | " --noprofile - do not use a security profile.\n" | 160 | " --noprofile - do not use a security profile.\n" |
159 | #ifdef HAVE_USERNS | 161 | #ifdef HAVE_USERNS |
diff --git a/src/firejail/util.c b/src/firejail/util.c index 2ad85acd6..2731f61dc 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c | |||
@@ -298,14 +298,14 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m | |||
298 | assert(destname); | 298 | assert(destname); |
299 | 299 | ||
300 | // open source | 300 | // open source |
301 | int src = open(srcname, O_RDONLY); | 301 | int src = open(srcname, O_RDONLY|O_CLOEXEC); |
302 | if (src < 0) { | 302 | if (src < 0) { |
303 | fwarning("cannot open source file %s, file not copied\n", srcname); | 303 | fwarning("cannot open source file %s, file not copied\n", srcname); |
304 | return -1; | 304 | return -1; |
305 | } | 305 | } |
306 | 306 | ||
307 | // open destination | 307 | // open destination |
308 | int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | 308 | int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
309 | if (dst < 0) { | 309 | if (dst < 0) { |
310 | fwarning("cannot open destination file %s, file not copied\n", destname); | 310 | fwarning("cannot open destination file %s, file not copied\n", destname); |
311 | close(src); | 311 | close(src); |
@@ -348,7 +348,7 @@ void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid | |||
348 | 348 | ||
349 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { | 349 | void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) { |
350 | // open destination | 350 | // open destination |
351 | int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | 351 | int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
352 | if (dst < 0) { | 352 | if (dst < 0) { |
353 | fwarning("cannot open destination file %s, file not copied\n", destname); | 353 | fwarning("cannot open destination file %s, file not copied\n", destname); |
354 | return; | 354 | return; |
@@ -361,7 +361,7 @@ void copy_file_from_user_to_root(const char *srcname, const char *destname, uid_ | |||
361 | // drop privileges | 361 | // drop privileges |
362 | drop_privs(0); | 362 | drop_privs(0); |
363 | 363 | ||
364 | int src = open(srcname, O_RDONLY); | 364 | int src = open(srcname, O_RDONLY|O_CLOEXEC); |
365 | if (src < 0) { | 365 | if (src < 0) { |
366 | fwarning("cannot open source file %s, file not copied\n", srcname); | 366 | fwarning("cannot open source file %s, file not copied\n", srcname); |
367 | } else { | 367 | } else { |
@@ -544,11 +544,13 @@ char *split_comma(char *str) { | |||
544 | } | 544 | } |
545 | 545 | ||
546 | 546 | ||
547 | // remove consecutive and trailing slashes | 547 | // simplify absolute path by removing |
548 | // and return allocated memory | 548 | // 1) consecutive and trailing slashes, and |
549 | // e.g. /home//user/ -> /home/user | 549 | // 2) segments with a single dot |
550 | // for example /foo//./bar/ -> /foo/bar | ||
550 | char *clean_pathname(const char *path) { | 551 | char *clean_pathname(const char *path) { |
551 | assert(path); | 552 | assert(path && path[0] == '/'); |
553 | |||
552 | size_t len = strlen(path); | 554 | size_t len = strlen(path); |
553 | char *rv = malloc(len + 1); | 555 | char *rv = malloc(len + 1); |
554 | if (!rv) | 556 | if (!rv) |
@@ -557,15 +559,23 @@ char *clean_pathname(const char *path) { | |||
557 | size_t i = 0; | 559 | size_t i = 0; |
558 | size_t j = 0; | 560 | size_t j = 0; |
559 | while (path[i]) { | 561 | while (path[i]) { |
560 | while (path[i] == '/' && path[i+1] == '/') | 562 | if (path[i] == '/') { |
561 | i++; | 563 | while (path[i+1] == '/' || |
564 | (path[i+1] == '.' && path[i+2] == '/')) | ||
565 | i++; | ||
566 | } | ||
567 | |||
562 | rv[j++] = path[i++]; | 568 | rv[j++] = path[i++]; |
563 | } | 569 | } |
564 | rv[j] = '\0'; | 570 | rv[j] = '\0'; |
565 | 571 | ||
572 | // remove a trailing dot | ||
573 | if (j > 1 && rv[j - 1] == '.' && rv[j - 2] == '/') | ||
574 | rv[--j] = '\0'; | ||
575 | |||
566 | // remove a trailing slash | 576 | // remove a trailing slash |
567 | if (j > 1 && rv[j - 1] == '/') | 577 | if (j > 1 && rv[j - 1] == '/') |
568 | rv[j - 1] = '\0'; | 578 | rv[--j] = '\0'; |
569 | 579 | ||
570 | return rv; | 580 | return rv; |
571 | } | 581 | } |
@@ -616,7 +626,7 @@ int find_child(pid_t parent, pid_t *child) { | |||
616 | perror("asprintf"); | 626 | perror("asprintf"); |
617 | exit(1); | 627 | exit(1); |
618 | } | 628 | } |
619 | FILE *fp = fopen(file, "r"); | 629 | FILE *fp = fopen(file, "re"); |
620 | if (!fp) { | 630 | if (!fp) { |
621 | free(file); | 631 | free(file); |
622 | continue; | 632 | continue; |
@@ -722,7 +732,7 @@ void update_map(char *mapping, char *map_file) { | |||
722 | if (mapping[j] == ',') | 732 | if (mapping[j] == ',') |
723 | mapping[j] = '\n'; | 733 | mapping[j] = '\n'; |
724 | 734 | ||
725 | fd = open(map_file, O_RDWR); | 735 | fd = open(map_file, O_RDWR|O_CLOEXEC); |
726 | if (fd == -1) { | 736 | if (fd == -1) { |
727 | fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno)); | 737 | fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno)); |
728 | exit(EXIT_FAILURE); | 738 | exit(EXIT_FAILURE); |
@@ -742,9 +752,9 @@ void wait_for_other(int fd) { | |||
742 | // wait for the parent to be initialized | 752 | // wait for the parent to be initialized |
743 | //**************************** | 753 | //**************************** |
744 | char childstr[BUFLEN + 1]; | 754 | char childstr[BUFLEN + 1]; |
745 | int newfd = dup(fd); | 755 | int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); |
746 | if (newfd == -1) | 756 | if (newfd == -1) |
747 | errExit("dup"); | 757 | errExit("fcntl"); |
748 | FILE* stream; | 758 | FILE* stream; |
749 | stream = fdopen(newfd, "r"); | 759 | stream = fdopen(newfd, "r"); |
750 | *childstr = '\0'; | 760 | *childstr = '\0'; |
@@ -791,9 +801,9 @@ void wait_for_other(int fd) { | |||
791 | 801 | ||
792 | void notify_other(int fd) { | 802 | void notify_other(int fd) { |
793 | FILE* stream; | 803 | FILE* stream; |
794 | int newfd = dup(fd); | 804 | int newfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); |
795 | if (newfd == -1) | 805 | if (newfd == -1) |
796 | errExit("dup"); | 806 | errExit("fcntl"); |
797 | stream = fdopen(newfd, "w"); | 807 | stream = fdopen(newfd, "w"); |
798 | fprintf(stream, "arg_noroot=%d\n", arg_noroot); | 808 | fprintf(stream, "arg_noroot=%d\n", arg_noroot); |
799 | fflush(stream); | 809 | fflush(stream); |
@@ -811,7 +821,7 @@ uid_t pid_get_uid(pid_t pid) { | |||
811 | exit(1); | 821 | exit(1); |
812 | } | 822 | } |
813 | EUID_ROOT(); // grsecurity fix | 823 | EUID_ROOT(); // grsecurity fix |
814 | FILE *fp = fopen(file, "r"); | 824 | FILE *fp = fopen(file, "re"); |
815 | if (!fp) { | 825 | if (!fp) { |
816 | free(file); | 826 | free(file); |
817 | fprintf(stderr, "Error: cannot open /proc file\n"); | 827 | fprintf(stderr, "Error: cannot open /proc file\n"); |
@@ -905,9 +915,9 @@ int remove_overlay_directory(void) { | |||
905 | errExit("fork"); | 915 | errExit("fork"); |
906 | if (child == 0) { | 916 | if (child == 0) { |
907 | // open ~/.firejail, fails if there is any symlink | 917 | // open ~/.firejail, fails if there is any symlink |
908 | int fd = safe_fd(path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 918 | int fd = safer_openat(-1, path, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
909 | if (fd == -1) | 919 | if (fd == -1) |
910 | errExit("safe_fd"); | 920 | errExit("safer_openat"); |
911 | // chdir to ~/.firejail | 921 | // chdir to ~/.firejail |
912 | if (fchdir(fd) == -1) | 922 | if (fchdir(fd) == -1) |
913 | errExit("fchdir"); | 923 | errExit("fchdir"); |
@@ -1021,8 +1031,7 @@ void create_empty_file_as_root(const char *fname, mode_t mode) { | |||
1021 | if (arg_debug) | 1031 | if (arg_debug) |
1022 | printf("Creating empty %s file\n", fname); | 1032 | printf("Creating empty %s file\n", fname); |
1023 | 1033 | ||
1024 | /* coverity[toctou] */ | 1034 | FILE *fp = fopen(fname, "wxe"); |
1025 | FILE *fp = fopen(fname, "w"); | ||
1026 | if (!fp) | 1035 | if (!fp) |
1027 | errExit("fopen"); | 1036 | errExit("fopen"); |
1028 | SET_PERMS_STREAM(fp, 0, 0, mode); | 1037 | SET_PERMS_STREAM(fp, 0, 0, mode); |
@@ -1126,13 +1135,13 @@ void disable_file_path(const char *path, const char *file) { | |||
1126 | } | 1135 | } |
1127 | 1136 | ||
1128 | // open an existing file without following any symbolic link | 1137 | // open an existing file without following any symbolic link |
1129 | int safe_fd(const char *path, int flags) { | 1138 | // relative paths are interpreted relative to dirfd |
1139 | // ignore dirfd if path is absolute | ||
1140 | // https://web.archive.org/web/20180419120236/https://blogs.gnome.org/jamesh/2018/04/19/secure-mounts | ||
1141 | int safer_openat(int dirfd, const char *path, int flags) { | ||
1142 | assert(path && path[0]); | ||
1130 | flags |= O_NOFOLLOW; | 1143 | flags |= O_NOFOLLOW; |
1131 | assert(path); | 1144 | |
1132 | if (*path != '/' || strstr(path, "..")) { | ||
1133 | fprintf(stderr, "Error: invalid path %s\n", path); | ||
1134 | exit(1); | ||
1135 | } | ||
1136 | int fd = -1; | 1145 | int fd = -1; |
1137 | 1146 | ||
1138 | #ifdef __NR_openat2 // kernel 5.6 or better | 1147 | #ifdef __NR_openat2 // kernel 5.6 or better |
@@ -1140,7 +1149,7 @@ int safe_fd(const char *path, int flags) { | |||
1140 | memset(&oh, 0, sizeof(oh)); | 1149 | memset(&oh, 0, sizeof(oh)); |
1141 | oh.flags = flags; | 1150 | oh.flags = flags; |
1142 | oh.resolve = RESOLVE_NO_SYMLINKS; | 1151 | oh.resolve = RESOLVE_NO_SYMLINKS; |
1143 | fd = syscall(__NR_openat2, -1, path, &oh, sizeof(struct open_how)); | 1152 | fd = syscall(__NR_openat2, dirfd, path, &oh, sizeof(struct open_how)); |
1144 | if (fd != -1 || errno != ENOSYS) | 1153 | if (fd != -1 || errno != ENOSYS) |
1145 | return fd; | 1154 | return fd; |
1146 | #endif | 1155 | #endif |
@@ -1151,18 +1160,23 @@ int safe_fd(const char *path, int flags) { | |||
1151 | if (!dup) | 1160 | if (!dup) |
1152 | errExit("strdup"); | 1161 | errExit("strdup"); |
1153 | char *tok = strtok(dup, "/"); | 1162 | char *tok = strtok(dup, "/"); |
1154 | if (!tok) { // root directory | 1163 | if (!tok) { // nothing to do, path is the root directory |
1155 | free(dup); | 1164 | free(dup); |
1156 | return open("/", flags); | 1165 | return openat(dirfd, path, flags); |
1157 | } | 1166 | } |
1158 | char *last_tok = EMPTY_STRING; | 1167 | char *last_tok = EMPTY_STRING; |
1159 | int parentfd = open("/", O_PATH|O_CLOEXEC); | 1168 | |
1169 | int parentfd; | ||
1170 | if (path[0] == '/') | ||
1171 | parentfd = open("/", O_PATH|O_CLOEXEC); | ||
1172 | else | ||
1173 | parentfd = fcntl(dirfd, F_DUPFD_CLOEXEC, 0); | ||
1160 | if (parentfd == -1) | 1174 | if (parentfd == -1) |
1161 | errExit("open"); | 1175 | errExit("open/fcntl"); |
1162 | 1176 | ||
1163 | while(1) { | 1177 | while (1) { |
1164 | // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link | 1178 | // open path component, assuming it is a directory; this fails with ENOTDIR if it is a symbolic link |
1165 | // if token is a single dot, the previous directory is reopened | 1179 | // if token is a single dot, the directory referred to by parentfd is reopened |
1166 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1180 | fd = openat(parentfd, tok, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1167 | if (fd == -1) { | 1181 | if (fd == -1) { |
1168 | // if the following token is NULL, the current token is the final path component | 1182 | // if the following token is NULL, the current token is the final path component |
@@ -1293,13 +1307,11 @@ pid_t require_pid(const char *name) { | |||
1293 | // return 1 if there is a link somewhere in path of directory | 1307 | // return 1 if there is a link somewhere in path of directory |
1294 | static int has_link(const char *dir) { | 1308 | static int has_link(const char *dir) { |
1295 | assert(dir); | 1309 | assert(dir); |
1296 | int fd = safe_fd(dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 1310 | int fd = safer_openat(-1, dir, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); |
1297 | if (fd == -1) { | 1311 | if (fd != -1) |
1298 | if ((errno == ELOOP || errno == ENOTDIR) && is_dir(dir)) | ||
1299 | return 1; | ||
1300 | } | ||
1301 | else | ||
1302 | close(fd); | 1312 | close(fd); |
1313 | else if (errno == ELOOP || (errno == ENOTDIR && is_dir(dir))) | ||
1314 | return 1; | ||
1303 | return 0; | 1315 | return 0; |
1304 | } | 1316 | } |
1305 | 1317 | ||
diff --git a/src/firejail/x11.c b/src/firejail/x11.c index 1dabf272e..257d376a1 100644 --- a/src/firejail/x11.c +++ b/src/firejail/x11.c | |||
@@ -84,7 +84,7 @@ int x11_display(void) { | |||
84 | static int x11_abstract_sockets_present(void) { | 84 | static int x11_abstract_sockets_present(void) { |
85 | 85 | ||
86 | EUID_ROOT(); // grsecurity fix | 86 | EUID_ROOT(); // grsecurity fix |
87 | FILE *fp = fopen("/proc/net/unix", "r"); | 87 | FILE *fp = fopen("/proc/net/unix", "re"); |
88 | if (!fp) | 88 | if (!fp) |
89 | errExit("fopen"); | 89 | errExit("fopen"); |
90 | EUID_USER(); | 90 | EUID_USER(); |
@@ -1239,9 +1239,9 @@ void x11_xorg(void) { | |||
1239 | } | 1239 | } |
1240 | } | 1240 | } |
1241 | // get a file descriptor for ~/.Xauthority | 1241 | // get a file descriptor for ~/.Xauthority |
1242 | int dst = safe_fd(dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 1242 | int dst = safer_openat(-1, dest, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
1243 | if (dst == -1) | 1243 | if (dst == -1) |
1244 | errExit("safe_fd"); | 1244 | errExit("safer_openat"); |
1245 | // check if the actual mount destination is a user owned regular file | 1245 | // check if the actual mount destination is a user owned regular file |
1246 | if (fstat(dst, &s) == -1) | 1246 | if (fstat(dst, &s) == -1) |
1247 | errExit("fstat"); | 1247 | errExit("fstat"); |
@@ -1263,9 +1263,9 @@ void x11_xorg(void) { | |||
1263 | fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_NOEXEC, 0); | 1263 | fs_remount(RUN_XAUTHORITY_SEC_DIR, MOUNT_NOEXEC, 0); |
1264 | 1264 | ||
1265 | // get a file descriptor for the new Xauthority file | 1265 | // get a file descriptor for the new Xauthority file |
1266 | int src = safe_fd(tmpfname, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 1266 | int src = safer_openat(-1, tmpfname, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
1267 | if (src == -1) | 1267 | if (src == -1) |
1268 | errExit("safe_fd"); | 1268 | errExit("safer_openat"); |
1269 | if (fstat(src, &s) == -1) | 1269 | if (fstat(src, &s) == -1) |
1270 | errExit("fstat"); | 1270 | errExit("fstat"); |
1271 | if (!S_ISREG(s.st_mode)) { | 1271 | if (!S_ISREG(s.st_mode)) { |
@@ -1363,7 +1363,7 @@ void fs_x11(void) { | |||
1363 | fs_logger("tmpfs /tmp/.X11-unix"); | 1363 | fs_logger("tmpfs /tmp/.X11-unix"); |
1364 | 1364 | ||
1365 | // create an empty root-owned file which will have the desired socket bind-mounted over it | 1365 | // create an empty root-owned file which will have the desired socket bind-mounted over it |
1366 | int fd = open(x11file, O_RDONLY|O_CREAT|O_EXCL, S_IRUSR | S_IWUSR); | 1366 | int fd = open(x11file, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC, S_IRUSR | S_IWUSR); |
1367 | if (fd < 0) | 1367 | if (fd < 0) |
1368 | errExit(x11file); | 1368 | errExit(x11file); |
1369 | close(fd); | 1369 | close(fd); |
@@ -1373,7 +1373,7 @@ void fs_x11(void) { | |||
1373 | char *wx11file; | 1373 | char *wx11file; |
1374 | if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) | 1374 | if (asprintf(&wx11file, "%s/X%d", RUN_WHITELIST_X11_DIR, display) == -1) |
1375 | errExit("asprintf"); | 1375 | errExit("asprintf"); |
1376 | fd = safe_fd(wx11file, O_PATH|O_NOFOLLOW|O_CLOEXEC); | 1376 | fd = safer_openat(-1, wx11file, O_PATH|O_NOFOLLOW|O_CLOEXEC); |
1377 | if (fd == -1) | 1377 | if (fd == -1) |
1378 | errExit("opening X11 socket"); | 1378 | errExit("opening X11 socket"); |
1379 | // confirm once more we are mounting a socket | 1379 | // confirm once more we are mounting a socket |
diff --git a/src/firemon/firemon.c b/src/firemon/firemon.c index 37870747d..6c34cd411 100644 --- a/src/firemon/firemon.c +++ b/src/firemon/firemon.c | |||
@@ -52,7 +52,7 @@ static void my_handler(int s){ | |||
52 | 52 | ||
53 | if (terminal_set) | 53 | if (terminal_set) |
54 | tcsetattr(0, TCSANOW, &tlocal); | 54 | tcsetattr(0, TCSANOW, &tlocal); |
55 | exit(0); | 55 | _exit(0); |
56 | } | 56 | } |
57 | 57 | ||
58 | // find the second child process for the specified pid | 58 | // find the second child process for the specified pid |
diff --git a/src/include/rundefs.h b/src/include/rundefs.h index d14f6782f..a172dd511 100644 --- a/src/include/rundefs.h +++ b/src/include/rundefs.h | |||
@@ -84,18 +84,6 @@ | |||
84 | #define RUN_DEVLOG_FILE RUN_MNT_DIR "/devlog" | 84 | #define RUN_DEVLOG_FILE RUN_MNT_DIR "/devlog" |
85 | 85 | ||
86 | #define RUN_WHITELIST_X11_DIR RUN_MNT_DIR "/orig-x11" | 86 | #define RUN_WHITELIST_X11_DIR RUN_MNT_DIR "/orig-x11" |
87 | #define RUN_WHITELIST_HOME_USER_DIR RUN_MNT_DIR "/orig-home-user" // home directory whitelisting | ||
88 | #define RUN_WHITELIST_RUN_USER_DIR RUN_MNT_DIR "/orig-run-user" // run directory whitelisting | ||
89 | #define RUN_WHITELIST_TMP_DIR RUN_MNT_DIR "/orig-tmp" | ||
90 | #define RUN_WHITELIST_MEDIA_DIR RUN_MNT_DIR "/orig-media" | ||
91 | #define RUN_WHITELIST_MNT_DIR RUN_MNT_DIR "/orig-mnt" | ||
92 | #define RUN_WHITELIST_VAR_DIR RUN_MNT_DIR "/orig-var" | ||
93 | #define RUN_WHITELIST_DEV_DIR RUN_MNT_DIR "/orig-dev" | ||
94 | #define RUN_WHITELIST_OPT_DIR RUN_MNT_DIR "/orig-opt" | ||
95 | #define RUN_WHITELIST_SRV_DIR RUN_MNT_DIR "/orig-srv" | ||
96 | #define RUN_WHITELIST_ETC_DIR RUN_MNT_DIR "/orig-etc" | ||
97 | #define RUN_WHITELIST_SHARE_DIR RUN_MNT_DIR "/orig-share" | ||
98 | #define RUN_WHITELIST_MODULE_DIR RUN_MNT_DIR "/orig-module" | ||
99 | 87 | ||
100 | #define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" // private options | 88 | #define RUN_XAUTHORITY_FILE RUN_MNT_DIR "/.Xauthority" // private options |
101 | #define RUN_XAUTH_FILE RUN_MNT_DIR "/xauth" // x11=xorg | 89 | #define RUN_XAUTH_FILE RUN_MNT_DIR "/xauth" // x11=xorg |
diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 9d11add06..49be8d0b0 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt | |||
@@ -1,12 +1,78 @@ | |||
1 | .TH FIREJAIL-PROFILE 5 "MONTH YEAR" "VERSION" "firejail profiles man page" | 1 | .TH FIREJAIL-PROFILE 5 "MONTH YEAR" "VERSION" "firejail profiles man page" |
2 | .SH NAME | 2 | .SH NAME |
3 | profile \- Security profile file syntax for Firejail | 3 | profile \- Security profile file syntax, and information about building new application profiles. |
4 | 4 | ||
5 | .SH USAGE | 5 | .SH SYNOPSIS |
6 | |||
7 | Using a specific profile: | ||
8 | .PP | ||
9 | .RS | ||
6 | .TP | 10 | .TP |
7 | firejail \-\-profile=filename.profile | 11 | \fBfirejail \-\-profile=filename.profile |
12 | .br | ||
13 | |||
14 | .br | ||
15 | Example: | ||
16 | .br | ||
17 | $ firejail --profile=/etc/firejail/kdenlive.profile --appimage kdenlive.appimage | ||
18 | .br | ||
19 | |||
20 | .br | ||
21 | .TP | ||
22 | \fBfirejail \-\-profile=profile_name | ||
23 | .br | ||
24 | |||
25 | .br | ||
26 | Example: | ||
27 | .br | ||
28 | $ firejail --profile=kdenlive --appimage kdenlive.appimage | ||
29 | .br | ||
30 | |||
31 | .br | ||
8 | .RE | 32 | .RE |
9 | firejail \-\-profile=profile_name | 33 | .PP |
34 | |||
35 | |||
36 | |||
37 | Building a profile manually: | ||
38 | .PP | ||
39 | .RS | ||
40 | Start with the template in /usr/share/doc/firejail/profile.template and modify it in a text editor. | ||
41 | To integrate the program in your desktop environment copy the profile file in ~/.config/firejail | ||
42 | directory and run "sudo firecfg". | ||
43 | .RE | ||
44 | .PP | ||
45 | |||
46 | Aliases and redirections: | ||
47 | .PP | ||
48 | .RS | ||
49 | In some cases the same profile can be used for several applications. | ||
50 | One such example is LibreOffice. | ||
51 | Build a regular profile for the main application, and for the rest use | ||
52 | /usr/share/doc/firejail/redirect_alias-profile.template. | ||
53 | .RE | ||
54 | .PP | ||
55 | |||
56 | Running the profile builder: | ||
57 | .PP | ||
58 | .RS | ||
59 | .TP | ||
60 | \fBfirejail \-\-build=appname.profile appname | ||
61 | .br | ||
62 | |||
63 | .br | ||
64 | Example: | ||
65 | .br | ||
66 | $ firejail --build=blobby.profile blobby | ||
67 | .br | ||
68 | |||
69 | .br | ||
70 | Run the program in "firejail \-\-build" and try to exercise as many program features as possible. | ||
71 | The profile is extracted and saved in the current directory. Open it in a text editor and add or remove | ||
72 | sandboxing options as necessary. Test again after modifying the profile. To integrate the program | ||
73 | in your desktop environment copy the profile file in ~/.config/firejail directory and run "sudo firecfg". | ||
74 | .RE | ||
75 | .PP | ||
10 | 76 | ||
11 | .SH DESCRIPTION | 77 | .SH DESCRIPTION |
12 | Several command line options can be passed to the program using | 78 | Several command line options can be passed to the program using |
@@ -205,6 +271,10 @@ Mount-bind file1 on top of file2. This option is only available when running as | |||
205 | \fBdisable-mnt | 271 | \fBdisable-mnt |
206 | Disable /mnt, /media, /run/mount and /run/media access. | 272 | Disable /mnt, /media, /run/mount and /run/media access. |
207 | .TP | 273 | .TP |
274 | \fBkeep-config-pulse | ||
275 | Disable automatic ~/.config/pulse init, for complex setups such as remote | ||
276 | pulse servers or non-standard socket paths. | ||
277 | .TP | ||
208 | \fBkeep-dev-shm | 278 | \fBkeep-dev-shm |
209 | /dev/shm directory is untouched (even with private-dev). | 279 | /dev/shm directory is untouched (even with private-dev). |
210 | .TP | 280 | .TP |
@@ -652,9 +722,8 @@ name browser | |||
652 | \fBno3d | 722 | \fBno3d |
653 | Disable 3D hardware acceleration. | 723 | Disable 3D hardware acceleration. |
654 | .TP | 724 | .TP |
655 | \fBnoautopulse | 725 | \fBnoautopulse \fR(deprecated) |
656 | Disable automatic ~/.config/pulse init, for complex setups such as remote | 726 | See keep-config-pulse. |
657 | pulse servers or non-standard socket paths. | ||
658 | .TP | 727 | .TP |
659 | \fBnodvd | 728 | \fBnodvd |
660 | Disable DVD and audio CD devices. | 729 | Disable DVD and audio CD devices. |
@@ -891,7 +960,21 @@ Join the sandbox identified by name or start a new one. | |||
891 | Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname". | 960 | Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname". |
892 | 961 | ||
893 | .SH FILES | 962 | .SH FILES |
894 | /etc/firejail/filename.profile, $HOME/.config/firejail/filename.profile | 963 | .TP |
964 | \fB/etc/firejail/appname.profile | ||
965 | Global Firejail configuration consisting mainly of profiles for each application supported by default. | ||
966 | |||
967 | .TP | ||
968 | \fB$HOME/.config/firejail/appname.profile | ||
969 | User application profiles, will take precedence over the global profiles. | ||
970 | |||
971 | .TP | ||
972 | \fB/usr/share/doc/firejail/profile.template | ||
973 | Template for building new profiles. | ||
974 | |||
975 | .TP | ||
976 | \fB/usr/share/doc/firejail/redirect_alias-profile.template | ||
977 | Template for aliasing/redirecting profiles. | ||
895 | 978 | ||
896 | .SH LICENSE | 979 | .SH LICENSE |
897 | Firejail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. | 980 | Firejail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 23ec23fb1..68aea5857 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt | |||
@@ -1052,6 +1052,17 @@ Same as "firejail --join=name" if sandbox with specified name exists, otherwise | |||
1052 | Note that in contrary to other join options there is respective profile option. | 1052 | Note that in contrary to other join options there is respective profile option. |
1053 | 1053 | ||
1054 | .TP | 1054 | .TP |
1055 | \fB\-\-keep-config-pulse | ||
1056 | Disable automatic ~/.config/pulse init, for complex setups such as remote | ||
1057 | pulse servers or non-standard socket paths. | ||
1058 | .br | ||
1059 | |||
1060 | .br | ||
1061 | Example: | ||
1062 | .br | ||
1063 | $ firejail \-\-keep-config-pulse firefox | ||
1064 | |||
1065 | .TP | ||
1055 | \fB\-\-keep-dev-shm | 1066 | \fB\-\-keep-dev-shm |
1056 | /dev/shm directory is untouched (even with --private-dev) | 1067 | /dev/shm directory is untouched (even with --private-dev) |
1057 | .br | 1068 | .br |
@@ -1460,15 +1471,8 @@ Example: | |||
1460 | $ firejail --no3d firefox | 1471 | $ firejail --no3d firefox |
1461 | 1472 | ||
1462 | .TP | 1473 | .TP |
1463 | \fB\-\-noautopulse | 1474 | \fB\-\-noautopulse \fR(deprecated) |
1464 | Disable automatic ~/.config/pulse init, for complex setups such as remote | 1475 | See --keep-config-pulse. |
1465 | pulse servers or non-standard socket paths. | ||
1466 | .br | ||
1467 | |||
1468 | .br | ||
1469 | Example: | ||
1470 | .br | ||
1471 | $ firejail \-\-noautopulse firefox | ||
1472 | 1476 | ||
1473 | .TP | 1477 | .TP |
1474 | \fB\-\-noblacklist=dirname_or_filename | 1478 | \fB\-\-noblacklist=dirname_or_filename |
@@ -1892,9 +1896,7 @@ $ | |||
1892 | Build a new /etc in a temporary | 1896 | Build a new /etc in a temporary |
1893 | filesystem, and copy the files and directories in the list. | 1897 | filesystem, and copy the files and directories in the list. |
1894 | The files and directories in the list must be expressed as relative to | 1898 | The files and directories in the list must be expressed as relative to |
1895 | the /etc directory, and must not contain the / character | 1899 | the /etc directory (e.g., /etc/foo must be expressed as foo). |
1896 | (e.g., /etc/foo must be expressed as foo, but /etc/foo/bar -- | ||
1897 | expressed as foo/bar -- is disallowed). | ||
1898 | If no listed file is found, /etc directory will be empty. | 1900 | If no listed file is found, /etc directory will be empty. |
1899 | All modifications are discarded when the sandbox is closed. | 1901 | All modifications are discarded when the sandbox is closed. |
1900 | .br | 1902 | .br |
@@ -1997,7 +1999,7 @@ Build a new /srv in a temporary | |||
1997 | filesystem, and copy the files and directories in the list. | 1999 | filesystem, and copy the files and directories in the list. |
1998 | The files and directories in the list must be expressed as relative to | 2000 | The files and directories in the list must be expressed as relative to |
1999 | the /srv directory, and must not contain the / character | 2001 | the /srv directory, and must not contain the / character |
2000 | (e.g., /opt/srv must be expressed as foo, but /srv/foo/bar -- | 2002 | (e.g., /srv/foo must be expressed as foo, but /srv/foo/bar -- |
2001 | expressed as srv/bar -- is disallowed). | 2003 | expressed as srv/bar -- is disallowed). |
2002 | If no listed file is found, /srv directory will be empty. | 2004 | If no listed file is found, /srv directory will be empty. |
2003 | All modifications are discarded when the sandbox is closed. | 2005 | All modifications are discarded when the sandbox is closed. |
diff --git a/src/profstats/main.c b/src/profstats/main.c index 5035280b1..10e44bd65 100644 --- a/src/profstats/main.c +++ b/src/profstats/main.c | |||
@@ -46,6 +46,7 @@ static int cnt_whitelistusrshare = 0; // include whitelist-usr-share-common.inc | |||
46 | static int cnt_ssh = 0; | 46 | static int cnt_ssh = 0; |
47 | static int cnt_mdwx = 0; | 47 | static int cnt_mdwx = 0; |
48 | static int cnt_whitelisthome = 0; | 48 | static int cnt_whitelisthome = 0; |
49 | static int cnt_noroot = 0; | ||
49 | 50 | ||
50 | static int level = 0; | 51 | static int level = 0; |
51 | static int arg_debug = 0; | 52 | static int arg_debug = 0; |
@@ -65,6 +66,7 @@ static int arg_mdwx = 0; | |||
65 | static int arg_dbus_system_none = 0; | 66 | static int arg_dbus_system_none = 0; |
66 | static int arg_dbus_user_none = 0; | 67 | static int arg_dbus_user_none = 0; |
67 | static int arg_whitelisthome = 0; | 68 | static int arg_whitelisthome = 0; |
69 | static int arg_noroot = 0; | ||
68 | 70 | ||
69 | 71 | ||
70 | static char *profile = NULL; | 72 | static char *profile = NULL; |
@@ -80,6 +82,7 @@ static void usage(void) { | |||
80 | printf(" --dbus-user-none - profiles without \"dbus-user none\"\n"); | 82 | printf(" --dbus-user-none - profiles without \"dbus-user none\"\n"); |
81 | printf(" --ssh - print profiles without \"include disable-common.inc\"\n"); | 83 | printf(" --ssh - print profiles without \"include disable-common.inc\"\n"); |
82 | printf(" --noexec - print profiles without \"include disable-exec.inc\"\n"); | 84 | printf(" --noexec - print profiles without \"include disable-exec.inc\"\n"); |
85 | printf(" --noroot - print profiles without \"noroot\"\n"); | ||
83 | printf(" --private-bin - print profiles without private-bin\n"); | 86 | printf(" --private-bin - print profiles without private-bin\n"); |
84 | printf(" --private-dev - print profiles without private-dev\n"); | 87 | printf(" --private-dev - print profiles without private-dev\n"); |
85 | printf(" --private-etc - print profiles without private-etc\n"); | 88 | printf(" --private-etc - print profiles without private-etc\n"); |
@@ -128,6 +131,8 @@ void process_file(const char *fname) { | |||
128 | cnt_caps++; | 131 | cnt_caps++; |
129 | else if (strncmp(ptr, "include disable-exec.inc", 24) == 0) | 132 | else if (strncmp(ptr, "include disable-exec.inc", 24) == 0) |
130 | cnt_noexec++; | 133 | cnt_noexec++; |
134 | else if (strncmp(ptr, "noroot", 6) == 0) | ||
135 | cnt_noroot++; | ||
131 | else if (strncmp(ptr, "include whitelist-var-common.inc", 32) == 0) | 136 | else if (strncmp(ptr, "include whitelist-var-common.inc", 32) == 0) |
132 | cnt_whitelistvar++; | 137 | cnt_whitelistvar++; |
133 | else if (strncmp(ptr, "include whitelist-runuser-common.inc", 36) == 0 || | 138 | else if (strncmp(ptr, "include whitelist-runuser-common.inc", 36) == 0 || |
@@ -212,6 +217,8 @@ int main(int argc, char **argv) { | |||
212 | arg_mdwx = 1; | 217 | arg_mdwx = 1; |
213 | else if (strcmp(argv[i], "--noexec") == 0) | 218 | else if (strcmp(argv[i], "--noexec") == 0) |
214 | arg_noexec = 1; | 219 | arg_noexec = 1; |
220 | else if (strcmp(argv[i], "--noroot") == 0) | ||
221 | arg_noroot = 1; | ||
215 | else if (strcmp(argv[i], "--private-bin") == 0) | 222 | else if (strcmp(argv[i], "--private-bin") == 0) |
216 | arg_privatebin = 1; | 223 | arg_privatebin = 1; |
217 | else if (strcmp(argv[i], "--private-dev") == 0) | 224 | else if (strcmp(argv[i], "--private-dev") == 0) |
@@ -256,6 +263,7 @@ int main(int argc, char **argv) { | |||
256 | int caps = cnt_caps; | 263 | int caps = cnt_caps; |
257 | int apparmor = cnt_apparmor; | 264 | int apparmor = cnt_apparmor; |
258 | int noexec = cnt_noexec; | 265 | int noexec = cnt_noexec; |
266 | int noroot = cnt_noroot; | ||
259 | int privatebin = cnt_privatebin; | 267 | int privatebin = cnt_privatebin; |
260 | int privatetmp = cnt_privatetmp; | 268 | int privatetmp = cnt_privatetmp; |
261 | int privatedev = cnt_privatedev; | 269 | int privatedev = cnt_privatedev; |
@@ -313,6 +321,8 @@ int main(int argc, char **argv) { | |||
313 | printf("No seccomp found in %s\n", argv[i]); | 321 | printf("No seccomp found in %s\n", argv[i]); |
314 | if (arg_noexec && noexec == cnt_noexec) | 322 | if (arg_noexec && noexec == cnt_noexec) |
315 | printf("No include disable-exec.inc found in %s\n", argv[i]); | 323 | printf("No include disable-exec.inc found in %s\n", argv[i]); |
324 | if (arg_noroot && noroot == cnt_noroot) | ||
325 | printf("No noroot found in %s\n", argv[i]); | ||
316 | if (arg_privatedev && privatedev == cnt_privatedev) | 326 | if (arg_privatedev && privatedev == cnt_privatedev) |
317 | printf("No private-dev found in %s\n", argv[i]); | 327 | printf("No private-dev found in %s\n", argv[i]); |
318 | if (arg_privatebin && privatebin == cnt_privatebin) | 328 | if (arg_privatebin && privatebin == cnt_privatebin) |
@@ -346,6 +356,7 @@ int main(int argc, char **argv) { | |||
346 | printf(" seccomp\t\t\t%d\n", cnt_seccomp); | 356 | printf(" seccomp\t\t\t%d\n", cnt_seccomp); |
347 | printf(" capabilities\t\t%d\n", cnt_caps); | 357 | printf(" capabilities\t\t%d\n", cnt_caps); |
348 | printf(" noexec\t\t\t%d (include disable-exec.inc)\n", cnt_noexec); | 358 | printf(" noexec\t\t\t%d (include disable-exec.inc)\n", cnt_noexec); |
359 | printf(" noroot\t\t\t%d\n", cnt_noroot); | ||
349 | printf(" memory-deny-write-execute\t%d\n", cnt_mdwx); | 360 | printf(" memory-deny-write-execute\t%d\n", cnt_mdwx); |
350 | printf(" apparmor\t\t\t%d\n", cnt_apparmor); | 361 | printf(" apparmor\t\t\t%d\n", cnt_apparmor); |
351 | printf(" private-bin\t\t\t%d\n", cnt_privatebin); | 362 | printf(" private-bin\t\t\t%d\n", cnt_privatebin); |
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in index fd27bb35f..f1a19b86d 100644 --- a/src/zsh_completion/_firejail.in +++ b/src/zsh_completion/_firejail.in | |||
@@ -98,6 +98,7 @@ _firejail_args=( | |||
98 | '*--ignore=-[ignore command in profile files]: :' | 98 | '*--ignore=-[ignore command in profile files]: :' |
99 | '--ipc-namespace[enable a new IPC namespace]' | 99 | '--ipc-namespace[enable a new IPC namespace]' |
100 | '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails' | 100 | '--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails' |
101 | '--keep-config-pulse[disable automatic ~/.config/pulse init]' | ||
101 | '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' | 102 | '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' |
102 | '--keep-var-tmp[/var/tmp directory is untouched]' | 103 | '--keep-var-tmp[/var/tmp directory is untouched]' |
103 | '--machine-id[preserve /etc/machine-id]' | 104 | '--machine-id[preserve /etc/machine-id]' |
@@ -116,6 +117,7 @@ _firejail_args=( | |||
116 | '--nodvd[disable DVD and audio CD devices]' | 117 | '--nodvd[disable DVD and audio CD devices]' |
117 | '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files' | 118 | '*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files' |
118 | '--nogroups[disable supplementary groups]' | 119 | '--nogroups[disable supplementary groups]' |
120 | '--noinput[disable input devices]' | ||
119 | '--nonewprivs[sets the NO_NEW_PRIVS prctl]' | 121 | '--nonewprivs[sets the NO_NEW_PRIVS prctl]' |
120 | '--nosound[disable sound system]' | 122 | '--nosound[disable sound system]' |
121 | '--nou2f[disable U2F devices]' | 123 | '--nou2f[disable U2F devices]' |
diff --git a/test/utils/build.exp b/test/utils/build.exp index cdc2f3b7b..7fbe969a4 100755 --- a/test/utils/build.exp +++ b/test/utils/build.exp | |||
@@ -21,35 +21,35 @@ expect { | |||
21 | } | 21 | } |
22 | expect { | 22 | expect { |
23 | timeout {puts "TESTING ERROR 2\n";exit} | 23 | timeout {puts "TESTING ERROR 2\n";exit} |
24 | "blacklist /usr/share" | 24 | "include whitelist-usr-share-common.inc" |
25 | } | 25 | } |
26 | expect { | 26 | expect { |
27 | timeout {puts "TESTING ERROR 3\n";exit} | 27 | timeout {puts "TESTING ERROR 3\n";exit} |
28 | "blacklist /var" | 28 | "include whitelist-var-common.inc" |
29 | } | 29 | } |
30 | expect { | 30 | expect { |
31 | timeout {puts "TESTING ERROR 4\n";exit} | 31 | timeout {puts "TESTING ERROR 4\n";exit} |
32 | "private-bin cat," | 32 | "caps.drop all" |
33 | } | 33 | } |
34 | expect { | 34 | expect { |
35 | timeout {puts "TESTING ERROR 5\n";exit} | 35 | timeout {puts "TESTING ERROR 5\n";exit} |
36 | "private-dev" | 36 | "ipc-namespace" |
37 | } | 37 | } |
38 | expect { | 38 | expect { |
39 | timeout {puts "TESTING ERROR 6\n";exit} | 39 | timeout {puts "TESTING ERROR 6\n";exit} |
40 | "private-etc" | 40 | "netfilter" |
41 | } | 41 | } |
42 | expect { | 42 | expect { |
43 | timeout {puts "TESTING ERROR 7\n";exit} | 43 | timeout {puts "TESTING ERROR 7\n";exit} |
44 | "private-tmp" | 44 | "nonewprivs" |
45 | } | 45 | } |
46 | expect { | 46 | expect { |
47 | timeout {puts "TESTING ERROR 8\n";exit} | 47 | timeout {puts "TESTING ERROR 8\n";exit} |
48 | "caps.drop all" | 48 | "noroot" |
49 | } | 49 | } |
50 | expect { | 50 | expect { |
51 | timeout {puts "TESTING ERROR 9\n";exit} | 51 | timeout {puts "TESTING ERROR 9\n";exit} |
52 | "nonewprivs" | 52 | "net none" |
53 | } | 53 | } |
54 | expect { | 54 | expect { |
55 | timeout {puts "TESTING ERROR 10\n";exit} | 55 | timeout {puts "TESTING ERROR 10\n";exit} |
@@ -57,11 +57,23 @@ expect { | |||
57 | } | 57 | } |
58 | expect { | 58 | expect { |
59 | timeout {puts "TESTING ERROR 11\n";exit} | 59 | timeout {puts "TESTING ERROR 11\n";exit} |
60 | "net none" | 60 | "shell none" |
61 | } | ||
62 | expect { | ||
63 | timeout {puts "TESTING ERROR 11\n";exit} | ||
64 | "private-bin cat," | ||
61 | } | 65 | } |
62 | expect { | 66 | expect { |
63 | timeout {puts "TESTING ERROR 12\n";exit} | 67 | timeout {puts "TESTING ERROR 12\n";exit} |
64 | "shell none" | 68 | "private-dev" |
69 | } | ||
70 | expect { | ||
71 | timeout {puts "TESTING ERROR 13\n";exit} | ||
72 | "private-etc none" | ||
73 | } | ||
74 | expect { | ||
75 | timeout {puts "TESTING ERROR 14\n";exit} | ||
76 | "private-tmp" | ||
65 | } | 77 | } |
66 | after 100 | 78 | after 100 |
67 | 79 | ||