diff options
Diffstat (limited to 'src/charon/sa/tasks/child_create.c')
-rw-r--r-- | src/charon/sa/tasks/child_create.c | 158 |
1 files changed, 141 insertions, 17 deletions
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; |