From: "Günther Noack" <gnoack@google.com>
To: "Mickaël Salaün" <mic@digikod.net>
Cc: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>,
Konstantin Meskhidze <konstantin.meskhidze@huawei.com>,
Paul Moore <paul@paul-moore.com>,
Tahera Fahimi <fahimitahera@gmail.com>,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org
Subject: Re: [PATCH v4 1/3] landlock: Refactor filesystem access mask management
Date: Sat, 9 Nov 2024 19:21:04 +0100 [thread overview]
Message-ID: <Zy-n_GtpJPV-5LrB@google.com> (raw)
In-Reply-To: <20241109110856.222842-2-mic@digikod.net>
On Sat, Nov 09, 2024 at 12:08:54PM +0100, Mickaël Salaün wrote:
> Replace get_raw_handled_fs_accesses() with a generic
> landlock_merge_access_masks(), and replace get_fs_domain() with a
> generic landlock_match_ruleset(). These helpers will also be useful for
> other types of access.
>
> Cc: Günther Noack <gnoack@google.com>
> Cc: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Link: https://lore.kernel.org/r/20241109110856.222842-2-mic@digikod.net
> ---
>
> Changes since v3:
> * Rename landlock_merge_access_masks() to landlock_union_access_masks(),
> suggested by Günther.
> * Rename landlock_match_ruleset() to landlock_get_applicable_domain(),
> suggested by Günther.
> * Rename the "ruleset" arguments to "domain" for consistency with these
> new helpers.
> * Use typeof_member(), suggested by Günther. Also include kernel.h
> (instead of the new container_of.h to ease backports).
>
> Changes since v2:
> * Create a new type union access_masks_all instead of changing struct
> acces_masks.
> * Replace get_fs_domain() with an explicit call to
> landlock_match_ruleset().
>
> Changes since v1:
> * Rename landlock_filter_access_masks() to landlock_match_ruleset().
> * Rename local variables from domain to ruleset to mach helpers'
> semantic. We'll rename and move these helpers when we'll have a
> dedicated domain struct type.
> * Rename the all_fs mask to any_fs.
> * Add documentation, as suggested by Günther.
> ---
> security/landlock/fs.c | 31 ++++-----------
> security/landlock/ruleset.h | 73 ++++++++++++++++++++++++++++++++----
> security/landlock/syscalls.c | 2 +-
> 3 files changed, 74 insertions(+), 32 deletions(-)
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 7d79fc8abe21..e31b97a9f175 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -388,38 +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_union_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;
> -
> - return domain;
> -}
> +static const struct access_masks any_fs = {
> + .fs = ~0,
> +};
>
> static const struct landlock_ruleset *get_current_fs_domain(void)
> {
> - return get_fs_domain(landlock_get_current_domain());
> + return landlock_get_applicable_domain(landlock_get_current_domain(),
> + any_fs);
> }
>
> /*
> @@ -1517,7 +1501,8 @@ static int hook_file_open(struct file *const file)
> access_mask_t open_access_request, full_access_request, allowed_access,
> optional_access;
> const struct landlock_ruleset *const dom =
> - get_fs_domain(landlock_cred(file->f_cred)->domain);
> + landlock_get_applicable_domain(
> + landlock_cred(file->f_cred)->domain, any_fs);
>
> if (!dom)
> return 0;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 61bdbc550172..698fa3114cf4 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -11,6 +11,7 @@
>
> #include <linux/bitops.h>
> #include <linux/build_bug.h>
> +#include <linux/kernel.h>
> #include <linux/mutex.h>
> #include <linux/rbtree.h>
> #include <linux/refcount.h>
> @@ -47,6 +48,15 @@ struct access_masks {
> access_mask_t scope : LANDLOCK_NUM_SCOPE;
> };
>
> +union access_masks_all {
> + struct access_masks masks;
> + u32 all;
> +};
> +
> +/* Makes sure all fields are covered. */
> +static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
> + sizeof(typeof_member(union access_masks_all, all)));
> +
> typedef u16 layer_mask_t;
> /* Makes sure all layers can be checked. */
> static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
> @@ -260,6 +270,60 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
> refcount_inc(&ruleset->usage);
> }
>
> +/**
> + * landlock_union_access_masks - Return all access rights handled in the
> + * domain
> + *
> + * @domain: Landlock ruleset (used as a domain)
> + *
> + * Returns: an access_masks result of the OR of all the domain's access masks.
> + */
> +static inline struct access_masks
> +landlock_union_access_masks(const struct landlock_ruleset *const domain)
> +{
> + union access_masks_all matches = {};
> + size_t layer_level;
> +
> + for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
> + union access_masks_all layer = {
> + .masks = domain->access_masks[layer_level],
> + };
> +
> + matches.all |= layer.all;
> + }
> +
> + return matches.masks;
> +}
> +
> +/**
> + * landlock_get_applicable_domain - Return @domain if it applies to (handles)
> + * the access rights specified in @masks
Very minor wording suggestion: For clarity, I would suggest "at least one of":
Return @domain if it applies to (handles)
at least one of the access rights specified in @masks
Otherwise this can be interpreted as "...all of the access rights specified in
@masks".
> + *
> + * @domain: Landlock ruleset (used as a domain)
> + * @masks: access masks
> + *
> + * Returns: @domain if any access rights specified in @masks is handled, or
> + * NULL otherwise.
> + */
> +static inline const struct landlock_ruleset *
> +landlock_get_applicable_domain(const struct landlock_ruleset *const domain,
> + const struct access_masks masks)
> +{
> + const union access_masks_all masks_all = {
> + .masks = masks,
> + };
> + union access_masks_all merge = {};
> +
> + if (!domain)
> + return NULL;
> +
> + merge.masks = landlock_union_access_masks(domain);
> + if (merge.all & masks_all.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 +359,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.47.0
>
Reviewed-by: Günther Noack <gnoack@google.com>
Looks good, thanks! Names are much clearer now, IMHO. 👍
—Günther
next prev parent reply other threads:[~2024-11-09 18:21 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-09 11:08 [PATCH v4 0/3] Refactor Landlock access mask management Mickaël Salaün
2024-11-09 11:08 ` [PATCH v4 1/3] landlock: Refactor filesystem " Mickaël Salaün
2024-11-09 11:14 ` Mickaël Salaün
2024-11-09 18:36 ` Günther Noack
2024-11-09 18:21 ` Günther Noack [this message]
2024-11-09 11:08 ` [PATCH v4 2/3] landlock: Refactor network " Mickaël Salaün
2024-11-09 18:23 ` Günther Noack
2024-11-09 11:08 ` [PATCH v4 3/3] landlock: Optimize scope enforcement Mickaël Salaün
2024-11-09 18:30 ` Günther Noack
2024-11-09 18:47 ` Mickaël Salaün
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Zy-n_GtpJPV-5LrB@google.com \
--to=gnoack@google.com \
--cc=fahimitahera@gmail.com \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=konstantin.meskhidze@huawei.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mic@digikod.net \
--cc=paul@paul-moore.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.