1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd, network proxy 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 #include <netinet/in.h> 33 #include <netinet/icmp6.h> 34 35 #include <assert.h> 36 #include <errno.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include "arp.h" 42 #include "bpf.h" 43 #include "dhcp.h" 44 #include "dhcp6.h" 45 #include "eloop.h" 46 #include "ipv6nd.h" 47 #include "logerr.h" 48 #include "privsep.h" 49 50 #ifdef HAVE_CAPSICUM 51 #include <sys/capsicum.h> 52 #endif 53 54 #ifdef INET 55 static void 56 ps_inet_recvbootp(void *arg) 57 { 58 struct dhcpcd_ctx *ctx = arg; 59 60 if (ps_recvmsg(ctx, ctx->udp_rfd, PS_BOOTP, ctx->ps_inet_fd) == -1) 61 logerr(__func__); 62 } 63 #endif 64 65 #ifdef INET6 66 static void 67 ps_inet_recvra(void *arg) 68 { 69 #ifdef __sun 70 struct interface *ifp = arg; 71 struct rs_state *state = RS_STATE(ifp); 72 struct dhcpcd_ctx *ctx = ifp->ctx; 73 74 if (ps_recvmsg(ctx, state->nd_fd, PS_ND, ctx->ps_inet_fd) == -1) 75 logerr(__func__); 76 #else 77 struct dhcpcd_ctx *ctx = arg; 78 79 if (ps_recvmsg(ctx, ctx->nd_fd, PS_ND, ctx->ps_inet_fd) == -1) 80 logerr(__func__); 81 #endif 82 } 83 #endif 84 85 #ifdef DHCP6 86 static void 87 ps_inet_recvdhcp6(void *arg) 88 { 89 struct dhcpcd_ctx *ctx = arg; 90 91 if (ps_recvmsg(ctx, ctx->dhcp6_rfd, PS_DHCP6, ctx->ps_inet_fd) == -1) 92 logerr(__func__); 93 } 94 #endif 95 96 static int 97 ps_inet_startcb(void *arg) 98 { 99 struct dhcpcd_ctx *ctx = arg; 100 int ret = 0; 101 102 if (ctx->options & DHCPCD_MASTER) 103 setproctitle("[network proxy]"); 104 else 105 setproctitle("[network proxy] %s%s%s", 106 ctx->ifv[0], 107 ctx->options & DHCPCD_IPV4 ? " [ip4]" : "", 108 ctx->options & DHCPCD_IPV6 ? " [ip6]" : ""); 109 110 /* This end is the main engine, so it's useless for us. */ 111 close(ctx->ps_data_fd); 112 ctx->ps_data_fd = -1; 113 114 errno = 0; 115 116 #ifdef INET 117 if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MASTER)) == 118 (DHCPCD_IPV4 | DHCPCD_MASTER)) 119 { 120 ctx->udp_rfd = dhcp_openudp(NULL); 121 if (ctx->udp_rfd == -1) 122 logerr("%s: dhcp_open", __func__); 123 #ifdef PRIVSEP_RIGHTS 124 else if (ps_rights_limit_fd_rdonly(ctx->udp_rfd) == -1) { 125 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 126 close(ctx->udp_rfd); 127 ctx->udp_rfd = -1; 128 } 129 #endif 130 else if (eloop_event_add(ctx->eloop, ctx->udp_rfd, 131 ps_inet_recvbootp, ctx) == -1) 132 { 133 logerr("%s: eloop_event_add DHCP", __func__); 134 close(ctx->udp_rfd); 135 ctx->udp_rfd = -1; 136 } else 137 ret++; 138 } 139 #endif 140 #if defined(INET6) && !defined(__sun) 141 if (ctx->options & DHCPCD_IPV6) { 142 ctx->nd_fd = ipv6nd_open(true); 143 if (ctx->nd_fd == -1) 144 logerr("%s: ipv6nd_open", __func__); 145 #ifdef PRIVSEP_RIGHTS 146 else if (ps_rights_limit_fd_rdonly(ctx->nd_fd) == -1) { 147 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 148 close(ctx->nd_fd); 149 ctx->nd_fd = -1; 150 } 151 #endif 152 else if (eloop_event_add(ctx->eloop, ctx->nd_fd, 153 ps_inet_recvra, ctx) == -1) 154 { 155 logerr("%s: eloop_event_add RA", __func__); 156 close(ctx->nd_fd); 157 ctx->nd_fd = -1; 158 } else 159 ret++; 160 } 161 #endif 162 #ifdef DHCP6 163 if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MASTER)) == 164 (DHCPCD_IPV6 | DHCPCD_MASTER)) 165 { 166 ctx->dhcp6_rfd = dhcp6_openudp(0, NULL); 167 if (ctx->dhcp6_rfd == -1) 168 logerr("%s: dhcp6_open", __func__); 169 #ifdef PRIVSEP_RIGHTS 170 else if (ps_rights_limit_fd_rdonly(ctx->dhcp6_rfd) == -1) { 171 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 172 close(ctx->dhcp6_rfd); 173 ctx->dhcp6_rfd = -1; 174 } 175 #endif 176 else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, 177 ps_inet_recvdhcp6, ctx) == -1) 178 { 179 logerr("%s: eloop_event_add DHCP6", __func__); 180 close(ctx->dhcp6_rfd); 181 ctx->dhcp6_rfd = -1; 182 } else 183 ret++; 184 } 185 #endif 186 187 if (ret == 0 && errno == 0) { 188 errno = ENXIO; 189 return -1; 190 } 191 return ret; 192 } 193 194 static bool 195 ps_inet_validudp(struct msghdr *msg, uint16_t sport, uint16_t dport) 196 { 197 struct udphdr udp; 198 struct iovec *iov = msg->msg_iov; 199 200 if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(udp)) { 201 errno = EINVAL; 202 return false; 203 } 204 205 memcpy(&udp, iov->iov_base, sizeof(udp)); 206 if (udp.uh_sport != htons(sport) || udp.uh_dport != htons(dport)) { 207 errno = EPERM; 208 return false; 209 } 210 return true; 211 } 212 213 #ifdef INET6 214 static bool 215 ps_inet_validnd(struct msghdr *msg) 216 { 217 struct icmp6_hdr icmp6; 218 struct iovec *iov = msg->msg_iov; 219 220 if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(icmp6)) { 221 errno = EINVAL; 222 return false; 223 } 224 225 memcpy(&icmp6, iov->iov_base, sizeof(icmp6)); 226 switch(icmp6.icmp6_type) { 227 case ND_ROUTER_SOLICIT: 228 case ND_NEIGHBOR_ADVERT: 229 break; 230 default: 231 errno = EPERM; 232 return false; 233 } 234 235 return true; 236 } 237 #endif 238 239 static ssize_t 240 ps_inet_sendmsg(struct dhcpcd_ctx *ctx, 241 struct ps_msghdr *psm, struct msghdr *msg) 242 { 243 struct ps_process *psp; 244 int s; 245 246 psp = ps_findprocess(ctx, &psm->ps_id); 247 if (psp != NULL) { 248 s = psp->psp_work_fd; 249 goto dosend; 250 } 251 252 switch (psm->ps_cmd) { 253 #ifdef INET 254 case PS_BOOTP: 255 if (!ps_inet_validudp(msg, BOOTPC, BOOTPS)) 256 return -1; 257 s = ctx->udp_wfd; 258 break; 259 #endif 260 #if defined(INET6) && !defined(__sun) 261 case PS_ND: 262 if (!ps_inet_validnd(msg)) 263 return -1; 264 s = ctx->nd_fd; 265 break; 266 #endif 267 #ifdef DHCP6 268 case PS_DHCP6: 269 if (!ps_inet_validudp(msg, DHCP6_CLIENT_PORT,DHCP6_SERVER_PORT)) 270 return -1; 271 s = ctx->dhcp6_wfd; 272 break; 273 #endif 274 default: 275 errno = EINVAL; 276 return -1; 277 } 278 279 dosend: 280 return sendmsg(s, msg, 0); 281 } 282 283 static void 284 ps_inet_recvmsg(void *arg) 285 { 286 struct dhcpcd_ctx *ctx = arg; 287 288 /* Receive shutdown */ 289 if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, NULL, NULL) == -1) 290 logerr(__func__); 291 } 292 293 ssize_t 294 ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 295 { 296 struct dhcpcd_ctx *ctx = arg; 297 298 switch (psm->ps_cmd) { 299 #ifdef INET 300 case PS_BOOTP: 301 dhcp_recvmsg(ctx, msg); 302 break; 303 #endif 304 #ifdef INET6 305 case PS_ND: 306 ipv6nd_recvmsg(ctx, msg); 307 break; 308 #endif 309 #ifdef DHCP6 310 case PS_DHCP6: 311 dhcp6_recvmsg(ctx, msg, NULL); 312 break; 313 #endif 314 default: 315 errno = ENOTSUP; 316 return -1; 317 } 318 return 1; 319 } 320 321 static void 322 ps_inet_dodispatch(void *arg) 323 { 324 struct dhcpcd_ctx *ctx = arg; 325 326 if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, ps_inet_dispatch, ctx) == -1) 327 logerr(__func__); 328 } 329 330 pid_t 331 ps_inet_start(struct dhcpcd_ctx *ctx) 332 { 333 pid_t pid; 334 335 pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd, 336 ps_inet_recvmsg, ps_inet_dodispatch, ctx, 337 ps_inet_startcb, NULL, 338 PSF_DROPPRIVS); 339 340 #ifdef HAVE_CAPSICUM 341 if (pid == 0 && cap_enter() == -1 && errno != ENOSYS) 342 logerr("%s: cap_enter", __func__); 343 #endif 344 #ifdef HAVE_PLEDGE 345 if (pid == 0 && pledge("stdio", NULL) == -1) 346 logerr("%s: pledge", __func__); 347 #endif 348 349 return pid; 350 } 351 352 int 353 ps_inet_stop(struct dhcpcd_ctx *ctx) 354 { 355 356 return ps_dostop(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd); 357 } 358 359 #ifdef INET 360 static void 361 ps_inet_recvinbootp(void *arg) 362 { 363 struct ps_process *psp = arg; 364 365 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 366 PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1) 367 logerr(__func__); 368 } 369 370 static int 371 ps_inet_listenin(void *arg) 372 { 373 struct ps_process *psp = arg; 374 struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; 375 char buf[INET_ADDRSTRLEN]; 376 377 inet_ntop(AF_INET, ia, buf, sizeof(buf)); 378 setproctitle("[network proxy] %s", buf); 379 380 psp->psp_work_fd = dhcp_openudp(ia); 381 if (psp->psp_work_fd == -1) { 382 logerr(__func__); 383 return -1; 384 } 385 386 #ifdef PRIVSEP_RIGHTS 387 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 388 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 389 return -1; 390 } 391 #endif 392 393 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 394 ps_inet_recvinbootp, psp) == -1) 395 { 396 logerr("%s: eloop_event_add DHCP", __func__); 397 return -1; 398 } 399 400 logdebugx("spawned listener %s on PID %d", buf, getpid()); 401 return 0; 402 } 403 #endif 404 405 #if defined(INET6) && defined(__sun) 406 static void 407 ps_inet_recvin6nd(void *arg) 408 { 409 struct ps_process *psp = arg; 410 411 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 412 PS_ND, psp->psp_ctx->ps_data_fd) == -1) 413 logerr(__func__); 414 } 415 416 static int 417 ps_inet_listennd(void *arg) 418 { 419 struct ps_process *psp = arg; 420 421 setproctitle("[ND network proxy]"); 422 423 psp->psp_work_fd = ipv6nd_open(&psp->psp_ifp); 424 if (psp->psp_work_fd == -1) { 425 logerr(__func__); 426 return -1; 427 } 428 429 #ifdef PRIVSEP_RIGHTS 430 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 431 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 432 return -1; 433 } 434 #endif 435 436 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 437 ps_inet_recvin6nd, psp) == -1) 438 { 439 logerr(__func__); 440 return -1; 441 } 442 443 logdebugx("spawned ND listener on PID %d", getpid()); 444 return 0; 445 } 446 #endif 447 448 #ifdef DHCP6 449 static void 450 ps_inet_recvin6dhcp6(void *arg) 451 { 452 struct ps_process *psp = arg; 453 454 if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, 455 PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1) 456 logerr(__func__); 457 } 458 459 static int 460 ps_inet_listenin6(void *arg) 461 { 462 struct ps_process *psp = arg; 463 struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr; 464 char buf[INET6_ADDRSTRLEN]; 465 466 inet_ntop(AF_INET6, ia, buf, sizeof(buf)); 467 setproctitle("[network proxy] %s", buf); 468 469 psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia); 470 if (psp->psp_work_fd == -1) { 471 logerr(__func__); 472 return -1; 473 } 474 475 #ifdef PRIVSEP_RIGHTS 476 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 477 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 478 return -1; 479 } 480 #endif 481 482 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 483 ps_inet_recvin6dhcp6, psp) == -1) 484 { 485 logerr("%s: eloop_event_add DHCP", __func__); 486 return -1; 487 } 488 489 logdebugx("spawned listener %s on PID %d", buf, getpid()); 490 return 0; 491 } 492 #endif 493 494 static void 495 ps_inet_recvmsgpsp(void *arg) 496 { 497 struct ps_process *psp = arg; 498 499 /* Receive shutdown. */ 500 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, NULL, NULL) == -1) 501 logerr(__func__); 502 } 503 504 ssize_t 505 ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) 506 { 507 uint16_t cmd; 508 struct ps_process *psp; 509 int (*start_func)(void *); 510 pid_t start; 511 512 cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); 513 if (cmd == psm->ps_cmd) 514 return ps_inet_sendmsg(ctx, psm, msg); 515 516 psp = ps_findprocess(ctx, &psm->ps_id); 517 518 #ifdef PRIVSEP_DEBUG 519 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); 520 #endif 521 522 if (psm->ps_cmd & PS_STOP) { 523 assert(psp == NULL); 524 return 0; 525 } 526 527 switch (cmd) { 528 #ifdef INET 529 case PS_BOOTP: 530 start_func = ps_inet_listenin; 531 break; 532 #endif 533 #ifdef INET6 534 #ifdef __sun 535 case PS_ND: 536 start_func = ps_inet_listennd; 537 break; 538 #endif 539 #ifdef DHCP6 540 case PS_DHCP6: 541 start_func = ps_inet_listenin6; 542 break; 543 #endif 544 #endif 545 default: 546 logerrx("%s: unknown command %x", __func__, psm->ps_cmd); 547 errno = ENOTSUP; 548 return -1; 549 } 550 551 if (!(psm->ps_cmd & PS_START)) { 552 errno = EINVAL; 553 return -1; 554 } 555 556 if (psp != NULL) 557 return 1; 558 559 psp = ps_newprocess(ctx, &psm->ps_id); 560 if (psp == NULL) 561 return -1; 562 563 start = ps_dostart(ctx, 564 &psp->psp_pid, &psp->psp_fd, 565 ps_inet_recvmsgpsp, NULL, psp, 566 start_func, NULL, 567 PSF_DROPPRIVS); 568 switch (start) { 569 case -1: 570 ps_freeprocess(psp); 571 return -1; 572 case 0: 573 #ifdef HAVE_CAPSICUM 574 if (cap_enter() == -1 && errno != ENOSYS) 575 logerr("%s: cap_enter", __func__); 576 #endif 577 #ifdef HAVE_PLEDGE 578 if (pledge("stdio", NULL) == -1) 579 logerr("%s: pledge", __func__); 580 #endif 581 break; 582 default: 583 break; 584 } 585 return start; 586 } 587 588 #ifdef INET 589 static ssize_t 590 ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg) 591 { 592 assert(ia != NULL); 593 struct dhcpcd_ctx *ctx = ia->iface->ctx; 594 struct ps_msghdr psm = { 595 .ps_cmd = cmd, 596 .ps_id = { 597 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 598 .psi_ifindex = ia->iface->index, 599 .psi_addr.psa_in_addr = ia->addr, 600 }, 601 }; 602 603 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 604 } 605 606 ssize_t 607 ps_inet_openbootp(struct ipv4_addr *ia) 608 { 609 610 return ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL); 611 } 612 613 ssize_t 614 ps_inet_closebootp(struct ipv4_addr *ia) 615 { 616 617 return ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL); 618 } 619 620 ssize_t 621 ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg) 622 { 623 624 return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_BOOTP, 0, msg); 625 } 626 #endif /* INET */ 627 628 #ifdef INET6 629 #ifdef __sun 630 static ssize_t 631 ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg) 632 { 633 struct dhcpcd_ctx *ctx = ifp->ctx; 634 struct ps_msghdr psm = { 635 .ps_cmd = cmd, 636 .ps_id = { 637 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 638 .psi_ifindex = ifp->index, 639 }, 640 }; 641 642 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 643 } 644 645 ssize_t 646 ps_inet_opennd(struct interface *ifp) 647 { 648 649 return ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL); 650 } 651 652 ssize_t 653 ps_inet_closend(struct interface *ifp) 654 { 655 656 return ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL); 657 } 658 659 ssize_t 660 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 661 { 662 663 return ps_inet_ifp_docmd(ifp, PS_ND, msg); 664 } 665 #else 666 ssize_t 667 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 668 { 669 670 return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_ND, 0, msg); 671 } 672 #endif 673 674 #ifdef DHCP6 675 static ssize_t 676 ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg) 677 { 678 struct dhcpcd_ctx *ctx = ia->iface->ctx; 679 struct ps_msghdr psm = { 680 .ps_cmd = cmd, 681 .ps_id = { 682 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 683 .psi_ifindex = ia->iface->index, 684 .psi_addr.psa_in6_addr = ia->addr, 685 }, 686 }; 687 688 return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); 689 } 690 691 ssize_t 692 ps_inet_opendhcp6(struct ipv6_addr *ia) 693 { 694 695 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL); 696 } 697 698 ssize_t 699 ps_inet_closedhcp6(struct ipv6_addr *ia) 700 { 701 702 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL); 703 } 704 705 ssize_t 706 ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg) 707 { 708 709 return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_DHCP6, 0, msg); 710 } 711 #endif /* DHCP6 */ 712 #endif /* INET6 */ 713