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