xref: /openbsd-src/usr.sbin/syslogd/privsep.c (revision c1f8818c70b0a2dda195c4121f78004f13ef6c05)
1*c1f8818cSbluhm /*	$OpenBSD: privsep.c,v 1.77 2023/10/12 22:36:54 bluhm Exp $	*/
238182445Savsm 
338182445Savsm /*
438182445Savsm  * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
5ece520b1Sbluhm  * Copyright (c) 2016 Alexander Bluhm <bluhm@openbsd.org>
638182445Savsm  *
738182445Savsm  * Permission to use, copy, modify, and distribute this software for any
838182445Savsm  * purpose with or without fee is hereby granted, provided that the above
938182445Savsm  * copyright notice and this permission notice appear in all copies.
1038182445Savsm  *
1138182445Savsm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1238182445Savsm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1338182445Savsm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1438182445Savsm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1538182445Savsm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1638182445Savsm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1738182445Savsm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1838182445Savsm  */
19b9fc9a72Sderaadt 
2038182445Savsm #include <sys/queue.h>
2138182445Savsm #include <sys/stat.h>
2238182445Savsm #include <sys/wait.h>
2338bc45fcSbluhm 
2438182445Savsm #include <err.h>
2538182445Savsm #include <errno.h>
2638182445Savsm #include <fcntl.h>
2738bc45fcSbluhm #include <limits.h>
2838182445Savsm #include <netdb.h>
2938182445Savsm #include <paths.h>
3038182445Savsm #include <pwd.h>
3138182445Savsm #include <signal.h>
3238182445Savsm #include <stdio.h>
3338182445Savsm #include <stdlib.h>
3438182445Savsm #include <string.h>
3538182445Savsm #include <unistd.h>
3638182445Savsm #include <utmp.h>
3738bc45fcSbluhm 
38ed00ae78Sbluhm #include "log.h"
3938182445Savsm #include "syslogd.h"
4038182445Savsm 
4138182445Savsm /*
4238182445Savsm  * syslogd can only go forward in these states; each state should represent
4338182445Savsm  * less privilege.   After STATE_INIT, the child is allowed to parse its
4438182445Savsm  * config file once, and communicate the information regarding what logfiles
4538182445Savsm  * it needs access to back to the parent.  When that is done, it sends a
4638182445Savsm  * message to the priv parent revoking this access, moving to STATE_RUNNING.
4738182445Savsm  * In this state, any log-files not in the access list are rejected.
4838182445Savsm  *
4938182445Savsm  * This allows a HUP signal to the child to reopen its log files, and
5038182445Savsm  * the config file to be parsed if it hasn't been changed (this is still
519ba52c13Savsm  * useful to force resolution of remote syslog servers again).
5238182445Savsm  * If the config file has been modified, then the child dies, and
5338182445Savsm  * the priv parent restarts itself.
5438182445Savsm  */
5538182445Savsm enum priv_state {
5638182445Savsm 	STATE_INIT,		/* just started up */
5738182445Savsm 	STATE_CONFIG,		/* parsing config file for first time */
5838182445Savsm 	STATE_RUNNING,		/* running and accepting network traffic */
595b2985feSderaadt 	STATE_QUIT		/* shutting down */
6038182445Savsm };
6138182445Savsm 
6238182445Savsm enum cmd_types {
6338182445Savsm 	PRIV_OPEN_TTY,		/* open terminal or console device */
6438182445Savsm 	PRIV_OPEN_LOG,		/* open logfile for appending */
6547607485Shenning 	PRIV_OPEN_PIPE,		/* fork & exec child that gets logs on stdin */
6638182445Savsm 	PRIV_OPEN_UTMP,		/* open utmp for reading only */
6738182445Savsm 	PRIV_OPEN_CONFIG,	/* open config file for reading only */
6838182445Savsm 	PRIV_CONFIG_MODIFIED,	/* check if config file has been modified */
692fa726a7Sbluhm 	PRIV_GETADDRINFO,	/* resolve host/service names */
70bd6df507Sbluhm 	PRIV_GETNAMEINFO,	/* resolve numeric address into hostname */
71c572cfa7Sbluhm 	PRIV_DONE_CONFIG_PARSE	/* signal that initial config parse is done */
7238182445Savsm };
7338182445Savsm 
7438182445Savsm static int priv_fd = -1;
751502b2edSotto static volatile pid_t child_pid = -1;
7638182445Savsm static volatile sig_atomic_t cur_state = STATE_INIT;
7738182445Savsm 
7838182445Savsm /* Queue for the allowed logfiles */
7938182445Savsm struct logname {
80b9fc9a72Sderaadt 	char path[PATH_MAX];
8138182445Savsm 	TAILQ_ENTRY(logname) next;
8238182445Savsm };
8338182445Savsm static TAILQ_HEAD(, logname) lognames;
8438182445Savsm 
8538182445Savsm static void check_log_name(char *, size_t);
8647607485Shenning static int open_file(char *);
8747607485Shenning static int open_pipe(char *);
8838182445Savsm static void check_tty_name(char *, size_t);
8938182445Savsm static void increase_state(int);
9038182445Savsm static void sig_pass_to_chld(int);
9138182445Savsm static void sig_got_chld(int);
9238182445Savsm static void must_read(int, void *, size_t);
9338182445Savsm static void must_write(int, void *, size_t);
94927e6d74Scanacar static int  may_read(int, void *, size_t);
9538182445Savsm 
96f22826eaSbluhm static struct passwd *pw;
97f22826eaSbluhm 
98ece520b1Sbluhm void
priv_init(int lockfd,int nullfd,int argc,char * argv[])99ece520b1Sbluhm priv_init(int lockfd, int nullfd, int argc, char *argv[])
10038182445Savsm {
101ece520b1Sbluhm 	int i, socks[2];
10236b99be9Sbluhm 	char *execpath, childnum[11], **privargv;
1031502b2edSotto 
10438182445Savsm 	/* Create sockets */
10538182445Savsm 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
10638182445Savsm 		err(1, "socketpair() failed");
10738182445Savsm 
10838182445Savsm 	pw = getpwnam("_syslogd");
10938182445Savsm 	if (pw == NULL)
11038182445Savsm 		errx(1, "unknown user _syslogd");
11138182445Savsm 
11238182445Savsm 	child_pid = fork();
113df69c215Sderaadt 	if (child_pid == -1)
11438182445Savsm 		err(1, "fork() failed");
11538182445Savsm 
11638182445Savsm 	if (!child_pid) {
11738182445Savsm 		/* Child - drop privileges and return */
11838182445Savsm 		if (chroot(pw->pw_dir) != 0)
11936b99be9Sbluhm 			err(1, "chroot %s", pw->pw_dir);
120769143deSotto 		if (chdir("/") != 0)
12136b99be9Sbluhm 			err(1, "chdir %s", pw->pw_dir);
12249837e0fSderaadt 
12352da46bbSdjm 		if (setgroups(1, &pw->pw_gid) == -1)
12449837e0fSderaadt 			err(1, "setgroups() failed");
12552da46bbSdjm 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
12652da46bbSdjm 			err(1, "setresgid() failed");
12752da46bbSdjm 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
12852da46bbSdjm 			err(1, "setresuid() failed");
12938182445Savsm 		close(socks[0]);
13038182445Savsm 		priv_fd = socks[1];
131ece520b1Sbluhm 		return;
13238182445Savsm 	}
133ece520b1Sbluhm 	close(socks[1]);
134c9ec0abeSbluhm 
13536b99be9Sbluhm 	if (strchr(argv[0], '/') == NULL)
13636b99be9Sbluhm 		execpath = argv[0];
13736b99be9Sbluhm 	else if ((execpath = realpath(argv[0], NULL)) == NULL)
13836b99be9Sbluhm 		err(1, "realpath %s", argv[0]);
13936b99be9Sbluhm 	if (chdir("/") != 0)
14036b99be9Sbluhm 		err(1, "chdir /");
14136b99be9Sbluhm 
14238182445Savsm 	if (!Debug) {
1433b557aedSderaadt 		close(lockfd);
14438182445Savsm 		dup2(nullfd, STDIN_FILENO);
14538182445Savsm 		dup2(nullfd, STDOUT_FILENO);
14638182445Savsm 		dup2(nullfd, STDERR_FILENO);
14738182445Savsm 	}
14838182445Savsm 	if (nullfd > 2)
14938182445Savsm 		close(nullfd);
15038182445Savsm 
151ece520b1Sbluhm 	if (dup3(socks[0], 3, 0) == -1)
152ece520b1Sbluhm 		err(1, "dup3 priv sock failed");
153f379a153Sbluhm 	if (closefrom(4) == -1)
154f379a153Sbluhm 		err(1, "closefrom 4 failed");
155f379a153Sbluhm 
156ece520b1Sbluhm 	snprintf(childnum, sizeof(childnum), "%d", child_pid);
157ece520b1Sbluhm 	if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
158ece520b1Sbluhm 		err(1, "alloc priv argv failed");
15936b99be9Sbluhm 	privargv[0] = execpath;
16036b99be9Sbluhm 	for (i = 1; i < argc; i++)
161ece520b1Sbluhm 		privargv[i] = argv[i];
162ece520b1Sbluhm 	privargv[i++] = "-P";
163ece520b1Sbluhm 	privargv[i++] = childnum;
164ece520b1Sbluhm 	privargv[i++] = NULL;
165ece520b1Sbluhm 	execvp(privargv[0], privargv);
166ece520b1Sbluhm 	err(1, "exec priv '%s' failed", privargv[0]);
167ece520b1Sbluhm }
16838182445Savsm 
169ece520b1Sbluhm __dead void
priv_exec(char * conf,int numeric,int child,int argc,char * argv[])170ece520b1Sbluhm priv_exec(char *conf, int numeric, int child, int argc, char *argv[])
171ece520b1Sbluhm {
172ece520b1Sbluhm 	int i, fd, sock, cmd, addr_len, result, restart;
173ece520b1Sbluhm 	size_t path_len, protoname_len, hostname_len, servname_len;
174ece520b1Sbluhm 	char path[PATH_MAX], protoname[5];
175ece520b1Sbluhm 	char hostname[NI_MAXHOST], servname[NI_MAXSERV];
176ece520b1Sbluhm 	struct sockaddr_storage addr;
177ece520b1Sbluhm 	struct stat cf_info, cf_stat;
178ece520b1Sbluhm 	struct addrinfo hints, *res0;
179ece520b1Sbluhm 	struct sigaction sa;
180e733ee17Sbluhm 	sigset_t sigmask;
181ece520b1Sbluhm 
182f22826eaSbluhm 	/* Redo the password lookup after re-exec of the privsep parent. */
183f22826eaSbluhm 	pw = getpwnam("_syslogd");
184f22826eaSbluhm 	if (pw == NULL)
185f22826eaSbluhm 		errx(1, "unknown user _syslogd");
186f22826eaSbluhm 
1878d92522dSderaadt 	if (unveil(conf, "r") == -1)
188bc5a8259Sbeck 		err(1, "unveil %s", conf);
1898d92522dSderaadt 	if (unveil(_PATH_UTMP, "r") == -1)
190bc5a8259Sbeck 		err(1, "unveil %s", _PATH_UTMP);
1918d92522dSderaadt 	if (unveil(_PATH_DEV, "rw") == -1)
192bc5a8259Sbeck 		err(1, "unveil %s", _PATH_DEV);
1931a7ae0c4Sbluhm 	if (unveil(_PATH_LOGPID, "c") == -1)
194bc5a8259Sbeck 		err(1, "unveil %s", _PATH_LOGPID);
1958d92522dSderaadt 
1968d92522dSderaadt 	/* for pipes */
1978d92522dSderaadt 	if (unveil(_PATH_BSHELL, "x") == -1)
198bc5a8259Sbeck 		err(1, "unveil %s", _PATH_BSHELL);
1998d92522dSderaadt 
2008d92522dSderaadt 	/* For HUP / re-exec */
2018d92522dSderaadt 	if (unveil("/usr/sbin/syslogd", "x") == -1)
202bc5a8259Sbeck 		err(1, "unveil /usr/sbin/syslogd");
2038d92522dSderaadt 	if (argv[0][0] == '/')
2048d92522dSderaadt 		if (unveil(argv[0], "x") == -1)
205bc5a8259Sbeck 			err(1, "unveil %s", argv[0]);
2068d92522dSderaadt 
2078d92522dSderaadt 	if (pledge("stdio unveil rpath wpath cpath dns sendfd id proc exec",
208ece520b1Sbluhm 	    NULL) == -1)
209ece520b1Sbluhm 		err(1, "pledge priv");
210ece520b1Sbluhm 
211ece520b1Sbluhm 	if (argc <= 2 || strcmp("-P", argv[argc - 2]) != 0)
212ece520b1Sbluhm 		errx(1, "exec without priv");
213ece520b1Sbluhm 	argv[argc -= 2] = NULL;
214ece520b1Sbluhm 
215ece520b1Sbluhm 	sock = 3;
216ece520b1Sbluhm 	closefrom(4);
217ece520b1Sbluhm 
218ece520b1Sbluhm 	child_pid = child;
219ece520b1Sbluhm 
220ece520b1Sbluhm 	memset(&sa, 0, sizeof(sa));
221ece520b1Sbluhm 	sigemptyset(&sa.sa_mask);
222ece520b1Sbluhm 	sa.sa_flags = SA_RESTART;
223ece520b1Sbluhm 	sa.sa_handler = SIG_DFL;
224ece520b1Sbluhm 	for (i = 1; i < _NSIG; i++)
225ece520b1Sbluhm 		sigaction(i, &sa, NULL);
226ece520b1Sbluhm 
227ece520b1Sbluhm 	/* Pass TERM/HUP/INT/QUIT through to child, and accept CHLD */
228ece520b1Sbluhm 	sa.sa_handler = sig_pass_to_chld;
229ece520b1Sbluhm 	sigaction(SIGTERM, &sa, NULL);
230ece520b1Sbluhm 	sigaction(SIGHUP, &sa, NULL);
231ece520b1Sbluhm 	sigaction(SIGINT, &sa, NULL);
232ece520b1Sbluhm 	sigaction(SIGQUIT, &sa, NULL);
233ece520b1Sbluhm 	sa.sa_handler = sig_got_chld;
234ece520b1Sbluhm 	sa.sa_flags |= SA_NOCLDSTOP;
235ece520b1Sbluhm 	sigaction(SIGCHLD, &sa, NULL);
236ece520b1Sbluhm 
237ece520b1Sbluhm 	setproctitle("[priv]");
238ed00ae78Sbluhm 	log_debug("[priv]: fork+exec done");
239ece520b1Sbluhm 
240e733ee17Sbluhm 	sigemptyset(&sigmask);
241e733ee17Sbluhm 	if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
242e733ee17Sbluhm 		err(1, "sigprocmask priv");
243e733ee17Sbluhm 
244df69c215Sderaadt 	if (stat(conf, &cf_info) == -1)
24538182445Savsm 		err(1, "stat config file failed");
24638182445Savsm 
24738182445Savsm 	TAILQ_INIT(&lognames);
24838182445Savsm 	increase_state(STATE_CONFIG);
249927e6d74Scanacar 	restart = 0;
25038182445Savsm 
25138182445Savsm 	while (cur_state < STATE_QUIT) {
252ece520b1Sbluhm 		if (may_read(sock, &cmd, sizeof(int)))
253927e6d74Scanacar 			break;
25438182445Savsm 		switch (cmd) {
25538182445Savsm 		case PRIV_OPEN_TTY:
256ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_OPEN_TTY received");
257878b8a60Savsm 			/* Expecting: length, path */
258ece520b1Sbluhm 			must_read(sock, &path_len, sizeof(size_t));
259878b8a60Savsm 			if (path_len == 0 || path_len > sizeof(path))
260b2be3b7aSbluhm 				_exit(1);
261ece520b1Sbluhm 			must_read(sock, &path, path_len);
262878b8a60Savsm 			path[path_len - 1] = '\0';
26305b66061Sbluhm 			check_tty_name(path, sizeof(path));
264b7041c07Sderaadt 			fd = open(path, O_WRONLY|O_NONBLOCK);
265ece520b1Sbluhm 			send_fd(sock, fd);
266df69c215Sderaadt 			if (fd == -1)
2671451439bSavsm 				warnx("priv_open_tty failed");
268d0480564Savsm 			else
26938182445Savsm 				close(fd);
27038182445Savsm 			break;
27138182445Savsm 
27238182445Savsm 		case PRIV_OPEN_LOG:
27347607485Shenning 		case PRIV_OPEN_PIPE:
274ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_OPEN_%s received",
27547607485Shenning 			    cmd == PRIV_OPEN_PIPE ? "PIPE" : "LOG");
276878b8a60Savsm 			/* Expecting: length, path */
277ece520b1Sbluhm 			must_read(sock, &path_len, sizeof(size_t));
278878b8a60Savsm 			if (path_len == 0 || path_len > sizeof(path))
279b2be3b7aSbluhm 				_exit(1);
280ece520b1Sbluhm 			must_read(sock, &path, path_len);
281878b8a60Savsm 			path[path_len - 1] = '\0';
28205b66061Sbluhm 			check_log_name(path, sizeof(path));
28347607485Shenning 
28447607485Shenning 			if (cmd == PRIV_OPEN_LOG)
28547607485Shenning 				fd = open_file(path);
28647607485Shenning 			else if (cmd == PRIV_OPEN_PIPE)
28747607485Shenning 				fd = open_pipe(path);
28847607485Shenning 			else
28947607485Shenning 				errx(1, "invalid cmd");
29047607485Shenning 
291ece520b1Sbluhm 			send_fd(sock, fd);
292df69c215Sderaadt 			if (fd == -1)
2931451439bSavsm 				warnx("priv_open_log failed");
294d0480564Savsm 			else
29538182445Savsm 				close(fd);
29638182445Savsm 			break;
29738182445Savsm 
29838182445Savsm 		case PRIV_OPEN_UTMP:
299ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_OPEN_UTMP received");
300b7041c07Sderaadt 			fd = open(_PATH_UTMP, O_RDONLY|O_NONBLOCK);
301ece520b1Sbluhm 			send_fd(sock, fd);
302df69c215Sderaadt 			if (fd == -1)
3031451439bSavsm 				warnx("priv_open_utmp failed");
304d0480564Savsm 			else
30538182445Savsm 				close(fd);
30638182445Savsm 			break;
30738182445Savsm 
30838182445Savsm 		case PRIV_OPEN_CONFIG:
309ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_OPEN_CONFIG received");
310ece520b1Sbluhm 			stat(conf, &cf_info);
311b7041c07Sderaadt 			fd = open(conf, O_RDONLY|O_NONBLOCK);
312ece520b1Sbluhm 			send_fd(sock, fd);
313df69c215Sderaadt 			if (fd == -1)
3141451439bSavsm 				warnx("priv_open_config failed");
315d0480564Savsm 			else
31638182445Savsm 				close(fd);
31738182445Savsm 			break;
31838182445Savsm 
31938182445Savsm 		case PRIV_CONFIG_MODIFIED:
320ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_CONFIG_MODIFIED received");
321df69c215Sderaadt 			if (stat(conf, &cf_stat) == -1 ||
32279b54589Sguenther 			    timespeccmp(&cf_info.st_mtim,
32379b54589Sguenther 			    &cf_stat.st_mtim, <) ||
32438182445Savsm 			    cf_info.st_size != cf_stat.st_size) {
325ed00ae78Sbluhm 				log_debug("config file modified: restarting");
326927e6d74Scanacar 				restart = result = 1;
327ece520b1Sbluhm 				must_write(sock, &result, sizeof(int));
32838182445Savsm 			} else {
32938182445Savsm 				result = 0;
330ece520b1Sbluhm 				must_write(sock, &result, sizeof(int));
33138182445Savsm 			}
33238182445Savsm 			break;
33338182445Savsm 
33438182445Savsm 		case PRIV_DONE_CONFIG_PARSE:
3358d92522dSderaadt 			if (pledge("stdio rpath wpath cpath dns sendfd id proc exec",
3368d92522dSderaadt 			    NULL) == -1)
3378d92522dSderaadt 				err(1, "pledge done config");
338ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_DONE_CONFIG_PARSE "
339ed00ae78Sbluhm 			    "received");
34038182445Savsm 			increase_state(STATE_RUNNING);
34138182445Savsm 			break;
34238182445Savsm 
3432fa726a7Sbluhm 		case PRIV_GETADDRINFO:
344ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_GETADDRINFO received");
345ad087aa9Sbluhm 			/* Expecting: len, proto, len, host, len, serv */
346ece520b1Sbluhm 			must_read(sock, &protoname_len, sizeof(size_t));
347ad087aa9Sbluhm 			if (protoname_len == 0 ||
348ad087aa9Sbluhm 			    protoname_len > sizeof(protoname))
349ad087aa9Sbluhm 				_exit(1);
350ece520b1Sbluhm 			must_read(sock, &protoname, protoname_len);
351ad087aa9Sbluhm 			protoname[protoname_len - 1] = '\0';
352ad087aa9Sbluhm 
353ece520b1Sbluhm 			must_read(sock, &hostname_len, sizeof(size_t));
3542fa726a7Sbluhm 			if (hostname_len == 0 ||
3552fa726a7Sbluhm 			    hostname_len > sizeof(hostname))
356b2be3b7aSbluhm 				_exit(1);
357ece520b1Sbluhm 			must_read(sock, &hostname, hostname_len);
358878b8a60Savsm 			hostname[hostname_len - 1] = '\0';
35997f3a029Sdjm 
360ece520b1Sbluhm 			must_read(sock, &servname_len, sizeof(size_t));
3612fa726a7Sbluhm 			if (servname_len == 0 ||
3622fa726a7Sbluhm 			    servname_len > sizeof(servname))
363b2be3b7aSbluhm 				_exit(1);
364ece520b1Sbluhm 			must_read(sock, &servname, servname_len);
36597f3a029Sdjm 			servname[servname_len - 1] = '\0';
36697f3a029Sdjm 
367e470716cSbluhm 			memset(&hints, 0, sizeof(hints));
368cb08fbc7Sbluhm 			switch (strlen(protoname)) {
369cb08fbc7Sbluhm 			case 3:
370e470716cSbluhm 				hints.ai_family = AF_UNSPEC;
371cb08fbc7Sbluhm 				break;
372cb08fbc7Sbluhm 			case 4:
373cb08fbc7Sbluhm 				switch (protoname[3]) {
374cb08fbc7Sbluhm 				case '4':
375ad087aa9Sbluhm 					hints.ai_family = AF_INET;
376cb08fbc7Sbluhm 					break;
377cb08fbc7Sbluhm 				case '6':
378ad087aa9Sbluhm 					hints.ai_family = AF_INET6;
379cb08fbc7Sbluhm 					break;
380cb08fbc7Sbluhm 				default:
381cb08fbc7Sbluhm 					errx(1, "bad ip version %s", protoname);
382cb08fbc7Sbluhm 				}
383cb08fbc7Sbluhm 				break;
384cb08fbc7Sbluhm 			default:
385cb08fbc7Sbluhm 				errx(1, "bad protocol length %s", protoname);
386cb08fbc7Sbluhm 			}
387cb08fbc7Sbluhm 			if (strncmp(protoname, "udp", 3) == 0) {
388cb08fbc7Sbluhm 				hints.ai_socktype = SOCK_DGRAM;
389cb08fbc7Sbluhm 				hints.ai_protocol = IPPROTO_UDP;
390cb08fbc7Sbluhm 			} else if (strncmp(protoname, "tcp", 3) == 0) {
391cb08fbc7Sbluhm 				hints.ai_socktype = SOCK_STREAM;
392cb08fbc7Sbluhm 				hints.ai_protocol = IPPROTO_TCP;
393ad087aa9Sbluhm 			} else {
394ad087aa9Sbluhm 				errx(1, "unknown protocol %s", protoname);
395ad087aa9Sbluhm 			}
39697f3a029Sdjm 			i = getaddrinfo(hostname, servname, &hints, &res0);
39797f3a029Sdjm 			if (i != 0 || res0 == NULL) {
39838182445Savsm 				addr_len = 0;
399ece520b1Sbluhm 				must_write(sock, &addr_len, sizeof(int));
40038182445Savsm 			} else {
40197f3a029Sdjm 				/* Just send the first address */
40297f3a029Sdjm 				i = res0->ai_addrlen;
403ece520b1Sbluhm 				must_write(sock, &i, sizeof(int));
404ece520b1Sbluhm 				must_write(sock, res0->ai_addr, i);
40597f3a029Sdjm 				freeaddrinfo(res0);
40638182445Savsm 			}
40738182445Savsm 			break;
40838182445Savsm 
409bd6df507Sbluhm 		case PRIV_GETNAMEINFO:
410ed00ae78Sbluhm 			log_debug("[priv]: msg PRIV_GETNAMEINFO received");
411ece520b1Sbluhm 			if (numeric)
412bd6df507Sbluhm 				errx(1, "rejected attempt to getnameinfo");
413bd6df507Sbluhm 			/* Expecting: length, sockaddr */
414ece520b1Sbluhm 			must_read(sock, &addr_len, sizeof(int));
41597e72a51Sbluhm 			if (addr_len <= 0 || (size_t)addr_len > sizeof(addr))
416b2be3b7aSbluhm 				_exit(1);
417ece520b1Sbluhm 			must_read(sock, &addr, addr_len);
418bd6df507Sbluhm 			if (getnameinfo((struct sockaddr *)&addr, addr_len,
419bd6df507Sbluhm 			    hostname, sizeof(hostname), NULL, 0,
420bd6df507Sbluhm 			    NI_NOFQDN|NI_NAMEREQD|NI_DGRAM) != 0) {
42138182445Savsm 				addr_len = 0;
422ece520b1Sbluhm 				must_write(sock, &addr_len, sizeof(int));
42338182445Savsm 			} else {
424bd6df507Sbluhm 				addr_len = strlen(hostname) + 1;
425ece520b1Sbluhm 				must_write(sock, &addr_len, sizeof(int));
426ece520b1Sbluhm 				must_write(sock, hostname, addr_len);
42738182445Savsm 			}
42838182445Savsm 			break;
42938182445Savsm 		default:
4301451439bSavsm 			errx(1, "unknown command %d", cmd);
43138182445Savsm 			break;
43238182445Savsm 		}
43338182445Savsm 	}
43438182445Savsm 
435ece520b1Sbluhm 	close(sock);
4364dfa96bcSdjm 
437927e6d74Scanacar 	if (restart) {
438ece520b1Sbluhm 		int status;
43938182445Savsm 
440ece520b1Sbluhm 		waitpid(child_pid, &status, 0);
441e733ee17Sbluhm 		sigemptyset(&sigmask);
442e733ee17Sbluhm 		sigaddset(&sigmask, SIGHUP);
443e733ee17Sbluhm 		if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1)
444e733ee17Sbluhm 			err(1, "sigprocmask exec");
44538182445Savsm 		execvp(argv[0], argv);
446ece520b1Sbluhm 		err(1, "exec restart '%s' failed", argv[0]);
44738182445Savsm 	}
4482f2a83bbSmoritz 	unlink(_PATH_LOGPID);
449ece520b1Sbluhm 	exit(0);
45038182445Savsm }
45138182445Savsm 
45247607485Shenning static int
open_file(char * path)45347607485Shenning open_file(char *path)
45447607485Shenning {
45547607485Shenning 	/* must not start with | */
45647607485Shenning 	if (path[0] == '|')
45747607485Shenning 		return (-1);
45847607485Shenning 
459b7041c07Sderaadt 	return (open(path, O_WRONLY|O_APPEND|O_NONBLOCK));
46047607485Shenning }
46147607485Shenning 
46247607485Shenning static int
open_pipe(char * cmd)46347607485Shenning open_pipe(char *cmd)
46447607485Shenning {
46547607485Shenning 	char *argp[] = {"sh", "-c", NULL, NULL};
46647607485Shenning 	int fd[2];
46747607485Shenning 	int bsize, flags;
46847607485Shenning 	pid_t pid;
46947607485Shenning 
47047607485Shenning 	/* skip over leading | and whitespace */
47147607485Shenning 	if (cmd[0] != '|')
47247607485Shenning 		return (-1);
47347607485Shenning 	for (cmd++; *cmd && *cmd == ' '; cmd++)
47447607485Shenning 		; /* nothing */
47547607485Shenning 	if (!*cmd)
47647607485Shenning 		return (-1);
47747607485Shenning 
47847607485Shenning 	argp[2] = cmd;
47947607485Shenning 
48047607485Shenning 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) {
48119f11518Sdjm 		warnx("open_pipe");
48247607485Shenning 		return (-1);
48347607485Shenning 	}
48447607485Shenning 
48547607485Shenning 	/* make the fd on syslogd's side nonblocking */
4867bb95e13Skrw 	if ((flags = fcntl(fd[1], F_GETFL)) == -1) {
48719f11518Sdjm 		warnx("fcntl");
48847607485Shenning 		return (-1);
48947607485Shenning 	}
49047607485Shenning 	flags |= O_NONBLOCK;
49147607485Shenning 	if ((flags = fcntl(fd[1], F_SETFL, flags)) == -1) {
49219f11518Sdjm 		warnx("fcntl");
49347607485Shenning 		return (-1);
49447607485Shenning 	}
49547607485Shenning 
49647607485Shenning 	switch (pid = fork()) {
49747607485Shenning 	case -1:
49819f11518Sdjm 		warnx("fork error");
49947607485Shenning 		return (-1);
50047607485Shenning 	case 0:
50147607485Shenning 		break;
50247607485Shenning 	default:
50347607485Shenning 		close(fd[0]);
50447607485Shenning 		return (fd[1]);
50547607485Shenning 	}
50647607485Shenning 
50747607485Shenning 	close(fd[1]);
50847607485Shenning 
50947607485Shenning 	/* grow receive buffer */
51047607485Shenning 	bsize = 65535;
51147607485Shenning 	while (bsize > 0 && setsockopt(fd[0], SOL_SOCKET, SO_RCVBUF,
51247607485Shenning 	    &bsize, sizeof(bsize)) == -1)
51347607485Shenning 		bsize /= 2;
51447607485Shenning 
51547607485Shenning 	if (setgroups(1, &pw->pw_gid) == -1 ||
51647607485Shenning 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
51747607485Shenning 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
51847607485Shenning 		err(1, "failure dropping privs");
51947607485Shenning 
52047607485Shenning 	if (dup2(fd[0], STDIN_FILENO) == -1)
52147607485Shenning 		err(1, "dup2 failed");
5222338eccdSmartijn 	closefrom(STDERR_FILENO + 1);
52347607485Shenning 	if (execv("/bin/sh", argp) == -1)
52447607485Shenning 		err(1, "execv %s", cmd);
52519f11518Sdjm 	/* NOTREACHED */
52619f11518Sdjm 	return (-1);
52747607485Shenning }
52847607485Shenning 
52938182445Savsm /* Check that the terminal device is ok, and if not, rewrite to /dev/null.
53038182445Savsm  * Either /dev/console or /dev/tty* are allowed.
53138182445Savsm  */
53238182445Savsm static void
check_tty_name(char * tty,size_t ttysize)53305b66061Sbluhm check_tty_name(char *tty, size_t ttysize)
53438182445Savsm {
53538182445Savsm 	const char ttypre[] = "/dev/tty";
53638182445Savsm 	char *p;
53738182445Savsm 
53838182445Savsm 	/* Any path containing '..' is invalid.  */
53905b66061Sbluhm 	for (p = tty; p + 1 < tty + ttysize && *p; p++)
54038182445Savsm 		if (*p == '.' && *(p + 1) == '.')
54138182445Savsm 			goto bad_path;
54238182445Savsm 
54338182445Savsm 	if (strcmp(_PATH_CONSOLE, tty) && strncmp(tty, ttypre, strlen(ttypre)))
54438182445Savsm 		goto bad_path;
54538182445Savsm 	return;
54638182445Savsm 
54738182445Savsm bad_path:
54838182445Savsm 	warnx ("%s: invalid attempt to open %s: rewriting to /dev/null",
549070b30bfSderaadt 	    "check_tty_name", tty);
55005b66061Sbluhm 	strlcpy(tty, "/dev/null", ttysize);
55138182445Savsm }
55238182445Savsm 
55338182445Savsm /* If we are in the initial configuration state, accept a logname and add
55438182445Savsm  * it to the list of acceptable logfiles.  Otherwise, check against this list
55538182445Savsm  * and rewrite to /dev/null if it's a bad path.
55638182445Savsm  */
55738182445Savsm static void
check_log_name(char * lognam,size_t logsize)55805b66061Sbluhm check_log_name(char *lognam, size_t logsize)
55938182445Savsm {
56038182445Savsm 	struct logname *lg;
56138182445Savsm 	char *p;
56238182445Savsm 
56338182445Savsm 	/* Any path containing '..' is invalid.  */
56405b66061Sbluhm 	for (p = lognam; p + 1 < lognam + logsize && *p; p++)
56538182445Savsm 		if (*p == '.' && *(p + 1) == '.')
56638182445Savsm 			goto bad_path;
56738182445Savsm 
56838182445Savsm 	switch (cur_state) {
56938182445Savsm 	case STATE_CONFIG:
57038182445Savsm 		lg = malloc(sizeof(struct logname));
57138182445Savsm 		if (!lg)
57238182445Savsm 			err(1, "check_log_name() malloc");
573b9fc9a72Sderaadt 		strlcpy(lg->path, lognam, PATH_MAX);
57438182445Savsm 		TAILQ_INSERT_TAIL(&lognames, lg, next);
5758d92522dSderaadt 		if (lognam[0] != '|') {
5768d92522dSderaadt 			if (unveil(lognam, "w") == -1)
5778d92522dSderaadt 				goto bad_path;
5788d92522dSderaadt 		}
57938182445Savsm 		break;
58038182445Savsm 	case STATE_RUNNING:
58138182445Savsm 		TAILQ_FOREACH(lg, &lognames, next)
582a4570f53Sdjm 			if (!strcmp(lg->path, lognam))
58338182445Savsm 				return;
58438182445Savsm 		goto bad_path;
58538182445Savsm 		break;
58638182445Savsm 	default:
58738182445Savsm 		/* Any other state should just refuse the request */
58838182445Savsm 		goto bad_path;
58938182445Savsm 		break;
59038182445Savsm 	}
59138182445Savsm 	return;
59238182445Savsm 
59338182445Savsm bad_path:
59438182445Savsm 	warnx("%s: invalid attempt to open %s: rewriting to /dev/null",
595070b30bfSderaadt 	    "check_log_name", lognam);
59605b66061Sbluhm 	strlcpy(lognam, "/dev/null", logsize);
59738182445Savsm }
59838182445Savsm 
59938182445Savsm /* Crank our state into less permissive modes */
60038182445Savsm static void
increase_state(int state)60138182445Savsm increase_state(int state)
60238182445Savsm {
60338182445Savsm 	if (state <= cur_state)
60438182445Savsm 		errx(1, "attempt to decrease or match current state");
605927e6d74Scanacar 	if (state < STATE_INIT || state > STATE_QUIT)
60638182445Savsm 		errx(1, "attempt to switch to invalid state");
60738182445Savsm 	cur_state = state;
60838182445Savsm }
60938182445Savsm 
61038182445Savsm /* Open console or a terminal device for writing */
61138182445Savsm int
priv_open_tty(const char * tty)61238182445Savsm priv_open_tty(const char *tty)
61338182445Savsm {
614b9fc9a72Sderaadt 	char path[PATH_MAX];
61538182445Savsm 	int cmd, fd;
616878b8a60Savsm 	size_t path_len;
61738182445Savsm 
61838182445Savsm 	if (priv_fd < 0)
61952c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
62038182445Savsm 
62138182445Savsm 	if (strlcpy(path, tty, sizeof path) >= sizeof(path))
62238182445Savsm 		return -1;
623878b8a60Savsm 	path_len = strlen(path) + 1;
624878b8a60Savsm 
62538182445Savsm 	cmd = PRIV_OPEN_TTY;
62638182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
627878b8a60Savsm 	must_write(priv_fd, &path_len, sizeof(size_t));
628878b8a60Savsm 	must_write(priv_fd, path, path_len);
62938182445Savsm 	fd = receive_fd(priv_fd);
63038182445Savsm 	return fd;
63138182445Savsm }
63238182445Savsm 
63338182445Savsm /* Open log-file */
63438182445Savsm int
priv_open_log(const char * lognam)635a4570f53Sdjm priv_open_log(const char *lognam)
63638182445Savsm {
637b9fc9a72Sderaadt 	char path[PATH_MAX];
63838182445Savsm 	int cmd, fd;
639878b8a60Savsm 	size_t path_len;
64038182445Savsm 
64138182445Savsm 	if (priv_fd < 0)
64252c0045bSbluhm 		errx(1, "%s: called from privileged child", __func__);
64338182445Savsm 
644a4570f53Sdjm 	if (strlcpy(path, lognam, sizeof path) >= sizeof(path))
64538182445Savsm 		return -1;
646878b8a60Savsm 	path_len = strlen(path) + 1;
647878b8a60Savsm 
64847607485Shenning 	if (lognam[0] == '|')
64947607485Shenning 		cmd = PRIV_OPEN_PIPE;
65047607485Shenning 	else
65138182445Savsm 		cmd = PRIV_OPEN_LOG;
65238182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
653878b8a60Savsm 	must_write(priv_fd, &path_len, sizeof(size_t));
654878b8a60Savsm 	must_write(priv_fd, path, path_len);
65538182445Savsm 	fd = receive_fd(priv_fd);
65638182445Savsm 	return fd;
65738182445Savsm }
65838182445Savsm 
65938182445Savsm /* Open utmp for reading */
66038182445Savsm FILE *
priv_open_utmp(void)66138182445Savsm priv_open_utmp(void)
66238182445Savsm {
66338182445Savsm 	int cmd, fd;
66438182445Savsm 	FILE *fp;
66538182445Savsm 
66638182445Savsm 	if (priv_fd < 0)
66752c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
66838182445Savsm 
66938182445Savsm 	cmd = PRIV_OPEN_UTMP;
67038182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
67138182445Savsm 	fd = receive_fd(priv_fd);
67238182445Savsm 	if (fd < 0)
67338182445Savsm 		return NULL;
67438182445Savsm 
67538182445Savsm 	fp = fdopen(fd, "r");
67638182445Savsm 	if (!fp) {
67738182445Savsm 		warn("priv_open_utmp: fdopen() failed");
67838182445Savsm 		close(fd);
67938182445Savsm 		return NULL;
68038182445Savsm 	}
68138182445Savsm 
68238182445Savsm 	return fp;
68338182445Savsm }
68438182445Savsm 
68538182445Savsm /* Open syslog config file for reading */
68638182445Savsm FILE *
priv_open_config(void)68738182445Savsm priv_open_config(void)
68838182445Savsm {
68938182445Savsm 	int cmd, fd;
69038182445Savsm 	FILE *fp;
69138182445Savsm 
69238182445Savsm 	if (priv_fd < 0)
69352c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
69438182445Savsm 
69538182445Savsm 	cmd = PRIV_OPEN_CONFIG;
69638182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
69738182445Savsm 	fd = receive_fd(priv_fd);
69838182445Savsm 	if (fd < 0)
69938182445Savsm 		return NULL;
70038182445Savsm 
70138182445Savsm 	fp = fdopen(fd, "r");
70238182445Savsm 	if (!fp) {
70338182445Savsm 		warn("priv_open_config: fdopen() failed");
70438182445Savsm 		close(fd);
70538182445Savsm 		return NULL;
70638182445Savsm 	}
70738182445Savsm 
70838182445Savsm 	return fp;
70938182445Savsm }
71038182445Savsm 
71138182445Savsm /* Ask if config file has been modified since last attempt to read it */
71238182445Savsm int
priv_config_modified(void)7133dad9bb3Sderaadt priv_config_modified(void)
71438182445Savsm {
71538182445Savsm 	int cmd, res;
71638182445Savsm 
71738182445Savsm 	if (priv_fd < 0)
71852c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
7191451439bSavsm 
72038182445Savsm 	cmd = PRIV_CONFIG_MODIFIED;
72138182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
72238182445Savsm 
72338182445Savsm 	/* Expect back integer signalling 1 for modification */
72438182445Savsm 	must_read(priv_fd, &res, sizeof(int));
72538182445Savsm 	return res;
72638182445Savsm }
72738182445Savsm 
72838182445Savsm /* Child can signal that its initial parsing is done, so that parent
72938182445Savsm  * can revoke further logfile permissions.  This call only works once. */
73038182445Savsm void
priv_config_parse_done(void)73138182445Savsm priv_config_parse_done(void)
73238182445Savsm {
73338182445Savsm 	int cmd;
73438182445Savsm 
73538182445Savsm 	if (priv_fd < 0)
73652c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
73738182445Savsm 
73838182445Savsm 	cmd = PRIV_DONE_CONFIG_PARSE;
73938182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
74038182445Savsm }
74138182445Savsm 
742bd6df507Sbluhm /* Name/service to address translation.  Response is placed into addr.
743bd6df507Sbluhm  * Return 0 for success or < 0 for error like getaddrinfo(3) */
74438182445Savsm int
priv_getaddrinfo(const char * proto,const char * host,const char * serv,struct sockaddr * addr,size_t addr_len)745*c1f8818cSbluhm priv_getaddrinfo(const char *proto, const char *host, const char *serv,
746*c1f8818cSbluhm     struct sockaddr *addr, size_t addr_len)
74738182445Savsm {
74838bc45fcSbluhm 	char protocpy[5], hostcpy[NI_MAXHOST], servcpy[NI_MAXSERV];
74938182445Savsm 	int cmd, ret_len;
750ad087aa9Sbluhm 	size_t protoname_len, hostname_len, servname_len;
75138182445Savsm 
75238182445Savsm 	if (priv_fd < 0)
75352c0045bSbluhm 		errx(1, "%s: called from privileged portion", __func__);
75438182445Savsm 
755ad087aa9Sbluhm 	if (strlcpy(protocpy, proto, sizeof(protocpy)) >= sizeof(protocpy))
756ad087aa9Sbluhm 		errx(1, "%s: overflow attempt in protoname", __func__);
757ad087aa9Sbluhm 	protoname_len = strlen(protocpy) + 1;
758ad087aa9Sbluhm 	if (strlcpy(hostcpy, host, sizeof(hostcpy)) >= sizeof(hostcpy))
75952c0045bSbluhm 		errx(1, "%s: overflow attempt in hostname", __func__);
760878b8a60Savsm 	hostname_len = strlen(hostcpy) + 1;
761ad087aa9Sbluhm 	if (strlcpy(servcpy, serv, sizeof(servcpy)) >= sizeof(servcpy))
76252c0045bSbluhm 		errx(1, "%s: overflow attempt in servname", __func__);
76397f3a029Sdjm 	servname_len = strlen(servcpy) + 1;
764878b8a60Savsm 
7652fa726a7Sbluhm 	cmd = PRIV_GETADDRINFO;
76638182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
767ad087aa9Sbluhm 	must_write(priv_fd, &protoname_len, sizeof(size_t));
768ad087aa9Sbluhm 	must_write(priv_fd, protocpy, protoname_len);
769878b8a60Savsm 	must_write(priv_fd, &hostname_len, sizeof(size_t));
770878b8a60Savsm 	must_write(priv_fd, hostcpy, hostname_len);
77197f3a029Sdjm 	must_write(priv_fd, &servname_len, sizeof(size_t));
77297f3a029Sdjm 	must_write(priv_fd, servcpy, servname_len);
77338182445Savsm 
77438182445Savsm 	/* Expect back an integer size, and then a string of that length */
77538182445Savsm 	must_read(priv_fd, &ret_len, sizeof(int));
77638182445Savsm 
77738182445Savsm 	/* Check there was no error (indicated by a return of 0) */
77838182445Savsm 	if (!ret_len)
7792fa726a7Sbluhm 		return (-1);
78038182445Savsm 
78138182445Savsm 	/* Make sure we aren't overflowing the passed in buffer */
78297e72a51Sbluhm 	if (ret_len < 0 || (size_t)ret_len > addr_len)
78352c0045bSbluhm 		errx(1, "%s: overflow attempt in return", __func__);
78438182445Savsm 
78538182445Savsm 	/* Read the resolved address and make sure we got all of it */
78697f3a029Sdjm 	memset(addr, '\0', addr_len);
78738182445Savsm 	must_read(priv_fd, addr, ret_len);
78897f3a029Sdjm 
7892fa726a7Sbluhm 	return (0);
79038182445Savsm }
79138182445Savsm 
792bd6df507Sbluhm /* Reverse address resolution; response is placed into host.
793bd6df507Sbluhm  * Return 0 for success or < 0 for error like getnameinfo(3) */
79438182445Savsm int
priv_getnameinfo(struct sockaddr * sa,socklen_t salen,char * host,size_t hostlen)795bd6df507Sbluhm priv_getnameinfo(struct sockaddr *sa, socklen_t salen, char *host,
796bd6df507Sbluhm     size_t hostlen)
79738182445Savsm {
79838182445Savsm 	int cmd, ret_len;
79938182445Savsm 
80038182445Savsm 	if (priv_fd < 0)
80152c0045bSbluhm 		errx(1, "%s called from privileged portion", __func__);
80238182445Savsm 
803bd6df507Sbluhm 	cmd = PRIV_GETNAMEINFO;
80438182445Savsm 	must_write(priv_fd, &cmd, sizeof(int));
805bd6df507Sbluhm 	must_write(priv_fd, &salen, sizeof(int));
806bd6df507Sbluhm 	must_write(priv_fd, sa, salen);
80738182445Savsm 
80838182445Savsm 	/* Expect back an integer size, and then a string of that length */
80938182445Savsm 	must_read(priv_fd, &ret_len, sizeof(int));
81038182445Savsm 
81138182445Savsm 	/* Check there was no error (indicated by a return of 0) */
8121451439bSavsm 	if (!ret_len)
813bd6df507Sbluhm 		return (-1);
81438182445Savsm 
81538182445Savsm 	/* Check we don't overflow the passed in buffer */
81697e72a51Sbluhm 	if (ret_len < 0 || (size_t)ret_len > hostlen)
81752c0045bSbluhm 		errx(1, "%s: overflow attempt in return", __func__);
81838182445Savsm 
81938182445Savsm 	/* Read the resolved hostname */
820bd6df507Sbluhm 	must_read(priv_fd, host, ret_len);
821bd6df507Sbluhm 	return (0);
82238182445Savsm }
82338182445Savsm 
824365bac07Scanacar /* Pass the signal through to child */
82538182445Savsm static void
sig_pass_to_chld(int sig)82638182445Savsm sig_pass_to_chld(int sig)
82738182445Savsm {
8283d480636Sderaadt 	int save_errno = errno;
8291502b2edSotto 
830927e6d74Scanacar 	if (child_pid != -1)
83138182445Savsm 		kill(child_pid, sig);
8323d480636Sderaadt 	errno = save_errno;
83338182445Savsm }
83438182445Savsm 
83538182445Savsm /* When child dies, move into the shutdown state */
83638182445Savsm static void
sig_got_chld(int sig)83738182445Savsm sig_got_chld(int sig)
83838182445Savsm {
8393d480636Sderaadt 	int save_errno = errno;
84047607485Shenning 	pid_t	pid;
84147607485Shenning 
84247607485Shenning 	do {
84347607485Shenning 		pid = waitpid(WAIT_ANY, NULL, WNOHANG);
84414aef417Smpf 		if (pid == child_pid && cur_state < STATE_QUIT)
84538182445Savsm 			cur_state = STATE_QUIT;
84614aef417Smpf 	} while (pid > 0 || (pid == -1 && errno == EINTR));
8473d480636Sderaadt 	errno = save_errno;
84838182445Savsm }
84938182445Savsm 
850927e6d74Scanacar /* Read all data or return 1 for error.  */
851927e6d74Scanacar static int
may_read(int fd,void * buf,size_t n)852927e6d74Scanacar may_read(int fd, void *buf, size_t n)
853927e6d74Scanacar {
854927e6d74Scanacar 	char *s = buf;
85597e72a51Sbluhm 	ssize_t res;
85697e72a51Sbluhm 	size_t pos = 0;
857927e6d74Scanacar 
858927e6d74Scanacar 	while (n > pos) {
859927e6d74Scanacar 		res = read(fd, s + pos, n - pos);
860927e6d74Scanacar 		switch (res) {
861927e6d74Scanacar 		case -1:
862927e6d74Scanacar 			if (errno == EINTR || errno == EAGAIN)
863927e6d74Scanacar 				continue;
864927e6d74Scanacar 		case 0:
865927e6d74Scanacar 			return (1);
866927e6d74Scanacar 		default:
867927e6d74Scanacar 			pos += res;
868927e6d74Scanacar 		}
869927e6d74Scanacar 	}
870927e6d74Scanacar 	return (0);
871927e6d74Scanacar }
872927e6d74Scanacar 
87338182445Savsm /* Read data with the assertion that it all must come through, or
87438182445Savsm  * else abort the process.  Based on atomicio() from openssh. */
87538182445Savsm static void
must_read(int fd,void * buf,size_t n)87638182445Savsm must_read(int fd, void *buf, size_t n)
87738182445Savsm {
87838182445Savsm 	char *s = buf;
87997e72a51Sbluhm 	ssize_t res;
88097e72a51Sbluhm 	size_t pos = 0;
88138182445Savsm 
88238182445Savsm 	while (n > pos) {
88338182445Savsm 		res = read(fd, s + pos, n - pos);
88438182445Savsm 		switch (res) {
88538182445Savsm 		case -1:
88638182445Savsm 			if (errno == EINTR || errno == EAGAIN)
88738182445Savsm 				continue;
88838182445Savsm 		case 0:
889b2be3b7aSbluhm 			_exit(1);
89038182445Savsm 		default:
89138182445Savsm 			pos += res;
89238182445Savsm 		}
89338182445Savsm 	}
89438182445Savsm }
89538182445Savsm 
89638182445Savsm /* Write data with the assertion that it all has to be written, or
89738182445Savsm  * else abort the process.  Based on atomicio() from openssh. */
89838182445Savsm static void
must_write(int fd,void * buf,size_t n)89938182445Savsm must_write(int fd, void *buf, size_t n)
90038182445Savsm {
90138182445Savsm 	char *s = buf;
90297e72a51Sbluhm 	ssize_t res;
90397e72a51Sbluhm 	size_t pos = 0;
90438182445Savsm 
90538182445Savsm 	while (n > pos) {
90638182445Savsm 		res = write(fd, s + pos, n - pos);
90738182445Savsm 		switch (res) {
90838182445Savsm 		case -1:
90938182445Savsm 			if (errno == EINTR || errno == EAGAIN)
91038182445Savsm 				continue;
91138182445Savsm 		case 0:
912b2be3b7aSbluhm 			_exit(1);
91338182445Savsm 		default:
91438182445Savsm 			pos += res;
91538182445Savsm 		}
91638182445Savsm 	}
91738182445Savsm }
918