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