1aa321596SBjoern A. Zeeb /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3aa321596SBjoern A. Zeeb * 4aa321596SBjoern A. Zeeb * Copyright (c) 2019 Bjoern A. Zeeb 54f02a7d7SMark Johnston * Copyright (c) 2024 Stormshield 6aa321596SBjoern A. Zeeb * 7aa321596SBjoern A. Zeeb * Redistribution and use in source and binary forms, with or without 8aa321596SBjoern A. Zeeb * modification, are permitted provided that the following conditions 9aa321596SBjoern A. Zeeb * are met: 10aa321596SBjoern A. Zeeb * 1. Redistributions of source code must retain the above copyright 11aa321596SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer. 12aa321596SBjoern A. Zeeb * 2. Redistributions in binary form must reproduce the above copyright 13aa321596SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer in the 14aa321596SBjoern A. Zeeb * documentation and/or other materials provided with the distribution. 15aa321596SBjoern A. Zeeb * 16aa321596SBjoern A. Zeeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17aa321596SBjoern A. Zeeb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18aa321596SBjoern A. Zeeb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19aa321596SBjoern A. Zeeb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20aa321596SBjoern A. Zeeb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21aa321596SBjoern A. Zeeb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22aa321596SBjoern A. Zeeb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23aa321596SBjoern A. Zeeb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24aa321596SBjoern A. Zeeb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25aa321596SBjoern A. Zeeb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26aa321596SBjoern A. Zeeb * SUCH DAMAGE. 27aa321596SBjoern A. Zeeb */ 28aa321596SBjoern A. Zeeb 294f02a7d7SMark Johnston #include <sys/param.h> 30aa321596SBjoern A. Zeeb #include <sys/socket.h> 314f02a7d7SMark Johnston #include <sys/wait.h> 324f02a7d7SMark Johnston 33aa321596SBjoern A. Zeeb #include <netinet/in.h> 344f02a7d7SMark Johnston 35b1c66bc4SMark Johnston #include <errno.h> 363aaaa2efSThomas Munro #include <poll.h> 374f02a7d7SMark Johnston #include <pwd.h> 384f02a7d7SMark Johnston #include <stdio.h> 394f02a7d7SMark Johnston #include <unistd.h> 40aa321596SBjoern A. Zeeb 41aa321596SBjoern A. Zeeb #include <atf-c.h> 42aa321596SBjoern A. Zeeb 43aa321596SBjoern A. Zeeb ATF_TC_WITHOUT_HEAD(socket_afinet); 44aa321596SBjoern A. Zeeb ATF_TC_BODY(socket_afinet, tc) 45aa321596SBjoern A. Zeeb { 46aa321596SBjoern A. Zeeb int sd; 47aa321596SBjoern A. Zeeb 48aa321596SBjoern A. Zeeb sd = socket(PF_INET, SOCK_DGRAM, 0); 49aa321596SBjoern A. Zeeb ATF_CHECK(sd >= 0); 50aa321596SBjoern A. Zeeb 51aa321596SBjoern A. Zeeb close(sd); 52aa321596SBjoern A. Zeeb } 53aa321596SBjoern A. Zeeb 54aa321596SBjoern A. Zeeb ATF_TC_WITHOUT_HEAD(socket_afinet_bind_zero); 55aa321596SBjoern A. Zeeb ATF_TC_BODY(socket_afinet_bind_zero, tc) 56aa321596SBjoern A. Zeeb { 57aa321596SBjoern A. Zeeb int sd, rc; 58aa321596SBjoern A. Zeeb struct sockaddr_in sin; 59aa321596SBjoern A. Zeeb 6069b7dbebSLi-Wen Hsu if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false)) 6169b7dbebSLi-Wen Hsu atf_tc_skip("doesn't work when mac_portacl(4) loaded (https://bugs.freebsd.org/238781)"); 6201e92e29SLi-Wen Hsu 63aa321596SBjoern A. Zeeb sd = socket(PF_INET, SOCK_DGRAM, 0); 64aa321596SBjoern A. Zeeb ATF_CHECK(sd >= 0); 65aa321596SBjoern A. Zeeb 66aa321596SBjoern A. Zeeb bzero(&sin, sizeof(sin)); 67aa321596SBjoern A. Zeeb /* 68aa321596SBjoern A. Zeeb * For AF_INET we do not check the family in in_pcbbind_setup(9), 69aa321596SBjoern A. Zeeb * sa_len gets set from the syscall argument in getsockaddr(9), 70aa321596SBjoern A. Zeeb * so we bind to 0:0. 71aa321596SBjoern A. Zeeb */ 72aa321596SBjoern A. Zeeb rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin)); 73aa321596SBjoern A. Zeeb ATF_CHECK_EQ(0, rc); 74aa321596SBjoern A. Zeeb 75aa321596SBjoern A. Zeeb close(sd); 76aa321596SBjoern A. Zeeb } 77aa321596SBjoern A. Zeeb 78aa321596SBjoern A. Zeeb ATF_TC_WITHOUT_HEAD(socket_afinet_bind_ok); 79aa321596SBjoern A. Zeeb ATF_TC_BODY(socket_afinet_bind_ok, tc) 80aa321596SBjoern A. Zeeb { 81aa321596SBjoern A. Zeeb int sd, rc; 82aa321596SBjoern A. Zeeb struct sockaddr_in sin; 83aa321596SBjoern A. Zeeb 84aa321596SBjoern A. Zeeb sd = socket(PF_INET, SOCK_DGRAM, 0); 85aa321596SBjoern A. Zeeb ATF_CHECK(sd >= 0); 86aa321596SBjoern A. Zeeb 87aa321596SBjoern A. Zeeb bzero(&sin, sizeof(sin)); 88aa321596SBjoern A. Zeeb sin.sin_family = AF_INET; 89aa321596SBjoern A. Zeeb sin.sin_len = sizeof(sin); 90150d8ca9SOlivier Cochard sin.sin_port = htons(0); 91aa321596SBjoern A. Zeeb sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 92aa321596SBjoern A. Zeeb rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin)); 93aa321596SBjoern A. Zeeb ATF_CHECK_EQ(0, rc); 94aa321596SBjoern A. Zeeb 95aa321596SBjoern A. Zeeb close(sd); 96aa321596SBjoern A. Zeeb } 97aa321596SBjoern A. Zeeb 983aaaa2efSThomas Munro ATF_TC_WITHOUT_HEAD(socket_afinet_poll_no_rdhup); 993aaaa2efSThomas Munro ATF_TC_BODY(socket_afinet_poll_no_rdhup, tc) 1003aaaa2efSThomas Munro { 1013aaaa2efSThomas Munro int ss, ss2, cs, rc; 1023aaaa2efSThomas Munro struct sockaddr_in sin; 103150d8ca9SOlivier Cochard socklen_t slen; 1043aaaa2efSThomas Munro struct pollfd pfd; 1053aaaa2efSThomas Munro int one = 1; 1063aaaa2efSThomas Munro 1073aaaa2efSThomas Munro /* Verify that we don't expose POLLRDHUP if not requested. */ 1083aaaa2efSThomas Munro 1093aaaa2efSThomas Munro /* Server setup. */ 1103aaaa2efSThomas Munro ss = socket(PF_INET, SOCK_STREAM, 0); 1113aaaa2efSThomas Munro ATF_CHECK(ss >= 0); 1123aaaa2efSThomas Munro rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 1133aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1143aaaa2efSThomas Munro bzero(&sin, sizeof(sin)); 1153aaaa2efSThomas Munro sin.sin_family = AF_INET; 1163aaaa2efSThomas Munro sin.sin_len = sizeof(sin); 117150d8ca9SOlivier Cochard sin.sin_port = htons(0); 1183aaaa2efSThomas Munro sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1193aaaa2efSThomas Munro rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); 1203aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1213aaaa2efSThomas Munro rc = listen(ss, 1); 1223aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 123150d8ca9SOlivier Cochard slen = sizeof(sin); 124150d8ca9SOlivier Cochard rc = getsockname(ss, (struct sockaddr *)&sin, &slen); 125150d8ca9SOlivier Cochard ATF_CHECK_EQ(0, rc); 1263aaaa2efSThomas Munro 1273aaaa2efSThomas Munro /* Client connects, server accepts. */ 1283aaaa2efSThomas Munro cs = socket(PF_INET, SOCK_STREAM, 0); 1293aaaa2efSThomas Munro ATF_CHECK(cs >= 0); 1303aaaa2efSThomas Munro rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 1313aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1323aaaa2efSThomas Munro ss2 = accept(ss, NULL, NULL); 1333aaaa2efSThomas Munro ATF_CHECK(ss2 >= 0); 1343aaaa2efSThomas Munro 1353aaaa2efSThomas Munro /* Server can write, sees only POLLOUT. */ 1363aaaa2efSThomas Munro pfd.fd = ss2; 1373aaaa2efSThomas Munro pfd.events = POLLIN | POLLOUT; 1383aaaa2efSThomas Munro rc = poll(&pfd, 1, 0); 1393aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 1403aaaa2efSThomas Munro ATF_CHECK_EQ(POLLOUT, pfd.revents); 1413aaaa2efSThomas Munro 1423aaaa2efSThomas Munro /* Client closes socket! */ 1433aaaa2efSThomas Munro rc = close(cs); 1443aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1453aaaa2efSThomas Munro 1463aaaa2efSThomas Munro /* 1473aaaa2efSThomas Munro * Server now sees POLLIN, but not POLLRDHUP because we didn't ask. 1483aaaa2efSThomas Munro * Need non-zero timeout to wait for the FIN to arrive and trigger the 1493aaaa2efSThomas Munro * socket to become readable. 1503aaaa2efSThomas Munro */ 1513aaaa2efSThomas Munro pfd.fd = ss2; 1523aaaa2efSThomas Munro pfd.events = POLLIN; 1533aaaa2efSThomas Munro rc = poll(&pfd, 1, 60000); 1543aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 1553aaaa2efSThomas Munro ATF_CHECK_EQ(POLLIN, pfd.revents); 1563aaaa2efSThomas Munro 1573aaaa2efSThomas Munro close(ss2); 1583aaaa2efSThomas Munro close(ss); 1593aaaa2efSThomas Munro } 1603aaaa2efSThomas Munro 1613aaaa2efSThomas Munro ATF_TC_WITHOUT_HEAD(socket_afinet_poll_rdhup); 1623aaaa2efSThomas Munro ATF_TC_BODY(socket_afinet_poll_rdhup, tc) 1633aaaa2efSThomas Munro { 1643aaaa2efSThomas Munro int ss, ss2, cs, rc; 1653aaaa2efSThomas Munro struct sockaddr_in sin; 166150d8ca9SOlivier Cochard socklen_t slen; 1673aaaa2efSThomas Munro struct pollfd pfd; 1683aaaa2efSThomas Munro char buffer; 1693aaaa2efSThomas Munro int one = 1; 1703aaaa2efSThomas Munro 1713aaaa2efSThomas Munro /* Verify that server sees POLLRDHUP if it asks for it. */ 1723aaaa2efSThomas Munro 1733aaaa2efSThomas Munro /* Server setup. */ 1743aaaa2efSThomas Munro ss = socket(PF_INET, SOCK_STREAM, 0); 1753aaaa2efSThomas Munro ATF_CHECK(ss >= 0); 1763aaaa2efSThomas Munro rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 1773aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1783aaaa2efSThomas Munro bzero(&sin, sizeof(sin)); 1793aaaa2efSThomas Munro sin.sin_family = AF_INET; 1803aaaa2efSThomas Munro sin.sin_len = sizeof(sin); 181150d8ca9SOlivier Cochard sin.sin_port = htons(0); 1823aaaa2efSThomas Munro sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1833aaaa2efSThomas Munro rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); 1843aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1853aaaa2efSThomas Munro rc = listen(ss, 1); 1863aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 187150d8ca9SOlivier Cochard slen = sizeof(sin); 188150d8ca9SOlivier Cochard rc = getsockname(ss, (struct sockaddr *)&sin, &slen); 189150d8ca9SOlivier Cochard ATF_CHECK_EQ(0, rc); 1903aaaa2efSThomas Munro 1913aaaa2efSThomas Munro /* Client connects, server accepts. */ 1923aaaa2efSThomas Munro cs = socket(PF_INET, SOCK_STREAM, 0); 1933aaaa2efSThomas Munro ATF_CHECK(cs >= 0); 1943aaaa2efSThomas Munro rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 1953aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 1963aaaa2efSThomas Munro ss2 = accept(ss, NULL, NULL); 1973aaaa2efSThomas Munro ATF_CHECK(ss2 >= 0); 1983aaaa2efSThomas Munro 1993aaaa2efSThomas Munro /* Server can write, so sees POLLOUT. */ 2003aaaa2efSThomas Munro pfd.fd = ss2; 2013aaaa2efSThomas Munro pfd.events = POLLIN | POLLOUT | POLLRDHUP; 2023aaaa2efSThomas Munro rc = poll(&pfd, 1, 0); 2033aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 2043aaaa2efSThomas Munro ATF_CHECK_EQ(POLLOUT, pfd.revents); 2053aaaa2efSThomas Munro 2063aaaa2efSThomas Munro /* Client writes two bytes, server reads only one of them. */ 2073aaaa2efSThomas Munro rc = write(cs, "xx", 2); 2083aaaa2efSThomas Munro ATF_CHECK_EQ(2, rc); 2093aaaa2efSThomas Munro rc = read(ss2, &buffer, 1); 2103aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 2113aaaa2efSThomas Munro 2123aaaa2efSThomas Munro /* Server can read, so sees POLLIN. */ 2133aaaa2efSThomas Munro pfd.fd = ss2; 2143aaaa2efSThomas Munro pfd.events = POLLIN | POLLOUT | POLLRDHUP; 2153aaaa2efSThomas Munro rc = poll(&pfd, 1, 0); 2163aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 2173aaaa2efSThomas Munro ATF_CHECK_EQ(POLLIN | POLLOUT, pfd.revents); 2183aaaa2efSThomas Munro 2193aaaa2efSThomas Munro /* Client closes socket! */ 2203aaaa2efSThomas Munro rc = close(cs); 2213aaaa2efSThomas Munro ATF_CHECK_EQ(0, rc); 2223aaaa2efSThomas Munro 2233aaaa2efSThomas Munro /* 2243aaaa2efSThomas Munro * Server sees Linux-style POLLRDHUP. Note that this is the case even 2253aaaa2efSThomas Munro * though one byte of data remains unread. 2263aaaa2efSThomas Munro * 2273aaaa2efSThomas Munro * This races against the delivery of FIN caused by the close() above. 2283aaaa2efSThomas Munro * Sometimes (more likely when run under truss or if another system 2293aaaa2efSThomas Munro * call is added in between) it hits the path where sopoll_generic() 2303aaaa2efSThomas Munro * immediately sees SBS_CANTRCVMORE, and sometimes it sleeps with flag 2313aaaa2efSThomas Munro * SB_SEL so that it's woken up almost immediately and runs again, 2323aaaa2efSThomas Munro * which is why we need a non-zero timeout here. 2333aaaa2efSThomas Munro */ 2343aaaa2efSThomas Munro pfd.fd = ss2; 2353aaaa2efSThomas Munro pfd.events = POLLRDHUP; 2363aaaa2efSThomas Munro rc = poll(&pfd, 1, 60000); 2373aaaa2efSThomas Munro ATF_CHECK_EQ(1, rc); 2383aaaa2efSThomas Munro ATF_CHECK_EQ(POLLRDHUP, pfd.revents); 2393aaaa2efSThomas Munro 2403aaaa2efSThomas Munro close(ss2); 2413aaaa2efSThomas Munro close(ss); 2423aaaa2efSThomas Munro } 2433aaaa2efSThomas Munro 244b1c66bc4SMark Johnston ATF_TC_WITHOUT_HEAD(socket_afinet_stream_reconnect); 245b1c66bc4SMark Johnston ATF_TC_BODY(socket_afinet_stream_reconnect, tc) 246b1c66bc4SMark Johnston { 247b1c66bc4SMark Johnston struct sockaddr_in sin; 248150d8ca9SOlivier Cochard socklen_t slen; 249b1c66bc4SMark Johnston int ss, cs, rc; 250b1c66bc4SMark Johnston 251b1c66bc4SMark Johnston /* 252b1c66bc4SMark Johnston * Make sure that an attempt to connect(2) a connected or disconnected 253b1c66bc4SMark Johnston * stream socket fails with EISCONN. 254b1c66bc4SMark Johnston */ 255b1c66bc4SMark Johnston 256b1c66bc4SMark Johnston /* Server setup. */ 257b1c66bc4SMark Johnston ss = socket(PF_INET, SOCK_STREAM, 0); 258b1c66bc4SMark Johnston ATF_CHECK(ss >= 0); 259b1c66bc4SMark Johnston bzero(&sin, sizeof(sin)); 260b1c66bc4SMark Johnston sin.sin_family = AF_INET; 261b1c66bc4SMark Johnston sin.sin_len = sizeof(sin); 262150d8ca9SOlivier Cochard sin.sin_port = htons(0); 263b1c66bc4SMark Johnston sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 264b1c66bc4SMark Johnston rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); 265b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 266b1c66bc4SMark Johnston rc = listen(ss, 1); 267b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 268150d8ca9SOlivier Cochard slen = sizeof(sin); 269150d8ca9SOlivier Cochard rc = getsockname(ss, (struct sockaddr *)&sin, &slen); 270150d8ca9SOlivier Cochard ATF_CHECK_EQ(0, rc); 271b1c66bc4SMark Johnston 272b1c66bc4SMark Johnston /* Client connects, shuts down. */ 273b1c66bc4SMark Johnston cs = socket(PF_INET, SOCK_STREAM, 0); 274b1c66bc4SMark Johnston ATF_CHECK(cs >= 0); 275b1c66bc4SMark Johnston rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 276b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 277b1c66bc4SMark Johnston rc = shutdown(cs, SHUT_RDWR); 278b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 279b1c66bc4SMark Johnston 280b1c66bc4SMark Johnston /* A subsequent connect(2) fails with EISCONN. */ 281b1c66bc4SMark Johnston rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 282b1c66bc4SMark Johnston ATF_CHECK_EQ(-1, rc); 283b1c66bc4SMark Johnston ATF_CHECK_EQ(errno, EISCONN); 284b1c66bc4SMark Johnston 285b1c66bc4SMark Johnston rc = close(cs); 286b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 287b1c66bc4SMark Johnston rc = close(ss); 288b1c66bc4SMark Johnston ATF_CHECK_EQ(0, rc); 289b1c66bc4SMark Johnston } 290b1c66bc4SMark Johnston 2914f02a7d7SMark Johnston /* 2924f02a7d7SMark Johnston * Make sure that unprivileged users can't set the IP_BINDANY or IPV6_BINDANY 2934f02a7d7SMark Johnston * socket options. 2944f02a7d7SMark Johnston */ 2954f02a7d7SMark Johnston ATF_TC(socket_afinet_bindany); 2964f02a7d7SMark Johnston ATF_TC_HEAD(socket_afinet_bindany, tc) 2974f02a7d7SMark Johnston { 2984f02a7d7SMark Johnston atf_tc_set_md_var(tc, "require.user", "unprivileged"); 2994f02a7d7SMark Johnston } 3004f02a7d7SMark Johnston ATF_TC_BODY(socket_afinet_bindany, tc) 3014f02a7d7SMark Johnston { 3024f02a7d7SMark Johnston int s; 3034f02a7d7SMark Johnston 3044f02a7d7SMark Johnston s = socket(AF_INET, SOCK_STREAM, 0); 3054f02a7d7SMark Johnston ATF_REQUIRE(s >= 0); 3064f02a7d7SMark Johnston ATF_REQUIRE_ERRNO(EPERM, 3074f02a7d7SMark Johnston setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) == 3084f02a7d7SMark Johnston -1); 3094f02a7d7SMark Johnston ATF_REQUIRE(close(s) == 0); 3104f02a7d7SMark Johnston 3114f02a7d7SMark Johnston s = socket(AF_INET, SOCK_DGRAM, 0); 3124f02a7d7SMark Johnston ATF_REQUIRE(s >= 0); 3134f02a7d7SMark Johnston ATF_REQUIRE_ERRNO(EPERM, 3144f02a7d7SMark Johnston setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) == 3154f02a7d7SMark Johnston -1); 3164f02a7d7SMark Johnston ATF_REQUIRE(close(s) == 0); 3174f02a7d7SMark Johnston 3184f02a7d7SMark Johnston s = socket(AF_INET6, SOCK_STREAM, 0); 3194f02a7d7SMark Johnston ATF_REQUIRE(s >= 0); 3204f02a7d7SMark Johnston ATF_REQUIRE_ERRNO(EPERM, 3214f02a7d7SMark Johnston setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) == 3224f02a7d7SMark Johnston -1); 3234f02a7d7SMark Johnston ATF_REQUIRE(close(s) == 0); 3244f02a7d7SMark Johnston 3254f02a7d7SMark Johnston s = socket(AF_INET6, SOCK_DGRAM, 0); 3264f02a7d7SMark Johnston ATF_REQUIRE(s >= 0); 3274f02a7d7SMark Johnston ATF_REQUIRE_ERRNO(EPERM, 3284f02a7d7SMark Johnston setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) == 3294f02a7d7SMark Johnston -1); 3304f02a7d7SMark Johnston ATF_REQUIRE(close(s) == 0); 3314f02a7d7SMark Johnston } 3324f02a7d7SMark Johnston 3334f02a7d7SMark Johnston /* 3344f02a7d7SMark Johnston * Bind a socket to the specified address, optionally dropping privileges and 3354f02a7d7SMark Johnston * setting one of the SO_REUSE* options first. 3364f02a7d7SMark Johnston * 3374f02a7d7SMark Johnston * Returns true if the bind succeeded, and false if it failed with EADDRINUSE. 3384f02a7d7SMark Johnston */ 3394f02a7d7SMark Johnston static bool 340*c9756953SMark Johnston child_bind(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt, 341*c9756953SMark Johnston bool unpriv) 3424f02a7d7SMark Johnston { 3434f02a7d7SMark Johnston const char *user; 3444f02a7d7SMark Johnston pid_t child; 3454f02a7d7SMark Johnston 3464f02a7d7SMark Johnston if (unpriv) { 3474f02a7d7SMark Johnston if (!atf_tc_has_config_var(tc, "unprivileged_user")) 3484f02a7d7SMark Johnston atf_tc_skip("unprivileged_user not set"); 3494f02a7d7SMark Johnston user = atf_tc_get_config_var(tc, "unprivileged_user"); 3504f02a7d7SMark Johnston } else { 3514f02a7d7SMark Johnston user = NULL; 3524f02a7d7SMark Johnston } 3534f02a7d7SMark Johnston 3544f02a7d7SMark Johnston child = fork(); 3554f02a7d7SMark Johnston ATF_REQUIRE(child != -1); 3564f02a7d7SMark Johnston if (child == 0) { 3574f02a7d7SMark Johnston int s; 3584f02a7d7SMark Johnston 3594f02a7d7SMark Johnston if (user != NULL) { 3604f02a7d7SMark Johnston struct passwd *passwd; 3614f02a7d7SMark Johnston 3624f02a7d7SMark Johnston passwd = getpwnam(user); 3634f02a7d7SMark Johnston if (seteuid(passwd->pw_uid) != 0) 3644f02a7d7SMark Johnston _exit(1); 3654f02a7d7SMark Johnston } 3664f02a7d7SMark Johnston 3674f02a7d7SMark Johnston s = socket(sa->sa_family, type, 0); 3684f02a7d7SMark Johnston if (s < 0) 3694f02a7d7SMark Johnston _exit(2); 3704f02a7d7SMark Johnston if (bind(s, sa, sa->sa_len) == 0) 3714f02a7d7SMark Johnston _exit(3); 3724f02a7d7SMark Johnston if (errno != EADDRINUSE) 3734f02a7d7SMark Johnston _exit(4); 3744f02a7d7SMark Johnston if (opt != 0) { 3754f02a7d7SMark Johnston if (setsockopt(s, SOL_SOCKET, opt, &(int){1}, 3764f02a7d7SMark Johnston sizeof(int)) != 0) 3774f02a7d7SMark Johnston _exit(5); 3784f02a7d7SMark Johnston } 3794f02a7d7SMark Johnston if (bind(s, sa, sa->sa_len) == 0) 3804f02a7d7SMark Johnston _exit(6); 3814f02a7d7SMark Johnston if (errno != EADDRINUSE) 3824f02a7d7SMark Johnston _exit(7); 3834f02a7d7SMark Johnston _exit(0); 3844f02a7d7SMark Johnston } else { 3854f02a7d7SMark Johnston int status; 3864f02a7d7SMark Johnston 3874f02a7d7SMark Johnston ATF_REQUIRE_EQ(waitpid(child, &status, 0), child); 3884f02a7d7SMark Johnston ATF_REQUIRE(WIFEXITED(status)); 3894f02a7d7SMark Johnston status = WEXITSTATUS(status); 3904f02a7d7SMark Johnston ATF_REQUIRE_MSG(status == 0 || status == 6, 3914f02a7d7SMark Johnston "child exited with %d", status); 3924f02a7d7SMark Johnston return (status == 6); 3934f02a7d7SMark Johnston } 3944f02a7d7SMark Johnston } 3954f02a7d7SMark Johnston 3964f02a7d7SMark Johnston static bool 3974f02a7d7SMark Johnston child_bind_priv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt) 3984f02a7d7SMark Johnston { 3994f02a7d7SMark Johnston return (child_bind(tc, type, sa, opt, false)); 4004f02a7d7SMark Johnston } 4014f02a7d7SMark Johnston 4024f02a7d7SMark Johnston static bool 4034f02a7d7SMark Johnston child_bind_unpriv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt) 4044f02a7d7SMark Johnston { 4054f02a7d7SMark Johnston return (child_bind(tc, type, sa, opt, true)); 4064f02a7d7SMark Johnston } 4074f02a7d7SMark Johnston 4084f02a7d7SMark Johnston static int 4094f02a7d7SMark Johnston bind_socket(int domain, int type, int opt, bool unspec, struct sockaddr *sa) 4104f02a7d7SMark Johnston { 4114f02a7d7SMark Johnston socklen_t slen; 4124f02a7d7SMark Johnston int s; 4134f02a7d7SMark Johnston 4144f02a7d7SMark Johnston s = socket(domain, type, 0); 4154f02a7d7SMark Johnston ATF_REQUIRE(s >= 0); 4164f02a7d7SMark Johnston 4174f02a7d7SMark Johnston if (domain == AF_INET) { 4184f02a7d7SMark Johnston struct sockaddr_in sin; 4194f02a7d7SMark Johnston 4204f02a7d7SMark Johnston bzero(&sin, sizeof(sin)); 4214f02a7d7SMark Johnston sin.sin_family = AF_INET; 4224f02a7d7SMark Johnston sin.sin_len = sizeof(sin); 4234f02a7d7SMark Johnston sin.sin_addr.s_addr = htonl(unspec ? 4244f02a7d7SMark Johnston INADDR_ANY : INADDR_LOOPBACK); 4254f02a7d7SMark Johnston sin.sin_port = htons(0); 4264f02a7d7SMark Johnston ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0); 4274f02a7d7SMark Johnston 4284f02a7d7SMark Johnston slen = sizeof(sin); 4294f02a7d7SMark Johnston } else /* if (domain == AF_INET6) */ { 4304f02a7d7SMark Johnston struct sockaddr_in6 sin6; 4314f02a7d7SMark Johnston 4324f02a7d7SMark Johnston bzero(&sin6, sizeof(sin6)); 4334f02a7d7SMark Johnston sin6.sin6_family = AF_INET6; 4344f02a7d7SMark Johnston sin6.sin6_len = sizeof(sin6); 4354f02a7d7SMark Johnston sin6.sin6_addr = unspec ? in6addr_any : in6addr_loopback; 4364f02a7d7SMark Johnston sin6.sin6_port = htons(0); 4374f02a7d7SMark Johnston ATF_REQUIRE(bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == 0); 4384f02a7d7SMark Johnston 4394f02a7d7SMark Johnston slen = sizeof(sin6); 4404f02a7d7SMark Johnston } 4414f02a7d7SMark Johnston 4424f02a7d7SMark Johnston if (opt != 0) { 4434f02a7d7SMark Johnston ATF_REQUIRE(setsockopt(s, SOL_SOCKET, opt, &(int){1}, 4444f02a7d7SMark Johnston sizeof(int)) == 0); 4454f02a7d7SMark Johnston } 4464f02a7d7SMark Johnston 4474f02a7d7SMark Johnston ATF_REQUIRE(getsockname(s, sa, &slen) == 0); 4484f02a7d7SMark Johnston 4494f02a7d7SMark Johnston return (s); 4504f02a7d7SMark Johnston } 4514f02a7d7SMark Johnston 4524f02a7d7SMark Johnston static void 4534f02a7d7SMark Johnston multibind_test(const atf_tc_t *tc, int domain, int type) 4544f02a7d7SMark Johnston { 4554f02a7d7SMark Johnston struct sockaddr_storage ss; 4564f02a7d7SMark Johnston int opts[4] = { 0, SO_REUSEADDR, SO_REUSEPORT, SO_REUSEPORT_LB }; 4574f02a7d7SMark Johnston int s; 4584f02a7d7SMark Johnston bool flags[2] = { false, true }; 4594f02a7d7SMark Johnston bool res; 4604f02a7d7SMark Johnston 4614f02a7d7SMark Johnston for (size_t flagi = 0; flagi < nitems(flags); flagi++) { 4624f02a7d7SMark Johnston for (size_t opti = 0; opti < nitems(opts); opti++) { 4634f02a7d7SMark Johnston s = bind_socket(domain, type, opts[opti], flags[flagi], 4644f02a7d7SMark Johnston (struct sockaddr *)&ss); 4654f02a7d7SMark Johnston for (size_t optj = 0; optj < nitems(opts); optj++) { 4664f02a7d7SMark Johnston int opt; 4674f02a7d7SMark Johnston 4684f02a7d7SMark Johnston opt = opts[optj]; 4694f02a7d7SMark Johnston res = child_bind_priv(tc, type, 4704f02a7d7SMark Johnston (struct sockaddr *)&ss, opt); 4714f02a7d7SMark Johnston /* 4724f02a7d7SMark Johnston * Multi-binding is only allowed when both 4734f02a7d7SMark Johnston * sockets have SO_REUSEPORT or SO_REUSEPORT_LB 4744f02a7d7SMark Johnston * set. 4754f02a7d7SMark Johnston */ 4764f02a7d7SMark Johnston if (opts[opti] != 0 && 4774f02a7d7SMark Johnston opts[opti] != SO_REUSEADDR && opti == optj) 4784f02a7d7SMark Johnston ATF_REQUIRE(res); 4794f02a7d7SMark Johnston else 4804f02a7d7SMark Johnston ATF_REQUIRE(!res); 4814f02a7d7SMark Johnston 4824f02a7d7SMark Johnston res = child_bind_unpriv(tc, type, 4834f02a7d7SMark Johnston (struct sockaddr *)&ss, opt); 4844f02a7d7SMark Johnston /* 4854f02a7d7SMark Johnston * Multi-binding is only allowed when both 4864f02a7d7SMark Johnston * sockets have the same owner. 4874f02a7d7SMark Johnston */ 4884f02a7d7SMark Johnston ATF_REQUIRE(!res); 4894f02a7d7SMark Johnston } 4904f02a7d7SMark Johnston ATF_REQUIRE(close(s) == 0); 4914f02a7d7SMark Johnston } 4924f02a7d7SMark Johnston } 4934f02a7d7SMark Johnston } 4944f02a7d7SMark Johnston 4954f02a7d7SMark Johnston /* 4964f02a7d7SMark Johnston * Try to bind two sockets to the same address/port tuple. Under some 4974f02a7d7SMark Johnston * conditions this is permitted. 4984f02a7d7SMark Johnston */ 4994f02a7d7SMark Johnston ATF_TC(socket_afinet_multibind); 5004f02a7d7SMark Johnston ATF_TC_HEAD(socket_afinet_multibind, tc) 5014f02a7d7SMark Johnston { 5024f02a7d7SMark Johnston atf_tc_set_md_var(tc, "require.user", "root"); 5034f02a7d7SMark Johnston atf_tc_set_md_var(tc, "require.config", "unprivileged_user"); 5044f02a7d7SMark Johnston } 5054f02a7d7SMark Johnston ATF_TC_BODY(socket_afinet_multibind, tc) 5064f02a7d7SMark Johnston { 5074f02a7d7SMark Johnston multibind_test(tc, AF_INET, SOCK_STREAM); 5084f02a7d7SMark Johnston multibind_test(tc, AF_INET, SOCK_DGRAM); 5094f02a7d7SMark Johnston multibind_test(tc, AF_INET6, SOCK_STREAM); 5104f02a7d7SMark Johnston multibind_test(tc, AF_INET6, SOCK_DGRAM); 5114f02a7d7SMark Johnston } 5124f02a7d7SMark Johnston 513*c9756953SMark Johnston static void 514*c9756953SMark Johnston bind_connected_port_test(const atf_tc_t *tc, int domain) 515*c9756953SMark Johnston { 516*c9756953SMark Johnston struct sockaddr_in sin; 517*c9756953SMark Johnston struct sockaddr_in6 sin6; 518*c9756953SMark Johnston struct sockaddr *sinp; 519*c9756953SMark Johnston int error, sd[3], tmp; 520*c9756953SMark Johnston bool res; 521*c9756953SMark Johnston 522*c9756953SMark Johnston /* 523*c9756953SMark Johnston * Create a connected socket pair. 524*c9756953SMark Johnston */ 525*c9756953SMark Johnston sd[0] = socket(domain, SOCK_STREAM, 0); 526*c9756953SMark Johnston ATF_REQUIRE_MSG(sd[0] >= 0, "socket failed: %s", strerror(errno)); 527*c9756953SMark Johnston sd[1] = socket(domain, SOCK_STREAM, 0); 528*c9756953SMark Johnston ATF_REQUIRE_MSG(sd[1] >= 0, "socket failed: %s", strerror(errno)); 529*c9756953SMark Johnston if (domain == PF_INET) { 530*c9756953SMark Johnston memset(&sin, 0, sizeof(sin)); 531*c9756953SMark Johnston sin.sin_family = AF_INET; 532*c9756953SMark Johnston sin.sin_len = sizeof(sin); 533*c9756953SMark Johnston sin.sin_addr.s_addr = htonl(INADDR_ANY); 534*c9756953SMark Johnston sin.sin_port = htons(0); 535*c9756953SMark Johnston sinp = (struct sockaddr *)&sin; 536*c9756953SMark Johnston } else { 537*c9756953SMark Johnston ATF_REQUIRE(domain == PF_INET6); 538*c9756953SMark Johnston memset(&sin6, 0, sizeof(sin6)); 539*c9756953SMark Johnston sin6.sin6_family = AF_INET6; 540*c9756953SMark Johnston sin6.sin6_len = sizeof(sin6); 541*c9756953SMark Johnston sin6.sin6_addr = in6addr_any; 542*c9756953SMark Johnston sin6.sin6_port = htons(0); 543*c9756953SMark Johnston sinp = (struct sockaddr *)&sin6; 544*c9756953SMark Johnston } 545*c9756953SMark Johnston 546*c9756953SMark Johnston error = bind(sd[0], sinp, sinp->sa_len); 547*c9756953SMark Johnston ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno)); 548*c9756953SMark Johnston error = listen(sd[0], 1); 549*c9756953SMark Johnston ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno)); 550*c9756953SMark Johnston 551*c9756953SMark Johnston error = getsockname(sd[0], sinp, &(socklen_t){ sinp->sa_len }); 552*c9756953SMark Johnston ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno)); 553*c9756953SMark Johnston 554*c9756953SMark Johnston error = connect(sd[1], sinp, sinp->sa_len); 555*c9756953SMark Johnston ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno)); 556*c9756953SMark Johnston tmp = accept(sd[0], NULL, NULL); 557*c9756953SMark Johnston ATF_REQUIRE_MSG(tmp >= 0, "accept failed: %s", strerror(errno)); 558*c9756953SMark Johnston ATF_REQUIRE(close(sd[0]) == 0); 559*c9756953SMark Johnston sd[0] = tmp; 560*c9756953SMark Johnston 561*c9756953SMark Johnston /* bind() should succeed even from an unprivileged user. */ 562*c9756953SMark Johnston res = child_bind(tc, SOCK_STREAM, sinp, 0, false); 563*c9756953SMark Johnston ATF_REQUIRE(!res); 564*c9756953SMark Johnston res = child_bind(tc, SOCK_STREAM, sinp, 0, true); 565*c9756953SMark Johnston ATF_REQUIRE(!res); 566*c9756953SMark Johnston } 567*c9756953SMark Johnston 568*c9756953SMark Johnston /* 569*c9756953SMark Johnston * Normally bind() prevents port stealing by a different user, even when 570*c9756953SMark Johnston * SO_REUSE* are specified. However, if the port is bound by a connected 571*c9756953SMark Johnston * socket, then it's fair game. 572*c9756953SMark Johnston */ 573*c9756953SMark Johnston ATF_TC(socket_afinet_bind_connected_port); 574*c9756953SMark Johnston ATF_TC_HEAD(socket_afinet_bind_connected_port, tc) 575*c9756953SMark Johnston { 576*c9756953SMark Johnston atf_tc_set_md_var(tc, "require.user", "root"); 577*c9756953SMark Johnston atf_tc_set_md_var(tc, "require.config", "unprivileged_user"); 578*c9756953SMark Johnston } 579*c9756953SMark Johnston ATF_TC_BODY(socket_afinet_bind_connected_port, tc) 580*c9756953SMark Johnston { 581*c9756953SMark Johnston bind_connected_port_test(tc, AF_INET); 582*c9756953SMark Johnston bind_connected_port_test(tc, AF_INET6); 583*c9756953SMark Johnston } 584*c9756953SMark Johnston 585aa321596SBjoern A. Zeeb ATF_TP_ADD_TCS(tp) 586aa321596SBjoern A. Zeeb { 587aa321596SBjoern A. Zeeb ATF_TP_ADD_TC(tp, socket_afinet); 588aa321596SBjoern A. Zeeb ATF_TP_ADD_TC(tp, socket_afinet_bind_zero); 589aa321596SBjoern A. Zeeb ATF_TP_ADD_TC(tp, socket_afinet_bind_ok); 5903aaaa2efSThomas Munro ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup); 5913aaaa2efSThomas Munro ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup); 592b1c66bc4SMark Johnston ATF_TP_ADD_TC(tp, socket_afinet_stream_reconnect); 5934f02a7d7SMark Johnston ATF_TP_ADD_TC(tp, socket_afinet_bindany); 5944f02a7d7SMark Johnston ATF_TP_ADD_TC(tp, socket_afinet_multibind); 595*c9756953SMark Johnston ATF_TP_ADD_TC(tp, socket_afinet_bind_connected_port); 596aa321596SBjoern A. Zeeb 597aa321596SBjoern A. Zeeb return atf_no_error(); 598aa321596SBjoern A. Zeeb } 599