6bc2f59905bbcb6cce4b8da8bf09ba7b687d04c6
[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(SIG_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(SIG_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(SIG_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(SIG_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(SIG_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(SIG_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(SIG_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(SIG_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(SIG_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 child_sa_t *child_sa;
410 sa_payload_t *sa_request = NULL;
411 nonce_payload_t *nonce_request = NULL;
412 ke_payload_t *ke_request = NULL;
413 nonce_payload_t *nonce_response;
414
415 /* check if we already have built a response (retransmission) */
416 if (this->message)
417 {
418 *result = this->message;
419 return SUCCESS;
420 }
421
422 me = this->ike_sa->get_my_host(this->ike_sa);
423 other = this->ike_sa->get_other_host(this->ike_sa);
424 this->message_id = request->get_message_id(request);
425
426 /* set up response */
427 response = message_create();
428 response->set_source(response, me->clone(me));
429 response->set_destination(response, other->clone(other));
430 response->set_exchange_type(response, CREATE_CHILD_SA);
431 response->set_request(response, FALSE);
432 response->set_message_id(response, this->message_id);
433 response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
434 this->message = response;
435 *result = response;
436
437 /* check message type */
438 if (request->get_exchange_type(request) != CREATE_CHILD_SA)
439 {
440 DBG1(SIG_DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
441 return FAILED;
442 }
443
444 /* if we already initiate a delete, we do not allow rekeying */
445 if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
446 {
447 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
448 DBG1(SIG_DBG_IKE, "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
449 return FAILED;
450 }
451
452 /* if we have a CHILD which is "half-open", we do not allow rekeying */
453 iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
454 while (iterator->iterate(iterator, (void**)&child_sa))
455 {
456 child_sa_state_t state = child_sa->get_state(child_sa);
457 if (state == CHILD_CREATED ||
458 state == CHILD_REKEYING ||
459 state == CHILD_DELETING)
460 {
461 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
462 DBG1(SIG_DBG_IKE, "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
463 iterator->destroy(iterator);
464 return FAILED;
465 }
466 }
467 iterator->destroy(iterator);
468
469 /* apply for notify processing */
470 this->next = next;
471
472
473 /* get a connection to replace current IKE_SA */
474 this->connection = charon->connections->get_connection_by_name(
475 charon->connections, this->ike_sa->get_name(this->ike_sa));
476 /* if connection lookup by name fails, try it with the hosts */
477 if (this->connection == NULL)
478 {
479 this->connection = charon->connections->get_connection_by_hosts(
480 charon->connections, me, other);
481 if (this->connection == NULL)
482 {
483 DBG1(SIG_DBG_IKE, "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN");
484 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
485 return FAILED;
486 }
487 }
488
489 /* Iterate over all payloads. */
490 payloads = request->get_payload_iterator(request);
491 while (payloads->has_next(payloads))
492 {
493 payload_t *payload;
494 payloads->current(payloads, (void**)&payload);
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(SIG_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_INITIALIZER, response, TRUE);
532 DBG1(SIG_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_INITIALIZER, 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(SIG_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(SIG_DBG_IKE, "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN");
565 build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, 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(SIG_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 = this->ike_sa->get_rekeying_transaction(this->ike_sa);
628 if (other)
629 {
630 /* store our lower nonce in the simultaneus transaction, we
631 * will later compare it against his nonces when we calls conclude().
632 * We do not adopt childrens yet, as we don't know if we'll win
633 * the race...
634 */
635 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
636 min(this->nonce_i.len, this->nonce_r.len)) < 0)
637 {
638 other->nonce_s = chunk_clone(this->nonce_i);
639 }
640 else
641 {
642 other->nonce_s = chunk_clone(this->nonce_r);
643 }
644 /* overwrite "other" in IKE_SA, allows "other" to access "this" */
645 this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
646 }
647 else
648 {
649 /* if we have no simultaneus transaction, we can safely adopt
650 * all children and complete. */
651 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
652 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
653 this->new_sa = NULL;
654 }
655 this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
656 }
657
658 return SUCCESS;
659 }
660
661 /**
662 * Implementation of transaction_t.conclude
663 */
664 static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
665 transaction_t **next)
666 {
667 iterator_t *payloads;
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(SIG_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->has_next(payloads))
691 {
692 payload_t *payload;
693 payloads->current(payloads, (void**)&payload);
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(SIG_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(SIG_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(SIG_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 both SAs */
763 *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
764 return DESTROY_ME;
765 }
766
767 /* IKE_SA successfully created. If the other peer initiated rekeying
768 * in the meantime, we detect this by comparing the rekeying_transaction
769 * of the SA. If it changed, we are not alone. Then we must compare the nonces.
770 * If no simultaneous rekeying is going on, we just initiate the delete of
771 * the superseded SA. */
772 other_trans = this->ike_sa->get_rekeying_transaction(this->ike_sa);
773 this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
774
775 if (this->nonce_s.ptr)
776 { /* simlutaneous rekeying is going on, not so good */
777 chunk_t this_lowest;
778
779 /* first get our lowest nonce */
780 if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
781 min(this->nonce_i.len, this->nonce_r.len)) < 0)
782 {
783 this_lowest = this->nonce_i;
784 }
785 else
786 {
787 this_lowest = this->nonce_r;
788 }
789 /* then compare against other lowest nonce */
790 if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
791 min(this_lowest.len, this->nonce_s.len)) < 0)
792 {
793 DBG1(SIG_DBG_IKE, "detected simultaneous IKE_SA rekeying, deleting ours");
794 this->lost = TRUE;
795 }
796 else
797 {
798 DBG1(SIG_DBG_IKE, "detected simultaneous IKE_SA rekeying, but ours is preferred");
799 }
800 if (this->lost)
801 {
802 /* the other has won, he gets our children */
803 other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
804 /* we have lost simlutaneous rekeying, delete the SA we just have created */
805 this->new_sa->delete(this->new_sa);
806 }
807 /* other trans' SA is still not checked in, so do it now. It's SA will get
808 * deleted by remote peer. */
809 charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
810 other_trans->new_sa = NULL;
811 }
812
813 if (!this->lost)
814 {
815 /* we have won. delete old IKE_SA, and migrate all children */
816 *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
817 this->new_sa->adopt_children(this->new_sa, this->ike_sa);
818 }
819
820 charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
821 this->new_sa = NULL;
822
823 return SUCCESS;
824 }
825
826 /**
827 * implements transaction_t.destroy
828 */
829 static void destroy(private_rekey_ike_sa_t *this)
830 {
831 if (this->new_sa)
832 {
833 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
834 this->new_sa);
835 }
836 DESTROY_IF(this->message);
837 DESTROY_IF(this->connection);
838 DESTROY_IF(this->diffie_hellman);
839 DESTROY_IF(this->proposal);
840 chunk_free(&this->nonce_i);
841 chunk_free(&this->nonce_r);
842 chunk_free(&this->nonce_s);
843 this->randomizer->destroy(this->randomizer);
844 free(this);
845 }
846
847 /*
848 * Described in header.
849 */
850 rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa)
851 {
852 private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t);
853
854 /* transaction interface functions */
855 this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
856 this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
857 this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
858 this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
859 this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
860 this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
861
862 /* public functions */
863 this->public.use_dh_group = (void(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group;
864 this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel;
865
866 /* private data */
867 this->ike_sa = ike_sa;
868 this->message_id = 0;
869 this->message = NULL;
870 this->requested = 0;
871 this->nonce_i = CHUNK_INITIALIZER;
872 this->nonce_r = CHUNK_INITIALIZER;
873 this->nonce_s = CHUNK_INITIALIZER;
874 this->new_sa = NULL;
875 this->lost = FALSE;
876 this->connection = NULL;
877 this->randomizer = randomizer_create();
878 this->diffie_hellman = NULL;
879 this->proposal = NULL;
880
881 return &this->public;
882 }