[apparmor] [PATCH] tests: Verify mediation of path-based UNIX domain sockets

Tyler Hicks tyhicks at canonical.com
Tue Oct 8 21:08:16 UTC 2013


The purpose is to provide test coverage for accessing UNIX domain socket
files. AppArmor write permissions are needed to create the socket files
and both read and write permissions are needed to connect to the socket.

This patch adds a test to the UNIX file descriptor passing tests and
creates an entirely new set of tests for sending and receiving messages
using path-based SOCK_STREAM, SOCK_DGRAM, and SOCK_SEQPACKET UNIX domain
sockets.

Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
---
 tests/regression/apparmor/Makefile                 |   3 +
 tests/regression/apparmor/unix_fd_server.sh        |   8 +
 tests/regression/apparmor/unix_socket_file.c       | 177 +++++++++++++++++++++
 tests/regression/apparmor/unix_socket_file.sh      | 105 ++++++++++++
 .../regression/apparmor/unix_socket_file_client.c  | 126 +++++++++++++++
 5 files changed, 419 insertions(+)
 create mode 100644 tests/regression/apparmor/unix_socket_file.c
 create mode 100755 tests/regression/apparmor/unix_socket_file.sh
 create mode 100644 tests/regression/apparmor/unix_socket_file_client.c

diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
index 370690f..0e49517 100644
--- a/tests/regression/apparmor/Makefile
+++ b/tests/regression/apparmor/Makefile
@@ -71,6 +71,8 @@ SRC=access.c \
     tcp.c \
     unix_fd_client.c \
     unix_fd_server.c \
+    unix_socket_file.c \
+    unix_socket_file_client.c \
     unlink.c \
     xattrs.c
 
@@ -158,6 +160,7 @@ TESTS=access \
       syscall \
       tcp \
       unix_fd_server \
+      unix_socket_file \
       unlink\
       xattrs\
       longpath
diff --git a/tests/regression/apparmor/unix_fd_server.sh b/tests/regression/apparmor/unix_fd_server.sh
index 68fdcf2..4de3b26 100755
--- a/tests/regression/apparmor/unix_fd_server.sh
+++ b/tests/regression/apparmor/unix_fd_server.sh
@@ -131,3 +131,11 @@ runchecktest "fd passing; confined -> confined (no perm)" fail $file $socket $fd
 
 sleep 1
 rm -f ${socket}
+
+# FAIL - confined client, no access to the socket file
+
+genprofile $file:$okperm $socket:rw $fd_client:px -- image=$fd_client $file:$okperm
+runchecktest "fd passing; confined client w/o socket access" fail $file $socket $fd_client
+
+sleep 1
+rm -f ${socket}
diff --git a/tests/regression/apparmor/unix_socket_file.c b/tests/regression/apparmor/unix_socket_file.c
new file mode 100644
index 0000000..a24e84e
--- /dev/null
+++ b/tests/regression/apparmor/unix_socket_file.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Canonical Ltd.
+ */
+
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#define MSG_BUF_MAX 1024
+
+static int connection_based_messaging(int sock, char *msg_buf,
+				      size_t msg_buf_len)
+{
+	int peer_sock, rc;
+
+	peer_sock = accept(sock, NULL, NULL);
+	if (peer_sock < 0) {
+		perror("FAIL - accept");
+		return 1;
+	}
+
+	rc = write(peer_sock, msg_buf, msg_buf_len);
+	if (rc < 0) {
+		perror("FAIL - write");
+		return 1;
+	}
+
+	rc = read(peer_sock, msg_buf, msg_buf_len);
+	if (rc < 0) {
+		perror("FAIL - read");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int connectionless_messaging(int sock, char *msg_buf, size_t msg_buf_len)
+{
+	struct sockaddr_un peer_addr;
+	socklen_t peer_addr_len = sizeof(peer_addr);
+	int rc;
+
+	peer_addr.sun_family = AF_UNIX;
+	rc = recvfrom(sock, NULL, 0, 0, (struct sockaddr *)&peer_addr,
+		      &peer_addr_len);
+	if (rc < 0) {
+		perror("FAIL - recvfrom");
+		return 1;
+	}
+
+	rc = sendto(sock, msg_buf, msg_buf_len, 0,
+		    (struct sockaddr *)&peer_addr, peer_addr_len);
+	if (rc < 0) {
+		perror("FAIL - sendto");
+		return 1;
+	}
+
+	rc = recv(sock, msg_buf, msg_buf_len, 0);
+	if (rc < 0) {
+		perror("FAIL - recv");
+		return 1;
+	}
+
+	return 0;
+}
+
+int main (int argc, char *argv[])
+{
+	struct sockaddr_un addr;
+	struct pollfd pfd;
+	char msg_buf[MSG_BUF_MAX];
+	size_t msg_buf_len;
+	pid_t pid;
+	int sock, type, rc;
+
+	if (argc != 5) {
+		fprintf(stderr,
+			"Usage: %s <socket> <type> <message> <client>\n\n"
+			"  type\t\tstream, dgram, or seqpacket\n",
+			argv[0]);
+		exit(1);
+	}
+
+	if (!strcmp(argv[2], "stream")) {
+		type = SOCK_STREAM;
+	} else if (!strcmp(argv[2], "dgram")) {
+		type = SOCK_DGRAM;
+	} else if (!strcmp(argv[2], "seqpacket")) {
+		type = SOCK_SEQPACKET;
+	} else {
+		fprintf(stderr, "FAIL - bad socket type: %s\n", argv[2]);
+		exit(1);
+	}
+
+	msg_buf_len = strlen(argv[3]) + 1;
+	if (msg_buf_len > MSG_BUF_MAX) {
+		fprintf(stderr, "FAIL - message too big\n");
+		exit(1);
+	}
+	memcpy(msg_buf, argv[3], msg_buf_len);
+
+	sock = socket(AF_UNIX, type, 0);
+	if (sock == -1) {
+		perror("FAIL - socket");
+		exit(1);
+	}
+
+	addr.sun_family = AF_UNIX;
+	strcpy(addr.sun_path, argv[1]);
+	rc = bind(sock, (struct sockaddr *)&addr,
+		  strlen(addr.sun_path) + sizeof(addr.sun_family));
+	if (rc < 0) {
+		perror("FAIL - bind");
+		exit(1);
+	}
+
+	if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
+		rc = listen(sock, 2);
+		if (rc < 0) {
+			perror("FAIL - listen");
+			exit(1);
+		}
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		perror("FAIL - fork");
+		exit(1);
+	} else if (!pid) {
+		execl(argv[4], argv[4], argv[1], argv[2], NULL);
+		exit(0);
+	}
+
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+	rc = poll(&pfd, 1, 500);
+	if (rc < 0) {
+		perror("FAIL - poll");
+		exit(1);
+	} else if (!rc) {
+		fprintf(stderr, "FAIL - poll timed out\n");
+		exit(1);
+	}
+
+	rc = (type == SOCK_STREAM || type == SOCK_SEQPACKET) ?
+		connection_based_messaging(sock, msg_buf, msg_buf_len) :
+		connectionless_messaging(sock, msg_buf, msg_buf_len);
+	if (rc)
+		exit(1);
+
+	if (memcmp(argv[3], msg_buf, msg_buf_len)) {
+		msg_buf[msg_buf_len] = '\0';
+		fprintf(stderr, "FAIL - buffer comparison. Got \"%s\", expected \"%s\"\n",
+			msg_buf, argv[3]);
+		exit(1);
+	}
+
+	printf("PASS\n");
+	exit(0);
+}
diff --git a/tests/regression/apparmor/unix_socket_file.sh b/tests/regression/apparmor/unix_socket_file.sh
new file mode 100755
index 0000000..6f38acb
--- /dev/null
+++ b/tests/regression/apparmor/unix_socket_file.sh
@@ -0,0 +1,105 @@
+#! /bin/bash
+#
+# Copyright (C) 2013 Canonical, Ltd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, contact Canonical Ltd.
+
+#=NAME unix_socket_file
+#=DESCRIPTION
+# This tests file access to path-based unix domain sockets. The server
+# opens a socket, forks a client with it's own profile, sends a message
+# to the client over the socket, and sees what happens.
+#=END
+
+pwd=`dirname $0`
+pwd=`cd $pwd ; /bin/pwd`
+
+bin=$pwd
+
+. $bin/prologue.inc
+
+client=$bin/unix_socket_file_client
+socket=${tmpdir}/unix_socket_file.sock
+message=4a0c83d87aaa7afa2baab5df3ee4df630f0046d5bfb7a3080c550b721f401b3b\
+8a738e1435a3b77aa6482a70fb51c44f20007221b85541b0184de66344d46a4c
+okserver=w
+badserver=r
+
+okclient=rw
+badclient1=r
+badclient2=w
+
+removesocket()
+{
+	rm -f ${socket}
+}
+
+testsocktype()
+{
+	local socktype=$1 # socket type - stream, dgram, or seqpacket
+	local args="$socket $socktype $message $client"
+
+	# PASS - unconfined
+
+	runchecktest "socket file ($socktype); unconfined" pass $args
+	removesocket
+
+	# PASS - server w/ access to the file
+
+	genprofile $socket:$okserver $client:Ux
+	runchecktest "socket file ($socktype); confined server w/ access ($okserver)" pass $args
+	removesocket
+
+	# FAIL - server w/o access to the file
+
+	genprofile $client:Ux
+	runchecktest "socket file ($socktype); confined server w/o access" fail $args
+	removesocket
+
+	# FAIL - server w/ bad access to the file
+
+	genprofile $socket:$badserver $client:Ux
+	runchecktest "socket file ($socktype); confined server w/ bad access ($badserver)" fail $args
+	removesocket
+
+	# PASS - client w/ access to the file
+
+	genprofile $socket:$okserver $client:px -- image=$client $socket:$okclient
+	runchecktest "socket file ($socktype); confined client w/ access ($okclient)" pass $args
+	removesocket
+
+	# FAIL - client w/o access to the file
+
+	genprofile $socket:$okserver $client:px -- image=$client
+	runchecktest "socket file ($socktype); confined client w/o access" fail $args
+	removesocket
+
+	# FAIL - client w/ bad access to the file
+
+	genprofile $socket:$okserver $client:px -- image=$client $socket:$badclient1
+	runchecktest "socket file ($socktype); confined client w/ bad access ($badclient1)" fail $args
+	removesocket
+
+	# FAIL - client w/ bad access to the file
+
+	genprofile $socket:$okserver $client:px -- image=$client $socket:$badclient2
+	runchecktest "socket file ($socktype); confined client w/ bad access ($badclient2)" fail $args
+	removesocket
+
+	removeprofile
+}
+
+removesocket
+testsocktype stream
+testsocktype dgram
+testsocktype seqpacket
diff --git a/tests/regression/apparmor/unix_socket_file_client.c b/tests/regression/apparmor/unix_socket_file_client.c
new file mode 100644
index 0000000..73aa8ba
--- /dev/null
+++ b/tests/regression/apparmor/unix_socket_file_client.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Canonical Ltd.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#define MSG_BUF_MAX	1024
+
+static int connection_based_messaging(int sock)
+{
+	char msg_buf[MSG_BUF_MAX];
+	int rc;
+
+	rc = read(sock, msg_buf, MSG_BUF_MAX);
+	if (rc < 0) {
+		perror("FAIL CLIENT - read");
+		return 1;
+	}
+
+	rc = write(sock, msg_buf, rc);
+	if (rc < 0) {
+		perror("FAIL CLIENT - write");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int connectionless_messaging(int sock)
+{
+	struct sockaddr_un addr;
+	char msg_buf[MSG_BUF_MAX];
+	int rc;
+
+	addr.sun_family = AF_UNIX;
+	rc = bind(sock, (struct sockaddr *)&addr, sizeof(sa_family_t));
+	if (rc < 0) {
+		perror("FAIL CLIENT - bind");
+		return 1;
+	}
+
+	rc = write(sock, NULL, 0);
+	if (rc < 0) {
+		perror("FAIL CLIENT - write");
+		return 1;
+	}
+
+	rc = read(sock, msg_buf, MSG_BUF_MAX);
+	if (rc < 0) {
+		perror("FAIL CLIENT - read");
+		return 1;
+	}
+
+	rc = write(sock, msg_buf, rc);
+	if (rc < 0) {
+		perror("FAIL CLIENT - write");
+		return 1;
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct sockaddr_un peer_addr;
+	int sock, type, rc;
+
+	if (argc != 3) {
+		fprintf(stderr, "Usage: %s <socket> <type>\n\n"
+			"  type\t\tstream, dgram, or seqpacket\n",
+			argv[0]);
+		exit(1);
+	}
+
+	if (!strcmp(argv[2], "stream")) {
+		type = SOCK_STREAM;
+	} else if (!strcmp(argv[2], "dgram")) {
+		type = SOCK_DGRAM;
+	} else if (!strcmp(argv[2], "seqpacket")) {
+		type = SOCK_SEQPACKET;
+	} else {
+		fprintf(stderr, "FAIL CLIENT - bad socket type: %s\n", argv[2]);
+		exit(1);
+	}
+
+	sock = socket(AF_UNIX, type, 0);
+	if (sock < 0) {
+		perror("FAIL CLIENT - socket");
+		exit(1);
+	}
+
+	peer_addr.sun_family = AF_UNIX;
+	strcpy(peer_addr.sun_path, argv[1]);
+	rc = connect(sock, (struct sockaddr *)&peer_addr,
+		     strlen(peer_addr.sun_path) + sizeof(peer_addr.sun_family));
+	if (rc < 0) {
+		perror("FAIL CLIENT - connect");
+		exit(1);
+	}
+
+	rc = (type == SOCK_STREAM || type == SOCK_SEQPACKET) ?
+		connection_based_messaging(sock) :
+		connectionless_messaging(sock);
+	if (rc)
+		exit(1);
+
+	exit(0);
+}
-- 
1.8.3.2




More information about the AppArmor mailing list