1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd, privileged proxy 4 * Copyright (c) 2006-2025 Roy Marples <roy (at) 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/ioctl.h> 30 #include <sys/socket.h> 31 #include <sys/stat.h> 32 #include <sys/time.h> 33 #include <sys/types.h> 34 #include <sys/wait.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <pwd.h> 40 #include <signal.h> 41 #include <stddef.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "auth.h" 47 #include "common.h" 48 #include "dev.h" 49 #include "dhcpcd.h" 50 #include "dhcp6.h" 51 #include "eloop.h" 52 #include "if.h" 53 #include "ipv6nd.h" 54 #include "logerr.h" 55 #include "privsep.h" 56 #include "sa.h" 57 #include "script.h" 58 59 __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long)); 60 61 struct psr_error 62 { 63 ssize_t psr_result; 64 int psr_errno; 65 char psr_pad[sizeof(ssize_t) - sizeof(int)]; 66 size_t psr_datalen; 67 }; 68 69 struct psr_ctx { 70 struct dhcpcd_ctx *psr_ctx; 71 struct psr_error psr_error; 72 size_t psr_datalen; 73 void *psr_data; 74 bool psr_mallocdata; 75 }; 76 77 static ssize_t 78 ps_root_readerrorcb(struct psr_ctx *pc) 79 { 80 struct dhcpcd_ctx *ctx = pc->psr_ctx; 81 int fd = PS_ROOT_FD(ctx); 82 struct psr_error *psr_error = &pc->psr_error; 83 struct iovec iov[] = { 84 { .iov_base = psr_error, .iov_len = sizeof(*psr_error) }, 85 { .iov_base = pc->psr_data, .iov_len = pc->psr_datalen }, 86 }; 87 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) }; 88 ssize_t len; 89 90 #define PSR_ERROR(e) \ 91 do { \ 92 psr_error->psr_errno = (e); \ 93 goto error; \ 94 } while (0 /* CONSTCOND */) 95 96 if (eloop_waitfd(fd) == -1) 97 PSR_ERROR(errno); 98 99 if (!pc->psr_mallocdata) 100 goto recv; 101 102 /* We peek at the psr_error structure to tell us how much of a buffer 103 * we need to read the whole packet. */ 104 msg.msg_iovlen--; 105 len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL); 106 if (len == -1) 107 PSR_ERROR(errno); 108 109 /* After this point, we MUST do another recvmsg even on a failure 110 * to remove the message after peeking. */ 111 if ((size_t)len < sizeof(*psr_error)) { 112 /* We can't use the header to work out buffers, so 113 * remove the message and bail. */ 114 (void)recvmsg(fd, &msg, MSG_WAITALL); 115 PSR_ERROR(EINVAL); 116 } 117 118 /* No data to read? Unlikely but ... */ 119 if (psr_error->psr_datalen == 0) 120 goto recv; 121 122 pc->psr_data = malloc(psr_error->psr_datalen); 123 if (pc->psr_data != NULL) { 124 iov[1].iov_base = pc->psr_data; 125 iov[1].iov_len = psr_error->psr_datalen; 126 msg.msg_iovlen++; 127 } 128 129 recv: 130 len = recvmsg(fd, &msg, MSG_WAITALL); 131 if (len == -1) 132 PSR_ERROR(errno); 133 else if ((size_t)len < sizeof(*psr_error)) 134 PSR_ERROR(EINVAL); 135 else if (msg.msg_flags & MSG_TRUNC) 136 PSR_ERROR(ENOBUFS); 137 else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) { 138 #ifdef PRIVSEP_DEBUG 139 logerrx("%s: recvmsg returned %zd, expecting %zu", __func__, 140 len, sizeof(*psr_error) + psr_error->psr_datalen); 141 #endif 142 PSR_ERROR(EBADMSG); 143 } 144 return len; 145 146 error: 147 psr_error->psr_result = -1; 148 if (pc->psr_mallocdata && pc->psr_data != NULL) { 149 free(pc->psr_data); 150 pc->psr_data = NULL; 151 } 152 return -1; 153 } 154 155 ssize_t 156 ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len) 157 { 158 struct psr_ctx pc = { 159 .psr_ctx = ctx, 160 .psr_data = data, 161 .psr_datalen = len, 162 .psr_mallocdata = false 163 }; 164 165 ps_root_readerrorcb(&pc); 166 167 errno = pc.psr_error.psr_errno; 168 return pc.psr_error.psr_result; 169 } 170 171 ssize_t 172 ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len) 173 { 174 struct psr_ctx pc = { 175 .psr_ctx = ctx, 176 .psr_data = NULL, 177 .psr_datalen = 0, 178 .psr_mallocdata = true 179 }; 180 181 ps_root_readerrorcb(&pc); 182 183 errno = pc.psr_error.psr_errno; 184 *data = pc.psr_data; 185 *len = pc.psr_error.psr_datalen; 186 return pc.psr_error.psr_result; 187 } 188 189 static ssize_t 190 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result, 191 void *data, size_t len) 192 { 193 struct psr_error psr = { 194 .psr_result = result, 195 .psr_errno = errno, 196 .psr_datalen = len, 197 }; 198 struct iovec iov[] = { 199 { .iov_base = &psr, .iov_len = sizeof(psr) }, 200 { .iov_base = data, .iov_len = len }, 201 }; 202 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) }; 203 ssize_t err; 204 int fd = PS_ROOT_FD(ctx); 205 206 #ifdef PRIVSEP_DEBUG 207 logdebugx("%s: result %zd errno %d", __func__, result, errno); 208 #endif 209 210 if (len == 0) 211 msg.msg_iovlen = 1; 212 err = sendmsg(fd, &msg, MSG_EOR); 213 214 /* Error sending the message? Try sending the error of sending. */ 215 if (err == -1 && errno != EPIPE) { 216 logerr("%s: result=%zd, data=%p, len=%zu", 217 __func__, result, data, len); 218 psr.psr_result = err; 219 psr.psr_errno = errno; 220 psr.psr_datalen = 0; 221 msg.msg_iovlen = 1; 222 err = sendmsg(fd, &msg, MSG_EOR); 223 } 224 225 return err; 226 } 227 228 static ssize_t 229 ps_root_doioctl(unsigned long req, void *data, size_t len) 230 { 231 int s, err; 232 233 /* Only allow these ioctls */ 234 switch(req) { 235 #ifdef SIOCAIFADDR 236 case SIOCAIFADDR: /* FALLTHROUGH */ 237 case SIOCDIFADDR: /* FALLTHROUGH */ 238 #endif 239 #ifdef SIOCSIFHWADDR 240 case SIOCSIFHWADDR: /* FALLTHROUGH */ 241 #endif 242 #ifdef SIOCGIFPRIORITY 243 case SIOCGIFPRIORITY: /* FALLTHROUGH */ 244 #endif 245 case SIOCSIFFLAGS: /* FALLTHROUGH */ 246 case SIOCGIFMTU: 247 break; 248 default: 249 errno = EPERM; 250 return -1; 251 } 252 253 s = xsocket(PF_INET, SOCK_DGRAM, 0); 254 if (s != -1) 255 #ifdef IOCTL_REQUEST_TYPE 256 { 257 ioctl_request_t reqt; 258 259 memcpy(&reqt, &req, sizeof(reqt)); 260 err = ioctl(s, reqt, data, len); 261 } 262 #else 263 err = ioctl(s, req, data, len); 264 #endif 265 else 266 err = -1; 267 if (s != -1) 268 close(s); 269 return err; 270 } 271 272 static ssize_t 273 ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len) 274 { 275 const char *envbuf = data; 276 char * const argv[] = { ctx->script, NULL }; 277 pid_t pid; 278 int status; 279 280 if (len == 0) 281 return 0; 282 283 if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL) 284 return -1; 285 286 pid = script_exec(argv, ctx->script_env); 287 if (pid == -1) 288 return -1; 289 290 /* Wait for the script to finish */ 291 while (waitpid(pid, &status, 0) == -1) { 292 if (errno != EINTR) { 293 logerr(__func__); 294 status = 0; 295 break; 296 } 297 } 298 return status; 299 } 300 301 static bool 302 ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path) 303 { 304 305 /* Avoid a previous directory attack to avoid /proc/../ 306 * dhcpcd should never use a path with double dots. */ 307 if (strstr(path, "..") != NULL) 308 return false; 309 310 if (cmd == PS_READFILE) { 311 #ifdef EMBEDDED_CONFIG 312 if (strcmp(ctx->cffile, EMBEDDED_CONFIG) == 0) 313 return true; 314 #endif 315 if (strcmp(ctx->cffile, path) == 0) 316 return true; 317 } 318 if (strncmp(DBDIR, path, strlen(DBDIR)) == 0) 319 return true; 320 if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0) 321 return true; 322 323 #ifdef __linux__ 324 if (strncmp("/proc/net/", path, strlen("/proc/net/")) == 0 || 325 strncmp("/proc/sys/net/", path, strlen("/proc/sys/net/")) == 0 || 326 strncmp("/sys/class/net/", path, strlen("/sys/class/net/")) == 0) 327 return true; 328 #endif 329 330 errno = EPERM; 331 return false; 332 } 333 334 static ssize_t 335 ps_root_dowritefile(const struct dhcpcd_ctx *ctx, 336 mode_t mode, void *data, size_t len) 337 { 338 char *file = data, *nc; 339 340 nc = memchr(file, '\0', len); 341 if (nc == NULL) { 342 errno = EINVAL; 343 return -1; 344 } 345 346 if (!ps_root_validpath(ctx, PS_WRITEFILE, file)) 347 return -1; 348 nc++; 349 return writefile(file, mode, nc, len - (size_t)(nc - file)); 350 } 351 352 #ifdef AUTH 353 static ssize_t 354 ps_root_monordm(uint64_t *rdm, size_t len) 355 { 356 357 if (len != sizeof(*rdm)) { 358 errno = EINVAL; 359 return -1; 360 } 361 return auth_get_rdm_monotonic(rdm); 362 } 363 #endif 364 365 #ifdef PRIVSEP_GETIFADDRS 366 #define IFA_NADDRS 4 367 static ssize_t 368 ps_root_dogetifaddrs(void **rdata, size_t *rlen) 369 { 370 struct ifaddrs *ifaddrs, *ifa; 371 size_t len; 372 uint8_t *buf, *sap; 373 socklen_t salen; 374 375 if (getifaddrs(&ifaddrs) == -1) 376 return -1; 377 if (ifaddrs == NULL) { 378 *rdata = NULL; 379 *rlen = 0; 380 return 0; 381 } 382 383 /* Work out the buffer length required. 384 * Ensure everything is aligned correctly, which does 385 * create a larger buffer than what is needed to send, 386 * but makes creating the same structure in the client 387 * much easier. */ 388 len = 0; 389 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 390 len += ALIGN(sizeof(*ifa)); 391 len += ALIGN(IFNAMSIZ); 392 len += ALIGN(sizeof(salen) * IFA_NADDRS); 393 if (ifa->ifa_addr != NULL) 394 len += ALIGN(sa_len(ifa->ifa_addr)); 395 if (ifa->ifa_netmask != NULL) 396 len += ALIGN(sa_len(ifa->ifa_netmask)); 397 if (ifa->ifa_broadaddr != NULL) 398 len += ALIGN(sa_len(ifa->ifa_broadaddr)); 399 #ifdef BSD 400 /* 401 * On BSD we need to carry ifa_data so we can access 402 * if_data->ifi_link_state 403 */ 404 if (ifa->ifa_addr != NULL && 405 ifa->ifa_addr->sa_family == AF_LINK) 406 len += ALIGN(sizeof(struct if_data)); 407 #endif 408 } 409 410 /* Use calloc to set everything to zero. 411 * This satisfies memory sanitizers because don't write 412 * where we don't need to. */ 413 buf = calloc(1, len); 414 if (buf == NULL) { 415 freeifaddrs(ifaddrs); 416 return -1; 417 } 418 *rdata = buf; 419 *rlen = len; 420 421 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 422 memcpy(buf, ifa, sizeof(*ifa)); 423 buf += ALIGN(sizeof(*ifa)); 424 425 strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ); 426 buf += ALIGN(IFNAMSIZ); 427 sap = buf; 428 buf += ALIGN(sizeof(salen) * IFA_NADDRS); 429 430 #define COPYINSA(addr) \ 431 do { \ 432 if ((addr) != NULL) \ 433 salen = sa_len((addr)); \ 434 else \ 435 salen = 0; \ 436 if (salen != 0) { \ 437 memcpy(sap, &salen, sizeof(salen)); \ 438 memcpy(buf, (addr), salen); \ 439 buf += ALIGN(salen); \ 440 } \ 441 sap += sizeof(salen); \ 442 } while (0 /*CONSTCOND */) 443 444 COPYINSA(ifa->ifa_addr); 445 COPYINSA(ifa->ifa_netmask); 446 COPYINSA(ifa->ifa_broadaddr); 447 448 #ifdef BSD 449 if (ifa->ifa_addr != NULL && 450 ifa->ifa_addr->sa_family == AF_LINK) 451 { 452 salen = (socklen_t)sizeof(struct if_data); 453 memcpy(buf, ifa->ifa_data, salen); 454 buf += ALIGN(salen); 455 } else 456 #endif 457 salen = 0; 458 memcpy(sap, &salen, sizeof(salen)); 459 } 460 461 freeifaddrs(ifaddrs); 462 return 0; 463 } 464 #endif 465 466 static ssize_t 467 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 468 { 469 struct dhcpcd_ctx *ctx = arg; 470 uint16_t cmd; 471 struct ps_process *psp; 472 struct iovec *iov = msg->msg_iov; 473 void *data = iov->iov_base, *rdata = NULL; 474 size_t len = iov->iov_len, rlen = 0; 475 uint8_t buf[PS_BUFLEN]; 476 time_t mtime; 477 ssize_t err; 478 bool free_rdata = false; 479 480 cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); 481 psp = ps_findprocess(ctx, &psm->ps_id); 482 483 #ifdef PRIVSEP_DEBUG 484 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); 485 #endif 486 487 if (psp != NULL) { 488 if (psm->ps_cmd & PS_STOP) { 489 return ps_stopprocess(psp); 490 } else if (psm->ps_cmd & PS_START) { 491 /* Process has already started .... */ 492 logdebugx("%s%sprocess %s already started on pid %d", 493 psp->psp_ifname, 494 psp->psp_ifname[0] != '\0' ? ": " : "", 495 psp->psp_name, psp->psp_pid); 496 return 0; 497 } 498 499 err = ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg); 500 if (err == -1) { 501 logerr("%s: failed to send message to pid %d", 502 __func__, psp->psp_pid); 503 ps_freeprocess(psp); 504 } 505 return 0; 506 } 507 508 if (psm->ps_cmd & PS_STOP && psp == NULL) 509 return 0; 510 511 switch (cmd) { 512 #ifdef INET 513 #ifdef ARP 514 case PS_BPF_ARP: /* FALLTHROUGH */ 515 #endif 516 case PS_BPF_BOOTP: 517 return ps_bpf_cmd(ctx, psm, msg); 518 #endif 519 #ifdef INET 520 case PS_BOOTP: 521 return ps_inet_cmd(ctx, psm, msg); 522 #endif 523 #ifdef INET6 524 #ifdef DHCP6 525 case PS_DHCP6: /* FALLTHROUGH */ 526 #endif 527 case PS_ND: 528 return ps_inet_cmd(ctx, psm, msg); 529 #endif 530 default: 531 break; 532 } 533 534 assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1); 535 536 /* Reset errno */ 537 errno = 0; 538 539 switch (psm->ps_cmd) { 540 case PS_IOCTL: 541 err = ps_root_doioctl(psm->ps_flags, data, len); 542 if (err != -1) { 543 rdata = data; 544 rlen = len; 545 } 546 break; 547 case PS_SCRIPT: 548 err = ps_root_run_script(ctx, data, len); 549 break; 550 case PS_STOPPROCS: 551 ctx->options |= DHCPCD_EXITING; 552 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 553 if (psp != ctx->ps_root) 554 ps_stopprocess(psp); 555 } 556 err = ps_stopwait(ctx); 557 break; 558 case PS_UNLINK: 559 if (!ps_root_validpath(ctx, psm->ps_cmd, data)) { 560 err = -1; 561 break; 562 } 563 err = unlink(data); 564 break; 565 case PS_READFILE: 566 if (!ps_root_validpath(ctx, psm->ps_cmd, data)) { 567 err = -1; 568 break; 569 } 570 err = readfile(data, buf, sizeof(buf)); 571 if (err != -1) { 572 rdata = buf; 573 rlen = (size_t)err; 574 } 575 break; 576 case PS_WRITEFILE: 577 err = ps_root_dowritefile(ctx, (mode_t)psm->ps_flags, 578 data, len); 579 break; 580 case PS_FILEMTIME: 581 err = filemtime(data, &mtime); 582 if (err != -1) { 583 rdata = &mtime; 584 rlen = sizeof(mtime); 585 } 586 break; 587 case PS_LOGREOPEN: 588 err = logopen(ctx->logfile); 589 break; 590 #ifdef AUTH 591 case PS_AUTH_MONORDM: 592 err = ps_root_monordm(data, len); 593 if (err != -1) { 594 rdata = data; 595 rlen = len; 596 } 597 break; 598 #endif 599 #ifdef PRIVSEP_GETIFADDRS 600 case PS_GETIFADDRS: 601 err = ps_root_dogetifaddrs(&rdata, &rlen); 602 free_rdata = true; 603 break; 604 #endif 605 #ifdef PLUGIN_DEV 606 case PS_DEV_INITTED: 607 err = dev_initialised(ctx, data); 608 break; 609 case PS_DEV_LISTENING: 610 err = dev_listening(ctx); 611 break; 612 #endif 613 default: 614 err = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata); 615 break; 616 } 617 618 err = ps_root_writeerror(ctx, err, rdata, rlen); 619 if (free_rdata) 620 free(rdata); 621 return err; 622 } 623 624 /* Receive from state engine, do an action. */ 625 static void 626 ps_root_recvmsg(void *arg, unsigned short events) 627 { 628 struct ps_process *psp = arg; 629 630 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, 631 ps_root_recvmsgcb, psp->psp_ctx) == -1) 632 logerr(__func__); 633 } 634 635 #ifdef PLUGIN_DEV 636 static int 637 ps_root_handleinterface(void *arg, int action, const char *ifname) 638 { 639 struct dhcpcd_ctx *ctx = arg; 640 unsigned long flag; 641 642 if (action == 1) 643 flag = PS_DEV_IFADDED; 644 else if (action == -1) 645 flag = PS_DEV_IFREMOVED; 646 else if (action == 0) 647 flag = PS_DEV_IFUPDATED; 648 else { 649 errno = EINVAL; 650 return -1; 651 } 652 653 return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, 654 flag, ifname, strlen(ifname) + 1); 655 } 656 #endif 657 658 static int 659 ps_root_startcb(struct ps_process *psp) 660 { 661 struct dhcpcd_ctx *ctx = psp->psp_ctx; 662 663 if (ctx->options & DHCPCD_MANAGER) 664 setproctitle("[privileged proxy]"); 665 else 666 setproctitle("[privileged proxy] %s%s%s", 667 ctx->ifv[0], 668 ctx->options & DHCPCD_IPV4 ? " [ip4]" : "", 669 ctx->options & DHCPCD_IPV6 ? " [ip6]" : ""); 670 ctx->options |= DHCPCD_PRIVSEPROOT; 671 672 if (if_opensockets(ctx) == -1) 673 logerr("%s: if_opensockets", __func__); 674 675 /* Open network sockets for sending. 676 * This is a small bit wasteful for non sandboxed OS's 677 * but makes life very easy for unicasting DHCPv6 in non manager 678 * mode as we no longer care about address selection. 679 * We can't call shutdown SHUT_RD on the socket because it's 680 * not connected. All we can do is try and set a zero sized 681 * receive buffer and just let it overflow. 682 * Reading from it just to drain it is a waste of CPU time. */ 683 #ifdef INET 684 if (ctx->options & DHCPCD_IPV4) { 685 int buflen = 1; 686 687 ctx->udp_wfd = xsocket(PF_INET, 688 SOCK_RAW | SOCK_CXNB, IPPROTO_UDP); 689 if (ctx->udp_wfd == -1) 690 logerr("%s: dhcp_openraw", __func__); 691 else if (setsockopt(ctx->udp_wfd, SOL_SOCKET, SO_RCVBUF, 692 &buflen, sizeof(buflen)) == -1) 693 logerr("%s: setsockopt SO_RCVBUF DHCP", __func__); 694 } 695 #endif 696 #ifdef INET6 697 if (ctx->options & DHCPCD_IPV6) { 698 int buflen = 1; 699 700 ctx->nd_fd = ipv6nd_open(false); 701 if (ctx->nd_fd == -1) 702 logerr("%s: ipv6nd_open", __func__); 703 else if (setsockopt(ctx->nd_fd, SOL_SOCKET, SO_RCVBUF, 704 &buflen, sizeof(buflen)) == -1) 705 logerr("%s: setsockopt SO_RCVBUF ND", __func__); 706 } 707 #endif 708 #ifdef DHCP6 709 if (ctx->options & DHCPCD_IPV6) { 710 int buflen = 1; 711 712 ctx->dhcp6_wfd = dhcp6_openraw(); 713 if (ctx->dhcp6_wfd == -1) 714 logerr("%s: dhcp6_openraw", __func__); 715 else if (setsockopt(ctx->dhcp6_wfd, SOL_SOCKET, SO_RCVBUF, 716 &buflen, sizeof(buflen)) == -1) 717 logerr("%s: setsockopt SO_RCVBUF DHCP6", __func__); 718 } 719 #endif 720 721 #ifdef PLUGIN_DEV 722 /* Start any dev listening plugin which may want to 723 * change the interface name provided by the kernel */ 724 if ((ctx->options & (DHCPCD_MANAGER | DHCPCD_DEV)) == 725 (DHCPCD_MANAGER | DHCPCD_DEV)) 726 dev_start(ctx, ps_root_handleinterface); 727 #endif 728 729 return 0; 730 } 731 732 void 733 ps_root_signalcb(int sig, void *arg) 734 { 735 struct dhcpcd_ctx *ctx = arg; 736 int status; 737 pid_t pid; 738 const char *ifname, *name; 739 struct ps_process *psp; 740 741 if (sig != SIGCHLD) 742 return; 743 744 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 745 psp = ps_findprocesspid(ctx, pid); 746 if (psp != NULL) { 747 ifname = psp->psp_ifname; 748 name = psp->psp_name; 749 } else { 750 /* Ignore logging the double fork */ 751 if (ctx->options & DHCPCD_LAUNCHER) 752 continue; 753 ifname = ""; 754 name = "unknown process"; 755 } 756 757 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 758 logerrx("%s%s%s exited unexpectedly from PID %d," 759 " code=%d", 760 ifname, ifname[0] != '\0' ? ": " : "", 761 name, pid, WEXITSTATUS(status)); 762 else if (WIFSIGNALED(status)) 763 logerrx("%s%s%s exited unexpectedly from PID %d," 764 " signal=%s", 765 ifname, ifname[0] != '\0' ? ": " : "", 766 name, pid, strsignal(WTERMSIG(status))); 767 else 768 logdebugx("%s%s%s exited from PID %d", 769 ifname, ifname[0] != '\0' ? ": " : "", 770 name, pid); 771 772 if (psp != NULL) 773 ps_freeprocess(psp); 774 } 775 776 if (!(ctx->options & DHCPCD_EXITING)) 777 return; 778 if (!(ps_waitforprocs(ctx))) 779 eloop_exit(ctx->eloop, EXIT_SUCCESS); 780 } 781 782 int (*handle_interface)(void *, int, const char *); 783 784 #ifdef PLUGIN_DEV 785 static ssize_t 786 ps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) 787 { 788 int action; 789 struct iovec *iov = msg->msg_iov; 790 791 if (msg->msg_iovlen != 1) { 792 errno = EINVAL; 793 return -1; 794 } 795 796 switch(psm->ps_flags) { 797 case PS_DEV_IFADDED: 798 action = 1; 799 break; 800 case PS_DEV_IFREMOVED: 801 action = -1; 802 break; 803 case PS_DEV_IFUPDATED: 804 action = 0; 805 break; 806 default: 807 errno = EINVAL; 808 return -1; 809 } 810 811 return dhcpcd_handleinterface(ctx, action, iov->iov_base); 812 } 813 #endif 814 815 static ssize_t 816 ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 817 { 818 struct dhcpcd_ctx *ctx = arg; 819 ssize_t err; 820 821 switch(psm->ps_cmd) { 822 #ifdef PLUGIN_DEV 823 case PS_DEV_IFCMD: 824 err = ps_root_devcb(ctx, psm, msg); 825 break; 826 #endif 827 default: 828 #ifdef INET 829 err = ps_bpf_dispatch(ctx, psm, msg); 830 if (err == -1 && errno == ENOTSUP) 831 #endif 832 err = ps_inet_dispatch(ctx, psm, msg); 833 } 834 return err; 835 } 836 837 static void 838 ps_root_dispatch(void *arg, unsigned short events) 839 { 840 struct dhcpcd_ctx *ctx = arg; 841 842 if (ps_recvpsmsg(ctx, ctx->ps_data_fd, events, 843 ps_root_dispatchcb, ctx) == -1) 844 logerr(__func__); 845 } 846 847 static void 848 ps_root_log(void *arg, unsigned short events) 849 { 850 struct dhcpcd_ctx *ctx = arg; 851 852 if (events != ELE_READ) 853 logerrx("%s: unexpected event 0x%04x", __func__, events); 854 855 if (logreadfd(ctx->ps_log_root_fd) == -1) 856 logerr(__func__); 857 } 858 859 pid_t 860 ps_root_start(struct dhcpcd_ctx *ctx) 861 { 862 struct ps_id id = { 863 .psi_ifindex = 0, 864 .psi_cmd = PS_ROOT, 865 }; 866 struct ps_process *psp; 867 int logfd[2] = { -1, -1}, datafd[2] = { -1, -1}; 868 pid_t pid; 869 870 if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1) 871 return -1; 872 #ifdef PRIVSEP_RIGHTS 873 if (ps_rights_limit_fdpair(logfd) == -1) 874 return -1; 875 #endif 876 877 if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1) 878 return -1; 879 880 if (ps_setbuf_fdpair(datafd) == -1) 881 return -1; 882 #ifdef PRIVSEP_RIGHTS 883 if (ps_rights_limit_fdpair(datafd) == -1) 884 return -1; 885 #endif 886 887 psp = ctx->ps_root = ps_newprocess(ctx, &id); 888 if (psp == NULL) 889 return -1; 890 891 strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name)); 892 pid = ps_startprocess(psp, ps_root_recvmsg, NULL, 893 ps_root_startcb, PSF_ELOOP); 894 if (pid == -1) 895 return -1; 896 897 if (pid == 0) { 898 ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */ 899 ctx->ps_log_root_fd = logfd[1]; 900 if (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ, 901 ps_root_log, ctx) == -1) 902 return -1; 903 ctx->ps_data_fd = datafd[1]; 904 close(datafd[0]); 905 return 0; 906 } else if (pid == -1) 907 return -1; 908 909 logsetfd(logfd[0]); 910 close(logfd[1]); 911 912 ctx->ps_data_fd = datafd[0]; 913 close(datafd[1]); 914 if (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ, 915 ps_root_dispatch, ctx) == -1) 916 return -1; 917 918 return pid; 919 } 920 921 void 922 ps_root_close(struct dhcpcd_ctx *ctx) 923 { 924 925 if_closesockets(ctx); 926 927 #ifdef INET 928 if (ctx->udp_wfd != -1) { 929 close(ctx->udp_wfd); 930 ctx->udp_wfd = -1; 931 } 932 #endif 933 #ifdef INET6 934 if (ctx->nd_fd != -1) { 935 close(ctx->nd_fd); 936 ctx->nd_fd = -1; 937 } 938 #endif 939 #ifdef DHCP6 940 if (ctx->dhcp6_wfd != -1) { 941 close(ctx->dhcp6_wfd); 942 ctx->dhcp6_wfd = -1; 943 } 944 #endif 945 } 946 947 int 948 ps_root_stop(struct dhcpcd_ctx *ctx) 949 { 950 struct ps_process *psp = ctx->ps_root; 951 int err; 952 953 if (!(ctx->options & DHCPCD_PRIVSEP)) 954 return 0; 955 956 /* If we are the root process then remove the pidfile */ 957 if (ctx->options & DHCPCD_PRIVSEPROOT) { 958 if (!(ctx->options & DHCPCD_TEST) && unlink(ctx->pidfile) == -1) 959 logerr("%s: unlink: %s", __func__, ctx->pidfile); 960 961 /* drain the log */ 962 if (ctx->ps_log_root_fd != -1) { 963 ssize_t loglen; 964 965 #ifdef __linux__ 966 /* Seems to help to get the last parts, 967 * sched_yield(2) does not. */ 968 sleep(0); 969 #endif 970 do { 971 loglen = logreadfd(ctx->ps_log_root_fd); 972 } while (loglen != 0 && loglen != -1); 973 close(ctx->ps_log_root_fd); 974 ctx->ps_log_root_fd = -1; 975 } 976 } 977 978 if (ctx->ps_data_fd != -1) { 979 eloop_event_delete(ctx->eloop, ctx->ps_data_fd); 980 close(ctx->ps_data_fd); 981 ctx->ps_data_fd = -1; 982 } 983 984 /* Only the manager process gets past this point. */ 985 if (ctx->options & DHCPCD_FORKED) 986 return 0; 987 988 /* We cannot log the root process exited before we 989 * log dhcpcd exits because the latter requires the former. 990 * So we just log the intent to exit. 991 * Even sending this will be a race to exit. */ 992 if (psp) { 993 logdebugx("%s%s%s will exit from PID %d", 994 psp->psp_ifname, 995 psp->psp_ifname[0] != '\0' ? ": " : "", 996 psp->psp_name, psp->psp_pid); 997 998 if (ps_stopprocess(psp) == -1) 999 return -1; 1000 } /* else the root process has already exited :( */ 1001 1002 err = ps_stopwait(ctx); 1003 if (ctx->ps_root != NULL) 1004 ps_freeprocess(ctx->ps_root); 1005 return err; 1006 } 1007 1008 ssize_t 1009 ps_root_stopprocesses(struct dhcpcd_ctx *ctx) 1010 { 1011 1012 if (!(IN_PRIVSEP_SE(ctx))) 1013 return 0; 1014 1015 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_STOPPROCS, 0, 1016 NULL, 0) == -1) 1017 return -1; 1018 return ps_root_readerror(ctx, NULL, 0); 1019 } 1020 1021 ssize_t 1022 ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len) 1023 { 1024 1025 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SCRIPT, 1026 0, data, len) == -1) 1027 return -1; 1028 return ps_root_readerror(ctx, NULL, 0); 1029 } 1030 1031 ssize_t 1032 ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data, 1033 size_t len) 1034 { 1035 int fd = PS_ROOT_FD(ctx); 1036 #ifdef IOCTL_REQUEST_TYPE 1037 unsigned long ulreq = 0; 1038 1039 memcpy(&ulreq, &req, sizeof(req)); 1040 if (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1) 1041 return -1; 1042 #else 1043 if (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1) 1044 return -1; 1045 #endif 1046 return ps_root_readerror(ctx, data, len); 1047 } 1048 1049 ssize_t 1050 ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file) 1051 { 1052 1053 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_UNLINK, 0, 1054 file, strlen(file) + 1) == -1) 1055 return -1; 1056 return ps_root_readerror(ctx, NULL, 0); 1057 } 1058 1059 ssize_t 1060 ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file, 1061 void *data, size_t len) 1062 { 1063 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_READFILE, 0, 1064 file, strlen(file) + 1) == -1) 1065 return -1; 1066 return ps_root_readerror(ctx, data, len); 1067 } 1068 1069 ssize_t 1070 ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode, 1071 const void *data, size_t len) 1072 { 1073 char buf[PS_BUFLEN]; 1074 size_t flen; 1075 1076 flen = strlcpy(buf, file, sizeof(buf)); 1077 flen += 1; 1078 if (flen > sizeof(buf) || flen + len > sizeof(buf)) { 1079 errno = ENOBUFS; 1080 return -1; 1081 } 1082 memcpy(buf + flen, data, len); 1083 1084 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode, 1085 buf, flen + len) == -1) 1086 return -1; 1087 return ps_root_readerror(ctx, NULL, 0); 1088 } 1089 1090 ssize_t 1091 ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time) 1092 { 1093 1094 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_FILEMTIME, 0, 1095 file, strlen(file) + 1) == -1) 1096 return -1; 1097 return ps_root_readerror(ctx, time, sizeof(*time)); 1098 } 1099 1100 ssize_t 1101 ps_root_logreopen(struct dhcpcd_ctx *ctx) 1102 { 1103 1104 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_LOGREOPEN, 0, 1105 NULL, 0) == -1) 1106 return -1; 1107 return ps_root_readerror(ctx, NULL, 0); 1108 } 1109 1110 #ifdef PRIVSEP_GETIFADDRS 1111 int 1112 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead) 1113 { 1114 struct ifaddrs *ifa; 1115 void *buf = NULL; 1116 char *bp, *sap; 1117 socklen_t salen; 1118 size_t len; 1119 ssize_t err; 1120 1121 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), 1122 PS_GETIFADDRS, 0, NULL, 0) == -1) 1123 return -1; 1124 err = ps_root_mreaderror(ctx, &buf, &len); 1125 1126 if (err == -1) 1127 return -1; 1128 1129 /* Should be impossible - lo0 will always exist. */ 1130 if (len == 0) { 1131 *ifahead = NULL; 1132 return 0; 1133 } 1134 1135 bp = buf; 1136 *ifahead = (struct ifaddrs *)(void *)bp; 1137 for (ifa = *ifahead; ifa != NULL; ifa = ifa->ifa_next) { 1138 if (len < ALIGN(sizeof(*ifa)) + 1139 ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS)) 1140 goto err; 1141 bp += ALIGN(sizeof(*ifa)); 1142 ifa->ifa_name = bp; 1143 bp += ALIGN(IFNAMSIZ); 1144 sap = bp; 1145 bp += ALIGN(sizeof(salen) * IFA_NADDRS); 1146 len -= ALIGN(sizeof(*ifa)) + 1147 ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS); 1148 1149 #define COPYOUTSA(addr) \ 1150 do { \ 1151 memcpy(&salen, sap, sizeof(salen)); \ 1152 if (len < salen) \ 1153 goto err; \ 1154 if (salen != 0) { \ 1155 (addr) = (struct sockaddr *)(void *)bp; \ 1156 bp += ALIGN(salen); \ 1157 len -= ALIGN(salen); \ 1158 } \ 1159 sap += sizeof(salen); \ 1160 } while (0 /* CONSTCOND */) 1161 1162 COPYOUTSA(ifa->ifa_addr); 1163 COPYOUTSA(ifa->ifa_netmask); 1164 COPYOUTSA(ifa->ifa_broadaddr); 1165 1166 memcpy(&salen, sap, sizeof(salen)); 1167 if (len < salen) 1168 goto err; 1169 if (salen != 0) { 1170 ifa->ifa_data = bp; 1171 bp += ALIGN(salen); 1172 len -= ALIGN(salen); 1173 } else 1174 ifa->ifa_data = NULL; 1175 1176 if (len != 0) 1177 ifa->ifa_next = (struct ifaddrs *)(void *)bp; 1178 else 1179 ifa->ifa_next = NULL; 1180 } 1181 return 0; 1182 1183 err: 1184 free(buf); 1185 *ifahead = NULL; 1186 errno = EINVAL; 1187 return -1; 1188 } 1189 #endif 1190 1191 #ifdef AUTH 1192 int 1193 ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm) 1194 { 1195 1196 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_AUTH_MONORDM, 0, 1197 rdm, sizeof(*rdm))== -1) 1198 return -1; 1199 return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm)); 1200 } 1201 #endif 1202 1203 #ifdef PLUGIN_DEV 1204 int 1205 ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname) 1206 { 1207 1208 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_INITTED, 0, 1209 ifname, strlen(ifname) + 1)== -1) 1210 return -1; 1211 return (int)ps_root_readerror(ctx, NULL, 0); 1212 } 1213 1214 int 1215 ps_root_dev_listening(struct dhcpcd_ctx * ctx) 1216 { 1217 1218 if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_LISTENING, 1219 0, NULL, 0) == -1) 1220 return -1; 1221 return (int)ps_root_readerror(ctx, NULL, 0); 1222 } 1223 #endif 1224