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