Extended nonce payload for IKEv1 support
[strongswan.git] / src / libcharon / sa / tasks / child_create.c
index ed468ec..587e37a 100644 (file)
@@ -117,6 +117,11 @@ struct private_child_create_t {
        ipsec_mode_t mode;
 
        /**
+        * peer accepts TFC padding for this SA
+        */
+       bool tfcv3;
+
+       /**
         * IPComp transform to use
         */
        ipcomp_transform_t ipcomp;
@@ -208,13 +213,13 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host)
 {
        traffic_selector_t *ts;
        bool is_host = TRUE;
-       iterator_t *iterator = list->create_iterator(list, TRUE);
+       enumerator_t *enumerator = list->create_enumerator(list);
 
-       while (is_host && iterator->iterate(iterator, (void**)&ts))
+       while (is_host && enumerator->enumerate(enumerator, (void**)&ts))
        {
                is_host = is_host && ts->is_host(ts, host);
        }
-       iterator->destroy(iterator);
+       enumerator->destroy(enumerator);
        return is_host;
 }
 
@@ -261,7 +266,7 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
        {
                close_ike = lib->settings->get_bool(lib->settings,
                                                                                "charon.inactivity_close_ike", FALSE);
-               charon->scheduler->schedule_job(charon->scheduler, (job_t*)
+               lib->scheduler->schedule_job(lib->scheduler, (job_t*)
                                inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
                                                                          timeout, close_ike), timeout);
        }
@@ -273,7 +278,8 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
  * - INVALID_ARG: diffie hellman group inacceptable
  * - NOT_FOUND: TS inacceptable
  */
-static status_t select_and_install(private_child_create_t *this, bool no_dh)
+static status_t select_and_install(private_child_create_t *this,
+                                                                  bool no_dh, bool ike_auth)
 {
        status_t status, status_i, status_o;
        chunk_t nonce_i, nonce_r;
@@ -364,6 +370,25 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
        other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts,
                                                                                                   other_vip);
 
+       if (this->initiator)
+       {
+               if (ike_auth)
+               {
+                       charon->bus->narrow(charon->bus, this->child_sa,
+                                                               NARROW_INITIATOR_POST_NOAUTH, my_ts, other_ts);
+               }
+               else
+               {
+                       charon->bus->narrow(charon->bus, this->child_sa,
+                                                               NARROW_INITIATOR_POST_AUTH, my_ts, other_ts);
+               }
+       }
+       else
+       {
+               charon->bus->narrow(charon->bus, this->child_sa,
+                                                       NARROW_RESPONDER, my_ts, other_ts);
+       }
+
        if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0)
        {
                my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
@@ -418,66 +443,6 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
                }
        }
 
-       /* check for any certificate-based IP address block constraints */
-       if (this->mode == MODE_BEET || this->mode == MODE_TUNNEL)
-       {
-               auth_cfg_t *auth;
-               enumerator_t *auth_enum;
-               certificate_t *cert = NULL;
-
-               auth_enum = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE);
-               while (auth_enum->enumerate(auth_enum, &auth))
-               {
-                       cert = auth->get(auth, AUTH_HELPER_SUBJECT_CERT);
-                       if (cert)
-                       {
-                               break;
-                       }
-               }
-               auth_enum->destroy(auth_enum);
-
-               if (cert && cert->get_type(cert) == CERT_X509)
-               {
-                       x509_t *x509 = (x509_t*)cert;
-
-                       if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS)
-                       {
-                               enumerator_t *enumerator, *block_enum;
-                               traffic_selector_t *ts, *block_ts;
-
-                               DBG1(DBG_IKE, "checking certificate-based traffic selector "
-                                                         "constraints [RFC 3779]");
-                               enumerator = other_ts->create_enumerator(other_ts);
-                               while (enumerator->enumerate(enumerator, &ts))
-                               {
-                                       bool contained = FALSE;
-
-                                       block_enum = x509->create_ipAddrBlock_enumerator(x509);
-                                       while (block_enum->enumerate(block_enum, &block_ts))
-                                       {
-                                               if (ts->is_contained_in(ts, block_ts))
-                                               {
-                                                       DBG1(DBG_IKE, "  TS %R is contained in address block"
-                                                                                 " constraint %R", ts, block_ts);
-                                                       contained = TRUE;
-                                                       break;
-                                               }
-                                       }
-                                       block_enum->destroy(block_enum);
-
-                                       if (!contained)
-                                       {
-                                               DBG1(DBG_IKE, "  TS %R is not contained in any"
-                                                                         " address block constraint", ts);
-                                               enumerator->destroy(enumerator);
-                                               return FAILED;
-                                       }
-                               }
-                               enumerator->destroy(enumerator);
-                       }
-               }
-       }
-
        this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
        this->child_sa->set_ipcomp(this->child_sa, this->ipcomp);
        this->child_sa->set_mode(this->child_sa, this->mode);
@@ -495,17 +460,21 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
        {
                if (this->initiator)
                {
-                       status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
-                                       this->my_spi, this->my_cpi, TRUE, my_ts, other_ts);
-                       status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
-                                       this->other_spi, this->other_cpi, FALSE, my_ts, other_ts);
+                       status_i = this->child_sa->install(this->child_sa,
+                                                       encr_r, integ_r, this->my_spi, this->my_cpi,
+                                                       TRUE, this->tfcv3, my_ts, other_ts);
+                       status_o = this->child_sa->install(this->child_sa,
+                                                       encr_i, integ_i, this->other_spi, this->other_cpi,
+                                                       FALSE, this->tfcv3, my_ts, other_ts);
                }
                else
                {
-                       status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
-                                       this->my_spi, this->my_cpi, TRUE, my_ts, other_ts);
-                       status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
-                                       this->other_spi, this->other_cpi, FALSE, my_ts, other_ts);
+                       status_i = this->child_sa->install(this->child_sa,
+                                                       encr_i, integ_i, this->my_spi, this->my_cpi,
+                                                       TRUE, this->tfcv3, my_ts, other_ts);
+                       status_o = this->child_sa->install(this->child_sa,
+                                                       encr_r, integ_r, this->other_spi, this->other_cpi,
+                                                       FALSE, this->tfcv3, my_ts, other_ts);
                }
        }
        chunk_clear(&integ_i);
@@ -529,8 +498,8 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
                return NOT_FOUND;
        }
 
-       charon->bus->child_keys(charon->bus, this->child_sa, this->dh,
-                                                       nonce_i, nonce_r);
+       charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
+                                                       this->dh, nonce_i, nonce_r);
 
        /* add to IKE_SA, and remove from task */
        this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
@@ -557,18 +526,20 @@ static void build_payloads(private_child_create_t *this, message_t *message)
        /* add SA payload */
        if (this->initiator)
        {
-               sa_payload = sa_payload_create_from_proposal_list(this->proposals);
+               sa_payload = sa_payload_create_from_proposal_list(SECURITY_ASSOCIATION,
+                                                                                                                 this->proposals);
        }
        else
        {
-               sa_payload = sa_payload_create_from_proposal(this->proposal);
+               sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION,
+                                                                                                        this->proposal);
        }
        message->add_payload(message, (payload_t*)sa_payload);
 
        /* add nonce payload if not in IKE_AUTH */
        if (message->get_exchange_type(message) == CREATE_CHILD_SA)
        {
-               nonce_payload = nonce_payload_create();
+               nonce_payload = nonce_payload_create(NONCE);
                nonce_payload->set_nonce(nonce_payload, this->my_nonce);
                message->add_payload(message, (payload_t*)nonce_payload);
        }
@@ -671,7 +642,13 @@ static void handle_notify(private_child_create_t *this, notify_payload_t *notify
                                                 ipcomp_transform_names, ipcomp);
                                        break;
                        }
+                       break;
                }
+               case ESP_TFC_PADDING_NOT_SUPPORTED:
+                       DBG1(DBG_IKE, "received %N, not using ESPv3 TFC padding",
+                                notify_type_names, notify->get_notify_type(notify));
+                       this->tfcv3 = FALSE;
+                       break;
                default:
                        break;
        }
@@ -731,10 +708,8 @@ static void process_payloads(private_child_create_t *this, message_t *message)
        enumerator->destroy(enumerator);
 }
 
-/**
- * Implementation of task_t.build for initiator
- */
-static status_t build_i(private_child_create_t *this, message_t *message)
+METHOD(task_t, build_i, status_t,
+       private_child_create_t *this, message_t *message)
 {
        host_t *me, *other, *vip;
        peer_cfg_t *peer_cfg;
@@ -848,6 +823,17 @@ static status_t build_i(private_child_create_t *this, message_t *message)
                add_ipcomp_notify(this, message, IPCOMP_DEFLATE);
        }
 
+       if (message->get_exchange_type(message) == IKE_AUTH)
+       {
+               charon->bus->narrow(charon->bus, this->child_sa,
+                                                       NARROW_INITIATOR_PRE_NOAUTH, this->tsi, this->tsr);
+       }
+       else
+       {
+               charon->bus->narrow(charon->bus, this->child_sa,
+                                                       NARROW_INITIATOR_PRE_AUTH, this->tsi, this->tsr);
+       }
+
        build_payloads(this, message);
 
        this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
@@ -860,10 +846,8 @@ static status_t build_i(private_child_create_t *this, message_t *message)
        return NEED_MORE;
 }
 
-/**
- * Implementation of task_t.process for responder
- */
-static status_t process_r(private_child_create_t *this, message_t *message)
+METHOD(task_t, process_r, status_t,
+       private_child_create_t *this, message_t *message)
 {
        switch (message->get_exchange_type(message))
        {
@@ -900,21 +884,23 @@ static void handle_child_sa_failure(private_child_create_t *this,
                /* we delay the delete for 100ms, as the IKE_AUTH response must arrive
                 * first */
                DBG1(DBG_IKE, "closing IKE_SA due CHILD_SA setup failure");
-               charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*)
+               lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
                        delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE),
                        100);
        }
+       else
+       {
+               DBG1(DBG_IKE, "failed to establish CHILD_SA, keeping IKE_SA");
+       }
 }
 
-/**
- * Implementation of task_t.build for responder
- */
-static status_t build_r(private_child_create_t *this, message_t *message)
+METHOD(task_t, build_r, status_t,
+       private_child_create_t *this, message_t *message)
 {
        peer_cfg_t *peer_cfg;
        payload_t *payload;
        enumerator_t *enumerator;
-       bool no_dh = TRUE;
+       bool no_dh = TRUE, ike_auth = FALSE;
 
        switch (message->get_exchange_type(message))
        {
@@ -934,6 +920,7 @@ static status_t build_r(private_child_create_t *this, message_t *message)
                        {       /* wait until all authentication round completed */
                                return NEED_MORE;
                        }
+                       ike_auth = TRUE;
                default:
                        break;
        }
@@ -986,7 +973,7 @@ static status_t build_r(private_child_create_t *this, message_t *message)
                                case INTERNAL_ADDRESS_FAILURE:
                                case FAILED_CP_REQUIRED:
                                {
-                                       DBG1(DBG_IKE,"configuration payload negotation "
+                                       DBG1(DBG_IKE,"configuration payload negotiation "
                                                 "failed, no CHILD_SA built");
                                        enumerator->destroy(enumerator);
                                        handle_child_sa_failure(this, message);
@@ -1016,7 +1003,7 @@ static status_t build_r(private_child_create_t *this, message_t *message)
                }
        }
 
-       switch (select_and_install(this, no_dh))
+       switch (select_and_install(this, no_dh, ike_auth))
        {
                case SUCCESS:
                        break;
@@ -1057,14 +1044,12 @@ static status_t build_r(private_child_create_t *this, message_t *message)
        return SUCCESS;
 }
 
-/**
- * Implementation of task_t.process for initiator
- */
-static status_t process_i(private_child_create_t *this, message_t *message)
+METHOD(task_t, process_i, status_t,
+       private_child_create_t *this, message_t *message)
 {
        enumerator_t *enumerator;
        payload_t *payload;
-       bool no_dh = TRUE;
+       bool no_dh = TRUE, ike_auth = FALSE;
 
        switch (message->get_exchange_type(message))
        {
@@ -1079,6 +1064,7 @@ static status_t process_i(private_child_create_t *this, message_t *message)
                        {       /* wait until all authentication round completed */
                                return NEED_MORE;
                        }
+                       ike_auth = TRUE;
                default:
                        break;
        }
@@ -1130,7 +1116,21 @@ static status_t process_i(private_child_create_t *this, message_t *message)
                                        return NEED_MORE;
                                }
                                default:
+                               {
+                                       if (message->get_exchange_type(message) == CREATE_CHILD_SA)
+                                       {       /* handle notifies if not handled in IKE_AUTH */
+                                               if (type <= 16383)
+                                               {
+                                                       DBG1(DBG_IKE, "received %N notify error",
+                                                                notify_type_names, type);
+                                                       enumerator->destroy(enumerator);
+                                                       return SUCCESS;
+                                               }
+                                               DBG2(DBG_IKE, "received %N notify",
+                                                        notify_type_names, type);
+                                       }
                                        break;
+                               }
                        }
                }
        }
@@ -1159,7 +1159,7 @@ static status_t process_i(private_child_create_t *this, message_t *message)
                return SUCCESS;
        }
 
-       if (select_and_install(this, no_dh) == SUCCESS)
+       if (select_and_install(this, no_dh, ike_auth) == SUCCESS)
        {
                DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
                         "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
@@ -1182,34 +1182,20 @@ static status_t process_i(private_child_create_t *this, message_t *message)
        return SUCCESS;
 }
 
-/**
- * Implementation of task_t.get_type
- */
-static task_type_t get_type(private_child_create_t *this)
-{
-       return CHILD_CREATE;
-}
-
-/**
- * Implementation of child_create_t.use_reqid
- */
-static void use_reqid(private_child_create_t *this, u_int32_t reqid)
+METHOD(child_create_t, use_reqid, void,
+       private_child_create_t *this, u_int32_t reqid)
 {
        this->reqid = reqid;
 }
 
-/**
- * Implementation of child_create_t.get_child
- */
-static child_sa_t* get_child(private_child_create_t *this)
+METHOD(child_create_t, get_child, child_sa_t*,
+       private_child_create_t *this)
 {
        return this->child_sa;
 }
 
-/**
- * Implementation of child_create_t.get_lower_nonce
- */
-static chunk_t get_lower_nonce(private_child_create_t *this)
+METHOD(child_create_t, get_lower_nonce, chunk_t,
+       private_child_create_t *this)
 {
        if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr,
                           min(this->my_nonce.len, this->other_nonce.len)) < 0)
@@ -1222,10 +1208,14 @@ static chunk_t get_lower_nonce(private_child_create_t *this)
        }
 }
 
-/**
- * Implementation of task_t.migrate
- */
-static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
+METHOD(task_t, get_type, task_type_t,
+       private_child_create_t *this)
+{
+       return CHILD_CREATE;
+}
+
+METHOD(task_t, migrate, void,
+       private_child_create_t *this, ike_sa_t *ike_sa)
 {
        chunk_free(&this->my_nonce);
        chunk_free(&this->other_nonce);
@@ -1261,10 +1251,8 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
        this->established = FALSE;
 }
 
-/**
- * Implementation of task_t.destroy
- */
-static void destroy(private_child_create_t *this)
+METHOD(task_t, destroy, void,
+       private_child_create_t *this)
 {
        chunk_free(&this->my_nonce);
        chunk_free(&this->other_nonce);
@@ -1300,52 +1288,45 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
                                                        child_cfg_t *config, bool rekey,
                                                        traffic_selector_t *tsi, traffic_selector_t *tsr)
 {
-       private_child_create_t *this = malloc_thing(private_child_create_t);
-
-       this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child;
-       this->public.get_lower_nonce = (chunk_t(*)(child_create_t*))get_lower_nonce;
-       this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid;
-       this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
-       this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
-       this->public.task.destroy = (void(*)(task_t*))destroy;
+       private_child_create_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_child = _get_child,
+                       .get_lower_nonce = _get_lower_nonce,
+                       .use_reqid = _use_reqid,
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .config = config,
+               .packet_tsi = tsi ? tsi->clone(tsi) : NULL,
+               .packet_tsr = tsr ? tsr->clone(tsr) : NULL,
+               .dh_group = MODP_NONE,
+               .keymat = ike_sa->get_keymat(ike_sa),
+               .mode = MODE_TUNNEL,
+               .tfcv3 = TRUE,
+               .ipcomp = IPCOMP_NONE,
+               .ipcomp_received = IPCOMP_NONE,
+               .rekey = rekey,
+       );
+
        if (config)
        {
-               this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
-               this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
                this->initiator = TRUE;
                config->get_ref(config);
        }
        else
        {
-               this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
-               this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+               this->public.task.build = _build_r;
+               this->public.task.process = _process_r;
                this->initiator = FALSE;
        }
 
-       this->ike_sa = ike_sa;
-       this->config = config;
-       this->my_nonce = chunk_empty;
-       this->other_nonce = chunk_empty;
-       this->proposals = NULL;
-       this->proposal = NULL;
-       this->tsi = NULL;
-       this->tsr = NULL;
-       this->packet_tsi = tsi ? tsi->clone(tsi) : NULL;
-       this->packet_tsr = tsr ? tsr->clone(tsr) : NULL;
-       this->dh = NULL;
-       this->dh_group = MODP_NONE;
-       this->keymat = ike_sa->get_keymat(ike_sa);
-       this->child_sa = NULL;
-       this->mode = MODE_TUNNEL;
-       this->ipcomp = IPCOMP_NONE;
-       this->ipcomp_received = IPCOMP_NONE;
-       this->my_spi = 0;
-       this->other_spi = 0;
-       this->my_cpi = 0;
-       this->other_cpi = 0;
-       this->reqid = 0;
-       this->established = FALSE;
-       this->rekey = rekey;
-
        return &this->public;
 }