Implemented first two exchanges of Main Mode as initiator
authorMartin Willi <martin@revosec.ch>
Fri, 18 Nov 2011 09:56:02 +0000 (10:56 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:30:43 +0000 (17:30 +0100)
src/libcharon/sa/ike_sa.c
src/libcharon/sa/task_manager_v1.c
src/libcharon/sa/tasks/main_mode.c

index 36ceea1..e7478bd 100644 (file)
@@ -44,6 +44,7 @@
 #include <sa/tasks/child_create.h>
 #include <sa/tasks/child_delete.h>
 #include <sa/tasks/child_rekey.h>
+#include <sa/tasks/main_mode.h>
 #include <processing/jobs/retransmit_job.h>
 #include <processing/jobs/delete_ike_sa_job.h>
 #include <processing/jobs/send_dpd_job.h>
@@ -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))
index 7eb81fc..99263aa 100644 (file)
@@ -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;
 }
index fa0ee25..d2e8921 100644 (file)
@@ -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,