xref: /openbsd/regress/sys/kern/pledge/ioctl/unfdpass.c (revision 5dea098c)
1 /*	$OpenBSD: unfdpass.c,v 1.4 2023/03/08 04:43:06 guenther 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  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Test passing of a /dev/pf file descriptors over socketpair,
36  * and of passing a fd opened before the first pledge call that
37  * is then used for ioctl()
38  */
39 
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
43 #include <sys/time.h>
44 #include <sys/wait.h>
45 #include <sys/un.h>
46 #include <net/if.h>
47 #include <net/pfvar.h>
48 
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 
58 #define	SOCK_NAME	"test-sock"
59 
60 int	main(int, char *[]);
61 void	child(int, int);
62 void	catch_sigchld(int);
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct msghdr msg;
68 	int sock, pfd[2], i;
69 	struct cmsghdr *cmp;
70 	int *files = NULL;
71 	int fdpf_prepledge, fdpf_postpledge;
72 	pid_t pid;
73 	union {
74 		struct cmsghdr hdr;
75 		char buf[CMSG_SPACE(sizeof(int))];
76 	} cmsgbuf;
77 	int type = SOCK_STREAM;
78 	int fail = 0;
79 	struct pf_status status;
80 	extern char *__progname;
81 
82 	if ((fdpf_prepledge = open("/dev/pf", O_RDWR)) == -1) {
83 		err(1, "%s: cannot open pf socket", __func__);
84 	}
85 
86 	if (pledge("stdio rpath wpath sendfd recvfd proc pf", NULL)
87 	    == -1)
88 		err(1, "pledge");
89 
90 	if ((fdpf_postpledge = open("/dev/pf", O_RDWR)) == -1) {
91 		err(1, "%s: cannot open pf socket", __func__);
92 	}
93 
94 	while ((i = getopt(argc, argv, "f")) != -1) {
95 		switch (i) {
96 		case 'f':
97 			fail = 1;
98 			break;
99 		default:
100 			fprintf(stderr, "usage: %s [-f]\n", __progname);
101 			exit(1);
102 		}
103 	}
104 
105 	if (socketpair(PF_LOCAL, type, 0, pfd) == -1)
106 		err(1, "socketpair");
107 
108 	/*
109 	 * Create the sender.
110 	 */
111 	(void) signal(SIGCHLD, catch_sigchld);
112 	pid = fork();
113 	switch (pid) {
114 	case -1:
115 		err(1, "fork");
116 		/* NOTREACHED */
117 
118 	case 0:
119 		if (pfd[0] != -1)
120 			close(pfd[0]);
121 		child(pfd[1], (fail ? fdpf_postpledge : fdpf_prepledge));
122 		/* NOTREACHED */
123 	}
124 
125 	if (pfd[0] != -1) {
126 		close(pfd[1]);
127 		sock = pfd[0];
128 	} else {
129 		err(1, "should not happen");
130 	}
131 
132 	if (pledge("stdio recvfd pf", NULL) == -1)
133 		err(1, "pledge");
134 
135 	/*
136 	 * Give sender a chance to run.  We will get going again
137 	 * once the SIGCHLD arrives.
138 	 */
139 	(void) sleep(10);
140 
141 	/*
142 	 * Grab the descriptors passed to us.
143 	 */
144 	(void) memset(&msg, 0, sizeof(msg));
145 	msg.msg_control = &cmsgbuf.buf;
146 	msg.msg_controllen = sizeof(cmsgbuf.buf);
147 
148 	if (recvmsg(sock, &msg, 0) < 0)
149 		err(1, "recvmsg");
150 
151 	(void) close(sock);
152 
153 	if (msg.msg_controllen == 0)
154 		errx(1, "no control messages received");
155 
156 	if (msg.msg_flags & MSG_CTRUNC)
157 		errx(1, "lost control message data");
158 
159 	for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
160 	    cmp = CMSG_NXTHDR(&msg, cmp)) {
161 		if (cmp->cmsg_level != SOL_SOCKET)
162 			errx(1, "bad control message level %d",
163 			    cmp->cmsg_level);
164 
165 		switch (cmp->cmsg_type) {
166 		case SCM_RIGHTS:
167 			if (cmp->cmsg_len != CMSG_LEN(sizeof(int)))
168 				errx(1, "bad fd control message length %d",
169 				    cmp->cmsg_len);
170 
171 			files = (int *)CMSG_DATA(cmp);
172 			break;
173 
174 		default:
175 			errx(1, "unexpected control message");
176 			/* NOTREACHED */
177 		}
178 	}
179 
180 	/*
181 	 * Read the files and print their contents.
182 	 */
183 	if (files == NULL)
184 		errx(1, "didn't get fd control message");
185 
186 	if (ioctl(files[0], DIOCGETSTATUS, &status) == -1)
187 		err(1, "%s: DIOCGETSTATUS", __func__);
188 	if (!status.running)
189 		warnx("%s: pf is disabled", __func__);
190 
191 	/*
192 	 * All done!
193 	 */
194 	return 0;
195 }
196 
197 void
198 catch_sigchld(sig)
199 	int sig;
200 {
201 	int save_errno = errno;
202 	int status;
203 
204 	(void) wait(&status);
205 	errno = save_errno;
206 }
207 
208 void
209 child(int sock, int fdpf)
210 {
211 	struct msghdr msg;
212 	struct cmsghdr *cmp;
213 	union {
214 		struct cmsghdr hdr;
215 		char buf[CMSG_SPACE(sizeof(int))];
216 	} cmsgbuf;
217 	int *files;
218 
219 	(void) memset(&msg, 0, sizeof(msg));
220 	msg.msg_control = &cmsgbuf.buf;
221 	msg.msg_controllen = sizeof(cmsgbuf.buf);
222 
223 	cmp = CMSG_FIRSTHDR(&msg);
224 	cmp->cmsg_len = CMSG_LEN(sizeof(int));
225 	cmp->cmsg_level = SOL_SOCKET;
226 	cmp->cmsg_type = SCM_RIGHTS;
227 
228 	files = (int *)CMSG_DATA(cmp);
229 	files[0] = fdpf;
230 
231 	if (pledge("stdio sendfd", NULL) == -1)
232 		errx(1, "pledge");
233 
234 	if (sendmsg(sock, &msg, 0))
235 		err(1, "child sendmsg");
236 
237 	/*
238 	 * All done!
239 	 */
240 	_exit(0);
241 }
242