Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Privilege Separation BPF Initiator
      3  * SPDX-License-Identifier: BSD-2-Clause
      4  * Copyright (c) 2006-2025 Roy Marples <roy (at) 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/types.h>
     30 #include <sys/socket.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 <errno.h>
     42 #include <pwd.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 /* We expect to have open 3 SOCK_STREAM and one RAW fd */
     57 
     58 static void
     59 ps_bpf_recvbpf(void *arg, unsigned short events)
     60 {
     61 	struct ps_process *psp = arg;
     62 	struct bpf *bpf = psp->psp_bpf;
     63 	uint8_t buf[FRAMELEN_MAX];
     64 	ssize_t len;
     65 	struct ps_msghdr psm = {
     66 		.ps_id = psp->psp_id,
     67 		.ps_cmd = psp->psp_id.psi_cmd,
     68 	};
     69 
     70 	if (!(events & (ELE_READ | ELE_ERROR)))
     71 		logerrx("%s: unexpected event 0x%04x", __func__, events);
     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 			int error = errno;
     80 
     81 			if (errno != ENETDOWN)
     82 				logerr("%s: %s", psp->psp_ifname, __func__);
     83 			if (error != ENXIO)
     84 				break;
     85 			/* If the interface has departed, close the BPF
     86 			 * socket. This stops log spam if RTM_IFANNOUNCE is
     87 			 * delayed in announcing the departing interface. */
     88 			eloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd);
     89 			bpf_close(bpf);
     90 			psp->psp_bpf = NULL;
     91 			break;
     92 		}
     93 		if (len == 0)
     94 			break;
     95 		psm.ps_flags = bpf->bpf_flags;
     96 		len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
     97 		    &psm, buf, (size_t)len);
     98 		if (len == -1)
     99 			logerr(__func__);
    100 		if (len == -1 || len == 0)
    101 			break;
    102 	}
    103 }
    104 
    105 static ssize_t
    106 ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
    107 {
    108 	struct ps_process *psp = arg;
    109 	struct iovec *iov = msg->msg_iov;
    110 
    111 #ifdef PRIVSEP_DEBUG
    112 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
    113 #endif
    114 
    115 	switch (psm->ps_cmd) {
    116 #ifdef ARP
    117 	case PS_BPF_ARP: /* FALLTHROUGH */
    118 #endif
    119 	case PS_BPF_BOOTP:
    120 		break;
    121 	default:
    122 		/* IPC failure, we should not be processing any commands
    123 		 * at this point!/ */
    124 		errno = EINVAL;
    125 		return -1;
    126 	}
    127 
    128 	/* We might have had an earlier ENXIO error. */
    129 	if (psp->psp_bpf == NULL) {
    130 		errno = ENXIO;
    131 		return -1;
    132 	}
    133 
    134 	return bpf_send(psp->psp_bpf, psp->psp_proto, iov->iov_base,
    135 	    iov->iov_len);
    136 }
    137 
    138 static void
    139 ps_bpf_recvmsg(void *arg, unsigned short events)
    140 {
    141 	struct ps_process *psp = arg;
    142 
    143 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_bpf_recvmsgcb,
    144 		arg) == -1)
    145 		logerr(__func__);
    146 }
    147 
    148 static int
    149 ps_bpf_start_bpf(struct ps_process *psp)
    150 {
    151 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
    152 	struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
    153 #ifdef HAVE_SETPROCTITLE
    154 	char *addr;
    155 #endif
    156 
    157 	if (ia->s_addr == INADDR_ANY)
    158 		ia = NULL;
    159 
    160 #ifdef HAVE_SETPROCTITLE
    161 	addr = ia != NULL ? inet_ntoa(*ia) : NULL;
    162 	setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
    163 	    addr != NULL ? " " : "", addr != NULL ? addr : "");
    164 #endif
    165 
    166 	ps_freeprocesses(ctx, psp);
    167 
    168 	psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
    169 #ifdef DEBUG_FD
    170 	logdebugx("pid %ld bpf_fd=%d", (long)getpid(), psp->psp_bpf->bpf_fd);
    171 #endif
    172 	if (psp->psp_bpf == NULL)
    173 		logerr("%s: bpf_open", __func__);
    174 #ifdef PRIVSEP_RIGHTS
    175 	else if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1)
    176 		logerr("%s: ps_rights_limit_fd", __func__);
    177 #endif
    178 	else if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ELE_READ,
    179 		     ps_bpf_recvbpf, psp) == -1)
    180 		logerr("%s: eloop_event_add", __func__);
    181 	else {
    182 		psp->psp_work_fd = psp->psp_bpf->bpf_fd;
    183 		return 0;
    184 	}
    185 
    186 	eloop_exit(ctx->eloop, EXIT_FAILURE);
    187 	return -1;
    188 }
    189 
    190 ssize_t
    191 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
    192 {
    193 	uint16_t cmd;
    194 	struct ps_process *psp;
    195 	pid_t start;
    196 	struct iovec *iov = msg->msg_iov;
    197 	struct interface *ifp;
    198 	struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;
    199 	const char *addr;
    200 
    201 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
    202 	psp = ps_findprocess(ctx, &psm->ps_id);
    203 
    204 #ifdef PRIVSEP_DEBUG
    205 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
    206 #endif
    207 
    208 	switch (cmd) {
    209 #ifdef ARP
    210 	case PS_BPF_ARP: /* FALLTHROUGH */
    211 #endif
    212 	case PS_BPF_BOOTP:
    213 		break;
    214 	default:
    215 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
    216 		errno = ENOTSUP;
    217 		return -1;
    218 	}
    219 
    220 	if (!(psm->ps_cmd & PS_START)) {
    221 		errno = EINVAL;
    222 		return -1;
    223 	}
    224 
    225 	if (psp != NULL)
    226 		return 1;
    227 
    228 	psp = ps_newprocess(ctx, &psm->ps_id);
    229 	if (psp == NULL)
    230 		return -1;
    231 
    232 	ifp = &psp->psp_ifp;
    233 	assert(msg->msg_iovlen == 1);
    234 	assert(iov->iov_len == sizeof(*ifp));
    235 	memcpy(ifp, iov->iov_base, sizeof(*ifp));
    236 	ifp->ctx = psp->psp_ctx;
    237 	ifp->options = NULL;
    238 	memset(ifp->if_data, 0, sizeof(ifp->if_data));
    239 
    240 	memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
    241 
    242 	switch (cmd) {
    243 #ifdef ARP
    244 	case PS_BPF_ARP:
    245 		psp->psp_proto = ETHERTYPE_ARP;
    246 		psp->psp_protostr = "ARP";
    247 		psp->psp_filter = bpf_filter_arp;
    248 		break;
    249 #endif
    250 	case PS_BPF_BOOTP:
    251 		psp->psp_proto = ETHERTYPE_IP;
    252 		psp->psp_protostr = "BOOTP";
    253 		psp->psp_filter = bpf_filter_bootp;
    254 		break;
    255 	}
    256 
    257 	if (ia->s_addr == INADDR_ANY)
    258 		addr = NULL;
    259 	else
    260 		addr = inet_ntoa(*ia);
    261 	snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s",
    262 	    psp->psp_protostr, addr != NULL ? " " : "",
    263 	    addr != NULL ? addr : "");
    264 
    265 	start = ps_startprocess(psp, ps_bpf_recvmsg, NULL, ps_bpf_start_bpf,
    266 	    PSF_DROPPRIVS);
    267 
    268 	switch (start) {
    269 	case -1:
    270 		ps_freeprocess(psp);
    271 		return -1;
    272 	case 0:
    273 		ps_entersandbox("stdio", NULL);
    274 		break;
    275 	default:
    276 		logdebugx("%s: spawned %s on PID %ld", psp->psp_ifname,
    277 		    psp->psp_name, (long)psp->psp_pid);
    278 		break;
    279 	}
    280 	return start;
    281 }
    282 
    283 ssize_t
    284 ps_bpf_dispatch(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
    285     struct msghdr *msg)
    286 {
    287 	struct iovec *iov = msg->msg_iov;
    288 	struct interface *ifp;
    289 	uint8_t *bpf;
    290 	size_t bpf_len;
    291 
    292 	switch (psm->ps_cmd) {
    293 #ifdef ARP
    294 	case PS_BPF_ARP:
    295 #endif
    296 	case PS_BPF_BOOTP:
    297 		break;
    298 	default:
    299 		errno = ENOTSUP;
    300 		return -1;
    301 	}
    302 
    303 	ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
    304 	/* interface may have departed .... */
    305 	if (ifp == NULL)
    306 		return -1;
    307 
    308 	bpf = iov->iov_base;
    309 	bpf_len = iov->iov_len;
    310 
    311 	switch (psm->ps_cmd) {
    312 #ifdef ARP
    313 	case PS_BPF_ARP:
    314 		arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
    315 		break;
    316 #endif
    317 	case PS_BPF_BOOTP:
    318 		dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
    319 		break;
    320 	}
    321 
    322 	return 1;
    323 }
    324 
    325 static ssize_t
    326 ps_bpf_send(const struct interface *ifp, const struct in_addr *ia, uint16_t cmd,
    327     const void *data, size_t len)
    328 {
    329 	struct dhcpcd_ctx *ctx = ifp->ctx;
    330 	struct ps_msghdr psm = {
    331 		.ps_cmd = cmd,
    332 		.ps_id = {
    333 			.psi_ifindex = ifp->index,
    334 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
    335 		},
    336 	};
    337 
    338 	if (ia != NULL)
    339 		psm.ps_id.psi_addr.psa_in_addr = *ia;
    340 
    341 	return ps_sendpsmdata(ctx, PS_ROOT_FD(ctx), &psm, data, len);
    342 }
    343 
    344 #ifdef ARP
    345 ssize_t
    346 ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
    347 {
    348 	assert(ia != NULL);
    349 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START, ifp, sizeof(*ifp));
    350 }
    351 
    352 ssize_t
    353 ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
    354 {
    355 	return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
    356 }
    357 
    358 ssize_t
    359 ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
    360     const void *data, size_t len)
    361 {
    362 	assert(ia != NULL);
    363 	return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
    364 }
    365 #endif
    366 
    367 ssize_t
    368 ps_bpf_openbootp(const struct interface *ifp)
    369 {
    370 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START, ifp,
    371 	    sizeof(*ifp));
    372 }
    373 
    374 ssize_t
    375 ps_bpf_closebootp(const struct interface *ifp)
    376 {
    377 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
    378 }
    379 
    380 ssize_t
    381 ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
    382 {
    383 	return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);
    384 }
    385