]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
landlock: Add UDP bind() access control
authorMatthieu Buffet <matthieu@buffet.re>
Thu, 11 Jun 2026 16:21:01 +0000 (18:21 +0200)
committerMickaël Salaün <mic@digikod.net>
Sat, 13 Jun 2026 21:15:04 +0000 (23:15 +0200)
Add support for a first fine-grained UDP access right.
LANDLOCK_ACCESS_NET_BIND_UDP controls the ability to set the local port
of a UDP socket (via bind()). It will be useful for servers (to start
receiving datagrams), and for some clients that need to use a specific
source port (e.g. mDNS requires to use port 5353)

For obvious performance concerns, access control is only enforced when
configuring sockets, not when using them for common send/recv
operations.

Bump ABI to allow userspace to detect and use this new right.

Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
Link: https://patch.msgid.link/20260611162107.49278-2-matthieu@buffet.re
[mic: Fix comment formatting]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
include/uapi/linux/landlock.h
security/landlock/audit.c
security/landlock/limits.h
security/landlock/net.c
security/landlock/syscalls.c
tools/testing/selftests/landlock/base_test.c
tools/testing/selftests/landlock/net_test.c

index 10a346e55e95fd56f8dcf6faa172425749883fda..f2927681e92db9c0be1ef492f183f023245a2628 100644 (file)
@@ -200,10 +200,10 @@ struct landlock_net_port_attr {
         * (also used for IPv6), and within that range, on a per-socket basis
         * with ``setsockopt(IP_LOCAL_PORT_RANGE)``.
         *
-        * A Landlock rule with port 0 and the %LANDLOCK_ACCESS_NET_BIND_TCP
-        * right means that requesting to bind on port 0 is allowed and it will
-        * automatically translate to binding on a kernel-assigned ephemeral
-        * port.
+        * A Landlock rule with port 0 and the %LANDLOCK_ACCESS_NET_BIND_TCP or
+        * %LANDLOCK_ACCESS_NET_BIND_UDP right means that requesting to bind on
+        * port 0 is allowed and it will automatically translate to binding on a
+        * kernel-assigned ephemeral port.
         */
        __u64 port;
 };
@@ -373,10 +373,16 @@ struct landlock_net_port_attr {
  *   port. Support added in Landlock ABI version 4.
  * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect TCP sockets to the given
  *   remote port. Support added in Landlock ABI version 4.
+ *
+ * And similarly for UDP port numbers:
+ *
+ * - %LANDLOCK_ACCESS_NET_BIND_UDP: Bind UDP sockets to the given local
+ *   port. Support added in Landlock ABI version 10.
  */
 /* clang-format off */
 #define LANDLOCK_ACCESS_NET_BIND_TCP                   (1ULL << 0)
 #define LANDLOCK_ACCESS_NET_CONNECT_TCP                        (1ULL << 1)
+#define LANDLOCK_ACCESS_NET_BIND_UDP                   (1ULL << 2)
 /* clang-format on */
 
 /**
index 8d0edf94037d720620f0ed1bb8d2aaea7dfcd9f5..e676ebffeebee170fc7e969dc7a43c5d5639bf71 100644 (file)
@@ -45,6 +45,7 @@ static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS);
 static const char *const net_access_strings[] = {
        [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_TCP)] = "net.bind_tcp",
        [BIT_INDEX(LANDLOCK_ACCESS_NET_CONNECT_TCP)] = "net.connect_tcp",
+       [BIT_INDEX(LANDLOCK_ACCESS_NET_BIND_UDP)] = "net.bind_udp",
 };
 
 static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);
index b454ad73b15e815d42793ba66e97f2452f92cba5..c0f30a4591b82f0d226467d7061e70e56b9ed053 100644 (file)
@@ -23,7 +23,7 @@
 #define LANDLOCK_MASK_ACCESS_FS                ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_FS         __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
 
-#define LANDLOCK_LAST_ACCESS_NET       LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_LAST_ACCESS_NET       LANDLOCK_ACCESS_NET_BIND_UDP
 #define LANDLOCK_MASK_ACCESS_NET       ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_NET                __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
 
index 4ee4002a8f56765e0998a5880dc9c0399ae69196..f57fe2a44f0d45408bf286cde5d6c821e1b97f03 100644 (file)
@@ -88,15 +88,17 @@ static int current_check_access_socket(struct socket *const sock,
                         * inconsistencies and return -EINVAL if needed.
                         */
                        return 0;
-               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
+               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
+                          access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
                        /*
                         * Binding to an AF_UNSPEC address is treated
                         * differently by IPv4 and IPv6 sockets. The socket's
                         * family may change under our feet due to
                         * setsockopt(IPV6_ADDRFORM), but that's ok: we either
-                        * reject entirely or require
-                        * %LANDLOCK_ACCESS_NET_BIND_TCP for the given port, so
-                        * it cannot be used to bypass the policy.
+                        * reject entirely for IPv6 or require
+                        * %LANDLOCK_ACCESS_NET_BIND_TCP or
+                        * %LANDLOCK_ACCESS_NET_BIND_UDP for IPv4, so it cannot
+                        * be used to bypass the policy.
                         *
                         * IPv4 sockets map AF_UNSPEC to AF_INET for
                         * retrocompatibility for bind accesses, only if the
@@ -142,7 +144,8 @@ static int current_check_access_socket(struct socket *const sock,
                if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) {
                        audit_net.dport = port;
                        audit_net.v4info.daddr = addr4->sin_addr.s_addr;
-               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
+               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
+                          access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
                        audit_net.sport = port;
                        audit_net.v4info.saddr = addr4->sin_addr.s_addr;
                } else {
@@ -164,7 +167,8 @@ static int current_check_access_socket(struct socket *const sock,
                if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) {
                        audit_net.dport = port;
                        audit_net.v6info.daddr = addr6->sin6_addr;
-               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
+               } else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
+                          access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
                        audit_net.sport = port;
                        audit_net.v6info.saddr = addr6->sin6_addr;
                } else {
@@ -224,6 +228,8 @@ static int hook_socket_bind(struct socket *const sock,
 
        if (sk_is_tcp(sock->sk))
                access_request = LANDLOCK_ACCESS_NET_BIND_TCP;
+       else if (sk_is_udp(sock->sk))
+               access_request = LANDLOCK_ACCESS_NET_BIND_UDP;
        else
                return 0;
 
index accfd2e5a0cd42dfc82095ce57082332326a281c..d45469d5d4644da188b0d721426699fadbfffc97 100644 (file)
@@ -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 = 9;
+const int landlock_abi_version = 10;
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
index 30d37234086c4bc9528fce5af92e8759edbaba16..6c8113c2ded128b73b30e0f359b628d5637057ee 100644 (file)
@@ -76,8 +76,8 @@ TEST(abi_version)
        const struct landlock_ruleset_attr ruleset_attr = {
                .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
        };
-       ASSERT_EQ(9, landlock_create_ruleset(NULL, 0,
-                                            LANDLOCK_CREATE_RULESET_VERSION));
+       ASSERT_EQ(10, landlock_create_ruleset(NULL, 0,
+                                             LANDLOCK_CREATE_RULESET_VERSION));
 
        ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
                                              LANDLOCK_CREATE_RULESET_VERSION));
index 0c256e7c86752e7f6e5196a16a39a8f88ea9d52f..135b09fd18806341f9528242ac479ef9169426b0 100644 (file)
@@ -1326,11 +1326,12 @@ FIXTURE_TEARDOWN(mini)
 
 /* clang-format off */
 
-#define ACCESS_LAST LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define ACCESS_LAST LANDLOCK_ACCESS_NET_BIND_UDP
 
 #define ACCESS_ALL ( \
        LANDLOCK_ACCESS_NET_BIND_TCP | \
-       LANDLOCK_ACCESS_NET_CONNECT_TCP)
+       LANDLOCK_ACCESS_NET_CONNECT_TCP | \
+       LANDLOCK_ACCESS_NET_BIND_UDP)
 
 /* clang-format on */