Home | History | Annotate | Line # | Download | only in unfdpass
unfdpass.c revision 1.5
      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