summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorRenato Botelho <renato@netgate.com>2016-01-07 18:08:08 -0200
committerRenato Botelho <renato@netgate.com>2016-01-07 18:08:08 -0200
commit7a579754077b3460c9509c75cdd1b78769c1db3f (patch)
treec15015685ccb82b7db1ac19d663a3c6e41e64587 /tests
parentd8ff3484131f428fcc0727cd504acb5050a36490 (diff)
parentbc6ee646001a22150936ff06bf11cd08195e208d (diff)
downloadFreeBSD-src-7a579754077b3460c9509c75cdd1b78769c1db3f.zip
FreeBSD-src-7a579754077b3460c9509c75cdd1b78769c1db3f.tar.gz
Merge remote-tracking branch 'origin/stable/10' into devel
Diffstat (limited to 'tests')
-rw-r--r--tests/sys/Makefile1
-rw-r--r--tests/sys/aio/aio_kqueue_test.c37
-rw-r--r--tests/sys/aio/lio_kqueue_test.c101
-rw-r--r--tests/sys/kern/Makefile1
-rw-r--r--tests/sys/kern/pipe/pipe_overcommit1_test.c7
-rw-r--r--tests/sys/kern/ptrace_test.c6
-rw-r--r--tests/sys/kern/unix_passfd_test.c396
-rw-r--r--tests/sys/kern/unix_seqpacket_test.c93
-rw-r--r--tests/sys/mac/Makefile8
-rw-r--r--tests/sys/mac/bsdextended/Makefile14
-rw-r--r--tests/sys/mac/bsdextended/matches_test.sh353
-rw-r--r--tests/sys/mac/bsdextended/ugidfw_test.c252
-rw-r--r--tests/sys/mac/portacl/LICENSE27
-rw-r--r--tests/sys/mac/portacl/Makefile16
-rwxr-xr-xtests/sys/mac/portacl/misc.sh106
-rwxr-xr-xtests/sys/mac/portacl/nobody_test.sh67
-rwxr-xr-xtests/sys/mac/portacl/root_test.sh51
17 files changed, 1431 insertions, 105 deletions
diff --git a/tests/sys/Makefile b/tests/sys/Makefile
index 015040d..4e653be 100644
--- a/tests/sys/Makefile
+++ b/tests/sys/Makefile
@@ -10,6 +10,7 @@ TESTS_SUBDIRS+= fifo
TESTS_SUBDIRS+= file
TESTS_SUBDIRS+= kern
TESTS_SUBDIRS+= kqueue
+TESTS_SUBDIRS+= mac
TESTS_SUBDIRS+= mqueue
TESTS_SUBDIRS+= netinet
TESTS_SUBDIRS+= opencrypto
diff --git a/tests/sys/aio/aio_kqueue_test.c b/tests/sys/aio/aio_kqueue_test.c
index 14e4729..97c2c38f 100644
--- a/tests/sys/aio/aio_kqueue_test.c
+++ b/tests/sys/aio/aio_kqueue_test.c
@@ -62,7 +62,10 @@ main (int argc, char *argv[])
struct kevent ke, kq_returned;
struct timespec ts;
char buffer[32768];
- int cancel, error, failed = 0, fd, kq, pending, result, run;
+#ifdef DEBUG
+ int cancel, error;
+#endif
+ int failed = 0, fd, kq, pending, result, run;
int tmp_file = 0;
unsigned i, j;
@@ -96,19 +99,19 @@ main (int argc, char *argv[])
if (iocb[i] == NULL)
err(1, "calloc");
}
-
- pending = 0;
+
+ pending = 0;
for (i = 0; i < nitems(iocb); i++) {
pending++;
iocb[i]->aio_nbytes = sizeof(buffer);
iocb[i]->aio_buf = buffer;
iocb[i]->aio_fildes = fd;
iocb[i]->aio_offset = iocb[i]->aio_nbytes * i * run;
-
+
iocb[i]->aio_sigevent.sigev_notify_kqueue = kq;
iocb[i]->aio_sigevent.sigev_value.sival_ptr = iocb[i];
iocb[i]->aio_sigevent.sigev_notify = SIGEV_KEVENT;
-
+
result = aio_write(iocb[i]);
if (result != 0) {
perror("aio_write");
@@ -133,7 +136,9 @@ main (int argc, char *argv[])
}
}
}
+#ifdef DEBUG
cancel = nitems(iocb) - pending;
+#endif
i = 0;
while (pending) {
@@ -144,34 +149,36 @@ main (int argc, char *argv[])
bzero(&kq_returned, sizeof(ke));
ts.tv_sec = 0;
ts.tv_nsec = 1;
- result = kevent(kq, NULL, 0,
+ result = kevent(kq, NULL, 0,
&kq_returned, 1, &ts);
+#ifdef DEBUG
error = errno;
+#endif
if (result < 0)
perror("kevent error: ");
kq_iocb = kq_returned.udata;
#ifdef DEBUG
printf("kevent %d %d errno %d return.ident %p "
- "return.data %p return.udata %p %p\n",
- i, result, error,
- kq_returned.ident, kq_returned.data,
- kq_returned.udata,
+ "return.data %p return.udata %p %p\n",
+ i, result, error,
+ kq_returned.ident, kq_returned.data,
+ kq_returned.udata,
kq_iocb);
#endif
-
+
if (kq_iocb)
break;
#ifdef DEBUG
printf("Try again left %d out of %d %d\n",
pending, nitems(iocb), cancel);
#endif
- }
-
+ }
+
for (j = 0; j < nitems(iocb) && iocb[j] != kq_iocb;
j++) ;
#ifdef DEBUG
printf("kq_iocb %p\n", kq_iocb);
-
+
printf("Error Result for %d is %d pending %d\n",
j, result, pending);
#endif
@@ -192,7 +199,7 @@ main (int argc, char *argv[])
iocb[j] = NULL;
pending--;
i++;
- }
+ }
for (i = 0; i < nitems(iocb); i++)
free(iocb[i]);
diff --git a/tests/sys/aio/lio_kqueue_test.c b/tests/sys/aio/lio_kqueue_test.c
index 5cc87b3..e69b9c9 100644
--- a/tests/sys/aio/lio_kqueue_test.c
+++ b/tests/sys/aio/lio_kqueue_test.c
@@ -42,6 +42,7 @@
#include <sys/time.h>
#include <aio.h>
#include <fcntl.h>
+#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -57,26 +58,26 @@
#define MAX_RUNS 300
int
-main(int argc, char *argv[]){
+main(int argc, char *argv[])
+{
int fd;
struct aiocb *iocb[MAX_IOCBS];
- struct aiocb **lio[LIO_MAX], **lio_element, **kq_lio;
+ struct aiocb **lio[LIO_MAX], **kq_lio;
int i, result, run, error, j, k;
char buffer[32768];
- int kq = kqueue();
+ int kq;
struct kevent ke, kq_returned;
struct timespec ts;
struct sigevent sig;
time_t time1, time2;
- char *file, pathname[sizeof(PATH_TEMPLATE)-1];
+ char *file, pathname[sizeof(PATH_TEMPLATE)];
int tmp_file = 0, failed = 0;
PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
- if (kq < 0) {
- perror("No kqeueue\n");
- exit(1);
- }
+ kq = kqueue();
+ if (kq < 0)
+ err(1, "kqeueue(2) failed");
if (argc == 1) {
strcpy(pathname, PATH_TEMPLATE);
@@ -87,34 +88,29 @@ main(int argc, char *argv[]){
file = argv[1];
fd = open(file, O_RDWR|O_CREAT, 0666);
}
- if (fd < 0){
- fprintf(stderr, "Can't open %s\n", argv[1]);
- perror("");
- exit(1);
- }
+ if (fd < 0)
+ err(1, "can't open %s", argv[1]);
#ifdef DEBUG
printf("Hello kq %d fd %d\n", kq, fd);
#endif
- for (run = 0; run < MAX_RUNS; run++){
+ for (run = 0; run < MAX_RUNS; run++) {
#ifdef DEBUG
printf("Run %d\n", run);
#endif
for (j = 0; j < LIO_MAX; j++) {
- lio[j] = (struct aiocb **)
+ lio[j] =
malloc(sizeof(struct aiocb *) * MAX_IOCBS/LIO_MAX);
- for(i = 0; i < MAX_IOCBS / LIO_MAX; i++) {
+ for (i = 0; i < MAX_IOCBS / LIO_MAX; i++) {
k = (MAX_IOCBS / LIO_MAX * j) + i;
- lio_element = lio[j];
- lio[j][i] = iocb[k] = (struct aiocb *)
- malloc(sizeof(struct aiocb));
- bzero(iocb[k], sizeof(struct aiocb));
+ lio[j][i] = iocb[k] =
+ calloc(1, sizeof(struct aiocb));
iocb[k]->aio_nbytes = sizeof(buffer);
iocb[k]->aio_buf = buffer;
iocb[k]->aio_fildes = fd;
- iocb[k]->aio_offset
- = iocb[k]->aio_nbytes * k * (run + 1);
+ iocb[k]->aio_offset
+ = iocb[k]->aio_nbytes * k * (run + 1);
#ifdef DEBUG
printf("hello iocb[k] %d\n",
@@ -131,27 +127,26 @@ main(int argc, char *argv[]){
error = errno;
time(&time2);
#ifdef DEBUG
- printf("Time %d %d %d result -> %d\n",
+ printf("Time %d %d %d result -> %d\n",
time1, time2, time2-time1, result);
#endif
if (result != 0) {
errno = error;
- perror("list_listio");
- printf("FAIL: Result %d iteration %d\n",result, j);
- exit(1);
+ err(1, "FAIL: Result %d iteration %d\n",
+ result, j);
}
#ifdef DEBUG
printf("write %d is at %p\n", j, lio[j]);
#endif
}
- for(i = 0; i < LIO_MAX; i++) {
- for(j = LIO_MAX - 1; j >=0; j--) {
+ for (i = 0; i < LIO_MAX; i++) {
+ for (j = LIO_MAX - 1; j >=0; j--) {
if (lio[j])
break;
}
- for(;;) {
+ for (;;) {
bzero(&ke, sizeof(ke));
bzero(&kq_returned, sizeof(ke));
ts.tv_sec = 0;
@@ -159,9 +154,9 @@ main(int argc, char *argv[]){
#ifdef DEBUG
printf("FOO lio %d -> %p\n", j, lio[j]);
#endif
- EV_SET(&ke, (uintptr_t)lio[j],
+ EV_SET(&ke, (uintptr_t)lio[j],
EVFILT_LIO, EV_ONESHOT, 0, 0, iocb[j]);
- result = kevent(kq, NULL, 0,
+ result = kevent(kq, NULL, 0,
&kq_returned, 1, &ts);
error = errno;
if (result < 0) {
@@ -170,14 +165,14 @@ main(int argc, char *argv[]){
kq_lio = kq_returned.udata;
#ifdef DEBUG
printf("kevent %d %d errno %d return.ident %p "
- "return.data %p return.udata %p %p\n",
- i, result, error,
- kq_returned.ident, kq_returned.data,
- kq_returned.udata,
+ "return.data %p return.udata %p %p\n",
+ i, result, error,
+ kq_returned.ident, kq_returned.data,
+ kq_returned.udata,
lio[j]);
#endif
- if(kq_lio)
+ if (kq_lio)
break;
#ifdef DEBUG
printf("Try again\n");
@@ -189,25 +184,21 @@ main(int argc, char *argv[]){
#endif
for (j = 0; j < LIO_MAX; j++) {
- if (lio[j] == kq_lio) {
+ if (lio[j] == kq_lio)
break;
- }
- }
- if (j == LIO_MAX) {
- printf("FAIL:\n");
- exit(1);
}
+ if (j == LIO_MAX)
+ errx(1, "FAIL: ");
#ifdef DEBUG
printf("Error Result for %d is %d\n", j, result);
#endif
if (result < 0) {
printf("FAIL: run %d, operation %d result %d \n", run, LIO_MAX - i -1, result);
- failed = 1;
- } else {
+ failed++;
+ } else
printf("PASS: run %d, operation %d result %d \n", run, LIO_MAX - i -1, result);
- }
- for(k = 0; k < MAX_IOCBS / LIO_MAX; k++){
+ for (k = 0; k < MAX_IOCBS / LIO_MAX; k++) {
result = aio_return(kq_lio[k]);
#ifdef DEBUG
printf("Return Resulto for %d %d is %d\n", j, k, result);
@@ -224,9 +215,8 @@ main(int argc, char *argv[]){
printf("\n");
#endif
- for(k = 0; k < MAX_IOCBS / LIO_MAX; k++) {
+ for (k = 0; k < MAX_IOCBS / LIO_MAX; k++)
free(lio[j][k]);
- }
free(lio[j]);
lio[j] = NULL;
}
@@ -235,15 +225,12 @@ main(int argc, char *argv[]){
printf("Done\n");
#endif
- if (tmp_file) {
+ if (tmp_file)
unlink(pathname);
- }
- if (failed) {
- printf("FAIL: Atleast one\n");
- exit(1);
- } else {
- printf("PASS: All\n");
- exit(0);
- }
+ if (failed)
+ errx(1, "FAIL: %d testcases failed", failed);
+ else
+ errx(0, "PASS: All\n");
+
}
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 03b4a5c..296bbed 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -9,6 +9,7 @@ TESTSDIR= ${TESTSBASE}/sys/kern
ATF_TESTS_C+= kern_descrip_test
ATF_TESTS_C+= ptrace_test
ATF_TESTS_C+= unix_seqpacket_test
+ATF_TESTS_C+= unix_passfd_test
TEST_METADATA.unix_seqpacket_test+= timeout="15"
LDADD.ptrace_test+= -lpthread
diff --git a/tests/sys/kern/pipe/pipe_overcommit1_test.c b/tests/sys/kern/pipe/pipe_overcommit1_test.c
index f8f881d..4e40be7 100644
--- a/tests/sys/kern/pipe/pipe_overcommit1_test.c
+++ b/tests/sys/kern/pipe/pipe_overcommit1_test.c
@@ -40,12 +40,11 @@
int
main(void)
{
- int pipes[10000], returnval;
+ int pipes[10000];
unsigned int i;
- for (i = 0; i < nitems(pipes); i++) {
- returnval = pipe(&pipes[i]);
- }
+ for (i = 0; i < nitems(pipes); i++)
+ (void)pipe(&pipes[i]);
printf("PASS\n");
exit(0);
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index 1731698..1be1144 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -1017,7 +1017,7 @@ ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
{
struct ptrace_lwpinfo pl;
pid_t fpid, wpid;
- lwpid_t main;
+ lwpid_t mainlwp;
int status;
ATF_REQUIRE((fpid = fork()) != -1);
@@ -1040,7 +1040,7 @@ ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
sizeof(pl)) != -1);
- main = pl.pl_lwpid;
+ mainlwp = pl.pl_lwpid;
/*
* Continue the child ignoring the SIGSTOP and tracing all
@@ -1065,7 +1065,7 @@ ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
sizeof(pl)) != -1);
ATF_REQUIRE((pl.pl_flags & PL_FLAG_SCX) != 0);
ATF_REQUIRE(pl.pl_syscall_code != 0);
- if (pl.pl_lwpid != main)
+ if (pl.pl_lwpid != mainlwp)
/* New thread seen. */
break;
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
new file mode 100644
index 0000000..12568389
--- /dev/null
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -0,0 +1,396 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * Copyright (c) 2015 Mark Johnston
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * UNIX domain sockets allow file descriptors to be passed via "ancillary
+ * data", or control messages. This regression test is intended to exercise
+ * this facility, both performing some basic tests that it operates, and also
+ * causing some kernel edge cases to execute, such as garbage collection when
+ * there are cyclic file descriptor references. Right now we test only with
+ * stream sockets, but ideally we'd also test with datagram sockets.
+ */
+
+static void
+domainsocketpair(int *fdp)
+{
+
+ ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) != -1,
+ "socketpair(PF_UNIX, SOCK_STREAM) failed: %s", strerror(errno));
+}
+
+static void
+closesocketpair(int *fdp)
+{
+
+ close(fdp[0]);
+ close(fdp[1]);
+}
+
+static void
+devnull(int *fdp)
+{
+ int fd;
+
+ fd = open("/dev/null", O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ *fdp = fd;
+}
+
+static void
+tempfile(int *fdp)
+{
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s/unix_passfd.XXXXXXXXXXXXXXX",
+ getenv("TMPDIR") == NULL ? "/tmp" : getenv("TMPDIR"));
+ fd = mkstemp(path);
+ ATF_REQUIRE_MSG(fd != -1, "mkstemp(%s) failed", path);
+ (void)unlink(path);
+ *fdp = fd;
+}
+
+static void
+dofstat(int fd, struct stat *sb)
+{
+
+ ATF_REQUIRE_MSG(fstat(fd, sb) == 0,
+ "fstat failed: %s", strerror(errno));
+}
+
+static void
+samefile(struct stat *sb1, struct stat *sb2)
+{
+
+ ATF_REQUIRE_MSG(sb1->st_dev == sb2->st_dev, "different device");
+ ATF_REQUIRE_MSG(sb1->st_ino == sb2->st_ino, "different inode");
+}
+
+static void
+sendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen)
+{
+ struct iovec iovec;
+ char message[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsghdr;
+ struct msghdr msghdr;
+ ssize_t len;
+
+ bzero(&msghdr, sizeof(msghdr));
+ bzero(&message, sizeof(message));
+
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = sizeof(message);
+
+ iovec.iov_base = payload;
+ iovec.iov_len = paylen;
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ cmsghdr = (struct cmsghdr *)(void*)message;
+ cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsghdr->cmsg_level = SOL_SOCKET;
+ cmsghdr->cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(cmsghdr), &send_fd, sizeof(int));
+
+ len = sendmsg(sockfd, &msghdr, 0);
+ ATF_REQUIRE_MSG(len != -1, "sendmsg failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG((size_t)len == paylen,
+ "sendmsg: %zd messages sent; expected: %zu; %s", len, paylen,
+ strerror(errno));
+}
+
+static void
+sendfd(int sockfd, int send_fd)
+{
+ char ch = 0;
+
+ return (sendfd_payload(sockfd, send_fd, &ch, sizeof(ch)));
+}
+
+static void
+recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen)
+{
+ struct cmsghdr *cmsghdr;
+ char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)];
+ struct msghdr msghdr;
+ struct iovec iovec;
+ ssize_t len;
+
+ bzero(&msghdr, sizeof(msghdr));
+
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = sizeof(message);
+
+ iovec.iov_base = buf;
+ iovec.iov_len = buflen;
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ len = recvmsg(sockfd, &msghdr, 0);
+ ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG((size_t)len == buflen,
+ "recvmsg: %zd bytes received; expected %zd", len, buflen);
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ ATF_REQUIRE_MSG(cmsghdr != NULL,
+ "recvmsg: did not receive control message");
+ *recv_fd = -1;
+ for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
+ if (cmsghdr->cmsg_level == SOL_SOCKET &&
+ cmsghdr->cmsg_type == SCM_RIGHTS &&
+ cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
+ memcpy(recv_fd, CMSG_DATA(cmsghdr), sizeof(int));
+ ATF_REQUIRE(*recv_fd != -1);
+ }
+ }
+ ATF_REQUIRE_MSG(*recv_fd != -1,
+ "recvmsg: did not receive single-fd message");
+}
+
+static void
+recvfd(int sockfd, int *recv_fd)
+{
+ char ch = 0;
+
+ return (recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch)));
+}
+
+/*
+ * Put a temporary file into a UNIX domain socket, then take it out and make
+ * sure it's the same file. First time around, don't close the reference
+ * after sending.
+ */
+ATF_TC_WITHOUT_HEAD(simple_send_fd);
+ATF_TC_BODY(simple_send_fd, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ recvfd(fd[1], &getfd);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Same as simple_send_fd, only close the file reference after sending, so that
+ * the only reference is the descriptor in the UNIX domain socket buffer.
+ */
+ATF_TC_WITHOUT_HEAD(send_and_close);
+ATF_TC_BODY(send_and_close, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ recvfd(fd[1], &getfd);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Put a temporary file into a UNIX domain socket, then close both endpoints
+ * causing garbage collection to kick off.
+ */
+ATF_TC_WITHOUT_HEAD(send_and_cancel);
+ATF_TC_BODY(send_and_cancel, tc)
+{
+ int fd[2], putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Send two files. Then receive them. Make sure they are returned in the
+ * right order, and both get there.
+ */
+ATF_TC_WITHOUT_HEAD(two_files);
+ATF_TC_BODY(two_files, tc)
+{
+ struct stat getfd_1_stat, getfd_2_stat, putfd_1_stat, putfd_2_stat;
+ int fd[2], getfd_1, getfd_2, putfd_1, putfd_2;
+
+ domainsocketpair(fd);
+ tempfile(&putfd_1);
+ tempfile(&putfd_2);
+ dofstat(putfd_1, &putfd_1_stat);
+ dofstat(putfd_2, &putfd_2_stat);
+ sendfd(fd[0], putfd_1);
+ sendfd(fd[0], putfd_2);
+ close(putfd_1);
+ close(putfd_2);
+ recvfd(fd[1], &getfd_1);
+ recvfd(fd[1], &getfd_2);
+ dofstat(getfd_1, &getfd_1_stat);
+ dofstat(getfd_2, &getfd_2_stat);
+ samefile(&putfd_1_stat, &getfd_1_stat);
+ samefile(&putfd_2_stat, &getfd_2_stat);
+ close(getfd_1);
+ close(getfd_2);
+ closesocketpair(fd);
+}
+
+/*
+ * Big bundling test. Send an endpoint of the UNIX domain socket over itself,
+ * closing the door behind it.
+ */
+ATF_TC_WITHOUT_HEAD(bundle);
+ATF_TC_BODY(bundle, tc)
+{
+ int fd[2], getfd;
+
+ domainsocketpair(fd);
+
+ sendfd(fd[0], fd[0]);
+ close(fd[0]);
+ recvfd(fd[1], &getfd);
+ close(getfd);
+ close(fd[1]);
+}
+
+/*
+ * Big bundling test part two: Send an endpoint of the UNIX domain socket over
+ * itself, close the door behind it, and never remove it from the other end.
+ */
+ATF_TC_WITHOUT_HEAD(bundle_cancel);
+ATF_TC_BODY(bundle_cancel, tc)
+{
+ int fd[2];
+
+ domainsocketpair(fd);
+ sendfd(fd[0], fd[0]);
+ sendfd(fd[1], fd[0]);
+ closesocketpair(fd);
+}
+
+/*
+ * Test for PR 151758: Send an character device over the UNIX domain socket
+ * and then close both sockets to orphan the device.
+ */
+ATF_TC_WITHOUT_HEAD(devfs_orphan);
+ATF_TC_BODY(devfs_orphan, tc)
+{
+ int fd[2], putfd;
+
+ domainsocketpair(fd);
+ devnull(&putfd);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ closesocketpair(fd);
+}
+
+#define LOCAL_SENDSPACE_SYSCTL "net.local.stream.sendspace"
+
+/*
+ * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel prepends a
+ * control message to the data. Sender sends large payload.
+ * Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer limit, and receiver
+ * receives truncated data.
+ */
+ATF_TC_WITHOUT_HEAD(rights_creds_payload);
+ATF_TC_BODY(rights_creds_payload, tc)
+{
+ const int on = 1;
+ u_long sendspace;
+ size_t len;
+ void *buf;
+ int fd[2], getfd, putfd, rc;
+
+ atf_tc_expect_fail("PR 181741: Packet loss when 'control' messages "
+ "are present with large data");
+
+ len = sizeof(sendspace);
+ rc = sysctlbyname(LOCAL_SENDSPACE_SYSCTL, &sendspace,
+ &len, NULL, 0);
+ ATF_REQUIRE_MSG(rc != -1,
+ "sysctl %s failed: %s", LOCAL_SENDSPACE_SYSCTL, strerror(errno));
+
+ buf = calloc(1, sendspace);
+ ATF_REQUIRE(buf != NULL);
+
+ domainsocketpair(fd);
+ rc = setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on));
+ ATF_REQUIRE_MSG(rc != -1, "setsockopt(LOCAL_CREDS) failed: %s",
+ strerror(errno));
+ tempfile(&putfd);
+ sendfd_payload(fd[0], putfd, buf, sendspace);
+ recvfd_payload(fd[1], &getfd, buf, sendspace);
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, simple_send_fd);
+ ATF_TP_ADD_TC(tp, send_and_close);
+ ATF_TP_ADD_TC(tp, send_and_cancel);
+ ATF_TP_ADD_TC(tp, two_files);
+ ATF_TP_ADD_TC(tp, bundle);
+ ATF_TP_ADD_TC(tp, bundle_cancel);
+ ATF_TP_ADD_TC(tp, devfs_orphan);
+ ATF_TP_ADD_TC(tp, rights_creds_payload);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/unix_seqpacket_test.c b/tests/sys/kern/unix_seqpacket_test.c
index 58d6c19..4d157dc 100644
--- a/tests/sys/kern/unix_seqpacket_test.c
+++ b/tests/sys/kern/unix_seqpacket_test.c
@@ -47,7 +47,7 @@ static void
do_socketpair(int *sv)
{
int s;
-
+
s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
ATF_REQUIRE_EQ(0, s);
ATF_REQUIRE(sv[0] >= 0);
@@ -59,7 +59,7 @@ static void
do_socketpair_nonblocking(int *sv)
{
int s;
-
+
s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
ATF_REQUIRE_EQ(0, s);
ATF_REQUIRE(sv[0] >= 0);
@@ -69,7 +69,7 @@ do_socketpair_nonblocking(int *sv)
ATF_REQUIRE(-1 != fcntl(sv[1], F_SETFL, O_NONBLOCK));
}
-/*
+/*
* Returns a pair of sockets made the hard way: bind, listen, connect & accept
* @return const char* The path to the socket
*/
@@ -91,7 +91,6 @@ mk_pair_of_sockets(int *sv)
err = bind(s, (struct sockaddr *)&sun, sizeof(sun));
err = listen(s, -1);
ATF_CHECK_EQ(0, err);
- ATF_CHECK_EQ(0, err);
/* Create the other socket */
s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
@@ -101,7 +100,7 @@ mk_pair_of_sockets(int *sv)
perror("connect");
atf_tc_fail("connect(2) failed");
}
-
+
/* Accept it */
s1 = accept(s, NULL, NULL);
if (s1 == -1) {
@@ -111,6 +110,9 @@ mk_pair_of_sockets(int *sv)
sv[0] = s1;
sv[1] = s2;
+
+ close(s);
+
return (path);
}
@@ -148,8 +150,11 @@ test_eagain(size_t sndbufsize, size_t rcvbufsize)
for(i=0; i < numpkts; i++) {
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize == -1) {
- if (errno == EAGAIN)
+ if (errno == EAGAIN) {
+ close(sv[0]);
+ close(sv[1]);
atf_tc_pass();
+ }
else {
perror("send");
atf_tc_fail("send returned < 0 but not EAGAIN");
@@ -199,6 +204,8 @@ test_sendrecv_symmetric_buffers(size_t bufsize, int blocking) {
}
ATF_CHECK_EQ_MSG(pktsize, rsize, "expected %zd=send(...) but got %zd",
pktsize, rsize);
+ close(sv[0]);
+ close(sv[1]);
}
static void
@@ -232,7 +239,7 @@ test_pipe_simulator(size_t sndbufsize, size_t rcvbufsize)
memset(sndbuf, num_sent, pktsize);
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
if (ssize < 0) {
- /*
+ /*
* XXX: This is bug-compatible with the kernel.
* The kernel returns EMSGSIZE when it should
* return EAGAIN
@@ -268,12 +275,14 @@ test_pipe_simulator(size_t sndbufsize, size_t rcvbufsize)
pktsize, rsize);
memset(comparebuf, num_received, pktsize);
ATF_CHECK_EQ_MSG(0, memcmp(comparebuf, rcvbuf,
- pktsize),
+ pktsize),
"Received data miscompare");
num_received++;
}
}
}
+ close(sv[0]);
+ close(sv[1]);
}
typedef struct {
@@ -324,7 +333,7 @@ test_pipe_reader(void* args)
"expected %zd=send(...) but got %zd",
td->pktsize, rsize);
d = memcmp(comparebuf, rcvbuf, td->pktsize);
- ATF_CHECK_EQ_MSG(0, d,
+ ATF_CHECK_EQ_MSG(0, d,
"Received data miscompare on packet %d", i);
}
return (0);
@@ -360,7 +369,7 @@ test_pipe(size_t sndbufsize, size_t rcvbufsize)
reader_data.so = sv[1];
ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, test_pipe_writer,
(void*)&writer_data));
- /*
+ /*
* Give the writer time to start writing, and hopefully block, before
* starting the reader. This increases the likelihood of the test case
* failing due to PR kern/185812
@@ -372,6 +381,8 @@ test_pipe(size_t sndbufsize, size_t rcvbufsize)
/* Join the children */
ATF_REQUIRE_EQ(0, pthread_join(writer, NULL));
ATF_REQUIRE_EQ(0, pthread_join(reader, NULL));
+ close(sv[0]);
+ close(sv[1]);
}
@@ -386,7 +397,8 @@ ATF_TC_BODY(create_socket, tc)
int s;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
- ATF_CHECK(s >= 0);
+ ATF_REQUIRE(s >= 0);
+ close(s);
}
/* Create SEQPACKET sockets using socketpair(2) */
@@ -401,6 +413,8 @@ ATF_TC_BODY(create_socketpair, tc)
ATF_CHECK(sv[0] >= 0);
ATF_CHECK(sv[1] >= 0);
ATF_CHECK(sv[0] != sv[1]);
+ close(sv[0]);
+ close(sv[1]);
}
/* Call listen(2) without first calling bind(2). It should fail */
@@ -414,6 +428,7 @@ ATF_TC_BODY(listen_unbound, tc)
r = listen(s, -1);
/* expect listen to fail since we haven't called bind(2) */
ATF_CHECK(r != 0);
+ close(s);
}
/* Bind the socket to a file */
@@ -434,6 +449,7 @@ ATF_TC_BODY(bind, tc)
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
r = bind(s, (struct sockaddr *)&sun, sizeof(sun));
ATF_CHECK_EQ(0, r);
+ close(s);
}
/* listen(2) a socket that is already bound(2) should succeed */
@@ -456,6 +472,7 @@ ATF_TC_BODY(listen_bound, tc)
l = listen(s, -1);
ATF_CHECK_EQ(0, r);
ATF_CHECK_EQ(0, l);
+ close(s);
}
/* connect(2) can make a connection */
@@ -487,6 +504,8 @@ ATF_TC_BODY(connect, tc)
perror("connect");
atf_tc_fail("connect(2) failed");
}
+ close(s);
+ close(s2);
}
/* accept(2) can receive a connection */
@@ -496,6 +515,8 @@ ATF_TC_BODY(accept, tc)
int sv[2];
mk_pair_of_sockets(sv);
+ close(sv[0]);
+ close(sv[1]);
}
@@ -511,6 +532,7 @@ ATF_TC_BODY(fcntl_nonblock, tc)
perror("fcntl");
atf_tc_fail("fcntl failed");
}
+ close(s);
}
/* Resize the send and receive buffers */
@@ -539,7 +561,7 @@ ATF_TC_BODY(resize_buffers, tc)
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
printf("After changing SNDBUF | %7d | %7d |\n", xs, xr);
-
+
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) != 0){
perror("setsockopt");
atf_tc_fail("setsockopt(SO_RCVBUF) failed");
@@ -547,6 +569,7 @@ ATF_TC_BODY(resize_buffers, tc)
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
printf("After changing RCVBUF | %7d | %7d |\n", xs, xr);
+ close(s);
}
/*
@@ -603,6 +626,8 @@ ATF_TC_BODY(resize_connected_buffers, tc)
ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
printf("After changing Left's RCVBUF | %7d | %7d | %7d | %7d |\n",
ls, lr, rs, rr);
+ close(sv[0]);
+ close(sv[1]);
}
@@ -632,6 +657,8 @@ ATF_TC_BODY(send_recv, tc)
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
}
/* sendto(2) and recvfrom(2) a single short record
@@ -676,7 +703,7 @@ ATF_TC_BODY(sendto_recvfrom, tc)
}
ATF_CHECK_EQ(datalen, rsize);
- /*
+ /*
* FreeBSD does not currently provide the source address for SEQ_PACKET
* AF_UNIX sockets, and POSIX does not require it, so these two checks
* are disabled. If FreeBSD gains that feature in the future, then
@@ -684,9 +711,11 @@ ATF_TC_BODY(sendto_recvfrom, tc)
*/
/* ATF_CHECK_EQ(PF_LOCAL, from.ss_family); */
/* ATF_CHECK_STREQ(path, ((struct sockaddr_un*)&from)->sun_path); */
+ close(sv[0]);
+ close(sv[1]);
}
-/*
+/*
* send(2) and recv(2) a single short record with sockets created the
* traditional way, involving bind, listen, connect, and accept
*/
@@ -714,6 +743,8 @@ ATF_TC_BODY(send_recv_with_connect, tc)
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
}
/* send(2) should fail on a shutdown socket */
@@ -721,16 +752,17 @@ ATF_TC_WITHOUT_HEAD(shutdown_send);
ATF_TC_BODY(shutdown_send, tc)
{
int s;
- const char *data = "data";
+ const char data[] = "data";
ssize_t ssize;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
- ATF_CHECK(s >= 0);
+ ATF_REQUIRE(s >= 0);
ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR));
/* USE MSG_NOSIGNAL so we don't get SIGPIPE */
ssize = send(s, data, sizeof(data), MSG_EOR | MSG_NOSIGNAL);
ATF_CHECK_EQ(EPIPE, errno);
ATF_CHECK_EQ(-1, ssize);
+ close(s);
}
/* send(2) should cause SIGPIPE on a shutdown socket */
@@ -738,15 +770,16 @@ ATF_TC_WITHOUT_HEAD(shutdown_send_sigpipe);
ATF_TC_BODY(shutdown_send_sigpipe, tc)
{
int s;
- const char *data = "data";
+ const char data[] = "data";
ssize_t ssize;
s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
- ATF_CHECK(s >= 0);
+ ATF_REQUIRE(s >= 0);
ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR));
ATF_REQUIRE(SIG_ERR != signal(SIGPIPE, shutdown_send_sigpipe_handler));
ssize = send(s, data, sizeof(data), MSG_EOR);
ATF_CHECK_EQ(1, got_sigpipe);
+ close(s);
}
/* nonblocking send(2) and recv(2) a single short record */
@@ -780,9 +813,11 @@ ATF_TC_BODY(send_recv_nonblocking, tc)
rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
}
-/*
+/*
* We should get EMSGSIZE if we try to send a message larger than the socket
* buffer, with blocking sockets
*/
@@ -807,9 +842,11 @@ ATF_TC_BODY(emsgsize, tc)
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
ATF_CHECK_EQ(EMSGSIZE, errno);
ATF_CHECK_EQ(-1, ssize);
+ close(sv[0]);
+ close(sv[1]);
}
-/*
+/*
* We should get EMSGSIZE if we try to send a message larger than the socket
* buffer, with nonblocking sockets
*/
@@ -834,10 +871,12 @@ ATF_TC_BODY(emsgsize_nonblocking, tc)
ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
ATF_CHECK_EQ(EMSGSIZE, errno);
ATF_CHECK_EQ(-1, ssize);
+ close(sv[0]);
+ close(sv[1]);
}
-/*
+/*
* We should get EAGAIN if we try to send a message larger than the socket
* buffer, with nonblocking sockets. Test with several different sockbuf sizes
*/
@@ -863,7 +902,7 @@ ATF_TC_BODY(eagain_128k_128k, tc)
}
-/*
+/*
* nonblocking send(2) and recv(2) of several records, which should collectively
* fill up the send buffer but not the receive buffer
*/
@@ -888,7 +927,7 @@ ATF_TC_BODY(rcvbuf_oversized, tc)
ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize)));
- /*
+ /*
* Send and receive packets that are collectively greater than the send
* buffer, but less than the receive buffer
*/
@@ -916,7 +955,7 @@ ATF_TC_BODY(rcvbuf_oversized, tc)
"expected %zd=send(...) but got %zd", pktsize, rsize);
/* Verify the contents */
- ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize),
+ ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize),
"Received data miscompare");
}
@@ -924,9 +963,11 @@ ATF_TC_BODY(rcvbuf_oversized, tc)
rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
ATF_CHECK_EQ(EAGAIN, errno);
ATF_CHECK_EQ(-1, rsize);
+ close(sv[0]);
+ close(sv[1]);
}
-/*
+/*
* Simulate the behavior of a blocking pipe. The sender will send until his
* buffer fills up, then we'll simulate a scheduler switch that will allow the
* receiver to read until his buffer empties. Repeat the process until the
@@ -957,7 +998,7 @@ ATF_TC_BODY(pipe_simulator_128k_128k, tc)
test_pipe_simulator(131072, 131072);
}
-/*
+/*
* Test blocking I/O by passing data between two threads. The total amount of
* data will be >> buffer size to force blocking. Repeat the test with multiple
* send and receive buffer sizes
diff --git a/tests/sys/mac/Makefile b/tests/sys/mac/Makefile
new file mode 100644
index 0000000..ae2c491
--- /dev/null
+++ b/tests/sys/mac/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+TESTSDIR= ${TESTSBASE}/sys/mac
+
+TESTS_SUBDIRS+= bsdextended
+TESTS_SUBDIRS+= portacl
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/bsdextended/Makefile b/tests/sys/mac/bsdextended/Makefile
new file mode 100644
index 0000000..39a778b
--- /dev/null
+++ b/tests/sys/mac/bsdextended/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+TESTSDIR= ${TESTSBASE}/sys/mac/bsdextended
+
+TAP_TESTS_C+= ugidfw_test
+TAP_TESTS_SH+= matches_test
+
+DPADD.ugidfw_test+= ${LIBUGIDFW}
+LDADD.ugidfw_test+= -lugidfw
+
+TEST_METADATA.matches_test+= required_user="root"
+TEST_METADATA.ugidfw_test+= required_user="root"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/bsdextended/matches_test.sh b/tests/sys/mac/bsdextended/matches_test.sh
new file mode 100644
index 0000000..5aff413
--- /dev/null
+++ b/tests/sys/mac/bsdextended/matches_test.sh
@@ -0,0 +1,353 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+uidrange="60000:100000"
+gidrange="60000:100000"
+uidinrange="nobody"
+uidoutrange="daemon"
+gidinrange="nobody" # We expect $uidinrange in this group
+gidoutrange="daemon" # We expect $uidinrange in this group
+
+test_num=1
+pass()
+{
+ echo "ok $test_num # $@"
+ : $(( test_num += 1 ))
+}
+
+fail()
+{
+ echo "not ok $test_num # $@"
+ : $(( test_num += 1 ))
+}
+
+#
+# Setup
+#
+
+: ${TMPDIR=/tmp}
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP test must be run as root"
+ exit 0
+fi
+if ! sysctl -N security.mac.bsdextended >/dev/null 2>&1; then
+ echo "1..0 # SKIP mac_bsdextended(4) support isn't available"
+ exit 0
+fi
+if ! playground=$(mktemp -d $TMPDIR/tmp.XXXXXXX); then
+ echo "1..0 # SKIP failed to create temporary directory"
+ exit 0
+fi
+trap "rmdir $playground" EXIT INT TERM
+if ! mdmfs -s 25m md $playground; then
+ echo "1..0 # SKIP failed to mount md device"
+ exit 0
+fi
+chmod a+rwx $playground
+md_device=$(mount -p | grep "$playground" | awk '{ gsub(/^\/dev\//, "", $1); print $1 }')
+trap "umount -f $playground; mdconfig -d -u $md_device; rmdir $playground" EXIT INT TERM
+if [ -z "$md_device" ]; then
+ mount -p | grep $playground
+ echo "1..0 # SKIP md device not properly attached to the system"
+fi
+
+ugidfw remove 1
+
+file1=$playground/test-$uidinrange
+file2=$playground/test-$uidoutrange
+cat > $playground/test-script.sh <<'EOF'
+#!/bin/sh
+: > $1
+EOF
+if [ $? -ne 0 ]; then
+ echo "1..0 # SKIP failed to create test script"
+ exit 0
+fi
+echo "1..30"
+
+command1="sh $playground/test-script.sh $file1"
+command2="sh $playground/test-script.sh $file2"
+
+desc="$uidinrange file"
+if su -m $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+chown "$uidinrange":"$gidinrange" $file1
+chmod a+w $file1
+
+desc="$uidoutrange file"
+if $command2; then
+ pass $desc
+else
+ fail $desc
+fi
+
+chown "$uidoutrange":"$gidoutrange" $file2
+chmod a+w $file2
+
+#
+# No rules
+#
+desc="no rules $uidinrange"
+if su -fm $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+desc="no rules $uidoutrange"
+if su -fm $uidoutrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Subject Match on uid
+#
+ugidfw set 1 subject uid $uidrange object mode rasx
+desc="subject uid in range"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="subject uid out range"
+if su -fm $uidoutrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Subject Match on gid
+#
+ugidfw set 1 subject gid $gidrange object mode rasx
+
+desc="subject gid in range"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="subject gid out range"
+if su -fm $uidoutrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Subject Match on jail
+#
+rm -f $playground/test-jail
+
+desc="subject matching jailid"
+jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch $playground/test-jail) &"`
+ugidfw set 1 subject jailid $jailid object mode rasx
+sleep 10
+
+if [ -f $playground/test-jail ]; then
+ fail "TODO $desc: this testcase fails (see bug # 205481)"
+else
+ pass $desc
+fi
+
+rm -f $playground/test-jail
+desc="subject nonmatching jailid"
+jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch $playground/test-jail) &"`
+sleep 10
+if [ -f $playground/test-jail ]; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Object uid
+#
+ugidfw set 1 subject object uid $uidrange mode rasx
+
+desc="object uid in range"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="object uid out range"
+if su -fm $uidinrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+ugidfw set 1 subject object uid $uidrange mode rasx
+
+desc="object uid in range (different subject)"
+if su -fm $uidoutrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="object uid out range (different subject)"
+if su -fm $uidoutrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Object gid
+#
+ugidfw set 1 subject object gid $uidrange mode rasx
+
+desc="object gid in range"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="object gid out range"
+if su -fm $uidinrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+desc="object gid in range (different subject)"
+if su -fm $uidoutrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+desc="object gid out range (different subject)"
+if su -fm $uidoutrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+#
+# Object filesys
+#
+ugidfw set 1 subject uid $uidrange object filesys / mode rasx
+desc="object out of filesys"
+if su -fm $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+ugidfw set 1 subject uid $uidrange object filesys $playground mode rasx
+desc="object in filesys"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+#
+# Object suid
+#
+ugidfw set 1 subject uid $uidrange object suid mode rasx
+desc="object notsuid"
+if su -fm $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+chmod u+s $file1
+desc="object suid"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+chmod u-s $file1
+
+#
+# Object sgid
+#
+ugidfw set 1 subject uid $uidrange object sgid mode rasx
+desc="object notsgid"
+if su -fm $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+chmod g+s $file1
+desc="object sgid"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+chmod g-s $file1
+
+#
+# Object uid matches subject
+#
+ugidfw set 1 subject uid $uidrange object uid_of_subject mode rasx
+
+desc="object uid notmatches subject"
+if su -fm $uidinrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+desc="object uid matches subject"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+#
+# Object gid matches subject
+#
+ugidfw set 1 subject uid $uidrange object gid_of_subject mode rasx
+
+desc="object gid notmatches subject"
+if su -fm $uidinrange -c "$command2"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+desc="object gid matches subject"
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
+
+#
+# Object type
+#
+desc="object not type"
+ugidfw set 1 subject uid $uidrange object type dbclsp mode rasx
+if su -fm $uidinrange -c "$command1"; then
+ pass $desc
+else
+ fail $desc
+fi
+
+desc="object type"
+ugidfw set 1 subject uid $uidrange object type r mode rasx
+if su -fm $uidinrange -c "$command1"; then
+ fail $desc
+else
+ pass $desc
+fi
diff --git a/tests/sys/mac/bsdextended/ugidfw_test.c b/tests/sys/mac/bsdextended/ugidfw_test.c
new file mode 100644
index 0000000..aab8553
--- /dev/null
+++ b/tests/sys/mac/bsdextended/ugidfw_test.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 2005 McAfee, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mac.h>
+#include <sys/mount.h>
+
+#include <security/mac_bsdextended/mac_bsdextended.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ugidfw.h>
+#include <unistd.h>
+
+/*
+ * Starting point for a regression test for mac_bsdextended(4) and the
+ * supporting libugidfw(3).
+ */
+
+/*
+ * This section of the regression test passes some test cases through the
+ * rule<->string routines to confirm they work approximately as desired.
+ */
+
+/*
+ * List of users and groups we must check exists before we can begin, since
+ * they are used in the string test rules. We use users and groups that will
+ * always exist in a default install used for regression testing.
+ */
+static const char *test_users[] = {
+ "root",
+ "daemon",
+ "operator",
+ "bin",
+};
+
+static const char *test_groups[] = {
+ "wheel",
+ "daemon",
+ "operator",
+ "bin",
+};
+
+int test_num;
+
+/*
+ * List of test strings that must go in (and come out) of libugidfw intact.
+ */
+static const char *test_strings[] = {
+ /* Variations on subject and object uids. */
+ "subject uid root object uid root mode n",
+ "subject uid root object uid daemon mode n",
+ "subject uid daemon object uid root mode n",
+ "subject uid daemon object uid daemon mode n",
+ /* Variations on mode. */
+ "subject uid root object uid root mode a",
+ "subject uid root object uid root mode r",
+ "subject uid root object uid root mode s",
+ "subject uid root object uid root mode w",
+ "subject uid root object uid root mode x",
+ "subject uid root object uid root mode arswx",
+ /* Variations on subject and object gids. */
+ "subject gid wheel object gid wheel mode n",
+ "subject gid wheel object gid daemon mode n",
+ "subject gid daemon object gid wheel mode n",
+ "subject gid daemon object gid daemon mode n",
+ /* Subject uids and subject gids. */
+ "subject uid bin gid daemon object uid operator gid wheel mode n",
+ /* Not */
+ "subject not uid operator object uid bin mode n",
+ "subject uid bin object not uid operator mode n",
+ "subject not uid daemon object not uid operator mode n",
+ /* Ranges */
+ "subject uid root:operator object gid wheel:bin mode n",
+ /* Jail ID */
+ "subject jailid 1 object uid root mode n",
+ /* Filesys */
+ "subject uid root object filesys / mode n",
+ "subject uid root object filesys /dev mode n",
+ /* S/UGID */
+ "subject not uid root object sgid mode n",
+ "subject not uid root object sgid mode n",
+ /* Matching uid/gid */
+ "subject not uid root:operator object not uid_of_subject mode n",
+ "subject not gid wheel:bin object not gid_of_subject mode n",
+ /* Object types */
+ "subject uid root object type a mode a",
+ "subject uid root object type r mode a",
+ "subject uid root object type d mode a",
+ "subject uid root object type b mode a",
+ "subject uid root object type c mode a",
+ "subject uid root object type l mode a",
+ "subject uid root object type s mode a",
+ "subject uid root object type rbc mode a",
+ "subject uid root object type dls mode a",
+ /* Empty rules always match */
+ "subject object mode a",
+ /* Partial negations */
+ "subject ! uid root object mode n",
+ "subject ! gid wheel object mode n",
+ "subject ! jailid 2 object mode n",
+ "subject object ! uid root mode n",
+ "subject object ! gid wheel mode n",
+ "subject object ! filesys / mode n",
+ "subject object ! suid mode n",
+ "subject object ! sgid mode n",
+ "subject object ! uid_of_subject mode n",
+ "subject object ! gid_of_subject mode n",
+ "subject object ! type d mode n",
+ /* All out nonsense */
+ "subject uid root ! gid wheel:bin ! jailid 1 "
+ "object ! uid root:daemon gid daemon filesys / suid sgid uid_of_subject gid_of_subject ! type r "
+ "mode rsx",
+};
+
+static void
+test_libugidfw_strings(void)
+{
+ struct mac_bsdextended_rule rule;
+ char errorstr[256];
+ char rulestr[256];
+ int error, i;
+
+ for (i = 0; i < nitems(test_users); i++, test_num++) {
+ if (getpwnam(test_users[i]) == NULL)
+ printf("not ok %d # test_libugidfw_strings: getpwnam(%s) "
+ "failed: %s\n", test_num, test_users[i], strerror(errno));
+ else
+ printf("ok %d\n", test_num);
+ }
+
+ for (i = 0; i < nitems(test_groups); i++, test_num++) {
+ if (getgrnam(test_groups[i]) == NULL)
+ printf("not ok %d # test_libugidfw_strings: getgrnam(%s) "
+ "failed: %s\n", test_num, test_groups[i], strerror(errno));
+ else
+ printf("ok %d\n", test_num);
+ }
+
+ for (i = 0; i < nitems(test_strings); i++) {
+ error = bsde_parse_rule_string(test_strings[i], &rule,
+ sizeof(errorstr), errorstr);
+ if (error == -1)
+ printf("not ok %d # bsde_parse_rule_string: '%s' (%d) "
+ "failed: %s\n", test_num, test_strings[i], i, errorstr);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+
+ error = bsde_rule_to_string(&rule, rulestr, sizeof(rulestr));
+ if (error < 0)
+ printf("not ok %d # bsde_rule_to_string: rule for '%s' "
+ "returned %d\n", test_num, test_strings[i], error);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+
+ if (strcmp(test_strings[i], rulestr) != 0)
+ printf("not ok %d # test_libugidfw: '%s' in, '%s' "
+ "out\n", test_num, test_strings[i], rulestr);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+ }
+}
+
+int
+main(void)
+{
+ char errorstr[256];
+ int count, slots;
+
+ test_num = 1;
+
+ /* Print an error if a non-root user attemps to run the tests. */
+ if (getuid() != 0) {
+ printf("1..0 # SKIP you must be root\n");
+ return (0);
+ }
+
+ switch (mac_is_present("bsdextended")) {
+ case -1:
+ printf("1..0 # SKIP mac_is_present failed: %s\n",
+ strerror(errno));
+ return (0);
+ case 1:
+ break;
+ case 0:
+ default:
+ printf("1..0 # SKIP mac_bsdextended not loaded\n");
+ return (0);
+ }
+
+ printf("1..%lu\n", nitems(test_users) + nitems(test_groups) +
+ 3 * nitems(test_strings) + 2);
+
+ test_libugidfw_strings();
+
+ /*
+ * Some simple up-front checks to see if we're able to query the
+ * policy for basic state. We want the rule count to be 0 before
+ * starting, but "slots" is a property of prior runs and so we ignore
+ * the return value.
+ */
+ count = bsde_get_rule_count(sizeof(errorstr), errorstr);
+ if (count == -1)
+ printf("not ok %d # bsde_get_rule_count: %s\n", test_num,
+ errorstr);
+ else
+ printf("ok %d\n", test_num);
+
+ test_num++;
+
+ slots = bsde_get_rule_slots(sizeof(errorstr), errorstr);
+ if (slots == -1)
+ printf("not ok %d # bsde_get_rule_slots: %s\n", test_num,
+ errorstr);
+ else
+ printf("ok %d\n", test_num);
+
+ return (0);
+}
diff --git a/tests/sys/mac/portacl/LICENSE b/tests/sys/mac/portacl/LICENSE
new file mode 100644
index 0000000..c95c149
--- /dev/null
+++ b/tests/sys/mac/portacl/LICENSE
@@ -0,0 +1,27 @@
+$FreeBSD$
+
+License for all mac_portacl regression tests:
+
+Copyright (c) 2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/tests/sys/mac/portacl/Makefile b/tests/sys/mac/portacl/Makefile
new file mode 100644
index 0000000..129d486
--- /dev/null
+++ b/tests/sys/mac/portacl/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+TESTSDIR= ${TESTSBASE}/sys/mac/portacl
+BINDIR= ${TESTSDIR}
+
+FILES+= misc.sh
+
+TAP_TESTS_SH+= nobody_test
+TAP_TESTS_SH+= root_test
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+TEST_METADATA.$t+= timeout="450"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/portacl/misc.sh b/tests/sys/mac/portacl/misc.sh
new file mode 100755
index 0000000..5a9e67b
--- /dev/null
+++ b/tests/sys/mac/portacl/misc.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+# $FreeBSD$
+
+sysctl security.mac.portacl >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "1..0 # SKIP MAC_PORTACL is unavailable."
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP testcases must be run as root"
+ exit 0
+fi
+
+ntest=1
+
+check_bind() {
+ local host idtype name proto port udpflag
+
+ host="127.0.0.1"
+
+ idtype=${1}
+ name=${2}
+ proto=${3}
+ port=${4}
+
+ [ "${proto}" = "udp" ] && udpflag="-u"
+
+ out=$(
+ case "${idtype}" in
+ uid|gid)
+ ( echo -n | su -m ${name} -c "nc ${udpflag} -l -w 10 $host $port" 2>&1 ) &
+ ;;
+ jail)
+ kill $$
+ ;;
+ *)
+ kill $$
+ esac
+ sleep 0.3
+ echo | nc ${udpflag} -w 10 $host $port >/dev/null 2>&1
+ wait
+ )
+ case "${out}" in
+ "nc: Permission denied"*|"nc: Operation not permitted"*)
+ echo fl
+ ;;
+ "")
+ echo ok
+ ;;
+ *)
+ echo ${out}
+ ;;
+ esac
+}
+
+bind_test() {
+ local expect_without_rule expect_with_rule idtype name proto port
+
+ expect_without_rule=${1}
+ expect_with_rule=${2}
+ idtype=${3}
+ name=${4}
+ proto=${5}
+ port=${6}
+
+ sysctl security.mac.portacl.rules= >/dev/null
+ out=$(check_bind ${idtype} ${name} ${proto} ${port})
+ if [ "${out}" = "${expect_without_rule}" ]; then
+ echo "ok ${ntest}"
+ elif [ "${out}" = "ok" -o "${out}" = "fl" ]; then
+ echo "not ok ${ntest} # '${out}' != '${expect_without_rule}'"
+ else
+ echo "not ok ${ntest} # unexpected output: '${out}'"
+ fi
+ : $(( ntest += 1 ))
+
+ if [ "${idtype}" = "uid" ]; then
+ idstr=$(id -u ${name})
+ elif [ "${idtype}" = "gid" ]; then
+ idstr=$(id -g ${name})
+ else
+ idstr=${name}
+ fi
+ sysctl security.mac.portacl.rules=${idtype}:${idstr}:${proto}:${port} >/dev/null
+ out=$(check_bind ${idtype} ${name} ${proto} ${port})
+ if [ "${out}" = "${expect_with_rule}" ]; then
+ echo "ok ${ntest}"
+ elif [ "${out}" = "ok" -o "${out}" = "fl" ]; then
+ echo "not ok ${ntest} # '${out}' != '${expect_with_rule}'"
+ else
+ echo "not ok ${ntest} # unexpected output: '${out}'"
+ fi
+ : $(( ntest += 1 ))
+
+ sysctl security.mac.portacl.rules= >/dev/null
+}
+
+reserved_high=$(sysctl -n net.inet.ip.portrange.reservedhigh)
+suser_exempt=$(sysctl -n security.mac.portacl.suser_exempt)
+port_high=$(sysctl -n security.mac.portacl.port_high)
+
+restore_settings() {
+ sysctl -n net.inet.ip.portrange.reservedhigh=${reserved_high} >/dev/null
+ sysctl -n security.mac.portacl.suser_exempt=${suser_exempt} >/dev/null
+ sysctl -n security.mac.portacl.port_high=${port_high} >/dev/null
+}
diff --git a/tests/sys/mac/portacl/nobody_test.sh b/tests/sys/mac/portacl/nobody_test.sh
new file mode 100755
index 0000000..7d8dbd6
--- /dev/null
+++ b/tests/sys/mac/portacl/nobody_test.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/misc.sh
+
+echo "1..64"
+
+# security.mac.portacl.suser_exempt value doesn't affect unprivileged users
+# behaviour.
+# mac_portacl has no impact on ports <= net.inet.ip.portrange.reservedhigh.
+
+trap restore_settings EXIT INT TERM
+
+sysctl security.mac.portacl.suser_exempt=1 >/dev/null
+sysctl net.inet.ip.portrange.reservedhigh=78 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
+
+sysctl security.mac.portacl.suser_exempt=0 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
+
+# Verify if security.mac.portacl.port_high works.
+
+sysctl security.mac.portacl.port_high=7778 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test fl ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test fl ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test fl ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test fl ok gid nobody udp 7777
+
+# Verify if mac_portacl rules work.
+
+sysctl net.inet.ip.portrange.reservedhigh=76 >/dev/null
+sysctl security.mac.portacl.port_high=7776 >/dev/null
+
+bind_test fl ok uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl ok uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl ok gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl ok gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
diff --git a/tests/sys/mac/portacl/root_test.sh b/tests/sys/mac/portacl/root_test.sh
new file mode 100755
index 0000000..9ed452f
--- /dev/null
+++ b/tests/sys/mac/portacl/root_test.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/misc.sh
+
+echo "1..48"
+
+# Verify if security.mac.portacl.suser_exempt=1 really exempts super-user.
+
+trap restore_settings EXIT INT TERM
+
+sysctl security.mac.portacl.suser_exempt=1 >/dev/null
+
+bind_test ok ok uid root tcp 77
+bind_test ok ok uid root tcp 7777
+bind_test ok ok uid root udp 77
+bind_test ok ok uid root udp 7777
+
+bind_test ok ok gid root tcp 77
+bind_test ok ok gid root tcp 7777
+bind_test ok ok gid root udp 77
+bind_test ok ok gid root udp 7777
+
+# Verify if security.mac.portacl.suser_exempt=0 really doesn't exempt super-user.
+
+sysctl security.mac.portacl.suser_exempt=0 >/dev/null
+
+bind_test fl ok uid root tcp 77
+bind_test ok ok uid root tcp 7777
+bind_test fl ok uid root udp 77
+bind_test ok ok uid root udp 7777
+
+bind_test fl ok gid root tcp 77
+bind_test ok ok gid root tcp 7777
+bind_test fl ok gid root udp 77
+bind_test ok ok gid root udp 7777
+
+# Verify if security.mac.portacl.port_high works for super-user.
+
+sysctl security.mac.portacl.port_high=7778 >/dev/null
+
+bind_test fl ok uid root tcp 77
+bind_test fl ok uid root tcp 7777
+bind_test fl ok uid root udp 77
+bind_test fl ok uid root udp 7777
+
+bind_test fl ok gid root tcp 77
+bind_test fl ok gid root tcp 7777
+bind_test fl ok gid root udp 77
+bind_test fl ok gid root udp 7777
OpenPOWER on IntegriCloud