diff options
Diffstat (limited to 'tools/regression')
30 files changed, 4059 insertions, 1356 deletions
diff --git a/tools/regression/bin/sh/builtins/eval6.0 b/tools/regression/bin/sh/builtins/eval6.0 new file mode 100644 index 0000000..6752bb6 --- /dev/null +++ b/tools/regression/bin/sh/builtins/eval6.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +# eval should preserve $? from command substitutions when starting +# the parsed command. +[ $(false; eval 'echo $?' $(:)) = 0 ] diff --git a/tools/regression/bin/sh/builtins/read6.0 b/tools/regression/bin/sh/builtins/read6.0 new file mode 100644 index 0000000..2168e10 --- /dev/null +++ b/tools/regression/bin/sh/builtins/read6.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +: | read x +r=$? +[ "$r" = 1 ] diff --git a/tools/regression/bin/sh/builtins/trap12.0 b/tools/regression/bin/sh/builtins/trap12.0 new file mode 100644 index 0000000..8c62ffd --- /dev/null +++ b/tools/regression/bin/sh/builtins/trap12.0 @@ -0,0 +1,10 @@ +# $FreeBSD$ + +f() { + trap 'return 42' USR1 + kill -USR1 $$ + return 3 +} +f +r=$? +[ "$r" = 42 ] diff --git a/tools/regression/bin/sh/builtins/wait4.0 b/tools/regression/bin/sh/builtins/wait4.0 new file mode 100644 index 0000000..7935131 --- /dev/null +++ b/tools/regression/bin/sh/builtins/wait4.0 @@ -0,0 +1,12 @@ +# $FreeBSD$ + +T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX` +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 +trapped= +trap trapped=1 QUIT +{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 & +wait $! <fifo1 +r=$? +[ "$r" -gt 128 ] && [ -n "$trapped" ] diff --git a/tools/regression/bin/sh/builtins/wait5.0 b/tools/regression/bin/sh/builtins/wait5.0 new file mode 100644 index 0000000..6874bf6 --- /dev/null +++ b/tools/regression/bin/sh/builtins/wait5.0 @@ -0,0 +1,12 @@ +# $FreeBSD$ + +T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX` +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 +trapped= +trap trapped=1 QUIT +{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 & +wait <fifo1 +r=$? +[ "$r" -gt 128 ] && [ -n "$trapped" ] diff --git a/tools/regression/bin/sh/builtins/wait6.0 b/tools/regression/bin/sh/builtins/wait6.0 new file mode 100644 index 0000000..20e3c68 --- /dev/null +++ b/tools/regression/bin/sh/builtins/wait6.0 @@ -0,0 +1,3 @@ +# $FreeBSD$ + +wait -- diff --git a/tools/regression/bin/sh/builtins/wait7.0 b/tools/regression/bin/sh/builtins/wait7.0 new file mode 100644 index 0000000..0fb092f --- /dev/null +++ b/tools/regression/bin/sh/builtins/wait7.0 @@ -0,0 +1,4 @@ +# $FreeBSD$ + +: & +wait -- $! diff --git a/tools/regression/bin/sh/execution/not1.0 b/tools/regression/bin/sh/execution/not1.0 new file mode 100644 index 0000000..12c6265 --- /dev/null +++ b/tools/regression/bin/sh/execution/not1.0 @@ -0,0 +1,4 @@ +# $FreeBSD$ + +f() { ! return $1; } +f 0 && ! f 1 diff --git a/tools/regression/bin/sh/execution/not2.0 b/tools/regression/bin/sh/execution/not2.0 new file mode 100644 index 0000000..1b128d0 --- /dev/null +++ b/tools/regression/bin/sh/execution/not2.0 @@ -0,0 +1,6 @@ +# $FreeBSD$ + +while :; do + ! break + exit 3 +done diff --git a/tools/regression/bin/test/regress.sh b/tools/regression/bin/test/regress.sh index 9229551..117a7e2 100644 --- a/tools/regression/bin/test/regress.sh +++ b/tools/regression/bin/test/regress.sh @@ -52,7 +52,7 @@ t () } count=0 -echo "1..130" +echo "1..266" t 0 'b = b' t 0 'b == b' @@ -194,3 +194,66 @@ t 1 '\( ! -a \)' t 0 '\( -n -o \)' t 1 '\( -z -o \)' t 1 '\( ! -o \)' + +# Test all file timestamp comparison operators +s() { + t ${1} "${35} -nt ${36}" + t ${2} "${35} -ntaa ${36}" + t ${3} "${35} -ntab ${36}" + t ${4} "${35} -ntac ${36}" + t ${5} "${35} -ntam ${36}" + t ${6} "${35} -ntba ${36}" + t ${7} "${35} -ntbb ${36}" + t ${8} "${35} -ntbc ${36}" + t ${9} "${35} -ntbm ${36}" + t ${10} "${35} -ntca ${36}" + t ${11} "${35} -ntcb ${36}" + t ${12} "${35} -ntcc ${36}" + t ${13} "${35} -ntcm ${36}" + t ${14} "${35} -ntma ${36}" + t ${15} "${35} -ntmb ${36}" + t ${16} "${35} -ntmc ${36}" + t ${17} "${35} -ntmm ${36}" + t ${18} "${35} -ot ${36}" + t ${19} "${35} -otaa ${36}" + t ${20} "${35} -otab ${36}" + t ${21} "${35} -otac ${36}" + t ${22} "${35} -otam ${36}" + t ${23} "${35} -otba ${36}" + t ${24} "${35} -otbb ${36}" + t ${25} "${35} -otbc ${36}" + t ${26} "${35} -otbm ${36}" + t ${27} "${35} -otca ${36}" + t ${28} "${35} -otcb ${36}" + t ${29} "${35} -otcc ${36}" + t ${30} "${35} -otcm ${36}" + t ${31} "${35} -otma ${36}" + t ${32} "${35} -otmb ${36}" + t ${33} "${35} -otmc ${36}" + t ${34} "${35} -otmm ${36}" +} + +a=/tmp/test$$.1 +b=/tmp/test$$.2 +trap "rm -f $a $b" EXIT + +# Tests 131-164 +s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 $a $b + +touch $a +# Tests 165-198 +s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 $a $b + +sleep 2 # Ensure $b is newer than $a +touch $b +# Tests 199-232 +s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 $a $b + +sleep 2 +echo >$b # Updates mtime & ctime +sleep 2 +touch -A 01 -a $b + +# $b now has ctime > mtime > atime > btime +# Tests 233-266 +s 1 1 0 1 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1 $b $b diff --git a/tools/regression/capsicum/syscalls/Makefile b/tools/regression/capsicum/syscalls/Makefile new file mode 100644 index 0000000..5d34226 --- /dev/null +++ b/tools/regression/capsicum/syscalls/Makefile @@ -0,0 +1,28 @@ +# $FreeBSD$ + +SYSCALLS= cap_fcntls_limit cap_getmode cap_ioctls_limit + +CFLAGS= -O2 -pipe -std=gnu99 -fstack-protector +CFLAGS+= -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type +CFLAGS+= -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wunused-parameter +CFLAGS+= -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls +CFLAGS+= -Wold-style-definition -Wno-pointer-sign + +all: ${SYSCALLS} ${SYSCALLS:=.t} + +.for SYSCALL in ${SYSCALLS} + +${SYSCALL}: ${SYSCALL}.c misc.c + ${CC} ${CFLAGS} ${@}.c misc.c -o $@ + +${SYSCALL}.t: ${SYSCALL} + @printf "#!/bin/sh\n\n%s/%s\n" ${.CURDIR} ${@:.t=} > $@ + +.endfor + +test: all + @prove -r ${.CURDIR} + +clean: + rm -f ${SYSCALLS} ${SYSCALLS:=.t} diff --git a/tools/regression/capsicum/syscalls/cap_fcntls_limit.c b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c new file mode 100644 index 0000000..c97203d --- /dev/null +++ b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c @@ -0,0 +1,540 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/capability.h> +#include <sys/procdesc.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "misc.h" + +static void +fcntl_tests_0(int fd) +{ + uint32_t fcntlrights; + + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_ALL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); + CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); + CHECK(fcntl(fd, F_SETFL, 0) == 0); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + errno = 0; + CHECK(cap_fcntls_limit(fd, ~CAP_FCNTL_ALL) == -1); + CHECK(errno == EINVAL); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); + CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); + CHECK(fcntl(fd, F_SETFL, 0) == 0); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + CHECK(cap_fcntls_limit(fd, 0) == 0); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); +} + +static void +fcntl_tests_1(int fd) +{ + uint32_t fcntlrights; + + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + + CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0); + + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); +} + +static void +fcntl_tests_2(int fd) +{ + uint32_t fcntlrights; + + CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0); + + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = CAP_FCNTL_ALL; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); +} + +static void +fcntl_tests_send_0(int sock) +{ + int fd; + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(cap_fcntls_limit(fd, 0) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); +} + +static void +fcntl_tests_recv_0(int sock) +{ + uint32_t fcntlrights; + int fd; + + CHECK(descriptor_recv(sock, &fd) == 0); + + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_ALL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); + CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); + CHECK(fcntl(fd, F_SETFL, 0) == 0); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); + CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); + CHECK(fcntl(fd, F_SETFL, 0) == 0); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == CAP_FCNTL_GETFL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFL) == O_RDWR); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + errno = 0; + CHECK(cap_fcntls_limit(fd, CAP_FCNTL_SETFL) == -1); + CHECK(errno == ENOTCAPABLE); + fcntlrights = 0; + CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); + CHECK(fcntlrights == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_SETFL, 0) == -1); + CHECK(errno == ENOTCAPABLE); + errno = 0; + CHECK(fcntl(fd, F_GETFL) == -1); + CHECK(errno == ENOTCAPABLE); + + CHECK(close(fd) == 0); +} + +int +main(void) +{ + int fd, pfd, sp[2]; + pid_t pid; + + printf("1..870\n"); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + fcntl_tests_0(fd); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + fcntl_tests_1(fd); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + fcntl_tests_2(fd); + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + fcntl_tests_0(fd); + CHECK(close(fd) == 0); + exit(0); + } else { + CHECK(waitpid(pid, NULL, 0) == pid); + fcntl_tests_0(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + sleep(1); + fcntl_tests_0(fd); + CHECK(close(fd) == 0); + exit(0); + } else { + fcntl_tests_0(fd); + CHECK(waitpid(pid, NULL, 0) == pid); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = pdfork(&pfd, 0)) >= 0); + if (pid == 0) { + fcntl_tests_1(fd); + exit(0); + } else { + CHECK(pdwait(pfd) == 0); +/* + It fails with EBADF, which I believe is a bug. + CHECK(close(pfd) == 0); +*/ + fcntl_tests_1(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = pdfork(&pfd, 0)) >= 0); + if (pid == 0) { + sleep(1); + fcntl_tests_1(fd); + exit(0); + } else { + fcntl_tests_1(fd); + CHECK(pdwait(pfd) == 0); +/* + It fails with EBADF, which I believe is a bug. + CHECK(close(pfd) == 0); +*/ + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + fcntl_tests_2(fd); + exit(0); + } else { + CHECK(waitpid(pid, NULL, 0) == pid); + fcntl_tests_2(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + sleep(1); + fcntl_tests_2(fd); + exit(0); + } else { + fcntl_tests_2(fd); + CHECK(waitpid(pid, NULL, 0) == pid); + } + CHECK(close(fd) == 0); + + /* Send descriptors from parent to child. */ + CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + CHECK(close(sp[0]) == 0); + fcntl_tests_recv_0(sp[1]); + CHECK(close(sp[1]) == 0); + exit(0); + } else { + CHECK(close(sp[1]) == 0); + fcntl_tests_send_0(sp[0]); + CHECK(waitpid(pid, NULL, 0) == pid); + CHECK(close(sp[0]) == 0); + } + + /* Send descriptors from child to parent. */ + CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + CHECK(close(sp[0]) == 0); + fcntl_tests_send_0(sp[1]); + CHECK(close(sp[1]) == 0); + exit(0); + } else { + CHECK(close(sp[1]) == 0); + fcntl_tests_recv_0(sp[0]); + CHECK(waitpid(pid, NULL, 0) == pid); + CHECK(close(sp[0]) == 0); + } + + exit(0); +} diff --git a/tools/regression/capsicum/syscalls/cap_getmode.c b/tools/regression/capsicum/syscalls/cap_getmode.c new file mode 100644 index 0000000..1a38202 --- /dev/null +++ b/tools/regression/capsicum/syscalls/cap_getmode.c @@ -0,0 +1,167 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/capability.h> +#include <sys/procdesc.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "misc.h" + +int +main(void) +{ + unsigned int mode; + pid_t pid; + int pfd; + + printf("1..27\n"); + + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are not in capability mode. */ + CHECK(mode == 0); + + /* Expect EFAULT. */ + errno = 0; + CHECK(cap_getmode(NULL) == -1); + CHECK(errno == EFAULT); + errno = 0; + CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); + CHECK(errno == EFAULT); + + /* If parent is not in capability mode, child after fork() also won't be. */ + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are not in capability mode. */ + CHECK(mode == 0); + exit(0); + default: + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid() failed"); + } + + /* If parent is not in capability mode, child after pdfork() also won't be. */ + pid = pdfork(&pfd, 0); + switch (pid) { + case -1: + err(1, "pdfork() failed"); + case 0: + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are not in capability mode. */ + CHECK(mode == 0); + exit(0); + default: + if (pdwait(pfd) == -1) + err(1, "pdwait() failed"); + close(pfd); + } + + /* In capability mode... */ + + CHECK(cap_enter() == 0); + + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are in capability mode. */ + CHECK(mode == 1); + + /* Expect EFAULT. */ + errno = 0; + CHECK(cap_getmode(NULL) == -1); + CHECK(errno == EFAULT); + errno = 0; + CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); + CHECK(errno == EFAULT); + + /* If parent is in capability mode, child after fork() also will be. */ + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are in capability mode. */ + CHECK(mode == 1); + exit(0); + default: + /* + * wait(2) and friends are not permitted in the capability mode, + * so we can only just wait for a while. + */ + sleep(1); + } + + /* If parent is in capability mode, child after pdfork() also will be. */ + pid = pdfork(&pfd, 0); + switch (pid) { + case -1: + err(1, "pdfork() failed"); + case 0: + mode = 666; + CHECK(cap_getmode(&mode) == 0); + /* If cap_getmode() succeeded mode should be modified. */ + CHECK(mode != 666); + /* We are in capability mode. */ + CHECK(mode == 1); + exit(0); + default: + if (pdwait(pfd) == -1) + err(1, "pdwait() failed"); + close(pfd); + } + + exit(0); +} diff --git a/tools/regression/capsicum/syscalls/cap_ioctls_limit.c b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c new file mode 100644 index 0000000..753aa71 --- /dev/null +++ b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c @@ -0,0 +1,462 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/capability.h> +#include <sys/ioctl.h> +#include <sys/procdesc.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "misc.h" + +static void +ioctl_tests_0(int fd) +{ + unsigned long cmds[2]; + + CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(ioctl(fd, FIONCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); + CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || + (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, 1) == nitems(cmds)); + CHECK(cmds[0] == FIOCLEX || cmds[0] == FIONCLEX); + CHECK(cmds[1] == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(ioctl(fd, FIONCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + cmds[0] = FIOCLEX; + CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); + CHECK(cmds[0] == FIOCLEX); + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); + CHECK(errno == ENOTCAPABLE); + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); + CHECK(cmds[0] == FIOCLEX); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + cmds[0] = FIOCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + errno = 0; + CHECK(ioctl(fd, FIOCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); +} + +static void +ioctl_tests_1(int fd) +{ + unsigned long cmds[2]; + + cmds[0] = FIOCLEX; + CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); + CHECK(cmds[0] == FIOCLEX); + CHECK(cmds[1] == 0); + + CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + cmds[0] = FIOCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + errno = 0; + CHECK(ioctl(fd, FIOCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); +} + +static void +ioctl_tests_2(int fd) +{ + unsigned long cmds[2]; + + CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + cmds[0] = FIOCLEX; + errno = 0; + CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + errno = 0; + CHECK(ioctl(fd, FIOCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); +} + +static void +ioctl_tests_send_0(int sock) +{ + unsigned long cmds[2]; + int fd; + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + cmds[0] = FIOCLEX; + cmds[1] = FIONCLEX; + CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + cmds[0] = FIOCLEX; + CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); + CHECK(descriptor_send(sock, fd) == 0); + CHECK(close(fd) == 0); +} + +static void +ioctl_tests_recv_0(int sock) +{ + unsigned long cmds[2]; + int fd; + + CHECK(descriptor_recv(sock, &fd) == 0); + + CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(ioctl(fd, FIONCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); + CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || + (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(ioctl(fd, FIONCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + cmds[0] = cmds[1] = 0; + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); + CHECK(cmds[0] == FIOCLEX); + + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(ioctl(fd, FIOCLEX) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(close(fd) == 0); + + CHECK(descriptor_recv(sock, &fd) == 0); + + CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); + + CHECK(fcntl(fd, F_GETFD) == 0); + errno = 0; + CHECK(ioctl(fd, FIOCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == 0); + CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + errno = 0; + CHECK(ioctl(fd, FIONCLEX) == -1); + CHECK(errno == ENOTCAPABLE); + CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); + CHECK(fcntl(fd, F_SETFD, 0) == 0); + CHECK(fcntl(fd, F_GETFD) == 0); + + CHECK(close(fd) == 0); +} + +int +main(void) +{ + int fd, pfd, sp[2]; + pid_t pid; + + printf("1..607\n"); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + ioctl_tests_0(fd); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + ioctl_tests_1(fd); + CHECK(close(fd) == 0); + + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + ioctl_tests_2(fd); + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + ioctl_tests_0(fd); + CHECK(close(fd) == 0); + exit(0); + default: + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid() failed"); + ioctl_tests_0(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + sleep(1); + ioctl_tests_0(fd); + CHECK(close(fd) == 0); + exit(0); + default: + ioctl_tests_0(fd); + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid() failed"); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = pdfork(&pfd, 0); + switch (pid) { + case -1: + err(1, "pdfork() failed"); + case 0: + ioctl_tests_1(fd); + exit(0); + default: + if (pdwait(pfd) == -1) + err(1, "pdwait() failed"); + close(pfd); + ioctl_tests_1(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = pdfork(&pfd, 0); + switch (pid) { + case -1: + err(1, "pdfork() failed"); + case 0: + sleep(1); + ioctl_tests_1(fd); + exit(0); + default: + ioctl_tests_1(fd); + if (pdwait(pfd) == -1) + err(1, "pdwait() failed"); + close(pfd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor and operates on it first. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + ioctl_tests_2(fd); + exit(0); + default: + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid() failed"); + ioctl_tests_2(fd); + } + CHECK(close(fd) == 0); + + /* Child inherits descriptor, but operates on it after parent. */ + CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); + pid = fork(); + switch (pid) { + case -1: + err(1, "fork() failed"); + case 0: + sleep(1); + ioctl_tests_2(fd); + exit(0); + default: + ioctl_tests_2(fd); + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid() failed"); + } + CHECK(close(fd) == 0); + + /* Send descriptors from parent to child. */ + CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + CHECK(close(sp[0]) == 0); + ioctl_tests_recv_0(sp[1]); + CHECK(close(sp[1]) == 0); + exit(0); + } else { + CHECK(close(sp[1]) == 0); + ioctl_tests_send_0(sp[0]); + CHECK(waitpid(pid, NULL, 0) == pid); + CHECK(close(sp[0]) == 0); + } + + /* Send descriptors from child to parent. */ + CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); + CHECK((pid = fork()) >= 0); + if (pid == 0) { + CHECK(close(sp[0]) == 0); + ioctl_tests_send_0(sp[1]); + CHECK(close(sp[1]) == 0); + exit(0); + } else { + CHECK(close(sp[1]) == 0); + ioctl_tests_recv_0(sp[0]); + CHECK(waitpid(pid, NULL, 0) == pid); + CHECK(close(sp[0]) == 0); + } + + exit(0); +} diff --git a/tools/regression/capsicum/syscalls/misc.c b/tools/regression/capsicum/syscalls/misc.c new file mode 100644 index 0000000..303a911 --- /dev/null +++ b/tools/regression/capsicum/syscalls/misc.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/select.h> +#include <sys/socket.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <strings.h> + +#include "misc.h" + +int +pdwait(int pfd) +{ + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(pfd, &fdset); + + return (select(pfd + 1, NULL, &fdset, NULL, NULL) == -1 ? -1 : 0); +} + +int +descriptor_send(int sock, int fd) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(fd))]; + struct msghdr msg; + struct cmsghdr *cmsg; + + assert(sock >= 0); + assert(fd >= 0); + + bzero(&msg, sizeof(msg)); + bzero(&ctrl, sizeof(ctrl)); + + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + if (sendmsg(sock, &msg, 0) == -1) + return (errno); + + return (0); +} + +int +descriptor_recv(int sock, int *fdp) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + int val; + + assert(sock >= 0); + assert(fdp != NULL); + + bzero(&msg, sizeof(msg)); + bzero(&ctrl, sizeof(ctrl)); + +#if 1 + /* + * This doesn't really make sense, as we don't plan to receive any + * data, but if no buffer is provided and recv(2) returns 0 without + * control message. Must be kernel bug. + */ + iov.iov_base = &val; + iov.iov_len = sizeof(val); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; +#else + msg.msg_iov = NULL; + msg.msg_iovlen = 0; +#endif + msg.msg_control = ctrl; + msg.msg_controllen = sizeof(ctrl); + + if (recvmsg(sock, &msg, 0) == -1) + return (errno); + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + return (EINVAL); + } + bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp)); + + return (0); +} diff --git a/tools/regression/capsicum/syscalls/misc.h b/tools/regression/capsicum/syscalls/misc.h new file mode 100644 index 0000000..4968c3e --- /dev/null +++ b/tools/regression/capsicum/syscalls/misc.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _MISC_H_ +#define _MISC_H_ + +#define OK() do { \ + int _serrno = errno; \ + printf("ok # line %u\n", __LINE__); \ + fflush(stdout); \ + errno = _serrno; \ +} while (0) +#define NOK() do { \ + int _serrno = errno; \ + printf("not ok # line %u\n", __LINE__); \ + fflush(stdout); \ + errno = _serrno; \ +} while (0) +#define CHECK(cond) do { \ + if ((cond)) \ + OK(); \ + else \ + NOK(); \ +} while (0) + +/* + * This can be removed once pdwait4(2) is implemented. + */ +int pdwait(int pfd); + +int descriptor_send(int sock, int fd); +int descriptor_recv(int sock, int *fdp); + +#endif /* !_MISC_H_ */ diff --git a/tools/regression/lib/libc/gen/test-wordexp.c b/tools/regression/lib/libc/gen/test-wordexp.c index df8f885..d94f870 100644 --- a/tools/regression/lib/libc/gen/test-wordexp.c +++ b/tools/regression/lib/libc/gen/test-wordexp.c @@ -208,6 +208,25 @@ main(int argc, char *argv[]) assert(strcmp(we.we_wordv[1], "world") == 0); assert(we.we_wordv[2] == NULL); wordfree(&we); + sa.sa_handler = SIG_DFL; + r = sigaction(SIGCHLD, &sa, NULL); + assert(r == 0); + + /* + * With IFS set to a non-default value (without depending on whether + * IFS is inherited or not). + */ + r = setenv("IFS", ":", 1); + assert(r == 0); + r = wordexp("hello world", &we, 0); + assert(r == 0); + assert(we.we_wordc == 2); + assert(strcmp(we.we_wordv[0], "hello") == 0); + assert(strcmp(we.we_wordv[1], "world") == 0); + assert(we.we_wordv[2] == NULL); + wordfree(&we); + r = unsetenv("IFS"); + assert(r == 0); printf("PASS wordexp()\n"); printf("PASS wordfree()\n"); diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile index d62ac84..f496f73 100644 --- a/tools/regression/lib/libc/stdio/Makefile +++ b/tools/regression/lib/libc/stdio/Makefile @@ -1,6 +1,8 @@ # $FreeBSD$ -TESTS= test-getdelim test-perror test-print-positional test-printbasic test-printfloat test-scanfloat +TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \ + test-perror test-print-positional test-printbasic test-printfloat \ + test-scanfloat CFLAGS+= -lm .PHONY: tests diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.c b/tools/regression/lib/libc/stdio/test-open_memstream.c new file mode 100644 index 0000000..1a168c6 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_memstream.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <jhb@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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +static char *buf; +static size_t len; + +static void +assert_stream(const char *contents) +{ + if (strlen(contents) != len) + printf("bad length %zd for \"%s\"\n", len, contents); + else if (strncmp(buf, contents, strlen(contents)) != 0) + printf("bad buffer \"%s\" for \"%s\"\n", buf, contents); +} + +static void +open_group_test(void) +{ + FILE *fp; + off_t eob; + + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + + fprintf(fp, "hello my world"); + fflush(fp); + assert_stream("hello my world"); + eob = ftello(fp); + rewind(fp); + fprintf(fp, "good-bye"); + fseeko(fp, eob, SEEK_SET); + fclose(fp); + assert_stream("good-bye world"); + free(buf); +} + +static void +simple_tests(void) +{ + static const char zerobuf[] = + { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 }; + char c; + FILE *fp; + + fp = open_memstream(&buf, NULL); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad length pointer"); + fp = open_memstream(NULL, &len); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad buffer pointer"); + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + fflush(fp); + assert_stream(""); + if (fwide(fp, 0) >= 0) + printf("stream is not byte-oriented\n"); + + fprintf(fp, "fo"); + fflush(fp); + assert_stream("fo"); + fputc('o', fp); + fflush(fp); + assert_stream("foo"); + rewind(fp); + fflush(fp); + assert_stream(""); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream("foo"); + + /* + * Test seeking out past the current end. Should zero-fill the + * intermediate area. + */ + fseek(fp, 4, SEEK_END); + fprintf(fp, "bar"); + fflush(fp); + + /* + * Can't use assert_stream() here since this should contain + * embedded null characters. + */ + if (len != 10) + printf("bad length %zd for zero-fill test\n", len); + else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0) + printf("bad buffer for zero-fill test\n"); + + fseek(fp, 3, SEEK_SET); + fprintf(fp, " in "); + fflush(fp); + assert_stream("foo in "); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream("foo in bar"); + + rewind(fp); + if (fread(&c, sizeof(c), 1, fp) != 0) + printf("fread did not fail\n"); + else if (!ferror(fp)) + printf("error indicator not set after fread\n"); + else + clearerr(fp); + + fseek(fp, 4, SEEK_SET); + fprintf(fp, "bar baz"); + fclose(fp); + assert_stream("foo bar baz"); + free(buf); +} + +static void +seek_tests(void) +{ + FILE *fp; + + fp = open_memstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + if (fseeko(fp, (offset), (whence)) == 0) \ + printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + else if (errno != (error)) \ + printf("fseeko(%s, %s) failed with %d rather than %s\n",\ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + if (fseeko(fp, (offset), (whence)) != 0) \ + printf("fseeko(%s, %s) failed: %s\n", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + else if (ftello(fp) != (result)) \ + printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp), __STRING(result)); \ +} while (0) + + SEEK_FAIL(-1, SEEK_SET, EINVAL); + SEEK_FAIL(-1, SEEK_CUR, EINVAL); + SEEK_FAIL(-1, SEEK_END, EINVAL); + fprintf(fp, "foo"); + SEEK_OK(-1, SEEK_CUR, 2); + SEEK_OK(0, SEEK_SET, 0); + SEEK_OK(-1, SEEK_END, 2); + SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1); + SEEK_FAIL(2, SEEK_CUR, EOVERFLOW); + fclose(fp); +} + +int +main(int ac, char **av) +{ + + open_group_test(); + simple_tests(); + seek_tests(); + return (0); +} diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.t b/tools/regression/lib/libc/stdio/test-open_memstream.t new file mode 100644 index 0000000..8bdfd03 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_memstream.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.c b/tools/regression/lib/libc/stdio/test-open_wmemstream.c new file mode 100644 index 0000000..4cd0ab9 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2013 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <jhb@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 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +static wchar_t *buf; +static size_t len; + +static void +assert_stream(const wchar_t *contents) +{ + if (wcslen(contents) != len) + printf("bad length %zd for \"%ls\"\n", len, contents); + else if (wcsncmp(buf, contents, wcslen(contents)) != 0) + printf("bad buffer \"%ls\" for \"%ls\"\n", buf, contents); +} + +static void +open_group_test(void) +{ + FILE *fp; + off_t eob; + + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + + fwprintf(fp, L"hello my world"); + fflush(fp); + assert_stream(L"hello my world"); + eob = ftello(fp); + rewind(fp); + fwprintf(fp, L"good-bye"); + fseeko(fp, eob, SEEK_SET); + fclose(fp); + assert_stream(L"good-bye world"); + free(buf); +} + +static void +simple_tests(void) +{ + static const wchar_t zerobuf[] = + { L'f', L'o', L'o', 0, 0, 0, 0, L'b', L'a', L'r', 0 }; + wchar_t c; + FILE *fp; + + fp = open_wmemstream(&buf, NULL); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad length pointer"); + fp = open_wmemstream(NULL, &len); + if (fp != NULL) + errx(1, "did not fail to open stream"); + else if (errno != EINVAL) + err(1, "incorrect error for bad buffer pointer"); + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); + fflush(fp); + assert_stream(L""); + if (fwide(fp, 0) <= 0) + printf("stream is not wide-oriented\n"); + + fwprintf(fp, L"fo"); + fflush(fp); + assert_stream(L"fo"); + fputwc(L'o', fp); + fflush(fp); + assert_stream(L"foo"); + rewind(fp); + fflush(fp); + assert_stream(L""); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream(L"foo"); + + /* + * Test seeking out past the current end. Should zero-fill the + * intermediate area. + */ + fseek(fp, 4, SEEK_END); + fwprintf(fp, L"bar"); + fflush(fp); + + /* + * Can't use assert_stream() here since this should contain + * embedded null characters. + */ + if (len != 10) + printf("bad length %zd for zero-fill test\n", len); + else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0) + printf("bad buffer for zero-fill test\n"); + + fseek(fp, 3, SEEK_SET); + fwprintf(fp, L" in "); + fflush(fp); + assert_stream(L"foo in "); + fseek(fp, 0, SEEK_END); + fflush(fp); + assert_stream(L"foo in bar"); + + rewind(fp); + if (fread(&c, sizeof(c), 1, fp) != 0) + printf("fread did not fail\n"); + else if (!ferror(fp)) + printf("error indicator not set after fread\n"); + else + clearerr(fp); + + fseek(fp, 4, SEEK_SET); + fwprintf(fp, L"bar baz"); + fclose(fp); + assert_stream(L"foo bar baz"); + free(buf); +} + +static void +seek_tests(void) +{ + FILE *fp; + + fp = open_wmemstream(&buf, &len); + if (fp == NULL) + err(1, "failed to open stream"); +#define SEEK_FAIL(offset, whence, error) do { \ + errno = 0; \ + if (fseeko(fp, (offset), (whence)) == 0) \ + printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp)); \ + else if (errno != (error)) \ + printf("fseeko(%s, %s) failed with %d rather than %s\n",\ + __STRING(offset), __STRING(whence), errno, \ + __STRING(error)); \ +} while (0) + +#define SEEK_OK(offset, whence, result) do { \ + if (fseeko(fp, (offset), (whence)) != 0) \ + printf("fseeko(%s, %s) failed: %s\n", \ + __STRING(offset), __STRING(whence), strerror(errno)); \ + else if (ftello(fp) != (result)) \ + printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \ + __STRING(offset), __STRING(whence), \ + (intmax_t)ftello(fp), __STRING(result)); \ +} while (0) + + SEEK_FAIL(-1, SEEK_SET, EINVAL); + SEEK_FAIL(-1, SEEK_CUR, EINVAL); + SEEK_FAIL(-1, SEEK_END, EINVAL); + fwprintf(fp, L"foo"); + SEEK_OK(-1, SEEK_CUR, 2); + SEEK_OK(0, SEEK_SET, 0); + SEEK_OK(-1, SEEK_END, 2); + SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1); + SEEK_FAIL(2, SEEK_CUR, EOVERFLOW); + fclose(fp); +} + +int +main(int ac, char **av) +{ + + open_group_test(); + simple_tests(); + seek_tests(); + return (0); +} diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.t b/tools/regression/lib/libc/stdio/test-open_wmemstream.t new file mode 100644 index 0000000..8bdfd03 --- /dev/null +++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.t @@ -0,0 +1,10 @@ +#!/bin/sh +# $FreeBSD$ + +cd `dirname $0` + +executable=`basename $0 .t` + +make $executable 2>&1 > /dev/null + +exec ./$executable diff --git a/tools/regression/pjdfstest/Makefile b/tools/regression/pjdfstest/Makefile index ca789fe..eca598f 100644 --- a/tools/regression/pjdfstest/Makefile +++ b/tools/regression/pjdfstest/Makefile @@ -6,7 +6,7 @@ ${PROG}: ${PROG}.c @OSTYPE=`uname`; \ CFLAGS=-D__OS_$${OSTYPE}__; \ if [ $$OSTYPE = "FreeBSD" ]; then \ - CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL"; \ + CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_CHFLAGSAT -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL -DHAS_BINDAT -DHAS_CONNECTAT"; \ elif [ $$OSTYPE = "SunOS" ]; then \ CFLAGS="$$CFLAGS -DHAS_TRUNCATE64 -DHAS_STAT64"; \ CFLAGS="$$CFLAGS -lsocket"; \ diff --git a/tools/regression/pjdfstest/pjdfstest.c b/tools/regression/pjdfstest/pjdfstest.c index c08fafd..fd19084 100644 --- a/tools/regression/pjdfstest/pjdfstest.c +++ b/tools/regression/pjdfstest/pjdfstest.c @@ -82,7 +82,13 @@ enum action { ACTION_MKNOD, ACTION_MKNODAT, ACTION_BIND, +#ifdef HAS_BINDAT + ACTION_BINDAT, +#endif ACTION_CONNECT, +#ifdef HAS_CONNECTAT + ACTION_CONNECTAT, +#endif ACTION_CHMOD, ACTION_FCHMOD, #ifdef HAS_LCHMOD @@ -99,6 +105,9 @@ enum action { #ifdef HAS_FCHFLAGS ACTION_FCHFLAGS, #endif +#ifdef HAS_CHFLAGSAT + ACTION_CHFLAGSAT, +#endif #ifdef HAS_LCHFLAGS ACTION_LCHFLAGS, #endif @@ -154,7 +163,13 @@ static struct syscall_desc syscalls[] = { { "mknod", ACTION_MKNOD, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} }, { "mknodat", ACTION_MKNODAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} }, { "bind", ACTION_BIND, { TYPE_STRING, TYPE_NONE } }, +#ifdef HAS_BINDAT + { "bindat", ACTION_BINDAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } }, +#endif { "connect", ACTION_CONNECT, { TYPE_STRING, TYPE_NONE } }, +#ifdef HAS_CONNECTAT + { "connectat", ACTION_CONNECTAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } }, +#endif { "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } }, { "fchmod", ACTION_FCHMOD, { TYPE_DESCRIPTOR, TYPE_NUMBER, TYPE_NONE } }, #ifdef HAS_LCHMOD @@ -171,6 +186,9 @@ static struct syscall_desc syscalls[] = { #ifdef HAS_FCHFLAGS { "fchflags", ACTION_FCHFLAGS, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } }, #endif +#ifdef HAS_CHFLAGSAT + { "chflagsat", ACTION_CHFLAGSAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NONE } }, +#endif #ifdef HAS_LCHFLAGS { "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, #endif @@ -294,6 +312,11 @@ static struct flag linkat_flags[] = { { 0, NULL } }; +static struct flag chflagsat_flags[] = { + { AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" }, + { 0, NULL } +}; + static struct flag fchmodat_flags[] = { { AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" }, { 0, NULL } @@ -732,6 +755,22 @@ call_syscall(struct syscall_desc *scall, char *argv[]) rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } +#ifdef HAS_BINDAT + case ACTION_BINDAT: + { + struct sockaddr_un sunx; + + sunx.sun_family = AF_UNIX; + strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); + sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; + rval = socket(AF_UNIX, SOCK_STREAM, 0); + if (rval < 0) + break; + rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx, + sizeof(sunx)); + break; + } +#endif case ACTION_CONNECT: { struct sockaddr_un sunx; @@ -745,6 +784,22 @@ call_syscall(struct syscall_desc *scall, char *argv[]) rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } +#ifdef HAS_CONNECTAT + case ACTION_CONNECTAT: + { + struct sockaddr_un sunx; + + sunx.sun_family = AF_UNIX; + strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); + sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; + rval = socket(AF_UNIX, SOCK_STREAM, 0); + if (rval < 0) + break; + rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx, + sizeof(sunx)); + break; + } +#endif case ACTION_CHMOD: rval = chmod(STR(0), (mode_t)NUM(1)); break; @@ -785,9 +840,17 @@ call_syscall(struct syscall_desc *scall, char *argv[]) (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif +#ifdef HAS_CHFLAGSAT + case ACTION_CHFLAGSAT: + rval = chflagsat(NUM(0), STR(1), + (unsigned long)str2flags(chflags_flags, STR(2)), + (int)str2flags(chflagsat_flags, STR(3))); + break; +#endif #ifdef HAS_LCHFLAGS case ACTION_LCHFLAGS: - rval = lchflags(STR(0), (int)str2flags(chflags_flags, STR(1))); + rval = lchflags(STR(0), + (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif case ACTION_TRUNCATE: diff --git a/tools/regression/pjdfstest/tests/misc.sh b/tools/regression/pjdfstest/tests/misc.sh index 0d3ce35..8978b5f 100644 --- a/tools/regression/pjdfstest/tests/misc.sh +++ b/tools/regression/pjdfstest/tests/misc.sh @@ -89,7 +89,7 @@ todo() namegen() { - echo "pjdfstest_`dd if=/dev/urandom bs=1k count=1 2>/dev/null | openssl md5`" + echo "pjdfstest_`dd if=/dev/urandom bs=1k count=1 2>/dev/null | openssl md5 | awk '{print $NF}'`" } namegen_len() @@ -98,7 +98,7 @@ namegen_len() name="" while :; do - namepart="`dd if=/dev/urandom bs=64 count=1 2>/dev/null | openssl md5`" + namepart="`dd if=/dev/urandom bs=64 count=1 2>/dev/null | openssl md5 | awk '{print $NF}'`" name="${name}${namepart}" curlen=`printf "%s" "${name}" | wc -c` [ ${curlen} -lt ${len} ] || break diff --git a/tools/regression/security/cap_test/cap_test_capabilities.c b/tools/regression/security/cap_test/cap_test_capabilities.c index 450ad88..d4347ee 100644 --- a/tools/regression/security/cap_test/cap_test_capabilities.c +++ b/tools/regression/security/cap_test/cap_test_capabilities.c @@ -1,8 +1,12 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson + * Copyright (c) 2012 FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Pawel Jakub Dawidek under + * sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -43,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <err.h> #include <fcntl.h> #include <poll.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -60,14 +65,20 @@ __FBSDID("$FreeBSD$"); */ #define CHECK_RESULT(syscall, rights_needed, succeeded) do { \ if ((rights & (rights_needed)) == (rights_needed)) { \ - if (!(succeeded)) \ + if (succeeded) { \ + if (success == -1) \ + success = PASSED; \ + } else { \ SYSCALL_FAIL(syscall, "failed"); \ + } \ } else { \ - if (succeeded) \ + if (succeeded) { \ FAILX("%s:\tsucceeded when it shouldn't have" \ - " (rights 0x%jx)", #syscall, rights); \ - else if (errno != ENOTCAPABLE) \ + " (rights 0x%jx)", #syscall, \ + (uintmax_t)rights); \ + } else if (errno != ENOTCAPABLE) { \ SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ + } \ } \ errno = 0; \ } while (0) @@ -79,8 +90,11 @@ __FBSDID("$FreeBSD$"); if ((rights & (rights_needed)) == (rights_needed)) { \ if (p == MAP_FAILED) \ SYSCALL_FAIL(mmap, "failed"); \ - else \ + else { \ (void)munmap(p, getpagesize()); \ + if (success == -1) \ + success = PASSED; \ + } \ } else { \ if (p != MAP_FAILED) { \ FAILX("%s:\tsucceeded when it shouldn't have" \ @@ -97,96 +111,231 @@ __FBSDID("$FreeBSD$"); * make sure only those rights work. */ static int -try_file_ops(int fd, cap_rights_t rights) +try_file_ops(int filefd, int dirfd, cap_rights_t rights) { struct stat sb; struct statfs sf; - int fd_cap, fd_capcap; + cap_rights_t erights; + int fd_cap, fd_capcap, dfd_cap; ssize_t ssize, ssize2; off_t off; void *p; char ch; int ret, is_nfs; struct pollfd pollfd; - int success = PASSED; + int success = -1; - REQUIRE(fstatfs(fd, &sf)); - is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename)) - == 0); + REQUIRE(fstatfs(filefd, &sf)); + is_nfs = (strcmp("nfs", sf.f_fstypename) == 0); - REQUIRE(fd_cap = cap_new(fd, rights)); + REQUIRE(fd_cap = cap_new(filefd, rights)); + CHECK(cap_getrights(fd_cap, &erights) == 0); + CHECK(rights == erights); REQUIRE(fd_capcap = cap_new(fd_cap, rights)); + CHECK(cap_getrights(fd_capcap, &erights) == 0); + CHECK(rights == erights); CHECK(fd_capcap != fd_cap); - - pollfd.fd = fd_cap; - pollfd.events = POLLIN | POLLERR | POLLHUP; - pollfd.revents = 0; + REQUIRE(dfd_cap = cap_new(dirfd, rights)); + CHECK(cap_getrights(dfd_cap, &erights) == 0); + CHECK(rights == erights); ssize = read(fd_cap, &ch, sizeof(ch)); - CHECK_RESULT(read, CAP_READ | CAP_SEEK, ssize >= 0); - - ssize = pread(fd_cap, &ch, sizeof(ch), 0); - ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); - CHECK_RESULT(pread, CAP_READ, ssize >= 0); - CHECK(ssize == ssize2); + CHECK_RESULT(read, CAP_READ, ssize >= 0); ssize = write(fd_cap, &ch, sizeof(ch)); - CHECK_RESULT(write, CAP_WRITE | CAP_SEEK, ssize >= 0); - - ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); - CHECK_RESULT(pwrite, CAP_WRITE, ssize >= 0); + CHECK_RESULT(write, CAP_WRITE, ssize >= 0); off = lseek(fd_cap, 0, SEEK_SET); CHECK_RESULT(lseek, CAP_SEEK, off >= 0); - /* - * Note: this is not expected to work over NFS. - */ - ret = fchflags(fd_cap, UF_NODUMP); - CHECK_RESULT(fchflags, CAP_FCHFLAGS, - (ret == 0) || (is_nfs && (errno == EOPNOTSUPP))); + ssize = pread(fd_cap, &ch, sizeof(ch), 0); + ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); + CHECK_RESULT(pread, CAP_PREAD, ssize >= 0); + CHECK(ssize == ssize2); - ret = fstat(fd_cap, &sb); - CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); + ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); + CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0); + + p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0); + CHECK_MMAP_RESULT(CAP_MMAP); p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ); + CHECK_MMAP_RESULT(CAP_MMAP_R); p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_WRITE); + CHECK_MMAP_RESULT(CAP_MMAP_W); p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC); + CHECK_MMAP_RESULT(CAP_MMAP_X); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE); + CHECK_MMAP_RESULT(CAP_MMAP_RW); p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_MAPEXEC); + CHECK_MMAP_RESULT(CAP_MMAP_RX); p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); + CHECK_MMAP_RESULT(CAP_MMAP_WX); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd_cap, 0); - CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); + CHECK_MMAP_RESULT(CAP_MMAP_RWX); + + ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600); + CHECK_RESULT(openat(O_CREATE | O_RDONLY), + CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); + ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND, + 0600); + CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND), + CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); + ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600); + CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND), + CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = fsync(fd_cap); CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); + ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY); + CHECK_RESULT(openat(O_FSYNC | O_RDONLY), + CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND); + CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND), + CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND); + CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND), + CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY); + CHECK_RESULT(openat(O_SYNC | O_RDONLY), + CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND); + CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND), + CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND); + CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND), + CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); + + ret = ftruncate(fd_cap, 0); + CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); + + ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY); + CHECK_RESULT(openat(O_TRUNC | O_RDONLY), + CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY); + CHECK_RESULT(openat(O_TRUNC | O_WRONLY), + CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR); + CHECK_RESULT(openat(O_TRUNC | O_RDWR), + CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0); + + ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600); + CHECK_RESULT(openat(O_CREATE | O_WRONLY), + CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); + ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600); + CHECK_RESULT(openat(O_CREATE | O_RDWR), + CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, + ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); + + ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY); + CHECK_RESULT(openat(O_FSYNC | O_WRONLY), + CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR); + CHECK_RESULT(openat(O_FSYNC | O_RDWR), + CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY); + CHECK_RESULT(openat(O_SYNC | O_WRONLY), + CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR); + CHECK_RESULT(openat(O_SYNC | O_RDWR), + CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); + CHECK(ret == -1 || close(ret) == 0); + CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); + + /* + * Note: this is not expected to work over NFS. + */ + ret = fchflags(fd_cap, UF_NODUMP); + CHECK_RESULT(fchflags, CAP_FCHFLAGS, + ret == 0 || (is_nfs && errno == EOPNOTSUPP)); + + ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0); + CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0); + ret = fchown(fd_cap, -1, -1); CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); + ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0); + CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0); + ret = fchmod(fd_cap, 0644); CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); + ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0); + CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0); + + ret = fcntl(fd_cap, F_GETFL); + CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0); + ret = fcntl(fd_cap, F_SETFL, ret); + CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0); + /* XXX flock */ - ret = ftruncate(fd_cap, 0); - CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); + ret = fstat(fd_cap, &sb); + CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); + + ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0); + CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0); ret = fstatfs(fd_cap, &sf); CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); @@ -197,6 +346,55 @@ try_file_ops(int fd, cap_rights_t rights) ret = futimes(fd_cap, NULL); CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); + ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = futimesat(dfd_cap, "cap_futimesat", NULL); + CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0); + + ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0); + CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0); + CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0); + + ret = mkdirat(dfd_cap, "cap_mkdirat", 0700); + CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0); + + ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600); + CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0); + + ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0); + CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0); + + /* TODO: renameat(2) */ + + ret = symlinkat("test", dfd_cap, "cap_symlinkat"); + CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0); + CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0); + + ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600); + CHECK(ret >= 0); + CHECK(close(ret) == 0); + ret = unlinkat(dfd_cap, "cap_unlinkat", 0); + CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); + CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0); + ret = mkdirat(dirfd, "cap_unlinkat", 0700); + CHECK(ret == 0); + ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR); + CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); + CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0); + + pollfd.fd = fd_cap; + pollfd.events = POLLIN | POLLERR | POLLHUP; + pollfd.revents = 0; + ret = poll(&pollfd, 1, 0); if (rights & CAP_POLL_EVENT) CHECK((pollfd.revents & POLLNVAL) == 0); @@ -205,79 +403,159 @@ try_file_ops(int fd, cap_rights_t rights) /* XXX: select, kqueue */ - close (fd_cap); + close(fd_cap); + close(fd_capcap); + + if (success == -1) { + fprintf(stderr, "No tests for rights 0x%jx.\n", + (uintmax_t)rights); + success = FAILED; + } return (success); } -#define TRY(fd, rights) \ +#define TRY(rights) \ do { \ if (success == PASSED) \ - success = try_file_ops(fd, rights); \ + success = try_file_ops(filefd, dirfd, (rights)); \ else \ /* We've already failed, but try the test anyway. */ \ - try_file_ops(fd, rights); \ + try_file_ops(filefd, dirfd, (rights)); \ } while (0) +#define KEEP_ERRNO(...) do { \ + int _saved_errno = errno; \ + __VA_ARGS__; \ + errno = _saved_errno; \ +} while (0); + int test_capabilities(void) { - int fd; + int filefd, dirfd, tmpfd; int success = PASSED; - - fd = open("/tmp/cap_test_capabilities", O_RDWR | O_CREAT, 0644); - if (fd < 0) + char file[] = "/tmp/cap_test.XXXXXXXXXX"; + char dir[] = "/tmp/cap_test.XXXXXXXXXX"; + + filefd = mkstemp(file); + if (filefd < 0) + err(-1, "mkstemp"); + if (mkdtemp(dir) == NULL) { + KEEP_ERRNO(unlink(file)); + err(-1, "mkdtemp"); + } + dirfd = open(dir, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + KEEP_ERRNO(unlink(file)); + KEEP_ERRNO(rmdir(dir)); err(-1, "open"); + } + tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY); + if (tmpfd == -1) { + KEEP_ERRNO(unlink(file)); + KEEP_ERRNO(rmdir(dir)); + err(-1, "open"); + } - if (cap_enter() < 0) + if (cap_enter() == -1) { + KEEP_ERRNO(unlink(file)); + KEEP_ERRNO(rmdir(dir)); err(-1, "cap_enter"); - - /* XXX: Really want to try all combinations. */ - TRY(fd, CAP_READ); - TRY(fd, CAP_READ | CAP_SEEK); - TRY(fd, CAP_WRITE); - TRY(fd, CAP_WRITE | CAP_SEEK); - TRY(fd, CAP_READ | CAP_WRITE); - TRY(fd, CAP_READ | CAP_WRITE | CAP_SEEK); - TRY(fd, CAP_SEEK); - TRY(fd, CAP_FCHFLAGS); - TRY(fd, CAP_IOCTL); - TRY(fd, CAP_FSTAT); - TRY(fd, CAP_MMAP); - TRY(fd, CAP_MMAP | CAP_READ); - TRY(fd, CAP_MMAP | CAP_WRITE); - TRY(fd, CAP_MMAP | CAP_MAPEXEC); - TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE); - TRY(fd, CAP_MMAP | CAP_READ | CAP_MAPEXEC); - TRY(fd, CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); - TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); - TRY(fd, CAP_FCNTL); - TRY(fd, CAP_POST_EVENT); - TRY(fd, CAP_POLL_EVENT); - TRY(fd, CAP_FSYNC); - TRY(fd, CAP_FCHOWN); - TRY(fd, CAP_FCHMOD); - TRY(fd, CAP_FTRUNCATE); - TRY(fd, CAP_FLOCK); - TRY(fd, CAP_FSTATFS); - TRY(fd, CAP_FPATHCONF); - TRY(fd, CAP_FUTIMES); - TRY(fd, CAP_ACL_GET); - TRY(fd, CAP_ACL_SET); - TRY(fd, CAP_ACL_DELETE); - TRY(fd, CAP_ACL_CHECK); - TRY(fd, CAP_EXTATTR_GET); - TRY(fd, CAP_EXTATTR_SET); - TRY(fd, CAP_EXTATTR_DELETE); - TRY(fd, CAP_EXTATTR_LIST); - TRY(fd, CAP_MAC_GET); - TRY(fd, CAP_MAC_SET); - - /* - * Socket-specific. - */ - TRY(fd, CAP_GETPEERNAME); - TRY(fd, CAP_GETSOCKNAME); - TRY(fd, CAP_ACCEPT); + } + + TRY(CAP_READ); + TRY(CAP_WRITE); + TRY(CAP_SEEK); + TRY(CAP_PREAD); + TRY(CAP_PWRITE); + TRY(CAP_READ | CAP_WRITE); + TRY(CAP_PREAD | CAP_PWRITE); + TRY(CAP_MMAP); + TRY(CAP_MMAP_R); + TRY(CAP_MMAP_W); + TRY(CAP_MMAP_X); + TRY(CAP_MMAP_RW); + TRY(CAP_MMAP_RX); + TRY(CAP_MMAP_WX); + TRY(CAP_MMAP_RWX); + TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP); + TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP); + TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); +#ifdef TODO + TRY(CAP_FEXECVE); +#endif + TRY(CAP_FSYNC); + TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP); + TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP); + TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP); + TRY(CAP_FTRUNCATE); + TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP); + TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP); + TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); +#ifdef TODO + TRY(CAP_FCHDIR); +#endif + TRY(CAP_FCHFLAGS); + TRY(CAP_FCHOWN); + TRY(CAP_FCHOWN | CAP_LOOKUP); + TRY(CAP_FCHMOD | CAP_LOOKUP); + TRY(CAP_FCNTL); +#ifdef TODO + TRY(CAP_FLOCK); +#endif + TRY(CAP_FPATHCONF); +#ifdef TODO + TRY(CAP_FSCK); +#endif + TRY(CAP_FSTAT | CAP_LOOKUP); + TRY(CAP_FSTATFS); + TRY(CAP_FUTIMES | CAP_LOOKUP); + TRY(CAP_LINKAT | CAP_LOOKUP); + TRY(CAP_MKDIRAT | CAP_LOOKUP); + TRY(CAP_MKFIFOAT | CAP_LOOKUP); + TRY(CAP_MKNODAT | CAP_LOOKUP); + TRY(CAP_SYMLINKAT | CAP_LOOKUP); + TRY(CAP_UNLINKAT | CAP_LOOKUP); + /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */ + TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP); +#ifdef TODO + TRY(CAP_LOOKUP); + TRY(CAP_EXTATTR_DELETE); + TRY(CAP_EXTATTR_GET); + TRY(CAP_EXTATTR_LIST); + TRY(CAP_EXTATTR_SET); + TRY(CAP_ACL_CHECK); + TRY(CAP_ACL_DELETE); + TRY(CAP_ACL_GET); + TRY(CAP_ACL_SET); + TRY(CAP_ACCEPT); + TRY(CAP_BIND); + TRY(CAP_CONNECT); + TRY(CAP_GETPEERNAME); + TRY(CAP_GETSOCKNAME); + TRY(CAP_GETSOCKOPT); + TRY(CAP_LISTEN); + TRY(CAP_PEELOFF); + TRY(CAP_RECV); + TRY(CAP_SEND); + TRY(CAP_SETSOCKOPT); + TRY(CAP_SHUTDOWN); + TRY(CAP_MAC_GET); + TRY(CAP_MAC_SET); + TRY(CAP_SEM_GETVALUE); + TRY(CAP_SEM_POST); + TRY(CAP_SEM_WAIT); + TRY(CAP_POST_EVENT); + TRY(CAP_POLL_EVENT); + TRY(CAP_IOCTL); + TRY(CAP_TTYHOOK); + TRY(CAP_PDGETPID); + TRY(CAP_PDWAIT); + TRY(CAP_PDKILL); +#endif + + (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0); + (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR); return (success); } diff --git a/tools/regression/security/cap_test/cap_test_relative.c b/tools/regression/security/cap_test/cap_test_relative.c index 1f62e84..27410eb 100644 --- a/tools/regression/security/cap_test/cap_test_relative.c +++ b/tools/regression/security/cap_test/cap_test_relative.c @@ -61,7 +61,8 @@ test_relative(void) cap_rights_t rights; REQUIRE(etc = open("/etc/", O_RDONLY)); - CHECK_SYSCALL_FAILS(EINVAL, cap_getrights, etc, &rights); + CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc, &rights); + CHECK_RIGHTS(rights, CAP_ALL); MAKE_CAPABILITY(etc_cap, etc, CAP_READ); MAKE_CAPABILITY(etc_cap_ro, etc, CAP_READ | CAP_LOOKUP); diff --git a/tools/regression/sockets/unix_cmsg/README b/tools/regression/sockets/unix_cmsg/README index 359da43..df51723 100644 --- a/tools/regression/sockets/unix_cmsg/README +++ b/tools/regression/sockets/unix_cmsg/README @@ -1,127 +1,160 @@ $FreeBSD$ About unix_cmsg -================ +=============== -This program is a collection of regression tests for ancillary (control) -data for PF_LOCAL sockets (local domain or Unix domain sockets). There -are tests for stream and datagram sockets. +This program is a collection of regression tests for ancillary data +(control information) for PF_LOCAL sockets (local domain or Unix domain +sockets). There are tests for stream and datagram sockets. -Usually each test does following steps: create Server, fork Client, -Client sends something to Server, Server verifies if everything -is correct in received message. Sometimes Client sends several -messages to Server. +Usually each test does following steps: creates Server, forks Client, +Client sends something to Server, Server verifies whether everything is +correct in received message(s). It is better to change the owner of unix_cmsg to some safe user -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests -can give correct results for wrong implementation. +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that +check credentials can give correct results for wrong implementation. + +It is better to run this program by a user that belongs to more +than 16 groups. Available options ================= --d Output debugging information, values of different fields of - received messages, etc. Will produce many lines of information. - --h Output help message and exit. +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno] + + Options are: + -d Output debugging information + -h Output the help message and exit + -n num Number of messages to send + -s size Specify size of data for IPC + -t type Specify socket type (stream, dgram) for tests + -z value Do not send data in a message (bit 0x1), do not send + data array associated with a cmsghdr structure (bit 0x2) + testno Run one test by its number (require the -t option) + +Description +=========== + +If Client sends something to Server, then it sends 5 messages by default. +Number of messages can be changed in the -n command line option. Number +of messages will be given as N in the following descriptions. + +If Client sends something to Server, then it sends some data (few bytes) +in each message by default. The size of this data can be changed by the -s +command line option. The "-s 0" command line option means, that Client will +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced +by the msg_iov field from struct msghdr{}. The "-z 1" or "-z 3" command line +option means, that Client will send zero bytes represented by the NULL value +in the msg_iov field from struct msghdr{}. + +If Client sends some ancillary data object, then this ancillary data object +always has associated data array by default. The "-z 2" or "-z 3" option +means, that Client will not send associated data array if possible. --t <socktype> - Run tests only for the given socket type: "stream" or "dgram". - With this option it is possible to run only particular test, - not all of them. +For SOCK_STREAM sockets: +----------------------- --z Do not send real control data if possible. Struct cmsghdr{} - should be followed by real control data. It is not clear if - a sender should give control data in all cases (this is not - documented and an arbitrary application can choose anything). + 1: Sending, receiving cmsgcred - At least for PF_LOCAL sockets' control messages with types - SCM_CREDS and SCM_TIMESTAMP the kernel does not need any - control data. This option allow to not send real control data - for SCM_CREDS and SCM_TIMESTAMP control messages. + Client connects to Server and sends N messages with SCM_CREDS ancillary + data object. Server should receive N messages, each message should + have SCM_CREDS ancillary data object followed by struct cmsgcred{}. -Description of tests -==================== + 2: Receiving sockcred (listening socket) -For SOCK_STREAM sockets: ------------------------ + Server creates a listening stream socket and sets the LOCAL_CREDS + socket option for it. Client connects to Server two times, each time + it sends N messages. Server accepts two connections and receives N + messages from each connection. The first message from each connection + should have SCM_CREDS ancillary data object followed by struct sockcred{}, + next messages from the same connection should not have ancillary data. - 1: Sending, receiving cmsgcred + 3: Receiving sockcred (accepted socket) - Client connects to Server and sends two messages with data and - control message with SCM_CREDS type to Server. Server should - receive two messages, in both messages there should be data and - control message with SCM_CREDS type followed by struct cmsgcred{} - and this structure should contain correct information. - - 2: Receiving sockcred (listening socket has LOCAL_CREDS) - - Server creates listen socket and set socket option LOCAL_CREDS - for it. Client connects to Server and sends two messages with data - to Server. Server should receive two messages, in first message - there should be data and control message with SCM_CREDS type followed - by struct sockcred{} and this structure should contain correct - information, in second message there should be data and no control - message. - - 3: Receiving sockcred (accepted socket has LOCAL_CREDS) - - Client connects to Server and sends two messages with data. Server - accepts connection and set socket option LOCAL_CREDS for just accepted - socket (here synchronization is used, to allow Client to see just set - flag on Server's socket before sending messages to Server). Server - should receive two messages, in first message there should be data and - control message with SOCK_CRED type followed by struct sockcred{} and - this structure should contain correct information, in second message - there should be data and no control message. + Client connects to Server. Server accepts connection and sets the + LOCAL_CREDS socket option for just accepted socket. Client sends N + messages to Server. Server should receive N messages, the first + message should have SCM_CREDS ancillary data object followed by + struct sockcred{}, next messages should not have ancillary data. 4: Sending cmsgcred, receiving sockcred - Server creates listen socket and set socket option LOCAL_CREDS - for it. Client connects to Server and sends one message with data - and control message with SCM_CREDS type to Server. Server should - receive one message with data and control message with SCM_CREDS type - followed by struct sockcred{} and this structure should contain - correct information. + Server creates a listening stream socket and sets the LOCAL_CREDS + socket option for it. Client connects to Server and sends N messages + with SCM_CREDS ancillary data object. Server should receive N messages, + the first message should have SCM_CREDS ancillary data object followed + by struct sockcred{}, each of next messages should have SCM_CREDS + ancillary data object followed by struct cmsgcred{}. + + 5: Sending, receiving timeval + + Client connects to Server and sends message with SCM_TIMESTAMP ancillary + data object. Server should receive one message with SCM_TIMESTAMP + ancillary data object followed by struct timeval{}. - 5: Sending, receiving timestamp + 6: Sending, receiving bintime - Client connects to Server and sends message with data and control - message with SCM_TIMESTAMP type to Server. Server should receive - message with data and control message with SCM_TIMESTAMP type - followed by struct timeval{}. + Client connects to Server and sends message with SCM_BINTIME ancillary + data object. Server should receive one message with SCM_BINTIME + ancillary data object followed by struct bintime{}. + + 7: Checking cmsghdr.cmsg_len + + Client connects to Server and tries to send several messages with + SCM_CREDS ancillary data object that has wrong cmsg_len field in its + struct cmsghdr{}. All these attempts should fail, since cmsg_len + in all requests is less than CMSG_LEN(0). + + 8: Check LOCAL_PEERCRED socket option + + This test does not use ancillary data, but can be implemented here. + Client connects to Server. Both Client and Server verify that + credentials of the peer are correct using LOCAL_PEERCRED socket option. For SOCK_DGRAM sockets: ---------------------- 1: Sending, receiving cmsgcred - Client sends to Server two messages with data and control message - with SCM_CREDS type to Server. Server should receive two messages, - in both messages there should be data and control message with - SCM_CREDS type followed by struct cmsgcred{} and this structure - should contain correct information. + Client connects to Server and sends N messages with SCM_CREDS ancillary + data object. Server should receive N messages, each message should + have SCM_CREDS ancillary data object followed by struct cmsgcred{}. 2: Receiving sockcred - Server creates datagram socket and set socket option LOCAL_CREDS - for it. Client sends two messages with data to Server. Server should - receive two messages, in both messages there should be data and control - message with SCM_CREDS type followed by struct sockcred{} and this - structure should contain correct information. + Server creates datagram socket and sets the LOCAL_CREDS socket option + for it. Client sends N messages to Server. Server should receive N + messages, each message should have SCM_CREDS ancillary data object + followed by struct sockcred{}. 3: Sending cmsgcred, receiving sockcred - - Server creates datagram socket and set socket option LOCAL_CREDS - for it. Client sends one message with data and control message with - SOCK_CREDS type to Server. Server should receive one message with - data and control message with SCM_CREDS type followed by struct - sockcred{} and this structure should contain correct information. - 4: Sending, receiving timestamp + Server creates datagram socket and sets the LOCAL_CREDS socket option + for it. Client sends N messages with SCM_CREDS ancillary data object + to Server. Server should receive N messages, the first message should + have SCM_CREDS ancillary data object followed by struct sockcred{}, + each of next messages should have SCM_CREDS ancillary data object + followed by struct cmsgcred{}. + + 4: Sending, receiving timeval + + Client sends one message with SCM_TIMESTAMP ancillary data object + to Server. Server should receive one message with SCM_TIMESTAMP + ancillary data object followed by struct timeval{}. + + 5: Sending, receiving bintime + + Client sends one message with SCM_BINTIME ancillary data object + to Server. Server should receive one message with SCM_BINTIME + ancillary data object followed by struct bintime{}. + + 6: Checking cmsghdr.cmsg_len - Client sends message with data and control message with SCM_TIMESTAMP - type to Server. Server should receive message with data and control - message with SCM_TIMESTAMP type followed by struct timeval{}. + Client tries to send Server several messages with SCM_CREDS ancillary + data object that has wrong cmsg_len field in its struct cmsghdr{}. + All these attempts should fail, since cmsg_len in all requests is less + than CMSG_LEN(0). - Andrey Simonenko -simon@comsys.ntu-kpi.kiev.ua +andreysimonenko@users.sourceforge.net diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.c b/tools/regression/sockets/unix_cmsg/unix_cmsg.c index 06728d7..d91cef4 100644 --- a/tools/regression/sockets/unix_cmsg/unix_cmsg.c +++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.c @@ -27,48 +27,46 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/types.h> +#include <sys/param.h> #include <sys/resource.h> #include <sys/time.h> +#include <sys/select.h> #include <sys/socket.h> +#include <sys/ucred.h> #include <sys/un.h> #include <sys/wait.h> -#include <assert.h> #include <ctype.h> #include <err.h> #include <errno.h> +#include <fcntl.h> #include <inttypes.h> #include <limits.h> -#include <setjmp.h> +#include <paths.h> #include <signal.h> #include <stdarg.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sysexits.h> #include <unistd.h> /* * There are tables with tests descriptions and pointers to test * functions. Each t_*() function returns 0 if its test passed, - * -1 if its test failed (something wrong was found in local domain - * control messages), -2 if some system error occurred. If test - * function returns -2, then a program exits. + * -1 if its test failed, -2 if some system error occurred. + * If a test function returns -2, then a program exits. * - * Each test function completely control what to do (eg. fork or - * do not fork a client process). If a test function forks a client - * process, then it waits for its termination. If a return code of a - * client process is not equal to zero, or if a client process was - * terminated by a signal, then test function returns -2. + * If a test function forks a client process, then it waits for its + * termination. If a return code of a client process is not equal + * to zero, or if a client process was terminated by a signal, then + * a test function returns -1 or -2 depending on exit status of + * a client process. * - * Each test function and complete program are not optimized - * a lot to allow easy to modify tests. - * - * Each function which can block, is run under TIMEOUT, if timeout - * occurs, then test function returns -2 or a client process exits - * with nonzero return code. + * Each function which can block, is run under TIMEOUT. If timeout + * occurs, then a test function returns -2 or a client process exits + * with a non-zero return code. */ #ifndef LISTENQ @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD$"); #endif #ifndef TIMEOUT -# define TIMEOUT 60 +# define TIMEOUT 2 #endif -#define EXTRA_CMSG_SPACE 512 /* Memory for not expected control data. */ - -static int t_cmsgcred(void), t_sockcred_stream1(void); -static int t_sockcred_stream2(void), t_cmsgcred_sockcred(void); -static int t_sockcred_dgram(void), t_timestamp(void); +static int t_cmsgcred(void); +static int t_sockcred_1(void); +static int t_sockcred_2(void); +static int t_cmsgcred_sockcred(void); +static int t_timeval(void); +static int t_bintime(void); +static int t_cmsg_len(void); +static int t_peercred(void); struct test_func { - int (*func)(void); /* Pointer to function. */ - const char *desc; /* Test description. */ + int (*func)(void); + const char *desc; }; -static struct test_func test_stream_tbl[] = { - { NULL, " 0: All tests" }, - { t_cmsgcred, " 1: Sending, receiving cmsgcred" }, - { t_sockcred_stream1, " 2: Receiving sockcred (listening socket has LOCAL_CREDS)" }, - { t_sockcred_stream2, " 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" }, - { t_cmsgcred_sockcred, " 4: Sending cmsgcred, receiving sockcred" }, - { t_timestamp, " 5: Sending, receiving timestamp" }, - { NULL, NULL } +static const struct test_func test_stream_tbl[] = { + { + .func = NULL, + .desc = "All tests" + }, + { + .func = t_cmsgcred, + .desc = "Sending, receiving cmsgcred" + }, + { + .func = t_sockcred_1, + .desc = "Receiving sockcred (listening socket)" + }, + { + .func = t_sockcred_2, + .desc = "Receiving sockcred (accepted socket)" + }, + { + .func = t_cmsgcred_sockcred, + .desc = "Sending cmsgcred, receiving sockcred" + }, + { + .func = t_timeval, + .desc = "Sending, receiving timeval" + }, + { + .func = t_bintime, + .desc = "Sending, receiving bintime" + }, + { + .func = t_cmsg_len, + .desc = "Check cmsghdr.cmsg_len" + }, + { + .func = t_peercred, + .desc = "Check LOCAL_PEERCRED socket option" + } }; -static struct test_func test_dgram_tbl[] = { - { NULL, " 0: All tests" }, - { t_cmsgcred, " 1: Sending, receiving cmsgcred" }, - { t_sockcred_dgram, " 2: Receiving sockcred" }, - { t_cmsgcred_sockcred, " 3: Sending cmsgcred, receiving sockcred" }, - { t_timestamp, " 4: Sending, receiving timestamp" }, - { NULL, NULL } +#define TEST_STREAM_TBL_SIZE \ + (sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0])) + +static const struct test_func test_dgram_tbl[] = { + { + .func = NULL, + .desc = "All tests" + }, + { + .func = t_cmsgcred, + .desc = "Sending, receiving cmsgcred" + }, + { + .func = t_sockcred_2, + .desc = "Receiving sockcred" + }, + { + .func = t_cmsgcred_sockcred, + .desc = "Sending cmsgcred, receiving sockcred" + }, + { + .func = t_timeval, + .desc = "Sending, receiving timeval" + }, + { + .func = t_bintime, + .desc = "Sending, receiving bintime" + }, + { + .func = t_cmsg_len, + .desc = "Check cmsghdr.cmsg_len" + } }; -#define TEST_STREAM_NO_MAX (sizeof(test_stream_tbl) / sizeof(struct test_func) - 2) -#define TEST_DGRAM_NO_MAX (sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2) +#define TEST_DGRAM_TBL_SIZE \ + (sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0])) -static const char *myname = "SERVER"; /* "SERVER" or "CLIENT" */ +static bool debug = false; +static bool server_flag = true; +static bool send_data_flag = true; +static bool send_array_flag = true; +static bool failed_flag = false; -static int debug = 0; /* 1, if -d. */ -static int no_control_data = 0; /* 1, if -z. */ +static int sock_type; +static const char *sock_type_str; -static u_int nfailed = 0; /* Number of failed tests. */ +static const char *proc_name; -static int sock_type; /* SOCK_STREAM or SOCK_DGRAM */ -static const char *sock_type_str; /* "SOCK_STREAM" or "SOCK_DGRAN" */ +static char work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX"; +static int serv_sock_fd; +static struct sockaddr_un serv_addr_sun; -static char tempdir[] = "/tmp/unix_cmsg.XXXXXXX"; -static char serv_sock_path[PATH_MAX]; +static struct { + char *buf_send; + char *buf_recv; + size_t buf_size; + u_int msg_num; +} ipc_msg; -static char ipc_message[] = "hello"; +#define IPC_MSG_NUM_DEF 5 +#define IPC_MSG_NUM_MAX 10 +#define IPC_MSG_SIZE_DEF 7 +#define IPC_MSG_SIZE_MAX 128 -#define IPC_MESSAGE_SIZE (sizeof(ipc_message)) +static struct { + uid_t uid; + uid_t euid; + gid_t gid; + gid_t egid; + gid_t *gid_arr; + int gid_num; +} proc_cred; -static struct sockaddr_un servaddr; /* Server address. */ +static pid_t client_pid; -static sigjmp_buf env_alrm; +#define SYNC_SERVER 0 +#define SYNC_CLIENT 1 +#define SYNC_RECV 0 +#define SYNC_SEND 1 -static uid_t my_uid; -static uid_t my_euid; -static gid_t my_gid; -static gid_t my_egid; +static int sync_fd[2][2]; -/* - * my_gids[0] is EGID, next items are supplementary GIDs, - * my_ngids determines valid items in my_gids array. - */ -static gid_t my_gids[NGROUPS_MAX]; -static int my_ngids; - -static pid_t client_pid; /* PID of forked client. */ - -#define dbgmsg(x) do { \ - if (debug) \ - logmsgx x ; \ -} while (/* CONSTCOND */0) +#define LOGMSG_SIZE 128 static void logmsg(const char *, ...) __printflike(1, 2); static void logmsgx(const char *, ...) __printflike(1, 2); +static void dbgmsg(const char *, ...) __printflike(1, 2); static void output(const char *, ...) __printflike(1, 2); -extern char *__progname; /* The name of program. */ - -/* - * Output the help message (-h switch). - */ static void -usage(int quick) +usage(bool verbose) { - const struct test_func *test_func; + u_int i; - fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n", - __progname); - if (quick) + printf("usage: %s [-dh] [-n num] [-s size] [-t type] " + "[-z value] [testno]\n", getprogname()); + if (!verbose) return; - fprintf(stderr, "\n Options are:\n\ - -d\t\t\tOutput debugging information\n\ - -h\t\t\tOutput this help message and exit\n\ - -t <socktype>\t\tRun test only for the given socket type:\n\ -\t\t\tstream or dgram\n\ - -z\t\t\tDo not send real control data if possible\n\n"); - fprintf(stderr, " Available tests for stream sockets:\n"); - for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func) - fprintf(stderr, " %s\n", test_func->desc); - fprintf(stderr, "\n Available tests for datagram sockets:\n"); - for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func) - fprintf(stderr, " %s\n", test_func->desc); + printf("\n Options are:\n\ + -d Output debugging information\n\ + -h Output the help message and exit\n\ + -n num Number of messages to send\n\ + -s size Specify size of data for IPC\n\ + -t type Specify socket type (stream, dgram) for tests\n\ + -z value Do not send data in a message (bit 0x1), do not send\n\ + data array associated with a cmsghdr structure (bit 0x2)\n\ + testno Run one test by its number (require the -t option)\n\n"); + printf(" Available tests for stream sockets:\n"); + for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i) + printf(" %u: %s\n", i, test_stream_tbl[i].desc); + printf("\n Available tests for datagram sockets:\n"); + for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i) + printf(" %u: %s\n", i, test_dgram_tbl[i].desc); } -/* - * printf-like function for outputting to STDOUT_FILENO. - */ static void output(const char *format, ...) { - char buf[128]; + char buf[LOGMSG_SIZE]; va_list ap; va_start(ap, format); if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "output: vsnprintf failed"); + err(EXIT_FAILURE, "output: vsnprintf failed"); write(STDOUT_FILENO, buf, strlen(buf)); va_end(ap); } -/* - * printf-like function for logging, also outputs message for errno. - */ static void logmsg(const char *format, ...) { - char buf[128]; + char buf[LOGMSG_SIZE]; va_list ap; int errno_save; - errno_save = errno; /* Save errno. */ - + errno_save = errno; va_start(ap, format); if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "logmsg: vsnprintf failed"); + err(EXIT_FAILURE, "logmsg: vsnprintf failed"); if (errno_save == 0) - output("%s: %s\n", myname, buf); + output("%s: %s\n", proc_name, buf); else - output("%s: %s: %s\n", myname, buf, strerror(errno_save)); + output("%s: %s: %s\n", proc_name, buf, strerror(errno_save)); va_end(ap); + errno = errno_save; +} + +static void +vlogmsgx(const char *format, va_list ap) +{ + char buf[LOGMSG_SIZE]; + + if (vsnprintf(buf, sizeof(buf), format, ap) < 0) + err(EXIT_FAILURE, "logmsgx: vsnprintf failed"); + output("%s: %s\n", proc_name, buf); - errno = errno_save; /* Restore errno. */ } -/* - * printf-like function for logging, do not output message for errno. - */ static void logmsgx(const char *format, ...) { - char buf[128]; va_list ap; va_start(ap, format); - if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EX_SOFTWARE, "logmsgx: vsnprintf failed"); - output("%s: %s\n", myname, buf); + vlogmsgx(format, ap); va_end(ap); } -/* - * Run tests from testno1 to testno2. - */ +static void +dbgmsg(const char *format, ...) +{ + va_list ap; + + if (debug) { + va_start(ap, format); + vlogmsgx(format, ap); + va_end(ap); + } +} + static int -run_tests(u_int testno1, u_int testno2) +run_tests(int type, u_int testno1) { - const struct test_func *test_func; - u_int i, nfailed1; + const struct test_func *tf; + u_int i, testno2, failed_num; - output("Running tests for %s sockets:\n", sock_type_str); - test_func = (sock_type == SOCK_STREAM ? - test_stream_tbl : test_dgram_tbl) + testno1; + sock_type = type; + if (type == SOCK_STREAM) { + sock_type_str = "SOCK_STREAM"; + tf = test_stream_tbl; + i = TEST_STREAM_TBL_SIZE - 1; + } else { + sock_type_str = "SOCK_DGRAM"; + tf = test_dgram_tbl; + i = TEST_DGRAM_TBL_SIZE - 1; + } + if (testno1 == 0) { + testno1 = 1; + testno2 = i; + } else + testno2 = testno1; - nfailed1 = 0; - for (i = testno1; i <= testno2; ++test_func, ++i) { - output(" %s\n", test_func->desc); - switch (test_func->func()) { + output("Running tests for %s sockets:\n", sock_type_str); + failed_num = 0; + for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) { + output(" %u: %s\n", i, tf->desc); + switch (tf->func()) { case -1: - ++nfailed1; + ++failed_num; break; case -2: - logmsgx("some system error occurred, exiting"); + logmsgx("some system error or timeout occurred"); return (-1); } } - nfailed += nfailed1; + if (failed_num != 0) + failed_flag = true; if (testno1 != testno2) { - if (nfailed1 == 0) - output("-- all tests were passed!\n"); + if (failed_num == 0) + output("-- all tests passed!\n"); else - output("-- %u test%s failed!\n", nfailed1, - nfailed1 == 1 ? "" : "s"); + output("-- %u test%s failed!\n", + failed_num, failed_num == 1 ? "" : "s"); } else { - if (nfailed == 0) - output("-- test was passed!\n"); + if (failed_num == 0) + output("-- test passed!\n"); else output("-- test failed!\n"); } @@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2) return (0); } -/* ARGSUSED */ -static void -sig_alrm(int signo __unused) +static int +init(void) { - siglongjmp(env_alrm, 1); + struct sigaction sigact; + size_t idx; + int rv; + + proc_name = "SERVER"; + + sigact.sa_handler = SIG_IGN; + sigact.sa_flags = 0; + sigemptyset(&sigact.sa_mask); + if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) { + logmsg("init: sigaction"); + return (-1); + } + + if (ipc_msg.buf_size == 0) + ipc_msg.buf_send = ipc_msg.buf_recv = NULL; + else { + ipc_msg.buf_send = malloc(ipc_msg.buf_size); + ipc_msg.buf_recv = malloc(ipc_msg.buf_size); + if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) { + logmsg("init: malloc"); + return (-1); + } + for (idx = 0; idx < ipc_msg.buf_size; ++idx) + ipc_msg.buf_send[idx] = (char)idx; + } + + proc_cred.uid = getuid(); + proc_cred.euid = geteuid(); + proc_cred.gid = getgid(); + proc_cred.egid = getegid(); + proc_cred.gid_num = getgroups(0, (gid_t *)NULL); + if (proc_cred.gid_num < 0) { + logmsg("init: getgroups"); + return (-1); + } + proc_cred.gid_arr = malloc(proc_cred.gid_num * + sizeof(*proc_cred.gid_arr)); + if (proc_cred.gid_arr == NULL) { + logmsg("init: malloc"); + return (-1); + } + if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) { + logmsg("init: getgroups"); + return (-1); + } + + memset(&serv_addr_sun, 0, sizeof(serv_addr_sun)); + rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path), + "%s/%s", work_dir, proc_name); + if (rv < 0) { + logmsg("init: snprintf"); + return (-1); + } + if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) { + logmsgx("init: not enough space for socket pathname"); + return (-1); + } + serv_addr_sun.sun_family = PF_LOCAL; + serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun); + + return (0); +} + +static int +client_fork(void) +{ + int fd1, fd2; + + if (pipe(sync_fd[SYNC_SERVER]) < 0 || + pipe(sync_fd[SYNC_CLIENT]) < 0) { + logmsg("client_fork: pipe"); + return (-1); + } + client_pid = fork(); + if (client_pid == (pid_t)-1) { + logmsg("client_fork: fork"); + return (-1); + } + if (client_pid == 0) { + proc_name = "CLIENT"; + server_flag = false; + fd1 = sync_fd[SYNC_SERVER][SYNC_RECV]; + fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND]; + } else { + fd1 = sync_fd[SYNC_SERVER][SYNC_SEND]; + fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV]; + } + if (close(fd1) < 0 || close(fd2) < 0) { + logmsg("client_fork: close"); + return (-1); + } + return (client_pid != 0); } -/* - * Initialize signals handlers. - */ static void -sig_init(void) +client_exit(int rv) +{ + if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 || + close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) { + logmsg("client_exit: close"); + rv = -1; + } + rv = rv == 0 ? EXIT_SUCCESS : -rv; + dbgmsg("exit: code %d", rv); + _exit(rv); +} + +static int +client_wait(void) { - struct sigaction sa; + int status; + pid_t pid; + + dbgmsg("waiting for client"); + + if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 || + close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) { + logmsg("client_wait: close"); + return (-1); + } + + pid = waitpid(client_pid, &status, 0); + if (pid == (pid_t)-1) { + logmsg("client_wait: waitpid"); + return (-1); + } - sa.sa_handler = SIG_IGN; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) - err(EX_OSERR, "sigaction(SIGPIPE)"); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != EXIT_SUCCESS) { + logmsgx("client exit status is %d", + WEXITSTATUS(status)); + return (-WEXITSTATUS(status)); + } + } else { + if (WIFSIGNALED(status)) + logmsgx("abnormal termination of client, signal %d%s", + WTERMSIG(status), WCOREDUMP(status) ? + " (core file generated)" : ""); + else + logmsgx("termination of client, unknown status"); + return (-1); + } - sa.sa_handler = sig_alrm; - if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0) - err(EX_OSERR, "sigaction(SIGALRM)"); + return (0); } int main(int argc, char *argv[]) { const char *errstr; - int opt, dgramflag, streamflag; - u_int testno1, testno2; - - dgramflag = streamflag = 0; - while ((opt = getopt(argc, argv, "dht:z")) != -1) + u_int testno, zvalue; + int opt, rv; + bool dgram_flag, stream_flag; + + ipc_msg.buf_size = IPC_MSG_SIZE_DEF; + ipc_msg.msg_num = IPC_MSG_NUM_DEF; + dgram_flag = stream_flag = false; + while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1) switch (opt) { case 'd': - debug = 1; + debug = true; break; case 'h': - usage(0); - return (EX_OK); + usage(true); + return (EXIT_SUCCESS); + case 'n': + ipc_msg.msg_num = strtonum(optarg, 1, + IPC_MSG_NUM_MAX, &errstr); + if (errstr != NULL) + errx(EXIT_FAILURE, "option -n: number is %s", + errstr); + break; + case 's': + ipc_msg.buf_size = strtonum(optarg, 0, + IPC_MSG_SIZE_MAX, &errstr); + if (errstr != NULL) + errx(EXIT_FAILURE, "option -s: number is %s", + errstr); + break; case 't': if (strcmp(optarg, "stream") == 0) - streamflag = 1; + stream_flag = true; else if (strcmp(optarg, "dgram") == 0) - dgramflag = 1; + dgram_flag = true; else - errx(EX_USAGE, "wrong socket type in -t option"); + errx(EXIT_FAILURE, "option -t: " + "wrong socket type"); break; case 'z': - no_control_data = 1; + zvalue = strtonum(optarg, 0, 3, &errstr); + if (errstr != NULL) + errx(EXIT_FAILURE, "option -z: number is %s", + errstr); + if (zvalue & 0x1) + send_data_flag = false; + if (zvalue & 0x2) + send_array_flag = false; break; - case '?': default: - usage(1); - return (EX_USAGE); + usage(false); + return (EXIT_FAILURE); } if (optind < argc) { if (optind + 1 != argc) - errx(EX_USAGE, "too many arguments"); - testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr); + errx(EXIT_FAILURE, "too many arguments"); + testno = strtonum(argv[optind], 0, UINT_MAX, &errstr); if (errstr != NULL) - errx(EX_USAGE, "wrong test number: %s", errstr); + errx(EXIT_FAILURE, "test number is %s", errstr); + if (stream_flag && testno >= TEST_STREAM_TBL_SIZE) + errx(EXIT_FAILURE, "given test %u for stream " + "sockets does not exist", testno); + if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE) + errx(EXIT_FAILURE, "given test %u for datagram " + "sockets does not exist", testno); } else - testno1 = 0; + testno = 0; - if (dgramflag == 0 && streamflag == 0) - dgramflag = streamflag = 1; - - if (dgramflag && streamflag && testno1 != 0) - errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets"); - - if (streamflag) { - if (testno1 > TEST_STREAM_NO_MAX) - errx(EX_USAGE, "given test %u for stream sockets does not exist", - testno1); - } else { - if (testno1 > TEST_DGRAM_NO_MAX) - errx(EX_USAGE, "given test %u for datagram sockets does not exist", - testno1); - } - - my_uid = getuid(); - my_euid = geteuid(); - my_gid = getgid(); - my_egid = getegid(); - switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) { - case -1: - err(EX_SOFTWARE, "getgroups"); - /* NOTREACHED */ - case 0: - errx(EX_OSERR, "getgroups returned 0 groups"); + if (!dgram_flag && !stream_flag) { + if (testno != 0) + errx(EXIT_FAILURE, "particular test number " + "can be used with the -t option only"); + dgram_flag = stream_flag = true; } - sig_init(); - - if (mkdtemp(tempdir) == NULL) - err(EX_OSERR, "mkdtemp"); + if (mkdtemp(work_dir) == NULL) + err(EXIT_FAILURE, "mkdtemp(%s)", work_dir); - if (streamflag) { - sock_type = SOCK_STREAM; - sock_type_str = "SOCK_STREAM"; - if (testno1 == 0) { - testno1 = 1; - testno2 = TEST_STREAM_NO_MAX; - } else - testno2 = testno1; - if (run_tests(testno1, testno2) < 0) - goto failed; - testno1 = 0; - } + rv = EXIT_FAILURE; + if (init() < 0) + goto done; - if (dgramflag) { - sock_type = SOCK_DGRAM; - sock_type_str = "SOCK_DGRAM"; - if (testno1 == 0) { - testno1 = 1; - testno2 = TEST_DGRAM_NO_MAX; - } else - testno2 = testno1; - if (run_tests(testno1, testno2) < 0) - goto failed; - } + if (stream_flag) + if (run_tests(SOCK_STREAM, testno) < 0) + goto done; + if (dgram_flag) + if (run_tests(SOCK_DGRAM, testno) < 0) + goto done; - if (rmdir(tempdir) < 0) { - logmsg("rmdir(%s)", tempdir); - return (EX_OSERR); + rv = EXIT_SUCCESS; +done: + if (rmdir(work_dir) < 0) { + logmsg("rmdir(%s)", work_dir); + rv = EXIT_FAILURE; } + return (failed_flag ? EXIT_FAILURE : rv); +} - return (nfailed ? EX_OSERR : EX_OK); +static int +socket_close(int fd) +{ + int rv; -failed: - if (rmdir(tempdir) < 0) - logmsg("rmdir(%s)", tempdir); - return (EX_OSERR); + rv = 0; + if (close(fd) < 0) { + logmsg("socket_close: close"); + rv = -1; + } + if (server_flag && fd == serv_sock_fd) + if (unlink(serv_addr_sun.sun_path) < 0) { + logmsg("socket_close: unlink(%s)", + serv_addr_sun.sun_path); + rv = -1; + } + return (rv); } -/* - * Create PF_LOCAL socket, if sock_path is not equal to NULL, then - * bind() it. Return socket address in addr. Return file descriptor - * or -1 if some error occurred. - */ static int -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr) +socket_create(void) { - int rv, fd; + struct timeval tv; + int fd; - if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) { - logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str); + fd = socket(PF_LOCAL, sock_type, 0); + if (fd < 0) { + logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str); return (-1); } + if (server_flag) + serv_sock_fd = fd; + + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 || + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { + logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)"); + goto failed; + } - if (sock_path != NULL) { - if ((rv = snprintf(sock_path, sock_path_len, "%s/%s", - tempdir, myname)) < 0) { - logmsg("create_socket: snprintf failed"); - goto failed; - } - if ((size_t)rv >= sock_path_len) { - logmsgx("create_socket: too long path name for given buffer"); - goto failed; - } - - memset(addr, 0, sizeof(*addr)); - addr->sun_family = AF_LOCAL; - if (strlen(sock_path) >= sizeof(addr->sun_path)) { - logmsgx("create_socket: too long path name (>= %lu) for local domain socket", - (u_long)sizeof(addr->sun_path)); + if (server_flag) { + if (bind(fd, (struct sockaddr *)&serv_addr_sun, + serv_addr_sun.sun_len) < 0) { + logmsg("socket_create: bind(%s)", + serv_addr_sun.sun_path); goto failed; } - strcpy(addr->sun_path, sock_path); + if (sock_type == SOCK_STREAM) { + int val; - if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) { - logmsg("create_socket: bind(%s)", sock_path); - goto failed; + if (listen(fd, LISTENQ) < 0) { + logmsg("socket_create: listen"); + goto failed; + } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + logmsg("socket_create: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) { + logmsg("socket_create: fcntl(F_SETFL)"); + goto failed; + } } } @@ -468,1163 +688,1282 @@ create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr) failed: if (close(fd) < 0) - logmsg("create_socket: close"); + logmsg("socket_create: close"); + if (server_flag) + if (unlink(serv_addr_sun.sun_path) < 0) + logmsg("socket_close: unlink(%s)", + serv_addr_sun.sun_path); return (-1); } -/* - * Call create_socket() for server listening socket. - * Return socket descriptor or -1 if some error occurred. - */ static int -create_server_socket(void) +socket_connect(int fd) { - return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr)); + dbgmsg("connect"); + + if (connect(fd, (struct sockaddr *)&serv_addr_sun, + serv_addr_sun.sun_len) < 0) { + logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path); + return (-1); + } + return (0); } -/* - * Create unbound socket. - */ static int -create_unbound_socket(void) +sync_recv(void) { - return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL)); + ssize_t ssize; + int fd; + char buf; + + dbgmsg("sync: wait"); + + fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV]; + + ssize = read(fd, &buf, 1); + if (ssize < 0) { + logmsg("sync_recv: read"); + return (-1); + } + if (ssize < 1) { + logmsgx("sync_recv: read %zd of 1 byte", ssize); + return (-1); + } + + dbgmsg("sync: received"); + + return (0); } -/* - * Close socket descriptor, if sock_path is not equal to NULL, - * then unlink the given path. - */ static int -close_socket(const char *sock_path, int fd) +sync_send(void) { - int error = 0; + ssize_t ssize; + int fd; - if (close(fd) < 0) { - logmsg("close_socket: close"); - error = -1; + dbgmsg("sync: send"); + + fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND]; + + ssize = write(fd, "", 1); + if (ssize < 0) { + logmsg("sync_send: write"); + return (-1); } - if (sock_path != NULL) - if (unlink(sock_path) < 0) { - logmsg("close_socket: unlink(%s)", sock_path); - error = -1; - } - return (error); + if (ssize < 1) { + logmsgx("sync_send: sent %zd of 1 byte", ssize); + return (-1); + } + + return (0); } -/* - * Connect to server (socket address in servaddr). - */ static int -connect_server(int fd) +message_send(int fd, const struct msghdr *msghdr) { - dbgmsg(("connecting to %s", serv_sock_path)); - - /* - * If PF_LOCAL listening socket's queue is full, then connect() - * returns ECONNREFUSED immediately, do not need timeout. - */ - if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { - logmsg("connect_server: connect(%s)", serv_sock_path); + const struct cmsghdr *cmsghdr; + size_t size; + ssize_t ssize; + + size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0; + dbgmsg("send: data size %zu", size); + dbgmsg("send: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); + cmsghdr = CMSG_FIRSTHDR(msghdr); + if (cmsghdr != NULL) + dbgmsg("send: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + + ssize = sendmsg(fd, msghdr, 0); + if (ssize < 0) { + logmsg("message_send: sendmsg"); + return (-1); + } + if ((size_t)ssize != size) { + logmsgx("message_send: sendmsg: sent %zd of %zu bytes", + ssize, size); return (-1); } + if (!send_data_flag) + if (sync_send() < 0) + return (-1); + return (0); } -/* - * sendmsg() with timeout. - */ static int -sendmsg_timeout(int fd, struct msghdr *msg, size_t n) +message_sendn(int fd, struct msghdr *msghdr) { - ssize_t nsent; - - dbgmsg(("sending %lu bytes", (u_long)n)); + u_int i; - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path); - return (-1); + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); + if (message_send(fd, msghdr) < 0) + return (-1); } + return (0); +} - (void)alarm(TIMEOUT); - - nsent = sendmsg(fd, msg, 0); +static int +message_recv(int fd, struct msghdr *msghdr) +{ + const struct cmsghdr *cmsghdr; + size_t size; + ssize_t ssize; - (void)alarm(0); + if (!send_data_flag) + if (sync_recv() < 0) + return (-1); - if (nsent < 0) { - logmsg("sendmsg_timeout: sendmsg"); + size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0; + ssize = recvmsg(fd, msghdr, MSG_WAITALL); + if (ssize < 0) { + logmsg("message_recv: recvmsg"); + return (-1); + } + if ((size_t)ssize != size) { + logmsgx("message_recv: recvmsg: received %zd of %zu bytes", + ssize, size); return (-1); } - if ((size_t)nsent != n) { - logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes", - (long)nsent, (u_long)n); + dbgmsg("recv: data size %zd", ssize); + dbgmsg("recv: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); + cmsghdr = CMSG_FIRSTHDR(msghdr); + if (cmsghdr != NULL) + dbgmsg("recv: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + + if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) { + logmsgx("message_recv: received message has wrong content"); return (-1); } return (0); } -/* - * accept() with timeout. - */ static int -accept_timeout(int listenfd) +socket_accept(int listenfd) { - int fd; - - dbgmsg(("accepting connection")); - - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("accept_timeout: cannot accept connection (timeout)"); + fd_set rset; + struct timeval tv; + int fd, rv, val; + + dbgmsg("accept"); + + FD_ZERO(&rset); + FD_SET(listenfd, &rset); + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv); + if (rv < 0) { + logmsg("socket_accept: select"); + return (-1); + } + if (rv == 0) { + logmsgx("socket_accept: select timeout"); return (-1); } - - (void)alarm(TIMEOUT); fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL); - - (void)alarm(0); - if (fd < 0) { - logmsg("accept_timeout: accept"); + logmsg("socket_accept: accept"); return (-1); } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + logmsg("socket_accept: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) { + logmsg("socket_accept: fcntl(F_SETFL)"); + goto failed; + } + return (fd); + +failed: + if (close(fd) < 0) + logmsg("socket_accept: close"); + return (-1); } -/* - * recvmsg() with timeout. - */ static int -recvmsg_timeout(int fd, struct msghdr *msg, size_t n) +check_msghdr(const struct msghdr *msghdr, size_t size) { - ssize_t nread; - - dbgmsg(("receiving %lu bytes", (u_long)n)); - - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("recvmsg_timeout: cannot receive message (timeout)"); + if (msghdr->msg_flags & MSG_TRUNC) { + logmsgx("msghdr.msg_flags has MSG_TRUNC"); return (-1); } - - (void)alarm(TIMEOUT); - - nread = recvmsg(fd, msg, MSG_WAITALL); - - (void)alarm(0); - - if (nread < 0) { - logmsg("recvmsg_timeout: recvmsg"); + if (msghdr->msg_flags & MSG_CTRUNC) { + logmsgx("msghdr.msg_flags has MSG_CTRUNC"); return (-1); } - - if ((size_t)nread != n) { - logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes", - (long)nread, (u_long)n); + if (msghdr->msg_controllen < size) { + logmsgx("msghdr.msg_controllen %u < %zu", + (u_int)msghdr->msg_controllen, size); + return (-1); + } + if (msghdr->msg_controllen > 0 && size == 0) { + logmsgx("msghdr.msg_controllen %u > 0", + (u_int)msghdr->msg_controllen); return (-1); } + return (0); +} +static int +check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size) +{ + if (cmsghdr == NULL) { + logmsgx("cmsghdr is NULL"); + return (-1); + } + if (cmsghdr->cmsg_level != SOL_SOCKET) { + logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET", + cmsghdr->cmsg_level); + return (-1); + } + if (cmsghdr->cmsg_type != type) { + logmsgx("cmsghdr.cmsg_type %d != %d", + cmsghdr->cmsg_type, type); + return (-1); + } + if (cmsghdr->cmsg_len != CMSG_LEN(size)) { + logmsgx("cmsghdr.cmsg_len %u != %zu", + (u_int)cmsghdr->cmsg_len, CMSG_LEN(size)); + return (-1); + } return (0); } -/* - * Wait for synchronization message (1 byte) with timeout. - */ static int -sync_recv(int fd) +check_groups(const char *gid_arr_str, const gid_t *gid_arr, + const char *gid_num_str, int gid_num, bool all_gids) { - ssize_t nread; - char buf; + int i; - dbgmsg(("waiting for sync message")); + for (i = 0; i < gid_num; ++i) + dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]); - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sync_recv: cannot receive sync message (timeout)"); + if (all_gids) { + if (gid_num != proc_cred.gid_num) { + logmsgx("%s %d != %d", gid_num_str, gid_num, + proc_cred.gid_num); + return (-1); + } + } else { + if (gid_num > proc_cred.gid_num) { + logmsgx("%s %d > %d", gid_num_str, gid_num, + proc_cred.gid_num); + return (-1); + } + } + if (memcmp(gid_arr, proc_cred.gid_arr, + gid_num * sizeof(*gid_arr)) != 0) { + logmsgx("%s content is wrong", gid_arr_str); + for (i = 0; i < gid_num; ++i) + if (gid_arr[i] != proc_cred.gid_arr[i]) { + logmsgx("%s[%d] %lu != %lu", + gid_arr_str, i, (u_long)gid_arr[i], + (u_long)proc_cred.gid_arr[i]); + break; + } return (-1); } + return (0); +} - (void)alarm(TIMEOUT); - - nread = read(fd, &buf, 1); +static int +check_xucred(const struct xucred *xucred, socklen_t len) +{ + if (len != sizeof(*xucred)) { + logmsgx("option value size %zu != %zu", + (size_t)len, sizeof(*xucred)); + return (-1); + } - (void)alarm(0); + dbgmsg("xucred.cr_version %u", xucred->cr_version); + dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid); + dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups); - if (nread < 0) { - logmsg("sync_recv: read"); + if (xucred->cr_version != XUCRED_VERSION) { + logmsgx("xucred.cr_version %u != %d", + xucred->cr_version, XUCRED_VERSION); return (-1); } - - if (nread != 1) { - logmsgx("sync_recv: read: short read: %ld of 1 byte", - (long)nread); + if (xucred->cr_uid != proc_cred.euid) { + logmsgx("xucred.cr_uid %lu != %lu (EUID)", + (u_long)xucred->cr_uid, (u_long)proc_cred.euid); return (-1); } - + if (xucred->cr_ngroups == 0) { + logmsgx("xucred.cr_ngroups == 0"); + return (-1); + } + if (xucred->cr_ngroups < 0) { + logmsgx("xucred.cr_ngroups < 0"); + return (-1); + } + if (xucred->cr_ngroups > XU_NGROUPS) { + logmsgx("xucred.cr_ngroups %hu > %u (max)", + xucred->cr_ngroups, XU_NGROUPS); + return (-1); + } + if (xucred->cr_groups[0] != proc_cred.egid) { + logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)", + (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid); + return (-1); + } + if (check_groups("xucred.cr_groups", xucred->cr_groups, + "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0) + return (-1); return (0); } -/* - * Send synchronization message (1 byte) with timeout. - */ static int -sync_send(int fd) +check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr) { - ssize_t nsent; - - dbgmsg(("sending sync message")); + const struct cmsgcred *cmsgcred; - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("sync_send: cannot send sync message (timeout)"); + if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0) return (-1); - } - - (void)alarm(TIMEOUT); - nsent = write(fd, "", 1); + cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr); - (void)alarm(0); + dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid); + dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid); + dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid); + dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid); + dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups); - if (nsent < 0) { - logmsg("sync_send: write"); + if (cmsgcred->cmcred_pid != client_pid) { + logmsgx("cmsgcred.cmcred_pid %ld != %ld", + (long)cmsgcred->cmcred_pid, (long)client_pid); return (-1); } - - if (nsent != 1) { - logmsgx("sync_send: write: short write: %ld of 1 byte", - (long)nsent); + if (cmsgcred->cmcred_uid != proc_cred.uid) { + logmsgx("cmsgcred.cmcred_uid %lu != %lu", + (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid); return (-1); } - + if (cmsgcred->cmcred_euid != proc_cred.euid) { + logmsgx("cmsgcred.cmcred_euid %lu != %lu", + (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid); + return (-1); + } + if (cmsgcred->cmcred_gid != proc_cred.gid) { + logmsgx("cmsgcred.cmcred_gid %lu != %lu", + (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid); + return (-1); + } + if (cmsgcred->cmcred_ngroups == 0) { + logmsgx("cmsgcred.cmcred_ngroups == 0"); + return (-1); + } + if (cmsgcred->cmcred_ngroups < 0) { + logmsgx("cmsgcred.cmcred_ngroups %d < 0", + cmsgcred->cmcred_ngroups); + return (-1); + } + if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) { + logmsgx("cmsgcred.cmcred_ngroups %d > %d", + cmsgcred->cmcred_ngroups, CMGROUP_MAX); + return (-1); + } + if (cmsgcred->cmcred_groups[0] != proc_cred.egid) { + logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)", + (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid); + return (-1); + } + if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups, + "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0) + return (-1); return (0); } -/* - * waitpid() for client with timeout. - */ static int -wait_client(void) +check_scm_creds_sockcred(struct cmsghdr *cmsghdr) { - int status; - pid_t pid; + const struct sockcred *sockcred; - if (sigsetjmp(env_alrm, 1) != 0) { - logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)", - (long)client_pid); + if (check_cmsghdr(cmsghdr, SCM_CREDS, + SOCKCREDSIZE(proc_cred.gid_num)) < 0) return (-1); - } - (void)alarm(TIMEOUT); - - pid = waitpid(client_pid, &status, 0); + sockcred = (struct sockcred *)CMSG_DATA(cmsghdr); - (void)alarm(0); + dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid); + dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid); + dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid); + dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid); + dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups); - if (pid == (pid_t)-1) { - logmsg("wait_client: waitpid"); + if (sockcred->sc_uid != proc_cred.uid) { + logmsgx("sockcred.sc_uid %lu != %lu", + (u_long)sockcred->sc_uid, (u_long)proc_cred.uid); return (-1); } - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - logmsgx("wait_client: exit status of client PID %ld is %d", - (long)client_pid, WEXITSTATUS(status)); - return (-1); - } - } else { - if (WIFSIGNALED(status)) - logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s", - (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : ""); - else - logmsgx("wait_client: termination of client PID %ld, unknown status", - (long)client_pid); + if (sockcred->sc_euid != proc_cred.euid) { + logmsgx("sockcred.sc_euid %lu != %lu", + (u_long)sockcred->sc_euid, (u_long)proc_cred.euid); return (-1); } - + if (sockcred->sc_gid != proc_cred.gid) { + logmsgx("sockcred.sc_gid %lu != %lu", + (u_long)sockcred->sc_gid, (u_long)proc_cred.gid); + return (-1); + } + if (sockcred->sc_egid != proc_cred.egid) { + logmsgx("sockcred.sc_egid %lu != %lu", + (u_long)sockcred->sc_egid, (u_long)proc_cred.egid); + return (-1); + } + if (sockcred->sc_ngroups == 0) { + logmsgx("sockcred.sc_ngroups == 0"); + return (-1); + } + if (sockcred->sc_ngroups < 0) { + logmsgx("sockcred.sc_ngroups %d < 0", + sockcred->sc_ngroups); + return (-1); + } + if (sockcred->sc_ngroups != proc_cred.gid_num) { + logmsgx("sockcred.sc_ngroups %d != %u", + sockcred->sc_ngroups, proc_cred.gid_num); + return (-1); + } + if (check_groups("sockcred.sc_groups", sockcred->sc_groups, + "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0) + return (-1); return (0); } -/* - * Check if n supplementary GIDs in gids are correct. (my_gids + 1) - * has (my_ngids - 1) supplementary GIDs of current process. - */ static int -check_groups(const gid_t *gids, int n) +check_scm_timestamp(struct cmsghdr *cmsghdr) { - char match[NGROUPS_MAX] = { 0 }; - int error, i, j; + const struct timeval *timeval; - if (n != my_ngids - 1) { - logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)", - n, my_ngids - 1); - error = -1; - } else - error = 0; - for (i = 0; i < n; ++i) { - for (j = 1; j < my_ngids; ++j) { - if (gids[i] == my_gids[j]) { - if (match[j]) { - logmsgx("duplicated GID %lu", - (u_long)gids[i]); - error = -1; - } else - match[j] = 1; - break; - } - } - if (j == my_ngids) { - logmsgx("unexpected GID %lu", (u_long)gids[i]); - error = -1; - } - } - for (j = 1; j < my_ngids; ++j) - if (match[j] == 0) { - logmsgx("did not receive supplementary GID %u", my_gids[j]); - error = -1; - } - return (error); -} + if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0) + return (-1); -/* - * Send n messages with data and control message with SCM_CREDS type - * to server and exit. - */ -static void -t_cmsgcred_client(u_int n) -{ - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct cmsgcred))]; - } control_un; - struct msghdr msg; - struct iovec iov[1]; - struct cmsghdr *cmptr; - int fd; - u_int i; + timeval = (struct timeval *)CMSG_DATA(cmsghdr); - assert(n == 1 || n == 2); + dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX, + (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec); - if ((fd = create_unbound_socket()) < 0) - goto failed; + return (0); +} - if (connect_server(fd) < 0) - goto failed_close; +static int +check_scm_bintime(struct cmsghdr *cmsghdr) +{ + const struct bintime *bintime; + + if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0) + return (-1); - iov[0].iov_base = ipc_message; - iov[0].iov_len = IPC_MESSAGE_SIZE; + bintime = (struct bintime *)CMSG_DATA(cmsghdr); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = control_un.control; - msg.msg_controllen = no_control_data ? - sizeof(struct cmsghdr) : sizeof(control_un.control); - msg.msg_flags = 0; + dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64, + (intmax_t)bintime->sec, bintime->frac); - cmptr = CMSG_FIRSTHDR(&msg); - cmptr->cmsg_len = CMSG_LEN(no_control_data ? - 0 : sizeof(struct cmsgcred)); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_CREDS; + return (0); +} - for (i = 0; i < n; ++i) { - dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; +static void +msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data) +{ + msghdr->msg_name = NULL; + msghdr->msg_namelen = 0; + if (send_data_flag) { + iov->iov_base = server_flag ? + ipc_msg.buf_recv : ipc_msg.buf_send; + iov->iov_len = ipc_msg.buf_size; + msghdr->msg_iov = iov; + msghdr->msg_iovlen = 1; + } else { + msghdr->msg_iov = NULL; + msghdr->msg_iovlen = 0; } + msghdr->msg_control = cmsg_data; + msghdr->msg_flags = 0; +} - if (close_socket((const char *)NULL, fd) < 0) - goto failed; +static void +msghdr_init_server(struct msghdr *msghdr, struct iovec *iov, + void *cmsg_data, size_t cmsg_size) +{ + msghdr_init_generic(msghdr, iov, cmsg_data); + msghdr->msg_controllen = cmsg_size; + dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ? + msghdr->msg_iov->iov_len : (size_t)0); + dbgmsg("init: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); +} - _exit(0); +static void +msghdr_init_client(struct msghdr *msghdr, struct iovec *iov, + void *cmsg_data, size_t cmsg_size, int type, size_t arr_size) +{ + struct cmsghdr *cmsghdr; + + msghdr_init_generic(msghdr, iov, cmsg_data); + if (cmsg_data != NULL) { + msghdr->msg_controllen = send_array_flag ? + cmsg_size : CMSG_SPACE(0); + cmsghdr = CMSG_FIRSTHDR(msghdr); + cmsghdr->cmsg_level = SOL_SOCKET; + cmsghdr->cmsg_type = type; + cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0); + } else + msghdr->msg_controllen = 0; +} -failed_close: - (void)close_socket((const char *)NULL, fd); +static int +t_generic(int (*client_func)(int), int (*server_func)(int)) +{ + int fd, rv, rv_client; -failed: - _exit(1); + switch (client_fork()) { + case 0: + fd = socket_create(); + if (fd < 0) + rv = -2; + else { + rv = client_func(fd); + if (socket_close(fd) < 0) + rv = -2; + } + client_exit(rv); + break; + case 1: + fd = socket_create(); + if (fd < 0) + rv = -2; + else { + rv = server_func(fd); + rv_client = client_wait(); + if (rv == 0 || (rv == -2 && rv_client != 0)) + rv = rv_client; + if (socket_close(fd) < 0) + rv = -2; + } + break; + default: + rv = -2; + } + return (rv); } -/* - * Receive two messages with data and control message with SCM_CREDS - * type followed by struct cmsgcred{} from client. fd1 is a listen - * socket for stream sockets or simply socket for datagram sockets. - */ static int -t_cmsgcred_server(int fd1) +t_cmsgcred_client(int fd) { - char buf[IPC_MESSAGE_SIZE]; - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE]; - } control_un; - struct msghdr msg; + struct msghdr msghdr; struct iovec iov[1]; - struct cmsghdr *cmptr; - const struct cmsgcred *cmcredptr; - socklen_t controllen; - int error, error2, fd2; - u_int i; - - if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) - return (-2); - } else - fd2 = fd1; - - error = 0; + void *cmsg_data; + size_t cmsg_size; + int rv; - controllen = sizeof(control_un.control); - - for (i = 0; i < 2; ++i) { - iov[0].iov_base = buf; - iov[0].iov_len = sizeof(buf); + if (sync_recv() < 0) + return (-2); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = control_un.control; - msg.msg_controllen = controllen; - msg.msg_flags = 0; + rv = -2; - controllen = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; + } + msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_CREDS, sizeof(struct cmsgcred)); - if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) - goto failed; + if (socket_connect(fd) < 0) + goto done; - if (msg.msg_flags & MSG_CTRUNC) { - logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on", - i); - goto next_error; - } + if (message_sendn(fd, &msghdr) < 0) + goto done; - if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))", - i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); - goto next_error; - } + rv = 0; +done: + free(cmsg_data); + return (rv); +} - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { - logmsgx("CMSG_FIRSTHDR is NULL"); - goto next_error; - } +static int +t_cmsgcred_server(int fd1) +{ + struct msghdr msghdr; + struct iovec iov[1]; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t cmsg_size; + u_int i; + int fd2, rv; - dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); + if (sync_send() < 0) + return (-2); - if (cmptr->cmsg_level != SOL_SOCKET) { - logmsgx("#%u cmsg_level %d != SOL_SOCKET", i, - cmptr->cmsg_level); - goto next_error; - } + fd2 = -1; + rv = -2; - if (cmptr->cmsg_type != SCM_CREDS) { - logmsgx("#%u cmsg_type %d != SCM_CREDS", i, - cmptr->cmsg_type); - goto next_error; - } + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; + } - if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) { - logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))", - i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred))); - goto next_error; - } + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; - cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr); + rv = -1; + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); - error2 = 0; - if (cmcredptr->cmcred_pid != client_pid) { - logmsgx("#%u cmcred_pid %ld != %ld (PID of client)", - i, (long)cmcredptr->cmcred_pid, (long)client_pid); - error2 = 1; - } - if (cmcredptr->cmcred_uid != my_uid) { - logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)", - i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid); - error2 = 1; - } - if (cmcredptr->cmcred_euid != my_euid) { - logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)", - i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid); - error2 = 1; - } - if (cmcredptr->cmcred_gid != my_gid) { - logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)", - i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid); - error2 = 1; - } - if (cmcredptr->cmcred_ngroups == 0) { - logmsgx("#%u cmcred_ngroups = 0, this is wrong", i); - error2 = 1; - } else { - if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) { - logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)", - i, cmcredptr->cmcred_ngroups, NGROUPS_MAX); - error2 = 1; - } else if (cmcredptr->cmcred_ngroups < 0) { - logmsgx("#%u cmcred_ngroups %d < 0", - i, cmcredptr->cmcred_ngroups); - error2 = 1; - } else { - dbgmsg(("#%u cmcred_ngroups = %d", i, - cmcredptr->cmcred_ngroups)); - if (cmcredptr->cmcred_groups[0] != my_egid) { - logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)", - i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid); - error2 = 1; - } - if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) { - logmsgx("#%u cmcred_groups has wrong GIDs", i); - error2 = 1; - } - } + msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; } - if (error2) - goto next_error; - - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("#%u control data has extra header", i); - goto next_error; - } + if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; - continue; -next_error: - error = -1; + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_creds_cmsgcred(cmsghdr) < 0) + break; } - - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); - -failed: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); + if (i > ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); } static int t_cmsgcred(void) { - int error, fd; + return (t_generic(t_cmsgcred_client, t_cmsgcred_server)); +} - if ((fd = create_server_socket()) < 0) - return (-2); +static int +t_sockcred_client(int type, int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + int rv; - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + if (sync_recv() < 0) + return (-2); - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + rv = -2; - if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); - t_cmsgcred_client(2); - } + msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0); - if ((error = t_cmsgcred_server(fd)) == -2) { - (void)wait_client(); - goto failed; - } + if (socket_connect(fd) < 0) + goto done; - if (wait_client() < 0) - goto failed; + if (type == 2) + if (sync_recv() < 0) + goto done; - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); - } - return (error); + if (message_sendn(fd, &msghdr) < 0) + goto done; -failed: - if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = 0; +done: + return (rv); } -/* - * Send two messages with data to server and exit. - */ -static void -t_sockcred_client(int type) +static int +t_sockcred_server(int type, int fd1) { - struct msghdr msg; + struct msghdr msghdr; struct iovec iov[1]; - int fd; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t cmsg_size; u_int i; + int fd2, rv, val; - assert(type == 0 || type == 1); + fd2 = -1; + rv = -2; - if ((fd = create_unbound_socket()) < 0) - goto failed; - - if (connect_server(fd) < 0) - goto failed_close; + cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; + } - if (type == 1) - if (sync_recv(fd) < 0) - goto failed_close; + if (type == 1) { + dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } + } - iov[0].iov_base = ipc_message; - iov[0].iov_len = IPC_MESSAGE_SIZE; + if (sync_send() < 0) + goto done; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; - for (i = 0; i < 2; ++i) - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; + if (type == 2) { + dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } + if (sync_send() < 0) + goto done; + } - if (close_socket((const char *)NULL, fd) < 0) - goto failed; + rv = -1; + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); - _exit(0); + msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } -failed_close: - (void)close_socket((const char *)NULL, fd); + if (i > 1 && sock_type == SOCK_STREAM) { + if (check_msghdr(&msghdr, 0) < 0) + break; + } else { + if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; -failed: - _exit(1); + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_creds_sockcred(cmsghdr) < 0) + break; + } + } + if (i > ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); } -/* - * Receive one message with data and control message with SCM_CREDS - * type followed by struct sockcred{} and if n is not equal 1, then - * receive another one message with data. fd1 is a listen socket for - * stream sockets or simply socket for datagram sockets. If type is - * 1, then set LOCAL_CREDS option for accepted stream socket. - */ static int -t_sockcred_server(int type, int fd1, u_int n) +t_sockcred_1(void) { - char buf[IPC_MESSAGE_SIZE]; - union { - struct cmsghdr cm; - char control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE]; - } control_un; - struct msghdr msg; - struct iovec iov[1]; - struct cmsghdr *cmptr; - const struct sockcred *sockcred; - int error, error2, fd2, optval; u_int i; + int fd, rv, rv_client; - assert(n == 1 || n == 2); - assert(type == 0 || type == 1); - - if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) - return (-2); - if (type == 1) { - optval = 1; - if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { - logmsg("setsockopt(LOCAL_CREDS) for accepted socket"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; + switch (client_fork()) { + case 0: + for (i = 1; i <= 2; ++i) { + dbgmsg("client #%u", i); + fd = socket_create(); + if (fd < 0) + rv = -2; + else { + rv = t_sockcred_client(1, fd); + if (socket_close(fd) < 0) + rv = -2; } - if (sync_send(fd2) < 0) - goto failed; + if (rv != 0) + break; } - } else - fd2 = fd1; + client_exit(rv); + break; + case 1: + fd = socket_create(); + if (fd < 0) + rv = -2; + else { + rv = t_sockcred_server(1, fd); + if (rv == 0) + rv = t_sockcred_server(3, fd); + rv_client = client_wait(); + if (rv == 0 || (rv == -2 && rv_client != 0)) + rv = rv_client; + if (socket_close(fd) < 0) + rv = -2; + } + break; + default: + rv = -2; + } - error = 0; + return (rv); +} - for (i = 0; i < n; ++i) { - iov[0].iov_base = buf; - iov[0].iov_len = sizeof buf; +static int +t_sockcred_2_client(int fd) +{ + return (t_sockcred_client(2, fd)); +} - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof control_un.control; - msg.msg_flags = 0; +static int +t_sockcred_2_server(int fd) +{ + return (t_sockcred_server(2, fd)); +} - if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0) - goto failed; +static int +t_sockcred_2(void) +{ + return (t_generic(t_sockcred_2_client, t_sockcred_2_server)); +} - if (msg.msg_flags & MSG_CTRUNC) { - logmsgx("control data was truncated, MSG_CTRUNC flag is on"); - goto next_error; - } +static int +t_cmsgcred_sockcred_server(int fd1) +{ + struct msghdr msghdr; + struct iovec iov[1]; + struct cmsghdr *cmsghdr; + void *cmsg_data, *cmsg1_data, *cmsg2_data; + size_t cmsg_size, cmsg1_size, cmsg2_size; + u_int i; + int fd2, rv, val; - if (i != 0 && sock_type == SOCK_STREAM) { - if (msg.msg_controllen != 0) { - logmsgx("second message has control data, this is wrong for stream sockets"); - goto next_error; - } - dbgmsg(("#%u msg_controllen = %u", i, - (u_int)msg.msg_controllen)); - continue; - } + fd2 = -1; + rv = -2; - if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))", - i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); - goto next_error; - } + cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num)); + cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg1_data = malloc(cmsg1_size); + cmsg2_data = malloc(cmsg2_size); + if (cmsg1_data == NULL || cmsg2_data == NULL) { + logmsg("malloc"); + goto done; + } - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { - logmsgx("CMSG_FIRSTHDR is NULL"); - goto next_error; - } + dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } - dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i, - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); + if (sync_send() < 0) + goto done; - if (cmptr->cmsg_level != SOL_SOCKET) { - logmsgx("#%u cmsg_level %d != SOL_SOCKET", i, - cmptr->cmsg_level); - goto next_error; - } + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; - if (cmptr->cmsg_type != SCM_CREDS) { - logmsgx("#%u cmsg_type %d != SCM_CREDS", i, - cmptr->cmsg_type); - goto next_error; - } + cmsg_data = cmsg1_data; + cmsg_size = cmsg1_size; + rv = -1; + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); - if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) { - logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))", - i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1))); - goto next_error; + msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; } - sockcred = (const struct sockcred *)CMSG_DATA(cmptr); + if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; - error2 = 0; - if (sockcred->sc_uid != my_uid) { - logmsgx("#%u sc_uid %lu != %lu (UID of current process)", - i, (u_long)sockcred->sc_uid, (u_long)my_uid); - error2 = 1; - } - if (sockcred->sc_euid != my_euid) { - logmsgx("#%u sc_euid %lu != %lu (EUID of current process)", - i, (u_long)sockcred->sc_euid, (u_long)my_euid); - error2 = 1; - } - if (sockcred->sc_gid != my_gid) { - logmsgx("#%u sc_gid %lu != %lu (GID of current process)", - i, (u_long)sockcred->sc_gid, (u_long)my_gid); - error2 = 1; - } - if (sockcred->sc_egid != my_egid) { - logmsgx("#%u sc_egid %lu != %lu (EGID of current process)", - i, (u_long)sockcred->sc_gid, (u_long)my_egid); - error2 = 1; - } - if (sockcred->sc_ngroups > NGROUPS_MAX) { - logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)", - i, sockcred->sc_ngroups, NGROUPS_MAX); - error2 = 1; - } else if (sockcred->sc_ngroups < 0) { - logmsgx("#%u sc_ngroups %d < 0", - i, sockcred->sc_ngroups); - error2 = 1; + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (i == 1 || sock_type == SOCK_DGRAM) { + if (check_scm_creds_sockcred(cmsghdr) < 0) + break; } else { - dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups)); - if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) { - logmsgx("#%u sc_groups has wrong GIDs", i); - error2 = 1; - } - } - - if (error2) - goto next_error; - - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("#%u control data has extra header, this is wrong", - i); - goto next_error; + if (check_scm_creds_cmsgcred(cmsghdr) < 0) + break; } - continue; -next_error: - error = -1; + cmsg_data = cmsg2_data; + cmsg_size = cmsg2_size; } - -done_close: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); - -failed: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); + if (i > ipc_msg.msg_num) + rv = 0; +done: + free(cmsg1_data); + free(cmsg2_data); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); } static int -t_sockcred(int type) +t_cmsgcred_sockcred(void) { - int error, fd, optval; + return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server)); +} - assert(type == 0 || type == 1); +static int +t_timeval_client(int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + void *cmsg_data; + size_t cmsg_size; + int rv; - if ((fd = create_server_socket()) < 0) + if (sync_recv() < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; - if (type == 0) { - optval = 1; - if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { - logmsg("setsockopt(LOCAL_CREDS) for %s socket", - sock_type == SOCK_STREAM ? "stream listening" : "datagram"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; - } + cmsg_size = CMSG_SPACE(sizeof(struct timeval)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; } + msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_TIMESTAMP, sizeof(struct timeval)); - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } + if (socket_connect(fd) < 0) + goto done; - if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); - t_sockcred_client(type); - } + if (message_sendn(fd, &msghdr) < 0) + goto done; - if ((error = t_sockcred_server(type, fd, 2)) == -2) { - (void)wait_client(); - goto failed; - } + rv = 0; +done: + free(cmsg_data); + return (rv); +} - if (wait_client() < 0) - goto failed; +static int +t_timeval_server(int fd1) +{ + struct msghdr msghdr; + struct iovec iov[1]; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t cmsg_size; + u_int i; + int fd2, rv; -done_close: - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); + if (sync_send() < 0) return (-2); + + fd2 = -1; + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct timeval)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; } - return (error); -failed: - if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); -} + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; -static int -t_sockcred_stream1(void) -{ - return (t_sockcred(0)); -} + rv = -1; + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); -static int -t_sockcred_stream2(void) -{ - return (t_sockcred(1)); + msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_timestamp(cmsghdr) < 0) + break; + } + if (i > ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); } static int -t_sockcred_dgram(void) +t_timeval(void) { - return (t_sockcred(0)); + return (t_generic(t_timeval_client, t_timeval_server)); } static int -t_cmsgcred_sockcred(void) +t_bintime_client(int fd) { - int error, fd, optval; + struct msghdr msghdr; + struct iovec iov[1]; + void *cmsg_data; + size_t cmsg_size; + int rv; - if ((fd = create_server_socket()) < 0) + if (sync_recv() < 0) return (-2); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + rv = -2; - optval = 1; - if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) { - logmsg("setsockopt(LOCAL_CREDS) for %s socket", - sock_type == SOCK_STREAM ? "stream listening" : "datagram"); - if (errno == ENOPROTOOPT) { - error = -1; - goto done_close; - } - goto failed; - } - - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; - } - - if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); - t_cmsgcred_client(1); - } - - if ((error = t_sockcred_server(0, fd, 1)) == -2) { - (void)wait_client(); - goto failed; + cmsg_size = CMSG_SPACE(sizeof(struct bintime)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; } + msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_BINTIME, sizeof(struct bintime)); - if (wait_client() < 0) - goto failed; + if (socket_connect(fd) < 0) + goto done; -done_close: - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); - return (-2); - } - return (error); + if (message_sendn(fd, &msghdr) < 0) + goto done; -failed: - if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + rv = 0; +done: + free(cmsg_data); + return (rv); } -/* - * Send one message with data and control message with SCM_TIMESTAMP - * type to server and exit. - */ -static void -t_timestamp_client(void) +static int +t_bintime_server(int fd1) { - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct timeval))]; - } control_un; - struct msghdr msg; + struct msghdr msghdr; struct iovec iov[1]; - struct cmsghdr *cmptr; - int fd; - - if ((fd = create_unbound_socket()) < 0) - goto failed; - - if (connect_server(fd) < 0) - goto failed_close; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t cmsg_size; + u_int i; + int fd2, rv; - iov[0].iov_base = ipc_message; - iov[0].iov_len = IPC_MESSAGE_SIZE; + if (sync_send() < 0) + return (-2); - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = control_un.control; - msg.msg_controllen = no_control_data ? - sizeof(struct cmsghdr) :sizeof control_un.control; - msg.msg_flags = 0; + fd2 = -1; + rv = -2; - cmptr = CMSG_FIRSTHDR(&msg); - cmptr->cmsg_len = CMSG_LEN(no_control_data ? - 0 : sizeof(struct timeval)); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_TIMESTAMP; + cmsg_size = CMSG_SPACE(sizeof(struct bintime)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); + goto done; + } - dbgmsg(("msg_controllen = %u, cmsg_len = %u", - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; - if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0) - goto failed_close; + rv = -1; + for (i = 1; i <= ipc_msg.msg_num; ++i) { + dbgmsg("message #%u", i); - if (close_socket((const char *)NULL, fd) < 0) - goto failed; + msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } - _exit(0); + if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; -failed_close: - (void)close_socket((const char *)NULL, fd); + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_bintime(cmsghdr) < 0) + break; + } + if (i > ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); +} -failed: - _exit(1); +static int +t_bintime(void) +{ + return (t_generic(t_bintime_client, t_bintime_server)); } -/* - * Receive one message with data and control message with SCM_TIMESTAMP - * type followed by struct timeval{} from client. - */ static int -t_timestamp_server(int fd1) +t_cmsg_len_client(int fd) { - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE]; - } control_un; - char buf[IPC_MESSAGE_SIZE]; - int error, fd2; - struct msghdr msg; + struct msghdr msghdr; struct iovec iov[1]; - struct cmsghdr *cmptr; - const struct timeval *timeval; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t size, cmsg_size; + socklen_t socklen; + int rv; - if (sock_type == SOCK_STREAM) { - if ((fd2 = accept_timeout(fd1)) < 0) - return (-2); - } else - fd2 = fd1; - - iov[0].iov_base = buf; - iov[0].iov_len = sizeof buf; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof control_un.control; - msg.msg_flags = 0; - - if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0) - goto failed; + if (sync_recv() < 0) + return (-2); - error = -1; + rv = -2; - if (msg.msg_flags & MSG_CTRUNC) { - logmsgx("control data was truncated, MSG_CTRUNC flag is on"); + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + logmsg("malloc"); goto done; } + msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_CREDS, sizeof(struct cmsgcred)); + cmsghdr = CMSG_FIRSTHDR(&msghdr); - if (msg.msg_controllen < sizeof(struct cmsghdr)) { - logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))", - (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr)); + if (socket_connect(fd) < 0) goto done; - } - if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) { - logmsgx("CMSG_FIRSTHDR is NULL"); - goto done; + size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0; + rv = -1; + for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) { + cmsghdr->cmsg_len = socklen; + dbgmsg("send: data size %zu", size); + dbgmsg("send: msghdr.msg_controllen %u", + (u_int)msghdr.msg_controllen); + dbgmsg("send: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + if (sendmsg(fd, &msghdr, 0) < 0) + continue; + logmsgx("sent message with cmsghdr.cmsg_len %u < %u", + (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0)); + break; } + if (socklen == CMSG_LEN(0)) + rv = 0; - dbgmsg(("msg_controllen = %u, cmsg_len = %u", - (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len)); - - if (cmptr->cmsg_level != SOL_SOCKET) { - logmsgx("cmsg_level %d != SOL_SOCKET", cmptr->cmsg_level); + if (sync_send() < 0) { + rv = -2; goto done; } +done: + free(cmsg_data); + return (rv); +} - if (cmptr->cmsg_type != SCM_TIMESTAMP) { - logmsgx("cmsg_type %d != SCM_TIMESTAMP", cmptr->cmsg_type); - goto done; - } +static int +t_cmsg_len_server(int fd1) +{ + int fd2, rv; - if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) { - logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))", - (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval))); - goto done; - } + if (sync_send() < 0) + return (-2); - timeval = (const struct timeval *)CMSG_DATA(cmptr); + rv = -2; - dbgmsg(("timeval tv_sec %jd, tv_usec %jd", - (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec)); + if (sock_type == SOCK_STREAM) { + fd2 = socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; - if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) { - logmsgx("control data has extra header"); + if (sync_recv() < 0) goto done; - } - - error = 0; + rv = 0; done: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) { - logmsg("close"); - return (-2); - } - return (error); + if (sock_type == SOCK_STREAM && fd2 >= 0) + if (socket_close(fd2) < 0) + rv = -2; + return (rv); +} -failed: - if (sock_type == SOCK_STREAM) - if (close(fd2) < 0) - logmsg("close"); - return (-2); +static int +t_cmsg_len(void) +{ + return (t_generic(t_cmsg_len_client, t_cmsg_len_server)); } static int -t_timestamp(void) +t_peercred_client(int fd) { - int error, fd; + struct xucred xucred; + socklen_t len; - if ((fd = create_server_socket()) < 0) - return (-2); + if (sync_recv() < 0) + return (-1); - if (sock_type == SOCK_STREAM) - if (listen(fd, LISTENQ) < 0) { - logmsg("listen"); - goto failed; - } + if (socket_connect(fd) < 0) + return (-1); - if ((client_pid = fork()) == (pid_t)-1) { - logmsg("fork"); - goto failed; + len = sizeof(xucred); + if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + logmsg("getsockopt(LOCAL_PEERCRED)"); + return (-1); } - if (client_pid == 0) { - myname = "CLIENT"; - if (close_socket((const char *)NULL, fd) < 0) - _exit(1); - t_timestamp_client(); - } + if (check_xucred(&xucred, len) < 0) + return (-1); - if ((error = t_timestamp_server(fd)) == -2) { - (void)wait_client(); - goto failed; - } + return (0); +} - if (wait_client() < 0) - goto failed; +static int +t_peercred_server(int fd1) +{ + struct xucred xucred; + socklen_t len; + int fd2, rv; - if (close_socket(serv_sock_path, fd) < 0) { - logmsgx("close_socket failed"); + if (sync_send() < 0) return (-2); + + fd2 = socket_accept(fd1); + if (fd2 < 0) + return (-2); + + len = sizeof(xucred); + if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + logmsg("getsockopt(LOCAL_PEERCRED)"); + rv = -2; + goto done; } - return (error); -failed: - if (close_socket(serv_sock_path, fd) < 0) - logmsgx("close_socket failed"); - return (-2); + if (check_xucred(&xucred, len) < 0) { + rv = -1; + goto done; + } + + rv = 0; +done: + if (socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +static int +t_peercred(void) +{ + return (t_generic(t_peercred_client, t_peercred_server)); } diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.t b/tools/regression/sockets/unix_cmsg/unix_cmsg.t index c8dea15..70d9277 100644 --- a/tools/regression/sockets/unix_cmsg/unix_cmsg.t +++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.t @@ -11,47 +11,78 @@ n=0 run() { - result=`${cmd} -t $2 $3 $4 2>&1` - if [ $? -eq 0 ]; then - echo -n "ok $1" - else - echo -n "not ok $1" + result=`${cmd} -t $2 $3 ${5%% *} 2>&1` + if [ $? -ne 0 ]; then + echo -n "not " fi - echo " -" $5 + echo "ok $1 - $4 ${5#* }" echo ${result} | grep -E "SERVER|CLIENT" | while read line; do echo "# ${line}" done } -echo "1..15" +echo "1..47" -for desc in \ - "Sending, receiving cmsgcred" \ - "Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \ - "Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \ - "Sending cmsgcred, receiving sockcred # TODO" \ - "Sending, receiving timestamp" +for t1 in \ + "1 Sending, receiving cmsgcred" \ + "4 Sending cmsgcred, receiving sockcred" \ + "5 Sending, receiving timeval" \ + "6 Sending, receiving bintime" \ + "7 Check cmsghdr.cmsg_len" do - n=`expr ${n} + 1` - run ${n} stream "" ${n} "STREAM ${desc}" + for t2 in \ + "0 " \ + "1 (no data)" \ + "2 (no array)" \ + "3 (no data, array)" + do + n=$((n + 1)) + run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }" + done done -i=0 -for desc in \ - "Sending, receiving cmsgcred" \ - "Receiving sockcred # TODO" \ - "Sending cmsgcred, receiving sockcred # TODO" \ - "Sending, receiving timestamp" +for t1 in \ + "2 Receiving sockcred (listening socket)" \ + "3 Receiving sockcred (accepted socket)" do - i=`expr ${i} + 1` - n=`expr ${n} + 1` - run ${n} dgram "" ${i} "DGRAM ${desc}" + for t2 in \ + "0 " \ + "1 (no data)" + do + n=$((n + 1)) + run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }" + done done -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)" -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO" -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)" +n=$((n + 1)) +run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option" -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)" -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO" -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)" +for t1 in \ + "1 Sending, receiving cmsgcred" \ + "3 Sending cmsgcred, receiving sockcred" \ + "4 Sending, receiving timeval" \ + "5 Sending, receiving bintime" \ + "6 Check cmsghdr.cmsg_len" +do + for t2 in \ + "0 " \ + "1 (no data)" \ + "2 (no array)" \ + "3 (no data, array)" + do + n=$((n + 1)) + run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }" + done +done + +for t1 in \ + "2 Receiving sockcred" +do + for t2 in \ + "0 " \ + "1 (no data)" + do + n=$((n + 1)) + run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }" + done +done |