[PATCH testsuite] Add AF_VSOCK SOCK_STREAM client/server tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If AF_VSOCK and vsock_loopback are supported by the system, run tests
that exchange a byte of data between a client and a server listening on
VMADDR_CID_LOCAL and a random port.

Various permissions are removed from the client/server between runs and
it is checked that the corresponding syscalls returned error.

A newly created vsock_socket inherits the SID of the current process and
it is tested that the vsock_socket returned by accept() inherits the same
SID from its parent.

SOCK_DGRAM is not tested as it is only supported in the VMCI transport.

These tests depend on an upstream commit 1f935e8e72ec ("selinux: vsock:
Set SID for socket returned by accept()"). It was first released in v5.12
and backported to all the stable branches.

Signed-off-by: David Brazdil <dbrazdil@xxxxxxxxxx>
---
This is also posted on GitHub as pull request #75:
  https://github.com/SELinuxProject/selinux-testsuite/pull/75

The patch that fixes the vsock_socket bug has been merged to 5.12 and
backported to 5.10-stable and 5.11-stable. Backport all the way back
to 4.4-stable is awaiting merging here:
  https://lkml.kernel.org/stable/20210329182443.1960963-1-dbrazdil@xxxxxxxxxx
Since the expectation is that all stable kernels will soon have the patch,
I skipped a kernel version check in this test.

 policy/Makefile                  |   2 +-
 policy/test_vsock_socket.te      |  52 ++++++++++++
 tests/Makefile                   |   2 +-
 tests/vsock_socket/.gitignore    |   3 +
 tests/vsock_socket/Makefile      |   7 ++
 tests/vsock_socket/check_vsock.c |  47 +++++++++++
 tests/vsock_socket/client.c      | 129 ++++++++++++++++++++++++++++
 tests/vsock_socket/server.c      | 140 +++++++++++++++++++++++++++++++
 tests/vsock_socket/test          | 118 ++++++++++++++++++++++++++
 9 files changed, 498 insertions(+), 2 deletions(-)
 create mode 100644 policy/test_vsock_socket.te
 create mode 100644 tests/vsock_socket/.gitignore
 create mode 100644 tests/vsock_socket/Makefile
 create mode 100644 tests/vsock_socket/check_vsock.c
 create mode 100644 tests/vsock_socket/client.c
 create mode 100644 tests/vsock_socket/server.c
 create mode 100755 tests/vsock_socket/test

diff --git a/policy/Makefile b/policy/Makefile
index b092bb3..0b30a46 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -27,7 +27,7 @@ TARGETS = \
 	test_setnice.te test_sigkill.te test_stat.te test_sysctl.te \
 	test_task_create.te test_task_getpgid.te test_task_getsched.te \
 	test_task_getsid.te test_task_setpgid.te test_task_setsched.te \
-	test_transition.te test_unix_socket.te \
+	test_transition.te test_unix_socket.te test_vsock_socket.te \
 	test_mmap.te test_overlayfs.te test_mqueue.te \
 	test_ibpkey.te test_atsecure.te test_cgroupfs.te test_userfaultfd.te
 
diff --git a/policy/test_vsock_socket.te b/policy/test_vsock_socket.te
new file mode 100644
index 0000000..c1cbb36
--- /dev/null
+++ b/policy/test_vsock_socket.te
@@ -0,0 +1,52 @@
+#################################
+#
+# Policy for testing vsock/local domain sockets.
+#
+
+attribute vsocksocketdomain;
+
+define(`vsock_server', `
+# Domain for server process.
+type test_vsock_server_$1_t;
+domain_type(test_vsock_server_$1_t)
+unconfined_runs_test(test_vsock_server_$1_t)
+typeattribute test_vsock_server_$1_t testdomain;
+typeattribute test_vsock_server_$1_t vsocksocketdomain;
+
+# Permissions of the server process.
+allow test_vsock_server_$1_t self:vsock_socket { $2 };
+
+# For writing to flag file:
+allow test_vsock_server_$1_t test_file_t:fifo_file rw_file_perms;
+')
+
+define(`vsock_client', `
+# Domain for stream client process.
+type test_vsock_client_$1_t;
+domain_type(test_vsock_client_$1_t)
+unconfined_runs_test(test_vsock_client_$1_t)
+typeattribute test_vsock_client_$1_t testdomain;
+typeattribute test_vsock_client_$1_t vsocksocketdomain;
+
+# client can connect to the server via the socket file or via abstract sockets.
+allow test_vsock_client_$1_t self:vsock_socket { $2 };
+')
+
+vsock_server(all, accept bind create getattr listen read write)
+vsock_server(nobind, accept create getattr listen read write)
+vsock_server(nolisten, accept bind create getattr read write)
+vsock_server(noaccept, bind create getattr listen read write)
+
+vsock_client(all, connect create getattr getopt setopt read shutdown write)
+vsock_client(nocreate, connect getattr getopt setopt read shutdown write)
+vsock_client(noconnect, create getattr getopt setopt read shutdown write)
+vsock_client(nowrite, connect create getattr getopt setopt read shutdown)
+vsock_client(noshutdown, connect create getattr getopt setopt read write)
+vsock_client(noread, connect create getattr getopt setopt shutdown write)
+vsock_client(nogetattr, connect create getopt setopt read shutdown write)
+vsock_client(nogetopt, connect create getattr setopt read shutdown write)
+vsock_client(nosetopt, connect create getattr getopt read shutdown write)
+
+# Allow all of these domains to be entered from the sysadm domain.
+miscfiles_domain_entry_test_files(vsocksocketdomain)
+userdom_sysadm_entry_spec_domtrans_to(vsocksocketdomain)
diff --git a/tests/Makefile b/tests/Makefile
index c19fcd7..fb95a97 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -27,7 +27,7 @@ SUBDIRS:= domain_trans entrypoint execshare exectrace execute_no_trans \
 	task_setnice task_setscheduler task_getscheduler task_getsid \
 	task_getpgid task_setpgid file ioctl capable_file capable_net \
 	capable_sys dyntrans dyntrace bounds nnp_nosuid mmap unix_socket \
-	inet_socket overlay checkreqprot mqueue mac_admin atsecure
+	inet_socket overlay checkreqprot mqueue mac_admin atsecure vsock_socket
 
 ifeq ($(shell grep -q cap_userns $(POLDEV)/include/support/all_perms.spt && echo true),true)
 ifneq ($(shell ./kvercmp $$(uname -r) 4.7),-1)
diff --git a/tests/vsock_socket/.gitignore b/tests/vsock_socket/.gitignore
new file mode 100644
index 0000000..13eeb1b
--- /dev/null
+++ b/tests/vsock_socket/.gitignore
@@ -0,0 +1,3 @@
+client
+server
+check_vsock
diff --git a/tests/vsock_socket/Makefile b/tests/vsock_socket/Makefile
new file mode 100644
index 0000000..bf6ec7b
--- /dev/null
+++ b/tests/vsock_socket/Makefile
@@ -0,0 +1,7 @@
+TARGETS=client server check_vsock
+
+LDLIBS+= -lselinux
+
+all: $(TARGETS)
+clean:
+	rm -f $(TARGETS)
diff --git a/tests/vsock_socket/check_vsock.c b/tests/vsock_socket/check_vsock.c
new file mode 100644
index 0000000..6eecd62
--- /dev/null
+++ b/tests/vsock_socket/check_vsock.c
@@ -0,0 +1,47 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+// Must be included after sys/socket.h
+#include <linux/vm_sockets.h>
+
+int main(int argc, char **argv)
+{
+	int sock;
+	struct sockaddr_vm svm;
+
+	sock = socket(AF_VSOCK, SOCK_STREAM, 0);
+	if (sock < 0) {
+		if (errno == EAFNOSUPPORT) {
+			// AF_VSOCK not supported
+			exit(2);
+		} else {
+			perror("socket");
+			exit(1);
+		}
+	}
+
+	bzero(&svm, sizeof(svm));
+	svm.svm_family = AF_VSOCK;
+	svm.svm_port = VMADDR_PORT_ANY;
+	svm.svm_cid = VMADDR_CID_LOCAL;
+
+	if (bind(sock, (struct sockaddr *)&svm, sizeof(svm)) < 0) {
+		if (errno == EADDRNOTAVAIL) {
+			// vsock_loopback not supported
+			close(sock);
+			exit(3);
+		} else {
+			perror("bind");
+			close(sock);
+			exit(1);
+		}
+	}
+
+	close(sock);
+	exit(0);
+}
diff --git a/tests/vsock_socket/client.c b/tests/vsock_socket/client.c
new file mode 100644
index 0000000..7362459
--- /dev/null
+++ b/tests/vsock_socket/client.c
@@ -0,0 +1,129 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+// Must be included after sys/socket.h
+#include <linux/vm_sockets.h>
+
+void usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s cid port\n"
+		"\nWhere:\n\t"
+		"cid     Context Identifier of the server\n\t"
+		"port    Server port\n", progname);
+	exit(1);
+}
+
+#define TEST_VALUE	42
+
+int
+main(int argc, char **argv)
+{
+	unsigned cid;
+	unsigned port;
+	int sock;
+	int opt;
+	int result;
+	char byte;
+	socklen_t len;
+	uint64_t bufsize;
+	struct sockaddr_vm svm;
+
+	while ((opt = getopt(argc, argv, ":")) != -1) {
+		switch (opt) {
+		default:
+			usage(argv[0]);
+			break;
+		}
+	}
+
+	if ((argc - optind) != 2)
+		usage(argv[0]);
+
+	cid = strtoul(argv[optind], NULL, 10);
+	if (!cid)
+		usage(argv[0]);
+
+	port = strtoull(argv[optind + 1], NULL, 10);
+	if (!port)
+		usage(argv[0]);
+
+	sock = socket(AF_VSOCK, SOCK_STREAM, 0);
+	if (sock < 0) {
+		perror("socket");
+		exit(2);
+	}
+
+	bzero(&svm, sizeof(svm));
+	svm.svm_family = AF_VSOCK;
+	svm.svm_port = port;
+	svm.svm_cid = cid;
+
+	result = connect(sock, (struct sockaddr *)&svm, sizeof(svm));
+	if (result < 0) {
+		perror("connect");
+		close(sock);
+		exit(3);
+	}
+
+	byte = TEST_VALUE;
+	result = write(sock, &byte, 1);
+	if (result < 0) {
+		perror("write");
+		close(sock);
+		exit(4);
+	}
+
+	result = shutdown(sock, SHUT_WR);
+	if (result < 0) {
+		perror("shutdown");
+		close(sock);
+		exit(5);
+	}
+
+	result = read(sock, &byte, 1);
+	if (result < 0) {
+		perror("read");
+		close(sock);
+		exit(6);
+	} else if (result != 1) {
+		fprintf(stderr, "%s: expected 1 byte, got %d\n",
+			argv[0], result);
+		close(sock);
+		exit(1);
+	} else if (byte != TEST_VALUE) {
+		fprintf(stderr, "%s: expected %d, got %d\n",
+			argv[0], TEST_VALUE, byte);
+		exit(1);
+	}
+
+	len = sizeof(svm);
+	result = getsockname(sock, (struct sockaddr *)&svm, &len);
+	if (result < 0) {
+		perror("getsockname");
+		close(sock);
+		exit(7);
+	}
+
+	result = getsockopt(sock, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &bufsize, &len);
+	if (result < 0) {
+		perror("getsockopt");
+		close(sock);
+		exit(8);
+	}
+
+	result = setsockopt(sock, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE, &bufsize, len);
+	if (result < 0) {
+		perror("setsockopt");
+		close(sock);
+		exit(9);
+	}
+
+	close(sock);
+	exit(0);
+}
diff --git a/tests/vsock_socket/server.c b/tests/vsock_socket/server.c
new file mode 100644
index 0000000..dd135d0
--- /dev/null
+++ b/tests/vsock_socket/server.c
@@ -0,0 +1,140 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+// Must be included after sys/socket.h
+#include <linux/vm_sockets.h>
+
+void usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-f file] [-p port]\n"
+		"\nWhere:\n\t"
+		"-f      Flag file signaling server readiness\n\t"
+		"-p      Listening port, otherwise random\n\t"
+		"-s      Single-client mode\n", progname);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	unsigned port = VMADDR_PORT_ANY;
+	int sock;
+	int opt;
+	int result;
+	struct sockaddr_vm svm;
+	socklen_t svmlen;
+	char *flag_file = NULL;
+	int single_client = 0;
+
+	while ((opt = getopt(argc, argv, "p:f:s")) != -1) {
+		switch (opt) {
+		case 'p':
+			port = strtoul(optarg, NULL, 10);
+			if (!port)
+				usage(argv[0]);
+			break;
+		case 'f':
+			flag_file = optarg;
+			break;
+		case 's':
+			single_client = 1;
+			break;
+		default:
+			usage(argv[0]);
+			break;
+		}
+	}
+
+	if ((argc - optind) != 0)
+		usage(argv[0]);
+
+	sock = socket(AF_VSOCK, SOCK_STREAM, 0);
+	if (sock < 0) {
+		perror("socket");
+		exit(2);
+	}
+
+	bzero(&svm, sizeof(svm));
+	svm.svm_family = AF_VSOCK;
+	svm.svm_port = port;
+	svm.svm_cid = VMADDR_CID_LOCAL;
+
+	svmlen = sizeof(svm);
+	if (bind(sock, (struct sockaddr *)&svm, svmlen) < 0) {
+		perror("bind");
+		close(sock);
+		exit(3);
+	}
+
+	/* Update svm with the port number selected by the kernel. */
+	if (getsockname(sock, (struct sockaddr *)&svm, &svmlen) < 0) {
+		perror("getsockname");
+		close(sock);
+		exit(4);
+	}
+
+	if (listen(sock, SOMAXCONN)) {
+		perror("listen");
+		close(sock);
+		exit(5);
+	}
+
+	if (flag_file) {
+		FILE *f = fopen(flag_file, "w");
+		if (!f) {
+			perror("Flag file open");
+			close(sock);
+			exit(1);
+		}
+		fprintf(f, "%u\n", svm.svm_port);
+		fclose(f);
+	}
+
+	for(;;) {
+		char byte;
+		int newsock;
+		int pid;
+		struct sockaddr_vm newsvm;
+		socklen_t newsvmlen;
+
+		newsvmlen = sizeof(newsvm);
+		newsock = accept(sock, (struct sockaddr *)&newsvm, &newsvmlen);
+		if (newsock < 0) {
+			perror("accept");
+			close(sock);
+			exit(6);
+		}
+
+		if (!single_client) {
+			pid = fork();
+			if (pid < 0) {
+				perror("fork");
+				close(sock);
+				exit(7);
+			} else if (pid > 0) {
+				close(newsock);
+				continue;
+			}
+		}
+
+		result = read(newsock, &byte, 1);
+		if (result < 0) {
+			perror("read");
+			exit(8);
+		}
+
+		result = write(newsock, &byte, 1);
+		if (result < 0 && errno != EPIPE) {
+			perror("write");
+			exit(9);
+		}
+
+		close(newsock);
+		exit(0);
+	}
+}
diff --git a/tests/vsock_socket/test b/tests/vsock_socket/test
new file mode 100755
index 0000000..41d9bc8
--- /dev/null
+++ b/tests/vsock_socket/test
@@ -0,0 +1,118 @@
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    # check if vsock and vsock_loopback are available
+    $rc = system("$basedir/check_vsock");
+
+    if ( $rc eq 0 ) {
+        plan tests => 12;
+    }
+    elsif ( $rc eq 2 << 8 ) {
+        plan skip_all => "vsock socket family not supported";
+    }
+    elsif ( $rc eq 3 << 8 ) {
+        plan skip_all => "vsock_loopback transport not supported";
+    }
+    else {
+        plan skip_all => "unexpected error when checking vsock support";
+    }
+}
+
+sub server_start {
+    my ( $runcon_args, $args ) = @_;
+    my $pid;
+
+    system("rm -f $basedir/flag");
+    system("mkfifo $basedir/flag");
+
+    if ( ( $pid = fork() ) == 0 ) {
+        exec "runcon $runcon_args $basedir/server -f $basedir/flag $args";
+    }
+
+    # Wait for it to initialize, read port number.
+    my $port = `read -t 5 <>$basedir/flag; echo \$REPLY`;
+
+    return ( $pid, $port );
+}
+
+sub server_kill {
+    my ($pid) = @_;
+
+    kill KILL, $pid;
+    waitpid $pid, 0;
+    system("rm -f $basedir/flag");
+}
+
+# Start server for client tests.
+( $pid, $port ) = server_start( "-t test_vsock_server_all_t", "" );
+ok( $port =~ /^[0-9]+$/ );
+
+# Verify that client can connect to server.
+$result = system "runcon -t test_vsock_client_all_t $basedir/client 1 $port";
+ok( $result eq 0 );
+
+# Verify that client cannot create vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_nocreate_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 2 << 8 );
+
+# Verify that client cannot connect to vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_noconnect_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 3 << 8 );
+
+# Verify that client cannot write to vsock_socket without permission.
+$result =
+  system(
+    "runcon -t test_vsock_client_nowrite_t 2>/dev/null $basedir/client 1 $port"
+  );
+ok( $result eq 4 << 8 );
+
+# Verify that client cannot read from vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_noread_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 6 << 8 );
+
+# Verify that client cannot getsockname from vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_nogetattr_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 7 << 8 );
+
+# Verify that client cannot getsockopt from vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_nogetopt_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 8 << 8 );
+
+# Verify that client cannot setsockopt from vsock_socket without permission.
+$result =
+  system
+  "runcon -t test_vsock_client_nosetopt_t 2>/dev/null $basedir/client 1 $port";
+ok( $result eq 9 << 8 );
+
+server_kill($pid);
+
+# Verify that server cannot bind to vsock_socket without permission.
+$result =
+  system "runcon -t test_vsock_server_nobind_t $basedir/server 2>/dev/null";
+ok( $result eq 3 << 8 );
+
+# Verify that server cannot listen on vsock_socket without permission.
+$result =
+  system "runcon -t test_vsock_server_nolisten_t $basedir/server 2>/dev/null";
+ok( $result eq 5 << 8 );
+
+# Verify that server cannot accept a vsock_socket connection without permission.
+$result =
+  system "runcon -t test_vsock_server_noaccept_t $basedir/server 2>/dev/null";
+ok( $result eq 6 << 8 );
+
+exit;
-- 
2.30.2




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux