diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bash_completion/firejail.bash_completion.in | 19 | ||||
-rw-r--r-- | src/firejail/checkcfg.c | 7 | ||||
-rw-r--r-- | src/firejail/firejail.h | 37 | ||||
-rw-r--r-- | src/firejail/landlock.c | 361 | ||||
-rw-r--r-- | src/firejail/main.c | 28 | ||||
-rw-r--r-- | src/firejail/profile.c | 41 | ||||
-rw-r--r-- | src/firejail/sandbox.c | 22 | ||||
-rw-r--r-- | src/firejail/usage.c | 8 | ||||
-rw-r--r-- | src/firejail/util.c | 7 | ||||
-rw-r--r-- | src/man/firejail-profile.5.in | 31 | ||||
-rw-r--r-- | src/man/firejail.1.in | 87 | ||||
-rw-r--r-- | src/zsh_completion/_firejail.in | 5 |
12 files changed, 651 insertions, 2 deletions
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..5a96fcbfd 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h | |||
@@ -150,6 +150,17 @@ typedef struct profile_entry_t { | |||
150 | 150 | ||
151 | } ProfileEntry; | 151 | } ProfileEntry; |
152 | 152 | ||
153 | typedef struct landlock_entry_t { | ||
154 | struct landlock_entry_t *next; | ||
155 | #define LL_READ 0 | ||
156 | #define LL_WRITE 1 | ||
157 | #define LL_SPECIAL 2 | ||
158 | #define LL_EXEC 3 | ||
159 | #define LL_MAX 4 | ||
160 | int type; | ||
161 | char *data; | ||
162 | } LandlockEntry; | ||
163 | |||
153 | typedef struct config_t { | 164 | typedef struct config_t { |
154 | // user data | 165 | // user data |
155 | char *username; | 166 | char *username; |
@@ -159,6 +170,7 @@ typedef struct config_t { | |||
159 | // filesystem | 170 | // filesystem |
160 | ProfileEntry *profile; | 171 | ProfileEntry *profile; |
161 | ProfileEntry *profile_rebuild_etc; // blacklist files in /etc directory used by fs_rebuild_etc() | 172 | ProfileEntry *profile_rebuild_etc; // blacklist files in /etc directory used by fs_rebuild_etc() |
173 | LandlockEntry *lprofile; | ||
162 | 174 | ||
163 | #define MAX_PROFILE_IGNORE 32 | 175 | #define MAX_PROFILE_IGNORE 32 |
164 | char *profile_ignore[MAX_PROFILE_IGNORE]; | 176 | char *profile_ignore[MAX_PROFILE_IGNORE]; |
@@ -281,6 +293,9 @@ extern int arg_overlay; // overlay option | |||
281 | extern int arg_overlay_keep; // place overlay diff in a known directory | 293 | extern int arg_overlay_keep; // place overlay diff in a known directory |
282 | extern int arg_overlay_reuse; // allow the reuse of overlays | 294 | extern int arg_overlay_reuse; // allow the reuse of overlays |
283 | 295 | ||
296 | extern int arg_landlock; // add basic Landlock rules | ||
297 | extern int arg_landlock_proc; // 0 - no access; 1 -read-only; 2 - read-write | ||
298 | |||
284 | extern int arg_seccomp; // enable default seccomp filter | 299 | extern int arg_seccomp; // enable default seccomp filter |
285 | extern int arg_seccomp32; // enable default seccomp filter for 32 bit arch | 300 | extern int arg_seccomp32; // enable default seccomp filter for 32 bit arch |
286 | extern int arg_seccomp_postexec; // need postexec ld.preload library? | 301 | extern int arg_seccomp_postexec; // need postexec ld.preload library? |
@@ -950,4 +965,26 @@ void run_ids(int argc, char **argv); | |||
950 | // oom.c | 965 | // oom.c |
951 | void oom_set(const char *oom_string); | 966 | void oom_set(const char *oom_string); |
952 | 967 | ||
968 | // landlock.c | ||
969 | #ifdef HAVE_LANDLOCK | ||
970 | int ll_get_fd(void); | ||
971 | int ll_is_supported(void); | ||
972 | int ll_read(const char *allowed_path); | ||
973 | int ll_write(const char *allowed_path); | ||
974 | int ll_special(const char *allowed_path); | ||
975 | int ll_exec(const char *allowed_path); | ||
976 | int ll_basic_system(void); | ||
977 | int ll_restrict(__u32 flags); | ||
978 | void ll_add_profile(int type, const char *data); | ||
979 | #else | ||
980 | static inline int ll_get_fd(void) { return -1; } | ||
981 | static inline int ll_read(...) { return 0; } | ||
982 | static inline int ll_write(...) { return 0; } | ||
983 | static inline int ll_special(...) { return 0; } | ||
984 | static inline int ll_exec(...) { return 0; } | ||
985 | static inline int ll_basic_system(void) { return 0; } | ||
986 | static inline int ll_restrict(...) { return 0; } | ||
987 | static inline void ll_add_profile(...) { return; } | ||
988 | #endif /* HAVE_LANDLOCK */ | ||
989 | |||
953 | #endif | 990 | #endif |
diff --git a/src/firejail/landlock.c b/src/firejail/landlock.c new file mode 100644 index 000000000..27fc1d748 --- /dev/null +++ b/src/firejail/landlock.c | |||
@@ -0,0 +1,361 @@ | |||
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 | |||
30 | static int ll_ruleset_fd = -1; | ||
31 | static int ll_abi = -1; | ||
32 | |||
33 | int ll_get_fd(void) { | ||
34 | return ll_ruleset_fd; | ||
35 | } | ||
36 | |||
37 | #ifndef landlock_create_ruleset | ||
38 | static inline int | ||
39 | landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, | ||
40 | const size_t size, const __u32 flags) { | ||
41 | return syscall(__NR_landlock_create_ruleset, attr, size, flags); | ||
42 | } | ||
43 | #endif | ||
44 | |||
45 | #ifndef landlock_add_rule | ||
46 | static inline int | ||
47 | landlock_add_rule(const int ruleset_fd, | ||
48 | const enum landlock_rule_type rule_type, | ||
49 | const void *const rule_attr, | ||
50 | const __u32 flags) { | ||
51 | return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, | ||
52 | rule_attr, flags); | ||
53 | } | ||
54 | #endif | ||
55 | |||
56 | #ifndef landlock_restrict_self | ||
57 | static inline int | ||
58 | landlock_restrict_self(const int ruleset_fd, const __u32 flags) { | ||
59 | return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); | ||
60 | } | ||
61 | #endif | ||
62 | |||
63 | int ll_is_supported(void) { | ||
64 | if (ll_abi != -1) | ||
65 | goto out; | ||
66 | |||
67 | ll_abi = landlock_create_ruleset(NULL, 0, | ||
68 | LANDLOCK_CREATE_RULESET_VERSION); | ||
69 | if (ll_abi < 1) { | ||
70 | ll_abi = 0; | ||
71 | fprintf(stderr, "Warning: Landlock is disabled or not supported: %s, " | ||
72 | "ignoring landlock commands\n", | ||
73 | strerror(errno)); | ||
74 | goto out; | ||
75 | } | ||
76 | if (arg_debug) { | ||
77 | printf("Detected Landlock ABI version %d\n", ll_abi); | ||
78 | } | ||
79 | out: | ||
80 | return ll_abi; | ||
81 | } | ||
82 | |||
83 | static int ll_create_full_ruleset() { | ||
84 | if (!ll_is_supported()) | ||
85 | return -1; | ||
86 | |||
87 | struct landlock_ruleset_attr attr; | ||
88 | attr.handled_access_fs = | ||
89 | LANDLOCK_ACCESS_FS_EXECUTE | | ||
90 | LANDLOCK_ACCESS_FS_MAKE_BLOCK | | ||
91 | LANDLOCK_ACCESS_FS_MAKE_CHAR | | ||
92 | LANDLOCK_ACCESS_FS_MAKE_DIR | | ||
93 | LANDLOCK_ACCESS_FS_MAKE_FIFO | | ||
94 | LANDLOCK_ACCESS_FS_MAKE_REG | | ||
95 | LANDLOCK_ACCESS_FS_MAKE_SOCK | | ||
96 | LANDLOCK_ACCESS_FS_MAKE_SYM | | ||
97 | LANDLOCK_ACCESS_FS_READ_DIR | | ||
98 | LANDLOCK_ACCESS_FS_READ_FILE | | ||
99 | LANDLOCK_ACCESS_FS_REMOVE_DIR | | ||
100 | LANDLOCK_ACCESS_FS_REMOVE_FILE | | ||
101 | LANDLOCK_ACCESS_FS_WRITE_FILE; | ||
102 | |||
103 | ll_ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); | ||
104 | if (ll_ruleset_fd < 0) { | ||
105 | fprintf(stderr, "Error: failed to create a Landlock ruleset: %s\n", | ||
106 | strerror(errno)); | ||
107 | } | ||
108 | return ll_ruleset_fd; | ||
109 | } | ||
110 | |||
111 | int ll_read(const char *allowed_path) { | ||
112 | if (!ll_is_supported()) | ||
113 | return 0; | ||
114 | |||
115 | if (ll_ruleset_fd == -1) | ||
116 | ll_ruleset_fd = ll_create_full_ruleset(); | ||
117 | |||
118 | int error; | ||
119 | int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC); | ||
120 | if (allowed_fd < 0) { | ||
121 | if (arg_debug) { | ||
122 | fprintf(stderr, "%s: failed to open %s: %s\n", | ||
123 | __func__, allowed_path, strerror(errno)); | ||
124 | } | ||
125 | return 0; | ||
126 | } | ||
127 | struct landlock_path_beneath_attr target; | ||
128 | target.parent_fd = allowed_fd; | ||
129 | target.allowed_access = | ||
130 | LANDLOCK_ACCESS_FS_READ_DIR | | ||
131 | LANDLOCK_ACCESS_FS_READ_FILE; | ||
132 | |||
133 | error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, | ||
134 | &target, 0); | ||
135 | if (error) { | ||
136 | fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n", | ||
137 | __func__, allowed_path, strerror(errno)); | ||
138 | } | ||
139 | close(allowed_fd); | ||
140 | return error; | ||
141 | } | ||
142 | |||
143 | int ll_write(const char *allowed_path) { | ||
144 | if (!ll_is_supported()) | ||
145 | return 0; | ||
146 | |||
147 | if (ll_ruleset_fd == -1) | ||
148 | ll_ruleset_fd = ll_create_full_ruleset(); | ||
149 | |||
150 | int error; | ||
151 | int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC); | ||
152 | if (allowed_fd < 0) { | ||
153 | if (arg_debug) { | ||
154 | fprintf(stderr, "%s: failed to open %s: %s\n", | ||
155 | __func__, allowed_path, strerror(errno)); | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | struct landlock_path_beneath_attr target; | ||
160 | target.parent_fd = allowed_fd; | ||
161 | target.allowed_access = | ||
162 | LANDLOCK_ACCESS_FS_MAKE_DIR | | ||
163 | LANDLOCK_ACCESS_FS_MAKE_REG | | ||
164 | LANDLOCK_ACCESS_FS_MAKE_SYM | | ||
165 | LANDLOCK_ACCESS_FS_REMOVE_DIR | | ||
166 | LANDLOCK_ACCESS_FS_REMOVE_FILE | | ||
167 | LANDLOCK_ACCESS_FS_WRITE_FILE; | ||
168 | |||
169 | error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, | ||
170 | &target, 0); | ||
171 | if (error) { | ||
172 | fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n", | ||
173 | __func__, allowed_path, strerror(errno)); | ||
174 | } | ||
175 | close(allowed_fd); | ||
176 | return error; | ||
177 | } | ||
178 | |||
179 | int ll_special(const char *allowed_path) { | ||
180 | if (!ll_is_supported()) | ||
181 | return 0; | ||
182 | |||
183 | if (ll_ruleset_fd == -1) | ||
184 | ll_ruleset_fd = ll_create_full_ruleset(); | ||
185 | |||
186 | int error; | ||
187 | int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC); | ||
188 | if (allowed_fd < 0) { | ||
189 | if (arg_debug) { | ||
190 | fprintf(stderr, "%s: failed to open %s: %s\n", | ||
191 | __func__, allowed_path, strerror(errno)); | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | struct landlock_path_beneath_attr target; | ||
196 | target.parent_fd = allowed_fd; | ||
197 | target.allowed_access = | ||
198 | LANDLOCK_ACCESS_FS_MAKE_BLOCK | | ||
199 | LANDLOCK_ACCESS_FS_MAKE_CHAR | | ||
200 | LANDLOCK_ACCESS_FS_MAKE_FIFO | | ||
201 | LANDLOCK_ACCESS_FS_MAKE_SOCK; | ||
202 | |||
203 | error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, | ||
204 | &target, 0); | ||
205 | if (error) { | ||
206 | fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n", | ||
207 | __func__, allowed_path, strerror(errno)); | ||
208 | } | ||
209 | close(allowed_fd); | ||
210 | return error; | ||
211 | } | ||
212 | |||
213 | int ll_exec(const char *allowed_path) { | ||
214 | if (!ll_is_supported()) | ||
215 | return 0; | ||
216 | |||
217 | if (ll_ruleset_fd == -1) | ||
218 | ll_ruleset_fd = ll_create_full_ruleset(); | ||
219 | |||
220 | int error; | ||
221 | int allowed_fd = open(allowed_path, O_PATH | O_CLOEXEC); | ||
222 | if (allowed_fd < 0) { | ||
223 | if (arg_debug) { | ||
224 | fprintf(stderr, "%s: failed to open %s: %s\n", | ||
225 | __func__, allowed_path, strerror(errno)); | ||
226 | } | ||
227 | return 0; | ||
228 | } | ||
229 | struct landlock_path_beneath_attr target; | ||
230 | target.parent_fd = allowed_fd; | ||
231 | target.allowed_access = | ||
232 | LANDLOCK_ACCESS_FS_EXECUTE; | ||
233 | |||
234 | error = landlock_add_rule(ll_ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, | ||
235 | &target, 0); | ||
236 | if (error) { | ||
237 | fprintf(stderr, "Error: %s: failed to add Landlock rule for %s: %s\n", | ||
238 | __func__, allowed_path, strerror(errno)); | ||
239 | } | ||
240 | close(allowed_fd); | ||
241 | return error; | ||
242 | } | ||
243 | |||
244 | int ll_basic_system(void) { | ||
245 | assert(cfg.homedir); | ||
246 | |||
247 | if (!ll_is_supported()) | ||
248 | return 0; | ||
249 | |||
250 | if (ll_ruleset_fd == -1) | ||
251 | ll_ruleset_fd = ll_create_full_ruleset(); | ||
252 | |||
253 | int error; | ||
254 | char *rundir; | ||
255 | if (asprintf(&rundir, "/run/user/%d", getuid()) == -1) | ||
256 | errExit("asprintf"); | ||
257 | |||
258 | error = | ||
259 | ll_read("/") || // whole system read | ||
260 | ll_special("/") || // sockets etc. | ||
261 | |||
262 | ll_write("/tmp") || // write access | ||
263 | ll_write("/dev") || | ||
264 | ll_write("/run/shm") || | ||
265 | ll_write(cfg.homedir) || | ||
266 | ll_write(rundir) || | ||
267 | |||
268 | ll_exec("/opt") || // exec access | ||
269 | ll_exec("/bin") || | ||
270 | ll_exec("/sbin") || | ||
271 | ll_exec("/lib") || | ||
272 | ll_exec("/lib32") || | ||
273 | ll_exec("/libx32") || | ||
274 | ll_exec("/lib64") || | ||
275 | ll_exec("/usr/bin") || | ||
276 | ll_exec("/usr/sbin") || | ||
277 | ll_exec("/usr/games") || | ||
278 | ll_exec("/usr/lib") || | ||
279 | ll_exec("/usr/lib32") || | ||
280 | ll_exec("/usr/libx32") || | ||
281 | ll_exec("/usr/lib64") || | ||
282 | ll_exec("/usr/local/bin") || | ||
283 | ll_exec("/usr/local/sbin") || | ||
284 | ll_exec("/usr/local/games") || | ||
285 | ll_exec("/usr/local/lib") || | ||
286 | ll_exec("/run/firejail"); // appimage and various firejail features | ||
287 | |||
288 | if (error) { | ||
289 | fprintf(stderr, "Error: %s: failed to set --landlock rules\n", | ||
290 | __func__); | ||
291 | } | ||
292 | free(rundir); | ||
293 | return error; | ||
294 | } | ||
295 | |||
296 | int ll_restrict(__u32 flags) { | ||
297 | if (!ll_is_supported()) | ||
298 | return 0; | ||
299 | |||
300 | int (*fnc[])(const char *) = { | ||
301 | ll_read, | ||
302 | ll_write, | ||
303 | ll_special, | ||
304 | ll_exec, | ||
305 | NULL | ||
306 | }; | ||
307 | |||
308 | LandlockEntry *ptr = cfg.lprofile; | ||
309 | while (ptr) { | ||
310 | fnc[ptr->type](ptr->data); | ||
311 | ptr = ptr->next; | ||
312 | } | ||
313 | |||
314 | if (ll_ruleset_fd == -1) | ||
315 | return 0; | ||
316 | |||
317 | int error; | ||
318 | error = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | ||
319 | if (error) { | ||
320 | fprintf(stderr, "Error: %s: failed to restrict privileges: %s\n", | ||
321 | __func__, strerror(errno)); | ||
322 | goto out; | ||
323 | } | ||
324 | error = landlock_restrict_self(ll_ruleset_fd, flags); | ||
325 | if (error) { | ||
326 | fprintf(stderr, "Error: %s: failed to enforce Landlock: %s\n", | ||
327 | __func__, strerror(errno)); | ||
328 | goto out; | ||
329 | } | ||
330 | if (arg_debug) | ||
331 | printf("%s: Enforcing Landlock\n", __func__); | ||
332 | out: | ||
333 | close(ll_ruleset_fd); | ||
334 | return error; | ||
335 | } | ||
336 | |||
337 | void ll_add_profile(int type, const char *data) { | ||
338 | assert(type >= 0); | ||
339 | assert(type < LL_MAX); | ||
340 | assert(data); | ||
341 | |||
342 | if (!ll_is_supported()) | ||
343 | return; | ||
344 | |||
345 | const char *str = data; | ||
346 | while (*str == ' ' || *str == '\t') | ||
347 | str++; | ||
348 | |||
349 | LandlockEntry *ptr = malloc(sizeof(LandlockEntry)); | ||
350 | if (!ptr) | ||
351 | errExit("malloc"); | ||
352 | memset(ptr, 0, sizeof(LandlockEntry)); | ||
353 | ptr->type = type; | ||
354 | ptr->data = strdup(str); | ||
355 | if (!ptr->data) | ||
356 | errExit("strdup"); | ||
357 | ptr->next = cfg.lprofile; | ||
358 | cfg.lprofile = ptr; | ||
359 | } | ||
360 | |||
361 | #endif /* HAVE_LANDLOCK */ | ||
diff --git a/src/firejail/main.c b/src/firejail/main.c index 0327f8bda..5bcc3a0e5 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c | |||
@@ -75,6 +75,9 @@ int arg_overlay = 0; // overlay option | |||
75 | int arg_overlay_keep = 0; // place overlay diff in a known directory | 75 | int arg_overlay_keep = 0; // place overlay diff in a known directory |
76 | int arg_overlay_reuse = 0; // allow the reuse of overlays | 76 | int arg_overlay_reuse = 0; // allow the reuse of overlays |
77 | 77 | ||
78 | int arg_landlock = 0; // add basic Landlock rules | ||
79 | int arg_landlock_proc = 2; // 0 - no access; 1 -read-only; 2 - read-write | ||
80 | |||
78 | int arg_seccomp = 0; // enable default seccomp filter | 81 | int arg_seccomp = 0; // enable default seccomp filter |
79 | int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch | 82 | int arg_seccomp32 = 0; // enable default seccomp filter for 32 bit arch |
80 | int arg_seccomp_postexec = 0; // need postexec ld.preload library? | 83 | int 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_add_profile(LL_READ, argv[i] + 16); | ||
1524 | else if (strncmp(argv[i], "--landlock.write=", 17) == 0) | ||
1525 | ll_add_profile(LL_WRITE, argv[i] + 17); | ||
1526 | else if (strncmp(argv[i], "--landlock.special=", 19) == 0) | ||
1527 | ll_add_profile(LL_SPECIAL, argv[i] + 19); | ||
1528 | else if (strncmp(argv[i], "--landlock.execute=", 19) == 0) | ||
1529 | ll_add_profile(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 0f60e9b7d..62d3c78e7 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c | |||
@@ -1073,6 +1073,44 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { | |||
1073 | return 0; | 1073 | return 0; |
1074 | } | 1074 | } |
1075 | 1075 | ||
1076 | #ifdef HAVE_LANDLOCK | ||
1077 | // Landlock ruleset paths | ||
1078 | if (strcmp(ptr, "landlock") == 0) { | ||
1079 | arg_landlock = 1; | ||
1080 | return 0; | ||
1081 | } | ||
1082 | if (strncmp(ptr, "landlock.proc ", 14) == 0) { | ||
1083 | if (strncmp(ptr + 14, "no", 2) == 0) | ||
1084 | arg_landlock_proc = 0; | ||
1085 | else if (strncmp(ptr + 14, "ro", 2) == 0) | ||
1086 | arg_landlock_proc = 1; | ||
1087 | else if (strncmp(ptr + 14, "rw", 2) == 0) | ||
1088 | arg_landlock_proc = 2; | ||
1089 | else { | ||
1090 | fprintf(stderr, "Error: invalid landlock.proc value: %s\n", | ||
1091 | ptr + 14); | ||
1092 | exit(1); | ||
1093 | } | ||
1094 | return 0; | ||
1095 | } | ||
1096 | if (strncmp(ptr, "landlock.read ", 14) == 0) { | ||
1097 | ll_add_profile(LL_READ, ptr + 14); | ||
1098 | return 0; | ||
1099 | } | ||
1100 | if (strncmp(ptr, "landlock.write ", 15) == 0) { | ||
1101 | ll_add_profile(LL_WRITE, ptr + 15); | ||
1102 | return 0; | ||
1103 | } | ||
1104 | if (strncmp(ptr, "landlock.special ", 17) == 0) { | ||
1105 | ll_add_profile(LL_SPECIAL, ptr + 17); | ||
1106 | return 0; | ||
1107 | } | ||
1108 | if (strncmp(ptr, "landlock.execute ", 17) == 0) { | ||
1109 | ll_add_profile(LL_EXEC, ptr + 17); | ||
1110 | return 0; | ||
1111 | } | ||
1112 | #endif | ||
1113 | |||
1076 | // memory deny write&execute | 1114 | // memory deny write&execute |
1077 | if (strcmp(ptr, "memory-deny-write-execute") == 0) { | 1115 | if (strcmp(ptr, "memory-deny-write-execute") == 0) { |
1078 | if (checkcfg(CFG_SECCOMP)) | 1116 | if (checkcfg(CFG_SECCOMP)) |
@@ -1897,8 +1935,7 @@ void profile_read(const char *fname) { | |||
1897 | fclose(fp); | 1935 | fclose(fp); |
1898 | } | 1936 | } |
1899 | 1937 | ||
1900 | char *profile_list_normalize(char *list) | 1938 | char *profile_list_normalize(char *list) { |
1901 | { | ||
1902 | /* Remove redundant commas. | 1939 | /* Remove redundant commas. |
1903 | * | 1940 | * |
1904 | * As result is always shorter than original, | 1941 | * As result is always shorter than original, |
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 |
509 | Whitelist given Linux capabilities. | 509 | Whitelist given Linux capabilities. |
510 | #ifdef HAVE_LANDLOCK | ||
511 | .TP | ||
512 | \fBlandlock | ||
513 | Create a Landlock ruleset (if it doesn't already exist) and add basic access | ||
514 | rules to it. | ||
515 | .TP | ||
516 | \fBlandlock.proc no|ro|rw | ||
517 | Add an access rule for /proc directory (read-only if set to \fBro\fR and | ||
518 | read-write if set to \fBrw\fR). | ||
519 | The access rule for /proc is added after this directory is set up in the | ||
520 | sandbox. | ||
521 | Access rules for /proc set up with other Landlock-related profile options have | ||
522 | no effect. | ||
523 | .TP | ||
524 | \fBlandlock.read path | ||
525 | Create a Landlock ruleset (if it doesn't already exist) and add a read access | ||
526 | rule for path. | ||
527 | .TP | ||
528 | \fBlandlock.write path | ||
529 | Create a Landlock ruleset (if it doesn't already exist) and add a write access | ||
530 | rule for path. | ||
531 | .TP | ||
532 | \fBlandlock.special path | ||
533 | Create a Landlock ruleset (if it doesn't already exist) and add a rule that | ||
534 | allows the creation of block devices, character devices, named pipes (FIFOs) | ||
535 | and Unix domain sockets beneath given path. | ||
536 | .TP | ||
537 | \fBlandlock.execute path | ||
538 | Create a Landlock ruleset (if it doesn't already exist) and add an execution | ||
539 | permission rule for path. | ||
540 | #endif | ||
510 | .TP | 541 | .TP |
511 | \fBmemory-deny-write-execute | 542 | \fBmemory-deny-write-execute |
512 | Install a seccomp filter to block attempts to create memory mappings | 543 | Install 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 | ||
1249 | Create a Landlock ruleset (if it doesn't already exist) and add basic access | ||
1250 | rules to it. | ||
1251 | The 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 | ||
1261 | See the \fBLANDLOCK\fR section for more information. | ||
1262 | .TP | ||
1263 | \fB\-\-landlock.proc=no|ro|rw | ||
1264 | Add an access rule for /proc directory (read-only if set to \fBro\fR and | ||
1265 | read-write if set to \fBrw\fR). | ||
1266 | The access rule for /proc is added after this directory is set up in the | ||
1267 | sandbox. | ||
1268 | Access rules for /proc set up with other Landlock-related command-line options | ||
1269 | have no effect. | ||
1270 | .TP | ||
1271 | \fB\-\-landlock.read=path | ||
1272 | Create a Landlock ruleset (if it doesn't already exist) and add a read access | ||
1273 | rule for path. | ||
1274 | .TP | ||
1275 | \fB\-\-landlock.write=path | ||
1276 | Create a Landlock ruleset (if it doesn't already exist) and add a write access | ||
1277 | rule for path. | ||
1278 | .TP | ||
1279 | \fB\-\-landlock.special=path | ||
1280 | Create a Landlock ruleset (if it doesn't already exist) and add a rule that | ||
1281 | allows the creation of block devices, character devices, named pipes (FIFOs) | ||
1282 | and Unix domain sockets beneath given path. | ||
1283 | .TP | ||
1284 | \fB\-\-landlock.execute=path | ||
1285 | Create a Landlock ruleset (if it doesn't already exist) and add an execution | ||
1286 | permission rule for path. | ||
1287 | .PP | ||
1288 | Example: | ||
1289 | .PP | ||
1290 | $ firejail \-\-landlock.read=/ \-\-landlock.write=/home \-\-landlock.execute=/usr | ||
1291 | #endif | ||
1246 | .TP | 1292 | .TP |
1247 | \fB\-\-list | 1293 | \fB\-\-list |
1248 | List all sandboxes, see \fBMONITORING\fR section for more details. | 1294 | List 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 | ||
3416 | Landlock is a Linux security module first introduced in version 5.13 of the | ||
3417 | Linux kernel. | ||
3418 | It allows unprivileged processes to restrict their access to the filesystem. | ||
3419 | Once imposed, these restrictions can never be removed, and all child processes | ||
3420 | created by a Landlock-restricted processes inherit these restrictions. | ||
3421 | Firejail supports Landlock as an additional sandboxing feature. | ||
3422 | It can be used to ensure that a sandboxed application can only access files and | ||
3423 | directories that it was explicitly allowed to access. | ||
3424 | Firejail 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 | ||
3427 | Important 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 | ||
3432 | Privileges" restriction enabled. | ||
3433 | Because of this, enabling the Landlock feature will also cause Firejail to | ||
3434 | enable 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 | ||
3438 | command line option. | ||
3439 | .PP | ||
3440 | - Access to the /etc directory is automatically allowed. | ||
3441 | To override this, use the \fB\-\-writable\-etc\fR command line option. | ||
3442 | You can also use the \fB\-\-private\-etc\fR option to restrict access to the | ||
3443 | /etc directory. | ||
3444 | .RE | ||
3445 | .PP | ||
3446 | To enable Landlock self-restriction on top of your current Firejail security | ||
3447 | features, pass \fB\-\-landlock\fR flag to Firejail command line. | ||
3448 | You 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. | ||
3451 | Example: | ||
3452 | .PP | ||
3453 | $ firejail \-\-landlock \-\-landlock.read=/media \-\-landlock.proc=ro mc | ||
3454 | #endif | ||
3368 | .SH DESKTOP INTEGRATION | 3455 | .SH DESKTOP INTEGRATION |
3369 | A symbolic link to /usr/bin/firejail under the name of a program, will start the program in Firejail sandbox. | 3456 | A symbolic link to /usr/bin/firejail under the name of a program, will start the program in Firejail sandbox. |
3370 | The symbolic link should be placed in the first $PATH position. On most systems, a good place | 3457 | The 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]:' |