diff options
Diffstat (limited to 'src/libcharon')
-rw-r--r-- | src/libcharon/sa/tasks/main_mode.c | 168 |
1 files changed, 143 insertions, 25 deletions
diff --git a/src/libcharon/sa/tasks/main_mode.c b/src/libcharon/sa/tasks/main_mode.c index e8bd62554..530568a8d 100644 --- a/src/libcharon/sa/tasks/main_mode.c +++ b/src/libcharon/sa/tasks/main_mode.c @@ -54,6 +54,34 @@ struct private_main_mode_t { * selected IKE proposal */ proposal_t *proposal; + + /** + * DH exchange + */ + diffie_hellman_t *dh; + + /** + * Received public DH value from peer + */ + chunk_t dh_value; + + /** + * Initiators nonce + */ + chunk_t nonce_i; + + /** + * Responder nonce + */ + chunk_t nonce_r; + + /** states of main mode */ + enum { + MM_INIT, + MM_SA, + MM_KE, + MM_ID, + } state; }; METHOD(task_t, build_i, status_t, @@ -66,43 +94,128 @@ METHOD(task_t, build_i, status_t, METHOD(task_t, process_r, status_t, private_main_mode_t *this, message_t *message) { - this->config = this->ike_sa->get_ike_cfg(this->ike_sa); - DBG0(DBG_IKE, "%H is initiating a Main Mode", message->get_source(message)); - this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); - - if (!this->proposal) + switch (this->state) { - linked_list_t *list; - sa_payload_t *sa_payload; - - sa_payload = (sa_payload_t*)message->get_payload(message, - SECURITY_ASSOCIATION_V1); - if (!sa_payload) + case MM_INIT: { - DBG1(DBG_IKE, "SA payload missing"); - return FAILED; - } - list = sa_payload->get_proposals(sa_payload); - this->proposal = this->config->select_proposal(this->config, list, FALSE); - if (!this->proposal) + linked_list_t *list; + sa_payload_t *sa_payload; + + this->config = this->ike_sa->get_ike_cfg(this->ike_sa); + DBG0(DBG_IKE, "%H is initiating a Main Mode", + message->get_source(message)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + sa_payload = (sa_payload_t*)message->get_payload(message, + SECURITY_ASSOCIATION_V1); + if (!sa_payload) + { + DBG1(DBG_IKE, "SA payload missing"); + return FAILED; + } + list = sa_payload->get_proposals(sa_payload); + this->proposal = this->config->select_proposal(this->config, + list, FALSE); + if (!this->proposal) + { + DBG1(DBG_IKE, "no proposal found"); + return FAILED; + } + this->state = MM_SA; + return NEED_MORE; + } + case MM_SA: { - DBG1(DBG_IKE, "no proposal found"); - return FAILED; + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + u_int16_t group; + + ke_payload = (ke_payload_t*)message->get_payload(message, + KEY_EXCHANGE_V1); + if (!ke_payload) + { + DBG1(DBG_IKE, "KE payload missing"); + return FAILED; + } + this->dh_value = ke_payload->get_key_exchange_data(ke_payload); + this->dh_value = chunk_clone(this->dh_value); + + if (!this->proposal->get_algorithm(this->proposal, + DIFFIE_HELLMAN_GROUP, &group, NULL)) + { + DBG1(DBG_IKE, "DH group selection failed"); + return FAILED; + } + this->dh = lib->crypto->create_dh(lib->crypto, group); + if (!this->dh) + { + DBG1(DBG_IKE, "negotiated DH group not supported"); + return FAILED; + } + this->dh->set_other_public_value(this->dh, this->dh_value); + + + nonce_payload = (nonce_payload_t*)message->get_payload(message, + NONCE_V1); + if (!nonce_payload) + { + DBG1(DBG_IKE, "Nonce payload missing"); + return FAILED; + } + this->nonce_i = nonce_payload->get_nonce(nonce_payload); + /* TODO-IKEv1: verify nonce length */ + + this->state = MM_KE; + return NEED_MORE; } + default: + return FAILED; } - return NEED_MORE; } METHOD(task_t, build_r, status_t, private_main_mode_t *this, message_t *message) { - sa_payload_t *sa_payload; + switch (this->state) + { + case MM_SA: + { + sa_payload_t *sa_payload; + + sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1, + this->proposal); + message->add_payload(message, &sa_payload->payload_interface); + return NEED_MORE; + } + case MM_KE: + { + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + rng_t *rng; + + ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, + this->dh); + message->add_payload(message, &ke_payload->payload_interface); - sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1, - this->proposal); - message->add_payload(message, &sa_payload->payload_interface); - return NEED_MORE; + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_IKE, "no RNG found to create nonce"); + return FAILED; + } + /* TODO-IKEv1: nonce size? */ + rng->allocate_bytes(rng, 20, &this->nonce_r); + rng->destroy(rng); + + nonce_payload = nonce_payload_create(NONCE_V1); + nonce_payload->set_nonce(nonce_payload, this->nonce_r); + message->add_payload(message, &nonce_payload->payload_interface); + return NEED_MORE; + } + default: + return FAILED; + } } METHOD(task_t, process_i, status_t, @@ -128,6 +241,10 @@ METHOD(task_t, destroy, void, private_main_mode_t *this) { DESTROY_IF(this->proposal); + DESTROY_IF(this->dh); + free(this->dh_value.ptr); + free(this->nonce_i.ptr); + free(this->nonce_r.ptr); free(this); } @@ -148,6 +265,7 @@ main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator) }, .ike_sa = ike_sa, .initiator = initiator, + .state = MM_INIT, ); if (initiator) |