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