1 /* $OpenBSD: privsep.c,v 1.71 2019/07/05 13:23:27 bluhm Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> 5 * Copyright (c) 2016 Alexander Bluhm <bluhm@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/queue.h> 21 #include <sys/stat.h> 22 #include <sys/wait.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <limits.h> 28 #include <netdb.h> 29 #include <paths.h> 30 #include <pwd.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <utmp.h> 37 38 #include "log.h" 39 #include "syslogd.h" 40 41 /* 42 * syslogd can only go forward in these states; each state should represent 43 * less privilege. After STATE_INIT, the child is allowed to parse its 44 * config file once, and communicate the information regarding what logfiles 45 * it needs access to back to the parent. When that is done, it sends a 46 * message to the priv parent revoking this access, moving to STATE_RUNNING. 47 * In this state, any log-files not in the access list are rejected. 48 * 49 * This allows a HUP signal to the child to reopen its log files, and 50 * the config file to be parsed if it hasn't been changed (this is still 51 * useful to force resolution of remote syslog servers again). 52 * If the config file has been modified, then the child dies, and 53 * the priv parent restarts itself. 54 */ 55 enum priv_state { 56 STATE_INIT, /* just started up */ 57 STATE_CONFIG, /* parsing config file for first time */ 58 STATE_RUNNING, /* running and accepting network traffic */ 59 STATE_QUIT /* shutting down */ 60 }; 61 62 enum cmd_types { 63 PRIV_OPEN_TTY, /* open terminal or console device */ 64 PRIV_OPEN_LOG, /* open logfile for appending */ 65 PRIV_OPEN_PIPE, /* fork & exec child that gets logs on stdin */ 66 PRIV_OPEN_UTMP, /* open utmp for reading only */ 67 PRIV_OPEN_CONFIG, /* open config file for reading only */ 68 PRIV_CONFIG_MODIFIED, /* check if config file has been modified */ 69 PRIV_GETADDRINFO, /* resolve host/service names */ 70 PRIV_GETNAMEINFO, /* resolve numeric address into hostname */ 71 PRIV_DONE_CONFIG_PARSE /* signal that initial config parse is done */ 72 }; 73 74 static int priv_fd = -1; 75 static volatile pid_t child_pid = -1; 76 static volatile sig_atomic_t cur_state = STATE_INIT; 77 78 /* Queue for the allowed logfiles */ 79 struct logname { 80 char path[PATH_MAX]; 81 TAILQ_ENTRY(logname) next; 82 }; 83 static TAILQ_HEAD(, logname) lognames; 84 85 static void check_log_name(char *, size_t); 86 static int open_file(char *); 87 static int open_pipe(char *); 88 static void check_tty_name(char *, size_t); 89 static void increase_state(int); 90 static void sig_pass_to_chld(int); 91 static void sig_got_chld(int); 92 static void must_read(int, void *, size_t); 93 static void must_write(int, void *, size_t); 94 static int may_read(int, void *, size_t); 95 96 static struct passwd *pw; 97 98 void 99 priv_init(int lockfd, int nullfd, int argc, char *argv[]) 100 { 101 int i, socks[2]; 102 char *execpath, childnum[11], **privargv; 103 104 /* Create sockets */ 105 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 106 err(1, "socketpair() failed"); 107 108 pw = getpwnam("_syslogd"); 109 if (pw == NULL) 110 errx(1, "unknown user _syslogd"); 111 112 child_pid = fork(); 113 if (child_pid == -1) 114 err(1, "fork() failed"); 115 116 if (!child_pid) { 117 /* Child - drop privileges and return */ 118 if (chroot(pw->pw_dir) != 0) 119 err(1, "chroot %s", pw->pw_dir); 120 if (chdir("/") != 0) 121 err(1, "chdir %s", pw->pw_dir); 122 123 if (setgroups(1, &pw->pw_gid) == -1) 124 err(1, "setgroups() failed"); 125 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) 126 err(1, "setresgid() failed"); 127 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 128 err(1, "setresuid() failed"); 129 close(socks[0]); 130 priv_fd = socks[1]; 131 return; 132 } 133 close(socks[1]); 134 135 if (strchr(argv[0], '/') == NULL) 136 execpath = argv[0]; 137 else if ((execpath = realpath(argv[0], NULL)) == NULL) 138 err(1, "realpath %s", argv[0]); 139 if (chdir("/") != 0) 140 err(1, "chdir /"); 141 142 if (!Debug) { 143 close(lockfd); 144 dup2(nullfd, STDIN_FILENO); 145 dup2(nullfd, STDOUT_FILENO); 146 dup2(nullfd, STDERR_FILENO); 147 } 148 if (nullfd > 2) 149 close(nullfd); 150 151 if (dup3(socks[0], 3, 0) == -1) 152 err(1, "dup3 priv sock failed"); 153 if (closefrom(4) == -1) 154 err(1, "closefrom 4 failed"); 155 156 snprintf(childnum, sizeof(childnum), "%d", child_pid); 157 if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL) 158 err(1, "alloc priv argv failed"); 159 privargv[0] = execpath; 160 for (i = 1; i < argc; i++) 161 privargv[i] = argv[i]; 162 privargv[i++] = "-P"; 163 privargv[i++] = childnum; 164 privargv[i++] = NULL; 165 execvp(privargv[0], privargv); 166 err(1, "exec priv '%s' failed", privargv[0]); 167 } 168 169 __dead void 170 priv_exec(char *conf, int numeric, int child, int argc, char *argv[]) 171 { 172 int i, fd, sock, cmd, addr_len, result, restart; 173 size_t path_len, protoname_len, hostname_len, servname_len; 174 char path[PATH_MAX], protoname[5]; 175 char hostname[NI_MAXHOST], servname[NI_MAXSERV]; 176 struct sockaddr_storage addr; 177 struct stat cf_info, cf_stat; 178 struct addrinfo hints, *res0; 179 struct sigaction sa; 180 sigset_t sigmask; 181 182 /* Redo the password lookup after re-exec of the privsep parent. */ 183 pw = getpwnam("_syslogd"); 184 if (pw == NULL) 185 errx(1, "unknown user _syslogd"); 186 187 if (unveil(conf, "r") == -1) 188 err(1, "unveil"); 189 if (unveil(_PATH_UTMP, "r") == -1) 190 err(1, "unveil"); 191 if (unveil(_PATH_DEV, "rw") == -1) 192 err(1, "unveil"); 193 if (unveil(_PATH_LOGPID, "c") == -1) 194 err(1, "unveil"); 195 196 /* for pipes */ 197 if (unveil(_PATH_BSHELL, "x") == -1) 198 err(1, "unveil"); 199 200 /* For HUP / re-exec */ 201 if (unveil("/usr/sbin/syslogd", "x") == -1) 202 err(1, "unveil"); 203 if (argv[0][0] == '/') 204 if (unveil(argv[0], "x") == -1) 205 err(1, "unveil"); 206 207 if (pledge("stdio unveil rpath wpath cpath dns sendfd id proc exec", 208 NULL) == -1) 209 err(1, "pledge priv"); 210 211 if (argc <= 2 || strcmp("-P", argv[argc - 2]) != 0) 212 errx(1, "exec without priv"); 213 argv[argc -= 2] = NULL; 214 215 sock = 3; 216 closefrom(4); 217 218 child_pid = child; 219 220 memset(&sa, 0, sizeof(sa)); 221 sigemptyset(&sa.sa_mask); 222 sa.sa_flags = SA_RESTART; 223 sa.sa_handler = SIG_DFL; 224 for (i = 1; i < _NSIG; i++) 225 sigaction(i, &sa, NULL); 226 227 /* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */ 228 sa.sa_handler = sig_pass_to_chld; 229 sigaction(SIGTERM, &sa, NULL); 230 sigaction(SIGHUP, &sa, NULL); 231 sigaction(SIGINT, &sa, NULL); 232 sigaction(SIGQUIT, &sa, NULL); 233 sa.sa_handler = sig_got_chld; 234 sa.sa_flags |= SA_NOCLDSTOP; 235 sigaction(SIGCHLD, &sa, NULL); 236 237 setproctitle("[priv]"); 238 log_debug("[priv]: fork+exec done"); 239 240 sigemptyset(&sigmask); 241 if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) 242 err(1, "sigprocmask priv"); 243 244 if (stat(conf, &cf_info) == -1) 245 err(1, "stat config file failed"); 246 247 TAILQ_INIT(&lognames); 248 increase_state(STATE_CONFIG); 249 restart = 0; 250 251 while (cur_state < STATE_QUIT) { 252 if (may_read(sock, &cmd, sizeof(int))) 253 break; 254 switch (cmd) { 255 case PRIV_OPEN_TTY: 256 log_debug("[priv]: msg PRIV_OPEN_TTY received"); 257 /* Expecting: length, path */ 258 must_read(sock, &path_len, sizeof(size_t)); 259 if (path_len == 0 || path_len > sizeof(path)) 260 _exit(1); 261 must_read(sock, &path, path_len); 262 path[path_len - 1] = '\0'; 263 check_tty_name(path, sizeof(path)); 264 fd = open(path, O_WRONLY|O_NONBLOCK, 0); 265 send_fd(sock, fd); 266 if (fd == -1) 267 warnx("priv_open_tty failed"); 268 else 269 close(fd); 270 break; 271 272 case PRIV_OPEN_LOG: 273 case PRIV_OPEN_PIPE: 274 log_debug("[priv]: msg PRIV_OPEN_%s received", 275 cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG"); 276 /* Expecting: length, path */ 277 must_read(sock, &path_len, sizeof(size_t)); 278 if (path_len == 0 || path_len > sizeof(path)) 279 _exit(1); 280 must_read(sock, &path, path_len); 281 path[path_len - 1] = '\0'; 282 check_log_name(path, sizeof(path)); 283 284 if (cmd == PRIV_OPEN_LOG) 285 fd = open_file(path); 286 else if (cmd == PRIV_OPEN_PIPE) 287 fd = open_pipe(path); 288 else 289 errx(1, "invalid cmd"); 290 291 send_fd(sock, fd); 292 if (fd == -1) 293 warnx("priv_open_log failed"); 294 else 295 close(fd); 296 break; 297 298 case PRIV_OPEN_UTMP: 299 log_debug("[priv]: msg PRIV_OPEN_UTMP received"); 300 fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0); 301 send_fd(sock, fd); 302 if (fd == -1) 303 warnx("priv_open_utmp failed"); 304 else 305 close(fd); 306 break; 307 308 case PRIV_OPEN_CONFIG: 309 log_debug("[priv]: msg PRIV_OPEN_CONFIG received"); 310 stat(conf, &cf_info); 311 fd = open(conf, O_RDONLY|O_NONBLOCK, 0); 312 send_fd(sock, fd); 313 if (fd == -1) 314 warnx("priv_open_config failed"); 315 else 316 close(fd); 317 break; 318 319 case PRIV_CONFIG_MODIFIED: 320 log_debug("[priv]: msg PRIV_CONFIG_MODIFIED received"); 321 if (stat(conf, &cf_stat) == -1 || 322 timespeccmp(&cf_info.st_mtimespec, 323 &cf_stat.st_mtimespec, <) || 324 cf_info.st_size != cf_stat.st_size) { 325 log_debug("config file modified: restarting"); 326 restart = result = 1; 327 must_write(sock, &result, sizeof(int)); 328 } else { 329 result = 0; 330 must_write(sock, &result, sizeof(int)); 331 } 332 break; 333 334 case PRIV_DONE_CONFIG_PARSE: 335 if (pledge("stdio rpath wpath cpath dns sendfd id proc exec", 336 NULL) == -1) 337 err(1, "pledge done config"); 338 log_debug("[priv]: msg PRIV_DONE_CONFIG_PARSE " 339 "received"); 340 increase_state(STATE_RUNNING); 341 break; 342 343 case PRIV_GETADDRINFO: 344 log_debug("[priv]: msg PRIV_GETADDRINFO received"); 345 /* Expecting: len, proto, len, host, len, serv */ 346 must_read(sock, &protoname_len, sizeof(size_t)); 347 if (protoname_len == 0 || 348 protoname_len > sizeof(protoname)) 349 _exit(1); 350 must_read(sock, &protoname, protoname_len); 351 protoname[protoname_len - 1] = '\0'; 352 353 must_read(sock, &hostname_len, sizeof(size_t)); 354 if (hostname_len == 0 || 355 hostname_len > sizeof(hostname)) 356 _exit(1); 357 must_read(sock, &hostname, hostname_len); 358 hostname[hostname_len - 1] = '\0'; 359 360 must_read(sock, &servname_len, sizeof(size_t)); 361 if (servname_len == 0 || 362 servname_len > sizeof(servname)) 363 _exit(1); 364 must_read(sock, &servname, servname_len); 365 servname[servname_len - 1] = '\0'; 366 367 memset(&hints, 0, sizeof(hints)); 368 switch (strlen(protoname)) { 369 case 3: 370 hints.ai_family = AF_UNSPEC; 371 break; 372 case 4: 373 switch (protoname[3]) { 374 case '4': 375 hints.ai_family = AF_INET; 376 break; 377 case '6': 378 hints.ai_family = AF_INET6; 379 break; 380 default: 381 errx(1, "bad ip version %s", protoname); 382 } 383 break; 384 default: 385 errx(1, "bad protocol length %s", protoname); 386 } 387 if (strncmp(protoname, "udp", 3) == 0) { 388 hints.ai_socktype = SOCK_DGRAM; 389 hints.ai_protocol = IPPROTO_UDP; 390 } else if (strncmp(protoname, "tcp", 3) == 0) { 391 hints.ai_socktype = SOCK_STREAM; 392 hints.ai_protocol = IPPROTO_TCP; 393 } else { 394 errx(1, "unknown protocol %s", protoname); 395 } 396 i = getaddrinfo(hostname, servname, &hints, &res0); 397 if (i != 0 || res0 == NULL) { 398 addr_len = 0; 399 must_write(sock, &addr_len, sizeof(int)); 400 } else { 401 /* Just send the first address */ 402 i = res0->ai_addrlen; 403 must_write(sock, &i, sizeof(int)); 404 must_write(sock, res0->ai_addr, i); 405 freeaddrinfo(res0); 406 } 407 break; 408 409 case PRIV_GETNAMEINFO: 410 log_debug("[priv]: msg PRIV_GETNAMEINFO received"); 411 if (numeric) 412 errx(1, "rejected attempt to getnameinfo"); 413 /* Expecting: length, sockaddr */ 414 must_read(sock, &addr_len, sizeof(int)); 415 if (addr_len <= 0 || (size_t)addr_len > sizeof(addr)) 416 _exit(1); 417 must_read(sock, &addr, addr_len); 418 if (getnameinfo((struct sockaddr *)&addr, addr_len, 419 hostname, sizeof(hostname), NULL, 0, 420 NI_NOFQDN|NI_NAMEREQD|NI_DGRAM) != 0) { 421 addr_len = 0; 422 must_write(sock, &addr_len, sizeof(int)); 423 } else { 424 addr_len = strlen(hostname) + 1; 425 must_write(sock, &addr_len, sizeof(int)); 426 must_write(sock, hostname, addr_len); 427 } 428 break; 429 default: 430 errx(1, "unknown command %d", cmd); 431 break; 432 } 433 } 434 435 close(sock); 436 437 if (restart) { 438 int status; 439 440 waitpid(child_pid, &status, 0); 441 sigemptyset(&sigmask); 442 sigaddset(&sigmask, SIGHUP); 443 if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) 444 err(1, "sigprocmask exec"); 445 execvp(argv[0], argv); 446 err(1, "exec restart '%s' failed", argv[0]); 447 } 448 unlink(_PATH_LOGPID); 449 exit(0); 450 } 451 452 static int 453 open_file(char *path) 454 { 455 /* must not start with | */ 456 if (path[0] == '|') 457 return (-1); 458 459 return (open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0)); 460 } 461 462 static int 463 open_pipe(char *cmd) 464 { 465 char *argp[] = {"sh", "-c", NULL, NULL}; 466 int fd[2]; 467 int bsize, flags; 468 pid_t pid; 469 470 /* skip over leading | and whitespace */ 471 if (cmd[0] != '|') 472 return (-1); 473 for (cmd++; *cmd && *cmd == ' '; cmd++) 474 ; /* nothing */ 475 if (!*cmd) 476 return (-1); 477 478 argp[2] = cmd; 479 480 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) { 481 warnx("open_pipe"); 482 return (-1); 483 } 484 485 /* make the fd on syslogd's side nonblocking */ 486 if ((flags = fcntl(fd[1], F_GETFL)) == -1) { 487 warnx("fcntl"); 488 return (-1); 489 } 490 flags |= O_NONBLOCK; 491 if ((flags = fcntl(fd[1], F_SETFL, flags)) == -1) { 492 warnx("fcntl"); 493 return (-1); 494 } 495 496 switch (pid = fork()) { 497 case -1: 498 warnx("fork error"); 499 return (-1); 500 case 0: 501 break; 502 default: 503 close(fd[0]); 504 return (fd[1]); 505 } 506 507 close(fd[1]); 508 509 /* grow receive buffer */ 510 bsize = 65535; 511 while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF, 512 &bsize, sizeof(bsize)) == -1) 513 bsize /= 2; 514 515 if (setgroups(1, &pw->pw_gid) == -1 || 516 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 517 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 518 err(1, "failure dropping privs"); 519 520 if (dup2(fd[0], STDIN_FILENO) == -1) 521 err(1, "dup2 failed"); 522 if (execv("/bin/sh", argp) == -1) 523 err(1, "execv %s", cmd); 524 /* NOTREACHED */ 525 return (-1); 526 } 527 528 /* Check that the terminal device is ok, and if not, rewrite to /dev/null. 529 * Either /dev/console or /dev/tty* are allowed. 530 */ 531 static void 532 check_tty_name(char *tty, size_t ttysize) 533 { 534 const char ttypre[] = "/dev/tty"; 535 char *p; 536 537 /* Any path containing '..' is invalid. */ 538 for (p = tty; p + 1 < tty + ttysize && *p; p++) 539 if (*p == '.' && *(p + 1) == '.') 540 goto bad_path; 541 542 if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre))) 543 goto bad_path; 544 return; 545 546 bad_path: 547 warnx ("%s: invalid attempt to open %s: rewriting to /dev/null", 548 "check_tty_name", tty); 549 strlcpy(tty, "/dev/null", ttysize); 550 } 551 552 /* If we are in the initial configuration state, accept a logname and add 553 * it to the list of acceptable logfiles. Otherwise, check against this list 554 * and rewrite to /dev/null if it's a bad path. 555 */ 556 static void 557 check_log_name(char *lognam, size_t logsize) 558 { 559 struct logname *lg; 560 char *p; 561 562 /* Any path containing '..' is invalid. */ 563 for (p = lognam; p + 1 < lognam + logsize && *p; p++) 564 if (*p == '.' && *(p + 1) == '.') 565 goto bad_path; 566 567 switch (cur_state) { 568 case STATE_CONFIG: 569 lg = malloc(sizeof(struct logname)); 570 if (!lg) 571 err(1, "check_log_name() malloc"); 572 strlcpy(lg->path, lognam, PATH_MAX); 573 TAILQ_INSERT_TAIL(&lognames, lg, next); 574 if (lognam[0] != '|') { 575 if (unveil(lognam, "w") == -1) 576 goto bad_path; 577 } 578 break; 579 case STATE_RUNNING: 580 TAILQ_FOREACH(lg, &lognames, next) 581 if (!strcmp(lg->path, lognam)) 582 return; 583 goto bad_path; 584 break; 585 default: 586 /* Any other state should just refuse the request */ 587 goto bad_path; 588 break; 589 } 590 return; 591 592 bad_path: 593 warnx("%s: invalid attempt to open %s: rewriting to /dev/null", 594 "check_log_name", lognam); 595 strlcpy(lognam, "/dev/null", logsize); 596 } 597 598 /* Crank our state into less permissive modes */ 599 static void 600 increase_state(int state) 601 { 602 if (state <= cur_state) 603 errx(1, "attempt to decrease or match current state"); 604 if (state < STATE_INIT || state > STATE_QUIT) 605 errx(1, "attempt to switch to invalid state"); 606 cur_state = state; 607 } 608 609 /* Open console or a terminal device for writing */ 610 int 611 priv_open_tty(const char *tty) 612 { 613 char path[PATH_MAX]; 614 int cmd, fd; 615 size_t path_len; 616 617 if (priv_fd < 0) 618 errx(1, "%s: called from privileged portion", __func__); 619 620 if (strlcpy(path, tty, sizeof path) >= sizeof(path)) 621 return -1; 622 path_len = strlen(path) + 1; 623 624 cmd = PRIV_OPEN_TTY; 625 must_write(priv_fd, &cmd, sizeof(int)); 626 must_write(priv_fd, &path_len, sizeof(size_t)); 627 must_write(priv_fd, path, path_len); 628 fd = receive_fd(priv_fd); 629 return fd; 630 } 631 632 /* Open log-file */ 633 int 634 priv_open_log(const char *lognam) 635 { 636 char path[PATH_MAX]; 637 int cmd, fd; 638 size_t path_len; 639 640 if (priv_fd < 0) 641 errx(1, "%s: called from privileged child", __func__); 642 643 if (strlcpy(path, lognam, sizeof path) >= sizeof(path)) 644 return -1; 645 path_len = strlen(path) + 1; 646 647 if (lognam[0] == '|') 648 cmd = PRIV_OPEN_PIPE; 649 else 650 cmd = PRIV_OPEN_LOG; 651 must_write(priv_fd, &cmd, sizeof(int)); 652 must_write(priv_fd, &path_len, sizeof(size_t)); 653 must_write(priv_fd, path, path_len); 654 fd = receive_fd(priv_fd); 655 return fd; 656 } 657 658 /* Open utmp for reading */ 659 FILE * 660 priv_open_utmp(void) 661 { 662 int cmd, fd; 663 FILE *fp; 664 665 if (priv_fd < 0) 666 errx(1, "%s: called from privileged portion", __func__); 667 668 cmd = PRIV_OPEN_UTMP; 669 must_write(priv_fd, &cmd, sizeof(int)); 670 fd = receive_fd(priv_fd); 671 if (fd < 0) 672 return NULL; 673 674 fp = fdopen(fd, "r"); 675 if (!fp) { 676 warn("priv_open_utmp: fdopen() failed"); 677 close(fd); 678 return NULL; 679 } 680 681 return fp; 682 } 683 684 /* Open syslog config file for reading */ 685 FILE * 686 priv_open_config(void) 687 { 688 int cmd, fd; 689 FILE *fp; 690 691 if (priv_fd < 0) 692 errx(1, "%s: called from privileged portion", __func__); 693 694 cmd = PRIV_OPEN_CONFIG; 695 must_write(priv_fd, &cmd, sizeof(int)); 696 fd = receive_fd(priv_fd); 697 if (fd < 0) 698 return NULL; 699 700 fp = fdopen(fd, "r"); 701 if (!fp) { 702 warn("priv_open_config: fdopen() failed"); 703 close(fd); 704 return NULL; 705 } 706 707 return fp; 708 } 709 710 /* Ask if config file has been modified since last attempt to read it */ 711 int 712 priv_config_modified(void) 713 { 714 int cmd, res; 715 716 if (priv_fd < 0) 717 errx(1, "%s: called from privileged portion", __func__); 718 719 cmd = PRIV_CONFIG_MODIFIED; 720 must_write(priv_fd, &cmd, sizeof(int)); 721 722 /* Expect back integer signalling 1 for modification */ 723 must_read(priv_fd, &res, sizeof(int)); 724 return res; 725 } 726 727 /* Child can signal that its initial parsing is done, so that parent 728 * can revoke further logfile permissions. This call only works once. */ 729 void 730 priv_config_parse_done(void) 731 { 732 int cmd; 733 734 if (priv_fd < 0) 735 errx(1, "%s: called from privileged portion", __func__); 736 737 cmd = PRIV_DONE_CONFIG_PARSE; 738 must_write(priv_fd, &cmd, sizeof(int)); 739 } 740 741 /* Name/service to address translation. Response is placed into addr. 742 * Return 0 for success or < 0 for error like getaddrinfo(3) */ 743 int 744 priv_getaddrinfo(char *proto, char *host, char *serv, struct sockaddr *addr, 745 size_t addr_len) 746 { 747 char protocpy[5], hostcpy[NI_MAXHOST], servcpy[NI_MAXSERV]; 748 int cmd, ret_len; 749 size_t protoname_len, hostname_len, servname_len; 750 751 if (priv_fd < 0) 752 errx(1, "%s: called from privileged portion", __func__); 753 754 if (strlcpy(protocpy, proto, sizeof(protocpy)) >= sizeof(protocpy)) 755 errx(1, "%s: overflow attempt in protoname", __func__); 756 protoname_len = strlen(protocpy) + 1; 757 if (strlcpy(hostcpy, host, sizeof(hostcpy)) >= sizeof(hostcpy)) 758 errx(1, "%s: overflow attempt in hostname", __func__); 759 hostname_len = strlen(hostcpy) + 1; 760 if (strlcpy(servcpy, serv, sizeof(servcpy)) >= sizeof(servcpy)) 761 errx(1, "%s: overflow attempt in servname", __func__); 762 servname_len = strlen(servcpy) + 1; 763 764 cmd = PRIV_GETADDRINFO; 765 must_write(priv_fd, &cmd, sizeof(int)); 766 must_write(priv_fd, &protoname_len, sizeof(size_t)); 767 must_write(priv_fd, protocpy, protoname_len); 768 must_write(priv_fd, &hostname_len, sizeof(size_t)); 769 must_write(priv_fd, hostcpy, hostname_len); 770 must_write(priv_fd, &servname_len, sizeof(size_t)); 771 must_write(priv_fd, servcpy, servname_len); 772 773 /* Expect back an integer size, and then a string of that length */ 774 must_read(priv_fd, &ret_len, sizeof(int)); 775 776 /* Check there was no error (indicated by a return of 0) */ 777 if (!ret_len) 778 return (-1); 779 780 /* Make sure we aren't overflowing the passed in buffer */ 781 if (ret_len < 0 || (size_t)ret_len > addr_len) 782 errx(1, "%s: overflow attempt in return", __func__); 783 784 /* Read the resolved address and make sure we got all of it */ 785 memset(addr, '\0', addr_len); 786 must_read(priv_fd, addr, ret_len); 787 788 return (0); 789 } 790 791 /* Reverse address resolution; response is placed into host. 792 * Return 0 for success or < 0 for error like getnameinfo(3) */ 793 int 794 priv_getnameinfo(struct sockaddr *sa, socklen_t salen, char *host, 795 size_t hostlen) 796 { 797 int cmd, ret_len; 798 799 if (priv_fd < 0) 800 errx(1, "%s called from privileged portion", __func__); 801 802 cmd = PRIV_GETNAMEINFO; 803 must_write(priv_fd, &cmd, sizeof(int)); 804 must_write(priv_fd, &salen, sizeof(int)); 805 must_write(priv_fd, sa, salen); 806 807 /* Expect back an integer size, and then a string of that length */ 808 must_read(priv_fd, &ret_len, sizeof(int)); 809 810 /* Check there was no error (indicated by a return of 0) */ 811 if (!ret_len) 812 return (-1); 813 814 /* Check we don't overflow the passed in buffer */ 815 if (ret_len < 0 || (size_t)ret_len > hostlen) 816 errx(1, "%s: overflow attempt in return", __func__); 817 818 /* Read the resolved hostname */ 819 must_read(priv_fd, host, ret_len); 820 return (0); 821 } 822 823 /* Pass the signal through to child */ 824 static void 825 sig_pass_to_chld(int sig) 826 { 827 int save_errno = errno; 828 829 if (child_pid != -1) 830 kill(child_pid, sig); 831 errno = save_errno; 832 } 833 834 /* When child dies, move into the shutdown state */ 835 /* ARGSUSED */ 836 static void 837 sig_got_chld(int sig) 838 { 839 int save_errno = errno; 840 pid_t pid; 841 842 do { 843 pid = waitpid(WAIT_ANY, NULL, WNOHANG); 844 if (pid == child_pid && cur_state < STATE_QUIT) 845 cur_state = STATE_QUIT; 846 } while (pid > 0 || (pid == -1 && errno == EINTR)); 847 errno = save_errno; 848 } 849 850 /* Read all data or return 1 for error. */ 851 static int 852 may_read(int fd, void *buf, size_t n) 853 { 854 char *s = buf; 855 ssize_t res; 856 size_t pos = 0; 857 858 while (n > pos) { 859 res = read(fd, s + pos, n - pos); 860 switch (res) { 861 case -1: 862 if (errno == EINTR || errno == EAGAIN) 863 continue; 864 case 0: 865 return (1); 866 default: 867 pos += res; 868 } 869 } 870 return (0); 871 } 872 873 /* Read data with the assertion that it all must come through, or 874 * else abort the process. Based on atomicio() from openssh. */ 875 static void 876 must_read(int fd, void *buf, size_t n) 877 { 878 char *s = buf; 879 ssize_t res; 880 size_t pos = 0; 881 882 while (n > pos) { 883 res = read(fd, s + pos, n - pos); 884 switch (res) { 885 case -1: 886 if (errno == EINTR || errno == EAGAIN) 887 continue; 888 case 0: 889 _exit(1); 890 default: 891 pos += res; 892 } 893 } 894 } 895 896 /* Write data with the assertion that it all has to be written, or 897 * else abort the process. Based on atomicio() from openssh. */ 898 static void 899 must_write(int fd, void *buf, size_t n) 900 { 901 char *s = buf; 902 ssize_t res; 903 size_t pos = 0; 904 905 while (n > pos) { 906 res = write(fd, s + pos, n - pos); 907 switch (res) { 908 case -1: 909 if (errno == EINTR || errno == EAGAIN) 910 continue; 911 case 0: 912 _exit(1); 913 default: 914 pos += res; 915 } 916 } 917 } 918