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