mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
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:
parent
fa8fecd610
commit
7adcc25aa4
5 changed files with 419 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
177
tests/regression/apparmor/unix_socket_file.c
Normal file
177
tests/regression/apparmor/unix_socket_file.c
Normal 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);
|
||||
}
|
105
tests/regression/apparmor/unix_socket_file.sh
Executable file
105
tests/regression/apparmor/unix_socket_file.sh
Executable 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
|
126
tests/regression/apparmor/unix_socket_file_client.c
Normal file
126
tests/regression/apparmor/unix_socket_file_client.c
Normal 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);
|
||||
}
|
Loading…
Add table
Reference in a new issue