xref: /openbsd-src/usr.sbin/sasyncd/monitor.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: monitor.c,v 1.12 2006/12/25 08:17:17 deraadt 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 <net/pfkeyv2.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40 #include <signal.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 
45 #include "monitor.h"
46 #include "sasyncd.h"
47 
48 struct m_state {
49 	pid_t	pid;
50 	int	s;
51 } m_state;
52 
53 volatile sig_atomic_t		sigchld = 0;
54 
55 static void	got_sigchld(int);
56 static void	sig_to_child(int);
57 static void	m_priv_pfkey_snap(int);
58 static void	m_priv_isakmpd_activate(void);
59 static void	m_priv_isakmpd_passivate(void);
60 static ssize_t	m_write(int, void *, size_t);
61 static ssize_t	m_read(int, void *, size_t);
62 
63 pid_t
64 monitor_init(void)
65 {
66 	struct passwd	*pw = getpwnam(SASYNCD_USER);
67 	extern char	*__progname;
68 	char		root[MAXPATHLEN];
69 	int		p[2];
70 
71 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0) {
72 		log_err("%s: socketpair failed - %s", __progname,
73 		    strerror(errno));
74 		exit(1);
75 	}
76 
77 	if (!pw) {
78 		log_err("%s: getpwnam(\"%s\") failed", __progname,
79 		    SASYNCD_USER);
80 		exit(1);
81 	}
82 	strlcpy(root, pw->pw_dir, sizeof root);
83 	endpwent();
84 
85 	signal(SIGCHLD, got_sigchld);
86 	signal(SIGTERM, sig_to_child);
87 	signal(SIGHUP, sig_to_child);
88 	signal(SIGINT, sig_to_child);
89 
90 	m_state.pid = fork();
91 
92 	if (m_state.pid == -1) {
93 		log_err("%s: fork failed - %s", __progname, strerror(errno));
94 		exit(1);
95 	} else if (m_state.pid == 0) {
96 		/* Child */
97 		m_state.s = p[0];
98 		close(p[1]);
99 
100 		if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) {
101 			log_err("%s: chroot failed", __progname);
102 			exit(1);
103 		}
104 
105 		if (setgroups(1, &pw->pw_gid) ||
106 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
107 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
108 			log_err("%s: failed to drop privileges", __progname);
109 			exit(1);
110 		}
111 	} else {
112 		/* Parent */
113 		setproctitle("[priv]");
114 		m_state.s = p[1];
115 		close(p[0]);
116 	}
117 	return m_state.pid;
118 }
119 
120 static void
121 got_sigchld(int s)
122 {
123 	sigchld = 1;
124 }
125 
126 static void
127 sig_to_child(int s)
128 {
129 	if (m_state.pid != -1)
130 		kill(m_state.pid, s);
131 }
132 
133 static void
134 monitor_drain_input(void)
135 {
136 	int		one = 1;
137 	u_int8_t	tmp;
138 
139 	ioctl(m_state.s, FIONBIO, &one);
140 	while (m_read(m_state.s, &tmp, 1) > 0)
141 		;
142 	ioctl(m_state.s, FIONBIO, 0);
143 }
144 
145 /* We only use privsep to get in-kernel SADB and SPD snapshots via sysctl */
146 void
147 monitor_loop(void)
148 {
149 	u_int32_t	v;
150 	ssize_t		r;
151 
152 	for (;;) {
153 		if (sigchld) {
154 			pid_t	pid;
155 			int	status;
156 			do {
157 				pid = waitpid(m_state.pid, &status, WNOHANG);
158 			} while (pid == -1 && errno == EINTR);
159 
160 			if (pid == m_state.pid &&
161 			    (WIFEXITED(status) || WIFSIGNALED(status)))
162 				break;
163 		}
164 
165 		/* Wait for next task */
166 		if ((r = m_read(m_state.s, &v, sizeof v)) < 1) {
167 			if (r == -1)
168 				log_err(0, "monitor_loop: read() ");
169 			break;
170 		}
171 
172 		switch (v) {
173 		case MONITOR_GETSNAP:
174 			/* Get the data. */
175 			m_priv_pfkey_snap(m_state.s);
176 			break;
177 		case MONITOR_CARPINC:
178 			carp_demote(CARP_INC, 1);
179 			break;
180 		case MONITOR_CARPDEC:
181 			carp_demote(CARP_DEC, 1);
182 			break;
183 		case MONITOR_ISAKMPD_ACTIVATE:
184 			m_priv_isakmpd_activate();
185 			break;
186 		case MONITOR_ISAKMPD_PASSIVATE:
187 			m_priv_isakmpd_passivate();
188 			break;
189 		}
190 	}
191 
192 	monitor_carpundemote(NULL);
193 
194 	if (!sigchld)
195 		log_msg(0, "monitor_loop: priv process exiting abnormally");
196 	exit(0);
197 }
198 
199 void
200 monitor_carpundemote(void *v)
201 {
202 	u_int32_t mtype = MONITOR_CARPDEC;
203 	if (!carp_demoted)
204 		return;
205 	if (m_write(m_state.s, &mtype, sizeof mtype) < 1)
206 		log_msg(1, "monitor_carpundemote: unable to write to monitor");
207 	else
208 		carp_demoted = 0;
209 }
210 
211 void
212 monitor_carpdemote(void *v)
213 {
214 	u_int32_t mtype = MONITOR_CARPINC;
215 	if (carp_demoted)
216 		return;
217 	if (m_write(m_state.s, &mtype, sizeof mtype) < 1)
218 		log_msg(1, "monitor_carpdemote: unable to write to monitor");
219 	else
220 		carp_demoted = 1;
221 }
222 
223 int
224 monitor_get_pfkey_snap(u_int8_t **sadb, u_int32_t *sadbsize, u_int8_t **spd,
225     u_int32_t *spdsize)
226 {
227 	u_int32_t	v;
228 	ssize_t		rbytes;
229 
230 	v = MONITOR_GETSNAP;
231 	if (m_write(m_state.s, &v, sizeof v) < 1)
232 		return -1;
233 
234 	/* Read SADB data. */
235 	*sadb = *spd = NULL;
236 	*spdsize = 0;
237 	if (m_read(m_state.s, sadbsize, sizeof *sadbsize) < 1)
238 		return -1;
239 	if (*sadbsize) {
240 		*sadb = (u_int8_t *)malloc(*sadbsize);
241 		if (!*sadb) {
242 			log_err("monitor_get_pfkey_snap: malloc()");
243 			monitor_drain_input();
244 			return -1;
245 		}
246 		rbytes = m_read(m_state.s, *sadb, *sadbsize);
247 		if (rbytes < 1) {
248 			memset(*sadb, 0, *sadbsize);
249 			free(*sadb);
250 			return -1;
251 		}
252 	}
253 
254 	/* Read SPD data */
255 	if (m_read(m_state.s, spdsize, sizeof *spdsize) < 1) {
256 		if (*sadbsize) {
257 			memset(*sadb, 0, *sadbsize);
258 			free(*sadb);
259 		}
260 		return -1;
261 	}
262 	if (*spdsize) {
263 		*spd = (u_int8_t *)malloc(*spdsize);
264 		if (!*spd) {
265 			log_err("monitor_get_pfkey_snap: malloc()");
266 			monitor_drain_input();
267 			if (*sadbsize) {
268 				memset(*sadb, 0, *sadbsize);
269 				free(*sadb);
270 			}
271 			return -1;
272 		}
273 		rbytes = m_read(m_state.s, *spd, *spdsize);
274 		if (rbytes < 1) {
275 			memset(*spd, 0, *spdsize);
276 			free(*spd);
277 			if (*sadbsize) {
278 				memset(*sadb, 0, *sadbsize);
279 				free(*sadb);
280 			}
281 			return -1;
282 		}
283 	}
284 
285 	log_msg(3, "monitor_get_pfkey_snap: got %u bytes SADB, %u bytes SPD",
286 	    *sadbsize, *spdsize);
287 	return 0;
288 }
289 
290 int
291 monitor_isakmpd_active(int active)
292 {
293 	u_int32_t	cmd =
294 	    active ? MONITOR_ISAKMPD_ACTIVATE : MONITOR_ISAKMPD_PASSIVATE;
295 	if (write(m_state.s, &cmd, sizeof cmd) < 1)
296 		return -1;
297 	return 0;
298 }
299 
300 /* Privileged */
301 static void
302 m_priv_pfkey_snap(int s)
303 {
304 	u_int8_t	*sadb_buf = 0, *spd_buf = 0;
305 	size_t		 sadb_buflen = 0, spd_buflen = 0, sz;
306 	int		 mib[5];
307 	u_int32_t	 v;
308 
309 	mib[0] = CTL_NET;
310 	mib[1] = PF_KEY;
311 	mib[2] = PF_KEY_V2;
312 	mib[3] = NET_KEY_SADB_DUMP;
313 	mib[4] = 0; /* Unspec SA type */
314 
315 	/* First, fetch SADB data */
316 	if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1
317 	    || sz == 0) {
318 		sadb_buflen = 0;
319 		goto try_spd;
320 	}
321 
322 	sadb_buflen = sz;
323 	if ((sadb_buf = malloc(sadb_buflen)) == NULL) {
324 		log_err("m_priv_pfkey_snap: malloc");
325 		sadb_buflen = 0;
326 		goto out;
327 	}
328 
329 	if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, NULL, 0)
330 	    == -1) {
331 		log_err("m_priv_pfkey_snap: sysctl");
332 		sadb_buflen = 0;
333 		goto out;
334 	}
335 
336 	/* Next, fetch SPD data */
337   try_spd:
338 	mib[3] = NET_KEY_SPD_DUMP;
339 
340 	if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1
341 	    || sz == 0) {
342 		spd_buflen = 0;
343 		goto out;
344 	}
345 
346 	spd_buflen = sz;
347 	if ((spd_buf = malloc(spd_buflen)) == NULL) {
348 		log_err("m_priv_pfkey_snap: malloc");
349 		spd_buflen = 0;
350 		goto out;
351 	}
352 
353 	if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, 0)
354 	    == -1) {
355 		log_err("m_priv_pfkey_snap: sysctl");
356 		spd_buflen = 0;
357 		goto out;
358 	}
359 
360   out:
361 	/* Return SADB data */
362 	v = (u_int32_t)sadb_buflen;
363 	if (m_write(s, &v, sizeof v) == -1) {
364 		log_err("m_priv_pfkey_snap: write");
365 		return;
366 	}
367 	if (sadb_buflen) {
368 		if (m_write(s, sadb_buf, sadb_buflen) == -1)
369 			log_err("m_priv_pfkey_snap: write");
370 		memset(sadb_buf, 0, sadb_buflen);
371 		free(sadb_buf);
372 	}
373 
374 	/* Return SPD data */
375 	v = (u_int32_t)spd_buflen;
376 	if (m_write(s, &v, sizeof v) == -1) {
377 		log_err("m_priv_pfkey_snap: write");
378 		return;
379 	}
380 	if (spd_buflen) {
381 		if (m_write(s, spd_buf, spd_buflen) == -1)
382 			log_err("m_priv_pfkey_snap: write");
383 		memset(spd_buf, 0, spd_buflen);
384 		free(spd_buf);
385 	}
386 	return;
387 }
388 
389 static void
390 m_priv_isakmpd_fifocmd(const char *cmd)
391 {
392 	struct stat	sb;
393 	int		fd = -1;
394 
395 	if ((fd = open(ISAKMPD_FIFO, O_WRONLY)) == -1) {
396 		log_err("m_priv_isakmpd_fifocmd: open(%s)", ISAKMPD_FIFO);
397 		goto out;
398 	}
399 	if (fstat(fd, &sb) == -1) {
400 		log_err("m_priv_isakmpd_fifocmd: fstat(%s)", ISAKMPD_FIFO);
401 		goto out;
402 	}
403 	if (!S_ISFIFO(sb.st_mode)) {
404 		log_err("m_priv_isakmpd_fifocmd: %s not a fifo", ISAKMPD_FIFO);
405 		goto out;
406 	}
407 
408 	if (write(fd, cmd, strlen(cmd)) == -1) {
409 		log_err("m_priv_isakmpd_fifocmd write");
410 		goto out;
411 	}
412  out:
413 	if (fd != -1)
414 		close(fd);
415 	/* No values returned. */
416 	return;
417 }
418 
419 static void
420 m_priv_isakmpd_activate(void)
421 {
422 	m_priv_isakmpd_fifocmd("M active\n");
423 }
424 
425 static void
426 m_priv_isakmpd_passivate(void)
427 {
428 	m_priv_isakmpd_fifocmd("M passive\n");
429 }
430 
431 ssize_t
432 m_write(int sock, void *buf, size_t len)
433 {
434 	ssize_t n;
435 	size_t pos = 0;
436 	char *ptr = buf;
437 
438 	while (len > pos) {
439 		switch (n = write(sock, ptr + pos, len - pos)) {
440 		case -1:
441 			if (errno == EINTR || errno == EAGAIN)
442 				continue;
443 			/* FALLTHROUGH */
444 		case 0:
445 			return n;
446 			/* NOTREACHED */
447 		default:
448 			pos += n;
449 		}
450 	}
451 	return pos;
452 }
453 
454 ssize_t
455 m_read(int sock, void *buf, size_t len)
456 {
457 	ssize_t n;
458 	size_t pos = 0;
459 	char *ptr = buf;
460 
461 	while (len > pos) {
462 		switch (n = read(sock, ptr + pos, len - pos)) {
463 		case -1:
464 			if (errno == EINTR || errno == EAGAIN)
465 				continue;
466 			/* FALLTHROUGH */
467 		case 0:
468 			return n;
469 			/* NOTREACHED */
470 		default:
471 			pos += n;
472 		}
473 	}
474 	return pos;
475 }
476