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.c263
1 files changed, 263 insertions, 0 deletions
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 */