10c80057386251d5843d535870937827b8d3dc03
[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.transaction);
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_reauth(this->connection),
370 this->connection->get_soft_lifetime(this->connection),
371 this->connection->get_hard_lifetime(this->connection));
372 return SUCCESS;
373 }
374
375 /**
376 * Build a notify message.
377 */
378 static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
379 {
380 notify_payload_t *notify;
381
382 if (flush_message)
383 {
384 payload_t *payload;
385 iterator_t *iterator = message->get_payload_iterator(message);
386 while (iterator->iterate(iterator, (void**)&payload))
387 {
388 payload->destroy(payload);
389 iterator->remove(iterator);
390 }
391 iterator->destroy(iterator);
392 }
393
394 notify = notify_payload_create();
395 notify->set_notify_type(notify, type);
396 notify->set_notification_data(notify, data);
397 message->add_payload(message, (payload_t*)notify);
398 }
399
400 /**
401 * Implementation of transaction_t.get_response.
402 */
403 static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
404 message_t **result, transaction_t **next)
405 {
406 host_t *me, *other;
407 message_t *response;
408 status_t status;
409 iterator_t *payloads, *iterator;
410 payload_t *payload;
411 child_sa_t *child_sa;
412 sa_payload_t *sa_request = NULL;
413 nonce_payload_t *nonce_request = NULL;
414 ke_payload_t *ke_request = NULL;
415 nonce_payload_t *nonce_response;
416
417 /* check if we already have built a response (retransmission) */
418 if (this->message)
419 {
420 *result = this->message;
421 return SUCCESS;
422 }
423
424 me = this->ike_sa->get_my_host(this->ike_sa);
425 other = this->ike_sa->get_other_host(this->ike_sa);
426 this->message_id = request->get_message_id(request);
427
428 /* set up response */
429 response = message_create();
430 response->set_source(response, me->clone(me));
431 response->set_destination(response, other->clone(other));
432 response->set_exchange_type(response, CREATE_CHILD_SA);
433 response->set_request(response, FALSE);
434 response->set_message_id(response, this->message_id);
435 response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
436 this->message = response;
437 *result = response;
438
439 /* check message type */
440 if (request->get_exchange_type(request) != CREATE_CHILD_SA)
441 {
442 DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
443 return FAILED;
444 }
445
446 /* if we already initiate a delete, we do not allow rekeying */
447 if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
448 {
449 build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
450 DBG1(DBG_IKE, "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
451 return FAILED;
452 }
453
454 /* if we have a CHILD which is "half-open", we do not allow rekeying */
455 iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
456 while (iterator->iterate(iterator, (void**)&child_sa))
457 {
458 child_sa_state_t state = child_sa->get_state(child_sa);
459 if (state == CHILD_CREATED ||
460 state == CHILD_REKEYING ||
461 state == CHILD_DELETING)
462 {
463 build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
464 DBG1(DBG_IKE, "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
465 iterator->destroy(iterator);
466 return FAILED;
467 }
468 }
469 iterator->destroy(iterator);
470
471 /* apply for notify processing */
472 this->next = next;
473
474
475 /* get a connection to replace current IKE_SA */
476 this->connection = charon->connections->get_connection_by_name(
477 charon->connections, this->ike_sa->get_name(this->ike_sa));
478 /* if connection lookup by name fails, try it with the hosts */
479 if (this->connection == NULL)
480 {
481 this->connection = charon->connections->get_connection_by_hosts(
482 charon->connections, me, other);
483 if (this->connection == NULL)
484 {
485 DBG1(DBG_IKE, "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN");
486 build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
487 return FAILED;
488 }
489 }
490
491 /* Iterate over all payloads. */
492 payloads = request->get_payload_iterator(request);
493 while (payloads->iterate(payloads, (void**)&payload))
494 {
495 switch (payload->get_type(payload))
496 {
497 case SECURITY_ASSOCIATION:
498 sa_request = (sa_payload_t*)payload;
499 break;
500 case NONCE:
501 nonce_request = (nonce_payload_t*)payload;
502 break;
503 case KEY_EXCHANGE:
504 {
505 ke_request = (ke_payload_t*)payload;
506 break;
507 }
508 case NOTIFY:
509 {
510 status = process_notifys(this, (notify_payload_t*)payload);
511 if (status != SUCCESS)
512 {
513 payloads->destroy(payloads);
514 return status;
515 }
516 break;
517 }
518 default:
519 {
520 DBG1(DBG_IKE, "ignoring %N payload",
521 payload_type_names, payload->get_type(payload));
522 break;
523 }
524 }
525 }
526 payloads->destroy(payloads);
527
528 /* check if we have all payloads */
529 if (!(sa_request && nonce_request && ke_request))
530 {
531 build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE);
532 DBG1(DBG_IKE, "request message incomplete, IKE_SA rekeying failed");
533 return FAILED;
534 }
535
536 { /* process nonce payload */
537 this->nonce_i = nonce_request->get_nonce(nonce_request);
538 if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
539 NONCE_SIZE, &this->nonce_r) != SUCCESS)
540 {
541 build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
542 return FAILED;
543 }
544 nonce_response = nonce_payload_create();
545 nonce_response->set_nonce(nonce_response, this->nonce_r);
546 }
547
548 { /* process SA payload */
549 linked_list_t *proposal_list;
550 sa_payload_t *sa_response;
551 u_int64_t spi;
552 ike_sa_id_t *ike_sa_id;
553
554 sa_response = sa_payload_create();
555 /* get proposals from request, and select one with ours */
556 proposal_list = sa_request->get_proposals(sa_request);
557 DBG2(DBG_IKE, "selecting proposals:");
558 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
559 proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
560
561 /* do we have a proposal? */
562 if (this->proposal == NULL)
563 {
564 DBG1(DBG_IKE, "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN");
565 build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
566 return FAILED;
567 }
568
569 /* create IKE_SA with new SPIs */
570 spi = this->proposal->get_spi(this->proposal);
571 ike_sa_id = ike_sa_id_create(spi, 0, FALSE);
572 this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
573 ike_sa_id);
574 spi = ike_sa_id->get_responder_spi(ike_sa_id);
575 ike_sa_id->destroy(ike_sa_id);
576 this->proposal->set_spi(this->proposal, spi);
577
578 sa_response->add_proposal(sa_response, this->proposal);
579 response->add_payload(response, (payload_t*)sa_response);
580 /* add nonce after sa payload */
581 response->add_payload(response, (payload_t *)nonce_response);
582 }
583
584 { /* process KE payload */
585 diffie_hellman_group_t used_group;
586 ke_payload_t *ke_response;
587
588 used_group = ke_request->get_dh_group_number(ke_request);
589
590 if (!this->connection->check_dh_group(this->connection, used_group) ||
591 (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
592 {
593 u_int16_t notify_group;
594 chunk_t notify_chunk;
595
596 notify_group = this->connection->get_dh_group(this->connection);
597 DBG1(DBG_IKE, "request used inacceptable DH group %N, sending "
598 "INVALID_KE_PAYLOAD with %N",
599 diffie_hellman_group_names, used_group,
600 diffie_hellman_group_names, notify_group);
601
602 notify_group = htons(notify_group);
603 notify_chunk.ptr = (u_int8_t*)&notify_group;
604 notify_chunk.len = sizeof(notify_group);
605 build_notify(INVALID_KE_PAYLOAD, notify_chunk, response, TRUE);
606 return FAILED;
607 }
608 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
609 ke_request->get_key_exchange_data(ke_request));
610
611 /* build response */
612 ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
613 response->add_payload(response, (payload_t*)ke_response);
614 }
615
616 status = switchto_new_sa(this, FALSE);
617 if (status != SUCCESS)
618 {
619 return status;
620 }
621
622 /* IKE_SA successfully created. If another transaction is already rekeying
623 * this SA, our lower nonce must be registered for a later nonce compare. */
624 {
625 private_rekey_ike_sa_t *other;
626
627 other = (private_rekey_ike_sa_t*)
628 this->ike_sa->get_rekeying_transaction(this->ike_sa);
629 if (other)
630 {
631 /* store our lower nonce in the simultaneus transaction, we
632 * will later compare it against his nonces when we calls conclude().
633 * We do not adopt childrens yet, as we don't know if we'll win
634 * the race...
635 */
636 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
637 min(this->nonce_i.len, this->nonce_r.len)) < 0)
638 {
639 other->nonce_s = chunk_clone(this->nonce_i);
640 }
641 else
642 {
643 other->nonce_s = chunk_clone(this->nonce_r);
644 }
645 /* overwrite "other" in IKE_SA, allows "other" to access "this" */
646 this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction);
647 }
648 else
649 {
650 /* if we have no simultaneus transaction, we can safely adopt
651 * all children and complete. */
652 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
653 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
654 this->new_sa = NULL;
655 }
656 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
657 }
658
659 return SUCCESS;
660 }
661
662 /**
663 * Implementation of transaction_t.conclude
664 */
665 static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
666 transaction_t **next)
667 {
668 iterator_t *payloads;
669 payload_t *payload;
670 host_t *me, *other;
671 sa_payload_t *sa_payload = NULL;
672 nonce_payload_t *nonce_payload = NULL;
673 ke_payload_t *ke_payload = NULL;
674 private_rekey_ike_sa_t *other_trans;
675 status_t status;
676
677 /* check message type */
678 if (response->get_exchange_type(response) != CREATE_CHILD_SA)
679 {
680 DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborting");
681 return FAILED;
682 }
683
684 me = this->ike_sa->get_my_host(this->ike_sa);
685 other = this->ike_sa->get_other_host(this->ike_sa);
686
687 /* apply for notify processing */
688 this->next = next;
689
690 /* Iterate over all payloads to collect them */
691 payloads = response->get_payload_iterator(response);
692 while (payloads->iterate(payloads, (void**)&payload))
693 {
694 switch (payload->get_type(payload))
695 {
696 case SECURITY_ASSOCIATION:
697 sa_payload = (sa_payload_t*)payload;
698 break;
699 case NONCE:
700 nonce_payload = (nonce_payload_t*)payload;
701 break;
702 case KEY_EXCHANGE:
703 ke_payload = (ke_payload_t*)payload;
704 break;
705 case NOTIFY:
706 {
707 status = process_notifys(this, (notify_payload_t*)payload);
708 if (status != SUCCESS)
709 {
710 payloads->destroy(payloads);
711 return status;
712 }
713 break;
714 }
715 default:
716 {
717 DBG1(DBG_IKE, "ignoring %N payload",
718 payload_type_names, payload->get_type(payload));
719 break;
720 }
721 }
722 }
723 payloads->destroy(payloads);
724
725 if (!(sa_payload && nonce_payload && ke_payload))
726 {
727 DBG1(DBG_IKE, "response message incomplete, rekeying IKE_SA failed");
728 return FAILED;
729 }
730
731 { /* process NONCE payload */
732 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
733 }
734
735 { /* process SA payload */
736 linked_list_t *proposal_list;
737 ike_sa_id_t *ike_sa_id;
738 u_int64_t spi;
739
740 proposal_list = sa_payload->get_proposals(sa_payload);
741 /* we have to re-check here if other's selection is valid */
742 this->proposal = this->connection->select_proposal(this->connection, proposal_list);
743 proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
744
745 if (this->proposal == NULL)
746 {
747 DBG1(DBG_IKE, "no proposal selected, rekeying IKE_SA failed");
748 return FAILED;
749 }
750 spi = this->proposal->get_spi(this->proposal);
751 ike_sa_id = this->new_sa->get_id(this->new_sa);
752 ike_sa_id->set_responder_spi(ike_sa_id, spi);
753 }
754
755 { /* process KE payload */
756 this->diffie_hellman->set_other_public_value(this->diffie_hellman,
757 ke_payload->get_key_exchange_data(ke_payload));
758 }
759
760 if (switchto_new_sa(this, TRUE) != SUCCESS)
761 {
762 /* this should not happen. But if, we destroy the new SAs */
763 this->new_sa->set_state(this->new_sa, IKE_REKEYING);
764 *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
765 return FAILED;
766 }
767
768 /* IKE_SA successfully created. If the other peer initiated rekeying
769 * in the meantime, we detect this by comparing the rekeying_transaction
770 * of the SA. If it changed, we are not alone. Then we must compare the nonces.
771 * If no simultaneous rekeying is going on, we just initiate the delete of
772 * the superseded SA. */
773 other_trans = (private_rekey_ike_sa_t*)
774 this->ike_sa->get_rekeying_transaction(this->ike_sa);
775 this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
776
777 if (this->nonce_s.ptr)
778 { /* simlutaneous rekeying is going on, not so good */
779 chunk_t this_lowest;
780
781 /* first get our lowest nonce */
782 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
783 min(this->nonce_i.len, this->nonce_r.len)) < 0)
784 {
785 this_lowest = this->nonce_i;
786 }
787 else
788 {
789 this_lowest = this->nonce_r;
790 }
791 /* then compare against other lowest nonce */
792 if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
793 min(this_lowest.len, this->nonce_s.len)) < 0)
794 {
795 DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, deleting ours");
796 this->lost = TRUE;
797 }
798 else
799 {
800 DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, but ours is preferred");
801 }
802 if (this->lost)
803 {
804 /* the other has won, he gets our children */
805 other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
806 /* we have lost simlutaneous rekeying, delete the SA we just have created */
807 this->new_sa->set_state(this->new_sa, IKE_REKEYING);
808 *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
809 }
810 else
811 {
812 other_trans->new_sa->set_state(other_trans->new_sa, IKE_REKEYING);
813 }
814 /* other trans' SA is still not checked in, so do it now. It's SA will get
815 * deleted by remote peer. */
816 charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
817 other_trans->new_sa = NULL;
818 }
819
820 if (!this->lost)
821 {
822 /* we have won. delete old IKE_SA, and migrate all children */
823 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
824 *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
825 }
826
827 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
828 this->new_sa = NULL;
829
830 return SUCCESS;
831 }
832
833 /**
834 * implements transaction_t.destroy
835 */
836 static void destroy(private_rekey_ike_sa_t *this)
837 {
838 if (this->new_sa)
839 {
840 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
841 this->new_sa);
842 }
843 DESTROY_IF(this->message);
844 DESTROY_IF(this->connection);
845 DESTROY_IF(this->diffie_hellman);
846 DESTROY_IF(this->proposal);
847 chunk_free(&this->nonce_i);
848 chunk_free(&this->nonce_r);
849 chunk_free(&this->nonce_s);
850 this->randomizer->destroy(this->randomizer);
851 free(this);
852 }
853
854 /*
855 * Described in header.
856 */
857 rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa)
858 {
859 private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t);
860
861 /* transaction interface functions */
862 this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
863 this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
864 this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
865 this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
866 this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
867 this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
868
869 /* public functions */
870 this->public.use_dh_group = (void(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group;
871 this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel;
872
873 /* private data */
874 this->ike_sa = ike_sa;
875 this->message_id = 0;
876 this->message = NULL;
877 this->requested = 0;
878 this->nonce_i = chunk_empty;
879 this->nonce_r = chunk_empty;
880 this->nonce_s = chunk_empty;
881 this->new_sa = NULL;
882 this->lost = FALSE;
883 this->connection = NULL;
884 this->randomizer = randomizer_create();
885 this->diffie_hellman = NULL;
886 this->proposal = NULL;
887
888 return &this->public;
889 }