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