Update to dhcpcd-9.2.0 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / privsep-bpf.c
blob3025fda8914a97a8c52e2f9405783ddf1eb0bd20
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
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.
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.
29 #include <sys/socket.h>
30 #include <sys/types.h>
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>
40 #include <assert.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
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"
56 #ifdef HAVE_CAPSICUM
57 #include <sys/capsicum.h>
58 #endif
60 static void
61 ps_bpf_recvbpf(void *arg)
63 struct ps_process *psp = arg;
64 struct bpf *bpf = psp->psp_bpf;
65 uint8_t buf[FRAMELEN_MAX];
66 ssize_t len;
67 struct ps_msghdr psm = {
68 .ps_id = psp->psp_id,
69 .ps_cmd = psp->psp_id.psi_cmd,
72 bpf->bpf_flags &= ~BPF_EOF;
73 /* A BPF read can read more than one filtered packet at time.
74 * This mechanism allows us to read each packet from the buffer. */
75 while (!(bpf->bpf_flags & BPF_EOF)) {
76 len = bpf_read(bpf, buf, sizeof(buf));
77 if (len == -1)
78 logerr(__func__);
79 if (len == -1 || len == 0)
80 break;
81 psm.ps_flags = bpf->bpf_flags;
82 len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
83 &psm, buf, (size_t)len);
84 if (len == -1)
85 logerr(__func__);
86 if (len == -1 || len == 0)
87 break;
91 static ssize_t
92 ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
94 struct ps_process *psp = arg;
95 struct iovec *iov = msg->msg_iov;
97 #ifdef PRIVSEP_DEBUG
98 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
99 #endif
101 switch(psm->ps_cmd) {
102 #ifdef ARP
103 case PS_BPF_ARP: /* FALLTHROUGH */
104 #endif
105 case PS_BPF_BOOTP:
106 break;
107 default:
108 /* IPC failure, we should not be processing any commands
109 * at this point!/ */
110 errno = EINVAL;
111 return -1;
114 return bpf_send(psp->psp_bpf, psp->psp_proto,
115 iov->iov_base, iov->iov_len);
118 static void
119 ps_bpf_recvmsg(void *arg)
121 struct ps_process *psp = arg;
123 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd,
124 ps_bpf_recvmsgcb, arg) == -1)
125 logerr(__func__);
128 static int
129 ps_bpf_start_bpf(void *arg)
131 struct ps_process *psp = arg;
132 struct dhcpcd_ctx *ctx = psp->psp_ctx;
133 char *addr;
134 struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
135 #ifdef HAVE_CAPSICUM
136 cap_rights_t rights;
138 /* We need CAP_IOCTL so we can change the BPF filter when we
139 * need to. */
140 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL);
141 #endif
143 if (ia->s_addr == INADDR_ANY) {
144 ia = NULL;
145 addr = NULL;
146 } else
147 addr = inet_ntoa(*ia);
148 setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
149 addr != NULL ? " " : "", addr != NULL ? addr : "");
150 ps_freeprocesses(ctx, psp);
152 psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
153 if (psp->psp_bpf == NULL)
154 logerr("%s: bpf_open",__func__);
155 #ifdef HAVE_CAPSICUM
156 else if (cap_rights_limit(psp->psp_bpf->bpf_fd, &rights) == -1 &&
157 errno != ENOSYS)
158 logerr("%s: cap_rights_limit", __func__);
159 #endif
160 else if (eloop_event_add(ctx->eloop,
161 psp->psp_bpf->bpf_fd, ps_bpf_recvbpf, psp) == -1)
162 logerr("%s: eloop_event_add", __func__);
163 else {
164 psp->psp_work_fd = psp->psp_bpf->bpf_fd;
165 return 0;
168 eloop_exit(ctx->eloop, EXIT_FAILURE);
169 return -1;
172 ssize_t
173 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
175 uint16_t cmd;
176 struct ps_process *psp;
177 pid_t start;
178 struct iovec *iov = msg->msg_iov;
179 struct interface *ifp;
181 cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
182 psp = ps_findprocess(ctx, &psm->ps_id);
184 #ifdef PRIVSEP_DEBUG
185 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
186 #endif
188 switch (cmd) {
189 #ifdef ARP
190 case PS_BPF_ARP: /* FALLTHROUGH */
191 #endif
192 case PS_BPF_BOOTP:
193 break;
194 default:
195 logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
196 errno = ENOTSUP;
197 return -1;
200 if (!(psm->ps_cmd & PS_START)) {
201 errno = EINVAL;
202 return -1;
205 if (psp != NULL)
206 return 1;
208 psp = ps_newprocess(ctx, &psm->ps_id);
209 if (psp == NULL)
210 return -1;
212 ifp = &psp->psp_ifp;
213 assert(msg->msg_iovlen == 1);
214 assert(iov->iov_len == sizeof(*ifp));
215 memcpy(ifp, iov->iov_base, sizeof(*ifp));
216 ifp->ctx = psp->psp_ctx;
217 ifp->options = NULL;
218 memset(ifp->if_data, 0, sizeof(ifp->if_data));
220 memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
222 switch (cmd) {
223 #ifdef ARP
224 case PS_BPF_ARP:
225 psp->psp_proto = ETHERTYPE_ARP;
226 psp->psp_protostr = "ARP";
227 psp->psp_filter = bpf_arp;
228 break;
229 #endif
230 case PS_BPF_BOOTP:
231 psp->psp_proto = ETHERTYPE_IP;
232 psp->psp_protostr = "BOOTP";
233 psp->psp_filter = bpf_bootp;
234 break;
237 start = ps_dostart(ctx,
238 &psp->psp_pid, &psp->psp_fd,
239 ps_bpf_recvmsg, NULL, psp,
240 ps_bpf_start_bpf, NULL,
241 PSF_DROPPRIVS);
242 switch (start) {
243 case -1:
244 ps_freeprocess(psp);
245 return -1;
246 case 0:
247 #ifdef HAVE_CAPSICUM
248 if (cap_enter() == -1 && errno != ENOSYS)
249 logerr("%s: cap_enter", __func__);
250 #endif
251 #ifdef HAVE_PLEDGE
252 if (pledge("stdio", NULL) == -1)
253 logerr("%s: pledge", __func__);
254 #endif
255 break;
256 default:
257 #ifdef PRIVSEP_DEBUG
258 logdebugx("%s: spawned BPF %s on PID %d",
259 psp->psp_ifname, psp->psp_protostr, start);
260 #endif
261 break;
263 return start;
266 ssize_t
267 ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
268 struct ps_msghdr *psm, struct msghdr *msg)
270 struct iovec *iov = msg->msg_iov;
271 struct interface *ifp;
272 uint8_t *bpf;
273 size_t bpf_len;
275 ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
276 bpf = iov->iov_base;
277 bpf_len = iov->iov_len;
279 switch (psm->ps_cmd) {
280 #ifdef ARP
281 case PS_BPF_ARP:
282 arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
283 break;
284 #endif
285 case PS_BPF_BOOTP:
286 dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
287 break;
288 default:
289 errno = ENOTSUP;
290 return -1;
293 return 1;
296 static ssize_t
297 ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
298 uint16_t cmd, const void *data, size_t len)
300 struct dhcpcd_ctx *ctx = ifp->ctx;
301 struct ps_msghdr psm = {
302 .ps_cmd = cmd,
303 .ps_id = {
304 .psi_ifindex = ifp->index,
305 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
309 if (ia != NULL)
310 psm.ps_id.psi_addr.psa_in_addr = *ia;
312 return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
315 #ifdef ARP
316 ssize_t
317 ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
320 assert(ia != NULL);
321 return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
322 ifp, sizeof(*ifp));
325 ssize_t
326 ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
329 return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
332 ssize_t
333 ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
334 const void *data, size_t len)
337 assert(ia != NULL);
338 return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
340 #endif
342 ssize_t
343 ps_bpf_openbootp(const struct interface *ifp)
346 return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
347 ifp, sizeof(*ifp));
350 ssize_t
351 ps_bpf_closebootp(const struct interface *ifp)
354 return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
357 ssize_t
358 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
361 return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);