GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/sasyncd/pfkey.c Lines: 0 222 0.0 %
Date: 2017-11-07 Branches: 0 161 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: pfkey.c,v 1.28 2017/04/18 02:29:56 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
/*
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
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 = 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 = reallocarray(NULL, hdr.sadb_msg_len, CHUNK);
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 = 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 %zu 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 = 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 %zu "
496
				    "to peer %s", m,
497
				    m->sadb_msg_len * CHUNK, p->name);
498
			}
499
		}
500
		freezero(sadb, sadbsz);
501
	}
502
503
	/* Parse SPD data */
504
	if (spdsz && spd) {
505
		dump_buf(2, spd, spdsz, "pfkey_snapshot: SPD data");
506
		max = spd + spdsz;
507
		for (next = spd; next < max; next += m->sadb_msg_len * CHUNK) {
508
			m = (struct sadb_msg *)next;
509
			if (m->sadb_msg_len == 0)
510
				break;
511
512
			/* Tweak msg type. */
513
			m->sadb_msg_type = SADB_X_ADDFLOW;
514
515
			if (pfkey_msg_filter(m))
516
				continue;
517
518
			/* Allocate msgbuffer, freed by net_queue(). */
519
			sendbuf = calloc(m->sadb_msg_len, CHUNK);
520
			if (sendbuf) {
521
				memcpy(sendbuf, m, m->sadb_msg_len * CHUNK);
522
				net_queue(p, MSG_PFKEYDATA, sendbuf,
523
				    m->sadb_msg_len * CHUNK);
524
				log_msg(2, "pfkey_snapshot: sync FLOW %p len "
525
				    "%zu to peer %s", m,
526
				    m->sadb_msg_len * CHUNK, p->name);
527
			}
528
		}
529
		/* Cleanup. */
530
		freezero(spd, spdsz);
531
	}
532
533
	net_ctl_send_endsnap(p);
534
	return;
535
}