xref: /netbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision 665d14aa61b7a8c9bffbc333a4d89649d8cba6e8)
1 /*	$NetBSD: unfdpass.c,v 1.4 1999/01/21 09:54:23 mycroft 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 
65 #define	FILE_SIZE	128
66 #define	MSG_SIZE	-1
67 #define	NFILES		24
68 
69 struct fdcmessage {
70 	struct cmsghdr cm;
71 	int files[NFILES];
72 };
73 
74 struct crcmessage {
75 	struct cmsghdr cm;
76 	char creds[SOCKCREDSIZE(NGROUPS)];
77 };
78 
79 /* ARGSUSED */
80 int
81 main(argc, argv)
82 	int argc;
83 	char *argv[];
84 {
85 #if MSG_SIZE >= 0
86 	struct iovec iov;
87 #endif
88 	struct msghdr msg;
89 	int listensock, sock, fd, i, status;
90 	char fname[16], buf[FILE_SIZE];
91 	struct cmsghdr *cmp;
92 	struct {
93 		struct fdcmessage fdcm;
94 		struct crcmessage crcm;
95 	} message;
96 	int *files = NULL;
97 	struct sockcred *sc = NULL;
98 	struct sockaddr_un sun, csun;
99 	int csunlen;
100 	fd_set oob;
101 	pid_t pid;
102 
103 	/*
104 	 * Create the test files.
105 	 */
106 	for (i = 0; i < NFILES; i++) {
107 		(void) sprintf(fname, "file%d", i + 1);
108 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
109 			err(1, "open %s", fname);
110 		(void) sprintf(buf, "This is file %d.\n", i + 1);
111 		if (write(fd, buf, strlen(buf)) != strlen(buf))
112 			err(1, "write %s", fname);
113 		(void) close(fd);
114 	}
115 
116 	/*
117 	 * Create the listen socket.
118 	 */
119 	if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
120 		err(1, "socket");
121 
122 	(void) unlink(SOCK_NAME);
123 	(void) memset(&sun, 0, sizeof(sun));
124 	sun.sun_family = AF_LOCAL;
125 	(void) strcpy(sun.sun_path, SOCK_NAME);
126 	sun.sun_len = SUN_LEN(&sun);
127 
128 	i = 1;
129 	if (setsockopt(listensock, 0, LOCAL_CREDS, &i, sizeof(i)) == -1)
130 		err(1, "setsockopt");
131 
132 	if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
133 		err(1, "bind");
134 
135 	if (listen(listensock, 1) == -1)
136 		err(1, "listen");
137 
138 	/*
139 	 * Create the sender.
140 	 */
141 	(void) signal(SIGCHLD, catch_sigchld);
142 	pid = fork();
143 	switch (pid) {
144 	case -1:
145 		err(1, "fork");
146 		/* NOTREACHED */
147 
148 	case 0:
149 		child();
150 		/* NOTREACHED */
151 	}
152 
153 	/*
154 	 * Wait for the sender to connect.
155 	 */
156 	if ((sock = accept(listensock, (struct sockaddr *)&csun,
157 	    &csunlen)) == -1)
158 		err(1, "accept");
159 
160 	/*
161 	 * Give sender a chance to run.  We will get going again
162 	 * once the SIGCHLD arrives.
163 	 */
164 	(void) sleep(10);
165 
166 	/*
167 	 * Grab the descriptors and credentials passed to us.
168 	 */
169 
170 	(void) memset(&msg, 0, sizeof(msg));
171 	msg.msg_control = (caddr_t) &message;
172 	msg.msg_controllen = sizeof(message);
173 #if MSG_SIZE >= 0
174 	iov.iov_base = buf;
175 	iov.iov_len = MSG_SIZE;
176 	msg.msg_iov = &iov;
177 	msg.msg_iovlen = 1;
178 #endif
179 
180 	if (recvmsg(sock, &msg, 0) == -1)
181 		err(1, "recvmsg");
182 
183 	(void) close(sock);
184 
185 	if (msg.msg_controllen == 0)
186 		errx(1, "no control messages received");
187 
188 	if (msg.msg_flags & MSG_CTRUNC)
189 		errx(1, "lost control message data");
190 
191 	cmp = CMSG_FIRSTHDR(&msg);
192 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
193 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
194 		if (cmp->cmsg_level != SOL_SOCKET)
195 			errx(1, "bad control message level %d",
196 			    cmp->cmsg_level);
197 
198 		switch (cmp->cmsg_type) {
199 		case SCM_RIGHTS:
200 			if (cmp->cmsg_len != sizeof(message.fdcm))
201 				errx(1, "bad fd control message length");
202 
203 			files = (int *)CMSG_DATA(cmp);
204 			break;
205 
206 		case SCM_CREDS:
207 			if (cmp->cmsg_len < sizeof(struct sockcred))
208 				errx(1, "bad cred control message length");
209 
210 			sc = (struct sockcred *)CMSG_DATA(cmp);
211 			break;
212 
213 		default:
214 			errx(1, "unexpected control message");
215 			/* NOTREACHED */
216 		}
217 	}
218 
219 	/*
220 	 * Read the files and print their contents.
221 	 */
222 	if (files == NULL)
223 		warnx("didn't get fd control message");
224 	else {
225 		for (i = 0; i < NFILES; i++) {
226 			(void) memset(buf, 0, sizeof(buf));
227 			if (read(files[i], buf, sizeof(buf)) <= 0)
228 				err(1, "read file %d", i + 1);
229 			printf("%s", buf);
230 		}
231 	}
232 
233 	/*
234 	 * Double-check credentials.
235 	 */
236 	if (sc == NULL)
237 		warnx("didn't get cred control message");
238 	else {
239 		if (sc->sc_uid == getuid() &&
240 		    sc->sc_euid == geteuid() &&
241 		    sc->sc_gid == getgid() &&
242 		    sc->sc_egid == getegid())
243 			printf("Credentials match.\n");
244 		else
245 			printf("Credentials do NOT match.\n");
246 	}
247 
248 	/*
249 	 * All done!
250 	 */
251 	exit(0);
252 }
253 
254 void
255 catch_sigchld(sig)
256 	int sig;
257 {
258 	int status;
259 
260 	(void) wait(&status);
261 }
262 
263 void
264 child()
265 {
266 #if MSG_SIZE >= 0
267 	struct iovec iov;
268 #endif
269 	struct msghdr msg;
270 	char fname[16], buf[FILE_SIZE];
271 	struct cmsghdr *cmp;
272 	struct fdcmessage fdcm;
273 	int i, fd, sock;
274 	struct sockaddr_un sun;
275 
276 	/*
277 	 * Create socket and connect to the receiver.
278 	 */
279 	if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
280 		errx(1, "child socket");
281 
282 	(void) memset(&sun, 0, sizeof(sun));
283 	sun.sun_family = AF_LOCAL;
284 	(void) strcpy(sun.sun_path, SOCK_NAME);
285 	sun.sun_len = SUN_LEN(&sun);
286 
287 	if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
288 		err(1, "child connect");
289 
290 	/*
291 	 * Open the files again, and pass them to the child over the socket.
292 	 */
293 	for (i = 0; i < NFILES; i++) {
294 		(void) sprintf(fname, "file%d", i + 1);
295 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
296 			err(1, "child open %s", fname);
297 		fdcm.files[i] = fd;
298 	}
299 
300 	(void) memset(&msg, 0, sizeof(msg));
301 	msg.msg_control = (caddr_t) &fdcm;
302 	msg.msg_controllen = sizeof(fdcm);
303 #if MSG_SIZE >= 0
304 	iov.iov_base = buf;
305 	iov.iov_len = MSG_SIZE;
306 	msg.msg_iov = &iov;
307 	msg.msg_iovlen = 1;
308 #endif
309 
310 	cmp = CMSG_FIRSTHDR(&msg);
311 	cmp->cmsg_len = sizeof(fdcm);
312 	cmp->cmsg_level = SOL_SOCKET;
313 	cmp->cmsg_type = SCM_RIGHTS;
314 
315 	if (sendmsg(sock, &msg, 0) == -1)
316 		err(1, "child sendmsg");
317 
318 	/*
319 	 * All done!
320 	 */
321 	exit(0);
322 }
323