diff options
author | Renato Botelho <renato@netgate.com> | 2016-01-07 18:08:08 -0200 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2016-01-07 18:08:08 -0200 |
commit | 7a579754077b3460c9509c75cdd1b78769c1db3f (patch) | |
tree | c15015685ccb82b7db1ac19d663a3c6e41e64587 /tests | |
parent | d8ff3484131f428fcc0727cd504acb5050a36490 (diff) | |
parent | bc6ee646001a22150936ff06bf11cd08195e208d (diff) | |
download | FreeBSD-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/Makefile | 1 | ||||
-rw-r--r-- | tests/sys/aio/aio_kqueue_test.c | 37 | ||||
-rw-r--r-- | tests/sys/aio/lio_kqueue_test.c | 101 | ||||
-rw-r--r-- | tests/sys/kern/Makefile | 1 | ||||
-rw-r--r-- | tests/sys/kern/pipe/pipe_overcommit1_test.c | 7 | ||||
-rw-r--r-- | tests/sys/kern/ptrace_test.c | 6 | ||||
-rw-r--r-- | tests/sys/kern/unix_passfd_test.c | 396 | ||||
-rw-r--r-- | tests/sys/kern/unix_seqpacket_test.c | 93 | ||||
-rw-r--r-- | tests/sys/mac/Makefile | 8 | ||||
-rw-r--r-- | tests/sys/mac/bsdextended/Makefile | 14 | ||||
-rw-r--r-- | tests/sys/mac/bsdextended/matches_test.sh | 353 | ||||
-rw-r--r-- | tests/sys/mac/bsdextended/ugidfw_test.c | 252 | ||||
-rw-r--r-- | tests/sys/mac/portacl/LICENSE | 27 | ||||
-rw-r--r-- | tests/sys/mac/portacl/Makefile | 16 | ||||
-rwxr-xr-x | tests/sys/mac/portacl/misc.sh | 106 | ||||
-rwxr-xr-x | tests/sys/mac/portacl/nobody_test.sh | 67 | ||||
-rwxr-xr-x | tests/sys/mac/portacl/root_test.sh | 51 |
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 |