]>
Commit | Line | Data |
---|---|---|
c60c7694 MW |
1 | /* |
2 | * Copyright (C) 2006-2007 Martin Willi | |
3 | * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger | |
4 | * Hochschule fuer Technik Rapperswil | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "ike_natd.h" | |
18 | ||
19 | #include <string.h> | |
20 | ||
21 | #include <daemon.h> | |
9164e49a | 22 | #include <config/peer_cfg.h> |
c60c7694 MW |
23 | #include <crypto/hashers/hasher.h> |
24 | #include <encoding/payloads/notify_payload.h> | |
25 | ||
26 | ||
27 | typedef struct private_ike_natd_t private_ike_natd_t; | |
28 | ||
29 | /** | |
30 | * Private members of a ike_natd_t task. | |
31 | */ | |
32 | struct private_ike_natd_t { | |
7daf5226 | 33 | |
c60c7694 MW |
34 | /** |
35 | * Public methods and task_t interface. | |
36 | */ | |
37 | ike_natd_t public; | |
7daf5226 | 38 | |
c60c7694 MW |
39 | /** |
40 | * Assigned IKE_SA. | |
41 | */ | |
42 | ike_sa_t *ike_sa; | |
7daf5226 | 43 | |
c60c7694 MW |
44 | /** |
45 | * Are we the initiator? | |
46 | */ | |
47 | bool initiator; | |
7daf5226 | 48 | |
c60c7694 MW |
49 | /** |
50 | * Hasher used to build NAT detection hashes | |
51 | */ | |
52 | hasher_t *hasher; | |
7daf5226 | 53 | |
c60c7694 MW |
54 | /** |
55 | * Did we process any NAT detection notifys for a source address? | |
56 | */ | |
57 | bool src_seen; | |
7daf5226 | 58 | |
c60c7694 MW |
59 | /** |
60 | * Did we process any NAT detection notifys for a destination address? | |
61 | */ | |
62 | bool dst_seen; | |
7daf5226 | 63 | |
c60c7694 MW |
64 | /** |
65 | * Have we found a matching source address NAT hash? | |
66 | */ | |
67 | bool src_matched; | |
7daf5226 | 68 | |
c60c7694 MW |
69 | /** |
70 | * Have we found a matching destination address NAT hash? | |
71 | */ | |
72 | bool dst_matched; | |
7daf5226 | 73 | |
9d9a772e MW |
74 | /** |
75 | * whether NAT mappings for our NATed address has changed | |
76 | */ | |
77 | bool mapping_changed; | |
c60c7694 MW |
78 | }; |
79 | ||
01955eec TB |
80 | /** |
81 | * Check if UDP encapsulation has to be forced either by config or required | |
82 | * by the kernel interface | |
83 | */ | |
84 | static bool force_encap(ike_cfg_t *ike_cfg) | |
85 | { | |
86 | if (!ike_cfg->force_encap(ike_cfg)) | |
87 | { | |
8394ea2a | 88 | return charon->kernel->get_features(charon->kernel) & |
01955eec TB |
89 | KERNEL_REQUIRE_UDP_ENCAPSULATION; |
90 | } | |
91 | return TRUE; | |
92 | } | |
c60c7694 MW |
93 | |
94 | /** | |
95 | * Build NAT detection hash for a host | |
96 | */ | |
97 | static chunk_t generate_natd_hash(private_ike_natd_t *this, | |
98 | ike_sa_id_t *ike_sa_id, host_t *host) | |
99 | { | |
100 | chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; | |
101 | chunk_t natd_hash; | |
102 | u_int64_t spi_i, spi_r; | |
103 | u_int16_t port; | |
7daf5226 | 104 | |
d5cc1758 | 105 | /* prepare all required chunks */ |
c60c7694 MW |
106 | spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); |
107 | spi_r = ike_sa_id->get_responder_spi(ike_sa_id); | |
108 | spi_i_chunk.ptr = (void*)&spi_i; | |
109 | spi_i_chunk.len = sizeof(spi_i); | |
110 | spi_r_chunk.ptr = (void*)&spi_r; | |
111 | spi_r_chunk.len = sizeof(spi_r); | |
112 | port = htons(host->get_port(host)); | |
113 | port_chunk.ptr = (void*)&port; | |
114 | port_chunk.len = sizeof(port); | |
115 | addr_chunk = host->get_address(host); | |
7daf5226 | 116 | |
c60c7694 MW |
117 | /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ |
118 | natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); | |
87dd205b MW |
119 | if (!this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash)) |
120 | { | |
121 | natd_hash = chunk_empty; | |
122 | } | |
c60c7694 MW |
123 | DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); |
124 | DBG3(DBG_IKE, "natd_hash %B", &natd_hash); | |
7daf5226 | 125 | |
c60c7694 MW |
126 | chunk_free(&natd_chunk); |
127 | return natd_hash; | |
128 | } | |
129 | ||
130 | /** | |
131 | * Build a NAT detection notify payload. | |
132 | */ | |
133 | static notify_payload_t *build_natd_payload(private_ike_natd_t *this, | |
134 | notify_type_t type, host_t *host) | |
135 | { | |
136 | chunk_t hash; | |
484a06bc | 137 | notify_payload_t *notify; |
9dae1bed | 138 | ike_sa_id_t *ike_sa_id; |
f53b74c9 | 139 | ike_cfg_t *config; |
7daf5226 | 140 | |
c60c7694 | 141 | ike_sa_id = this->ike_sa->get_id(this->ike_sa); |
f53b74c9 | 142 | config = this->ike_sa->get_ike_cfg(this->ike_sa); |
01955eec | 143 | if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP) |
9dae1bed | 144 | { |
8484d2b0 TB |
145 | u_int32_t addr; |
146 | ||
147 | /* chunk_hash() is randomly keyed so this produces a random IPv4 address | |
148 | * that changes with every restart but otherwise stays the same */ | |
149 | addr = chunk_hash(chunk_from_chars(0x00, 0x00, 0x00, 0x00)); | |
150 | host = host_create_from_chunk(AF_INET, chunk_from_thing(addr), 0); | |
151 | hash = generate_natd_hash(this, ike_sa_id, host); | |
152 | host->destroy(host); | |
9dae1bed MW |
153 | } |
154 | else | |
155 | { | |
156 | hash = generate_natd_hash(this, ike_sa_id, host); | |
157 | } | |
87dd205b MW |
158 | if (!hash.len) |
159 | { | |
160 | return NULL; | |
161 | } | |
3ecfc83c | 162 | notify = notify_payload_create(PLV2_NOTIFY); |
f53b74c9 | 163 | notify->set_notify_type(notify, type); |
c60c7694 MW |
164 | notify->set_notification_data(notify, hash); |
165 | chunk_free(&hash); | |
7daf5226 | 166 | |
c60c7694 MW |
167 | return notify; |
168 | } | |
169 | ||
170 | /** | |
171 | * read notifys from message and evaluate them | |
172 | */ | |
173 | static void process_payloads(private_ike_natd_t *this, message_t *message) | |
174 | { | |
a44bb934 | 175 | enumerator_t *enumerator; |
c60c7694 MW |
176 | payload_t *payload; |
177 | notify_payload_t *notify; | |
178 | chunk_t hash, src_hash, dst_hash; | |
179 | ike_sa_id_t *ike_sa_id; | |
180 | host_t *me, *other; | |
f53b74c9 | 181 | ike_cfg_t *config; |
7daf5226 | 182 | |
c60c7694 MW |
183 | /* Precompute NAT-D hashes for incoming NAT notify comparison */ |
184 | ike_sa_id = message->get_ike_sa_id(message); | |
12fa4387 MW |
185 | me = message->get_destination(message); |
186 | other = message->get_source(message); | |
c60c7694 MW |
187 | dst_hash = generate_natd_hash(this, ike_sa_id, me); |
188 | src_hash = generate_natd_hash(this, ike_sa_id, other); | |
7daf5226 | 189 | |
755bdcc2 MW |
190 | DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); |
191 | DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); | |
7daf5226 | 192 | |
a44bb934 MW |
193 | enumerator = message->create_payload_enumerator(message); |
194 | while (enumerator->enumerate(enumerator, &payload)) | |
c60c7694 | 195 | { |
3ecfc83c | 196 | if (payload->get_type(payload) != PLV2_NOTIFY) |
c60c7694 MW |
197 | { |
198 | continue; | |
199 | } | |
200 | notify = (notify_payload_t*)payload; | |
201 | switch (notify->get_notify_type(notify)) | |
202 | { | |
203 | case NAT_DETECTION_DESTINATION_IP: | |
204 | { | |
205 | this->dst_seen = TRUE; | |
9d9a772e | 206 | hash = notify->get_notification_data(notify); |
c60c7694 MW |
207 | if (!this->dst_matched) |
208 | { | |
755bdcc2 | 209 | DBG3(DBG_IKE, "received dst_hash %B", &hash); |
c60c7694 MW |
210 | if (chunk_equals(hash, dst_hash)) |
211 | { | |
212 | this->dst_matched = TRUE; | |
213 | } | |
214 | } | |
9d9a772e MW |
215 | /* RFC4555 says we should also compare against IKE_SA_INIT |
216 | * NATD payloads, but this does not work: We are running | |
217 | * there at port 500, but use 4500 afterwards... */ | |
218 | if (message->get_exchange_type(message) == INFORMATIONAL && | |
219 | this->initiator && !this->dst_matched) | |
220 | { | |
221 | this->mapping_changed = this->ike_sa->has_mapping_changed( | |
222 | this->ike_sa, hash); | |
223 | } | |
c60c7694 MW |
224 | break; |
225 | } | |
226 | case NAT_DETECTION_SOURCE_IP: | |
227 | { | |
228 | this->src_seen = TRUE; | |
229 | if (!this->src_matched) | |
230 | { | |
231 | hash = notify->get_notification_data(notify); | |
755bdcc2 | 232 | DBG3(DBG_IKE, "received src_hash %B", &hash); |
c60c7694 MW |
233 | if (chunk_equals(hash, src_hash)) |
234 | { | |
235 | this->src_matched = TRUE; | |
236 | } | |
237 | } | |
238 | break; | |
239 | } | |
240 | default: | |
241 | break; | |
242 | } | |
243 | } | |
a44bb934 | 244 | enumerator->destroy(enumerator); |
7daf5226 | 245 | |
c60c7694 MW |
246 | chunk_free(&src_hash); |
247 | chunk_free(&dst_hash); | |
7daf5226 | 248 | |
c60c7694 MW |
249 | if (this->src_seen && this->dst_seen) |
250 | { | |
3b04350a | 251 | this->ike_sa->enable_extension(this->ike_sa, EXT_NATT); |
fc2d1c42 MW |
252 | |
253 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, | |
254 | !this->dst_matched); | |
255 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, | |
484a06bc | 256 | !this->src_matched); |
f53b74c9 MW |
257 | config = this->ike_sa->get_ike_cfg(this->ike_sa); |
258 | if (this->dst_matched && this->src_matched && | |
01955eec | 259 | force_encap(config)) |
9dae1bed | 260 | { |
484a06bc | 261 | this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); |
9dae1bed | 262 | } |
c60c7694 MW |
263 | } |
264 | } | |
265 | ||
6ade8d61 AS |
266 | METHOD(task_t, process_i, status_t, |
267 | private_ike_natd_t *this, message_t *message) | |
c60c7694 MW |
268 | { |
269 | process_payloads(this, message); | |
7daf5226 | 270 | |
9164e49a MW |
271 | if (message->get_exchange_type(message) == IKE_SA_INIT) |
272 | { | |
2402dee1 TB |
273 | peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); |
274 | if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY) || | |
275 | /* if peer supports NAT-T, we switch to port 4500 even | |
276 | * if no NAT is detected. can't be done later (when we would know | |
277 | * whether the peer supports MOBIKE) because there would be no | |
278 | * exchange to actually do the switch (other than a forced DPD). */ | |
279 | (peer_cfg->use_mobike(peer_cfg) && | |
280 | this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))) | |
9164e49a | 281 | { |
277f02ce | 282 | this->ike_sa->float_ports(this->ike_sa); |
9164e49a | 283 | } |
c60c7694 | 284 | } |
7daf5226 | 285 | |
c60c7694 MW |
286 | return SUCCESS; |
287 | } | |
288 | ||
6ade8d61 AS |
289 | METHOD(task_t, build_i, status_t, |
290 | private_ike_natd_t *this, message_t *message) | |
c60c7694 MW |
291 | { |
292 | notify_payload_t *notify; | |
507f26f6 | 293 | enumerator_t *enumerator; |
cc2eadde | 294 | ike_cfg_t *ike_cfg; |
c60c7694 | 295 | host_t *host; |
7daf5226 | 296 | |
552cc11b MW |
297 | if (this->hasher == NULL) |
298 | { | |
299 | DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); | |
300 | return NEED_MORE; | |
301 | } | |
7daf5226 | 302 | |
cc2eadde MW |
303 | ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); |
304 | ||
7068410b | 305 | /* destination is always set */ |
12fa4387 | 306 | host = message->get_destination(message); |
7068410b | 307 | notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); |
87dd205b MW |
308 | if (notify) |
309 | { | |
310 | message->add_payload(message, (payload_t*)notify); | |
311 | } | |
7daf5226 | 312 | |
7068410b MW |
313 | /* source may be any, we have 3 possibilities to get our source address: |
314 | * 1. It is defined in the config => use the one of the IKE_SA | |
315 | * 2. We do a routing lookup in the kernel interface | |
316 | * 3. Include all possbile addresses | |
317 | */ | |
12fa4387 | 318 | host = message->get_source(message); |
01955eec | 319 | if (!host->is_anyaddr(host) || force_encap(ike_cfg)) |
893c3a4e | 320 | { /* 1. or if we force UDP encap, as it doesn't matter if it's %any */ |
7068410b | 321 | notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); |
87dd205b MW |
322 | if (notify) |
323 | { | |
324 | message->add_payload(message, (payload_t*)notify); | |
325 | } | |
7068410b MW |
326 | } |
327 | else | |
c60c7694 | 328 | { |
8394ea2a | 329 | host = charon->kernel->get_source_addr(charon->kernel, |
ce5b1708 | 330 | this->ike_sa->get_other_host(this->ike_sa), NULL); |
7068410b MW |
331 | if (host) |
332 | { /* 2. */ | |
cc2eadde | 333 | host->set_port(host, ike_cfg->get_my_port(ike_cfg)); |
c60c7694 | 334 | notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); |
87dd205b MW |
335 | if (notify) |
336 | { | |
337 | message->add_payload(message, (payload_t*)notify); | |
338 | } | |
7068410b MW |
339 | host->destroy(host); |
340 | } | |
341 | else | |
342 | { /* 3. */ | |
8394ea2a TB |
343 | enumerator = charon->kernel->create_address_enumerator( |
344 | charon->kernel, ADDR_TYPE_REGULAR); | |
507f26f6 | 345 | while (enumerator->enumerate(enumerator, (void**)&host)) |
7068410b | 346 | { |
d3fbc75e MW |
347 | /* apply port 500 to host, but work on a copy */ |
348 | host = host->clone(host); | |
cc2eadde | 349 | host->set_port(host, ike_cfg->get_my_port(ike_cfg)); |
7068410b | 350 | notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); |
d3fbc75e | 351 | host->destroy(host); |
87dd205b MW |
352 | if (notify) |
353 | { | |
354 | message->add_payload(message, (payload_t*)notify); | |
355 | } | |
7068410b | 356 | } |
507f26f6 | 357 | enumerator->destroy(enumerator); |
c60c7694 | 358 | } |
c60c7694 | 359 | } |
c60c7694 MW |
360 | return NEED_MORE; |
361 | } | |
362 | ||
6ade8d61 AS |
363 | METHOD(task_t, build_r, status_t, |
364 | private_ike_natd_t *this, message_t *message) | |
c60c7694 MW |
365 | { |
366 | notify_payload_t *notify; | |
367 | host_t *me, *other; | |
7daf5226 | 368 | |
f3bb1bd0 | 369 | /* only add notifies on successful responses. */ |
fc2d1c42 | 370 | if (message->get_exchange_type(message) == IKE_SA_INIT && |
3ecfc83c | 371 | message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL) |
c60c7694 | 372 | { |
37c6ebb7 | 373 | return SUCCESS; |
c60c7694 MW |
374 | } |
375 | ||
376 | if (this->src_seen && this->dst_seen) | |
377 | { | |
552cc11b MW |
378 | if (this->hasher == NULL) |
379 | { | |
380 | DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); | |
381 | return SUCCESS; | |
382 | } | |
7daf5226 | 383 | |
c60c7694 | 384 | /* initiator seems to support NAT detection, add response */ |
12fa4387 | 385 | me = message->get_source(message); |
c60c7694 | 386 | notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); |
87dd205b MW |
387 | if (notify) |
388 | { | |
389 | message->add_payload(message, (payload_t*)notify); | |
390 | } | |
12fa4387 | 391 | other = message->get_destination(message); |
c60c7694 | 392 | notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); |
87dd205b MW |
393 | if (notify) |
394 | { | |
395 | message->add_payload(message, (payload_t*)notify); | |
396 | } | |
c60c7694 MW |
397 | } |
398 | return SUCCESS; | |
399 | } | |
400 | ||
6ade8d61 AS |
401 | METHOD(task_t, process_r, status_t, |
402 | private_ike_natd_t *this, message_t *message) | |
484a06bc | 403 | { |
c60c7694 | 404 | process_payloads(this, message); |
7daf5226 | 405 | |
c60c7694 MW |
406 | return NEED_MORE; |
407 | } | |
408 | ||
6ade8d61 AS |
409 | METHOD(task_t, get_type, task_type_t, |
410 | private_ike_natd_t *this) | |
c60c7694 | 411 | { |
a09972df | 412 | return TASK_IKE_NATD; |
c60c7694 MW |
413 | } |
414 | ||
6ade8d61 AS |
415 | METHOD(task_t, migrate, void, |
416 | private_ike_natd_t *this, ike_sa_t *ike_sa) | |
c60c7694 MW |
417 | { |
418 | this->ike_sa = ike_sa; | |
419 | this->src_seen = FALSE; | |
420 | this->dst_seen = FALSE; | |
421 | this->src_matched = FALSE; | |
422 | this->dst_matched = FALSE; | |
9d9a772e MW |
423 | this->mapping_changed = FALSE; |
424 | } | |
425 | ||
6ade8d61 AS |
426 | METHOD(task_t, destroy, void, |
427 | private_ike_natd_t *this) | |
9d9a772e | 428 | { |
6ade8d61 AS |
429 | DESTROY_IF(this->hasher); |
430 | free(this); | |
c60c7694 MW |
431 | } |
432 | ||
6ade8d61 AS |
433 | METHOD(ike_natd_t, has_mapping_changed, bool, |
434 | private_ike_natd_t *this) | |
c60c7694 | 435 | { |
6ade8d61 | 436 | return this->mapping_changed; |
c60c7694 MW |
437 | } |
438 | ||
439 | /* | |
440 | * Described in header. | |
441 | */ | |
442 | ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) | |
443 | { | |
6ade8d61 AS |
444 | private_ike_natd_t *this; |
445 | ||
446 | INIT(this, | |
447 | .public = { | |
448 | .task = { | |
449 | .get_type = _get_type, | |
450 | .migrate = _migrate, | |
451 | .destroy = _destroy, | |
452 | }, | |
453 | .has_mapping_changed = _has_mapping_changed, | |
454 | }, | |
455 | .ike_sa = ike_sa, | |
456 | .initiator = initiator, | |
457 | .hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1), | |
458 | ); | |
7daf5226 | 459 | |
c60c7694 MW |
460 | if (initiator) |
461 | { | |
6ade8d61 AS |
462 | this->public.task.build = _build_i; |
463 | this->public.task.process = _process_i; | |
c60c7694 MW |
464 | } |
465 | else | |
466 | { | |
6ade8d61 AS |
467 | this->public.task.build = _build_r; |
468 | this->public.task.process = _process_r; | |
c60c7694 | 469 | } |
7daf5226 | 470 | |
c60c7694 MW |
471 | return &this->public; |
472 | } |