]>
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 | |
1b671669 | 5 | * HSR Hochschule fuer Technik Rapperswil |
1cc4ec46 TB |
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 | ||
1cc4ec46 | 44 | #include <daemon.h> |
15a682f4 | 45 | #include <sa/ikev1/keymat_v1.h> |
1cc4ec46 TB |
46 | #include <config/peer_cfg.h> |
47 | #include <crypto/hashers/hasher.h> | |
48 | #include <encoding/payloads/hash_payload.h> | |
49 | ||
79d6fc7f | 50 | typedef struct private_isakmp_natd_t private_isakmp_natd_t; |
1cc4ec46 TB |
51 | |
52 | /** | |
53 | * Private members of a ike_natt_t task. | |
54 | */ | |
79d6fc7f | 55 | struct private_isakmp_natd_t { |
1cc4ec46 TB |
56 | |
57 | /** | |
58 | * Public interface. | |
59 | */ | |
79d6fc7f | 60 | isakmp_natd_t public; |
1cc4ec46 TB |
61 | |
62 | /** | |
63 | * Assigned IKE_SA. | |
64 | */ | |
65 | ike_sa_t *ike_sa; | |
66 | ||
67 | /** | |
68 | * Are we the initiator? | |
69 | */ | |
70 | bool initiator; | |
71 | ||
72 | /** | |
73 | * Keymat derivation (from SA) | |
74 | */ | |
75 | keymat_v1_t *keymat; | |
76 | ||
77 | /** | |
78 | * Did we process any NAT detection payloads for a source address? | |
79 | */ | |
80 | bool src_seen; | |
81 | ||
82 | /** | |
83 | * Did we process any NAT detection payloads for a destination address? | |
84 | */ | |
85 | bool dst_seen; | |
86 | ||
87 | /** | |
88 | * Have we found a matching source address NAT hash? | |
89 | */ | |
90 | bool src_matched; | |
91 | ||
92 | /** | |
93 | * Have we found a matching destination address NAT hash? | |
94 | */ | |
95 | bool dst_matched; | |
96 | }; | |
97 | ||
01955eec TB |
98 | /** |
99 | * Check if UDP encapsulation has to be forced either by config or required | |
100 | * by the kernel interface | |
101 | */ | |
102 | static bool force_encap(ike_cfg_t *ike_cfg) | |
103 | { | |
104 | if (!ike_cfg->force_encap(ike_cfg)) | |
105 | { | |
8394ea2a | 106 | return charon->kernel->get_features(charon->kernel) & |
01955eec TB |
107 | KERNEL_REQUIRE_UDP_ENCAPSULATION; |
108 | } | |
109 | return TRUE; | |
110 | } | |
111 | ||
0ff8d20a VR |
112 | /** |
113 | * Get NAT-D payload type (RFC 3947 or RFC 3947 drafts). | |
114 | */ | |
115 | static payload_type_t get_nat_d_payload_type(ike_sa_t *ike_sa) | |
116 | { | |
117 | if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03)) | |
118 | { | |
3ecfc83c | 119 | return PLV1_NAT_D_DRAFT_00_03; |
0ff8d20a | 120 | } |
3ecfc83c | 121 | return PLV1_NAT_D; |
0ff8d20a VR |
122 | } |
123 | ||
1cc4ec46 TB |
124 | /** |
125 | * Build NAT detection hash for a host. | |
126 | */ | |
79d6fc7f | 127 | static chunk_t generate_natd_hash(private_isakmp_natd_t *this, |
1cc4ec46 TB |
128 | ike_sa_id_t *ike_sa_id, host_t *host) |
129 | { | |
130 | hasher_t *hasher; | |
131 | chunk_t natd_chunk, natd_hash; | |
b12c53ce AS |
132 | uint64_t spi_i, spi_r; |
133 | uint16_t port; | |
1cc4ec46 TB |
134 | |
135 | hasher = this->keymat->get_hasher(this->keymat); | |
136 | if (!hasher) | |
137 | { | |
138 | DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); | |
139 | return chunk_empty; | |
140 | } | |
141 | ||
142 | spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); | |
143 | spi_r = ike_sa_id->get_responder_spi(ike_sa_id); | |
144 | port = htons(host->get_port(host)); | |
145 | ||
146 | /* natd_hash = HASH(CKY-I | CKY-R | IP | Port) */ | |
147 | natd_chunk = chunk_cata("cccc", chunk_from_thing(spi_i), | |
148 | chunk_from_thing(spi_r), host->get_address(host), | |
149 | chunk_from_thing(port)); | |
87dd205b MW |
150 | if (!hasher->allocate_hash(hasher, natd_chunk, &natd_hash)) |
151 | { | |
152 | DBG1(DBG_IKE, "creating NAT-D payload hash failed"); | |
153 | return chunk_empty; | |
154 | } | |
1cc4ec46 TB |
155 | DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); |
156 | DBG3(DBG_IKE, "natd_hash %B", &natd_hash); | |
157 | ||
158 | return natd_hash; | |
159 | } | |
160 | ||
161 | /** | |
162 | * Build a faked NAT-D payload to enforce UDP encapsulation. | |
163 | */ | |
79d6fc7f | 164 | static chunk_t generate_natd_hash_faked(private_isakmp_natd_t *this) |
1cc4ec46 TB |
165 | { |
166 | hasher_t *hasher; | |
167 | chunk_t chunk; | |
168 | rng_t *rng; | |
169 | ||
170 | hasher = this->keymat->get_hasher(this->keymat); | |
171 | if (!hasher) | |
172 | { | |
173 | DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); | |
174 | return chunk_empty; | |
175 | } | |
176 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); | |
92f20747 TB |
177 | if (!rng || |
178 | !rng->allocate_bytes(rng, hasher->get_hash_size(hasher), &chunk)) | |
1cc4ec46 TB |
179 | { |
180 | DBG1(DBG_IKE, "unable to get random bytes for NAT-D fake"); | |
92f20747 | 181 | DESTROY_IF(rng); |
1cc4ec46 TB |
182 | return chunk_empty; |
183 | } | |
1cc4ec46 TB |
184 | rng->destroy(rng); |
185 | return chunk; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Build a NAT-D payload. | |
190 | */ | |
79d6fc7f | 191 | static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src, |
1cc4ec46 TB |
192 | host_t *host) |
193 | { | |
194 | hash_payload_t *payload; | |
195 | ike_cfg_t *config; | |
196 | chunk_t hash; | |
197 | ||
198 | config = this->ike_sa->get_ike_cfg(this->ike_sa); | |
01955eec | 199 | if (src && force_encap(config)) |
1cc4ec46 TB |
200 | { |
201 | hash = generate_natd_hash_faked(this); | |
202 | } | |
203 | else | |
204 | { | |
205 | ike_sa_id_t *ike_sa_id = this->ike_sa->get_id(this->ike_sa); | |
206 | hash = generate_natd_hash(this, ike_sa_id, host); | |
207 | } | |
87dd205b MW |
208 | if (!hash.len) |
209 | { | |
210 | return NULL; | |
211 | } | |
0ff8d20a | 212 | payload = hash_payload_create(get_nat_d_payload_type(this->ike_sa)); |
1cc4ec46 TB |
213 | payload->set_hash(payload, hash); |
214 | chunk_free(&hash); | |
215 | return payload; | |
216 | } | |
217 | ||
218 | /** | |
219 | * Add NAT-D payloads to the message. | |
220 | */ | |
79d6fc7f | 221 | static void add_natd_payloads(private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
222 | { |
223 | hash_payload_t *payload; | |
224 | host_t *host; | |
225 | ||
226 | /* destination has to be added first */ | |
227 | host = message->get_destination(message); | |
228 | payload = build_natd_payload(this, FALSE, host); | |
87dd205b MW |
229 | if (payload) |
230 | { | |
231 | message->add_payload(message, (payload_t*)payload); | |
232 | } | |
1cc4ec46 TB |
233 | |
234 | /* source is added second, compared with IKEv2 we always know the source, | |
235 | * as these payloads are added in the second Phase 1 exchange or the | |
236 | * response to the first */ | |
237 | host = message->get_source(message); | |
238 | payload = build_natd_payload(this, TRUE, host); | |
87dd205b MW |
239 | if (payload) |
240 | { | |
241 | message->add_payload(message, (payload_t*)payload); | |
242 | } | |
1cc4ec46 TB |
243 | } |
244 | ||
245 | /** | |
246 | * Read NAT-D payloads from message and evaluate them. | |
247 | */ | |
79d6fc7f | 248 | static void process_payloads(private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
249 | { |
250 | enumerator_t *enumerator; | |
251 | payload_t *payload; | |
252 | hash_payload_t *hash_payload; | |
253 | chunk_t hash, src_hash, dst_hash; | |
254 | ike_sa_id_t *ike_sa_id; | |
255 | host_t *me, *other; | |
256 | ike_cfg_t *config; | |
257 | ||
258 | /* precompute hashes for incoming NAT-D comparison */ | |
259 | ike_sa_id = message->get_ike_sa_id(message); | |
260 | me = message->get_destination(message); | |
261 | other = message->get_source(message); | |
262 | dst_hash = generate_natd_hash(this, ike_sa_id, me); | |
263 | src_hash = generate_natd_hash(this, ike_sa_id, other); | |
264 | ||
265 | DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); | |
266 | DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); | |
267 | ||
268 | enumerator = message->create_payload_enumerator(message); | |
269 | while (enumerator->enumerate(enumerator, &payload)) | |
270 | { | |
3ecfc83c MW |
271 | if (payload->get_type(payload) != PLV1_NAT_D && |
272 | payload->get_type(payload) != PLV1_NAT_D_DRAFT_00_03) | |
1cc4ec46 TB |
273 | { |
274 | continue; | |
275 | } | |
276 | hash_payload = (hash_payload_t*)payload; | |
277 | if (!this->dst_seen) | |
278 | { /* the first NAT-D payload contains the destination hash */ | |
279 | this->dst_seen = TRUE; | |
280 | hash = hash_payload->get_hash(hash_payload); | |
281 | DBG3(DBG_IKE, "received dst_hash %B", &hash); | |
282 | if (chunk_equals(hash, dst_hash)) | |
283 | { | |
284 | this->dst_matched = TRUE; | |
285 | } | |
286 | continue; | |
287 | } | |
288 | /* the other NAT-D payloads contain source hashes */ | |
289 | this->src_seen = TRUE; | |
290 | if (!this->src_matched) | |
291 | { | |
292 | hash = hash_payload->get_hash(hash_payload); | |
293 | DBG3(DBG_IKE, "received src_hash %B", &hash); | |
294 | if (chunk_equals(hash, src_hash)) | |
295 | { | |
296 | this->src_matched = TRUE; | |
297 | } | |
298 | } | |
299 | } | |
300 | enumerator->destroy(enumerator); | |
301 | ||
302 | chunk_free(&src_hash); | |
303 | chunk_free(&dst_hash); | |
304 | ||
305 | if (this->src_seen && this->dst_seen) | |
306 | { | |
307 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, | |
308 | !this->dst_matched); | |
309 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, | |
310 | !this->src_matched); | |
311 | config = this->ike_sa->get_ike_cfg(this->ike_sa); | |
312 | if (this->dst_matched && this->src_matched && | |
01955eec | 313 | force_encap(config)) |
1cc4ec46 TB |
314 | { |
315 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | METHOD(task_t, build_i, status_t, | |
79d6fc7f | 321 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
322 | { |
323 | status_t result = NEED_MORE; | |
324 | ||
325 | switch (message->get_exchange_type(message)) | |
326 | { | |
327 | case AGGRESSIVE: | |
328 | { /* add NAT-D payloads to the second request, already processed | |
329 | * those by the responder contained in the first response */ | |
330 | result = SUCCESS; | |
331 | /* fall */ | |
332 | } | |
333 | case ID_PROT: | |
334 | { /* add NAT-D payloads to the second request, need to process | |
335 | * those by the responder contained in the second response */ | |
3ecfc83c | 336 | if (message->get_payload(message, PLV1_SECURITY_ASSOCIATION)) |
1cc4ec46 TB |
337 | { /* wait for the second exchange */ |
338 | return NEED_MORE; | |
339 | } | |
340 | add_natd_payloads(this, message); | |
341 | return result; | |
342 | } | |
343 | default: | |
344 | break; | |
345 | } | |
346 | return SUCCESS; | |
347 | } | |
348 | ||
349 | METHOD(task_t, process_i, status_t, | |
79d6fc7f | 350 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
351 | { |
352 | status_t result = NEED_MORE; | |
353 | ||
354 | if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) | |
b3ab7a48 | 355 | { /* we didn't receive VIDs indicating support for NAT-T */ |
1cc4ec46 TB |
356 | return SUCCESS; |
357 | } | |
358 | ||
359 | switch (message->get_exchange_type(message)) | |
360 | { | |
361 | case ID_PROT: | |
362 | { /* process NAT-D payloads in the second response, added them in the | |
363 | * second request already, so we're done afterwards */ | |
3ecfc83c | 364 | if (message->get_payload(message, PLV1_SECURITY_ASSOCIATION)) |
1cc4ec46 TB |
365 | { /* wait for the second exchange */ |
366 | return NEED_MORE; | |
367 | } | |
368 | result = SUCCESS; | |
369 | /* fall */ | |
370 | } | |
371 | case AGGRESSIVE: | |
372 | { /* process NAT-D payloads in the first response, add them in the | |
373 | * following second request */ | |
374 | process_payloads(this, message); | |
375 | ||
376 | if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) | |
377 | { | |
378 | this->ike_sa->float_ports(this->ike_sa); | |
379 | } | |
380 | return result; | |
381 | } | |
382 | default: | |
383 | break; | |
384 | } | |
385 | return SUCCESS; | |
386 | } | |
387 | ||
388 | METHOD(task_t, process_r, status_t, | |
79d6fc7f | 389 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
390 | { |
391 | status_t result = NEED_MORE; | |
392 | ||
393 | if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) | |
394 | { /* we didn't receive VIDs indicating NAT-T support */ | |
395 | return SUCCESS; | |
396 | } | |
397 | ||
398 | switch (message->get_exchange_type(message)) | |
399 | { | |
400 | case AGGRESSIVE: | |
ef33a4ab | 401 | { /* process NAT-D payloads in the second request, already added ours |
1cc4ec46 TB |
402 | * in the first response */ |
403 | result = SUCCESS; | |
404 | /* fall */ | |
405 | } | |
406 | case ID_PROT: | |
407 | { /* process NAT-D payloads in the second request, need to add ours | |
408 | * to the second response */ | |
3ecfc83c | 409 | if (message->get_payload(message, PLV1_SECURITY_ASSOCIATION)) |
1cc4ec46 TB |
410 | { /* wait for the second exchange */ |
411 | return NEED_MORE; | |
412 | } | |
413 | process_payloads(this, message); | |
414 | return result; | |
415 | } | |
416 | default: | |
417 | break; | |
418 | } | |
419 | return SUCCESS; | |
420 | } | |
421 | ||
422 | METHOD(task_t, build_r, status_t, | |
79d6fc7f | 423 | private_isakmp_natd_t *this, message_t *message) |
1cc4ec46 TB |
424 | { |
425 | switch (message->get_exchange_type(message)) | |
426 | { | |
427 | case ID_PROT: | |
428 | { /* add NAT-D payloads to second response, already processed those | |
429 | * contained in the second request */ | |
3ecfc83c | 430 | if (message->get_payload(message, PLV1_SECURITY_ASSOCIATION)) |
1cc4ec46 TB |
431 | { /* wait for the second exchange */ |
432 | return NEED_MORE; | |
433 | } | |
434 | add_natd_payloads(this, message); | |
435 | return SUCCESS; | |
436 | } | |
437 | case AGGRESSIVE: | |
438 | { /* add NAT-D payloads to the first response, process those contained | |
439 | * in the following second request */ | |
440 | add_natd_payloads(this, message); | |
441 | return NEED_MORE; | |
442 | } | |
443 | default: | |
444 | break; | |
445 | } | |
446 | return SUCCESS; | |
447 | } | |
448 | ||
449 | METHOD(task_t, get_type, task_type_t, | |
79d6fc7f | 450 | private_isakmp_natd_t *this) |
1cc4ec46 | 451 | { |
79d6fc7f | 452 | return TASK_ISAKMP_NATD; |
1cc4ec46 TB |
453 | } |
454 | ||
455 | METHOD(task_t, migrate, void, | |
79d6fc7f | 456 | private_isakmp_natd_t *this, ike_sa_t *ike_sa) |
1cc4ec46 TB |
457 | { |
458 | this->ike_sa = ike_sa; | |
94450913 | 459 | this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); |
1cc4ec46 TB |
460 | this->src_seen = FALSE; |
461 | this->dst_seen = FALSE; | |
462 | this->src_matched = FALSE; | |
463 | this->dst_matched = FALSE; | |
464 | } | |
465 | ||
466 | METHOD(task_t, destroy, void, | |
79d6fc7f | 467 | private_isakmp_natd_t *this) |
1cc4ec46 TB |
468 | { |
469 | free(this); | |
470 | } | |
471 | ||
472 | /* | |
473 | * Described in header. | |
474 | */ | |
79d6fc7f | 475 | isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator) |
1cc4ec46 | 476 | { |
79d6fc7f | 477 | private_isakmp_natd_t *this; |
1cc4ec46 TB |
478 | |
479 | INIT(this, | |
480 | .public = { | |
481 | .task = { | |
482 | .get_type = _get_type, | |
483 | .migrate = _migrate, | |
484 | .destroy = _destroy, | |
485 | }, | |
486 | }, | |
487 | .ike_sa = ike_sa, | |
488 | .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), | |
489 | .initiator = initiator, | |
490 | ); | |
491 | ||
492 | if (initiator) | |
493 | { | |
494 | this->public.task.build = _build_i; | |
495 | this->public.task.process = _process_i; | |
496 | } | |
497 | else | |
498 | { | |
499 | this->public.task.build = _build_r; | |
500 | this->public.task.process = _process_r; | |
501 | } | |
502 | ||
503 | return &this->public; | |
504 | } |