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