2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2005-2008 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include <sa/keymat_v2.h>
24 #include <crypto/diffie_hellman.h>
25 #include <encoding/payloads/sa_payload.h>
26 #include <encoding/payloads/ke_payload.h>
27 #include <encoding/payloads/nonce_payload.h>
29 /** maximum retries to do with cookies/other dh groups */
32 typedef struct private_ike_init_t private_ike_init_t
;
35 * Private members of a ike_init_t task.
37 struct private_ike_init_t
{
40 * Public methods and task_t interface.
50 * Are we the initiator?
55 * IKE config to establish
60 * diffie hellman group to use
62 diffie_hellman_group_t dh_group
;
65 * diffie hellman key exchange
70 * Keymat derivation (from IKE_SA)
80 * nonce chosen by peer
85 * Negotiated proposal used for IKE_SA
90 * Old IKE_SA which gets rekeyed
95 * cookie received from responder
100 * retries done so far after failure (cookie or bad dh group)
106 * build the payloads for the message
108 static void build_payloads(private_ike_init_t
*this, message_t
*message
)
110 sa_payload_t
*sa_payload
;
111 ke_payload_t
*ke_payload
;
112 nonce_payload_t
*nonce_payload
;
113 linked_list_t
*proposal_list
;
115 proposal_t
*proposal
;
116 enumerator_t
*enumerator
;
118 id
= this->ike_sa
->get_id(this->ike_sa
);
120 this->config
= this->ike_sa
->get_ike_cfg(this->ike_sa
);
124 proposal_list
= this->config
->get_proposals(this->config
);
127 /* include SPI of new IKE_SA when we are rekeying */
128 enumerator
= proposal_list
->create_enumerator(proposal_list
);
129 while (enumerator
->enumerate(enumerator
, (void**)&proposal
))
131 proposal
->set_spi(proposal
, id
->get_initiator_spi(id
));
133 enumerator
->destroy(enumerator
);
136 sa_payload
= sa_payload_create_from_proposals_v2(proposal_list
);
137 proposal_list
->destroy_offset(proposal_list
, offsetof(proposal_t
, destroy
));
143 /* include SPI of new IKE_SA when we are rekeying */
144 this->proposal
->set_spi(this->proposal
, id
->get_responder_spi(id
));
146 sa_payload
= sa_payload_create_from_proposal_v2(this->proposal
);
148 message
->add_payload(message
, (payload_t
*)sa_payload
);
150 nonce_payload
= nonce_payload_create(NONCE
);
151 nonce_payload
->set_nonce(nonce_payload
, this->my_nonce
);
152 ke_payload
= ke_payload_create_from_diffie_hellman(KEY_EXCHANGE
, this->dh
);
155 { /* payload order differs if we are rekeying */
156 message
->add_payload(message
, (payload_t
*)nonce_payload
);
157 message
->add_payload(message
, (payload_t
*)ke_payload
);
161 message
->add_payload(message
, (payload_t
*)ke_payload
);
162 message
->add_payload(message
, (payload_t
*)nonce_payload
);
167 * Read payloads from message
169 static void process_payloads(private_ike_init_t
*this, message_t
*message
)
171 enumerator_t
*enumerator
;
174 enumerator
= message
->create_payload_enumerator(message
);
175 while (enumerator
->enumerate(enumerator
, &payload
))
177 switch (payload
->get_type(payload
))
179 case SECURITY_ASSOCIATION
:
181 sa_payload_t
*sa_payload
= (sa_payload_t
*)payload
;
182 linked_list_t
*proposal_list
;
185 proposal_list
= sa_payload
->get_proposals(sa_payload
);
186 private = this->ike_sa
->supports_extension(this->ike_sa
,
188 this->proposal
= this->config
->select_proposal(this->config
,
189 proposal_list
, private);
190 proposal_list
->destroy_offset(proposal_list
,
191 offsetof(proposal_t
, destroy
));
196 ke_payload_t
*ke_payload
= (ke_payload_t
*)payload
;
198 this->dh_group
= ke_payload
->get_dh_group_number(ke_payload
);
199 if (!this->initiator
)
201 this->dh
= this->keymat
->keymat
.create_dh(
202 &this->keymat
->keymat
, this->dh_group
);
206 this->dh
->set_other_public_value(this->dh
,
207 ke_payload
->get_key_exchange_data(ke_payload
));
213 nonce_payload_t
*nonce_payload
= (nonce_payload_t
*)payload
;
215 this->other_nonce
= nonce_payload
->get_nonce(nonce_payload
);
222 enumerator
->destroy(enumerator
);
225 METHOD(task_t
, build_i
, status_t
,
226 private_ike_init_t
*this, message_t
*message
)
230 this->config
= this->ike_sa
->get_ike_cfg(this->ike_sa
);
231 DBG0(DBG_IKE
, "initiating IKE_SA %s[%d] to %H",
232 this->ike_sa
->get_name(this->ike_sa
),
233 this->ike_sa
->get_unique_id(this->ike_sa
),
234 this->ike_sa
->get_other_host(this->ike_sa
));
235 this->ike_sa
->set_state(this->ike_sa
, IKE_CONNECTING
);
237 if (this->retry
>= MAX_RETRIES
)
239 DBG1(DBG_IKE
, "giving up after %d retries", MAX_RETRIES
);
243 /* if the DH group is set via use_dh_group(), we already have a DH object */
246 this->dh_group
= this->config
->get_dh_group(this->config
);
247 this->dh
= this->keymat
->keymat
.create_dh(&this->keymat
->keymat
,
251 DBG1(DBG_IKE
, "configured DH group %N not supported",
252 diffie_hellman_group_names
, this->dh_group
);
257 /* generate nonce only when we are trying the first time */
258 if (this->my_nonce
.ptr
== NULL
)
260 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
263 DBG1(DBG_IKE
, "error generating nonce");
266 rng
->allocate_bytes(rng
, NONCE_SIZE
, &this->my_nonce
);
270 if (this->cookie
.ptr
)
272 message
->add_notify(message
, FALSE
, COOKIE
, this->cookie
);
275 build_payloads(this, message
);
279 chunk_t connect_id
= this->ike_sa
->get_connect_id(this->ike_sa
);
282 message
->add_notify(message
, FALSE
, ME_CONNECTID
, connect_id
);
290 METHOD(task_t
, process_r
, status_t
,
291 private_ike_init_t
*this, message_t
*message
)
295 this->config
= this->ike_sa
->get_ike_cfg(this->ike_sa
);
296 DBG0(DBG_IKE
, "%H is initiating an IKE_SA", message
->get_source(message
));
297 this->ike_sa
->set_state(this->ike_sa
, IKE_CONNECTING
);
299 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
302 DBG1(DBG_IKE
, "error generating nonce");
305 rng
->allocate_bytes(rng
, NONCE_SIZE
, &this->my_nonce
);
310 notify_payload_t
*notify
= message
->get_notify(message
, ME_CONNECTID
);
313 chunk_t connect_id
= notify
->get_notification_data(notify
);
314 DBG2(DBG_IKE
, "received ME_CONNECTID %#B", &connect_id
);
315 charon
->connect_manager
->stop_checks(charon
->connect_manager
,
321 process_payloads(this, message
);
327 * Derive the keymat for the IKE_SA
329 static bool derive_keys(private_ike_init_t
*this,
330 chunk_t nonce_i
, chunk_t nonce_r
)
332 keymat_v2_t
*old_keymat
;
333 pseudo_random_function_t prf_alg
= PRF_UNDEFINED
;
334 chunk_t skd
= chunk_empty
;
337 id
= this->ike_sa
->get_id(this->ike_sa
);
340 /* rekeying: Include old SKd, use old PRF, apply SPI */
341 old_keymat
= (keymat_v2_t
*)this->old_sa
->get_keymat(this->old_sa
);
342 prf_alg
= old_keymat
->get_skd(old_keymat
, &skd
);
345 id
->set_responder_spi(id
, this->proposal
->get_spi(this->proposal
));
349 id
->set_initiator_spi(id
, this->proposal
->get_spi(this->proposal
));
352 if (!this->keymat
->derive_ike_keys(this->keymat
, this->proposal
, this->dh
,
353 nonce_i
, nonce_r
, id
, prf_alg
, skd
))
357 charon
->bus
->ike_keys(charon
->bus
, this->ike_sa
, this->dh
,
358 nonce_i
, nonce_r
, this->old_sa
);
362 METHOD(task_t
, build_r
, status_t
,
363 private_ike_init_t
*this, message_t
*message
)
365 /* check if we have everything we need */
366 if (this->proposal
== NULL
||
367 this->other_nonce
.len
== 0 || this->my_nonce
.len
== 0)
369 DBG1(DBG_IKE
, "received proposals inacceptable");
370 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
373 this->ike_sa
->set_proposal(this->ike_sa
, this->proposal
);
375 if (this->dh
== NULL
||
376 !this->proposal
->has_dh_group(this->proposal
, this->dh_group
))
380 if (this->proposal
->get_algorithm(this->proposal
, DIFFIE_HELLMAN_GROUP
,
383 DBG1(DBG_IKE
, "DH group %N inacceptable, requesting %N",
384 diffie_hellman_group_names
, this->dh_group
,
385 diffie_hellman_group_names
, group
);
386 this->dh_group
= group
;
387 group
= htons(group
);
388 message
->add_notify(message
, FALSE
, INVALID_KE_PAYLOAD
,
389 chunk_from_thing(group
));
393 DBG1(DBG_IKE
, "no acceptable proposal found");
398 if (!derive_keys(this, this->other_nonce
, this->my_nonce
))
400 DBG1(DBG_IKE
, "key derivation failed");
401 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
404 build_payloads(this, message
);
408 METHOD(task_t
, process_i
, status_t
,
409 private_ike_init_t
*this, message_t
*message
)
411 enumerator_t
*enumerator
;
414 /* check for erronous notifies */
415 enumerator
= message
->create_payload_enumerator(message
);
416 while (enumerator
->enumerate(enumerator
, &payload
))
418 if (payload
->get_type(payload
) == NOTIFY
)
420 notify_payload_t
*notify
= (notify_payload_t
*)payload
;
421 notify_type_t type
= notify
->get_notify_type(notify
);
425 case INVALID_KE_PAYLOAD
:
428 diffie_hellman_group_t bad_group
;
430 bad_group
= this->dh_group
;
431 data
= notify
->get_notification_data(notify
);
432 this->dh_group
= ntohs(*((u_int16_t
*)data
.ptr
));
433 DBG1(DBG_IKE
, "peer didn't accept DH group %N, "
434 "it requested %N", diffie_hellman_group_names
,
435 bad_group
, diffie_hellman_group_names
, this->dh_group
);
437 if (this->old_sa
== NULL
)
438 { /* reset the IKE_SA if we are not rekeying */
439 this->ike_sa
->reset(this->ike_sa
);
442 enumerator
->destroy(enumerator
);
446 case NAT_DETECTION_SOURCE_IP
:
447 case NAT_DETECTION_DESTINATION_IP
:
448 /* skip, handled in ike_natd_t */
450 case MULTIPLE_AUTH_SUPPORTED
:
451 /* handled in ike_auth_t */
455 chunk_free(&this->cookie
);
456 this->cookie
= chunk_clone(notify
->get_notification_data(notify
));
457 this->ike_sa
->reset(this->ike_sa
);
458 enumerator
->destroy(enumerator
);
459 DBG2(DBG_IKE
, "received %N notify", notify_type_names
, type
);
467 DBG1(DBG_IKE
, "received %N notify error",
468 notify_type_names
, type
);
469 enumerator
->destroy(enumerator
);
472 DBG2(DBG_IKE
, "received %N notify",
473 notify_type_names
, type
);
479 enumerator
->destroy(enumerator
);
481 process_payloads(this, message
);
483 /* check if we have everything */
484 if (this->proposal
== NULL
||
485 this->other_nonce
.len
== 0 || this->my_nonce
.len
== 0)
487 DBG1(DBG_IKE
, "peers proposal selection invalid");
490 this->ike_sa
->set_proposal(this->ike_sa
, this->proposal
);
492 if (this->dh
== NULL
||
493 !this->proposal
->has_dh_group(this->proposal
, this->dh_group
))
495 DBG1(DBG_IKE
, "peer DH group selection invalid");
499 if (!derive_keys(this, this->my_nonce
, this->other_nonce
))
501 DBG1(DBG_IKE
, "key derivation failed");
507 METHOD(task_t
, get_type
, task_type_t
,
508 private_ike_init_t
*this)
510 return TASK_IKE_INIT
;
513 METHOD(task_t
, migrate
, void,
514 private_ike_init_t
*this, ike_sa_t
*ike_sa
)
516 DESTROY_IF(this->proposal
);
517 chunk_free(&this->other_nonce
);
519 this->ike_sa
= ike_sa
;
520 this->keymat
= (keymat_v2_t
*)ike_sa
->get_keymat(ike_sa
);
521 this->proposal
= NULL
;
522 DESTROY_IF(this->dh
);
523 this->dh
= this->keymat
->keymat
.create_dh(&this->keymat
->keymat
,
527 METHOD(task_t
, destroy
, void,
528 private_ike_init_t
*this)
530 DESTROY_IF(this->dh
);
531 DESTROY_IF(this->proposal
);
532 chunk_free(&this->my_nonce
);
533 chunk_free(&this->other_nonce
);
534 chunk_free(&this->cookie
);
538 METHOD(ike_init_t
, get_lower_nonce
, chunk_t
,
539 private_ike_init_t
*this)
541 if (memcmp(this->my_nonce
.ptr
, this->other_nonce
.ptr
,
542 min(this->my_nonce
.len
, this->other_nonce
.len
)) < 0)
544 return this->my_nonce
;
548 return this->other_nonce
;
553 * Described in header.
555 ike_init_t
*ike_init_create(ike_sa_t
*ike_sa
, bool initiator
, ike_sa_t
*old_sa
)
557 private_ike_init_t
*this;
562 .get_type
= _get_type
,
566 .get_lower_nonce
= _get_lower_nonce
,
569 .initiator
= initiator
,
570 .dh_group
= MODP_NONE
,
571 .keymat
= (keymat_v2_t
*)ike_sa
->get_keymat(ike_sa
),
577 this->public.task
.build
= _build_i
;
578 this->public.task
.process
= _process_i
;
582 this->public.task
.build
= _build_r
;
583 this->public.task
.process
= _process_r
;
586 return &this->public;