Unmask_layers() helper function moves to ruleset.c and rule_type argument is added. This modification supports implementing new rule types into next landlock versions. Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@xxxxxxxxxx> --- Changes since v3: * Split commit. * Refactoring landlock_unmask_layers functions. --- security/landlock/fs.c | 67 +++++++++---------------------------- security/landlock/ruleset.c | 44 ++++++++++++++++++++++++ security/landlock/ruleset.h | 5 +++ 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 1497948d754f..75ebdce5cd16 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -178,51 +178,6 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset, return err; } -/* Access-control management */ - -static inline u64 unmask_layers( - const struct landlock_ruleset *const domain, - const struct path *const path, const u32 access_request, - u64 layer_mask) -{ - const struct landlock_rule *rule; - const struct inode *inode; - size_t i; - - if (d_is_negative(path->dentry)) - /* Ignore nonexistent leafs. */ - return layer_mask; - inode = d_backing_inode(path->dentry); - rcu_read_lock(); - rule = landlock_find_rule(domain, - (uintptr_t)rcu_dereference(landlock_inode(inode)->object), - LANDLOCK_RULE_PATH_BENEATH); - rcu_read_unlock(); - if (!rule) - return layer_mask; - - /* - * An access is granted if, for each policy layer, at least one rule - * encountered on the pathwalk grants the requested accesses, - * regardless of their position in the layer stack. We must then check - * the remaining layers for each inode, from the first added layer to - * the last one. - */ - for (i = 0; i < rule->num_layers; i++) { - const struct landlock_layer *const layer = &rule->layers[i]; - const u64 layer_level = BIT_ULL(layer->level - 1); - - /* Checks that the layer grants access to the full request. */ - if ((layer->access & access_request) == access_request) { - layer_mask &= ~layer_level; - - if (layer_mask == 0) - return layer_mask; - } - } - return layer_mask; -} - static int check_access_path(const struct landlock_ruleset *const domain, const struct path *const path, u32 access_request) { @@ -268,15 +223,23 @@ static int check_access_path(const struct landlock_ruleset *const domain, */ while (true) { struct dentry *parent_dentry; + const struct inode *inode; + struct landlock_object *object_ptr; - layer_mask = unmask_layers(domain, &walker_path, - access_request, layer_mask); - if (layer_mask == 0) { - /* Stops when a rule from each layer grants access. */ - allowed = true; - break; + /* Ignore nonexistent leafs. */ + if (!d_is_negative(walker_path.dentry)) { + + inode = d_backing_inode(walker_path.dentry); + object_ptr = landlock_inode(inode)->object; + layer_mask = landlock_unmask_layers(domain, object_ptr, + access_request, layer_mask, + LANDLOCK_RULE_PATH_BENEATH); + if (layer_mask == 0) { + /* Stops when a rule from each layer grants access. */ + allowed = true; + break; + } } - jump_up: if (walker_path.dentry == walker_path.mnt->mnt_root) { if (follow_up(&walker_path)) { diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index f2baa1c96b16..7179b10f3538 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -582,3 +582,47 @@ const struct landlock_rule *landlock_find_rule( } return NULL; } + +/* Access-control management */ +u64 landlock_unmask_layers(const struct landlock_ruleset *const domain, + const struct landlock_object *object_ptr, + const u32 access_request, u64 layer_mask, + const u16 rule_type) +{ + const struct landlock_rule *rule; + size_t i; + + switch (rule_type) { + case LANDLOCK_RULE_PATH_BENEATH: + rcu_read_lock(); + rule = landlock_find_rule(domain, + (uintptr_t)rcu_dereference(object_ptr), + LANDLOCK_RULE_PATH_BENEATH); + rcu_read_unlock(); + break; + } + + if (!rule) + return layer_mask; + + /* + * An access is granted if, for each policy layer, at least one rule + * encountered on the pathwalk grants the requested accesses, + * regardless of their position in the layer stack. We must then check + * the remaining layers for each inode, from the first added layer to + * the last one. + */ + for (i = 0; i < rule->num_layers; i++) { + const struct landlock_layer *const layer = &rule->layers[i]; + const u64 layer_level = BIT_ULL(layer->level - 1); + + /* Checks that the layer grants access to the full request. */ + if ((layer->access & access_request) == access_request) { + layer_mask &= ~layer_level; + + if (layer_mask == 0) + return layer_mask; + } + } + return layer_mask; +} diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 088b8d95f653..0a7d4b1f51fd 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -183,4 +183,9 @@ void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset, u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level); +u64 landlock_unmask_layers(const struct landlock_ruleset *const domain, + const struct landlock_object *object_ptr, + const u32 access_request, u64 layer_mask, + const u16 rule_type); + #endif /* _SECURITY_LANDLOCK_RULESET_H */ -- 2.25.1