Mercurial > hg > dhcpcd
view src/privsep-bpf.c @ 5556:99bfd2eb77ab draft
privsep: Don't log a BPF error that network is down
The master process will catch this clean remove the BPF process.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Wed, 09 Dec 2020 11:15:30 +0000 |
| parents | 357fddea9365 |
| children |
line wrap: on
line source
/* SPDX-License-Identifier: BSD-2-Clause */ /* * Privilege Separation BPF Initiator * Copyright (c) 2006-2020 Roy Marples <roy@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/socket.h> #include <sys/types.h> /* Need these headers just for if_ether on some OS. */ #ifndef __NetBSD__ #include <net/if.h> #include <net/if_arp.h> #include <netinet/in.h> #endif #include <netinet/if_ether.h> #include <assert.h> #include <pwd.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "arp.h" #include "bpf.h" #include "dhcp.h" #include "dhcp6.h" #include "eloop.h" #include "ipv6nd.h" #include "logerr.h" #include "privsep.h" static void ps_bpf_recvbpf(void *arg) { struct ps_process *psp = arg; struct bpf *bpf = psp->psp_bpf; uint8_t buf[FRAMELEN_MAX]; ssize_t len; struct ps_msghdr psm = { .ps_id = psp->psp_id, .ps_cmd = psp->psp_id.psi_cmd, }; bpf->bpf_flags &= ~BPF_EOF; /* A BPF read can read more than one filtered packet at time. * This mechanism allows us to read each packet from the buffer. */ while (!(bpf->bpf_flags & BPF_EOF)) { len = bpf_read(bpf, buf, sizeof(buf)); if (len == -1) { int error = errno; if (errno != ENETDOWN) logerr("%s: %s", psp->psp_ifname, __func__); if (error != ENXIO) break; /* If the interface has departed, close the BPF * socket. This stops log spam if RTM_IFANNOUNCE is * delayed in announcing the departing interface. */ eloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd); bpf_close(bpf); psp->psp_bpf = NULL; break; } if (len == 0) break; psm.ps_flags = bpf->bpf_flags; len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd, &psm, buf, (size_t)len); if (len == -1) logerr(__func__); if (len == -1 || len == 0) break; } } static ssize_t ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) { struct ps_process *psp = arg; struct iovec *iov = msg->msg_iov; #ifdef PRIVSEP_DEBUG logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); #endif switch(psm->ps_cmd) { #ifdef ARP case PS_BPF_ARP: /* FALLTHROUGH */ #endif case PS_BPF_BOOTP: break; default: /* IPC failure, we should not be processing any commands * at this point!/ */ errno = EINVAL; return -1; } /* We might have had an earlier ENXIO error. */ if (psp->psp_bpf == NULL) { errno = ENXIO; return -1; } return bpf_send(psp->psp_bpf, psp->psp_proto, iov->iov_base, iov->iov_len); } static void ps_bpf_recvmsg(void *arg) { struct ps_process *psp = arg; if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, ps_bpf_recvmsgcb, arg) == -1) logerr(__func__); } static int ps_bpf_start_bpf(void *arg) { struct ps_process *psp = arg; struct dhcpcd_ctx *ctx = psp->psp_ctx; char *addr; struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; if (ia->s_addr == INADDR_ANY) { ia = NULL; addr = NULL; } else addr = inet_ntoa(*ia); setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname, addr != NULL ? " " : "", addr != NULL ? addr : ""); ps_freeprocesses(ctx, psp); psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia); if (psp->psp_bpf == NULL) logerr("%s: bpf_open",__func__); #ifdef PRIVSEP_RIGHTS else if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1) logerr("%s: ps_rights_limit_fd", __func__); #endif else if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ps_bpf_recvbpf, psp) == -1) logerr("%s: eloop_event_add", __func__); else { psp->psp_work_fd = psp->psp_bpf->bpf_fd; return 0; } eloop_exit(ctx->eloop, EXIT_FAILURE); return -1; } ssize_t ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) { uint16_t cmd; struct ps_process *psp; pid_t start; struct iovec *iov = msg->msg_iov; struct interface *ifp; cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); psp = ps_findprocess(ctx, &psm->ps_id); #ifdef PRIVSEP_DEBUG logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); #endif switch (cmd) { #ifdef ARP case PS_BPF_ARP: /* FALLTHROUGH */ #endif case PS_BPF_BOOTP: break; default: logerrx("%s: unknown command %x", __func__, psm->ps_cmd); errno = ENOTSUP; return -1; } if (!(psm->ps_cmd & PS_START)) { errno = EINVAL; return -1; } if (psp != NULL) return 1; psp = ps_newprocess(ctx, &psm->ps_id); if (psp == NULL) return -1; ifp = &psp->psp_ifp; assert(msg->msg_iovlen == 1); assert(iov->iov_len == sizeof(*ifp)); memcpy(ifp, iov->iov_base, sizeof(*ifp)); ifp->ctx = psp->psp_ctx; ifp->options = NULL; memset(ifp->if_data, 0, sizeof(ifp->if_data)); memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname)); switch (cmd) { #ifdef ARP case PS_BPF_ARP: psp->psp_proto = ETHERTYPE_ARP; psp->psp_protostr = "ARP"; psp->psp_filter = bpf_arp; break; #endif case PS_BPF_BOOTP: psp->psp_proto = ETHERTYPE_IP; psp->psp_protostr = "BOOTP"; psp->psp_filter = bpf_bootp; break; } start = ps_dostart(ctx, &psp->psp_pid, &psp->psp_fd, ps_bpf_recvmsg, NULL, psp, ps_bpf_start_bpf, NULL, PSF_DROPPRIVS); switch (start) { case -1: ps_freeprocess(psp); return -1; case 0: ps_entersandbox("stdio", NULL); break; default: #ifdef PRIVSEP_DEBUG logdebugx("%s: spawned BPF %s on PID %d", psp->psp_ifname, psp->psp_protostr, start); #endif break; } return start; } ssize_t ps_bpf_dispatch(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) { struct iovec *iov = msg->msg_iov; struct interface *ifp; uint8_t *bpf; size_t bpf_len; switch (psm->ps_cmd) { #ifdef ARP case PS_BPF_ARP: #endif case PS_BPF_BOOTP: break; default: errno = ENOTSUP; return -1; } ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex); /* interface may have departed .... */ if (ifp == NULL) return -1; bpf = iov->iov_base; bpf_len = iov->iov_len; switch (psm->ps_cmd) { #ifdef ARP case PS_BPF_ARP: arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags); break; #endif case PS_BPF_BOOTP: dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags); break; } return 1; } static ssize_t ps_bpf_send(const struct interface *ifp, const struct in_addr *ia, uint16_t cmd, const void *data, size_t len) { struct dhcpcd_ctx *ctx = ifp->ctx; struct ps_msghdr psm = { .ps_cmd = cmd, .ps_id = { .psi_ifindex = ifp->index, .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), }, }; if (ia != NULL) psm.ps_id.psi_addr.psa_in_addr = *ia; return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len); } #ifdef ARP ssize_t ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia) { assert(ia != NULL); return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START, ifp, sizeof(*ifp)); } ssize_t ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia) { return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0); } ssize_t ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia, const void *data, size_t len) { assert(ia != NULL); return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len); } #endif ssize_t ps_bpf_openbootp(const struct interface *ifp) { return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START, ifp, sizeof(*ifp)); } ssize_t ps_bpf_closebootp(const struct interface *ifp) { return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0); } ssize_t ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len) { return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len); }
