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