child-sa: Add methods that allow partial installation of CHILD_SA
authorTobias Brunner <tobias@strongswan.org>
Tue, 28 Feb 2017 14:03:12 +0000 (15:03 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 23 May 2017 16:46:03 +0000 (18:46 +0200)
Using install() for the inbound SA and register_outbound() for the
outbound SA followed by install_policies(), will delay the installation of
the outbound SA as well as the installation of the outbound policies
in the kernel until install_outbound() is called later.

src/libcharon/sa/child_sa.c
src/libcharon/sa/child_sa.h

index 9fe936a..93a2120 100644 (file)
@@ -93,6 +93,26 @@ struct private_child_sa_t {
        array_t *other_ts;
 
        /**
+        * Outbound encryption key cached during a rekeying
+        */
+       chunk_t encr_r;
+
+       /**
+        * Outbound integrity key cached during a rekeying
+        */
+       chunk_t integ_r;
+
+       /**
+        * Whether the outbound SA has only been registered yet during a rekeying
+        */
+       bool outbound_registered;
+
+       /**
+        * Whether the peer supports TFCv3
+        */
+       bool tfcv3;
+
+       /**
         * Protocol used to protect this SA, ESP|AH
         */
        protocol_id_t protocol;
@@ -692,9 +712,12 @@ METHOD(child_sa_t, alloc_cpi, uint16_t,
        return 0;
 }
 
-METHOD(child_sa_t, install, status_t,
-       private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
-       uint16_t cpi, bool initiator, bool inbound, bool tfcv3)
+/**
+ * Install the given SA in the kernel
+ */
+static status_t install_internal(private_child_sa_t *this, chunk_t encr,
+       chunk_t integ, uint32_t spi, uint16_t cpi, bool initiator, bool inbound,
+       bool tfcv3)
 {
        uint16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
        uint16_t esn = NO_EXT_SEQ_NUMBERS;
@@ -834,6 +857,14 @@ METHOD(child_sa_t, install, status_t,
        return status;
 }
 
+METHOD(child_sa_t, install, status_t,
+       private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
+       uint16_t cpi, bool initiator, bool inbound, bool tfcv3)
+{
+       return install_internal(this, encr, integ, spi, cpi, initiator, inbound,
+                                                       tfcv3);
+}
+
 /**
  * Check kernel interface if policy updates are required
  */
@@ -1167,7 +1198,7 @@ METHOD(child_sa_t, install_policies, status_t,
                        /* install outbound drop policy to avoid packets leaving unencrypted
                         * when updating policies */
                        if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 &&
-                               require_policy_update())
+                               require_policy_update() && !this->outbound_registered)
                        {
                                status |= install_policies_outbound(this, this->my_addr,
                                                                        this->other_addr, my_ts, other_ts,
@@ -1175,11 +1206,19 @@ METHOD(child_sa_t, install_policies, status_t,
                                                                        POLICY_PRIORITY_FALLBACK, 0);
                        }
 
-                       status |= install_policies_internal(this, this->my_addr,
+                       status |= install_policies_inbound(this, this->my_addr,
                                                                        this->other_addr, my_ts, other_ts,
                                                                        &my_sa, &other_sa, POLICY_IPSEC,
                                                                        priority, manual_prio);
 
+                       if (!this->outbound_registered)
+                       {
+                               status |= install_policies_outbound(this, this->my_addr,
+                                                                       this->other_addr, my_ts, other_ts,
+                                                                       &my_sa, &other_sa, POLICY_IPSEC,
+                                                                       priority, manual_prio);
+
+                       }
                        if (status != SUCCESS)
                        {
                                break;
@@ -1195,6 +1234,75 @@ METHOD(child_sa_t, install_policies, status_t,
        return status;
 }
 
+METHOD(child_sa_t, register_outbound, void,
+       private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
+       uint16_t cpi, bool tfcv3)
+{
+       DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names,
+                this->protocol);
+       DBG2(DBG_CHD, "  SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr,
+                this->other_addr);
+
+       this->other_spi = spi;
+       this->other_cpi = cpi;
+       this->encr_r = chunk_clone(encr);
+       this->integ_r = chunk_clone(integ);
+       this->tfcv3 = tfcv3;
+       this->outbound_registered = TRUE;
+}
+
+METHOD(child_sa_t, install_outbound, status_t,
+       private_child_sa_t *this)
+{
+       enumerator_t *enumerator;
+       traffic_selector_t *my_ts, *other_ts;
+       status_t status;
+
+       this->outbound_registered = FALSE;
+
+       status = install_internal(this, this->encr_r, this->integ_r,
+                                                         this->other_spi, this->other_cpi, FALSE, FALSE,
+                                                         this->tfcv3);
+       chunk_clear(&this->encr_r);
+       chunk_clear(&this->integ_r);
+       if (status != SUCCESS)
+       {
+               return status;
+       }
+       if (!this->config->has_option(this->config, OPT_NO_POLICIES))
+       {
+               ipsec_sa_cfg_t my_sa, other_sa;
+               uint32_t manual_prio;
+
+               prepare_sa_cfg(this, &my_sa, &other_sa);
+               manual_prio = this->config->get_manual_prio(this->config);
+
+               enumerator = create_policy_enumerator(this);
+               while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+               {
+                       /* install outbound drop policy to avoid packets leaving unencrypted
+                        * when updating policies */
+                       if (manual_prio == 0 && require_policy_update())
+                       {
+                               status |= install_policies_outbound(this, this->my_addr,
+                                                                       this->other_addr, my_ts, other_ts,
+                                                                       &my_sa, &other_sa, POLICY_DROP,
+                                                                       POLICY_PRIORITY_FALLBACK, 0);
+                       }
+                       status |= install_policies_outbound(this, this->my_addr,
+                                                                       this->other_addr, my_ts, other_ts,
+                                                                       &my_sa, &other_sa, POLICY_IPSEC,
+                                                                       POLICY_PRIORITY_DEFAULT, manual_prio);
+                       if (status != SUCCESS)
+                       {
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       return status;
+}
+
 /**
  * Callback to reinstall a virtual IP
  */
@@ -1451,6 +1559,8 @@ METHOD(child_sa_t, destroy, void,
        this->other_addr->destroy(this->other_addr);
        DESTROY_IF(this->proposal);
        this->config->destroy(this->config);
+       chunk_clear(&this->encr_r);
+       chunk_clear(&this->integ_r);
        free(this);
 }
 
@@ -1530,6 +1640,8 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
                        .alloc_spi = _alloc_spi,
                        .alloc_cpi = _alloc_cpi,
                        .install = _install,
+                       .register_outbound = _register_outbound,
+                       .install_outbound = _install_outbound,
                        .update = _update,
                        .set_policies = _set_policies,
                        .install_policies = _install_policies,
index 4d64091..98fa247 100644 (file)
@@ -368,6 +368,29 @@ struct child_sa_t {
                                                bool initiator, bool inbound, bool tfcv3);
 
        /**
+        * Register data for the installation of an outbound SA as responder during
+        * a rekeying.
+        *
+        * The SA is not installed until install_outbound() is called.
+        *
+        * @param encr          encryption key, if any (cloned)
+        * @param integ         integrity key (cloned)
+        * @param spi           SPI to use, allocated for inbound
+        * @param cpi           CPI to use, allocated for outbound
+        * @param tfcv3         TRUE if peer supports ESPv3 TFC
+        */
+       void (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ,
+                                                         uint32_t spi, uint16_t cpi, bool tfcv3);
+
+       /**
+        * Install the outbound SA and the outbound policies as responder during a
+        * rekeying.
+        *
+        * @return                      SUCCESS or FAILED
+        */
+       status_t (*install_outbound)(child_sa_t *this);
+
+       /**
         * Configure the policies using some traffic selectors.
         *
         * Supplied lists of traffic_selector_t's specify the policies
@@ -387,6 +410,10 @@ struct child_sa_t {
        /**
         * Install the configured policies.
         *
+        * If register_outbound() was called previously this only installs the
+        * inbound and forward policies, the outbound policies are installed when
+        * install_outbound() is called.
+        *
         * @return                      SUCCESS or FAILED
         */
        status_t (*install_policies)(child_sa_t *this);