xref: /openbsd/sbin/isakmpd/nat_traversal.c (revision 35dfcbf4)
1 /*	$OpenBSD: nat_traversal.c,v 1.8 2004/11/18 18:15:46 hshoexer 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 "exchange.h"
35 #include "hash.h"
36 #include "ipsec.h"
37 #include "isakmp_fld.h"
38 #include "isakmp_num.h"
39 #include "ipsec_num.h"
40 #include "hash.h"
41 #include "log.h"
42 #include "message.h"
43 #include "nat_traversal.h"
44 #include "prf.h"
45 #include "sa.h"
46 #include "timer.h"
47 #include "transport.h"
48 #include "util.h"
49 #include "virtual.h"
50 
51 /*
52  * XXX According to draft-ietf-ipsec-nat-t-ike-07.txt, the NAT-T
53  * capability of the other peer is determined by a particular vendor ID
54  * sent as the first message. This vendor ID string is supposed to be a
55  * MD5 hash of "RFC XXXX", where XXXX is the future RFC number.
56  *
57  * These seem to be the "well" known variants of this string in use by
58  * products today.
59  */
60 static const char *isakmp_nat_t_cap_text[] = {
61 	"draft-ietf-ipsec-nat-t-ike-02\n",	/* V2 */
62 	"draft-ietf-ipsec-nat-t-ike-03",	/* V3 */
63 #ifdef notyet
64 	"RFC XXXX",
65 #endif
66 };
67 
68 /* In seconds. Recommended in draft-ietf-ipsec-udp-encaps-09.  */
69 #define NAT_T_KEEPALIVE_INTERVAL	20
70 
71 /* The MD5 hashes of the above strings is put in this array.  */
72 static char	**nat_t_hashes;
73 static size_t	  nat_t_hashsize;
74 
75 static int	nat_t_setup_hashes(void);
76 static int	nat_t_add_vendor_payload(struct message *, char *);
77 static int	nat_t_add_nat_d(struct message *, struct sockaddr *);
78 static int	nat_t_match_nat_d_payload(struct message *, struct sockaddr *);
79 
80 void
81 nat_t_init(void)
82 {
83 	nat_t_hashes = (char **)NULL;
84 }
85 
86 /* Generate the NAT-T capability marker hashes. Executed only once.  */
87 static int
88 nat_t_setup_hashes(void)
89 {
90 	struct hash *hash;
91 	int n = sizeof isakmp_nat_t_cap_text / sizeof isakmp_nat_t_cap_text[0];
92 	int i;
93 
94 	/* The draft says to use MD5.  */
95 	hash = hash_get(HASH_MD5);
96 	if (!hash) {
97 		/* Should never happen.  */
98 		log_print("nat_t_setup_hashes: "
99 		    "could not find MD5 hash structure!");
100 		return -1;
101 	}
102 	nat_t_hashsize = hash->hashsize;
103 
104 	/* Allocate one more than is necessary, i.e NULL terminated.  */
105 	nat_t_hashes = (char **)calloc((size_t)(n + 1), sizeof(char *));
106 	if (!nat_t_hashes) {
107 		log_error("nat_t_setup_hashes: calloc (%lu,%lu) failed",
108 		    (unsigned long)n, (unsigned long)sizeof(char *));
109 		return -1;
110 	}
111 
112 	/* Populate with hashes.  */
113 	for (i = 0; i < n; i++) {
114 		nat_t_hashes[i] = (char *)malloc(nat_t_hashsize);
115 		if (!nat_t_hashes[i]) {
116 			log_error("nat_t_setup_hashes: malloc (%lu) failed",
117 			    (unsigned long)nat_t_hashsize);
118 			goto errout;
119 		}
120 
121 		hash->Init(hash->ctx);
122 		hash->Update(hash->ctx,
123 		    (unsigned char *)isakmp_nat_t_cap_text[i],
124 		    strlen(isakmp_nat_t_cap_text[i]));
125 		hash->Final(nat_t_hashes[i], hash->ctx);
126 
127 		LOG_DBG((LOG_EXCHANGE, 50, "nat_t_setup_hashes: "
128 		    "MD5(\"%s\") (%lu bytes)", isakmp_nat_t_cap_text[i],
129 		    (unsigned long)nat_t_hashsize));
130 		LOG_DBG_BUF((LOG_EXCHANGE, 50, "nat_t_setup_hashes",
131 		    nat_t_hashes[i], nat_t_hashsize));
132 	}
133 
134 	return 0;
135 
136   errout:
137 	for (i = 0; i < n; i++)
138 		if (nat_t_hashes[i])
139 			free(nat_t_hashes[i]);
140 	free(nat_t_hashes);
141 	nat_t_hashes = NULL;
142 	return -1;
143 }
144 
145 /* Add one NAT-T VENDOR payload.  */
146 static int
147 nat_t_add_vendor_payload(struct message *msg, char *hash)
148 {
149 	size_t	 buflen = nat_t_hashsize + ISAKMP_GEN_SZ;
150 	u_int8_t *buf;
151 
152 	buf = malloc(buflen);
153 	if (!buf) {
154 		log_error("nat_t_add_vendor_payload: malloc (%lu) failed",
155 		    (unsigned long)buflen);
156 		return -1;
157 	}
158 
159 	SET_ISAKMP_GEN_LENGTH(buf, buflen);
160 	memcpy(buf + ISAKMP_VENDOR_ID_OFF, hash, nat_t_hashsize);
161 	if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) {
162 		free(buf);
163 		return -1;
164 	}
165 
166 	return 0;
167 }
168 
169 /* Add the NAT-T capability markers (VENDOR payloads).  */
170 int
171 nat_t_add_vendor_payloads(struct message *msg)
172 {
173 	int i = 0;
174 
175 	if (!nat_t_hashes)
176 		if (nat_t_setup_hashes())
177 			return 0;  /* XXX should this be an error?  */
178 
179 	while (nat_t_hashes[i])
180 		if (nat_t_add_vendor_payload(msg, nat_t_hashes[i++]))
181 			return -1;
182 
183 	return 0;
184 }
185 
186 /*
187  * Check an incoming message for NAT-T capability markers.
188  */
189 void
190 nat_t_check_vendor_payload(struct message *msg, struct payload *p)
191 {
192 	u_int8_t *pbuf = p->p;
193 	size_t	  vlen;
194 	int	  i = 0;
195 
196 	/* Already checked? */
197 	if (p->flags & PL_MARK ||
198 	    msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER)
199 		return;
200 
201 	if (!nat_t_hashes)
202 		if (nat_t_setup_hashes())
203 			return;
204 
205 	vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ;
206 	if (vlen != nat_t_hashsize) {
207 		LOG_DBG((LOG_EXCHANGE, 50, "nat_t_check_vendor_payload: "
208 		    "bad size %lu != %lu", (unsigned long)vlen,
209 		    (unsigned long)nat_t_hashsize));
210 		return;
211 	}
212 
213 	while (nat_t_hashes[i])
214 		if (memcmp(nat_t_hashes[i++], pbuf + ISAKMP_GEN_SZ,
215 		    vlen) == 0) {
216 			/* This peer is NAT-T capable.  */
217 			msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_CAP_PEER;
218 			LOG_DBG((LOG_EXCHANGE, 10,
219 			    "nat_t_check_vendor_payload: "
220 			    "NAT-T capable peer detected"));
221 			p->flags |= PL_MARK;
222 			return;
223 		}
224 
225 	return;
226 }
227 
228 /* Generate the NAT-D payload hash : HASH(CKY-I | CKY-R | IP | Port).  */
229 static u_int8_t *
230 nat_t_generate_nat_d_hash(struct message *msg, struct sockaddr *sa,
231     size_t *hashlen)
232 {
233 	struct ipsec_exch *ie = (struct ipsec_exch *)msg->exchange->data;
234 	struct hash	 *hash;
235 	u_int8_t	 *res;
236 	in_port_t	  port;
237 
238 	hash = hash_get(ie->hash->type);
239 	if (hash == NULL) {
240 		log_print ("nat_t_generate_nat_d_hash: no hash");
241 		return NULL;
242 	}
243 
244 	*hashlen = hash->hashsize;
245 
246 	res = (u_int8_t *)malloc((unsigned long)*hashlen);
247 	if (!res) {
248 		log_print("nat_t_generate_nat_d_hash: malloc (%lu) failed",
249 		    (unsigned long)*hashlen);
250 		*hashlen = 0;
251 		return NULL;
252 	}
253 
254 	port = sockaddr_port(sa);
255 	memset(res, 0, *hashlen);
256 
257 	hash->Init(hash->ctx);
258 	hash->Update(hash->ctx, msg->exchange->cookies,
259 	    sizeof msg->exchange->cookies);
260 	hash->Update(hash->ctx, sockaddr_addrdata(sa), sockaddr_addrlen(sa));
261 	hash->Update(hash->ctx, (unsigned char *)&port, sizeof port);
262 	hash->Final(res, hash->ctx);
263 
264 	return res;
265 }
266 
267 /* Add a NAT-D payload to our message.  */
268 static int
269 nat_t_add_nat_d(struct message *msg, struct sockaddr *sa)
270 {
271 	u_int8_t *hbuf, *buf;
272 	size_t	  hbuflen, buflen;
273 
274 	hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen);
275 	if (!hbuf) {
276 		log_print("nat_t_add_nat_d: NAT-D hash gen failed");
277 		return -1;
278 	}
279 
280 	buflen = ISAKMP_NAT_D_DATA_OFF + hbuflen;
281 	buf = malloc(buflen);
282 	if (!buf) {
283 		log_error("nat_t_add_nat_d: malloc (%lu) failed",
284 		    (unsigned long)buflen);
285 		free(hbuf);
286 		return -1;
287 	}
288 
289 	SET_ISAKMP_GEN_LENGTH(buf, buflen);
290 	memcpy(buf + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen);
291 	free(hbuf);
292 
293 	if (message_add_payload(msg, ISAKMP_PAYLOAD_NAT_D, buf, buflen, 1)) {
294 		free(buf);
295 		return -1;
296 	}
297 
298 	return 0;
299 }
300 
301 /* We add two NAT-D payloads, one each for src and dst.  */
302 int
303 nat_t_exchange_add_nat_d(struct message *msg)
304 {
305 	struct sockaddr *sa;
306 
307 	/* Remote address first. */
308 	msg->transport->vtbl->get_dst(msg->transport, &sa);
309 	if (nat_t_add_nat_d(msg, sa))
310 		return -1;
311 
312 	msg->transport->vtbl->get_src(msg->transport, &sa);
313 	if (nat_t_add_nat_d(msg, sa))
314 		return -1;
315 
316 	return 0;
317 }
318 
319 /* Generate and match a NAT-D hash against the NAT-D payload (pl.) data.  */
320 static int
321 nat_t_match_nat_d_payload(struct message *msg, struct sockaddr *sa)
322 {
323 	struct payload *p;
324 	u_int8_t *hbuf;
325 	size_t	 hbuflen;
326 	int	 found = 0;
327 
328 	/*
329 	 * If there are no NAT-D payloads in the message, return "found"
330 	 * as this will avoid NAT-T (see nat_t_exchange_check_nat_d()).
331 	 */
332 	p = payload_first(msg, ISAKMP_PAYLOAD_NAT_D);
333 	if (!p)
334 		return 1;
335 
336 	hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen);
337 	if (!hbuf)
338 		return 0;
339 
340 	while (p) {
341 		if (GET_ISAKMP_GEN_LENGTH (p->p) !=
342 		    hbuflen + ISAKMP_NAT_D_DATA_OFF)
343 			continue;
344 
345 		if (memcmp(p->p + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen) == 0) {
346 			found++;
347 			break;
348 		}
349 		p = TAILQ_NEXT(p, link);
350 	}
351 	free(hbuf);
352 	return found;
353 }
354 
355 /*
356  * Check if we need to activate NAT-T, and if we need to send keepalive
357  * messages to the other side, i.e if we are a nat:ed peer.
358  */
359 int
360 nat_t_exchange_check_nat_d(struct message *msg)
361 {
362 	struct sockaddr *sa;
363 	int	 outgoing_path_is_clear, incoming_path_is_clear;
364 
365 	/* Assume trouble, i.e NAT-boxes in our path.  */
366 	outgoing_path_is_clear = incoming_path_is_clear = 0;
367 
368 	msg->transport->vtbl->get_src(msg->transport, &sa);
369 	if (nat_t_match_nat_d_payload(msg, sa))
370 		outgoing_path_is_clear = 1;
371 
372 	msg->transport->vtbl->get_dst(msg->transport, &sa);
373 	if (nat_t_match_nat_d_payload(msg, sa))
374 		incoming_path_is_clear = 1;
375 
376 	if (outgoing_path_is_clear && incoming_path_is_clear) {
377 		LOG_DBG((LOG_EXCHANGE, 40, "nat_t_exchange_check_nat_d: "
378 		    "no NAT"));
379 		return 0; /* No NAT-T required.  */
380 	}
381 
382 	/* NAT-T handling required.  */
383 	msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_ENABLE;
384 
385 	if (!outgoing_path_is_clear) {
386 		msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_KEEPALIVE;
387 		LOG_DBG((LOG_EXCHANGE, 10, "nat_t_exchange_check_nat_d: "
388 		    "NAT detected, we're behind it"));
389 	} else
390 		LOG_DBG ((LOG_EXCHANGE, 10,
391 		    "nat_t_exchange_check_nat_d: NAT detected"));
392 	return 1;
393 }
394 
395 static void
396 nat_t_send_keepalive(void *v_arg)
397 {
398 	struct sa *sa = (struct sa *)v_arg;
399 	struct transport *t;
400 	struct timeval now;
401 	int interval;
402 
403 	/* Send the keepalive message.  */
404 	t = ((struct virtual_transport *)sa->transport)->encap;
405 	t->vtbl->send_message(NULL, t);
406 
407 	/* Set new timer.  */
408 	interval = conf_get_num("General", "NAT-T-Keepalive", 0);
409 	if (interval < 1)
410 		interval = NAT_T_KEEPALIVE_INTERVAL;
411 	gettimeofday(&now, 0);
412 	now.tv_sec += interval;
413 
414 	sa->nat_t_keepalive = timer_add_event("nat_t_send_keepalive",
415 	    nat_t_send_keepalive, v_arg, &now);
416 	if (!sa->nat_t_keepalive)
417 		log_print("nat_t_send_keepalive: "
418 		    "timer_add_event() failed, will send no more keepalives");
419 }
420 
421 void
422 nat_t_setup_keepalive(struct sa *sa)
423 {
424 	struct sockaddr *src;
425 	struct timeval now;
426 
427 	if (sa->initiator)
428 		sa->transport->vtbl->get_src(sa->transport, &src);
429 	else
430 		sa->transport->vtbl->get_dst(sa->transport, &src);
431 
432 	if (!virtual_listen_lookup(src))
433 		return;
434 
435 	gettimeofday(&now, 0);
436 	now.tv_sec += NAT_T_KEEPALIVE_INTERVAL;
437 
438 	sa->nat_t_keepalive = timer_add_event("nat_t_send_keepalive",
439 	    nat_t_send_keepalive, sa, &now);
440 	if (!sa->nat_t_keepalive)
441 		log_print("nat_t_setup_keepalive: "
442 		    "timer_add_event() failed, will not send keepalives");
443 
444 	LOG_DBG((LOG_TRANSPORT, 50, "nat_t_setup_keepalive: "
445 	    "added event for phase 1 SA %p", sa));
446 }
447