tests: Verify mediation of path-based UNIX domain sockets

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@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
This commit is contained in:
Tyler Hicks 2013-10-29 10:35:51 -07:00
parent fa8fecd610
commit 7adcc25aa4
5 changed files with 419 additions and 0 deletions

View file

@ -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

View file

@ -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}

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}