Home | History | Annotate | Line # | Download | only in net
      1 /*	$NetBSD: t_unix.c,v 1.28 2025/03/27 11:01:07 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Christos Zoulas.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #define _GNU_SOURCE
     40 #include <sys/cdefs.h>
     41 #ifdef __RCSID
     42 __RCSID("$Id: t_unix.c,v 1.28 2025/03/27 11:01:07 riastradh Exp $");
     43 #else
     44 #define getprogname() argv[0]
     45 #endif
     46 
     47 #ifdef __linux__
     48 #define LX -1
     49 #else
     50 #define LX
     51 #endif
     52 
     53 #include <sys/param.h>
     54 
     55 #include <sys/socket.h>
     56 #include <sys/stat.h>
     57 #include <sys/un.h>
     58 #include <sys/wait.h>
     59 
     60 #include <err.h>
     61 #include <errno.h>
     62 #include <poll.h>
     63 #include <stdbool.h>
     64 #include <stddef.h>
     65 #include <stdint.h>
     66 #include <stdio.h>
     67 #include <stdlib.h>
     68 #include <string.h>
     69 #include <unistd.h>
     70 
     71 #include "test.h"
     72 
     73 #define UID 666
     74 #define GID 999
     75 
     76 uid_t srvruid, clntuid;
     77 gid_t srvrgid, clntgid;
     78 
     79 #define OF offsetof(struct sockaddr_un, sun_path)
     80 
     81 static void
     82 print(const char *msg, struct sockaddr_un *addr, socklen_t len)
     83 {
     84 	size_t i;
     85 
     86 	printf("%s: client socket length: %zu\n", msg, (size_t)len);
     87 	printf("%s: client family %d\n", msg, addr->sun_family);
     88 #ifdef BSD4_4
     89 	printf("%s: client len %d\n", msg, addr->sun_len);
     90 #endif
     91 	printf("%s: socket name: ", msg);
     92 	for (i = 0; i < len - OF; i++) {
     93 		int ch = addr->sun_path[i];
     94 		if (ch < ' ' || '~' < ch)
     95 			printf("\\x%02x", ch);
     96 		else
     97 			printf("%c", ch);
     98 	}
     99 	printf("\n");
    100 }
    101 
    102 static int
    103 acc(int s)
    104 {
    105 	char guard1;
    106 	struct sockaddr_un sun;
    107 	char guard2;
    108 	socklen_t len;
    109 
    110 	guard1 = guard2 = 's';
    111 
    112 	memset(&sun, 0, sizeof(sun));
    113 	len = sizeof(sun);
    114 	if ((s = accept(s, (struct sockaddr *)&sun, &len)) == -1)
    115 		FAIL("accept");
    116 	if (guard1 != 's')
    117 		FAIL("guard1 = '%c'", guard1);
    118 	if (guard2 != 's')
    119 		FAIL("guard2 = '%c'", guard2);
    120 #ifdef DEBUG
    121 	print("accept", &sun, len);
    122 #endif
    123 	if (len != 2)
    124 		FAIL("len %d != 2", len);
    125 	if (sun.sun_family != AF_UNIX)
    126 		FAIL("sun->sun_family %d != AF_UNIX", sun.sun_family);
    127 #ifdef BSD4_4
    128 	if (sun.sun_len != 2)
    129 		FAIL("sun->sun_len %d != 2", sun.sun_len);
    130 #endif
    131 	for (size_t i = 0; i < sizeof(sun.sun_path); i++)
    132 		if (sun.sun_path[i])
    133 			FAIL("sun.sun_path[%zu] %d != NULL", i,
    134 			    sun.sun_path[i]);
    135 	return s;
    136 fail:
    137 	if (s != -1)
    138 		close(s);
    139 	return -1;
    140 }
    141 
    142 static int
    143 peercred(int s, uid_t *euid, gid_t *egid, pid_t *pid)
    144 {
    145 #ifdef SO_PEERCRED
    146 	/* Linux */
    147 # define unpcbid ucred
    148 # define unp_euid uid
    149 # define unp_egid gid
    150 # define unp_pid pid
    151 # define LOCAL_PEEREID SO_PEERCRED
    152 # define LEVEL SOL_SOCKET
    153 #else
    154 # define LEVEL SOL_LOCAL
    155 #endif
    156 
    157 #ifdef LOCAL_PEEREID
    158 	/* NetBSD */
    159 	struct unpcbid cred;
    160 	socklen_t crl;
    161 	crl = sizeof(cred);
    162 	if (getsockopt(s, LEVEL, LOCAL_PEEREID, &cred, &crl) == -1)
    163 		return -1;
    164 	*euid = cred.unp_euid;
    165 	*egid = cred.unp_egid;
    166 	*pid = cred.unp_pid;
    167 	return 0;
    168 #else
    169 	/* FreeBSD */
    170 	*pid = -1;
    171 	return getpeereid(s, euid, egid);
    172 #endif
    173 }
    174 
    175 static int
    176 check_cred(int fd, bool statit, pid_t checkpid, pid_t otherpid, const char *s)
    177 {
    178 	pid_t pid;
    179 	uid_t euid, uid;
    180 	gid_t egid, gid;
    181 
    182 	if (statit) {
    183 		struct stat st;
    184 		if (fstat(fd, &st) == -1)
    185 			FAIL("fstat (%s)", s);
    186 		euid = st.st_uid;
    187 		egid = st.st_gid;
    188 		pid = checkpid;
    189 	} else {
    190 		if (peercred(fd, &euid, &egid, &pid) == -1)
    191 			FAIL("peercred (%s)", s);
    192 	}
    193 	printf("%s(%s) euid=%jd egid=%jd pid=%jd\n",
    194 	    statit ? "fstat" : "peercred", s,
    195 	    (intmax_t)euid, (intmax_t)egid, (intmax_t)pid);
    196 
    197 	if (statit) {
    198 		if (strcmp(s, "server") == 0) {
    199 			uid = srvruid;
    200 			gid = srvrgid;
    201 		} else {
    202 			uid = clntuid;
    203 			gid = clntgid;
    204 		}
    205 	} else {
    206 		if (checkpid != otherpid && strcmp(s, "server") == 0) {
    207 			uid = clntuid;
    208 			gid = clntgid;
    209 		} else {
    210 			uid = srvruid;
    211 			gid = srvrgid;
    212 		}
    213 	}
    214 	fflush(stdout);
    215 
    216 	CHECK_EQUAL(euid, uid, s);
    217 	CHECK_EQUAL(egid, gid, s);
    218 	CHECK_EQUAL(pid, checkpid, s);
    219 	return 0;
    220 fail:
    221 	return -1;
    222 }
    223 
    224 static int
    225 test(bool forkit, bool closeit, bool statit, size_t len)
    226 {
    227 	size_t slen;
    228 	socklen_t sl;
    229 	int srvr = -1, clnt = -1, acpt = -1;
    230 	pid_t srvrpid, clntpid;
    231 	struct sockaddr_un *sock_addr = NULL, *sun = NULL;
    232 	socklen_t sock_addrlen;
    233 	socklen_t peer_addrlen;
    234 	struct sockaddr_un peer_addr;
    235 
    236 	srvruid = geteuid();
    237 	srvrgid = getegid();
    238 	srvrpid = clntpid = getpid();
    239 	srvr = socket(AF_UNIX, SOCK_STREAM, 0);
    240 	if (srvr == -1)
    241 		FAIL("socket(server)");
    242 
    243 	slen = len + OF + 1;
    244 
    245 	if ((sun = calloc(1, slen)) == NULL)
    246 		FAIL("calloc");
    247 
    248 	if (mkdir("sock", 0777) == -1)
    249 		FAIL("mkdir");
    250 
    251 	if (chdir("sock") == -1)
    252 		FAIL("chdir");
    253 
    254 	memset(sun->sun_path, 'a', len);
    255 	sun->sun_path[len] = '\0';
    256 	(void)unlink(sun->sun_path);
    257 
    258 	sl = SUN_LEN(sun);
    259 #ifdef BSD4_4
    260 	sun->sun_len = sl;
    261 #endif
    262 	sun->sun_family = AF_UNIX;
    263 
    264 	unlink(sun->sun_path);
    265 
    266 	if (bind(srvr, (struct sockaddr *)sun, sl) == -1) {
    267 		if (errno == EINVAL && sl >= 256) {
    268 			close(srvr);
    269 			return -1;
    270 		}
    271 		FAIL("bind");
    272 	}
    273 	if (chmod(sun->sun_path, 0777) == -1)
    274 		FAIL("chmod `%s'", sun->sun_path);
    275 
    276 	if (listen(srvr, SOMAXCONN) == -1)
    277 		FAIL("listen");
    278 
    279 	if (forkit) {
    280 		switch (clntpid = fork()) {
    281 		case 0:	/* child */
    282 			srvrpid = getppid();
    283 			clntpid = getpid();
    284 			if (srvruid == 0) {
    285 				setgid(clntgid = GID);
    286 				setuid(clntuid = UID);
    287 			} else {
    288 				clntgid = srvrgid;
    289 				clntuid = srvruid;
    290 			}
    291 			break;
    292 		case -1:
    293 			FAIL("fork");
    294 		default:
    295 			if (srvruid == 0) {
    296 				clntgid = GID;
    297 				clntuid = UID;
    298 			}
    299 			break;
    300 		}
    301 	}
    302 
    303 	if (clntpid == getpid()) {
    304 		clnt = socket(AF_UNIX, SOCK_STREAM, 0);
    305 		if (clnt == -1)
    306 			FAIL("socket(client)");
    307 
    308 		if (connect(clnt, (const struct sockaddr *)sun, sl) == -1)
    309 			FAIL("connect");
    310 		check_cred(clnt, statit, srvrpid, clntpid, "client");
    311 
    312 	}
    313 
    314 	if (srvrpid == getpid()) {
    315 		acpt = acc(srvr);
    316 		peer_addrlen = sizeof(peer_addr);
    317 		memset(&peer_addr, 0, sizeof(peer_addr));
    318 		if (getpeername(acpt, (struct sockaddr *)&peer_addr,
    319 		    &peer_addrlen) == -1)
    320 			FAIL("getpeername");
    321 		print("peer", &peer_addr, peer_addrlen);
    322 	}
    323 
    324 	if (clntpid == getpid()) {
    325 		if (closeit) {
    326 			if (close(clnt) == -1)
    327 				FAIL("close");
    328 			clnt = -1;
    329 		}
    330 	}
    331 
    332 	if (srvrpid == getpid()) {
    333 		check_cred(acpt, statit, clntpid, srvrpid, "server");
    334 		if ((sock_addr = calloc(1, slen)) == NULL)
    335 			FAIL("calloc");
    336 		sock_addrlen = slen;
    337 		if (getsockname(srvr, (struct sockaddr *)sock_addr,
    338 		    &sock_addrlen) == -1)
    339 			FAIL("getsockname");
    340 		print("sock", sock_addr, sock_addrlen);
    341 
    342 		if (sock_addr->sun_family != AF_UNIX)
    343 			FAIL("sock_addr->sun_family %d != AF_UNIX",
    344 			    sock_addr->sun_family);
    345 
    346 		len += OF;
    347 		if (sock_addrlen LX != len)
    348 			FAIL("sock_addr_len %zu != %zu", (size_t)sock_addrlen,
    349 			    len);
    350 #ifdef BSD4_4
    351 		if (sock_addr->sun_len != sl)
    352 			FAIL("sock_addr.sun_len %d != %zu", sock_addr->sun_len,
    353 			    (size_t)sl);
    354 #endif
    355 		for (size_t i = 0; i < slen - OF; i++)
    356 			if (sock_addr->sun_path[i] != sun->sun_path[i])
    357 				FAIL("sock_addr.sun_path[%zu] %d != "
    358 				    "sun->sun_path[%zu] %d\n", i,
    359 				    sock_addr->sun_path[i], i,
    360 				    sun->sun_path[i]);
    361 
    362 		if (acpt != -1)
    363 			(void)close(acpt);
    364 		if (srvr != -1)
    365 			(void)close(srvr);
    366 		free(sock_addr);
    367 		sock_addr = NULL;
    368 		if (forkit && waitpid(clntpid, NULL, 0) == -1)
    369 			FAIL("waitpid");
    370 	}
    371 	if (clnt != -1 && !closeit)
    372 		(void)close(clnt);
    373 
    374 	free(sock_addr);
    375 	free(sun);
    376 	if (forkit && clntpid == getpid())
    377 		_exit(0);
    378 	return 0;
    379 fail:
    380 	if (srvrpid == getpid()) {
    381 		if (acpt != -1)
    382 			(void)close(acpt);
    383 		if (srvr != -1)
    384 			(void)close(srvr);
    385 	}
    386 	if (clntpid == getpid()) {
    387 		if (clnt != -1 && !closeit)
    388 			(void)close(clnt);
    389 	}
    390 	free(sock_addr);
    391 	free(sun);
    392 	if (forkit && clntpid == getpid())
    393 		_exit(0);
    394 	return -1;
    395 }
    396 
    397 #ifndef TEST
    398 
    399 #include "h_macros.h"
    400 
    401 ATF_TC(sockaddr_un_len_exceed);
    402 ATF_TC_HEAD(sockaddr_un_len_exceed, tc)
    403 {
    404 
    405 	atf_tc_set_md_var(tc, "descr", "Check that exceeding the size of "
    406 	    "unix domain sockets does not trash memory or kernel when "
    407 	    "exceeding the size of the fixed sun_path");
    408 }
    409 
    410 ATF_TC_BODY(sockaddr_un_len_exceed, tc)
    411 {
    412 	ATF_REQUIRE_MSG(test(false, false, false, 254) == -1,
    413 	    "test(false, false, false, 254): %s", strerror(errno));
    414 }
    415 
    416 ATF_TC(sockaddr_un_len_max);
    417 ATF_TC_HEAD(sockaddr_un_len_max, tc)
    418 {
    419 
    420 	atf_tc_set_md_var(tc, "descr", "Check that we can use the maximum "
    421 	    "unix domain socket pathlen (253): 255 - sizeof(sun_len) - "
    422 	    "sizeof(sun_family)");
    423 }
    424 
    425 ATF_TC_BODY(sockaddr_un_len_max, tc)
    426 {
    427 	ATF_REQUIRE_MSG(test(false, false, false, 253) == 0,
    428 	    "test(false, false, false, 253): %s", strerror(errno));
    429 }
    430 
    431 ATF_TC(sockaddr_un_closed);
    432 ATF_TC_HEAD(sockaddr_un_closed, tc)
    433 {
    434 
    435 	atf_tc_set_md_var(tc, "descr", "Check that we can use the accepted "
    436 	    "address of unix domain socket when closed");
    437 }
    438 
    439 ATF_TC_BODY(sockaddr_un_closed, tc)
    440 {
    441 	ATF_REQUIRE_MSG(test(false, true, false, 100) == 0,
    442 	    "test(false, true, false, 100): %s", strerror(errno));
    443 }
    444 
    445 ATF_TC(sockaddr_un_local_connwait);
    446 ATF_TC_HEAD(sockaddr_un_local_connwait, tc)
    447 {
    448 
    449 	atf_tc_set_md_var(tc, "descr", "Check that LOCAL_CONNWAIT works");
    450 }
    451 
    452 ATF_TC_BODY(sockaddr_un_local_connwait, tc)
    453 {
    454 	/* too annoying to fit this into the test(...) framework above */
    455 	struct sockaddr_un sun = {.sun_family = AF_UNIX, .sun_path = "sock"};
    456 	int listener, conn, acc;
    457 	const int one = 1;
    458 	struct pollfd pfd;
    459 	int nfd, error;
    460 	socklen_t errorlen = sizeof(error);
    461 
    462 	/*
    463 	 * Create and bind a listening socket.
    464 	 */
    465 	RL(listener = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
    466 	RL(bind(listener, (const struct sockaddr *)&sun, sizeof(sun)));
    467 	RL(listen(listener, 5));
    468 
    469 	/*
    470 	 * Nobody's trying to connect, so accept would block.
    471 	 */
    472 	ATF_REQUIRE_ERRNO(EAGAIN, accept(listener, NULL, NULL) == -1);
    473 
    474 	/*
    475 	 * Connect should succeed immediately even though nobody is
    476 	 * waiting to accept on the other end yet.
    477 	 */
    478 	RL(conn = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
    479 	RL(connect(conn, (const struct sockaddr *)&sun, sizeof(sun)));
    480 
    481 	/*
    482 	 * Accept should succeed now that a client connected.  Close
    483 	 * both sides; we're done with this connection and will try
    484 	 * again with LOCAL_CONNWAIT next.
    485 	 */
    486 	RL(acc = accept(listener, NULL, NULL));
    487 	RL(close(acc));
    488 	RL(close(conn));
    489 
    490 	/*
    491 	 * If we set LOCAL_CONNWAIT, connect should fail, but with
    492 	 * EINPROGRESS -- note: not EAGAIN, because it has changed
    493 	 * state.  If we try to connect again, it should fail with
    494 	 * EALREADY because the connection is already pending.
    495 	 */
    496 	RL(conn = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
    497 	RL(setsockopt(conn, SOL_LOCAL, LOCAL_CONNWAIT, &one, sizeof(one)));
    498 	ATF_REQUIRE_ERRNO(EINPROGRESS,
    499 	    connect(conn, (const struct sockaddr *)&sun, sizeof(sun)) == -1);
    500 	ATF_REQUIRE_ERRNO(EALREADY,
    501 	    connect(conn, (const struct sockaddr *)&sun, sizeof(sun)) == -1);
    502 
    503 	/*
    504 	 * Accept should succeed immediately now.
    505 	 */
    506 	RL(acc = accept(listener, NULL, NULL));
    507 
    508 	/*
    509 	 * Verify via poll(2) that the pending connect(2) has finished
    510 	 * -- it will report POLLOUT when that happens.  And then
    511 	 * verify that there was no error.
    512 	 */
    513 	pfd = (struct pollfd){.fd = conn, .events = POLLOUT};
    514 	RL(nfd = poll(&pfd, 1, 0));
    515 	ATF_REQUIRE_MSG(pfd.revents & POLLOUT, "revents=0x%x", pfd.revents);
    516 	RL(getsockopt(conn, SOL_SOCKET, SO_ERROR, &error, &errorlen));
    517 	ATF_REQUIRE_MSG(errorlen == sizeof(error), "errorlen=%d", errorlen);
    518 	ATF_REQUIRE_MSG(error == 0, "error=%d", error);
    519 
    520 	RL(close(acc));
    521 	RL(close(conn));
    522 	RL(close(listener));
    523 }
    524 
    525 ATF_TC(sockaddr_un_local_peereid);
    526 ATF_TC_HEAD(sockaddr_un_local_peereid, tc)
    527 {
    528 
    529 	atf_tc_set_md_var(tc, "descr", "Check that we get the right information"
    530 	    " from LOCAL_PEEREID");
    531 }
    532 
    533 ATF_TC_BODY(sockaddr_un_local_peereid, tc)
    534 {
    535 	ATF_REQUIRE_MSG(test(true, true, false, 100) == 0,
    536 	    "test(true, true, false, 100): %s", strerror(errno));
    537 }
    538 
    539 ATF_TC(sockaddr_un_fstat);
    540 ATF_TC_HEAD(sockaddr_un_fstat, tc)
    541 {
    542 
    543 	atf_tc_set_md_var(tc, "descr", "Check that we get the right information"
    544 	    " from fstat");
    545 }
    546 
    547 ATF_TC_BODY(sockaddr_un_fstat, tc)
    548 {
    549 	ATF_REQUIRE_MSG(test(true, true, true, 100) == 0,
    550 	    "test(true, true, true, 100): %s", strerror(errno));
    551 }
    552 
    553 ATF_TP_ADD_TCS(tp)
    554 {
    555 
    556 	ATF_TP_ADD_TC(tp, sockaddr_un_len_exceed);
    557 	ATF_TP_ADD_TC(tp, sockaddr_un_len_max);
    558 	ATF_TP_ADD_TC(tp, sockaddr_un_closed);
    559 	ATF_TP_ADD_TC(tp, sockaddr_un_local_connwait);
    560 	ATF_TP_ADD_TC(tp, sockaddr_un_local_peereid);
    561 	ATF_TP_ADD_TC(tp, sockaddr_un_fstat);
    562 	return atf_no_error();
    563 }
    564 #else
    565 int
    566 main(int argc, char *argv[])
    567 {
    568 	size_t len;
    569 
    570 	if (argc == 1) {
    571 		fprintf(stderr, "Usage: %s <len>\n", getprogname());
    572 		return EXIT_FAILURE;
    573 	}
    574 	test(false, false, false, atoi(argv[1]));
    575 	test(false, true, false, atoi(argv[1]));
    576 	test(true, false, false, atoi(argv[1]));
    577 	test(true, true, false, atoi(argv[1]));
    578 	test(true, true, true, atoi(argv[1]));
    579 }
    580 #endif
    581