[PATCH v1 1/3] landlock: Refactor filesystem access mask management
From: Mickaël Salaün <mic@digikod.net>
Date: 2024-10-01 15:59:43
Also in:
lkml
Subsystem:
landlock security module, security subsystem, the rest · Maintainers:
Mickaël Salaün, Paul Moore, James Morris, "Serge E. Hallyn", Linus Torvalds
Replace get_raw_handled_fs_accesses() with a generic landlock_merge_access_masks(), and replace the get_fs_domain() implementation with a call to the new landlock_filter_access_masks() helper. These helpers will also be useful for other types of access. Replace struct access_masks with union access_masks that includes a new "all" field to simplify mask filtering. Cc: Günther Noack <gnoack@google.com> Cc: Mikhail Ivanov <redacted> Signed-off-by: Mickaël Salaün <mic@digikod.net> Link: https://lore.kernel.org/r/20241001141234.397649-2-mic@digikod.net (local) --- security/landlock/fs.c | 21 ++++----------- security/landlock/ruleset.h | 51 +++++++++++++++++++++++++++--------- security/landlock/syscalls.c | 2 +- 3 files changed, 44 insertions(+), 30 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 7d79fc8abe21..a2ef7d151c81 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c@@ -388,33 +388,22 @@ static bool is_nouser_or_private(const struct dentry *dentry) unlikely(IS_PRIVATE(d_backing_inode(dentry)))); } -static access_mask_t -get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain) -{ - access_mask_t access_dom = 0; - size_t layer_level; - - for (layer_level = 0; layer_level < domain->num_layers; layer_level++) - access_dom |= - landlock_get_raw_fs_access_mask(domain, layer_level); - return access_dom; -} - static access_mask_t get_handled_fs_accesses(const struct landlock_ruleset *const domain) { /* Handles all initially denied by default access rights. */ - return get_raw_handled_fs_accesses(domain) | + return landlock_merge_access_masks(domain).fs | LANDLOCK_ACCESS_FS_INITIALLY_DENIED; } static const struct landlock_ruleset * get_fs_domain(const struct landlock_ruleset *const domain) { - if (!domain || !get_raw_handled_fs_accesses(domain)) - return NULL; + const union access_masks all_fs = { + .fs = ~0, + }; - return domain; + return landlock_filter_access_masks(domain, all_fs); } static const struct landlock_ruleset *get_current_fs_domain(void)
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 61bdbc550172..a816042ca8f3 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h@@ -41,12 +41,19 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE); static_assert(sizeof(unsigned long) >= sizeof(access_mask_t)); /* Ruleset access masks. */ -struct access_masks { - access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; - access_mask_t net : LANDLOCK_NUM_ACCESS_NET; - access_mask_t scope : LANDLOCK_NUM_SCOPE; +union access_masks { + struct { + access_mask_t fs : LANDLOCK_NUM_ACCESS_FS; + access_mask_t net : LANDLOCK_NUM_ACCESS_NET; + access_mask_t scope : LANDLOCK_NUM_SCOPE; + }; + u32 all; }; +/* Makes sure all fields are covered. */ +static_assert(sizeof(((union access_masks *)NULL)->all) == + sizeof(union access_masks)); + typedef u16 layer_mask_t; /* Makes sure all layers can be checked. */ static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
@@ -229,7 +236,7 @@ struct landlock_ruleset { * layers are set once and never changed for the * lifetime of the ruleset. */ - struct access_masks access_masks[]; + union access_masks access_masks[]; }; }; };
@@ -260,6 +267,31 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset) refcount_inc(&ruleset->usage); } +static inline union access_masks +landlock_merge_access_masks(const struct landlock_ruleset *const domain) +{ + size_t layer_level; + union access_masks matches = {}; + + for (layer_level = 0; layer_level < domain->num_layers; layer_level++) + matches.all |= domain->access_masks[layer_level].all; + + return matches; +} + +static inline const struct landlock_ruleset * +landlock_filter_access_masks(const struct landlock_ruleset *const domain, + const union access_masks masks) +{ + if (!domain) + return NULL; + + if (landlock_merge_access_masks(domain).all & masks.all) + return domain; + + return NULL; +} + static inline void landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset, const access_mask_t fs_access_mask,
@@ -295,19 +327,12 @@ landlock_add_scope_mask(struct landlock_ruleset *const ruleset, ruleset->access_masks[layer_level].scope |= mask; } -static inline access_mask_t -landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset, - const u16 layer_level) -{ - return ruleset->access_masks[layer_level].fs; -} - static inline access_mask_t landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset, const u16 layer_level) { /* Handles all initially denied by default access rights. */ - return landlock_get_raw_fs_access_mask(ruleset, layer_level) | + return ruleset->access_masks[layer_level].fs | LANDLOCK_ACCESS_FS_INITIALLY_DENIED; }
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index f5a0e7182ec0..c097d356fa45 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c@@ -329,7 +329,7 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset, return -ENOMSG; /* Checks that allowed_access matches the @ruleset constraints. */ - mask = landlock_get_raw_fs_access_mask(ruleset, 0); + mask = ruleset->access_masks[0].fs; if ((path_beneath_attr.allowed_access | mask) != mask) return -EINVAL;
--
2.46.1