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
60int	main(int, char *[]);
61void	child(int, int);
62void	catch_sigchld(int);
63
64int
65main(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
197void
198catch_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
208void
209child(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