xref: /openbsd/usr.sbin/syslogd/privsep.c (revision 404b540a)
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