xref: /dragonfly/contrib/dhcpcd/src/privsep-bpf.c (revision 54175cef)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation BPF Initiator
4  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 
32 /* Need these headers just for if_ether on some OS. */
33 #ifndef __NetBSD__
34 #include <net/if.h>
35 #include <net/if_arp.h>
36 #include <netinet/in.h>
37 #endif
38 #include <netinet/if_ether.h>
39 
40 #include <assert.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "arp.h"
48 #include "bpf.h"
49 #include "dhcp.h"
50 #include "dhcp6.h"
51 #include "eloop.h"
52 #include "ipv6nd.h"
53 #include "logerr.h"
54 #include "privsep.h"
55 
56 /* We expect to have open 3 SEQPACKET and one RAW fd */
57 
58 static void
ps_bpf_recvbpf(void * arg,unsigned short events)59 ps_bpf_recvbpf(void *arg, unsigned short events)
60 {
61 	struct ps_process *psp = arg;
62 	struct bpf *bpf = psp->psp_bpf;
63 	uint8_t buf[FRAMELEN_MAX];
64 	ssize_t len;
65 	struct ps_msghdr psm = {
66 		.ps_id = psp->psp_id,
67 		.ps_cmd = psp->psp_id.psi_cmd,
68 	};
69 
70 	if (events != ELE_READ)
71 		logerrx("%s: unexpected event 0x%04x", __func__, events);
72 
73 	bpf->bpf_flags &= ~BPF_EOF;
74 	/* A BPF read can read more than one filtered packet at time.
75 	 * This mechanism allows us to read each packet from the buffer. */
76 	while (!(bpf->bpf_flags & BPF_EOF)) {
77 		len = bpf_read(bpf, buf, sizeof(buf));
78 		if (len == -1) {
79 			int error = errno;
80 
81 			if (errno != ENETDOWN)
82 				logerr("%s: %s", psp->psp_ifname, __func__);
83 			if (error != ENXIO)
84 				break;
85 			/* If the interface has departed, close the BPF
86 			 * socket. This stops log spam if RTM_IFANNOUNCE is
87 			 * delayed in announcing the departing interface. */
88 			eloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd);
89 			bpf_close(bpf);
90 			psp->psp_bpf = NULL;
91 			break;
92 		}
93 		if (len == 0)
94 			break;
95 		psm.ps_flags = bpf->bpf_flags;
96 		len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
97 		    &psm, buf, (size_t)len);
98 		if (len == -1)
99 			logerr(__func__);
100 		if (len == -1 || len == 0)
101 			break;
102 	}
103 }
104 
105 static ssize_t
ps_bpf_recvmsgcb(void * arg,struct ps_msghdr * psm,struct msghdr * msg)106 ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
107 {
108 	struct ps_process *psp = arg;
109 	struct iovec *iov = msg->msg_iov;
110 
111 #ifdef PRIVSEP_DEBUG
112 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
113 #endif
114 
115 	switch(psm->ps_cmd) {
116 #ifdef ARP
117 	case PS_BPF_ARP:	/* FALLTHROUGH */
118 #endif
119 	case PS_BPF_BOOTP:
120 		break;
121 	default:
122 		/* IPC failure, we should not be processing any commands
123 		 * at this point!/ */
124 		errno = EINVAL;
125 		return -1;
126 	}
127 
128 	/* We might have had an earlier ENXIO error. */
129 	if (psp->psp_bpf == NULL) {
130 		errno = ENXIO;
131 		return -1;
132 	}
133 
134 	return bpf_send(psp->psp_bpf, psp->psp_proto,
135 	    iov->iov_base, iov->iov_len);
136 }
137 
138 static void
ps_bpf_recvmsg(void * arg,unsigned short events)139 ps_bpf_recvmsg(void *arg, unsigned short events)
140 {
141 	struct ps_process *psp = arg;
142 
143 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
144 	    ps_bpf_recvmsgcb, arg) == -1)
145 		logerr(__func__);
146 }
147 
148 static int
ps_bpf_start_bpf(struct ps_process * psp)149 ps_bpf_start_bpf(struct ps_process *psp)
150 {
151 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
152 	char *addr;
153 	struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
154 
155 	if (ia->s_addr == INADDR_ANY) {
156 		ia = NULL;
157 		addr = NULL;
158 	} else
159 		addr = inet_ntoa(*ia);
160 	setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
161 	    addr != NULL ? " " : "", addr != NULL ? addr : "");
162 	ps_freeprocesses(ctx, psp);
163 
164 	psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
165 #ifdef DEBUG_FD
166 	logdebugx("pid %d bpf_fd=%d", getpid(), psp->psp_bpf->bpf_fd);
167 #endif
168 	if (psp->psp_bpf == NULL)
169 		logerr("%s: bpf_open",__func__);
170 #ifdef PRIVSEP_RIGHTS
171 	else if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1)
172 		logerr("%s: ps_rights_limit_fd", __func__);
173 #endif
174 	else if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ELE_READ,
175 	    ps_bpf_recvbpf, psp) == -1)
176 		logerr("%s: eloop_event_add", __func__);
177 	else {
178 		psp->psp_work_fd = psp->psp_bpf->bpf_fd;
179 		return 0;
180 	}
181 
182 	eloop_exit(ctx->eloop, EXIT_FAILURE);
183 	return -1;
184 }
185 
186 ssize_t
ps_bpf_cmd(struct dhcpcd_ctx * ctx,struct ps_msghdr * psm,struct msghdr * msg)187 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
188 {
189 	uint16_t cmd;
190 	struct ps_process *psp;
191 	pid_t start;
192 	struct iovec *iov = msg->msg_iov;
193 	struct interface *ifp;
194 	struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;
195 	const char *addr;
196 
197 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
198 	psp = ps_findprocess(ctx, &psm->ps_id);
199 
200 #ifdef PRIVSEP_DEBUG
201 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
202 #endif
203 
204 	switch (cmd) {
205 #ifdef ARP
206 	case PS_BPF_ARP:	/* FALLTHROUGH */
207 #endif
208 	case PS_BPF_BOOTP:
209 		break;
210 	default:
211 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
212 		errno = ENOTSUP;
213 		return -1;
214 	}
215 
216 	if (!(psm->ps_cmd & PS_START)) {
217 		errno = EINVAL;
218 		return -1;
219 	}
220 
221 	if (psp != NULL)
222 		return 1;
223 
224 	psp = ps_newprocess(ctx, &psm->ps_id);
225 	if (psp == NULL)
226 		return -1;
227 
228 	ifp = &psp->psp_ifp;
229 	assert(msg->msg_iovlen == 1);
230 	assert(iov->iov_len == sizeof(*ifp));
231 	memcpy(ifp, iov->iov_base, sizeof(*ifp));
232 	ifp->ctx = psp->psp_ctx;
233 	ifp->options = NULL;
234 	memset(ifp->if_data, 0, sizeof(ifp->if_data));
235 
236 	memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
237 
238 	switch (cmd) {
239 #ifdef ARP
240 	case PS_BPF_ARP:
241 		psp->psp_proto = ETHERTYPE_ARP;
242 		psp->psp_protostr = "ARP";
243 		psp->psp_filter = bpf_arp;
244 		break;
245 #endif
246 	case PS_BPF_BOOTP:
247 		psp->psp_proto = ETHERTYPE_IP;
248 		psp->psp_protostr = "BOOTP";
249 		psp->psp_filter = bpf_bootp;
250 		break;
251 	}
252 
253 	if (ia->s_addr == INADDR_ANY)
254 		addr = NULL;
255 	else
256 		addr = inet_ntoa(*ia);
257 	snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s",
258 	    psp->psp_protostr,
259 	    addr != NULL ? " " : "", addr != NULL ? addr : "");
260 
261 	start = ps_startprocess(psp, ps_bpf_recvmsg, NULL,
262 	    ps_bpf_start_bpf, NULL, PSF_DROPPRIVS);
263 	switch (start) {
264 	case -1:
265 		ps_freeprocess(psp);
266 		return -1;
267 	case 0:
268 		ps_entersandbox("stdio", NULL);
269 		break;
270 	default:
271 		logdebugx("%s: spawned %s on PID %d",
272 		    psp->psp_ifname, psp->psp_name, psp->psp_pid);
273 		break;
274 	}
275 	return start;
276 }
277 
278 ssize_t
ps_bpf_dispatch(struct dhcpcd_ctx * ctx,struct ps_msghdr * psm,struct msghdr * msg)279 ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
280     struct ps_msghdr *psm, struct msghdr *msg)
281 {
282 	struct iovec *iov = msg->msg_iov;
283 	struct interface *ifp;
284 	uint8_t *bpf;
285 	size_t bpf_len;
286 
287 	switch (psm->ps_cmd) {
288 #ifdef ARP
289 	case PS_BPF_ARP:
290 #endif
291 	case PS_BPF_BOOTP:
292 		break;
293 	default:
294 		errno = ENOTSUP;
295 		return -1;
296 	}
297 
298 	ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
299 	/* interface may have departed .... */
300 	if (ifp == NULL)
301 		return -1;
302 
303 	bpf = iov->iov_base;
304 	bpf_len = iov->iov_len;
305 
306 	switch (psm->ps_cmd) {
307 #ifdef ARP
308 	case PS_BPF_ARP:
309 		arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
310 		break;
311 #endif
312 	case PS_BPF_BOOTP:
313 		dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
314 		break;
315 	}
316 
317 	return 1;
318 }
319 
320 static ssize_t
ps_bpf_send(const struct interface * ifp,const struct in_addr * ia,uint16_t cmd,const void * data,size_t len)321 ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
322     uint16_t cmd, const void *data, size_t len)
323 {
324 	struct dhcpcd_ctx *ctx = ifp->ctx;
325 	struct ps_msghdr psm = {
326 		.ps_cmd = cmd,
327 		.ps_id = {
328 			.psi_ifindex = ifp->index,
329 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
330 		},
331 	};
332 
333 	if (ia != NULL)
334 		psm.ps_id.psi_addr.psa_in_addr = *ia;
335 
336 	return ps_sendpsmdata(ctx, PS_ROOT_FD(ctx), &psm, data, len);
337 }
338 
339 #ifdef ARP
340 ssize_t
ps_bpf_openarp(const struct interface * ifp,const struct in_addr * ia)341 ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
342 {
343 
344 	assert(ia != NULL);
345 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
346 	    ifp, sizeof(*ifp));
347 }
348 
349 ssize_t
ps_bpf_closearp(const struct interface * ifp,const struct in_addr * ia)350 ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
351 {
352 
353 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
354 }
355 
356 ssize_t
ps_bpf_sendarp(const struct interface * ifp,const struct in_addr * ia,const void * data,size_t len)357 ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
358     const void *data, size_t len)
359 {
360 
361 	assert(ia != NULL);
362 	return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
363 }
364 #endif
365 
366 ssize_t
ps_bpf_openbootp(const struct interface * ifp)367 ps_bpf_openbootp(const struct interface *ifp)
368 {
369 
370 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
371 	    ifp, sizeof(*ifp));
372 }
373 
374 ssize_t
ps_bpf_closebootp(const struct interface * ifp)375 ps_bpf_closebootp(const struct interface *ifp)
376 {
377 
378 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
379 }
380 
381 ssize_t
ps_bpf_sendbootp(const struct interface * ifp,const void * data,size_t len)382 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
383 {
384 
385 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);
386 }
387