]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/landlock: Add tests for quiet flag with net rules
authorTingmao Wang <m@maowtm.org>
Fri, 12 Jun 2026 01:48:53 +0000 (02:48 +0100)
committerMickaël Salaün <mic@digikod.net>
Sun, 14 Jun 2026 18:17:23 +0000 (20:17 +0200)
Tests that:
- Quiet flag works on network rules
- Quiet flag applied to unrelated ports has no effect
- Denied access not in quiet_access_net is still logged

This is not as thorough as the fs tests, but given the shared logic it
should be sufficient.  There is also no "optional" access for network
rules.

Signed-off-by: Tingmao Wang <m@maowtm.org>
Assisted-by: GitHub-Copilot:claude-opus-4.7 copilot-review
Link: https://patch.msgid.link/364fbd08081318d64bc23049d3a7721f0a3a3624.1781228815.git.m@maowtm.org
Signed-off-by: Mickaël Salaün <mic@digikod.net>
tools/testing/selftests/landlock/net_test.c

index fb244aaef86fbd657998fff259f00088a5e9706c..2ed1f76b7a8b15eef16ca9c9760d11d9485d975c 100644 (file)
@@ -2742,12 +2742,22 @@ TEST_F(port_specific, bind_connect_1023)
        EXPECT_EQ(0, close(bind_fd));
 }
 
+/**
+ * matches_auditlog - Check audit log for a network access denial
+ *
+ * @audit_fd:   Audit file descriptor.
+ * @blockers:   A regex-escaped blocker string, e.g., "net\.bind_tcp".
+ * @dir_addr:   Either "saddr" or "daddr", ignored if addr is NULL.
+ * @addr:       A regex-escaped IP address string, or NULL.
+ * @dir_port:   Either "src" or "dest", ignored if addr is NULL.
+ * @port:       A port number, ignored if addr is NULL.
+ */
 static int matches_auditlog(const int audit_fd, const char *const blockers,
                            const char *const dir_addr, const char *const addr,
-                           const char *const dir_port)
+                           const char *const dir_port, const __u16 port)
 {
        static const char log_with_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX
-               " blockers=%s %s=%s %s=1024$";
+               " blockers=%s %s=%s %s=%u$";
        static const char log_without_addrport_tmpl[] = REGEX_LANDLOCK_PREFIX
                " blockers=%s";
        /*
@@ -2755,8 +2765,9 @@ static int matches_auditlog(const int audit_fd, const char *const blockers,
         * Max strlen(dir_addr): 5
         * Max strlen(addr): 12
         * Max strlen(dir_port): 4
+        * Max strlen(%u port): 5
         */
-       char log_match[sizeof(log_with_addrport_tmpl) + 37];
+       char log_match[sizeof(log_with_addrport_tmpl) + 42];
        int log_match_len;
 
        if (addr == NULL)
@@ -2765,7 +2776,7 @@ static int matches_auditlog(const int audit_fd, const char *const blockers,
        else
                log_match_len = snprintf(log_match, sizeof(log_match),
                                         log_with_addrport_tmpl, blockers,
-                                        dir_addr, addr, dir_port);
+                                        dir_addr, addr, dir_port, port);
        if (log_match_len > sizeof(log_match))
                return -E2BIG;
 
@@ -2777,6 +2788,8 @@ FIXTURE(audit)
 {
        struct service_fixture srv0;
        struct service_fixture srv1;
+       /* srv2 has a rule with no access but quiet bit set. */
+       struct service_fixture srv2;
        struct service_fixture unspec_srv0;
        struct audit_filter audit_filter;
        int audit_fd;
@@ -2836,6 +2849,7 @@ FIXTURE_SETUP(audit)
 
        ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0));
        ASSERT_EQ(0, set_service(&self->srv1, variant->prot, 1));
+       ASSERT_EQ(0, set_service(&self->srv2, variant->prot, 2));
        ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0));
 
        setup_loopback(_metadata);
@@ -2866,6 +2880,11 @@ TEST_F(audit, bind)
                                 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
        const struct landlock_ruleset_attr ruleset_attr = {
                .handled_access_net = access_rights,
+               .quiet_access_net = access_rights,
+       };
+       const struct landlock_net_port_attr quiet_rule = {
+               .allowed_access = 0,
+               .port = self->srv2.port,
        };
        struct audit_records records;
        int ruleset_fd, sock_fd;
@@ -2873,6 +2892,8 @@ TEST_F(audit, bind)
        ruleset_fd =
                landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
        ASSERT_LE(0, ruleset_fd);
+       ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
+                                      &quiet_rule, LANDLOCK_ADD_RULE_QUIET));
        enforce_ruleset(_metadata, ruleset_fd);
        EXPECT_EQ(0, close(ruleset_fd));
 
@@ -2880,13 +2901,24 @@ TEST_F(audit, bind)
        ASSERT_LE(0, sock_fd);
        EXPECT_EQ(-EACCES, bind_variant(sock_fd, &self->srv0));
        EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "saddr",
-                                     variant->addr, "src"));
+                                     variant->addr, "src", self->srv0.port));
 
        EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
        EXPECT_EQ(0, records.access);
        EXPECT_EQ(1, records.domain);
 
        EXPECT_EQ(0, close(sock_fd));
+
+       /* Bind to srv2 (with quiet rule): no new audit logs. */
+       sock_fd = socket_variant(&self->srv2);
+       ASSERT_LE(0, sock_fd);
+       EXPECT_EQ(-EACCES, bind_variant(sock_fd, &self->srv2));
+
+       EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+       EXPECT_EQ(0, records.access);
+       EXPECT_EQ(0, records.domain);
+
+       EXPECT_EQ(0, close(sock_fd));
 }
 
 TEST_F(audit, connect)
@@ -2903,11 +2935,16 @@ TEST_F(audit, connect)
        const __u64 access_rights = bind_right | conn_right;
        const struct landlock_ruleset_attr ruleset_attr = {
                .handled_access_net = access_rights,
+               .quiet_access_net = access_rights,
        };
        const struct landlock_net_port_attr rule_connect_p1 = {
                .allowed_access = conn_right,
                .port = self->srv1.port,
        };
+       const struct landlock_net_port_attr quiet_rule = {
+               .allowed_access = 0,
+               .port = self->srv2.port,
+       };
        struct audit_records records;
        int ruleset_fd, sock_fd;
 
@@ -2916,6 +2953,8 @@ TEST_F(audit, connect)
        ASSERT_LE(0, ruleset_fd);
        ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
                                       &rule_connect_p1, 0));
+       ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
+                                      &quiet_rule, LANDLOCK_ADD_RULE_QUIET));
        enforce_ruleset(_metadata, ruleset_fd);
        EXPECT_EQ(0, close(ruleset_fd));
 
@@ -2923,7 +2962,7 @@ TEST_F(audit, connect)
        ASSERT_LE(0, sock_fd);
        EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv0));
        EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "daddr",
-                                     variant->addr, "dest"));
+                                     variant->addr, "dest", self->srv0.port));
 
        EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
        EXPECT_EQ(0, records.access);
@@ -2934,13 +2973,91 @@ TEST_F(audit, connect)
                EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv1));
 
                EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp",
-                                             NULL, NULL, NULL));
+                                             NULL, NULL, NULL, 0));
                EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
                EXPECT_EQ(0, records.access);
                EXPECT_EQ(0, records.domain);
        }
 
        EXPECT_EQ(0, close(sock_fd));
+
+       /* Connect to srv2 (with quiet rule): no new audit logs. */
+       sock_fd = socket_variant(&self->srv2);
+       ASSERT_LE(0, sock_fd);
+       EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2));
+
+       EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+       EXPECT_EQ(0, records.access);
+       EXPECT_EQ(0, records.domain);
+
+       EXPECT_EQ(0, close(sock_fd));
+}
+
+/* Quieting bind access has no effect on connect. */
+TEST_F(audit, connect_quiet_bind)
+{
+       const char *audit_evt = (variant->prot.type == SOCK_STREAM ?
+                                        "net\\.connect_tcp" :
+                                        "net\\.connect_send_udp");
+       const int bind_right = (variant->prot.type == SOCK_STREAM ?
+                                       LANDLOCK_ACCESS_NET_BIND_TCP :
+                                       LANDLOCK_ACCESS_NET_BIND_UDP);
+       const int conn_right = (variant->prot.type == SOCK_STREAM ?
+                                       LANDLOCK_ACCESS_NET_CONNECT_TCP :
+                                       LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+       const int access_rights = bind_right | conn_right;
+       const struct landlock_ruleset_attr ruleset_attr = {
+               .handled_access_net = access_rights,
+               .quiet_access_net = bind_right,
+       };
+       const struct landlock_ruleset_attr ruleset_attr_2 = {
+               .handled_access_net = access_rights,
+               .quiet_access_net = conn_right,
+       };
+       const struct landlock_net_port_attr quiet_rule = {
+               .allowed_access = 0,
+               .port = self->srv2.port,
+       };
+       struct audit_records records;
+       int ruleset_fd, sock_fd;
+
+       ruleset_fd =
+               landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+       ASSERT_LE(0, ruleset_fd);
+       ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
+                                      &quiet_rule, LANDLOCK_ADD_RULE_QUIET));
+       enforce_ruleset(_metadata, ruleset_fd);
+       EXPECT_EQ(0, close(ruleset_fd));
+
+       sock_fd = socket_variant(&self->srv2);
+       ASSERT_LE(0, sock_fd);
+       EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2));
+       EXPECT_EQ(0, matches_auditlog(self->audit_fd, audit_evt, "daddr",
+                                     variant->addr, "dest", self->srv2.port));
+
+       EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+       EXPECT_EQ(0, records.access);
+
+       EXPECT_EQ(0, close(sock_fd));
+
+       /* New layer that also denies connect but has the correct quiet bit. */
+       ruleset_fd = landlock_create_ruleset(&ruleset_attr_2,
+                                            sizeof(ruleset_attr_2), 0);
+       ASSERT_LE(0, ruleset_fd);
+       ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
+                                      &quiet_rule, LANDLOCK_ADD_RULE_QUIET));
+       enforce_ruleset(_metadata, ruleset_fd);
+       EXPECT_EQ(0, close(ruleset_fd));
+
+       sock_fd = socket_variant(&self->srv2);
+       ASSERT_LE(0, sock_fd);
+       EXPECT_EQ(-EACCES, connect_variant(sock_fd, &self->srv2));
+
+       /* Quieted - no logs expected. */
+       EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+       EXPECT_EQ(0, records.access);
+
+       EXPECT_EQ(0, close(sock_fd));
 }
 
 static int matches_log_connect_bound(int audit_fd, const char *const blockers,
@@ -3045,7 +3162,8 @@ TEST_F(audit, sendmsg)
        ASSERT_LE(0, sock_fd);
        EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv0, "A", 1, 0));
        EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp",
-                                     "daddr", variant->addr, "dest"));
+                                     "daddr", variant->addr, "dest",
+                                     self->srv0.port));
 
        EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
        EXPECT_EQ(0, records.access);
@@ -3054,7 +3172,7 @@ TEST_F(audit, sendmsg)
        /* Check that autobind generates a denied bind event. */
        EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv1, "A", 1, 0));
        EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", NULL,
-                                     NULL, NULL));
+                                     NULL, NULL, 0));
        EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
        EXPECT_EQ(0, records.access);
        EXPECT_EQ(0, records.domain);
@@ -3062,7 +3180,7 @@ TEST_F(audit, sendmsg)
        EXPECT_EQ(-EACCES,
                  sendto_variant(sock_fd, &self->unspec_srv0, "B", 1, 0));
        EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp",
-                                     "daddr", NULL, "dest"));
+                                     "daddr", NULL, "dest", 0));
        EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
        EXPECT_EQ(0, records.access);
        EXPECT_EQ(0, records.domain);