xref: /openbsd-src/sbin/isakmpd/monitor.c (revision ff9f425c2bdebe956f8fce43c085b69af60129ec)
1*ff9f425cStb /* $OpenBSD: monitor.c,v 1.83 2023/02/08 08:03:11 tb Exp $	 */
2da35d433Sho 
3da35d433Sho /*
4da35d433Sho  * Copyright (c) 2003 H�kan Olsson.  All rights reserved.
5da35d433Sho  *
6da35d433Sho  * Redistribution and use in source and binary forms, with or without
7da35d433Sho  * modification, are permitted provided that the following conditions
8da35d433Sho  * are met:
9da35d433Sho  * 1. Redistributions of source code must retain the above copyright
10da35d433Sho  *    notice, this list of conditions and the following disclaimer.
11da35d433Sho  * 2. Redistributions in binary form must reproduce the above copyright
12da35d433Sho  *    notice, this list of conditions and the following disclaimer in the
13da35d433Sho  *    documentation and/or other materials provided with the distribution.
14da35d433Sho  *
15da35d433Sho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16da35d433Sho  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17da35d433Sho  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18da35d433Sho  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19da35d433Sho  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20da35d433Sho  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21da35d433Sho  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22da35d433Sho  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23da35d433Sho  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24da35d433Sho  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25da35d433Sho  */
26da35d433Sho 
27da35d433Sho #include <sys/types.h>
28da35d433Sho #include <sys/socket.h>
29da35d433Sho #include <sys/ioctl.h>
30da35d433Sho #include <sys/stat.h>
310191409fSho #include <sys/wait.h>
3231516eecShshoexer #include <netinet/in.h>
3398777286Shshoexer 
3498777286Shshoexer #include <dirent.h>
35da35d433Sho #include <errno.h>
36da35d433Sho #include <fcntl.h>
37da35d433Sho #include <pwd.h>
38da35d433Sho #include <signal.h>
39da35d433Sho #include <stdlib.h>
40da35d433Sho #include <string.h>
41da35d433Sho #include <unistd.h>
42b9fc9a72Sderaadt #include <limits.h>
43da35d433Sho 
44ea1948caSho #include <regex.h>
45ea1948caSho #include <keynote.h>
46ea1948caSho 
47ea1948caSho #include "conf.h"
48da35d433Sho #include "log.h"
49da35d433Sho #include "monitor.h"
50ea1948caSho #include "policy.h"
51b09056b7Shshoexer #include "ui.h"
52ea1948caSho #include "util.h"
531ec19420Shshoexer #include "pf_key_v2.h"
54da35d433Sho 
55fb9475d6Sderaadt struct monitor_state {
56da35d433Sho 	pid_t           pid;
57da35d433Sho 	int             s;
58b9fc9a72Sderaadt 	char            root[PATH_MAX];
59da35d433Sho } m_state;
60da35d433Sho 
61e8a27e95Smoritz extern char *pid_file;
6231516eecShshoexer 
63b8909177Smoritz extern void	set_slave_signals(void);
64b8909177Smoritz 
65da35d433Sho /* Private functions.  */
666c5cc4d7Smoritz static void	must_read(void *, size_t);
676c5cc4d7Smoritz static void	must_write(const void *, size_t);
68da35d433Sho 
696c5cc4d7Smoritz static void	m_priv_getfd(void);
706c5cc4d7Smoritz static void	m_priv_setsockopt(void);
7177033115Smoritz static void	m_priv_req_readdir(void);
726c5cc4d7Smoritz static void	m_priv_bind(void);
736c5cc4d7Smoritz static void	m_priv_pfkey_open(void);
74811d2842Sbluhm static int	m_priv_local_sanitize_path(const char *, size_t, int);
7531516eecShshoexer static int	m_priv_check_sockopt(int, int);
7631516eecShshoexer static int	m_priv_check_bind(const struct sockaddr *, socklen_t);
77ea1948caSho 
78b8909177Smoritz static void	set_monitor_signals(void);
79b8909177Smoritz static void	sig_pass_to_chld(int);
80b8909177Smoritz 
81da35d433Sho /*
82da35d433Sho  * Public functions, unprivileged.
83da35d433Sho  */
84da35d433Sho 
85da35d433Sho /* Setup monitor context, fork, drop child privs.  */
86da35d433Sho pid_t
monitor_init(int debug)87db6e54dbSho monitor_init(int debug)
88da35d433Sho {
89da35d433Sho 	struct passwd  *pw;
90da35d433Sho 	int             p[2];
91fb9475d6Sderaadt 
920dc10397Shshoexer 	bzero(&m_state, sizeof m_state);
93da35d433Sho 
94da35d433Sho 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0)
95da35d433Sho 		log_fatal("monitor_init: socketpair() failed");
96da35d433Sho 
97da35d433Sho 	pw = getpwnam(ISAKMPD_PRIVSEP_USER);
98da35d433Sho 	if (pw == NULL)
995695480dShshoexer 		log_fatalx("monitor_init: getpwnam(\"%s\") failed",
100da35d433Sho 		    ISAKMPD_PRIVSEP_USER);
1015695480dShshoexer 	strlcpy(m_state.root, pw->pw_dir, sizeof m_state.root);
102da35d433Sho 
103b8909177Smoritz 	set_monitor_signals();
104da35d433Sho 	m_state.pid = fork();
105da35d433Sho 
1065695480dShshoexer 	if (m_state.pid == -1)
10737b1005cSmoritz 		log_fatal("monitor_init: fork of unprivileged child failed");
1085695480dShshoexer 	if (m_state.pid == 0) {
1095695480dShshoexer 		/* The child process drops privileges. */
110b8909177Smoritz 		set_slave_signals();
1115695480dShshoexer 
112c4a356f1Shshoexer 		if (chroot(pw->pw_dir) != 0 || chdir("/") != 0)
113c4a356f1Shshoexer 			log_fatal("monitor_init: chroot failed");
114da35d433Sho 
1155695480dShshoexer 		if (setgroups(1, &pw->pw_gid) == -1 ||
1165695480dShshoexer 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1175695480dShshoexer 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1185695480dShshoexer 			log_fatal("monitor_init: can't drop privileges");
1195695480dShshoexer 
1205695480dShshoexer 		m_state.s = p[0];
1215695480dShshoexer 		close(p[1]);
122da35d433Sho 
123da35d433Sho 		LOG_DBG((LOG_MISC, 10,
124da35d433Sho 		    "monitor_init: privileges dropped for child process"));
125fb9475d6Sderaadt 	} else {
1265695480dShshoexer 		/* Privileged monitor. */
127da35d433Sho 		setproctitle("monitor [priv]");
1285695480dShshoexer 
1295695480dShshoexer 		m_state.s = p[1];
1305695480dShshoexer 		close(p[0]);
131da35d433Sho 	}
132da35d433Sho 
133db6e54dbSho 	/* With "-dd", stop and wait here. For gdb "attach" etc.  */
134db6e54dbSho 	if (debug > 1) {
135db6e54dbSho 		log_print("monitor_init: stopped %s PID %d fd %d%s",
136db6e54dbSho 		    m_state.pid ? "priv" : "child", getpid(), m_state.s,
137db6e54dbSho 		    m_state.pid ? ", waiting for SIGCONT" : "");
138db6e54dbSho 		kill(getpid(), SIGSTOP);	/* Wait here for SIGCONT.  */
139db6e54dbSho 		if (m_state.pid)
140db6e54dbSho 			kill(m_state.pid, SIGCONT); /* Continue child.  */
141db6e54dbSho 	}
142db6e54dbSho 
143da35d433Sho 	return m_state.pid;
144da35d433Sho }
145da35d433Sho 
146b09056b7Shshoexer void
monitor_exit(int code)147b09056b7Shshoexer monitor_exit(int code)
148b09056b7Shshoexer {
149356e1c1cSyasuoka 	int status = 0, gotstatus = 0;
150e8a27e95Smoritz 	pid_t pid;
151e8a27e95Smoritz 
152e8a27e95Smoritz 	if (m_state.pid != 0) {
153e8a27e95Smoritz 		/* When called from the monitor, kill slave and wait for it  */
154e8a27e95Smoritz 		kill(m_state.pid, SIGTERM);
155e8a27e95Smoritz 
156e8a27e95Smoritz 		do {
157e8a27e95Smoritz 			pid = waitpid(m_state.pid, &status, 0);
158e8a27e95Smoritz 		} while (pid == -1 && errno == EINTR);
159356e1c1cSyasuoka 		if (pid != -1)
160356e1c1cSyasuoka 			gotstatus = 1;
161e8a27e95Smoritz 
162e8a27e95Smoritz 		/* Remove FIFO and pid files.  */
163e8a27e95Smoritz 		unlink(ui_fifo);
164e8a27e95Smoritz 		unlink(pid_file);
165e8a27e95Smoritz 	}
166b09056b7Shshoexer 
167366b3c74Shshoexer 	close(m_state.s);
168356e1c1cSyasuoka 	if (code == 0 && gotstatus)
169356e1c1cSyasuoka 		exit(WIFEXITED(status)? WEXITSTATUS(status) : 1);
170356e1c1cSyasuoka 	else
171b09056b7Shshoexer 		exit(code);
172b09056b7Shshoexer }
173b09056b7Shshoexer 
174da35d433Sho int
monitor_pf_key_v2_open(void)1751ec19420Shshoexer monitor_pf_key_v2_open(void)
1761ec19420Shshoexer {
177ca544a24Shshoexer 	int	err, cmd;
1781ec19420Shshoexer 
179ca544a24Shshoexer 	cmd = MONITOR_PFKEY_OPEN;
1806c5cc4d7Smoritz 	must_write(&cmd, sizeof cmd);
1811ec19420Shshoexer 
1826c5cc4d7Smoritz 	must_read(&err, sizeof err);
1831ec19420Shshoexer 	if (err < 0) {
1841ec19420Shshoexer 		log_error("monitor_pf_key_v2_open: parent could not create "
1851ec19420Shshoexer 		    "PF_KEY socket");
1861ec19420Shshoexer 		return -1;
1871ec19420Shshoexer 	}
1881ec19420Shshoexer 	pf_key_v2_socket = mm_receive_fd(m_state.s);
1891ec19420Shshoexer 	if (pf_key_v2_socket < 0) {
1906eb09906Smoritz 		log_error("monitor_pf_key_v2_open: mm_receive_fd() failed");
1911ec19420Shshoexer 		return -1;
1921ec19420Shshoexer 	}
1931ec19420Shshoexer 
194ca544a24Shshoexer 	return pf_key_v2_socket;
1951ec19420Shshoexer }
1961ec19420Shshoexer 
1971ec19420Shshoexer int
monitor_open(const char * path,int flags,mode_t mode)198da35d433Sho monitor_open(const char *path, int flags, mode_t mode)
199da35d433Sho {
200a8f2fa08Scloder 	size_t	len;
201a8f2fa08Scloder 	int	fd, err, cmd;
202b9fc9a72Sderaadt 	char	pathreal[PATH_MAX];
203da35d433Sho 
204da35d433Sho 	if (path[0] == '/')
20525337b8eShshoexer 		strlcpy(pathreal, path, sizeof pathreal);
206da35d433Sho 	else
20725337b8eShshoexer 		snprintf(pathreal, sizeof pathreal, "%s/%s", m_state.root,
20812f43dabShshoexer 		    path);
209da35d433Sho 
210ca544a24Shshoexer 	cmd = MONITOR_GET_FD;
2116c5cc4d7Smoritz 	must_write(&cmd, sizeof cmd);
212da35d433Sho 
213d0f6fb84Smoritz 	len = strlen(pathreal);
2146c5cc4d7Smoritz 	must_write(&len, sizeof len);
2156c5cc4d7Smoritz 	must_write(&pathreal, len);
216da35d433Sho 
2176c5cc4d7Smoritz 	must_write(&flags, sizeof flags);
2186c5cc4d7Smoritz 	must_write(&mode, sizeof mode);
219da35d433Sho 
2206c5cc4d7Smoritz 	must_read(&err, sizeof err);
221fb9475d6Sderaadt 	if (err != 0) {
222ca544a24Shshoexer 		errno = err;
22331516eecShshoexer 		return -1;
22431516eecShshoexer 	}
225ca544a24Shshoexer 
226da35d433Sho 	fd = mm_receive_fd(m_state.s);
227fb9475d6Sderaadt 	if (fd < 0) {
228aff085cdSmoritz 		log_error("monitor_open: mm_receive_fd () failed");
229da35d433Sho 		return -1;
230da35d433Sho 	}
231da35d433Sho 
232ca544a24Shshoexer 	return fd;
233da35d433Sho }
234da35d433Sho 
235da35d433Sho FILE *
monitor_fopen(const char * path,const char * mode)236da35d433Sho monitor_fopen(const char *path, const char *mode)
237da35d433Sho {
238da35d433Sho 	FILE	*fp;
239be6714aaSho 	int	 fd, flags = 0, saved_errno;
240be6714aaSho 	mode_t	 mask, cur_umask;
241da35d433Sho 
242da35d433Sho 	/* Only the child process is supposed to run this.  */
243da35d433Sho 	if (m_state.pid)
244da35d433Sho 		log_fatal("[priv] bad call to monitor_fopen");
245da35d433Sho 
246fb9475d6Sderaadt 	switch (mode[0]) {
247da35d433Sho 	case 'r':
248da35d433Sho 		flags = (mode[1] == '+' ? O_RDWR : O_RDONLY);
249da35d433Sho 		break;
250da35d433Sho 	case 'w':
25112f43dabShshoexer 		flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
25212f43dabShshoexer 		    O_TRUNC;
253da35d433Sho 		break;
254da35d433Sho 	case 'a':
25512f43dabShshoexer 		flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
25612f43dabShshoexer 		    O_APPEND;
257da35d433Sho 		break;
258da35d433Sho 	default:
259da35d433Sho 		log_fatal("monitor_fopen: bad call");
260da35d433Sho 	}
261da35d433Sho 
262be6714aaSho 	cur_umask = umask(0);
263be6714aaSho 	(void)umask(cur_umask);
26431516eecShshoexer 	mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
265be6714aaSho 	mask &= ~cur_umask;
26631516eecShshoexer 
26731516eecShshoexer 	fd = monitor_open(path, flags, mask);
268da35d433Sho 	if (fd < 0)
269da35d433Sho 		return NULL;
270da35d433Sho 
271da35d433Sho 	/* Got the fd, attach a FILE * to it.  */
272da35d433Sho 	fp = fdopen(fd, mode);
273fb9475d6Sderaadt 	if (!fp) {
274da35d433Sho 		log_error("monitor_fopen: fdopen() failed");
27531516eecShshoexer 		saved_errno = errno;
276da35d433Sho 		close(fd);
27731516eecShshoexer 		errno = saved_errno;
278da35d433Sho 		return NULL;
279da35d433Sho 	}
280da35d433Sho 	return fp;
281da35d433Sho }
282da35d433Sho 
283da35d433Sho int
monitor_stat(const char * path,struct stat * sb)284da35d433Sho monitor_stat(const char *path, struct stat *sb)
285da35d433Sho {
286da35d433Sho 	int	fd, r, saved_errno;
287da35d433Sho 
28831516eecShshoexer 	/* O_NONBLOCK is needed for stat'ing fifos. */
28931516eecShshoexer 	fd = monitor_open(path, O_RDONLY | O_NONBLOCK, 0);
290da35d433Sho 	if (fd < 0)
291da35d433Sho 		return -1;
292da35d433Sho 
293da35d433Sho 	r = fstat(fd, sb);
294da35d433Sho 	saved_errno = errno;
295ea1948caSho 	close(fd);
296da35d433Sho 	errno = saved_errno;
297da35d433Sho 	return r;
298da35d433Sho }
299da35d433Sho 
300da35d433Sho int
monitor_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)301da35d433Sho monitor_setsockopt(int s, int level, int optname, const void *optval,
302da35d433Sho     socklen_t optlen)
303da35d433Sho {
304ca544a24Shshoexer 	int	ret, err, cmd;
305da35d433Sho 
306ca544a24Shshoexer 	cmd = MONITOR_SETSOCKOPT;
3076c5cc4d7Smoritz 	must_write(&cmd, sizeof cmd);
308be428636Shshoexer 	if (mm_send_fd(m_state.s, s)) {
309be428636Shshoexer 		log_print("monitor_setsockopt: read/write error");
310be428636Shshoexer 		return -1;
311be428636Shshoexer 	}
312da35d433Sho 
3136c5cc4d7Smoritz 	must_write(&level, sizeof level);
3146c5cc4d7Smoritz 	must_write(&optname, sizeof optname);
3156c5cc4d7Smoritz 	must_write(&optlen, sizeof optlen);
316a8f2fa08Scloder 	must_write(optval, optlen);
317da35d433Sho 
3186c5cc4d7Smoritz 	must_read(&err, sizeof err);
3196c5cc4d7Smoritz 	must_read(&ret, sizeof ret);
32031516eecShshoexer 	if (err != 0)
321ca544a24Shshoexer 		errno = err;
322ca544a24Shshoexer 	return ret;
323da35d433Sho }
324da35d433Sho 
325da35d433Sho int
monitor_bind(int s,const struct sockaddr * name,socklen_t namelen)326da35d433Sho monitor_bind(int s, const struct sockaddr *name, socklen_t namelen)
327da35d433Sho {
328ca544a24Shshoexer 	int	ret, err, cmd;
329da35d433Sho 
330ca544a24Shshoexer 	cmd = MONITOR_BIND;
3316c5cc4d7Smoritz 	must_write(&cmd, sizeof cmd);
332be428636Shshoexer 	if (mm_send_fd(m_state.s, s)) {
333be428636Shshoexer 		log_print("monitor_bind: read/write error");
334be428636Shshoexer 		return -1;
335be428636Shshoexer 	}
336da35d433Sho 
3376c5cc4d7Smoritz 	must_write(&namelen, sizeof namelen);
338a8f2fa08Scloder 	must_write(name, namelen);
339da35d433Sho 
3406c5cc4d7Smoritz 	must_read(&err, sizeof err);
3416c5cc4d7Smoritz 	must_read(&ret, sizeof ret);
34231516eecShshoexer 	if (err != 0)
343ca544a24Shshoexer 		errno = err;
344ca544a24Shshoexer 	return ret;
345da35d433Sho }
346da35d433Sho 
34777033115Smoritz int
monitor_req_readdir(const char * filename)34877033115Smoritz monitor_req_readdir(const char *filename)
34931516eecShshoexer {
35077033115Smoritz 	int cmd, err;
35177033115Smoritz 	size_t len;
35231516eecShshoexer 
35377033115Smoritz 	cmd = MONITOR_REQ_READDIR;
35477033115Smoritz 	must_write(&cmd, sizeof cmd);
35531516eecShshoexer 
35677033115Smoritz 	len = strlen(filename);
35777033115Smoritz 	must_write(&len, sizeof len);
35877033115Smoritz 	must_write(filename, len);
35931516eecShshoexer 
36077033115Smoritz 	must_read(&err, sizeof err);
36177033115Smoritz 	if (err == -1)
36277033115Smoritz 		must_read(&errno, sizeof errno);
36377033115Smoritz 
364151ead9aSho 	return err;
36531516eecShshoexer }
36631516eecShshoexer 
36777033115Smoritz int
monitor_readdir(char * file,size_t size)36877033115Smoritz monitor_readdir(char *file, size_t size)
36931516eecShshoexer {
37077033115Smoritz 	int fd;
37177033115Smoritz 	size_t len;
37231516eecShshoexer 
37377033115Smoritz 	must_read(&len, sizeof len);
37477033115Smoritz 	if (len == 0)
37577033115Smoritz 		return -1;
37677033115Smoritz 	if (len >= size)
37777033115Smoritz 		log_fatal("monitor_readdir: received bad length from monitor");
37877033115Smoritz 	must_read(file, len);
37977033115Smoritz 	file[len] = '\0';
38077033115Smoritz 	fd = mm_receive_fd(m_state.s);
38177033115Smoritz 	return fd;
38231516eecShshoexer }
38331516eecShshoexer 
38431516eecShshoexer void
monitor_init_done(void)38531516eecShshoexer monitor_init_done(void)
38631516eecShshoexer {
387ca544a24Shshoexer 	int	cmd;
388ca544a24Shshoexer 
389ca544a24Shshoexer 	cmd = MONITOR_INIT_DONE;
3906c5cc4d7Smoritz 	must_write(&cmd, sizeof cmd);
39131516eecShshoexer }
39231516eecShshoexer 
393da35d433Sho /*
394da35d433Sho  * Start of code running with privileges (the monitor process).
395da35d433Sho  */
396da35d433Sho 
397b8909177Smoritz static void
set_monitor_signals(void)398b8909177Smoritz set_monitor_signals(void)
399b8909177Smoritz {
400b8909177Smoritz 	int n;
401b8909177Smoritz 
40208b252c1Smoritz 	for (n = 1; n < _NSIG; n++)
403b8909177Smoritz 		signal(n, SIG_DFL);
404b8909177Smoritz 
405b8909177Smoritz 	/* Forward some signals to the child. */
406b8909177Smoritz 	signal(SIGTERM, sig_pass_to_chld);
407b8909177Smoritz 	signal(SIGHUP, sig_pass_to_chld);
408b8909177Smoritz 	signal(SIGUSR1, sig_pass_to_chld);
409b8909177Smoritz }
410b8909177Smoritz 
41131516eecShshoexer static void
sig_pass_to_chld(int sig)41231516eecShshoexer sig_pass_to_chld(int sig)
41331516eecShshoexer {
41431516eecShshoexer 	int	oerrno = errno;
41531516eecShshoexer 
4164d4ad54fSmoritz 	if (m_state.pid > 0)
41731516eecShshoexer 		kill(m_state.pid, sig);
41831516eecShshoexer 	errno = oerrno;
41931516eecShshoexer }
42031516eecShshoexer 
421da35d433Sho /* This function is where the privileged process waits(loops) indefinitely.  */
422da35d433Sho void
monitor_loop(int debug)423db6e54dbSho monitor_loop(int debug)
424da35d433Sho {
425e8a27e95Smoritz 	int	 msgcode;
426da35d433Sho 
427db6e54dbSho 	if (!debug)
428da35d433Sho 		log_to(0);
429da35d433Sho 
430f952d032Shshoexer 	for (;;) {
4316c5cc4d7Smoritz 		must_read(&msgcode, sizeof msgcode);
432da35d433Sho 
433fb9475d6Sderaadt 		switch (msgcode) {
434da35d433Sho 		case MONITOR_GET_FD:
4356c5cc4d7Smoritz 			m_priv_getfd();
436da35d433Sho 			break;
437da35d433Sho 
4381ec19420Shshoexer 		case MONITOR_PFKEY_OPEN:
4391ec19420Shshoexer 			LOG_DBG((LOG_MISC, 80,
440097b81a0Shshoexer 			    "monitor_loop: MONITOR_PFKEY_OPEN"));
4416c5cc4d7Smoritz 			m_priv_pfkey_open();
4421ec19420Shshoexer 			break;
4431ec19420Shshoexer 
444da35d433Sho 		case MONITOR_SETSOCKOPT:
44550eea14cSho 			LOG_DBG((LOG_MISC, 80,
446097b81a0Shshoexer 			    "monitor_loop: MONITOR_SETSOCKOPT"));
4476c5cc4d7Smoritz 			m_priv_setsockopt();
448da35d433Sho 			break;
449da35d433Sho 
450da35d433Sho 		case MONITOR_BIND:
45150eea14cSho 			LOG_DBG((LOG_MISC, 80,
452097b81a0Shshoexer 			    "monitor_loop: MONITOR_BIND"));
4536c5cc4d7Smoritz 			m_priv_bind();
454da35d433Sho 			break;
455da35d433Sho 
45677033115Smoritz 		case MONITOR_REQ_READDIR:
45777033115Smoritz 			LOG_DBG((LOG_MISC, 80,
45877033115Smoritz 			    "monitor_loop: MONITOR_REQ_READDIR"));
45977033115Smoritz 			m_priv_req_readdir();
46077033115Smoritz 			break;
46177033115Smoritz 
46231516eecShshoexer 		case MONITOR_INIT_DONE:
46350eea14cSho 			LOG_DBG((LOG_MISC, 80,
464097b81a0Shshoexer 			    "monitor_loop: MONITOR_INIT_DONE"));
46531516eecShshoexer 			break;
46631516eecShshoexer 
467da35d433Sho 		case MONITOR_SHUTDOWN:
46850eea14cSho 			LOG_DBG((LOG_MISC, 80,
469097b81a0Shshoexer 			    "monitor_loop: MONITOR_SHUTDOWN"));
470da35d433Sho 			break;
471da35d433Sho 
472da35d433Sho 		default:
473097b81a0Shshoexer 			log_print("monitor_loop: got unknown code %d",
47450eea14cSho 			    msgcode);
475da35d433Sho 		}
476da35d433Sho 	}
477da35d433Sho 
478da35d433Sho 	exit(0);
479da35d433Sho }
480da35d433Sho 
481b09056b7Shshoexer 
482b09056b7Shshoexer /* Privileged: called by monitor_loop.  */
483b09056b7Shshoexer static void
m_priv_pfkey_open(void)4846c5cc4d7Smoritz m_priv_pfkey_open(void)
4851ec19420Shshoexer {
486ca544a24Shshoexer 	int	fd, err = 0;
4871ec19420Shshoexer 
4881ec19420Shshoexer 	fd = pf_key_v2_open();
4891ec19420Shshoexer 	if (fd < 0)
4901ec19420Shshoexer 		err = -1;
4911ec19420Shshoexer 
4926c5cc4d7Smoritz 	must_write(&err, sizeof err);
4931ec19420Shshoexer 
4946c5cc4d7Smoritz 	if (fd > 0 && mm_send_fd(m_state.s, fd)) {
495be428636Shshoexer 		log_error("m_priv_pfkey_open: read/write operation failed");
4961ec19420Shshoexer 		close(fd);
497be428636Shshoexer 		return;
4981ec19420Shshoexer 	}
4991ec19420Shshoexer 	close(fd);
5001ec19420Shshoexer }
5011ec19420Shshoexer 
5021ec19420Shshoexer /* Privileged: called by monitor_loop.  */
5031ec19420Shshoexer static void
m_priv_getfd(void)5046c5cc4d7Smoritz m_priv_getfd(void)
505da35d433Sho {
506b9fc9a72Sderaadt 	char	path[PATH_MAX];
507a8f2fa08Scloder 	size_t	len;
508e8022babShshoexer 	int	v, flags, ret;
509ca544a24Shshoexer 	int	err = 0;
510da35d433Sho 	mode_t	mode;
511da35d433Sho 
5126c5cc4d7Smoritz 	must_read(&len, sizeof len);
513a8f2fa08Scloder 	if (len == 0 || len >= sizeof path)
514ca544a24Shshoexer 		log_fatal("m_priv_getfd: invalid pathname length");
515da35d433Sho 
5166c5cc4d7Smoritz 	must_read(path, len);
517d0f6fb84Smoritz 	path[len] = '\0';
518882b2dfeScloder 	if (strlen(path) != len)
519882b2dfeScloder 		log_fatal("m_priv_getfd: invalid pathname");
520882b2dfeScloder 
5216c5cc4d7Smoritz 	must_read(&flags, sizeof flags);
5226c5cc4d7Smoritz 	must_read(&mode, sizeof mode);
523da35d433Sho 
524e8022babShshoexer 	if ((ret = m_priv_local_sanitize_path(path, sizeof path, flags))
525811d2842Sbluhm 	    != 0) {
526811d2842Sbluhm 		if (errno != ENOENT)
527bef23cb1Smarkus 			log_print("m_priv_getfd: illegal path \"%s\"", path);
528f9fe46d2Sbluhm 		err = errno;
52931516eecShshoexer 		v = -1;
530fb9475d6Sderaadt 	} else {
531aff085cdSmoritz 		if ((v = open(path, flags, mode)) == -1)
532ca544a24Shshoexer 			err = errno;
53331516eecShshoexer 	}
53431516eecShshoexer 
5356c5cc4d7Smoritz 	must_write(&err, sizeof err);
53631516eecShshoexer 
537aff085cdSmoritz 	if (v != -1) {
538aff085cdSmoritz 		if (mm_send_fd(m_state.s, v) == -1)
539aff085cdSmoritz 			log_error("m_priv_getfd: sending fd failed");
540ea1948caSho 		close(v);
541be428636Shshoexer 	}
542da35d433Sho }
543da35d433Sho 
544da35d433Sho /* Privileged: called by monitor_loop.  */
54531516eecShshoexer static void
m_priv_setsockopt(void)5466c5cc4d7Smoritz m_priv_setsockopt(void)
547da35d433Sho {
548ca544a24Shshoexer 	int		 sock, level, optname, v;
549ca544a24Shshoexer 	int		 err = 0;
550da35d433Sho 	char		*optval = 0;
551da35d433Sho 	socklen_t	 optlen;
552da35d433Sho 
5536c5cc4d7Smoritz 	sock = mm_receive_fd(m_state.s);
554a4e97817Shshoexer 	if (sock < 0) {
555a4e97817Shshoexer 		log_print("m_priv_setsockopt: read/write error");
556a4e97817Shshoexer 		return;
557a4e97817Shshoexer 	}
558da35d433Sho 
5596c5cc4d7Smoritz 	must_read(&level, sizeof level);
5606c5cc4d7Smoritz 	must_read(&optname, sizeof optname);
5616c5cc4d7Smoritz 	must_read(&optlen, sizeof optlen);
562da35d433Sho 
5635ae94ef8Sderaadt 	optval = malloc(optlen);
564a4e97817Shshoexer 	if (!optval) {
565a4e97817Shshoexer 		log_print("m_priv_setsockopt: malloc failed");
566a4e97817Shshoexer 		close(sock);
567a4e97817Shshoexer 		return;
568a4e97817Shshoexer 	}
569da35d433Sho 
570a8f2fa08Scloder 	must_read(optval, optlen);
571da35d433Sho 
572fb9475d6Sderaadt 	if (m_priv_check_sockopt(level, optname) != 0) {
57331516eecShshoexer 		err = EACCES;
57431516eecShshoexer 		v = -1;
575fb9475d6Sderaadt 	} else {
576ca544a24Shshoexer 		v = setsockopt(sock, level, optname, optval, optlen);
57731516eecShshoexer 		if (v < 0)
578ca544a24Shshoexer 			err = errno;
57931516eecShshoexer 	}
58031516eecShshoexer 
581ea1948caSho 	close(sock);
582fcdca22eStedu 	sock = -1;
58331516eecShshoexer 
5846c5cc4d7Smoritz 	must_write(&err, sizeof err);
5856c5cc4d7Smoritz 	must_write(&v, sizeof v);
586da35d433Sho 
587da35d433Sho 	free(optval);
588da35d433Sho 	return;
589da35d433Sho }
590da35d433Sho 
591da35d433Sho /* Privileged: called by monitor_loop.  */
59231516eecShshoexer static void
m_priv_bind(void)5936c5cc4d7Smoritz m_priv_bind(void)
594da35d433Sho {
595ca544a24Shshoexer 	int		 sock, v, err = 0;
596da35d433Sho 	struct sockaddr *name = 0;
597da35d433Sho 	socklen_t        namelen;
598da35d433Sho 
5996c5cc4d7Smoritz 	sock = mm_receive_fd(m_state.s);
600a4e97817Shshoexer 	if (sock < 0) {
601a4e97817Shshoexer 		log_print("m_priv_bind: read/write error");
602a4e97817Shshoexer 		return;
603a4e97817Shshoexer 	}
604da35d433Sho 
605a8f2fa08Scloder 	must_read(&namelen, sizeof namelen);
6065ae94ef8Sderaadt 	name = malloc(namelen);
607a4e97817Shshoexer 	if (!name) {
608a4e97817Shshoexer 		log_print("m_priv_bind: malloc failed");
609a4e97817Shshoexer 		close(sock);
610a4e97817Shshoexer 		return;
611a4e97817Shshoexer 	}
612a8f2fa08Scloder 	must_read((char *)name, namelen);
613da35d433Sho 
614fb9475d6Sderaadt 	if (m_priv_check_bind(name, namelen) != 0) {
61531516eecShshoexer 		err = EACCES;
61631516eecShshoexer 		v = -1;
617fb9475d6Sderaadt 	} else {
618ca544a24Shshoexer 		v = bind(sock, name, namelen);
619df69c215Sderaadt 		if (v == -1) {
620ea1948caSho 			log_error("m_priv_bind: bind(%d,%p,%d) returned %d",
621ea1948caSho 			    sock, name, namelen, v);
622ca544a24Shshoexer 			err = errno;
62331516eecShshoexer 		}
62431516eecShshoexer 	}
625ea1948caSho 
626ea1948caSho 	close(sock);
627fcdca22eStedu 	sock = -1;
62831516eecShshoexer 
6296c5cc4d7Smoritz 	must_write(&err, sizeof err);
6306c5cc4d7Smoritz 	must_write(&v, sizeof v);
631da35d433Sho 
632da35d433Sho 	free(name);
633da35d433Sho 	return;
634da35d433Sho }
635da35d433Sho 
636da35d433Sho /*
637da35d433Sho  * Help functions, used by both privileged and unprivileged code
638da35d433Sho  */
639da35d433Sho 
640ca544a24Shshoexer /*
641ca544a24Shshoexer  * Read data with the assertion that it all must come through, or else abort
642ca544a24Shshoexer  * the process.  Based on atomicio() from openssh.
643ca544a24Shshoexer  */
644ca544a24Shshoexer static void
must_read(void * buf,size_t n)6456c5cc4d7Smoritz must_read(void *buf, size_t n)
646da35d433Sho {
647ca544a24Shshoexer         char *s = buf;
648ca544a24Shshoexer 	size_t pos = 0;
649ca544a24Shshoexer         ssize_t res;
65012f43dabShshoexer 
651ca544a24Shshoexer         while (n > pos) {
6526c5cc4d7Smoritz                 res = read(m_state.s, s + pos, n - pos);
653ca544a24Shshoexer                 switch (res) {
654ca544a24Shshoexer                 case -1:
655ca544a24Shshoexer                         if (errno == EINTR || errno == EAGAIN)
656ca544a24Shshoexer                                 continue;
657ca544a24Shshoexer                 case 0:
658e8a27e95Smoritz 			monitor_exit(0);
659ca544a24Shshoexer                 default:
660ca544a24Shshoexer                         pos += res;
661ca544a24Shshoexer                 }
662ca544a24Shshoexer         }
663da35d433Sho }
664da35d433Sho 
665ca544a24Shshoexer /*
666ca544a24Shshoexer  * Write data with the assertion that it all has to be written, or else abort
667ca544a24Shshoexer  * the process.  Based on atomicio() from openssh.
668ca544a24Shshoexer  */
669ca544a24Shshoexer static void
must_write(const void * buf,size_t n)6706c5cc4d7Smoritz must_write(const void *buf, size_t n)
671da35d433Sho {
672ca544a24Shshoexer         const char *s = buf;
673a8f2fa08Scloder 	size_t pos = 0;
674a8f2fa08Scloder 	ssize_t res;
675ca544a24Shshoexer 
676ca544a24Shshoexer         while (n > pos) {
6776c5cc4d7Smoritz                 res = write(m_state.s, s + pos, n - pos);
678ca544a24Shshoexer                 switch (res) {
679ca544a24Shshoexer                 case -1:
680ca544a24Shshoexer                         if (errno == EINTR || errno == EAGAIN)
681ca544a24Shshoexer                                 continue;
682ca544a24Shshoexer                 case 0:
683e8a27e95Smoritz 			monitor_exit(0);
684ca544a24Shshoexer                 default:
685ca544a24Shshoexer                         pos += res;
686da35d433Sho                 }
687da35d433Sho         }
688da35d433Sho }
689ea1948caSho 
690112b090cSho /* Check that path/mode is permitted.  */
69131516eecShshoexer static int
m_priv_local_sanitize_path(const char * path,size_t pmax,int flags)692811d2842Sbluhm m_priv_local_sanitize_path(const char *path, size_t pmax, int flags)
693112b090cSho {
694811d2842Sbluhm 	char new_path[PATH_MAX], var_run[PATH_MAX], *enddir;
695112b090cSho 
696112b090cSho 	/*
69731516eecShshoexer 	 * We only permit paths starting with
698112b090cSho 	 *  /etc/isakmpd/	(read only)
69931516eecShshoexer 	 *  /var/run/		(rw)
700112b090cSho 	 */
701112b090cSho 
702811d2842Sbluhm 	if (realpath(path, new_path) == NULL) {
703811d2842Sbluhm 		if (errno != ENOENT)
704811d2842Sbluhm 			return 1;
705811d2842Sbluhm 		/*
706811d2842Sbluhm 		 * It is ok if the directory exists,
707811d2842Sbluhm 		 * but the file should be created.
708811d2842Sbluhm 		 */
709811d2842Sbluhm 		if (strlcpy(new_path, path, sizeof(new_path)) >=
710811d2842Sbluhm 		    sizeof(new_path))
711811d2842Sbluhm 			return 1;
712811d2842Sbluhm 		enddir = strrchr(new_path, '/');
713811d2842Sbluhm 		if (enddir == NULL || enddir[1] == '\0')
714811d2842Sbluhm 			return 1;
715811d2842Sbluhm 		enddir[1] = '\0';
716811d2842Sbluhm 		if (realpath(new_path, new_path) == NULL) {
717811d2842Sbluhm 			errno = ENOENT;
718811d2842Sbluhm 			return 1;
719811d2842Sbluhm 		}
720811d2842Sbluhm 		enddir = strrchr(path, '/');
721811d2842Sbluhm 		strlcat(new_path, enddir, sizeof(new_path));
722811d2842Sbluhm 	}
723811d2842Sbluhm 
724811d2842Sbluhm 	if (realpath("/var/run/", var_run) == NULL)
725f9fe46d2Sbluhm 		return 1;
726c7b82f9eSmillert 	strlcat(var_run, "/", sizeof(var_run));
727112b090cSho 
728c7b82f9eSmillert 	if (strncmp(var_run, new_path, strlen(var_run)) == 0)
72931516eecShshoexer 		return 0;
7302455151dSmoritz 
7312455151dSmoritz 	if (strncmp(ISAKMPD_ROOT, new_path, strlen(ISAKMPD_ROOT)) == 0 &&
7322455151dSmoritz 	    (flags & O_ACCMODE) == O_RDONLY)
73331516eecShshoexer 		return 0;
734112b090cSho 
735f9fe46d2Sbluhm 	errno = EACCES;
73631516eecShshoexer 	return 1;
737112b090cSho }
738112b090cSho 
73931516eecShshoexer /* Check setsockopt */
74031516eecShshoexer static int
m_priv_check_sockopt(int level,int name)74131516eecShshoexer m_priv_check_sockopt(int level, int name)
74231516eecShshoexer {
743fb9475d6Sderaadt 	switch (level) {
74431516eecShshoexer 		/* These are allowed */
74531516eecShshoexer 		case SOL_SOCKET:
74631516eecShshoexer 		case IPPROTO_IP:
74731516eecShshoexer 		case IPPROTO_IPV6:
74831516eecShshoexer 		break;
74931516eecShshoexer 
75031516eecShshoexer 	default:
75131516eecShshoexer 		log_print("m_priv_check_sockopt: Illegal level %d", level);
75231516eecShshoexer 		return 1;
75331516eecShshoexer 	}
75431516eecShshoexer 
755fb9475d6Sderaadt 	switch (name) {
75631516eecShshoexer 		/* These are allowed */
75731516eecShshoexer 	case SO_REUSEPORT:
75831516eecShshoexer 	case SO_REUSEADDR:
75931516eecShshoexer 	case IP_AUTH_LEVEL:
76031516eecShshoexer 	case IP_ESP_TRANS_LEVEL:
76131516eecShshoexer 	case IP_ESP_NETWORK_LEVEL:
76231516eecShshoexer 	case IP_IPCOMP_LEVEL:
76331516eecShshoexer 	case IPV6_AUTH_LEVEL:
76431516eecShshoexer 	case IPV6_ESP_TRANS_LEVEL:
76531516eecShshoexer 	case IPV6_ESP_NETWORK_LEVEL:
76631516eecShshoexer 	case IPV6_IPCOMP_LEVEL:
76731516eecShshoexer 		break;
76831516eecShshoexer 
76931516eecShshoexer 	default:
77050eea14cSho 		log_print("m_priv_check_sockopt: Illegal option name %d",
77150eea14cSho 		    name);
77231516eecShshoexer 		return 1;
77331516eecShshoexer 	}
77431516eecShshoexer 
77531516eecShshoexer 	return 0;
77631516eecShshoexer }
77731516eecShshoexer 
77831516eecShshoexer /* Check bind */
77931516eecShshoexer static int
m_priv_check_bind(const struct sockaddr * sa,socklen_t salen)78031516eecShshoexer m_priv_check_bind(const struct sockaddr *sa, socklen_t salen)
78131516eecShshoexer {
78231516eecShshoexer 	in_port_t       port;
78331516eecShshoexer 
784fb9475d6Sderaadt 	if (sa == NULL) {
78531516eecShshoexer 		log_print("NULL address");
78631516eecShshoexer 		return 1;
78731516eecShshoexer 	}
788c506f982Shshoexer 	if (SA_LEN(sa) != salen) {
789c506f982Shshoexer 		log_print("Length mismatch: %lu %lu", (unsigned long)sa->sa_len,
790c506f982Shshoexer 		    (unsigned long)salen);
79131516eecShshoexer 		return 1;
79231516eecShshoexer 	}
793fb9475d6Sderaadt 	switch (sa->sa_family) {
79431516eecShshoexer 	case AF_INET:
795fb9475d6Sderaadt 		if (salen != sizeof(struct sockaddr_in)) {
79631516eecShshoexer 			log_print("Invalid inet address length");
79731516eecShshoexer 			return 1;
79831516eecShshoexer 		}
79931516eecShshoexer 		port = ((const struct sockaddr_in *)sa)->sin_port;
80031516eecShshoexer 		break;
80131516eecShshoexer 	case AF_INET6:
802fb9475d6Sderaadt 		if (salen != sizeof(struct sockaddr_in6)) {
80331516eecShshoexer 			log_print("Invalid inet6 address length");
80431516eecShshoexer 			return 1;
80531516eecShshoexer 		}
80631516eecShshoexer 		port = ((const struct sockaddr_in6 *)sa)->sin6_port;
80731516eecShshoexer 		break;
80831516eecShshoexer 	default:
80931516eecShshoexer 		log_print("Unknown address family");
81031516eecShshoexer 		return 1;
81131516eecShshoexer 	}
81231516eecShshoexer 
81331516eecShshoexer 	port = ntohs(port);
81431516eecShshoexer 
815fb9475d6Sderaadt 	if (port != ISAKMP_PORT_DEFAULT && port < 1024) {
81631516eecShshoexer 		log_print("Disallowed port %u", port);
81731516eecShshoexer 		return 1;
81831516eecShshoexer 	}
81931516eecShshoexer 	return 0;
82031516eecShshoexer }
82131516eecShshoexer 
82277033115Smoritz static void
m_priv_req_readdir(void)823*ff9f425cStb m_priv_req_readdir(void)
82477033115Smoritz {
82577033115Smoritz 	size_t len;
826b9fc9a72Sderaadt 	char path[PATH_MAX];
82777033115Smoritz 	DIR *dp;
82877033115Smoritz 	struct dirent *file;
829bef23cb1Smarkus 	struct stat sb;
83077033115Smoritz 	int off, size, fd, ret, serrno;
83177033115Smoritz 
83277033115Smoritz 	must_read(&len, sizeof len);
83377033115Smoritz 	if (len == 0 || len >= sizeof path)
83477033115Smoritz 		log_fatal("m_priv_req_readdir: invalid pathname length");
83577033115Smoritz 	must_read(path, len);
83677033115Smoritz 	path[len] = '\0';
83777033115Smoritz 	if (strlen(path) != len)
83877033115Smoritz 		log_fatal("m_priv_req_readdir: invalid pathname");
83977033115Smoritz 
84077033115Smoritz 	off = strlen(path);
84177033115Smoritz 	size = sizeof path - off;
84277033115Smoritz 
84377033115Smoritz 	if ((dp = opendir(path)) == NULL) {
84477033115Smoritz 		serrno = errno;
84577033115Smoritz 		ret = -1;
84677033115Smoritz 		must_write(&ret, sizeof ret);
84777033115Smoritz 		must_write(&serrno, sizeof serrno);
84877033115Smoritz 		return;
84977033115Smoritz 	}
85077033115Smoritz 
85177033115Smoritz 	/* report opendir() success */
85277033115Smoritz 	ret = 0;
85377033115Smoritz 	must_write(&ret, sizeof ret);
85477033115Smoritz 
85577033115Smoritz 	while ((file = readdir(dp)) != NULL) {
85677033115Smoritz 		strlcpy(path + off, file->d_name, size);
85777033115Smoritz 
858280ff4feSmoritz 		if (m_priv_local_sanitize_path(path, sizeof path, O_RDONLY)
859bef23cb1Smarkus 		    != 0)
860280ff4feSmoritz 			continue;
861b7041c07Sderaadt 		fd = open(path, O_RDONLY);
86277033115Smoritz 		if (fd == -1) {
86377033115Smoritz 			log_error("m_priv_req_readdir: open "
86477033115Smoritz 			    "(\"%s\", O_RDONLY, 0) failed", path);
86577033115Smoritz 			continue;
86677033115Smoritz 		}
867bef23cb1Smarkus 		if ((fstat(fd, &sb) == -1) ||
868bef23cb1Smarkus 		    !(S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))) {
869bef23cb1Smarkus 			close(fd);
870bef23cb1Smarkus 			continue;
871bef23cb1Smarkus 		}
87277033115Smoritz 
87377033115Smoritz 		len = strlen(path);
87477033115Smoritz 		must_write(&len, sizeof len);
87577033115Smoritz 		must_write(path, len);
87677033115Smoritz 
87777033115Smoritz 		mm_send_fd(m_state.s, fd);
87877033115Smoritz 		close(fd);
87977033115Smoritz 	}
88077033115Smoritz 	closedir(dp);
88177033115Smoritz 
88277033115Smoritz 	len = 0;
88377033115Smoritz 	must_write(&len, sizeof len);
88477033115Smoritz }
885