]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
landlock: Move and rename layer helpers
authorKonstantin Meskhidze <konstantin.meskhidze@huawei.com>
Thu, 26 Oct 2023 01:47:44 +0000 (09:47 +0800)
committerMickaël Salaün <mic@digikod.net>
Thu, 26 Oct 2023 19:07:13 +0000 (21:07 +0200)
Move and rename landlock_unmask_layers() and landlock_init_layer_masks()
helpers to ruleset.c to share them with Landlock network implementation
in following commits.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
Link: https://lore.kernel.org/r/20231026014751.414649-6-konstantin.meskhidze@huawei.com
Signed-off-by: Mickaël Salaün <mic@digikod.net>
security/landlock/fs.c
security/landlock/ruleset.c
security/landlock/ruleset.h

index 9edb64ac8251d585331ea1546b4c3a2a412856c4..e6a19ff1765af4bf1ab1fe35afae2bd130235e55 100644 (file)
@@ -215,60 +215,6 @@ find_rule(const struct landlock_ruleset *const domain,
        return rule;
 }
 
-/*
- * @layer_masks is read and may be updated according to the access request and
- * the matching rule.
- *
- * Returns true if the request is allowed (i.e. relevant layer masks for the
- * request are empty).
- */
-static inline bool
-unmask_layers(const struct landlock_rule *const rule,
-             const access_mask_t access_request,
-             layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
-       size_t layer_level;
-
-       if (!access_request || !layer_masks)
-               return true;
-       if (!rule)
-               return false;
-
-       /*
-        * An access is granted if, for each policy layer, at least one rule
-        * encountered on the pathwalk grants the requested access,
-        * regardless of its position in the layer stack.  We must then check
-        * the remaining layers for each inode, from the first added layer to
-        * the last one.  When there is multiple requested accesses, for each
-        * policy layer, the full set of requested accesses may not be granted
-        * by only one rule, but by the union (binary OR) of multiple rules.
-        * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
-        */
-       for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
-               const struct landlock_layer *const layer =
-                       &rule->layers[layer_level];
-               const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
-               const unsigned long access_req = access_request;
-               unsigned long access_bit;
-               bool is_empty;
-
-               /*
-                * Records in @layer_masks which layer grants access to each
-                * requested access.
-                */
-               is_empty = true;
-               for_each_set_bit(access_bit, &access_req,
-                                ARRAY_SIZE(*layer_masks)) {
-                       if (layer->access & BIT_ULL(access_bit))
-                               (*layer_masks)[access_bit] &= ~layer_bit;
-                       is_empty = is_empty && !(*layer_masks)[access_bit];
-               }
-               if (is_empty)
-                       return true;
-       }
-       return false;
-}
-
 /*
  * Allows access to pseudo filesystems that will never be mountable (e.g.
  * sockfs, pipefs), but can still be reachable through
@@ -293,50 +239,6 @@ get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain)
        return access_dom;
 }
 
-/**
- * init_layer_masks - Initialize layer masks from an access request
- *
- * Populates @layer_masks such that for each access right in @access_request,
- * the bits for all the layers are set where this access right is handled.
- *
- * @domain: The domain that defines the current restrictions.
- * @access_request: The requested access rights to check.
- * @layer_masks: The layer masks to populate.
- *
- * Returns: An access mask where each access right bit is set which is handled
- * in any of the active layers in @domain.
- */
-static inline access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
-                const access_mask_t access_request,
-                layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
-       access_mask_t handled_accesses = 0;
-       size_t layer_level;
-
-       memset(layer_masks, 0, sizeof(*layer_masks));
-       /* An empty access request can happen because of O_WRONLY | O_RDWR. */
-       if (!access_request)
-               return 0;
-
-       /* Saves all handled accesses per layer. */
-       for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
-               const unsigned long access_req = access_request;
-               unsigned long access_bit;
-
-               for_each_set_bit(access_bit, &access_req,
-                                ARRAY_SIZE(*layer_masks)) {
-                       if (BIT_ULL(access_bit) &
-                           landlock_get_fs_access_mask(domain, layer_level)) {
-                               (*layer_masks)[access_bit] |=
-                                       BIT_ULL(layer_level);
-                               handled_accesses |= BIT_ULL(access_bit);
-                       }
-               }
-       }
-       return handled_accesses;
-}
-
 static access_mask_t
 get_handled_fs_accesses(const struct landlock_ruleset *const domain)
 {
@@ -540,18 +442,20 @@ static bool is_access_to_paths_allowed(
        }
 
        if (unlikely(dentry_child1)) {
-               unmask_layers(find_rule(domain, dentry_child1),
-                             init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+               landlock_unmask_layers(find_rule(domain, dentry_child1),
+                                      landlock_init_layer_masks(
+                                              domain, LANDLOCK_MASK_ACCESS_FS,
                                               &_layer_masks_child1),
-                             &_layer_masks_child1);
+                                      &_layer_masks_child1);
                layer_masks_child1 = &_layer_masks_child1;
                child1_is_directory = d_is_dir(dentry_child1);
        }
        if (unlikely(dentry_child2)) {
-               unmask_layers(find_rule(domain, dentry_child2),
-                             init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+               landlock_unmask_layers(find_rule(domain, dentry_child2),
+                                      landlock_init_layer_masks(
+                                              domain, LANDLOCK_MASK_ACCESS_FS,
                                               &_layer_masks_child2),
-                             &_layer_masks_child2);
+                                      &_layer_masks_child2);
                layer_masks_child2 = &_layer_masks_child2;
                child2_is_directory = d_is_dir(dentry_child2);
        }
@@ -603,10 +507,10 @@ static bool is_access_to_paths_allowed(
                }
 
                rule = find_rule(domain, walker_path.dentry);
-               allowed_parent1 = unmask_layers(rule, access_masked_parent1,
-                                               layer_masks_parent1);
-               allowed_parent2 = unmask_layers(rule, access_masked_parent2,
-                                               layer_masks_parent2);
+               allowed_parent1 = landlock_unmask_layers(
+                       rule, access_masked_parent1, layer_masks_parent1);
+               allowed_parent2 = landlock_unmask_layers(
+                       rule, access_masked_parent2, layer_masks_parent2);
 
                /* Stops when a rule from each layer grants access. */
                if (allowed_parent1 && allowed_parent2)
@@ -650,7 +554,8 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
 {
        layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
 
-       access_request = init_layer_masks(domain, access_request, &layer_masks);
+       access_request =
+               landlock_init_layer_masks(domain, access_request, &layer_masks);
        if (is_access_to_paths_allowed(domain, path, access_request,
                                       &layer_masks, NULL, 0, NULL, NULL))
                return 0;
@@ -735,16 +640,16 @@ static bool collect_domain_accesses(
        if (is_nouser_or_private(dir))
                return true;
 
-       access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-                                     layer_masks_dom);
+       access_dom = landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+                                              layer_masks_dom);
 
        dget(dir);
        while (true) {
                struct dentry *parent_dentry;
 
                /* Gets all layers allowing all domain accesses. */
-               if (unmask_layers(find_rule(domain, dir), access_dom,
-                                 layer_masks_dom)) {
+               if (landlock_unmask_layers(find_rule(domain, dir), access_dom,
+                                          layer_masks_dom)) {
                        /*
                         * Stops when all handled accesses are allowed by at
                         * least one rule in each layer.
@@ -857,7 +762,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
                 * The LANDLOCK_ACCESS_FS_REFER access right is not required
                 * for same-directory referer (i.e. no reparenting).
                 */
-               access_request_parent1 = init_layer_masks(
+               access_request_parent1 = landlock_init_layer_masks(
                        dom, access_request_parent1 | access_request_parent2,
                        &layer_masks_parent1);
                if (is_access_to_paths_allowed(
@@ -1234,7 +1139,8 @@ static int hook_file_open(struct file *const file)
 
        if (is_access_to_paths_allowed(
                    dom, &file->f_path,
-                   init_layer_masks(dom, full_access_request, &layer_masks),
+                   landlock_init_layer_masks(dom, full_access_request,
+                                             &layer_masks),
                    &layer_masks, NULL, 0, NULL, NULL)) {
                allowed_access = full_access_request;
        } else {
index cf88754a3f308c262e12b8663507ccb590518fb7..28ffeedd21d6caf05ece508301488556a3af8bb8 100644 (file)
@@ -569,3 +569,101 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
        }
        return NULL;
 }
+
+/*
+ * @layer_masks is read and may be updated according to the access request and
+ * the matching rule.
+ *
+ * Returns true if the request is allowed (i.e. relevant layer masks for the
+ * request are empty).
+ */
+bool landlock_unmask_layers(
+       const struct landlock_rule *const rule,
+       const access_mask_t access_request,
+       layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+{
+       size_t layer_level;
+
+       if (!access_request || !layer_masks)
+               return true;
+       if (!rule)
+               return false;
+
+       /*
+        * An access is granted if, for each policy layer, at least one rule
+        * encountered on the pathwalk grants the requested access,
+        * regardless of its position in the layer stack.  We must then check
+        * the remaining layers for each inode, from the first added layer to
+        * the last one.  When there is multiple requested accesses, for each
+        * policy layer, the full set of requested accesses may not be granted
+        * by only one rule, but by the union (binary OR) of multiple rules.
+        * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
+        */
+       for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
+               const struct landlock_layer *const layer =
+                       &rule->layers[layer_level];
+               const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
+               const unsigned long access_req = access_request;
+               unsigned long access_bit;
+               bool is_empty;
+
+               /*
+                * Records in @layer_masks which layer grants access to each
+                * requested access.
+                */
+               is_empty = true;
+               for_each_set_bit(access_bit, &access_req,
+                                ARRAY_SIZE(*layer_masks)) {
+                       if (layer->access & BIT_ULL(access_bit))
+                               (*layer_masks)[access_bit] &= ~layer_bit;
+                       is_empty = is_empty && !(*layer_masks)[access_bit];
+               }
+               if (is_empty)
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * landlock_init_layer_masks - Initialize layer masks from an access request
+ *
+ * Populates @layer_masks such that for each access right in @access_request,
+ * the bits for all the layers are set where this access right is handled.
+ *
+ * @domain: The domain that defines the current restrictions.
+ * @access_request: The requested access rights to check.
+ * @layer_masks: The layer masks to populate.
+ *
+ * Returns: An access mask where each access right bit is set which is handled
+ * in any of the active layers in @domain.
+ */
+access_mask_t landlock_init_layer_masks(
+       const struct landlock_ruleset *const domain,
+       const access_mask_t access_request,
+       layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+{
+       access_mask_t handled_accesses = 0;
+       size_t layer_level;
+
+       memset(layer_masks, 0, sizeof(*layer_masks));
+       /* An empty access request can happen because of O_WRONLY | O_RDWR. */
+       if (!access_request)
+               return 0;
+
+       /* Saves all handled accesses per layer. */
+       for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
+               const unsigned long access_req = access_request;
+               unsigned long access_bit;
+
+               for_each_set_bit(access_bit, &access_req,
+                                ARRAY_SIZE(*layer_masks)) {
+                       if (BIT_ULL(access_bit) &
+                           landlock_get_fs_access_mask(domain, layer_level)) {
+                               (*layer_masks)[access_bit] |=
+                                       BIT_ULL(layer_level);
+                               handled_accesses |= BIT_ULL(access_bit);
+                       }
+               }
+       }
+       return handled_accesses;
+}
index 9e04c666b23c596295373114d8f4d7762cc6ff42..760e049f3e0a93a5e46832325b164fee33fb221f 100644 (file)
@@ -267,4 +267,14 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
               LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
 }
 
+bool landlock_unmask_layers(
+       const struct landlock_rule *const rule,
+       const access_mask_t access_request,
+       layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+
+access_mask_t landlock_init_layer_masks(
+       const struct landlock_ruleset *const domain,
+       const access_mask_t access_request,
+       layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+
 #endif /* _SECURITY_LANDLOCK_RULESET_H */