From 8a3a389e3dd2cbf4df916145df0e02b2fdbe4edb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Feb 2017 15:03:12 +0100 Subject: [PATCH] child-sa: Add methods that allow partial installation of CHILD_SA 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 | 122 ++++++++++++++++++++++++++++++++++-- src/libcharon/sa/child_sa.h | 27 ++++++++ 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 9fe936a4e3..93a2120e60 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -92,6 +92,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 */ @@ -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, diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 4d64091756..98fa24701c 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -367,6 +367,29 @@ struct child_sa_t { uint32_t spi, uint16_t cpi, 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. * @@ -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); -- 2.47.3