landlock: UNIX connect() control by pathname and scope
Subsystems (7)
v8 cover landlock: UNIX connect() control by pathname and scope
Hello!
This patch set introduces a filesystem-based Landlock restriction mechanism for connecting to UNIX domain sockets (or addressing them with sendmsg(2)). It introduces the filesystem access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX.
For the connection-oriented SOCK_STREAM and SOCK_SEQPACKET type sockets, the access right makes the connect(2) operation fail with EACCES, if denied.
SOCK_DGRAM-type UNIX sockets can be used both with connect(2), or by passing an explicit recipient address with every sendmsg(2) invocation. In the latter case, the Landlock check is done when an explicit recipient address is passed to sendmsg(2) and can make sendmsg(2) return EACCES. When UNIX datagram sockets are connected with connect(2), a fixed recipient address is associated with the socket and the check happens during connect(2) and may return EACCES.
When LANDLOCK_ACCESS_FS_RESOLVE_UNIX is handled within a Landlock domain, this domain will only allow connect(2) and sendmsg(2) to server sockets that were created within the same domain. Or, to phrase it the other way around: Unless it is allow-listed with a LANDLOCK_PATH_BENEATH rule, the newly created domain denies connect(2) and sendmsg(2) actions that are directed *outwards* of that domain. In that regard, LANDLOCK_ACCESS_FS_RESOLVE_UNIX has the same semantics as one of the "scoped" access rights.
== Motivation
Currently, landlocked processes can connect to named UNIX sockets through the BSD socket API described in unix(7), by invoking socket(2) followed by connect(2) with a suitable struct sockname_un holding the socket's filename. This is a surprising gap in Landlock's sandboxing capabilities for users (e.g. in [1]) and it can be used to escape a sandbox when a Unix service offers command execution (various such scenarios were listed by Tingmao Wang in [2]).
The original feature request is at [4].
== Alternatives and Related Work
=== Alternative: Use existing LSM hooks
We have carefully and seriously considered the use of existing LSM hooks, but still came to the conclusion that a new LSM hook is better suited in this case:
The existing hooks security_unix_stream_connect(), security_unix_may_send() and security_socket_connect() do not give access to the resolved filesystem path.
* Resolving the filesystem path in the struct sockaddr_un again within a Landlock would produce a TOCTOU race, so this is not an option.
* We would therefore need to wire through the resolved struct path from unix_find_bsd() to one of the existing LSM hooks which get called later. This would be a more substantial change to af_unix.c.
The struct path that is available in the listening-side struct sock is can be read through the existing hooks, but it is not an option to use this information: As the listening socket may have been bound from within a different namespace, the path that was used for that can is in the general case not meaningful for a sandboxed process. In particular, it is not possible to use this path (or prefixes thereof) when constructing a sandbox policy in the client-side process.
Paul Moore also chimed in in support of adding a new hook, with the rationale that the simplest change to the LSM hook interface has traditionally proven to be the most robust. [11]
More details are on the Github issue at [6] and on the LKML at [9].
In a the discussion of the V2 review, started by Christian Brauner [10], we have further explored the approach of reusing the existing LSM hooks but still ended up leaning on the side of introducing a new hook, with Paul Moore and me (gnoack) arguing for that option.
Further insights about the LSM hook were shared in the V3 review by Tingmao Wang [12], who spotted additional requirements due to the two approaches being merged into one patch set. The summary of that discussion is in [13].
=== Related work: Scope Control for Pathname Unix Sockets
The motivation for this patch is the same as in Tingmao Wang's patch set for "scoped" control for pathname Unix sockets [2], originally proposed in the Github feature request [5].
In [14], we have settled on the decision to merge the two patch sets into this one, whose primary way of controlling connect(2) is LANDLOCK_ACCESS_FS_RESOLVE_UNIX, but where this flag additionally has the semantics of only restricting this unix(7) IPC *outwards* of the created Landlock domain, in line with the logic that exists for the existing "scoped" flags already.
By having LANDLOCK_ACCESS_FS_RESOLVE_UNIX implement "scoping" semantics, we can avoid introducing two separate interacting flags for now, but we retain the option of introducing LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET at a later point in time, should such a flag be needed to express additional rules.
== Credits
The feature was originally suggested by Jann Horn in [7].
Tingmao Wang and Demi Marie Obenour have taken the initiative to revive this discussion again in [1], [4] and [5].
Tingmao Wang has sent the patch set for the scoped access control for pathname Unix sockets [2] and has contributed substantial insights during the code review, shaping the form of the LSM hook and agreeing to merge the pathname and scoped-flag patch sets.
Justin Suess has sent the patch for the LSM hook in [8] and subsequently through this patch set.
Christian Brauner and Paul Moore have contributed to the design of the new LSM hook, discussing the tradeoffs in [10].
Sebastian Andrzej Siewior and Kuniyuki Iwashima have helped with locking questions in the networking subsystem. [15] [16]
Ryan Sullivan has started on an initial implementation and has brought up relevant discussion points on the Github issue at [4].
As maintainer of Landlock, Mickaël Salaün has done the main review so far and particularly pointed out ways in which the UNIX connect() patch sets interact with each other and what we need to look for with regards to UAPI consistency as Landlock evolves.
[1] https://lore.kernel.org/landlock/[email protected]/
[2] Tingmao Wang's "Implement scope control for pathname Unix sockets"
https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
[4] Github issue for FS-based control for named Unix sockets:
https://github.com/landlock-lsm/linux/issues/36
[5] Github issue for scope-based restriction of named Unix sockets:
https://github.com/landlock-lsm/linux/issues/51
[6] https://github.com/landlock-lsm/linux/issues/36#issuecomment-2950632277
[7] https://lore.kernel.org/linux-security-module/CAG48ez3NvVnonOqKH4oRwRqbSOLO0p9djBqgvxVwn6gtGQBPcw@mail.gmail.com/
[8] Patch for the LSM hook:
https://lore.kernel.org/all/[email protected]/
[9] https://lore.kernel.org/all/[email protected]/
[10] https://lore.kernel.org/all/20260113-kerngesund-etage-86de4a21da24@brauner/
[11] https://lore.kernel.org/all/CAHC9VhQHZCe0LMx4xzSo-h1SWY489U4frKYnxu4YVrcJN3x7nA@mail.gmail.com/
[12] https://lore.kernel.org/all/[email protected]/
[13] https://lore.kernel.org/all/[email protected]/
[14] https://lore.kernel.org/all/[email protected]/
[15] https://lore.kernel.org/all/[email protected]/
[16] https://lore.kernel.org/all/CAAVpQUC95mSjX1vRK===pubHofcYqbkNE7goYKiu6vha5GYAFw@mail.gmail.com/
---
== Patch set history
V1: https://lore.kernel.org/all/[email protected]/
V2: https://lore.kernel.org/all/[email protected]/
V3: https://lore.kernel.org/all/[email protected]/
V4: https://lore.kernel.org/all/[email protected]/
V5: https://lore.kernel.org/all/[email protected]/
V6: https://lore.kernel.org/all/[email protected]/
V7: https://lore.kernel.org/all/[email protected]/
Changes in V8:
* Added a fix for the m68k GCC 15 compiler failure
* Reorder BUILD_BUG_ON commit to be directly after the implementation commit
* Documentation: Use the current month (March 2026) in uapi doc comment
Changes in V7:
* Implementation:
* LSM hook: Small header file layout restructurings, typos (Justin)
* Hold unix_state_lock across the usage of the other Landlock domain This prevents a UAF; spotted by Mickaël and Sebastian in code review 🏆
* Ignore the return value from landlock_init_layer_masks() - it is not needed in this specific case
* Add a BUILD_BUG_ON to unmask_scoped_access() like in domain_is_scoped(). Revise the BUILD_BUG_ON logic and bring both implementations in line.
* Documentation and commentary:
* Mention access_mask_t change from u16 to u32 in commit message
* Improve documentation for unmask_scoped_access()
* Various typos and smaller documentation fixes, caught in code review
* Add ABI version remark to landlock.h
* Add comment to explain why returning 0 on SOCK_DEAD is correct - This is not obvious in the code and requires understanding the callers
* In kernel docs, use "case 6 ... 8" in a place
Changes in V6:
* Implementation:
* Move the LSM hook call after the check that checks for the other end's matching socket type. (Justin Suess)
* Lock with unix_state_lock() and check SOCK_DEAD.
* Remove unnecessary layer_access_masks_empty() call (and its implementation).
* Documentation:
* Squash docs with design rationale into main implementation commit, and cross-referece it from the header docs.
* Clarify that denials result in EACCES and that this is consistent with other filesystem access rights.
* Minor:
* Use mem_is_zero() in is_layer_masks_allowed() (minor cleanup)
* Omit unnecessary __attribute__((fallthrough)) usages
* Remove comment at the end of a line in a place.
* Selftests:
* sun_path population fixes
* coredump test: Set EUID to 0 (needed for UML-based selftests)
Link[1]: https://lore.kernel.org/all/[email protected]/
Changes in V5:
This change primarily adds tests, changing the testing approach for the main test to use the scoped_domains fixture as in Tingmao's patch set [2], and adding tests for the audit and coredump cases.
* Selftests:
* Replaced the main selftest with one based on scoped_domains
* Added audit test
* Added test for the coredump case
* Added a follow-up commit that simplifies ruleset enforcement
* Kernel code:
* Mark coredump check as unlikely (per Justin's review)
* Drop check for socket type (per Mickaël's review)
Changes in V4:
Since this version, this patch set subsumes the scoping semantics from Tingmao Wang's "Scope Control" patch set [2], per discussion with Tingmao Wang and Mickaël Salaün in [14] and in the thread leading up to it.
Now, LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET only restricts connect(2) and sendmsg(2) *outwards* of the domain where it is restricted, *with the same semantics as a "scoped" flag*.
* Implement a layer-mask based version of domain_is_scoped():
unmask_scoped_access(). Rationale: domain_is_scoped() returns early, which we can't do in the layer masks based variant. The two variants are similar enough.
* LSM hook: Replace 'type' argument with 'sk' argument,
per discussion in [12] and [13].
* Bump ABI version to 9 (pessimistically assuming that we won't make it for 7.0)
* Documentation fixes in header file and in Documentation/
* selftests: more test variants, now also parameterizing whether the server socket gets created within the Landlock domain or before that
* selftests: use EXPECT_EQ() for test cleanup
Changes in V3:
* LSM hook: rename it to security_unix_find() (Justin Suess) (resolving the previously open question about the LSM hook name) Related discussions:
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/CAHC9VhSRiHwLEWfFkQdPEwgB4AXKbXzw_+3u=9hPpvUTnu02Bg@mail.gmail.com/
* Reunite the three UNIX resolving access rights back into one (resolving the previously open question about the access right structuring) Related discussion:
https://lore.kernel.org/all/[email protected]/)
* Sample tool: Add new UNIX lookup access rights to ACCESS_FILE
Changes in V2:
* Send Justin Suess's LSM hook patch together with the Landlock implementation
* LSM hook: Pass type and flags parameters to the hook, to make the access right more generally usable across LSMs, per suggestion from Paul Moore (Implemented by Justin)
* Split the access right into the three types of UNIX domain sockets:
SOCK_STREAM, SOCK_DGRAM and SOCK_SEQPACKET.
* selftests: More exhaustive tests.
* Removed a minor commit from V1 which adds a missing close(fd) to a test (it is already in the mic-next branch)
Günther Noack (11):
landlock: Use mem_is_zero() in is_layer_masks_allowed()
landlock: Replace union access_masks_all with helper functions
landlock: Control pathname UNIX domain socket resolution by path
landlock: Clarify BUILD_BUG_ON check in scoping logic samples/landlock: Add support for named UNIX domain socket
restrictions
selftests/landlock: Replace access_fs_16 with ACCESS_ALL in fs_test selftests/landlock: Test LANDLOCK_ACCESS_FS_RESOLVE_UNIX selftests/landlock: Audit test for LANDLOCK_ACCESS_FS_RESOLVE_UNIX selftests/landlock: Check that coredump sockets stay unrestricted selftests/landlock: fs_test: Simplify ruleset creation and enforcement
landlock: Document FS access right for pathname UNIX sockets
Justin Suess (1):
lsm: Add LSM hook security_unix_find
Documentation/security/landlock.rst | 42 +- Documentation/userspace-api/landlock.rst | 16 +- include/linux/lsm_hook_defs.h | 5 + include/linux/security.h | 11 + include/uapi/linux/landlock.h | 21 + net/unix/af_unix.c | 10 +- samples/landlock/sandboxer.c | 12 +- security/landlock/access.h | 23 +- security/landlock/audit.c | 1 + security/landlock/cred.h | 10 +- security/landlock/fs.c | 135 +- security/landlock/limits.h | 2 +- security/landlock/ruleset.h | 13 +- security/landlock/syscalls.c | 2 +- security/landlock/task.c | 9 +- security/security.c | 20 + tools/testing/selftests/landlock/base_test.c | 2 +- tools/testing/selftests/landlock/fs_test.c | 1345 ++++++++++-------- 18 files changed, 1025 insertions(+), 654 deletions(-)
Range-diff against v7:
-: ------------ > 1: dec515e7d7bf lsm: Add LSM hook security_unix_find -: ------------ > 2: aaa334660a52 landlock: Use mem_is_zero() in is_layer_masks_allowed() -: ------------ > 3: 79dd5036482d landlock: Replace union access_masks_all with helper functions 1: 4d455134c5d9 ! 4: b39e336967de landlock: Control pathname UNIX domain socket resolution by path
@@ Commit message
## Documentation/security/landlock.rst ##
+@@ Documentation/security/landlock.rst: Landlock LSM: kernel documentation
+ ==================================
+
+ :Author: Mickaël Salaün
+-:Date: September 2025
++:Date: March 2026
+
+ 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,
@@ Documentation/security/landlock.rst: 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).
@@ Documentation/security/landlock.rst: this is required to keep access controls co
=====
+ ## Documentation/userspace-api/landlock.rst ##
+@@ Documentation/userspace-api/landlock.rst: Landlock: unprivileged access control
+ =====================================
+
+ :Author: Mickaël Salaün
+-:Date: January 2026
++:Date: March 2026
+
+ 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 +
## include/uapi/linux/landlock.h ##
@@ include/uapi/linux/landlock.h: struct landlock_net_port_attr {
*
3: 00c90045f470 = 5: 3c8678a4a482 landlock: Clarify BUILD_BUG_ON check in scoping logic 2: 70354dfcefcb = 6: d0c8731fb751 samples/landlock: Add support for named UNIX domain socket restrictions 4: b8e6477b2f9a = 7: 49fa86251f1b selftests/landlock: Replace access_fs_16 with ACCESS_ALL in fs_test 5: 66554c5b5b9c = 8: 69b368c23083 selftests/landlock: Test LANDLOCK_ACCESS_FS_RESOLVE_UNIX 6: fab3d3d71215 = 9: 7306a1da2947 selftests/landlock: Audit test for LANDLOCK_ACCESS_FS_RESOLVE_UNIX 7: 4e8a07344278 = 10: e63e98c44e58 selftests/landlock: Check that coredump sockets stay unrestricted 8: fcaef2882dbd = 11: 497bcce69260 selftests/landlock: fs_test: Simplify ruleset creation and enforcement 9: 362a0e8f84a0 = 12: c4716fc8e131 landlock: Document FS access right for pathname UNIX sockets
Signature · 1 line
2.53.0
PATCH v8 1/12 lsm: Add LSM hook security_unix_find
From: Justin Suess <[email protected]>
Add an LSM hook security_unix_find.
This hook is called to check the path of a named UNIX socket before a connection is initiated. The peer socket may be inspected as well.
Why existing hooks are unsuitable:
Existing socket hooks, security_unix_stream_connect(), security_unix_may_send(), and security_socket_connect() don't provide TOCTOU-free / namespace independent access to the paths of sockets.
(1) We cannot resolve the path from the struct sockaddr in existing hooks. This requires another path lookup. A change in the path between the two lookups will cause a TOCTOU bug.
(2) We cannot use the struct path from the listening socket, because it may be bound to a path in a different namespace than the caller, resulting in a path that cannot be referenced at policy creation time.
Consumers of the hook wishing to reference @other are responsible for acquiring the unix_state_lock and checking for the SOCK_DEAD flag therein, ensuring the socket hasn't died since lookup.
--- include/linux/lsm_hook_defs.h | 5 +++++ include/linux/security.h | 11 +++++++++++ net/unix/af_unix.c | 10 +++++++--- security/security.c | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-)
include/linux/lsm_hook_defs.h +5 -0
@@ -317,6 +317,11 @@ LSM_HOOK(int, 0, post_notification, const struct cred *w_cred, LSM_HOOK(int, 0, watch_key, struct key *key)#endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */ +#if defined(CONFIG_SECURITY_NETWORK ) && defined(CONFIG_SECURITY_PATH ) +LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other, + int flags) +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */ +#ifdef CONFIG_SECURITY_NETWORK LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other, struct sock *newsk)
include/linux/security.h +11 -0
@@ -1931,6 +1931,17 @@ static inline int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk) }#endif /* CONFIG_SECURITY_NETWORK */ +#if defined(CONFIG_SECURITY_NETWORK ) && defined(CONFIG_SECURITY_PATH ) + +int security_unix_find (const struct path *path,struct sock *other,int flags); + +#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */ +static inline int security_unix_find (const struct path *path,struct sock *other,int flags) +{ +return 0 ; +} +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */ +#ifdef CONFIG_SECURITY_INFINIBAND int security_ib_pkey_access (void *sec,u64 subnet_prefix,u16 pkey);int security_ib_endport_manage_subnet (void *sec,const char *name,u8 port_num);
net/unix/af_unix.c +7 -3
@@ -1231,11 +1231,15 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len, goto path_put; err = -EPROTOTYPE ; -if (sk->sk_type == type) -touch_atime (&path); -else +if (sk->sk_type != type) goto sock_put; + err =security_unix_find (&path, sk, flags); +if (err) + goto sock_put; + +touch_atime (&path); +path_put (&path);return sk;
security/security.c +20 -0
@@ -4731,6 +4731,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)#endif /* CONFIG_SECURITY_NETWORK */ +#if defined(CONFIG_SECURITY_NETWORK ) && defined(CONFIG_SECURITY_PATH ) +/** +* security_unix_find() - Check if a named AF_UNIX socket can connect +* @path: path of the socket being connected to +* @other: peer sock +* @flags: flags associated with the socket +* +* This hook is called to check permissions before connecting to a named +* AF_UNIX socket. The caller does not hold any locks on @other. +* +* Return: Returns 0 if permission is granted. +*/ +int security_unix_find (const struct path *path,struct sock *other,int flags) +{ +return call_int_hook (unix_find, path, other, flags); +} +EXPORT_SYMBOL (security_unix_find); + +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */ +#ifdef CONFIG_SECURITY_INFINIBAND /** * security_ib_pkey_access() - Check if access to an IB pkey is allowed
Signature · 1 line
2.53.0
Paul Moore
maintainerRe: [PATCH v8 01/12] lsm: Add LSM hook security_unix_find
Quoted 32 lines · Günther Noack
From: Justin Suess <[email protected]>
Add an LSM hook
security_unix_find.This hook is called to check the path of a named UNIX socket before a connection is initiated. The peer socket may be inspected as well.
Why existing hooks are unsuitable:
Existing socket hooks,
security_unix_stream_connect(),security_unix_may_send(), andsecurity_socket_connect()don't provide TOCTOU-free / namespace independent access to the paths of sockets.(1) We cannot resolve the path from the struct sockaddr in existing hooks. This requires another path lookup. A change in the path between the two lookups will cause a TOCTOU bug.
(2) We cannot use the struct path from the listening socket, because it may be bound to a path in a different namespace than the caller, resulting in a path that cannot be referenced at policy creation time.
Consumers of the hook wishing to reference @other are responsible for acquiring the
unix_state_lockand checking for theSOCK_DEADflag therein, ensuring the socket hasn't died since lookup.Cc Günther Noack <[email protected]>Cc Tingmao Wang <[email protected]>Cc Mickaël Salaün <[email protected]>Cc Paul Moore <[email protected]>Signed-off-by Justin Suess <[email protected]>Signed-off-by Günther Noack <[email protected]>
Quoted 6 lines
--- include/linux/lsm_hook_defs.h | 5 +++++ include/linux/security.h | 11 +++++++++++ net/unix/af_unix.c | 10 +++++++--- security/security.c | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-)
This patch doesn't look like it changed significantly in this revision, is there a reason you dropped the tags from Georgia and I?
Signature · 1 line
paul-moore.com
Mickaël Salaün
maintainerRe: [PATCH v8 01/12] lsm: Add LSM hook security_unix_find
Quoted 40 lines · Paul Moore
Quoted 32 lines · Günther Noack
From: Justin Suess <[email protected]>
Add an LSM hook
security_unix_find.This hook is called to check the path of a named UNIX socket before a connection is initiated. The peer socket may be inspected as well.
Why existing hooks are unsuitable:
Existing socket hooks,
security_unix_stream_connect(),security_unix_may_send(), andsecurity_socket_connect()don't provide TOCTOU-free / namespace independent access to the paths of sockets.(1) We cannot resolve the path from the struct sockaddr in existing hooks. This requires another path lookup. A change in the path between the two lookups will cause a TOCTOU bug.
(2) We cannot use the struct path from the listening socket, because it may be bound to a path in a different namespace than the caller, resulting in a path that cannot be referenced at policy creation time.
Consumers of the hook wishing to reference @other are responsible for acquiring the
unix_state_lockand checking for theSOCK_DEADflag therein, ensuring the socket hasn't died since lookup.Cc Günther Noack <[email protected]>Cc Tingmao Wang <[email protected]>Cc Mickaël Salaün <[email protected]>Cc Paul Moore <[email protected]>Signed-off-by Justin Suess <[email protected]>Signed-off-by Günther Noack <[email protected]>Quoted 6 lines
--- include/linux/lsm_hook_defs.h | 5 +++++ include/linux/security.h | 11 +++++++++++ net/unix/af_unix.c | 10 +++++++--- security/security.c | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-)
Quoted 2 lines
This patch doesn't look like it changed significantly in this revision, is there a reason you dropped the tags from Georgia and I?
You'r right, the patch didn't change at all. I added Georgia's tag in my -next branch for the previous version, I guess Günther forgot to add it for this version, but I updated my branch with the same tag, so it's still there. Thank you both BTW!
I just included a one-line fix because of the m68k warning, we'll see if it works as expected, and we should be good to go. It would be nice to have John's feedback though.
Re: [PATCH v8 01/12] lsm: Add LSM hook security_unix_find
Quoted 15 lines · Mickaël Salaün
Quoted 14 lines · Paul Moore
Quoted 12 lines · Günther Noack
Cc Günther Noack <[email protected]>Cc Tingmao Wang <[email protected]>Cc Mickaël Salaün <[email protected]>Cc Paul Moore <[email protected]>Signed-off-by Justin Suess <[email protected]>Signed-off-by Günther Noack <[email protected]>--- include/linux/lsm_hook_defs.h | 5 +++++ include/linux/security.h | 11 +++++++++++ net/unix/af_unix.c | 10 +++++++--- security/security.c | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-)
Quoted 7 lines
Quoted 2 lines
This patch doesn't look like it changed significantly in this revision, is there a reason you dropped the tags from Georgia and I?
You'r right, the patch didn't change at all. I added Georgia's tag in my -next branch for the previous version, I guess Günther forgot to add it for this version, but I updated my branch with the same tag, so it's still there. Thank you both BTW!
I only missed to add them by accident, thanks for adding it back, Mickaël, and Paul for spotting it!
Quoted 3 lines
I just included a one-line fix because of the m68k warning, we'll see if it works as expected, and we should be good to go. It would be nice to have John's feedback though.
Agreed. John, I would also appreciate if you could find the time to have a look.
–Günther
PATCH v8 2/12 landlock: Use mem_is_zero() in is_layer_masks_allowed()
In reply to Günther NoackThis is equivalent, but expresses the intent a bit clearer.
--- security/landlock/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
security/landlock/fs.c +1 -1
@@ -564,7 +564,7 @@ static void test_no_more_access(struct kunit *const test)static bool is_layer_masks_allowed (const struct layer_access_masks *masks) { -return !memchr_inv (&masks->access,0 ,sizeof (masks->access)); +return mem_is_zero (&masks->access,sizeof (masks->access)); }/*
Signature · 1 line
2.53.0
PATCH v8 3/12 landlock: Replace union access_masks_all with helper functions
In reply to Günther Noack* Stop using a union for access_masks_all.
* Expose helper functions for intersection checks and union operations.
The memory layout of bitfields is only loosely defined by the C standard, so our static assertion that expects a fixed size was brittle, and it broke on some compilers when we attempted to add a 17th file system access right.
--- security/landlock/access.h | 21 ++++++++++++++------- security/landlock/cred.h | 10 ++-------- security/landlock/ruleset.h | 13 ++++--------- 3 files changed, 20 insertions(+), 24 deletions(-)
security/landlock/access.h +14 -7
@@ -52,14 +52,21 @@ struct access_masks { access_mask_t scope : LANDLOCK_NUM_SCOPE; }; -union access_masks_all { -struct access_masks masks; -u32 all; -}; +/* Checks whether two access masks have any common bit set. */ +static inline bool access_masks_intersect (const struct access_masks a, +const struct access_masks b) +{ +return (a.fs & b.fs) || (a.net & b.net) || (a.scope & b.scope); +} -/* Makes sure all fields are covered. */ -static_assert (sizeof (typeof_member (union access_masks_all ,masks )) == -sizeof (typeof_member (union access_masks_all ,all ))); +/* ORs the bits of @src into @dst. */ +static inline void access_masks_merge (struct access_masks *dst, +const struct access_masks src) +{ + dst->fs |= src.fs; + dst->net |= src.net; + dst->scope |= src.scope; +}/** * struct layer_access_masks - A boolean matrix of layers and access rights
security/landlock/cred.h +2 -8
@@ -123,9 +123,6 @@ 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;
@@ -138,11 +135,8 @@ landlock_get_applicable_subject(const struct cred *const cred,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 (access_masks_intersect (domain->access_masks[layer_level], + masks)) {if (handle_layer) *handle_layer = layer_level;
security/landlock/ruleset.h +4 -9
@@ -229,18 +229,13 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)static inline struct access_masks landlock_union_access_masks (const struct landlock_ruleset *const domain) { -union access_masks_all matches = {}; +struct access_masks 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], - }; +for (layer_level =0 ; layer_level < domain->num_layers; layer_level++) +access_masks_merge (&matches, domain->access_masks[layer_level]); - matches.all |= layer.all; - } - -return matches.masks; +return matches; } static inline void
Signature · 1 line
2.53.0
Mickaël Salaün
maintainerRe: [PATCH v8 03/12] landlock: Replace union access_masks_all with helper functions
Quoted 8 lines · Günther Noack
* Stop using a union for
access_masks_all.
* Expose helper functions for intersection checks and union operations.The memory layout of bitfields is only loosely defined by the C standard, so our static assertion that expects a fixed size was brittle, and it broke on some compilers when we attempted to add a 17th file system access right.
Quoted 8 lines
Reported-by kernel test robot <[email protected]>Signed-off-by Günther Noack <[email protected]>--- security/landlock/access.h | 21 ++++++++++++++------- security/landlock/cred.h | 10 ++-------- security/landlock/ruleset.h | 13 ++++--------- 3 files changed, 20 insertions(+), 24 deletions(-)
I'd prefer this approach:
security/landlock/access.h +2 -2
@@ -50,7 +50,7 @@ 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;union access_masks_all {struct access_masks masks;
@@ -58,7 +58,7 @@ union access_masks_all { };/* Makes sure all fields are covered. */ -static_assert (sizeof (typeof_member (union access_masks_all ,masks )) == +static_assert (sizeof (typeof_member (union access_masks_all ,masks )) <=sizeof (typeof_member (union access_masks_all ,all )));/**
This keep the check and make sure new field will not introduce issues, but more importantly it save memory, which was one of the goal of struct access_masks.
If that's good with you I'll replace your patch and squash this packed annotation with the following patch.
Quoted 93 lines
security/landlock/access.h +14
-7@@ -52,14 +52,21 @@ struct access_masks { access_mask_t scope : LANDLOCK_NUM_SCOPE; }; -union access_masks_all { -struct access_masks masks; -u32 all; -}; +/* Checks whether two access masks have any common bit set. */ +static inline bool access_masks_intersect (const struct access_masks a, +const struct access_masks b) +{ +return (a.fs & b.fs) || (a.net & b.net) || (a.scope & b.scope); +} -/* Makes sure all fields are covered. */ -static_assert (sizeof (typeof_member (union access_masks_all ,masks )) == -sizeof (typeof_member (union access_masks_all ,all ))); +/* ORs the bits of @src into @dst. */ +static inline void access_masks_merge (struct access_masks *dst, +const struct access_masks src) +{ + dst->fs |= src.fs; + dst->net |= src.net; + dst->scope |= src.scope; +}/** * struct layer_access_masks - A boolean matrix of layers and access rights security/landlock/cred.h +2
-8@@ -123,9 +123,6 @@ 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;@@ -138,11 +135,8 @@ landlock_get_applicable_subject(const struct cred *const cred,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 (access_masks_intersect (domain->access_masks[layer_level], + masks)) {if (handle_layer) *handle_layer = layer_level;security/landlock/ruleset.h +4
-9@@ -229,18 +229,13 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)static inline struct access_masks landlock_union_access_masks (const struct landlock_ruleset *const domain) { -union access_masks_all matches = {}; +struct access_masks 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], - }; +for (layer_level =0 ; layer_level < domain->num_layers; layer_level++) +access_masks_merge (&matches, domain->access_masks[layer_level]); - matches.all |= layer.all; - } - -return matches.masks; +return matches; } static inline voidSignature · 1 line
2.53.0
Mickaël Salaün
maintainerRe: [PATCH v8 03/12] landlock: Replace union access_masks_all with helper functions
Quoted 25 lines · Mickaël Salaün
Quoted 8 lines · Günther Noack
* Stop using a union for
access_masks_all.
* Expose helper functions for intersection checks and union operations.The memory layout of bitfields is only loosely defined by the C standard, so our static assertion that expects a fixed size was brittle, and it broke on some compilers when we attempted to add a 17th file system access right.
Quoted 8 lines
Reported-by kernel test robot <[email protected]>Signed-off-by Günther Noack <[email protected]>--- security/landlock/access.h | 21 ++++++++++++++------- security/landlock/cred.h | 10 ++-------- security/landlock/ruleset.h | 13 ++++--------- 3 files changed, 20 insertions(+), 24 deletions(-)I'd prefer this approach:
security/landlock/access.h +0
-0
security/landlock/access.h struct access_masks { · line 53
Quoted 5 lines
security/landlock/access.h +1
-1@@ -50,7 +50,7 @@ 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;
Actually, we can just use '__packed __aligned(sizeof(u32))' and avoid the static_assert change. That would have no impact on x86, but pack it on m68k.
Quoted 114 lines
security/landlock/access.h +1
-1union access_masks_all {struct access_masks masks;@@ -58,7 +58,7 @@ union access_masks_all { };/* Makes sure all fields are covered. */ -static_assert (sizeof (typeof_member (union access_masks_all ,masks )) == +static_assert (sizeof (typeof_member (union access_masks_all ,masks )) <=sizeof (typeof_member (union access_masks_all ,all )));/** This keep the check and make sure new field will not introduce issues, but more importantly it save memory, which was one of the goal of struct
access_masks.If that's good with you I'll replace your patch and squash this packed annotation with the following patch.
Quoted 93 lines
security/landlock/access.h +14
-7@@ -52,14 +52,21 @@ struct access_masks { access_mask_t scope : LANDLOCK_NUM_SCOPE; }; -union access_masks_all { -struct access_masks masks; -u32 all; -}; +/* Checks whether two access masks have any common bit set. */ +static inline bool access_masks_intersect (const struct access_masks a, +const struct access_masks b) +{ +return (a.fs & b.fs) || (a.net & b.net) || (a.scope & b.scope); +} -/* Makes sure all fields are covered. */ -static_assert (sizeof (typeof_member (union access_masks_all ,masks )) == -sizeof (typeof_member (union access_masks_all ,all ))); +/* ORs the bits of @src into @dst. */ +static inline void access_masks_merge (struct access_masks *dst, +const struct access_masks src) +{ + dst->fs |= src.fs; + dst->net |= src.net; + dst->scope |= src.scope; +}/** * struct layer_access_masks - A boolean matrix of layers and access rights security/landlock/cred.h +2
-8@@ -123,9 +123,6 @@ 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;@@ -138,11 +135,8 @@ landlock_get_applicable_subject(const struct cred *const cred,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 (access_masks_intersect (domain->access_masks[layer_level], + masks)) {if (handle_layer) *handle_layer = layer_level;security/landlock/ruleset.h +4
-9@@ -229,18 +229,13 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)static inline struct access_masks landlock_union_access_masks (const struct landlock_ruleset *const domain) { -union access_masks_all matches = {}; +struct access_masks 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], - }; +for (layer_level =0 ; layer_level < domain->num_layers; layer_level++) +access_masks_merge (&matches, domain->access_masks[layer_level]); - matches.all |= layer.all; - } - -return matches.masks; +return matches; } static inline voidSignature · 1 line
2.53.0
Re: [PATCH v8 03/12] landlock: Replace union access_masks_all with helper functions
Quoted 21 lines · Mickaël Salaün
Quoted 20 lines · Mickaël Salaün
Quoted 8 lines · Günther Noack
* Stop using a union for
access_masks_all.
* Expose helper functions for intersection checks and union operations.The memory layout of bitfields is only loosely defined by the C standard, so our static assertion that expects a fixed size was brittle, and it broke on some compilers when we attempted to add a 17th file system access right.
Quoted 8 lines
Reported-by kernel test robot <[email protected]>Signed-off-by Günther Noack <[email protected]>--- security/landlock/access.h | 21 ++++++++++++++------- security/landlock/cred.h | 10 ++-------- security/landlock/ruleset.h | 13 ++++--------- 3 files changed, 20 insertions(+), 24 deletions(-)I'd prefer this approach:
Quoted 14 lines
security/landlock/access.h struct access_masks { · line 53
Quoted 10 lines
security/landlock/access.h +1
-1@@ -50,7 +50,7 @@ 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;Actually, we can just use '
__packed__aligned(sizeof(u32))' and avoid thestatic_assertchange. That would have no impact on x86, but pack it on m68k.
Thanks, good catch (and thanks for pushing it to mic-next). Fingers crossed that this works on m68k.
–Günther
Mickaël Salaün
maintainerRe: [PATCH v8 03/12] landlock: Replace union access_masks_all with helper functions
Quoted 37 lines · Günther Noack
Quoted 21 lines · Mickaël Salaün
Quoted 20 lines · Mickaël Salaün
Quoted 8 lines · Günther Noack
* Stop using a union for
access_masks_all.
* Expose helper functions for intersection checks and union operations.The memory layout of bitfields is only loosely defined by the C standard, so our static assertion that expects a fixed size was brittle, and it broke on some compilers when we attempted to add a 17th file system access right.
Quoted 8 lines
Reported-by kernel test robot <[email protected]>Signed-off-by Günther Noack <[email protected]>--- security/landlock/access.h | 21 ++++++++++++++------- security/landlock/cred.h | 10 ++-------- security/landlock/ruleset.h | 13 ++++--------- 3 files changed, 20 insertions(+), 24 deletions(-)I'd prefer this approach:
Quoted 14 lines
security/landlock/access.h struct access_masks { · line 53
Quoted 10 lines
security/landlock/access.h +1
-1@@ -50,7 +50,7 @@ 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;Actually, we can just use '
__packed__aligned(sizeof(u32))' and avoid thestatic_assertchange. That would have no impact on x86, but pack it on m68k.
Quoted 2 lines
Thanks, good catch (and thanks for pushing it to mic-next). Fingers crossed that this works on m68k.
So, this works! I did some experiments with m68k and this architecture is very special: it packs bitfields at byte granularity, not at storage-unit granularity, except when the size of a bitfield is a multiple of 8, in which case it aligns on this size.
I also look at the past versions of Landlock (in the stable branches), and they are good because struct access_masks (and the related assert) was introduced in v6.11 and fs was exactly 16 bits, which makes m68k aligns on 2 bytes and then the size of the struct was 4 bytes. Switching fs to 17 bits removes this optimization (I guess) and pack (back) to 3 bytes, so recording more bits can take less space!
That's why we don't need a standalone fix to backport...
PATCH v8 4/12 landlock: Control pathname UNIX domain socket resolution by path
In reply to Günther Noack* 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.
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 [2].
This feature was created with substantial discussion and input from Justin Suess, Tingmao Wang and Mickaël Salaün.
Link[1]: https://github.com/landlock-lsm/linux/issues/36
Link[2]: https://lore.kernel.org/all/[email protected]/
--- Documentation/security/landlock.rst | 42 +++++- Documentation/userspace-api/landlock.rst | 2 +- include/uapi/linux/landlock.h | 21 +++ security/landlock/access.h | 2 +- security/landlock/audit.c | 1 + security/landlock/fs.c | 130 ++++++++++++++++++- security/landlock/limits.h | 2 +- security/landlock/syscalls.c | 2 +- tools/testing/selftests/landlock/base_test.c | 2 +- tools/testing/selftests/landlock/fs_test.c | 5 +- 10 files changed, 200 insertions(+), 9 deletions(-)
Documentation/security/landlock.rst +41 -1
@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation ================================== :Author: Mickaël Salaün -:Date: September 2025 +:Date: March 2026 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,
@@ -89,6 +89,46 @@ 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). +.. _scoped-flags-interaction: + +Interaction between scoped flags and other access rights +-------------------------------------------------------- + +The ``scoped`` flags in ``struct landlock_ruleset_attr`` restrict the +use of *outgoing* IPC from the created Landlock domain, while they +permit reaching out to IPC endpoints *within* the created Landlock +domain. + +In the future, scoped flags *may* interact with other access rights, +e.g. so that abstract UNIX sockets can be allow-listed by name, or so +that signals can be allow-listed by signal number or target process. + +When introducing ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``, we defined it to +implicitly have the same scoping semantics as a +``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` flag would have: connecting to +UNIX sockets within the same domain (where +``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` is used) is unconditionally +allowed. + +The reasoning is: + +* Like other IPC mechanisms, connecting to named UNIX sockets in the + same domain should be expected and harmless. (If needed, users can + further refine their Landlock policies with nested domains or by + restricting ``LANDLOCK_ACCESS_FS_MAKE_SOCK``.) +* We reserve the option to still introduce + ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the future. (This would + be useful if we wanted to have a Landlock rule to permit IPC access + to other Landlock domains.) +* But we can postpone the point in time when users have to deal with + two interacting flags visible in the userspace API. (In particular, + it is possible that it won't be needed in practice, in which case we + can avoid the second flag altogether.) +* If we *do* introduce ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the + future, setting this scoped flag in a ruleset does *not reduce* the + restrictions, because access within the same scope is already + allowed based on ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``. + Tests =====
Documentation/userspace-api/landlock.rst +1 -1
@@ -8,7 +8,7 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: January 2026 +:Date: March 2026 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
include/uapi/linux/landlock.h +21 -0
@@ -248,6 +248,26 @@ struct landlock_net_port_attr {* * This access right is available since the fifth version of the Landlock * ABI. +* - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX: Look up pathname UNIX domain sockets +* (:manpage:`unix(7)`). On UNIX domain sockets, this restricts both calls to +* :manpage:`connect(2)` as well as calls to :manpage:`sendmsg(2)` with an +* explicit recipient address. +* +* This access right only applies to connections to UNIX server sockets which +* were created outside of the newly created Landlock domain (e.g. from within +* a parent domain or from an unrestricted process). Newly created UNIX +* servers within the same Landlock domain continue to be accessible. In this +* regard, %LANDLOCK_ACCESS_FS_RESOLVE_UNIX has the same semantics as the +* ``LANDLOCK_SCOPE_*`` flags. +* +* If a resolve attempt is denied, the operation returns an ``EACCES`` error, +* in line with other filesystem access rights (but different to denials for +* abstract UNIX domain sockets). +* +* This access right is available since the ninth version of the Landlock ABI. +* +* The rationale for this design is described in +* :ref:`Documentation/security/landlock.rst <scoped-flags-interaction>`. * * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as
@@ -333,6 +353,7 @@ struct landlock_net_port_attr {#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)#define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) +#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16)/* clang-format on */ /**
security/landlock/access.h +1 -1
@@ -34,7 +34,7 @@ LANDLOCK_ACCESS_FS_IOCTL_DEV)/* clang-format on */ -typedef u16 access_mask_t ; +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 );
security/landlock/audit.c +1 -0
@@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer", [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate", [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev", + [BIT_INDEX (LANDLOCK_ACCESS_FS_RESOLVE_UNIX )] ="fs.resolve_unix" , };static_assert (ARRAY_SIZE (fs_access_strings) ==LANDLOCK_NUM_ACCESS_FS );
security/landlock/fs.c +129 -1
160 lines not on this page · raw message
security/landlock/limits.h +1 -1
@@ -19,7 +19,7 @@#define LANDLOCK_MAX_NUM_LAYERS 16#define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
security/landlock/syscalls.c +1 -1
@@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = {* If the change involves a fix that requires userspace awareness, also update * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version =8 ; +const int landlock_abi_version =9 ;/** * sys_landlock_create_ruleset - Create a new ruleset
tools/testing/selftests/landlock/base_test.c +1 -1
@@ -76,7 +76,7 @@ TEST(abi_version)const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs =LANDLOCK_ACCESS_FS_READ_FILE , }; -ASSERT_EQ (8 ,landlock_create_ruleset (NULL ,0 , +ASSERT_EQ (9 ,landlock_create_ruleset (NULL ,0 ,LANDLOCK_CREATE_RULESET_VERSION )); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
tools/testing/selftests/landlock/fs_test.c +3 -2
@@ -575,9 +575,10 @@ TEST_F_FORK(layout1, inval) LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) -#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV +#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX#define ACCESS_ALL ( \ ACCESS_FILE | \
Signature · 1 line
2.53.0
Re: [PATCH v8 04/12] landlock: Control pathname UNIX domain socket resolution by path
Quoted 4 lines · Günther Noack
* Add a new access right
LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which controls the lookup operations for named UNIX domain sockets. The resolution happens duringconnect()andsendmsg()(depending on socket type).
…
Quoted 9 lines
Cc Tingmao Wang <[email protected]>Cc Justin Suess <[email protected]>Cc Mickaël Salaün <[email protected]>Cc Sebastian Andrzej Siewior <[email protected]>Cc Kuniyuki Iwashima <[email protected]>Suggested-by Jann Horn <[email protected]>Link[1]: https://github.com/landlock-lsm/linux/issues/36
Link[2]: https://lore.kernel.org/all/[email protected]/Signed-off-by Günther Noack <[email protected]>
The unix bits look okay to me,
Sebastian
Kuniyuki Iwashima
maintainerRe: [PATCH v8 04/12] landlock: Control pathname UNIX domain socket resolution by path
In reply to Günther NoackQuoted 318 lines · Günther Noack
* Add a new access right
LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which controls the lookup operations for named UNIX domain sockets. The resolution happens duringconnect()andsendmsg()(depending on socket type).
* Changeaccess_mask_tfrom u16 to u32 (see below)
* Hook into the path lookup inunix_find_bsd()inaf_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_BENEATHrule, *or*:
* The target socket was created in the same Landlock domain in whichLANDLOCK_ACCESS_FS_RESOLVE_UNIXwas restricted.In case of a denial,
connect()andsendmsg()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_ttype grows from u16 to u32 to make space for the new access right. This also doubles the size of structlayer_access_masksfrom 32 byte to 64 byte.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 [2].This feature was created with substantial discussion and input from Justin Suess, Tingmao Wang and Mickaël Salaün.
Cc Tingmao Wang <[email protected]>Cc Justin Suess <[email protected]>Cc Mickaël Salaün <[email protected]>Cc Sebastian Andrzej Siewior <[email protected]>Cc Kuniyuki Iwashima <[email protected]>Suggested-by Jann Horn <[email protected]>Link[1]: https://github.com/landlock-lsm/linux/issues/36
Link[2]: https://lore.kernel.org/all/[email protected]/Signed-off-by Günther Noack <[email protected]>--- Documentation/security/landlock.rst | 42 +++++- Documentation/userspace-api/landlock.rst | 2 +- include/uapi/linux/landlock.h | 21 +++ security/landlock/access.h | 2 +- security/landlock/audit.c | 1 + security/landlock/fs.c | 130 ++++++++++++++++++- security/landlock/limits.h | 2 +- security/landlock/syscalls.c | 2 +- tools/testing/selftests/landlock/base_test.c | 2 +- tools/testing/selftests/landlock/fs_test.c | 5 +- 10 files changed, 200 insertions(+), 9 deletions(-)Documentation/security/landlock.rst +41
-1@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation ================================== :Author: Mickaël Salaün -:Date: September 2025 +:Date: March 2026 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,@@ -89,6 +89,46 @@ 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). +.. _scoped-flags-interaction: + +Interaction between scoped flags and other access rights +-------------------------------------------------------- + +The ``scoped`` flags in ``struct landlock_ruleset_attr`` restrict the +use of *outgoing* IPC from the created Landlock domain, while they +permit reaching out to IPC endpoints *within* the created Landlock +domain. + +In the future, scoped flags *may* interact with other access rights, +e.g. so that abstract UNIX sockets can be allow-listed by name, or so +that signals can be allow-listed by signal number or target process. + +When introducing ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``, we defined it to +implicitly have the same scoping semantics as a +``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` flag would have: connecting to +UNIX sockets within the same domain (where +``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` is used) is unconditionally +allowed. + +The reasoning is: + +* Like other IPC mechanisms, connecting to named UNIX sockets in the + same domain should be expected and harmless. (If needed, users can + further refine their Landlock policies with nested domains or by + restricting ``LANDLOCK_ACCESS_FS_MAKE_SOCK``.) +* We reserve the option to still introduce + ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the future. (This would + be useful if we wanted to have a Landlock rule to permit IPC access + to other Landlock domains.) +* But we can postpone the point in time when users have to deal with + two interacting flags visible in the userspace API. (In particular, + it is possible that it won't be needed in practice, in which case we + can avoid the second flag altogether.) +* If we *do* introduce ``LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET`` in the + future, setting this scoped flag in a ruleset does *not reduce* the + restrictions, because access within the same scope is already + allowed based on ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``. + Tests =====Documentation/userspace-api/landlock.rst +1
-1@@ -8,7 +8,7 @@ Landlock: unprivileged access control ===================================== :Author: Mickaël Salaün -:Date: January 2026 +:Date: March 2026 The goal of Landlock is to enable restriction of ambient rights (e.g. global filesystem or network access) for a set of processes. Because Landlockinclude/uapi/linux/landlock.h +21
-0@@ -248,6 +248,26 @@ struct landlock_net_port_attr {* * This access right is available since the fifth version of the Landlock * ABI. +* - %LANDLOCK_ACCESS_FS_RESOLVE_UNIX: Look up pathname UNIX domain sockets +* (:manpage:`unix(7)`). On UNIX domain sockets, this restricts both calls to +* :manpage:`connect(2)` as well as calls to :manpage:`sendmsg(2)` with an +* explicit recipient address. +* +* This access right only applies to connections to UNIX server sockets which +* were created outside of the newly created Landlock domain (e.g. from within +* a parent domain or from an unrestricted process). Newly created UNIX +* servers within the same Landlock domain continue to be accessible. In this +* regard, %LANDLOCK_ACCESS_FS_RESOLVE_UNIX has the same semantics as the +* ``LANDLOCK_SCOPE_*`` flags. +* +* If a resolve attempt is denied, the operation returns an ``EACCES`` error, +* in line with other filesystem access rights (but different to denials for +* abstract UNIX domain sockets). +* +* This access right is available since the ninth version of the Landlock ABI. +* +* The rationale for this design is described in +* :ref:`Documentation/security/landlock.rst <scoped-flags-interaction>`. * * Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used * with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as @@ -333,6 +353,7 @@ struct landlock_net_port_attr {#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)#define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15) +#define LANDLOCK_ACCESS_FS_RESOLVE_UNIX (1ULL << 16)/* clang-format on */ /** security/landlock/access.h +1
-1@@ -34,7 +34,7 @@ LANDLOCK_ACCESS_FS_IOCTL_DEV)/* clang-format on */ -typedef u16 access_mask_t ; +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 );security/landlock/audit.c +1
-0@@ -37,6 +37,7 @@ static const char *const fs_access_strings[] = { [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = "fs.refer", [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = "fs.truncate", [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = "fs.ioctl_dev", + [BIT_INDEX (LANDLOCK_ACCESS_FS_RESOLVE_UNIX )] ="fs.resolve_unix" , };static_assert (ARRAY_SIZE (fs_access_strings) ==LANDLOCK_NUM_ACCESS_FS );security/landlock/fs.c +93
-1@@ -27,6 +27,7 @@#include <linux/lsm_hooks.h> #include <linux/mount.h> #include <linux/namei.h> +#include <linux/net.h> #include <linux/path.h> #include <linux/pid.h> #include <linux/rcupdate.h> @@ -36,6 +37,7 @@#include <linux/types.h> #include <linux/wait_bit.h> #include <linux/workqueue.h> +#include <net/af_unix.h> #include <uapi/linux/fiemap.h> #include <uapi/linux/landlock.h> @@ -314,7 +316,8 @@ static struct landlock_object *get_inode_object(struct inode *const inode)LANDLOCK_ACCESS_FS_WRITE_FILE | \LANDLOCK_ACCESS_FS_READ_FILE | \LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) +LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)/* clang-format on */ /* @@ -1557,6 +1560,130 @@ static int hook_path_truncate(const struct path *const path)return current_check_access_path (path,LANDLOCK_ACCESS_FS_TRUNCATE ); } +/** +* unmask_scoped_access - Remove access right bits in @masks in all layers +* where @client and @server have the same domain +* +* This does the same as domain_is_scoped(), but unmasks bits in @masks. +* It can not return early as domain_is_scoped() does. +* +* A scoped access for a given access right bit is allowed iff, for all layer +* depths where the access bit is set, the client and server domain are the +* same. This function clears the access rights @access in @masks at all layer +* depths where the client and server domain are the same, so that, when they +* are all cleared, the access is allowed. +* +* @client: Client domain +* @server: Server domain +* @masks: Layer access masks to unmask +* @access: Access bits that control scoping +*/ +static void unmask_scoped_access (const struct landlock_ruleset *const client, +const struct landlock_ruleset *const server, +struct layer_access_masks *const masks, +const access_mask_t access) +{ +int client_layer, server_layer; +const struct landlock_hierarchy *client_walker, *server_walker; + +/* This should not happen. */ +if (WARN_ON_ONCE (!client)) +return ; + +/* Server has no Landlock domain; nothing to clear. */ +if (!server) +return ; + +/* +* client_layer must be a signed integer with greater capacity +* than client->num_layers to ensure the following loop stops. +*/ +BUILD_BUG_ON (sizeof (client_layer) >sizeof (client->num_layers)); + + client_layer = client->num_layers -1 ; + client_walker = client->hierarchy; + server_layer = server->num_layers -1 ; + server_walker = server->hierarchy; + +/* +* Clears the access bits at all layers where the client domain is the +* same as the server domain. We start the walk at min(client_layer, +* server_layer). The layer bits until there can not be cleared because +* either the client or the server domain is missing. +*/ +for (; client_layer > server_layer; client_layer--) + client_walker = client_walker->parent; + +for (; server_layer > client_layer; server_layer--) + server_walker = server_walker->parent; + +for (; client_layer >=0 ; client_layer--) { +if (masks->access[client_layer] & access && + client_walker == server_walker) + masks->access[client_layer] &= ~access; + + client_walker = client_walker->parent; + server_walker = server_walker->parent; + } +} + +static int hook_unix_find (const struct path *const path,struct sock *other, +int flags) +{ +const struct landlock_ruleset *dom_other; +const struct landlock_cred_security *subject; +struct layer_access_masks layer_masks; +struct landlock_request request = {}; +static const struct access_masks fs_resolve_unix = { + .fs =LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + }; + +/* Lookup for the purpose of saving coredumps is OK. */ +if (unlikely (flags &SOCK_COREDUMP )) +return 0 ; + + subject =landlock_get_applicable_subject (current_cred (), + fs_resolve_unix,NULL ); + +if (!subject) +return 0 ; + +/*
security/landlock/fs.c static int hook_path_truncate(const struct path *const path) · line 1661
Quoted 10 lines
security/landlock/fs.c +10
-0+* Ignoring return value: that the domains apply was already checked in +* landlock_get_applicable_subject() above. +*/ +landlock_init_layer_masks (subject->domain, fs_resolve_unix.fs, + &layer_masks,LANDLOCK_KEY_INODE ); + +/* Checks the layers in which we are connecting within the same domain. */ +unix_state_lock (other); +if (unlikely (sock_flag (other,SOCK_DEAD ) || !other->sk_socket || + !other->sk_socket->file)) {
When will the latter two condition be true when !SOCK_DEAD ?
unix_find_bsd() should not find embryo sockets.
Quoted 95 lines
security/landlock/fs.c +26
-0+unix_state_unlock (other); +/* +* We rely on the caller to catch the (non-reversible) SOCK_DEAD +* condition and retry the lookup. If we returned an error +* here, the lookup would not get retried. +*/ +return 0 ; + } + dom_other =landlock_cred (other->sk_socket->file->f_cred)->domain; + +/* Access to the same (or a lower) domain is always allowed. */ +unmask_scoped_access (subject->domain, dom_other, &layer_masks, + fs_resolve_unix.fs); +unix_state_unlock (other); + +/* Checks the connections to allow-listed paths. */ +if (is_access_to_paths_allowed (subject->domain, path, + fs_resolve_unix.fs, &layer_masks, + &request,NULL ,0 ,NULL ,NULL ,NULL )) +return 0 ; + +landlock_log_denial (subject, &request); +return -EACCES ; +} +/* File hooks */ /** @@ -1834,6 +1961,7 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {LSM_HOOK_INIT (path_unlink, hook_path_unlink),LSM_HOOK_INIT (path_rmdir, hook_path_rmdir),LSM_HOOK_INIT (path_truncate, hook_path_truncate), +LSM_HOOK_INIT (unix_find, hook_unix_find),LSM_HOOK_INIT (file_alloc_security, hook_file_alloc_security), LSM_HOOK_INIT(file_open, hook_file_open),security/landlock/limits.h +1
-1@@ -19,7 +19,7 @@#define LANDLOCK_MAX_NUM_LAYERS 16#define LANDLOCK_MAX_NUM_RULES U32_MAX -#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV +#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_RESOLVE_UNIX#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)security/landlock/syscalls.c +1
-1@@ -166,7 +166,7 @@ static const struct file_operations ruleset_fops = {* If the change involves a fix that requires userspace awareness, also update * the errata documentation in Documentation/userspace-api/landlock.rst . */ -const int landlock_abi_version =8 ; +const int landlock_abi_version =9 ;/** * sys_landlock_create_ruleset - Create a new ruleset tools/testing/selftests/landlock/base_test.c +1
-1@@ -76,7 +76,7 @@ TEST(abi_version)const struct landlock_ruleset_attr ruleset_attr = { .handled_access_fs =LANDLOCK_ACCESS_FS_READ_FILE , }; -ASSERT_EQ (8 ,landlock_create_ruleset (NULL ,0 , +ASSERT_EQ (9 ,landlock_create_ruleset (NULL ,0 ,LANDLOCK_CREATE_RULESET_VERSION )); ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,tools/testing/selftests/landlock/fs_test.c +3
-3@@ -575,9 +575,10 @@ TEST_F_FORK(layout1, inval) LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX) -#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV +#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX#define ACCESS_ALL ( \ ACCESS_FILE | \ --2.53.0
PATCH v8 5/12 landlock: Clarify BUILD_BUG_ON check in scoping logic
In reply to Günther NoackThe BUILD_BUG_ON check in domain_is_scoped() and unmask_scoped_access() should check that the loop that counts down client_layer finishes. We therefore check that the numbers LANDLOCK_MAX_NUM_LAYERS-1 and -1 are both representable by that integer. If they are representable, the numbers in between are representable too, and the loop finishes.
--- security/landlock/fs.c | 9 ++++++--- security/landlock/task.c | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-)
security/landlock/fs.c +6 -3
@@ -1595,10 +1595,13 @@ static void unmask_scoped_access(const struct landlock_ruleset *const client,return ;/* -* client_layer must be a signed integer with greater capacity -* than client->num_layers to ensure the following loop stops. +* client_layer must be able to represent all numbers from +* LANDLOCK_MAX_NUM_LAYERS - 1 to -1 for the loop below to terminate. +* (It must be large enough, and it must be signed.) */ -BUILD_BUG_ON (sizeof (client_layer) >sizeof (client->num_layers)); +BUILD_BUG_ON (!is_signed_type (typeof (client_layer))); +BUILD_BUG_ON (LANDLOCK_MAX_NUM_LAYERS -1 > +type_max (typeof (client_layer))); client_layer = client->num_layers -1 ; client_walker = client->hierarchy;
security/landlock/task.c +6 -3
@@ -191,10 +191,13 @@ static bool domain_is_scoped(const struct landlock_ruleset *const client, client_layer = client->num_layers -1 ; client_walker = client->hierarchy;/* -* client_layer must be a signed integer with greater capacity -* than client->num_layers to ensure the following loop stops. +* client_layer must be able to represent all numbers from +* LANDLOCK_MAX_NUM_LAYERS - 1 to -1 for the loop below to terminate. +* (It must be large enough, and it must be signed.) */ -BUILD_BUG_ON (sizeof (client_layer) >sizeof (client->num_layers)); +BUILD_BUG_ON (!is_signed_type (typeof (client_layer))); +BUILD_BUG_ON (LANDLOCK_MAX_NUM_LAYERS -1 > +type_max (typeof (client_layer))); server_layer = server ? (server->num_layers -1 ) :-1 ; server_walker = server ? server->hierarchy :NULL ;
Signature · 1 line
2.53.0
PATCH v8 6/12 samples/landlock: Add support for named UNIX domain socket restrictions
In reply to Günther NoackThe access right for UNIX domain socket lookups is grouped with the read-write rights in the sample tool. Rationale: In the general case, any operations are possible through a UNIX domain socket, including data-mutating operations.
--- samples/landlock/sandboxer.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
samples/landlock/sandboxer.c +9 -3
@@ -111,7 +111,8 @@ static int parse_path(char *env_path, const char ***const path_list)LANDLOCK_ACCESS_FS_WRITE_FILE | \LANDLOCK_ACCESS_FS_READ_FILE | \LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) +LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)/* clang-format on */
@@ -295,11 +296,12 @@ static bool check_ruleset_scope(const char *const env_var, LANDLOCK_ACCESS_FS_MAKE_SYM | \ LANDLOCK_ACCESS_FS_REFER | \ LANDLOCK_ACCESS_FS_TRUNCATE | \ - LANDLOCK_ACCESS_FS_IOCTL_DEV) + LANDLOCK_ACCESS_FS_IOCTL_DEV | \ + LANDLOCK_ACCESS_FS_RESOLVE_UNIX)/* clang-format on */ -#define LANDLOCK_ABI_LAST 8 +#define LANDLOCK_ABI_LAST 9#define XSTR (s) #s#define STR (s) XSTR(s)
@@ -438,6 +440,10 @@ int main(const int argc, char *const argv[], char *const *const envp) ~LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON ; __attribute__((fallthrough));case 7 : +case 8 : +/* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */ + ruleset_attr.handled_access_fs &= + ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX ;/* Must be printed for any ABI < LANDLOCK_ABI_LAST. */ fprintf (stderr, "Hint: You should update the running kernel "
Signature · 1 line
2.53.0
PATCH v8 7/12 selftests/landlock: Replace access_fs_16 with ACCESS_ALL in fs_test
In reply to Günther NoackThe access_fs_16 variable was originally intended to stay frozen at 16 access rights so that audit tests would not need updating when new access rights are added. Now that we have 17 access rights, the name is confusing.
Replace all uses of access_fs_16 with ACCESS_ALL and delete the variable.
--- tools/testing/selftests/landlock/fs_test.c | 54 +++++++--------------- 1 file changed, 17 insertions(+), 37 deletions(-)
tools/testing/selftests/landlock/fs_test.c +17 -37
@@ -7161,26 +7161,6 @@ TEST_F(audit_layout1, execute_make)* only the blocked ones are logged. */ -/* clang-format off */ -static const __u64 access_fs_16 = -LANDLOCK_ACCESS_FS_EXECUTE | -LANDLOCK_ACCESS_FS_WRITE_FILE | -LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_READ_DIR | -LANDLOCK_ACCESS_FS_REMOVE_DIR | -LANDLOCK_ACCESS_FS_REMOVE_FILE | -LANDLOCK_ACCESS_FS_MAKE_CHAR | -LANDLOCK_ACCESS_FS_MAKE_DIR | -LANDLOCK_ACCESS_FS_MAKE_REG | -LANDLOCK_ACCESS_FS_MAKE_SOCK | -LANDLOCK_ACCESS_FS_MAKE_FIFO | -LANDLOCK_ACCESS_FS_MAKE_BLOCK | -LANDLOCK_ACCESS_FS_MAKE_SYM | -LANDLOCK_ACCESS_FS_REFER | -LANDLOCK_ACCESS_FS_TRUNCATE | -LANDLOCK_ACCESS_FS_IOCTL_DEV ; -/* clang-format on */ - TEST_F(audit_layout1, execute_read) {struct audit_records records;
@@ -7190,7 +7170,7 @@ TEST_F(audit_layout1, execute_read)test_check_exec (_metadata,0 , file1_s1d1);drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });/*
@@ -7214,7 +7194,7 @@ TEST_F(audit_layout1, write_file)struct audit_records records;drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (EACCES ,test_open (file1_s1d1,O_WRONLY ));
@@ -7231,7 +7211,7 @@ TEST_F(audit_layout1, read_file)struct audit_records records;drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -7248,7 +7228,7 @@ TEST_F(audit_layout1, read_dir)struct audit_records records;drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (EACCES ,test_open (dir_s1d1,O_DIRECTORY ));
@@ -7268,7 +7248,7 @@ TEST_F(audit_layout1, remove_dir)EXPECT_EQ (0 ,unlink (file2_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,rmdir (dir_s1d3));
@@ -7291,7 +7271,7 @@ TEST_F(audit_layout1, remove_file)struct audit_records records;drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,unlink (file1_s1d3));
@@ -7311,7 +7291,7 @@ TEST_F(audit_layout1, make_char)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFCHR |0644 ,0 ));
@@ -7331,7 +7311,7 @@ TEST_F(audit_layout1, make_dir)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mkdir (file1_s1d3,0755 ));
@@ -7351,7 +7331,7 @@ TEST_F(audit_layout1, make_reg)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFREG |0644 ,0 ));
@@ -7371,7 +7351,7 @@ TEST_F(audit_layout1, make_sock)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFSOCK |0644 ,0 ));
@@ -7391,7 +7371,7 @@ TEST_F(audit_layout1, make_fifo)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFIFO |0644 ,0 ));
@@ -7411,7 +7391,7 @@ TEST_F(audit_layout1, make_block)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFBLK |0644 ,0 ));
@@ -7431,7 +7411,7 @@ TEST_F(audit_layout1, make_sym)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,symlink ("target" , file1_s1d3));
@@ -7501,7 +7481,7 @@ TEST_F(audit_layout1, refer_rename)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (EACCES ,test_rename (file1_s1d2, file1_s2d3));
@@ -7523,7 +7503,7 @@ TEST_F(audit_layout1, refer_exchange)EXPECT_EQ (0 ,unlink (file1_s1d3));drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });/*
@@ -7586,7 +7566,7 @@ TEST_F(audit_layout1, truncate)struct audit_records records;drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = access_fs_16, + .handled_access_fs =ACCESS_ALL , });EXPECT_EQ (-1 ,truncate (file1_s1d3,0 ));
@@ -7607,7 +7587,7 @@ TEST_F(audit_layout1, ioctl_dev)drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ .handled_access_fs = - access_fs_16 & +ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE , });
Signature · 1 line
2.53.0
PATCH v8 8/12 selftests/landlock: Test LANDLOCK_ACCESS_FS_RESOLVE_UNIX
In reply to Günther Noack* Extract common helpers from an existing IOCTL test that also uses pathname unix(7) sockets.
* These tests use the common scoped domains fixture which is also used in other Landlock scoping tests and which was used in Tingmao Wang's earlier patch set in [1].
These tests exercise the cross product of the following scenarios:
* Stream connect(), Datagram connect(), Datagram sendmsg() and Seqpacket connect().
* Child-to-parent and parent-to-child communication
* The Landlock policy configuration as listed in the scoped_domains fixture.
* In the default variant, Landlock domains are only placed where prescribed in the fixture.
* In the "ALL_DOMAINS" variant, Landlock domains are also placed in the places where the fixture says to omit them, but with a LANDLOCK_RULE_PATH_BENEATH that allows connection.
Cc: Justin Suess <[email protected]>
Cc: Tingmao Wang <[email protected]>
Cc: Mickaël Salaün <[email protected]>
Link[1]: https://lore.kernel.org/all/53b9883648225d5a08e82d2636ab0b4fda003bc9.1767115163.git.m@maowtm.org/
--- tools/testing/selftests/landlock/fs_test.c | 390 ++++++++++++++++++++- 1 file changed, 374 insertions(+), 16 deletions(-)
tools/testing/selftests/landlock/fs_test.c +374 -16
@@ -4358,30 +4358,66 @@ TEST_F_FORK(layout1, named_pipe_ioctl)ASSERT_EQ (child_pid,waitpid (child_pid,NULL ,0 )); } +/* +* set_up_named_unix_server - Create a pathname unix socket +* +* If the socket type is not SOCK_DGRAM, also invoke listen(2). +* +* Return: The listening FD - it is the caller responsibility to close it. +*/ +static int set_up_named_unix_server (struct __test_metadata *const _metadata, +int type,const char *const path) +{ +int fd; +struct sockaddr_un addr = { + .sun_family =AF_UNIX , + }; + + fd =socket (AF_UNIX , type,0 ); +ASSERT_LE (0 , fd); + +ASSERT_LT (strlen (path),sizeof (addr.sun_path)); +strncpy (addr.sun_path, path,sizeof (addr.sun_path) -1 ); + +ASSERT_EQ (0 ,bind (fd, (struct sockaddr *)&addr,sizeof (addr))); + +if (type !=SOCK_DGRAM ) +ASSERT_EQ (0 ,listen (fd,10 /* qlen */ )); +return fd; +} + +/* +* test_connect_named_unix - connect to the given named UNIX socket +* +* Return: The errno from connect(), or 0 +*/ +static int test_connect_named_unix (struct __test_metadata *const _metadata, +int fd,const char *const path) +{ +struct sockaddr_un addr = { + .sun_family =AF_UNIX , + }; + +ASSERT_LT (strlen (path),sizeof (addr.sun_path)); +strncpy (addr.sun_path, path,sizeof (addr.sun_path) -1 ); + +if (connect (fd, (struct sockaddr *)&addr,sizeof (addr)) ==-1 ) +return errno; +return 0 ; +} +/* For named UNIX domain sockets, no IOCTL restrictions apply. */ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) {const char *const path = file1_s1d1;int srv_fd, cli_fd, ruleset_fd; -struct sockaddr_un srv_un = { - .sun_family =AF_UNIX , - }; -struct sockaddr_un cli_un = { - .sun_family =AF_UNIX , - };const struct landlock_ruleset_attr attr = { .handled_access_fs =LANDLOCK_ACCESS_FS_IOCTL_DEV , };/* Sets up a server */ ASSERT_EQ (0 ,unlink (path)); - srv_fd =socket (AF_UNIX ,SOCK_STREAM ,0 ); -ASSERT_LE (0 , srv_fd); - -strncpy (srv_un.sun_path, path,sizeof (srv_un.sun_path)); -ASSERT_EQ (0 ,bind (srv_fd, (struct sockaddr *)&srv_un,sizeof (srv_un))); - -ASSERT_EQ (0 ,listen (srv_fd,10 /* qlen */ )); + srv_fd =set_up_named_unix_server (_metadata,SOCK_STREAM , path);/* Enables Landlock. */ ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 );
@@ -4393,9 +4429,7 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) cli_fd =socket (AF_UNIX ,SOCK_STREAM ,0 );ASSERT_LE (0 , cli_fd); -strncpy (cli_un.sun_path, path,sizeof (cli_un.sun_path)); -ASSERT_EQ (0 , -connect (cli_fd, (struct sockaddr *)&cli_un,sizeof (cli_un))); +ASSERT_EQ (0 ,test_connect_named_unix (_metadata, cli_fd, path));/* FIONREAD and other IOCTLs should not be forbidden. */ EXPECT_EQ (0 ,test_fionread_ioctl (cli_fd));
@@ -4570,6 +4604,330 @@ TEST_F_FORK(ioctl, handle_file_access_file)ASSERT_EQ (0 ,close (file_fd)); } +/* +* test_sendto_named_unix - sendto to the given named UNIX socket +* +* sendto() is equivalent to sendmsg() in this respect. +* +* Return: The errno from sendto(), or 0 +*/ +static int test_sendto_named_unix (struct __test_metadata *const _metadata, +int fd,const char *const path) +{ +static const char buf[] ="dummy" ; +struct sockaddr_un addr = { + .sun_family =AF_UNIX , + }; + +ASSERT_LT (strlen (path),sizeof (addr.sun_path)); +strncpy (addr.sun_path, path,sizeof (addr.sun_path) -1 ); + +if (sendto (fd, buf,sizeof (buf),0 , (struct sockaddr *)&addr, +sizeof (addr)) ==-1 ) +return errno; +return 0 ; +} + +/* clang-format off */ +FIXTURE (scoped_domains) {}; +/* clang-format on */ + +#include "scoped_base_variants.h" + +FIXTURE_SETUP (scoped_domains) +{ +drop_caps (_metadata); +}; + +FIXTURE_TEARDOWN (scoped_domains) +{ +} + +static void enforce_fs_resolve_unix (struct __test_metadata *const _metadata, +const struct rule rules[]) +{ +if (rules) { +int fd =create_ruleset (_metadata, +LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules); +enforce_ruleset (_metadata, fd); +EXPECT_EQ (0 ,close (fd)); + }else { +struct landlock_ruleset_attr attr = { + .handled_access_fs =LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + }; +drop_access_rights (_metadata, &attr); + } +} + +/* +* Flags for test_connect_to_parent and test_connect_to_child: +* +* USE_SENDTO: Use sendto() instead of connect() (for SOCK_DGRAM only) +* ENFORCE_ALL: Enforce a Landlock domain even when the variant says +* we shouldn't. We enforce a domain where the path is allow-listed, +* and expect the behavior to be the same as if none was used. +*/ +#define USE_SENDTO (1 << 0) +#define ENFORCE_ALL (1 << 1) + +static void test_connect_to_parent (struct __test_metadata *const _metadata, +const FIXTURE_VARIANT (scoped_domains) * + variant, +int sock_type,int flags) +{ +const char *const path ="sock" ; +const struct rule rules[] = { + { + .path ="." , + .access =LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + }, + {}, + }; +int cli_fd, srv_fd, res, status; +pid_t child_pid; +int readiness_pipe[2 ]; +char buf[1 ]; + +if (variant->domain_both) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + +unlink (path); +ASSERT_EQ (0 ,pipe2 (readiness_pipe,O_CLOEXEC )); + + child_pid =fork (); +ASSERT_LE (0 , child_pid); + +if (child_pid ==0 ) { +if (variant->domain_child) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + +/* Wait for server to be available. */ +EXPECT_EQ (0 ,close (readiness_pipe[1 ])); +EXPECT_EQ (1 ,read (readiness_pipe[0 ], &buf,1 )); +EXPECT_EQ (0 ,close (readiness_pipe[0 ])); + +/* Talk to server. */ + cli_fd =socket (AF_UNIX , sock_type,0 ); +ASSERT_LE (0 , cli_fd); + +if (flags &USE_SENDTO ) + res =test_sendto_named_unix (_metadata, cli_fd, path); +else + res =test_connect_named_unix (_metadata, cli_fd, path); + +EXPECT_EQ (variant->domain_child ?EACCES :0 , res); + +/* Clean up. */ +EXPECT_EQ (0 ,close (cli_fd)); + +_exit (_metadata->exit_code); +return ; + } + +if (variant->domain_parent) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + + srv_fd =set_up_named_unix_server (_metadata, sock_type, path); + +/* Tell the child that it can connect. */ +EXPECT_EQ (0 ,close (readiness_pipe[0 ])); +EXPECT_EQ (sizeof (buf),write (readiness_pipe[1 ], buf,sizeof (buf))); +EXPECT_EQ (0 ,close (readiness_pipe[1 ])); + +/* Wait for child. */ +ASSERT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + +/* Clean up. */ +EXPECT_EQ (0 ,close (srv_fd)); +EXPECT_EQ (0 ,unlink (path)); +} + +static void test_connect_to_child (struct __test_metadata *const _metadata, +const FIXTURE_VARIANT (scoped_domains) * + variant, +int sock_type,int flags) +{ +const char *const path ="sock" ; +const struct rule rules[] = { + { + .path ="." , + .access =LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + }, + {}, + }; +int readiness_pipe[2 ]; +int shutdown_pipe[2 ]; +int cli_fd, srv_fd, res, status; +pid_t child_pid; +char buf[1 ]; + +if (variant->domain_both) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + +unlink (path); +ASSERT_EQ (0 ,pipe2 (readiness_pipe,O_CLOEXEC )); +ASSERT_EQ (0 ,pipe2 (shutdown_pipe,O_CLOEXEC )); + + child_pid =fork (); +ASSERT_LE (0 , child_pid); + +if (child_pid ==0 ) { +if (variant->domain_child) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + + srv_fd =set_up_named_unix_server (_metadata, sock_type, path); + +/* Tell the parent that it can connect. */ +EXPECT_EQ (0 ,close (readiness_pipe[0 ])); +EXPECT_EQ (sizeof (buf), +write (readiness_pipe[1 ], buf,sizeof (buf))); +EXPECT_EQ (0 ,close (readiness_pipe[1 ])); + +/* Wait until it is time to shut down. */ +EXPECT_EQ (0 ,close (shutdown_pipe[1 ])); +EXPECT_EQ (1 ,read (shutdown_pipe[0 ], &buf,1 )); +EXPECT_EQ (0 ,close (shutdown_pipe[0 ])); + +/* Cleanup */ +EXPECT_EQ (0 ,close (srv_fd)); +EXPECT_EQ (0 ,unlink (path)); + +_exit (_metadata->exit_code); +return ; + } + +if (variant->domain_parent) +enforce_fs_resolve_unix (_metadata,NULL ); +else if (flags &ENFORCE_ALL ) +enforce_fs_resolve_unix (_metadata, rules); + +/* Wait for server to be available. */ +EXPECT_EQ (0 ,close (readiness_pipe[1 ])); +EXPECT_EQ (1 ,read (readiness_pipe[0 ], &buf,1 )); +EXPECT_EQ (0 ,close (readiness_pipe[0 ])); + +/* Talk to server. */ + cli_fd =socket (AF_UNIX , sock_type,0 ); +ASSERT_LE (0 , cli_fd); + +if (flags &USE_SENDTO ) + res =test_sendto_named_unix (_metadata, cli_fd, path); +else + res =test_connect_named_unix (_metadata, cli_fd, path); + +EXPECT_EQ (variant->domain_parent ?EACCES :0 , res); + +/* Clean up. */ +EXPECT_EQ (0 ,close (cli_fd)); + +/* Tell the server to shut down. */ +EXPECT_EQ (0 ,close (shutdown_pipe[0 ])); +EXPECT_EQ (sizeof (buf),write (shutdown_pipe[1 ], buf,sizeof (buf))); +EXPECT_EQ (0 ,close (shutdown_pipe[1 ])); + +/* Wait for child. */ +ASSERT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); +} + +TEST_F(scoped_domains, unix_stream_connect_to_parent) +{ +test_connect_to_parent (_metadata, variant,SOCK_STREAM ,0 ); +} + +TEST_F(scoped_domains, unix_dgram_connect_to_parent) +{ +test_connect_to_parent (_metadata, variant,SOCK_DGRAM ,0 ); +} + +TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent) +{ +test_connect_to_parent (_metadata, variant,SOCK_DGRAM ,USE_SENDTO ); +} + +TEST_F(scoped_domains, unix_seqpacket_connect_to_parent) +{ +test_connect_to_parent (_metadata, variant,SOCK_SEQPACKET ,0 ); +} + +TEST_F(scoped_domains, unix_stream_connect_to_parent_full) +{ +test_connect_to_parent (_metadata, variant,SOCK_STREAM ,ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_dgram_connect_to_parent_full) +{ +test_connect_to_parent (_metadata, variant,SOCK_DGRAM ,ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent_full) +{ +test_connect_to_parent (_metadata, variant,SOCK_DGRAM , +USE_SENDTO |ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_seqpacket_connect_to_parent_full) +{ +test_connect_to_parent (_metadata, variant,SOCK_SEQPACKET ,ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_stream_connect_to_child) +{ +test_connect_to_child (_metadata, variant,SOCK_STREAM ,0 ); +} + +TEST_F(scoped_domains, unix_dgram_connect_to_child) +{ +test_connect_to_child (_metadata, variant,SOCK_DGRAM ,0 ); +} + +TEST_F(scoped_domains, unix_dgram_sendmsg_to_child) +{ +test_connect_to_child (_metadata, variant,SOCK_DGRAM ,USE_SENDTO ); +} + +TEST_F(scoped_domains, unix_seqpacket_connect_to_child) +{ +test_connect_to_child (_metadata, variant,SOCK_SEQPACKET ,0 ); +} + +TEST_F(scoped_domains, unix_stream_connect_to_child_full) +{ +test_connect_to_child (_metadata, variant,SOCK_STREAM ,ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_dgram_connect_to_child_full) +{ +test_connect_to_child (_metadata, variant,SOCK_DGRAM ,ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_dgram_sendmsg_to_child_full) +{ +test_connect_to_child (_metadata, variant,SOCK_DGRAM , +USE_SENDTO |ENFORCE_ALL ); +} + +TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full) +{ +test_connect_to_child (_metadata, variant,SOCK_SEQPACKET ,ENFORCE_ALL ); +} + +#undef USE_SENDTO +#undef ENFORCE_ALL +/* clang-format off */ FIXTURE (layout1_bind) {};/* clang-format on */
Signature · 1 line
2.53.0
PATCH v8 9/12 selftests/landlock: Audit test for LANDLOCK_ACCESS_FS_RESOLVE_UNIX
In reply to Günther NoackAdd an audit test to check that Landlock denials from LANDLOCK_ACCESS_FS_RESOLVE_UNIX result in audit logs in the expected format. (There is one audit test for each filesystem access right, so we should add one for LANDLOCK_ACCESS_FS_RESOLVE_UNIX as well.)
--- tools/testing/selftests/landlock/fs_test.c | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+)
tools/testing/selftests/landlock/fs_test.c +40 -0
@@ -7961,6 +7961,46 @@ TEST_F(audit_layout1, ioctl_dev)EXPECT_EQ (1 , records.domain); } +TEST_F(audit_layout1, resolve_unix) +{ +struct audit_records records; +const char *const path ="sock" ; +int srv_fd, cli_fd, status; +pid_t child_pid; + + srv_fd =set_up_named_unix_server (_metadata,SOCK_STREAM , path); + + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (!child_pid) { +drop_access_rights (_metadata, + &(struct landlock_ruleset_attr ){ + .handled_access_fs =ACCESS_ALL , + }); + + cli_fd =socket (AF_UNIX ,SOCK_STREAM ,0 ); +ASSERT_LE (0 , cli_fd); +EXPECT_EQ (EACCES , +test_connect_named_unix (_metadata, cli_fd, path)); + +EXPECT_EQ (0 ,close (cli_fd)); +_exit (_metadata->exit_code); + } + +ASSERT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + +EXPECT_EQ (0 ,matches_log_fs_extra (_metadata, self->audit_fd, +"fs\\.resolve_unix" , path,NULL )); + +EXPECT_EQ (0 ,audit_count_records (self->audit_fd, &records)); +EXPECT_EQ (0 , records.access); +EXPECT_EQ (1 , records.domain); + +EXPECT_EQ (0 ,close (srv_fd)); +} + TEST_F(audit_layout1, mount) {struct audit_records records;
Signature · 1 line
2.53.0
PATCH v8 10/12 selftests/landlock: Check that coredump sockets stay unrestricted
In reply to Günther NoackEven when a process is restricted with the new LANDLOCK_ACCESS_FS_RESOLVE_UNIX right, the kernel can continue writing its coredump to the configured coredump socket.
In the test, we create a local server and rewire the system to write coredumps into it. We then create a child process within a Landlock domain where LANDLOCK_ACCESS_FS_RESOLVE_UNIX is restricted and make the process crash. The test uses SO_PEERCRED to check that the connecting client process is the expected one.
Includes a fix by Mickaël Salaün for setting the EUID to 0 (see [1]).
Link[1]: https://lore.kernel.org/all/[email protected]/
--- tools/testing/selftests/landlock/fs_test.c | 143 +++++++++++++++++++++ 1 file changed, 143 insertions(+)
tools/testing/selftests/landlock/fs_test.c +143 -0
@@ -22,6 +22,7 @@#include <sys/ioctl.h> #include <sys/mount.h> #include <sys/prctl.h> +#include <sys/resource.h> #include <sys/sendfile.h> #include <sys/socket.h> #include <sys/stat.h>
@@ -4928,6 +4929,148 @@ TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full)#undef USE_SENDTO#undef ENFORCE_ALL +static void read_core_pattern (struct __test_metadata *const _metadata, +char *buf,size_t buf_size) +{ +int fd; +ssize_t ret; + + fd =open ("/proc/sys/kernel/core_pattern" ,O_RDONLY |O_CLOEXEC ); +ASSERT_LE (0 , fd); + + ret =read (fd, buf, buf_size -1 ); +ASSERT_LE (0 , ret); +EXPECT_EQ (0 ,close (fd)); + + buf[ret] ='\0' ; +} + +static void set_core_pattern (struct __test_metadata *const _metadata, +const char *pattern) +{ +int fd; +size_t len =strlen (pattern); + +/* +* Writing to /proc/sys/kernel/core_pattern requires EUID 0 because +* sysctl_perm() checks that, ignoring capabilities like +* CAP_SYS_ADMIN or CAP_DAC_OVERRIDE. +* +* Switching EUID clears the dumpable flag, which must be restored +* afterwards to allow coredumps. +*/ +set_cap (_metadata,CAP_SETUID ); +ASSERT_EQ (0 ,seteuid (0 )); +clear_cap (_metadata,CAP_SETUID ); + + fd =open ("/proc/sys/kernel/core_pattern" ,O_WRONLY |O_CLOEXEC ); + ASSERT_LE(0, fd) + { +TH_LOG ("Failed to open core_pattern for writing: %s" , +strerror (errno)); + } + +ASSERT_EQ (len,write (fd, pattern, len)); +EXPECT_EQ (0 ,close (fd)); + +set_cap (_metadata,CAP_SETUID ); +ASSERT_EQ (0 ,seteuid (getuid ())); +clear_cap (_metadata,CAP_SETUID ); + +/* Restore dumpable flag cleared by seteuid(). */ +ASSERT_EQ (0 ,prctl (PR_SET_DUMPABLE ,1 ,0 ,0 ,0 )); +} + +FIXTURE (coredump) +{ +char original_core_pattern[256 ]; +}; + +FIXTURE_SETUP (coredump) +{ +disable_caps (_metadata); +read_core_pattern (_metadata, self->original_core_pattern, +sizeof (self->original_core_pattern)); +} + +FIXTURE_TEARDOWN_PARENT (coredump) +{ +set_core_pattern (_metadata, self->original_core_pattern); +} + +/* +* Test that even when a process is restricted with +* LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection +* to the coredump socket on the processes' behalf. +*/ +TEST_F_FORK(coredump, socket_not_restricted) +{ +static const char core_pattern[] ="@/tmp/landlock_coredump_test.sock" ; +const char *const sock_path = core_pattern +1 ; +int srv_fd, conn_fd, status; +pid_t child_pid; +struct ucred cred; +socklen_t cred_len =sizeof (cred); +char buf[4096 ]; + +/* Set up the coredump server socket. */ +unlink (sock_path); + srv_fd =set_up_named_unix_server (_metadata,SOCK_STREAM , sock_path); + +/* Point coredumps at our socket. */ +set_core_pattern (_metadata, core_pattern); + +/* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ +drop_access_rights (_metadata, + &(struct landlock_ruleset_attr ){ + .handled_access_fs = +LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + }); + +/* Fork a child that crashes. */ + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (child_pid ==0 ) { +struct rlimit rl = { + .rlim_cur =RLIM_INFINITY , + .rlim_max =RLIM_INFINITY , + }; + +ASSERT_EQ (0 ,setrlimit (RLIMIT_CORE , &rl)); + +/* Crash on purpose. */ +kill (getpid (),SIGSEGV ); +_exit (1 ); + } + +/* +* Accept the coredump connection. If Landlock incorrectly denies the +* kernel's coredump connect, accept() will block forever, so the test +* would time out. +*/ + conn_fd =accept (srv_fd,NULL ,NULL ); +ASSERT_LE (0 , conn_fd); + +/* Check that the connection came from the crashing child. */ +ASSERT_EQ (0 ,getsockopt (conn_fd,SOL_SOCKET ,SO_PEERCRED , &cred, + &cred_len)); +EXPECT_EQ (child_pid, cred.pid); + +/* Drain the coredump data so the kernel can finish. */ +while (read (conn_fd, buf,sizeof (buf)) >0 ) + ; + +EXPECT_EQ (0 ,close (conn_fd)); + +/* Wait for the child and verify it coredumped. */ +ASSERT_EQ (child_pid,waitpid (child_pid, &status,0 )); +ASSERT_TRUE (WIFSIGNALED (status)); +ASSERT_TRUE (WCOREDUMP (status)); + +EXPECT_EQ (0 ,close (srv_fd)); +EXPECT_EQ (0 ,unlink (sock_path)); +} +/* clang-format off */ FIXTURE (layout1_bind) {};/* clang-format on */
Signature · 1 line
2.53.0
PATCH v8 11/12 selftests/landlock: fs_test: Simplify ruleset creation and enforcement
In reply to Günther Noack* Add enforce_fs() for defining and enforcing a ruleset in one step
* In some places, dropped "ASSERT_LE(0, fd)" checks after create_ruleset() call -- create_ruleset() already checks that.
* In some places, rename "file_fd" to "fd" if it is not needed to disambiguate any more.
--- tools/testing/selftests/landlock/fs_test.c | 821 ++++++--------------- 1 file changed, 210 insertions(+), 611 deletions(-)
tools/testing/selftests/landlock/fs_test.c +210 -611
@@ -767,15 +767,6 @@ static int create_ruleset(struct __test_metadata *const _metadata, .handled_access_fs = handled_access_fs, }; - ASSERT_NE(NULL, rules) - { -TH_LOG ("No rule list" ); - } - ASSERT_NE(NULL, rules[0].path) - { -TH_LOG ("Empty rule list" ); - } - ruleset_fd =landlock_create_ruleset (&ruleset_attr,sizeof (ruleset_attr),0 );ASSERT_LE (0 , ruleset_fd)
@@ -783,16 +774,26 @@ static int create_ruleset(struct __test_metadata *const _metadata,TH_LOG ("Failed to create a ruleset: %s" ,strerror (errno)); } -for (i =0 ; rules[i].path; i++) { -if (!rules[i].access) -continue ; +if (rules) +for (i =0 ; rules[i].path; i++) { +if (!rules[i].access) +continue ; -add_path_beneath (_metadata, ruleset_fd, rules[i].access, - rules[i].path); - } +add_path_beneath (_metadata, ruleset_fd, rules[i].access, + rules[i].path); + }return ruleset_fd; } +static void enforce_fs (struct __test_metadata *const _metadata, +const __u64 access_fs,const struct rule rules[]) +{ +const int ruleset_fd =create_ruleset (_metadata, access_fs, rules); + +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); +} + TEST_F_FORK(layout0, proc_nsfs) {const struct rule rules[] = {
@@ -879,13 +880,10 @@ TEST_F_FORK(layout1, effective_access) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules);char buf;int reg_fd; -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);/* Tests on a directory (with or without O_PATH). */ ASSERT_EQ (EACCES ,test_open ("/" ,O_RDONLY ));
@@ -932,12 +930,9 @@ TEST_F_FORK(layout1, unhandled_access) }, {}, }; -/* Here, we only handle read accesses, not write accesses. */ -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RO , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +/* Here, we only handle read accesses, not write accesses. */ +enforce_fs (_metadata,ACCESS_RO , rules);/* * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
@@ -966,11 +961,8 @@ TEST_F_FORK(layout1, ruleset_overlap) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);/* Checks s1d1 hierarchy. */ ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -1022,11 +1014,8 @@ TEST_F_FORK(layout1, layer_rule_unions) }, {}, }; -int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer1);/* Checks s1d1 hierarchy with layer1. */ ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -1048,10 +1037,7 @@ TEST_F_FORK(layout1, layer_rule_unions)ASSERT_EQ (EACCES ,test_open (dir_s1d1,O_RDONLY |O_DIRECTORY ));/* Doesn't change anything from layer1. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer2); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer2);/* Checks s1d1 hierarchy with layer2. */ ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -1073,10 +1059,7 @@ TEST_F_FORK(layout1, layer_rule_unions)ASSERT_EQ (EACCES ,test_open (dir_s1d1,O_RDONLY |O_DIRECTORY ));/* Only allows write (but not read) to dir_s1d3. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer3); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer3);/* Checks s1d1 hierarchy with layer3. */ ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -1114,27 +1097,18 @@ TEST_F_FORK(layout1, non_overlapping_accesses) }, {}, }; -int ruleset_fd;ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file1_s1d2)); - ruleset_fd = -create_ruleset (_metadata,LANDLOCK_ACCESS_FS_MAKE_REG , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_MAKE_REG , layer1);ASSERT_EQ (-1 ,mknod (file1_s1d1,S_IFREG |0700 ,0 ));ASSERT_EQ (EACCES , errno);ASSERT_EQ (0 ,mknod (file1_s1d2,S_IFREG |0700 ,0 ));ASSERT_EQ (0 ,unlink (file1_s1d2)); - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_REMOVE_FILE , - layer2); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_REMOVE_FILE , layer2);/* Unchanged accesses for file creation. */ ASSERT_EQ (-1 ,mknod (file1_s1d1,S_IFREG |0700 ,0 ));
@@ -1238,37 +1212,24 @@ TEST_F_FORK(layout1, interleaved_masked_accesses) }, {}, }; -int ruleset_fd; - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , - layer1_read); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , layer1_read);/* Checks that read access is granted for file1_s1d3 with layer 1. */ ASSERT_EQ (0 ,test_open (file1_s1d3,O_RDWR ));ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_RDONLY ));ASSERT_EQ (0 ,test_open (file2_s1d3,O_WRONLY )); - ruleset_fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_WRITE_FILE , - layer2_read_write); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , + layer2_read_write);/* Checks that previous access rights are unchanged with layer 2. */ ASSERT_EQ (0 ,test_open (file1_s1d3,O_RDWR ));ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_RDONLY ));ASSERT_EQ (0 ,test_open (file2_s1d3,O_WRONLY )); - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , - layer3_read); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , layer3_read);/* Checks that previous access rights are unchanged with layer 3. */ ASSERT_EQ (0 ,test_open (file1_s1d3,O_RDWR ));
@@ -1276,13 +1237,9 @@ TEST_F_FORK(layout1, interleaved_masked_accesses)ASSERT_EQ (0 ,test_open (file2_s1d3,O_WRONLY ));/* This time, denies write access for the file hierarchy. */ - ruleset_fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_WRITE_FILE , - layer4_read_write); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , + layer4_read_write);/* * Checks that the only change with layer 4 is that write access is
@@ -1293,11 +1250,7 @@ TEST_F_FORK(layout1, interleaved_masked_accesses)ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_RDONLY ));ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_WRONLY )); - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , - layer5_read); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , layer5_read);/* Checks that previous access rights are unchanged with layer 5. */ ASSERT_EQ (0 ,test_open (file1_s1d3,O_RDONLY ));
@@ -1305,11 +1258,7 @@ TEST_F_FORK(layout1, interleaved_masked_accesses)ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_WRONLY ));ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_RDONLY )); - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_EXECUTE , - layer6_execute); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_EXECUTE , layer6_execute);/* Checks that previous access rights are unchanged with layer 6. */ ASSERT_EQ (0 ,test_open (file1_s1d3,O_RDONLY ));
@@ -1317,13 +1266,9 @@ TEST_F_FORK(layout1, interleaved_masked_accesses)ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_WRONLY ));ASSERT_EQ (EACCES ,test_open (file2_s1d3,O_RDONLY )); - ruleset_fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_WRITE_FILE , - layer7_read_write); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , + layer7_read_write);/* Checks read access is now denied with layer 7. */ ASSERT_EQ (EACCES ,test_open (file1_s1d3,O_RDONLY ));
@@ -1344,7 +1289,6 @@ TEST_F_FORK(layout1, inherit_subset) };const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd);enforce_ruleset (_metadata, ruleset_fd);ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_WRONLY ));
@@ -1460,7 +1404,6 @@ TEST_F_FORK(layout1, inherit_superset) };const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd);enforce_ruleset (_metadata, ruleset_fd);/* Readdir access is denied for dir_s1d2. */
@@ -1476,7 +1419,7 @@ TEST_F_FORK(layout1, inherit_superset)LANDLOCK_ACCESS_FS_READ_DIR , dir_s1d2);enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +EXPECT_EQ (0 ,close (ruleset_fd));/* Readdir access is still denied for dir_s1d2. */ ASSERT_EQ (EACCES ,test_open (dir_s1d2,O_RDONLY |O_DIRECTORY ));
@@ -1498,7 +1441,6 @@ TEST_F_FORK(layout0, max_layers) };const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd);for (i =0 ; i <16 ; i++)enforce_ruleset (_metadata, ruleset_fd);
@@ -1507,7 +1449,7 @@ TEST_F_FORK(layout0, max_layers)ASSERT_EQ (-1 , err);ASSERT_EQ (E2BIG , errno); } -ASSERT_EQ (0 ,close (ruleset_fd)); +EXPECT_EQ (0 ,close (ruleset_fd)); }TEST_F_FORK (layout1, empty_or_same_ruleset)
@@ -1521,20 +1463,15 @@ TEST_F_FORK(layout1, empty_or_same_ruleset)ASSERT_LE (-1 , ruleset_fd);ASSERT_EQ (ENOMSG , errno); -/* Enforces policy which deny read access to all files. */ - ruleset_attr.handled_access_fs =LANDLOCK_ACCESS_FS_READ_FILE ; - ruleset_fd = -landlock_create_ruleset (&ruleset_attr,sizeof (ruleset_attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); +/* Enforces policy which denies read access to all files. */ +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE ,NULL ); +ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));ASSERT_EQ (0 ,test_open (dir_s1d1,O_RDONLY )); -/* Nests a policy which deny read access to all directories. */ - ruleset_attr.handled_access_fs =LANDLOCK_ACCESS_FS_READ_DIR ; +/* Nests a policy which denies read access to all directories. */ ruleset_fd = -landlock_create_ruleset (&ruleset_attr,sizeof (ruleset_attr),0 ); -ASSERT_LE (0 , ruleset_fd); +create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_DIR ,NULL );enforce_ruleset (_metadata, ruleset_fd);ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));ASSERT_EQ (EACCES ,test_open (dir_s1d1,O_RDONLY ));
@@ -1558,11 +1495,8 @@ TEST_F_FORK(layout1, rule_on_mountpoint) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);ASSERT_EQ (0 ,test_open (dir_s1d1,O_RDONLY ));
@@ -1587,11 +1521,8 @@ TEST_F_FORK(layout1, rule_over_mountpoint) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);ASSERT_EQ (0 ,test_open (dir_s1d1,O_RDONLY ));
@@ -1615,21 +1546,15 @@ TEST_F_FORK(layout1, rule_over_root_allow_then_deny) }, {}, }; -int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);/* Checks allowed access. */ ASSERT_EQ (0 ,test_open ("/" ,O_RDONLY ));ASSERT_EQ (0 ,test_open (dir_s1d1,O_RDONLY )); rules[0 ].access =LANDLOCK_ACCESS_FS_READ_FILE ; - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);/* Checks denied access (on a directory). */ ASSERT_EQ (EACCES ,test_open ("/" ,O_RDONLY ));
@@ -1645,11 +1570,8 @@ TEST_F_FORK(layout1, rule_over_root_deny) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);/* Checks denied access (on a directory). */ ASSERT_EQ (EACCES ,test_open ("/" ,O_RDONLY ));
@@ -1665,7 +1587,6 @@ TEST_F_FORK(layout1, rule_inside_mount_ns) }, {}, }; -int ruleset_fd;set_cap (_metadata,CAP_SYS_ADMIN );ASSERT_EQ (0 ,syscall (__NR_pivot_root, dir_s3d2, dir_s3d3))
@@ -1675,10 +1596,7 @@ TEST_F_FORK(layout1, rule_inside_mount_ns)ASSERT_EQ (0 ,chdir ("/" ));clear_cap (_metadata,CAP_SYS_ADMIN ); - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);ASSERT_EQ (0 ,test_open ("s3d3" ,O_RDONLY ));ASSERT_EQ (EACCES ,test_open ("/" ,O_RDONLY ));
@@ -1693,11 +1611,8 @@ TEST_F_FORK(layout1, mount_and_pivot) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);set_cap (_metadata,CAP_SYS_ADMIN );ASSERT_EQ (-1 ,mount (NULL , dir_s3d2,NULL ,MS_RDONLY ,NULL ));
@@ -1716,9 +1631,6 @@ TEST_F_FORK(layout1, move_mount) }, {}, }; -const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); - -ASSERT_LE (0 , ruleset_fd);set_cap (_metadata,CAP_SYS_ADMIN ); ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
@@ -1731,8 +1643,7 @@ TEST_F_FORK(layout1, move_mount) dir_s3d2, 0));clear_cap (_metadata,CAP_SYS_ADMIN ); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , rules);set_cap (_metadata,CAP_SYS_ADMIN ); ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
@@ -1747,14 +1658,9 @@ TEST_F_FORK(layout1, topology_changes_with_net_only) .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP, }; -int ruleset_fd;/* Add network restrictions. */ - ruleset_fd = -landlock_create_ruleset (&ruleset_net,sizeof (ruleset_net),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +drop_access_rights (_metadata, &ruleset_net);/* Mount, remount, move_mount, umount, and pivot_root checks. */ set_cap (_metadata,CAP_SYS_ADMIN );
@@ -1775,14 +1681,9 @@ TEST_F_FORK(layout1, topology_changes_with_net_and_fs) LANDLOCK_ACCESS_NET_CONNECT_TCP, .handled_access_fs =LANDLOCK_ACCESS_FS_EXECUTE , }; -int ruleset_fd;/* Add network and filesystem restrictions. */ - ruleset_fd =landlock_create_ruleset (&ruleset_net_fs, -sizeof (ruleset_net_fs),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +drop_access_rights (_metadata, &ruleset_net_fs);/* Mount, remount, move_mount, umount, and pivot_root checks. */ set_cap (_metadata,CAP_SYS_ADMIN );
@@ -1819,14 +1720,13 @@ TEST_F_FORK(layout1, release_inodes) };const int ruleset_fd =create_ruleset (_metadata,ACCESS_RW , rules); -ASSERT_LE (0 , ruleset_fd);/* Unmount a file hierarchy while it is being used by a ruleset. */ set_cap (_metadata,CAP_SYS_ADMIN );ASSERT_EQ (0 ,umount (dir_s3d2));clear_cap (_metadata,CAP_SYS_ADMIN );enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +EXPECT_EQ (0 ,close (ruleset_fd));ASSERT_EQ (0 ,test_open (file1_s1d1,O_RDONLY ));ASSERT_EQ (EACCES ,test_open (dir_s3d2,O_RDONLY ));
@@ -1858,7 +1758,6 @@ TEST_F_FORK(layout1, covered_rule)/* Creates a ruleset with the future hidden directory. */ ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_DIR , layer1); -ASSERT_LE (0 , ruleset_fd);/* Covers with a new mount point. */ set_cap (_metadata,CAP_SYS_ADMIN );
@@ -1908,10 +1807,7 @@ static void test_relative_path(struct __test_metadata *const _metadata, };int dirfd, ruleset_fd; - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer1_base); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer1_base); ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer2_subs);
@@ -2092,10 +1988,7 @@ TEST_F_FORK(layout1, execute) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); -ASSERT_LE (0 , ruleset_fd);copy_file (_metadata, bin_true, file1_s1d1);copy_file (_metadata, bin_true, file1_s1d2);copy_file (_metadata, bin_true, file1_s1d3);
@@ -2104,8 +1997,7 @@ TEST_F_FORK(layout1, execute)test_execute (_metadata,0 , file1_s1d1);test_check_exec (_metadata,0 , file1_s1d1); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);ASSERT_EQ (0 ,test_open (dir_s1d1,O_RDONLY ));ASSERT_EQ (0 ,test_open (file1_s1d1,O_RDONLY ));
@@ -2216,16 +2108,12 @@ TEST_F_FORK(layout1, link) }, {}, }; -int ruleset_fd =create_ruleset (_metadata, layer1[0 ].access, layer1); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file1_s1d2));ASSERT_EQ (0 ,unlink (file1_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, layer1[0 ].access, layer1);ASSERT_EQ (-1 ,link (file2_s1d1, file1_s1d1));ASSERT_EQ (EACCES , errno);
@@ -2245,10 +2133,7 @@ TEST_F_FORK(layout1, link)ASSERT_EQ (0 ,unlink (file2_s1d2));ASSERT_EQ (0 ,unlink (file2_s1d3)); - ruleset_fd =create_ruleset (_metadata, layer2[0 ].access, layer2); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, layer2[0 ].access, layer2);/* Checks that linkind doesn't require the ability to delete a file. */ ASSERT_EQ (0 ,link (file1_s1d2, file2_s1d2));
@@ -2298,15 +2183,10 @@ TEST_F_FORK(layout1, rename_file) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d2)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);/* * Tries to replace a file, from a directory that allows file removal,
@@ -2380,17 +2260,12 @@ TEST_F_FORK(layout1, rename_dir) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); - -ASSERT_LE (0 , ruleset_fd);/* Empties dir_s1d3 to allow renaming. */ ASSERT_EQ (0 ,unlink (file1_s1d3));ASSERT_EQ (0 ,unlink (file2_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);/* Exchanges and renames directory to a different parent. */ ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
@@ -2444,12 +2319,8 @@ TEST_F_FORK(layout1, reparent_refer) }, {}, }; -int ruleset_fd = -create_ruleset (_metadata,LANDLOCK_ACCESS_FS_REFER , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_REFER , layer1);ASSERT_EQ (-1 ,rename (dir_s1d2, dir_s2d1));ASSERT_EQ (EXDEV , errno);
@@ -2479,14 +2350,9 @@ static void refer_denied_by_default(struct __test_metadata *const _metadata,const int layer1_err, const struct rule layer2[]) { - int ruleset_fd; - ASSERT_EQ(0, unlink(file1_s1d2)); - ruleset_fd =create_ruleset (_metadata, layer1[0 ].access, layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, layer1[0 ].access, layer1);/* * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
@@ -2498,10 +2364,7 @@ static void refer_denied_by_default(struct __test_metadata *const _metadata,ASSERT_EQ (layer1_err,test_exchange (file2_s1d1, file2_s1d2));ASSERT_EQ (layer1_err,test_exchange (file2_s1d2, file2_s1d1)); - ruleset_fd =create_ruleset (_metadata, layer2[0 ].access, layer2); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, layer2[0 ].access, layer2);/* * Now, either the first or the second layer does not handle
@@ -2587,10 +2450,7 @@ TEST_F_FORK(layout1, refer_denied_by_default4)*/ TEST_F_FORK(layout1, refer_mount_root_deny) { -const struct landlock_ruleset_attr ruleset_attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_MAKE_DIR , - }; -int root_fd, ruleset_fd; +int root_fd;/* Creates a mount object from a non-mount point. */ set_cap (_metadata,CAP_SYS_ADMIN );
@@ -2600,13 +2460,7 @@ TEST_F_FORK(layout1, refer_mount_root_deny)clear_cap (_metadata,CAP_SYS_ADMIN );ASSERT_LE (0 , root_fd); - ruleset_fd = -landlock_create_ruleset (&ruleset_attr,sizeof (ruleset_attr),0 ); -ASSERT_LE (0 , ruleset_fd); - -ASSERT_EQ (0 ,prctl (PR_SET_NO_NEW_PRIVS ,1 ,0 ,0 ,0 )); -ASSERT_EQ (0 ,landlock_restrict_self (ruleset_fd,0 )); -EXPECT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_MAKE_DIR ,NULL );/* Link denied by Landlock: EACCES. */ EXPECT_EQ (-1 ,linkat (root_fd,"." , root_fd,"does_not_exist" ,0 ));
@@ -2641,18 +2495,12 @@ TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed) }, {}, }; -int ruleset_fd;ASSERT_EQ (0 ,unlink (file1_s3d3)); - ruleset_fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_REFER | -LANDLOCK_ACCESS_FS_MAKE_REG | -LANDLOCK_ACCESS_FS_REMOVE_FILE , - layer1); - -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + layer1);ASSERT_EQ (0 ,rename (file1_s3d4, file1_s3d3)); }
@@ -2678,13 +2526,10 @@ TEST_F_FORK(layout1, reparent_link) }, {}, }; -const int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , + layer1);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file1_s1d2));
@@ -2756,13 +2601,10 @@ TEST_F_FORK(layout1, reparent_rename) }, {}, }; -const int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , + layer1);ASSERT_EQ (0 ,unlink (file1_s1d2));ASSERT_EQ (0 ,unlink (file1_s1d3));
@@ -2902,13 +2744,9 @@ reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) }, {}, }; -const int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , layer1); - -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , + layer1); } static void
@@ -2925,12 +2763,7 @@ reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)* Same checks as before but with a second layer and a new MAKE_DIR * rule (and no explicit handling of REFER). */ -const int ruleset_fd = -create_ruleset (_metadata,LANDLOCK_ACCESS_FS_MAKE_DIR , layer2); - -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_MAKE_DIR , layer2); }TEST_F_FORK (layout1, reparent_exdev_layers_rename1)
@@ -3199,15 +3032,11 @@ TEST_F_FORK(layout1, reparent_remove) }, {}, }; -const int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_REMOVE_DIR | -LANDLOCK_ACCESS_FS_REMOVE_FILE , - layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_REMOVE_DIR | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + layer1);/* Access denied because of wrong/swapped remove file/dir. */ ASSERT_EQ (-1 ,rename (file1_s1d1, dir_s2d2));
@@ -3271,17 +3100,13 @@ TEST_F_FORK(layout1, reparent_dom_superset) }, {}, }; -int ruleset_fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_REFER | -LANDLOCK_ACCESS_FS_EXECUTE | -LANDLOCK_ACCESS_FS_MAKE_SOCK | -LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_MAKE_FIFO , - layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_EXECUTE | +LANDLOCK_ACCESS_FS_MAKE_SOCK | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_FIFO , + layer1);ASSERT_EQ (-1 ,rename (file1_s1d2, file1_s2d1));ASSERT_EQ (EXDEV , errno);
@@ -3344,18 +3169,13 @@ TEST_F_FORK(layout1, remove_dir) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file1_s1d2));ASSERT_EQ (0 ,unlink (file1_s1d3));ASSERT_EQ (0 ,unlink (file2_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);ASSERT_EQ (0 ,rmdir (dir_s1d3));ASSERT_EQ (0 ,mkdir (dir_s1d3,0700 ));
@@ -3381,12 +3201,8 @@ TEST_F_FORK(layout1, remove_file) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);ASSERT_EQ (-1 ,unlink (file1_s1d1));ASSERT_EQ (EACCES , errno);
@@ -3407,9 +3223,6 @@ static void test_make_file(struct __test_metadata *const _metadata, }, {}, }; -const int ruleset_fd =create_ruleset (_metadata, access, rules); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file2_s1d1));
@@ -3425,8 +3238,7 @@ static void test_make_file(struct __test_metadata *const _metadata,ASSERT_EQ (0 ,unlink (file1_s1d3));ASSERT_EQ (0 ,unlink (file2_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, access, rules);ASSERT_EQ (-1 ,mknod (file1_s1d1, mode |0400 , dev));ASSERT_EQ (EACCES , errno);
@@ -3495,10 +3307,6 @@ TEST_F_FORK(layout1, make_sym) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file2_s1d1));
@@ -3510,8 +3318,7 @@ TEST_F_FORK(layout1, make_sym)ASSERT_EQ (0 ,unlink (file1_s1d3));ASSERT_EQ (0 ,unlink (file2_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);ASSERT_EQ (-1 ,symlink ("none" , file1_s1d1));ASSERT_EQ (EACCES , errno);
@@ -3540,17 +3347,12 @@ TEST_F_FORK(layout1, make_dir) }, {}, }; -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); - -ASSERT_LE (0 , ruleset_fd);ASSERT_EQ (0 ,unlink (file1_s1d1));ASSERT_EQ (0 ,unlink (file1_s1d2));ASSERT_EQ (0 ,unlink (file1_s1d3)); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, rules[0 ].access, rules);/* Uses file_* as directory names. */ ASSERT_EQ (-1 ,mkdir (file1_s1d1,0700 ));
@@ -3581,14 +3383,10 @@ TEST_F_FORK(layout1, proc_unlinked_file) {}, };int reg_fd, proc_fd; -const int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , - rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , + rules);ASSERT_EQ (EACCES ,test_open (file1_s1d2,O_RDWR ));ASSERT_EQ (0 ,test_open (file1_s1d2,O_RDONLY ));
@@ -3624,13 +3422,9 @@ TEST_F_FORK(layout1, proc_pipe) }, {}, }; -/* Limits read and write access to files tied to the filesystem. */ -const int ruleset_fd = -create_ruleset (_metadata, rules[0 ].access, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +/* Limits read and write access to files tied to the filesystem. */ +enforce_fs (_metadata, rules[0 ].access, rules);/* Checks enforcement for normal files. */ ASSERT_EQ (0 ,test_open (file1_s1d2,O_RDWR ));
@@ -3720,16 +3514,10 @@ TEST_F_FORK(layout1, truncate_unhandled) {}, }; -const __u64 handled =LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_WRITE_FILE ; -int ruleset_fd; -/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, handled, rules); - -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE |LANDLOCK_ACCESS_FS_WRITE_FILE , + rules);/* * Checks read right: truncate and open with O_TRUNC work, unless the
@@ -3802,17 +3590,13 @@ TEST_F_FORK(layout1, truncate) }, {}, }; -const __u64 handled =LANDLOCK_ACCESS_FS_READ_FILE | -LANDLOCK_ACCESS_FS_WRITE_FILE | -LANDLOCK_ACCESS_FS_TRUNCATE ; -int ruleset_fd;/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, handled, rules); - -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_WRITE_FILE | +LANDLOCK_ACCESS_FS_TRUNCATE , + rules);/* Checks read, write and truncate rights: truncation works. */ EXPECT_EQ (0 ,test_truncate (file_rwt));
@@ -3912,34 +3696,25 @@ TEST_F_FORK(layout1, ftruncate) }, {}, }; -int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; +int fd_layer0, fd_layer1, fd_layer2, fd_layer3; fd_layer0 =open (path,O_WRONLY );EXPECT_EQ (0 ,test_ftruncate (fd_layer0)); - ruleset_fd =create_ruleset (_metadata, handled1, layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, handled1, layer1); fd_layer1 =open (path,O_WRONLY );EXPECT_EQ (0 ,test_ftruncate (fd_layer0));EXPECT_EQ (0 ,test_ftruncate (fd_layer1)); - ruleset_fd =create_ruleset (_metadata, handled2, layer2); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, handled2, layer2); fd_layer2 =open (path,O_WRONLY );EXPECT_EQ (0 ,test_ftruncate (fd_layer0));EXPECT_EQ (0 ,test_ftruncate (fd_layer1));EXPECT_EQ (0 ,test_ftruncate (fd_layer2)); - ruleset_fd =create_ruleset (_metadata, handled3, layer3); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, handled3, layer3); fd_layer3 =open (path,O_WRONLY );EXPECT_EQ (0 ,test_ftruncate (fd_layer0));
@@ -4031,13 +3806,10 @@ TEST_F_FORK(ftruncate, open_and_ftruncate) }, {}, }; -int fd, ruleset_fd; +int fd;/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, variant->handled, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, variant->handled, rules); fd =open (path,O_WRONLY );EXPECT_EQ (variant->expected_open_result, (fd <0 ? errno :0 ));
@@ -4072,12 +3844,9 @@ TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) }, {}, }; -int fd, ruleset_fd; +int fd; - ruleset_fd =create_ruleset (_metadata, variant->handled, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, variant->handled, rules); fd =open (path,O_WRONLY );ASSERT_EQ (variant->expected_open_result, (fd <0 ? errno :0 ));
@@ -4122,10 +3891,7 @@ static int test_fs_ioc_getflags_ioctl(int fd)TEST (memfd_ftruncate_and_ioctl) { -const struct landlock_ruleset_attr attr = { - .handled_access_fs =ACCESS_ALL , - }; -int ruleset_fd, fd, i; +int fd, i;/* * We exercise the same test both with and without Landlock enabled, to
@@ -4147,10 +3913,7 @@ TEST(memfd_ftruncate_and_ioctl)ASSERT_EQ (0 ,close (fd));/* Enables Landlock. */ - ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_ALL ,NULL ); } }
@@ -4165,10 +3928,7 @@ static int test_fionread_ioctl(int fd) TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl) { -const struct landlock_ruleset_attr attr = { - .handled_access_fs =ACCESS_ALL , - }; -int ruleset_fd, fd; +int fd;/* * Checks that for files opened with O_PATH, both ioctl(2) and
@@ -4184,10 +3944,7 @@ TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)ASSERT_EQ (0 ,close (fd));/* Enables Landlock. */ - ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_ALL ,NULL );/* * Checks that after enabling Landlock,
@@ -4261,16 +4018,10 @@ struct space_resv {*/ TEST_F_FORK(layout1, blanket_permitted_ioctls) { -const struct landlock_ruleset_attr attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_IOCTL_DEV , - }; -int ruleset_fd, fd; +int fd;/* Enables Landlock. */ - ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_IOCTL_DEV ,NULL ); fd =open ("/dev/null" ,O_RDWR |O_CLOEXEC );ASSERT_LE (0 , fd);
@@ -4323,20 +4074,14 @@ TEST_F_FORK(layout1, blanket_permitted_ioctls) TEST_F_FORK(layout1, named_pipe_ioctl) {pid_t child_pid; -int fd, ruleset_fd; +int fd;const char *const path = file1_s1d1; -const struct landlock_ruleset_attr attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_IOCTL_DEV , - };ASSERT_EQ (0 ,unlink (path));ASSERT_EQ (0 ,mkfifo (path,0600 ));/* Enables Landlock. */ - ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_IOCTL_DEV ,NULL );/* The child process opens the pipe for writing. */ child_pid =fork ();
@@ -4411,20 +4156,14 @@ static int test_connect_named_unix(struct __test_metadata *const _metadata, TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) {const char *const path = file1_s1d1; -int srv_fd, cli_fd, ruleset_fd; -const struct landlock_ruleset_attr attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_IOCTL_DEV , - }; +int srv_fd, cli_fd;/* Sets up a server */ ASSERT_EQ (0 ,unlink (path)); srv_fd =set_up_named_unix_server (_metadata,SOCK_STREAM , path);/* Enables Landlock. */ - ruleset_fd =landlock_create_ruleset (&attr,sizeof (attr),0 ); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_IOCTL_DEV ,NULL );/* Sets up a client connection to it */ cli_fd =socket (AF_UNIX ,SOCK_STREAM ,0 );
@@ -4497,29 +4236,25 @@ TEST_F_FORK(ioctl, handle_dir_access_file) }, {}, }; -int file_fd, ruleset_fd; +int fd;/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, variant->handled, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, variant->handled, rules); - file_fd =open ("/dev/zero" , variant->open_mode); -ASSERT_LE (0 , file_fd); + fd =open ("/dev/zero" , variant->open_mode); +ASSERT_LE (0 , fd);/* Checks that IOCTL commands return the expected errors. */ -EXPECT_EQ (variant->expected_fionread_result, -test_fionread_ioctl (file_fd)); +EXPECT_EQ (variant->expected_fionread_result,test_fionread_ioctl (fd));/* Checks that unrestrictable commands are unrestricted. */ -EXPECT_EQ (0 ,ioctl (file_fd,FIOCLEX )); -EXPECT_EQ (0 ,ioctl (file_fd,FIONCLEX )); -EXPECT_EQ (0 ,ioctl (file_fd,FIONBIO , &flag)); -EXPECT_EQ (0 ,ioctl (file_fd,FIOASYNC , &flag)); -EXPECT_EQ (0 ,ioctl (file_fd,FIGETBSZ , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIOCLEX )); +EXPECT_EQ (0 ,ioctl (fd,FIONCLEX )); +EXPECT_EQ (0 ,ioctl (fd,FIONBIO , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIOASYNC , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIGETBSZ , &flag)); -ASSERT_EQ (0 ,close (file_fd)); +ASSERT_EQ (0 ,close (fd)); }TEST_F_FORK (ioctl, handle_dir_access_dir)
@@ -4532,13 +4267,10 @@ TEST_F_FORK(ioctl, handle_dir_access_dir) }, {}, }; -int dir_fd, ruleset_fd; +int dir_fd;/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, variant->handled, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, variant->handled, rules);/* * Ignore variant->open_mode for this test, as we intend to open a
@@ -4577,32 +4309,28 @@ TEST_F_FORK(ioctl, handle_file_access_file) }, {}, }; -int file_fd, ruleset_fd; +int fd;/* Enables Landlock. */ - ruleset_fd =create_ruleset (_metadata, variant->handled, rules); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, variant->handled, rules); - file_fd =open ("/dev/zero" , variant->open_mode); - ASSERT_LE(0, file_fd) + fd =open ("/dev/zero" , variant->open_mode); + ASSERT_LE(0, fd) {TH_LOG ("Failed to open /dev/zero: %s" ,strerror (errno)); }/* Checks that IOCTL commands return the expected errors. */ -EXPECT_EQ (variant->expected_fionread_result, -test_fionread_ioctl (file_fd)); +EXPECT_EQ (variant->expected_fionread_result,test_fionread_ioctl (fd));/* Checks that unrestrictable commands are unrestricted. */ -EXPECT_EQ (0 ,ioctl (file_fd,FIOCLEX )); -EXPECT_EQ (0 ,ioctl (file_fd,FIONCLEX )); -EXPECT_EQ (0 ,ioctl (file_fd,FIONBIO , &flag)); -EXPECT_EQ (0 ,ioctl (file_fd,FIOASYNC , &flag)); -EXPECT_EQ (0 ,ioctl (file_fd,FIGETBSZ , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIOCLEX )); +EXPECT_EQ (0 ,ioctl (fd,FIONCLEX )); +EXPECT_EQ (0 ,ioctl (fd,FIONBIO , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIOASYNC , &flag)); +EXPECT_EQ (0 ,ioctl (fd,FIGETBSZ , &flag)); -ASSERT_EQ (0 ,close (file_fd)); +ASSERT_EQ (0 ,close (fd)); }/*
@@ -4644,22 +4372,6 @@ FIXTURE_TEARDOWN(scoped_domains) { } -static void enforce_fs_resolve_unix (struct __test_metadata *const _metadata, -const struct rule rules[]) -{ -if (rules) { -int fd =create_ruleset (_metadata, -LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules); -enforce_ruleset (_metadata, fd); -EXPECT_EQ (0 ,close (fd)); - }else { -struct landlock_ruleset_attr attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_RESOLVE_UNIX , - }; -drop_access_rights (_metadata, &attr); - } -} -/* * Flags for test_connect_to_parent and test_connect_to_child: *
@@ -4690,9 +4402,9 @@ static void test_connect_to_parent(struct __test_metadata *const _metadata,char buf[1 ];if (variant->domain_both) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX ,NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules);unlink (path);ASSERT_EQ (0 ,pipe2 (readiness_pipe,O_CLOEXEC ));
@@ -4702,9 +4414,11 @@ static void test_connect_to_parent(struct __test_metadata *const _metadata,if (child_pid ==0 ) {if (variant->domain_child) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , +NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + rules);/* Wait for server to be available. */ EXPECT_EQ (0 ,close (readiness_pipe[1 ]));
@@ -4730,9 +4444,9 @@ static void test_connect_to_parent(struct __test_metadata *const _metadata, }if (variant->domain_parent) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX ,NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules); srv_fd =set_up_named_unix_server (_metadata, sock_type, path);
@@ -4771,9 +4485,9 @@ static void test_connect_to_child(struct __test_metadata *const _metadata,char buf[1 ];if (variant->domain_both) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX ,NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules);unlink (path);ASSERT_EQ (0 ,pipe2 (readiness_pipe,O_CLOEXEC ));
@@ -4784,9 +4498,11 @@ static void test_connect_to_child(struct __test_metadata *const _metadata,if (child_pid ==0 ) {if (variant->domain_child) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , +NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , + rules); srv_fd =set_up_named_unix_server (_metadata, sock_type, path);
@@ -4810,9 +4526,9 @@ static void test_connect_to_child(struct __test_metadata *const _metadata, }if (variant->domain_parent) -enforce_fs_resolve_unix (_metadata,NULL ); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX ,NULL );else if (flags &ENFORCE_ALL ) -enforce_fs_resolve_unix (_metadata, rules); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX , rules);/* Wait for server to be available. */ EXPECT_EQ (0 ,close (readiness_pipe[1 ]));
@@ -5021,11 +4737,7 @@ TEST_F_FORK(coredump, socket_not_restricted)set_core_pattern (_metadata, core_pattern);/* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs = -LANDLOCK_ACCESS_FS_RESOLVE_UNIX , - }); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_RESOLVE_UNIX ,NULL );/* Fork a child that crashes. */ child_pid =fork ();
@@ -5212,13 +4924,9 @@ TEST_F_FORK(layout1_bind, same_content_same_file) }, {}, }; -int ruleset_fd;/* Sets rules for the parent directories. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer1_parent); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer1_parent);/* Checks source hierarchy. */ ASSERT_EQ (0 ,test_open (file1_s1d1,O_RDONLY ));
@@ -5237,10 +4945,7 @@ TEST_F_FORK(layout1_bind, same_content_same_file)ASSERT_EQ (0 ,test_open (dir_s2d2,O_RDONLY |O_DIRECTORY ));/* Sets rules for the mount points. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer2_mount_point); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer2_mount_point);/* Checks source hierarchy. */ ASSERT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY ));
@@ -5261,10 +4966,7 @@ TEST_F_FORK(layout1_bind, same_content_same_file)ASSERT_EQ (0 ,test_open (bind_dir_s1d3,O_RDONLY |O_DIRECTORY ));/* Sets a (shared) rule only on the source. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer3_source); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer3_source);/* Checks source hierarchy. */ ASSERT_EQ (EACCES ,test_open (file1_s1d2,O_RDONLY ));
@@ -5285,10 +4987,7 @@ TEST_F_FORK(layout1_bind, same_content_same_file)ASSERT_EQ (EACCES ,test_open (bind_dir_s1d3,O_RDONLY |O_DIRECTORY ));/* Sets a (shared) rule only on the destination. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer4_destination); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer4_destination);/* Checks source hierarchy. */ ASSERT_EQ (EACCES ,test_open (file1_s1d3,O_RDONLY ));
@@ -5313,13 +5012,10 @@ TEST_F_FORK(layout1_bind, reparent_cross_mount) }, {}, }; -int ruleset_fd =create_ruleset ( - _metadata, -LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_EXECUTE , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_EXECUTE , + layer1);/* Checks basic denied move. */ ASSERT_EQ (-1 ,rename (file1_s1d1, file1_s1d2));
@@ -5376,10 +5072,6 @@ TEST_F_FORK(layout1_bind, path_disconnected)create_ruleset (_metadata,ACCESS_RW , layer3_only_s1d2);int bind_s1d3_fd; -ASSERT_LE (0 , ruleset_fd_l1); -ASSERT_LE (0 , ruleset_fd_l2); -ASSERT_LE (0 , ruleset_fd_l3); -enforce_ruleset (_metadata, ruleset_fd_l1);EXPECT_EQ (0 ,close (ruleset_fd_l1));
@@ -5483,8 +5175,6 @@ TEST_F_FORK(layout1_bind, path_disconnected_rename) ruleset_fd_l1 =create_ruleset (_metadata,ACCESS_ALL , layer1); ruleset_fd_l2 =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , layer2_only_s1d2); -ASSERT_LE (0 , ruleset_fd_l1); -ASSERT_LE (0 , ruleset_fd_l2);enforce_ruleset (_metadata, ruleset_fd_l1);EXPECT_EQ (0 ,close (ruleset_fd_l1));
@@ -5630,7 +5320,7 @@ TEST_F_FORK(layout1_bind, path_disconnected_link) }, {} }; -int ruleset_fd, bind_s1d3_fd; +int bind_s1d3_fd;/* Removes unneeded files created by layout1, otherwise it will EEXIST. */ ASSERT_EQ (0 ,unlink (file1_s1d2));
@@ -5653,10 +5343,7 @@ TEST_F_FORK(layout1_bind, path_disconnected_link)TH_LOG ("Failed to create %s: %s" , dir_s4d2,strerror (errno)); } - ruleset_fd =create_ruleset (_metadata,ACCESS_ALL , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -EXPECT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_ALL , layer1);/* From disconnected to connected. */ ASSERT_EQ (0 ,linkat (bind_s1d3_fd, file1_name,AT_FDCWD , file1_s2d2,0 ))
@@ -6194,7 +5881,6 @@ TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange)int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; ruleset_fd =create_ruleset (_metadata, handled_access, rules); -ASSERT_LE (0 , ruleset_fd);/* Adds rule for the covered directory. */ if (variant->allowed_s2d2) {
@@ -7127,7 +6813,6 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) }, {}, }; -int ruleset_fd;size_t i;const char *path_entry;
@@ -7135,10 +6820,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)SKIP (return,"overlayfs is not supported (test)" );/* Sets rules on base directories (i.e. outside overlay scope). */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer1_base); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer1_base);/* Checks lower layer. */ for_each_path (lower_base_files, path_entry, i) {
@@ -7183,10 +6865,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) }/* Sets rules on data directories (i.e. inside overlay scope). */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer2_data); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer2_data);/* Checks merge. */ for_each_path (merge_base_files, path_entry, i) {
@@ -7200,10 +6879,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) }/* Same checks with tighter rules. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer3_subdirs); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer3_subdirs);/* Checks changes for lower layer. */ for_each_path (lower_base_files, path_entry, i) {
@@ -7225,10 +6901,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) }/* Sets rules directly on overlayed files. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer4_files); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer4_files);/* Checks unchanged accesses on lower layer. */ for_each_path (lower_sub_files, path_entry, i) {
@@ -7253,10 +6926,7 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) }/* Only allowes access to the merge hierarchy. */ - ruleset_fd =create_ruleset (_metadata,ACCESS_RW , layer5_merge_only); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,ACCESS_RW , layer5_merge_only);/* Checks new accesses on lower layer. */ for_each_path (lower_sub_files, path_entry, i) {
@@ -7442,11 +7112,7 @@ static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, }, {}, }; -const struct landlock_ruleset_attr layer2_deny_everything_attr = { - .handled_access_fs =LANDLOCK_ACCESS_FS_READ_FILE , - };const char *const dev_null_path ="/dev/null" ; -int ruleset_fd;if (self->skip_test)SKIP (return,"this filesystem is not supported (test)" );
@@ -7455,22 +7121,14 @@ static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,EXPECT_EQ (0 ,test_open (dev_null_path,O_RDONLY |O_CLOEXEC ));EXPECT_EQ (0 ,test_open (variant->file_path,O_RDONLY |O_CLOEXEC )); - ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , - layer1_allow_read_file); -EXPECT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -EXPECT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , + layer1_allow_read_file);EXPECT_EQ (EACCES ,test_open (dev_null_path,O_RDONLY |O_CLOEXEC ));EXPECT_EQ (0 ,test_open (variant->file_path,O_RDONLY |O_CLOEXEC ));/* Forbids directory reading. */ - ruleset_fd = -landlock_create_ruleset (&layer2_deny_everything_attr, -sizeof (layer2_deny_everything_attr),0 ); -EXPECT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -EXPECT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_READ_FILE ,NULL );/* Checks with Landlock and forbidden access. */ EXPECT_EQ (EACCES ,test_open (dev_null_path,O_RDONLY |O_CLOEXEC ));
@@ -7532,7 +7190,6 @@ TEST_F_FORK(layout3_fs, release_inodes) ruleset_fd =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_DIR , layer1); -ASSERT_LE (0 , ruleset_fd);/* Unmount the filesystem while it is being used by a ruleset. */ set_cap (_metadata,CAP_SYS_ADMIN );
@@ -7639,11 +7296,7 @@ TEST_F(audit_layout1, execute_make)test_execute (_metadata,0 , file1_s1d1);test_check_exec (_metadata,0 , file1_s1d1); -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs = -LANDLOCK_ACCESS_FS_EXECUTE , - }); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_EXECUTE ,NULL );test_execute (_metadata,EACCES , file1_s1d1); EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute",
@@ -7670,9 +7323,7 @@ TEST_F(audit_layout1, execute_read)test_execute (_metadata,0 , file1_s1d1);test_check_exec (_metadata,0 , file1_s1d1); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );/* * The only difference with the previous audit_layout1.execute_read test is
@@ -7694,9 +7345,7 @@ TEST_F(audit_layout1, write_file) {struct audit_records records; -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (EACCES ,test_open (file1_s1d1,O_WRONLY )); EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
@@ -7711,9 +7360,7 @@ TEST_F(audit_layout1, read_file) {struct audit_records records; -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (EACCES ,test_open (file1_s1d1,O_RDONLY )); EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file",
@@ -7728,9 +7375,7 @@ TEST_F(audit_layout1, read_dir) {struct audit_records records; -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (EACCES ,test_open (dir_s1d1,O_DIRECTORY )); EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir",
@@ -7748,9 +7393,7 @@ TEST_F(audit_layout1, remove_dir)EXPECT_EQ (0 ,unlink (file1_s1d3));EXPECT_EQ (0 ,unlink (file2_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,rmdir (dir_s1d3));EXPECT_EQ (EACCES , errno);
@@ -7771,9 +7414,7 @@ TEST_F(audit_layout1, remove_file) {struct audit_records records; -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,unlink (file1_s1d3));EXPECT_EQ (EACCES , errno);
@@ -7791,9 +7432,7 @@ TEST_F(audit_layout1, make_char)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFCHR |0644 ,0 ));EXPECT_EQ (EACCES , errno);
@@ -7811,9 +7450,7 @@ TEST_F(audit_layout1, make_dir)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mkdir (file1_s1d3,0755 ));EXPECT_EQ (EACCES , errno);
@@ -7831,9 +7468,7 @@ TEST_F(audit_layout1, make_reg)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFREG |0644 ,0 ));EXPECT_EQ (EACCES , errno);
@@ -7851,9 +7486,7 @@ TEST_F(audit_layout1, make_sock)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFSOCK |0644 ,0 ));EXPECT_EQ (EACCES , errno);
@@ -7871,9 +7504,7 @@ TEST_F(audit_layout1, make_fifo)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFIFO |0644 ,0 ));EXPECT_EQ (EACCES , errno);
@@ -7891,9 +7522,7 @@ TEST_F(audit_layout1, make_block)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,mknod (file1_s1d3,S_IFBLK |0644 ,0 ));EXPECT_EQ (EACCES , errno);
@@ -7911,9 +7540,7 @@ TEST_F(audit_layout1, make_sym)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,symlink ("target" , file1_s1d3));EXPECT_EQ (EACCES , errno);
@@ -7931,10 +7558,7 @@ TEST_F(audit_layout1, refer_handled)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs = -LANDLOCK_ACCESS_FS_REFER , - }); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_REFER ,NULL );EXPECT_EQ (-1 ,link (file1_s1d1, file1_s1d3));EXPECT_EQ (EXDEV , errno);
@@ -7956,12 +7580,9 @@ TEST_F(audit_layout1, refer_make)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs = -LANDLOCK_ACCESS_FS_MAKE_REG | -LANDLOCK_ACCESS_FS_REFER , - }); +enforce_fs (_metadata, +LANDLOCK_ACCESS_FS_MAKE_REG |LANDLOCK_ACCESS_FS_REFER , +NULL );EXPECT_EQ (-1 ,link (file1_s1d1, file1_s1d3));EXPECT_EQ (EACCES , errno);
@@ -7981,9 +7602,7 @@ TEST_F(audit_layout1, refer_rename)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (EACCES ,test_rename (file1_s1d2, file1_s2d3)); EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
@@ -8003,9 +7622,7 @@ TEST_F(audit_layout1, refer_exchange)EXPECT_EQ (0 ,unlink (file1_s1d3)); -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );/* * The only difference with the previous audit_layout1.refer_rename test is
@@ -8043,12 +7660,8 @@ TEST_F(audit_layout1, refer_rename_half) }, {}, }; -int ruleset_fd = -create_ruleset (_metadata,LANDLOCK_ACCESS_FS_REFER , layer1); -ASSERT_LE (0 , ruleset_fd); -enforce_ruleset (_metadata, ruleset_fd); -ASSERT_EQ (0 ,close (ruleset_fd)); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_REFER , layer1);ASSERT_EQ (-1 ,rename (dir_s1d2, dir_s2d3));ASSERT_EQ (EXDEV , errno);
@@ -8066,9 +7679,7 @@ TEST_F(audit_layout1, truncate) {struct audit_records records; -drop_access_rights (_metadata, &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL );EXPECT_EQ (-1 ,truncate (file1_s1d3,0 ));EXPECT_EQ (EACCES , errno);
@@ -8085,12 +7696,7 @@ TEST_F(audit_layout1, ioctl_dev)struct audit_records records;int fd; -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs = -ACCESS_ALL & - ~LANDLOCK_ACCESS_FS_READ_FILE , - }); +enforce_fs (_metadata,ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE ,NULL ); fd =open ("/dev/null" ,O_RDONLY |O_CLOEXEC );ASSERT_LE (0 , fd);
@@ -8116,10 +7722,7 @@ TEST_F(audit_layout1, resolve_unix) child_pid =fork ();ASSERT_LE (0 , child_pid);if (!child_pid) { -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs =ACCESS_ALL , - }); +enforce_fs (_metadata,ACCESS_ALL ,NULL ); cli_fd =socket (AF_UNIX ,SOCK_STREAM ,0 );ASSERT_LE (0 , cli_fd);
@@ -8148,11 +7751,7 @@ TEST_F(audit_layout1, mount) {struct audit_records records; -drop_access_rights (_metadata, - &(struct landlock_ruleset_attr ){ - .handled_access_fs = -LANDLOCK_ACCESS_FS_EXECUTE , - }); +enforce_fs (_metadata,LANDLOCK_ACCESS_FS_EXECUTE ,NULL );set_cap (_metadata,CAP_SYS_ADMIN );EXPECT_EQ (-1 ,mount (NULL , dir_s3d2,NULL ,MS_RDONLY ,NULL ));
Signature · 1 line
2.53.0
PATCH v8 12/12 landlock: Document FS access right for pathname UNIX sockets
In reply to Günther NoackAdd LANDLOCK_ACCESS_FS_RESOLVE_UNIX to the example code, and explain it in the section about previous limitations. The bulk of the interesting flag documentation lives in the kernel header and is included in the Sphinx rendering.
--- Documentation/userspace-api/landlock.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
Documentation/userspace-api/landlock.rst +13 -1
@@ -77,7 +77,8 @@ to be explicit about the denied-by-default access rights. LANDLOCK_ACCESS_FS_MAKE_SYM | LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_TRUNCATE | - LANDLOCK_ACCESS_FS_IOCTL_DEV, + LANDLOCK_ACCESS_FS_IOCTL_DEV | + LANDLOCK_ACCESS_FS_RESOLVE_UNIX, .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP,
@@ -127,6 +128,10 @@ version, and only use the available subset of access rights: /* Removes LANDLOCK_SCOPE_* for ABI < 6 */ ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | LANDLOCK_SCOPE_SIGNAL); + __attribute__((fallthrough)); + case 6 ... 8: + /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 9 */ + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX; } This enables the creation of an inclusive ruleset that will contain our rules.
@@ -685,6 +690,13 @@ enforce Landlock rulesets across all threads of the calling process using the ``LANDLOCK_RESTRICT_SELF_TSYNC`` flag passed to sys_landlock_restrict_self(). +Pathname UNIX sockets (ABI < 9) +------------------------------- + +Starting with the Landlock ABI version 9, it is possible to restrict +connections to pathname UNIX domain sockets (:manpage:`unix(7)`) using +the new ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX`` right. + .. _kernel_support: Kernel support
Signature · 1 line
2.53.0