xref: /openbsd-src/sbin/iked/iked.c (revision d51e05ad1ce492cb403556552f25cba79ecf2ce5)
1*d51e05adSsthen /*	$OpenBSD: iked.c,v 1.72 2024/12/26 18:24:54 sthen Exp $	*/
245ae9d61Sreyk 
345ae9d61Sreyk /*
465c540d0Spatrick  * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
5fcebd35dSreyk  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
645ae9d61Sreyk  *
745ae9d61Sreyk  * Permission to use, copy, modify, and distribute this software for any
845ae9d61Sreyk  * purpose with or without fee is hereby granted, provided that the above
945ae9d61Sreyk  * copyright notice and this permission notice appear in all copies.
1045ae9d61Sreyk  *
1145ae9d61Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1245ae9d61Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1345ae9d61Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1445ae9d61Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1545ae9d61Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1645ae9d61Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1745ae9d61Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1845ae9d61Sreyk  */
1945ae9d61Sreyk 
2045ae9d61Sreyk #include <sys/queue.h>
2145ae9d61Sreyk #include <sys/socket.h>
22fc7fd3e3Sreyk #include <sys/wait.h>
2345ae9d61Sreyk #include <sys/uio.h>
2445ae9d61Sreyk 
2545ae9d61Sreyk #include <stdlib.h>
2645ae9d61Sreyk #include <stdio.h>
2745ae9d61Sreyk #include <unistd.h>
2845ae9d61Sreyk #include <string.h>
2945ae9d61Sreyk #include <getopt.h>
3045ae9d61Sreyk #include <signal.h>
310f12961aSreyk #include <syslog.h>
3245ae9d61Sreyk #include <errno.h>
3345ae9d61Sreyk #include <err.h>
3445ae9d61Sreyk #include <pwd.h>
3545ae9d61Sreyk #include <event.h>
3645ae9d61Sreyk 
3745ae9d61Sreyk #include "iked.h"
3845ae9d61Sreyk #include "ikev2.h"
39c183a39cStobhe #include "version.h"
4045ae9d61Sreyk 
4145ae9d61Sreyk __dead void usage(void);
4245ae9d61Sreyk 
4345ae9d61Sreyk void	 parent_shutdown(struct iked *);
4445ae9d61Sreyk void	 parent_sig_handler(int, short, void *);
45f2f2a684Sreyk int	 parent_dispatch_ca(int, struct privsep_proc *, struct imsg *);
46ebfc3693Sreyk int	 parent_dispatch_control(int, struct privsep_proc *, struct imsg *);
47cd3f460fStobhe int	 parent_dispatch_ikev2(int, struct privsep_proc *, struct imsg *);
488e8f56e9Stobhe void	 parent_connected(struct privsep *);
4945ae9d61Sreyk int	 parent_configure(struct iked *);
5045ae9d61Sreyk 
51e8e9d77fStobhe struct iked	*iked_env;
52e8e9d77fStobhe 
53f2f2a684Sreyk static struct privsep_proc procs[] = {
54ebfc3693Sreyk 	{ "ca",		PROC_CERT,	parent_dispatch_ca, caproc, IKED_CA },
55ebfc3693Sreyk 	{ "control",	PROC_CONTROL,	parent_dispatch_control, control },
56cd3f460fStobhe 	{ "ikev2",	PROC_IKEV2,	parent_dispatch_ikev2, ikev2 }
5745ae9d61Sreyk };
5845ae9d61Sreyk 
5945ae9d61Sreyk __dead void
6045ae9d61Sreyk usage(void)
6145ae9d61Sreyk {
6245ae9d61Sreyk 	extern char	*__progname;
6345ae9d61Sreyk 
64b49e1847Sjmc 	fprintf(stderr, "usage: %s [-dnSTtVv] [-D macro=value] "
65791e0891Sjmc 	    "[-f file] [-p udpencap_port] [-s socket]\n", __progname);
6645ae9d61Sreyk 	exit(1);
6745ae9d61Sreyk }
6845ae9d61Sreyk 
6945ae9d61Sreyk int
7045ae9d61Sreyk main(int argc, char *argv[])
7145ae9d61Sreyk {
7245ae9d61Sreyk 	int			 c;
7345ae9d61Sreyk 	int			 debug = 0, verbose = 0;
7445ae9d61Sreyk 	int			 opts = 0;
751ae9ce49Stobhe 	enum natt_mode		 natt_mode = NATT_DEFAULT;
7659c69d76Stobhe 	in_port_t		 port = IKED_NATT_PORT;
7745ae9d61Sreyk 	const char		*conffile = IKED_CONFIG;
78fbc39eabStobhe 	const char		*sock = IKED_SOCKET;
79a7dbf4aeStobhe 	const char		*errstr, *title = NULL;
8045ae9d61Sreyk 	struct iked		*env = NULL;
81701048fbSreyk 	struct privsep		*ps;
82a7dbf4aeStobhe 	enum privsep_procid	 proc_id = PROC_PARENT;
83a7dbf4aeStobhe 	int			 proc_instance = 0;
84a7dbf4aeStobhe 	int			 argc0 = argc;
8545ae9d61Sreyk 
860f12961aSreyk 	log_init(1, LOG_DAEMON);
8745ae9d61Sreyk 
88a7dbf4aeStobhe 	while ((c = getopt(argc, argv, "6D:df:I:nP:p:Ss:TtvV")) != -1) {
8945ae9d61Sreyk 		switch (c) {
905342a326Sreyk 		case '6':
9133e50974Ssthen 			log_warnx("the -6 option is ignored and will be "
9253b41a44Stobhe 			    "removed in the future.");
935342a326Sreyk 			break;
9445ae9d61Sreyk 		case 'D':
9545ae9d61Sreyk 			if (cmdline_symset(optarg) < 0)
9645ae9d61Sreyk 				log_warnx("could not parse macro definition %s",
9745ae9d61Sreyk 				    optarg);
9845ae9d61Sreyk 			break;
99f767955eStobhe 		case 'd':
100f767955eStobhe 			debug++;
10145ae9d61Sreyk 			break;
10245ae9d61Sreyk 		case 'f':
10345ae9d61Sreyk 			conffile = optarg;
10445ae9d61Sreyk 			break;
105a7dbf4aeStobhe 		case 'I':
106a7dbf4aeStobhe 			proc_instance = strtonum(optarg, 0,
107a7dbf4aeStobhe 			    PROC_MAX_INSTANCES, &errstr);
108a7dbf4aeStobhe 			if (errstr)
109a7dbf4aeStobhe 				fatalx("invalid process instance");
110a7dbf4aeStobhe 			break;
111f767955eStobhe 		case 'n':
112f767955eStobhe 			debug = 1;
113f767955eStobhe 			opts |= IKED_OPT_NOACTION;
114fbc39eabStobhe 			break;
115a7dbf4aeStobhe 		case 'P':
116a7dbf4aeStobhe 			title = optarg;
117a7dbf4aeStobhe 			proc_id = proc_getid(procs, nitems(procs), title);
118a7dbf4aeStobhe 			if (proc_id == PROC_MAX)
119a7dbf4aeStobhe 				fatalx("invalid process name");
120a7dbf4aeStobhe 			break;
121f767955eStobhe 		case 'p':
122f767955eStobhe 			if (natt_mode == NATT_DISABLE)
123f767955eStobhe 				errx(1, "-T and -p are mutually exclusive");
124277a74a3Stobhe 			port = strtonum(optarg, 1, UINT16_MAX, &errstr);
125277a74a3Stobhe 			if (errstr != NULL)
126277a74a3Stobhe 				errx(1, "port is %s: %s", errstr, optarg);
127f767955eStobhe 			natt_mode = NATT_FORCE;
12845ae9d61Sreyk 			break;
1293372b54cSreyk 		case 'S':
1303372b54cSreyk 			opts |= IKED_OPT_PASSIVE;
1313372b54cSreyk 			break;
132f767955eStobhe 		case 's':
133f767955eStobhe 			sock = optarg;
134f767955eStobhe 			break;
13545ae9d61Sreyk 		case 'T':
1361ae9ce49Stobhe 			if (natt_mode == NATT_FORCE)
1371ae9ce49Stobhe 				errx(1, "-T and -t/-p are mutually exclusive");
1381ae9ce49Stobhe 			natt_mode = NATT_DISABLE;
13945ae9d61Sreyk 			break;
14012c9fd31Sreyk 		case 't':
1411ae9ce49Stobhe 			if (natt_mode == NATT_DISABLE)
1421ae9ce49Stobhe 				errx(1, "-T and -t are mutually exclusive");
1431ae9ce49Stobhe 			natt_mode = NATT_FORCE;
14412c9fd31Sreyk 			break;
145f767955eStobhe 		case 'v':
146f767955eStobhe 			verbose++;
147f767955eStobhe 			opts |= IKED_OPT_VERBOSE;
14859c69d76Stobhe 			break;
149c183a39cStobhe 		case 'V':
150c183a39cStobhe 			fprintf(stderr, "OpenIKED %s\n", IKED_VERSION);
151c183a39cStobhe 			return 0;
15245ae9d61Sreyk 		default:
15345ae9d61Sreyk 			usage();
15445ae9d61Sreyk 		}
15545ae9d61Sreyk 	}
15645ae9d61Sreyk 
157a7dbf4aeStobhe 	/* log to stderr until daemonized */
158a7dbf4aeStobhe 	log_init(debug ? debug : 1, LOG_DAEMON);
159a7dbf4aeStobhe 
1601a82444dSpatrick 	argc -= optind;
1611a82444dSpatrick 	if (argc > 0)
1621a82444dSpatrick 		usage();
1631a82444dSpatrick 
16445ae9d61Sreyk 	if ((env = calloc(1, sizeof(*env))) == NULL)
16545ae9d61Sreyk 		fatal("calloc: env");
16645ae9d61Sreyk 
167e8e9d77fStobhe 	iked_env = env;
16845ae9d61Sreyk 	env->sc_opts = opts;
1697e57f0c1Stobhe 	env->sc_nattmode = natt_mode;
17059c69d76Stobhe 	env->sc_nattport = port;
17145ae9d61Sreyk 
172701048fbSreyk 	ps = &env->sc_ps;
173701048fbSreyk 	ps->ps_env = env;
174701048fbSreyk 
175b9fc9a72Sderaadt 	if (strlcpy(env->sc_conffile, conffile, PATH_MAX) >= PATH_MAX)
176b9fc9a72Sderaadt 		errx(1, "config file exceeds PATH_MAX");
17745ae9d61Sreyk 
178a7dbf4aeStobhe 	group_init();
17945ae9d61Sreyk 	policy_init(env);
18045ae9d61Sreyk 
181701048fbSreyk 	if ((ps->ps_pw =  getpwnam(IKED_USER)) == NULL)
18245ae9d61Sreyk 		errx(1, "unknown user %s", IKED_USER);
18345ae9d61Sreyk 
18445ae9d61Sreyk 	/* Configure the control socket */
185fbc39eabStobhe 	ps->ps_csock.cs_name = sock;
18645ae9d61Sreyk 
1870f12961aSreyk 	log_init(debug, LOG_DAEMON);
188871fc12cSreyk 	log_setverbose(verbose);
18945ae9d61Sreyk 
190ea9a120cSjsg 	if (opts & IKED_OPT_NOACTION)
191ea9a120cSjsg 		ps->ps_noaction = 1;
192*d51e05adSsthen 	else {
193*d51e05adSsthen 		/* check for root privileges */
194*d51e05adSsthen 		if (geteuid())
195*d51e05adSsthen 			errx(1, "need root privileges");
196*d51e05adSsthen 	}
197ea9a120cSjsg 
198a7dbf4aeStobhe 	ps->ps_instance = proc_instance;
199a7dbf4aeStobhe 	if (title != NULL)
200a7dbf4aeStobhe 		ps->ps_title[proc_id] = title;
20145ae9d61Sreyk 
202a7dbf4aeStobhe 	/* only the parent returns */
203a7dbf4aeStobhe 	proc_init(ps, procs, nitems(procs), debug, argc0, argv, proc_id);
20445ae9d61Sreyk 
20545ae9d61Sreyk 	setproctitle("parent");
2060f12961aSreyk 	log_procinit("parent");
20745ae9d61Sreyk 
20845ae9d61Sreyk 	event_init();
20945ae9d61Sreyk 
210701048fbSreyk 	signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
211701048fbSreyk 	signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
212701048fbSreyk 	signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps);
213701048fbSreyk 	signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
214701048fbSreyk 	signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
215d5fd2e4bSreyk 	signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps);
21645ae9d61Sreyk 
217701048fbSreyk 	signal_add(&ps->ps_evsigint, NULL);
218701048fbSreyk 	signal_add(&ps->ps_evsigterm, NULL);
219701048fbSreyk 	signal_add(&ps->ps_evsigchld, NULL);
220701048fbSreyk 	signal_add(&ps->ps_evsighup, NULL);
221701048fbSreyk 	signal_add(&ps->ps_evsigpipe, NULL);
222d5fd2e4bSreyk 	signal_add(&ps->ps_evsigusr1, NULL);
22345ae9d61Sreyk 
224264f8b22Stobhe 	vroute_init(env);
225264f8b22Stobhe 
2268e8f56e9Stobhe 	proc_connect(ps, parent_connected);
22745ae9d61Sreyk 
22845ae9d61Sreyk 	event_dispatch();
22945ae9d61Sreyk 
23045ae9d61Sreyk 	log_debug("%d parent exiting", getpid());
23191e971e4Stobhe 	parent_shutdown(env);
23245ae9d61Sreyk 
23345ae9d61Sreyk 	return (0);
23445ae9d61Sreyk }
23545ae9d61Sreyk 
2368e8f56e9Stobhe void
2378e8f56e9Stobhe parent_connected(struct privsep *ps)
2388e8f56e9Stobhe {
2398e8f56e9Stobhe 	struct iked	*env = ps->ps_env;
2408e8f56e9Stobhe 
2418e8f56e9Stobhe 	if (parent_configure(env) == -1)
2428e8f56e9Stobhe 		fatalx("configuration failed");
2438e8f56e9Stobhe }
2448e8f56e9Stobhe 
24545ae9d61Sreyk int
24645ae9d61Sreyk parent_configure(struct iked *env)
24745ae9d61Sreyk {
24845ae9d61Sreyk 	struct sockaddr_storage	 ss;
24945ae9d61Sreyk 
25045ae9d61Sreyk 	if (parse_config(env->sc_conffile, env) == -1) {
251fc7fd3e3Sreyk 		proc_kill(&env->sc_ps);
25245ae9d61Sreyk 		exit(1);
25345ae9d61Sreyk 	}
25445ae9d61Sreyk 
25545ae9d61Sreyk 	if (env->sc_opts & IKED_OPT_NOACTION) {
25645ae9d61Sreyk 		fprintf(stderr, "configuration OK\n");
257fc7fd3e3Sreyk 		proc_kill(&env->sc_ps);
25845ae9d61Sreyk 		exit(0);
25945ae9d61Sreyk 	}
26045ae9d61Sreyk 
26145ae9d61Sreyk 	env->sc_pfkey = -1;
262ba38eea7Stobhe 	config_setpfkey(env);
26345ae9d61Sreyk 
264e8b444cdSreyk 	/* Send private and public keys to cert after forking the children */
265e8b444cdSreyk 	if (config_setkeys(env) == -1)
266e8b444cdSreyk 		fatalx("%s: failed to send keys", __func__);
267e8b444cdSreyk 	config_setreset(env, RESET_CA, PROC_CERT);
268e8b444cdSreyk 
269e2015428Sreyk 	/* Now compile the policies and calculate skip steps */
270e2015428Sreyk 	config_setcompile(env, PROC_IKEV2);
271e2015428Sreyk 
27245ae9d61Sreyk 	bzero(&ss, sizeof(ss));
27345ae9d61Sreyk 	ss.ss_family = AF_INET;
27445ae9d61Sreyk 
2751ae9ce49Stobhe 	/* see comment on config_setsocket() */
2767e57f0c1Stobhe 	if (env->sc_nattmode != NATT_FORCE)
2773a5a2baaStobhe 		config_setsocket(env, &ss, htons(IKED_IKE_PORT), PROC_IKEV2);
2787e57f0c1Stobhe 	if (env->sc_nattmode != NATT_DISABLE)
2793a5a2baaStobhe 		config_setsocket(env, &ss, htons(env->sc_nattport), PROC_IKEV2);
28045ae9d61Sreyk 
28145ae9d61Sreyk 	bzero(&ss, sizeof(ss));
28245ae9d61Sreyk 	ss.ss_family = AF_INET6;
28345ae9d61Sreyk 
2847e57f0c1Stobhe 	if (env->sc_nattmode != NATT_FORCE)
2853a5a2baaStobhe 		config_setsocket(env, &ss, htons(IKED_IKE_PORT), PROC_IKEV2);
2867e57f0c1Stobhe 	if (env->sc_nattmode != NATT_DISABLE)
2873a5a2baaStobhe 		config_setsocket(env, &ss, htons(env->sc_nattport), PROC_IKEV2);
28845ae9d61Sreyk 
289ebfc3693Sreyk 	/*
290ebfc3693Sreyk 	 * pledge in the parent process:
291ebfc3693Sreyk 	 * It has to run fairly late to allow forking the processes and
292ebfc3693Sreyk 	 * opening the PFKEY socket and the listening UDP sockets (once)
293ebfc3693Sreyk 	 * that need the bypass ioctls that are never allowed by pledge.
294ebfc3693Sreyk 	 *
295ebfc3693Sreyk 	 * Other flags:
296ebfc3693Sreyk 	 * stdio - for malloc and basic I/O including events.
297ebfc3693Sreyk 	 * rpath - for reload to open and read the configuration files.
298ebfc3693Sreyk 	 * proc - run kill to terminate its children safely.
299ebfc3693Sreyk 	 * dns - for reload and ocsp connect.
300ebfc3693Sreyk 	 * inet - for ocsp connect.
301ebfc3693Sreyk 	 * route - for using interfaces in iked.conf (SIOCGIFGMEMB)
302264f8b22Stobhe 	 * wroute - for adding and removing addresses (SIOCAIFGMEMB)
303ebfc3693Sreyk 	 * sendfd - for ocsp sockets.
304ebfc3693Sreyk 	 */
305264f8b22Stobhe 	if (pledge("stdio rpath proc dns inet route wroute sendfd", NULL) == -1)
306ebfc3693Sreyk 		fatal("pledge");
307ebfc3693Sreyk 
308421819b6Stobhe 	config_setstatic(env);
309fc20f985Sreyk 	config_setcoupled(env, env->sc_decoupled ? 0 : 1);
3106d3b905bSmarkus 	config_setocsp(env);
311f36db9c4Syasuoka 	config_setradauth(env);
312f36db9c4Syasuoka 	config_setradacct(env);
313f9967a42Stobhe 	/* Must be last */
314f9967a42Stobhe 	config_setmode(env, env->sc_passive ? 1 : 0);
315fc20f985Sreyk 
31645ae9d61Sreyk 	return (0);
31745ae9d61Sreyk }
31845ae9d61Sreyk 
31945ae9d61Sreyk void
32045ae9d61Sreyk parent_reload(struct iked *env, int reset, const char *filename)
32145ae9d61Sreyk {
32245ae9d61Sreyk 	/* Switch back to the default config file */
32345ae9d61Sreyk 	if (filename == NULL || *filename == '\0')
32445ae9d61Sreyk 		filename = env->sc_conffile;
32545ae9d61Sreyk 
32645ae9d61Sreyk 	log_debug("%s: level %d config file %s", __func__, reset, filename);
32745ae9d61Sreyk 
32845ae9d61Sreyk 	if (reset == RESET_RELOAD) {
32945ae9d61Sreyk 		config_setreset(env, RESET_POLICY, PROC_IKEV2);
330f36db9c4Syasuoka 		config_setreset(env, RESET_RADIUS, PROC_IKEV2);
331e8b444cdSreyk 		if (config_setkeys(env) == -1)
332e8b444cdSreyk 			fatalx("%s: failed to send keys", __func__);
33345ae9d61Sreyk 		config_setreset(env, RESET_CA, PROC_CERT);
33445ae9d61Sreyk 
33545ae9d61Sreyk 		if (parse_config(filename, env) == -1) {
33645ae9d61Sreyk 			log_debug("%s: failed to load config file %s",
33745ae9d61Sreyk 			    __func__, filename);
33845ae9d61Sreyk 		}
339fc20f985Sreyk 
340e2015428Sreyk 		/* Re-compile policies and skip steps */
341e2015428Sreyk 		config_setcompile(env, PROC_IKEV2);
342e2015428Sreyk 
343421819b6Stobhe 		config_setstatic(env);
344fc20f985Sreyk 		config_setcoupled(env, env->sc_decoupled ? 0 : 1);
3456d3b905bSmarkus 		config_setocsp(env);
346f9967a42Stobhe 		/* Must be last */
347f9967a42Stobhe 		config_setmode(env, env->sc_passive ? 1 : 0);
34845ae9d61Sreyk 	} else {
34945ae9d61Sreyk 		config_setreset(env, reset, PROC_IKEV2);
35045ae9d61Sreyk 		config_setreset(env, reset, PROC_CERT);
35145ae9d61Sreyk 	}
35245ae9d61Sreyk }
35345ae9d61Sreyk 
35445ae9d61Sreyk void
355701048fbSreyk parent_sig_handler(int sig, short event, void *arg)
35645ae9d61Sreyk {
357701048fbSreyk 	struct privsep	*ps = arg;
35845ae9d61Sreyk 	int		 die = 0, status, fail, id;
35945ae9d61Sreyk 	pid_t		 pid;
36045ae9d61Sreyk 	char		*cause;
36145ae9d61Sreyk 
36245ae9d61Sreyk 	switch (sig) {
36345ae9d61Sreyk 	case SIGHUP:
36445ae9d61Sreyk 		log_info("%s: reload requested with SIGHUP", __func__);
36545ae9d61Sreyk 
36645ae9d61Sreyk 		/*
36745ae9d61Sreyk 		 * This is safe because libevent uses async signal handlers
36845ae9d61Sreyk 		 * that run in the event loop and not in signal context.
36945ae9d61Sreyk 		 */
370701048fbSreyk 		parent_reload(ps->ps_env, 0, NULL);
37145ae9d61Sreyk 		break;
37245ae9d61Sreyk 	case SIGPIPE:
37345ae9d61Sreyk 		log_info("%s: ignoring SIGPIPE", __func__);
37445ae9d61Sreyk 		break;
375d5fd2e4bSreyk 	case SIGUSR1:
376d5fd2e4bSreyk 		log_info("%s: ignoring SIGUSR1", __func__);
377d5fd2e4bSreyk 		break;
37845ae9d61Sreyk 	case SIGTERM:
37945ae9d61Sreyk 	case SIGINT:
38091e971e4Stobhe 		die = 1;
38191e971e4Stobhe 		/* FALLTHROUGH */
38245ae9d61Sreyk 	case SIGCHLD:
38345ae9d61Sreyk 		do {
3846acea849Stobhe 			int len = 0;
385aa48e8d1Smillert 
38645ae9d61Sreyk 			pid = waitpid(-1, &status, WNOHANG);
38745ae9d61Sreyk 			if (pid <= 0)
38845ae9d61Sreyk 				continue;
38945ae9d61Sreyk 
39045ae9d61Sreyk 			fail = 0;
39145ae9d61Sreyk 			if (WIFSIGNALED(status)) {
39245ae9d61Sreyk 				fail = 1;
393aa48e8d1Smillert 				len = asprintf(&cause, "terminated; signal %d",
39445ae9d61Sreyk 				    WTERMSIG(status));
39545ae9d61Sreyk 			} else if (WIFEXITED(status)) {
39645ae9d61Sreyk 				if (WEXITSTATUS(status) != 0) {
39745ae9d61Sreyk 					fail = 1;
398aa48e8d1Smillert 					len = asprintf(&cause,
399aa48e8d1Smillert 					    "exited abnormally");
40045ae9d61Sreyk 				} else
401aa48e8d1Smillert 					len = asprintf(&cause, "exited okay");
40245ae9d61Sreyk 			} else
40345ae9d61Sreyk 				fatalx("unexpected cause of SIGCHLD");
40445ae9d61Sreyk 
405aa48e8d1Smillert 			if (len == -1)
406aa48e8d1Smillert 				fatal("asprintf");
407aa48e8d1Smillert 
40845ae9d61Sreyk 			die = 1;
40945ae9d61Sreyk 
41045ae9d61Sreyk 			for (id = 0; id < PROC_MAX; id++)
411701048fbSreyk 				if (pid == ps->ps_pid[id]) {
412e08ac948Sreyk 					if (fail)
41345ae9d61Sreyk 						log_warnx("lost child: %s %s",
414701048fbSreyk 						    ps->ps_title[id], cause);
41545ae9d61Sreyk 					break;
41645ae9d61Sreyk 				}
41745ae9d61Sreyk 
41845ae9d61Sreyk 			free(cause);
41945ae9d61Sreyk 		} while (pid > 0 || (pid == -1 && errno == EINTR));
42045ae9d61Sreyk 
42145ae9d61Sreyk 		if (die)
422701048fbSreyk 			parent_shutdown(ps->ps_env);
42345ae9d61Sreyk 		break;
42445ae9d61Sreyk 	default:
42545ae9d61Sreyk 		fatalx("unexpected signal");
42645ae9d61Sreyk 	}
42745ae9d61Sreyk }
42845ae9d61Sreyk 
42945ae9d61Sreyk int
430ebfc3693Sreyk parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg)
43145ae9d61Sreyk {
432e8e9d77fStobhe 	struct iked	*env = iked_env;
43345ae9d61Sreyk 
434ebfc3693Sreyk 	switch (imsg->hdr.type) {
4353fdfc9aaStobhe 	case IMSG_CTL_ACTIVE:
4363fdfc9aaStobhe 	case IMSG_CTL_PASSIVE:
4373fdfc9aaStobhe 		proc_forward_imsg(&env->sc_ps, imsg, PROC_IKEV2, -1);
4383fdfc9aaStobhe 		break;
439ebfc3693Sreyk 	case IMSG_OCSP_FD:
4404f9da335Stobhe 		ocsp_connect(env, imsg);
441ebfc3693Sreyk 		break;
442ebfc3693Sreyk 	default:
44345ae9d61Sreyk 		return (-1);
44445ae9d61Sreyk 	}
44545ae9d61Sreyk 
446ebfc3693Sreyk 	return (0);
447ebfc3693Sreyk }
448ebfc3693Sreyk 
44945ae9d61Sreyk int
450ebfc3693Sreyk parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
45145ae9d61Sreyk {
452e8e9d77fStobhe 	struct iked	*env = iked_env;
45345ae9d61Sreyk 	int		 v;
45445ae9d61Sreyk 	char		*str = NULL;
455d09d3a7dSreyk 	unsigned int	 type = imsg->hdr.type;
45645ae9d61Sreyk 
457fc20f985Sreyk 	switch (type) {
45845ae9d61Sreyk 	case IMSG_CTL_RESET:
45945ae9d61Sreyk 		IMSG_SIZE_CHECK(imsg, &v);
46045ae9d61Sreyk 		memcpy(&v, imsg->data, sizeof(v));
46145ae9d61Sreyk 		parent_reload(env, v, NULL);
462484f668cSreyk 		break;
463fc20f985Sreyk 	case IMSG_CTL_COUPLE:
464fc20f985Sreyk 	case IMSG_CTL_DECOUPLE:
465fc20f985Sreyk 	case IMSG_CTL_ACTIVE:
466fc20f985Sreyk 	case IMSG_CTL_PASSIVE:
467c205e972Sreyk 		proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0);
468484f668cSreyk 		break;
46945ae9d61Sreyk 	case IMSG_CTL_RELOAD:
47045ae9d61Sreyk 		if (IMSG_DATA_SIZE(imsg) > 0)
47145ae9d61Sreyk 			str = get_string(imsg->data, IMSG_DATA_SIZE(imsg));
47245ae9d61Sreyk 		parent_reload(env, 0, str);
47345ae9d61Sreyk 		free(str);
47445ae9d61Sreyk 		break;
4759a12c596Sreyk 	case IMSG_CTL_VERBOSE:
4769a12c596Sreyk 		proc_forward_imsg(&env->sc_ps, imsg, PROC_IKEV2, -1);
4779a12c596Sreyk 		proc_forward_imsg(&env->sc_ps, imsg, PROC_CERT, -1);
4789a12c596Sreyk 
4799a12c596Sreyk 		/* return 1 to let proc.c handle it locally */
4809a12c596Sreyk 		return (1);
481484f668cSreyk 	default:
482484f668cSreyk 		return (-1);
48345ae9d61Sreyk 	}
48445ae9d61Sreyk 
485484f668cSreyk 	return (0);
48645ae9d61Sreyk }
48745ae9d61Sreyk 
488cd3f460fStobhe int
489cd3f460fStobhe parent_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg)
490cd3f460fStobhe {
491e8e9d77fStobhe 	struct iked	*env = iked_env;
492cd3f460fStobhe 
493cd3f460fStobhe 	switch (imsg->hdr.type) {
494264f8b22Stobhe 	case IMSG_IF_ADDADDR:
495264f8b22Stobhe 	case IMSG_IF_DELADDR:
496264f8b22Stobhe 		return (vroute_getaddr(env, imsg));
4979ef39cf4Stobhe 	case IMSG_VDNS_ADD:
4989ef39cf4Stobhe 	case IMSG_VDNS_DEL:
4999ef39cf4Stobhe 		return (vroute_getdns(env, imsg));
500264f8b22Stobhe 	case IMSG_VROUTE_ADD:
501264f8b22Stobhe 	case IMSG_VROUTE_DEL:
502264f8b22Stobhe 		return (vroute_getroute(env, imsg));
503264f8b22Stobhe 	case IMSG_VROUTE_CLONE:
504264f8b22Stobhe 		return (vroute_getcloneroute(env, imsg));
505cd3f460fStobhe 	default:
506cd3f460fStobhe 		return (-1);
507cd3f460fStobhe 	}
508cd3f460fStobhe 
509cd3f460fStobhe 	return (0);
510cd3f460fStobhe }
511cd3f460fStobhe 
51245ae9d61Sreyk void
51345ae9d61Sreyk parent_shutdown(struct iked *env)
51445ae9d61Sreyk {
515fc7fd3e3Sreyk 	proc_kill(&env->sc_ps);
51645ae9d61Sreyk 
51791e971e4Stobhe 	vroute_cleanup(env);
51847ed2a28Stobhe 	free(env->sc_vroute);
51945ae9d61Sreyk 	free(env);
52045ae9d61Sreyk 
52145ae9d61Sreyk 	log_warnx("parent terminating");
52245ae9d61Sreyk 	exit(0);
52345ae9d61Sreyk }
524