Install negotiated IKEv1 CHILD_SA negotiated in quick mode
authorMartin Willi <martin@revosec.ch>
Tue, 22 Nov 2011 14:24:24 +0000 (15:24 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:30:48 +0000 (17:30 +0100)
src/libcharon/sa/tasks/quick_mode.c

index aa6e4e6..987b8d5 100644 (file)
@@ -18,6 +18,7 @@
 #include <string.h>
 
 #include <daemon.h>
 #include <string.h>
 
 #include <daemon.h>
+#include <sa/keymat_v1.h>
 #include <encoding/payloads/sa_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <encoding/payloads/id_payload.h>
 #include <encoding/payloads/sa_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <encoding/payloads/id_payload.h>
@@ -40,6 +41,11 @@ struct private_quick_mode_t {
        ike_sa_t *ike_sa;
 
        /**
        ike_sa_t *ike_sa;
 
        /**
+        * TRUE if we are initiating quick mode
+        */
+       bool initiator;
+
+       /**
         * Traffic selector of initiator
         */
        traffic_selector_t *tsi;
         * Traffic selector of initiator
         */
        traffic_selector_t *tsi;
@@ -60,6 +66,16 @@ struct private_quick_mode_t {
        chunk_t nonce_r;
 
        /**
        chunk_t nonce_r;
 
        /**
+        * Initiators ESP SPI
+        */
+       u_int32_t spi_i;
+
+       /**
+        * Responder ESP SPI
+        */
+       u_int32_t spi_r;
+
+       /**
         * selected CHILD_SA proposal
         */
        proposal_t *proposal;
         * selected CHILD_SA proposal
         */
        proposal_t *proposal;
@@ -74,6 +90,11 @@ struct private_quick_mode_t {
         */
        child_sa_t *child_sa;
 
         */
        child_sa_t *child_sa;
 
+       /**
+        * IKEv1 keymat
+        */
+       keymat_v1_t *keymat;
+
        /** states of quick mode */
        enum {
                QM_INIT,
        /** states of quick mode */
        enum {
                QM_INIT,
@@ -81,6 +102,100 @@ struct private_quick_mode_t {
        } state;
 };
 
        } state;
 };
 
+/**
+ * Install negotiated CHILD_SA
+ */
+static bool install(private_quick_mode_t *this)
+{
+       status_t status, status_i, status_o;
+       chunk_t encr_i, encr_r, integ_i, integ_r;
+       linked_list_t *tsi, *tsr;
+
+       this->child_sa->set_proposal(this->child_sa, this->proposal);
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
+       this->child_sa->set_mode(this->child_sa, MODE_TUNNEL);
+       this->child_sa->set_protocol(this->child_sa,
+                                                                this->proposal->get_protocol(this->proposal));
+
+       status_i = status_o = FAILED;
+       encr_i = encr_r = integ_i = integ_r = chunk_empty;
+       tsi = linked_list_create();
+       tsr = linked_list_create();
+       tsi->insert_last(tsi, this->tsi);
+       tsr->insert_last(tsr, this->tsr);
+       if (this->keymat->derive_child_keys(this->keymat, this->proposal, NULL,
+                       this->nonce_i, this->nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
+       {
+               if (this->initiator)
+               {
+                       status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
+                                                       this->spi_i, 0, TRUE, FALSE, tsi, tsr);
+                       status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
+                                                       this->spi_r, 0, FALSE, FALSE, tsi, tsr);
+               }
+               else
+               {
+                       status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
+                                                       this->spi_r, 0, TRUE, FALSE, tsr, tsi);
+                       status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
+                                                       this->spi_i, 0, FALSE, FALSE, tsr, tsi);
+               }
+       }
+       chunk_clear(&integ_i);
+       chunk_clear(&integ_r);
+       chunk_clear(&encr_i);
+       chunk_clear(&encr_r);
+
+       if (status_i != SUCCESS || status_o != SUCCESS)
+       {
+               DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel",
+                       (status_i != SUCCESS) ? "inbound " : "",
+                       (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
+                       (status_o != SUCCESS) ? "outbound " : "");
+               tsi->destroy(tsi);
+               tsr->destroy(tsr);
+               return FALSE;
+       }
+
+       if (this->initiator)
+       {
+               status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
+       }
+       else
+       {
+               status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
+       }
+       tsi->destroy(tsi);
+       tsr->destroy(tsr);
+       if (status != SUCCESS)
+       {
+               DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
+               return FALSE;
+       }
+
+       charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
+                                                       NULL, this->nonce_i, this->nonce_r);
+
+       /* add to IKE_SA, and remove from task */
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+       this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+
+       DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
+                "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+                this->child_sa->get_name(this->child_sa),
+                this->child_sa->get_reqid(this->child_sa),
+                ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
+                ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
+                this->child_sa->get_traffic_selectors(this->child_sa, TRUE),
+                this->child_sa->get_traffic_selectors(this->child_sa, FALSE));
+
+       charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
+
+       this->child_sa = NULL;
+
+       return TRUE;
+}
+
 METHOD(task_t, build_i, status_t,
        private_quick_mode_t *this, message_t *message)
 {
 METHOD(task_t, build_i, status_t,
        private_quick_mode_t *this, message_t *message)
 {
@@ -88,14 +203,35 @@ METHOD(task_t, build_i, status_t,
        {
                case QM_INIT:
                {
        {
                case QM_INIT:
                {
+                       enumerator_t *enumerator;
                        sa_payload_t *sa_payload;
                        nonce_payload_t *nonce_payload;
                        id_payload_t *id_payload;
                        traffic_selector_t *ts;
                        linked_list_t *list;
                        sa_payload_t *sa_payload;
                        nonce_payload_t *nonce_payload;
                        id_payload_t *id_payload;
                        traffic_selector_t *ts;
                        linked_list_t *list;
+                       proposal_t *proposal;
                        rng_t *rng;
 
                        rng_t *rng;
 
+                       this->child_sa = child_sa_create(
+                                                                       this->ike_sa->get_my_host(this->ike_sa),
+                                                                       this->ike_sa->get_other_host(this->ike_sa),
+                                                                       this->config, 0, FALSE);
+
                        list = this->config->get_proposals(this->config, TRUE);
                        list = this->config->get_proposals(this->config, TRUE);
+
+                       this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+                       if (!this->spi_i)
+                       {
+                               DBG1(DBG_IKE, "allocating SPI from kernel failed");
+                               return FAILED;
+                       }
+                       enumerator = list->create_enumerator(list);
+                       while (enumerator->enumerate(enumerator, &proposal))
+                       {
+                               proposal->set_spi(proposal, this->spi_i);
+                       }
+                       enumerator->destroy(enumerator);
+
                        sa_payload = sa_payload_create_from_proposal_list(
                                                                                                SECURITY_ASSOCIATION_V1, list);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
                        sa_payload = sa_payload_create_from_proposal_list(
                                                                                                SECURITY_ASSOCIATION_V1, list);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
@@ -234,6 +370,7 @@ METHOD(task_t, process_r, status_t,
                                DBG1(DBG_IKE, "no matching proposal found");
                                return FAILED;
                        }
                                DBG1(DBG_IKE, "no matching proposal found");
                                return FAILED;
                        }
+                       this->spi_i = this->proposal->get_spi(this->proposal);
 
                        nonce_payload = (nonce_payload_t*)message->get_payload(message,
                                                                                                                                   NONCE_V1);
 
                        nonce_payload = (nonce_payload_t*)message->get_payload(message,
                                                                                                                                   NONCE_V1);
@@ -246,12 +383,21 @@ METHOD(task_t, process_r, status_t,
 
                        /* TODO-IKEv1: verify HASH(1) */
 
 
                        /* TODO-IKEv1: verify HASH(1) */
 
+                       this->child_sa = child_sa_create(
+                                                                       this->ike_sa->get_my_host(this->ike_sa),
+                                                                       this->ike_sa->get_other_host(this->ike_sa),
+                                                                       this->config, 0, FALSE);
                        return NEED_MORE;
                }
                case QM_NEGOTIATED:
                {
                        /* TODO-IKEv1: verify HASH(3) */
 
                        return NEED_MORE;
                }
                case QM_NEGOTIATED:
                {
                        /* TODO-IKEv1: verify HASH(3) */
 
+                       if (!install(this))
+                       {
+                               return FAILED;
+                       }
+
                        return SUCCESS;
                }
                default:
                        return SUCCESS;
                }
                default:
@@ -271,6 +417,14 @@ METHOD(task_t, build_r, status_t,
                        id_payload_t *id_payload;
                        rng_t *rng;
 
                        id_payload_t *id_payload;
                        rng_t *rng;
 
+                       this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+                       if (!this->spi_r)
+                       {
+                               DBG1(DBG_IKE, "allocating SPI from kernel failed");
+                               return FAILED;
+                       }
+                       this->proposal->set_spi(this->proposal, this->spi_r);
+
                        sa_payload = sa_payload_create_from_proposal(
                                                                        SECURITY_ASSOCIATION_V1, this->proposal);
                        message->add_payload(message, &sa_payload->payload_interface);
                        sa_payload = sa_payload_create_from_proposal(
                                                                        SECURITY_ASSOCIATION_V1, this->proposal);
                        message->add_payload(message, &sa_payload->payload_interface);
@@ -370,6 +524,8 @@ METHOD(task_t, process_i, status_t,
                                DBG1(DBG_IKE, "no matching proposal found");
                                return FAILED;
                        }
                                DBG1(DBG_IKE, "no matching proposal found");
                                return FAILED;
                        }
+                       this->spi_r = this->proposal->get_spi(this->proposal);
+
                        nonce_payload = (nonce_payload_t*)message->get_payload(message,
                                                                                                                                   NONCE_V1);
                        if (!nonce_payload)
                        nonce_payload = (nonce_payload_t*)message->get_payload(message,
                                                                                                                                   NONCE_V1);
                        if (!nonce_payload)
@@ -381,6 +537,11 @@ METHOD(task_t, process_i, status_t,
 
                        /* TODO-IKEv1: verify HASH(2) */
 
 
                        /* TODO-IKEv1: verify HASH(2) */
 
+                       if (!install(this))
+                       {
+                               return FAILED;
+                       }
+
                        this->state = QM_NEGOTIATED;
                        return NEED_MORE;
                }
                        this->state = QM_NEGOTIATED;
                        return NEED_MORE;
                }
@@ -431,7 +592,9 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                        },
                },
                .ike_sa = ike_sa,
                        },
                },
                .ike_sa = ike_sa,
+               .initiator = config != NULL,
                .config = config,
                .config = config,
+               .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
                .state = QM_INIT,
        );
 
                .state = QM_INIT,
        );