freebsd-skq/dist/sctp.cc
ngie 70d75d7acf Import capsicum-test into ^/vendor/google/capsicum-test/dist
The following change imports google/capsicum-test@9333154 from GitHub, omitting
the embedded version of googletest, as well as the incomplete libcasper.

This test suite helps verify capsicum(3) support via functional tests
written in the GoogleTest test framework.

Kernel support for capsicum(4) is tested by side-effect of testing
capsicum(3).

NB: as discussed in a previous [closed] PR [1], the casper(3) tests are
incomplete/buggy and will not pass on FreeBSD. Thus, I have no intention of
integrating them into the build/test on FreeBSD as-is.

The import command used was:
```
curl -L https://github.com/google/capsicum-test/tarball/9333154 | tar --strip-components=1 -xvzf - -C dist/
rm -Rf dist/*/
```

1. https://github.com/google/capsicum-test/pull/26

Reviewed by:	emaste (mentor)
Differential Revision:	https://reviews.freebsd.org/D19261
2019-03-12 01:43:01 +00:00

213 lines
7.1 KiB
C++

// Tests of SCTP functionality
// Requires: libsctp-dev package on Debian Linux, CONFIG_IP_SCTP in kernel config
#ifdef HAVE_SCTP
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include "syscalls.h"
#include "capsicum.h"
#include "capsicum-test.h"
static cap_rights_t r_ro;
static cap_rights_t r_wo;
static cap_rights_t r_rw;
static cap_rights_t r_all;
static cap_rights_t r_all_nopeel;
#define DO_PEELOFF 0x1A
#define DO_TERM 0x1B
static int SctpClient(int port, unsigned char byte) {
// Create sockets
int sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
EXPECT_OK(sock);
if (sock < 0) return sock;
int cap_sock_ro = dup(sock);
EXPECT_OK(cap_sock_ro);
EXPECT_OK(cap_rights_limit(cap_sock_ro, &r_rw));
int cap_sock_rw = dup(sock);
EXPECT_OK(cap_sock_rw);
EXPECT_OK(cap_rights_limit(cap_sock_rw, &r_rw));
int cap_sock_all = dup(sock);
EXPECT_OK(cap_sock_all);
EXPECT_OK(cap_rights_limit(cap_sock_all, &r_all));
close(sock);
// Send a message. Requires CAP_WRITE and CAP_CONNECT.
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(port);
EXPECT_NOTCAPABLE(sctp_sendmsg(cap_sock_ro, &byte, 1,
(struct sockaddr*)&serv_addr, sizeof(serv_addr),
0, 0, 1, 0, 0));
EXPECT_NOTCAPABLE(sctp_sendmsg(cap_sock_rw, &byte, 1,
(struct sockaddr*)&serv_addr, sizeof(serv_addr),
0, 0, 1, 0, 0));
if (verbose) fprintf(stderr, " [%d]sctp_sendmsg(%02x)\n", getpid_(), byte);
EXPECT_OK(sctp_sendmsg(cap_sock_all, &byte, 1,
(struct sockaddr*)&serv_addr, sizeof(serv_addr),
0, 0, 1, 0, 0));
close(cap_sock_ro);
close(cap_sock_rw);
return cap_sock_all;
}
TEST(Sctp, Socket) {
int sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
EXPECT_OK(sock);
if (sock < 0) return;
cap_rights_init(&r_ro, CAP_READ);
cap_rights_init(&r_wo, CAP_WRITE);
cap_rights_init(&r_rw, CAP_READ, CAP_WRITE);
cap_rights_init(&r_all, CAP_READ, CAP_WRITE, CAP_SOCK_CLIENT, CAP_SOCK_SERVER);
cap_rights_init(&r_all_nopeel, CAP_READ, CAP_WRITE, CAP_SOCK_CLIENT, CAP_SOCK_SERVER);
cap_rights_clear(&r_all_nopeel, CAP_PEELOFF);
int cap_sock_wo = dup(sock);
EXPECT_OK(cap_sock_wo);
EXPECT_OK(cap_rights_limit(cap_sock_wo, &r_wo));
int cap_sock_rw = dup(sock);
EXPECT_OK(cap_sock_rw);
EXPECT_OK(cap_rights_limit(cap_sock_rw, &r_rw));
int cap_sock_all = dup(sock);
EXPECT_OK(cap_sock_all);
EXPECT_OK(cap_rights_limit(cap_sock_all, &r_all));
int cap_sock_all_nopeel = dup(sock);
EXPECT_OK(cap_sock_all_nopeel);
EXPECT_OK(cap_rights_limit(cap_sock_all_nopeel, &r_all_nopeel));
close(sock);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(0);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(addr);
// Can only bind the fully-capable socket.
EXPECT_NOTCAPABLE(bind(cap_sock_rw, (struct sockaddr *)&addr, len));
EXPECT_OK(bind(cap_sock_all, (struct sockaddr *)&addr, len));
EXPECT_OK(getsockname(cap_sock_all, (struct sockaddr *)&addr, &len));
int port = ntohs(addr.sin_port);
// Now we know the port involved, fork off children to run clients.
pid_t child1 = fork();
if (child1 == 0) {
// Child process 1: wait for server setup
sleep(1);
// Send a message that triggers peeloff
int client_sock = SctpClient(port, DO_PEELOFF);
sleep(1);
close(client_sock);
exit(HasFailure());
}
pid_t child2 = fork();
if (child2 == 0) {
// Child process 2: wait for server setup
sleep(2);
// Send a message that triggers server exit
int client_sock = SctpClient(port, DO_TERM);
close(client_sock);
exit(HasFailure());
}
// Can only listen on the fully-capable socket.
EXPECT_NOTCAPABLE(listen(cap_sock_rw, 3));
EXPECT_OK(listen(cap_sock_all, 3));
// Can only do socket operations on the fully-capable socket.
len = sizeof(addr);
EXPECT_NOTCAPABLE(getsockname(cap_sock_rw, (struct sockaddr*)&addr, &len));
struct sctp_event_subscribe events;
memset(&events, 0, sizeof(events));
events.sctp_association_event = 1;
events.sctp_data_io_event = 1;
EXPECT_NOTCAPABLE(setsockopt(cap_sock_rw, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)));
len = sizeof(events);
EXPECT_NOTCAPABLE(getsockopt(cap_sock_rw, IPPROTO_SCTP, SCTP_EVENTS, &events, &len));
memset(&events, 0, sizeof(events));
events.sctp_association_event = 1;
events.sctp_data_io_event = 1;
EXPECT_OK(setsockopt(cap_sock_all, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)));
len = sizeof(events);
EXPECT_OK(getsockopt(cap_sock_all, IPPROTO_SCTP, SCTP_EVENTS, &events, &len));
len = sizeof(addr);
memset(&addr, 0, sizeof(addr));
EXPECT_OK(getsockname(cap_sock_all, (struct sockaddr*)&addr, &len));
EXPECT_EQ(AF_INET, addr.sin_family);
EXPECT_EQ(htons(port), addr.sin_port);
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
unsigned char buffer[1024];
struct sctp_sndrcvinfo sri;
memset(&sri, 0, sizeof(sri));
int flags = 0;
EXPECT_NOTCAPABLE(sctp_recvmsg(cap_sock_wo, buffer, sizeof(buffer),
(struct sockaddr*)&client_addr, &addr_len,
&sri, &flags));
while (true) {
retry:
memset(&sri, 0, sizeof(sri));
int len = sctp_recvmsg(cap_sock_rw, buffer, sizeof(buffer),
(struct sockaddr*)&client_addr, &addr_len,
&sri, &flags);
if (len < 0 && errno == EAGAIN) goto retry;
EXPECT_OK(len);
if (len > 0) {
if (verbose) fprintf(stderr, "[%d]sctp_recvmsg(%02x..)", getpid_(), (unsigned)buffer[0]);
if (buffer[0] == DO_PEELOFF) {
if (verbose) fprintf(stderr, "..peeling off association %08lx\n", (long)sri.sinfo_assoc_id);
// Peel off the association. Needs CAP_PEELOFF.
int rc1 = sctp_peeloff(cap_sock_all_nopeel, sri.sinfo_assoc_id);
EXPECT_NOTCAPABLE(rc1);
int rc2 = sctp_peeloff(cap_sock_all, sri.sinfo_assoc_id);
EXPECT_OK(rc2);
int peeled = std::max(rc1, rc2);
if (peeled > 0) {
#ifdef CAP_FROM_PEELOFF
// Peeled off FD should have same rights as original socket.
cap_rights_t rights;
EXPECT_OK(cap_rights_get(peeled, &rights));
EXPECT_RIGHTS_EQ(&r_all, &rights);
#endif
close(peeled);
}
} else if (buffer[0] == DO_TERM) {
if (verbose) fprintf(stderr, "..terminating server\n");
break;
}
} else if (len < 0) {
break;
}
}
// Wait for the children.
int status;
int rc;
EXPECT_EQ(child1, waitpid(child1, &status, 0));
rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
EXPECT_EQ(0, rc);
EXPECT_EQ(child2, waitpid(child2, &status, 0));
rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
EXPECT_EQ(0, rc);
close(cap_sock_wo);
close(cap_sock_rw);
close(cap_sock_all);
close(cap_sock_all_nopeel);
}
#endif