1 /* 2 * Privilege Separation for dhcpcd 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 /* 30 * The current design is this: 31 * Spawn a priv process to carry out privileged actions and 32 * spawning unpriv process to initate network connections such as BPF 33 * or address specific listener. 34 * Spawn an unpriv process to send/receive common network data. 35 * Then drop all privs and start running. 36 * Every process aside from the privileged proxy is chrooted. 37 * All privsep processes ignore signals - only the manager process accepts them. 38 * 39 * dhcpcd will maintain the config file in the chroot, no need to handle 40 * this in a script or something. 41 */ 42 43 #include <sys/types.h> 44 #include <sys/resource.h> 45 #include <sys/socket.h> 46 #include <sys/stat.h> 47 #include <sys/wait.h> 48 49 #ifdef AF_LINK 50 #include <net/if_dl.h> 51 #endif 52 53 #include <assert.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <grp.h> 57 #include <paths.h> 58 #include <pwd.h> 59 #include <signal.h> 60 #include <stddef.h> /* For offsetof, struct padding debug */ 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 65 #include "arp.h" 66 #include "common.h" 67 #include "control.h" 68 #include "dev.h" 69 #include "dhcp.h" 70 #include "dhcp6.h" 71 #include "eloop.h" 72 #include "ipv6nd.h" 73 #include "logerr.h" 74 #include "privsep.h" 75 76 #ifdef HAVE_CAPSICUM 77 #include <sys/capsicum.h> 78 #include <sys/procdesc.h> 79 80 #include <capsicum_helpers.h> 81 #endif 82 #ifdef HAVE_UTIL_H 83 #include <util.h> 84 #endif 85 86 /* CMSG_ALIGN is a Linux extension */ 87 #ifndef CMSG_ALIGN 88 #define CMSG_ALIGN(n) (CMSG_SPACE((n)) - CMSG_SPACE(0)) 89 #endif 90 91 /* Calculate number of padding bytes to achieve 'struct cmsghdr' alignment */ 92 #define CALC_CMSG_PADLEN(has_cmsg, pos) \ 93 ((has_cmsg) ? (socklen_t)(CMSG_ALIGN((pos)) - (pos)) : 0) 94 95 int 96 ps_init(struct dhcpcd_ctx *ctx) 97 { 98 struct passwd *pw; 99 struct stat st; 100 101 errno = 0; 102 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) { 103 ctx->options &= ~DHCPCD_PRIVSEP; 104 if (errno == 0) { 105 logerrx("no such user %s", PRIVSEP_USER); 106 /* Just incase logerrx caused an error... */ 107 errno = 0; 108 } else 109 logerr("getpwnam"); 110 return -1; 111 } 112 113 if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) { 114 ctx->options &= ~DHCPCD_PRIVSEP; 115 logerrx("refusing chroot: %s: %s", PRIVSEP_USER, pw->pw_dir); 116 errno = 0; 117 return -1; 118 } 119 120 ctx->options |= DHCPCD_PRIVSEP; 121 return 0; 122 } 123 124 static int 125 ps_dropprivs(struct dhcpcd_ctx *ctx) 126 { 127 struct passwd *pw = ctx->ps_user; 128 int fd_out = ctx->options & DHCPCD_DUMPLEASE ? STDOUT_FILENO : 129 STDERR_FILENO; 130 131 if (ctx->options & DHCPCD_LAUNCHER) 132 #ifdef ASAN 133 logwarnx("not chrooting as compiled for ASAN"); 134 #elif defined(__sun) 135 /* libdpli complains */ 136 logwarnx("not chrooting on sun"); 137 #else 138 logdebugx("chrooting as %s to %s", pw->pw_name, pw->pw_dir); 139 140 if (chroot(pw->pw_dir) == -1 && 141 (errno != EPERM || ctx->options & DHCPCD_FORKED)) 142 logerr("%s: chroot: %s", __func__, pw->pw_dir); 143 #endif 144 145 if (chdir("/") == -1) 146 logerr("%s: chdir: /", __func__); 147 148 #ifdef __sun 149 #warning not dropping any privileges on this platform .... eek! 150 #else 151 if ((setgroups(1, &pw->pw_gid) == -1 || setgid(pw->pw_gid) == -1 || 152 setuid(pw->pw_uid) == -1) && 153 (errno != EPERM || ctx->options & DHCPCD_FORKED)) { 154 logerr("failed to drop privileges"); 155 return -1; 156 } 157 #endif 158 159 struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 }; 160 161 #ifndef __sun /* RLIMIT_NOFILE and ppoll don't mix */ 162 /* Prohibit new files, sockets, etc 163 * The control proxy *does* need to create new fd's via accept(2). */ 164 if (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) { 165 if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) 166 logerr("setrlimit RLIMIT_NOFILE"); 167 } 168 #endif 169 170 #define DHC_NOCHKIO (DHCPCD_STARTED | DHCPCD_DAEMONISE) 171 /* Prohibit writing to files. 172 * Obviously this won't work if we are using a logfile 173 * or redirecting stderr to a file. */ 174 if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO || 175 (ctx->logfile == NULL && isatty(fd_out) == 1)) { 176 if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) 177 logerr("setrlimit RLIMIT_FSIZE"); 178 } 179 180 #ifdef RLIMIT_NPROC 181 /* Prohibit forks */ 182 if (setrlimit(RLIMIT_NPROC, &rzero) == -1) 183 logerr("setrlimit RLIMIT_NPROC"); 184 #endif 185 186 return 0; 187 } 188 189 #ifdef PRIVSEP_RIGHTS 190 int 191 ps_rights_limit_ioctl(int fd) 192 { 193 cap_rights_t rights; 194 195 cap_rights_init(&rights, CAP_IOCTL); 196 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 197 return -1; 198 return 0; 199 } 200 201 int 202 ps_rights_limit_fd_getsockopt(int fd) 203 { 204 cap_rights_t rights; 205 206 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_ACCEPT, 207 CAP_GETSOCKOPT | CAP_FCNTL); 208 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 209 return -1; 210 return 0; 211 } 212 213 int 214 ps_rights_limit_fd_fctnl(int fd) 215 { 216 cap_rights_t rights; 217 218 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_ACCEPT, 219 CAP_FCNTL); 220 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 221 return -1; 222 return 0; 223 } 224 225 int 226 ps_rights_limit_fd(int fd) 227 { 228 cap_rights_t rights; 229 230 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN); 231 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 232 return -1; 233 return 0; 234 } 235 236 int 237 ps_rights_limit_fd_sockopt(int fd) 238 { 239 cap_rights_t rights; 240 241 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_GETSOCKOPT, 242 CAP_SETSOCKOPT); 243 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 244 return -1; 245 return 0; 246 } 247 248 int 249 ps_rights_limit_fd_rdonly(int fd) 250 { 251 cap_rights_t rights; 252 253 cap_rights_init(&rights, CAP_READ, CAP_EVENT); 254 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 255 return -1; 256 return 0; 257 } 258 259 int 260 ps_rights_limit_fdpair(int fd[]) 261 { 262 if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1) 263 return -1; 264 return 0; 265 } 266 267 static int 268 ps_rights_limit_stdio() 269 { 270 const int iebadf = CAPH_IGNORE_EBADF; 271 int error = 0; 272 273 if (caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1) 274 error = -1; 275 if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1) 276 error = -1; 277 if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1) 278 error = -1; 279 280 return error; 281 } 282 #endif 283 284 #ifdef HAVE_CAPSICUM 285 static void 286 ps_processhangup(void *arg, unsigned short events) 287 { 288 struct ps_process *psp = arg; 289 struct dhcpcd_ctx *ctx = psp->psp_ctx; 290 291 if (!(events & ELE_HANGUP)) 292 logerrx("%s: unexpected event 0x%04x", __func__, events); 293 294 logdebugx("%s%s%s exited from PID %d", psp->psp_ifname, 295 psp->psp_ifname[0] != '\0' ? ": " : "", psp->psp_name, 296 psp->psp_pid); 297 298 ps_freeprocess(psp); 299 300 if (!(ctx->options & DHCPCD_EXITING)) 301 return; 302 if (!(ps_waitforprocs(ctx))) 303 eloop_exit(ctx->eloop, EXIT_SUCCESS); 304 } 305 #endif 306 307 pid_t 308 ps_startprocess(struct ps_process *psp, 309 void (*recv_msg)(void *, unsigned short), 310 void (*recv_unpriv_msg)(void *, unsigned short), 311 int (*callback)(struct ps_process *), unsigned int flags) 312 { 313 struct dhcpcd_ctx *ctx = psp->psp_ctx; 314 int fd[2]; 315 pid_t pid; 316 317 if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) == -1) { 318 logerr("%s: socketpair", __func__); 319 return -1; 320 } 321 322 #ifdef PRIVSEP_RIGHTS 323 if (ps_rights_limit_fdpair(fd) == -1) { 324 logerr("%s: ps_rights_limit_fdpair", __func__); 325 return -1; 326 } 327 #endif 328 329 #ifdef HAVE_CAPSICUM 330 pid = pdfork(&psp->psp_pfd, PD_CLOEXEC); 331 #else 332 pid = fork(); 333 #endif 334 switch (pid) { 335 case -1: 336 #ifdef HAVE_CAPSICUM 337 logerr("pdfork"); 338 #else 339 logerr("fork"); 340 #endif 341 return -1; 342 case 0: 343 psp->psp_pid = getpid(); 344 psp->psp_fd = fd[1]; 345 close(fd[0]); 346 break; 347 default: 348 psp->psp_pid = pid; 349 psp->psp_fd = fd[0]; 350 close(fd[1]); 351 if (recv_unpriv_msg == NULL) 352 ; 353 else if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, 354 recv_unpriv_msg, psp) == -1) { 355 logerr("%s: eloop_event_add fd %d", __func__, 356 psp->psp_fd); 357 return -1; 358 } 359 #ifdef HAVE_CAPSICUM 360 if (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP, 361 ps_processhangup, psp) == -1) { 362 logerr("%s: eloop_event_add pfd %d", __func__, 363 psp->psp_pfd); 364 return -1; 365 } 366 #endif 367 psp->psp_started = true; 368 return pid; 369 } 370 371 /* Close things we no longer need */ 372 pidfile_unlock(); 373 if (ctx->ps_ctl != psp) 374 eloop_closefdwaiter(ctx->eloop); 375 376 /* Close more if we are not root */ 377 if (ctx->ps_root != psp) { 378 if (ctx->ps_ctl != psp) 379 ps_root_close(ctx); 380 #ifdef PLUGIN_DEV 381 dev_stop(ctx); 382 #endif 383 } 384 385 ctx->options |= DHCPCD_FORKED; 386 if (ctx->ps_log_fd != -1) 387 logsetfd(ctx->ps_log_fd); 388 389 #ifdef DEBUG_FD 390 logerrx("pid %d log_fd=%d data_fd=%d psp_fd=%d", getpid(), 391 ctx->ps_log_fd, ctx->ps_data_fd, psp->psp_fd); 392 #endif 393 394 if (ctx->fork_fd != -1) { 395 close(ctx->fork_fd); 396 ctx->fork_fd = -1; 397 } 398 399 if (eloop_forked(ctx->eloop, ELF_KEEP_SIGNALS) == -1) { 400 logerr("%s: eloop_forked", __func__); 401 goto errexit; 402 } 403 404 ps_freeprocesses(ctx, psp); 405 406 if (ctx->ps_root != psp) { 407 ctx->options &= ~DHCPCD_PRIVSEPROOT; 408 if (ctx->ps_log_root_fd != -1) { 409 /* Already removed from eloop thanks to above clear. */ 410 close(ctx->ps_log_root_fd); 411 ctx->ps_log_root_fd = -1; 412 } 413 #ifdef PRIVSEP_RIGHTS 414 if (ps_rights_limit_stdio() == -1) { 415 logerr("ps_rights_limit_stdio"); 416 goto errexit; 417 } 418 #endif 419 } 420 421 if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, recv_msg, psp) == 422 -1) { 423 logerr("%s: eloop_event_add", __func__); 424 goto errexit; 425 } 426 427 if (callback(psp) == -1) 428 goto errexit; 429 430 if (flags & PSF_DROPPRIVS) 431 ps_dropprivs(ctx); 432 433 psp->psp_started = true; 434 return 0; 435 436 errexit: 437 if (psp->psp_fd != -1) { 438 close(psp->psp_fd); 439 psp->psp_fd = -1; 440 } 441 eloop_exit(ctx->eloop, EXIT_FAILURE); 442 return -1; 443 } 444 445 void 446 ps_process_timeout(void *arg) 447 { 448 struct dhcpcd_ctx *ctx = arg; 449 450 logerrx("%s: timed out", __func__); 451 eloop_exit(ctx->eloop, EXIT_FAILURE); 452 } 453 454 int 455 ps_stopprocess(struct ps_process *psp) 456 { 457 int err = 0; 458 459 if (psp == NULL) 460 return 0; 461 462 psp->psp_started = false; 463 464 #ifdef PRIVSEP_DEBUG 465 logdebugx("%s: me=%d pid=%d fd=%d %s", __func__, getpid(), psp->psp_pid, 466 psp->psp_fd, psp->psp_name); 467 #endif 468 469 if (psp->psp_fd != -1) { 470 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); 471 #if 0 472 if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_STOP, 0, 473 NULL, 0) == -1) 474 { 475 logerr("%d %d %s %s", getpid(), psp->psp_pid, psp->psp_name, __func__); 476 err = -1; 477 } 478 shutdown(psp->psp_fd, SHUT_WR); 479 #else 480 if (shutdown(psp->psp_fd, SHUT_WR) == -1) { 481 logerr(__func__); 482 err = -1; 483 } 484 #endif 485 } 486 487 /* Don't wait for the process as it may not respond to the shutdown 488 * request. We'll reap the process on receipt of SIGCHLD where we 489 * also close the fd. */ 490 return err; 491 } 492 493 int 494 ps_bufalloc(struct dhcpcd_ctx *ctx, size_t len) 495 { 496 void *nbuf; 497 498 if (ctx->ps_buflen >= len) 499 return 0; 500 501 nbuf = realloc(ctx->ps_buf, len); 502 if (nbuf == NULL) 503 return -1; 504 505 ctx->ps_buf = nbuf; 506 ctx->ps_buflen = len; 507 return 0; 508 } 509 510 int 511 ps_start(struct dhcpcd_ctx *ctx) 512 { 513 pid_t pid; 514 uint32_t rnd; 515 516 TAILQ_INIT(&ctx->ps_processes); 517 /* Alloc a reasonable buffer up front */ 518 if (ps_bufalloc(ctx, BUFSIZ) == -1) { 519 logerr("%s: ps_bufalloc", __func__); 520 return -1; 521 } 522 523 switch (pid = ps_root_start(ctx)) { 524 case -1: 525 logerr("ps_root_start"); 526 return -1; 527 case 0: 528 return 0; 529 default: 530 logdebugx("spawned privileged proxy on PID %ld", (long)pid); 531 } 532 533 /* No point in spawning the generic network listener if we're 534 * not going to use it. */ 535 if (!ps_inet_canstart(ctx)) 536 goto started_net; 537 538 switch (pid = ps_inet_start(ctx)) { 539 case -1: 540 return -1; 541 case 0: 542 return 0; 543 default: 544 logdebugx("spawned network proxy on PID %ld", (long)pid); 545 } 546 547 started_net: 548 if (!(ctx->options & DHCPCD_TEST)) { 549 switch (pid = ps_ctl_start(ctx)) { 550 case -1: 551 return -1; 552 case 0: 553 return 0; 554 default: 555 logdebugx("spawned controller proxy on PID %ld", 556 (long)pid); 557 } 558 } 559 560 /* Seed the random number generator early incase it needs 561 * to cache /dev/urandom which won't be available in the chroot. */ 562 rnd = arc4random(); 563 UNUSED(rnd); 564 565 return 1; 566 } 567 568 int 569 ps_entersandbox(const char *_pledge, const char **sandbox) 570 { 571 #if !defined(HAVE_PLEDGE) 572 UNUSED(_pledge); 573 #endif 574 575 #if defined(HAVE_CAPSICUM) 576 if (sandbox != NULL) 577 *sandbox = "capsicum"; 578 return cap_enter(); 579 #elif defined(HAVE_PLEDGE) 580 if (sandbox != NULL) 581 *sandbox = "pledge"; 582 // There is no need to use unveil(2) because we are in an empty chroot 583 // This is encouraged by Theo de Raadt himself: 584 // https://www.mail-archive.com/misc@openbsd.org/msg171655.html 585 return pledge(_pledge, NULL); 586 #elif defined(HAVE_SECCOMP) 587 if (sandbox != NULL) 588 *sandbox = "seccomp"; 589 return ps_seccomp_enter(); 590 #else 591 if (sandbox != NULL) 592 #ifdef __sun 593 *sandbox = "none"; 594 #else 595 *sandbox = "posix resource limited"; 596 #endif 597 return 0; 598 #endif 599 } 600 601 int 602 ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge) 603 { 604 const char *sandbox = NULL; 605 bool forked; 606 int dropped; 607 608 forked = ctx->options & DHCPCD_FORKED; 609 ctx->options &= ~DHCPCD_FORKED; 610 dropped = ps_dropprivs(ctx); 611 if (forked) 612 ctx->options |= DHCPCD_FORKED; 613 614 /* 615 * If we don't have a root process, we cannot use syslog. 616 * If it cannot be opened before chrooting then syslog(3) will fail. 617 * openlog(3) does not return an error which doubly sucks. 618 */ 619 if (ctx->ps_root == NULL) { 620 unsigned int logopts = loggetopts(); 621 622 logopts &= ~LOGERR_LOG; 623 logsetopts(logopts); 624 } 625 626 if (dropped == -1) { 627 logerr("%s: ps_dropprivs", __func__); 628 return -1; 629 } 630 631 /* We can no longer unlink the pidfile. */ 632 pidfile_unremoveable(); 633 634 #ifdef PRIVSEP_RIGHTS 635 if ((ctx->pf_inet_fd != -1 && 636 ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) || 637 ps_rights_limit_stdio() == -1) { 638 logerr("%s: cap_rights_limit", __func__); 639 return -1; 640 } 641 #endif 642 643 if (_pledge == NULL) 644 _pledge = "stdio"; 645 if (ps_entersandbox(_pledge, &sandbox) == -1) { 646 if (errno == ENOSYS) { 647 if (sandbox != NULL) 648 logwarnx("sandbox unavailable: %s", sandbox); 649 return 0; 650 } 651 logerr("%s: %s", __func__, sandbox); 652 return -1; 653 } else if (ctx->options & DHCPCD_LAUNCHER || 654 ((!(ctx->options & DHCPCD_DAEMONISE)) && 655 ctx->options & DHCPCD_MANAGER)) 656 logdebugx("sandbox: %s", sandbox); 657 return 0; 658 } 659 660 int 661 ps_stop(struct dhcpcd_ctx *ctx) 662 { 663 int r, ret = 0; 664 665 if (!(ctx->options & DHCPCD_PRIVSEP) || ctx->options & DHCPCD_FORKED || 666 ctx->eloop == NULL) 667 return 0; 668 669 if (ctx->ps_ctl != NULL) { 670 r = ps_ctl_stop(ctx); 671 if (r != 0) 672 ret = r; 673 } 674 675 if (ctx->ps_inet != NULL) { 676 r = ps_inet_stop(ctx); 677 if (r != 0) 678 ret = r; 679 } 680 681 if (ctx->ps_root != NULL) { 682 if (ps_root_stopprocesses(ctx) == -1) 683 ret = -1; 684 } 685 686 return ret; 687 } 688 689 bool 690 ps_waitforprocs(struct dhcpcd_ctx *ctx) 691 { 692 struct ps_process *psp = TAILQ_FIRST(&ctx->ps_processes); 693 694 if (psp == NULL) 695 return false; 696 697 /* Different processes */ 698 if (psp != TAILQ_LAST(&ctx->ps_processes, ps_process_head)) 699 return true; 700 701 return !psp->psp_started; 702 } 703 704 int 705 ps_stopwait(struct dhcpcd_ctx *ctx) 706 { 707 int error = EXIT_SUCCESS; 708 709 if (!ps_waitforprocs(ctx)) 710 return 0; 711 712 ctx->options |= DHCPCD_EXITING; 713 if (eloop_timeout_add_sec(ctx->eloop, PS_PROCESS_TIMEOUT, 714 ps_process_timeout, ctx) == -1) 715 logerr("%s: eloop_timeout_add_sec", __func__); 716 717 #ifdef HAVE_CAPSICUM 718 struct ps_process *psp; 719 720 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 721 if (psp->psp_pfd == -1) 722 continue; 723 if (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP, 724 ps_processhangup, psp) == -1) 725 logerr("%s: eloop_event_add pfd %d", __func__, 726 psp->psp_pfd); 727 } 728 #endif 729 730 error = eloop_start(ctx->eloop); 731 if (error < 0) 732 logerr("%s: eloop_start", __func__); 733 734 eloop_timeout_delete(ctx->eloop, ps_process_timeout, ctx); 735 736 return error; 737 } 738 739 void 740 ps_freeprocess(struct ps_process *psp) 741 { 742 struct dhcpcd_ctx *ctx = psp->psp_ctx; 743 744 TAILQ_REMOVE(&ctx->ps_processes, psp, next); 745 746 if (psp->psp_fd != -1) { 747 eloop_event_delete(ctx->eloop, psp->psp_fd); 748 close(psp->psp_fd); 749 } 750 if (psp->psp_work_fd != -1) { 751 eloop_event_delete(ctx->eloop, psp->psp_work_fd); 752 close(psp->psp_work_fd); 753 } 754 #ifdef HAVE_CAPSICUM 755 if (psp->psp_pfd != -1) { 756 eloop_event_delete(ctx->eloop, psp->psp_pfd); 757 close(psp->psp_pfd); 758 } 759 #endif 760 if (ctx->ps_root == psp) 761 ctx->ps_root = NULL; 762 if (ctx->ps_inet == psp) 763 ctx->ps_inet = NULL; 764 if (ctx->ps_ctl == psp) 765 ctx->ps_ctl = NULL; 766 #ifdef INET 767 if (psp->psp_bpf != NULL) 768 bpf_close(psp->psp_bpf); 769 #endif 770 free(psp); 771 } 772 773 static void 774 ps_free(struct dhcpcd_ctx *ctx) 775 { 776 struct ps_process *ppsp, *psp; 777 bool stop; 778 779 if (ctx->ps_root != NULL) 780 ppsp = ctx->ps_root; 781 else if (ctx->ps_ctl != NULL) 782 ppsp = ctx->ps_ctl; 783 else 784 ppsp = NULL; 785 if (ppsp != NULL) 786 stop = ppsp->psp_pid == getpid(); 787 else 788 stop = false; 789 790 while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) { 791 if (stop && psp != ppsp) 792 ps_stopprocess(psp); 793 ps_freeprocess(psp); 794 } 795 } 796 797 int 798 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm, const void *data, 799 size_t len) 800 { 801 uint8_t *datap, *namep, *controlp; 802 socklen_t cmsg_padlen = CALC_CMSG_PADLEN(psm->ps_controllen, 803 psm->ps_namelen); 804 805 namep = UNCONST(data); 806 controlp = namep + psm->ps_namelen + cmsg_padlen; 807 datap = controlp + psm->ps_controllen; 808 809 if (psm->ps_namelen != 0) { 810 if (psm->ps_namelen > len) { 811 errno = EINVAL; 812 return -1; 813 } 814 msg->msg_name = namep; 815 len -= psm->ps_namelen; 816 } else 817 msg->msg_name = NULL; 818 msg->msg_namelen = psm->ps_namelen; 819 820 if (psm->ps_controllen != 0) { 821 if (psm->ps_controllen > len) { 822 errno = EINVAL; 823 return -1; 824 } 825 msg->msg_control = controlp; 826 len -= psm->ps_controllen + cmsg_padlen; 827 } else 828 msg->msg_control = NULL; 829 msg->msg_controllen = psm->ps_controllen; 830 831 if (len != 0) { 832 msg->msg_iovlen = 1; 833 msg->msg_iov[0].iov_base = datap; 834 msg->msg_iov[0].iov_len = len; 835 } else { 836 msg->msg_iovlen = 0; 837 msg->msg_iov[0].iov_base = NULL; 838 msg->msg_iov[0].iov_len = 0; 839 } 840 return 0; 841 } 842 843 ssize_t 844 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm, 845 const struct msghdr *msg) 846 { 847 long padding[1] = { 0 }; 848 struct iovec iov[] = { 849 { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) }, 850 { 851 .iov_base = NULL, 852 }, /* name */ 853 { 854 .iov_base = NULL, 855 }, /* control padding */ 856 { 857 .iov_base = NULL, 858 }, /* control */ 859 { 860 .iov_base = NULL, 861 }, /* payload 1 */ 862 { 863 .iov_base = NULL, 864 }, /* payload 2 */ 865 { 866 .iov_base = NULL, 867 }, /* payload 3 */ 868 }; 869 struct msghdr m = { .msg_iov = iov, .msg_iovlen = 1 }; 870 ssize_t len; 871 872 if (msg != NULL) { 873 struct iovec *iovp = &iov[1]; 874 int i; 875 socklen_t cmsg_padlen; 876 877 psm->ps_namelen = msg->msg_namelen; 878 psm->ps_controllen = (socklen_t)msg->msg_controllen; 879 psm->ps_datalen = 0; 880 881 iovp->iov_base = msg->msg_name; 882 iovp->iov_len = msg->msg_namelen; 883 iovp++; 884 m.msg_iovlen++; 885 886 cmsg_padlen = CALC_CMSG_PADLEN(msg->msg_controllen, 887 msg->msg_namelen); 888 assert(cmsg_padlen <= sizeof(padding)); 889 iovp->iov_len = cmsg_padlen; 890 iovp->iov_base = cmsg_padlen != 0 ? padding : NULL; 891 iovp++; 892 m.msg_iovlen++; 893 894 iovp->iov_base = msg->msg_control; 895 iovp->iov_len = msg->msg_controllen; 896 iovp++; 897 m.msg_iovlen++; 898 899 for (i = 0; i < (int)msg->msg_iovlen; i++) { 900 if ((size_t)m.msg_iovlen >= __arraycount(iov)) { 901 errno = ENOBUFS; 902 return -1; 903 } 904 m.msg_iovlen++; 905 iovp->iov_base = msg->msg_iov[i].iov_base; 906 iovp->iov_len = msg->msg_iov[i].iov_len; 907 iovp++; 908 psm->ps_datalen += msg->msg_iov[i].iov_len; 909 } 910 } 911 912 len = sendmsg(fd, &m, 0); 913 914 if (len == -1 && ctx != NULL) { 915 if (ctx->options & DHCPCD_FORKED && 916 !(ctx->options & DHCPCD_PRIVSEPROOT)) 917 eloop_exit(ctx->eloop, EXIT_FAILURE); 918 } 919 return len; 920 } 921 922 ssize_t 923 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm, 924 const void *data, size_t len) 925 { 926 struct iovec iov[] = { 927 { .iov_base = UNCONST(data), .iov_len = len }, 928 }; 929 struct msghdr msg = { 930 .msg_iov = iov, 931 .msg_iovlen = 1, 932 }; 933 934 return ps_sendpsmmsg(ctx, fd, psm, &msg); 935 } 936 937 ssize_t 938 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 939 const struct msghdr *msg) 940 { 941 struct ps_msghdr psm = { 942 .ps_cmd = cmd, 943 .ps_flags = flags, 944 .ps_namelen = msg->msg_namelen, 945 .ps_controllen = (socklen_t)msg->msg_controllen, 946 }; 947 size_t i; 948 949 for (i = 0; i < (size_t)msg->msg_iovlen; i++) 950 psm.ps_datalen += msg->msg_iov[i].iov_len; 951 952 #if 0 /* For debugging structure padding. */ 953 logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family)); 954 logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad)); 955 logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u)); 956 logerrx("psa %zu", sizeof(psm.ps_id.psi_addr)); 957 958 logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr)); 959 logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex)); 960 logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd)); 961 logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad)); 962 logerrx("psi %zu", sizeof(struct ps_id)); 963 964 logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd)); 965 logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad)); 966 logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags)); 967 968 logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id)); 969 970 logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen)); 971 logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen)); 972 logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2)); 973 logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen)); 974 logerrx("psm %zu", sizeof(psm)); 975 #endif 976 977 return ps_sendpsmmsg(ctx, fd, &psm, msg); 978 } 979 980 ssize_t 981 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 982 const void *data, size_t len) 983 { 984 struct ps_msghdr psm = { 985 .ps_cmd = cmd, 986 .ps_flags = flags, 987 }; 988 struct iovec iov[] = { { .iov_base = UNCONST(data), .iov_len = len } }; 989 struct msghdr msg = { 990 .msg_iov = iov, 991 .msg_iovlen = 1, 992 }; 993 994 return ps_sendpsmmsg(ctx, fd, &psm, &msg); 995 } 996 997 ssize_t 998 ps_sendcmdmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 999 const struct msghdr *msg) 1000 { 1001 struct ps_msghdr psm = { .ps_cmd = cmd, .ps_flags = flags }; 1002 1003 return ps_sendpsmmsg(ctx, fd, &psm, msg); 1004 } 1005 1006 ssize_t 1007 ps_recvmsg(int rfd, unsigned short events, uint16_t cmd, int wfd) 1008 { 1009 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC }; 1010 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 }; 1011 uint8_t databuf[64 * 1024]; 1012 struct iovec iov[] = { { .iov_base = databuf, 1013 .iov_len = sizeof(databuf) } }; 1014 struct msghdr msg = { 1015 .msg_name = &ss, 1016 .msg_namelen = sizeof(ss), 1017 .msg_control = controlbuf, 1018 .msg_controllen = sizeof(controlbuf), 1019 .msg_iov = iov, 1020 .msg_iovlen = 1, 1021 }; 1022 ssize_t len; 1023 1024 if (!(events & ELE_READ)) 1025 logerrx("%s: unexpected event 0x%04x", __func__, events); 1026 1027 len = recvmsg(rfd, &msg, MSG_WAITALL); 1028 if (len == -1) { 1029 logerr("%s: recvmsg", __func__); 1030 return len; 1031 } 1032 1033 iov[0].iov_len = (size_t)len; 1034 len = ps_sendcmdmsg(NULL, wfd, cmd, 0, &msg); 1035 if (len == -1) 1036 logerr("%s: ps_sendcmdmsg", __func__); 1037 return len; 1038 } 1039 1040 ssize_t 1041 ps_daemonised(struct dhcpcd_ctx *ctx) 1042 { 1043 struct ps_process *psp; 1044 ssize_t err = 0; 1045 1046 dhcpcd_daemonised(ctx); 1047 1048 /* Echo the message to all processes */ 1049 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 1050 if (psp->psp_pid == getpid()) 1051 continue; 1052 if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_DAEMONISED, 0, 1053 NULL, 0) == -1) 1054 err = -1; 1055 } 1056 1057 return err; 1058 } 1059 1060 ssize_t 1061 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, 1062 ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), 1063 void *cbctx) 1064 { 1065 struct ps_msghdr psm; 1066 ssize_t len; 1067 size_t dlen; 1068 struct iovec iov[1]; 1069 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 }; 1070 bool stop = false; 1071 socklen_t cmsg_padlen; 1072 1073 if (events & ELE_HANGUP) { 1074 len = 0; 1075 goto stop; 1076 } 1077 if (!(events & ELE_READ)) 1078 logerrx("%s: unexpected event 0x%04x", __func__, events); 1079 1080 len = recv(fd, &psm, sizeof(psm), MSG_WAITALL); 1081 #ifdef PRIVSEP_DEBUG 1082 logdebugx("%s: pid=%d fd=%d len=%zd", __func__, getpid(), fd, len); 1083 #endif 1084 1085 if (len == -1 || len == 0) 1086 stop = true; 1087 else { 1088 dlen = (size_t)len; 1089 if (dlen < sizeof(psm)) { 1090 errno = EINVAL; 1091 goto stop; 1092 } 1093 1094 if (psm.ps_cmd == PS_STOP) { 1095 stop = true; 1096 len = 0; 1097 } else if (psm.ps_cmd == PS_DAEMONISED) { 1098 ps_daemonised(ctx); 1099 return 0; 1100 } 1101 } 1102 1103 if (stop) { 1104 stop: 1105 ctx->options |= DHCPCD_EXITING; 1106 #ifdef PRIVSEP_DEBUG 1107 logdebugx("process %d stopping", getpid()); 1108 #endif 1109 ps_free(ctx); 1110 eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE); 1111 return len; 1112 } 1113 1114 cmsg_padlen = CALC_CMSG_PADLEN(psm.ps_controllen, psm.ps_namelen); 1115 dlen = psm.ps_namelen + psm.ps_controllen + cmsg_padlen + 1116 psm.ps_datalen; 1117 if (dlen != 0) { 1118 if (ps_bufalloc(ctx, dlen) == -1) 1119 goto stop; 1120 len = recv(fd, ctx->ps_buf, dlen, MSG_WAITALL); 1121 if ((size_t)len != dlen) { 1122 errno = EINVAL; 1123 goto stop; 1124 } 1125 } 1126 1127 if (ps_unrollmsg(&msg, &psm, ctx->ps_buf, dlen) == -1) 1128 return -1; 1129 1130 if (callback == NULL) 1131 return 0; 1132 1133 errno = 0; 1134 return callback(cbctx, &psm, &msg); 1135 } 1136 1137 struct ps_process * 1138 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 1139 { 1140 struct ps_process *psp; 1141 1142 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 1143 if (!(psp->psp_started)) 1144 continue; 1145 if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0) 1146 return psp; 1147 } 1148 errno = ESRCH; 1149 return NULL; 1150 } 1151 1152 struct ps_process * 1153 ps_findprocesspid(struct dhcpcd_ctx *ctx, pid_t pid) 1154 { 1155 struct ps_process *psp; 1156 1157 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 1158 if (psp->psp_pid == pid) 1159 return psp; 1160 } 1161 errno = ESRCH; 1162 return NULL; 1163 } 1164 1165 struct ps_process * 1166 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 1167 { 1168 struct ps_process *psp; 1169 1170 psp = calloc(1, sizeof(*psp)); 1171 if (psp == NULL) 1172 return NULL; 1173 psp->psp_ctx = ctx; 1174 memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); 1175 psp->psp_fd = -1; 1176 psp->psp_work_fd = -1; 1177 #ifdef HAVE_CAPSICUM 1178 psp->psp_pfd = -1; 1179 #endif 1180 1181 if (psid->psi_ifindex != 0) { 1182 psp->psp_ifindex = psid->psi_ifindex; 1183 if_indextoname(psid->psi_ifindex, psp->psp_ifname); 1184 } else { 1185 if (!(ctx->options & DHCPCD_MANAGER) && ctx->ifc != 0) 1186 strlcpy(psp->psp_ifname, ctx->ifv[0], 1187 sizeof(psp->psp_ifname)); 1188 } 1189 TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next); 1190 return psp; 1191 } 1192 1193 void 1194 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis) 1195 { 1196 struct ps_process *psp, *psn; 1197 1198 TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) { 1199 if (psp == notthis) 1200 continue; 1201 /* control needs root access to work out user group */ 1202 if (ctx->ps_ctl == notthis && psp == ctx->ps_root) 1203 continue; 1204 ps_freeprocess(psp); 1205 } 1206 } 1207