xref: /dragonfly/contrib/dhcpcd/src/privsep-bpf.c (revision 335b9e93)
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 #ifdef HAVE_CAPSICUM
58 #include <sys/capsicum.h>
59 #endif
60 
61 static void
62 ps_bpf_recvbpf(void *arg)
63 {
64 	struct ps_process *psp = arg;
65 	struct bpf *bpf = psp->psp_bpf;
66 	uint8_t buf[FRAMELEN_MAX];
67 	ssize_t len;
68 	struct ps_msghdr psm = {
69 		.ps_id = psp->psp_id,
70 		.ps_cmd = psp->psp_id.psi_cmd,
71 	};
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 			logerr(__func__);
80 		if (len == -1 || len == 0)
81 			break;
82 		psm.ps_flags = bpf->bpf_flags;
83 		len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
84 		    &psm, buf, (size_t)len);
85 		if (len == -1 && errno != ECONNRESET)
86 			logerr(__func__);
87 		if (len == -1 || len == 0)
88 			break;
89 	}
90 }
91 
92 static ssize_t
93 ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
94 {
95 	struct ps_process *psp = arg;
96 	struct iovec *iov = msg->msg_iov;
97 
98 #ifdef PRIVSEP_DEBUG
99 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
100 #endif
101 
102 	switch(psm->ps_cmd) {
103 #ifdef ARP
104 	case PS_BPF_ARP:	/* FALLTHROUGH */
105 #endif
106 	case PS_BPF_BOOTP:
107 		break;
108 	default:
109 		/* IPC failure, we should not be processing any commands
110 		 * at this point!/ */
111 		errno = EINVAL;
112 		return -1;
113 	}
114 
115 	return bpf_send(psp->psp_bpf, psp->psp_proto,
116 	    iov->iov_base, iov->iov_len);
117 }
118 
119 static void
120 ps_bpf_recvmsg(void *arg)
121 {
122 	struct ps_process *psp = arg;
123 
124 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd,
125 	    ps_bpf_recvmsgcb, arg) == -1)
126 		logerr(__func__);
127 }
128 
129 static int
130 ps_bpf_start_bpf(void *arg)
131 {
132 	struct ps_process *psp = arg;
133 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
134 	char *addr;
135 	struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
136 #ifdef HAVE_CAPSICUM
137 	cap_rights_t rights;
138 
139 	/* We need CAP_IOCTL so we can change the BPF filter when we
140 	 * need to. */
141 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL);
142 #endif
143 
144 	if (ia->s_addr == INADDR_ANY) {
145 		ia = NULL;
146 		addr = NULL;
147 	} else
148 		addr = inet_ntoa(*ia);
149 	setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
150 	    addr != NULL ? " " : "", addr != NULL ? addr : "");
151 	ps_freeprocesses(ctx, psp);
152 
153 	psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
154 	if (psp->psp_bpf == NULL)
155 		logerr("%s: bpf_open",__func__);
156 #ifdef HAVE_CAPSICUM
157 	else if (cap_rights_limit(psp->psp_bpf->bpf_fd, &rights) == -1 &&
158 	    errno != ENOSYS)
159 		logerr("%s: cap_rights_limit", __func__);
160 #endif
161 	else if (eloop_event_add(ctx->eloop,
162 	    psp->psp_bpf->bpf_fd, ps_bpf_recvbpf, psp) == -1)
163 		logerr("%s: eloop_event_add", __func__);
164 	else {
165 		psp->psp_work_fd = psp->psp_bpf->bpf_fd;
166 		return 0;
167 	}
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 	uint16_t cmd;
185 	struct ps_process *psp;
186 	pid_t start;
187 	struct iovec *iov = msg->msg_iov;
188 	struct interface *ifp;
189 
190 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
191 	psp = ps_findprocess(ctx, &psm->ps_id);
192 
193 #ifdef PRIVSEP_DEBUG
194 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
195 #endif
196 
197 	switch (cmd) {
198 #ifdef ARP
199 	case PS_BPF_ARP:	/* FALLTHROUGH */
200 #endif
201 	case PS_BPF_BOOTP:
202 		break;
203 	default:
204 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
205 		errno = ENOTSUP;
206 		return -1;
207 	}
208 
209 	if (!(psm->ps_cmd & PS_START)) {
210 		errno = EINVAL;
211 		return -1;
212 	}
213 
214 	if (psp != NULL)
215 		return 1;
216 
217 	psp = ps_newprocess(ctx, &psm->ps_id);
218 	if (psp == NULL)
219 		return -1;
220 
221 	ifp = &psp->psp_ifp;
222 	assert(msg->msg_iovlen == 1);
223 	assert(iov->iov_len == sizeof(*ifp));
224 	memcpy(ifp, iov->iov_base, sizeof(*ifp));
225 	ifp->ctx = psp->psp_ctx;
226 	ifp->options = NULL;
227 	memset(ifp->if_data, 0, sizeof(ifp->if_data));
228 
229 	memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
230 
231 	switch (cmd) {
232 #ifdef ARP
233 	case PS_BPF_ARP:
234 		psp->psp_proto = ETHERTYPE_ARP;
235 		psp->psp_protostr = "ARP";
236 		psp->psp_filter = bpf_arp;
237 		break;
238 #endif
239 	case PS_BPF_BOOTP:
240 		psp->psp_proto = ETHERTYPE_IP;
241 		psp->psp_protostr = "BOOTP";
242 		psp->psp_filter = bpf_bootp;
243 		break;
244 	}
245 
246 	start = ps_dostart(ctx,
247 	    &psp->psp_pid, &psp->psp_fd,
248 	    ps_bpf_recvmsg, NULL, psp,
249 	    ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
250 	    PSF_DROPPRIVS);
251 	switch (start) {
252 	case -1:
253 		ps_freeprocess(psp);
254 		return -1;
255 	case 0:
256 #ifdef HAVE_CAPSICUM
257 		if (cap_enter() == -1 && errno != ENOSYS)
258 			logerr("%s: cap_enter", __func__);
259 #endif
260 #ifdef HAVE_PLEDGE
261 		if (pledge("stdio", NULL) == -1)
262 			logerr("%s: pledge", __func__);
263 #endif
264 		break;
265 	default:
266 #ifdef PRIVSEP_DEBUG
267 		logdebugx("%s: spawned BPF %s on PID %d",
268 		    psp->psp_ifname, psp->psp_protostr, start);
269 #endif
270 		break;
271 	}
272 	return start;
273 }
274 
275 ssize_t
276 ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
277     struct ps_msghdr *psm, struct msghdr *msg)
278 {
279 	struct iovec *iov = msg->msg_iov;
280 	struct interface *ifp;
281 	uint8_t *bpf;
282 	size_t bpf_len;
283 
284 	ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
285 	bpf = iov->iov_base;
286 	bpf_len = iov->iov_len;
287 
288 	switch (psm->ps_cmd) {
289 #ifdef ARP
290 	case PS_BPF_ARP:
291 		arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
292 		break;
293 #endif
294 	case PS_BPF_BOOTP:
295 		dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
296 		break;
297 	default:
298 		errno = ENOTSUP;
299 		return -1;
300 	}
301 
302 	return 1;
303 }
304 
305 static ssize_t
306 ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
307     uint16_t cmd, const void *data, size_t len)
308 {
309 	struct dhcpcd_ctx *ctx = ifp->ctx;
310 	struct ps_msghdr psm = {
311 		.ps_cmd = cmd,
312 		.ps_id = {
313 			.psi_ifindex = ifp->index,
314 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
315 		},
316 	};
317 
318 	if (ia != NULL)
319 		psm.ps_id.psi_addr.psa_in_addr = *ia;
320 
321 	return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
322 }
323 
324 #ifdef ARP
325 ssize_t
326 ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
327 {
328 
329 	assert(ia != NULL);
330 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
331 	    ifp, sizeof(*ifp));
332 }
333 
334 ssize_t
335 ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
336 {
337 
338 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
339 }
340 
341 ssize_t
342 ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
343     const void *data, size_t len)
344 {
345 
346 	assert(ia != NULL);
347 	return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
348 }
349 #endif
350 
351 ssize_t
352 ps_bpf_openbootp(const struct interface *ifp)
353 {
354 
355 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
356 	    ifp, sizeof(*ifp));
357 }
358 
359 ssize_t
360 ps_bpf_closebootp(const struct interface *ifp)
361 {
362 
363 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
364 }
365 
366 ssize_t
367 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
368 {
369 
370 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);
371 }
372