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