diff options
author | Linus Torvalds | 2024-11-12 13:01:09 -0800 |
---|---|---|
committer | Linus Torvalds | 2024-11-12 13:01:09 -0800 |
commit | 92dda329e337b7ab9cb63f4563dd7a21d001e47c (patch) | |
tree | 89625b657a8779e19b56a0cc183a40fa194d5d0d | |
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
-rw-r--r-- | Documentation/security/landlock.rst | 14 | ||||
-rw-r--r-- | Documentation/userspace-api/landlock.rst | 90 | ||||
-rw-r--r-- | samples/landlock/sandboxer.c | 112 | ||||
-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 |
8 files changed, 217 insertions, 152 deletions
diff --git a/Documentation/security/landlock.rst b/Documentation/security/landlock.rst index 36f26501fd15..59ecdb1c0d4d 100644 --- a/Documentation/security/landlock.rst +++ b/Documentation/security/landlock.rst @@ -11,18 +11,18 @@ Landlock LSM: kernel documentation Landlock's goal is to create scoped access-control (i.e. sandboxing). To harden a whole system, this feature should be available to any process, -including unprivileged ones. Because such process may be compromised or +including unprivileged ones. Because such a process may be compromised or backdoored (i.e. untrusted), Landlock's features must be safe to use from the kernel and other processes point of view. Landlock's interface must therefore expose a minimal attack surface. Landlock is designed to be usable by unprivileged processes while following the system security policy enforced by other access control mechanisms (e.g. DAC, -LSM). Indeed, a Landlock rule shall not interfere with other access-controls -enforced on the system, only add more restrictions. +LSM). A Landlock rule shall not interfere with other access-controls enforced +on the system, only add more restrictions. Any user can enforce Landlock rulesets on their processes. They are merged and -evaluated according to the inherited ones in a way that ensures that only more +evaluated against inherited rulesets in a way that ensures that only more constraints can be added. User space documentation can be found here: @@ -43,7 +43,7 @@ Guiding principles for safe access controls only impact the processes requesting them. * Resources (e.g. file descriptors) directly obtained from the kernel by a sandboxed process shall retain their scoped accesses (at the time of resource - acquisition) whatever process use them. + acquisition) whatever process uses them. Cf. `File descriptor access rights`_. Design choices @@ -71,7 +71,7 @@ the same results, when they are executed under the same Landlock domain. Taking the ``LANDLOCK_ACCESS_FS_TRUNCATE`` right as an example, it may be allowed to open a file for writing without being allowed to :manpage:`ftruncate` the resulting file descriptor if the related file -hierarchy doesn't grant such access right. The following sequences of +hierarchy doesn't grant that access right. The following sequences of operations have the same semantic and should then have the same result: * ``truncate(path);`` @@ -81,7 +81,7 @@ Similarly to file access modes (e.g. ``O_RDWR``), Landlock access rights attached to file descriptors are retained even if they are passed between processes (e.g. through a Unix domain socket). Such access rights will then be enforced even if the receiving process is not sandboxed by Landlock. Indeed, -this is required to keep a consistent access control over the whole system, and +this is required to keep access controls consistent over the whole system, and this avoids unattended bypasses through file descriptor passing (i.e. confused deputy attack). diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst index c8d3e46badc5..d639c61cb472 100644 --- a/Documentation/userspace-api/landlock.rst +++ b/Documentation/userspace-api/landlock.rst @@ -8,13 +8,13 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: September 2024 +:Date: October 2024 -The goal of Landlock is to enable to restrict ambient rights (e.g. global +The goal of Landlock is to enable restriction of ambient rights (e.g. global filesystem or network access) for a set of processes. Because Landlock -is a stackable LSM, it makes possible to create safe security sandboxes as new -security layers in addition to the existing system-wide access-controls. This -kind of sandbox is expected to help mitigate the security impact of bugs or +is a stackable LSM, it makes it possible to create safe security sandboxes as +new security layers in addition to the existing system-wide access-controls. +This kind of sandbox is expected to help mitigate the security impact of bugs or unexpected/malicious behaviors in user space applications. Landlock empowers any process, including unprivileged ones, to securely restrict themselves. @@ -86,8 +86,8 @@ to be explicit about the denied-by-default access rights. LANDLOCK_SCOPE_SIGNAL, }; -Because we may not know on which kernel version an application will be -executed, it is safer to follow a best-effort security approach. Indeed, we +Because we may not know which kernel version an application will be executed +on, it is safer to follow a best-effort security approach. Indeed, we should try to protect users as much as possible whatever the kernel they are using. @@ -129,7 +129,7 @@ version, and only use the available subset of access rights: LANDLOCK_SCOPE_SIGNAL); } -This enables to create an inclusive ruleset that will contain our rules. +This enables the creation of an inclusive ruleset that will contain our rules. .. code-block:: c @@ -219,42 +219,41 @@ If the ``landlock_restrict_self`` system call succeeds, the current thread is now restricted and this policy will be enforced on all its subsequently created children as well. Once a thread is landlocked, there is no way to remove its security policy; only adding more restrictions is allowed. These threads are -now in a new Landlock domain, merge of their parent one (if any) with the new -ruleset. +now in a new Landlock domain, which is a merger of their parent one (if any) +with the new ruleset. Full working code can be found in `samples/landlock/sandboxer.c`_. Good practices -------------- -It is recommended setting access rights to file hierarchy leaves as much as +It is recommended to set access rights to file hierarchy leaves as much as possible. For instance, it is better to be able to have ``~/doc/`` as a read-only hierarchy and ``~/tmp/`` as a read-write hierarchy, compared to ``~/`` as a read-only hierarchy and ``~/tmp/`` as a read-write hierarchy. Following this good practice leads to self-sufficient hierarchies that do not depend on their location (i.e. parent directories). This is particularly relevant when we want to allow linking or renaming. Indeed, having consistent -access rights per directory enables to change the location of such directory +access rights per directory enables changing the location of such directories without relying on the destination directory access rights (except those that are required for this operation, see ``LANDLOCK_ACCESS_FS_REFER`` documentation). Having self-sufficient hierarchies also helps to tighten the required access rights to the minimal set of data. This also helps avoid sinkhole directories, -i.e. directories where data can be linked to but not linked from. However, +i.e. directories where data can be linked to but not linked from. However, this depends on data organization, which might not be controlled by developers. In this case, granting read-write access to ``~/tmp/``, instead of write-only -access, would potentially allow to move ``~/tmp/`` to a non-readable directory +access, would potentially allow moving ``~/tmp/`` to a non-readable directory and still keep the ability to list the content of ``~/tmp/``. Layers of file path access rights --------------------------------- Each time a thread enforces a ruleset on itself, it updates its Landlock domain -with a new layer of policy. Indeed, this complementary policy is stacked with -the potentially other rulesets already restricting this thread. A sandboxed -thread can then safely add more constraints to itself with a new enforced -ruleset. +with a new layer of policy. This complementary policy is stacked with any +other rulesets potentially already restricting this thread. A sandboxed thread +can then safely add more constraints to itself with a new enforced ruleset. One policy layer grants access to a file path if at least one of its rules encountered on the path grants the access. A sandboxed thread can only access @@ -265,7 +264,7 @@ etc.). Bind mounts and OverlayFS ------------------------- -Landlock enables to restrict access to file hierarchies, which means that these +Landlock enables restricting access to file hierarchies, which means that these access rights can be propagated with bind mounts (cf. Documentation/filesystems/sharedsubtree.rst) but not with Documentation/filesystems/overlayfs.rst. @@ -278,21 +277,21 @@ access to multiple file hierarchies at the same time, whether these hierarchies are the result of bind mounts or not. An OverlayFS mount point consists of upper and lower layers. These layers are -combined in a merge directory, result of the mount point. This merge hierarchy -may include files from the upper and lower layers, but modifications performed -on the merge hierarchy only reflects on the upper layer. From a Landlock -policy point of view, each OverlayFS layers and merge hierarchies are -standalone and contains their own set of files and directories, which is -different from bind mounts. A policy restricting an OverlayFS layer will not -restrict the resulted merged hierarchy, and vice versa. Landlock users should -then only think about file hierarchies they want to allow access to, regardless -of the underlying filesystem. +combined in a merge directory, and that merged directory becomes available at +the mount point. This merge hierarchy may include files from the upper and +lower layers, but modifications performed on the merge hierarchy only reflect +on the upper layer. From a Landlock policy point of view, all OverlayFS layers +and merge hierarchies are standalone and each contains their own set of files +and directories, which is different from bind mounts. A policy restricting an +OverlayFS layer will not restrict the resulted merged hierarchy, and vice versa. +Landlock users should then only think about file hierarchies they want to allow +access to, regardless of the underlying filesystem. Inheritance ----------- Every new thread resulting from a :manpage:`clone(2)` inherits Landlock domain -restrictions from its parent. This is similar to the seccomp inheritance (cf. +restrictions from its parent. This is similar to seccomp inheritance (cf. Documentation/userspace-api/seccomp_filter.rst) or any other LSM dealing with task's :manpage:`credentials(7)`. For instance, one process's thread may apply Landlock rules to itself, but they will not be automatically applied to other @@ -311,8 +310,8 @@ Ptrace restrictions A sandboxed process has less privileges than a non-sandboxed process and must then be subject to additional restrictions when manipulating another process. To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target -process, a sandboxed process should have a subset of the target process rules, -which means the tracee must be in a sub-domain of the tracer. +process, a sandboxed process should have a superset of the target process's +access rights, which means the tracee must be in a sub-domain of the tracer. IPC scoping ----------- @@ -322,7 +321,7 @@ interactions between sandboxes. Each Landlock domain can be explicitly scoped for a set of actions by specifying it on a ruleset. For example, if a sandboxed process should not be able to :manpage:`connect(2)` to a non-sandboxed process through abstract :manpage:`unix(7)` sockets, we can -specify such restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``. +specify such a restriction with ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET``. Moreover, if a sandboxed process should not be able to send a signal to a non-sandboxed process, we can specify this restriction with ``LANDLOCK_SCOPE_SIGNAL``. @@ -394,7 +393,7 @@ Backward and forward compatibility Landlock is designed to be compatible with past and future versions of the kernel. This is achieved thanks to the system call attributes and the associated bitflags, particularly the ruleset's ``handled_access_fs``. Making -handled access right explicit enables the kernel and user space to have a clear +handled access rights explicit enables the kernel and user space to have a clear contract with each other. This is required to make sure sandboxing will not get stricter with a system update, which could break applications. @@ -563,33 +562,34 @@ always allowed when using a kernel that only supports the first or second ABI. Starting with the Landlock ABI version 3, it is now possible to securely control truncation thanks to the new ``LANDLOCK_ACCESS_FS_TRUNCATE`` access right. -Network support (ABI < 4) -------------------------- +TCP bind and connect (ABI < 4) +------------------------------ Starting with the Landlock ABI version 4, it is now possible to restrict TCP bind and connect actions to only a set of allowed ports thanks to the new ``LANDLOCK_ACCESS_NET_BIND_TCP`` and ``LANDLOCK_ACCESS_NET_CONNECT_TCP`` access rights. -IOCTL (ABI < 5) ---------------- +Device IOCTL (ABI < 5) +---------------------- IOCTL operations could not be denied before the fifth Landlock ABI, so :manpage:`ioctl(2)` is always allowed when using a kernel that only supports an earlier ABI. Starting with the Landlock ABI version 5, it is possible to restrict the use of -:manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right. +:manpage:`ioctl(2)` on character and block devices using the new +``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right. -Abstract UNIX socket scoping (ABI < 6) --------------------------------------- +Abstract UNIX socket (ABI < 6) +------------------------------ Starting with the Landlock ABI version 6, it is possible to restrict connections to an abstract :manpage:`unix(7)` socket by setting ``LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET`` to the ``scoped`` ruleset attribute. -Signal scoping (ABI < 6) ------------------------- +Signal (ABI < 6) +---------------- Starting with the Landlock ABI version 6, it is possible to restrict :manpage:`signal(7)` sending by setting ``LANDLOCK_SCOPE_SIGNAL`` to the @@ -605,9 +605,9 @@ Build time configuration Landlock was first introduced in Linux 5.13 but it must be configured at build time with ``CONFIG_SECURITY_LANDLOCK=y``. Landlock must also be enabled at boot -time as the other security modules. The list of security modules enabled by +time like other security modules. The list of security modules enabled by default is set with ``CONFIG_LSM``. The kernel configuration should then -contains ``CONFIG_LSM=landlock,[...]`` with ``[...]`` as the list of other +contain ``CONFIG_LSM=landlock,[...]`` with ``[...]`` as the list of other potentially useful security modules for the running system (see the ``CONFIG_LSM`` help). @@ -669,7 +669,7 @@ Questions and answers What about user space sandbox managers? --------------------------------------- -Using user space process to enforce restrictions on kernel resources can lead +Using user space processes to enforce restrictions on kernel resources can lead to race conditions or inconsistent evaluations (i.e. `Incorrect mirroring of the OS code and state <https://www.ndss-symposium.org/ndss2003/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools/>`_). diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index f847e832ba14..57565dfd74a2 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -60,6 +60,25 @@ static inline int landlock_restrict_self(const int ruleset_fd, #define ENV_SCOPED_NAME "LL_SCOPED" #define ENV_DELIMITER ":" +static int str2num(const char *numstr, __u64 *num_dst) +{ + char *endptr = NULL; + int err = 0; + __u64 num; + + errno = 0; + num = strtoull(numstr, &endptr, 10); + if (errno != 0) + err = errno; + /* Was the string empty, or not entirely parsed successfully? */ + else if ((*numstr == '\0') || (*endptr != '\0')) + err = EINVAL; + else + *num_dst = num; + + return err; +} + static int parse_path(char *env_path, const char ***const path_list) { int i, num_paths = 0; @@ -160,7 +179,6 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, char *env_port_name, *env_port_name_next, *strport; struct landlock_net_port_attr net_port = { .allowed_access = allowed_access, - .port = 0, }; env_port_name = getenv(env_var); @@ -171,7 +189,17 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd, env_port_name_next = env_port_name; while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) { - net_port.port = atoi(strport); + __u64 port; + + if (strcmp(strport, "") == 0) + continue; + + if (str2num(strport, &port)) { + fprintf(stderr, "Failed to parse port at \"%s\"\n", + strport); + goto out_free_name; + } + net_port.port = port; if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &net_port, 0)) { fprintf(stderr, @@ -262,6 +290,44 @@ out_unset: #define LANDLOCK_ABI_LAST 6 +#define XSTR(s) #s +#define STR(s) XSTR(s) + +/* clang-format off */ + +static const char help[] = + "usage: " ENV_FS_RO_NAME "=\"...\" " ENV_FS_RW_NAME "=\"...\" " + "[other environment variables] %1$s <cmd> [args]...\n" + "\n" + "Execute the given command in a restricted environment.\n" + "Multi-valued settings (lists of ports, paths, scopes) are colon-delimited.\n" + "\n" + "Mandatory settings:\n" + "* " ENV_FS_RO_NAME ": paths allowed to be used in a read-only way\n" + "* " ENV_FS_RW_NAME ": paths allowed to be used in a read-write way\n" + "\n" + "Optional settings (when not set, their associated access check " + "is always allowed, which is different from an empty string which " + "means an empty list):\n" + "* " ENV_TCP_BIND_NAME ": ports allowed to bind (server)\n" + "* " ENV_TCP_CONNECT_NAME ": ports allowed to connect (client)\n" + "* " ENV_SCOPED_NAME ": actions denied on the outside of the landlock domain\n" + " - \"a\" to restrict opening abstract unix sockets\n" + " - \"s\" to restrict sending signals\n" + "\n" + "Example:\n" + ENV_FS_RO_NAME "=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " + ENV_FS_RW_NAME "=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " + ENV_TCP_BIND_NAME "=\"9418\" " + ENV_TCP_CONNECT_NAME "=\"80:443\" " + ENV_SCOPED_NAME "=\"a:s\" " + "%1$s bash -i\n" + "\n" + "This sandboxer can use Landlock features up to ABI version " + STR(LANDLOCK_ABI_LAST) ".\n"; + +/* clang-format on */ + int main(const int argc, char *const argv[], char *const *const envp) { const char *cmd_path; @@ -280,47 +346,7 @@ int main(const int argc, char *const argv[], char *const *const envp) }; if (argc < 2) { - fprintf(stderr, - "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\" %s " - "<cmd> [args]...\n\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, - ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); - fprintf(stderr, - "Execute a command in a restricted environment.\n\n"); - fprintf(stderr, - "Environment variables containing paths and ports " - "each separated by a colon:\n"); - fprintf(stderr, - "* %s: list of paths allowed to be used in a read-only way.\n", - ENV_FS_RO_NAME); - fprintf(stderr, - "* %s: list of paths allowed to be used in a read-write way.\n\n", - ENV_FS_RW_NAME); - fprintf(stderr, - "Environment variables containing ports are optional " - "and could be skipped.\n"); - fprintf(stderr, - "* %s: list of ports allowed to bind (server).\n", - ENV_TCP_BIND_NAME); - fprintf(stderr, - "* %s: list of ports allowed to connect (client).\n", - ENV_TCP_CONNECT_NAME); - fprintf(stderr, "* %s: list of scoped IPCs.\n", - ENV_SCOPED_NAME); - fprintf(stderr, - "\nexample:\n" - "%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" " - "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " - "%s=\"9418\" " - "%s=\"80:443\" " - "%s=\"a:s\" " - "%s bash -i\n\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME, - ENV_TCP_CONNECT_NAME, ENV_SCOPED_NAME, argv[0]); - fprintf(stderr, - "This sandboxer can use Landlock features " - "up to ABI version %d.\n", - LANDLOCK_ABI_LAST); + fprintf(stderr, help, argv[0]); return 1; } 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) |