redesigned IKE_SA using a transaction mechanism:
[strongswan.git] / src / charon / sa / transactions / ike_sa_init.c
1 /**
2 * @file ike_sa_init.c
3 *
4 * @brief Implementation of ike_sa_init_t transaction.
5 *
6 */
7
8 /*
9 * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
10 * Copyright (C) 2005 Jan Hutter, Martin Willi
11 * Hochschule fuer Technik Rapperswil
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 */
23
24 #include "ike_sa_init.h"
25
26 #include <string.h>
27
28 #include <daemon.h>
29 #include <crypto/diffie_hellman.h>
30 #include <crypto/hashers/hasher.h>
31 #include <encoding/payloads/sa_payload.h>
32 #include <encoding/payloads/ke_payload.h>
33 #include <encoding/payloads/nonce_payload.h>
34 #include <sa/transactions/ike_auth.h>
35 #include <queues/jobs/delete_half_open_ike_sa_job.h>
36
37
38 typedef struct private_ike_sa_init_t private_ike_sa_init_t;
39
40 /**
41 * Private members of a ike_sa_init_t object..
42 */
43 struct private_ike_sa_init_t {
44
45 /**
46 * Public methods and transaction_t interface.
47 */
48 ike_sa_init_t public;
49
50 /**
51 * Assigned IKE_SA.
52 */
53 ike_sa_t *ike_sa;
54
55 /**
56 * Message sent by our peer, if already generated
57 */
58 message_t *message;
59
60 /**
61 * Message ID this transaction uses
62 */
63 u_int32_t message_id;
64
65 /**
66 * Times we did send the request
67 */
68 u_int32_t requested;
69
70 /**
71 * Next transaction followed to this one. May be IKE_AUTH,
72 * or a IKE_SA_INIT retry
73 */
74 transaction_t **next;
75
76 /**
77 * Diffie hellman object used to generate public DH value.
78 */
79 diffie_hellman_t *diffie_hellman;
80
81 /**
82 * initiator chosen nonce
83 */
84 chunk_t nonce_i;
85
86 /**
87 * responder chosen nonce
88 */
89 chunk_t nonce_r;
90
91 /**
92 * connection definition used
93 */
94 connection_t *connection;
95
96 /**
97 * Negotiated proposal used for IKE_SA
98 */
99 proposal_t *proposal;
100
101 /**
102 * Randomizer to generate nonces
103 */
104 randomizer_t *randomizer;
105
106 /**
107 * Hasher used to build NAT detection hashes
108 */
109 hasher_t *nat_hasher;
110
111 /**
112 * Precomputed NAT hash for source address
113 */
114 chunk_t natd_src_hash;
115
116 /**
117 * Precomputed NAT hash for destination address
118 */
119 chunk_t natd_dst_hash;
120
121 /**
122 * Did we process any NAT detection notifys for a source address?
123 */
124 bool natd_src_seen;
125
126 /**
127 * Did we process any NAT detection notifys for a destination address?
128 */
129 bool natd_dst_seen;
130
131 /**
132 * Have we found a matching source address NAT hash?
133 */
134 bool natd_src_matched;
135
136 /**
137 * Have we found a matching destination address NAT hash?
138 */
139 bool natd_dst_matched;
140
141 /**
142 * Assigned logger.
143 */
144 logger_t *logger;
145 };
146
147 /**
148 * Implementation of ike_sa_init_t.use_dh_group.
149 */
150 static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group)
151 {
152 this->connection = this->ike_sa->get_connection(this->ike_sa);
153
154 if (this->connection->check_dh_group(this->connection, dh_group))
155 {
156 this->diffie_hellman = diffie_hellman_create(dh_group);
157 if (this->diffie_hellman)
158 {
159 return TRUE;
160 }
161 }
162 return FALSE;
163 }
164
165 /**
166 * Implementation of transaction_t.get_message_id.
167 */
168 static u_int32_t get_message_id(private_ike_sa_init_t *this)
169 {
170 return this->message_id;
171 }
172
173 /**
174 * Implementation of transaction_t.requested.
175 */
176 static u_int32_t requested(private_ike_sa_init_t *this)
177 {
178 return this->requested++;
179 }
180
181 /**
182 * Build NAT detection hash for a host
183 */
184 static chunk_t generate_natd_hash(private_ike_sa_init_t *this,
185 ike_sa_id_t * ike_sa_id, host_t *host)
186 {
187 chunk_t natd_string;
188 chunk_t natd_hash;
189 u_int8_t *p;
190 u_int64_t spi_i, spi_r;
191 char buf[512];
192
193 spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
194 spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
195
196 switch (host->get_family(host))
197 {
198 case AF_INET:
199 {
200 struct sockaddr_in* sai;
201 natd_string = chunk_alloc(sizeof(spi_i) + sizeof(spi_r) +
202 sizeof(sai->sin_addr.s_addr) +
203 sizeof(sai->sin_port));
204 sai = (struct sockaddr_in*)host->get_sockaddr(host);
205 p = natd_string.ptr;
206 *(u_int64_t*)p = spi_i; p += sizeof(spi_i);
207 *(u_int64_t*)p = spi_r; p += sizeof(spi_r);
208 *(u_int32_t*)p = sai->sin_addr.s_addr; p += sizeof(sai->sin_addr.s_addr);
209 *(u_int16_t*)p = sai->sin_port; p += sizeof(sai->sin_port);
210 break;
211 }
212 case AF_INET6:
213 default:
214 /* TODO: Add IPv6 support */
215 natd_string = CHUNK_INITIALIZER;
216 }
217
218 this->nat_hasher->allocate_hash(this->nat_hasher, natd_string, &natd_hash);
219
220 sprintf(buf, "natd_hash(%016llx %016llx %s:%d) == SHA1(", spi_i, spi_r,
221 host->get_address(host), host->get_port(host));
222 chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string);
223 strcat(buf, ") == ");
224 chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash);
225 this->logger->log(this->logger, CONTROL|LEVEL3, buf);
226
227 chunk_free(&natd_string);
228 return natd_hash;
229 }
230
231 /**
232 * Build a NAT detection notify payload.
233 */
234 static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this,
235 notify_type_t type, host_t *host)
236 {
237 chunk_t hash;
238 notify_payload_t *notify;
239 ike_sa_id_t *ike_sa_id;
240
241 ike_sa_id = this->ike_sa->get_id(this->ike_sa);
242 notify = notify_payload_create();
243 notify->set_notify_type(notify, type);
244 hash = generate_natd_hash(this, ike_sa_id, host);
245 notify->set_notification_data(notify, hash);
246 chunk_free(&hash);
247
248 return notify;
249 }
250
251 /**
252 * Implementation of transaction_t.get_request.
253 */
254 static status_t get_request(private_ike_sa_init_t *this, message_t **result)
255 {
256 message_t *request;
257 host_t *me, *other;
258
259 /* check if we already have built a message (retransmission) */
260 if (this->message)
261 {
262 *result = this->message;
263 return SUCCESS;
264 }
265
266 this->connection = this->ike_sa->get_connection(this->ike_sa);
267 me = this->connection->get_my_host(this->connection);
268 other = this->connection->get_other_host(this->connection);
269
270 /* build the request */
271 request = message_create();
272 request->set_source(request, me->clone(me));
273 request->set_destination(request, other->clone(other));
274 request->set_exchange_type(request, IKE_SA_INIT);
275 request->set_request(request, TRUE);
276 request->set_message_id(request, this->message_id);
277 request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
278 /* apply for caller */
279 *result = request;
280 /* store for retransmission */
281 this->message = request;
282
283 /* if the DH group is set via use_dh_group(), we already have a DH object */
284 if (!this->diffie_hellman)
285 {
286 diffie_hellman_group_t dh_group;
287
288 dh_group = this->connection->get_dh_group(this->connection);
289 this->diffie_hellman = diffie_hellman_create(dh_group);
290 if (this->diffie_hellman == NULL)
291 {
292 this->logger->log(this->logger, AUDIT,
293 "DH group %s (%d) not supported, aborting",
294 mapping_find(diffie_hellman_group_m, dh_group), dh_group);
295 return DESTROY_ME;
296 }
297 }
298
299 { /* build the SA payload from proposals */
300 sa_payload_t *sa_payload;
301 linked_list_t *proposal_list;
302
303 proposal_list = this->connection->get_proposals(this->connection);
304 sa_payload = sa_payload_create_from_proposal_list(proposal_list);
305
306 request->add_payload(request, (payload_t*)sa_payload);
307 }
308
309 { /* build the KE payload from the DH object */
310 ke_payload_t *ke_payload;
311
312 ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
313
314 request->add_payload(request, (payload_t*)ke_payload);
315 }
316
317 { /* build the NONCE payload for us (initiator) */
318 nonce_payload_t *nonce_payload;
319 randomizer_t *randomizer;
320
321 randomizer = randomizer_create();
322 if (randomizer->allocate_pseudo_random_bytes(randomizer,
323 NONCE_SIZE, &this->nonce_i) != SUCCESS)
324 {
325 randomizer->destroy(randomizer);
326 request->destroy(request);
327 return DESTROY_ME;
328 }
329 randomizer->destroy(randomizer);
330 nonce_payload = nonce_payload_create();
331 nonce_payload->set_nonce(nonce_payload, this->nonce_i);
332
333 request->add_payload(request, (payload_t*)nonce_payload);
334 }
335
336 { /* build NAT_DETECTION notifys */
337 notify_payload_t *notify;
338 iterator_t *iterator;
339 host_t *host;
340
341 /* N(NAT_DETECTION_SOURCE_IP)+ */
342 iterator = charon->interfaces->create_address_iterator(charon->interfaces);
343 while (iterator->iterate(iterator, (void**)&host))
344 {
345 notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
346 request->add_payload(request, (payload_t*)notify);
347 }
348 iterator->destroy(iterator);
349
350 /* N(NAT_DETECTION_DESTINATION_IP) */
351 notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
352 request->add_payload(request, (payload_t*)notify);
353 }
354
355 /* set new state */
356 this->ike_sa->set_state(this->ike_sa, SA_CONNECTING);
357 return SUCCESS;
358 }
359
360 /**
361 * Handle all kind of notifys
362 */
363 static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *notify_payload)
364 {
365 chunk_t notification_data;
366 notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
367
368 this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s",
369 mapping_find(notify_type_m, notify_type));
370
371 switch (notify_type)
372 {
373 case NO_PROPOSAL_CHOSEN:
374 {
375 this->logger->log(this->logger, AUDIT,
376 "received a NO_PROPOSAL_CHOSEN notify, deleting IKE_SA");
377 return DESTROY_ME;
378 }
379 case INVALID_MAJOR_VERSION:
380 {
381 this->logger->log(this->logger, AUDIT,
382 "received a INVALID_MAJOR_VERSION notify, deleting IKE_SA");
383 return DESTROY_ME;
384 }
385 case INVALID_KE_PAYLOAD:
386 {
387 chunk_t notify_data;
388 diffie_hellman_group_t dh_group, old_dh_group;
389 ike_sa_init_t *retry;
390
391 old_dh_group = this->connection->get_dh_group(this->connection);
392 notify_data = notify_payload->get_notification_data(notify_payload);
393 dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
394
395 this->logger->log(this->logger, AUDIT,
396 "peer didn't accept DH group %s, it requested %s",
397 mapping_find(diffie_hellman_group_m, old_dh_group),
398 mapping_find(diffie_hellman_group_m, dh_group));
399 if (!this->connection->check_dh_group(this->connection, dh_group))
400 {
401 this->logger->log(this->logger, AUDIT,
402 "requested DH group not acceptable, aborting");
403 return DESTROY_ME;
404 }
405 retry = ike_sa_init_create(this->ike_sa, 0);
406 retry->use_dh_group(retry, dh_group);
407 *this->next = (transaction_t*)retry;
408 return FAILED;
409 }
410 case NAT_DETECTION_DESTINATION_IP:
411 {
412 this->natd_dst_seen = TRUE;
413 if (this->natd_dst_matched)
414 {
415 return SUCCESS;
416 }
417 notification_data = notify_payload->get_notification_data(notify_payload);
418 if (chunk_equals(notification_data, this->natd_dst_hash))
419 {
420 this->natd_dst_matched = TRUE;
421 this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D dst hash match");
422 }
423 else
424 {
425 this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D dst hash mismatch");
426 }
427 return SUCCESS;
428 }
429 case NAT_DETECTION_SOURCE_IP:
430 {
431 this->natd_src_seen = TRUE;;
432 if (this->natd_src_matched)
433 {
434 return SUCCESS;
435 }
436 notification_data = notify_payload->get_notification_data(notify_payload);
437 if (chunk_equals(notification_data, this->natd_src_hash))
438 {
439 this->natd_src_matched = TRUE;
440 this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D src hash match");
441 }
442 else
443 {
444 this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D src hash mismatch");
445 }
446 return SUCCESS;
447 }
448 default:
449 {
450 if (notify_type < 16383)
451 {
452 this->logger->log(this->logger, AUDIT,
453 "received %s notify error (%d), deleting IKE_SA",
454 mapping_find(notify_type_m, notify_type),
455 notify_type);
456 return DESTROY_ME;
457 }
458 else
459 {
460 this->logger->log(this->logger, CONTROL,
461 "received %s notify (%d), ignored",
462 mapping_find(notify_type_m, notify_type),
463 notify_type);
464 return SUCCESS;
465 }
466 }
467 }
468 }
469
470 /**
471 * Implementation of transaction_t.get_response.
472 */
473 static status_t get_response(private_ike_sa_init_t *this,
474 message_t *request, message_t **result,
475 transaction_t **next)
476 {
477 host_t *me, *other;
478 message_t *response;
479 status_t status;
480 iterator_t *payloads;
481 sa_payload_t *sa_request = NULL;
482 ke_payload_t *ke_request = NULL;
483 nonce_payload_t *nonce_request = NULL;
484 ike_sa_id_t *ike_sa_id;
485 u_int32_t timeout;
486
487 /* check message type */
488 if (request->get_exchange_type(request) != IKE_SA_INIT)
489 {
490 this->logger->log(this->logger, ERROR,
491 "IKE_SA_INIT request of invalid type, deleting IKE_SA");
492 return DESTROY_ME;
493 }
494
495 /* check if we already have built a response (retransmission) */
496 if (this->message)
497 {
498 *result = this->message;
499 return SUCCESS;
500 }
501
502 me = request->get_destination(request);
503 other = request->get_source(request);
504
505 /* set up response */
506 response = message_create();
507 response->set_source(response, me->clone(me));
508 response->set_destination(response, other->clone(other));
509 response->set_exchange_type(response, IKE_SA_INIT);
510 response->set_request(response, FALSE);
511 response->set_message_id(response, this->message_id);
512 response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
513 this->message = response;
514 *result = response;
515
516 /* this is the first message to process, find a connection for IKE_SA */
517 this->connection = charon->connections->get_connection_by_hosts(
518 charon->connections, me, other);
519 if (this->connection == NULL)
520 {
521 notify_payload_t *notify = notify_payload_create();
522 notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
523 response->add_payload(response, (payload_t*)notify);
524
525 this->logger->log(this->logger, AUDIT,
526 "no connection for hosts %s...%s found, deleting IKE_SA",
527 me->get_address(me), other->get_address(other));
528 return DESTROY_ME;
529 }
530 this->ike_sa->set_connection(this->ike_sa, this->connection);
531
532 /* Precompute NAT-D hashes for incoming NAT notify comparison */
533 ike_sa_id = request->get_ike_sa_id(request);
534 this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
535 this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
536
537 /* Iterate over all payloads. */
538 payloads = request->get_payload_iterator(request);
539 while (payloads->has_next(payloads))
540 {
541 payload_t *payload;
542 payloads->current(payloads, (void**)&payload);
543 switch (payload->get_type(payload))
544 {
545 case SECURITY_ASSOCIATION:
546 sa_request = (sa_payload_t*)payload;
547 break;
548 case KEY_EXCHANGE:
549 ke_request = (ke_payload_t*)payload;
550 break;
551 case NONCE:
552 nonce_request = (nonce_payload_t*)payload;
553 break;
554 case NOTIFY:
555 {
556 status = process_notifys(this, (notify_payload_t*)payload);
557 if (status == FAILED)
558 {
559 payloads->destroy(payloads);
560 /* we return SUCCESS, returned FAILED means do next transaction */
561 return SUCCESS;
562 }
563 if (status == DESTROY_ME)
564 {
565 payloads->destroy(payloads);
566 return DESTROY_ME;
567 }
568 break;
569 }
570 default:
571 {
572 this->logger->log(this->logger, ERROR|LEVEL1,
573 "ignoring %s payload (%d)",
574 mapping_find(payload_type_m, payload->get_type(payload)),
575 payload->get_type(payload));
576 break;
577 }
578 }
579 }
580 payloads->destroy(payloads);
581
582 /* check if we have all payloads */
583 if (!(sa_request && ke_request && nonce_request))
584 {
585 notify_payload_t *notify = notify_payload_create();
586 notify->set_notify_type(notify, INVALID_SYNTAX);
587 response->add_payload(response, (payload_t*)notify);
588 this->logger->log(this->logger, AUDIT,
589 "request message incomplete, deleting IKE_SA");
590 return DESTROY_ME;
591 }
592
593 { /* process SA payload:
594 * -------------------
595 * - extract proposals
596 * - select our most preferred proposal found in extracted
597 * - if no matches, return NO_PROPOSAL_CHOSEN
598 * - add sa payload with selected proposal
599 */
600 sa_payload_t* sa_response;
601 linked_list_t *proposal_list;
602 proposal_t *proposal;
603
604 proposal_list = sa_request->get_proposals(sa_request);
605 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
606 while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
607 {
608 proposal->destroy(proposal);
609 }
610 proposal_list->destroy(proposal_list);
611 if (this->proposal == NULL)
612 {
613 notify_payload_t *notify = notify_payload_create();
614 notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
615 response->add_payload(response, (payload_t*)notify);
616 this->logger->log(this->logger, AUDIT,
617 "request did not contain any acceptable proposals, deleting IKE_SA");
618 return DESTROY_ME;
619 }
620 sa_response = sa_payload_create_from_proposal(this->proposal);
621 response->add_payload(response, (payload_t *)sa_response);
622 }
623
624 { /* process KE payload:
625 * --------------------
626 * - check if used group match the selected proposal
627 * - if not, stop with INVALID_KE_PAYLOAD
628 * - apply others public value to complete diffie hellman exchange
629 * - add our public value to response
630 */
631 diffie_hellman_group_t used_group;
632 ke_payload_t *ke_response;
633
634 used_group = ke_request->get_dh_group_number(ke_request);
635
636 if (!this->connection->check_dh_group(this->connection, used_group) ||
637 (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
638 {
639 u_int16_t notify_group;
640 chunk_t notify_chunk;
641 notify_payload_t *notify;
642 iterator_t *iterator;
643 payload_t *payload;
644
645 notify_group = this->connection->get_dh_group(this->connection);
646 this->logger->log(this->logger, AUDIT,
647 "request used inacceptable DH group %s, sending INVALID_KE_PAYLOAD with %s, deleting IKE_SA",
648 mapping_find(diffie_hellman_group_m, used_group),
649 mapping_find(diffie_hellman_group_m, notify_group));
650
651 /* remove already added payloads */
652 iterator = response->get_payload_iterator(response);
653 while (iterator->has_next(iterator))
654 {
655 iterator->current(iterator, (void**)&payload);
656 iterator->remove(iterator);
657 payload->destroy(payload);
658 }
659 iterator->destroy(iterator);
660
661 notify_group = htons(notify_group);
662 notify_chunk.ptr = (u_int8_t*)&notify_group;
663 notify_chunk.len = sizeof(notify_group);
664 notify = notify_payload_create();
665 notify->set_notify_type(notify, INVALID_KE_PAYLOAD);
666 notify->set_notification_data(notify, notify_chunk);
667 response->add_payload(response, (payload_t*)notify);
668 return DESTROY_ME;
669 }
670 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
671 ke_request->get_key_exchange_data(ke_request));
672
673 /* build response */
674 ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
675 response->add_payload(response, (payload_t*)ke_response);
676 }
677
678 { /* process nonce payload:
679 * ----------------------
680 * - get nonce from payload
681 * - generate own nonce and add to reply
682 */
683 nonce_payload_t *nonce_response;
684
685 this->nonce_i = nonce_request->get_nonce(nonce_request);
686
687 /* build response nonce */
688 if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
689 NONCE_SIZE, &this->nonce_r) != SUCCESS)
690 {
691 notify_payload_t *notify = notify_payload_create();
692 notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
693 response->add_payload(response, (payload_t*)notify);
694 this->logger->log(this->logger, AUDIT,
695 "could not get random bytes for nonce, deleting IKE_SA");
696 return DESTROY_ME;
697 }
698 nonce_response = nonce_payload_create();
699 nonce_response->set_nonce(nonce_response, this->nonce_r);
700 response->add_payload(response, (payload_t *)nonce_response);
701 }
702
703 { /* processs NATT stuff:
704 * --------------------
705 * - check if we or other is behind NAT
706 * - enable NATT if so
707 * - build NAT detection notifys for reply
708 */
709 notify_payload_t *notify;
710
711 if ((!this->natd_src_seen && this->natd_dst_seen) ||
712 (this->natd_src_seen && !this->natd_dst_seen))
713 {
714 notify = notify_payload_create();
715 notify->set_notify_type(notify, INVALID_SYNTAX);
716 response->add_payload(response, (payload_t*)notify);
717 this->logger->log(this->logger, AUDIT,
718 "request contained wrong number of NAT-D payloads, deleting IKE_SA");
719 return DESTROY_ME;
720 }
721 if (this->natd_dst_seen && !this->natd_dst_matched)
722 {
723 this->ike_sa->enable_natt(this->ike_sa, TRUE);
724 }
725 if (this->natd_src_seen && !this->natd_src_matched)
726 {
727 this->ike_sa->enable_natt(this->ike_sa, FALSE);
728 }
729 /* build response NAT DETECTION notifys, if remote supports it */
730 if (this->natd_src_seen || this->natd_dst_seen)
731 {
732 /* N(NAT_DETECTION_SOURCE_IP) */
733 notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
734 response->add_payload(response, (payload_t*)notify);
735
736 /* N(NAT_DETECTION_DESTINATION_IP) */
737 notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
738 response->add_payload(response, (payload_t*)notify);
739 }
740 }
741
742 /* derive all the keys used in the IKE_SA */
743 if (this->ike_sa->build_transforms(this->ike_sa, this->proposal,
744 this->diffie_hellman,
745 this->nonce_i, this->nonce_r,
746 FALSE) != SUCCESS)
747 {
748 notify_payload_t *notify = notify_payload_create();
749 notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
750 response->add_payload(response, (payload_t*)notify);
751 this->logger->log(this->logger, AUDIT,
752 "transform objects could not be created from selected proposal, deleting IKE_SA");
753 return DESTROY_ME;
754 }
755
756 { /* create ike_auth transaction, which will store informations for us */
757 packet_t *response_packet;
758 chunk_t request_chunk, response_chunk;
759 ike_auth_t *ike_auth;
760
761 /* we normally do not generate the message. But we need the generated message
762 * for authentication in the next state, so we do it here. This is not problematic,
763 * as we don't use a crypter/signer in ike_sa_init... */
764 if (response->generate(response, NULL, NULL, &response_packet) != SUCCESS)
765 {
766 this->logger->log(this->logger, AUDIT,
767 "error in response generation, deleting IKE_SA");
768 return DESTROY_ME;
769 }
770 response_packet->destroy(response_packet);
771 request_chunk = request->get_packet_data(request);
772 response_chunk = response->get_packet_data(response);
773
774 /* create next transaction, for which we except a message */
775 ike_auth = ike_auth_create(this->ike_sa, 1);
776 ike_auth->set_nonces(ike_auth,
777 chunk_clone(this->nonce_i),
778 chunk_clone(this->nonce_r));
779 ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
780 *next = (transaction_t*)ike_auth;
781 }
782
783 /* everything went fine. Now we set a timeout to destroy half initiated IKE_SAs */
784 timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration);
785 if (timeout)
786 {
787 job_t *job = (job_t*)delete_half_open_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa));
788 charon->event_queue->add_relative(charon->event_queue, job, timeout);
789 }
790 /* set new state */
791 this->ike_sa->set_state(this->ike_sa, SA_CONNECTING);
792 return SUCCESS;
793 }
794
795
796 /**
797 * Implementation of transaction_t.conclude
798 */
799 static status_t conclude(private_ike_sa_init_t *this, message_t *response,
800 transaction_t **next)
801 {
802 u_int64_t responder_spi;
803 ike_sa_id_t *ike_sa_id;
804 iterator_t *payloads;
805 host_t *me, *other;
806 sa_payload_t *sa_payload = NULL;
807 ke_payload_t *ke_payload = NULL;
808 nonce_payload_t *nonce_payload = NULL;
809 policy_t *policy;
810 status_t status;
811
812 /* check message type */
813 if (response->get_exchange_type(response) != IKE_SA_INIT)
814 {
815 this->logger->log(this->logger, ERROR,
816 "IKE_SA_INIT response of invalid type, deleting IKE_SA");
817 return DESTROY_ME;
818 }
819
820 /* allow setting of next transaction in other functions */
821 this->next = next;
822
823 this->connection = this->ike_sa->get_connection(this->ike_sa);
824 me = this->connection->get_my_host(this->connection);
825 other = this->connection->get_other_host(this->connection);
826
827 /* check if SPI has been updated, but apply only if all goes ok later */
828 responder_spi = response->get_responder_spi(response);
829 if (responder_spi == 0)
830 {
831 this->logger->log(this->logger, ERROR,
832 "response contained a SPI of zero, deleting IKE_SA");
833 return DESTROY_ME;
834 }
835
836 /* Precompute NAT-D hashes for later comparison */
837 ike_sa_id = response->get_ike_sa_id(response);
838 this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
839 this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
840
841 /* Iterate over all payloads to collect them */
842 payloads = response->get_payload_iterator(response);
843 while (payloads->has_next(payloads))
844 {
845 payload_t *payload;
846 payloads->current(payloads, (void**)&payload);
847
848 switch (payload->get_type(payload))
849 {
850 case SECURITY_ASSOCIATION:
851 {
852 sa_payload = (sa_payload_t*)payload;
853 break;
854 }
855 case KEY_EXCHANGE:
856 {
857 ke_payload = (ke_payload_t*)payload;
858 break;
859 }
860 case NONCE:
861 {
862 nonce_payload = (nonce_payload_t*)payload;
863 break;
864 }
865 case NOTIFY:
866 {
867 status = process_notifys(this, (notify_payload_t*)payload);
868 if (status == FAILED)
869 {
870 payloads->destroy(payloads);
871 /* we return SUCCESS, returned FAILED means do next transaction */
872 return SUCCESS;
873 }
874 if (status == DESTROY_ME)
875 {
876 payloads->destroy(payloads);
877 return status;
878 }
879 break;
880 }
881 default:
882 {
883 this->logger->log(this->logger, ERROR, "ignoring payload %s (%d)",
884 mapping_find(payload_type_m, payload->get_type(payload)),
885 payload->get_type(payload));
886 break;
887 }
888 }
889 }
890 payloads->destroy(payloads);
891
892 if (!(nonce_payload && sa_payload && ke_payload))
893 {
894 this->logger->log(this->logger, AUDIT, "response message incomplete, deleting IKE_SA");
895 return DESTROY_ME;
896 }
897
898 { /* process SA payload:
899 * -------------------
900 * - get proposals from it
901 * - check if peer selected a proposal
902 * - verify it's selection againts our set
903 */
904 proposal_t *proposal;
905 linked_list_t *proposal_list;
906
907 /* get the list of selected proposals, the peer has to select only one proposal */
908 proposal_list = sa_payload->get_proposals (sa_payload);
909 if (proposal_list->get_count(proposal_list) != 1)
910 {
911 this->logger->log(this->logger, AUDIT,
912 "response did not contain a single proposal, deleting IKE_SA");
913 while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
914 {
915 proposal->destroy(proposal);
916 }
917 proposal_list->destroy(proposal_list);
918 return DESTROY_ME;
919 }
920
921 /* we have to re-check if the others selection is valid */
922 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
923 while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
924 {
925 proposal->destroy(proposal);
926 }
927 proposal_list->destroy(proposal_list);
928
929 if (this->proposal == NULL)
930 {
931 this->logger->log(this->logger, AUDIT,
932 "peer selected a proposal we did not offer, deleting IKE_SA");
933 return DESTROY_ME;
934 }
935 }
936
937 { /* process KE payload:
938 * -------------------
939 * - extract others public value
940 * - complete diffie-hellman exchange
941 */
942 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
943 ke_payload->get_key_exchange_data(ke_payload));
944 }
945
946 { /* process NONCE payload:
947 * ----------------------
948 * - extract nonce used for key derivation */
949 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
950 }
951
952 { /* process NATT stuff:
953 * -------------------
954 * - check if we or other is NATted
955 * - switch to port 4500 if so
956 */
957 if ((!this->natd_dst_seen && this->natd_src_seen) ||
958 (this->natd_dst_seen && !this->natd_src_seen))
959 {
960 this->logger->log(this->logger, AUDIT,
961 "request contained wrong number of NAT-D payloads, deleting IKE_SA");
962 return DESTROY_ME;
963 }
964 if (this->natd_src_seen && !this->natd_src_matched)
965 {
966 this->ike_sa->enable_natt(this->ike_sa, FALSE);
967 }
968 if (this->natd_dst_seen && !this->natd_dst_matched)
969 {
970 this->ike_sa->enable_natt(this->ike_sa, TRUE);
971 }
972 if (this->ike_sa->is_natt_enabled(this->ike_sa))
973 {
974 me->set_port(me, IKEV2_NATT_PORT);
975 other->set_port(other, IKEV2_NATT_PORT);
976 this->logger->log(this->logger, CONTROL|LEVEL1, "switching to port %d", IKEV2_NATT_PORT);
977 }
978 policy = this->ike_sa->get_policy(this->ike_sa);
979 policy->update_my_ts(policy, me);
980 policy->update_other_ts(policy, other);
981 }
982
983 /* because we are original initiator we have to update the responder SPI to the new one */
984 ike_sa_id = this->ike_sa->get_id(this->ike_sa);
985 ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
986
987 /* derive all the keys used in the IKE_SA */
988 if (this->ike_sa->build_transforms(this->ike_sa, this->proposal,
989 this->diffie_hellman,
990 this->nonce_i, this->nonce_r,
991 TRUE) != SUCCESS)
992 {
993 this->logger->log(this->logger, AUDIT,
994 "transform objects could not be created from selected proposal, deleting IKE_SA");
995 return DESTROY_ME;
996 }
997
998 { /* create ike_auth transaction, which will continue IKE_SA setup */
999 chunk_t request_chunk, response_chunk;
1000 ike_auth_t *ike_auth;
1001
1002 request_chunk = this->message->get_packet_data(this->message);
1003 response_chunk = response->get_packet_data(response);
1004
1005 /* create next transaction, for which we except a message */
1006 ike_auth = ike_auth_create(this->ike_sa, 1);
1007 ike_auth->set_nonces(ike_auth,
1008 chunk_clone(this->nonce_i),
1009 chunk_clone(this->nonce_r));
1010 ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
1011 *next = (transaction_t*)ike_auth;
1012 }
1013
1014 return SUCCESS;
1015 }
1016
1017 static void destroy(private_ike_sa_init_t *this)
1018 {
1019 if (this->message)
1020 {
1021 this->message->destroy(this->message);
1022 }
1023 if (this->diffie_hellman)
1024 {
1025 this->diffie_hellman->destroy(this->diffie_hellman);
1026 }
1027 if (this->proposal)
1028 {
1029 this->proposal->destroy(this->proposal);
1030 }
1031 chunk_free(&this->nonce_i);
1032 chunk_free(&this->nonce_r);
1033 this->randomizer->destroy(this->randomizer);
1034 this->nat_hasher->destroy(this->nat_hasher);
1035 chunk_free(&this->natd_src_hash);
1036 chunk_free(&this->natd_dst_hash);
1037 free(this);
1038 }
1039
1040 /*
1041 * Described in header.
1042 */
1043 ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id)
1044 {
1045 private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_t);
1046
1047 /* transaction interface functions */
1048 this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
1049 this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
1050 this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
1051 this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
1052 this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
1053 this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
1054
1055 /* public functions */
1056 this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group;
1057
1058 /* private data */
1059 this->ike_sa = ike_sa;
1060 this->message_id = message_id;
1061 this->message = NULL;
1062 this->requested = 0;
1063 this->diffie_hellman = NULL;
1064 this->nonce_i = CHUNK_INITIALIZER;
1065 this->nonce_r = CHUNK_INITIALIZER;
1066 this->connection = NULL;
1067 this->proposal = NULL;
1068 this->randomizer = randomizer_create();
1069 this->nat_hasher = hasher_create(HASH_SHA1);
1070 this->natd_src_hash = CHUNK_INITIALIZER;
1071 this->natd_dst_hash = CHUNK_INITIALIZER;
1072 this->natd_src_seen = FALSE;
1073 this->natd_dst_seen = FALSE;
1074 this->natd_src_matched = FALSE;
1075 this->natd_dst_matched = FALSE;
1076 this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
1077
1078 return &this->public;
1079 }