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