1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Privilege Separation for dhcpcd 4 * Copyright (c) 2006-2021 Roy Marples <roy@marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 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/resource.h> 44 #include <sys/socket.h> 45 #include <sys/stat.h> 46 #include <sys/types.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 <stddef.h> /* For offsetof, struct padding debug */ 60 #include <signal.h> 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 <capsicum_helpers.h> 79 #endif 80 #ifdef HAVE_UTIL_H 81 #include <util.h> 82 #endif 83 84 /* CMSG_ALIGN is a Linux extension */ 85 #ifndef CMSG_ALIGN 86 #define CMSG_ALIGN(n) (CMSG_SPACE((n)) - CMSG_SPACE(0)) 87 #endif 88 89 /* Calculate number of padding bytes to achieve 'struct cmsghdr' alignment */ 90 #define CALC_CMSG_PADLEN(has_cmsg, pos) \ 91 ((has_cmsg) ? (socklen_t)(CMSG_ALIGN((pos)) - (pos)) : 0) 92 93 int 94 ps_init(struct dhcpcd_ctx *ctx) 95 { 96 struct passwd *pw; 97 struct stat st; 98 99 errno = 0; 100 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) { 101 ctx->options &= ~DHCPCD_PRIVSEP; 102 if (errno == 0) { 103 logerrx("no such user %s", PRIVSEP_USER); 104 /* Just incase logerrx caused an error... */ 105 errno = 0; 106 } else 107 logerr("getpwnam"); 108 return -1; 109 } 110 111 if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) { 112 ctx->options &= ~DHCPCD_PRIVSEP; 113 logerrx("refusing chroot: %s: %s", 114 PRIVSEP_USER, pw->pw_dir); 115 errno = 0; 116 return -1; 117 } 118 119 ctx->options |= DHCPCD_PRIVSEP; 120 return 0; 121 } 122 123 static int 124 ps_dropprivs(struct dhcpcd_ctx *ctx) 125 { 126 struct passwd *pw = ctx->ps_user; 127 128 if (ctx->options & DHCPCD_LAUNCHER) 129 logdebugx("chrooting as %s to %s", pw->pw_name, pw->pw_dir); 130 if (chroot(pw->pw_dir) == -1 && 131 (errno != EPERM || ctx->options & DHCPCD_FORKED)) 132 logerr("%s: chroot: %s", __func__, pw->pw_dir); 133 if (chdir("/") == -1) 134 logerr("%s: chdir: /", __func__); 135 136 if ((setgroups(1, &pw->pw_gid) == -1 || 137 setgid(pw->pw_gid) == -1 || 138 setuid(pw->pw_uid) == -1) && 139 (errno != EPERM || ctx->options & DHCPCD_FORKED)) 140 { 141 logerr("failed to drop privileges"); 142 return -1; 143 } 144 145 struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 }; 146 147 if (ctx->ps_control_pid != getpid()) { 148 /* Prohibit new files, sockets, etc */ 149 #if defined(__linux__) || defined(__sun) || defined(__OpenBSD__) 150 /* 151 * If poll(2) is called with nfds > RLIMIT_NOFILE 152 * then it returns EINVAL. 153 * This blows. 154 * Do the best we can and limit to what we need. 155 * An attacker could potentially close a file and 156 * open a new one still, but that cannot be helped. 157 */ 158 unsigned long maxfd; 159 maxfd = (unsigned long)eloop_event_count(ctx->eloop); 160 if (IN_PRIVSEP_SE(ctx)) 161 maxfd++; /* XXX why? */ 162 163 struct rlimit rmaxfd = { 164 .rlim_cur = maxfd, 165 .rlim_max = maxfd 166 }; 167 if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1) 168 logerr("setrlimit RLIMIT_NOFILE"); 169 #else 170 if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) 171 logerr("setrlimit RLIMIT_NOFILE"); 172 #endif 173 } 174 175 #define DHC_NOCHKIO (DHCPCD_STARTED | DHCPCD_DAEMONISE) 176 /* Prohibit writing to files. 177 * Obviously this won't work if we are using a logfile 178 * or redirecting stderr to a file. */ 179 if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO || 180 (ctx->logfile == NULL && 181 (!ctx->stderr_valid || isatty(STDERR_FILENO) == 1))) 182 { 183 if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) 184 logerr("setrlimit RLIMIT_FSIZE"); 185 } 186 187 #ifdef RLIMIT_NPROC 188 /* Prohibit forks */ 189 if (setrlimit(RLIMIT_NPROC, &rzero) == -1) 190 logerr("setrlimit RLIMIT_NPROC"); 191 #endif 192 193 return 0; 194 } 195 196 static int 197 ps_setbuf0(int fd, int ctl, int minlen) 198 { 199 int len; 200 socklen_t slen; 201 202 slen = sizeof(len); 203 if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1) 204 return -1; 205 206 #ifdef __linux__ 207 len /= 2; 208 #endif 209 if (len >= minlen) 210 return 0; 211 212 return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen)); 213 } 214 215 static int 216 ps_setbuf(int fd) 217 { 218 /* Ensure we can receive a fully sized privsep message. 219 * Double the send buffer. */ 220 int minlen = (int)sizeof(struct ps_msg); 221 222 if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 || 223 ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1) 224 { 225 logerr(__func__); 226 return -1; 227 } 228 return 0; 229 } 230 231 int 232 ps_setbuf_fdpair(int fd[]) 233 { 234 235 if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1) 236 return -1; 237 return 0; 238 } 239 240 #ifdef PRIVSEP_RIGHTS 241 int 242 ps_rights_limit_ioctl(int fd) 243 { 244 cap_rights_t rights; 245 246 cap_rights_init(&rights, CAP_IOCTL); 247 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 248 return -1; 249 return 0; 250 } 251 252 int 253 ps_rights_limit_fd_fctnl(int fd) 254 { 255 cap_rights_t rights; 256 257 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, 258 CAP_ACCEPT, CAP_FCNTL); 259 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 260 return -1; 261 return 0; 262 } 263 264 int 265 ps_rights_limit_fd(int fd) 266 { 267 cap_rights_t rights; 268 269 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN); 270 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 271 return -1; 272 return 0; 273 } 274 275 int 276 ps_rights_limit_fd_sockopt(int fd) 277 { 278 cap_rights_t rights; 279 280 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, 281 CAP_GETSOCKOPT, CAP_SETSOCKOPT); 282 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 283 return -1; 284 return 0; 285 } 286 287 int 288 ps_rights_limit_fd_rdonly(int fd) 289 { 290 cap_rights_t rights; 291 292 cap_rights_init(&rights, CAP_READ, CAP_EVENT); 293 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 294 return -1; 295 return 0; 296 } 297 298 int 299 ps_rights_limit_fdpair(int fd[]) 300 { 301 302 if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1) 303 return -1; 304 return 0; 305 } 306 307 static int 308 ps_rights_limit_stdio(struct dhcpcd_ctx *ctx) 309 { 310 const int iebadf = CAPH_IGNORE_EBADF; 311 int error = 0; 312 313 if (ctx->stdin_valid && 314 caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1) 315 error = -1; 316 if (ctx->stdout_valid && 317 caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1) 318 error = -1; 319 if (ctx->stderr_valid && 320 caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1) 321 error = -1; 322 323 return error; 324 } 325 #endif 326 327 pid_t 328 ps_dostart(struct dhcpcd_ctx *ctx, 329 pid_t *priv_pid, int *priv_fd, 330 void (*recv_msg)(void *), void (*recv_unpriv_msg), 331 void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *), 332 unsigned int flags) 333 { 334 int fd[2]; 335 pid_t pid; 336 337 if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) { 338 logerr("%s: socketpair", __func__); 339 return -1; 340 } 341 if (ps_setbuf_fdpair(fd) == -1) { 342 logerr("%s: ps_setbuf_fdpair", __func__); 343 return -1; 344 } 345 #ifdef PRIVSEP_RIGHTS 346 if (ps_rights_limit_fdpair(fd) == -1) { 347 logerr("%s: ps_rights_limit_fdpair", __func__); 348 return -1; 349 } 350 #endif 351 352 switch (pid = fork()) { 353 case -1: 354 logerr("fork"); 355 return -1; 356 case 0: 357 *priv_fd = fd[1]; 358 close(fd[0]); 359 break; 360 default: 361 *priv_pid = pid; 362 *priv_fd = fd[0]; 363 close(fd[1]); 364 if (recv_unpriv_msg == NULL) 365 ; 366 else if (eloop_event_add(ctx->eloop, *priv_fd, 367 recv_unpriv_msg, recv_ctx) == -1) 368 { 369 logerr("%s: eloop_event_add", __func__); 370 return -1; 371 } 372 return pid; 373 } 374 375 ctx->options |= DHCPCD_FORKED; 376 if (ctx->fork_fd != -1) { 377 close(ctx->fork_fd); 378 ctx->fork_fd = -1; 379 } 380 pidfile_clean(); 381 382 eloop_clear(ctx->eloop); 383 eloop_signal_set_cb(ctx->eloop, 384 dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx); 385 /* ctx->sigset aready has the initial sigmask set in main() */ 386 if (eloop_signal_mask(ctx->eloop, NULL) == -1) { 387 logerr("%s: eloop_signal_mask", __func__); 388 goto errexit; 389 } 390 391 /* We are not root */ 392 if (priv_fd != &ctx->ps_root_fd) { 393 ps_freeprocesses(ctx, recv_ctx); 394 if (ctx->ps_root_fd != -1) { 395 close(ctx->ps_root_fd); 396 ctx->ps_root_fd = -1; 397 } 398 399 #ifdef PRIVSEP_RIGHTS 400 /* We cannot limit the root process in any way. */ 401 if (ps_rights_limit_stdio(ctx) == -1) { 402 logerr("ps_rights_limit_stdio"); 403 goto errexit; 404 } 405 #endif 406 } 407 408 if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) { 409 close(ctx->ps_inet_fd); 410 ctx->ps_inet_fd = -1; 411 } 412 413 if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1) 414 { 415 logerr("%s: eloop_event_add", __func__); 416 goto errexit; 417 } 418 419 if (callback(recv_ctx) == -1) 420 goto errexit; 421 422 if (flags & PSF_DROPPRIVS) 423 ps_dropprivs(ctx); 424 425 return 0; 426 427 errexit: 428 /* Failure to start root or inet processes is fatal. */ 429 if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd) 430 (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0); 431 shutdown(*priv_fd, SHUT_RDWR); 432 *priv_fd = -1; 433 eloop_exit(ctx->eloop, EXIT_FAILURE); 434 return -1; 435 } 436 437 int 438 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd) 439 { 440 int err = 0; 441 442 #ifdef PRIVSEP_DEBUG 443 logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd); 444 #endif 445 446 if (*fd != -1) { 447 eloop_event_delete(ctx->eloop, *fd); 448 if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) { 449 logerr(__func__); 450 err = -1; 451 } 452 (void)shutdown(*fd, SHUT_RDWR); 453 close(*fd); 454 *fd = -1; 455 } 456 457 /* Don't wait for the process as it may not respond to the shutdown 458 * request. We'll reap the process on receipt of SIGCHLD. */ 459 *pid = 0; 460 return err; 461 } 462 463 int 464 ps_start(struct dhcpcd_ctx *ctx) 465 { 466 pid_t pid; 467 468 TAILQ_INIT(&ctx->ps_processes); 469 470 switch (pid = ps_root_start(ctx)) { 471 case -1: 472 logerr("ps_root_start"); 473 return -1; 474 case 0: 475 return 0; 476 default: 477 logdebugx("spawned privileged proxy on PID %d", pid); 478 } 479 480 /* No point in spawning the generic network listener if we're 481 * not going to use it. */ 482 if (!ps_inet_canstart(ctx)) 483 goto started_net; 484 485 switch (pid = ps_inet_start(ctx)) { 486 case -1: 487 return -1; 488 case 0: 489 return 0; 490 default: 491 logdebugx("spawned network proxy on PID %d", pid); 492 } 493 494 started_net: 495 if (!(ctx->options & DHCPCD_TEST)) { 496 switch (pid = ps_ctl_start(ctx)) { 497 case -1: 498 return -1; 499 case 0: 500 return 0; 501 default: 502 logdebugx("spawned controller proxy on PID %d", pid); 503 } 504 } 505 506 #ifdef ARC4RANDOM_H 507 /* Seed the random number generator early incase it needs /dev/urandom 508 * which won't be available in the chroot. */ 509 arc4random(); 510 #endif 511 512 return 1; 513 } 514 515 int 516 ps_entersandbox(const char *_pledge, const char **sandbox) 517 { 518 519 #if !defined(HAVE_PLEDGE) 520 UNUSED(_pledge); 521 #endif 522 523 #if defined(HAVE_CAPSICUM) 524 if (sandbox != NULL) 525 *sandbox = "capsicum"; 526 return cap_enter(); 527 #elif defined(HAVE_PLEDGE) 528 if (sandbox != NULL) 529 *sandbox = "pledge"; 530 return pledge(_pledge, NULL); 531 #elif defined(HAVE_SECCOMP) 532 if (sandbox != NULL) 533 *sandbox = "seccomp"; 534 return ps_seccomp_enter(); 535 #else 536 if (sandbox != NULL) 537 *sandbox = "posix resource limited"; 538 return 0; 539 #endif 540 } 541 542 int 543 ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge) 544 { 545 const char *sandbox = NULL; 546 bool forked; 547 int dropped; 548 549 forked = ctx->options & DHCPCD_FORKED; 550 ctx->options &= ~DHCPCD_FORKED; 551 dropped = ps_dropprivs(ctx); 552 if (forked) 553 ctx->options |= DHCPCD_FORKED; 554 555 /* 556 * If we don't have a root process, we cannot use syslog. 557 * If it cannot be opened before chrooting then syslog(3) will fail. 558 * openlog(3) does not return an error which doubly sucks. 559 */ 560 if (ctx->ps_root_fd == -1) { 561 unsigned int logopts = loggetopts(); 562 563 logopts &= ~LOGERR_LOG; 564 logsetopts(logopts); 565 } 566 567 if (dropped == -1) { 568 logerr("%s: ps_dropprivs", __func__); 569 return -1; 570 } 571 572 #ifdef PRIVSEP_RIGHTS 573 if ((ctx->pf_inet_fd != -1 && 574 ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) || 575 ps_rights_limit_stdio(ctx) == -1) 576 { 577 logerr("%s: cap_rights_limit", __func__); 578 return -1; 579 } 580 #endif 581 582 if (_pledge == NULL) 583 _pledge = "stdio"; 584 if (ps_entersandbox(_pledge, &sandbox) == -1) { 585 if (errno == ENOSYS) { 586 if (sandbox != NULL) 587 logwarnx("sandbox unavailable: %s", sandbox); 588 return 0; 589 } 590 logerr("%s: %s", __func__, sandbox); 591 return -1; 592 } else if (ctx->options & DHCPCD_LAUNCHER || 593 ((!(ctx->options & DHCPCD_DAEMONISE)) && 594 ctx->options & DHCPCD_MANAGER)) 595 logdebugx("sandbox: %s", sandbox); 596 return 0; 597 } 598 599 int 600 ps_stop(struct dhcpcd_ctx *ctx) 601 { 602 int r, ret = 0; 603 604 if (!(ctx->options & DHCPCD_PRIVSEP) || 605 ctx->options & DHCPCD_FORKED || 606 ctx->eloop == NULL) 607 return 0; 608 609 r = ps_ctl_stop(ctx); 610 if (r != 0) 611 ret = r; 612 613 r = ps_inet_stop(ctx); 614 if (r != 0) 615 ret = r; 616 617 /* We've been chrooted, so we need to tell the 618 * privileged proxy to remove the pidfile. */ 619 ps_root_unlink(ctx, ctx->pidfile); 620 621 r = ps_root_stop(ctx); 622 if (r != 0) 623 ret = r; 624 625 ctx->options &= ~DHCPCD_PRIVSEP; 626 return ret; 627 } 628 629 void 630 ps_freeprocess(struct ps_process *psp) 631 { 632 633 TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next); 634 if (psp->psp_fd != -1) { 635 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); 636 close(psp->psp_fd); 637 } 638 if (psp->psp_work_fd != -1) { 639 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd); 640 close(psp->psp_work_fd); 641 } 642 #ifdef INET 643 if (psp->psp_bpf != NULL) 644 bpf_close(psp->psp_bpf); 645 #endif 646 free(psp); 647 } 648 649 static void 650 ps_free(struct dhcpcd_ctx *ctx) 651 { 652 struct ps_process *psp; 653 bool stop = ctx->ps_root_pid == getpid(); 654 655 while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) { 656 if (stop) 657 ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); 658 ps_freeprocess(psp); 659 } 660 } 661 662 int 663 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm, 664 const void *data, size_t len) 665 { 666 uint8_t *datap, *namep, *controlp; 667 socklen_t cmsg_padlen = 668 CALC_CMSG_PADLEN(psm->ps_controllen, psm->ps_namelen); 669 670 namep = UNCONST(data); 671 controlp = namep + psm->ps_namelen + cmsg_padlen; 672 datap = controlp + psm->ps_controllen; 673 674 if (psm->ps_namelen != 0) { 675 if (psm->ps_namelen > len) { 676 errno = EINVAL; 677 return -1; 678 } 679 msg->msg_name = namep; 680 len -= psm->ps_namelen; 681 } else 682 msg->msg_name = NULL; 683 msg->msg_namelen = psm->ps_namelen; 684 685 if (psm->ps_controllen != 0) { 686 if (psm->ps_controllen > len) { 687 errno = EINVAL; 688 return -1; 689 } 690 msg->msg_control = controlp; 691 len -= psm->ps_controllen + cmsg_padlen; 692 } else 693 msg->msg_control = NULL; 694 msg->msg_controllen = psm->ps_controllen; 695 696 if (len != 0) { 697 msg->msg_iovlen = 1; 698 msg->msg_iov[0].iov_base = datap; 699 msg->msg_iov[0].iov_len = len; 700 } else { 701 msg->msg_iovlen = 0; 702 msg->msg_iov[0].iov_base = NULL; 703 msg->msg_iov[0].iov_len = 0; 704 } 705 return 0; 706 } 707 708 ssize_t 709 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, 710 struct ps_msghdr *psm, const struct msghdr *msg) 711 { 712 long padding[1] = { 0 }; 713 struct iovec iov[] = { 714 { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) }, 715 { .iov_base = NULL, }, /* name */ 716 { .iov_base = NULL, }, /* control padding */ 717 { .iov_base = NULL, }, /* control */ 718 { .iov_base = NULL, }, /* payload 1 */ 719 { .iov_base = NULL, }, /* payload 2 */ 720 { .iov_base = NULL, }, /* payload 3 */ 721 }; 722 int iovlen; 723 ssize_t len; 724 725 if (msg != NULL) { 726 struct iovec *iovp = &iov[1]; 727 int i; 728 socklen_t cmsg_padlen; 729 730 psm->ps_namelen = msg->msg_namelen; 731 psm->ps_controllen = (socklen_t)msg->msg_controllen; 732 733 iovp->iov_base = msg->msg_name; 734 iovp->iov_len = msg->msg_namelen; 735 iovp++; 736 737 cmsg_padlen = 738 CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen); 739 assert(cmsg_padlen <= sizeof(padding)); 740 iovp->iov_len = cmsg_padlen; 741 iovp->iov_base = cmsg_padlen != 0 ? padding : NULL; 742 iovp++; 743 744 iovp->iov_base = msg->msg_control; 745 iovp->iov_len = msg->msg_controllen; 746 iovlen = 4; 747 748 for (i = 0; i < (int)msg->msg_iovlen; i++) { 749 if ((size_t)(iovlen + i) > __arraycount(iov)) { 750 errno = ENOBUFS; 751 return -1; 752 } 753 iovp++; 754 iovp->iov_base = msg->msg_iov[i].iov_base; 755 iovp->iov_len = msg->msg_iov[i].iov_len; 756 } 757 iovlen += i; 758 } else 759 iovlen = 1; 760 761 len = writev(fd, iov, iovlen); 762 if (len == -1) { 763 logerr(__func__); 764 if (ctx->options & DHCPCD_FORKED && 765 !(ctx->options & DHCPCD_PRIVSEPROOT)) 766 eloop_exit(ctx->eloop, EXIT_FAILURE); 767 } 768 return len; 769 } 770 771 ssize_t 772 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd, 773 struct ps_msghdr *psm, const void *data, size_t len) 774 { 775 struct iovec iov[] = { 776 { .iov_base = UNCONST(data), .iov_len = len }, 777 }; 778 struct msghdr msg = { 779 .msg_iov = iov, .msg_iovlen = 1, 780 }; 781 782 return ps_sendpsmmsg(ctx, fd, psm, &msg); 783 } 784 785 786 ssize_t 787 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 788 const struct msghdr *msg) 789 { 790 struct ps_msghdr psm = { 791 .ps_cmd = cmd, 792 .ps_flags = flags, 793 .ps_namelen = msg->msg_namelen, 794 .ps_controllen = (socklen_t)msg->msg_controllen, 795 }; 796 size_t i; 797 798 for (i = 0; i < (size_t)msg->msg_iovlen; i++) 799 psm.ps_datalen += msg->msg_iov[i].iov_len; 800 801 #if 0 /* For debugging structure padding. */ 802 logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family)); 803 logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad)); 804 logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u)); 805 logerrx("psa %zu", sizeof(psm.ps_id.psi_addr)); 806 807 logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr)); 808 logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex)); 809 logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd)); 810 logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad)); 811 logerrx("psi %zu", sizeof(struct ps_id)); 812 813 logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd)); 814 logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad)); 815 logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags)); 816 817 logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id)); 818 819 logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen)); 820 logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen)); 821 logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2)); 822 logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen)); 823 logerrx("psm %zu", sizeof(psm)); 824 #endif 825 826 return ps_sendpsmmsg(ctx, fd, &psm, msg); 827 } 828 829 ssize_t 830 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 831 const void *data, size_t len) 832 { 833 struct ps_msghdr psm = { 834 .ps_cmd = cmd, 835 .ps_flags = flags, 836 }; 837 struct iovec iov[] = { 838 { .iov_base = UNCONST(data), .iov_len = len } 839 }; 840 struct msghdr msg = { 841 .msg_iov = iov, .msg_iovlen = 1, 842 }; 843 844 return ps_sendpsmmsg(ctx, fd, &psm, &msg); 845 } 846 847 static ssize_t 848 ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg) 849 { 850 struct ps_msghdr psm = { .ps_cmd = cmd }; 851 uint8_t data[PS_BUFLEN], *p = data; 852 struct iovec iov[] = { 853 { .iov_base = &psm, .iov_len = sizeof(psm) }, 854 { .iov_base = data, .iov_len = 0 }, 855 }; 856 size_t dl = sizeof(data); 857 socklen_t cmsg_padlen = 858 CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen); 859 860 if (msg->msg_namelen != 0) { 861 if (msg->msg_namelen > dl) 862 goto nobufs; 863 psm.ps_namelen = msg->msg_namelen; 864 memcpy(p, msg->msg_name, msg->msg_namelen); 865 p += msg->msg_namelen; 866 dl -= msg->msg_namelen; 867 } 868 869 if (msg->msg_controllen != 0) { 870 if (msg->msg_controllen + cmsg_padlen > dl) 871 goto nobufs; 872 if (cmsg_padlen != 0) { 873 memset(p, 0, cmsg_padlen); 874 p += cmsg_padlen; 875 dl -= cmsg_padlen; 876 } 877 psm.ps_controllen = (socklen_t)msg->msg_controllen; 878 memcpy(p, msg->msg_control, msg->msg_controllen); 879 p += msg->msg_controllen; 880 dl -= msg->msg_controllen; 881 } 882 883 psm.ps_datalen = msg->msg_iov[0].iov_len; 884 if (psm.ps_datalen > dl) 885 goto nobufs; 886 887 iov[1].iov_len = 888 psm.ps_namelen + psm.ps_controllen + psm.ps_datalen + cmsg_padlen; 889 if (psm.ps_datalen != 0) 890 memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen); 891 return writev(fd, iov, __arraycount(iov)); 892 893 nobufs: 894 errno = ENOBUFS; 895 return -1; 896 } 897 898 ssize_t 899 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd) 900 { 901 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC }; 902 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 }; 903 uint8_t databuf[64 * 1024]; 904 struct iovec iov[] = { 905 { .iov_base = databuf, .iov_len = sizeof(databuf) } 906 }; 907 struct msghdr msg = { 908 .msg_name = &ss, .msg_namelen = sizeof(ss), 909 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf), 910 .msg_iov = iov, .msg_iovlen = 1, 911 }; 912 913 ssize_t len = recvmsg(rfd, &msg, 0); 914 915 if (len == -1) 916 logerr("%s: recvmsg", __func__); 917 if (len == -1 || len == 0) { 918 if (ctx->options & DHCPCD_FORKED && 919 !(ctx->options & DHCPCD_PRIVSEPROOT)) 920 eloop_exit(ctx->eloop, 921 len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 922 return len; 923 } 924 925 iov[0].iov_len = (size_t)len; 926 len = ps_sendcmdmsg(wfd, cmd, &msg); 927 if (len == -1) { 928 logerr("ps_sendcmdmsg"); 929 if (ctx->options & DHCPCD_FORKED && 930 !(ctx->options & DHCPCD_PRIVSEPROOT)) 931 eloop_exit(ctx->eloop, EXIT_FAILURE); 932 } 933 return len; 934 } 935 936 ssize_t 937 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, 938 ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), 939 void *cbctx) 940 { 941 struct ps_msg psm; 942 ssize_t len; 943 size_t dlen; 944 struct iovec iov[1]; 945 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 }; 946 bool stop = false; 947 948 len = read(fd, &psm, sizeof(psm)); 949 #ifdef PRIVSEP_DEBUG 950 logdebugx("%s: %zd", __func__, len); 951 #endif 952 953 if (len == -1 || len == 0) 954 stop = true; 955 else { 956 dlen = (size_t)len; 957 if (dlen < sizeof(psm.psm_hdr)) { 958 errno = EINVAL; 959 return -1; 960 } 961 962 if (psm.psm_hdr.ps_cmd == PS_STOP) { 963 stop = true; 964 len = 0; 965 } 966 } 967 968 if (stop) { 969 #ifdef PRIVSEP_DEBUG 970 logdebugx("process %d stopping", getpid()); 971 #endif 972 ps_free(ctx); 973 #ifdef PLUGIN_DEV 974 dev_stop(ctx); 975 #endif 976 eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE); 977 return len; 978 } 979 dlen -= sizeof(psm.psm_hdr); 980 981 if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1) 982 return -1; 983 984 if (callback == NULL) 985 return 0; 986 987 errno = 0; 988 return callback(cbctx, &psm.psm_hdr, &msg); 989 } 990 991 struct ps_process * 992 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 993 { 994 struct ps_process *psp; 995 996 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 997 if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0) 998 return psp; 999 } 1000 errno = ESRCH; 1001 return NULL; 1002 } 1003 1004 struct ps_process * 1005 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 1006 { 1007 struct ps_process *psp; 1008 1009 psp = calloc(1, sizeof(*psp)); 1010 if (psp == NULL) 1011 return NULL; 1012 psp->psp_ctx = ctx; 1013 memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); 1014 psp->psp_work_fd = -1; 1015 TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next); 1016 return psp; 1017 } 1018 1019 void 1020 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis) 1021 { 1022 struct ps_process *psp, *psn; 1023 1024 TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) { 1025 if (psp == notthis) 1026 continue; 1027 ps_freeprocess(psp); 1028 } 1029 } 1030