Use the vararg list constructor in quick mode task
[strongswan.git] / src / libcharon / sa / ikev1 / tasks / quick_mode.c
old mode 100755 (executable)
new mode 100644 (file)
index 30dc956..39fbd59
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2011 Martin Willi
  * Copyright (C) 2011 revosec AG
  *
@@ -81,6 +84,16 @@ struct private_quick_mode_t {
        u_int32_t spi_r;
 
        /**
+        * Initiators IPComp CPI
+        */
+       u_int16_t cpi_i;
+
+       /**
+        * Responders IPComp CPI
+        */
+       u_int16_t cpi_r;
+
+       /**
         * selected CHILD_SA proposal
         */
        proposal_t *proposal;
@@ -154,7 +167,7 @@ static void schedule_inactivity_timeout(private_quick_mode_t *this)
        if (timeout)
        {
                close_ike = lib->settings->get_bool(lib->settings,
-                                                                               "charon.inactivity_close_ike", FALSE);
+                                                               "%s.inactivity_close_ike", FALSE, charon->name);
                lib->scheduler->schedule_job(lib->scheduler, (job_t*)
                                inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
                                                                          timeout, close_ike), timeout);
@@ -162,6 +175,62 @@ static void schedule_inactivity_timeout(private_quick_mode_t *this)
 }
 
 /**
+ * Check if we have a an address pool configured
+ */
+static bool have_pool(ike_sa_t *ike_sa)
+{
+       enumerator_t *enumerator;
+       peer_cfg_t *peer_cfg;
+       char *pool;
+       bool found = FALSE;
+
+       peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+       if (peer_cfg)
+       {
+               enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+               if (enumerator->enumerate(enumerator, &pool))
+               {
+                       found = TRUE;
+               }
+               enumerator->destroy(enumerator);
+       }
+       return found;
+}
+
+/**
+ * Get host to use for dynamic traffic selectors
+ */
+static host_t *get_dynamic_host(ike_sa_t *ike_sa, bool local)
+{
+       enumerator_t *enumerator;
+       host_t *host;
+
+       enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
+       if (!enumerator->enumerate(enumerator, &host))
+       {
+               if (local)
+               {
+                       host = ike_sa->get_my_host(ike_sa);
+               }
+               else
+               {
+                       if (have_pool(ike_sa))
+                       {
+                               /* we have an IP address pool, but didn't negotiate a
+                                * virtual IP. */
+                               host = NULL;
+                       }
+                       else
+                       {
+                               host = ike_sa->get_other_host(ike_sa);
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       return host;
+}
+
+/**
  * Install negotiated CHILD_SA
  */
 static bool install(private_quick_mode_t *this)
@@ -174,15 +243,23 @@ static bool install(private_quick_mode_t *this)
        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, this->mode);
+
+       if (this->cpi_i && this->cpi_r)
+       {       /* DEFLATE is the only transform we currently support */
+               this->child_sa->set_ipcomp(this->child_sa, IPCOMP_DEFLATE);
+       }
+       else
+       {
+               this->cpi_i = this->cpi_r = 0;
+       }
+
        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->clone(this->tsi));
-       tsr->insert_last(tsr, this->tsr->clone(this->tsr));
+       tsi = linked_list_create_with_items(this->tsi->clone(this->tsi), NULL);
+       tsr = linked_list_create_with_items(this->tsr->clone(this->tsr), NULL);
        if (this->initiator)
        {
                charon->bus->narrow(charon->bus, this->child_sa,
@@ -191,7 +268,7 @@ static bool install(private_quick_mode_t *this)
        else
        {
                charon->bus->narrow(charon->bus, this->child_sa,
-                                                       NARROW_RESPONDER, tsr, tsi);
+                                                       NARROW_RESPONDER_POST, tsr, tsi);
        }
        if (tsi->get_count(tsi) == 0 || tsr->get_count(tsr) == 0)
        {
@@ -208,16 +285,16 @@ static bool install(private_quick_mode_t *this)
                if (this->initiator)
                {
                        status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
-                                                       this->spi_i, 0, TRUE, FALSE, tsi, tsr);
+                                                       this->spi_i, this->cpi_i, 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);
+                                                       this->spi_r, this->cpi_r, 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);
+                                                       this->spi_r, this->cpi_r, 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);
+                                                       this->spi_i, this->cpi_i, FALSE, FALSE, tsr, tsi);
                }
        }
        chunk_clear(&integ_i);
@@ -297,16 +374,21 @@ static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
                                          message_t *message)
 {
        nonce_payload_t *nonce_payload;
-       rng_t *rng;
+       nonce_gen_t *nonceg;
 
-       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
-       if (!rng)
+       nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+       if (!nonceg)
+       {
+               DBG1(DBG_IKE, "no nonce generator found to create nonce");
+               return FALSE;
+       }
+       if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, nonce))
        {
-               DBG1(DBG_IKE, "no RNG found to create nonce");
+               DBG1(DBG_IKE, "nonce allocation failed");
+               nonceg->destroy(nonceg);
                return FALSE;
        }
-       rng->allocate_bytes(rng, NONCE_SIZE, nonce);
-       rng->destroy(rng);
+       nonceg->destroy(nonceg);
 
        nonce_payload = nonce_payload_create(NONCE_V1);
        nonce_payload->set_nonce(nonce_payload, *nonce);
@@ -371,25 +453,12 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
 {
        traffic_selector_t *ts;
        linked_list_t *list;
-       host_t *host;
 
-       host = this->ike_sa->get_virtual_ip(this->ike_sa, local);
-       if (!host)
-       {
-               if (local)
-               {
-                       host = this->ike_sa->get_my_host(this->ike_sa);
-               }
-               else
-               {
-                       host = this->ike_sa->get_other_host(this->ike_sa);
-               }
-       }
        list = this->config->get_traffic_selectors(this->config, local,
-                                                                                          supplied, host);
+                                                       supplied, get_dynamic_host(this->ike_sa, local));
        if (list->get_first(list, (void**)&ts) == SUCCESS)
        {
-               if (list->get_count(list) > 1)
+               if (this->initiator && list->get_count(list) > 1)
                {
                        DBG1(DBG_IKE, "configuration has more than one %s traffic selector,"
                                 " using first only", local ? "local" : "remote");
@@ -495,6 +564,15 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
                tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
                                                        hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0);
        }
+       if (!this->initiator && this->mode == MODE_TRANSPORT && this->udp &&
+          (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr)))
+       {       /* change TS in case of a NAT in transport mode */
+               DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT",
+                        tsi, tsr);
+               tsi->set_address(tsi, hsi);
+               tsr->set_address(tsr, hsr);
+       }
+
        if (this->initiator)
        {
                /* check if peer selection valid */
@@ -605,6 +683,8 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
        this->ike_sa->queue_task(this->ike_sa,
                                                (task_t*)informational_create(this->ike_sa, notify));
        /* cancel all active/passive tasks in favour of informational */
+       this->ike_sa->flush_queue(this->ike_sa,
+                                       this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
        return ALREADY_DONE;
 }
 
@@ -622,12 +702,35 @@ METHOD(task_t, build_i, status_t,
                        diffie_hellman_group_t group;
 
                        this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
+                       this->mode = this->config->get_mode(this->config);
                        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, this->reqid, this->udp);
 
-                       list = this->config->get_proposals(this->config, FALSE);
+                       if (this->udp && this->mode == MODE_TRANSPORT)
+                       {
+                               /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
+                               add_nat_oa_payloads(this, message);
+                       }
+
+                       if (this->config->use_ipcomp(this->config))
+                       {
+                               if (this->udp)
+                               {
+                                       DBG1(DBG_IKE, "IPComp is not supported if either peer is "
+                                                "natted, IPComp disabled");
+                               }
+                               else
+                               {
+                                       this->cpi_i = this->child_sa->alloc_cpi(this->child_sa);
+                                       if (!this->cpi_i)
+                                       {
+                                               DBG1(DBG_IKE, "unable to allocate a CPI from kernel, "
+                                                        "IPComp disabled");
+                                       }
+                               }
+                       }
 
                        this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
                        if (!this->spi_i)
@@ -635,6 +738,8 @@ METHOD(task_t, build_i, status_t,
                                DBG1(DBG_IKE, "allocating SPI from kernel failed");
                                return FAILED;
                        }
+
+                       list = this->config->get_proposals(this->config, FALSE);
                        enumerator = list->create_enumerator(list);
                        while (enumerator->enumerate(enumerator, &proposal))
                        {
@@ -642,17 +747,10 @@ METHOD(task_t, build_i, status_t,
                        }
                        enumerator->destroy(enumerator);
 
-                       this->mode = this->config->get_mode(this->config);
-                       if (this->udp && this->mode == MODE_TRANSPORT)
-                       {
-                               /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
-                               add_nat_oa_payloads(this, message);
-                       }
-
                        get_lifetimes(this);
                        sa_payload = sa_payload_create_from_proposals_v1(list,
                                                                this->lifetime, this->lifebytes, AUTH_NONE,
-                                                               this->mode, this->udp);
+                                                               this->mode, this->udp, this->cpi_i);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
                        message->add_payload(message, &sa_payload->payload_interface);
 
@@ -674,12 +772,16 @@ METHOD(task_t, build_i, status_t,
                                }
                                add_ke(this, message);
                        }
-                       this->tsi = select_ts(this, TRUE, NULL);
-                       this->tsr = select_ts(this, FALSE, NULL);
-                       tsi = linked_list_create();
-                       tsr = linked_list_create();
-                       tsi->insert_last(tsi, this->tsi);
-                       tsr->insert_last(tsr, this->tsr);
+                       if (!this->tsi)
+                       {
+                               this->tsi = select_ts(this, TRUE, NULL);
+                       }
+                       if (!this->tsr)
+                       {
+                               this->tsr = select_ts(this, FALSE, NULL);
+                       }
+                       tsi = linked_list_create_with_items(this->tsi, NULL);
+                       tsr = linked_list_create_with_items(this->tsr, NULL);
                        this->tsi = this->tsr = NULL;
                        charon->bus->narrow(charon->bus, this->child_sa,
                                                                NARROW_INITIATOR_PRE_AUTH, tsi, tsr);
@@ -785,33 +887,32 @@ METHOD(task_t, process_r, status_t,
                case QM_INIT:
                {
                        sa_payload_t *sa_payload;
-                       linked_list_t *tsi, *tsr, *list;
+                       linked_list_t *tsi, *tsr, *list = NULL;
                        peer_cfg_t *peer_cfg;
-                       host_t *me, *other;
                        u_int16_t group;
+                       bool private;
 
-                       if (!get_ts(this, message))
-                       {
-                               return FAILED;
-                       }
-                       me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
-                       if (!me)
+                       sa_payload = (sa_payload_t*)message->get_payload(message,
+                                                                                                       SECURITY_ASSOCIATION_V1);
+                       if (!sa_payload)
                        {
-                               me = this->ike_sa->get_my_host(this->ike_sa);
+                               DBG1(DBG_IKE, "sa payload missing");
+                               return send_notify(this, INVALID_PAYLOAD_TYPE);
                        }
-                       other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
-                       if (!other)
+
+                       this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp);
+
+                       if (!get_ts(this, message))
                        {
-                               other = this->ike_sa->get_other_host(this->ike_sa);
+                               return FAILED;
                        }
                        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);
+                       tsi = linked_list_create_with_items(this->tsi, NULL);
+                       tsr = linked_list_create_with_items(this->tsr, NULL);
                        this->tsi = this->tsr = NULL;
                        this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
-                                                                                                         me, other);
+                                                                               get_dynamic_host(this->ike_sa, TRUE),
+                                                                               get_dynamic_host(this->ike_sa, FALSE));
                        if (this->config)
                        {
                                this->tsi = select_ts(this, FALSE, tsi);
@@ -825,20 +926,36 @@ METHOD(task_t, process_r, status_t,
                                return send_notify(this, INVALID_ID_INFORMATION);
                        }
 
-                       sa_payload = (sa_payload_t*)message->get_payload(message,
-                                                                                                       SECURITY_ASSOCIATION_V1);
-                       if (!sa_payload)
+                       if (this->config->use_ipcomp(this->config))
                        {
-                               DBG1(DBG_IKE, "sa payload missing");
-                               return send_notify(this, INVALID_PAYLOAD_TYPE);
+                               if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
+                               {
+                                       DBG1(DBG_IKE, "IPComp is not supported if either peer is "
+                                                "natted, IPComp disabled");
+                               }
+                               else
+                               {
+                                       list = sa_payload->get_ipcomp_proposals(sa_payload,
+                                                                                                                       &this->cpi_i);
+                                       if (!list->get_count(list))
+                                       {
+                                               DBG1(DBG_IKE, "expected IPComp proposal but peer did "
+                                                        "not send one, IPComp disabled");
+                                               this->cpi_i = 0;
+                                       }
+                               }
                        }
-                       list = sa_payload->get_proposals(sa_payload);
+                       if (!list || !list->get_count(list))
+                       {
+                               DESTROY_IF(list);
+                               list = sa_payload->get_proposals(sa_payload);
+                       }
+                       private = this->ike_sa->supports_extension(this->ike_sa,
+                                                                                                          EXT_STRONGSWAN);
                        this->proposal = this->config->select_proposal(this->config,
-                                                                                                                  list, FALSE, FALSE);
+                                                                                                                  list, FALSE, private);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
 
-                       this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp);
-
                        get_lifetimes(this);
                        apply_lifetimes(this, sa_payload);
 
@@ -878,6 +995,22 @@ METHOD(task_t, process_r, status_t,
                                                                        this->ike_sa->get_my_host(this->ike_sa),
                                                                        this->ike_sa->get_other_host(this->ike_sa),
                                                                        this->config, this->reqid, this->udp);
+
+                       tsi = linked_list_create_with_items(this->tsi, NULL);
+                       tsr = linked_list_create_with_items(this->tsr, NULL);
+                       this->tsi = this->tsr = NULL;
+                       charon->bus->narrow(charon->bus, this->child_sa,
+                                                               NARROW_RESPONDER, tsr, tsi);
+                       if (tsi->remove_first(tsi, (void**)&this->tsi) != SUCCESS ||
+                               tsr->remove_first(tsr, (void**)&this->tsr) != SUCCESS)
+                       {
+                               tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+                               tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+                               return send_notify(this, INVALID_ID_INFORMATION);
+                       }
+                       tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+                       tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+
                        return NEED_MORE;
                }
                case QM_NEGOTIATED:
@@ -889,10 +1022,15 @@ METHOD(task_t, process_r, status_t,
                        }
                        if (!install(this))
                        {
-                               this->ike_sa->queue_task(this->ike_sa,
-                                       (task_t*)quick_delete_create(this->ike_sa,
+                               ike_sa_t *ike_sa = this->ike_sa;
+                               task_t *task;
+
+                               task = (task_t*)quick_delete_create(this->ike_sa,
                                                                this->proposal->get_protocol(this->proposal),
-                                                               this->spi_i, TRUE, TRUE));
+                                                               this->spi_i, TRUE, TRUE);
+                               /* flush_queue() destroys the current task */
+                               ike_sa->flush_queue(ike_sa, TASK_QUEUE_PASSIVE);
+                               ike_sa->queue_task(ike_sa, task);
                                return ALREADY_DONE;
                        }
                        return SUCCESS;
@@ -919,6 +1057,17 @@ METHOD(task_t, build_r, status_t,
                        }
                        this->proposal->set_spi(this->proposal, this->spi_r);
 
+                       if (this->cpi_i)
+                       {
+                               this->cpi_r = this->child_sa->alloc_cpi(this->child_sa);
+                               if (!this->cpi_r)
+                               {
+                                       DBG1(DBG_IKE, "unable to allocate a CPI from "
+                                                "kernel, IPComp disabled");
+                                       return send_notify(this, NO_PROPOSAL_CHOSEN);
+                               }
+                       }
+
                        if (this->udp && this->mode == MODE_TRANSPORT)
                        {
                                /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
@@ -927,7 +1076,7 @@ METHOD(task_t, build_r, status_t,
 
                        sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
                                                                this->lifetime, this->lifebytes, AUTH_NONE,
-                                                               this->mode, this->udp);
+                                                               this->mode, this->udp, this->cpi_r);
                        message->add_payload(message, &sa_payload->payload_interface);
 
                        if (!add_nonce(this, &this->nonce_r, message))
@@ -957,7 +1106,8 @@ METHOD(task_t, process_i, status_t,
                case QM_INIT:
                {
                        sa_payload_t *sa_payload;
-                       linked_list_t *list;
+                       linked_list_t *list = NULL;
+                       bool private;
 
                        sa_payload = (sa_payload_t*)message->get_payload(message,
                                                                                                        SECURITY_ASSOCIATION_V1);
@@ -966,9 +1116,26 @@ METHOD(task_t, process_i, status_t,
                                DBG1(DBG_IKE, "sa payload missing");
                                return send_notify(this, NO_PROPOSAL_CHOSEN);
                        }
-                       list = sa_payload->get_proposals(sa_payload);
+                       if (this->cpi_i)
+                       {
+                               list = sa_payload->get_ipcomp_proposals(sa_payload,
+                                                                                                               &this->cpi_r);
+                               if (!list->get_count(list))
+                               {
+                                       DBG1(DBG_IKE, "peer did not acccept our IPComp proposal, "
+                                                "IPComp disabled");
+                                       this->cpi_i = 0;
+                               }
+                       }
+                       if (!list || !list->get_count(list))
+                       {
+                               DESTROY_IF(list);
+                               list = sa_payload->get_proposals(sa_payload);
+                       }
+                       private = this->ike_sa->supports_extension(this->ike_sa,
+                                                                                                          EXT_STRONGSWAN);
                        this->proposal = this->config->select_proposal(this->config,
-                                                                                                                  list, FALSE, FALSE);
+                                                                                                                  list, FALSE, private);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
                        if (!this->proposal)
                        {
@@ -1087,6 +1254,8 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                .config = config,
                .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
                .state = QM_INIT,
+               .tsi = tsi ? tsi->clone(tsi) : NULL,
+               .tsr = tsr ? tsr->clone(tsr) : NULL,
        );
 
        if (config)