1 /*	$OpenBSD: privsep.c,v 1.26 2005/06/06 23:20:44 djm 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_UTMP,		/* open utmp for reading only */
67 	PRIV_OPEN_CONFIG,	/* open config file for reading only */
68 	PRIV_CONFIG_MODIFIED,	/* check if config file has been modified */
69 	PRIV_GETHOSTSERV,	/* resolve host/service names */
70 	PRIV_GETHOSTBYADDR,	/* resolve numeric address into hostname */
71 	PRIV_DONE_CONFIG_PARSE	/* signal that the initial config parse is done */
72 };
73 
74 static int priv_fd = -1;
75 static volatile pid_t child_pid = -1;
76 static char config_file[MAXPATHLEN];
77 static struct stat cf_info;
78 static int allow_gethostbyaddr = 0;
79 static volatile sig_atomic_t cur_state = STATE_INIT;
80 
81 /* Queue for the allowed logfiles */
82 struct logname {
83 	char path[MAXPATHLEN];
84 	TAILQ_ENTRY(logname) next;
85 };
86 static TAILQ_HEAD(, logname) lognames;
87 
88 static void check_log_name(char *, size_t);
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
priv_init(char * conf,int numeric,int lockfd,int nullfd,char * argv[])98 priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[])
99 {
100 	int i, fd, socks[2], cmd, addr_len, addr_af, result, restart;
101 	size_t path_len, hostname_len, servname_len;
102 	char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN];
103 	char servname[MAXHOSTNAMELEN];
104 	struct stat cf_stat;
105 	struct hostent *hp;
106 	struct passwd *pw;
107 	struct addrinfo hints, *res0;
108 
109 	for (i = 1; i < _NSIG; i++)
110 		signal(i, SIG_DFL);
111 
112 	/* Create sockets */
113 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
114 		err(1, "socketpair() failed");
115 
116 	pw = getpwnam("_syslogd");
117 	if (pw == NULL)
118 		errx(1, "unknown user _syslogd");
119 
120 	child_pid = fork();
121 	if (child_pid < 0)
122 		err(1, "fork() failed");
123 
124 	if (!child_pid) {
125 		/* Child - drop privileges and return */
126 		if (chroot(pw->pw_dir) != 0)
127 			err(1, "unable to chroot");
128 		if (chdir("/") != 0)
129 			err(1, "unable to chdir");
130 
131 		if (setgroups(1, &pw->pw_gid) == -1)
132 			err(1, "setgroups() failed");
133 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
134 			err(1, "setresgid() failed");
135 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
136 			err(1, "setresuid() failed");
137 		close(socks[0]);
138 		priv_fd = socks[1];
139 		return 0;
140 	}
141 
142 	close(lockfd);
143 	if (!Debug) {
144 		dup2(nullfd, STDIN_FILENO);
145 		dup2(nullfd, STDOUT_FILENO);
146 		dup2(nullfd, STDERR_FILENO);
147 	}
148 
149 	if (nullfd > 2)
150 		close(nullfd);
151 
152 	/* Father */
153 	/* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */
154 	signal(SIGTERM, sig_pass_to_chld);
155 	signal(SIGHUP, sig_pass_to_chld);
156 	signal(SIGINT, sig_pass_to_chld);
157 	signal(SIGQUIT, sig_pass_to_chld);
158 	signal(SIGCHLD, sig_got_chld);
159 
160 	setproctitle("[priv]");
161 	close(socks[1]);
162 
163 	/* Close descriptors that only the unpriv child needs */
164 	for (i = 0; i < nfunix; i++)
165 		if (pfd[PFD_UNIX_0 + i].fd != -1)
166 			close(pfd[PFD_UNIX_0 + i].fd);
167 	if (pfd[PFD_INET].fd != -1)
168 		close(pfd[PFD_INET].fd);
169 	if (pfd[PFD_CTLSOCK].fd != -1)
170 		close(pfd[PFD_CTLSOCK].fd);
171 	if (pfd[PFD_CTLCONN].fd != -1)
172 		close(pfd[PFD_CTLCONN].fd);
173 	if (pfd[PFD_KLOG].fd)
174 		close(pfd[PFD_KLOG].fd);
175 
176 	/* Save the config file specified by the child process */
177 	if (strlcpy(config_file, conf, sizeof config_file) >= sizeof(config_file))
178 		errx(1, "config_file truncation");
179 
180 	if (stat(config_file, &cf_info) < 0)
181 		err(1, "stat config file failed");
182 
183 	/* Save whether or not the child can have access to gethostbyaddr(3) */
184 	if (numeric > 0)
185 		allow_gethostbyaddr = 0;
186 	else
187 		allow_gethostbyaddr = 1;
188 
189 	TAILQ_INIT(&lognames);
190 	increase_state(STATE_CONFIG);
191 	restart = 0;
192 
193 	while (cur_state < STATE_QUIT) {
194 		if (may_read(socks[0], &cmd, sizeof(int)))
195 			break;
196 		switch (cmd) {
197 		case PRIV_OPEN_TTY:
198 			dprintf("[priv]: msg PRIV_OPEN_TTY received\n");
199 			/* Expecting: length, path */
200 			must_read(socks[0], &path_len, sizeof(size_t));
201 			if (path_len == 0 || path_len > sizeof(path))
202 				_exit(0);
203 			must_read(socks[0], &path, path_len);
204 			path[path_len - 1] = '\0';
205 			check_tty_name(path, path_len);
206 			fd = open(path, O_WRONLY|O_NONBLOCK, 0);
207 			send_fd(socks[0], fd);
208 			if (fd < 0)
209 				warnx("priv_open_tty failed");
210 			else
211 				close(fd);
212 			break;
213 
214 		case PRIV_OPEN_LOG:
215 			dprintf("[priv]: msg PRIV_OPEN_LOG received\n");
216 			/* Expecting: length, path */
217 			must_read(socks[0], &path_len, sizeof(size_t));
218 			if (path_len == 0 || path_len > sizeof(path))
219 				_exit(0);
220 			must_read(socks[0], &path, path_len);
221 			path[path_len - 1] = '\0';
222 			check_log_name(path, path_len);
223 			fd = open(path, O_WRONLY|O_APPEND|O_NONBLOCK, 0);
224 			send_fd(socks[0], fd);
225 			if (fd < 0)
226 				warnx("priv_open_log failed");
227 			else
228 				close(fd);
229 			break;
230 
231 		case PRIV_OPEN_UTMP:
232 			dprintf("[priv]: msg PRIV_OPEN_UTMP received\n");
233 			fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK, 0);
234 			send_fd(socks[0], fd);
235 			if (fd < 0)
236 				warnx("priv_open_utmp failed");
237 			else
238 				close(fd);
239 			break;
240 
241 		case PRIV_OPEN_CONFIG:
242 			dprintf("[priv]: msg PRIV_OPEN_CONFIG received\n");
243 			stat(config_file, &cf_info);
244 			fd = open(config_file, O_RDONLY|O_NONBLOCK, 0);
245 			send_fd(socks[0], fd);
246 			if (fd < 0)
247 				warnx("priv_open_config failed");
248 			else
249 				close(fd);
250 			break;
251 
252 		case PRIV_CONFIG_MODIFIED:
253 			dprintf("[priv]: msg PRIV_CONFIG_MODIFIED received\n");
254 			if (stat(config_file, &cf_stat) < 0 ||
255 			    timespeccmp(&cf_info.st_mtimespec,
256 			    &cf_stat.st_mtimespec, <) ||
257 			    cf_info.st_size != cf_stat.st_size) {
258 				dprintf("config file modified: restarting\n");
259 				restart = result = 1;
260 				must_write(socks[0], &result, sizeof(int));
261 			} else {
262 				result = 0;
263 				must_write(socks[0], &result, sizeof(int));
264 			}
265 			break;
266 
267 		case PRIV_DONE_CONFIG_PARSE:
268 			dprintf("[priv]: msg PRIV_DONE_CONFIG_PARSE received\n");
269 			increase_state(STATE_RUNNING);
270 			break;
271 
272 		case PRIV_GETHOSTSERV:
273 			dprintf("[priv]: msg PRIV_GETHOSTSERV received\n");
274 			/* Expecting: len, hostname, len, servname */
275 			must_read(socks[0], &hostname_len, sizeof(size_t));
276 			if (hostname_len == 0 || hostname_len > sizeof(hostname))
277 				_exit(0);
278 			must_read(socks[0], &hostname, hostname_len);
279 			hostname[hostname_len - 1] = '\0';
280 
281 			must_read(socks[0], &servname_len, sizeof(size_t));
282 			if (servname_len == 0 || servname_len > sizeof(servname))
283 				_exit(0);
284 			must_read(socks[0], &servname, servname_len);
285 			servname[servname_len - 1] = '\0';
286 
287 			memset(&hints, '\0', sizeof(hints));
288 			hints.ai_family = AF_INET;
289 			hints.ai_socktype = SOCK_DGRAM;
290 			i = getaddrinfo(hostname, servname, &hints, &res0);
291 			if (i != 0 || res0 == NULL) {
292 				addr_len = 0;
293 				must_write(socks[0], &addr_len, sizeof(int));
294 			} else {
295 				/* Just send the first address */
296 				i = res0->ai_addrlen;
297 				must_write(socks[0], &i, sizeof(int));
298 				must_write(socks[0], res0->ai_addr, i);
299 				freeaddrinfo(res0);
300 			}
301 			break;
302 
303 		case PRIV_GETHOSTBYADDR:
304 			dprintf("[priv]: msg PRIV_GETHOSTBYADDR received\n");
305 			if (!allow_gethostbyaddr)
306 				errx(1, "rejected attempt to gethostbyaddr");
307 			/* Expecting: length, address, address family */
308 			must_read(socks[0], &addr_len, sizeof(int));
309 			if (addr_len <= 0 || addr_len > sizeof(hostname))
310 				_exit(0);
311 			must_read(socks[0], hostname, addr_len);
312 			must_read(socks[0], &addr_af, sizeof(int));
313 			hp = gethostbyaddr(hostname, addr_len, addr_af);
314 			if (hp == NULL) {
315 				addr_len = 0;
316 				must_write(socks[0], &addr_len, sizeof(int));
317 			} else {
318 				addr_len = strlen(hp->h_name) + 1;
319 				must_write(socks[0], &addr_len, sizeof(int));
320 				must_write(socks[0], hp->h_name, addr_len);
321 			}
322 			break;
323 		default:
324 			errx(1, "unknown command %d", cmd);
325 			break;
326 		}
327 	}
328 
329 	close(socks[0]);
330 
331 	/* Unlink any domain sockets that have been opened */
332 	for (i = 0; i < nfunix; i++)
333 		if (funixn[i] != NULL && pfd[PFD_UNIX_0 + i].fd != -1)
334 			(void)unlink(funixn[i]);
335 	if (ctlsock_path != NULL && pfd[PFD_CTLSOCK].fd != -1)
336 		(void)unlink(ctlsock_path);
337 
338 	if (restart) {
339 		int r;
340 
341 		wait(&r);
342 		execvp(argv[0], argv);
343 	}
344 	_exit(1);
345 }
346 
347 /* Check that the terminal device is ok, and if not, rewrite to /dev/null.
348  * Either /dev/console or /dev/tty* are allowed.
349  */
350 static void
check_tty_name(char * tty,size_t ttylen)351 check_tty_name(char *tty, size_t ttylen)
352 {
353 	const char ttypre[] = "/dev/tty";
354 	char *p;
355 
356 	/* Any path containing '..' is invalid.  */
357 	for (p = tty; *p && (p - tty) < ttylen; p++)
358 		if (*p == '.' && *(p + 1) == '.')
359 			goto bad_path;
360 
361 	if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre)))
362 		goto bad_path;
363 	return;
364 
365 bad_path:
366 	warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
367 	    "check_tty_name", tty);
368 	strlcpy(tty, "/dev/null", ttylen);
369 }
370 
371 /* If we are in the initial configuration state, accept a logname and add
372  * it to the list of acceptable logfiles.  Otherwise, check against this list
373  * and rewrite to /dev/null if it's a bad path.
374  */
375 static void
check_log_name(char * lognam,size_t loglen)376 check_log_name(char *lognam, size_t loglen)
377 {
378 	struct logname *lg;
379 	char *p;
380 
381 	/* Any path containing '..' is invalid.  */
382 	for (p = lognam; *p && (p - lognam) < loglen; p++)
383 		if (*p == '.' && *(p + 1) == '.')
384 			goto bad_path;
385 
386 	switch (cur_state) {
387 	case STATE_CONFIG:
388 		lg = malloc(sizeof(struct logname));
389 		if (!lg)
390 			err(1, "check_log_name() malloc");
391 		strlcpy(lg->path, lognam, MAXPATHLEN);
392 		TAILQ_INSERT_TAIL(&lognames, lg, next);
393 		break;
394 	case STATE_RUNNING:
395 		TAILQ_FOREACH(lg, &lognames, next)
396 			if (!strcmp(lg->path, lognam))
397 				return;
398 		goto bad_path;
399 		break;
400 	default:
401 		/* Any other state should just refuse the request */
402 		goto bad_path;
403 		break;
404 	}
405 	return;
406 
407 bad_path:
408 	warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
409 	    "check_log_name", lognam);
410 	strlcpy(lognam, "/dev/null", loglen);
411 }
412 
413 /* Crank our state into less permissive modes */
414 static void
increase_state(int state)415 increase_state(int state)
416 {
417 	if (state <= cur_state)
418 		errx(1, "attempt to decrease or match current state");
419 	if (state < STATE_INIT || state > STATE_QUIT)
420 		errx(1, "attempt to switch to invalid state");
421 	cur_state = state;
422 }
423 
424 /* Open console or a terminal device for writing */
425 int
priv_open_tty(const char * tty)426 priv_open_tty(const char *tty)
427 {
428 	char path[MAXPATHLEN];
429 	int cmd, fd;
430 	size_t path_len;
431 
432 	if (priv_fd < 0)
433 		errx(1, "%s: called from privileged portion", "priv_open_tty");
434 
435 	if (strlcpy(path, tty, sizeof path) >= sizeof(path))
436 		return -1;
437 	path_len = strlen(path) + 1;
438 
439 	cmd = PRIV_OPEN_TTY;
440 	must_write(priv_fd, &cmd, sizeof(int));
441 	must_write(priv_fd, &path_len, sizeof(size_t));
442 	must_write(priv_fd, path, path_len);
443 	fd = receive_fd(priv_fd);
444 	return fd;
445 }
446 
447 /* Open log-file */
448 int
priv_open_log(const char * lognam)449 priv_open_log(const char *lognam)
450 {
451 	char path[MAXPATHLEN];
452 	int cmd, fd;
453 	size_t path_len;
454 
455 	if (priv_fd < 0)
456 		errx(1, "%s: called from privileged child", "priv_open_log");
457 
458 	if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
459 		return -1;
460 	path_len = strlen(path) + 1;
461 
462 	cmd = PRIV_OPEN_LOG;
463 	must_write(priv_fd, &cmd, sizeof(int));
464 	must_write(priv_fd, &path_len, sizeof(size_t));
465 	must_write(priv_fd, path, path_len);
466 	fd = receive_fd(priv_fd);
467 	return fd;
468 }
469 
470 /* Open utmp for reading */
471 FILE *
priv_open_utmp(void)472 priv_open_utmp(void)
473 {
474 	int cmd, fd;
475 	FILE *fp;
476 
477 	if (priv_fd < 0)
478 		errx(1, "%s: called from privileged portion", "priv_open_utmp");
479 
480 	cmd = PRIV_OPEN_UTMP;
481 	must_write(priv_fd, &cmd, sizeof(int));
482 	fd = receive_fd(priv_fd);
483 	if (fd < 0)
484 		return NULL;
485 
486 	fp = fdopen(fd, "r");
487 	if (!fp) {
488 		warn("priv_open_utmp: fdopen() failed");
489 		close(fd);
490 		return NULL;
491 	}
492 
493 	return fp;
494 }
495 
496 /* Open syslog config file for reading */
497 FILE *
priv_open_config(void)498 priv_open_config(void)
499 {
500 	int cmd, fd;
501 	FILE *fp;
502 
503 	if (priv_fd < 0)
504 		errx(1, "%s: called from privileged portion", "priv_open_config");
505 
506 	cmd = PRIV_OPEN_CONFIG;
507 	must_write(priv_fd, &cmd, sizeof(int));
508 	fd = receive_fd(priv_fd);
509 	if (fd < 0)
510 		return NULL;
511 
512 	fp = fdopen(fd, "r");
513 	if (!fp) {
514 		warn("priv_open_config: fdopen() failed");
515 		close(fd);
516 		return NULL;
517 	}
518 
519 	return fp;
520 }
521 
522 /* Ask if config file has been modified since last attempt to read it */
523 int
priv_config_modified(void)524 priv_config_modified(void)
525 {
526 	int cmd, res;
527 
528 	if (priv_fd < 0)
529 		errx(1, "%s: called from privileged portion",
530 		    "priv_config_modified");
531 
532 	cmd = PRIV_CONFIG_MODIFIED;
533 	must_write(priv_fd, &cmd, sizeof(int));
534 
535 	/* Expect back integer signalling 1 for modification */
536 	must_read(priv_fd, &res, sizeof(int));
537 	return res;
538 }
539 
540 /* Child can signal that its initial parsing is done, so that parent
541  * can revoke further logfile permissions.  This call only works once. */
542 void
priv_config_parse_done(void)543 priv_config_parse_done(void)
544 {
545 	int cmd;
546 
547 	if (priv_fd < 0)
548 		errx(1, "%s: called from privileged portion",
549 		    "priv_config_parse_done");
550 
551 	cmd = PRIV_DONE_CONFIG_PARSE;
552 	must_write(priv_fd, &cmd, sizeof(int));
553 }
554 
555 /* Name/service to address translation.  Response is placed into addr, and
556  * the length is returned (zero on error) */
557 int
priv_gethostserv(char * host,char * serv,struct sockaddr * addr,size_t addr_len)558 priv_gethostserv(char *host, char *serv, struct sockaddr *addr,
559     size_t addr_len)
560 {
561 	char hostcpy[MAXHOSTNAMELEN], servcpy[MAXHOSTNAMELEN];
562 	int cmd, ret_len;
563 	size_t hostname_len, servname_len;
564 
565 	if (priv_fd < 0)
566 		errx(1, "%s: called from privileged portion", "priv_gethostserv");
567 
568 	if (strlcpy(hostcpy, host, sizeof hostcpy) >= sizeof(hostcpy))
569 		errx(1, "%s: overflow attempt in hostname", "priv_gethostserv");
570 	hostname_len = strlen(hostcpy) + 1;
571 	if (strlcpy(servcpy, serv, sizeof servcpy) >= sizeof(servcpy))
572 		errx(1, "%s: overflow attempt in servname", "priv_gethostserv");
573 	servname_len = strlen(servcpy) + 1;
574 
575 	cmd = PRIV_GETHOSTSERV;
576 	must_write(priv_fd, &cmd, sizeof(int));
577 	must_write(priv_fd, &hostname_len, sizeof(size_t));
578 	must_write(priv_fd, hostcpy, hostname_len);
579 	must_write(priv_fd, &servname_len, sizeof(size_t));
580 	must_write(priv_fd, servcpy, servname_len);
581 
582 	/* Expect back an integer size, and then a string of that length */
583 	must_read(priv_fd, &ret_len, sizeof(int));
584 
585 	/* Check there was no error (indicated by a return of 0) */
586 	if (!ret_len)
587 		return 0;
588 
589 	/* Make sure we aren't overflowing the passed in buffer */
590 	if (addr_len < ret_len)
591 		errx(1, "%s: overflow attempt in return", "priv_gethostserv");
592 
593 	/* Read the resolved address and make sure we got all of it */
594 	memset(addr, '\0', addr_len);
595 	must_read(priv_fd, addr, ret_len);
596 
597 	return ret_len;
598 }
599 
600 /* Reverse address resolution; response is placed into res, and length of
601  * response is returned (zero on error) */
602 int
priv_gethostbyaddr(char * addr,int addr_len,int af,char * res,size_t res_len)603 priv_gethostbyaddr(char *addr, int addr_len, int af, char *res, size_t res_len)
604 {
605 	int cmd, ret_len;
606 
607 	if (priv_fd < 0)
608 		errx(1, "%s called from privileged portion", "priv_gethostbyaddr");
609 
610 	cmd = PRIV_GETHOSTBYADDR;
611 	must_write(priv_fd, &cmd, sizeof(int));
612 	must_write(priv_fd, &addr_len, sizeof(int));
613 	must_write(priv_fd, addr, addr_len);
614 	must_write(priv_fd, &af, sizeof(int));
615 
616 	/* Expect back an integer size, and then a string of that length */
617 	must_read(priv_fd, &ret_len, sizeof(int));
618 
619 	/* Check there was no error (indicated by a return of 0) */
620 	if (!ret_len)
621 		return 0;
622 
623 	/* Check we don't overflow the passed in buffer */
624 	if (res_len < ret_len)
625 		errx(1, "%s: overflow attempt in return", "priv_gethostbyaddr");
626 
627 	/* Read the resolved hostname */
628 	must_read(priv_fd, res, ret_len);
629 	return ret_len;
630 }
631 
632 /* Pass the signal through to child */
633 static void
sig_pass_to_chld(int sig)634 sig_pass_to_chld(int sig)
635 {
636 	int oerrno = errno;
637 
638 	if (child_pid != -1)
639 		kill(child_pid, sig);
640 	errno = oerrno;
641 }
642 
643 /* When child dies, move into the shutdown state */
644 /* ARGSUSED */
645 static void
sig_got_chld(int sig)646 sig_got_chld(int sig)
647 {
648 	if (cur_state < STATE_QUIT)
649 		cur_state = STATE_QUIT;
650 }
651 
652 /* Read all data or return 1 for error.  */
653 static int
may_read(int fd,void * buf,size_t n)654 may_read(int fd, void *buf, size_t n)
655 {
656 	char *s = buf;
657 	ssize_t res, pos = 0;
658 
659 	while (n > pos) {
660 		res = read(fd, s + pos, n - pos);
661 		switch (res) {
662 		case -1:
663 			if (errno == EINTR || errno == EAGAIN)
664 				continue;
665 		case 0:
666 			return (1);
667 		default:
668 			pos += res;
669 		}
670 	}
671 	return (0);
672 }
673 
674 /* Read data with the assertion that it all must come through, or
675  * else abort the process.  Based on atomicio() from openssh. */
676 static void
must_read(int fd,void * buf,size_t n)677 must_read(int fd, void *buf, size_t n)
678 {
679 	char *s = buf;
680 	ssize_t res, pos = 0;
681 
682 	while (n > pos) {
683 		res = read(fd, s + pos, n - pos);
684 		switch (res) {
685 		case -1:
686 			if (errno == EINTR || errno == EAGAIN)
687 				continue;
688 		case 0:
689 			_exit(0);
690 		default:
691 			pos += res;
692 		}
693 	}
694 }
695 
696 /* Write data with the assertion that it all has to be written, or
697  * else abort the process.  Based on atomicio() from openssh. */
698 static void
must_write(int fd,void * buf,size_t n)699 must_write(int fd, void *buf, size_t n)
700 {
701 	char *s = buf;
702 	ssize_t res, pos = 0;
703 
704 	while (n > pos) {
705 		res = write(fd, s + pos, n - pos);
706 		switch (res) {
707 		case -1:
708 			if (errno == EINTR || errno == EAGAIN)
709 				continue;
710 		case 0:
711 			_exit(0);
712 		default:
713 			pos += res;
714 		}
715 	}
716 }
717