Tests abstract UNIX domain sockets with various combinations of implied permissions, explicit permissions, and conditionals. It also tests with bad permissions and conditionals.
Signed-off-by: Tyler Hicks <[email protected]> --- tests/regression/apparmor/unix_socket.c | 89 +++++++++++++----- tests/regression/apparmor/unix_socket_client.c | 113 +++++++++++++++-------- tests/regression/apparmor/unix_socket_unnamed.sh | 109 ++++++++++++++++++++++ 3 files changed, 248 insertions(+), 63 deletions(-) create mode 100755 tests/regression/apparmor/unix_socket_unnamed.sh diff --git a/tests/regression/apparmor/unix_socket.c b/tests/regression/apparmor/unix_socket.c index 1b89c45..a895a26 100644 --- a/tests/regression/apparmor/unix_socket.c +++ b/tests/regression/apparmor/unix_socket.c @@ -14,6 +14,8 @@ * along with this program; if not, contact Canonical Ltd. */ +#define _GNU_SOURCE + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -21,18 +23,24 @@ #include <sys/types.h> #include <sys/un.h> #include <unistd.h> +#include <fcntl.h> -#define MSG_BUF_MAX 1024 +#define MSG_BUF_MAX 1024 +#define PATH_FOR_UNNAMED "none" -static int connection_based_messaging(int sock, char *msg_buf, - size_t msg_buf_len) +static int connection_based_messaging(int sock, int sock_is_peer_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; + if (sock_is_peer_sock) { + peer_sock = sock; + } else { + peer_sock = accept(sock, NULL, NULL); + if (peer_sock < 0) { + perror("FAIL - accept"); + return 1; + } } rc = write(peer_sock, msg_buf, msg_buf_len); @@ -118,7 +126,8 @@ int main (int argc, char *argv[]) const char *sun_path; size_t sun_path_len; pid_t pid; - int sock, type, rc; + int sock, peer_sock, type, rc; + int unnamed = 0; if (argc != 5) { fprintf(stderr, @@ -141,6 +150,8 @@ int main (int argc, char *argv[]) } memcpy(addr.sun_path, sun_path, sun_path_len); addr.sun_path[0] = '\0'; + } else if (!strcmp(sun_path, PATH_FOR_UNNAMED)) { + unnamed = 1; } else { /* include the nul terminator for pathname addr types */ sun_path_len++; @@ -169,25 +180,43 @@ int main (int argc, char *argv[]) } memcpy(msg_buf, argv[3], msg_buf_len); - sock = socket(AF_UNIX, type | SOCK_CLOEXEC, 0); - if (sock == -1) { - perror("FAIL - socket"); - exit(1); - } + if (unnamed) { + int sv[2]; - rc = bind(sock, (struct sockaddr *)&addr, - sun_path_len + sizeof(addr.sun_family)); - if (rc < 0) { - perror("FAIL - bind"); - exit(1); - } + rc = socketpair(AF_UNIX, type, 0, sv); + if (rc == -1) { + perror("FAIL - socketpair"); + exit(1); + } + sock = sv[0]; + peer_sock = sv[1]; - if (type & SOCK_STREAM || type & SOCK_SEQPACKET) { - rc = listen(sock, 2); + rc = fcntl(sock, F_SETFD, FD_CLOEXEC); + if (rc == -1) { + perror("FAIL - fcntl"); + exit(1); + } + } else { + sock = socket(AF_UNIX, type | SOCK_CLOEXEC, 0); + if (sock == -1) { + perror("FAIL - socket"); + exit(1); + } + + rc = bind(sock, (struct sockaddr *)&addr, + sun_path_len + sizeof(addr.sun_family)); if (rc < 0) { - perror("FAIL - listen"); + 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(); @@ -195,8 +224,20 @@ int main (int argc, char *argv[]) perror("FAIL - fork"); exit(1); } else if (!pid) { - execl(argv[4], argv[4], sun_path, argv[2], NULL); + char *fd_number = NULL; + + if (unnamed) { + rc = asprintf(&fd_number, "%d", peer_sock); + if (rc == -1) { + perror("FAIL - asprintf"); + exit(1); + } + } + + /* fd_number will be NULL for pathname and abstract sockets */ + execl(argv[4], argv[4], sun_path, argv[2], fd_number, NULL); perror("FAIL - execl"); + free(fd_number); exit(1); } @@ -205,7 +246,7 @@ int main (int argc, char *argv[]) exit(1); rc = (type & SOCK_STREAM || type & SOCK_SEQPACKET) ? - connection_based_messaging(sock, msg_buf, msg_buf_len) : + connection_based_messaging(sock, unnamed, msg_buf, msg_buf_len) : connectionless_messaging(sock, msg_buf, msg_buf_len); if (rc) exit(1); diff --git a/tests/regression/apparmor/unix_socket_client.c b/tests/regression/apparmor/unix_socket_client.c index 015c41d..a4ccf0c 100644 --- a/tests/regression/apparmor/unix_socket_client.c +++ b/tests/regression/apparmor/unix_socket_client.c @@ -22,21 +22,25 @@ #include <sys/un.h> #include <unistd.h> -#define MSG_BUF_MAX 1024 +#define MSG_BUF_MAX 1024 +#define PATH_FOR_UNNAMED "none" #define SUN_PATH_SUFFIX ".client" #define SUN_PATH_SUFFIX_LEN strlen(SUN_PATH_SUFFIX) +/* Pass NULL for peer_addr if the two sockets are already connected */ static int connection_based_messaging(int sock, struct sockaddr_un *peer_addr, socklen_t peer_addr_len) { char msg_buf[MSG_BUF_MAX]; int rc; - rc = connect(sock, (struct sockaddr *)peer_addr, peer_addr_len); - if (rc < 0) { - perror("FAIL CLIENT - connect"); - exit(1); + if (peer_addr) { + rc = connect(sock, (struct sockaddr *)peer_addr, peer_addr_len); + if (rc < 0) { + perror("FAIL CLIENT - connect"); + exit(1); + } } rc = read(sock, msg_buf, MSG_BUF_MAX); @@ -54,37 +58,42 @@ static int connection_based_messaging(int sock, struct sockaddr_un *peer_addr, return 0; } +/* Pass NULL for peer_addr if the two sockets are already connected */ static int connectionless_messaging(int sock, struct sockaddr_un *peer_addr, socklen_t peer_addr_len) { - struct sockaddr_un addr; - size_t peer_path_len = peer_addr_len - sizeof(addr.sun_family); - size_t path_len = peer_path_len + SUN_PATH_SUFFIX_LEN; char msg_buf[MSG_BUF_MAX]; socklen_t len = peer_addr_len; int rc; - if (path_len > sizeof(addr.sun_path)) { - fprintf(stderr, "FAIL CLIENT - path_len too big\n"); - return 1; - } - - /** - * Subtract 1 to get rid of nul-terminator in pathname address types. - * We're essentially moving the nul char so path_len stays the same. - */ - if (peer_addr->sun_path[0]) - peer_path_len--; + if (peer_addr) { + struct sockaddr_un addr; + size_t peer_path_len = peer_addr_len - sizeof(addr.sun_family); + size_t path_len = peer_path_len + SUN_PATH_SUFFIX_LEN; - addr.sun_family = AF_UNIX; - memcpy(addr.sun_path, peer_addr->sun_path, peer_path_len); - strcpy(addr.sun_path + peer_path_len, SUN_PATH_SUFFIX); + if (path_len > sizeof(addr.sun_path)) { + fprintf(stderr, "FAIL CLIENT - path_len too big\n"); + return 1; + } - rc = bind(sock, (struct sockaddr *)&addr, - path_len + sizeof(addr.sun_family)); - if (rc < 0) { - perror("FAIL CLIENT - bind"); - return 1; + /** + * Subtract 1 to get rid of nul-terminator in pathname address + * types. We're essentially moving the nul char so path_len + * stays the same. + */ + if (peer_addr->sun_path[0]) + peer_path_len--; + + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, peer_addr->sun_path, peer_path_len); + strcpy(addr.sun_path + peer_path_len, SUN_PATH_SUFFIX); + + rc = bind(sock, (struct sockaddr *)&addr, + path_len + sizeof(addr.sun_family)); + if (rc < 0) { + perror("FAIL CLIENT - bind"); + return 1; + } } rc = sendto(sock, NULL, 0, 0, (struct sockaddr *)peer_addr, len); @@ -154,18 +163,26 @@ static int test_getattr(int sock) return 0; } +static void usage(const char *name) +{ + fprintf(stderr, "Usage: %s <socket> <type> [<fd_number>]\n\n" + " type\t\tstream, dgram, or seqpacket\n" + " fd_number\t\tfd number for inherited unnamed socket\n", + name); +} + int main(int argc, char *argv[]) { - struct sockaddr_un peer_addr, *pa; - socklen_t pa_len; + struct sockaddr_un peer_addr, *pa = NULL; + socklen_t pa_len = 0; const char *sun_path; size_t sun_path_len; int sock, type, rc; + int unnamed = 0; + const char *fd_number = NULL; - if (argc != 3) { - fprintf(stderr, "Usage: %s <socket> <type>\n\n" - " type\t\tstream, dgram, or seqpacket\n", - argv[0]); + if (argc < 3 || argc > 4) { + usage(argv[0]); exit(1); } @@ -181,6 +198,13 @@ int main(int argc, char *argv[]) } memcpy(peer_addr.sun_path, sun_path, sun_path_len); peer_addr.sun_path[0] = '\0'; + } else if (!strcmp(sun_path, PATH_FOR_UNNAMED)) { + unnamed = 1; + if (argc != 4) { + usage(argv[0]); + exit(1); + } + fd_number = argv[3]; } else { /* include the nul terminator for pathname addr types */ sun_path_len++; @@ -202,10 +226,19 @@ int main(int argc, char *argv[]) exit(1); } - sock = socket(AF_UNIX, type, 0); - if (sock < 0) { - perror("FAIL CLIENT - socket"); - exit(1); + if (unnamed) { + rc = sscanf(fd_number, "%d", &sock); + if (rc != 1) { + perror("FAIL CLIENT - sscanf"); + usage(argv[0]); + exit(1); + } + } else { + sock = socket(AF_UNIX, type, 0); + if (sock < 0) { + perror("FAIL CLIENT - socket"); + exit(1); + } } rc = get_set_sock_io_timeo(sock); @@ -216,8 +249,10 @@ int main(int argc, char *argv[]) if (rc) exit(1); - pa = &peer_addr; - pa_len = sun_path_len + sizeof(peer_addr.sun_family); + if (!unnamed) { + pa = &peer_addr; + pa_len = sun_path_len + sizeof(peer_addr.sun_family); + } rc = (type == SOCK_STREAM || type == SOCK_SEQPACKET) ? connection_based_messaging(sock, pa, pa_len) : diff --git a/tests/regression/apparmor/unix_socket_unnamed.sh b/tests/regression/apparmor/unix_socket_unnamed.sh new file mode 100755 index 0000000..4da51f9 --- /dev/null +++ b/tests/regression/apparmor/unix_socket_unnamed.sh @@ -0,0 +1,109 @@ +#! /bin/bash +# +# Copyright (C) 2014 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_unnamed +#=DESCRIPTION +# This tests access to unnamed unix domain sockets. The server opens a socket, +# forks a client with it's own profile, passes an fd across exec, sends a +# message to the client over the socket pair, and sees what happens. +#=END + +pwd=`dirname $0` +pwd=`cd $pwd ; /bin/pwd` + +bin=$pwd + +. $bin/prologue.inc +. $bin/unix_socket.inc +requires_features policy/versions/v7 +requires_features network/af_unix + +settest unix_socket + +addr=none +client_addr=none + +# Test unnamed stream server and client +test_server "unnamed" \ + "create,getopt,setopt,shutdown" \ + stream \ + "$addr" \ + "read,write" \ + "$test" \ + "" \ + dgram \ + "@none" \ + "${test}XXX" \ + "" +test_client "unnamed" \ + "getopt,setopt,getattr" \ + stream \ + "" \ + "write,read" \ + "$test" \ + "$addr" \ + seqpacket \ + "" \ + "${test}XXX" \ + "@none" + +# Test unnamed dgram server and client +test_server "unnamed" \ + "create,getopt,setopt,shutdown" \ + dgram \ + "$addr" \ + "read,write" \ + "$test" \ + "$client_addr" \ + seqpacket \ + "@none" \ + "${test}XXX" \ + "@none" +test_client "unnamed" \ + "getopt,setopt,getattr" \ + dgram \ + "$client_addr" \ + "write,read" \ + "$test" \ + "$addr" \ + stream \ + "@none" \ + "${test}XXX" \ + "@none" + +# Test unnamed seqpacket server and client +test_server "unnamed" \ + "create,getopt,setopt,shutdown" \ + seqpacket \ + "$addr" \ + "read,write" \ + "$test" \ + "" \ + stream \ + "@none" \ + "${test}XXX" \ + "" +test_client "unnamed" \ + "getopt,setopt,getattr" \ + seqpacket \ + "" \ + "write,read" \ + "$test" \ + "$addr" \ + dgram \ + "" \ + "${test}XXX" \ + "@none" -- 2.1.0 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
