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