1 1.12 nia /* $NetBSD: unfdpass.c,v 1.12 2021/08/08 20:54:48 nia Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /*- 4 1.1 thorpej * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.1 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.1 thorpej * NASA Ames Research Center. 10 1.1 thorpej * 11 1.1 thorpej * Redistribution and use in source and binary forms, with or without 12 1.1 thorpej * modification, are permitted provided that the following conditions 13 1.1 thorpej * are met: 14 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer. 16 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 18 1.1 thorpej * documentation and/or other materials provided with the distribution. 19 1.1 thorpej * 20 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.1 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.1 thorpej */ 32 1.1 thorpej 33 1.1 thorpej /* 34 1.2 thorpej * Test passing of file descriptors and credentials over Unix domain sockets. 35 1.1 thorpej */ 36 1.1 thorpej 37 1.1 thorpej #include <sys/param.h> 38 1.1 thorpej #include <sys/socket.h> 39 1.1 thorpej #include <sys/time.h> 40 1.1 thorpej #include <sys/wait.h> 41 1.1 thorpej #include <sys/un.h> 42 1.4 mycroft #include <sys/uio.h> 43 1.11 christos #include <sys/stat.h> 44 1.4 mycroft 45 1.1 thorpej #include <err.h> 46 1.2 thorpej #include <errno.h> 47 1.1 thorpej #include <fcntl.h> 48 1.2 thorpej #include <signal.h> 49 1.1 thorpej #include <stdio.h> 50 1.1 thorpej #include <string.h> 51 1.6 thorpej #include <stdlib.h> 52 1.1 thorpej #include <unistd.h> 53 1.1 thorpej 54 1.2 thorpej #define SOCK_NAME "test-sock" 55 1.1 thorpej 56 1.7 perry int main(int, char *[]); 57 1.7 perry void child(void); 58 1.7 perry void catch_sigchld(int); 59 1.7 perry void usage(char *progname); 60 1.1 thorpej 61 1.4 mycroft #define FILE_SIZE 128 62 1.4 mycroft #define MSG_SIZE -1 63 1.4 mycroft #define NFILES 24 64 1.4 mycroft 65 1.6 thorpej #define FDCM_DATASIZE (sizeof(int) * NFILES) 66 1.6 thorpej #define CRCM_DATASIZE (SOCKCREDSIZE(NGROUPS)) 67 1.6 thorpej 68 1.6 thorpej #define MESSAGE_SIZE (CMSG_SPACE(FDCM_DATASIZE) + \ 69 1.6 thorpej CMSG_SPACE(CRCM_DATASIZE)) 70 1.2 thorpej 71 1.5 sommerfe int chroot_rcvr = 0; 72 1.5 sommerfe int pass_dir = 0; 73 1.5 sommerfe int pass_root_dir = 0; 74 1.5 sommerfe int exit_early = 0; 75 1.5 sommerfe int exit_later = 0; 76 1.5 sommerfe int pass_sock = 0; 77 1.5 sommerfe int make_pretzel = 0; 78 1.5 sommerfe 79 1.1 thorpej /* ARGSUSED */ 80 1.1 thorpej int 81 1.1 thorpej main(argc, argv) 82 1.1 thorpej int argc; 83 1.1 thorpej char *argv[]; 84 1.1 thorpej { 85 1.4 mycroft #if MSG_SIZE >= 0 86 1.4 mycroft struct iovec iov; 87 1.4 mycroft #endif 88 1.5 sommerfe char *progname=argv[0]; 89 1.1 thorpej struct msghdr msg; 90 1.6 thorpej int listensock, sock, fd, i; 91 1.4 mycroft char fname[16], buf[FILE_SIZE]; 92 1.2 thorpej struct cmsghdr *cmp; 93 1.6 thorpej void *message; 94 1.2 thorpej int *files = NULL; 95 1.2 thorpej struct sockcred *sc = NULL; 96 1.2 thorpej struct sockaddr_un sun, csun; 97 1.8 mrg socklen_t csunlen; 98 1.1 thorpej pid_t pid; 99 1.5 sommerfe int ch; 100 1.6 thorpej 101 1.6 thorpej message = malloc(CMSG_SPACE(MESSAGE_SIZE)); 102 1.6 thorpej if (message == NULL) 103 1.6 thorpej err(1, "unable to malloc message buffer"); 104 1.6 thorpej memset(message, 0, CMSG_SPACE(MESSAGE_SIZE)); 105 1.5 sommerfe 106 1.5 sommerfe while ((ch = getopt(argc, argv, "DESdepr")) != -1) { 107 1.5 sommerfe switch(ch) { 108 1.5 sommerfe 109 1.5 sommerfe case 'e': 110 1.5 sommerfe exit_early++; /* test early GC */ 111 1.5 sommerfe break; 112 1.5 sommerfe 113 1.5 sommerfe case 'E': 114 1.5 sommerfe exit_later++; /* test later GC */ 115 1.5 sommerfe break; 116 1.5 sommerfe 117 1.5 sommerfe case 'd': 118 1.5 sommerfe pass_dir++; 119 1.5 sommerfe break; 120 1.5 sommerfe 121 1.5 sommerfe case 'D': 122 1.5 sommerfe pass_dir++; 123 1.5 sommerfe pass_root_dir++; 124 1.5 sommerfe break; 125 1.5 sommerfe 126 1.5 sommerfe case 'S': 127 1.5 sommerfe pass_sock++; 128 1.5 sommerfe break; 129 1.5 sommerfe 130 1.5 sommerfe case 'r': 131 1.5 sommerfe chroot_rcvr++; 132 1.5 sommerfe break; 133 1.5 sommerfe 134 1.5 sommerfe case 'p': 135 1.5 sommerfe make_pretzel++; 136 1.5 sommerfe break; 137 1.5 sommerfe 138 1.5 sommerfe case '?': 139 1.5 sommerfe default: 140 1.5 sommerfe usage(progname); 141 1.5 sommerfe } 142 1.5 sommerfe } 143 1.5 sommerfe 144 1.1 thorpej 145 1.1 thorpej /* 146 1.1 thorpej * Create the test files. 147 1.1 thorpej */ 148 1.4 mycroft for (i = 0; i < NFILES; i++) { 149 1.1 thorpej (void) sprintf(fname, "file%d", i + 1); 150 1.1 thorpej if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) 151 1.1 thorpej err(1, "open %s", fname); 152 1.1 thorpej (void) sprintf(buf, "This is file %d.\n", i + 1); 153 1.1 thorpej if (write(fd, buf, strlen(buf)) != strlen(buf)) 154 1.1 thorpej err(1, "write %s", fname); 155 1.1 thorpej (void) close(fd); 156 1.1 thorpej } 157 1.1 thorpej 158 1.1 thorpej /* 159 1.2 thorpej * Create the listen socket. 160 1.1 thorpej */ 161 1.3 thorpej if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 162 1.2 thorpej err(1, "socket"); 163 1.2 thorpej 164 1.2 thorpej (void) unlink(SOCK_NAME); 165 1.2 thorpej (void) memset(&sun, 0, sizeof(sun)); 166 1.2 thorpej sun.sun_family = AF_LOCAL; 167 1.2 thorpej (void) strcpy(sun.sun_path, SOCK_NAME); 168 1.2 thorpej sun.sun_len = SUN_LEN(&sun); 169 1.2 thorpej 170 1.2 thorpej i = 1; 171 1.12 nia if (setsockopt(listensock, SOL_LOCAL, LOCAL_CREDS, &i, sizeof(i)) == -1) 172 1.2 thorpej err(1, "setsockopt"); 173 1.2 thorpej 174 1.2 thorpej if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 175 1.2 thorpej err(1, "bind"); 176 1.2 thorpej 177 1.2 thorpej if (listen(listensock, 1) == -1) 178 1.2 thorpej err(1, "listen"); 179 1.1 thorpej 180 1.2 thorpej /* 181 1.2 thorpej * Create the sender. 182 1.2 thorpej */ 183 1.2 thorpej (void) signal(SIGCHLD, catch_sigchld); 184 1.1 thorpej pid = fork(); 185 1.1 thorpej switch (pid) { 186 1.1 thorpej case -1: 187 1.1 thorpej err(1, "fork"); 188 1.1 thorpej /* NOTREACHED */ 189 1.1 thorpej 190 1.1 thorpej case 0: 191 1.1 thorpej child(); 192 1.1 thorpej /* NOTREACHED */ 193 1.1 thorpej } 194 1.1 thorpej 195 1.5 sommerfe if (exit_early) 196 1.5 sommerfe exit(0); 197 1.5 sommerfe 198 1.5 sommerfe if (chroot_rcvr && 199 1.5 sommerfe ((chroot(".") < 0))) 200 1.5 sommerfe err(1, "chroot"); 201 1.5 sommerfe 202 1.2 thorpej /* 203 1.2 thorpej * Wait for the sender to connect. 204 1.2 thorpej */ 205 1.9 ad csunlen = sizeof(csun); 206 1.2 thorpej if ((sock = accept(listensock, (struct sockaddr *)&csun, 207 1.2 thorpej &csunlen)) == -1) 208 1.2 thorpej err(1, "accept"); 209 1.1 thorpej 210 1.1 thorpej /* 211 1.2 thorpej * Give sender a chance to run. We will get going again 212 1.2 thorpej * once the SIGCHLD arrives. 213 1.1 thorpej */ 214 1.2 thorpej (void) sleep(10); 215 1.1 thorpej 216 1.5 sommerfe if (exit_later) 217 1.5 sommerfe exit(0); 218 1.5 sommerfe 219 1.2 thorpej /* 220 1.2 thorpej * Grab the descriptors and credentials passed to us. 221 1.2 thorpej */ 222 1.4 mycroft 223 1.6 thorpej /* Expect 2 messages; descriptors and creds. */ 224 1.5 sommerfe do { 225 1.5 sommerfe (void) memset(&msg, 0, sizeof(msg)); 226 1.6 thorpej msg.msg_control = message; 227 1.6 thorpej msg.msg_controllen = MESSAGE_SIZE; 228 1.4 mycroft #if MSG_SIZE >= 0 229 1.5 sommerfe iov.iov_base = buf; 230 1.5 sommerfe iov.iov_len = MSG_SIZE; 231 1.5 sommerfe msg.msg_iov = &iov; 232 1.5 sommerfe msg.msg_iovlen = 1; 233 1.4 mycroft #endif 234 1.2 thorpej 235 1.5 sommerfe if (recvmsg(sock, &msg, 0) == -1) 236 1.5 sommerfe err(1, "recvmsg"); 237 1.5 sommerfe 238 1.5 sommerfe (void) close(sock); 239 1.5 sommerfe sock = -1; 240 1.2 thorpej 241 1.5 sommerfe if (msg.msg_controllen == 0) 242 1.5 sommerfe errx(1, "no control messages received"); 243 1.2 thorpej 244 1.5 sommerfe if (msg.msg_flags & MSG_CTRUNC) 245 1.5 sommerfe errx(1, "lost control message data"); 246 1.2 thorpej 247 1.5 sommerfe for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL; 248 1.5 sommerfe cmp = CMSG_NXTHDR(&msg, cmp)) { 249 1.5 sommerfe if (cmp->cmsg_level != SOL_SOCKET) 250 1.5 sommerfe errx(1, "bad control message level %d", 251 1.5 sommerfe cmp->cmsg_level); 252 1.1 thorpej 253 1.5 sommerfe switch (cmp->cmsg_type) { 254 1.5 sommerfe case SCM_RIGHTS: 255 1.6 thorpej if (cmp->cmsg_len != CMSG_LEN(FDCM_DATASIZE)) 256 1.6 thorpej errx(1, "bad fd control message " 257 1.6 thorpej "length %d", cmp->cmsg_len); 258 1.2 thorpej 259 1.5 sommerfe files = (int *)CMSG_DATA(cmp); 260 1.5 sommerfe break; 261 1.2 thorpej 262 1.5 sommerfe case SCM_CREDS: 263 1.6 thorpej if (cmp->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) 264 1.6 thorpej errx(1, "bad cred control message " 265 1.6 thorpej "length %d", cmp->cmsg_len); 266 1.2 thorpej 267 1.5 sommerfe sc = (struct sockcred *)CMSG_DATA(cmp); 268 1.5 sommerfe break; 269 1.2 thorpej 270 1.5 sommerfe default: 271 1.5 sommerfe errx(1, "unexpected control message"); 272 1.5 sommerfe /* NOTREACHED */ 273 1.5 sommerfe } 274 1.2 thorpej } 275 1.1 thorpej 276 1.5 sommerfe /* 277 1.5 sommerfe * Read the files and print their contents. 278 1.5 sommerfe */ 279 1.5 sommerfe if (files == NULL) 280 1.5 sommerfe warnx("didn't get fd control message"); 281 1.5 sommerfe else { 282 1.5 sommerfe for (i = 0; i < NFILES; i++) { 283 1.5 sommerfe struct stat st; 284 1.5 sommerfe (void) memset(buf, 0, sizeof(buf)); 285 1.5 sommerfe fstat(files[i], &st); 286 1.5 sommerfe if (S_ISDIR(st.st_mode)) { 287 1.5 sommerfe printf("file %d is a directory\n", i+1); 288 1.5 sommerfe } else if (S_ISSOCK(st.st_mode)) { 289 1.5 sommerfe printf("file %d is a socket\n", i+1); 290 1.5 sommerfe sock = files[i]; 291 1.5 sommerfe } else { 292 1.5 sommerfe int c; 293 1.5 sommerfe c = read (files[i], buf, sizeof(buf)); 294 1.5 sommerfe if (c < 0) 295 1.5 sommerfe err(1, "read file %d", i + 1); 296 1.5 sommerfe else if (c == 0) 297 1.5 sommerfe printf("[eof on %d]\n", i + 1); 298 1.5 sommerfe else 299 1.5 sommerfe printf("%s", buf); 300 1.5 sommerfe } 301 1.5 sommerfe } 302 1.5 sommerfe } 303 1.5 sommerfe /* 304 1.5 sommerfe * Double-check credentials. 305 1.5 sommerfe */ 306 1.5 sommerfe if (sc == NULL) 307 1.5 sommerfe warnx("didn't get cred control message"); 308 1.5 sommerfe else { 309 1.5 sommerfe if (sc->sc_uid == getuid() && 310 1.5 sommerfe sc->sc_euid == geteuid() && 311 1.5 sommerfe sc->sc_gid == getgid() && 312 1.5 sommerfe sc->sc_egid == getegid()) 313 1.5 sommerfe printf("Credentials match.\n"); 314 1.5 sommerfe else 315 1.5 sommerfe printf("Credentials do NOT match.\n"); 316 1.2 thorpej } 317 1.5 sommerfe } while (sock != -1); 318 1.1 thorpej 319 1.2 thorpej /* 320 1.2 thorpej * All done! 321 1.2 thorpej */ 322 1.1 thorpej exit(0); 323 1.1 thorpej } 324 1.1 thorpej 325 1.1 thorpej void 326 1.5 sommerfe usage(progname) 327 1.5 sommerfe char *progname; 328 1.5 sommerfe { 329 1.5 sommerfe fprintf(stderr, "usage: %s [-derDES]\n", progname); 330 1.5 sommerfe exit(1); 331 1.5 sommerfe } 332 1.5 sommerfe 333 1.5 sommerfe void 334 1.2 thorpej catch_sigchld(sig) 335 1.2 thorpej int sig; 336 1.2 thorpej { 337 1.2 thorpej int status; 338 1.2 thorpej 339 1.2 thorpej (void) wait(&status); 340 1.2 thorpej } 341 1.2 thorpej 342 1.2 thorpej void 343 1.1 thorpej child() 344 1.1 thorpej { 345 1.4 mycroft #if MSG_SIZE >= 0 346 1.4 mycroft struct iovec iov; 347 1.4 mycroft #endif 348 1.1 thorpej struct msghdr msg; 349 1.6 thorpej char fname[16]; 350 1.1 thorpej struct cmsghdr *cmp; 351 1.6 thorpej void *fdcm; 352 1.6 thorpej int i, fd, sock, nfd, *files; 353 1.2 thorpej struct sockaddr_un sun; 354 1.5 sommerfe int spair[2]; 355 1.6 thorpej 356 1.6 thorpej fdcm = malloc(CMSG_SPACE(FDCM_DATASIZE)); 357 1.6 thorpej if (fdcm == NULL) 358 1.6 thorpej err(1, "unable to malloc fd control message"); 359 1.6 thorpej memset(fdcm, 0, CMSG_SPACE(FDCM_DATASIZE)); 360 1.6 thorpej 361 1.6 thorpej cmp = fdcm; 362 1.6 thorpej files = (int *)CMSG_DATA(fdcm); 363 1.6 thorpej 364 1.1 thorpej /* 365 1.2 thorpej * Create socket and connect to the receiver. 366 1.1 thorpej */ 367 1.3 thorpej if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) 368 1.2 thorpej errx(1, "child socket"); 369 1.2 thorpej 370 1.2 thorpej (void) memset(&sun, 0, sizeof(sun)); 371 1.2 thorpej sun.sun_family = AF_LOCAL; 372 1.2 thorpej (void) strcpy(sun.sun_path, SOCK_NAME); 373 1.2 thorpej sun.sun_len = SUN_LEN(&sun); 374 1.2 thorpej 375 1.2 thorpej if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 376 1.2 thorpej err(1, "child connect"); 377 1.1 thorpej 378 1.5 sommerfe nfd = NFILES; 379 1.5 sommerfe i = 0; 380 1.5 sommerfe 381 1.5 sommerfe if (pass_sock) { 382 1.6 thorpej files[i++] = sock; 383 1.5 sommerfe } 384 1.5 sommerfe 385 1.5 sommerfe if (pass_dir) 386 1.5 sommerfe nfd--; 387 1.5 sommerfe 388 1.1 thorpej /* 389 1.5 sommerfe * Open the files again, and pass them to the child 390 1.5 sommerfe * over the socket. 391 1.1 thorpej */ 392 1.5 sommerfe 393 1.5 sommerfe for (; i < nfd; i++) { 394 1.2 thorpej (void) sprintf(fname, "file%d", i + 1); 395 1.2 thorpej if ((fd = open(fname, O_RDONLY, 0666)) == -1) 396 1.2 thorpej err(1, "child open %s", fname); 397 1.6 thorpej files[i] = fd; 398 1.2 thorpej } 399 1.5 sommerfe 400 1.5 sommerfe if (pass_dir) { 401 1.5 sommerfe char *dirname = pass_root_dir ? "/" : "."; 402 1.5 sommerfe 403 1.5 sommerfe 404 1.5 sommerfe if ((fd = open(dirname, O_RDONLY, 0)) == -1) { 405 1.5 sommerfe err(1, "child open directory %s", dirname); 406 1.5 sommerfe } 407 1.6 thorpej files[i] = fd; 408 1.5 sommerfe } 409 1.5 sommerfe 410 1.1 thorpej (void) memset(&msg, 0, sizeof(msg)); 411 1.6 thorpej msg.msg_control = fdcm; 412 1.6 thorpej msg.msg_controllen = CMSG_LEN(FDCM_DATASIZE); 413 1.4 mycroft #if MSG_SIZE >= 0 414 1.4 mycroft iov.iov_base = buf; 415 1.4 mycroft iov.iov_len = MSG_SIZE; 416 1.4 mycroft msg.msg_iov = &iov; 417 1.4 mycroft msg.msg_iovlen = 1; 418 1.4 mycroft #endif 419 1.1 thorpej 420 1.1 thorpej cmp = CMSG_FIRSTHDR(&msg); 421 1.6 thorpej cmp->cmsg_len = CMSG_LEN(FDCM_DATASIZE); 422 1.2 thorpej cmp->cmsg_level = SOL_SOCKET; 423 1.2 thorpej cmp->cmsg_type = SCM_RIGHTS; 424 1.5 sommerfe 425 1.5 sommerfe while (make_pretzel > 0) { 426 1.5 sommerfe if (socketpair(PF_LOCAL, SOCK_STREAM, 0, spair) < 0) 427 1.5 sommerfe err(1, "socketpair"); 428 1.5 sommerfe 429 1.5 sommerfe printf("send pretzel\n"); 430 1.5 sommerfe if (sendmsg(spair[0], &msg, 0) < 0) 431 1.5 sommerfe err(1, "child prezel sendmsg"); 432 1.5 sommerfe 433 1.6 thorpej close(files[0]); 434 1.6 thorpej close(files[1]); 435 1.6 thorpej files[0] = spair[0]; 436 1.6 thorpej files[1] = spair[1]; 437 1.5 sommerfe make_pretzel--; 438 1.5 sommerfe } 439 1.1 thorpej 440 1.4 mycroft if (sendmsg(sock, &msg, 0) == -1) 441 1.2 thorpej err(1, "child sendmsg"); 442 1.1 thorpej 443 1.1 thorpej /* 444 1.1 thorpej * All done! 445 1.1 thorpej */ 446 1.1 thorpej exit(0); 447 1.1 thorpej } 448