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