xref: /dragonfly/contrib/dhcpcd/src/privsep-bpf.c (revision 8f5a7c01)
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)
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 	if (sig != SIGTERM)
179 		return;
180 
181 	eloop_exit(ctx->eloop, EXIT_SUCCESS);
182 }
183 
184 ssize_t
185 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
186 {
187 	uint16_t cmd;
188 	struct ps_process *psp;
189 	pid_t start;
190 	struct iovec *iov = msg->msg_iov;
191 	struct interface *ifp;
192 
193 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
194 	psp = ps_findprocess(ctx, &psm->ps_id);
195 
196 #ifdef PRIVSEP_DEBUG
197 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
198 #endif
199 
200 	switch (cmd) {
201 #ifdef ARP
202 	case PS_BPF_ARP:	/* FALLTHROUGH */
203 #endif
204 	case PS_BPF_BOOTP:
205 		break;
206 	default:
207 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
208 		errno = ENOTSUP;
209 		return -1;
210 	}
211 
212 	if (!(psm->ps_cmd & PS_START)) {
213 		errno = EINVAL;
214 		return -1;
215 	}
216 
217 	if (psp != NULL)
218 		return 1;
219 
220 	psp = ps_newprocess(ctx, &psm->ps_id);
221 	if (psp == NULL)
222 		return -1;
223 
224 	ifp = &psp->psp_ifp;
225 	assert(msg->msg_iovlen == 1);
226 	assert(iov->iov_len == sizeof(*ifp));
227 	memcpy(ifp, iov->iov_base, sizeof(*ifp));
228 	ifp->ctx = psp->psp_ctx;
229 	ifp->options = NULL;
230 	memset(ifp->if_data, 0, sizeof(ifp->if_data));
231 
232 	memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
233 
234 	switch (cmd) {
235 #ifdef ARP
236 	case PS_BPF_ARP:
237 		psp->psp_proto = ETHERTYPE_ARP;
238 		psp->psp_protostr = "ARP";
239 		psp->psp_filter = bpf_arp;
240 		break;
241 #endif
242 	case PS_BPF_BOOTP:
243 		psp->psp_proto = ETHERTYPE_IP;
244 		psp->psp_protostr = "BOOTP";
245 		psp->psp_filter = bpf_bootp;
246 		break;
247 	}
248 
249 	start = ps_dostart(ctx,
250 	    &psp->psp_pid, &psp->psp_fd,
251 	    ps_bpf_recvmsg, NULL, psp,
252 	    ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
253 	    PSF_DROPPRIVS);
254 	switch (start) {
255 	case -1:
256 		ps_freeprocess(psp);
257 		return -1;
258 	case 0:
259 #ifdef HAVE_CAPSICUM
260 		if (cap_enter() == -1 && errno != ENOSYS)
261 			logerr("%s: cap_enter", __func__);
262 #endif
263 #ifdef HAVE_PLEDGE
264 		if (pledge("stdio", NULL) == -1)
265 			logerr("%s: pledge", __func__);
266 #endif
267 		break;
268 	default:
269 #ifdef PRIVSEP_DEBUG
270 		logdebugx("%s: spawned BPF %s on PID %d",
271 		    psp->psp_ifname, psp->psp_protostr, start);
272 #endif
273 		break;
274 	}
275 	return start;
276 }
277 
278 ssize_t
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 	ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
288 	bpf = iov->iov_base;
289 	bpf_len = iov->iov_len;
290 
291 	switch (psm->ps_cmd) {
292 #ifdef ARP
293 	case PS_BPF_ARP:
294 		arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
295 		break;
296 #endif
297 	case PS_BPF_BOOTP:
298 		dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
299 		break;
300 	default:
301 		errno = ENOTSUP;
302 		return -1;
303 	}
304 
305 	return 1;
306 }
307 
308 static ssize_t
309 ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
310     uint16_t cmd, const void *data, size_t len)
311 {
312 	struct dhcpcd_ctx *ctx = ifp->ctx;
313 	struct ps_msghdr psm = {
314 		.ps_cmd = cmd,
315 		.ps_id = {
316 			.psi_ifindex = ifp->index,
317 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
318 		},
319 	};
320 
321 	if (ia != NULL)
322 		psm.ps_id.psi_addr.psa_in_addr = *ia;
323 
324 	return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
325 }
326 
327 #ifdef ARP
328 ssize_t
329 ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
330 {
331 
332 	assert(ia != NULL);
333 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
334 	    ifp, sizeof(*ifp));
335 }
336 
337 ssize_t
338 ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
339 {
340 
341 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
342 }
343 
344 ssize_t
345 ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
346     const void *data, size_t len)
347 {
348 
349 	assert(ia != NULL);
350 	return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
351 }
352 #endif
353 
354 ssize_t
355 ps_bpf_openbootp(const struct interface *ifp)
356 {
357 
358 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
359 	    ifp, sizeof(*ifp));
360 }
361 
362 ssize_t
363 ps_bpf_closebootp(const struct interface *ifp)
364 {
365 
366 	return ps_bpf_send(ifp, NULL, 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, NULL, PS_BPF_BOOTP, data, len);
374 }
375