]>
Commit | Line | Data |
---|---|---|
1cc4ec46 TB |
1 | /* |
2 | * Copyright (C) 2006-2011 Tobias Brunner, | |
3 | * Copyright (C) 2006-2007 Martin Willi | |
4 | * Copyright (C) 2006 Daniel Roethlisberger | |
5 | * Hochschule fuer Technik Rapperswil | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
16 | */ | |
17 | ||
0ff8d20a VR |
18 | /* |
19 | * Copyright (C) 2012 Volker Rümelin | |
20 | * | |
21 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
22 | * of this software and associated documentation files (the "Software"), to deal | |
23 | * in the Software without restriction, including without limitation the rights | |
24 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
25 | * copies of the Software, and to permit persons to whom the Software is | |
26 | * furnished to do so, subject to the following conditions: | |
27 | * | |
28 | * The above copyright notice and this permission notice shall be included in | |
29 | * all copies or substantial portions of the Software. | |
30 | * | |
31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
32 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
33 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
34 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
35 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
36 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
37 | * THE SOFTWARE. | |
38 | */ | |
39 | ||
79d6fc7f | 40 | #include "isakmp_natd.h" |
1cc4ec46 TB |
41 | |
42 | #include <string.h> | |
43 | ||
44 | #include <hydra.h> | |
45 | #include <daemon.h> | |
15a682f4 | 46 | #include <sa/ikev1/keymat_v1.h> |
1cc4ec46 TB |
47 | #include <config/peer_cfg.h> |
48 | #include <crypto/hashers/hasher.h> | |
49 | #include <encoding/payloads/hash_payload.h> | |
50 | ||
79d6fc7f | 51 | typedef struct private_isakmp_natd_t private_isakmp_natd_t; |
1cc4ec46 TB |
52 | |
53 | /** | |
54 | * Private members of a ike_natt_t task. | |
55 | */ | |
79d6fc7f | 56 | struct private_isakmp_natd_t { |
1cc4ec46 TB |
57 | |
58 | /** | |
59 | * Public interface. | |
60 | */ | |
79d6fc7f | 61 | isakmp_natd_t public; |
1cc4ec46 TB |
62 | |
63 | /** | |
64 | * Assigned IKE_SA. | |
65 | */ | |
66 | ike_sa_t *ike_sa; | |
67 | ||
68 | /** | |
69 | * Are we the initiator? | |
70 | */ | |
71 | bool initiator; | |
72 | ||
73 | /** | |
74 | * Keymat derivation (from SA) | |
75 | */ | |
76 | keymat_v1_t *keymat; | |
77 | ||
78 | /** | |
79 | * Did we process any NAT detection payloads for a source address? | |
80 | */ | |
81 | bool src_seen; | |
82 | ||
83 | /** | |
84 | * Did we process any NAT detection payloads for a destination address? | |
85 | */ | |
86 | bool dst_seen; | |
87 | ||
88 | /** | |
89 | * Have we found a matching source address NAT hash? | |
90 | */ | |
91 | bool src_matched; | |
92 | ||
93 | /** | |
94 | * Have we found a matching destination address NAT hash? | |
95 | */ | |
96 | bool dst_matched; | |
97 | }; | |
98 | ||
0ff8d20a VR |
99 | /** |
100 | * Get NAT-D payload type (RFC 3947 or RFC 3947 drafts). | |
101 | */ | |
102 | static payload_type_t get_nat_d_payload_type(ike_sa_t *ike_sa) | |
103 | { | |
104 | if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) | |
105 | { | |
106 | return NAT_D_DRAFT_00_03_V1; | |
107 | } | |
108 | return NAT_D_V1; | |
109 | } | |
110 | ||
1cc4ec46 TB |
111 | /** |
112 | * Build NAT detection hash for a host. | |
113 | */ | |
79d6fc7f | 114 | static chunk_t generate_natd_hash(private_isakmp_natd_t *this, |
1cc4ec46 TB |
115 | ike_sa_id_t *ike_sa_id, host_t *host) |
116 | { | |
117 | hasher_t *hasher; | |
118 | chunk_t natd_chunk, natd_hash; | |
119 | u_int64_t spi_i, spi_r; | |
120 | u_int16_t port; | |
121 | ||
122 | hasher = this->keymat->get_hasher(this->keymat); | |
123 | if (!hasher) | |
124 | { | |
125 | DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); | |
126 | return chunk_empty; | |
127 | } | |
128 | ||
129 | spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); | |
130 | spi_r = ike_sa_id->get_responder_spi(ike_sa_id); | |
131 | port = htons(host->get_port(host)); | |
132 | ||
133 | /* natd_hash = HASH(CKY-I | CKY-R | IP | Port) */ | |
134 | natd_chunk = chunk_cata("cccc", chunk_from_thing(spi_i), | |
135 | chunk_from_thing(spi_r), host->get_address(host), | |
136 | chunk_from_thing(port)); | |
87dd205b MW |
137 | if (!hasher->allocate_hash(hasher, natd_chunk, &natd_hash)) |
138 | { | |
139 | DBG1(DBG_IKE, "creating NAT-D payload hash failed"); | |
140 | return chunk_empty; | |
141 | } | |
1cc4ec46 TB |
142 | DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); |
143 | DBG3(DBG_IKE, "natd_hash %B", &natd_hash); | |
144 | ||
145 | return natd_hash; | |
146 | } | |
147 | ||
148 | /** | |
149 | * Build a faked NAT-D payload to enforce UDP encapsulation. | |
150 | */ | |
79d6fc7f | 151 | static chunk_t generate_natd_hash_faked(private_isakmp_natd_t *this) |
1cc4ec46 TB |
152 | { |
153 | hasher_t *hasher; | |
154 | chunk_t chunk; | |
155 | rng_t *rng; | |
156 | ||
157 | hasher = this->keymat->get_hasher(this->keymat); | |
158 | if (!hasher) | |
159 | { | |
160 | DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); | |
161 | return chunk_empty; | |
162 | } | |
163 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); | |
92f20747 TB |
164 | if (!rng || |
165 | !rng->allocate_bytes(rng, hasher->get_hash_size(hasher), &chunk)) | |
1cc4ec46 TB |
166 | { |
167 | DBG1(DBG_IKE, "unable to get random bytes for NAT-D fake"); | |
92f20747 | 168 | DESTROY_IF(rng); |
1cc4ec46 TB |
169 | return chunk_empty; |
170 | } | |
1cc4ec46 TB |
171 | rng->destroy(rng); |
172 | return chunk; | |
173 | } | |
174 | ||
175 | /** | |
176 | * Build a NAT-D payload. | |
177 | */ | |
79d6fc7f | 178 | static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src, |
1cc4ec46 TB |
179 | host_t *host) |
180 | { | |
181 | hash_payload_t *payload; | |
182 | ike_cfg_t *config; | |
183 | chunk_t hash; | |
184 | ||
185 | config = this->ike_sa->get_ike_cfg(this->ike_sa); | |
186 | if (src && config->force_encap(config)) | |
187 | { | |
188 | hash = generate_natd_hash_faked(this); | |
189 | } | |
190 | else | |
191 | { | |
192 | ike_sa_id_t *ike_sa_id = this->ike_sa->get_id(this->ike_sa); | |
193 | hash = generate_natd_hash(this, ike_sa_id, host); | |
194 | } | |
87dd205b MW |
195 | if (!hash.len) |
196 | { | |
197 | return NULL; | |
198 | } | |
0ff8d20a | 199 | payload = hash_payload_create(get_nat_d_payload_type(this->ike_sa)); |
1cc4ec46 TB |
200 | payload->set_hash(payload, hash); |
201 | chunk_free(&hash); | |
202 | return payload; | |
203 | } | |
204 | ||
205 | /** | |
206 | * Add NAT-D payloads to the message. | |
207 | */ | |
79d6fc7f | 208 | static void add_natd_payloads(private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
209 | { |
210 | hash_payload_t *payload; | |
211 | host_t *host; | |
212 | ||
213 | /* destination has to be added first */ | |
214 | host = message->get_destination(message); | |
215 | payload = build_natd_payload(this, FALSE, host); | |
87dd205b MW |
216 | if (payload) |
217 | { | |
218 | message->add_payload(message, (payload_t*)payload); | |
219 | } | |
1cc4ec46 TB |
220 | |
221 | /* source is added second, compared with IKEv2 we always know the source, | |
222 | * as these payloads are added in the second Phase 1 exchange or the | |
223 | * response to the first */ | |
224 | host = message->get_source(message); | |
225 | payload = build_natd_payload(this, TRUE, host); | |
87dd205b MW |
226 | if (payload) |
227 | { | |
228 | message->add_payload(message, (payload_t*)payload); | |
229 | } | |
1cc4ec46 TB |
230 | } |
231 | ||
232 | /** | |
233 | * Read NAT-D payloads from message and evaluate them. | |
234 | */ | |
79d6fc7f | 235 | static void process_payloads(private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
236 | { |
237 | enumerator_t *enumerator; | |
238 | payload_t *payload; | |
239 | hash_payload_t *hash_payload; | |
240 | chunk_t hash, src_hash, dst_hash; | |
241 | ike_sa_id_t *ike_sa_id; | |
242 | host_t *me, *other; | |
243 | ike_cfg_t *config; | |
244 | ||
245 | /* precompute hashes for incoming NAT-D comparison */ | |
246 | ike_sa_id = message->get_ike_sa_id(message); | |
247 | me = message->get_destination(message); | |
248 | other = message->get_source(message); | |
249 | dst_hash = generate_natd_hash(this, ike_sa_id, me); | |
250 | src_hash = generate_natd_hash(this, ike_sa_id, other); | |
251 | ||
252 | DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); | |
253 | DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); | |
254 | ||
255 | enumerator = message->create_payload_enumerator(message); | |
256 | while (enumerator->enumerate(enumerator, &payload)) | |
257 | { | |
0ff8d20a VR |
258 | if (payload->get_type(payload) != NAT_D_V1 && |
259 | payload->get_type(payload) != NAT_D_DRAFT_00_03_V1) | |
1cc4ec46 TB |
260 | { |
261 | continue; | |
262 | } | |
263 | hash_payload = (hash_payload_t*)payload; | |
264 | if (!this->dst_seen) | |
265 | { /* the first NAT-D payload contains the destination hash */ | |
266 | this->dst_seen = TRUE; | |
267 | hash = hash_payload->get_hash(hash_payload); | |
268 | DBG3(DBG_IKE, "received dst_hash %B", &hash); | |
269 | if (chunk_equals(hash, dst_hash)) | |
270 | { | |
271 | this->dst_matched = TRUE; | |
272 | } | |
273 | continue; | |
274 | } | |
275 | /* the other NAT-D payloads contain source hashes */ | |
276 | this->src_seen = TRUE; | |
277 | if (!this->src_matched) | |
278 | { | |
279 | hash = hash_payload->get_hash(hash_payload); | |
280 | DBG3(DBG_IKE, "received src_hash %B", &hash); | |
281 | if (chunk_equals(hash, src_hash)) | |
282 | { | |
283 | this->src_matched = TRUE; | |
284 | } | |
285 | } | |
286 | } | |
287 | enumerator->destroy(enumerator); | |
288 | ||
289 | chunk_free(&src_hash); | |
290 | chunk_free(&dst_hash); | |
291 | ||
292 | if (this->src_seen && this->dst_seen) | |
293 | { | |
294 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, | |
295 | !this->dst_matched); | |
296 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, | |
297 | !this->src_matched); | |
298 | config = this->ike_sa->get_ike_cfg(this->ike_sa); | |
299 | if (this->dst_matched && this->src_matched && | |
300 | config->force_encap(config)) | |
301 | { | |
302 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); | |
303 | } | |
304 | } | |
305 | } | |
306 | ||
307 | METHOD(task_t, build_i, status_t, | |
79d6fc7f | 308 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
309 | { |
310 | status_t result = NEED_MORE; | |
311 | ||
312 | switch (message->get_exchange_type(message)) | |
313 | { | |
314 | case AGGRESSIVE: | |
315 | { /* add NAT-D payloads to the second request, already processed | |
316 | * those by the responder contained in the first response */ | |
317 | result = SUCCESS; | |
318 | /* fall */ | |
319 | } | |
320 | case ID_PROT: | |
321 | { /* add NAT-D payloads to the second request, need to process | |
322 | * those by the responder contained in the second response */ | |
323 | if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) | |
324 | { /* wait for the second exchange */ | |
325 | return NEED_MORE; | |
326 | } | |
327 | add_natd_payloads(this, message); | |
328 | return result; | |
329 | } | |
330 | default: | |
331 | break; | |
332 | } | |
333 | return SUCCESS; | |
334 | } | |
335 | ||
336 | METHOD(task_t, process_i, status_t, | |
79d6fc7f | 337 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
338 | { |
339 | status_t result = NEED_MORE; | |
340 | ||
341 | if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) | |
342 | { /* we didn't receive VIDs inidcating support for NAT-T */ | |
343 | return SUCCESS; | |
344 | } | |
345 | ||
346 | switch (message->get_exchange_type(message)) | |
347 | { | |
348 | case ID_PROT: | |
349 | { /* process NAT-D payloads in the second response, added them in the | |
350 | * second request already, so we're done afterwards */ | |
351 | if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) | |
352 | { /* wait for the second exchange */ | |
353 | return NEED_MORE; | |
354 | } | |
355 | result = SUCCESS; | |
356 | /* fall */ | |
357 | } | |
358 | case AGGRESSIVE: | |
359 | { /* process NAT-D payloads in the first response, add them in the | |
360 | * following second request */ | |
361 | process_payloads(this, message); | |
362 | ||
363 | if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) | |
364 | { | |
365 | this->ike_sa->float_ports(this->ike_sa); | |
366 | } | |
367 | return result; | |
368 | } | |
369 | default: | |
370 | break; | |
371 | } | |
372 | return SUCCESS; | |
373 | } | |
374 | ||
375 | METHOD(task_t, process_r, status_t, | |
79d6fc7f | 376 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
377 | { |
378 | status_t result = NEED_MORE; | |
379 | ||
380 | if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) | |
381 | { /* we didn't receive VIDs indicating NAT-T support */ | |
382 | return SUCCESS; | |
383 | } | |
384 | ||
385 | switch (message->get_exchange_type(message)) | |
386 | { | |
387 | case AGGRESSIVE: | |
ef33a4ab | 388 | { /* process NAT-D payloads in the second request, already added ours |
1cc4ec46 TB |
389 | * in the first response */ |
390 | result = SUCCESS; | |
391 | /* fall */ | |
392 | } | |
393 | case ID_PROT: | |
394 | { /* process NAT-D payloads in the second request, need to add ours | |
395 | * to the second response */ | |
396 | if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) | |
397 | { /* wait for the second exchange */ | |
398 | return NEED_MORE; | |
399 | } | |
400 | process_payloads(this, message); | |
401 | return result; | |
402 | } | |
403 | default: | |
404 | break; | |
405 | } | |
406 | return SUCCESS; | |
407 | } | |
408 | ||
409 | METHOD(task_t, build_r, status_t, | |
79d6fc7f | 410 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
411 | { |
412 | switch (message->get_exchange_type(message)) | |
413 | { | |
414 | case ID_PROT: | |
415 | { /* add NAT-D payloads to second response, already processed those | |
416 | * contained in the second request */ | |
417 | if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) | |
418 | { /* wait for the second exchange */ | |
419 | return NEED_MORE; | |
420 | } | |
421 | add_natd_payloads(this, message); | |
422 | return SUCCESS; | |
423 | } | |
424 | case AGGRESSIVE: | |
425 | { /* add NAT-D payloads to the first response, process those contained | |
426 | * in the following second request */ | |
427 | add_natd_payloads(this, message); | |
428 | return NEED_MORE; | |
429 | } | |
430 | default: | |
431 | break; | |
432 | } | |
433 | return SUCCESS; | |
434 | } | |
435 | ||
436 | METHOD(task_t, get_type, task_type_t, | |
79d6fc7f | 437 | private_isakmp_natd_t *this) |
1cc4ec46 | 438 | { |
79d6fc7f | 439 | return TASK_ISAKMP_NATD; |
1cc4ec46 TB |
440 | } |
441 | ||
442 | METHOD(task_t, migrate, void, | |
79d6fc7f | 443 | private_isakmp_natd_t *this, ike_sa_t *ike_sa) |
1cc4ec46 TB |
444 | { |
445 | this->ike_sa = ike_sa; | |
94450913 | 446 | this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); |
1cc4ec46 TB |
447 | this->src_seen = FALSE; |
448 | this->dst_seen = FALSE; | |
449 | this->src_matched = FALSE; | |
450 | this->dst_matched = FALSE; | |
451 | } | |
452 | ||
453 | METHOD(task_t, destroy, void, | |
79d6fc7f | 454 | private_isakmp_natd_t *this) |
1cc4ec46 TB |
455 | { |
456 | free(this); | |
457 | } | |
458 | ||
459 | /* | |
460 | * Described in header. | |
461 | */ | |
79d6fc7f | 462 | isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator) |
1cc4ec46 | 463 | { |
79d6fc7f | 464 | private_isakmp_natd_t *this; |
1cc4ec46 TB |
465 | |
466 | INIT(this, | |
467 | .public = { | |
468 | .task = { | |
469 | .get_type = _get_type, | |
470 | .migrate = _migrate, | |
471 | .destroy = _destroy, | |
472 | }, | |
473 | }, | |
474 | .ike_sa = ike_sa, | |
475 | .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), | |
476 | .initiator = initiator, | |
477 | ); | |
478 | ||
479 | if (initiator) | |
480 | { | |
481 | this->public.task.build = _build_i; | |
482 | this->public.task.process = _process_i; | |
483 | } | |
484 | else | |
485 | { | |
486 | this->public.task.build = _build_r; | |
487 | this->public.task.process = _process_r; | |
488 | } | |
489 | ||
490 | return &this->public; | |
491 | } |