lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <a9e8016aaa5846252623b158c8f1ce0d666944f4.1725494372.git.fahimitahera@gmail.com>
Date: Wed,  4 Sep 2024 18:13:58 -0600
From: Tahera Fahimi <fahimitahera@...il.com>
To: outreachy@...ts.linux.dev
Cc: mic@...ikod.net,
	gnoack@...gle.com,
	paul@...l-moore.com,
	jmorris@...ei.org,
	serge@...lyn.com,
	linux-security-module@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	bjorn3_gh@...tonmail.com,
	jannh@...gle.com,
	netdev@...r.kernel.org,
	Tahera Fahimi <fahimitahera@...il.com>
Subject: [PATCH v11 4/8] selftests/landlock: Add tests for UNIX sockets with any address formats

This patch expands abstract UNIX socket restriction tests by examining
different scenarios for UNIX sockets with pathname or unnamed address
formats connection with scoped domain.

The test "various_address_sockets" ensures that UNIX sockets bound to a
filesystem pathname and unnamed sockets created by socketpair can still
connect to a socket outside of their scoped domain, meaning that even if
the domain is scoped with LANDLOCK_SCOPED_ABSTRACT_UNIX_SOCKET, the
socket can connect to a socket outside the scoped domain.

Signed-off-by: Tahera Fahimi <fahimitahera@...il.com>
---
changes in versions:
v11:
- Using generalized scoped domain creation, "create_scoped_domain"
- Rename pathname_address_sockets to various_address_sockets
- Using local variables.
- Commit improvement.
- Support test for unnamed datagram sockets(via socketpair(2)).
v10:
- Code improvements by changing fixture variables to local ones.
- Commit improvement.
v9:
- Moving remove_path() back to fs_test.c, and using unlink(2) and
  rmdir(2) instead.
- Removing hard-coded numbers and using "backlog" instead.
V8:
- Adding pathname_address_sockets to cover all types of address formats
  for unix sockets, and moving remove_path() to common.h to reuse in
  this test.
---
 .../landlock/scoped_abstract_unix_test.c      | 202 ++++++++++++++++++
 1 file changed, 202 insertions(+)

diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
index 00ea5151979f..8fc47e45d17e 100644
--- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
+++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
@@ -617,4 +617,206 @@ TEST_F(outside_socket, socket_with_different_domain)
 		_metadata->exit_code = KSFT_FAIL;
 }
 
+static const char path1[] = TMP_DIR "/s1_variant1";
+static const char path2[] = TMP_DIR "/s2_variant1";
+
+/* clang-format off */
+FIXTURE(various_address_sockets)
+{
+	struct service_fixture stream_address, dgram_address;
+};
+/* clang-format on */
+
+FIXTURE_VARIANT(various_address_sockets)
+{
+	const int domain;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_scoped_domain) {
+	/* clang-format on */
+	.domain = SCOPE_SANDBOX,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_other_domain) {
+	/* clang-format on */
+	.domain = OTHER_SANDBOX,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_no_domain) {
+	/* clang-format on */
+	.domain = NO_SANDBOX,
+};
+
+FIXTURE_SETUP(various_address_sockets)
+{
+	disable_caps(_metadata);
+	umask(0077);
+	ASSERT_EQ(0, mkdir(TMP_DIR, 0700));
+
+	ASSERT_EQ(0, mknod(path1, S_IFREG | 0700, 0))
+	{
+		TH_LOG("Failed to create file \"%s\": %s", path1,
+		       strerror(errno));
+		ASSERT_EQ(0, unlink(TMP_DIR) & rmdir(TMP_DIR));
+	}
+	ASSERT_EQ(0, mknod(path2, S_IFREG | 0700, 0))
+	{
+		TH_LOG("Failed to create file \"%s\": %s", path2,
+		       strerror(errno));
+		ASSERT_EQ(0, unlink(TMP_DIR) & rmdir(TMP_DIR));
+	}
+	memset(&self->stream_address, 0, sizeof(self->stream_address));
+	set_unix_address(&self->stream_address, 0);
+	memset(&self->dgram_address, 0, sizeof(self->dgram_address));
+	set_unix_address(&self->dgram_address, 1);
+}
+
+FIXTURE_TEARDOWN(various_address_sockets)
+{
+	ASSERT_EQ(0, unlink(path1) & rmdir(path1));
+	ASSERT_EQ(0, unlink(path2) & rmdir(path2));
+	ASSERT_EQ(0, unlink(TMP_DIR) & rmdir(TMP_DIR));
+}
+
+TEST_F(various_address_sockets, scoped_pathname_sockets)
+{
+	const char *const stream_path = path1;
+	const char *const dgram_path = path2;
+	socklen_t size, size_dg;
+	struct sockaddr_un stream_pathname_addr, dgram_pathname_addr;
+	int unnamed_sockets[2];
+	int stream_pathname_socket, dgram_pathname_socket,
+		stream_abstract_socket, dgram_abstract_socket;
+	int pipe_parent[2];
+	pid_t child;
+	int status;
+	char buf_child;
+	char data = 'S';
+	char buf[5];
+	int nbyte;
+
+	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_DGRAM, 0, unnamed_sockets));
+
+	stream_pathname_addr.sun_family = AF_UNIX;
+	snprintf(stream_pathname_addr.sun_path,
+		 sizeof(stream_pathname_addr.sun_path), "%s", stream_path);
+	size = offsetof(struct sockaddr_un, sun_path) +
+	       strlen(stream_pathname_addr.sun_path);
+
+	dgram_pathname_addr.sun_family = AF_UNIX;
+	snprintf(dgram_pathname_addr.sun_path,
+		 sizeof(dgram_pathname_addr.sun_path), "%s", dgram_path);
+	size_dg = offsetof(struct sockaddr_un, sun_path) +
+		  strlen(dgram_pathname_addr.sun_path);
+
+	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		int err, err_dg;
+
+		ASSERT_EQ(0, close(pipe_parent[1]));
+
+		if (variant->domain == SCOPE_SANDBOX)
+			create_scoped_domain(
+				_metadata,
+				LANDLOCK_SCOPED_ABSTRACT_UNIX_SOCKET);
+		else if (variant->domain == OTHER_SANDBOX)
+			create_fs_domain(_metadata);
+
+		ASSERT_EQ(0, close(unnamed_sockets[1]));
+		ASSERT_NE(-1, write(unnamed_sockets[0], &data, sizeof(data)));
+		ASSERT_EQ(0, close(unnamed_sockets[0]));
+
+		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
+
+		/* Connect with pathname sockets. */
+		stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+		ASSERT_LE(0, stream_pathname_socket);
+		ASSERT_EQ(0, connect(stream_pathname_socket,
+				     &stream_pathname_addr, size));
+		dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
+		ASSERT_LE(0, dgram_pathname_socket);
+		ASSERT_EQ(0, connect(dgram_pathname_socket,
+				     &dgram_pathname_addr, size_dg));
+
+		/* Connect with abstract sockets. */
+		stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+		dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
+
+		ASSERT_NE(-1, stream_abstract_socket);
+		ASSERT_NE(-1, dgram_abstract_socket);
+
+		err = connect(stream_abstract_socket,
+			      &self->stream_address.unix_addr,
+			      self->stream_address.unix_addr_len);
+		err_dg = connect(dgram_abstract_socket,
+				 &self->dgram_address.unix_addr,
+				 self->dgram_address.unix_addr_len);
+		if (variant->domain == SCOPE_SANDBOX) {
+			EXPECT_EQ(-1, err);
+			EXPECT_EQ(-1, err_dg);
+			EXPECT_EQ(EPERM, errno);
+		} else {
+			EXPECT_EQ(0, err);
+			EXPECT_EQ(0, err_dg);
+		}
+		ASSERT_EQ(0, close(stream_abstract_socket));
+		ASSERT_EQ(0, close(dgram_abstract_socket));
+		ASSERT_EQ(0, close(stream_pathname_socket));
+		ASSERT_EQ(0, close(dgram_pathname_socket));
+		_exit(_metadata->exit_code);
+		return;
+	}
+	ASSERT_EQ(0, close(pipe_parent[0]));
+
+	ASSERT_EQ(0, close(unnamed_sockets[0]));
+	nbyte = read(unnamed_sockets[1], buf, sizeof(buf));
+	ASSERT_EQ(sizeof(data), nbyte);
+	buf[nbyte] = '\0';
+	ASSERT_EQ(0, strcmp(&data, buf));
+	ASSERT_LE(0, close(unnamed_sockets[1]));
+
+	/* Sets up pathname servers */
+	stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+	ASSERT_LE(0, stream_pathname_socket);
+	ASSERT_EQ(0, unlink(stream_path));
+	ASSERT_EQ(0, bind(stream_pathname_socket, &stream_pathname_addr, size));
+	ASSERT_EQ(0, listen(stream_pathname_socket, backlog));
+
+	ASSERT_EQ(0, unlink(dgram_path));
+	dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
+	ASSERT_LE(0, dgram_pathname_socket);
+	ASSERT_EQ(0,
+		  bind(dgram_pathname_socket, &dgram_pathname_addr, size_dg));
+
+	/* Set up abstract servers */
+	stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+	dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
+	ASSERT_NE(-1, stream_abstract_socket);
+	ASSERT_NE(-1, dgram_abstract_socket);
+	ASSERT_EQ(0,
+		  bind(stream_abstract_socket, &self->stream_address.unix_addr,
+		       self->stream_address.unix_addr_len));
+	ASSERT_EQ(0, bind(dgram_abstract_socket, &self->dgram_address.unix_addr,
+			  self->dgram_address.unix_addr_len));
+	ASSERT_EQ(0, listen(stream_abstract_socket, backlog));
+
+	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+
+	ASSERT_EQ(0, close(stream_abstract_socket));
+	ASSERT_EQ(0, close(dgram_abstract_socket));
+	ASSERT_EQ(0, close(stream_pathname_socket));
+	ASSERT_EQ(0, close(dgram_pathname_socket));
+
+	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
+	    WEXITSTATUS(status) != EXIT_SUCCESS)
+		_metadata->exit_code = KSFT_FAIL;
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ