diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-12 13:01:09 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-12 13:01:09 -0800 |
commit | 92dda329e337b7ab9cb63f4563dd7a21d001e47c (patch) | |
tree | 89625b657a8779e19b56a0cc183a40fa194d5d0d /security | |
parent | 3022e9d00ebec31ed435ae0844e3f235dba998a9 (diff) | |
parent | 03197e40a22c2641a1f9d1744418cd29f4954b83 (diff) |
Merge tag 'landlock-6.12-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock fixes from Mickaël Salaün:
"This fixes issues in the Landlock's sandboxer sample and
documentation, slightly refactors helpers (required for ongoing patch
series), and improve/fix a feature merged in v6.12 (signal and
abstract UNIX socket scoping)"
* tag 'landlock-6.12-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
landlock: Optimize scope enforcement
landlock: Refactor network access mask management
landlock: Refactor filesystem access mask management
samples/landlock: Clarify option parsing behaviour
samples/landlock: Refactor help message
samples/landlock: Fix port parsing in sandboxer
landlock: Fix grammar issues in documentation
landlock: Improve documentation of previous limitations
Diffstat (limited to 'security')
-rw-r--r-- | security/landlock/fs.c | 31 | ||||
-rw-r--r-- | security/landlock/net.c | 28 | ||||
-rw-r--r-- | security/landlock/ruleset.h | 74 | ||||
-rw-r--r-- | security/landlock/syscalls.c | 2 | ||||
-rw-r--r-- | security/landlock/task.c | 18 |
5 files changed, 96 insertions, 57 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 @@ -389,37 +389,21 @@ static bool is_nouser_or_private(const struct dentry *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/net.c b/security/landlock/net.c index c8bcd29bde09..d5dcc4407a19 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -39,27 +39,9 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset, return err; } -static access_mask_t -get_raw_handled_net_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_net_access_mask(domain, layer_level); - return access_dom; -} - -static const struct landlock_ruleset *get_current_net_domain(void) -{ - const struct landlock_ruleset *const dom = - landlock_get_current_domain(); - - if (!dom || !get_raw_handled_net_accesses(dom)) - return NULL; - - return dom; -} +static const struct access_masks any_net = { + .net = ~0, +}; static int current_check_access_socket(struct socket *const sock, struct sockaddr *const address, @@ -72,7 +54,9 @@ static int current_check_access_socket(struct socket *const sock, struct landlock_id id = { .type = LANDLOCK_KEY_NET_PORT, }; - const struct landlock_ruleset *const dom = get_current_net_domain(); + const struct landlock_ruleset *const dom = + landlock_get_applicable_domain(landlock_get_current_domain(), + any_net); if (!dom) return 0; diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 61bdbc550172..631e24d4ffe9 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,61 @@ 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) + * at least one 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, @@ -296,18 +361,11 @@ landlock_add_scope_mask(struct landlock_ruleset *const ruleset, } 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; diff --git a/security/landlock/task.c b/security/landlock/task.c index 4acbd7c40eee..dc7dab78392e 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -204,12 +204,17 @@ static bool is_abstract_socket(struct sock *const sock) return false; } +static const struct access_masks unix_scope = { + .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, +}; + static int hook_unix_stream_connect(struct sock *const sock, struct sock *const other, struct sock *const newsk) { const struct landlock_ruleset *const dom = - landlock_get_current_domain(); + landlock_get_applicable_domain(landlock_get_current_domain(), + unix_scope); /* Quick return for non-landlocked tasks. */ if (!dom) @@ -225,7 +230,8 @@ static int hook_unix_may_send(struct socket *const sock, struct socket *const other) { const struct landlock_ruleset *const dom = - landlock_get_current_domain(); + landlock_get_applicable_domain(landlock_get_current_domain(), + unix_scope); if (!dom) return 0; @@ -243,6 +249,10 @@ static int hook_unix_may_send(struct socket *const sock, return 0; } +static const struct access_masks signal_scope = { + .scope = LANDLOCK_SCOPE_SIGNAL, +}; + static int hook_task_kill(struct task_struct *const p, struct kernel_siginfo *const info, const int sig, const struct cred *const cred) @@ -256,6 +266,7 @@ static int hook_task_kill(struct task_struct *const p, } else { dom = landlock_get_current_domain(); } + dom = landlock_get_applicable_domain(dom, signal_scope); /* Quick return for non-landlocked tasks. */ if (!dom) @@ -279,7 +290,8 @@ static int hook_file_send_sigiotask(struct task_struct *tsk, /* Lock already held by send_sigio() and send_sigurg(). */ lockdep_assert_held(&fown->lock); - dom = landlock_file(fown->file)->fown_domain; + dom = landlock_get_applicable_domain( + landlock_file(fown->file)->fown_domain, signal_scope); /* Quick return for unowned socket. */ if (!dom) |