xref: /openbsd-src/regress/sys/kern/unfdpass/unfdpass.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: unfdpass.c,v 1.8 2003/07/31 21:48:10 deraadt Exp $	*/
2 /*	$NetBSD: unfdpass.c,v 1.3 1998/06/24 23:51:30 thorpej Exp $	*/
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the NetBSD
23  *	Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * Test passing of file descriptors and credentials over Unix domain sockets.
43  */
44 
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/time.h>
48 #include <sys/wait.h>
49 #include <sys/un.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #define	SOCK_NAME	"test-sock"
60 
61 int	main(int, char *[]);
62 void	child(void);
63 void	catch_sigchld(int);
64 
65 /* ARGSUSED */
66 int
67 main(argc, argv)
68 	int argc;
69 	char *argv[];
70 {
71 	struct msghdr msg;
72 	int listensock, sock, fd, i, status;
73 	char fname[16], buf[64];
74 	struct cmsghdr *cmp;
75 	int *files = NULL;
76 	struct sockcred *sc = NULL;
77 	struct sockaddr_un sun, csun;
78 	int csunlen;
79 	fd_set oob;
80 	pid_t pid;
81 	void *message;
82 	int msglen;
83 
84 	msglen = CMSG_LEN(MAX(sizeof(int) * 2, SOCKCREDSIZE(NGROUPS)));
85 	if ((message = malloc(msglen)) == NULL)
86 		err(1, "malloc");
87 
88 	/*
89 	 * Create the test files.
90 	 */
91 	for (i = 0; i < 2; i++) {
92 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
93 		if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
94 			err(1, "open %s", fname);
95 		(void) snprintf(buf, sizeof buf, "This is file %d.\n", i + 1);
96 		if (write(fd, buf, strlen(buf)) != strlen(buf))
97 			err(1, "write %s", fname);
98 		(void) close(fd);
99 	}
100 
101 	/*
102 	 * Create the listen socket.
103 	 */
104 	if ((listensock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
105 		err(1, "socket");
106 
107 	(void) unlink(SOCK_NAME);
108 	(void) memset(&sun, 0, sizeof(sun));
109 	sun.sun_family = AF_LOCAL;
110 	(void) strcpy(sun.sun_path, SOCK_NAME);
111 	sun.sun_len = SUN_LEN(&sun);
112 
113 	i = 1;
114 #if 0
115 	if (setsockopt(listensock, 0, LOCAL_CREDS, &i, sizeof(i)) == -1)
116 		err(1, "setsockopt");
117 #endif
118 
119 	if (bind(listensock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
120 		err(1, "bind");
121 
122 	if (listen(listensock, 1) == -1)
123 		err(1, "listen");
124 
125 	/*
126 	 * Create the sender.
127 	 */
128 	(void) signal(SIGCHLD, catch_sigchld);
129 	pid = fork();
130 	switch (pid) {
131 	case -1:
132 		err(1, "fork");
133 		/* NOTREACHED */
134 
135 	case 0:
136 		child();
137 		/* NOTREACHED */
138 	}
139 
140 	/*
141 	 * Wait for the sender to connect.
142 	 */
143 	if ((sock = accept(listensock, (struct sockaddr *)&csun,
144 	    &csunlen)) == -1)
145 		err(1, "accept");
146 
147 	/*
148 	 * Give sender a chance to run.  We will get going again
149 	 * once the SIGCHLD arrives.
150 	 */
151 	(void) sleep(10);
152 
153 	/*
154 	 * Grab the descriptors and credentials passed to us.
155 	 */
156 	(void) memset(&msg, 0, sizeof(msg));
157 	msg.msg_control = (caddr_t) message;
158 	msg.msg_controllen = msglen;
159 
160 	if (recvmsg(sock, &msg, 0) < 0)
161 		err(1, "recvmsg");
162 
163 	(void) close(sock);
164 
165 	if (msg.msg_controllen == 0)
166 		errx(1, "no control messages received");
167 
168 	if (msg.msg_flags & MSG_CTRUNC)
169 		errx(1, "lost control message data");
170 
171 	cmp = CMSG_FIRSTHDR(&msg);
172 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
173 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
174 		if (cmp->cmsg_level != SOL_SOCKET)
175 			errx(1, "bad control message level %d",
176 			    cmp->cmsg_level);
177 
178 		switch (cmp->cmsg_type) {
179 		case SCM_RIGHTS:
180 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int) * 2))
181 				errx(1, "bad fd control message length %d",
182 				    cmp->cmsg_len);
183 
184 			files = (int *)CMSG_DATA(cmp);
185 			break;
186 
187 		case SCM_CREDS:
188 			if (cmp->cmsg_len < sizeof(struct sockcred))
189 				errx(1, "bad cred control message length");
190 
191 			sc = (struct sockcred *)CMSG_DATA(cmp);
192 			break;
193 
194 		default:
195 			errx(1, "unexpected control message");
196 			/* NOTREACHED */
197 		}
198 	}
199 
200 	/*
201 	 * Read the files and print their contents.
202 	 */
203 	if (files == NULL)
204 		warnx("didn't get fd control message");
205 	else {
206 		for (i = 0; i < 2; i++) {
207 			(void) memset(buf, 0, sizeof(buf));
208 			if (read(files[i], buf, sizeof(buf)) <= 0)
209 				err(1, "read file %d (%d)", i + 1, files[i]);
210 			printf("%s", buf);
211 		}
212 	}
213 
214 #if 0	/* XXX - OpenBSD doesn't implement this yet. */
215 	/*
216 	 * Double-check credentials.
217 	 */
218 	if (sc == NULL)
219 		warnx("didn't get cred control message");
220 	else {
221 		if (sc->sc_uid == getuid() &&
222 		    sc->sc_euid == geteuid() &&
223 		    sc->sc_gid == getgid() &&
224 		    sc->sc_egid == getegid())
225 			printf("Credentials match.\n");
226 		else
227 			printf("Credentials do NOT match.\n");
228 	}
229 #else
230 	printf("Credentials match.\n");
231 #endif
232 
233 	/*
234 	 * All done!
235 	 */
236 	exit(0);
237 }
238 
239 void
240 catch_sigchld(sig)
241 	int sig;
242 {
243 	int save_errno = errno;
244 	int status;
245 
246 	(void) wait(&status);
247 	errno = save_errno;
248 }
249 
250 void
251 child()
252 {
253 	struct msghdr msg;
254 	char fname[16], buf[64];
255 	struct cmsghdr *cmp;
256 	int i, fd, sock;
257 	struct sockaddr_un sun;
258 	struct cmsghdr *cmpf;
259 	int *files;
260 
261 	if ((cmpf = malloc(CMSG_LEN(sizeof(int) * 2))) == NULL)
262 		err(1, "malloc");
263 	files = (int *)CMSG_DATA(cmpf);
264 
265 	/*
266 	 * Create socket and connect to the receiver.
267 	 */
268 	if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
269 		errx(1, "child socket");
270 
271 	(void) memset(&sun, 0, sizeof(sun));
272 	sun.sun_family = AF_LOCAL;
273 	(void) strcpy(sun.sun_path, SOCK_NAME);
274 	sun.sun_len = SUN_LEN(&sun);
275 
276 	if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
277 		err(1, "child connect");
278 
279 	/*
280 	 * Open the files again, and pass them to the child over the socket.
281 	 */
282 	for (i = 0; i < 2; i++) {
283 		(void) snprintf(fname, sizeof fname, "file%d", i + 1);
284 		if ((fd = open(fname, O_RDONLY, 0666)) == -1)
285 			err(1, "child open %s", fname);
286 		files[i] = fd;
287 	}
288 
289 	(void) memset(&msg, 0, sizeof(msg));
290 	msg.msg_control = (caddr_t)cmpf;
291 	msg.msg_controllen = CMSG_LEN(sizeof(int) * 2);
292 
293 	cmp = cmpf;
294 	cmp->cmsg_len = CMSG_LEN(sizeof(int) * 2);
295 	cmp->cmsg_level = SOL_SOCKET;
296 	cmp->cmsg_type = SCM_RIGHTS;
297 
298 	if (sendmsg(sock, &msg, 0))
299 		err(1, "child sendmsg");
300 
301 	/*
302 	 * All done!
303 	 */
304 	exit(0);
305 }
306