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