implement proper handling of most simultaneous IKE_SA rekeying cases
[strongswan.git] / src / charon / sa / transactions / rekey_ike_sa.c
1 /**
2 * @file rekey_ike_sa.c
3 *
4 * @brief Implementation of rekey_ike_sa_t transaction.
5 *
6 */
7
8 /*
9 * Copyright (C) 2006 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "rekey_ike_sa.h"
24
25 #include <string.h>
26
27 #include <daemon.h>
28 #include <encoding/payloads/sa_payload.h>
29 #include <encoding/payloads/nonce_payload.h>
30 #include <encoding/payloads/ke_payload.h>
31 #include <sa/transactions/delete_ike_sa.h>
32 #include <utils/randomizer.h>
33
34
35 typedef struct private_rekey_ike_sa_t private_rekey_ike_sa_t;
36
37 /**
38 * Private members of a rekey_ike_sa_t object..
39 */
40 struct private_rekey_ike_sa_t {
41
42 /**
43 * Public methods and transaction_t interface.
44 */
45 rekey_ike_sa_t public;
46
47 /**
48 * Assigned IKE_SA.
49 */
50 ike_sa_t *ike_sa;
51
52 /**
53 * Message sent by our peer, if already generated
54 */
55 message_t *message;
56
57 /**
58 * Message ID this transaction uses
59 */
60 u_int32_t message_id;
61
62 /**
63 * Times we did send the request
64 */
65 u_int32_t requested;
66
67 /**
68 * IKE_SA we set up, replaces ike_sa
69 */
70 ike_sa_t *new_sa;
71
72 /**
73 * Connection used to replace IKE_SA
74 */
75 connection_t *connection;
76
77 /**
78 * initiator chosen nonce
79 */
80 chunk_t nonce_i;
81
82 /**
83 * responder chosen nonce
84 */
85 chunk_t nonce_r;
86
87 /**
88 * lower of the nonces of a simultaneus rekeying request
89 */
90 chunk_t nonce_s;
91
92 /**
93 * Diffie hellman to generate new shared secret
94 */
95 diffie_hellman_t *diffie_hellman;
96
97 /**
98 * negotiated proposal to use
99 */
100 proposal_t *proposal;
101
102 /**
103 * Have we lost the simultaneous rekeying nonce compare?
104 */
105 bool lost;
106
107 /**
108 * source of randomness for nonces
109 */
110 randomizer_t *randomizer;
111
112 /**
113 * next transaction processed by the IKE_SA
114 */
115 transaction_t **next;
116
117 /**
118 * Assigned logger.
119 */
120 logger_t *logger;
121 };
122
123 /**
124 * Implementation of transaction_t.get_message_id.
125 */
126 static u_int32_t get_message_id(private_rekey_ike_sa_t *this)
127 {
128 return this->message_id;
129 }
130
131 /**
132 * Implementation of transaction_t.requested.
133 */
134 static u_int32_t requested(private_rekey_ike_sa_t *this)
135 {
136 return this->requested++;
137 }
138
139
140 /**
141 * Implementation of rekey_ike_sa_t.use_dh_group.
142 */
143 static bool use_dh_group(private_rekey_ike_sa_t *this, diffie_hellman_group_t dh_group)
144 {
145 if (this->connection->check_dh_group(this->connection, dh_group))
146 {
147 this->diffie_hellman = diffie_hellman_create(dh_group);
148 if (this->diffie_hellman)
149 {
150 return TRUE;
151 }
152 }
153 return FALSE;
154 }
155
156 /**
157 * Implementation of rekey_ike_sa_t.cancel.
158 */
159 static void cancel(private_rekey_ike_sa_t *this)
160 {
161 this->lost = TRUE;
162 }
163
164 /**
165 * destroy a list of proposals
166 */
167 static void destroy_proposal_list(linked_list_t *list)
168 {
169 proposal_t *proposal;
170
171 while (list->remove_last(list, (void**)&proposal) == SUCCESS)
172 {
173 proposal->destroy(proposal);
174 }
175 list->destroy(list);
176 }
177
178 /**
179 * Implementation of transaction_t.get_request.
180 */
181 static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
182 {
183 message_t *request;
184 host_t *me, *other;
185
186 /* check if we already have built a message (retransmission) */
187 if (this->message)
188 {
189 *result = this->message;
190 return SUCCESS;
191 }
192
193 if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
194 {
195 this->logger->log(this->logger, ERROR,
196 "tried to rekey in state %s, aborted",
197 mapping_find(ike_sa_state_m,
198 this->ike_sa->get_state(this->ike_sa)));
199 return FAILED;
200 }
201
202 me = this->ike_sa->get_my_host(this->ike_sa);
203 other = this->ike_sa->get_other_host(this->ike_sa);
204
205 /* build the request */
206 request = message_create();
207 request->set_source(request, me->clone(me));
208 request->set_destination(request, other->clone(other));
209 request->set_exchange_type(request, CREATE_CHILD_SA);
210 request->set_request(request, TRUE);
211 request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
212 *result = request;
213 this->message = request;
214
215 { /* build SA payload */
216 sa_payload_t *sa_payload;
217 linked_list_t *proposals;
218 ike_sa_id_t *ike_sa_id;
219 iterator_t *iterator;
220 proposal_t *proposal;
221 u_int64_t spi;
222
223 /* get a connection to replace current IKE_SA */
224 this->connection = charon->connections->get_connection_by_name(
225 charon->connections,
226 this->ike_sa->get_name(this->ike_sa));
227 /* if connection lookup by name fails, try it with the hosts */
228 if (this->connection == NULL)
229 {
230 this->connection = charon->connections->get_connection_by_hosts(
231 charon->connections,
232 me, other);
233 if (this->connection == NULL)
234 {
235 this->logger->log(this->logger, ERROR,
236 "no connection found to rekey IKE_SA");
237 return FAILED;
238 }
239 }
240
241 /* create a new SA */
242 ike_sa_id = ike_sa_id_create(0, 0, TRUE);
243 this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
244 ike_sa_id);
245 spi = ike_sa_id->get_initiator_spi(ike_sa_id);
246 ike_sa_id->destroy(ike_sa_id);
247
248 proposals = this->connection->get_proposals(this->connection);
249 iterator = proposals->create_iterator(proposals, TRUE);
250 while (iterator->iterate(iterator, (void**)&proposal))
251 {
252 proposal->set_spi(proposal, spi);
253 }
254 iterator->destroy(iterator);
255
256 sa_payload = sa_payload_create_from_proposal_list(proposals);
257 destroy_proposal_list(proposals);
258 request->add_payload(request, (payload_t*)sa_payload);
259 }
260
261 { /* build the NONCE payload for us (initiator) */
262 nonce_payload_t *nonce_payload;
263
264 if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
265 NONCE_SIZE, &this->nonce_i) != SUCCESS)
266 {
267 return FAILED;
268 }
269 nonce_payload = nonce_payload_create();
270 nonce_payload->set_nonce(nonce_payload, this->nonce_i);
271 request->add_payload(request, (payload_t*)nonce_payload);
272 }
273
274 /* if the DH group is set via use_dh_group(), we already have a DH object */
275 if (!this->diffie_hellman)
276 {
277 diffie_hellman_group_t dh_group;
278
279 dh_group = this->connection->get_dh_group(this->connection);
280 this->diffie_hellman = diffie_hellman_create(dh_group);
281 if (this->diffie_hellman == NULL)
282 {
283 this->logger->log(this->logger, AUDIT,
284 "DH group %s (%d) not supported, aborting",
285 mapping_find(diffie_hellman_group_m, dh_group), dh_group);
286 return FAILED;
287 }
288 }
289
290 { /* build the KE payload from the DH object */
291 ke_payload_t *ke_payload;
292
293 ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
294 request->add_payload(request, (payload_t*)ke_payload);
295 }
296
297 this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
298 request->set_message_id(request, this->message_id);
299
300 /* register us as rekeying to detect multiple rekeying */
301 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
302 this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
303
304 return SUCCESS;
305 }
306
307 /**
308 * Handle all kind of notifys
309 */
310 static status_t process_notifys(private_rekey_ike_sa_t *this, notify_payload_t *notify_payload)
311 {
312 notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
313
314 this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s",
315 mapping_find(notify_type_m, notify_type));
316
317 switch (notify_type)
318 {
319 case NO_PROPOSAL_CHOSEN:
320 {
321 this->logger->log(this->logger, AUDIT,
322 "received a NO_PROPOSAL_CHOSEN notify, IKE_SA rekeying failed");
323 return FAILED;
324 }
325 case INVALID_KE_PAYLOAD:
326 {
327 chunk_t notify_data;
328 diffie_hellman_group_t dh_group, old_dh_group;
329 rekey_ike_sa_t *retry;
330
331 old_dh_group = this->connection->get_dh_group(this->connection);
332 notify_data = notify_payload->get_notification_data(notify_payload);
333 dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
334
335 this->logger->log(this->logger, AUDIT,
336 "peer didn't accept DH group %s, it requested %s",
337 mapping_find(diffie_hellman_group_m, old_dh_group),
338 mapping_find(diffie_hellman_group_m, dh_group));
339 if (!this->connection->check_dh_group(this->connection, dh_group))
340 {
341 this->logger->log(this->logger, AUDIT,
342 "requested DH group not acceptable, IKE_SA rekeying failed");
343 return FAILED;
344 }
345 retry = rekey_ike_sa_create(this->ike_sa);
346 retry->use_dh_group(retry, dh_group);
347 *this->next = (transaction_t*)retry;
348 return FAILED;
349 }
350 default:
351 {
352 if (notify_type < 16383)
353 {
354 this->logger->log(this->logger, AUDIT,
355 "received %s notify error (%d, IKE_SA rekeying failed",
356 mapping_find(notify_type_m, notify_type),
357 notify_type);
358 return FAILED;
359 }
360 else
361 {
362 this->logger->log(this->logger, CONTROL,
363 "received %s notify (%d), ignored",
364 mapping_find(notify_type_m, notify_type),
365 notify_type);
366 return SUCCESS;
367 }
368 }
369 }
370 }
371
372 /**
373 * Switch to the new created IKE_SA
374 */
375 static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
376 {
377 identification_t *my_id, *other_id;
378 host_t *my_host, *other_host;
379 char *name;
380
381 my_id = this->ike_sa->get_my_id(this->ike_sa);
382 other_id = this->ike_sa->get_other_id(this->ike_sa);
383 my_host = this->ike_sa->get_my_host(this->ike_sa);
384 other_host = this->ike_sa->get_other_host(this->ike_sa);
385 name = this->connection->get_name(this->connection);
386
387 this->new_sa->set_my_id(this->new_sa, my_id->clone(my_id));
388 this->new_sa->set_other_id(this->new_sa, other_id->clone(other_id));
389 this->new_sa->set_my_host(this->new_sa, my_host->clone(my_host));
390 this->new_sa->set_other_host(this->new_sa, other_host->clone(other_host));
391 this->new_sa->set_name(this->new_sa, name);
392
393 if (this->new_sa->derive_keys(this->new_sa, this->proposal,
394 this->diffie_hellman,
395 this->nonce_i, this->nonce_r, initiator,
396 this->ike_sa->get_child_prf(this->ike_sa),
397 this->ike_sa->get_prf(this->ike_sa)
398 ) != SUCCESS)
399 {
400 return FAILED;
401 }
402
403 this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
404
405 this->new_sa->set_lifetimes(this->new_sa,
406 this->connection->get_soft_lifetime(this->connection),
407 this->connection->get_hard_lifetime(this->connection));
408 return SUCCESS;
409 }
410
411 /**
412 * Build a notify message.
413 */
414 static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
415 {
416 notify_payload_t *notify;
417
418 if (flush_message)
419 {
420 payload_t *payload;
421 iterator_t *iterator = message->get_payload_iterator(message);
422 while (iterator->iterate(iterator, (void**)&payload))
423 {
424 payload->destroy(payload);
425 iterator->remove(iterator);
426 }
427 iterator->destroy(iterator);
428 }
429
430 notify = notify_payload_create();
431 notify->set_notify_type(notify, type);
432 notify->set_notification_data(notify, data);
433 message->add_payload(message, (payload_t*)notify);
434 }
435
436 /**
437 * Implementation of transaction_t.get_response.
438 */
439 static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
440 message_t **result, transaction_t **next)
441 {
442 host_t *me, *other;
443 message_t *response;
444 status_t status;
445 iterator_t *payloads, *iterator;
446 child_sa_t *child_sa;
447 sa_payload_t *sa_request = NULL;
448 nonce_payload_t *nonce_request = NULL;
449 ke_payload_t *ke_request = NULL;
450 nonce_payload_t *nonce_response;
451
452 /* check if we already have built a response (retransmission) */
453 if (this->message)
454 {
455 *result = this->message;
456 return SUCCESS;
457 }
458
459 me = this->ike_sa->get_my_host(this->ike_sa);
460 other = this->ike_sa->get_other_host(this->ike_sa);
461 this->message_id = request->get_message_id(request);
462
463 /* set up response */
464 response = message_create();
465 response->set_source(response, me->clone(me));
466 response->set_destination(response, other->clone(other));
467 response->set_exchange_type(response, CREATE_CHILD_SA);
468 response->set_request(response, FALSE);
469 response->set_message_id(response, this->message_id);
470 response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
471 this->message = response;
472 *result = response;
473
474 /* check message type */
475 if (request->get_exchange_type(request) != CREATE_CHILD_SA)
476 {
477 this->logger->log(this->logger, ERROR,
478 "CREATE_CHILD_SA response of invalid type, aborted");
479 return FAILED;
480 }
481
482 /* if we already initiate a delete, we do not allow rekeying */
483 if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
484 {
485 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
486 this->logger->log(this->logger, CONTROL,
487 "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
488 return FAILED;
489 }
490
491 /* if we have a CHILD which is "half-open", we do not allow rekeying */
492 iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
493 while (iterator->iterate(iterator, (void**)&child_sa))
494 {
495 child_sa_state_t state = child_sa->get_state(child_sa);
496 if (state == CHILD_CREATED ||
497 state == CHILD_REKEYING ||
498 state == CHILD_DELETING)
499 {
500 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
501 this->logger->log(this->logger, CONTROL,
502 "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
503 iterator->destroy(iterator);
504 return FAILED;
505 }
506 }
507 iterator->destroy(iterator);
508
509 /* apply for notify processing */
510 this->next = next;
511
512 /* Iterate over all payloads. */
513 payloads = request->get_payload_iterator(request);
514 while (payloads->has_next(payloads))
515 {
516 payload_t *payload;
517 payloads->current(payloads, (void**)&payload);
518 switch (payload->get_type(payload))
519 {
520 case SECURITY_ASSOCIATION:
521 sa_request = (sa_payload_t*)payload;
522 break;
523 case NONCE:
524 nonce_request = (nonce_payload_t*)payload;
525 break;
526 case KEY_EXCHANGE:
527 {
528 ke_request = (ke_payload_t*)payload;
529 break;
530 }
531 case NOTIFY:
532 {
533 status = process_notifys(this, (notify_payload_t*)payload);
534 if (status != SUCCESS)
535 {
536 payloads->destroy(payloads);
537 return status;
538 }
539 break;
540 }
541 default:
542 {
543 this->logger->log(this->logger, ERROR, "ignoring %s payload (%d)",
544 mapping_find(payload_type_m, payload->get_type(payload)),
545 payload->get_type(payload));
546 break;
547 }
548 }
549 }
550 payloads->destroy(payloads);
551
552 /* check if we have all payloads */
553 if (!(sa_request && nonce_request && ke_request))
554 {
555 build_notify(INVALID_SYNTAX, CHUNK_INITIALIZER, response, TRUE);
556 this->logger->log(this->logger, AUDIT,
557 "request message incomplete, IKE_SA rekeying failed");
558 return FAILED;
559 }
560
561 { /* process nonce payload */
562 this->nonce_i = nonce_request->get_nonce(nonce_request);
563 if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
564 NONCE_SIZE, &this->nonce_r) != SUCCESS)
565 {
566 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
567 return FAILED;
568 }
569 nonce_response = nonce_payload_create();
570 nonce_response->set_nonce(nonce_response, this->nonce_r);
571 }
572
573 { /* get a connection to replace current IKE_SA */
574 this->connection = charon->connections->get_connection_by_name(
575 charon->connections,
576 this->ike_sa->get_name(this->ike_sa));
577 /* if connection lookup by name fails, try it with the hosts */
578 if (this->connection == NULL)
579 {
580 this->connection = charon->connections->get_connection_by_hosts(
581 charon->connections,
582 me, other);
583 if (this->connection == NULL)
584 {
585 this->logger->log(this->logger, ERROR,
586 "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN");
587 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
588 return FAILED;
589 }
590 }
591 }
592
593 { /* process SA payload */
594 linked_list_t *proposal_list;
595 sa_payload_t *sa_response;
596 u_int64_t spi;
597 ike_sa_id_t *ike_sa_id;
598
599 sa_response = sa_payload_create();
600 /* get proposals from request, and select one with ours */
601 proposal_list = sa_request->get_proposals(sa_request);
602 this->logger->log(this->logger, CONTROL|LEVEL1, "selecting proposals:");
603 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
604 destroy_proposal_list(proposal_list);
605
606 /* do we have a proposal? */
607 if (this->proposal == NULL)
608 {
609 this->logger->log(this->logger, AUDIT,
610 "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN");
611 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
612 return FAILED;
613 }
614
615 /* create IKE_SA with new SPIs */
616 spi = this->proposal->get_spi(this->proposal);
617 ike_sa_id = ike_sa_id_create(spi, 0, FALSE);
618 this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
619 ike_sa_id);
620 spi = ike_sa_id->get_responder_spi(ike_sa_id);
621 ike_sa_id->destroy(ike_sa_id);
622 this->proposal->set_spi(this->proposal, spi);
623
624 sa_response->add_proposal(sa_response, this->proposal);
625 response->add_payload(response, (payload_t*)sa_response);
626 /* add nonce after sa payload */
627 response->add_payload(response, (payload_t *)nonce_response);
628 }
629
630 { /* process KE payload */
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
642 notify_group = this->connection->get_dh_group(this->connection);
643 this->logger->log(this->logger, AUDIT,
644 "request used inacceptable DH group %s, sending INVALID_KE_PAYLOAD with %s",
645 mapping_find(diffie_hellman_group_m, used_group),
646 mapping_find(diffie_hellman_group_m, notify_group));
647
648 notify_group = htons(notify_group);
649 notify_chunk.ptr = (u_int8_t*)&notify_group;
650 notify_chunk.len = sizeof(notify_group);
651 build_notify(INVALID_KE_PAYLOAD, notify_chunk, response, TRUE);
652 return DESTROY_ME;
653 }
654 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
655 ke_request->get_key_exchange_data(ke_request));
656
657 /* build response */
658 ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
659 response->add_payload(response, (payload_t*)ke_response);
660 }
661
662 status = switchto_new_sa(this, FALSE);
663 if (status != SUCCESS)
664 {
665 return status;
666 }
667
668 /* IKE_SA successfully created. If another transaction is already rekeying
669 * this SA, our lower nonce must be registered for a later nonce compare. */
670 {
671 private_rekey_ike_sa_t *other;
672
673 other = this->ike_sa->get_rekeying_transaction(this->ike_sa);
674 if (other)
675 {
676 /* store our lower nonce in the simultaneus transaction, we
677 * will later compare it against his nonces when we calls conclude().
678 * We do not adopt childrens yet, as we don't know if we'll win
679 * the race...
680 */
681 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
682 min(this->nonce_i.len, this->nonce_r.len)) < 0)
683 {
684 other->nonce_s = chunk_clone(this->nonce_i);
685 }
686 else
687 {
688 other->nonce_s = chunk_clone(this->nonce_r);
689 }
690 /* overwrite "other" in IKE_SA, allows "other" to access "this" */
691 this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
692 }
693 else
694 {
695 /* if we have no simultaneus transaction, we can safely adopt
696 * all children and complete. */
697 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
698 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
699 this->new_sa = NULL;
700 }
701 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
702 }
703
704 return SUCCESS;
705 }
706
707 /**
708 * Implementation of transaction_t.conclude
709 */
710 static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
711 transaction_t **next)
712 {
713 iterator_t *payloads;
714 host_t *me, *other;
715 sa_payload_t *sa_payload = NULL;
716 nonce_payload_t *nonce_payload = NULL;
717 ke_payload_t *ke_payload = NULL;
718 private_rekey_ike_sa_t *other_trans;
719 status_t status;
720
721 /* check message type */
722 if (response->get_exchange_type(response) != CREATE_CHILD_SA)
723 {
724 this->logger->log(this->logger, ERROR,
725 "CREATE_CHILD_SA response of invalid type, aborting");
726 return FAILED;
727 }
728
729 me = this->ike_sa->get_my_host(this->ike_sa);
730 other = this->ike_sa->get_other_host(this->ike_sa);
731
732 /* apply for notify processing */
733 this->next = next;
734
735 /* Iterate over all payloads to collect them */
736 payloads = response->get_payload_iterator(response);
737 while (payloads->has_next(payloads))
738 {
739 payload_t *payload;
740 payloads->current(payloads, (void**)&payload);
741 switch (payload->get_type(payload))
742 {
743 case SECURITY_ASSOCIATION:
744 sa_payload = (sa_payload_t*)payload;
745 break;
746 case NONCE:
747 nonce_payload = (nonce_payload_t*)payload;
748 break;
749 case KEY_EXCHANGE:
750 ke_payload = (ke_payload_t*)payload;
751 break;
752 case NOTIFY:
753 {
754 status = process_notifys(this, (notify_payload_t*)payload);
755 if (status != SUCCESS)
756 {
757 payloads->destroy(payloads);
758 return status;
759 }
760 break;
761 }
762 default:
763 {
764 this->logger->log(this->logger, ERROR, "ignoring %s payload (%d)",
765 mapping_find(payload_type_m, payload->get_type(payload)),
766 payload->get_type(payload));
767 break;
768 }
769 }
770 }
771 payloads->destroy(payloads);
772
773 if (!(sa_payload && nonce_payload && ke_payload))
774 {
775 this->logger->log(this->logger, AUDIT, "response message incomplete, rekeying IKE_SA failed");
776 return FAILED;
777 }
778
779 { /* process NONCE payload */
780 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
781 }
782
783 { /* process SA payload */
784 linked_list_t *proposal_list;
785 ike_sa_id_t *ike_sa_id;
786 u_int64_t spi;
787
788 proposal_list = sa_payload->get_proposals(sa_payload);
789 /* we have to re-check here if other's selection is valid */
790 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
791 destroy_proposal_list(proposal_list);
792
793 if (this->proposal == NULL)
794 {
795 this->logger->log(this->logger, AUDIT,
796 "no proposal selected, rekeying IKE_SA failed");
797 return FAILED;
798 }
799 spi = this->proposal->get_spi(this->proposal);
800 ike_sa_id = this->new_sa->get_id(this->new_sa);
801 ike_sa_id->set_responder_spi(ike_sa_id, spi);
802 }
803
804 { /* process KE payload */
805 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
806 ke_payload->get_key_exchange_data(ke_payload));
807 }
808
809 if (switchto_new_sa(this, TRUE) != SUCCESS)
810 {
811 /* this should not happen. But if, we destroy both SAs */
812 *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
813 return DESTROY_ME;
814 }
815
816 /* IKE_SA successfully created. If the other peer initiated rekeying
817 * in the meantime, we detect this by comparing the rekeying_transaction
818 * of the SA. If it changed, we are not alone. Then we must compare the nonces.
819 * If no simultaneous rekeying is going on, we just initiate the delete of
820 * the superseded SA. */
821 other_trans = this->ike_sa->get_rekeying_transaction(this->ike_sa);
822 this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
823
824 if (this->nonce_s.ptr)
825 { /* simlutaneous rekeying is going on, not so good */
826 chunk_t this_lowest;
827
828 /* first get our lowest nonce */
829 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
830 min(this->nonce_i.len, this->nonce_r.len)) < 0)
831 {
832 this_lowest = this->nonce_i;
833 }
834 else
835 {
836 this_lowest = this->nonce_r;
837 }
838 /* then compare against other lowest nonce */
839 if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
840 min(this_lowest.len, this->nonce_s.len)) < 0)
841 {
842 this->logger->log(this->logger, ERROR,
843 "detected simultaneous IKE_SA rekeying, deleting ours");
844 this->lost = TRUE;
845 }
846 else
847 {
848 this->logger->log(this->logger, ERROR,
849 "detected simultaneous IKE_SA rekeying, but ours is preferred");
850 }
851 if (this->lost)
852 {
853 /* the other has won, he gets our children */
854 other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
855 /* we have lost simlutaneous rekeying, delete the SA we just have created */
856 this->new_sa->delete(this->new_sa);
857 }
858 /* other trans' SA is still not checked in, so do it now. It's SA will get
859 * deleted by remote peer. */
860 charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
861 other_trans->new_sa = NULL;
862 }
863
864 if (!this->lost)
865 {
866 /* we have won. delete old IKE_SA, and migrate all children */
867 *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
868 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
869 }
870
871 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
872 this->new_sa = NULL;
873
874 return SUCCESS;
875 }
876
877 /**
878 * implements transaction_t.destroy
879 */
880 static void destroy(private_rekey_ike_sa_t *this)
881 {
882 if (this->new_sa)
883 {
884 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
885 this->new_sa);
886 }
887 DESTROY_IF(this->message);
888 DESTROY_IF(this->connection);
889 DESTROY_IF(this->diffie_hellman);
890 DESTROY_IF(this->proposal);
891 chunk_free(&this->nonce_i);
892 chunk_free(&this->nonce_r);
893 chunk_free(&this->nonce_s);
894 this->randomizer->destroy(this->randomizer);
895 free(this);
896 }
897
898 /*
899 * Described in header.
900 */
901 rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa)
902 {
903 private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t);
904
905 /* transaction interface functions */
906 this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
907 this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
908 this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
909 this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
910 this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
911 this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
912
913 /* public functions */
914 this->public.use_dh_group = (bool(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group;
915 this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel;
916
917 /* private data */
918 this->ike_sa = ike_sa;
919 this->message_id = 0;
920 this->message = NULL;
921 this->requested = 0;
922 this->nonce_i = CHUNK_INITIALIZER;
923 this->nonce_r = CHUNK_INITIALIZER;
924 this->nonce_s = CHUNK_INITIALIZER;
925 this->new_sa = NULL;
926 this->lost = FALSE;
927 this->connection = NULL;
928 this->randomizer = randomizer_create();
929 this->diffie_hellman = NULL;
930 this->proposal = NULL;
931 this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
932
933 return &this->public;
934 }