diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile index 370690f14..0e49517e7 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 68fdcf21a..4de3b26c3 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 000000000..a24e84e1b --- /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 +#include +#include +#include +#include +#include +#include +#include + +#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 \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 000000000..6f38acb6a --- /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 000000000..73aa8ba8c --- /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 +#include +#include +#include +#include +#include +#include + +#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 \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); +}