From 1fab62b1e1d2e8667e1c7762d1d2b96bee4c8f0d Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Mon, 5 Dec 2016 17:21:04 +0000 Subject: [PATCH] Refactor the regression test code by splitting huge monolithic C file into smaller pieces that are hopefully easier to understand and extend. This is to pave the ground for adding few more socket timestamp formats that I am working on here. No functional changes (I hope). --- tools/regression/sockets/unix_cmsg/Makefile | 4 + .../regression/sockets/unix_cmsg/t_bintime.c | 156 ++ .../regression/sockets/unix_cmsg/t_bintime.h | 30 + .../regression/sockets/unix_cmsg/t_cmsg_len.c | 138 ++ .../regression/sockets/unix_cmsg/t_cmsg_len.h | 32 + .../regression/sockets/unix_cmsg/t_cmsgcred.c | 139 ++ .../regression/sockets/unix_cmsg/t_cmsgcred.h | 30 + .../sockets/unix_cmsg/t_cmsgcred_sockcred.c | 125 ++ .../sockets/unix_cmsg/t_cmsgcred_sockcred.h | 30 + .../regression/sockets/unix_cmsg/t_generic.c | 73 + .../regression/sockets/unix_cmsg/t_generic.h | 30 + .../regression/sockets/unix_cmsg/t_peercred.c | 153 ++ .../regression/sockets/unix_cmsg/t_peercred.h | 29 + .../regression/sockets/unix_cmsg/t_sockcred.c | 215 +++ .../regression/sockets/unix_cmsg/t_sockcred.h | 31 + .../regression/sockets/unix_cmsg/t_timeval.c | 155 ++ .../regression/sockets/unix_cmsg/t_timeval.h | 29 + .../regression/sockets/unix_cmsg/uc_common.c | 744 +++++++ .../regression/sockets/unix_cmsg/uc_common.h | 86 + .../regression/sockets/unix_cmsg/unix_cmsg.c | 1714 +---------------- 20 files changed, 2300 insertions(+), 1643 deletions(-) create mode 100644 tools/regression/sockets/unix_cmsg/t_bintime.c create mode 100644 tools/regression/sockets/unix_cmsg/t_bintime.h create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsg_len.c create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsg_len.h create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsgcred.c create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsgcred.h create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c create mode 100644 tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h create mode 100644 tools/regression/sockets/unix_cmsg/t_generic.c create mode 100644 tools/regression/sockets/unix_cmsg/t_generic.h create mode 100644 tools/regression/sockets/unix_cmsg/t_peercred.c create mode 100644 tools/regression/sockets/unix_cmsg/t_peercred.h create mode 100644 tools/regression/sockets/unix_cmsg/t_sockcred.c create mode 100644 tools/regression/sockets/unix_cmsg/t_sockcred.h create mode 100644 tools/regression/sockets/unix_cmsg/t_timeval.c create mode 100644 tools/regression/sockets/unix_cmsg/t_timeval.h create mode 100644 tools/regression/sockets/unix_cmsg/uc_common.c create mode 100644 tools/regression/sockets/unix_cmsg/uc_common.h diff --git a/tools/regression/sockets/unix_cmsg/Makefile b/tools/regression/sockets/unix_cmsg/Makefile index 9ccab6d2bd03..106798782941 100644 --- a/tools/regression/sockets/unix_cmsg/Makefile +++ b/tools/regression/sockets/unix_cmsg/Makefile @@ -1,6 +1,10 @@ # $FreeBSD$ PROG= unix_cmsg +SRCS= unix_cmsg.c t_bintime.h t_bintime.c uc_common.h uc_common.c \ + t_generic.h t_generic.c t_peercred.h t_peercred.c t_timeval.h \ + t_timeval.c t_cmsgcred.h t_cmsgcred.c t_sockcred.h t_sockcred.c \ + t_cmsgcred_sockcred.h t_cmsgcred_sockcred.c t_cmsg_len.h t_cmsg_len.c MAN= WARNS?= 3 diff --git a/tools/regression/sockets/unix_cmsg/t_bintime.c b/tools/regression/sockets/unix_cmsg/t_bintime.c new file mode 100644 index 000000000000..4c3941370bea --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_bintime.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "t_bintime.h" +#include "t_generic.h" +#include "uc_common.h" + +static int +check_scm_bintime(struct cmsghdr *cmsghdr) +{ + const struct bintime *bt; + + if (uc_check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0) + return (-1); + + bt = (struct bintime *)CMSG_DATA(cmsghdr); + + uc_dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64, + (intmax_t)bt->sec, bt->frac); + + return (0); +} + +static int +t_bintime_client(int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + void *cmsg_data; + size_t cmsg_size; + int rv; + + if (uc_sync_recv() < 0) + return (-2); + + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct bintime)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_BINTIME, sizeof(struct bintime)); + + if (uc_socket_connect(fd) < 0) + goto done; + + if (uc_message_sendn(fd, &msghdr) < 0) + goto done; + + rv = 0; +done: + free(cmsg_data); + return (rv); +} + +static int +t_bintime_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; + + if (uc_sync_send() < 0) + return (-2); + + fd2 = -1; + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct bintime)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + rv = -1; + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + + uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (uc_message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_bintime(cmsghdr) < 0) + break; + } + if (i > uc_cfg.ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_bintime(void) +{ + return (t_generic(t_bintime_client, t_bintime_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_bintime.h b/tools/regression/sockets/unix_cmsg/t_bintime.h new file mode 100644 index 000000000000..1fdfe1de7ec1 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_bintime.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_bintime(void); diff --git a/tools/regression/sockets/unix_cmsg/t_cmsg_len.c b/tools/regression/sockets/unix_cmsg/t_cmsg_len.c new file mode 100644 index 000000000000..3a88cebf1645 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsg_len.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_cmsg_len.h" + +#ifndef __LP64__ +static int +t_cmsg_len_client(int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + struct cmsghdr *cmsghdr; + void *cmsg_data; + size_t size, cmsg_size; + socklen_t socklen; + int rv; + + if (uc_sync_recv() < 0) + return (-2); + + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_CREDS, sizeof(struct cmsgcred)); + cmsghdr = CMSG_FIRSTHDR(&msghdr); + + if (uc_socket_connect(fd) < 0) + 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; + uc_dbgmsg("send: data size %zu", size); + uc_dbgmsg("send: msghdr.msg_controllen %u", + (u_int)msghdr.msg_controllen); + uc_dbgmsg("send: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + if (sendmsg(fd, &msghdr, 0) < 0) { + uc_dbgmsg("sendmsg(2) failed: %s; retrying", + strerror(errno)); + continue; + } + uc_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; + + if (uc_sync_send() < 0) { + rv = -2; + goto done; + } +done: + free(cmsg_data); + return (rv); +} + +static int +t_cmsg_len_server(int fd1) +{ + int fd2, rv; + + if (uc_sync_send() < 0) + return (-2); + + rv = -2; + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + if (uc_sync_recv() < 0) + goto done; + + rv = 0; +done: + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_cmsg_len(void) +{ + return (t_generic(t_cmsg_len_client, t_cmsg_len_server)); +} +#endif diff --git a/tools/regression/sockets/unix_cmsg/t_cmsg_len.h b/tools/regression/sockets/unix_cmsg/t_cmsg_len.h new file mode 100644 index 000000000000..5ec735144439 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsg_len.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __LP64__ +int t_cmsg_len(void); +#endif diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred.c b/tools/regression/sockets/unix_cmsg/t_cmsgcred.c new file mode 100644 index 000000000000..3e3653c15179 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_cmsgcred.h" + +int +t_cmsgcred_client(int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + void *cmsg_data; + size_t cmsg_size; + int rv; + + if (uc_sync_recv() < 0) + return (-2); + + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_CREDS, sizeof(struct cmsgcred)); + + if (uc_socket_connect(fd) < 0) + goto done; + + if (uc_message_sendn(fd, &msghdr) < 0) + goto done; + + rv = 0; +done: + free(cmsg_data); + return (rv); +} + +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; + + if (uc_sync_send() < 0) + return (-2); + + fd2 = -1; + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + rv = -1; + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + + uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (uc_message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (uc_check_scm_creds_cmsgcred(cmsghdr) < 0) + break; + } + if (i > uc_cfg.ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_cmsgcred(void) +{ + return (t_generic(t_cmsgcred_client, t_cmsgcred_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred.h b/tools/regression/sockets/unix_cmsg/t_cmsgcred.h new file mode 100644 index 000000000000..2aee226161df --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_cmsgcred_client(int fd); +int t_cmsgcred(void); diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c new file mode 100644 index 000000000000..72110b32af64 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_cmsgcred.h" +#include "t_cmsgcred_sockcred.h" + +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; + + fd2 = -1; + rv = -2; + + cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(uc_cfg.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) { + uc_logmsg("malloc"); + goto done; + } + + uc_dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + uc_logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } + + if (uc_sync_send() < 0) + goto done; + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + cmsg_data = cmsg1_data; + cmsg_size = cmsg1_size; + rv = -1; + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + + uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (uc_message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (i == 1 || uc_cfg.sock_type == SOCK_DGRAM) { + if (uc_check_scm_creds_sockcred(cmsghdr) < 0) + break; + } else { + if (uc_check_scm_creds_cmsgcred(cmsghdr) < 0) + break; + } + + cmsg_data = cmsg2_data; + cmsg_size = cmsg2_size; + } + if (i > uc_cfg.ipc_msg.msg_num) + rv = 0; +done: + free(cmsg1_data); + free(cmsg2_data); + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_cmsgcred_sockcred(void) +{ + return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h new file mode 100644 index 000000000000..df849c65afd7 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_cmsgcred_sockcred.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_cmsgcred_sockcred(void); diff --git a/tools/regression/sockets/unix_cmsg/t_generic.c b/tools/regression/sockets/unix_cmsg/t_generic.c new file mode 100644 index 000000000000..02a65255011d --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_generic.c @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" + +int +t_generic(int (*client_func)(int), int (*server_func)(int)) +{ + int fd, rv, rv_client; + + switch (uc_client_fork()) { + case 0: + fd = uc_socket_create(); + if (fd < 0) + rv = -2; + else { + rv = client_func(fd); + if (uc_socket_close(fd) < 0) + rv = -2; + } + uc_client_exit(rv); + break; + case 1: + fd = uc_socket_create(); + if (fd < 0) + rv = -2; + else { + rv = server_func(fd); + rv_client = uc_client_wait(); + if (rv == 0 || (rv == -2 && rv_client != 0)) + rv = rv_client; + if (uc_socket_close(fd) < 0) + rv = -2; + } + break; + default: + rv = -2; + } + return (rv); +} diff --git a/tools/regression/sockets/unix_cmsg/t_generic.h b/tools/regression/sockets/unix_cmsg/t_generic.h new file mode 100644 index 000000000000..a9b3d1be703c --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_generic.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_generic(int (*client_func)(int), int (*server_func)(int)); diff --git a/tools/regression/sockets/unix_cmsg/t_peercred.c b/tools/regression/sockets/unix_cmsg/t_peercred.c new file mode 100644 index 000000000000..9b6e14868fcb --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_peercred.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_peercred.h" + +static int +check_xucred(const struct xucred *xucred, socklen_t len) +{ + int rc; + + if (len != sizeof(*xucred)) { + uc_logmsgx("option value size %zu != %zu", + (size_t)len, sizeof(*xucred)); + return (-1); + } + + uc_dbgmsg("xucred.cr_version %u", xucred->cr_version); + uc_dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid); + uc_dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups); + + rc = 0; + + if (xucred->cr_version != XUCRED_VERSION) { + uc_logmsgx("xucred.cr_version %u != %d", + xucred->cr_version, XUCRED_VERSION); + rc = -1; + } + if (xucred->cr_uid != uc_cfg.proc_cred.euid) { + uc_logmsgx("xucred.cr_uid %lu != %lu (EUID)", + (u_long)xucred->cr_uid, (u_long)uc_cfg.proc_cred.euid); + rc = -1; + } + if (xucred->cr_ngroups == 0) { + uc_logmsgx("xucred.cr_ngroups == 0"); + rc = -1; + } + if (xucred->cr_ngroups < 0) { + uc_logmsgx("xucred.cr_ngroups < 0"); + rc = -1; + } + if (xucred->cr_ngroups > XU_NGROUPS) { + uc_logmsgx("xucred.cr_ngroups %hu > %u (max)", + xucred->cr_ngroups, XU_NGROUPS); + rc = -1; + } + if (xucred->cr_groups[0] != uc_cfg.proc_cred.egid) { + uc_logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)", + (u_long)xucred->cr_groups[0], (u_long)uc_cfg.proc_cred.egid); + rc = -1; + } + if (uc_check_groups("xucred.cr_groups", xucred->cr_groups, + "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0) + rc = -1; + return (rc); +} + +static int +t_peercred_client(int fd) +{ + struct xucred xucred; + socklen_t len; + + if (uc_sync_recv() < 0) + return (-1); + + if (uc_socket_connect(fd) < 0) + return (-1); + + len = sizeof(xucred); + if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + uc_logmsg("getsockopt(LOCAL_PEERCRED)"); + return (-1); + } + + if (check_xucred(&xucred, len) < 0) + return (-1); + + return (0); +} + +static int +t_peercred_server(int fd1) +{ + struct xucred xucred; + socklen_t len; + int fd2, rv; + + if (uc_sync_send() < 0) + return (-2); + + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + return (-2); + + len = sizeof(xucred); + if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + uc_logmsg("getsockopt(LOCAL_PEERCRED)"); + rv = -2; + goto done; + } + + if (check_xucred(&xucred, len) < 0) { + rv = -1; + goto done; + } + + rv = 0; +done: + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_peercred(void) +{ + return (t_generic(t_peercred_client, t_peercred_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_peercred.h b/tools/regression/sockets/unix_cmsg/t_peercred.h new file mode 100644 index 000000000000..1200734722ac --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_peercred.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_peercred(void); diff --git a/tools/regression/sockets/unix_cmsg/t_sockcred.c b/tools/regression/sockets/unix_cmsg/t_sockcred.c new file mode 100644 index 000000000000..45ac5bfe823c --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_sockcred.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_sockcred.h" + +static int +t_sockcred_client(int type, int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + int rv; + + if (uc_sync_recv() < 0) + return (-2); + + rv = -2; + + uc_msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0); + + if (uc_socket_connect(fd) < 0) + goto done; + + if (type == 2) + if (uc_sync_recv() < 0) + goto done; + + if (uc_message_sendn(fd, &msghdr) < 0) + goto done; + + rv = 0; +done: + return (rv); +} + +static int +t_sockcred_server(int type, 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, val; + + fd2 = -1; + rv = -2; + + cmsg_size = CMSG_SPACE(SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + + if (type == 1) { + uc_dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + uc_logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } + } + + if (uc_sync_send() < 0) + goto done; + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + if (type == 2) { + uc_dbgmsg("setting LOCAL_CREDS"); + val = 1; + if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { + uc_logmsg("setsockopt(LOCAL_CREDS)"); + goto done; + } + if (uc_sync_send() < 0) + goto done; + } + + rv = -1; + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + + uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (uc_message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (i > 1 && uc_cfg.sock_type == SOCK_STREAM) { + if (uc_check_msghdr(&msghdr, 0) < 0) + break; + } else { + if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (uc_check_scm_creds_sockcred(cmsghdr) < 0) + break; + } + } + if (i > uc_cfg.ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_sockcred_1(void) +{ + u_int i; + int fd, rv, rv_client; + + switch (uc_client_fork()) { + case 0: + for (i = 1; i <= 2; ++i) { + uc_dbgmsg("client #%u", i); + fd = uc_socket_create(); + if (fd < 0) + rv = -2; + else { + rv = t_sockcred_client(1, fd); + if (uc_socket_close(fd) < 0) + rv = -2; + } + if (rv != 0) + break; + } + uc_client_exit(rv); + break; + case 1: + fd = uc_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 = uc_client_wait(); + if (rv == 0 || (rv == -2 && rv_client != 0)) + rv = rv_client; + if (uc_socket_close(fd) < 0) + rv = -2; + } + break; + default: + rv = -2; + } + + return (rv); +} + +static int +t_sockcred_2_client(int fd) +{ + return (t_sockcred_client(2, fd)); +} + +static int +t_sockcred_2_server(int fd) +{ + return (t_sockcred_server(2, fd)); +} + +int +t_sockcred_2(void) +{ + return (t_generic(t_sockcred_2_client, t_sockcred_2_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_sockcred.h b/tools/regression/sockets/unix_cmsg/t_sockcred.h new file mode 100644 index 000000000000..c0c52b90fb65 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_sockcred.h @@ -0,0 +1,31 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_sockcred_1(void); +int t_sockcred_2(void); diff --git a/tools/regression/sockets/unix_cmsg/t_timeval.c b/tools/regression/sockets/unix_cmsg/t_timeval.c new file mode 100644 index 000000000000..4d4ab82cbcc0 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_timeval.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" +#include "t_generic.h" +#include "t_timeval.h" + +static int +check_scm_timestamp(struct cmsghdr *cmsghdr) +{ + const struct timeval *tv; + + if (uc_check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0) + return (-1); + + tv = (struct timeval *)CMSG_DATA(cmsghdr); + + uc_dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX, + (intmax_t)tv->tv_sec, (intmax_t)tv->tv_usec); + + return (0); +} + +static int +t_timeval_client(int fd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + void *cmsg_data; + size_t cmsg_size; + int rv; + + if (uc_sync_recv() < 0) + return (-2); + + rv = -2; + + cmsg_size = CMSG_SPACE(sizeof(struct timeval)); + cmsg_data = malloc(cmsg_size); + if (cmsg_data == NULL) { + uc_logmsg("malloc"); + goto done; + } + uc_msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size, + SCM_TIMESTAMP, sizeof(struct timeval)); + + if (uc_socket_connect(fd) < 0) + goto done; + + if (uc_message_sendn(fd, &msghdr) < 0) + goto done; + + rv = 0; +done: + free(cmsg_data); + return (rv); +} + +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; + + if (uc_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) { + uc_logmsg("malloc"); + goto done; + } + + if (uc_cfg.sock_type == SOCK_STREAM) { + fd2 = uc_socket_accept(fd1); + if (fd2 < 0) + goto done; + } else + fd2 = fd1; + + rv = -1; + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + + uc_msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); + if (uc_message_recv(fd2, &msghdr) < 0) { + rv = -2; + break; + } + + if (uc_check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) + break; + + cmsghdr = CMSG_FIRSTHDR(&msghdr); + if (check_scm_timestamp(cmsghdr) < 0) + break; + } + if (i > uc_cfg.ipc_msg.msg_num) + rv = 0; +done: + free(cmsg_data); + if (uc_cfg.sock_type == SOCK_STREAM && fd2 >= 0) + if (uc_socket_close(fd2) < 0) + rv = -2; + return (rv); +} + +int +t_timeval(void) +{ + return (t_generic(t_timeval_client, t_timeval_server)); +} diff --git a/tools/regression/sockets/unix_cmsg/t_timeval.h b/tools/regression/sockets/unix_cmsg/t_timeval.h new file mode 100644 index 000000000000..081b4c2f01a0 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/t_timeval.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int t_timeval(void); diff --git a/tools/regression/sockets/unix_cmsg/uc_common.c b/tools/regression/sockets/unix_cmsg/uc_common.c new file mode 100644 index 000000000000..0799df2e0d73 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/uc_common.c @@ -0,0 +1,744 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uc_common.h" + +#ifndef LISTENQ +# define LISTENQ 1 +#endif + +#ifndef TIMEOUT +# define TIMEOUT 2 +#endif + +#define SYNC_SERVER 0 +#define SYNC_CLIENT 1 +#define SYNC_RECV 0 +#define SYNC_SEND 1 + +#define LOGMSG_SIZE 128 + +void +uc_output(const char *format, ...) +{ + char buf[LOGMSG_SIZE]; + va_list ap; + + va_start(ap, format); + if (vsnprintf(buf, sizeof(buf), format, ap) < 0) + err(EXIT_FAILURE, "output: vsnprintf failed"); + write(STDOUT_FILENO, buf, strlen(buf)); + va_end(ap); +} + +void +uc_logmsg(const char *format, ...) +{ + char buf[LOGMSG_SIZE]; + va_list ap; + int errno_save; + + errno_save = errno; + va_start(ap, format); + if (vsnprintf(buf, sizeof(buf), format, ap) < 0) + err(EXIT_FAILURE, "logmsg: vsnprintf failed"); + if (errno_save == 0) + uc_output("%s: %s\n", uc_cfg.proc_name, buf); + else + uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf, + strerror(errno_save)); + va_end(ap); + errno = errno_save; +} + +void +uc_vlogmsgx(const char *format, va_list ap) +{ + char buf[LOGMSG_SIZE]; + + if (vsnprintf(buf, sizeof(buf), format, ap) < 0) + err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed"); + uc_output("%s: %s\n", uc_cfg.proc_name, buf); +} + +void +uc_logmsgx(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + uc_vlogmsgx(format, ap); + va_end(ap); +} + +void +uc_dbgmsg(const char *format, ...) +{ + va_list ap; + + if (uc_cfg.debug) { + va_start(ap, format); + uc_vlogmsgx(format, ap); + va_end(ap); + } +} + +int +uc_socket_create(void) +{ + struct timeval tv; + int fd; + + fd = socket(PF_LOCAL, uc_cfg.sock_type, 0); + if (fd < 0) { + uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str); + return (-1); + } + if (uc_cfg.server_flag) + uc_cfg.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) { + uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)"); + goto failed; + } + + if (uc_cfg.server_flag) { + if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun, + uc_cfg.serv_addr_sun.sun_len) < 0) { + uc_logmsg("socket_create: bind(%s)", + uc_cfg.serv_addr_sun.sun_path); + goto failed; + } + if (uc_cfg.sock_type == SOCK_STREAM) { + int val; + + if (listen(fd, LISTENQ) < 0) { + uc_logmsg("socket_create: listen"); + goto failed; + } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + uc_logmsg("socket_create: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) { + uc_logmsg("socket_create: fcntl(F_SETFL)"); + goto failed; + } + } + } + + return (fd); + +failed: + if (close(fd) < 0) + uc_logmsg("socket_create: close"); + if (uc_cfg.server_flag) + if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) + uc_logmsg("socket_close: unlink(%s)", + uc_cfg.serv_addr_sun.sun_path); + return (-1); +} + +int +uc_socket_close(int fd) +{ + int rv; + + rv = 0; + if (close(fd) < 0) { + uc_logmsg("socket_close: close"); + rv = -1; + } + if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd) + if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) { + uc_logmsg("socket_close: unlink(%s)", + uc_cfg.serv_addr_sun.sun_path); + rv = -1; + } + return (rv); +} + +int +uc_socket_connect(int fd) +{ + uc_dbgmsg("connect"); + + if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun, + uc_cfg.serv_addr_sun.sun_len) < 0) { + uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path); + return (-1); + } + return (0); +} + +int +uc_sync_recv(void) +{ + ssize_t ssize; + int fd; + char buf; + + uc_dbgmsg("sync: wait"); + + fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV]; + + ssize = read(fd, &buf, 1); + if (ssize < 0) { + uc_logmsg("sync_recv: read"); + return (-1); + } + if (ssize < 1) { + uc_logmsgx("sync_recv: read %zd of 1 byte", ssize); + return (-1); + } + + uc_dbgmsg("sync: received"); + + return (0); +} + +int +uc_sync_send(void) +{ + ssize_t ssize; + int fd; + + uc_dbgmsg("sync: send"); + + fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND]; + + ssize = write(fd, "", 1); + if (ssize < 0) { + uc_logmsg("uc_sync_send: write"); + return (-1); + } + if (ssize < 1) { + uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize); + return (-1); + } + + return (0); +} + +int +uc_message_send(int fd, const struct msghdr *msghdr) +{ + const struct cmsghdr *cmsghdr; + size_t size; + ssize_t ssize; + + size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0; + uc_dbgmsg("send: data size %zu", size); + uc_dbgmsg("send: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); + cmsghdr = CMSG_FIRSTHDR(msghdr); + if (cmsghdr != NULL) + uc_dbgmsg("send: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + + ssize = sendmsg(fd, msghdr, 0); + if (ssize < 0) { + uc_logmsg("message_send: sendmsg"); + return (-1); + } + if ((size_t)ssize != size) { + uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes", + ssize, size); + return (-1); + } + + if (!uc_cfg.send_data_flag) + if (uc_sync_send() < 0) + return (-1); + + return (0); +} + +int +uc_message_sendn(int fd, struct msghdr *msghdr) +{ + u_int i; + + for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) { + uc_dbgmsg("message #%u", i); + if (uc_message_send(fd, msghdr) < 0) + return (-1); + } + return (0); +} + +int +uc_message_recv(int fd, struct msghdr *msghdr) +{ + const struct cmsghdr *cmsghdr; + size_t size; + ssize_t ssize; + + if (!uc_cfg.send_data_flag) + if (uc_sync_recv() < 0) + return (-1); + + size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0; + ssize = recvmsg(fd, msghdr, MSG_WAITALL); + if (ssize < 0) { + uc_logmsg("message_recv: recvmsg"); + return (-1); + } + if ((size_t)ssize != size) { + uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes", + ssize, size); + return (-1); + } + + uc_dbgmsg("recv: data size %zd", ssize); + uc_dbgmsg("recv: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); + cmsghdr = CMSG_FIRSTHDR(msghdr); + if (cmsghdr != NULL) + uc_dbgmsg("recv: cmsghdr.cmsg_len %u", + (u_int)cmsghdr->cmsg_len); + + if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) { + uc_logmsgx("message_recv: received message has wrong content"); + return (-1); + } + + return (0); +} + +int +uc_socket_accept(int listenfd) +{ + fd_set rset; + struct timeval tv; + int fd, rv, val; + + uc_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) { + uc_logmsg("socket_accept: select"); + return (-1); + } + if (rv == 0) { + uc_logmsgx("socket_accept: select timeout"); + return (-1); + } + + fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL); + if (fd < 0) { + uc_logmsg("socket_accept: accept"); + return (-1); + } + + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + uc_logmsg("socket_accept: fcntl(F_GETFL)"); + goto failed; + } + if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) { + uc_logmsg("socket_accept: fcntl(F_SETFL)"); + goto failed; + } + + return (fd); + +failed: + if (close(fd) < 0) + uc_logmsg("socket_accept: close"); + return (-1); +} + +int +uc_check_msghdr(const struct msghdr *msghdr, size_t size) +{ + if (msghdr->msg_flags & MSG_TRUNC) { + uc_logmsgx("msghdr.msg_flags has MSG_TRUNC"); + return (-1); + } + if (msghdr->msg_flags & MSG_CTRUNC) { + uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC"); + return (-1); + } + if (msghdr->msg_controllen < size) { + uc_logmsgx("msghdr.msg_controllen %u < %zu", + (u_int)msghdr->msg_controllen, size); + return (-1); + } + if (msghdr->msg_controllen > 0 && size == 0) { + uc_logmsgx("msghdr.msg_controllen %u > 0", + (u_int)msghdr->msg_controllen); + return (-1); + } + return (0); +} + +int +uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size) +{ + if (cmsghdr == NULL) { + uc_logmsgx("cmsghdr is NULL"); + return (-1); + } + if (cmsghdr->cmsg_level != SOL_SOCKET) { + uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET", + cmsghdr->cmsg_level); + return (-1); + } + if (cmsghdr->cmsg_type != type) { + uc_logmsgx("cmsghdr.cmsg_type %d != %d", + cmsghdr->cmsg_type, type); + return (-1); + } + if (cmsghdr->cmsg_len != CMSG_LEN(size)) { + uc_logmsgx("cmsghdr.cmsg_len %u != %zu", + (u_int)cmsghdr->cmsg_len, CMSG_LEN(size)); + return (-1); + } + return (0); +} + +static void +uc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data) +{ + msghdr->msg_name = NULL; + msghdr->msg_namelen = 0; + if (uc_cfg.send_data_flag) { + iov->iov_base = uc_cfg.server_flag ? + uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send; + iov->iov_len = uc_cfg.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; +} + +void +uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov, + void *cmsg_data, size_t cmsg_size) +{ + uc_msghdr_init_generic(msghdr, iov, cmsg_data); + msghdr->msg_controllen = cmsg_size; + uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ? + msghdr->msg_iov->iov_len : (size_t)0); + uc_dbgmsg("init: msghdr.msg_controllen %u", + (u_int)msghdr->msg_controllen); +} + +void +uc_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; + + uc_msghdr_init_generic(msghdr, iov, cmsg_data); + if (cmsg_data != NULL) { + if (uc_cfg.send_array_flag) + uc_dbgmsg("sending an array"); + else + uc_dbgmsg("sending a scalar"); + msghdr->msg_controllen = uc_cfg.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(uc_cfg.send_array_flag ? arr_size : 0); + } else + msghdr->msg_controllen = 0; +} + +int +uc_client_fork(void) +{ + int fd1, fd2; + + if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 || + pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) { + uc_logmsg("client_fork: pipe"); + return (-1); + } + uc_cfg.client_pid = fork(); + if (uc_cfg.client_pid == (pid_t)-1) { + uc_logmsg("client_fork: fork"); + return (-1); + } + if (uc_cfg.client_pid == 0) { + uc_cfg.proc_name = "CLIENT"; + uc_cfg.server_flag = false; + fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]; + fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]; + } else { + fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]; + fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]; + } + if (close(fd1) < 0 || close(fd2) < 0) { + uc_logmsg("client_fork: close"); + return (-1); + } + return (uc_cfg.client_pid != 0); +} + +void +uc_client_exit(int rv) +{ + if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 || + close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) { + uc_logmsg("client_exit: close"); + rv = -1; + } + rv = rv == 0 ? EXIT_SUCCESS : -rv; + uc_dbgmsg("exit: code %d", rv); + _exit(rv); +} + +int +uc_client_wait(void) +{ + int status; + pid_t pid; + + uc_dbgmsg("waiting for client"); + + if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 || + close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) { + uc_logmsg("client_wait: close"); + return (-1); + } + + pid = waitpid(uc_cfg.client_pid, &status, 0); + if (pid == (pid_t)-1) { + uc_logmsg("client_wait: waitpid"); + return (-1); + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != EXIT_SUCCESS) { + uc_logmsgx("client exit status is %d", + WEXITSTATUS(status)); + return (-WEXITSTATUS(status)); + } + } else { + if (WIFSIGNALED(status)) + uc_logmsgx("abnormal termination of client, signal %d%s", + WTERMSIG(status), WCOREDUMP(status) ? + " (core file generated)" : ""); + else + uc_logmsgx("termination of client, unknown status"); + return (-1); + } + + return (0); +} + +int +uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr, + const char *gid_num_str, int gid_num, bool all_gids) +{ + int i; + + for (i = 0; i < gid_num; ++i) + uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]); + + if (all_gids) { + if (gid_num != uc_cfg.proc_cred.gid_num) { + uc_logmsgx("%s %d != %d", gid_num_str, gid_num, + uc_cfg.proc_cred.gid_num); + return (-1); + } + } else { + if (gid_num > uc_cfg.proc_cred.gid_num) { + uc_logmsgx("%s %d > %d", gid_num_str, gid_num, + uc_cfg.proc_cred.gid_num); + return (-1); + } + } + if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr, + gid_num * sizeof(*gid_arr)) != 0) { + uc_logmsgx("%s content is wrong", gid_arr_str); + for (i = 0; i < gid_num; ++i) + if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) { + uc_logmsgx("%s[%d] %lu != %lu", + gid_arr_str, i, (u_long)gid_arr[i], + (u_long)uc_cfg.proc_cred.gid_arr[i]); + break; + } + return (-1); + } + return (0); +} + +int +uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr) +{ + const struct cmsgcred *cmcred; + int rc; + + if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0) + return (-1); + + cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr); + + uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid); + uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid); + uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid); + uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid); + uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups); + + rc = 0; + + if (cmcred->cmcred_pid != uc_cfg.client_pid) { + uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld", + (long)cmcred->cmcred_pid, (long)uc_cfg.client_pid); + rc = -1; + } + if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) { + uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu", + (u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid); + rc = -1; + } + if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) { + uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu", + (u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid); + rc = -1; + } + if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) { + uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu", + (u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid); + rc = -1; + } + if (cmcred->cmcred_ngroups == 0) { + uc_logmsgx("cmsgcred.cmcred_ngroups == 0"); + rc = -1; + } + if (cmcred->cmcred_ngroups < 0) { + uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0", + cmcred->cmcred_ngroups); + rc = -1; + } + if (cmcred->cmcred_ngroups > CMGROUP_MAX) { + uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d", + cmcred->cmcred_ngroups, CMGROUP_MAX); + rc = -1; + } + if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) { + uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)", + (u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid); + rc = -1; + } + if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups, + "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0) + rc = -1; + return (rc); +} + +int +uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr) +{ + const struct sockcred *sc; + int rc; + + if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, + SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0) + return (-1); + + sc = (struct sockcred *)CMSG_DATA(cmsghdr); + + rc = 0; + + uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid); + uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid); + uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid); + uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid); + uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups); + + if (sc->sc_uid != uc_cfg.proc_cred.uid) { + uc_logmsgx("sockcred.sc_uid %lu != %lu", + (u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid); + rc = -1; + } + if (sc->sc_euid != uc_cfg.proc_cred.euid) { + uc_logmsgx("sockcred.sc_euid %lu != %lu", + (u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid); + rc = -1; + } + if (sc->sc_gid != uc_cfg.proc_cred.gid) { + uc_logmsgx("sockcred.sc_gid %lu != %lu", + (u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid); + rc = -1; + } + if (sc->sc_egid != uc_cfg.proc_cred.egid) { + uc_logmsgx("sockcred.sc_egid %lu != %lu", + (u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid); + rc = -1; + } + if (sc->sc_ngroups == 0) { + uc_logmsgx("sockcred.sc_ngroups == 0"); + rc = -1; + } + if (sc->sc_ngroups < 0) { + uc_logmsgx("sockcred.sc_ngroups %d < 0", + sc->sc_ngroups); + rc = -1; + } + if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) { + uc_logmsgx("sockcred.sc_ngroups %d != %u", + sc->sc_ngroups, uc_cfg.proc_cred.gid_num); + rc = -1; + } + if (uc_check_groups("sockcred.sc_groups", sc->sc_groups, + "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0) + rc = -1; + return (rc); +} diff --git a/tools/regression/sockets/unix_cmsg/uc_common.h b/tools/regression/sockets/unix_cmsg/uc_common.h new file mode 100644 index 000000000000..5e119cabc7e9 --- /dev/null +++ b/tools/regression/sockets/unix_cmsg/uc_common.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2005 Andrey Simonenko + * Copyright (c) 2016 Maksym Sobolyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +struct uc_cfg { + int sock_type; + const char *sock_type_str; + bool debug; + const char *proc_name; + int sync_fd[2][2]; + int serv_sock_fd; + bool server_flag; + bool send_data_flag; + struct sockaddr_un serv_addr_sun; + bool send_array_flag; + pid_t client_pid; + struct { + char *buf_send; + char *buf_recv; + size_t buf_size; + u_int msg_num; + } ipc_msg; + struct { + uid_t uid; + uid_t euid; + gid_t gid; + gid_t egid; + gid_t *gid_arr; + int gid_num; + } proc_cred; +}; + +extern struct uc_cfg uc_cfg; + +int uc_check_msghdr(const struct msghdr *msghdr, size_t size); +int uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size); +void uc_output(const char *format, ...) __printflike(1, 2); +void uc_logmsgx(const char *format, ...) __printflike(1, 2); +void uc_dbgmsg(const char *format, ...) __printflike(1, 2); +void uc_logmsg(const char *format, ...) __printflike(1, 2); +void uc_vlogmsgx(const char *format, va_list ap); +int uc_message_recv(int fd, struct msghdr *msghdr); +int uc_message_send(int fd, const struct msghdr *msghdr); +int uc_message_sendn(int fd, struct msghdr *msghdr); +void uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov, + void *cmsg_data, size_t cmsg_size); +void uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov, + void *cmsg_data, size_t cmsg_size, int type, size_t arr_size); +int uc_socket_create(void); +int uc_socket_accept(int listenfd); +int uc_socket_close(int fd); +int uc_socket_connect(int fd); +int uc_sync_recv(void); +int uc_sync_send(void); +int uc_client_fork(void); +void uc_client_exit(int rv); +int uc_client_wait(void); +int uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr, + const char *gid_num_str, int gid_num, bool all_gids); +int uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr); +int uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr); diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.c b/tools/regression/sockets/unix_cmsg/unix_cmsg.c index 7f6b45cf4270..7ce138b0e406 100644 --- a/tools/regression/sockets/unix_cmsg/unix_cmsg.c +++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.c @@ -27,31 +27,32 @@ #include __FBSDID("$FreeBSD$"); -#include -#include -#include -#include +#include +#include #include -#include #include -#include -#include #include -#include -#include #include -#include #include #include #include #include -#include #include #include #include #include +#include "uc_common.h" +#include "t_cmsgcred.h" +#include "t_bintime.h" +#include "t_generic.h" +#include "t_peercred.h" +#include "t_timeval.h" +#include "t_sockcred.h" +#include "t_cmsgcred_sockcred.h" +#include "t_cmsg_len.h" + /* * There are tables with tests descriptions and pointers to test * functions. Each t_*() function returns 0 if its test passed, @@ -69,29 +70,6 @@ __FBSDID("$FreeBSD$"); * with a non-zero return code. */ -#ifndef LISTENQ -# define LISTENQ 1 -#endif - -#ifndef TIMEOUT -# define TIMEOUT 2 -#endif - -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); -/* - * The testcase fails on 64-bit architectures (amd64), but passes on 32-bit - * architectures (i386); see bug 206543 - */ -#ifndef __LP64__ -static int t_cmsg_len(void); -#endif -static int t_peercred(void); - struct test_func { int (*func)(void); const char *desc; @@ -126,6 +104,10 @@ static const struct test_func test_stream_tbl[] = { .func = t_bintime, .desc = "Sending, receiving bintime" }, +/* + * The testcase fails on 64-bit architectures (amd64), but passes on 32-bit + * architectures (i386); see bug 206543 + */ #ifndef __LP64__ { .func = t_cmsg_len, @@ -177,58 +159,17 @@ static const struct test_func test_dgram_tbl[] = { #define TEST_DGRAM_TBL_SIZE \ (sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0])) -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 sock_type; -static const char *sock_type_str; - -static const char *proc_name; +struct uc_cfg uc_cfg; static char work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX"; -static int serv_sock_fd; -static struct sockaddr_un serv_addr_sun; - -static struct { - char *buf_send; - char *buf_recv; - size_t buf_size; - u_int msg_num; -} ipc_msg; #define IPC_MSG_NUM_DEF 5 #define IPC_MSG_NUM_MAX 10 #define IPC_MSG_SIZE_DEF 7 #define IPC_MSG_SIZE_MAX 128 -static struct { - uid_t uid; - uid_t euid; - gid_t gid; - gid_t egid; - gid_t *gid_arr; - int gid_num; -} proc_cred; - -static pid_t client_pid; - -#define SYNC_SERVER 0 -#define SYNC_CLIENT 1 -#define SYNC_RECV 0 -#define SYNC_SEND 1 - -static int sync_fd[2][2]; - -#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); - static void usage(bool verbose) { @@ -255,84 +196,19 @@ usage(bool verbose) printf(" %u: %s\n", i, test_dgram_tbl[i].desc); } -static void -output(const char *format, ...) -{ - char buf[LOGMSG_SIZE]; - va_list ap; - - va_start(ap, format); - if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EXIT_FAILURE, "output: vsnprintf failed"); - write(STDOUT_FILENO, buf, strlen(buf)); - va_end(ap); -} - -static void -logmsg(const char *format, ...) -{ - char buf[LOGMSG_SIZE]; - va_list ap; - int errno_save; - - errno_save = errno; - va_start(ap, format); - if (vsnprintf(buf, sizeof(buf), format, ap) < 0) - err(EXIT_FAILURE, "logmsg: vsnprintf failed"); - if (errno_save == 0) - output("%s: %s\n", proc_name, buf); - else - 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); - -} - -static void -logmsgx(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vlogmsgx(format, ap); - va_end(ap); -} - -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(int type, u_int testno1) { const struct test_func *tf; u_int i, testno2, failed_num; - sock_type = type; + uc_cfg.sock_type = type; if (type == SOCK_STREAM) { - sock_type_str = "SOCK_STREAM"; + uc_cfg.sock_type_str = "SOCK_STREAM"; tf = test_stream_tbl; i = TEST_STREAM_TBL_SIZE - 1; } else { - sock_type_str = "SOCK_DGRAM"; + uc_cfg.sock_type_str = "SOCK_DGRAM"; tf = test_dgram_tbl; i = TEST_DGRAM_TBL_SIZE - 1; } @@ -342,16 +218,16 @@ run_tests(int type, u_int testno1) } else testno2 = testno1; - output("Running tests for %s sockets:\n", sock_type_str); + uc_output("Running tests for %s sockets:\n", uc_cfg.sock_type_str); failed_num = 0; for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) { - output(" %u: %s\n", i, tf->desc); + uc_output(" %u: %s\n", i, tf->desc); switch (tf->func()) { case -1: ++failed_num; break; case -2: - logmsgx("some system error or timeout occurred"); + uc_logmsgx("some system error or timeout occurred"); return (-1); } } @@ -361,15 +237,15 @@ run_tests(int type, u_int testno1) if (testno1 != testno2) { if (failed_num == 0) - output("-- all tests passed!\n"); + uc_output("-- all tests passed!\n"); else - output("-- %u test%s failed!\n", + uc_output("-- %u test%s failed!\n", failed_num, failed_num == 1 ? "" : "s"); } else { if (failed_num == 0) - output("-- test passed!\n"); + uc_output("-- test passed!\n"); else - output("-- test failed!\n"); + uc_output("-- test failed!\n"); } return (0); @@ -382,145 +258,62 @@ init(void) size_t idx; int rv; - proc_name = "SERVER"; + uc_cfg.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"); + uc_logmsg("init: sigaction"); return (-1); } - if (ipc_msg.buf_size == 0) - ipc_msg.buf_send = ipc_msg.buf_recv = NULL; + if (uc_cfg.ipc_msg.buf_size == 0) + uc_cfg.ipc_msg.buf_send = uc_cfg.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"); + uc_cfg.ipc_msg.buf_send = malloc(uc_cfg.ipc_msg.buf_size); + uc_cfg.ipc_msg.buf_recv = malloc(uc_cfg.ipc_msg.buf_size); + if (uc_cfg.ipc_msg.buf_send == NULL || uc_cfg.ipc_msg.buf_recv == NULL) { + uc_logmsg("init: malloc"); return (-1); } - for (idx = 0; idx < ipc_msg.buf_size; ++idx) - ipc_msg.buf_send[idx] = (char)idx; + for (idx = 0; idx < uc_cfg.ipc_msg.buf_size; ++idx) + uc_cfg.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"); + uc_cfg.proc_cred.uid = getuid(); + uc_cfg.proc_cred.euid = geteuid(); + uc_cfg.proc_cred.gid = getgid(); + uc_cfg.proc_cred.egid = getegid(); + uc_cfg.proc_cred.gid_num = getgroups(0, (gid_t *)NULL); + if (uc_cfg.proc_cred.gid_num < 0) { + uc_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"); + uc_cfg.proc_cred.gid_arr = malloc(uc_cfg.proc_cred.gid_num * + sizeof(*uc_cfg.proc_cred.gid_arr)); + if (uc_cfg.proc_cred.gid_arr == NULL) { + uc_logmsg("init: malloc"); return (-1); } - if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) { - logmsg("init: getgroups"); + if (getgroups(uc_cfg.proc_cred.gid_num, uc_cfg.proc_cred.gid_arr) < 0) { + uc_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); + memset(&uc_cfg.serv_addr_sun, 0, sizeof(uc_cfg.serv_addr_sun)); + rv = snprintf(uc_cfg.serv_addr_sun.sun_path, sizeof(uc_cfg.serv_addr_sun.sun_path), + "%s/%s", work_dir, uc_cfg.proc_name); if (rv < 0) { - logmsg("init: snprintf"); + uc_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); -} - -static 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) -{ - 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); - } - - 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"); + if ((size_t)rv >= sizeof(uc_cfg.serv_addr_sun.sun_path)) { + uc_logmsgx("init: not enough space for socket pathname"); return (-1); } + uc_cfg.serv_addr_sun.sun_family = PF_LOCAL; + uc_cfg.serv_addr_sun.sun_len = SUN_LEN(&uc_cfg.serv_addr_sun); return (0); } @@ -533,26 +326,31 @@ main(int argc, char *argv[]) int opt, rv; bool dgram_flag, stream_flag; - ipc_msg.buf_size = IPC_MSG_SIZE_DEF; - ipc_msg.msg_num = IPC_MSG_NUM_DEF; + memset(&uc_cfg, '\0', sizeof(uc_cfg)); + uc_cfg.debug = false; + uc_cfg.server_flag = true; + uc_cfg.send_data_flag = true; + uc_cfg.send_array_flag = true; + uc_cfg.ipc_msg.buf_size = IPC_MSG_SIZE_DEF; + uc_cfg.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 = true; + uc_cfg.debug = true; break; case 'h': usage(true); return (EXIT_SUCCESS); case 'n': - ipc_msg.msg_num = strtonum(optarg, 1, + uc_cfg.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, + uc_cfg.ipc_msg.buf_size = strtonum(optarg, 0, IPC_MSG_SIZE_MAX, &errstr); if (errstr != NULL) errx(EXIT_FAILURE, "option -s: number is %s", @@ -573,9 +371,9 @@ main(int argc, char *argv[]) errx(EXIT_FAILURE, "option -z: number is %s", errstr); if (zvalue & 0x1) - send_data_flag = false; + uc_cfg.send_data_flag = false; if (zvalue & 0x2) - send_array_flag = false; + uc_cfg.send_array_flag = false; break; default: usage(false); @@ -621,1378 +419,8 @@ main(int argc, char *argv[]) rv = EXIT_SUCCESS; done: if (rmdir(work_dir) < 0) { - logmsg("rmdir(%s)", work_dir); + uc_logmsg("rmdir(%s)", work_dir); rv = EXIT_FAILURE; } return (failed_flag ? EXIT_FAILURE : rv); } - -static int -socket_close(int fd) -{ - int rv; - - 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); -} - -static int -socket_create(void) -{ - struct timeval tv; - int fd; - - 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 (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; - } - if (sock_type == SOCK_STREAM) { - int val; - - 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; - } - } - } - - return (fd); - -failed: - if (close(fd) < 0) - 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); -} - -static int -socket_connect(int fd) -{ - 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); -} - -static int -sync_recv(void) -{ - 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); -} - -static int -sync_send(void) -{ - ssize_t ssize; - int fd; - - 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 (ssize < 1) { - logmsgx("sync_send: sent %zd of 1 byte", ssize); - return (-1); - } - - return (0); -} - -static int -message_send(int fd, const struct msghdr *msghdr) -{ - 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); -} - -static int -message_sendn(int fd, struct msghdr *msghdr) -{ - u_int i; - - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - if (message_send(fd, msghdr) < 0) - return (-1); - } - return (0); -} - -static int -message_recv(int fd, struct msghdr *msghdr) -{ - const struct cmsghdr *cmsghdr; - size_t size; - ssize_t ssize; - - if (!send_data_flag) - if (sync_recv() < 0) - return (-1); - - 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); - } - - 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); -} - -static int -socket_accept(int listenfd) -{ - 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); - } - - fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL); - if (fd < 0) { - 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); -} - -static int -check_msghdr(const struct msghdr *msghdr, size_t size) -{ - if (msghdr->msg_flags & MSG_TRUNC) { - logmsgx("msghdr.msg_flags has MSG_TRUNC"); - return (-1); - } - if (msghdr->msg_flags & MSG_CTRUNC) { - logmsgx("msghdr.msg_flags has MSG_CTRUNC"); - return (-1); - } - 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); -} - -static int -check_groups(const char *gid_arr_str, const gid_t *gid_arr, - const char *gid_num_str, int gid_num, bool all_gids) -{ - int i; - - for (i = 0; i < gid_num; ++i) - dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]); - - 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); -} - -static int -check_xucred(const struct xucred *xucred, socklen_t len) -{ - int rc; - - if (len != sizeof(*xucred)) { - logmsgx("option value size %zu != %zu", - (size_t)len, sizeof(*xucred)); - return (-1); - } - - 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); - - rc = 0; - - if (xucred->cr_version != XUCRED_VERSION) { - logmsgx("xucred.cr_version %u != %d", - xucred->cr_version, XUCRED_VERSION); - rc = -1; - } - if (xucred->cr_uid != proc_cred.euid) { - logmsgx("xucred.cr_uid %lu != %lu (EUID)", - (u_long)xucred->cr_uid, (u_long)proc_cred.euid); - rc = -1; - } - if (xucred->cr_ngroups == 0) { - logmsgx("xucred.cr_ngroups == 0"); - rc = -1; - } - if (xucred->cr_ngroups < 0) { - logmsgx("xucred.cr_ngroups < 0"); - rc = -1; - } - if (xucred->cr_ngroups > XU_NGROUPS) { - logmsgx("xucred.cr_ngroups %hu > %u (max)", - xucred->cr_ngroups, XU_NGROUPS); - rc = -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); - rc = -1; - } - if (check_groups("xucred.cr_groups", xucred->cr_groups, - "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0) - rc = -1; - return (rc); -} - -static int -check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr) -{ - const struct cmsgcred *cmcred; - int rc; - - if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0) - return (-1); - - cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr); - - dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid); - dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid); - dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid); - dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid); - dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups); - - rc = 0; - - if (cmcred->cmcred_pid != client_pid) { - logmsgx("cmsgcred.cmcred_pid %ld != %ld", - (long)cmcred->cmcred_pid, (long)client_pid); - rc = -1; - } - if (cmcred->cmcred_uid != proc_cred.uid) { - logmsgx("cmsgcred.cmcred_uid %lu != %lu", - (u_long)cmcred->cmcred_uid, (u_long)proc_cred.uid); - rc = -1; - } - if (cmcred->cmcred_euid != proc_cred.euid) { - logmsgx("cmsgcred.cmcred_euid %lu != %lu", - (u_long)cmcred->cmcred_euid, (u_long)proc_cred.euid); - rc = -1; - } - if (cmcred->cmcred_gid != proc_cred.gid) { - logmsgx("cmsgcred.cmcred_gid %lu != %lu", - (u_long)cmcred->cmcred_gid, (u_long)proc_cred.gid); - rc = -1; - } - if (cmcred->cmcred_ngroups == 0) { - logmsgx("cmsgcred.cmcred_ngroups == 0"); - rc = -1; - } - if (cmcred->cmcred_ngroups < 0) { - logmsgx("cmsgcred.cmcred_ngroups %d < 0", - cmcred->cmcred_ngroups); - rc = -1; - } - if (cmcred->cmcred_ngroups > CMGROUP_MAX) { - logmsgx("cmsgcred.cmcred_ngroups %d > %d", - cmcred->cmcred_ngroups, CMGROUP_MAX); - rc = -1; - } - if (cmcred->cmcred_groups[0] != proc_cred.egid) { - logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)", - (u_long)cmcred->cmcred_groups[0], (u_long)proc_cred.egid); - rc = -1; - } - if (check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups, - "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0) - rc = -1; - return (rc); -} - -static int -check_scm_creds_sockcred(struct cmsghdr *cmsghdr) -{ - const struct sockcred *sc; - int rc; - - if (check_cmsghdr(cmsghdr, SCM_CREDS, - SOCKCREDSIZE(proc_cred.gid_num)) < 0) - return (-1); - - sc = (struct sockcred *)CMSG_DATA(cmsghdr); - - rc = 0; - - dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid); - dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid); - dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid); - dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid); - dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups); - - if (sc->sc_uid != proc_cred.uid) { - logmsgx("sockcred.sc_uid %lu != %lu", - (u_long)sc->sc_uid, (u_long)proc_cred.uid); - rc = -1; - } - if (sc->sc_euid != proc_cred.euid) { - logmsgx("sockcred.sc_euid %lu != %lu", - (u_long)sc->sc_euid, (u_long)proc_cred.euid); - rc = -1; - } - if (sc->sc_gid != proc_cred.gid) { - logmsgx("sockcred.sc_gid %lu != %lu", - (u_long)sc->sc_gid, (u_long)proc_cred.gid); - rc = -1; - } - if (sc->sc_egid != proc_cred.egid) { - logmsgx("sockcred.sc_egid %lu != %lu", - (u_long)sc->sc_egid, (u_long)proc_cred.egid); - rc = -1; - } - if (sc->sc_ngroups == 0) { - logmsgx("sockcred.sc_ngroups == 0"); - rc = -1; - } - if (sc->sc_ngroups < 0) { - logmsgx("sockcred.sc_ngroups %d < 0", - sc->sc_ngroups); - rc = -1; - } - if (sc->sc_ngroups != proc_cred.gid_num) { - logmsgx("sockcred.sc_ngroups %d != %u", - sc->sc_ngroups, proc_cred.gid_num); - rc = -1; - } - if (check_groups("sockcred.sc_groups", sc->sc_groups, - "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0) - rc = -1; - return (rc); -} - -static int -check_scm_timestamp(struct cmsghdr *cmsghdr) -{ - const struct timeval *tv; - - if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0) - return (-1); - - tv = (struct timeval *)CMSG_DATA(cmsghdr); - - dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX, - (intmax_t)tv->tv_sec, (intmax_t)tv->tv_usec); - - return (0); -} - -static int -check_scm_bintime(struct cmsghdr *cmsghdr) -{ - const struct bintime *bt; - - if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0) - return (-1); - - bt = (struct bintime *)CMSG_DATA(cmsghdr); - - dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64, - (intmax_t)bt->sec, bt->frac); - - return (0); -} - -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; -} - -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); -} - -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) { - if (send_array_flag) - dbgmsg("sending an array"); - else - dbgmsg("sending a scalar"); - 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; -} - -static int -t_generic(int (*client_func)(int), int (*server_func)(int)) -{ - int fd, rv, rv_client; - - 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); -} - -static int -t_cmsgcred_client(int fd) -{ - struct msghdr msghdr; - struct iovec iov[1]; - void *cmsg_data; - size_t cmsg_size; - int rv; - - if (sync_recv() < 0) - return (-2); - - rv = -2; - - 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 (socket_connect(fd) < 0) - goto done; - - if (message_sendn(fd, &msghdr) < 0) - goto done; - - rv = 0; -done: - free(cmsg_data); - return (rv); -} - -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; - - if (sync_send() < 0) - return (-2); - - fd2 = -1; - rv = -2; - - cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)); - cmsg_data = malloc(cmsg_size); - if (cmsg_data == NULL) { - logmsg("malloc"); - goto done; - } - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - rv = -1; - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - - 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_creds_cmsgcred(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_cmsgcred(void) -{ - return (t_generic(t_cmsgcred_client, t_cmsgcred_server)); -} - -static int -t_sockcred_client(int type, int fd) -{ - struct msghdr msghdr; - struct iovec iov[1]; - int rv; - - if (sync_recv() < 0) - return (-2); - - rv = -2; - - msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0); - - if (socket_connect(fd) < 0) - goto done; - - if (type == 2) - if (sync_recv() < 0) - goto done; - - if (message_sendn(fd, &msghdr) < 0) - goto done; - - rv = 0; -done: - return (rv); -} - -static int -t_sockcred_server(int type, 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, val; - - fd2 = -1; - rv = -2; - - 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) { - dbgmsg("setting LOCAL_CREDS"); - val = 1; - if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { - logmsg("setsockopt(LOCAL_CREDS)"); - goto done; - } - } - - if (sync_send() < 0) - goto done; - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - 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; - } - - rv = -1; - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - - msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size); - if (message_recv(fd2, &msghdr) < 0) { - rv = -2; - break; - } - - if (i > 1 && sock_type == SOCK_STREAM) { - if (check_msghdr(&msghdr, 0) < 0) - break; - } else { - if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0) - break; - - 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); -} - -static int -t_sockcred_1(void) -{ - u_int i; - int fd, rv, rv_client; - - 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 (rv != 0) - break; - } - 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; - } - - return (rv); -} - -static int -t_sockcred_2_client(int fd) -{ - return (t_sockcred_client(2, fd)); -} - -static int -t_sockcred_2_server(int fd) -{ - return (t_sockcred_server(2, fd)); -} - -static int -t_sockcred_2(void) -{ - return (t_generic(t_sockcred_2_client, t_sockcred_2_server)); -} - -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; - - fd2 = -1; - rv = -2; - - 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; - } - - dbgmsg("setting LOCAL_CREDS"); - val = 1; - if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) { - logmsg("setsockopt(LOCAL_CREDS)"); - goto done; - } - - if (sync_send() < 0) - goto done; - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - cmsg_data = cmsg1_data; - cmsg_size = cmsg1_size; - rv = -1; - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - - 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 (i == 1 || sock_type == SOCK_DGRAM) { - if (check_scm_creds_sockcred(cmsghdr) < 0) - break; - } else { - if (check_scm_creds_cmsgcred(cmsghdr) < 0) - break; - } - - cmsg_data = cmsg2_data; - cmsg_size = cmsg2_size; - } - 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_cmsgcred_sockcred(void) -{ - return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server)); -} - -static int -t_timeval_client(int fd) -{ - struct msghdr msghdr; - struct iovec iov[1]; - void *cmsg_data; - size_t cmsg_size; - int rv; - - if (sync_recv() < 0) - return (-2); - - rv = -2; - - 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 (socket_connect(fd) < 0) - goto done; - - if (message_sendn(fd, &msghdr) < 0) - goto done; - - rv = 0; -done: - free(cmsg_data); - return (rv); -} - -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; - - 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; - } - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - rv = -1; - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - - 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_timeval(void) -{ - return (t_generic(t_timeval_client, t_timeval_server)); -} - -static int -t_bintime_client(int fd) -{ - struct msghdr msghdr; - struct iovec iov[1]; - void *cmsg_data; - size_t cmsg_size; - int rv; - - if (sync_recv() < 0) - return (-2); - - rv = -2; - - 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 (socket_connect(fd) < 0) - goto done; - - if (message_sendn(fd, &msghdr) < 0) - goto done; - - rv = 0; -done: - free(cmsg_data); - return (rv); -} - -static int -t_bintime_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; - - if (sync_send() < 0) - return (-2); - - fd2 = -1; - rv = -2; - - cmsg_size = CMSG_SPACE(sizeof(struct bintime)); - cmsg_data = malloc(cmsg_size); - if (cmsg_data == NULL) { - logmsg("malloc"); - goto done; - } - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - rv = -1; - for (i = 1; i <= ipc_msg.msg_num; ++i) { - dbgmsg("message #%u", i); - - 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_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); -} - -static int -t_bintime(void) -{ - return (t_generic(t_bintime_client, t_bintime_server)); -} - -#ifndef __LP64__ -static int -t_cmsg_len_client(int fd) -{ - struct msghdr msghdr; - struct iovec iov[1]; - struct cmsghdr *cmsghdr; - void *cmsg_data; - size_t size, cmsg_size; - socklen_t socklen; - int rv; - - if (sync_recv() < 0) - return (-2); - - rv = -2; - - 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 (socket_connect(fd) < 0) - 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) { - dbgmsg("sendmsg(2) failed: %s; retrying", - strerror(errno)); - 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; - - if (sync_send() < 0) { - rv = -2; - goto done; - } -done: - free(cmsg_data); - return (rv); -} - -static int -t_cmsg_len_server(int fd1) -{ - int fd2, rv; - - if (sync_send() < 0) - return (-2); - - rv = -2; - - if (sock_type == SOCK_STREAM) { - fd2 = socket_accept(fd1); - if (fd2 < 0) - goto done; - } else - fd2 = fd1; - - if (sync_recv() < 0) - goto done; - - rv = 0; -done: - if (sock_type == SOCK_STREAM && fd2 >= 0) - if (socket_close(fd2) < 0) - rv = -2; - return (rv); -} - -static int -t_cmsg_len(void) -{ - return (t_generic(t_cmsg_len_client, t_cmsg_len_server)); -} -#endif - -static int -t_peercred_client(int fd) -{ - struct xucred xucred; - socklen_t len; - - if (sync_recv() < 0) - return (-1); - - if (socket_connect(fd) < 0) - return (-1); - - len = sizeof(xucred); - if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { - logmsg("getsockopt(LOCAL_PEERCRED)"); - return (-1); - } - - if (check_xucred(&xucred, len) < 0) - return (-1); - - return (0); -} - -static int -t_peercred_server(int fd1) -{ - struct xucred xucred; - socklen_t len; - int fd2, rv; - - 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; - } - - 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)); -}