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