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 * 38 * dhcpcd will maintain the config file in the chroot, no need to handle 39 * this in a script or something. 40 */ 41 42 #include <sys/resource.h> 43 #include <sys/socket.h> 44 #include <sys/stat.h> 45 #include <sys/types.h> 46 #include <sys/wait.h> 47 48 #ifdef AF_LINK 49 #include <net/if_dl.h> 50 #endif 51 52 #include <assert.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <grp.h> 56 #include <paths.h> 57 #include <pwd.h> 58 #include <stddef.h> /* For offsetof, struct padding debug */ 59 #include <signal.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "arp.h" 65 #include "common.h" 66 #include "control.h" 67 #include "dev.h" 68 #include "dhcp.h" 69 #include "dhcp6.h" 70 #include "eloop.h" 71 #include "ipv6nd.h" 72 #include "logerr.h" 73 #include "privsep.h" 74 75 #ifdef HAVE_CAPSICUM 76 #include <sys/capsicum.h> 77 #endif 78 #ifdef HAVE_UTIL_H 79 #include <util.h> 80 #endif 81 82 int 83 ps_init(struct dhcpcd_ctx *ctx) 84 { 85 struct passwd *pw; 86 struct stat st; 87 88 errno = 0; 89 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) { 90 ctx->options &= ~DHCPCD_PRIVSEP; 91 if (errno == 0) { 92 logerrx("no such user %s", PRIVSEP_USER); 93 /* Just incase logerrx caused an error... */ 94 errno = 0; 95 } else 96 logerr("getpwnam"); 97 return -1; 98 } 99 100 if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) { 101 ctx->options &= ~DHCPCD_PRIVSEP; 102 logerrx("refusing chroot: %s: %s", 103 PRIVSEP_USER, pw->pw_dir); 104 errno = 0; 105 return -1; 106 } 107 108 ctx->options |= DHCPCD_PRIVSEP; 109 return 0; 110 } 111 112 int 113 ps_dropprivs(struct dhcpcd_ctx *ctx) 114 { 115 struct passwd *pw = ctx->ps_user; 116 117 if (!(ctx->options & DHCPCD_FORKED)) 118 logdebugx("chrooting to `%s' as %s", pw->pw_dir, pw->pw_name); 119 if (chroot(pw->pw_dir) == -1) 120 logerr("%s: chroot `%s'", __func__, pw->pw_dir); 121 if (chdir("/") == -1) 122 logerr("%s: chdir `/'", __func__); 123 124 if (setgroups(1, &pw->pw_gid) == -1 || 125 setgid(pw->pw_gid) == -1 || 126 setuid(pw->pw_uid) == -1) 127 { 128 logerr("failed to drop privileges"); 129 return -1; 130 } 131 132 struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 }; 133 134 if (ctx->ps_control_pid != getpid()) { 135 /* Prohibit new files, sockets, etc */ 136 #if defined(__linux__) || defined(__sun) || defined(__OpenBSD__) 137 /* 138 * If poll(2) is called with nfds > RLIMIT_NOFILE 139 * then it returns EINVAL. 140 * This blows. 141 * Do the best we can and limit to what we need. 142 * An attacker could potentially close a file and 143 * open a new one still, but that cannot be helped. 144 */ 145 unsigned long maxfd; 146 maxfd = (unsigned long)eloop_event_count(ctx->eloop); 147 if (IN_PRIVSEP_SE(ctx)) 148 maxfd++; /* XXX why? */ 149 150 struct rlimit rmaxfd = { 151 .rlim_cur = maxfd, 152 .rlim_max = maxfd 153 }; 154 if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1) 155 logerr("setrlimit RLIMIT_NOFILE"); 156 #else 157 if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) 158 logerr("setrlimit RLIMIT_NOFILE"); 159 #endif 160 } 161 162 /* Prohibit writing to files. 163 * Obviously this won't work if we are using a logfile 164 * or redirecting stderr to a file. */ 165 if (ctx->logfile == NULL && isatty(loggeterrfd())) { 166 if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) 167 logerr("setrlimit RLIMIT_FSIZE"); 168 } 169 170 #ifdef RLIMIT_NPROC 171 /* Prohibit forks */ 172 if (setrlimit(RLIMIT_NPROC, &rzero) == -1) 173 logerr("setrlimit RLIMIT_NPROC"); 174 #endif 175 176 return 0; 177 } 178 179 static int 180 ps_setbuf0(int fd, int ctl, int minlen) 181 { 182 int len; 183 socklen_t slen; 184 185 slen = sizeof(len); 186 if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1) 187 return -1; 188 189 #ifdef __linux__ 190 len /= 2; 191 #endif 192 if (len >= minlen) 193 return 0; 194 195 return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen)); 196 } 197 198 static int 199 ps_setbuf(int fd) 200 { 201 /* Ensure we can receive a fully sized privsep message. 202 * Double the send buffer. */ 203 int minlen = (int)sizeof(struct ps_msg); 204 205 if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 || 206 ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1) 207 { 208 logerr(__func__); 209 return -1; 210 } 211 return 0; 212 } 213 214 int 215 ps_setbuf_fdpair(int fd[]) 216 { 217 218 if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1) 219 return -1; 220 return 0; 221 } 222 223 #ifdef PRIVSEP_RIGHTS 224 int 225 ps_rights_limit_ioctl(int fd) 226 { 227 cap_rights_t rights; 228 229 cap_rights_init(&rights, CAP_IOCTL); 230 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 231 return -1; 232 return 0; 233 } 234 235 int 236 ps_rights_limit_fd_fctnl(int fd) 237 { 238 cap_rights_t rights; 239 240 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, 241 CAP_ACCEPT, CAP_FCNTL); 242 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 243 return -1; 244 return 0; 245 } 246 247 int 248 ps_rights_limit_fd(int fd) 249 { 250 cap_rights_t rights; 251 252 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN); 253 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 254 return -1; 255 return 0; 256 } 257 258 int 259 ps_rights_limit_fd_rdonly(int fd) 260 { 261 cap_rights_t rights; 262 263 cap_rights_init(&rights, CAP_READ, CAP_EVENT); 264 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) 265 return -1; 266 return 0; 267 } 268 269 int 270 ps_rights_limit_fdpair(int fd[]) 271 { 272 273 if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1) 274 return -1; 275 return 0; 276 } 277 #endif 278 279 pid_t 280 ps_dostart(struct dhcpcd_ctx *ctx, 281 pid_t *priv_pid, int *priv_fd, 282 void (*recv_msg)(void *), void (*recv_unpriv_msg), 283 void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *), 284 unsigned int flags) 285 { 286 int stype; 287 int fd[2]; 288 pid_t pid; 289 290 stype = SOCK_CLOEXEC | SOCK_NONBLOCK; 291 if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) { 292 logerr("%s: socketpair", __func__); 293 return -1; 294 } 295 if (ps_setbuf_fdpair(fd) == -1) { 296 logerr("%s: ps_setbuf_fdpair", __func__); 297 return -1; 298 } 299 #ifdef PRIVSEP_RIGHTS 300 if (ps_rights_limit_fdpair(fd) == -1) { 301 logerr("%s: ps_rights_limit_fdpair", __func__); 302 return -1; 303 } 304 #endif 305 306 switch (pid = fork()) { 307 case -1: 308 logerr("fork"); 309 return -1; 310 case 0: 311 *priv_fd = fd[1]; 312 close(fd[0]); 313 break; 314 default: 315 *priv_pid = pid; 316 *priv_fd = fd[0]; 317 close(fd[1]); 318 if (recv_unpriv_msg == NULL) 319 ; 320 else if (eloop_event_add(ctx->eloop, *priv_fd, 321 recv_unpriv_msg, recv_ctx) == -1) 322 { 323 logerr("%s: eloop_event_add", __func__); 324 return -1; 325 } 326 return pid; 327 } 328 329 ctx->options |= DHCPCD_UNPRIV | DHCPCD_FORKED; 330 if (ctx->fork_fd != -1) { 331 close(ctx->fork_fd); 332 ctx->fork_fd = -1; 333 } 334 pidfile_clean(); 335 eloop_clear(ctx->eloop); 336 337 /* We are not root */ 338 if (priv_fd != &ctx->ps_root_fd) { 339 ps_freeprocesses(ctx, recv_ctx); 340 if (ctx->ps_root_fd != -1) { 341 close(ctx->ps_root_fd); 342 ctx->ps_root_fd = -1; 343 } 344 } 345 346 if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) { 347 close(ctx->ps_inet_fd); 348 ctx->ps_inet_fd = -1; 349 } 350 351 eloop_signal_set_cb(ctx->eloop, 352 dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx); 353 354 /* ctx->sigset aready has the initial sigmask set in main() */ 355 if (eloop_signal_mask(ctx->eloop, NULL) == -1) { 356 logerr("%s: eloop_signal_mask", __func__); 357 goto errexit; 358 } 359 360 if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1) 361 { 362 logerr("%s: eloop_event_add", __func__); 363 goto errexit; 364 } 365 366 if (callback(recv_ctx) == -1) 367 goto errexit; 368 369 if (flags & PSF_DROPPRIVS) 370 ps_dropprivs(ctx); 371 372 return 0; 373 374 errexit: 375 /* Failure to start root or inet processes is fatal. */ 376 if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd) 377 (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0); 378 shutdown(*priv_fd, SHUT_RDWR); 379 *priv_fd = -1; 380 eloop_exit(ctx->eloop, EXIT_FAILURE); 381 return -1; 382 } 383 384 int 385 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd) 386 { 387 int err = 0; 388 389 #ifdef PRIVSEP_DEBUG 390 logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd); 391 #endif 392 393 if (*fd != -1) { 394 eloop_event_delete(ctx->eloop, *fd); 395 if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) { 396 logerr(__func__); 397 err = -1; 398 } 399 (void)shutdown(*fd, SHUT_RDWR); 400 close(*fd); 401 *fd = -1; 402 } 403 404 /* Don't wait for the process as it may not respond to the shutdown 405 * request. We'll reap the process on receipt of SIGCHLD. */ 406 *pid = 0; 407 return err; 408 } 409 410 int 411 ps_start(struct dhcpcd_ctx *ctx) 412 { 413 pid_t pid; 414 415 TAILQ_INIT(&ctx->ps_processes); 416 417 switch (pid = ps_root_start(ctx)) { 418 case -1: 419 logerr("ps_root_start"); 420 return -1; 421 case 0: 422 return 0; 423 default: 424 logdebugx("spawned privileged actioneer on PID %d", pid); 425 } 426 427 /* No point in spawning the generic network listener if we're 428 * not going to use it. */ 429 if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6))) 430 goto started_net; 431 432 switch (pid = ps_inet_start(ctx)) { 433 case -1: 434 if (errno == ENXIO) 435 return 0; 436 return -1; 437 case 0: 438 return 0; 439 default: 440 logdebugx("spawned network proxy on PID %d", pid); 441 } 442 443 started_net: 444 if (!(ctx->options & DHCPCD_TEST)) { 445 switch (pid = ps_ctl_start(ctx)) { 446 case -1: 447 return -1; 448 case 0: 449 return 0; 450 default: 451 logdebugx("spawned controller proxy on PID %d", pid); 452 } 453 } 454 455 #ifdef ARC4RANDOM_H 456 /* Seed the random number generator early incase it needs /dev/urandom 457 * which won't be available in the chroot. */ 458 arc4random(); 459 #endif 460 461 return 1; 462 } 463 464 int 465 ps_mastersandbox(struct dhcpcd_ctx *ctx) 466 { 467 468 if (ps_dropprivs(ctx) == -1) { 469 logerr("%s: ps_dropprivs", __func__); 470 return -1; 471 } 472 473 #ifdef PRIVSEP_RIGHTS 474 if ((ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1 || 475 ps_rights_limit_fd(ctx->link_fd) == -1) && 476 errno != ENOSYS) 477 { 478 logerr("%s: cap_rights_limit", __func__); 479 return -1; 480 } 481 #endif 482 #ifdef HAVE_CAPSICUM 483 if (cap_enter() == -1 && errno != ENOSYS) { 484 logerr("%s: cap_enter", __func__); 485 return -1; 486 } 487 #endif 488 #ifdef HAVE_PLEDGE 489 if (pledge("stdio route", NULL) == -1) { 490 logerr("%s: pledge", __func__); 491 return -1; 492 } 493 #endif 494 495 return 0; 496 } 497 498 int 499 ps_stop(struct dhcpcd_ctx *ctx) 500 { 501 int r, ret = 0; 502 503 if (!(ctx->options & DHCPCD_PRIVSEP) || 504 ctx->options & DHCPCD_FORKED || 505 ctx->eloop == NULL) 506 return 0; 507 508 r = ps_ctl_stop(ctx); 509 if (r != 0) 510 ret = r; 511 512 r = ps_inet_stop(ctx); 513 if (r != 0) 514 ret = r; 515 516 /* We've been chrooted, so we need to tell the 517 * privileged actioneer to remove the pidfile. */ 518 ps_root_unlink(ctx, ctx->pidfile); 519 520 r = ps_root_stop(ctx); 521 if (r != 0) 522 ret = r; 523 524 ctx->options &= ~DHCPCD_PRIVSEP; 525 return ret; 526 } 527 528 void 529 ps_freeprocess(struct ps_process *psp) 530 { 531 532 TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next); 533 if (psp->psp_fd != -1) { 534 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); 535 close(psp->psp_fd); 536 } 537 if (psp->psp_work_fd != -1) { 538 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd); 539 close(psp->psp_work_fd); 540 } 541 #ifdef INET 542 if (psp->psp_bpf != NULL) 543 bpf_close(psp->psp_bpf); 544 #endif 545 free(psp); 546 } 547 548 static void 549 ps_free(struct dhcpcd_ctx *ctx) 550 { 551 struct ps_process *psp; 552 bool stop = ctx->ps_root_pid == getpid(); 553 554 while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) { 555 if (stop) 556 ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); 557 ps_freeprocess(psp); 558 } 559 } 560 561 int 562 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm, 563 const void *data, size_t len) 564 { 565 uint8_t *datap, *namep, *controlp; 566 567 namep = UNCONST(data); 568 controlp = namep + psm->ps_namelen; 569 datap = controlp + psm->ps_controllen; 570 571 if (psm->ps_namelen != 0) { 572 if (psm->ps_namelen > len) { 573 errno = EINVAL; 574 return -1; 575 } 576 msg->msg_name = namep; 577 len -= psm->ps_namelen; 578 } else 579 msg->msg_name = NULL; 580 msg->msg_namelen = psm->ps_namelen; 581 582 if (psm->ps_controllen != 0) { 583 if (psm->ps_controllen > len) { 584 errno = EINVAL; 585 return -1; 586 } 587 msg->msg_control = controlp; 588 len -= psm->ps_controllen; 589 } else 590 msg->msg_control = NULL; 591 msg->msg_controllen = psm->ps_controllen; 592 593 if (len != 0) { 594 msg->msg_iovlen = 1; 595 msg->msg_iov[0].iov_base = datap; 596 msg->msg_iov[0].iov_len = len; 597 } else { 598 msg->msg_iovlen = 0; 599 msg->msg_iov[0].iov_base = NULL; 600 msg->msg_iov[0].iov_len = 0; 601 } 602 return 0; 603 } 604 605 ssize_t 606 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, 607 struct ps_msghdr *psm, const struct msghdr *msg) 608 { 609 struct iovec iov[] = { 610 { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) }, 611 { .iov_base = NULL, }, /* name */ 612 { .iov_base = NULL, }, /* control */ 613 { .iov_base = NULL, }, /* payload 1 */ 614 { .iov_base = NULL, }, /* payload 2 */ 615 { .iov_base = NULL, }, /* payload 3 */ 616 }; 617 int iovlen; 618 ssize_t len; 619 620 if (msg != NULL) { 621 struct iovec *iovp = &iov[1]; 622 int i; 623 624 psm->ps_namelen = msg->msg_namelen; 625 psm->ps_controllen = (socklen_t)msg->msg_controllen; 626 627 iovp->iov_base = msg->msg_name; 628 iovp->iov_len = msg->msg_namelen; 629 iovp++; 630 iovp->iov_base = msg->msg_control; 631 iovp->iov_len = msg->msg_controllen; 632 iovlen = 3; 633 634 for (i = 0; i < (int)msg->msg_iovlen; i++) { 635 if ((size_t)(iovlen + i) > __arraycount(iov)) { 636 errno = ENOBUFS; 637 return -1; 638 } 639 iovp++; 640 iovp->iov_base = msg->msg_iov[i].iov_base; 641 iovp->iov_len = msg->msg_iov[i].iov_len; 642 } 643 iovlen += i; 644 } else 645 iovlen = 1; 646 647 len = writev(fd, iov, iovlen); 648 #ifdef PRIVSEP_DEBUG 649 logdebugx("%s: %zd", __func__, len); 650 #endif 651 if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED && 652 !(ctx->options & DHCPCD_PRIVSEPROOT)) 653 eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 654 return len; 655 } 656 657 ssize_t 658 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd, 659 struct ps_msghdr *psm, const void *data, size_t len) 660 { 661 struct iovec iov[] = { 662 { .iov_base = UNCONST(data), .iov_len = len }, 663 }; 664 struct msghdr msg = { 665 .msg_iov = iov, .msg_iovlen = 1, 666 }; 667 668 return ps_sendpsmmsg(ctx, fd, psm, &msg); 669 } 670 671 672 ssize_t 673 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 674 const struct msghdr *msg) 675 { 676 struct ps_msghdr psm = { 677 .ps_cmd = cmd, 678 .ps_flags = flags, 679 .ps_namelen = msg->msg_namelen, 680 .ps_controllen = (socklen_t)msg->msg_controllen, 681 }; 682 size_t i; 683 684 for (i = 0; i < (size_t)msg->msg_iovlen; i++) 685 psm.ps_datalen += msg->msg_iov[i].iov_len; 686 687 #if 0 /* For debugging structure padding. */ 688 logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family)); 689 logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad)); 690 logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u)); 691 logerrx("psa %zu", sizeof(psm.ps_id.psi_addr)); 692 693 logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr)); 694 logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex)); 695 logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd)); 696 logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad)); 697 logerrx("psi %zu", sizeof(struct ps_id)); 698 699 logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd)); 700 logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad)); 701 logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags)); 702 703 logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id)); 704 705 logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen)); 706 logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen)); 707 logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2)); 708 logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen)); 709 logerrx("psm %zu", sizeof(psm)); 710 #endif 711 712 return ps_sendpsmmsg(ctx, fd, &psm, msg); 713 } 714 715 ssize_t 716 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags, 717 const void *data, size_t len) 718 { 719 struct ps_msghdr psm = { 720 .ps_cmd = cmd, 721 .ps_flags = flags, 722 }; 723 struct iovec iov[] = { 724 { .iov_base = UNCONST(data), .iov_len = len } 725 }; 726 struct msghdr msg = { 727 .msg_iov = iov, .msg_iovlen = 1, 728 }; 729 730 return ps_sendpsmmsg(ctx, fd, &psm, &msg); 731 } 732 733 static ssize_t 734 ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg) 735 { 736 struct ps_msghdr psm = { .ps_cmd = cmd }; 737 uint8_t data[PS_BUFLEN], *p = data; 738 struct iovec iov[] = { 739 { .iov_base = &psm, .iov_len = sizeof(psm) }, 740 { .iov_base = data, .iov_len = 0 }, 741 }; 742 size_t dl = sizeof(data); 743 744 if (msg->msg_namelen != 0) { 745 if (msg->msg_namelen > dl) 746 goto nobufs; 747 psm.ps_namelen = msg->msg_namelen; 748 memcpy(p, msg->msg_name, msg->msg_namelen); 749 p += msg->msg_namelen; 750 dl -= msg->msg_namelen; 751 } 752 753 if (msg->msg_controllen != 0) { 754 if (msg->msg_controllen > dl) 755 goto nobufs; 756 psm.ps_controllen = (socklen_t)msg->msg_controllen; 757 memcpy(p, msg->msg_control, msg->msg_controllen); 758 p += msg->msg_controllen; 759 dl -= msg->msg_controllen; 760 } 761 762 psm.ps_datalen = msg->msg_iov[0].iov_len; 763 if (psm.ps_datalen > dl) 764 goto nobufs; 765 766 iov[1].iov_len = psm.ps_namelen + psm.ps_controllen + psm.ps_datalen; 767 if (psm.ps_datalen != 0) 768 memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen); 769 return writev(fd, iov, __arraycount(iov)); 770 771 nobufs: 772 errno = ENOBUFS; 773 return -1; 774 } 775 776 ssize_t 777 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd) 778 { 779 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC }; 780 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 }; 781 uint8_t databuf[64 * 1024]; 782 struct iovec iov[] = { 783 { .iov_base = databuf, .iov_len = sizeof(databuf) } 784 }; 785 struct msghdr msg = { 786 .msg_name = &ss, .msg_namelen = sizeof(ss), 787 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf), 788 .msg_iov = iov, .msg_iovlen = 1, 789 }; 790 791 ssize_t len = recvmsg(rfd, &msg, 0); 792 #ifdef PRIVSEP_DEBUG 793 logdebugx("%s: recv fd %d, %zd bytes", __func__, rfd, len); 794 #endif 795 796 if (len == -1 || len == 0) { 797 if (ctx->options & DHCPCD_FORKED && 798 !(ctx->options & DHCPCD_PRIVSEPROOT)) 799 eloop_exit(ctx->eloop, 800 len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 801 return len; 802 } 803 804 iov[0].iov_len = (size_t)len; 805 len = ps_sendcmdmsg(wfd, cmd, &msg); 806 #ifdef PRIVSEP_DEBUG 807 logdebugx("%s: send fd %d, %zu bytes", __func__, wfd, len); 808 #endif 809 if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED && 810 !(ctx->options & DHCPCD_PRIVSEPROOT)) 811 eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 812 return len; 813 } 814 815 ssize_t 816 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, 817 ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), 818 void *cbctx) 819 { 820 struct ps_msg psm; 821 ssize_t len; 822 size_t dlen; 823 struct iovec iov[1]; 824 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 }; 825 bool stop = false; 826 827 len = read(fd, &psm, sizeof(psm)); 828 #ifdef PRIVSEP_DEBUG 829 logdebugx("%s: %zd", __func__, len); 830 #endif 831 832 if (len == -1 || len == 0) 833 stop = true; 834 else { 835 dlen = (size_t)len; 836 if (dlen < sizeof(psm.psm_hdr)) { 837 errno = EINVAL; 838 return -1; 839 } 840 841 if (psm.psm_hdr.ps_cmd == PS_STOP) { 842 stop = true; 843 len = 0; 844 } 845 } 846 847 if (stop) { 848 #ifdef PRIVSEP_DEBUG 849 logdebugx("process %d stopping", getpid()); 850 #endif 851 ps_free(ctx); 852 #ifdef PLUGIN_DEV 853 dev_stop(ctx); 854 #endif 855 eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE); 856 return len; 857 } 858 dlen -= sizeof(psm.psm_hdr); 859 860 if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1) 861 return -1; 862 863 if (callback == NULL) 864 return 0; 865 866 errno = 0; 867 return callback(cbctx, &psm.psm_hdr, &msg); 868 } 869 870 struct ps_process * 871 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 872 { 873 struct ps_process *psp; 874 875 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 876 if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0) 877 return psp; 878 } 879 errno = ESRCH; 880 return NULL; 881 } 882 883 struct ps_process * 884 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 885 { 886 struct ps_process *psp; 887 888 psp = calloc(1, sizeof(*psp)); 889 if (psp == NULL) 890 return NULL; 891 psp->psp_ctx = ctx; 892 memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); 893 psp->psp_work_fd = -1; 894 TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next); 895 return psp; 896 } 897 898 void 899 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis) 900 { 901 struct ps_process *psp, *psn; 902 903 TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) { 904 if (psp == notthis) 905 continue; 906 ps_freeprocess(psp); 907 } 908 } 909