diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/charon/config/child_cfg.c | 67 | ||||
-rw-r--r-- | src/charon/config/child_cfg.h | 15 | ||||
-rw-r--r-- | src/charon/config/ike_cfg.c | 31 | ||||
-rw-r--r-- | src/charon/config/ike_cfg.h | 11 | ||||
-rw-r--r-- | src/charon/config/proposal.c | 78 | ||||
-rw-r--r-- | src/charon/config/proposal.h | 10 | ||||
-rw-r--r-- | src/charon/sa/task_manager.c | 6 | ||||
-rw-r--r-- | src/charon/sa/tasks/child_create.c | 158 | ||||
-rw-r--r-- | src/charon/sa/tasks/child_rekey.c | 7 | ||||
-rw-r--r-- | src/charon/sa/tasks/ike_init.c | 113 | ||||
-rw-r--r-- | src/charon/sa/tasks/ike_rekey.c | 55 |
11 files changed, 361 insertions, 190 deletions
diff --git a/src/charon/config/child_cfg.c b/src/charon/config/child_cfg.c index 2f5376753..440f7710b 100644 --- a/src/charon/config/child_cfg.c +++ b/src/charon/config/child_cfg.c @@ -120,9 +120,26 @@ static void add_proposal(private_child_cfg_t *this, proposal_t *proposal) } /** + * strip out DH groups from a proposal + */ +static void strip_dh_from_proposal(proposal_t *proposal) +{ + iterator_t *iterator; + algorithm_t *algo; + + iterator = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (iterator->iterate(iterator, (void**)&algo)) + { + iterator->remove(iterator); + free(algo); + } + iterator->destroy(iterator); +} + +/** * Implementation of child_cfg_t.get_proposals */ -static linked_list_t* get_proposals(private_child_cfg_t *this) +static linked_list_t* get_proposals(private_child_cfg_t *this, bool strip_dh) { iterator_t *iterator; proposal_t *current; @@ -132,6 +149,10 @@ static linked_list_t* get_proposals(private_child_cfg_t *this) while (iterator->iterate(iterator, (void**)¤t)) { current = current->clone(current); + if (strip_dh) + { + strip_dh_from_proposal(current); + } proposals->insert_last(proposals, current); } iterator->destroy(iterator); @@ -142,7 +163,8 @@ static linked_list_t* get_proposals(private_child_cfg_t *this) /** * Implementation of child_cfg_t.get_name */ -static proposal_t* select_proposal(private_child_cfg_t*this, linked_list_t *proposals) +static proposal_t* select_proposal(private_child_cfg_t*this, + linked_list_t *proposals, bool strip_dh) { iterator_t *stored_iter, *supplied_iter; proposal_t *stored, *supplied, *selected = NULL; @@ -156,7 +178,18 @@ static proposal_t* select_proposal(private_child_cfg_t*this, linked_list_t *prop supplied_iter->reset(supplied_iter); while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) { - selected = stored->select(stored, supplied); + if (strip_dh) + { + /* remove DH groups on a copy */ + stored = stored->clone(stored); + strip_dh_from_proposal(stored); + selected = stored->select(stored, supplied); + stored->destroy(stored); + } + else + { + selected = stored->select(stored, supplied); + } if (selected) { break; @@ -329,6 +362,29 @@ static mode_t get_mode(private_child_cfg_t *this) } /** + * Implementation of child_cfg_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_child_cfg_t *this) +{ + iterator_t *iterator; + proposal_t *proposal; + algorithm_t *algo; + diffie_hellman_group_t dh_group = MODP_NONE; + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &algo)) + { + dh_group = algo->algorithm; + break; + } + } + iterator->destroy(iterator); + return dh_group; +} + +/** * Implementation of child_cfg_t.get_name */ static void get_ref(private_child_cfg_t *this) @@ -369,12 +425,13 @@ child_cfg_t *child_cfg_create(char *name, u_int32_t lifetime, this->public.add_traffic_selector = (void (*)(child_cfg_t*,bool,traffic_selector_t*))add_traffic_selector; this->public.get_traffic_selectors = (linked_list_t*(*)(child_cfg_t*,bool,linked_list_t*,host_t*))get_traffic_selectors; this->public.add_proposal = (void (*) (child_cfg_t*,proposal_t*))add_proposal; - this->public.get_proposals = (linked_list_t* (*) (child_cfg_t*))get_proposals; - this->public.select_proposal = (proposal_t* (*) (child_cfg_t*,linked_list_t*))select_proposal; + this->public.get_proposals = (linked_list_t* (*) (child_cfg_t*,bool))get_proposals; + this->public.select_proposal = (proposal_t* (*) (child_cfg_t*,linked_list_t*,bool))select_proposal; this->public.get_updown = (char* (*) (child_cfg_t*))get_updown; this->public.get_hostaccess = (bool (*) (child_cfg_t*))get_hostaccess; this->public.get_mode = (mode_t (*) (child_cfg_t *))get_mode; this->public.get_lifetime = (u_int32_t (*) (child_cfg_t *,bool))get_lifetime; + this->public.get_dh_group = (diffie_hellman_group_t(*)(child_cfg_t*)) get_dh_group; this->public.get_ref = (void (*) (child_cfg_t*))get_ref; this->public.destroy = (void (*) (child_cfg_t*))destroy; diff --git a/src/charon/config/child_cfg.h b/src/charon/config/child_cfg.h index 6b2299089..e1a6553b4 100644 --- a/src/charon/config/child_cfg.h +++ b/src/charon/config/child_cfg.h @@ -96,9 +96,10 @@ struct child_cfg_t { * Resulting list and all of its proposals must be freed after use. * * @param this calling object + * @param strip_dh TRUE strip out diffie hellman groups * @return list of proposals */ - linked_list_t* (*get_proposals)(child_cfg_t *this); + linked_list_t* (*get_proposals)(child_cfg_t *this, bool strip_dh); /** * @brief Select a proposal from a supplied list. @@ -107,9 +108,11 @@ struct child_cfg_t { * * @param this calling object * @param proposals list from from wich proposals are selected + * @param strip_dh TRUE strip out diffie hellman groups * @return selected proposal, or NULL if nothing matches */ - proposal_t* (*select_proposal)(child_cfg_t*this, linked_list_t *proposals); + proposal_t* (*select_proposal)(child_cfg_t*this, linked_list_t *proposals, + bool strip_dh); /** * @brief Add a traffic selector to the config. @@ -189,6 +192,14 @@ struct child_cfg_t { mode_t (*get_mode) (child_cfg_t *this); /** + * @brief Get the DH group to use for CHILD_SA setup. + * + * @param this calling object + * @return dh group to use + */ + diffie_hellman_group_t (*get_dh_group)(child_cfg_t *this); + + /** * @brief Get a new reference. * * Get a new reference to this child_cfg by increasing diff --git a/src/charon/config/ike_cfg.c b/src/charon/config/ike_cfg.c index 61e62115d..35f46a6b7 100644 --- a/src/charon/config/ike_cfg.c +++ b/src/charon/config/ike_cfg.c @@ -176,36 +176,6 @@ static diffie_hellman_group_t get_dh_group(private_ike_cfg_t *this) } /** - * Implementation of ike_cfg_t.check_dh_group. - */ -static bool check_dh_group(private_ike_cfg_t *this, - diffie_hellman_group_t dh_group) -{ - iterator_t *prop_iter, *alg_iter; - proposal_t *proposal; - algorithm_t *algo; - - prop_iter = this->proposals->create_iterator(this->proposals, TRUE); - while (prop_iter->iterate(prop_iter, (void**)&proposal)) - { - alg_iter = proposal->create_algorithm_iterator(proposal, - DIFFIE_HELLMAN_GROUP); - while (alg_iter->iterate(alg_iter, (void**)&algo)) - { - if (algo->algorithm == dh_group) - { - prop_iter->destroy(prop_iter); - alg_iter->destroy(alg_iter); - return TRUE; - } - } - alg_iter->destroy(alg_iter); - } - prop_iter->destroy(prop_iter); - return FALSE; -} - -/** * Implementation of ike_cfg_t.get_ref. */ static void get_ref(private_ike_cfg_t *this) @@ -243,7 +213,6 @@ ike_cfg_t *ike_cfg_create(bool certreq, host_t *my_host, host_t *other_host) this->public.get_proposals = (linked_list_t*(*)(ike_cfg_t*))get_proposals; this->public.select_proposal = (proposal_t*(*)(ike_cfg_t*,linked_list_t*))select_proposal; this->public.get_dh_group = (diffie_hellman_group_t(*)(ike_cfg_t*)) get_dh_group; - this->public.check_dh_group = (bool(*)(ike_cfg_t*,diffie_hellman_group_t)) check_dh_group; this->public.get_ref = (void(*)(ike_cfg_t*))get_ref; this->public.destroy = (void(*)(ike_cfg_t*))destroy; diff --git a/src/charon/config/ike_cfg.h b/src/charon/config/ike_cfg.h index ccea0a527..bcdc90d9e 100644 --- a/src/charon/config/ike_cfg.h +++ b/src/charon/config/ike_cfg.h @@ -110,17 +110,6 @@ struct ike_cfg_t { diffie_hellman_group_t (*get_dh_group)(ike_cfg_t *this); /** - * @brief Check if a suggested DH group is acceptable. - * - * If we guess a wrong DH group for IKE_SA_INIT, the other - * peer will send us a offer. But is this acceptable for us? - * - * @param this calling object - * @return TRUE if group acceptable - */ - bool (*check_dh_group) (ike_cfg_t *this, diffie_hellman_group_t dh_group); - - /** * @brief Get a new reference to this ike_cfg. * * Get a new reference to this ike_cfg by increasing diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c index dcab8cbdd..fe113b1d8 100644 --- a/src/charon/config/proposal.c +++ b/src/charon/config/proposal.c @@ -144,39 +144,6 @@ static void add_algorithm(private_proposal_t *this, transform_type_t type, u_int } /** - * Implements proposal_t.get_algorithm. - */ -static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo) -{ - linked_list_t *list; - switch (type) - { - case ENCRYPTION_ALGORITHM: - list = this->encryption_algos; - break; - case INTEGRITY_ALGORITHM: - list = this->integrity_algos; - break; - case PSEUDO_RANDOM_FUNCTION: - list = this->prf_algos; - break; - case DIFFIE_HELLMAN_GROUP: - list = this->dh_groups; - break; - case EXTENDED_SEQUENCE_NUMBERS: - list = this->esns; - break; - default: - return FALSE; - } - if (list->get_first(list, (void**)algo) != SUCCESS) - { - return FALSE; - } - return TRUE; -} - -/** * Implements proposal_t.create_algorithm_iterator. */ static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform_type_t type) @@ -200,6 +167,50 @@ static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform } /** + * Implements proposal_t.get_algorithm. + */ +static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo) +{ + iterator_t *iterator = create_algorithm_iterator(this, type); + if (iterator->iterate(iterator, (void**)algo)) + { + iterator->destroy(iterator); + return TRUE; + } + iterator->destroy(iterator); + return FALSE; +} + +/** + * Implements proposal_t.has_dh_group + */ +static bool has_dh_group(private_proposal_t *this, diffie_hellman_group_t group) +{ + algorithm_t *current; + iterator_t *iterator; + bool result = FALSE; + + iterator = this->dh_groups->create_iterator(this->dh_groups, TRUE); + if (iterator->get_count(iterator)) + { + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->algorithm == group) + { + result = TRUE; + break; + } + } + } + else if (group == MODP_NONE) + { + result = TRUE; + } + iterator->destroy(iterator); + return result; +} + +/** * Find a matching alg/keysize in two linked lists */ static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add, u_int16_t *alg, size_t *key_size) @@ -530,6 +541,7 @@ proposal_t *proposal_create(protocol_id_t protocol) this->public.add_algorithm = (void (*)(proposal_t*,transform_type_t,u_int16_t,size_t))add_algorithm; this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,transform_type_t))create_algorithm_iterator; this->public.get_algorithm = (bool (*)(proposal_t*,transform_type_t,algorithm_t**))get_algorithm; + this->public.has_dh_group = (bool (*)(proposal_t*,diffie_hellman_group_t))has_dh_group; this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal; this->public.get_protocol = (protocol_id_t(*)(proposal_t*))get_protocol; this->public.set_spi = (void(*)(proposal_t*,u_int64_t))set_spi; diff --git a/src/charon/config/proposal.h b/src/charon/config/proposal.h index 4bee08e28..379550f44 100644 --- a/src/charon/config/proposal.h +++ b/src/charon/config/proposal.h @@ -164,7 +164,6 @@ struct proposal_t { * @brief Get the algorithm for a type to use. * * If there are multiple algorithms, only the first is returned. - * Result is still owned by proposal, do not modify! * * @param this calling object * @param type kind of algorithm @@ -172,6 +171,15 @@ struct proposal_t { * @return TRUE if algorithm of this kind available */ bool (*get_algorithm) (proposal_t *this, transform_type_t type, algorithm_t** algo); + + /** + * @brief Check if the proposal has a specific DH group. + * + * @param this calling object + * @param group group to check for + * @return TRUE if algorithm included + */ + bool (*has_dh_group) (proposal_t *this, diffie_hellman_group_t group); /** * @brief Compare two proposal, and select a matching subset. diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index 9633ba740..eb707c1f9 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -279,7 +279,6 @@ static status_t build_request(private_task_manager_t *this) if (activate_task(this, CHILD_CREATE)) { exchange = CREATE_CHILD_SA; - activate_task(this, IKE_CONFIG); break; } if (activate_task(this, CHILD_DELETE)) @@ -333,6 +332,11 @@ static status_t build_request(private_task_manager_t *this) case IKE_AUTHENTICATE: exchange = IKE_AUTH; break; + case CHILD_CREATE: + case CHILD_REKEY: + case IKE_REKEY: + exchange = CREATE_CHILD_SA; + break; default: continue; } diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c index 5ed9791ab..ac03a33fe 100644 --- a/src/charon/sa/tasks/child_create.c +++ b/src/charon/sa/tasks/child_create.c @@ -26,6 +26,7 @@ #include <daemon.h> #include <crypto/diffie_hellman.h> #include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ke_payload.h> #include <encoding/payloads/ts_payload.h> #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/notify_payload.h> @@ -89,6 +90,16 @@ struct private_child_create_t { linked_list_t *tsr; /** + * optional diffie hellman exchange + */ + diffie_hellman_t *dh; + + /** + * group used for DH exchange + */ + diffie_hellman_group_t dh_group; + + /** * mode the new CHILD_SA uses (transport/tunnel/beet) */ mode_t mode; @@ -162,21 +173,29 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host) } /** - * Install a CHILD_SA for usage + * Install a CHILD_SA for usage, return value: + * - FAILED: no acceptable proposal + * - INVALID_ARG: diffie hellman group inacceptable + * - NOT_FOUND: TS inacceptable */ -static status_t select_and_install(private_child_create_t *this) +static status_t select_and_install(private_child_create_t *this, bool no_dh) { prf_plus_t *prf_plus; status_t status; - chunk_t nonce_i, nonce_r, seed; + chunk_t nonce_i, nonce_r, secret, seed; linked_list_t *my_ts, *other_ts; host_t *me, *other, *other_vip, *my_vip; - if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL) + if (this->proposals == NULL) { - SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message"); + SIG(CHILD_UP_FAILED, "SA payload missing in message"); return FAILED; } + if (this->tsi == NULL || this->tsr == NULL) + { + SIG(CHILD_UP_FAILED, "TS payloads missing in message"); + return NOT_FOUND; + } if (this->initiator) { @@ -198,14 +217,34 @@ static status_t select_and_install(private_child_create_t *this) my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - this->proposal = this->config->select_proposal(this->config, this->proposals); - + this->proposal = this->config->select_proposal(this->config, this->proposals, + no_dh); if (this->proposal == NULL) { SIG(CHILD_UP_FAILED, "no acceptable proposal found"); return FAILED; } + if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) + { + algorithm_t *algo; + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, + &algo)) + { + u_int16_t group = algo->algorithm; + SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N", + diffie_hellman_group_names, this->dh_group, + diffie_hellman_group_names, group); + this->dh_group = group; + return INVALID_ARG; + } + else + { + SIG(CHILD_UP_FAILED, "no acceptable proposal found"); + return FAILED; + } + } + if (my_vip == NULL) { my_vip = me; @@ -228,7 +267,7 @@ static status_t select_and_install(private_child_create_t *this) if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) { SIG(CHILD_UP_FAILED, "no acceptable traffic selectors found"); - return FAILED; + return NOT_FOUND; } this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); @@ -275,7 +314,20 @@ static status_t select_and_install(private_child_create_t *this) } } - seed = chunk_cata("cc", nonce_i, nonce_r); + if (this->dh) + { + if (this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) + { + SIG(CHILD_UP_FAILED, "DH exchange incomplete"); + return FAILED; + } + DBG3(DBG_IKE, "DH secret %B", &secret); + seed = chunk_cata("mcc", secret, nonce_i, nonce_r); + } + else + { + seed = chunk_cata("cc", nonce_i, nonce_r); + } prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); if (this->initiator) @@ -293,7 +345,7 @@ static status_t select_and_install(private_child_create_t *this) if (status != SUCCESS) { SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel"); - return status; + return FAILED; } status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts, @@ -302,7 +354,7 @@ static status_t select_and_install(private_child_create_t *this) if (status != SUCCESS) { SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel"); - return status; + return NOT_FOUND; } /* add to IKE_SA, and remove from task */ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); @@ -317,8 +369,9 @@ static status_t select_and_install(private_child_create_t *this) static void build_payloads(private_child_create_t *this, message_t *message) { sa_payload_t *sa_payload; - ts_payload_t *ts_payload; nonce_payload_t *nonce_payload; + ke_payload_t *ke_payload; + ts_payload_t *ts_payload; /* add SA payload */ if (this->initiator) @@ -339,6 +392,13 @@ static void build_payloads(private_child_create_t *this, message_t *message) message->add_payload(message, (payload_t*)nonce_payload); } + /* diffie hellman exchange, if PFS enabled */ + if (this->dh) + { + ke_payload = ke_payload_create_from_diffie_hellman(this->dh); + message->add_payload(message, (payload_t*)ke_payload); + } + /* add TSi/TSr payloads */ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); message->add_payload(message, (payload_t*)ts_payload); @@ -367,6 +427,7 @@ static void process_payloads(private_child_create_t *this, message_t *message) iterator_t *iterator; payload_t *payload; sa_payload_t *sa_payload; + ke_payload_t *ke_payload; ts_payload_t *ts_payload; notify_payload_t *notify_payload; @@ -382,6 +443,19 @@ static void process_payloads(private_child_create_t *this, message_t *message) sa_payload = (sa_payload_t*)payload; this->proposals = sa_payload->get_proposals(sa_payload); break; + case KEY_EXCHANGE: + ke_payload = (ke_payload_t*)payload; + if (!this->initiator) + { + this->dh_group = ke_payload->get_dh_group_number(ke_payload); + this->dh = diffie_hellman_create(this->dh_group); + } + if (this->dh) + { + this->dh->set_other_public_value(this->dh, + ke_payload->get_key_exchange_data(ke_payload)); + } + break; case TRAFFIC_SELECTOR_INITIATOR: ts_payload = (ts_payload_t*)payload; this->tsi = ts_payload->get_traffic_selectors(ts_payload); @@ -429,6 +503,10 @@ static status_t build_i(private_child_create_t *this, message_t *message) message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } + if (this->dh_group == MODP_NONE) + { + this->dh_group = this->config->get_dh_group(this->config); + } break; case IKE_AUTH: if (!message->get_payload(message, ID_INITIATOR)) @@ -461,7 +539,8 @@ static status_t build_i(private_child_create_t *this, message_t *message) } this->tsr = this->config->get_traffic_selectors(this->config, FALSE, NULL, other); - this->proposals = this->config->get_proposals(this->config); + this->proposals = this->config->get_proposals(this->config, + this->dh_group == MODP_NONE); this->mode = this->config->get_mode(this->config); this->child_sa = child_sa_create(me, other, @@ -476,6 +555,11 @@ static status_t build_i(private_child_create_t *this, message_t *message) return FAILED; } + if (this->dh_group != MODP_NONE) + { + this->dh = diffie_hellman_create(this->dh_group); + } + build_payloads(this, message); this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); @@ -535,6 +619,8 @@ static status_t process_r(private_child_create_t *this, message_t *message) */ static status_t build_r(private_child_create_t *this, message_t *message) { + bool no_dh = TRUE; + switch (message->get_exchange_type(message)) { case IKE_SA_INIT: @@ -545,6 +631,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } + no_dh = FALSE; break; case IKE_AUTH: if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) @@ -578,10 +665,24 @@ static status_t build_r(private_child_create_t *this, message_t *message) this->config, this->reqid, this->ike_sa->is_natt_enabled(this->ike_sa)); - if (select_and_install(this) != SUCCESS) + switch (select_and_install(this, no_dh)) { - message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); - return SUCCESS; + case SUCCESS: + break; + case NOT_FOUND: + message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); + return SUCCESS; + case INVALID_ARG: + { + u_int16_t group = htons(this->dh_group); + message->add_notify(message, FALSE, INVALID_KE_PAYLOAD, + chunk_from_thing(group)); + return SUCCESS; + } + case FAILED: + default: + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; } build_payloads(this, message); @@ -598,6 +699,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) { iterator_t *iterator; payload_t *payload; + bool no_dh = TRUE; switch (message->get_exchange_type(message)) { @@ -605,6 +707,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) return get_nonce(message, &this->other_nonce); case CREATE_CHILD_SA: get_nonce(message, &this->other_nonce); + no_dh = FALSE; break; case IKE_AUTH: if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) @@ -642,6 +745,22 @@ static status_t process_i(private_child_create_t *this, message_t *message) /* an error in CHILD_SA creation is not critical */ return SUCCESS; } + case INVALID_KE_PAYLOAD: + { + chunk_t data; + diffie_hellman_group_t bad_group; + + bad_group = this->dh_group; + data = notify->get_notification_data(notify); + this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + DBG1(DBG_IKE, "peer didn't accept DH group %N, " + "it requested %N", diffie_hellman_group_names, + bad_group, diffie_hellman_group_names, this->dh_group); + + this->public.task.migrate(&this->public.task, this->ike_sa); + iterator->destroy(iterator); + return NEED_MORE; + } default: break; } @@ -651,7 +770,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) process_payloads(this, message); - if (select_and_install(this) == SUCCESS) + if (select_and_install(this, no_dh) == SUCCESS) { SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); } @@ -715,6 +834,7 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) } DESTROY_IF(this->child_sa); DESTROY_IF(this->proposal); + DESTROY_IF(this->dh); if (this->proposals) { this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); @@ -724,6 +844,7 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) this->proposals = NULL; this->tsi = NULL; this->tsr = NULL; + this->dh = NULL; this->child_sa = NULL; this->mode = MODE_TUNNEL; this->reqid = 0; @@ -750,6 +871,7 @@ static void destroy(private_child_create_t *this) DESTROY_IF(this->child_sa); } DESTROY_IF(this->proposal); + DESTROY_IF(this->dh); if (this->proposals) { this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); @@ -794,6 +916,8 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config) this->proposal = NULL; this->tsi = NULL; this->tsr = NULL; + this->dh = NULL; + this->dh_group = MODP_NONE; this->child_sa = NULL; this->mode = MODE_TUNNEL; this->reqid = 0; diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c index 1621357cc..4f3c69034 100644 --- a/src/charon/sa/tasks/child_rekey.c +++ b/src/charon/sa/tasks/child_rekey.c @@ -183,7 +183,12 @@ static status_t process_i(private_child_rekey_t *this, message_t *message) u_int32_t spi; child_sa_t *to_delete; - this->child_create->task.process(&this->child_create->task, message); + if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) + { + /* bad DH group while rekeying, try again */ + this->child_create->task.migrate(&this->child_create->task, this->ike_sa); + return NEED_MORE; + } if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) { /* establishing new child failed, reuse old. but not when we diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c index 8165a01a2..f78b5dd66 100644 --- a/src/charon/sa/tasks/ike_init.c +++ b/src/charon/sa/tasks/ike_init.c @@ -69,7 +69,7 @@ struct private_ike_init_t { /** * Diffie hellman object used to generate public DH value. */ - diffie_hellman_t *diffie_hellman; + diffie_hellman_t *dh; /** * nonce chosen by us @@ -151,7 +151,7 @@ static void build_payloads(private_ike_init_t *this, message_t *message) nonce_payload->set_nonce(nonce_payload, this->my_nonce); message->add_payload(message, (payload_t*)nonce_payload); - ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + ke_payload = ke_payload_create_from_diffie_hellman(this->dh); message->add_payload(message, (payload_t*)ke_payload); } @@ -183,33 +183,16 @@ static void process_payloads(private_ike_init_t *this, message_t *message) case KEY_EXCHANGE: { ke_payload_t *ke_payload = (ke_payload_t*)payload; - diffie_hellman_group_t dh_group; - chunk_t key_data; - dh_group = ke_payload->get_dh_group_number(ke_payload); - - if (this->initiator) - { - if (dh_group != this->dh_group) - { - DBG1(DBG_IKE, "received a DH group not requested (%N)", - diffie_hellman_group_names, dh_group); - break; - } - } - else + this->dh_group = ke_payload->get_dh_group_number(ke_payload); + if (!this->initiator) { - this->dh_group = dh_group; - if (!this->config->check_dh_group(this->config, dh_group)) - { - break; - } - this->diffie_hellman = diffie_hellman_create(dh_group); + this->dh = diffie_hellman_create(this->dh_group); } - if (this->diffie_hellman) + if (this->dh) { - key_data = ke_payload->get_key_exchange_data(ke_payload); - this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data); + this->dh->set_other_public_value(this->dh, + ke_payload->get_key_exchange_data(ke_payload)); } break; } @@ -246,11 +229,11 @@ static status_t build_i(private_ike_init_t *this, message_t *message) } /* if the DH group is set via use_dh_group(), we already have a DH object */ - if (!this->diffie_hellman) + if (!this->dh) { this->dh_group = this->config->get_dh_group(this->config); - this->diffie_hellman = diffie_hellman_create(this->dh_group); - if (this->diffie_hellman == NULL) + this->dh = diffie_hellman_create(this->dh_group); + if (this->dh == NULL) { SIG(IKE_UP_FAILED, "configured DH group %N not supported", diffie_hellman_group_names, this->dh_group); @@ -325,25 +308,29 @@ static status_t build_r(private_ike_init_t *this, message_t *message) return FAILED; } - if (this->diffie_hellman == NULL || - this->diffie_hellman->get_shared_secret(this->diffie_hellman, - &secret) != SUCCESS) + if (this->dh == NULL || + !this->proposal->has_dh_group(this->proposal, this->dh_group) || + this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) { - chunk_t chunk; - u_int16_t dh_enc; - - SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)", - diffie_hellman_group_names, this->dh_group); - this->dh_group = this->config->get_dh_group(this->config); - dh_enc = htons(this->dh_group); - chunk.ptr = (u_int8_t*)&dh_enc; - chunk.len = sizeof(dh_enc); - message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk); - DBG1(DBG_IKE, "requesting DH group %N", - diffie_hellman_group_names, this->dh_group); + algorithm_t *algo; + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, + &algo)) + { + u_int16_t group = algo->algorithm; + SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N", + diffie_hellman_group_names, this->dh_group, + diffie_hellman_group_names, group); + this->dh_group = group; + group = htons(group); + message->add_notify(message, FALSE, INVALID_KE_PAYLOAD, + chunk_from_thing(group)); + } + else + { + SIG(IKE_UP_FAILED, "no acceptable proposal found"); + } return FAILED; } - if (this->old_sa) { @@ -404,26 +391,20 @@ static status_t process_i(private_ike_init_t *this, message_t *message) case INVALID_KE_PAYLOAD: { chunk_t data; - diffie_hellman_group_t old_dh_group; + diffie_hellman_group_t bad_group; - old_dh_group = this->dh_group; + bad_group = this->dh_group; data = notify->get_notification_data(notify); this->dh_group = ntohs(*((u_int16_t*)data.ptr)); - - DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested" - " %N", diffie_hellman_group_names, old_dh_group, - diffie_hellman_group_names, this->dh_group); - if (!this->config->check_dh_group(this->config, this->dh_group)) - { - DBG1(DBG_IKE, "requested DH group %N not acceptable, " - "giving up", diffie_hellman_group_names, - this->dh_group); - iterator->destroy(iterator); - return FAILED; + DBG1(DBG_IKE, "peer didn't accept DH group %N, " + "it requested %N", diffie_hellman_group_names, + bad_group, diffie_hellman_group_names, this->dh_group); + + if (this->old_sa == NULL) + { /* reset the IKE_SA if we are not rekeying */ + this->ike_sa->reset(this->ike_sa); } - this->ike_sa->reset(this->ike_sa); - iterator->destroy(iterator); return NEED_MORE; } @@ -468,9 +449,9 @@ static status_t process_i(private_ike_init_t *this, message_t *message) return FAILED; } - if (this->diffie_hellman == NULL || - this->diffie_hellman->get_shared_secret(this->diffie_hellman, - &secret) != SUCCESS) + if (this->dh == NULL || + !this->proposal->has_dh_group(this->proposal, this->dh_group) || + this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) { SIG(IKE_UP_FAILED, "peers DH group selection invalid"); return FAILED; @@ -537,12 +518,12 @@ static chunk_t get_lower_nonce(private_ike_init_t *this) static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) { DESTROY_IF(this->proposal); - DESTROY_IF(this->diffie_hellman); + DESTROY_IF(this->dh); chunk_free(&this->other_nonce); this->ike_sa = ike_sa; this->proposal = NULL; - this->diffie_hellman = diffie_hellman_create(this->dh_group); + this->dh = diffie_hellman_create(this->dh_group); } /** @@ -551,7 +532,7 @@ static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) static void destroy(private_ike_init_t *this) { DESTROY_IF(this->proposal); - DESTROY_IF(this->diffie_hellman); + DESTROY_IF(this->dh); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); chunk_free(&this->cookie); @@ -583,7 +564,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) this->ike_sa = ike_sa; this->initiator = initiator; this->dh_group = MODP_NONE; - this->diffie_hellman = NULL; + this->dh = NULL; this->my_nonce = chunk_empty; this->other_nonce = chunk_empty; this->cookie = chunk_empty; diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c index 3c3eae5df..d54fc3524 100644 --- a/src/charon/sa/tasks/ike_rekey.c +++ b/src/charon/sa/tasks/ike_rekey.c @@ -75,14 +75,18 @@ static status_t build_i(private_ike_rekey_t *this, message_t *message) { peer_cfg_t *peer_cfg; - this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, - TRUE); - - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - this->new_sa->set_peer_cfg(this->new_sa, peer_cfg); - this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa); + /* create new SA only on first try */ + if (this->new_sa == NULL) + { + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + TRUE); + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + this->new_sa->set_peer_cfg(this->new_sa, peer_cfg); + this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa); + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + } this->ike_init->task.build(&this->ike_init->task, message); - this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); return NEED_MORE; } @@ -162,22 +166,29 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) job_t *job; ike_sa_id_t *to_delete; - if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED) + switch (this->ike_init->task.process(&this->ike_init->task, message)) { - /* rekeying failed, fallback to old SA */ - if (!(this->collision && - this->collision->get_type(this->collision) == IKE_DELETE)) - { - job_t *job; - u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER); - job = (job_t*)rekey_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - DBG1(DBG_IKE, "IKE_SA rekeying failed, " - "trying again in %d seconds", retry); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); - } - return SUCCESS; + case FAILED: + /* rekeying failed, fallback to old SA */ + if (!(this->collision && + this->collision->get_type(this->collision) == IKE_DELETE)) + { + job_t *job; + u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER); + job = (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), FALSE); + DBG1(DBG_IKE, "IKE_SA rekeying failed, " + "trying again in %d seconds", retry); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); + } + return SUCCESS; + case NEED_MORE: + /* bad dh group, try again */ + this->ike_init->task.migrate(&this->ike_init->task, this->new_sa); + return NEED_MORE; + default: + break; } this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); |