]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
landlock: Prepare to use credential instead of domain for filesystem
authorMickaël Salaün <mic@digikod.net>
Thu, 20 Mar 2025 19:06:53 +0000 (20:06 +0100)
committerMickaël Salaün <mic@digikod.net>
Wed, 26 Mar 2025 12:59:35 +0000 (13:59 +0100)
This cosmetic change is needed for audit support, specifically to be
able to filter according to cross-execution boundaries.

Add landlock_get_applicable_subject(), mainly a copy of
landlock_get_applicable_domain(), which will fully replace it in a
following commit.

Optimize current_check_access_path() to only handle the access request.

Partially replace get_current_fs_domain() with explicit calls to
landlock_get_applicable_subject().  The remaining ones will follow with
more changes.

Remove explicit domain->num_layers check which is now part of the
landlock_get_applicable_subject() call.

Cc: Günther Noack <gnoack@google.com>
Link: https://lore.kernel.org/r/20250320190717.2287696-5-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
security/landlock/cred.h
security/landlock/fs.c

index bf755459838a557348541183864a1885cd52f89d..eb691130dd6766797fb236fc0c54a444f7b0bc5e 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Landlock LSM - Credential hooks
+ * Landlock - Credential hooks
  *
  * Copyright © 2019-2020 Mickaël Salaün <mic@digikod.net>
  * Copyright © 2019-2020 ANSSI
+ * Copyright © 2021-2025 Microsoft Corporation
  */
 
 #ifndef _SECURITY_LANDLOCK_CRED_H
@@ -13,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/rcupdate.h>
 
+#include "access.h"
 #include "ruleset.h"
 #include "setup.h"
 
@@ -53,6 +55,55 @@ static inline bool landlocked(const struct task_struct *const task)
        return has_dom;
 }
 
+/**
+ * landlock_get_applicable_subject - Return the subject's Landlock credential
+ *                                   if its enforced domain applies to (i.e.
+ *                                   handles) at least one of the access rights
+ *                                   specified in @masks
+ *
+ * @cred: credential
+ * @masks: access masks
+ * @handle_layer: returned youngest layer handling a subset of @masks.  Not set
+ *                if the function returns NULL.
+ *
+ * Returns: landlock_cred(@cred) if any access rights specified in @masks is
+ * handled, or NULL otherwise.
+ */
+static inline const struct landlock_cred_security *
+landlock_get_applicable_subject(const struct cred *const cred,
+                               const struct access_masks masks,
+                               size_t *const handle_layer)
+{
+       const union access_masks_all masks_all = {
+               .masks = masks,
+       };
+       const struct landlock_ruleset *domain;
+       ssize_t layer_level;
+
+       if (!cred)
+               return NULL;
+
+       domain = landlock_cred(cred)->domain;
+       if (!domain)
+               return NULL;
+
+       for (layer_level = domain->num_layers - 1; layer_level >= 0;
+            layer_level--) {
+               union access_masks_all layer = {
+                       .masks = domain->access_masks[layer_level],
+               };
+
+               if (layer.all & masks_all.all) {
+                       if (handle_layer)
+                               *handle_layer = layer_level;
+
+                       return landlock_cred(cred);
+               }
+       }
+
+       return NULL;
+}
+
 __init void landlock_add_cred_hooks(void);
 
 #endif /* _SECURITY_LANDLOCK_CRED_H */
index c19aab87c4d2d7771a00a7f8d89e9bfc2a7dca2b..f59db97333f31d26859cedecedb5982469de8673 100644 (file)
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Landlock LSM - Filesystem management and hooks
+ * Landlock - Filesystem management and hooks
  *
  * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
  * Copyright © 2018-2020 ANSSI
- * Copyright © 2021-2022 Microsoft Corporation
+ * Copyright © 2021-2025 Microsoft Corporation
  * Copyright © 2022 Günther Noack <gnoack3000@gmail.com>
  * Copyright © 2023-2024 Google LLC
  */
@@ -773,11 +773,14 @@ static bool is_access_to_paths_allowed(
 
        if (!access_request_parent1 && !access_request_parent2)
                return true;
-       if (WARN_ON_ONCE(!domain || !path))
+
+       if (WARN_ON_ONCE(!path))
                return true;
+
        if (is_nouser_or_private(path->dentry))
                return true;
-       if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1))
+
+       if (WARN_ON_ONCE(!layer_masks_parent1))
                return false;
 
        allowed_parent1 = is_layer_masks_allowed(layer_masks_parent1);
@@ -928,16 +931,21 @@ jump_up:
 static int current_check_access_path(const struct path *const path,
                                     access_mask_t access_request)
 {
-       const struct landlock_ruleset *const dom = get_current_fs_domain();
+       const struct access_masks masks = {
+               .fs = access_request,
+       };
+       const struct landlock_cred_security *const subject =
+               landlock_get_applicable_subject(current_cred(), masks, NULL);
        layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
 
-       if (!dom)
+       if (!subject)
                return 0;
 
-       access_request = landlock_init_layer_masks(
-               dom, access_request, &layer_masks, LANDLOCK_KEY_INODE);
-       if (is_access_to_paths_allowed(dom, path, access_request, &layer_masks,
-                                      NULL, 0, NULL, NULL))
+       access_request = landlock_init_layer_masks(subject->domain,
+                                                  access_request, &layer_masks,
+                                                  LANDLOCK_KEY_INODE);
+       if (is_access_to_paths_allowed(subject->domain, path, access_request,
+                                      &layer_masks, NULL, 0, NULL, NULL))
                return 0;
 
        return -EACCES;
@@ -1100,7 +1108,8 @@ static int current_check_refer_path(struct dentry *const old_dentry,
                                    struct dentry *const new_dentry,
                                    const bool removable, const bool exchange)
 {
-       const struct landlock_ruleset *const dom = get_current_fs_domain();
+       const struct landlock_cred_security *const subject =
+               landlock_get_applicable_subject(current_cred(), any_fs, NULL);
        bool allow_parent1, allow_parent2;
        access_mask_t access_request_parent1, access_request_parent2;
        struct path mnt_dir;
@@ -1108,10 +1117,9 @@ static int current_check_refer_path(struct dentry *const old_dentry,
        layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
                     layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {};
 
-       if (!dom)
+       if (!subject)
                return 0;
-       if (WARN_ON_ONCE(dom->num_layers < 1))
-               return -EACCES;
+
        if (unlikely(d_is_negative(old_dentry)))
                return -ENOENT;
        if (exchange) {
@@ -1136,10 +1144,11 @@ static int current_check_refer_path(struct dentry *const old_dentry,
                 * for same-directory referer (i.e. no reparenting).
                 */
                access_request_parent1 = landlock_init_layer_masks(
-                       dom, access_request_parent1 | access_request_parent2,
+                       subject->domain,
+                       access_request_parent1 | access_request_parent2,
                        &layer_masks_parent1, LANDLOCK_KEY_INODE);
                if (is_access_to_paths_allowed(
-                           dom, new_dir, access_request_parent1,
+                           subject->domain, new_dir, access_request_parent1,
                            &layer_masks_parent1, NULL, 0, NULL, NULL))
                        return 0;
                return -EACCES;
@@ -1162,10 +1171,12 @@ static int current_check_refer_path(struct dentry *const old_dentry,
                                                      old_dentry->d_parent;
 
        /* new_dir->dentry is equal to new_dentry->d_parent */
-       allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent,
+       allow_parent1 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
+                                               old_parent,
                                                &layer_masks_parent1);
-       allow_parent2 = collect_domain_accesses(
-               dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2);
+       allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
+                                               new_dir->dentry,
+                                               &layer_masks_parent2);
 
        if (allow_parent1 && allow_parent2)
                return 0;
@@ -1177,9 +1188,9 @@ static int current_check_refer_path(struct dentry *const old_dentry,
         * destination parent access rights.
         */
        if (is_access_to_paths_allowed(
-                   dom, &mnt_dir, access_request_parent1, &layer_masks_parent1,
-                   old_dentry, access_request_parent2, &layer_masks_parent2,
-                   exchange ? new_dentry : NULL))
+                   subject->domain, &mnt_dir, access_request_parent1,
+                   &layer_masks_parent1, old_dentry, access_request_parent2,
+                   &layer_masks_parent2, exchange ? new_dentry : NULL))
                return 0;
 
        /*
@@ -1506,11 +1517,10 @@ static int hook_file_open(struct file *const file)
        layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
        access_mask_t open_access_request, full_access_request, allowed_access,
                optional_access;
-       const struct landlock_ruleset *const dom =
-               landlock_get_applicable_domain(
-                       landlock_cred(file->f_cred)->domain, any_fs);
+       const struct landlock_cred_security *const subject =
+               landlock_get_applicable_subject(file->f_cred, any_fs, NULL);
 
-       if (!dom)
+       if (!subject)
                return 0;
 
        /*
@@ -1531,9 +1541,10 @@ static int hook_file_open(struct file *const file)
        full_access_request = open_access_request | optional_access;
 
        if (is_access_to_paths_allowed(
-                   dom, &file->f_path,
-                   landlock_init_layer_masks(dom, full_access_request,
-                                             &layer_masks, LANDLOCK_KEY_INODE),
+                   subject->domain, &file->f_path,
+                   landlock_init_layer_masks(subject->domain,
+                                             full_access_request, &layer_masks,
+                                             LANDLOCK_KEY_INODE),
                    &layer_masks, NULL, 0, NULL, NULL)) {
                allowed_access = full_access_request;
        } else {