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