1
2
3
4
5
6
7
8
9#include <asm/current.h>
10#include <linux/anon_inodes.h>
11#include <linux/build_bug.h>
12#include <linux/capability.h>
13#include <linux/compiler_types.h>
14#include <linux/dcache.h>
15#include <linux/err.h>
16#include <linux/errno.h>
17#include <linux/fs.h>
18#include <linux/limits.h>
19#include <linux/mount.h>
20#include <linux/path.h>
21#include <linux/sched.h>
22#include <linux/security.h>
23#include <linux/stddef.h>
24#include <linux/syscalls.h>
25#include <linux/types.h>
26#include <linux/uaccess.h>
27#include <uapi/linux/landlock.h>
28
29#include "cred.h"
30#include "fs.h"
31#include "limits.h"
32#include "ruleset.h"
33#include "setup.h"
34
35
36
37
38
39
40
41
42
43
44
45
46static __always_inline int copy_min_struct_from_user(void *const dst,
47 const size_t ksize, const size_t ksize_min,
48 const void __user *const src, const size_t usize)
49{
50
51 BUILD_BUG_ON(!dst);
52 if (!src)
53 return -EFAULT;
54
55
56 BUILD_BUG_ON(ksize <= 0);
57 BUILD_BUG_ON(ksize < ksize_min);
58 if (usize < ksize_min)
59 return -EINVAL;
60 if (usize > PAGE_SIZE)
61 return -E2BIG;
62
63
64 return copy_struct_from_user(dst, ksize, src, usize);
65}
66
67
68
69
70
71
72static void build_check_abi(void)
73{
74 struct landlock_ruleset_attr ruleset_attr;
75 struct landlock_path_beneath_attr path_beneath_attr;
76 size_t ruleset_size, path_beneath_size;
77
78
79
80
81
82
83 ruleset_size = sizeof(ruleset_attr.handled_access_fs);
84 BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
85 BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
86
87 path_beneath_size = sizeof(path_beneath_attr.allowed_access);
88 path_beneath_size += sizeof(path_beneath_attr.parent_fd);
89 BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
90 BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
91}
92
93
94
95static int fop_ruleset_release(struct inode *const inode,
96 struct file *const filp)
97{
98 struct landlock_ruleset *ruleset = filp->private_data;
99
100 landlock_put_ruleset(ruleset);
101 return 0;
102}
103
104static ssize_t fop_dummy_read(struct file *const filp, char __user *const buf,
105 const size_t size, loff_t *const ppos)
106{
107
108 return -EINVAL;
109}
110
111static ssize_t fop_dummy_write(struct file *const filp,
112 const char __user *const buf, const size_t size,
113 loff_t *const ppos)
114{
115
116 return -EINVAL;
117}
118
119
120
121
122
123
124
125static const struct file_operations ruleset_fops = {
126 .release = fop_ruleset_release,
127 .read = fop_dummy_read,
128 .write = fop_dummy_write,
129};
130
131#define LANDLOCK_ABI_VERSION 1
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156SYSCALL_DEFINE3(landlock_create_ruleset,
157 const struct landlock_ruleset_attr __user *const, attr,
158 const size_t, size, const __u32, flags)
159{
160 struct landlock_ruleset_attr ruleset_attr;
161 struct landlock_ruleset *ruleset;
162 int err, ruleset_fd;
163
164
165 build_check_abi();
166
167 if (!landlock_initialized)
168 return -EOPNOTSUPP;
169
170 if (flags) {
171 if ((flags == LANDLOCK_CREATE_RULESET_VERSION)
172 && !attr && !size)
173 return LANDLOCK_ABI_VERSION;
174 return -EINVAL;
175 }
176
177
178 err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
179 offsetofend(typeof(ruleset_attr), handled_access_fs),
180 attr, size);
181 if (err)
182 return err;
183
184
185 if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
186 LANDLOCK_MASK_ACCESS_FS)
187 return -EINVAL;
188
189
190 ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
191 if (IS_ERR(ruleset))
192 return PTR_ERR(ruleset);
193
194
195 ruleset_fd = anon_inode_getfd("landlock-ruleset", &ruleset_fops,
196 ruleset, O_RDWR | O_CLOEXEC);
197 if (ruleset_fd < 0)
198 landlock_put_ruleset(ruleset);
199 return ruleset_fd;
200}
201
202
203
204
205
206static struct landlock_ruleset *get_ruleset_from_fd(const int fd,
207 const fmode_t mode)
208{
209 struct fd ruleset_f;
210 struct landlock_ruleset *ruleset;
211
212 ruleset_f = fdget(fd);
213 if (!ruleset_f.file)
214 return ERR_PTR(-EBADF);
215
216
217 if (ruleset_f.file->f_op != &ruleset_fops) {
218 ruleset = ERR_PTR(-EBADFD);
219 goto out_fdput;
220 }
221 if (!(ruleset_f.file->f_mode & mode)) {
222 ruleset = ERR_PTR(-EPERM);
223 goto out_fdput;
224 }
225 ruleset = ruleset_f.file->private_data;
226 if (WARN_ON_ONCE(ruleset->num_layers != 1)) {
227 ruleset = ERR_PTR(-EINVAL);
228 goto out_fdput;
229 }
230 landlock_get_ruleset(ruleset);
231
232out_fdput:
233 fdput(ruleset_f);
234 return ruleset;
235}
236
237
238
239
240
241
242static int get_path_from_fd(const s32 fd, struct path *const path)
243{
244 struct fd f;
245 int err = 0;
246
247 BUILD_BUG_ON(!__same_type(fd,
248 ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
249
250
251 f = fdget_raw(fd);
252 if (!f.file)
253 return -EBADF;
254
255
256
257
258
259 if ((f.file->f_op == &ruleset_fops) ||
260 (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) ||
261 (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) ||
262 d_is_negative(f.file->f_path.dentry) ||
263 IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) {
264 err = -EBADFD;
265 goto out_fdput;
266 }
267 *path = f.file->f_path;
268 path_get(path);
269
270out_fdput:
271 fdput(f);
272 return err;
273}
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304SYSCALL_DEFINE4(landlock_add_rule,
305 const int, ruleset_fd, const enum landlock_rule_type, rule_type,
306 const void __user *const, rule_attr, const __u32, flags)
307{
308 struct landlock_path_beneath_attr path_beneath_attr;
309 struct path path;
310 struct landlock_ruleset *ruleset;
311 int res, err;
312
313 if (!landlock_initialized)
314 return -EOPNOTSUPP;
315
316
317 if (flags)
318 return -EINVAL;
319
320 if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
321 return -EINVAL;
322
323
324 res = copy_from_user(&path_beneath_attr, rule_attr,
325 sizeof(path_beneath_attr));
326 if (res)
327 return -EFAULT;
328
329
330 ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
331 if (IS_ERR(ruleset))
332 return PTR_ERR(ruleset);
333
334
335
336
337
338 if (!path_beneath_attr.allowed_access) {
339 err = -ENOMSG;
340 goto out_put_ruleset;
341 }
342
343
344
345
346 if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
347 ruleset->fs_access_masks[0]) {
348 err = -EINVAL;
349 goto out_put_ruleset;
350 }
351
352
353 err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
354 if (err)
355 goto out_put_ruleset;
356
357
358 err = landlock_append_fs_rule(ruleset, &path,
359 path_beneath_attr.allowed_access);
360 path_put(&path);
361
362out_put_ruleset:
363 landlock_put_ruleset(ruleset);
364 return err;
365}
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392SYSCALL_DEFINE2(landlock_restrict_self,
393 const int, ruleset_fd, const __u32, flags)
394{
395 struct landlock_ruleset *new_dom, *ruleset;
396 struct cred *new_cred;
397 struct landlock_cred_security *new_llcred;
398 int err;
399
400 if (!landlock_initialized)
401 return -EOPNOTSUPP;
402
403
404 if (flags)
405 return -EINVAL;
406
407
408
409
410
411 if (!task_no_new_privs(current) &&
412 !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
413 return -EPERM;
414
415
416 ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ);
417 if (IS_ERR(ruleset))
418 return PTR_ERR(ruleset);
419
420
421 new_cred = prepare_creds();
422 if (!new_cred) {
423 err = -ENOMEM;
424 goto out_put_ruleset;
425 }
426 new_llcred = landlock_cred(new_cred);
427
428
429
430
431
432 new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset);
433 if (IS_ERR(new_dom)) {
434 err = PTR_ERR(new_dom);
435 goto out_put_creds;
436 }
437
438
439 landlock_put_ruleset(new_llcred->domain);
440 new_llcred->domain = new_dom;
441
442 landlock_put_ruleset(ruleset);
443 return commit_creds(new_cred);
444
445out_put_creds:
446 abort_creds(new_cred);
447
448out_put_ruleset:
449 landlock_put_ruleset(ruleset);
450 return err;
451}
452