xref: /openbsd/sbin/isakmpd/dpd.c (revision 6ee513e5)
1 /*	$OpenBSD: dpd.c,v 1.20 2017/12/05 20:31:45 jca 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
dpd_add_vendor_payload(struct message * msg)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
dpd_check_vendor_payload(struct message * msg,struct payload * p)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
dpd_start(struct sa * isakmp_sa)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
dpd_handle_notify(struct message * msg,struct payload * p)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
dpd_timer_interval(u_int32_t offset)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
dpd_timer_reset(struct sa * sa,u_int32_t time_passed,enum dpd_tstate mode)217 dpd_timer_reset(struct sa *sa, u_int32_t time_passed, enum dpd_tstate mode)
218 {
219 	struct timespec	ts;
220 
221 	if (sa->dpd_event)
222 		timer_remove_event(sa->dpd_event);
223 
224 	clock_gettime(CLOCK_MONOTONIC, &ts);
225 	switch (mode) {
226 	case DPD_TIMER_NORMAL:
227 		sa->dpd_failcount = 0;
228 		ts.tv_sec += dpd_timer_interval(time_passed);
229 		sa->dpd_event = timer_add_event("dpd_event", dpd_event, sa,
230 		    &ts);
231 		break;
232 	case DPD_TIMER_CHECK:
233 		ts.tv_sec += DPD_RETRANS_WAIT;
234 		sa->dpd_event = timer_add_event("dpd_check_event",
235 		    dpd_check_event, sa, &ts);
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
dpd_find_sa(struct sa * sa,void * v_sa)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
dpd_check_time(struct sa * sa,void * v_arg)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 timespec ts;
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 	clock_gettime(CLOCK_MONOTONIC, &ts);
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)(ts.tv_sec - ksa->last_used)));
291 
292 	if ((u_int32_t)(ts.tv_sec - ksa->last_used) < args->interval) {
293 		args->interval = (u_int32_t)(ts.tv_sec - ksa->last_used);
294 		return 1;
295 	}
296 	return 0;
297 }
298 
299 /* Called by the timer.  */
300 static void
dpd_event(void * v_sa)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
dpd_check_event(void * v_sa)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 }
381