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