[LTP] [PATCH v2 3/4] Add landlock08 test

Andrea Cervesato
2024-11-05T09:35:43Z

From: Andrea Cervesato <andrea.cervesato@suse.com>

Verify the landlock support for bind()/connect() syscalls in IPV4
and IPV6 protocols. In particular, check that bind() is assigning
the address only on the TCP port enforced by
LANDLOCK_ACCESS_NET_BIND_TCP and check that connect() is connecting
only to a specific TCP port enforced by
LANDLOCK_ACCESS_NET_CONNECT_TCP.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
runtest/syscalls | 1 +
testcases/kernel/syscalls/landlock/.gitignore | 1 +
testcases/kernel/syscalls/landlock/landlock08.c | 208 ++++++++++++++++++++++++
3 files changed, 210 insertions(+)

diff --git a/runtest/syscalls b/runtest/syscalls
index 7dc308fa88486b9ace80ef0d906201dd407dcf3e..5fd62617df1a116b1d94c57ff30f74693320a2ab 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -708,6 +708,7 @@ landlock04 landlock04
landlock05 landlock05
landlock06 landlock06
landlock07 landlock07
+landlock08 landlock08

lchown01 lchown01
lchown01_16 lchown01_16
diff --git a/testcases/kernel/syscalls/landlock/.gitignore b/testcases/kernel/syscalls/landlock/.gitignore
index db11bff2fe245d462e5b7e5691a9eb2ee2305aab..fc7317394948c4ac20cd14c3cd7ba7a47282b2bf 100644
--- a/testcases/kernel/syscalls/landlock/.gitignore
+++ b/testcases/kernel/syscalls/landlock/.gitignore
@@ -6,3 +6,4 @@ landlock04
landlock05
landlock06
landlock07
+landlock08
diff --git a/testcases/kernel/syscalls/landlock/landlock08.c b/testcases/kernel/syscalls/landlock/landlock08.c
new file mode 100644
index 0000000000000000000000000000000000000000..7bc3f0f8d81730659a66c268fdad1981ae90aea6
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock08.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * [Description]
+ *
+ * Verify the landlock support for bind()/connect() syscalls in IPV4 and IPV6
+ * protocols. In particular, check that bind() is assigning the address only on
+ * the TCP port enforced by LANDLOCK_ACCESS_NET_BIND_TCP and check that
+ * connect() is connecting only to a specific TCP port enforced by
+ * LANDLOCK_ACCESS_NET_CONNECT_TCP.
+ *
+ * [Algorithm]
+ *
+ * Repeat the following procedure for IPV4 and IPV6:
+ *
+ * - create a socket on PORT1, bind() it and check if it passes
+ * - enforce the current sandbox with LANDLOCK_ACCESS_NET_BIND_TCP on PORT1
+ * - create a socket on PORT1, bind() it and check if it passes
+ * - create a socket on PORT2, bind() it and check if it fails
+ *
+ * - create a server listening on PORT1
+ * - create a socket on PORT1, connect() to it and check if it passes
+ * - enforce the current sandbox with LANDLOCK_ACCESS_NET_CONNECT_TCP on PORT1
+ * - create a socket on PORT1, connect() to it and check if it passes
+ * - create a socket on PORT2, connect() to it and check if it fails
+ */
+
+#include "landlock_common.h"
+
+#define ADDRESS_PORT 0x7c90
+
+static int variants[] = {
+ AF_INET,
+ AF_INET6,
+};
+
+static struct tst_landlock_ruleset_attr_abi4 *ruleset_attr;
+static struct landlock_net_port_attr *net_port_attr;
+static in_port_t *server_port;
+
+static void create_server(const int addr_family)
+{
+ struct socket_data socket;
+ struct sockaddr *addr = NULL;
+
+ create_socket(&socket, addr_family, 0);
+ getsocket_addr(&socket, addr_family, &addr);
+
+ SAFE_BIND(socket.fd, addr, socket.address_size);
+ SAFE_LISTEN(socket.fd, 1);
+
+ *server_port = getsocket_port(&socket, addr_family);
+
+ tst_res(TDEBUG, "Server listening on port %u", *server_port);
+
+ TST_CHECKPOINT_WAKE_AND_WAIT(0);
+
+ SAFE_CLOSE(socket.fd);
+}
+
+static void test_bind(const int addr_family, const in_port_t port,
+ const int exp_err)
+{
+ struct socket_data socket;
+ struct sockaddr *addr = NULL;
+
+ create_socket(&socket, addr_family, port);
+ getsocket_addr(&socket, addr_family, &addr);
+
+ if (exp_err) {
+ TST_EXP_FAIL(
+ bind(socket.fd, addr, socket.address_size),
+ exp_err, "bind() access on port %u", port);
+ } else {
+ TST_EXP_PASS(
+ bind(socket.fd, addr, socket.address_size),
+ "bind() access on port %u", port);
+ }
+
+ SAFE_CLOSE(socket.fd);
+}
+
+static void test_connect(const int addr_family, const in_port_t port,
+ const int exp_err)
+{
+ struct socket_data socket;
+ struct sockaddr *addr = NULL;
+
+ create_socket(&socket, addr_family, port);
+ getsocket_addr(&socket, addr_family, &addr);
+
+ if (exp_err) {
+ TST_EXP_FAIL(
+ connect(socket.fd, addr, socket.address_size),
+ exp_err, "connect() on port %u", port);
+ } else {
+ TST_EXP_PASS(
+ connect(socket.fd, addr, socket.address_size),
+ "connect() on port %u", port);
+ }
+
+ SAFE_CLOSE(socket.fd);
+}
+
+static void run(void)
+{
+ int addr_family = variants[tst_variant];
+
+ tst_res(TINFO, "Using %s protocol",
+ addr_family == AF_INET ? "IPV4" : "IPV6");
+
+ if (!SAFE_FORK()) {
+ create_server(addr_family);
+ exit(0);
+ }
+
+ TST_CHECKPOINT_WAIT(0);
+
+ /* verify bind() syscall accessibility */
+ if (!SAFE_FORK()) {
+ ruleset_attr->handled_access_net =
+ LANDLOCK_ACCESS_NET_BIND_TCP;
+
+ test_bind(addr_family, ADDRESS_PORT, 0);
+
+ tst_res(TINFO, "Enable bind() access only for port %u",
+ ADDRESS_PORT);
+
+ apply_landlock_net_layer(
+ ruleset_attr,
+ sizeof(struct tst_landlock_ruleset_attr_abi4),
+ net_port_attr,
+ ADDRESS_PORT,
+ LANDLOCK_ACCESS_NET_BIND_TCP);
+
+ test_bind(addr_family, ADDRESS_PORT, 0);
+ test_bind(addr_family, ADDRESS_PORT + 0x80, EACCES);
+
+ exit(0);
+ }
+
+ /* verify connect() syscall accessibility */
+ if (!SAFE_FORK()) {
+ ruleset_attr->handled_access_net =
+ LANDLOCK_ACCESS_NET_CONNECT_TCP;
+
+ test_connect(addr_family, *server_port, 0);
+
+ tst_res(TINFO, "Enable connect() access only on port %u",
+ *server_port);
+
+ apply_landlock_net_layer(
+ ruleset_attr,
+ sizeof(struct tst_landlock_ruleset_attr_abi4),
+ net_port_attr,
+ *server_port,
+ LANDLOCK_ACCESS_NET_CONNECT_TCP);
+
+ test_connect(addr_family, *server_port, 0);
+ test_connect(addr_family, *server_port + 0x80, EACCES);
+
+ TST_CHECKPOINT_WAKE(0);
+
+ exit(0);
+ }
+}
+
+static void setup(void)
+{
+ if (verify_landlock_is_enabled() < 4)
+ tst_brk(TCONF, "Landlock network is not supported");
+
+ server_port = SAFE_MMAP(NULL, sizeof(in_port_t), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+}
+
+static void cleanup(void)
+{
+ if (server_port)
+ SAFE_MUNMAP(server_port, sizeof(in_port_t));
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_checkpoints = 1,
+ .forks_child = 1,
+ .test_variants = ARRAY_SIZE(variants),
+ .bufs = (struct tst_buffers[]) {
+ {&ruleset_attr, .size = sizeof(struct tst_landlock_ruleset_attr_abi4)},
+ {&net_port_attr, .size = sizeof(struct landlock_net_port_attr)},
+ {},
+ },
+ .caps = (struct tst_cap []) {
+ TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN),
+ TST_CAP(TST_CAP_REQ, CAP_NET_BIND_SERVICE),
+ {}
+ },
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_INET=y",
+ NULL
+ },
+};

--
2.43.0