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