From 26b55dc6c84cbe165a47728d376c652f157723c2 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 18 Nov 2011 10:56:02 +0100 Subject: [PATCH] Implemented first two exchanges of Main Mode as initiator --- src/libcharon/sa/ike_sa.c | 70 ++++++---- src/libcharon/sa/task_manager_v1.c | 210 ++++++++++++++++++++++++++++- src/libcharon/sa/tasks/main_mode.c | 136 +++++++++++++++++-- 3 files changed, 376 insertions(+), 40 deletions(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 36ceea121..e7478bd38 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1129,31 +1130,39 @@ METHOD(ike_sa_t, initiate, status_t, set_condition(this, COND_ORIGINAL_INITIATOR, TRUE); - task = (task_t*)ike_vendor_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_init_create(&this->public, TRUE, NULL); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_natd_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_cert_pre_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_auth_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_cert_post_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_config_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - if (this->peer_cfg->use_mobike(this->peer_cfg)) + if (this->version == IKEV1) { - task = (task_t*)ike_mobike_create(&this->public, TRUE); + task = (task_t*)main_mode_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); } + else + { + task = (task_t*)ike_vendor_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_pre_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_post_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + if (this->peer_cfg->use_mobike(this->peer_cfg)) + { + task = (task_t*)ike_mobike_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + } #ifdef ME - task = (task_t*)ike_me_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_me_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); #endif /* ME */ + } } #ifdef ME @@ -1172,15 +1181,22 @@ METHOD(ike_sa_t, initiate, status_t, #endif /* ME */ { /* normal IKE_SA with CHILD_SA */ - task = (task_t*)child_create_create(&this->public, child_cfg, FALSE, - tsi, tsr); - child_cfg->destroy(child_cfg); - if (reqid) + if (this->version == IKEV2) { - child_create_t *child_create = (child_create_t*)task; - child_create->use_reqid(child_create, reqid); + task = (task_t*)child_create_create(&this->public, child_cfg, FALSE, + tsi, tsr); + if (reqid) + { + child_create_t *child_create = (child_create_t*)task; + child_create->use_reqid(child_create, reqid); + } + this->task_manager->queue_task(this->task_manager, task); } - this->task_manager->queue_task(this->task_manager, task); + else + { + /* TODO-IKEv1: create quick mode task */ + } + child_cfg->destroy(child_cfg); #ifdef ME if (this->peer_cfg->get_mediated_by(this->peer_cfg)) diff --git a/src/libcharon/sa/task_manager_v1.c b/src/libcharon/sa/task_manager_v1.c index 7eb81fc44..99263aa5d 100644 --- a/src/libcharon/sa/task_manager_v1.c +++ b/src/libcharon/sa/task_manager_v1.c @@ -154,10 +154,155 @@ METHOD(task_manager_t, retransmit, status_t, return FAILED; } +/** + * move a task of a specific type from the queue to the active list + */ +static bool activate_task(private_task_manager_t *this, task_type_t type) +{ + enumerator_t *enumerator; + task_t *task; + bool found = FALSE; + + enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, (void**)&task)) + { + if (task->get_type(task) == type) + { + DBG2(DBG_IKE, " activating %N task", task_type_names, type); + this->queued_tasks->remove_at(this->queued_tasks, enumerator); + this->active_tasks->insert_last(this->active_tasks, task); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + METHOD(task_manager_t, initiate, status_t, private_task_manager_t *this) { - return FAILED; + enumerator_t *enumerator; + task_t *task; + message_t *message; + host_t *me, *other; + status_t status; + exchange_type_t exchange = 0; + + if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) + { + DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress", + exchange_type_names, this->initiating.type); + /* do not initiate if we already have a message in the air */ + return SUCCESS; + } + + if (this->active_tasks->get_count(this->active_tasks) == 0) + { + DBG2(DBG_IKE, "activating new tasks"); + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_CREATED: + if (activate_task(this, MAIN_MODE)) + { + exchange = ID_PROT; + } + break; + default: + break; + } + } + else + { + DBG2(DBG_IKE, "reinitiating already active tasks"); + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void**)&task)) + { + DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); + switch (task->get_type(task)) + { + case MAIN_MODE: + exchange = ID_PROT; + break; + default: + continue; + } + break; + } + enumerator->destroy(enumerator); + } + + if (exchange == 0) + { + DBG2(DBG_IKE, "nothing to initiate"); + /* nothing to do yet... */ + return SUCCESS; + } + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + + message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); + if (exchange != ID_PROT) + { + /* TODO-IKEv1: Set random message id */ + } + message->set_source(message, me->clone(me)); + message->set_destination(message, other->clone(other)); + message->set_exchange_type(message, exchange); + this->initiating.type = exchange; + this->initiating.retransmitted = 0; + + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + { + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + } + /* FALL */ + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + message->destroy(message); + flush(this); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + + /* update exchange type if a task changed it */ + this->initiating.type = message->get_exchange_type(message); + + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->initiating.packet); + if (status != SUCCESS) + { + /* message generation failed. There is nothing more to do than to + * close the SA */ + message->destroy(message); + flush(this); + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + message->destroy(message); + + charon->sender->send(charon->sender, + this->initiating.packet->clone(this->initiating.packet)); + + return SUCCESS; } /** @@ -310,11 +455,62 @@ static status_t process_request(private_task_manager_t *this, return build_response(this, message); } +/** + * handle an incoming response message + */ +static status_t process_response(private_task_manager_t *this, + message_t *message) +{ + enumerator_t *enumerator; + task_t *task; + + if (message->get_exchange_type(message) != this->initiating.type) + { + DBG1(DBG_IKE, "received %N response, but expected %N", + exchange_type_names, message->get_exchange_type(message), + exchange_type_names, this->initiating.type); + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + /* FALL */ + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + enumerator->destroy(enumerator); + task->destroy(task); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + this->initiating.packet->destroy(this->initiating.packet); + this->initiating.packet = NULL; + + return initiate(this); +} + METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) { - /* TODO-IKEv1: detect request/response */ - if (TRUE) + if (this->active_tasks->get_count(this->active_tasks) == 0) { /* TODO-IKEv1: detect mainmode retransmission */ charon->bus->message(charon->bus, msg, TRUE); @@ -326,8 +522,12 @@ METHOD(task_manager_t, process_message, status_t, } else { - /* TODO-IKEv1: handle response */ - return DESTROY_ME; + charon->bus->message(charon->bus, msg, FALSE); + if (process_response(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } } return SUCCESS; } diff --git a/src/libcharon/sa/tasks/main_mode.c b/src/libcharon/sa/tasks/main_mode.c index fa0ee259c..d2e89210f 100644 --- a/src/libcharon/sa/tasks/main_mode.c +++ b/src/libcharon/sa/tasks/main_mode.c @@ -48,7 +48,7 @@ struct private_main_mode_t { /** * IKE config to establish */ - ike_cfg_t *config; + ike_cfg_t *ike_cfg; /** * selected IKE proposal @@ -87,8 +87,74 @@ struct private_main_mode_t { METHOD(task_t, build_i, status_t, private_main_mode_t *this, message_t *message) { - /* TODO-IKEv1: initiate mainmode */ - return FAILED; + switch (this->state) + { + case MM_INIT: + { + sa_payload_t *sa_payload; + linked_list_t *proposals; + + this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H", + this->ike_sa->get_name(this->ike_sa), + this->ike_sa->get_unique_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + proposals = this->ike_cfg->get_proposals(this->ike_cfg); + + sa_payload = sa_payload_create_from_proposal_list( + SECURITY_ASSOCIATION_V1, proposals); + proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); + + message->add_payload(message, &sa_payload->payload_interface); + + this->state = MM_SA; + return NEED_MORE; + } + case MM_SA: + { + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + u_int16_t group; + rng_t *rng; + + 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; + } + ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, + this->dh); + message->add_payload(message, &ke_payload->payload_interface); + + 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_i); + rng->destroy(rng); + + nonce_payload = nonce_payload_create(NONCE_V1); + nonce_payload->set_nonce(nonce_payload, this->nonce_i); + message->add_payload(message, &nonce_payload->payload_interface); + + this->state = MM_KE; + return NEED_MORE; + } + default: + return FAILED; + } } METHOD(task_t, process_r, status_t, @@ -102,7 +168,7 @@ METHOD(task_t, process_r, status_t, linked_list_t *list; sa_payload_t *sa_payload; - this->config = this->ike_sa->get_ike_cfg(this->ike_sa); + this->ike_cfg = 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); @@ -115,8 +181,8 @@ METHOD(task_t, process_r, status_t, return FAILED; } list = sa_payload->get_proposals(sa_payload); - this->proposal = this->config->select_proposal(this->config, - list, FALSE); + this->proposal = this->ike_cfg->select_proposal(this->ike_cfg, + list, FALSE); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { @@ -222,8 +288,62 @@ METHOD(task_t, build_r, status_t, METHOD(task_t, process_i, status_t, private_main_mode_t *this, message_t *message) { - /* TODO-IKEv1: process main mode as initiator */ - return FAILED; + switch (this->state) + { + case MM_SA: + { + linked_list_t *list; + sa_payload_t *sa_payload; + + 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->ike_cfg->select_proposal(this->ike_cfg, + list, FALSE); + list->destroy_offset(list, offsetof(proposal_t, destroy)); + if (!this->proposal) + { + DBG1(DBG_IKE, "no proposal found"); + return FAILED; + } + return NEED_MORE; + } + case MM_KE: + { + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + + 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); + 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_r = nonce_payload->get_nonce(nonce_payload); + /* TODO-IKEv1: verify nonce length */ + + return NEED_MORE; + } + default: + return FAILED; + } } METHOD(task_t, get_type, task_type_t,