diff mbox series

[v3,3/4] Add landlock08 test

Message ID 20241106-landlock_network-v3-3-855b14df63c6@suse.com
State Accepted
Headers show
Series landlock network coverage support | expand

Commit Message

Andrea Cervesato Nov. 6, 2024, 11:13 a.m. UTC
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.

Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
 runtest/syscalls                                |   1 +
 testcases/kernel/syscalls/landlock/.gitignore   |   1 +
 testcases/kernel/syscalls/landlock/landlock08.c | 209 ++++++++++++++++++++++++
 3 files changed, 211 insertions(+)
diff mbox series

Patch

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..c3c320c340355b6b910079c1bf31984dcf2c906b
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock08.c
@@ -0,0 +1,209 @@ 
+// 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"
+
+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 int addr_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, addr_port, 0);
+
+		tst_res(TINFO, "Enable bind() access only for port %u",
+			addr_port);
+
+		apply_landlock_net_layer(
+			ruleset_attr,
+			sizeof(struct tst_landlock_ruleset_attr_abi4),
+			net_port_attr,
+			addr_port,
+			LANDLOCK_ACCESS_NET_BIND_TCP);
+
+		test_bind(addr_family, addr_port, 0);
+		test_bind(addr_family, addr_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");
+
+	addr_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_STREAM);
+
+	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
+	},
+};