aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2023-10-24 12:43:46 -0400
committerLibravatar Kelvin M. Klann <kmk3.code@protonmail.com>2023-11-07 17:55:13 -0300
commit13b2c566df883269b55f77757bb50a5d2890ec20 (patch)
tree5c7ccc9d00886b93c4429a91671161fa6464f1f9
parentcleanup (diff)
downloadfirejail-13b2c566df883269b55f77757bb50a5d2890ec20.tar.gz
firejail-13b2c566df883269b55f77757bb50a5d2890ec20.tar.zst
firejail-13b2c566df883269b55f77757bb50a5d2890ec20.zip
feature: add Landlock support
Based on 5315 by ChrysoliteAzalea. It is based on the same underlying structure, but with a lot of refactoring/simplification and with bugfixes and improvements. Co-authored-by: Kelvin M. Klann <kmk3.code@protonmail.com> Co-authored-by: Азалия Смарагдова <charming.flurry@yandex.ru>
-rw-r--r--README3
-rw-r--r--config.mk.in2
-rwxr-xr-xconfigure86
-rw-r--r--configure.ac12
-rw-r--r--contrib/syntax/lists/profile_commands_arg0.list1
-rw-r--r--contrib/syntax/lists/profile_commands_arg1.list5
-rw-r--r--src/bash_completion/firejail.bash_completion.in19
-rw-r--r--src/firejail/checkcfg.c7
-rw-r--r--src/firejail/firejail.h22
-rw-r--r--src/firejail/landlock.c263
-rw-r--r--src/firejail/main.c28
-rw-r--r--src/firejail/profile.c38
-rw-r--r--src/firejail/sandbox.c22
-rw-r--r--src/firejail/usage.c8
-rw-r--r--src/firejail/util.c7
-rw-r--r--src/man/firejail-profile.5.in31
-rw-r--r--src/man/firejail.1.in87
-rw-r--r--src/zsh_completion/_firejail.in5
18 files changed, 616 insertions, 30 deletions
diff --git a/README b/README
index 174530cc6..2514cdeeb 100644
--- a/README
+++ b/README
@@ -197,7 +197,8 @@ avoidr (https://github.com/avoidr)
197 - fixed mpv profile 197 - fixed mpv profile
198 - various other fixes 198 - various other fixes
199Азалия Смарагдова/ChrysoliteAzalea (https://github.com/ChrysoliteAzalea) 199Азалия Смарагдова/ChrysoliteAzalea (https://github.com/ChrysoliteAzalea)
200 - add support for custom AppArmor profiles (--apparmor=) 200 - add support for custom AppArmor profiles (--apparmor=)
201 - add Landlock support
201backspac (https://github.com/backspac) 202backspac (https://github.com/backspac)
202 - firecfg fixes 203 - firecfg fixes
203 - add steam-runtime alias 204 - add steam-runtime alias
diff --git a/config.mk.in b/config.mk.in
index c76ca1a98..d50c7d2f5 100644
--- a/config.mk.in
+++ b/config.mk.in
@@ -38,6 +38,7 @@ HAVE_FIRETUNNEL=@HAVE_FIRETUNNEL@
38HAVE_FORCE_NONEWPRIVS=@HAVE_FORCE_NONEWPRIVS@ 38HAVE_FORCE_NONEWPRIVS=@HAVE_FORCE_NONEWPRIVS@
39HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ 39HAVE_GLOBALCFG=@HAVE_GLOBALCFG@
40HAVE_IDS=@HAVE_IDS@ 40HAVE_IDS=@HAVE_IDS@
41HAVE_LANDLOCK=@HAVE_LANDLOCK@
41HAVE_LTS=@HAVE_LTS@ 42HAVE_LTS=@HAVE_LTS@
42HAVE_NETWORK=@HAVE_NETWORK@ 43HAVE_NETWORK=@HAVE_NETWORK@
43HAVE_ONLY_SYSCFG_PROFILES=@HAVE_ONLY_SYSCFG_PROFILES@ 44HAVE_ONLY_SYSCFG_PROFILES=@HAVE_ONLY_SYSCFG_PROFILES@
@@ -60,6 +61,7 @@ MANFLAGS = \
60 $(HAVE_FORCE_NONEWPRIVS) \ 61 $(HAVE_FORCE_NONEWPRIVS) \
61 $(HAVE_GLOBALCFG) \ 62 $(HAVE_GLOBALCFG) \
62 $(HAVE_IDS) \ 63 $(HAVE_IDS) \
64 $(HAVE_LANDLOCK) \
63 $(HAVE_LTS) \ 65 $(HAVE_LTS) \
64 $(HAVE_NETWORK) \ 66 $(HAVE_NETWORK) \
65 $(HAVE_ONLY_SYSCFG_PROFILES) \ 67 $(HAVE_ONLY_SYSCFG_PROFILES) \
diff --git a/configure b/configure
index 54b9d4da3..8c2d3b894 100755
--- a/configure
+++ b/configure
@@ -675,6 +675,7 @@ HAVE_OVERLAYFS
675HAVE_DBUSPROXY 675HAVE_DBUSPROXY
676EXTRA_LDFLAGS 676EXTRA_LDFLAGS
677EXTRA_CFLAGS 677EXTRA_CFLAGS
678HAVE_LANDLOCK
678HAVE_SELINUX 679HAVE_SELINUX
679AA_LIBS 680AA_LIBS
680AA_CFLAGS 681AA_CFLAGS
@@ -737,6 +738,7 @@ enable_sanitizer
737enable_ids 738enable_ids
738enable_apparmor 739enable_apparmor
739enable_selinux 740enable_selinux
741enable_landlock
740enable_dbusproxy 742enable_dbusproxy
741enable_output 743enable_output
742enable_usertmpfs 744enable_usertmpfs
@@ -1396,6 +1398,7 @@ Optional Features:
1396 --enable-ids enable ids 1398 --enable-ids enable ids
1397 --enable-apparmor enable apparmor 1399 --enable-apparmor enable apparmor
1398 --enable-selinux SELinux labeling support 1400 --enable-selinux SELinux labeling support
1401 --enable-landlock Landlock self-restriction support
1399 --disable-dbusproxy disable dbus proxy 1402 --disable-dbusproxy disable dbus proxy
1400 --disable-output disable --output logging 1403 --disable-output disable --output logging
1401 --disable-usertmpfs disable tmpfs as regular user 1404 --disable-usertmpfs disable tmpfs as regular user
@@ -3739,6 +3742,58 @@ then :
3739 3742
3740fi 3743fi
3741 3744
3745HAVE_LANDLOCK=""
3746
3747# Check whether --enable-landlock was given.
3748if test ${enable_landlock+y}
3749then :
3750 enableval=$enable_landlock;
3751fi
3752
3753ac_header= ac_cache=
3754for ac_item in $ac_header_c_list
3755do
3756 if test $ac_cache; then
3757 ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
3758 if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
3759 printf "%s\n" "#define $ac_item 1" >> confdefs.h
3760 fi
3761 ac_header= ac_cache=
3762 elif test $ac_header; then
3763 ac_cache=$ac_item
3764 else
3765 ac_header=$ac_item
3766 fi
3767done
3768
3769
3770
3771
3772
3773
3774
3775
3776if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
3777then :
3778
3779printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
3780
3781fi
3782if test "x$enable_landlock" != "xno"
3783then :
3784
3785 ac_fn_c_check_header_compile "$LINENO" "linux/landlock.h" "ac_cv_header_linux_landlock_h" "$ac_includes_default"
3786if test "x$ac_cv_header_linux_landlock_h" = xyes
3787then :
3788 HAVE_LANDLOCK="-DHAVE_LANDLOCK"
3789else $as_nop
3790 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: header not found: linux/landlock.h, building without Landlock support" >&5
3791printf "%s\n" "$as_me: WARNING: header not found: linux/landlock.h, building without Landlock support" >&2;}
3792fi
3793
3794
3795fi
3796
3742 3797
3743 3798
3744 3799
@@ -4112,6 +4167,7 @@ if test "x$enable_lts" = "xyes"
4112then : 4167then :
4113 4168
4114 HAVE_LTS="-DHAVE_LTS" 4169 HAVE_LTS="-DHAVE_LTS"
4170 HAVE_LANDLOCK=""
4115 HAVE_IDS="" 4171 HAVE_IDS=""
4116 HAVE_DBUSPROXY="" 4172 HAVE_DBUSPROXY=""
4117 HAVE_OVERLAYFS="" 4173 HAVE_OVERLAYFS=""
@@ -4132,35 +4188,6 @@ then :
4132 4188
4133fi 4189fi
4134 4190
4135ac_header= ac_cache=
4136for ac_item in $ac_header_c_list
4137do
4138 if test $ac_cache; then
4139 ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
4140 if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
4141 printf "%s\n" "#define $ac_item 1" >> confdefs.h
4142 fi
4143 ac_header= ac_cache=
4144 elif test $ac_header; then
4145 ac_cache=$ac_item
4146 else
4147 ac_header=$ac_item
4148 fi
4149done
4150
4151
4152
4153
4154
4155
4156
4157
4158if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
4159then :
4160
4161printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
4162
4163fi
4164ac_fn_c_check_header_compile "$LINENO" "linux/seccomp.h" "ac_cv_header_linux_seccomp_h" "$ac_includes_default" 4191ac_fn_c_check_header_compile "$LINENO" "linux/seccomp.h" "ac_cv_header_linux_seccomp_h" "$ac_includes_default"
4165if test "x$ac_cv_header_linux_seccomp_h" = xyes 4192if test "x$ac_cv_header_linux_seccomp_h" = xyes
4166then : 4193then :
@@ -5360,6 +5387,7 @@ Features:
5360 firetunnel support: $HAVE_FIRETUNNEL 5387 firetunnel support: $HAVE_FIRETUNNEL
5361 global config: $HAVE_GLOBALCFG 5388 global config: $HAVE_GLOBALCFG
5362 IDS support: $HAVE_IDS 5389 IDS support: $HAVE_IDS
5390 Landlock support: $HAVE_LANDLOCK
5363 LTS: $HAVE_LTS 5391 LTS: $HAVE_LTS
5364 manpage support: $HAVE_MAN 5392 manpage support: $HAVE_MAN
5365 network: $HAVE_NETWORK 5393 network: $HAVE_NETWORK
diff --git a/configure.ac b/configure.ac
index 93de61b95..bd80150ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,6 +81,16 @@ AS_IF([test "x$enable_selinux" = "xyes"], [
81 LIBS="$LIBS -lselinux" 81 LIBS="$LIBS -lselinux"
82]) 82])
83 83
84HAVE_LANDLOCK=""
85AC_SUBST([HAVE_LANDLOCK])
86AC_ARG_ENABLE([landlock],
87 [AS_HELP_STRING([--enable-landlock], [Landlock self-restriction support])])
88AS_IF([test "x$enable_landlock" != "xno"], [
89 AC_CHECK_HEADER([linux/landlock.h],
90 [HAVE_LANDLOCK="-DHAVE_LANDLOCK"],
91 [AC_MSG_WARN([header not found: linux/landlock.h, building without Landlock support])])
92])
93
84AC_SUBST([EXTRA_CFLAGS]) 94AC_SUBST([EXTRA_CFLAGS])
85AC_SUBST([EXTRA_LDFLAGS]) 95AC_SUBST([EXTRA_LDFLAGS])
86 96
@@ -264,6 +274,7 @@ AC_ARG_ENABLE([lts],
264 [AS_HELP_STRING([--enable-lts], [enable long-term support software version (LTS)])]) 274 [AS_HELP_STRING([--enable-lts], [enable long-term support software version (LTS)])])
265AS_IF([test "x$enable_lts" = "xyes"], [ 275AS_IF([test "x$enable_lts" = "xyes"], [
266 HAVE_LTS="-DHAVE_LTS" 276 HAVE_LTS="-DHAVE_LTS"
277 HAVE_LANDLOCK=""
267 HAVE_IDS="" 278 HAVE_IDS=""
268 HAVE_DBUSPROXY="" 279 HAVE_DBUSPROXY=""
269 HAVE_OVERLAYFS="" 280 HAVE_OVERLAYFS=""
@@ -324,6 +335,7 @@ Features:
324 firetunnel support: $HAVE_FIRETUNNEL 335 firetunnel support: $HAVE_FIRETUNNEL
325 global config: $HAVE_GLOBALCFG 336 global config: $HAVE_GLOBALCFG
326 IDS support: $HAVE_IDS 337 IDS support: $HAVE_IDS
338 Landlock support: $HAVE_LANDLOCK
327 LTS: $HAVE_LTS 339 LTS: $HAVE_LTS
328 manpage support: $HAVE_MAN 340 manpage support: $HAVE_MAN
329 network: $HAVE_NETWORK 341 network: $HAVE_NETWORK
diff --git a/contrib/syntax/lists/profile_commands_arg0.list b/contrib/syntax/lists/profile_commands_arg0.list
index e7fecef4b..4d49e96d9 100644
--- a/contrib/syntax/lists/profile_commands_arg0.list
+++ b/contrib/syntax/lists/profile_commands_arg0.list
@@ -12,6 +12,7 @@ keep-config-pulse
12keep-dev-shm 12keep-dev-shm
13keep-shell-rc 13keep-shell-rc
14keep-var-tmp 14keep-var-tmp
15landlock
15machine-id 16machine-id
16memory-deny-write-execute 17memory-deny-write-execute
17netfilter 18netfilter
diff --git a/contrib/syntax/lists/profile_commands_arg1.list b/contrib/syntax/lists/profile_commands_arg1.list
index 5862f16ac..cce37efa0 100644
--- a/contrib/syntax/lists/profile_commands_arg1.list
+++ b/contrib/syntax/lists/profile_commands_arg1.list
@@ -29,6 +29,11 @@ ip6
29iprange 29iprange
30join-or-start 30join-or-start
31keep-fd 31keep-fd
32landlock.execute
33landlock.proc
34landlock.read
35landlock.special
36landlock.write
32mac 37mac
33mkdir 38mkdir
34mkfile 39mkfile
diff --git a/src/bash_completion/firejail.bash_completion.in b/src/bash_completion/firejail.bash_completion.in
index 98e3a035e..eab0f7df6 100644
--- a/src/bash_completion/firejail.bash_completion.in
+++ b/src/bash_completion/firejail.bash_completion.in
@@ -42,6 +42,25 @@ _firejail()
42 _filedir -d 42 _filedir -d
43 return 0 43 return 0
44 ;; 44 ;;
45 --landlock)
46 return 0
47 ;;
48 --landlock.read)
49 _filedir
50 return 0
51 ;;
52 --landlock.write)
53 _filedir
54 return 0
55 ;;
56 --landlock.special)
57 _filedir
58 return 0
59 ;;
60 --landlock.execute)
61 _filedir
62 return 0
63 ;;
45 --tmpfs) 64 --tmpfs)
46 _filedir 65 _filedir
47 return 0 66 return 0
diff --git a/src/firejail/checkcfg.c b/src/firejail/checkcfg.c
index d2289bb40..7792c6541 100644
--- a/src/firejail/checkcfg.c
+++ b/src/firejail/checkcfg.c
@@ -363,6 +363,13 @@ static const char *const compiletime_support =
363 "disabled" 363 "disabled"
364#endif 364#endif
365 365
366 "\n\t- Landlock support is "
367#ifdef HAVE_LANDLOCK
368 "enabled"
369#else
370 "disabled"
371#endif
372
366 "\n\t- networking support is " 373 "\n\t- networking support is "
367#ifdef HAVE_NETWORK 374#ifdef HAVE_NETWORK
368 "enabled" 375 "enabled"
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index c791913ea..efeda7228 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -281,6 +281,9 @@ extern int arg_overlay; // overlay option
281extern int arg_overlay_keep; // place overlay diff in a known directory 281extern int arg_overlay_keep; // place overlay diff in a known directory
282extern int arg_overlay_reuse; // allow the reuse of overlays 282extern int arg_overlay_reuse; // allow the reuse of overlays
283 283
284extern int arg_landlock; // add basic Landlock rules
285extern int arg_landlock_proc; // 0 - no access; 1 -read-only; 2 - read-write
286
284extern int arg_seccomp; // enable default seccomp filter 287extern int arg_seccomp; // enable default seccomp filter
285extern int arg_seccomp32; // enable default seccomp filter for 32 bit arch 288extern int arg_seccomp32; // enable default seccomp filter for 32 bit arch
286extern int arg_seccomp_postexec; // need postexec ld.preload library? 289extern int arg_seccomp_postexec; // need postexec ld.preload library?
@@ -950,4 +953,23 @@ void run_ids(int argc, char **argv);
950// oom.c 953// oom.c
951void oom_set(const char *oom_string); 954void oom_set(const char *oom_string);
952 955
956// landlock.c
957#ifdef HAVE_LANDLOCK
958int ll_get_fd(void);
959int ll_read(const char *allowed_path);
960int ll_write(const char *allowed_path);
961int ll_special(const char *allowed_path);
962int ll_exec(const char *allowed_path);
963int ll_basic_system(void);
964int ll_restrict(__u32 flags);
965#else
966static inline int ll_get_fd(void) { return -1; }
967static inline int ll_read(...) { return 0; }
968static inline int ll_write(...) { return 0; }
969static inline int ll_special(...) { return 0; }
970static inline int ll_exec(...) { return 0; }
971static inline int ll_basic_system(void) { return 0; }
972static inline int ll_restrict(...) { return 0; }
973#endif /* HAVE_LANDLOCK */
974
953#endif 975#endif
diff --git a/src/firejail/landlock.c b/src/firejail/landlock.c
new file mode 100644
index 000000000..b5f4140c5
--- /dev/null
+++ b/src/firejail/landlock.c
@@ -0,0 +1,263 @@
1/*
2 * Copyright (C) 2014-2023 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20
21#ifdef HAVE_LANDLOCK
22#include "firejail.h"
23#include <linux/landlock.h>
24#include <sys/prctl.h>
25#include <sys/syscall.h>
26#include <sys/types.h>
27#include <errno.h>
28#include <fcntl.h>
29
30static int ll_ruleset_fd = -1;
31
32int ll_get_fd(void) {
33 return ll_ruleset_fd;
34}
35
36#ifndef landlock_create_ruleset
37static inline int
38landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
39 const size_t size, const __u32 flags) {
40 return syscall(__NR_landlock_create_ruleset, attr, size, flags);
41}
42#endif
43
44#ifndef landlock_add_rule
45static inline int
46landlock_add_rule(const int ruleset_fd,
47 const enum landlock_rule_type rule_type,
48 const void *const rule_attr,
49 const __u32 flags) {
50 return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type,
51 rule_attr, flags);
52}
53#endif
54
55#ifndef landlock_restrict_self
56static inline int
57landlock_restrict_self(const int ruleset_fd, const __u32 flags) {
58 return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
59}
60#endif
61
62static int ll_create_full_ruleset() {
63 struct landlock_ruleset_attr attr;
64 attr.handled_access_fs =
65 LANDLOCK_ACCESS_FS_EXECUTE |
66 LANDLOCK_ACCESS_FS_MAKE_BLOCK |
67 LANDLOCK_ACCESS_FS_MAKE_CHAR |
68 LANDLOCK_ACCESS_FS_MAKE_DIR |
69 LANDLOCK_ACCESS_FS_MAKE_FIFO |
70 LANDLOCK_ACCESS_FS_MAKE_REG |
71 LANDLOCK_ACCESS_FS_MAKE_SOCK |
72 LANDLOCK_ACCESS_FS_MAKE_SYM |
73 LANDLOCK_ACCESS_FS_READ_DIR |
74 LANDLOCK_ACCESS_FS_READ_FILE |
75 LANDLOCK_ACCESS_FS_REMOVE_DIR |
76 LANDLOCK_ACCESS_FS_REMOVE_FILE |
77 LANDLOCK_ACCESS_FS_WRITE_FILE;
78
79 ll_ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
80 if (ll_ruleset_fd < 0) {
81 fprintf(stderr, "Error: failed to create a Landlock ruleset: %s\n",
82 strerror(errno));
83 }
84 return ll_ruleset_fd;
85}
86
87int ll_read(const char *allowed_path) {
88 if (ll_ruleset_fd == -1)
89 ll_ruleset_fd = ll_create_full_ruleset();
90
91 int error;
92 int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC);
93 if (allowed_fd < 0) {
94 if (arg_debug) {
95 fprintf(stderr, "%s: failed to open %s: %s\n",
96 __func__, allowed_path, strerror(errno));
97 }
98 return 0;
99 }
100 struct landlock_path_beneath_attr target;
101 target.parent_fd = allowed_fd;
102 target.allowed_access =
103 LANDLOCK_ACCESS_FS_READ_DIR |
104 LANDLOCK_ACCESS_FS_READ_FILE;
105
106 error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
107 &target, 0);
108 if (error) {
109 fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n",
110 __func__, allowed_path, strerror(errno));
111 }
112 close(allowed_fd);
113 return error;
114}
115
116int ll_write(const char *allowed_path) {
117 if (ll_ruleset_fd == -1)
118 ll_ruleset_fd = ll_create_full_ruleset();
119
120 int error;
121 int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC);
122 if (allowed_fd < 0) {
123 if (arg_debug) {
124 fprintf(stderr, "%s: failed to open %s: %s\n",
125 __func__, allowed_path, strerror(errno));
126 }
127 return 0;
128 }
129 struct landlock_path_beneath_attr target;
130 target.parent_fd = allowed_fd;
131 target.allowed_access =
132 LANDLOCK_ACCESS_FS_MAKE_DIR |
133 LANDLOCK_ACCESS_FS_MAKE_REG |
134 LANDLOCK_ACCESS_FS_MAKE_SYM |
135 LANDLOCK_ACCESS_FS_REMOVE_DIR |
136 LANDLOCK_ACCESS_FS_REMOVE_FILE |
137 LANDLOCK_ACCESS_FS_WRITE_FILE;
138
139 error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
140 &target, 0);
141 if (error) {
142 fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n",
143 __func__, allowed_path, strerror(errno));
144 }
145 close(allowed_fd);
146 return error;
147}
148
149int ll_special(const char *allowed_path) {
150 if (ll_ruleset_fd == -1)
151 ll_ruleset_fd = ll_create_full_ruleset();
152
153 int error;
154 int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC);
155 if (allowed_fd < 0) {
156 if (arg_debug) {
157 fprintf(stderr, "%s: failed to open %s: %s\n",
158 __func__, allowed_path, strerror(errno));
159 }
160 return 0;
161 }
162 struct landlock_path_beneath_attr target;
163 target.parent_fd = allowed_fd;
164 target.allowed_access =
165 LANDLOCK_ACCESS_FS_MAKE_BLOCK |
166 LANDLOCK_ACCESS_FS_MAKE_CHAR |
167 LANDLOCK_ACCESS_FS_MAKE_FIFO |
168 LANDLOCK_ACCESS_FS_MAKE_SOCK;
169
170 error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
171 &target, 0);
172 if (error) {
173 fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n",
174 __func__, allowed_path, strerror(errno));
175 }
176 close(allowed_fd);
177 return error;
178}
179
180int ll_exec(const char *allowed_path) {
181 if (ll_ruleset_fd == -1)
182 ll_ruleset_fd = ll_create_full_ruleset();
183
184 int error;
185 int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC);
186 if (allowed_fd < 0) {
187 if (arg_debug) {
188 fprintf(stderr, "%s: failed to open %s: %s\n",
189 __func__, allowed_path, strerror(errno));
190 }
191 return 0;
192 }
193 struct landlock_path_beneath_attr target;
194 target.parent_fd = allowed_fd;
195 target.allowed_access =
196 LANDLOCK_ACCESS_FS_EXECUTE;
197
198 error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
199 &target, 0);
200 if (error) {
201 fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n",
202 __func__, allowed_path, strerror(errno));
203 }
204 close(allowed_fd);
205 return error;
206}
207
208int ll_basic_system(void) {
209 assert(cfg.homedir);
210
211 if (ll_ruleset_fd == -1)
212 ll_ruleset_fd = ll_create_full_ruleset();
213
214 int error =
215 ll_read("/bin/") ||
216 ll_read("/dev/") ||
217 ll_read("/etc/") ||
218 ll_read("/lib/") ||
219 ll_read("/opt/") ||
220 ll_read("/usr/") ||
221 ll_read("/var/") ||
222 ll_read(cfg.homedir) ||
223
224 ll_write("/dev/") ||
225 ll_write(cfg.homedir) ||
226
227 ll_exec("/bin/") ||
228 ll_exec("/lib/") ||
229 ll_exec("/opt/") ||
230 ll_exec("/usr/");
231
232 if (error) {
233 fprintf(stderr, "Error: %s: failed to set --landlock rules\n",
234 __func__);
235 }
236 return error;
237}
238
239int ll_restrict(__u32 flags) {
240 if (ll_ruleset_fd == -1)
241 return 0;
242
243 int error;
244 error = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
245 if (error) {
246 fprintf(stderr, "Error: %s: failed to restrict privileges: %s\n",
247 __func__, strerror(errno));
248 goto out;
249 }
250 error = landlock_restrict_self(ll_ruleset_fd, flags);
251 if (error) {
252 fprintf(stderr, "Error: %s: failed to enforce Landlock: %s\n",
253 __func__, strerror(errno));
254 goto out;
255 }
256 if (arg_debug)
257 printf("%s: Enforcing Landlock\n", __func__);
258out:
259 close(ll_ruleset_fd);
260 return error;
261}
262
263#endif /* HAVE_LANDLOCK */
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 0c9c80137..df31fe2ce 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -75,6 +75,9 @@ int arg_overlay = 0; // overlay option
75int arg_overlay_keep = 0; // place overlay diff in a known directory 75int arg_overlay_keep = 0; // place overlay diff in a known directory
76int arg_overlay_reuse = 0; // allow the reuse of overlays 76int arg_overlay_reuse = 0; // allow the reuse of overlays
77 77
78int arg_landlock = 0; // add basic Landlock rules
79int arg_landlock_proc = 2; // 0 - no access; 1 -read-only; 2 - read-write
80
78int arg_seccomp = 0; // enable default seccomp filter 81int arg_seccomp = 0; // enable default seccomp filter
79int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch 82int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch
80int arg_seccomp_postexec = 0; // need postexec ld.preload library? 83int arg_seccomp_postexec = 0; // need postexec ld.preload library?
@@ -1500,6 +1503,31 @@ int main(int argc, char **argv, char **envp) {
1500 else 1503 else
1501 exit_err_feature("seccomp"); 1504 exit_err_feature("seccomp");
1502 } 1505 }
1506#ifdef HAVE_LANDLOCK
1507 else if (strcmp(argv[i], "--landlock") == 0)
1508 arg_landlock = 1;
1509 else if (strncmp(argv[i], "--landlock.proc=", 16) == 0) {
1510 if (strncmp(argv[i] + 16, "no", 2) == 0)
1511 arg_landlock_proc = 0;
1512 else if (strncmp(argv[i] + 16, "ro", 2) == 0)
1513 arg_landlock_proc = 1;
1514 else if (strncmp(argv[i] + 16, "rw", 2) == 0)
1515 arg_landlock_proc = 2;
1516 else {
1517 fprintf(stderr, "Error: invalid landlock.proc value: %s\n",
1518 argv[i] + 16);
1519 exit(1);
1520 }
1521 }
1522 else if (strncmp(argv[i], "--landlock.read=", 16) == 0)
1523 ll_read(argv[i] + 16);
1524 else if (strncmp(argv[i], "--landlock.write=", 17) == 0)
1525 ll_write(argv[i] + 17);
1526 else if (strncmp(argv[i], "--landlock.special=", 19) == 0)
1527 ll_special(argv[i] + 19);
1528 else if (strncmp(argv[i], "--landlock.execute=", 19) == 0)
1529 ll_exec(argv[i] + 19);
1530#endif
1503 else if (strcmp(argv[i], "--memory-deny-write-execute") == 0) { 1531 else if (strcmp(argv[i], "--memory-deny-write-execute") == 0) {
1504 if (checkcfg(CFG_SECCOMP)) 1532 if (checkcfg(CFG_SECCOMP))
1505 arg_memory_deny_write_execute = 1; 1533 arg_memory_deny_write_execute = 1;
diff --git a/src/firejail/profile.c b/src/firejail/profile.c
index a250e5611..08804c5f3 100644
--- a/src/firejail/profile.c
+++ b/src/firejail/profile.c
@@ -1077,6 +1077,44 @@ int profile_check_line(char *ptr, int lineno, const char *fname) {
1077 return 0; 1077 return 0;
1078 } 1078 }
1079 1079
1080#ifdef HAVE_LANDLOCK
1081 // Landlock ruleset paths
1082 if (strcmp(ptr, "landlock") == 0) {
1083 arg_landlock = 1;
1084 return 0;
1085 }
1086 if (strncmp(ptr, "landlock.proc ", 14) == 0) {
1087 if (strncmp(ptr + 14, "no", 2) == 0)
1088 arg_landlock_proc = 0;
1089 else if (strncmp(ptr + 14, "ro", 2) == 0)
1090 arg_landlock_proc = 1;
1091 else if (strncmp(ptr + 14, "rw", 2) == 0)
1092 arg_landlock_proc = 2;
1093 else {
1094 fprintf(stderr, "Error: invalid landlock.proc value: %s\n",
1095 ptr + 14);
1096 exit(1);
1097 }
1098 return 0;
1099 }
1100 if (strncmp(ptr, "landlock.read ", 14) == 0) {
1101 ll_read(ptr + 14);
1102 return 0;
1103 }
1104 if (strncmp(ptr, "landlock.write ", 15) == 0) {
1105 ll_write(ptr + 15);
1106 return 0;
1107 }
1108 if (strncmp(ptr, "landlock.special ", 17) == 0) {
1109 ll_special(ptr + 17);
1110 return 0;
1111 }
1112 if (strncmp(ptr, "landlock.execute ", 17) == 0) {
1113 ll_exec(ptr + 17);
1114 return 0;
1115 }
1116#endif
1117
1080 // memory deny write&execute 1118 // memory deny write&execute
1081 if (strcmp(ptr, "memory-deny-write-execute") == 0) { 1119 if (strcmp(ptr, "memory-deny-write-execute") == 0) {
1082 if (checkcfg(CFG_SECCOMP)) 1120 if (checkcfg(CFG_SECCOMP))
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 827be5d85..dbc115137 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -516,6 +516,28 @@ void start_application(int no_sandbox, int fd, char *set_sandbox_status) {
516 printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD")); 516 printf("LD_PRELOAD=%s\n", getenv("LD_PRELOAD"));
517 } 517 }
518 518
519#ifdef HAVE_LANDLOCK
520 //****************************
521 // Configure Landlock
522 //****************************
523 if (arg_landlock)
524 ll_basic_system();
525
526 if (ll_get_fd() != -1) {
527 if (arg_landlock_proc >= 1)
528 ll_read("/proc/");
529 if (arg_landlock_proc == 2)
530 ll_write("/proc/");
531 }
532
533 if (ll_restrict(0)) {
534 // It isn't safe to continue if Landlock self-restriction was
535 // enabled and the "landlock_restrict_self" syscall has failed.
536 fprintf(stderr, "Error: ll_restrict() failed, exiting...\n");
537 exit(1);
538 }
539#endif
540
519 if (just_run_the_shell) { 541 if (just_run_the_shell) {
520 char *arg[2]; 542 char *arg[2];
521 arg[0] = cfg.usershell; 543 arg[0] = cfg.usershell;
diff --git a/src/firejail/usage.c b/src/firejail/usage.c
index e8758c807..5f9185da9 100644
--- a/src/firejail/usage.c
+++ b/src/firejail/usage.c
@@ -133,6 +133,14 @@ static const char *const usage_str =
133 " --keep-fd - inherit open file descriptors to sandbox.\n" 133 " --keep-fd - inherit open file descriptors to sandbox.\n"
134 " --keep-shell-rc - do not copy shell rc files from /etc/skel\n" 134 " --keep-shell-rc - do not copy shell rc files from /etc/skel\n"
135 " --keep-var-tmp - /var/tmp directory is untouched.\n" 135 " --keep-var-tmp - /var/tmp directory is untouched.\n"
136#ifdef HAVE_LANDLOCK
137 " --landlock - add basic rules to the Landlock ruleset.\n"
138 " --landlock.proc=no|ro|rw - add an access rule for /proc to the Landlock ruleset.\n"
139 " --landlock.read=path - add a read access rule for the path to the Landlock ruleset.\n"
140 " --landlock.write=path - add a write access rule for the path to the Landlock ruleset.\n"
141 " --landlock.special=path - add an access rule for the path to the Landlock ruleset for creating block/char devices, named pipes and sockets.\n"
142 " --landlock.execute=path - add an execute access rule for the path to the Landlock ruleset.\n"
143#endif
136 " --list - list all sandboxes.\n" 144 " --list - list all sandboxes.\n"
137#ifdef HAVE_FILE_TRANSFER 145#ifdef HAVE_FILE_TRANSFER
138 " --ls=name|pid dir_or_filename - list files in sandbox container.\n" 146 " --ls=name|pid dir_or_filename - list files in sandbox container.\n"
diff --git a/src/firejail/util.c b/src/firejail/util.c
index bd32181b5..c0f30931c 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -1338,6 +1338,13 @@ void close_all(int *keep_list, size_t sz) {
1338 if (keep) 1338 if (keep)
1339 continue; 1339 continue;
1340 1340
1341#ifdef HAVE_LANDLOCK
1342 // Don't close the file descriptor of the Landlock ruleset; it
1343 // will be automatically closed by the "ll_restrict" wrapper
1344 // function.
1345 if (fd == ll_get_fd())
1346 continue;
1347#endif
1341 close(fd); 1348 close(fd);
1342 } 1349 }
1343 closedir(dir); 1350 closedir(dir);
diff --git a/src/man/firejail-profile.5.in b/src/man/firejail-profile.5.in
index 3a678b14f..76f5e4d20 100644
--- a/src/man/firejail-profile.5.in
+++ b/src/man/firejail-profile.5.in
@@ -507,6 +507,37 @@ Blacklist all Linux capabilities.
507.TP 507.TP
508\fBcaps.keep capability,capability,capability 508\fBcaps.keep capability,capability,capability
509Whitelist given Linux capabilities. 509Whitelist given Linux capabilities.
510#ifdef HAVE_LANDLOCK
511.TP
512\fBlandlock
513Create a Landlock ruleset (if it doesn't already exist) and add basic access
514rules to it.
515.TP
516\fBlandlock.proc no|ro|rw
517Add an access rule for /proc directory (read-only if set to \fBro\fR and
518read-write if set to \fBrw\fR).
519The access rule for /proc is added after this directory is set up in the
520sandbox.
521Access rules for /proc set up with other Landlock-related profile options have
522no effect.
523.TP
524\fBlandlock.read path
525Create a Landlock ruleset (if it doesn't already exist) and add a read access
526rule for path.
527.TP
528\fBlandlock.write path
529Create a Landlock ruleset (if it doesn't already exist) and add a write access
530rule for path.
531.TP
532\fBlandlock.special path
533Create a Landlock ruleset (if it doesn't already exist) and add a rule that
534allows the creation of block devices, character devices, named pipes (FIFOs)
535and Unix domain sockets beneath given path.
536.TP
537\fBlandlock.execute path
538Create a Landlock ruleset (if it doesn't already exist) and add an execution
539permission rule for path.
540#endif
510.TP 541.TP
511\fBmemory-deny-write-execute 542\fBmemory-deny-write-execute
512Install a seccomp filter to block attempts to create memory mappings 543Install a seccomp filter to block attempts to create memory mappings
diff --git a/src/man/firejail.1.in b/src/man/firejail.1.in
index 06969e851..d5a00c41b 100644
--- a/src/man/firejail.1.in
+++ b/src/man/firejail.1.in
@@ -1243,6 +1243,52 @@ Example:
1243.br 1243.br
1244$ firejail --keep-var-tmp 1244$ firejail --keep-var-tmp
1245 1245
1246#ifdef HAVE_LANDLOCK
1247.TP
1248\fB\-\-landlock
1249Create a Landlock ruleset (if it doesn't already exist) and add basic access
1250rules to it.
1251The basic set of rules applies the following access permissions:
1252.PP
1253.RS
1254- read: /bin, /dev, /etc, /lib, /opt, /proc, /usr, /var
1255.br
1256- write: /dev, /proc
1257.br
1258- exec: /bin, /lib, /opt, /usr
1259.RE
1260.PP
1261See the \fBLANDLOCK\fR section for more information.
1262.TP
1263\fB\-\-landlock.proc=no|ro|rw
1264Add an access rule for /proc directory (read-only if set to \fBro\fR and
1265read-write if set to \fBrw\fR).
1266The access rule for /proc is added after this directory is set up in the
1267sandbox.
1268Access rules for /proc set up with other Landlock-related command-line options
1269have no effect.
1270.TP
1271\fB\-\-landlock.read=path
1272Create a Landlock ruleset (if it doesn't already exist) and add a read access
1273rule for path.
1274.TP
1275\fB\-\-landlock.write=path
1276Create a Landlock ruleset (if it doesn't already exist) and add a write access
1277rule for path.
1278.TP
1279\fB\-\-landlock.special=path
1280Create a Landlock ruleset (if it doesn't already exist) and add a rule that
1281allows the creation of block devices, character devices, named pipes (FIFOs)
1282and Unix domain sockets beneath given path.
1283.TP
1284\fB\-\-landlock.execute=path
1285Create a Landlock ruleset (if it doesn't already exist) and add an execution
1286permission rule for path.
1287.PP
1288Example:
1289.PP
1290$ firejail \-\-landlock.read=/ \-\-landlock.write=/home \-\-landlock.execute=/usr
1291#endif
1246.TP 1292.TP
1247\fB\-\-list 1293\fB\-\-list
1248List all sandboxes, see \fBMONITORING\fR section for more details. 1294List all sandboxes, see \fBMONITORING\fR section for more details.
@@ -3365,6 +3411,47 @@ To enable AppArmor confinement on top of your current Firejail security features
3365$ firejail --apparmor firefox 3411$ firejail --apparmor firefox
3366#endif 3412#endif
3367 3413
3414#ifdef HAVE_LANDLOCK
3415.SH LANDLOCK
3416Landlock is a Linux security module first introduced in version 5.13 of the
3417Linux kernel.
3418It allows unprivileged processes to restrict their access to the filesystem.
3419Once imposed, these restrictions can never be removed, and all child processes
3420created by a Landlock-restricted processes inherit these restrictions.
3421Firejail supports Landlock as an additional sandboxing feature.
3422It can be used to ensure that a sandboxed application can only access files and
3423directories that it was explicitly allowed to access.
3424Firejail supports populating the ruleset with both a basic set of rules (see
3425\fB\-\-landlock\fR) and with a custom set of rules.
3426.TP
3427Important notes:
3428.PP
3429.RS
3430- A process can install a Landlock ruleset only if it has either
3431\fBCAP_SYS_ADMIN\fR in its effective capability set, or the "No New
3432Privileges" restriction enabled.
3433Because of this, enabling the Landlock feature will also cause Firejail to
3434enable the "No New Privileges" restriction, regardless of the profile or the
3435\fB\-\-no\-new\-privs\fR command line option.
3436.PP
3437- Access to the /proc directory is managed through the \fB\-\-landlock.proc\fR
3438command line option.
3439.PP
3440- Access to the /etc directory is automatically allowed.
3441To override this, use the \fB\-\-writable\-etc\fR command line option.
3442You can also use the \fB\-\-private\-etc\fR option to restrict access to the
3443/etc directory.
3444.RE
3445.PP
3446To enable Landlock self-restriction on top of your current Firejail security
3447features, pass \fB\-\-landlock\fR flag to Firejail command line.
3448You can also use \fB\-\-landlock.read\fR, \fB\-\-landlock.write\fR,
3449\fB\-\-landlock.special\fR and \fB\-\-landlock.execute\fR options together with
3450\fB\-\-landlock\fR or instead of it.
3451Example:
3452.PP
3453$ firejail \-\-landlock \-\-landlock.read=/media \-\-landlock.proc=ro mc
3454#endif
3368.SH DESKTOP INTEGRATION 3455.SH DESKTOP INTEGRATION
3369A symbolic link to /usr/bin/firejail under the name of a program, will start the program in Firejail sandbox. 3456A symbolic link to /usr/bin/firejail under the name of a program, will start the program in Firejail sandbox.
3370The symbolic link should be placed in the first $PATH position. On most systems, a good place 3457The symbolic link should be placed in the first $PATH position. On most systems, a good place
diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in
index 7e87bb991..89cb1b84c 100644
--- a/src/zsh_completion/_firejail.in
+++ b/src/zsh_completion/_firejail.in
@@ -106,6 +106,11 @@ _firejail_args=(
106 '--keep-fd[inherit open file descriptors to sandbox]: :' 106 '--keep-fd[inherit open file descriptors to sandbox]: :'
107 '--keep-shell-rc[do not copy shell rc files from /etc/skel]' 107 '--keep-shell-rc[do not copy shell rc files from /etc/skel]'
108 '--keep-var-tmp[/var/tmp directory is untouched]' 108 '--keep-var-tmp[/var/tmp directory is untouched]'
109 '--landlock.proc=-[add an access rule for /proc to the Landlock ruleset]: :(no ro rw)'
110 '--landlock.read=-[add a read access rule for the path to the Landlock ruleset]: :_files'
111 '--landlock.write=-[add a write access rule for the path to the Landlock ruleset]: :_files'
112 '--landlock.special=-[add an access rule for the path to the Landlock ruleset for creating block/char devices, named pipes and sockets]: :_files'
113 '--landlock.execute=-[add an execute access rule for the path to the Landlock ruleset]: :_files'
109 '--machine-id[spoof /etc/machine-id with a random id]' 114 '--machine-id[spoof /etc/machine-id with a random id]'
110 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' 115 '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]'
111 '*--mkdir=-[create a directory]:' 116 '*--mkdir=-[create a directory]:'