GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/isakmpd/dpd.c Lines: 0 136 0.0 %
Date: 2017-11-07 Branches: 0 78 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dpd.c,v 1.19 2015/12/10 17:27:00 mmcc Exp $	*/
2
3
/*
4
 * Copyright (c) 2004 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
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 */
26
27
#include <sys/types.h>
28
#include <stdlib.h>
29
#include <string.h>
30
31
#include "conf.h"
32
#include "dpd.h"
33
#include "exchange.h"
34
#include "hash.h"
35
#include "ipsec.h"
36
#include "isakmp_fld.h"
37
#include "log.h"
38
#include "message.h"
39
#include "pf_key_v2.h"
40
#include "sa.h"
41
#include "timer.h"
42
#include "transport.h"
43
#include "util.h"
44
45
/* From RFC 3706.  */
46
#define DPD_MAJOR		0x01
47
#define DPD_MINOR		0x00
48
#define DPD_SEQNO_SZ		4
49
50
static const u_int8_t dpd_vendor_id[] = {
51
	0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1,	/* RFC 3706 */
52
	0xC9, 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57,
53
	DPD_MAJOR,
54
	DPD_MINOR
55
};
56
57
#define DPD_RETRANS_MAX		5	/* max number of retries.  */
58
#define DPD_RETRANS_WAIT	5	/* seconds between retries.  */
59
60
/* DPD Timer State */
61
enum dpd_tstate { DPD_TIMER_NORMAL, DPD_TIMER_CHECK };
62
63
static void	 dpd_check_event(void *);
64
static void	 dpd_event(void *);
65
static u_int32_t dpd_timer_interval(u_int32_t);
66
static void	 dpd_timer_reset(struct sa *, u_int32_t, enum dpd_tstate);
67
68
/* Add the DPD VENDOR ID payload.  */
69
int
70
dpd_add_vendor_payload(struct message *msg)
71
{
72
	u_int8_t *buf;
73
	size_t buflen = sizeof dpd_vendor_id + ISAKMP_GEN_SZ;
74
75
	buf = malloc(buflen);
76
	if (!buf) {
77
		log_error("dpd_add_vendor_payload: malloc(%lu) failed",
78
		    (unsigned long)buflen);
79
		return -1;
80
	}
81
82
	SET_ISAKMP_GEN_LENGTH(buf, buflen);
83
	memcpy(buf + ISAKMP_VENDOR_ID_OFF, dpd_vendor_id,
84
	    sizeof dpd_vendor_id);
85
	if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) {
86
		free(buf);
87
		return -1;
88
	}
89
90
	return 0;
91
}
92
93
/*
94
 * Check an incoming message for DPD capability markers.
95
 */
96
void
97
dpd_check_vendor_payload(struct message *msg, struct payload *p)
98
{
99
	u_int8_t *pbuf = p->p;
100
	size_t vlen;
101
102
	/* Already checked? */
103
	if (msg->exchange->flags & EXCHANGE_FLAG_DPD_CAP_PEER) {
104
		/* Just mark it as handled and return.  */
105
		p->flags |= PL_MARK;
106
		return;
107
	}
108
109
	vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ;
110
	if (vlen != sizeof dpd_vendor_id) {
111
		LOG_DBG((LOG_EXCHANGE, 90,
112
		    "dpd_check_vendor_payload: bad size %lu != %lu",
113
		    (unsigned long)vlen, (unsigned long)sizeof dpd_vendor_id));
114
		return;
115
	}
116
117
	if (memcmp(dpd_vendor_id, pbuf + ISAKMP_GEN_SZ, vlen) == 0) {
118
		/* This peer is DPD capable.  */
119
		if (msg->isakmp_sa) {
120
			msg->exchange->flags |= EXCHANGE_FLAG_DPD_CAP_PEER;
121
			LOG_DBG((LOG_EXCHANGE, 10, "dpd_check_vendor_payload: "
122
			    "DPD capable peer detected"));
123
		}
124
		p->flags |= PL_MARK;
125
	}
126
}
127
128
/*
129
 * Arm the DPD timer
130
 */
131
void
132
dpd_start(struct sa *isakmp_sa)
133
{
134
	if (dpd_timer_interval(0) != 0) {
135
		LOG_DBG((LOG_EXCHANGE, 10, "dpd_enable: enabling"));
136
		isakmp_sa->flags |= SA_FLAG_DPD;
137
		dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_NORMAL);
138
	}
139
}
140
141
/*
142
 * All incoming DPD Notify messages enter here. Message has been validated.
143
 */
144
void
145
dpd_handle_notify(struct message *msg, struct payload *p)
146
{
147
	struct sa	*isakmp_sa = msg->isakmp_sa;
148
	u_int16_t	 notify = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p);
149
	u_int32_t	 p_seq;
150
151
	/* Extract the sequence number.  */
152
	memcpy(&p_seq, p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN,
153
	    sizeof p_seq);
154
	p_seq = ntohl(p_seq);
155
156
	LOG_DBG((LOG_MESSAGE, 40, "dpd_handle_notify: got %s seq %u",
157
	    constant_name(isakmp_notify_cst, notify), p_seq));
158
159
	switch (notify) {
160
	case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE:
161
		/* The other peer wants to know we're alive.  */
162
		if (p_seq < isakmp_sa->dpd_rseq ||
163
		    (p_seq == isakmp_sa->dpd_rseq &&
164
		    ++isakmp_sa->dpd_rdupcount >= DPD_RETRANS_MAX)) {
165
			log_print("dpd_handle_notify: bad R_U_THERE seqno "
166
			    "%u <= %u", p_seq, isakmp_sa->dpd_rseq);
167
			return;
168
		}
169
		if (isakmp_sa->dpd_rseq != p_seq) {
170
			isakmp_sa->dpd_rdupcount = 0;
171
			isakmp_sa->dpd_rseq = p_seq;
172
		}
173
		message_send_dpd_notify(isakmp_sa,
174
		    ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK, p_seq);
175
		break;
176
177
	case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK:
178
		/* This should be a response to a R_U_THERE we've sent.  */
179
		if (isakmp_sa->dpd_seq != p_seq) {
180
			log_print("dpd_handle_notify: got bad ACK seqno %u, "
181
			    "expected %u", p_seq, isakmp_sa->dpd_seq);
182
			/* XXX Give up? Retry? */
183
			return;
184
		}
185
		break;
186
	default:
187
		break;
188
	}
189
190
	/* Mark handled.  */
191
	p->flags |= PL_MARK;
192
193
	/* The other peer is alive, so we can safely wait a while longer.  */
194
	if (isakmp_sa->flags & SA_FLAG_DPD)
195
		dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_NORMAL);
196
}
197
198
/* Calculate the time until next DPD exchange.  */
199
static u_int32_t
200
dpd_timer_interval(u_int32_t offset)
201
{
202
	int32_t v = 0;
203
204
#ifdef notyet
205
	v = ...; /* XXX Per-peer specified DPD intervals?  */
206
#endif
207
	if (!v)
208
		v = conf_get_num("General", "DPD-check-interval", 0);
209
	if (v < 1)
210
		return 0;	/* DPD-Check-Interval < 1 means disable DPD */
211
212
	v -= offset;
213
	return v < 1 ? 1 : v;
214
}
215
216
static void
217
dpd_timer_reset(struct sa *sa, u_int32_t time_passed, enum dpd_tstate mode)
218
{
219
	struct timeval	tv;
220
221
	if (sa->dpd_event)
222
		timer_remove_event(sa->dpd_event);
223
224
	gettimeofday(&tv, 0);
225
	switch (mode) {
226
	case DPD_TIMER_NORMAL:
227
		sa->dpd_failcount = 0;
228
		tv.tv_sec += dpd_timer_interval(time_passed);
229
		sa->dpd_event = timer_add_event("dpd_event", dpd_event, sa,
230
		    &tv);
231
		break;
232
	case DPD_TIMER_CHECK:
233
		tv.tv_sec += DPD_RETRANS_WAIT;
234
		sa->dpd_event = timer_add_event("dpd_check_event",
235
		    dpd_check_event, sa, &tv);
236
		break;
237
	default:
238
		break;
239
	}
240
	if (!sa->dpd_event)
241
		log_print("dpd_timer_reset: timer_add_event failed");
242
}
243
244
/* Helper function for dpd_exchange_finalization().  */
245
static int
246
dpd_find_sa(struct sa *sa, void *v_sa)
247
{
248
	struct sa	*isakmp_sa = v_sa;
249
250
	if (!isakmp_sa->id_i || !isakmp_sa->id_r)
251
		return 0;
252
	return (sa->phase == 2 && (sa->flags & SA_FLAG_READY) &&
253
	    memcmp(sa->id_i, isakmp_sa->id_i, sa->id_i_len) == 0 &&
254
	    memcmp(sa->id_r, isakmp_sa->id_r, sa->id_r_len) == 0);
255
}
256
257
struct dpd_args {
258
	struct sa	*isakmp_sa;
259
	u_int32_t	 interval;
260
};
261
262
/* Helper function for dpd_event().  */
263
static int
264
dpd_check_time(struct sa *sa, void *v_arg)
265
{
266
	struct dpd_args *args = v_arg;
267
	struct sockaddr *dst;
268
	struct proto *proto;
269
	struct sa_kinfo *ksa;
270
	struct timeval tv;
271
272
	if (sa->phase == 1 || (args->isakmp_sa->flags & SA_FLAG_DPD) == 0 ||
273
	    dpd_find_sa(sa, args->isakmp_sa) == 0)
274
		return 0;
275
276
	proto = TAILQ_FIRST(&sa->protos);
277
	if (!proto || !proto->data)
278
		return 0;
279
	sa->transport->vtbl->get_src(sa->transport, &dst);
280
281
	gettimeofday(&tv, 0);
282
	ksa = pf_key_v2_get_kernel_sa(proto->spi[1], proto->spi_sz[1],
283
	    proto->proto, dst);
284
285
	if (!ksa || !ksa->last_used)
286
		return 0;
287
288
	LOG_DBG((LOG_MESSAGE, 80, "dpd_check_time: "
289
	    "SA %p last use %u second(s) ago", sa,
290
	    (u_int32_t)(tv.tv_sec - ksa->last_used)));
291
292
	if ((u_int32_t)(tv.tv_sec - ksa->last_used) < args->interval) {
293
		args->interval = (u_int32_t)(tv.tv_sec - ksa->last_used);
294
		return 1;
295
	}
296
	return 0;
297
}
298
299
/* Called by the timer.  */
300
static void
301
dpd_event(void *v_sa)
302
{
303
	struct sa	*isakmp_sa = v_sa;
304
	struct dpd_args args;
305
	struct sockaddr *dst;
306
	char *addr;
307
308
	isakmp_sa->dpd_event = 0;
309
310
	/* Check if there's been any incoming SA activity since last time.  */
311
	args.isakmp_sa = isakmp_sa;
312
	args.interval = dpd_timer_interval(0);
313
	if (sa_find(dpd_check_time, &args)) {
314
		if (args.interval > dpd_timer_interval(0))
315
			args.interval = 0;
316
		dpd_timer_reset(isakmp_sa, args.interval, DPD_TIMER_NORMAL);
317
		return;
318
	}
319
320
	/* No activity seen, do a DPD exchange.  */
321
	if (isakmp_sa->dpd_seq == 0) {
322
		/*
323
		 * RFC 3706: first seq# should be random, with MSB zero,
324
		 * otherwise we just increment it.
325
		 */
326
		arc4random_buf((u_int8_t *)&isakmp_sa->dpd_seq,
327
		    sizeof isakmp_sa->dpd_seq);
328
		isakmp_sa->dpd_seq &= 0x7FFF;
329
	} else
330
		isakmp_sa->dpd_seq++;
331
332
	isakmp_sa->transport->vtbl->get_dst(isakmp_sa->transport, &dst);
333
	if (sockaddr2text(dst, &addr, 0) == -1)
334
		addr = 0;
335
	LOG_DBG((LOG_MESSAGE, 30, "dpd_event: sending R_U_THERE to %s seq %u",
336
	    addr ? addr : "<unknown>", isakmp_sa->dpd_seq));
337
	free(addr);
338
	message_send_dpd_notify(isakmp_sa, ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE,
339
	    isakmp_sa->dpd_seq);
340
341
	/* And set the short timer.  */
342
	dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK);
343
}
344
345
/*
346
 * Called by the timer. If this function is called, it means we did not
347
 * received any R_U_THERE_ACK confirmation from the other peer.
348
 */
349
static void
350
dpd_check_event(void *v_sa)
351
{
352
	struct sa	*isakmp_sa = v_sa;
353
	struct sa	*sa;
354
355
	isakmp_sa->dpd_event = 0;
356
357
	if (++isakmp_sa->dpd_failcount < DPD_RETRANS_MAX) {
358
		LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: "
359
		    "peer not responding, retry %u of %u",
360
		    isakmp_sa->dpd_failcount, DPD_RETRANS_MAX));
361
		message_send_dpd_notify(isakmp_sa,
362
		    ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, isakmp_sa->dpd_seq);
363
		dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK);
364
		return;
365
	}
366
367
	/*
368
	 * Peer is considered dead. Delete all SAs created under isakmp_sa.
369
	 */
370
	LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: peer is dead, "
371
	    "deleting all SAs connected to SA %p", isakmp_sa));
372
	while ((sa = sa_find(dpd_find_sa, isakmp_sa)) != 0) {
373
		LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting SA %p",
374
		    sa));
375
		sa_delete(sa, 0);
376
	}
377
	LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting ISAKMP SA %p",
378
	    isakmp_sa));
379
	sa_delete(isakmp_sa, 0);
380
}