mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
* Add a new access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which controls the lookup operations for named UNIX domain sockets. The resolution happens during connect() and sendmsg() (depending on socket type). * Change access_mask_t from u16 to u32 (see below) * Hook into the path lookup in unix_find_bsd() in af_unix.c, using a LSM hook. Make policy decisions based on the new access rights * Increment the Landlock ABI version. * Minor test adaptations to keep the tests working. * Document the design rationale for scoped access rights, and cross-reference it from the header documentation. With this access right, access is granted if either of the following conditions is met: * The target socket's filesystem path was allow-listed using a LANDLOCK_RULE_PATH_BENEATH rule, *or*: * The target socket was created in the same Landlock domain in which LANDLOCK_ACCESS_FS_RESOLVE_UNIX was restricted. In case of a denial, connect() and sendmsg() return EACCES, which is the same error as it is returned if the user does not have the write bit in the traditional UNIX file system permissions of that file. The access_mask_t type grows from u16 to u32 to make space for the new access right. This also doubles the size of struct layer_access_masks from 32 byte to 64 byte. To avoid memory layout inconsistencies between architectures (especially m68k), pack and align struct access_masks [2]. Document the (possible future) interaction between scoped flags and other access rights in struct landlock_ruleset_attr, and summarize the rationale, as discussed in code review leading up to [3]. This feature was created with substantial discussion and input from Justin Suess, Tingmao Wang and Mickaël Salaün. Cc: Tingmao Wang <m@maowtm.org> Cc: Justin Suess <utilityemal77@gmail.com> Cc: Kuniyuki Iwashima <kuniyu@google.com> Suggested-by: Jann Horn <jannh@google.com> Link[1]: https://github.com/landlock-lsm/linux/issues/36 Link[2]: https://lore.kernel.org/all/20260401.Re1Eesu1Yaij@digikod.net/ Link[3]: https://lore.kernel.org/all/20260205.8531e4005118@gnoack.org/ Signed-off-by: Günther Noack <gnoack3000@gmail.com> Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Link: https://lore.kernel.org/r/20260327164838.38231-5-gnoack3000@gmail.com [mic: Fix kernel-doc formatting, pack and align access_masks] Signed-off-by: Mickaël Salaün <mic@digikod.net>
124 lines
3.9 KiB
C
124 lines
3.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Landlock - Access types and helpers
|
|
*
|
|
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
|
|
* Copyright © 2018-2020 ANSSI
|
|
* Copyright © 2024-2025 Microsoft Corporation
|
|
*/
|
|
|
|
#ifndef _SECURITY_LANDLOCK_ACCESS_H
|
|
#define _SECURITY_LANDLOCK_ACCESS_H
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/build_bug.h>
|
|
#include <linux/kernel.h>
|
|
#include <uapi/linux/landlock.h>
|
|
|
|
#include "limits.h"
|
|
|
|
/*
|
|
* All access rights that are denied by default whether they are handled or not
|
|
* by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
|
|
* entries when we need to get the absolute handled access masks, see
|
|
* landlock_upgrade_handled_access_masks().
|
|
*/
|
|
/* clang-format off */
|
|
#define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
|
|
LANDLOCK_ACCESS_FS_REFER)
|
|
/* clang-format on */
|
|
|
|
/* clang-format off */
|
|
#define _LANDLOCK_ACCESS_FS_OPTIONAL ( \
|
|
LANDLOCK_ACCESS_FS_TRUNCATE | \
|
|
LANDLOCK_ACCESS_FS_IOCTL_DEV)
|
|
/* clang-format on */
|
|
|
|
typedef u32 access_mask_t;
|
|
|
|
/* Makes sure all filesystem access rights can be stored. */
|
|
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
|
|
/* Makes sure all network access rights can be stored. */
|
|
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
|
|
/* Makes sure all scoped rights can be stored. */
|
|
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
|
|
/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
|
|
static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
|
|
|
|
/* Ruleset access masks. */
|
|
struct access_masks {
|
|
access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
|
|
access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
|
|
access_mask_t scope : LANDLOCK_NUM_SCOPE;
|
|
} __packed __aligned(sizeof(u32));
|
|
|
|
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)));
|
|
|
|
/**
|
|
* struct layer_access_masks - A boolean matrix of layers and access rights
|
|
*
|
|
* This has a bit for each combination of layer numbers and access rights.
|
|
* During access checks, it is used to represent the access rights for each
|
|
* layer which still need to be fulfilled. When all bits are 0, the access
|
|
* request is considered to be fulfilled.
|
|
*/
|
|
struct layer_access_masks {
|
|
/**
|
|
* @access: The unfulfilled access rights for each layer.
|
|
*/
|
|
access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
|
|
};
|
|
|
|
/*
|
|
* Tracks domains responsible of a denied access. This avoids storing in each
|
|
* object the full matrix of per-layer unfulfilled access rights, which is
|
|
* required by update_request().
|
|
*
|
|
* Each nibble represents the layer index of the newest layer which denied a
|
|
* certain access right. For file system access rights, the upper four bits are
|
|
* the index of the layer which denies LANDLOCK_ACCESS_FS_IOCTL_DEV and the
|
|
* lower nibble represents LANDLOCK_ACCESS_FS_TRUNCATE.
|
|
*/
|
|
typedef u8 deny_masks_t;
|
|
|
|
/*
|
|
* Makes sure all optional access rights can be tied to a layer index (cf.
|
|
* get_deny_mask).
|
|
*/
|
|
static_assert(BITS_PER_TYPE(deny_masks_t) >=
|
|
(HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1) *
|
|
HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL)));
|
|
|
|
/* LANDLOCK_MAX_NUM_LAYERS must be a power of two (cf. deny_masks_t assert). */
|
|
static_assert(HWEIGHT(LANDLOCK_MAX_NUM_LAYERS) == 1);
|
|
|
|
/* Upgrades with all initially denied by default access rights. */
|
|
static inline struct access_masks
|
|
landlock_upgrade_handled_access_masks(struct access_masks access_masks)
|
|
{
|
|
/*
|
|
* All access rights that are denied by default whether they are
|
|
* explicitly handled or not.
|
|
*/
|
|
if (access_masks.fs)
|
|
access_masks.fs |= _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
|
|
|
|
return access_masks;
|
|
}
|
|
|
|
/* Checks the subset relation between access masks. */
|
|
static inline bool access_mask_subset(access_mask_t subset,
|
|
access_mask_t superset)
|
|
{
|
|
return (subset | superset) == superset;
|
|
}
|
|
|
|
#endif /* _SECURITY_LANDLOCK_ACCESS_H */
|