Implemented quick mode protocol handling, no CHILD_SA or HASH payloads yet
authorMartin Willi <martin@revosec.ch>
Mon, 21 Nov 2011 16:56:39 +0000 (17:56 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:30:47 +0000 (17:30 +0100)
src/libcharon/sa/task_manager_v1.c
src/libcharon/sa/tasks/quick_mode.c

index 9be034f..1764a80 100644 (file)
@@ -454,7 +454,8 @@ static status_t process_request(private_task_manager_t *this,
                                /* task completed, remove it */
                                this->passive_tasks->remove_at(this->passive_tasks, enumerator);
                                task->destroy(task);
-                               break;
+                               enumerator->destroy(enumerator);
+                               return SUCCESS;
                        case NEED_MORE:
                                /* processed, but task needs at least another call to build() */
                                break;
index 5db02b2..aa6e4e6 100644 (file)
@@ -18,6 +18,9 @@
 #include <string.h>
 
 #include <daemon.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
 
 typedef struct private_quick_mode_t private_quick_mode_t;
 
@@ -74,31 +77,316 @@ struct private_quick_mode_t {
        /** states of quick mode */
        enum {
                QM_INIT,
+               QM_NEGOTIATED,
        } state;
 };
 
 METHOD(task_t, build_i, status_t,
        private_quick_mode_t *this, message_t *message)
 {
-       return NEED_MORE;
+       switch (this->state)
+       {
+               case QM_INIT:
+               {
+                       sa_payload_t *sa_payload;
+                       nonce_payload_t *nonce_payload;
+                       id_payload_t *id_payload;
+                       traffic_selector_t *ts;
+                       linked_list_t *list;
+                       rng_t *rng;
+
+                       list = this->config->get_proposals(this->config, TRUE);
+                       sa_payload = sa_payload_create_from_proposal_list(
+                                                                                               SECURITY_ASSOCIATION_V1, list);
+                       list->destroy_offset(list, offsetof(proposal_t, destroy));
+                       message->add_payload(message, &sa_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;
+                       }
+                       rng->allocate_bytes(rng, NONCE_SIZE, &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);
+
+                       list = this->config->get_traffic_selectors(this->config, TRUE, NULL,
+                                                                       this->ike_sa->get_my_host(this->ike_sa));
+                       if (list->get_first(list, (void**)&ts) != SUCCESS)
+                       {
+                               list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+                               DBG1(DBG_IKE, "traffic selector missing");
+                               return FAILED;
+                       }
+                       id_payload = id_payload_create_from_ts(ts);
+                       this->tsi = ts->clone(ts);
+                       list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+                       message->add_payload(message, &id_payload->payload_interface);
+
+                       list = this->config->get_traffic_selectors(this->config, FALSE, NULL,
+                                                                       this->ike_sa->get_other_host(this->ike_sa));
+                       if (list->get_first(list, (void**)&ts) != SUCCESS)
+                       {
+                               list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+                               DBG1(DBG_IKE, "traffic selector missing");
+                               return FAILED;
+                       }
+                       id_payload = id_payload_create_from_ts(ts);
+                       this->tsr = ts->clone(ts);
+                       list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+                       message->add_payload(message, &id_payload->payload_interface);
+
+                       /* TODO-IKEv1: Add HASH(1) */
+
+                       return NEED_MORE;
+               }
+               case QM_NEGOTIATED:
+               {
+                       /* TODO-IKEv1: Send HASH(3) */
+                       return SUCCESS;
+               }
+               default:
+                       return FAILED;
+       }
 }
 
 METHOD(task_t, process_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
-       return NEED_MORE;
+       switch (this->state)
+       {
+               case QM_INIT:
+               {
+                       sa_payload_t *sa_payload;
+                       nonce_payload_t *nonce_payload;
+                       id_payload_t *id_payload;
+                       payload_t *payload;
+                       linked_list_t *tsi, *tsr, *list;
+                       peer_cfg_t *peer_cfg;
+                       host_t *me, *other;
+                       enumerator_t *enumerator;
+                       bool first = TRUE;
+
+                       enumerator = message->create_payload_enumerator(message);
+                       while (enumerator->enumerate(enumerator, &payload))
+                       {
+                               if (payload->get_type(payload) == ID_V1)
+                               {
+                                       id_payload = (id_payload_t*)payload;
+
+                                       if (first)
+                                       {
+                                               this->tsi = id_payload->get_ts(id_payload);
+                                               first = FALSE;
+                                       }
+                                       else
+                                       {
+                                               this->tsr = id_payload->get_ts(id_payload);
+                                               break;
+                                       }
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+
+                       /* TODO-IKEv1: create host2host TS if ID payloads missing */
+
+                       me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
+                       if (!me)
+                       {
+                               me = this->ike_sa->get_my_host(this->ike_sa);
+                       }
+                       other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
+                       if (!other)
+                       {
+                               other = this->ike_sa->get_other_host(this->ike_sa);
+                       }
+                       peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+                       tsi = linked_list_create();
+                       tsr = linked_list_create();
+                       tsi->insert_last(tsi, this->tsi);
+                       tsr->insert_last(tsr, this->tsr);
+                       this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
+                                                                                                         me, other);
+                       tsi->destroy(tsi);
+                       tsr->destroy(tsr);
+                       if (!this->config)
+                       {
+                               DBG1(DBG_IKE, "no child config found");
+                               return FAILED;
+                       }
+
+                       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, TRUE, FALSE);
+                       list->destroy_offset(list, offsetof(proposal_t, destroy));
+                       if (!this->proposal)
+                       {
+                               DBG1(DBG_IKE, "no matching proposal found");
+                               return FAILED;
+                       }
+
+                       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 HASH(1) */
+
+                       return NEED_MORE;
+               }
+               case QM_NEGOTIATED:
+               {
+                       /* TODO-IKEv1: verify HASH(3) */
+
+                       return SUCCESS;
+               }
+               default:
+                       return FAILED;
+       }
 }
 
 METHOD(task_t, build_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
-       return SUCCESS;
+       switch (this->state)
+       {
+               case QM_INIT:
+               {
+                       sa_payload_t *sa_payload;
+                       nonce_payload_t *nonce_payload;
+                       id_payload_t *id_payload;
+                       rng_t *rng;
+
+                       sa_payload = sa_payload_create_from_proposal(
+                                                                       SECURITY_ASSOCIATION_V1, this->proposal);
+                       message->add_payload(message, &sa_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;
+                       }
+                       rng->allocate_bytes(rng, NONCE_SIZE, &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);
+
+                       id_payload = id_payload_create_from_ts(this->tsi);
+                       message->add_payload(message, &id_payload->payload_interface);
+                       id_payload = id_payload_create_from_ts(this->tsr);
+                       message->add_payload(message, &id_payload->payload_interface);
+
+                       /* TODO-IKEv1: add HASH(2) */
+
+                       this->state = QM_NEGOTIATED;
+                       return NEED_MORE;
+               }
+               default:
+                       return FAILED;
+       }
 }
 
 METHOD(task_t, process_i, status_t,
        private_quick_mode_t *this, message_t *message)
 {
-       return SUCCESS;
+       switch (this->state)
+       {
+               case QM_INIT:
+               {
+                       sa_payload_t *sa_payload;
+                       nonce_payload_t *nonce_payload;
+                       id_payload_t *id_payload;
+                       payload_t *payload;
+                       traffic_selector_t *tsi = NULL, *tsr = NULL;
+                       linked_list_t *list;
+                       enumerator_t *enumerator;
+                       bool first = TRUE;
+
+                       enumerator = message->create_payload_enumerator(message);
+                       while (enumerator->enumerate(enumerator, &payload))
+                       {
+                               if (payload->get_type(payload) == ID_V1)
+                               {
+                                       id_payload = (id_payload_t*)payload;
+
+                                       if (first)
+                                       {
+                                               tsi = id_payload->get_ts(id_payload);
+                                               first = FALSE;
+                                       }
+                                       else
+                                       {
+                                               tsr = id_payload->get_ts(id_payload);
+                                               break;
+                                       }
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+
+                       /* TODO-IKEv1: create host2host TS if ID payloads missing */
+
+                       if (!tsr->is_contained_in(tsr, this->tsr) ||
+                               !tsi->is_contained_in(tsi, this->tsi))
+                       {
+                               tsi->destroy(tsi);
+                               tsr->destroy(tsr);
+                               DBG1(DBG_IKE, "TS mismatch");
+                               return FAILED;
+                       }
+                       this->tsi->destroy(this->tsi);
+                       this->tsr->destroy(this->tsr);
+                       this->tsi = tsi;
+                       this->tsr = tsr;
+
+                       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, TRUE, FALSE);
+                       list->destroy_offset(list, offsetof(proposal_t, destroy));
+                       if (!this->proposal)
+                       {
+                               DBG1(DBG_IKE, "no matching proposal found");
+                               return FAILED;
+                       }
+                       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 HASH(2) */
+
+                       this->state = QM_NEGOTIATED;
+                       return NEED_MORE;
+               }
+               default:
+                       return FAILED;
+       }
 }
 
 METHOD(task_t, get_type, task_type_t,
@@ -143,8 +431,6 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                        },
                },
                .ike_sa = ike_sa,
-               .tsi = tsi ? tsi->clone(tsi) : NULL,
-               .tsr = tsr ? tsr->clone(tsr) : NULL,
                .config = config,
                .state = QM_INIT,
        );