aboutsummaryrefslogtreecommitdiffstats
path: root/src/firejail/landlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/landlock.c')
-rw-r--r--src/firejail/landlock.c361
1 files changed, 361 insertions, 0 deletions
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
30static int ll_ruleset_fd = -1;
31static int ll_abi = -1;
32
33int ll_get_fd(void) {
34 return ll_ruleset_fd;
35}
36
37#ifndef landlock_create_ruleset
38static inline int
39landlock_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
46static inline int
47landlock_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
57static inline int
58landlock_restrict_self(const int ruleset_fd, const __u32 flags) {
59 return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
60}
61#endif
62
63int 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 }
79out:
80 return ll_abi;
81}
82
83static 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
111int 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
143int 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
179int 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
213int 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
244int 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
296int 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__);
332out:
333 close(ll_ruleset_fd);
334 return error;
335}
336
337void 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 */