xref: /openbsd-src/usr.sbin/sasyncd/monitor.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: monitor.c,v 1.17 2012/09/12 07:45:19 haesbaert Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 H�kan Olsson.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 #include <sys/wait.h>
35 #include <sys/un.h>
36 #include <net/pfkeyv2.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <pwd.h>
41 #include <signal.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <imsg.h>
46 
47 #include "types.h"	/* iked imsg types */
48 
49 #include "monitor.h"
50 #include "sasyncd.h"
51 
52 struct m_state {
53 	pid_t	pid;
54 	int	s;
55 } m_state;
56 
57 volatile sig_atomic_t		sigchld = 0;
58 
59 static void	got_sigchld(int);
60 static void	sig_to_child(int);
61 static void	m_priv_pfkey_snap(int);
62 static int	m_priv_control_activate(void);
63 static int	m_priv_control_passivate(void);
64 static ssize_t	m_write(int, void *, size_t);
65 static ssize_t	m_read(int, void *, size_t);
66 
67 pid_t
68 monitor_init(void)
69 {
70 	struct passwd	*pw = getpwnam(SASYNCD_USER);
71 	extern char	*__progname;
72 	char		root[MAXPATHLEN];
73 	int		p[2];
74 
75 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0) {
76 		log_err("%s: socketpair failed - %s", __progname,
77 		    strerror(errno));
78 		exit(1);
79 	}
80 
81 	if (!pw) {
82 		log_err("%s: getpwnam(\"%s\") failed", __progname,
83 		    SASYNCD_USER);
84 		exit(1);
85 	}
86 	strlcpy(root, pw->pw_dir, sizeof root);
87 	endpwent();
88 
89 	signal(SIGCHLD, got_sigchld);
90 	signal(SIGTERM, sig_to_child);
91 	signal(SIGHUP, sig_to_child);
92 	signal(SIGINT, sig_to_child);
93 
94 	m_state.pid = fork();
95 
96 	if (m_state.pid == -1) {
97 		log_err("%s: fork failed - %s", __progname, strerror(errno));
98 		exit(1);
99 	} else if (m_state.pid == 0) {
100 		/* Child */
101 		m_state.s = p[0];
102 		close(p[1]);
103 
104 		if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) {
105 			log_err("%s: chroot failed", __progname);
106 			exit(1);
107 		}
108 
109 		if (setgroups(1, &pw->pw_gid) ||
110 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
111 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
112 			log_err("%s: failed to drop privileges", __progname);
113 			exit(1);
114 		}
115 	} else {
116 		/* Parent */
117 		setproctitle("[priv]");
118 		m_state.s = p[1];
119 		close(p[0]);
120 	}
121 	return m_state.pid;
122 }
123 
124 static void
125 got_sigchld(int s)
126 {
127 	sigchld = 1;
128 }
129 
130 static void
131 sig_to_child(int s)
132 {
133 	if (m_state.pid != -1)
134 		kill(m_state.pid, s);
135 }
136 
137 static void
138 monitor_drain_input(void)
139 {
140 	int		one = 1;
141 	u_int8_t	tmp;
142 
143 	ioctl(m_state.s, FIONBIO, &one);
144 	while (m_read(m_state.s, &tmp, 1) > 0)
145 		;
146 	ioctl(m_state.s, FIONBIO, 0);
147 }
148 
149 /* We only use privsep to get in-kernel SADB and SPD snapshots via sysctl */
150 void
151 monitor_loop(void)
152 {
153 	u_int32_t	 v, vn;
154 	ssize_t		 r;
155 	fd_set		 rfds;
156 	int		 ret;
157 	struct timeval	*tvp, tv;
158 
159 	FD_ZERO(&rfds);
160 	tvp = NULL;
161 	vn = 0;
162 
163 	for (;;) {
164 		ret = 0;
165 		v = 0;
166 
167 		if (sigchld) {
168 			pid_t	pid;
169 			int	status;
170 			do {
171 				pid = waitpid(m_state.pid, &status, WNOHANG);
172 			} while (pid == -1 && errno == EINTR);
173 
174 			if (pid == m_state.pid &&
175 			    (WIFEXITED(status) || WIFSIGNALED(status)))
176 				break;
177 		}
178 
179 		FD_SET(m_state.s, &rfds);
180 		if (select(m_state.s + 1, &rfds, NULL, NULL, tvp) == -1) {
181 			if (errno == EINTR || errno == EAGAIN)
182 				continue;
183 			log_err("monitor_loop: select()");
184 			break;
185 		}
186 
187 		/* Wait for next task */
188 		if (FD_ISSET(m_state.s, &rfds)) {
189 			if ((r = m_read(m_state.s, &v, sizeof v)) < 1) {
190 				if (r == -1)
191 					log_err("monitor_loop: read()");
192 				break;
193 			}
194 		}
195 
196 		/* Retry after timeout */
197 		if (v == 0 && tvp != NULL) {
198 			v = vn;
199 			tvp = NULL;
200 			vn = 0;
201 		}
202 
203 		switch (v) {
204 		case MONITOR_GETSNAP:
205 			/* Get the data. */
206 			m_priv_pfkey_snap(m_state.s);
207 			break;
208 		case MONITOR_CARPINC:
209 			carp_demote(CARP_INC, 1);
210 			break;
211 		case MONITOR_CARPDEC:
212 			carp_demote(CARP_DEC, 1);
213 			break;
214 		case MONITOR_CONTROL_ACTIVATE:
215 			ret = m_priv_control_activate();
216 			break;
217 		case MONITOR_CONTROL_PASSIVATE:
218 			ret = m_priv_control_passivate();
219 			break;
220 		}
221 
222 		if (ret == -1) {
223 			/* Trigger retry after timeout */
224 			tv.tv_sec = MONITOR_RETRY_TIMEOUT;
225 			tv.tv_usec = 0;
226 			tvp = &tv;
227 			vn = v;
228 		}
229 	}
230 
231 	monitor_carpundemote(NULL);
232 
233 	if (!sigchld)
234 		log_msg(0, "monitor_loop: priv process exiting abnormally");
235 	exit(0);
236 }
237 
238 void
239 monitor_carpundemote(void *v)
240 {
241 	u_int32_t mtype = MONITOR_CARPDEC;
242 	if (!carp_demoted)
243 		return;
244 	if (m_write(m_state.s, &mtype, sizeof mtype) < 1)
245 		log_msg(1, "monitor_carpundemote: unable to write to monitor");
246 	else
247 		carp_demoted = 0;
248 }
249 
250 void
251 monitor_carpdemote(void *v)
252 {
253 	u_int32_t mtype = MONITOR_CARPINC;
254 	if (carp_demoted)
255 		return;
256 	if (m_write(m_state.s, &mtype, sizeof mtype) < 1)
257 		log_msg(1, "monitor_carpdemote: unable to write to monitor");
258 	else
259 		carp_demoted = 1;
260 }
261 
262 int
263 monitor_get_pfkey_snap(u_int8_t **sadb, u_int32_t *sadbsize, u_int8_t **spd,
264     u_int32_t *spdsize)
265 {
266 	u_int32_t	v;
267 	ssize_t		rbytes;
268 
269 	v = MONITOR_GETSNAP;
270 	if (m_write(m_state.s, &v, sizeof v) < 1)
271 		return -1;
272 
273 	/* Read SADB data. */
274 	*sadb = *spd = NULL;
275 	*spdsize = 0;
276 	if (m_read(m_state.s, sadbsize, sizeof *sadbsize) < 1)
277 		return -1;
278 	if (*sadbsize) {
279 		*sadb = (u_int8_t *)malloc(*sadbsize);
280 		if (!*sadb) {
281 			log_err("monitor_get_pfkey_snap: malloc()");
282 			monitor_drain_input();
283 			return -1;
284 		}
285 		rbytes = m_read(m_state.s, *sadb, *sadbsize);
286 		if (rbytes < 1) {
287 			memset(*sadb, 0, *sadbsize);
288 			free(*sadb);
289 			return -1;
290 		}
291 	}
292 
293 	/* Read SPD data */
294 	if (m_read(m_state.s, spdsize, sizeof *spdsize) < 1) {
295 		if (*sadbsize) {
296 			memset(*sadb, 0, *sadbsize);
297 			free(*sadb);
298 		}
299 		return -1;
300 	}
301 	if (*spdsize) {
302 		*spd = (u_int8_t *)malloc(*spdsize);
303 		if (!*spd) {
304 			log_err("monitor_get_pfkey_snap: malloc()");
305 			monitor_drain_input();
306 			if (*sadbsize) {
307 				memset(*sadb, 0, *sadbsize);
308 				free(*sadb);
309 			}
310 			return -1;
311 		}
312 		rbytes = m_read(m_state.s, *spd, *spdsize);
313 		if (rbytes < 1) {
314 			memset(*spd, 0, *spdsize);
315 			free(*spd);
316 			if (*sadbsize) {
317 				memset(*sadb, 0, *sadbsize);
318 				free(*sadb);
319 			}
320 			return -1;
321 		}
322 	}
323 
324 	log_msg(2, "monitor_get_pfkey_snap: got %u bytes SADB, %u bytes SPD",
325 	    *sadbsize, *spdsize);
326 	return 0;
327 }
328 
329 int
330 monitor_control_active(int active)
331 {
332 	u_int32_t	cmd =
333 	    active ? MONITOR_CONTROL_ACTIVATE : MONITOR_CONTROL_PASSIVATE;
334 	if (write(m_state.s, &cmd, sizeof cmd) < 1)
335 		return -1;
336 	return 0;
337 }
338 
339 /* Privileged */
340 static void
341 m_priv_pfkey_snap(int s)
342 {
343 	u_int8_t	*sadb_buf = NULL, *spd_buf = NULL;
344 	size_t		 sadb_buflen = 0, spd_buflen = 0, sz;
345 	int		 mib[5];
346 	u_int32_t	 v;
347 
348 	mib[0] = CTL_NET;
349 	mib[1] = PF_KEY;
350 	mib[2] = PF_KEY_V2;
351 	mib[3] = NET_KEY_SADB_DUMP;
352 	mib[4] = 0; /* Unspec SA type */
353 
354 	/* First, fetch SADB data */
355 	for (;;) {
356 		if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0)
357 		    == -1)
358 			break;
359 
360 		if (!sz)
361 			break;
362 
363 		/* Try to catch newly added data */
364 		sz *= 2;
365 
366 		if ((sadb_buf = malloc(sz)) == NULL)
367 			break;
368 
369 		if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, NULL, 0)
370 		    == -1) {
371 			free(sadb_buf);
372 			sadb_buf = NULL;
373 			/*
374 			 * If new SAs were added meanwhile and the given buffer is
375 			 * too small, retry.
376 			 */
377 			if (errno == ENOMEM)
378 				continue;
379 			break;
380 		}
381 
382 		sadb_buflen = sz;
383 		break;
384 	}
385 
386 	/* Next, fetch SPD data */
387 	mib[3] = NET_KEY_SPD_DUMP;
388 
389 	for (;;) {
390 		if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0)
391 		    == -1)
392 			break;
393 
394 		if (!sz)
395 			break;
396 
397 		/* Try to catch newly added data */
398 		sz *= 2;
399 
400 		if ((spd_buf = malloc(sz)) == NULL)
401 			break;
402 
403 		if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, 0)
404 		    == -1) {
405 			free(spd_buf);
406 			spd_buf = NULL;
407 			/*
408 			 * If new SPDs were added meanwhile and the given buffer is
409 			 * too small, retry.
410 			 */
411 			if (errno == ENOMEM)
412 				continue;
413 			break;
414 		}
415 
416 		spd_buflen = sz;
417 		break;
418 	}
419 
420 	/* Return SADB data */
421 	v = (u_int32_t)sadb_buflen;
422 	if (m_write(s, &v, sizeof v) == -1) {
423 		log_err("m_priv_pfkey_snap: write");
424 		goto cleanup;
425 	}
426 	if (m_write(s, sadb_buf, sadb_buflen) == -1) {
427 		log_err("m_priv_pfkey_snap: write");
428 		goto cleanup;
429 	}
430 
431 	/* Return SPD data */
432 	v = (u_int32_t)spd_buflen;
433 	if (m_write(s, &v, sizeof v) == -1) {
434 		log_err("m_priv_pfkey_snap: write");
435 		goto cleanup;
436 	}
437 	if (m_write(s, spd_buf, spd_buflen) == -1) {
438 		log_err("m_priv_pfkey_snap: write");
439 		goto cleanup;
440 	}
441 
442 cleanup:
443 	if (sadb_buf) {
444 		memset(sadb_buf, 0, sadb_buflen);
445 		free(sadb_buf);
446 	}
447 	if (spd_buf) {
448 		memset(spd_buf, 0, spd_buflen);
449 		free(spd_buf);
450 	}
451 }
452 
453 static int
454 m_priv_isakmpd_fifocmd(const char *cmd)
455 {
456 	struct stat	sb;
457 	int		fd = -1, ret = -1;
458 
459 	if ((fd = open(ISAKMPD_FIFO, O_WRONLY)) == -1) {
460 		log_err("m_priv_isakmpd_fifocmd: open(%s)", ISAKMPD_FIFO);
461 		goto out;
462 	}
463 	if (fstat(fd, &sb) == -1) {
464 		log_err("m_priv_isakmpd_fifocmd: fstat(%s)", ISAKMPD_FIFO);
465 		goto out;
466 	}
467 	if (!S_ISFIFO(sb.st_mode)) {
468 		log_err("m_priv_isakmpd_fifocmd: %s not a fifo", ISAKMPD_FIFO);
469 		goto out;
470 	}
471 
472 	if (write(fd, cmd, strlen(cmd)) == -1) {
473 		log_err("m_priv_isakmpd_fifocmd write");
474 		goto out;
475 	}
476 
477 	ret = 0;
478  out:
479 	if (fd != -1)
480 		close(fd);
481 
482 	return (ret);
483 }
484 
485 static int
486 m_priv_iked_imsg(u_int cmd)
487 {
488 	struct sockaddr_un	 sun;
489 	int			 fd = -1, ret = -1;
490 	struct imsgbuf		 ibuf;
491 
492 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
493 		log_err("m_priv_iked_imsg: socket");
494 		goto out;
495 	}
496 
497 	bzero(&sun, sizeof(sun));
498 	sun.sun_family = AF_UNIX;
499 	strlcpy(sun.sun_path, IKED_SOCKET, sizeof(sun.sun_path));
500 
501 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
502 		log_err("m_priv_iked_imsg: connect");
503 		goto out;
504 	}
505 
506 	imsg_init(&ibuf, fd);
507 	if (imsg_compose(&ibuf, cmd, 0, 0, -1, NULL, 0) == -1) {
508 		log_err("m_priv_iked_imsg: compose");
509 		goto err;
510 	}
511 	if (imsg_flush(&ibuf) == -1) {
512 		log_err("m_priv_iked_imsg: flush");
513 		goto err;
514 	}
515 
516 	ret = 0;
517  err:
518 	imsg_clear(&ibuf);
519  out:
520 	if (fd != -1)
521 		close(fd);
522 
523 	return (ret);
524 }
525 
526 static int
527 m_priv_control_activate(void)
528 {
529 	if (cfgstate.flags & CTL_ISAKMPD)
530 		if (m_priv_isakmpd_fifocmd("M active\n") == -1)
531 			return (-1);
532 	if (cfgstate.flags & CTL_IKED)
533 		if (m_priv_iked_imsg(IMSG_CTL_ACTIVE) == -1)
534 			return (-1);
535 	return (0);
536 }
537 
538 static int
539 m_priv_control_passivate(void)
540 {
541 	if (cfgstate.flags & CTL_ISAKMPD)
542 		if (m_priv_isakmpd_fifocmd("M passive\n") == -1)
543 			return (-1);
544 	if (cfgstate.flags & CTL_IKED)
545 		if (m_priv_iked_imsg(IMSG_CTL_PASSIVE) == -1)
546 			return (-1);
547 	return (0);
548 }
549 
550 ssize_t
551 m_write(int sock, void *buf, size_t len)
552 {
553 	ssize_t n;
554 	size_t pos = 0;
555 	char *ptr = buf;
556 
557 	while (len > pos) {
558 		switch (n = write(sock, ptr + pos, len - pos)) {
559 		case -1:
560 			if (errno == EINTR || errno == EAGAIN)
561 				continue;
562 			/* FALLTHROUGH */
563 		case 0:
564 			return n;
565 			/* NOTREACHED */
566 		default:
567 			pos += n;
568 		}
569 	}
570 	return pos;
571 }
572 
573 ssize_t
574 m_read(int sock, void *buf, size_t len)
575 {
576 	ssize_t n;
577 	size_t pos = 0;
578 	char *ptr = buf;
579 
580 	while (len > pos) {
581 		switch (n = read(sock, ptr + pos, len - pos)) {
582 		case -1:
583 			if (errno == EINTR || errno == EAGAIN)
584 				continue;
585 			/* FALLTHROUGH */
586 		case 0:
587 			return n;
588 			/* NOTREACHED */
589 		default:
590 			pos += n;
591 		}
592 	}
593 	return pos;
594 }
595