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