Skip to content

Commit d722036

Browse files
committed
landlock: Allow FS topology changes for domains without such rule type
Allow mount point and root directory changes when there is no filesystem rule tied to the current Landlock domain. This doesn't change anything for now because a domain must have at least a (filesystem) rule, but this will change when other rule types will come. For instance, a domain only restricting the network should have no impact on filesystem restrictions. Add a new get_current_fs_domain() helper to quickly check filesystem rule existence for all filesystem LSM hooks. Remove unnecessary inlining. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mickaël Salaün <[email protected]>
1 parent 13fc645 commit d722036

File tree

4 files changed

+63
-43
lines changed

4 files changed

+63
-43
lines changed

Documentation/userspace-api/landlock.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,9 @@ Current limitations
387387
Filesystem topology modification
388388
--------------------------------
389389

390-
As for file renaming and linking, a sandboxed thread cannot modify its
391-
filesystem topology, whether via :manpage:`mount(2)` or
392-
:manpage:`pivot_root(2)`. However, :manpage:`chroot(2)` calls are not denied.
390+
Threads sandboxed with filesystem restrictions cannot modify filesystem
391+
topology, whether via :manpage:`mount(2)` or :manpage:`pivot_root(2)`.
392+
However, :manpage:`chroot(2)` calls are not denied.
393393

394394
Special filesystems
395395
-------------------

security/landlock/fs.c

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,6 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
150150
LANDLOCK_ACCESS_FS_TRUNCATE)
151151
/* clang-format on */
152152

153-
/*
154-
* All access rights that are denied by default whether they are handled or not
155-
* by a ruleset/layer. This must be ORed with all ruleset->fs_access_masks[]
156-
* entries when we need to get the absolute handled access masks.
157-
*/
158-
/* clang-format off */
159-
#define ACCESS_INITIALLY_DENIED ( \
160-
LANDLOCK_ACCESS_FS_REFER)
161-
/* clang-format on */
162-
163153
/*
164154
* @path: Should have been checked by get_path_from_fd().
165155
*/
@@ -179,8 +169,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
179169

180170
/* Transforms relative access rights to absolute ones. */
181171
access_rights |= LANDLOCK_MASK_ACCESS_FS &
182-
~(landlock_get_fs_access_mask(ruleset, 0) |
183-
ACCESS_INITIALLY_DENIED);
172+
~landlock_get_fs_access_mask(ruleset, 0);
184173
object = get_inode_object(d_backing_inode(path->dentry));
185174
if (IS_ERR(object))
186175
return PTR_ERR(object);
@@ -287,15 +276,16 @@ static inline bool is_nouser_or_private(const struct dentry *dentry)
287276
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
288277
}
289278

290-
static inline access_mask_t
291-
get_handled_accesses(const struct landlock_ruleset *const domain)
279+
static access_mask_t
280+
get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain)
292281
{
293-
access_mask_t access_dom = ACCESS_INITIALLY_DENIED;
282+
access_mask_t access_dom = 0;
294283
size_t layer_level;
295284

296285
for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
297-
access_dom |= landlock_get_fs_access_mask(domain, layer_level);
298-
return access_dom & LANDLOCK_MASK_ACCESS_FS;
286+
access_dom |=
287+
landlock_get_raw_fs_access_mask(domain, layer_level);
288+
return access_dom;
299289
}
300290

301291
/**
@@ -331,13 +321,8 @@ init_layer_masks(const struct landlock_ruleset *const domain,
331321

332322
for_each_set_bit(access_bit, &access_req,
333323
ARRAY_SIZE(*layer_masks)) {
334-
/*
335-
* Artificially handles all initially denied by default
336-
* access rights.
337-
*/
338324
if (BIT_ULL(access_bit) &
339-
(landlock_get_fs_access_mask(domain, layer_level) |
340-
ACCESS_INITIALLY_DENIED)) {
325+
landlock_get_fs_access_mask(domain, layer_level)) {
341326
(*layer_masks)[access_bit] |=
342327
BIT_ULL(layer_level);
343328
handled_accesses |= BIT_ULL(access_bit);
@@ -347,6 +332,25 @@ init_layer_masks(const struct landlock_ruleset *const domain,
347332
return handled_accesses;
348333
}
349334

335+
static access_mask_t
336+
get_handled_fs_accesses(const struct landlock_ruleset *const domain)
337+
{
338+
/* Handles all initially denied by default access rights. */
339+
return get_raw_handled_fs_accesses(domain) |
340+
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
341+
}
342+
343+
static const struct landlock_ruleset *get_current_fs_domain(void)
344+
{
345+
const struct landlock_ruleset *const dom =
346+
landlock_get_current_domain();
347+
348+
if (!dom || !get_raw_handled_fs_accesses(dom))
349+
return NULL;
350+
351+
return dom;
352+
}
353+
350354
/*
351355
* Check that a destination file hierarchy has more restrictions than a source
352356
* file hierarchy. This is only used for link and rename actions.
@@ -519,7 +523,7 @@ static bool is_access_to_paths_allowed(
519523
* a superset of the meaningful requested accesses).
520524
*/
521525
access_masked_parent1 = access_masked_parent2 =
522-
get_handled_accesses(domain);
526+
get_handled_fs_accesses(domain);
523527
is_dom_check = true;
524528
} else {
525529
if (WARN_ON_ONCE(dentry_child1 || dentry_child2))
@@ -651,8 +655,7 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
651655
static inline int current_check_access_path(const struct path *const path,
652656
const access_mask_t access_request)
653657
{
654-
const struct landlock_ruleset *const dom =
655-
landlock_get_current_domain();
658+
const struct landlock_ruleset *const dom = get_current_fs_domain();
656659

657660
if (!dom)
658661
return 0;
@@ -815,8 +818,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
815818
struct dentry *const new_dentry,
816819
const bool removable, const bool exchange)
817820
{
818-
const struct landlock_ruleset *const dom =
819-
landlock_get_current_domain();
821+
const struct landlock_ruleset *const dom = get_current_fs_domain();
820822
bool allow_parent1, allow_parent2;
821823
access_mask_t access_request_parent1, access_request_parent2;
822824
struct path mnt_dir;
@@ -1050,15 +1052,15 @@ static int hook_sb_mount(const char *const dev_name,
10501052
const struct path *const path, const char *const type,
10511053
const unsigned long flags, void *const data)
10521054
{
1053-
if (!landlock_get_current_domain())
1055+
if (!get_current_fs_domain())
10541056
return 0;
10551057
return -EPERM;
10561058
}
10571059

10581060
static int hook_move_mount(const struct path *const from_path,
10591061
const struct path *const to_path)
10601062
{
1061-
if (!landlock_get_current_domain())
1063+
if (!get_current_fs_domain())
10621064
return 0;
10631065
return -EPERM;
10641066
}
@@ -1069,14 +1071,14 @@ static int hook_move_mount(const struct path *const from_path,
10691071
*/
10701072
static int hook_sb_umount(struct vfsmount *const mnt, const int flags)
10711073
{
1072-
if (!landlock_get_current_domain())
1074+
if (!get_current_fs_domain())
10731075
return 0;
10741076
return -EPERM;
10751077
}
10761078

10771079
static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts)
10781080
{
1079-
if (!landlock_get_current_domain())
1081+
if (!get_current_fs_domain())
10801082
return 0;
10811083
return -EPERM;
10821084
}
@@ -1092,7 +1094,7 @@ static int hook_sb_remount(struct super_block *const sb, void *const mnt_opts)
10921094
static int hook_sb_pivotroot(const struct path *const old_path,
10931095
const struct path *const new_path)
10941096
{
1095-
if (!landlock_get_current_domain())
1097+
if (!get_current_fs_domain())
10961098
return 0;
10971099
return -EPERM;
10981100
}
@@ -1128,8 +1130,7 @@ static int hook_path_mknod(const struct path *const dir,
11281130
struct dentry *const dentry, const umode_t mode,
11291131
const unsigned int dev)
11301132
{
1131-
const struct landlock_ruleset *const dom =
1132-
landlock_get_current_domain();
1133+
const struct landlock_ruleset *const dom = get_current_fs_domain();
11331134

11341135
if (!dom)
11351136
return 0;
@@ -1208,8 +1209,7 @@ static int hook_file_open(struct file *const file)
12081209
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
12091210
access_mask_t open_access_request, full_access_request, allowed_access;
12101211
const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
1211-
const struct landlock_ruleset *const dom =
1212-
landlock_get_current_domain();
1212+
const struct landlock_ruleset *const dom = get_current_fs_domain();
12131213

12141214
if (!dom)
12151215
return 0;

security/landlock/ruleset.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,21 @@
1515
#include <linux/rbtree.h>
1616
#include <linux/refcount.h>
1717
#include <linux/workqueue.h>
18+
#include <uapi/linux/landlock.h>
1819

1920
#include "limits.h"
2021
#include "object.h"
2122

23+
/*
24+
* All access rights that are denied by default whether they are handled or not
25+
* by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
26+
* entries when we need to get the absolute handled access masks.
27+
*/
28+
/* clang-format off */
29+
#define LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
30+
LANDLOCK_ACCESS_FS_REFER)
31+
/* clang-format on */
32+
2233
typedef u16 access_mask_t;
2334
/* Makes sure all filesystem access rights can be stored. */
2435
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
@@ -196,12 +207,21 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
196207
}
197208

198209
static inline access_mask_t
199-
landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
200-
const u16 layer_level)
210+
landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
211+
const u16 layer_level)
201212
{
202213
return (ruleset->access_masks[layer_level] >>
203214
LANDLOCK_SHIFT_ACCESS_FS) &
204215
LANDLOCK_MASK_ACCESS_FS;
205216
}
206217

218+
static inline access_mask_t
219+
landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
220+
const u16 layer_level)
221+
{
222+
/* Handles all initially denied by default access rights. */
223+
return landlock_get_raw_fs_access_mask(ruleset, layer_level) |
224+
LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
225+
}
226+
207227
#endif /* _SECURITY_LANDLOCK_RULESET_H */

security/landlock/syscalls.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
349349
* Checks that allowed_access matches the @ruleset constraints
350350
* (ruleset->access_masks[0] is automatically upgraded to 64-bits).
351351
*/
352-
mask = landlock_get_fs_access_mask(ruleset, 0);
352+
mask = landlock_get_raw_fs_access_mask(ruleset, 0);
353353
if ((path_beneath_attr.allowed_access | mask) != mask) {
354354
err = -EINVAL;
355355
goto out_put_ruleset;

0 commit comments

Comments
 (0)