* [PATCH v3 1/5] lsm: Add hook security_unix_find
2026-01-19 20:34 [PATCH v3 0/5] landlock: Pathname-based UNIX connect() control Günther Noack
@ 2026-01-19 20:34 ` Günther Noack
2026-02-04 10:25 ` Günther Noack
2026-01-19 20:34 ` [PATCH v3 2/5] landlock: Control pathname UNIX domain socket resolution by path Günther Noack
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Günther Noack @ 2026-01-19 20:34 UTC (permalink / raw)
To: Mickaël Salaün, John Johansen, Paul Moore, James Morris,
Serge E . Hallyn
Cc: Günther Noack, Justin Suess, linux-security-module,
Tingmao Wang, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Jann Horn, Tahera Fahimi, Simon Horman, netdev,
Alexander Viro, Christian Brauner
From: Justin Suess <utilityemal77@gmail.com>
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.
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.
Why existing hooks are unsuitable:
(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.
Cc: Günther Noack <gnoack3000@gmail.com>
Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
include/linux/lsm_hook_defs.h | 4 ++++
include/linux/security.h | 11 +++++++++++
net/unix/af_unix.c | 9 +++++++++
security/security.c | 20 ++++++++++++++++++++
4 files changed, 44 insertions(+)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..84c1fac3ada6 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -317,6 +317,10 @@ 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, int type, 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)
diff --git a/include/linux/security.h b/include/linux/security.h
index 83a646d72f6f..cdcd340b085c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -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, int type, int flags);
+
+#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+static inline int security_unix_find(const struct path *path, int type, 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);
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d0511225799b..227467236930 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1226,6 +1226,15 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
if (!S_ISSOCK(inode->i_mode))
goto path_put;
+ /*
+ * We call the hook because we know that the inode is a socket
+ * and we hold a valid reference to it via the path.
+ */
+ err = security_unix_find(&path, type, flags);
+ if (err)
+ goto path_put;
+
+ err = -ECONNREFUSED;
sk = unix_find_socket_byinode(inode);
if (!sk)
goto path_put;
diff --git a/security/security.c b/security/security.c
index 31a688650601..df4e3f99de7d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -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
+ * @type: type of the socket
+ * @flags: flags associated with the socket
+ *
+ * This hook is called to check permissions before connecting to a named
+ * AF_UNIX socket.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_unix_find(const struct path *path, int type, int flags)
+{
+ return call_int_hook(unix_find, path, type, 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
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v3 1/5] lsm: Add hook security_unix_find
2026-01-19 20:34 ` [PATCH v3 1/5] lsm: Add hook security_unix_find Günther Noack
@ 2026-02-04 10:25 ` Günther Noack
2026-02-05 10:36 ` Mickaël Salaün
2026-02-09 17:09 ` Paul Moore
0 siblings, 2 replies; 9+ messages in thread
From: Günther Noack @ 2026-02-04 10:25 UTC (permalink / raw)
To: Günther Noack, Paul Moore, John Johansen, Tingmao Wang
Cc: Mickaël Salaün, James Morris, Serge E . Hallyn,
Justin Suess, linux-security-module, Samasth Norway Ananda,
Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
Demi Marie Obenour, Alyssa Ross, Jann Horn, Tahera Fahimi,
Simon Horman, netdev, Alexander Viro, Christian Brauner
Hello!
John:
Friendly ping; as Paul said in [1], we would appreciate a look from
the AppArmor side whether this path-based LSM hook makes sense for
you.
Everyone:
In [2], we are currently discussing how the UNIX restriction feature
would work in the bigger scheme in Landlock, and the current plan is
that long-term we would like to support semantics where a UNIX
connection attempt is allowed if EITHER:
(a) the path is allow-listed in the policy, OR
(b) the server side we connect to is part of the same Landlock
sandbox ("domain")
With the currently proposed hook, (a) can be checked in the
security_unix_find() hook, and (b) can be checked in the
security_hook_socket_connect() hook. Unfortunately, it also would
mean that if the (a) check fails, we would have to store that
information on the side (struct sock LSM blob?), return 0 from (a) and
then later use that information in hook (b), so that we can check
whether maybe the second possible condition is met.
Q: The passing of information across multiple LSM hooks is slightly
more complicated than I had hoped; is this an approach that is
recommended?
Therefore, in [2], Tingmao is suggesting that we change the
security_unix_find() hook and pass the "other" struct sock instead of
the type.
There is obviously a balance between hooks that are very generic and
usable across multiple LSMs and hooks that are convenient to use for
every LSM.
Paul:
You have previously said that you would like hooks to be generic and
ideally reflect the arguments of the same function that they are
called from [3].
Q: Would it be acceptable to change the hook arguments, if we can then
avoid passing additional data between hooks through that side-storage?
You can see Tingmao's proposal for that in [2]. TL;DR: It moves the
call to security_unix_find() just after the place where the sk
variable ("other"-side socket) is looked up and then calls the hook
with the sk as argument instead of with the type. That way, we can do
both check (a) and (b) from above in the same hook and do not need to
store data on the side. Is that an acceptable trade-off for the LSM
interface?
Thanks,
—Günther
[1] https://lore.kernel.org/all/CAHC9VhQZ_J9316Us0squV_f-MjYXPcex34BnJ14vEBxS9Jyjbg@mail.gmail.com/
[2] https://lore.kernel.org/all/e6b6b069-384c-4c45-a56b-fa54b26bc72a@maowtm.org/
[3] https://lore.kernel.org/all/CAHC9VhQ234xihpndTs4e5ToNJ3tGCsP7AVtXuz8GajG-_jn3Ow@mail.gmail.com/
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 1/5] lsm: Add hook security_unix_find
2026-02-04 10:25 ` Günther Noack
@ 2026-02-05 10:36 ` Mickaël Salaün
2026-02-09 17:09 ` Paul Moore
1 sibling, 0 replies; 9+ messages in thread
From: Mickaël Salaün @ 2026-02-05 10:36 UTC (permalink / raw)
To: Günther Noack
Cc: Günther Noack, Paul Moore, John Johansen, Tingmao Wang,
James Morris, Serge E . Hallyn, Justin Suess,
linux-security-module, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Jann Horn, Tahera Fahimi, Simon Horman, netdev,
Alexander Viro, Christian Brauner
On Wed, Feb 04, 2026 at 11:25:33AM +0100, Günther Noack wrote:
> Hello!
>
>
> John:
>
> Friendly ping; as Paul said in [1], we would appreciate a look from
> the AppArmor side whether this path-based LSM hook makes sense for
> you.
FYI, we plan to merge this patch series with another one where this new
LSM hook will be used as Günther explained:
>
>
> Everyone:
>
> In [2], we are currently discussing how the UNIX restriction feature
> would work in the bigger scheme in Landlock, and the current plan is
> that long-term we would like to support semantics where a UNIX
> connection attempt is allowed if EITHER:
>
> (a) the path is allow-listed in the policy, OR
> (b) the server side we connect to is part of the same Landlock
> sandbox ("domain")
>
>
> With the currently proposed hook, (a) can be checked in the
> security_unix_find() hook, and (b) can be checked in the
> security_hook_socket_connect() hook. Unfortunately, it also would
> mean that if the (a) check fails, we would have to store that
> information on the side (struct sock LSM blob?), return 0 from (a) and
> then later use that information in hook (b), so that we can check
> whether maybe the second possible condition is met.
>
> Q: The passing of information across multiple LSM hooks is slightly
> more complicated than I had hoped; is this an approach that is
> recommended?
>
> Therefore, in [2], Tingmao is suggesting that we change the
> security_unix_find() hook and pass the "other" struct sock instead of
> the type.
This new approach is much more generic and should please any LSM wishing
to use it.
>
> There is obviously a balance between hooks that are very generic and
> usable across multiple LSMs and hooks that are convenient to use for
> every LSM.
>
> Paul:
>
> You have previously said that you would like hooks to be generic and
> ideally reflect the arguments of the same function that they are
> called from [3].
>
> Q: Would it be acceptable to change the hook arguments, if we can then
> avoid passing additional data between hooks through that side-storage?
> You can see Tingmao's proposal for that in [2]. TL;DR: It moves the
> call to security_unix_find() just after the place where the sk
> variable ("other"-side socket) is looked up and then calls the hook
> with the sk as argument instead of with the type. That way, we can do
> both check (a) and (b) from above in the same hook and do not need to
> store data on the side. Is that an acceptable trade-off for the LSM
> interface?
I think it's a good interface because it let any LSM check both the
resolved path and the resolved socket (without race condition), which
makes sense and align with most other hooks.
>
> Thanks,
> —Günther
>
> [1] https://lore.kernel.org/all/CAHC9VhQZ_J9316Us0squV_f-MjYXPcex34BnJ14vEBxS9Jyjbg@mail.gmail.com/
> [2] https://lore.kernel.org/all/e6b6b069-384c-4c45-a56b-fa54b26bc72a@maowtm.org/
> [3] https://lore.kernel.org/all/CAHC9VhQ234xihpndTs4e5ToNJ3tGCsP7AVtXuz8GajG-_jn3Ow@mail.gmail.com/
>
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 1/5] lsm: Add hook security_unix_find
2026-02-04 10:25 ` Günther Noack
2026-02-05 10:36 ` Mickaël Salaün
@ 2026-02-09 17:09 ` Paul Moore
1 sibling, 0 replies; 9+ messages in thread
From: Paul Moore @ 2026-02-09 17:09 UTC (permalink / raw)
To: Günther Noack
Cc: Günther Noack, John Johansen, Tingmao Wang,
Mickaël Salaün, James Morris, Serge E . Hallyn,
Justin Suess, linux-security-module, Samasth Norway Ananda,
Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
Demi Marie Obenour, Alyssa Ross, Jann Horn, Tahera Fahimi,
Simon Horman, netdev, Alexander Viro, Christian Brauner
On Wed, Feb 4, 2026 at 5:25 AM Günther Noack <gnoack@google.com> wrote:
>
> Paul:
>
> You have previously said that you would like hooks to be generic and
> ideally reflect the arguments of the same function that they are
> called from [3].
To clarify, I didn't say that it is generally ideal for the LSM hook
to reflect the arguments of the calling function; while that might be
a good starting point, we have plenty of examples where that is not
desirable. In this particular case I said it seems like it would be a
good idea to pass the "type" and "flags" parameters from the caller to
the LSM hook.
> Q: Would it be acceptable to change the hook arguments, if we can then
> avoid passing additional data between hooks through that side-storage?
If you're passing the sock, I think we can skip passing the type,
however, I could envision someone wanting the path in addition to just
the sock, but let's wait to hear back from the AppArmor folks.
--
paul-moore.com
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 2/5] landlock: Control pathname UNIX domain socket resolution by path
2026-01-19 20:34 [PATCH v3 0/5] landlock: Pathname-based UNIX connect() control Günther Noack
2026-01-19 20:34 ` [PATCH v3 1/5] lsm: Add hook security_unix_find Günther Noack
@ 2026-01-19 20:34 ` Günther Noack
2026-01-19 20:34 ` [PATCH v3 3/5] samples/landlock: Add support for named UNIX domain socket restrictions Günther Noack
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Günther Noack @ 2026-01-19 20:34 UTC (permalink / raw)
To: Mickaël Salaün, John Johansen
Cc: Günther Noack, Justin Suess, Jann Horn,
linux-security-module, Tingmao Wang, Samasth Norway Ananda,
Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
Demi Marie Obenour, Alyssa Ross, Tahera Fahimi
* Add a new access right LANDLOCK_ACCESS_FS_RESOLVE_UNIX, which
controls the look up operations for named UNIX domain sockets. The
resolution happens during connect() and sendmsg() (depending on
socket type).
* 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 adaptions to keep the tests working.
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.
Cc: Justin Suess <utilityemal77@gmail.com>
Cc: Mickaël Salaün <mic@digikod.net>
Suggested-by: Jann Horn <jannh@google.com>
Link: https://github.com/landlock-lsm/linux/issues/36
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
include/uapi/linux/landlock.h | 5 +++++
security/landlock/access.h | 2 +-
security/landlock/audit.c | 1 +
security/landlock/fs.c | 18 +++++++++++++++++-
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 +++--
8 files changed, 30 insertions(+), 7 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 75fd7f5e6cc3..1d9917fc16d2 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -233,6 +233,10 @@ 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.
*
* 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
@@ -318,6 +322,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 */
/**
diff --git a/security/landlock/access.h b/security/landlock/access.h
index 7961c6630a2d..c7784922be3c 100644
--- a/security/landlock/access.h
+++ b/security/landlock/access.h
@@ -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);
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index e899995f1fd5..3e754d2fb0e1 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -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);
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 8205673c8b1c..e69f2f809f02 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -9,6 +9,7 @@
* Copyright © 2023-2024 Google LLC
*/
+#include "linux/net.h"
#include <asm/ioctls.h>
#include <kunit/test.h>
#include <linux/atomic.h>
@@ -314,7 +315,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 */
/*
@@ -1588,6 +1590,19 @@ static int hook_path_truncate(const struct path *const path)
return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
}
+static int hook_unix_find(const struct path *const path, int type, int flags)
+{
+ /* Lookup for the purpose of saving coredumps is OK. */
+ if (flags & SOCK_COREDUMP)
+ return 0;
+
+ /* Only stream, dgram and seqpacket sockets are restricted. */
+ if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET)
+ return 0;
+
+ return current_check_access_path(path, LANDLOCK_ACCESS_FS_RESOLVE_UNIX);
+}
+
/* File hooks */
/**
@@ -1872,6 +1887,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),
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 65b5ff051674..a07302e6bbcb 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -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)
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 0116e9f93ffe..66fd196be85a 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -161,7 +161,7 @@ static const struct file_operations ruleset_fops = {
* Documentation/userspace-api/landlock.rst should be updated to reflect the
* UAPI change.
*/
-const int landlock_abi_version = 7;
+const int landlock_abi_version = 8;
/**
* sys_landlock_create_ruleset - Create a new ruleset
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 7b69002239d7..f4b1a275d8d9 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -76,7 +76,7 @@ TEST(abi_version)
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
- ASSERT_EQ(7, landlock_create_ruleset(NULL, 0,
+ ASSERT_EQ(8, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 968a91c927a4..b318627e7561 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -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.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 3/5] samples/landlock: Add support for named UNIX domain socket restrictions
2026-01-19 20:34 [PATCH v3 0/5] landlock: Pathname-based UNIX connect() control Günther Noack
2026-01-19 20:34 ` [PATCH v3 1/5] lsm: Add hook security_unix_find Günther Noack
2026-01-19 20:34 ` [PATCH v3 2/5] landlock: Control pathname UNIX domain socket resolution by path Günther Noack
@ 2026-01-19 20:34 ` Günther Noack
2026-01-19 20:34 ` [PATCH v3 4/5] landlock/selftests: Test " Günther Noack
2026-01-19 20:34 ` [PATCH v3 5/5] landlock: Document FS access right for pathname UNIX sockets Günther Noack
4 siblings, 0 replies; 9+ messages in thread
From: Günther Noack @ 2026-01-19 20:34 UTC (permalink / raw)
To: Mickaël Salaün, John Johansen
Cc: Günther Noack, Justin Suess, linux-security-module,
Tingmao Wang, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Jann Horn, Tahera Fahimi
The 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.
Cc: Justin Suess <utilityemal77@gmail.com>
Cc: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
samples/landlock/sandboxer.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index e7af02f98208..8dab67ac7915 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -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 7
+#define LANDLOCK_ABI_LAST 8
#define XSTR(s) #s
#define STR(s) XSTR(s)
@@ -444,6 +446,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
"provided by ABI version %d (instead of %d).\n",
LANDLOCK_ABI_LAST, abi);
__attribute__((fallthrough));
+ case 7:
+ /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 8 */
+ ruleset_attr.handled_access_fs &=
+ ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
+ __attribute__((fallthrough));
case LANDLOCK_ABI_LAST:
break;
default:
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/5] landlock/selftests: Test named UNIX domain socket restrictions
2026-01-19 20:34 [PATCH v3 0/5] landlock: Pathname-based UNIX connect() control Günther Noack
` (2 preceding siblings ...)
2026-01-19 20:34 ` [PATCH v3 3/5] samples/landlock: Add support for named UNIX domain socket restrictions Günther Noack
@ 2026-01-19 20:34 ` Günther Noack
2026-01-19 20:34 ` [PATCH v3 5/5] landlock: Document FS access right for pathname UNIX sockets Günther Noack
4 siblings, 0 replies; 9+ messages in thread
From: Günther Noack @ 2026-01-19 20:34 UTC (permalink / raw)
To: Mickaël Salaün, John Johansen
Cc: Günther Noack, Justin Suess, linux-security-module,
Tingmao Wang, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Jann Horn, Tahera Fahimi
* Exercise the access right for connect() and sendmsg() on named UNIX
domain sockets, in various combinations of Landlock domains and
socket types.
* Extract common helpers from an existing IOCTL test that
also uses pathname unix(7) sockets.
Cc: Justin Suess <utilityemal77@gmail.com>
Cc: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
tools/testing/selftests/landlock/fs_test.c | 218 +++++++++++++++++++--
1 file changed, 202 insertions(+), 16 deletions(-)
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index b318627e7561..5c2780efa266 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -4358,30 +4358,61 @@ 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);
+
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+ 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(int fd, const char *const path)
+{
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ 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 +4424,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(cli_fd, path));
/* FIONREAD and other IOCTLs should not be forbidden. */
EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
@@ -4570,6 +4599,163 @@ TEST_F_FORK(ioctl, handle_file_access_file)
ASSERT_EQ(0, close(file_fd));
}
+/* clang-format off */
+FIXTURE(unix_socket) {};
+
+FIXTURE_SETUP(unix_socket) {};
+
+FIXTURE_TEARDOWN(unix_socket) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(unix_socket)
+{
+ const __u64 handled;
+ const __u64 allowed;
+ const int sock_type;
+ const int expected;
+ const bool use_sendto;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, stream_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = 0,
+ .sock_type = SOCK_STREAM,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, stream_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .sock_type = SOCK_STREAM,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = 0,
+ .sock_type = SOCK_DGRAM,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .sock_type = SOCK_DGRAM,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = 0,
+ .sock_type = SOCK_DGRAM,
+ .use_sendto = true,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .sock_type = SOCK_DGRAM,
+ .use_sendto = true,
+ .expected = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_not_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = 0,
+ .sock_type = SOCK_SEQPACKET,
+ .expected = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_and_allowed)
+{
+ /* clang-format on */
+ .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
+ .sock_type = SOCK_SEQPACKET,
+ .expected = 0,
+};
+
+/*
+ * 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(int fd, const char *const path)
+{
+ static const char buf[] = "dummy";
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ };
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
+ sizeof(addr)) == -1)
+ return errno;
+ return 0;
+}
+
+TEST_F_FORK(unix_socket, test)
+{
+ const char *const path = "sock";
+ int cli_fd, srv_fd, ruleset_fd, res;
+ const struct rule rules[] = {
+ {
+ .path = path,
+ .access = variant->allowed,
+ },
+ {},
+ };
+
+ /* Sets up a server */
+ srv_fd = set_up_named_unix_server(_metadata, variant->sock_type, path);
+
+ /* 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));
+
+ /* Sets up a client connection to it */
+ cli_fd = socket(AF_UNIX, variant->sock_type, 0);
+ ASSERT_LE(0, cli_fd);
+
+ /* Connecting or sendto to the Unix socket is denied. */
+ if (variant->use_sendto)
+ res = test_sendto_named_unix(cli_fd, path);
+ else
+ res = test_connect_named_unix(cli_fd, path);
+ EXPECT_EQ(variant->expected, res);
+
+ ASSERT_EQ(0, close(cli_fd));
+ ASSERT_EQ(0, close(srv_fd));
+ ASSERT_EQ(0, unlink(path));
+}
+
/* clang-format off */
FIXTURE(layout1_bind) {};
/* clang-format on */
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 5/5] landlock: Document FS access right for pathname UNIX sockets
2026-01-19 20:34 [PATCH v3 0/5] landlock: Pathname-based UNIX connect() control Günther Noack
` (3 preceding siblings ...)
2026-01-19 20:34 ` [PATCH v3 4/5] landlock/selftests: Test " Günther Noack
@ 2026-01-19 20:34 ` Günther Noack
4 siblings, 0 replies; 9+ messages in thread
From: Günther Noack @ 2026-01-19 20:34 UTC (permalink / raw)
To: Mickaël Salaün, John Johansen
Cc: Günther Noack, Justin Suess, linux-security-module,
Tingmao Wang, Samasth Norway Ananda, Matthieu Buffet,
Mikhail Ivanov, konstantin.meskhidze, Demi Marie Obenour,
Alyssa Ross, Jann Horn, Tahera Fahimi
Cc: Justin Suess <utilityemal77@gmail.com>
Cc: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
Documentation/userspace-api/landlock.rst | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 1d0c2c15c22e..980ee5dc482d 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -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 7:
+ /* Removes LANDLOCK_ACCESS_FS_RESOLVE_UNIX for ABI < 8 */
+ ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_RESOLVE_UNIX;
}
This enables the creation of an inclusive ruleset that will contain our rules.
@@ -604,6 +609,13 @@ Landlock audit events with the ``LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF``,
sys_landlock_restrict_self(). See Documentation/admin-guide/LSM/landlock.rst
for more details on audit.
+Pathname UNIX sockets (ABI < 8)
+-------------------------------
+
+Starting with the Landlock ABI version 8, 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
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread