xref: /openbsd/usr.sbin/sasyncd/pfkey.c (revision cca36db2)
1 /*	$OpenBSD: pfkey.c,v 1.20 2010/06/29 18:10:04 kjell 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 /*
29  * This code was written under funding by Multicom Security AB.
30  */
31 
32 
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <sys/queue.h>
38 #include <sys/sysctl.h>
39 #include <net/pfkeyv2.h>
40 #include <netinet/ip_ipsp.h>
41 
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "sasyncd.h"
49 #include "monitor.h"
50 #include "net.h"
51 
52 struct pfkey_msg
53 {
54 	SIMPLEQ_ENTRY(pfkey_msg)	next;
55 
56 	u_int8_t	*buf;
57 	u_int32_t	 len;
58 };
59 
60 SIMPLEQ_HEAD(, pfkey_msg)		pfkey_msglist;
61 
62 static const char *msgtypes[] = {
63 	"RESERVED", "GETSPI", "UPDATE", "ADD", "DELETE", "GET", "ACQUIRE",
64 	"REGISTER", "EXPIRE", "FLUSH", "DUMP", "X_PROMISC", "X_ADDFLOW",
65 	"X_DELFLOW", "X_GRPSPIS", "X_ASKPOLICY", "X_SPDDUMP"
66 };
67 
68 #define CHUNK sizeof(u_int64_t)
69 
70 static const char *pfkey_print_type(struct sadb_msg *);
71 
72 static int
73 pfkey_write(u_int8_t *buf, ssize_t len)
74 {
75 	struct sadb_msg *msg = (struct sadb_msg *)buf;
76 	ssize_t n;
77 
78 	if (cfgstate.pfkey_socket == -1)
79 		return 0;
80 
81 	do {
82 		n = write(cfgstate.pfkey_socket, buf, len);
83 	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
84 	if (n == -1) {
85 		log_err("pfkey: msg %s write() failed on socket %d",
86 		    pfkey_print_type(msg), cfgstate.pfkey_socket);
87 		return -1;
88 	}
89 
90 	return 0;
91 }
92 
93 int
94 pfkey_set_promisc(void)
95 {
96 	struct sadb_msg	msg;
97 	static u_int32_t seq = 1;
98 
99 	memset(&msg, 0, sizeof msg);
100 	msg.sadb_msg_version = PF_KEY_V2;
101 	msg.sadb_msg_seq = seq++;
102 	msg.sadb_msg_satype = 1; /* Special; 1 to enable, 0 to disable */
103 	msg.sadb_msg_type = SADB_X_PROMISC;
104 	msg.sadb_msg_pid = getpid();
105 	msg.sadb_msg_len = sizeof msg / CHUNK;
106 
107 	return pfkey_write((u_int8_t *)&msg, sizeof msg);
108 }
109 
110 /* Send a SADB_FLUSH PFKEY message to peer 'p' */
111 static void
112 pfkey_send_flush(struct syncpeer *p)
113 {
114 	struct sadb_msg *m = (struct sadb_msg *)calloc(1, sizeof *m);
115 	static u_int32_t seq = 1;
116 
117 	if (m) {
118 		memset(m, 0, sizeof *m);
119 		m->sadb_msg_version = PF_KEY_V2;
120 		m->sadb_msg_seq = seq++;
121 		m->sadb_msg_type = SADB_FLUSH;
122 		m->sadb_msg_satype = SADB_SATYPE_UNSPEC;
123 		m->sadb_msg_pid = getpid();
124 		m->sadb_msg_len = sizeof *m / CHUNK;
125 
126 		log_msg(2, "pfkey_send_flush: sending FLUSH to peer %s",
127 		    p->name);
128 		net_queue(p, MSG_PFKEYDATA, (u_int8_t *)m, sizeof *m);
129 	}
130 }
131 
132 static const char *
133 pfkey_print_type(struct sadb_msg *msg)
134 {
135 	static char	uk[20];
136 
137 	if (msg->sadb_msg_type < sizeof msgtypes / sizeof msgtypes[0])
138 		return msgtypes[msg->sadb_msg_type];
139 	else {
140 		snprintf(uk, sizeof uk, "<unknown(%d)>", msg->sadb_msg_type);
141 		return uk;
142 	}
143 }
144 
145 static struct sadb_ext *
146 pfkey_find_ext(struct sadb_msg *msg, u_int16_t type)
147 {
148 	struct sadb_ext	*ext;
149 	u_int8_t	*e;
150 
151 	for (e = (u_int8_t *)msg + sizeof *msg;
152 	     e < (u_int8_t *)msg + msg->sadb_msg_len * CHUNK;
153 	     e += ext->sadb_ext_len * CHUNK) {
154 		ext = (struct sadb_ext *)e;
155 		if (ext->sadb_ext_len == 0)
156 			break;
157 		if (ext->sadb_ext_type != type)
158 			continue;
159 		return ext;
160 	}
161 	return NULL;
162 }
163 
164 /* Return: 0 means ok to sync msg, 1 means to skip it */
165 static int
166 pfkey_msg_filter(struct sadb_msg *msg)
167 {
168 	struct sockaddr		*src = 0, *dst = 0;
169 	struct syncpeer		*p;
170 	struct sadb_ext		*ext;
171 	u_int8_t		*max;
172 
173 	switch (msg->sadb_msg_type) {
174 	case SADB_X_PROMISC:
175 	case SADB_DUMP:
176 	case SADB_GET:
177 	case SADB_GETSPI:
178 	case SADB_ACQUIRE:
179 	case SADB_X_ASKPOLICY:
180 	case SADB_REGISTER:
181 		/* Some messages should not be synced. */
182 		return 1;
183 
184 	case SADB_ADD:
185 		/* No point in syncing LARVAL SAs */
186 		if (pfkey_find_ext(msg, SADB_EXT_KEY_ENCRYPT) == 0)
187 			return 1;
188 	case SADB_DELETE:
189 	case SADB_X_ADDFLOW:
190 	case SADB_X_DELFLOW:
191 	case SADB_EXPIRE:
192 		/* Continue below */
193 		break;
194 	case SADB_FLUSH:
195 		if ((cfgstate.flags & FM_MASK) == FM_NEVER)
196 			return 1;
197 		break;
198 	default:
199 		return 0;
200 	}
201 
202 	if ((cfgstate.flags & SKIP_LOCAL_SAS) == 0)
203 		return 0;
204 
205 	/* SRC or DST address of this msg must not be one of our peers. */
206 	ext = pfkey_find_ext(msg, SADB_EXT_ADDRESS_SRC);
207 	if (ext)
208 		src = (struct sockaddr *)((struct sadb_address *)ext + 1);
209 	ext = pfkey_find_ext(msg, SADB_EXT_ADDRESS_DST);
210 	if (ext)
211 		dst = (struct sockaddr *)((struct sadb_address *)ext + 1);
212 	if (!src && !dst)
213 		return 0;
214 
215 	max = (u_int8_t *)msg + msg->sadb_msg_len * CHUNK;
216 	if (src && ((u_int8_t *)src + src->sa_len) > max)
217 		return 1;
218 	if (dst && ((u_int8_t *)dst + dst->sa_len) > max)
219 		return 1;
220 
221 	/* Found SRC or DST, check it against our peers */
222 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
223 		if (p->socket < 0 || p->sa->sa_family !=
224 		    (src ? src->sa_family : dst->sa_family))
225 			continue;
226 
227 		switch (p->sa->sa_family) {
228 		case AF_INET:
229 			if (src && memcmp(
230 			    &((struct sockaddr_in *)p->sa)->sin_addr.s_addr,
231 			    &((struct sockaddr_in *)src)->sin_addr.s_addr,
232 			    sizeof(struct in_addr)) == 0)
233 				return 1;
234 			if (dst && memcmp(
235 			    &((struct sockaddr_in *)p->sa)->sin_addr.s_addr,
236 			    &((struct sockaddr_in *)dst)->sin_addr.s_addr,
237 			    sizeof(struct in_addr)) == 0)
238 				return 1;
239 			break;
240 		case AF_INET6:
241 			if (src &&
242 			    memcmp(&((struct sockaddr_in6 *)p->sa)->sin6_addr,
243 			    &((struct sockaddr_in6 *)src)->sin6_addr,
244 			    sizeof(struct in_addr)) == 0)
245 				return 1;
246 			if (dst &&
247 			    memcmp(&((struct sockaddr_in6 *)p->sa)->sin6_addr,
248 			    &((struct sockaddr_in6 *)dst)->sin6_addr,
249 			    sizeof(struct in_addr)) == 0)
250 				return 1;
251 			break;
252 		}
253 	}
254 	return 0;
255 }
256 
257 static int
258 pfkey_handle_message(struct sadb_msg *m)
259 {
260 	struct sadb_msg	*msg = m;
261 
262 	/*
263 	 * Report errors, but ignore for DELETE (both isakmpd and kernel will
264 	 * expire the SA, if the kernel is first, DELETE returns failure).
265 	 */
266 	if (msg->sadb_msg_errno && msg->sadb_msg_type != SADB_DELETE &&
267 	    msg->sadb_msg_pid == (u_int32_t)getpid()) {
268 		errno = msg->sadb_msg_errno;
269 		log_msg(1, "pfkey error (%s)", pfkey_print_type(msg));
270 	}
271 
272 	/* We only want promiscuous messages here, skip all others. */
273 	if (msg->sadb_msg_type != SADB_X_PROMISC ||
274 	    (msg->sadb_msg_len * CHUNK) < 2 * sizeof *msg) {
275 		free(m);
276 		return 0;
277 	}
278 	/* Move next msg to start of the buffer. */
279 	msg++;
280 
281 	/*
282 	 * We should not listen to PFKEY messages when we are not running
283 	 * as MASTER, or the pid is our own.
284 	 */
285 	if (cfgstate.runstate != MASTER ||
286 	    msg->sadb_msg_pid == (u_int32_t)getpid()) {
287 		free(m);
288 		return 0;
289 	}
290 
291 	if (pfkey_msg_filter(msg)) {
292 		free(m);
293 		return 0;
294 	}
295 
296 	switch (msg->sadb_msg_type) {
297 	case SADB_UPDATE:
298 		/*
299 		 * Tweak -- the peers do not have a larval SA to update, so
300 		 * instead we ADD it here.
301 		 */
302 		msg->sadb_msg_type = SADB_ADD;
303 		/* FALLTHROUGH */
304 
305 	default:
306 		/* Pass the rest along to our peers. */
307 		memmove(m, msg, msg->sadb_msg_len * CHUNK); /* for realloc */
308 		return net_queue(NULL, MSG_PFKEYDATA, (u_int8_t *)m,
309 		    m->sadb_msg_len * CHUNK);
310 	}
311 
312 	return 0;
313 }
314 
315 static int
316 pfkey_read(void)
317 {
318 	struct sadb_msg  hdr, *msg;
319 	u_int8_t	*data;
320 	ssize_t		 datalen;
321 	int		 fd = cfgstate.pfkey_socket;
322 
323 	if (recv(fd, &hdr, sizeof hdr, MSG_PEEK) != sizeof hdr) {
324 		log_err("pfkey_read: recv() failed");
325 		return -1;
326 	}
327 	datalen = hdr.sadb_msg_len * CHUNK;
328 	data = (u_int8_t *)malloc(datalen);
329 	if (!data) {
330 		log_err("pfkey_read: malloc(%lu) failed", datalen);
331 		return -1;
332 	}
333 	msg = (struct sadb_msg *)data;
334 
335 	if (read(fd, data, datalen) != datalen) {
336 		log_err("pfkey_read: read() failed, %lu bytes", datalen);
337 		free(data);
338 		return -1;
339 	}
340 
341 	return pfkey_handle_message(msg);
342 }
343 
344 int
345 pfkey_init(int reinit)
346 {
347 	int fd;
348 
349 	fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
350 	if (fd == -1) {
351 		perror("failed to open PF_KEY socket");
352 		return -1;
353 	}
354 	cfgstate.pfkey_socket = fd;
355 
356 	if (cfgstate.runstate == MASTER)
357 		pfkey_set_promisc();
358 
359 	if (reinit)
360 		return (fd > -1 ? 0 : -1);
361 
362 	SIMPLEQ_INIT(&pfkey_msglist);
363 	return 0;
364 }
365 
366 void
367 pfkey_set_rfd(fd_set *fds)
368 {
369 	if (cfgstate.pfkey_socket != -1)
370 		FD_SET(cfgstate.pfkey_socket, fds);
371 }
372 
373 void
374 pfkey_set_pending_wfd(fd_set *fds)
375 {
376 	if (cfgstate.pfkey_socket != -1 && SIMPLEQ_FIRST(&pfkey_msglist))
377 		FD_SET(cfgstate.pfkey_socket, fds);
378 }
379 
380 void
381 pfkey_read_message(fd_set *fds)
382 {
383 	if (cfgstate.pfkey_socket != -1)
384 		if (FD_ISSET(cfgstate.pfkey_socket, fds))
385 			(void)pfkey_read();
386 }
387 
388 void
389 pfkey_send_message(fd_set *fds)
390 {
391 	struct pfkey_msg *pmsg = SIMPLEQ_FIRST(&pfkey_msglist);
392 
393 	if (!pmsg || !FD_ISSET(cfgstate.pfkey_socket, fds))
394 		return;
395 
396 	if (cfgstate.pfkey_socket == -1)
397 		if (pfkey_init(1)) /* Reinit socket */
398 			return;
399 
400 	(void)pfkey_write(pmsg->buf, pmsg->len);
401 
402 	SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next);
403 	free(pmsg->buf);
404 	free(pmsg);
405 
406 	return;
407 }
408 
409 int
410 pfkey_queue_message(u_int8_t *data, u_int32_t datalen)
411 {
412 	struct pfkey_msg	*pmsg;
413 	struct sadb_msg		*sadb = (struct sadb_msg *)data;
414 	static u_int32_t	 seq = 1;
415 
416 	pmsg = (struct pfkey_msg *)malloc(sizeof *pmsg);
417 	if (!pmsg) {
418 		log_err("malloc()");
419 		return -1;
420 	}
421 	memset(pmsg, 0, sizeof *pmsg);
422 
423 	pmsg->buf = data;
424 	pmsg->len = datalen;
425 
426 	sadb->sadb_msg_pid = getpid();
427 	sadb->sadb_msg_seq = seq++;
428 	log_msg(2, "pfkey_queue_message: pfkey %s len %u seq %u",
429 	    pfkey_print_type(sadb), sadb->sadb_msg_len * CHUNK,
430 	    sadb->sadb_msg_seq);
431 
432 	SIMPLEQ_INSERT_TAIL(&pfkey_msglist, pmsg, next);
433 	return 0;
434 }
435 
436 void
437 pfkey_shutdown(void)
438 {
439 	struct pfkey_msg *p = SIMPLEQ_FIRST(&pfkey_msglist);
440 
441 	while ((p = SIMPLEQ_FIRST(&pfkey_msglist))) {
442 		SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next);
443 		free(p->buf);
444 		free(p);
445 	}
446 
447 	if (cfgstate.pfkey_socket > -1)
448 		close(cfgstate.pfkey_socket);
449 }
450 
451 /* ------------------------------------------------------------------------- */
452 
453 void
454 pfkey_snapshot(void *v)
455 {
456 	struct syncpeer		*p = (struct syncpeer *)v;
457 	struct sadb_msg		*m;
458 	u_int8_t		*sadb, *spd, *max, *next, *sendbuf;
459 	u_int32_t		 sadbsz, spdsz;
460 
461 	if (!p)
462 		return;
463 
464 	if (monitor_get_pfkey_snap(&sadb, &sadbsz, &spd, &spdsz)) {
465 		log_msg(0, "pfkey_snapshot: failed to get pfkey snapshot");
466 		return;
467 	}
468 
469 	/* XXX needs moving if snapshot is called more than once per peer */
470 	if ((cfgstate.flags & FM_MASK) == FM_STARTUP)
471 		pfkey_send_flush(p);
472 
473 	/* Parse SADB data */
474 	if (sadbsz && sadb) {
475 		dump_buf(2, sadb, sadbsz, "pfkey_snapshot: SADB data");
476 		max = sadb + sadbsz;
477 		for (next = sadb; next < max;
478 		     next += m->sadb_msg_len * CHUNK) {
479 			m = (struct sadb_msg *)next;
480 			if (m->sadb_msg_len == 0)
481 				break;
482 
483 			/* Tweak and send this SA to the peer. */
484 			m->sadb_msg_type = SADB_ADD;
485 
486 			if (pfkey_msg_filter(m))
487 				continue;
488 
489 			/* Allocate msgbuffer, net_queue() will free it. */
490 			sendbuf = (u_int8_t *)calloc(m->sadb_msg_len, CHUNK);
491 			if (sendbuf) {
492 				memcpy(sendbuf, m, m->sadb_msg_len * CHUNK);
493 				net_queue(p, MSG_PFKEYDATA, sendbuf,
494 				    m->sadb_msg_len * CHUNK);
495 				log_msg(2, "pfkey_snapshot: sync SA %p len %u "
496 				    "to peer %s", m,
497 				    m->sadb_msg_len * CHUNK, p->name);
498 			}
499 		}
500 		memset(sadb, 0, sadbsz);
501 		free(sadb);
502 	}
503 
504 	/* Parse SPD data */
505 	if (spdsz && spd) {
506 		dump_buf(2, spd, spdsz, "pfkey_snapshot: SPD data");
507 		max = spd + spdsz;
508 		for (next = spd; next < max; next += m->sadb_msg_len * CHUNK) {
509 			m = (struct sadb_msg *)next;
510 			if (m->sadb_msg_len == 0)
511 				break;
512 
513 			/* Tweak msg type. */
514 			m->sadb_msg_type = SADB_X_ADDFLOW;
515 
516 			if (pfkey_msg_filter(m))
517 				continue;
518 
519 			/* Allocate msgbuffer, freed by net_queue(). */
520 			sendbuf = (u_int8_t *)calloc(m->sadb_msg_len, CHUNK);
521 			if (sendbuf) {
522 				memcpy(sendbuf, m, m->sadb_msg_len * CHUNK);
523 				net_queue(p, MSG_PFKEYDATA, sendbuf,
524 				    m->sadb_msg_len * CHUNK);
525 				log_msg(2, "pfkey_snapshot: sync FLOW %p len "
526 				    "%u to peer %s", m,
527 				    m->sadb_msg_len * CHUNK, p->name);
528 			}
529 		}
530 		/* Cleanup. */
531 		memset(spd, 0, spdsz);
532 		free(spd);
533 	}
534 
535 	net_ctl_send_endsnap(p);
536 	return;
537 }
538