xref: /dragonfly/contrib/dhcpcd/src/privsep-bpf.c (revision 2dac8a3e)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation BPF Initiator
4  * Copyright (c) 2006-2020 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 <signal.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "arp.h"
49 #include "bpf.h"
50 #include "dhcp.h"
51 #include "dhcp6.h"
52 #include "eloop.h"
53 #include "ipv6nd.h"
54 #include "logerr.h"
55 #include "privsep.h"
56 
57 static void
58 ps_bpf_recvbpf(void *arg)
59 {
60 	struct ps_process *psp = arg;
61 	unsigned int flags;
62 	uint8_t buf[FRAMELEN_MAX];
63 	ssize_t len;
64 	struct ps_msghdr psm = {
65 		.ps_id = psp->psp_id,
66 		.ps_cmd = psp->psp_id.psi_cmd,
67 	};
68 
69 	/* A BPF read can read more than one filtered packet at time.
70 	 * This mechanism allows us to read each packet from the buffer. */
71 	flags = 0;
72 	while (!(flags & BPF_EOF)) {
73 		len = bpf_read(&psp->psp_ifp, psp->psp_work_fd,
74 		    buf, sizeof(buf), &flags);
75 		if (len == -1)
76 			logerr(__func__);
77 		if (len == -1 || len == 0)
78 			break;
79 		len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
80 		    &psm, buf, (size_t)len);
81 		if (len == -1 && errno != ECONNRESET)
82 			logerr(__func__);
83 		if (len == -1 || len == 0)
84 			break;
85 	}
86 }
87 
88 #ifdef ARP
89 static ssize_t
90 ps_bpf_arp_addr(uint8_t cmd, struct ps_process *psp, struct msghdr *msg)
91 {
92 	struct interface *ifp = &psp->psp_ifp;
93 	struct iovec *iov = msg->msg_iov;
94 	struct in_addr addr;
95 	struct arp_state *astate;
96 
97 	if (psp == NULL) {
98 		errno = ESRCH;
99 		return -1;
100 	}
101 
102 	assert(msg->msg_iovlen == 1);
103 	assert(iov->iov_len == sizeof(addr));
104 	memcpy(&addr, iov->iov_base, sizeof(addr));
105 	if (cmd & PS_START) {
106 		astate = arp_new(ifp, &addr);
107 		if (astate == NULL)
108 			return -1;
109 	} else if (cmd & PS_DELETE) {
110 		astate = arp_find(ifp, &addr);
111 		if (astate == NULL) {
112 			errno = ESRCH;
113 			return -1;
114 		}
115 		arp_free(astate);
116 	} else {
117 		errno = EINVAL;
118 		return -1;
119 	}
120 
121 	return bpf_arp(ifp, psp->psp_work_fd);
122 }
123 #endif
124 
125 static ssize_t
126 ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
127 {
128 	struct ps_process *psp = arg;
129 	struct iovec *iov = msg->msg_iov;
130 
131 #ifdef ARP
132 	if (psm->ps_cmd & (PS_START | PS_DELETE))
133 		return ps_bpf_arp_addr(psm->ps_cmd, psp, msg);
134 #endif
135 
136 	return bpf_send(&psp->psp_ifp, psp->psp_work_fd, psp->psp_proto,
137 	    iov->iov_base, iov->iov_len);
138 }
139 
140 static void
141 ps_bpf_recvmsg(void *arg)
142 {
143 	struct ps_process *psp = arg;
144 
145 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd,
146 	    ps_bpf_recvmsgcb, arg) == -1)
147 		logerr(__func__);
148 }
149 
150 static int
151 ps_bpf_start_bpf(void *arg)
152 {
153 	struct ps_process *psp = arg;
154 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
155 
156 	setproctitle("[BPF %s] %s", psp->psp_protostr, psp->psp_ifname);
157 
158 	ps_freeprocesses(ctx, psp);
159 
160 	psp->psp_work_fd = bpf_open(&psp->psp_ifp, psp->psp_filter);
161 	if (psp->psp_work_fd == -1)
162 		logerr("%s: bpf_open",__func__);
163 	else if (eloop_event_add(ctx->eloop,
164 	    psp->psp_work_fd, ps_bpf_recvbpf, psp) == -1)
165 		logerr("%s: eloop_event_add", __func__);
166 	else
167 		return 0;
168 
169 	eloop_exit(ctx->eloop, EXIT_FAILURE);
170 	return -1;
171 }
172 
173 static void
174 ps_bpf_signal_bpfcb(int sig, void *arg)
175 {
176 	struct dhcpcd_ctx *ctx = arg;
177 
178 	eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
179 }
180 
181 ssize_t
182 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
183 {
184 	uint8_t cmd;
185 	struct ps_process *psp;
186 	pid_t start;
187 	struct iovec *iov = msg->msg_iov;
188 	struct interface *ifp;
189 	struct ipv4_state *istate;
190 
191 	cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
192 	psp = ps_findprocess(ctx, &psm->ps_id);
193 
194 #ifdef PRIVSEP_DEBUG
195 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
196 #endif
197 
198 	switch (cmd) {
199 #ifdef ARP
200 	case PS_BPF_ARP:	/* FALLTHROUGH */
201 #endif
202 	case PS_BPF_BOOTP:
203 		break;
204 	default:
205 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
206 		errno = ENOTSUP;
207 		return -1;
208 	}
209 
210 	if (!(psm->ps_cmd & PS_START)) {
211 		errno = EINVAL;
212 		return -1;
213 	}
214 
215 	if (psp != NULL)
216 		return 1;
217 
218 	psp = ps_newprocess(ctx, &psm->ps_id);
219 	if (psp == NULL)
220 		return -1;
221 
222 	ifp = &psp->psp_ifp;
223 	assert(msg->msg_iovlen == 1);
224 	assert(iov->iov_len == sizeof(*ifp));
225 	memcpy(ifp, iov->iov_base, sizeof(*ifp));
226 	ifp->ctx = psp->psp_ctx;
227 	ifp->options = NULL;
228 	memset(ifp->if_data, 0, sizeof(ifp->if_data));
229 
230 	if ((istate = ipv4_getstate(ifp)) == NULL) {
231 		ps_freeprocess(psp);
232 		return -1;
233 	}
234 
235 	memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
236 
237 	switch (cmd) {
238 #ifdef ARP
239 	case PS_BPF_ARP:
240 		psp->psp_proto = ETHERTYPE_ARP;
241 		psp->psp_protostr = "ARP";
242 		psp->psp_filter = bpf_arp;
243 		break;
244 #endif
245 	case PS_BPF_BOOTP:
246 		psp->psp_proto = ETHERTYPE_IP;
247 		psp->psp_protostr = "BOOTP";
248 		psp->psp_filter = bpf_bootp;
249 		break;
250 	}
251 
252 	start = ps_dostart(ctx,
253 	    &psp->psp_pid, &psp->psp_fd,
254 	    ps_bpf_recvmsg, NULL, psp,
255 	    ps_bpf_start_bpf, ps_bpf_signal_bpfcb, PSF_DROPPRIVS);
256 	switch (start) {
257 	case -1:
258 		ps_freeprocess(psp);
259 		return -1;
260 	case 0:
261 		break;
262 	default:
263 #ifdef PRIVSEP_DEBUG
264 		logdebugx("%s: spawned BPF %s on PID %d",
265 		    psp->psp_ifname, psp->psp_protostr, start);
266 #endif
267 		break;
268 	}
269 	return start;
270 }
271 
272 ssize_t
273 ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
274     struct ps_msghdr *psm, struct msghdr *msg)
275 {
276 	struct iovec *iov = msg->msg_iov;
277 	struct interface *ifp;
278 
279 	ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
280 
281 	switch (psm->ps_cmd) {
282 #ifdef ARP
283 	case PS_BPF_ARP:
284 		arp_packet(ifp, iov->iov_base, iov->iov_len);
285 		break;
286 #endif
287 	case PS_BPF_BOOTP:
288 		dhcp_packet(ifp, iov->iov_base, iov->iov_len);
289 		break;
290 	default:
291 		errno = ENOTSUP;
292 		return -1;
293 	}
294 
295 	return 1;
296 }
297 
298 static ssize_t
299 ps_bpf_send(const struct interface *ifp, uint8_t cmd,
300     const void *data, size_t len)
301 {
302 	struct dhcpcd_ctx *ctx = ifp->ctx;
303 	struct ps_msghdr psm = {
304 		.ps_cmd = cmd,
305 		.ps_id = {
306 			.psi_ifindex = ifp->index,
307 			.psi_cmd = (uint8_t)(cmd &
308 			    ~(PS_START | PS_STOP | PS_DELETE)),
309 		},
310 	};
311 
312 	if (psm.ps_id.psi_cmd == PS_BPF_ARP_ADDR)
313 		psm.ps_id.psi_cmd = PS_BPF_ARP;
314 
315 	return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
316 }
317 
318 #ifdef ARP
319 ssize_t
320 ps_bpf_openarp(const struct interface *ifp)
321 {
322 
323 	return ps_bpf_send(ifp, PS_BPF_ARP | PS_START, ifp, sizeof(*ifp));
324 }
325 
326 ssize_t
327 ps_bpf_addaddr(const struct interface *ifp, const struct in_addr *addr)
328 {
329 
330 	return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_START, addr, sizeof(*addr));
331 }
332 
333 ssize_t
334 ps_bpf_deladdr(const struct interface *ifp, const struct in_addr *addr)
335 {
336 
337 	return ps_bpf_send(ifp, PS_BPF_ARP_ADDR | PS_DELETE, addr, sizeof(*addr));
338 }
339 
340 ssize_t
341 ps_bpf_closearp(const struct interface *ifp)
342 {
343 
344 	return ps_bpf_send(ifp, PS_BPF_ARP | PS_STOP, NULL, 0);
345 }
346 
347 ssize_t
348 ps_bpf_sendarp(const struct interface *ifp, const void *data, size_t len)
349 {
350 
351 	return ps_bpf_send(ifp, PS_BPF_ARP, data, len);
352 }
353 #endif
354 
355 ssize_t
356 ps_bpf_openbootp(const struct interface *ifp)
357 {
358 
359 	return ps_bpf_send(ifp, PS_BPF_BOOTP | PS_START, ifp, sizeof(*ifp));
360 }
361 
362 ssize_t
363 ps_bpf_closebootp(const struct interface *ifp)
364 {
365 
366 	return ps_bpf_send(ifp, PS_BPF_BOOTP | PS_STOP, NULL, 0);
367 }
368 
369 ssize_t
370 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
371 {
372 
373 	return ps_bpf_send(ifp, PS_BPF_BOOTP, data, len);
374 }
375