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/socket.h> 43 #include <sys/stat.h> 44 #include <sys/types.h> 45 #include <sys/wait.h> 46 47 #ifdef AF_LINK 48 #include <net/if_dl.h> 49 #endif 50 51 #include <assert.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <grp.h> 55 #include <paths.h> 56 #include <pwd.h> 57 #include <stddef.h> /* For offsetof, struct padding debug */ 58 #include <signal.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 63 #include "arp.h" 64 #include "common.h" 65 #include "control.h" 66 #include "dhcp.h" 67 #include "dhcp6.h" 68 #include "eloop.h" 69 #include "ipv6nd.h" 70 #include "logerr.h" 71 #include "privsep.h" 72 73 #ifdef HAVE_UTIL_H 74 #include <util.h> 75 #endif 76 77 int 78 ps_mkdir(char *path) 79 { 80 char *slash; 81 bool done; 82 83 slash = path; 84 for (;;) { 85 slash += strspn(slash, "/"); 86 slash += strcspn(slash, "/"); 87 done = (*slash == '\0'); 88 *slash = '\0'; 89 if (mkdir(path, 0755) == -1 && errno != EEXIST) 90 return -1; 91 if (done) 92 break; 93 *slash = '/'; 94 } 95 return 0; 96 } 97 98 int 99 ps_init(struct dhcpcd_ctx *ctx) 100 { 101 char path[PATH_MAX]; 102 struct passwd *pw = ctx->ps_user; 103 104 errno = 0; 105 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) { 106 ctx->options &= ~DHCPCD_PRIVSEP; 107 if (errno == 0) { 108 logerrx("no such user %s", PRIVSEP_USER); 109 /* Just incase logerrx caused an error... */ 110 errno = 0; 111 } else 112 logerr("getpwnam"); 113 return -1; 114 } 115 116 /* If we pickup the _dhcp user refuse the default directory */ 117 if (strcmp(pw->pw_dir, "/var/empty") == 0) { 118 ctx->options &= ~DHCPCD_PRIVSEP; 119 logerrx("refusing chroot: %s: %s", PRIVSEP_USER, pw->pw_dir); 120 errno = 0; 121 return -1; 122 } 123 124 /* Create the database directory. */ 125 if (snprintf(path, sizeof(path), "%s%s", pw->pw_dir, DBDIR) == -1 || 126 ps_mkdir(path) == -1 || 127 chown(path, pw->pw_uid, pw->pw_gid) == -1 || 128 chmod(path, 0755) == -1) 129 logerr("%s: %s", __func__, path); 130 131 /* Ensure we have a localtime to correctly format dates. */ 132 if (ps_root_docopychroot(ctx, "/etc/localtime") == -1 && errno!=ENOENT) 133 logerr("%s: %s", __func__, "/etc/localtime"); 134 135 ctx->options |= DHCPCD_PRIVSEP; 136 return 0; 137 } 138 139 int 140 ps_dropprivs(struct dhcpcd_ctx *ctx) 141 { 142 struct passwd *pw = ctx->ps_user; 143 144 if (!(ctx->options & DHCPCD_FORKED)) 145 logdebugx("chrooting to `%s'", pw->pw_dir); 146 147 if (chroot(pw->pw_dir) == -1) 148 logerr("%s: chroot `%s'", __func__, pw->pw_dir); 149 if (chdir("/") == -1) 150 logerr("%s: chdir `/'", __func__); 151 152 if (setgroups(1, &pw->pw_gid) == -1 || 153 setgid(pw->pw_gid) == -1 || 154 setuid(pw->pw_uid) == -1) 155 { 156 logerr("failed to drop privileges"); 157 return -1; 158 } 159 160 return 0; 161 } 162 163 pid_t 164 ps_dostart(struct dhcpcd_ctx *ctx, 165 pid_t *priv_pid, int *priv_fd, 166 void (*recv_msg)(void *), void (*recv_unpriv_msg), 167 void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *), 168 unsigned int flags) 169 { 170 int stype; 171 int fd[2]; 172 pid_t pid; 173 174 stype = SOCK_CLOEXEC | SOCK_NONBLOCK; 175 if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) { 176 logerr("socketpair"); 177 return -1; 178 } 179 180 switch (pid = fork()) { 181 case -1: 182 logerr("fork"); 183 return -1; 184 case 0: 185 *priv_fd = fd[1]; 186 close(fd[0]); 187 break; 188 default: 189 *priv_pid = pid; 190 *priv_fd = fd[0]; 191 close(fd[1]); 192 if (recv_unpriv_msg != NULL && 193 eloop_event_add(ctx->eloop, *priv_fd, 194 recv_unpriv_msg, recv_ctx) == -1) 195 { 196 logerr("%s: eloop_event_add", __func__); 197 return -1; 198 } 199 return pid; 200 } 201 202 ctx->options |= DHCPCD_UNPRIV | DHCPCD_FORKED; 203 if (ctx->fork_fd != -1) { 204 close(ctx->fork_fd); 205 ctx->fork_fd = -1; 206 } 207 pidfile_clean(); 208 eloop_clear(ctx->eloop); 209 210 /* We are not root */ 211 if (priv_fd != &ctx->ps_root_fd) { 212 ps_freeprocesses(ctx, recv_ctx); 213 if (ctx->ps_root_fd != -1) { 214 close(ctx->ps_root_fd); 215 ctx->ps_root_fd = -1; 216 } 217 } 218 219 if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) { 220 close(ctx->ps_inet_fd); 221 ctx->ps_inet_fd = -1; 222 } 223 224 if (eloop_signal_set_cb(ctx->eloop, 225 dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx) == -1) 226 { 227 logerr("%s: eloop_signal_set_cb", __func__); 228 goto errexit; 229 } 230 231 /* ctx->sigset aready has the initial sigmask set in main() */ 232 if (eloop_signal_mask(ctx->eloop, NULL) == -1) { 233 logerr("%s: eloop_signal_mask", __func__); 234 goto errexit; 235 } 236 237 if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1) 238 { 239 logerr("%s: eloop_event_add", __func__); 240 goto errexit; 241 } 242 243 if (callback(recv_ctx) == -1) 244 goto errexit; 245 246 if (!(ctx->options & DHCPCD_DEBUG) && 247 (!(ctx->options & DHCPCD_TEST) || loggetopts() & LOGERR_QUIET)) 248 { 249 freopen(_PATH_DEVNULL, "w", stdout); 250 freopen(_PATH_DEVNULL, "w", stderr); 251 } 252 253 if (flags & PSF_DROPPRIVS) 254 ps_dropprivs(ctx); 255 256 return 0; 257 258 errexit: 259 /* Failure to start root or inet processes is fatal. */ 260 if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd) 261 ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0); 262 shutdown(*priv_fd, SHUT_RDWR); 263 *priv_fd = -1; 264 return -1; 265 } 266 267 int 268 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd) 269 { 270 int status; 271 272 #ifdef PRIVSEP_DEBUG 273 logdebugx("%s: pid %d fd %d", __func__, *pid, *fd); 274 #endif 275 if (*pid == 0) 276 return 0; 277 eloop_event_delete(ctx->eloop, *fd); 278 if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1 && 279 errno != ECONNRESET) 280 logerr(__func__); 281 if (shutdown(*fd, SHUT_RDWR) == -1 && errno != ENOTCONN) 282 logerr(__func__); 283 close(*fd); 284 *fd = -1; 285 /* We won't have permission for all processes .... */ 286 #if 0 287 if (kill(*pid, SIGTERM) == -1) 288 logerr(__func__); 289 #endif 290 status = 0; 291 /* Wait for the process to finish */ 292 while (waitpid(*pid, &status, 0) == -1) { 293 if (errno != EINTR) { 294 logerr("%s: waitpid", __func__); 295 status = 0; 296 break; 297 } 298 #ifdef PRIVSEP_DEBUG 299 else 300 logerr("%s: waitpid ", __func__); 301 #endif 302 } 303 *pid = 0; 304 305 #ifdef PRIVSEP_DEBUG 306 logdebugx("%s: status %d", __func__, status); 307 #endif 308 309 return status; 310 } 311 312 int 313 ps_start(struct dhcpcd_ctx *ctx) 314 { 315 pid_t pid; 316 317 TAILQ_INIT(&ctx->ps_processes); 318 319 switch (pid = ps_root_start(ctx)) { 320 case -1: 321 return -1; 322 case 0: 323 return 0; 324 default: 325 logdebugx("spawned privileged actioneer on PID %d", pid); 326 } 327 328 /* No point in spawning the generic network listener if we're 329 * not going to use it. */ 330 if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6RS))) 331 goto started; 332 333 switch (pid = ps_inet_start(ctx)) { 334 case -1: 335 if (errno == ENXIO) 336 return 0; 337 return -1; 338 case 0: 339 return 0; 340 default: 341 logdebugx("spawned network proxy on PID %d", pid); 342 } 343 344 started: 345 return 1; 346 } 347 348 int 349 ps_stop(struct dhcpcd_ctx *ctx) 350 { 351 int r, ret = 0; 352 353 if (!(ctx->options & DHCPCD_PRIVSEP) || 354 ctx->options & DHCPCD_FORKED || 355 ctx->eloop == NULL) 356 return 0; 357 358 r = ps_inet_stop(ctx); 359 if (r != 0) 360 ret = r; 361 362 /* We've been chrooted, so we need to tell the 363 * privileged actioneer to remove the pidfile. */ 364 ps_root_unlink(ctx, ctx->pidfile); 365 366 r = ps_root_stop(ctx); 367 if (r != 0) 368 ret = r; 369 370 ctx->options &= ~DHCPCD_PRIVSEP; 371 return ret; 372 } 373 374 void 375 ps_freeprocess(struct ps_process *psp) 376 { 377 #ifdef INET 378 struct ipv4_state *istate = IPV4_STATE(&psp->psp_ifp); 379 380 if (istate != NULL) { 381 free(istate->buffer); 382 free(istate); 383 } 384 #endif 385 386 TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next); 387 if (psp->psp_fd != -1) { 388 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); 389 close(psp->psp_fd); 390 } 391 if (psp->psp_work_fd != -1) { 392 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd); 393 close(psp->psp_work_fd); 394 } 395 free(psp); 396 } 397 398 static void 399 ps_free(struct dhcpcd_ctx *ctx) 400 { 401 struct ps_process *psp; 402 bool stop = ctx->ps_root_pid == getpid(); 403 404 while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) { 405 if (stop) 406 ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); 407 ps_freeprocess(psp); 408 } 409 } 410 411 int 412 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm, 413 const void *data, size_t len) 414 { 415 uint8_t *datap, *namep, *controlp; 416 417 namep = UNCONST(data); 418 controlp = namep + psm->ps_namelen; 419 datap = controlp + psm->ps_controllen; 420 421 if (psm->ps_namelen != 0) { 422 if (psm->ps_namelen > len) { 423 errno = EINVAL; 424 return -1; 425 } 426 msg->msg_name = namep; 427 len -= psm->ps_namelen; 428 } else 429 msg->msg_name = NULL; 430 msg->msg_namelen = psm->ps_namelen; 431 432 if (psm->ps_controllen != 0) { 433 if (psm->ps_controllen > len) { 434 errno = EINVAL; 435 return -1; 436 } 437 msg->msg_control = controlp; 438 len -= psm->ps_controllen; 439 } else 440 msg->msg_control = NULL; 441 msg->msg_controllen = psm->ps_controllen; 442 443 if (len != 0) { 444 msg->msg_iovlen = 1; 445 msg->msg_iov[0].iov_base = datap; 446 msg->msg_iov[0].iov_len = len; 447 } else { 448 msg->msg_iovlen = 0; 449 msg->msg_iov[0].iov_base = NULL; 450 msg->msg_iov[0].iov_len = 0; 451 } 452 return 0; 453 } 454 455 456 ssize_t 457 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, 458 struct ps_msghdr *psm, const struct msghdr *msg) 459 { 460 assert(msg == NULL || msg->msg_iovlen == 1); 461 462 struct iovec iov[] = { 463 { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) }, 464 { .iov_base = NULL, }, /* name */ 465 { .iov_base = NULL, }, /* control */ 466 { .iov_base = NULL, }, /* payload */ 467 }; 468 int iovlen = __arraycount(iov); 469 ssize_t len; 470 471 if (msg != NULL) { 472 struct iovec *iovp = &iov[1]; 473 474 assert(msg->msg_iovlen == 1); 475 476 psm->ps_namelen = msg->msg_namelen; 477 psm->ps_controllen = (socklen_t)msg->msg_controllen; 478 479 iovp->iov_base = msg->msg_name; 480 iovp->iov_len = msg->msg_namelen; 481 iovp++; 482 iovp->iov_base = msg->msg_control; 483 iovp->iov_len = msg->msg_controllen; 484 iovp++; 485 iovp->iov_base = msg->msg_iov[0].iov_base; 486 iovp->iov_len = msg->msg_iov[0].iov_len; 487 iovlen = __arraycount(iov); 488 } else 489 iovlen = 1; 490 491 len = writev(fd, iov, iovlen); 492 #ifdef PRIVSEP_DEBUG 493 logdebugx("%s: %zd", __func__, len); 494 #endif 495 if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) 496 eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 497 return len; 498 } 499 500 ssize_t 501 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd, 502 struct ps_msghdr *psm, const void *data, size_t len) 503 { 504 struct iovec iov[] = { 505 { .iov_base = UNCONST(data), .iov_len = len }, 506 }; 507 struct msghdr msg = { 508 .msg_iov = iov, .msg_iovlen = 1, 509 }; 510 511 return ps_sendpsmmsg(ctx, fd, psm, &msg); 512 } 513 514 515 ssize_t 516 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags, 517 const struct msghdr *msg) 518 { 519 assert(msg->msg_iovlen == 1); 520 521 struct ps_msghdr psm = { 522 .ps_cmd = cmd, 523 .ps_flags = flags, 524 .ps_namelen = msg->msg_namelen, 525 .ps_controllen = (socklen_t)msg->msg_controllen, 526 .ps_datalen = msg->msg_iov[0].iov_len, 527 }; 528 529 #if 0 /* For debugging structure padding. */ 530 logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family)); 531 logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad)); 532 logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u)); 533 logerrx("psa %zu", sizeof(psm.ps_id.psi_addr)); 534 535 logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr)); 536 logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex)); 537 logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd)); 538 logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad)); 539 logerrx("psi %zu", sizeof(struct ps_id)); 540 541 logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd)); 542 logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad)); 543 logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags)); 544 545 logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id)); 546 547 logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen)); 548 logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen)); 549 logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2)); 550 logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen)); 551 logerrx("psm %zu", sizeof(psm)); 552 #endif 553 554 return ps_sendpsmmsg(ctx, fd, &psm, msg); 555 } 556 557 ssize_t 558 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags, 559 const void *data, size_t len) 560 { 561 struct ps_msghdr psm = { 562 .ps_cmd = cmd, 563 .ps_flags = flags, 564 }; 565 struct iovec iov[] = { 566 { .iov_base = UNCONST(data), .iov_len = len } 567 }; 568 struct msghdr msg = { 569 .msg_iov = iov, .msg_iovlen = 1, 570 }; 571 572 return ps_sendpsmmsg(ctx, fd, &psm, &msg); 573 } 574 575 static ssize_t 576 ps_sendcmdmsg(int fd, uint8_t cmd, const struct msghdr *msg) 577 { 578 struct ps_msghdr psm = { .ps_cmd = cmd }; 579 uint8_t data[PS_BUFLEN], *p = data; 580 struct iovec iov[] = { 581 { .iov_base = &psm, .iov_len = sizeof(psm) }, 582 { .iov_base = data, .iov_len = 0 }, 583 }; 584 size_t dl = sizeof(data); 585 586 if (msg->msg_namelen != 0) { 587 if (msg->msg_namelen > dl) 588 goto nobufs; 589 psm.ps_namelen = msg->msg_namelen; 590 memcpy(p, msg->msg_name, msg->msg_namelen); 591 p += msg->msg_namelen; 592 dl -= msg->msg_namelen; 593 } 594 595 if (msg->msg_controllen != 0) { 596 if (msg->msg_controllen > dl) 597 goto nobufs; 598 psm.ps_controllen = (socklen_t)msg->msg_controllen; 599 memcpy(p, msg->msg_control, msg->msg_controllen); 600 p += msg->msg_controllen; 601 dl -= msg->msg_controllen; 602 } 603 604 psm.ps_datalen = msg->msg_iov[0].iov_len; 605 if (psm.ps_datalen > dl) 606 goto nobufs; 607 608 iov[1].iov_len = psm.ps_namelen + psm.ps_controllen + psm.ps_datalen; 609 if (psm.ps_datalen != 0) 610 memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen); 611 return writev(fd, iov, __arraycount(iov)); 612 613 nobufs: 614 errno = ENOBUFS; 615 return -1; 616 } 617 618 ssize_t 619 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint8_t cmd, int wfd) 620 { 621 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC }; 622 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 }; 623 uint8_t databuf[64 * 1024]; 624 struct iovec iov[] = { 625 { .iov_base = databuf, .iov_len = sizeof(databuf) } 626 }; 627 struct msghdr msg = { 628 .msg_name = &ss, .msg_namelen = sizeof(ss), 629 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf), 630 .msg_iov = iov, .msg_iovlen = 1, 631 }; 632 633 ssize_t len = recvmsg(rfd, &msg, 0); 634 #ifdef PRIVSEP_DEBUG 635 logdebugx("%s: recv fd %d, %zd bytes", __func__, rfd, len); 636 #endif 637 if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) { 638 eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 639 return len; 640 } 641 642 iov[0].iov_len = (size_t)len; 643 len = ps_sendcmdmsg(wfd, cmd, &msg); 644 #ifdef PRIVSEP_DEBUG 645 logdebugx("%s: send fd %d, %zu bytes", __func__, wfd, len); 646 #endif 647 if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) 648 eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 649 return len; 650 } 651 652 ssize_t 653 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, 654 ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), 655 void *cbctx) 656 { 657 struct ps_msg psm; 658 ssize_t len; 659 size_t dlen; 660 struct iovec iov[1]; 661 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 }; 662 bool stop = false; 663 664 len = read(fd, &psm, sizeof(psm)); 665 #ifdef PRIVSEP_DEBUG 666 logdebugx("%s: %zd", __func__, len); 667 #endif 668 669 if (len == -1 && (errno == ECONNRESET || errno == EBADF)) 670 len = 0; 671 if (len == -1 || len == 0) 672 stop = true; 673 else { 674 dlen = (size_t)len; 675 if (dlen < sizeof(psm.psm_hdr)) { 676 errno = EINVAL; 677 return -1; 678 } 679 680 if (psm.psm_hdr.ps_cmd == PS_STOP) { 681 stop = true; 682 len = 0; 683 } 684 } 685 686 if (stop) { 687 #ifdef PRIVSEP_DEBUG 688 logdebugx("process %d stopping", getpid()); 689 #endif 690 ps_free(ctx); 691 eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE); 692 return len; 693 } 694 dlen -= sizeof(psm.psm_hdr); 695 696 if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1) 697 return -1; 698 699 errno = 0; 700 return callback(cbctx, &psm.psm_hdr, &msg); 701 } 702 703 struct ps_process * 704 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 705 { 706 struct ps_process *psp; 707 708 TAILQ_FOREACH(psp, &ctx->ps_processes, next) { 709 if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0) 710 return psp; 711 } 712 errno = ESRCH; 713 return NULL; 714 } 715 716 struct ps_process * 717 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) 718 { 719 struct ps_process *psp; 720 721 psp = calloc(1, sizeof(*psp)); 722 if (psp == NULL) 723 return NULL; 724 psp->psp_ctx = ctx; 725 memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); 726 psp->psp_work_fd = -1; 727 TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next); 728 return psp; 729 } 730 731 void 732 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis) 733 { 734 struct ps_process *psp, *psn; 735 736 TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) { 737 if (psp == notthis) 738 continue; 739 ps_freeprocess(psp); 740 } 741 } 742