xref: /netbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
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