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