child-sa: Install drop policies while updating IPsec SAs and policies

If we have to remove and reinstall SAs for address updates (as with the
Linux kernel) there is a short time where there is no SA installed.  If
we keep the policies installed they (or any traps) might cause acquires
and temporary kernel states that could prevent the updated SA from
getting installed again.

This replaces the previous workaround to avoid plaintext traffic leaks
during policy updates, which used low-priority drop policies.
This commit is contained in:
Tobias Brunner 2018-02-06 18:07:34 +01:00
parent 4664992f7d
commit 35ef1b032d
1 changed files with 121 additions and 122 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2017 Tobias Brunner
* Copyright (C) 2006-2018 Tobias Brunner
* Copyright (C) 2016 Andreas Steffen
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
@ -1249,17 +1249,6 @@ METHOD(child_sa_t, install_policies, status_t,
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 (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 &&
require_policy_update() && install_outbound)
{
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_inbound(this, this->my_addr,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
@ -1350,15 +1339,6 @@ METHOD(child_sa_t, install_outbound, status_t,
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,
@ -1407,12 +1387,6 @@ METHOD(child_sa_t, remove_outbound, void,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_IPSEC, POLICY_PRIORITY_DEFAULT,
manual_prio);
if (manual_prio == 0 && require_policy_update())
{
del_policies_outbound(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0);
}
}
enumerator->destroy(enumerator);
}
@ -1458,8 +1432,65 @@ CALLBACK(reinstall_vip, void,
}
}
/**
* Update addresses and encap state of IPsec SAs in the kernel
*/
static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
bool encap)
{
/* update our (initiator) SA */
if (this->my_spi)
{
kernel_ipsec_sa_id_t id = {
.src = this->other_addr,
.dst = this->my_addr,
.spi = this->my_spi,
.proto = proto_ike2ip(this->protocol),
.mark = mark_in_sa(this),
};
kernel_ipsec_update_sa_t sa = {
.cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
.new_src = other,
.new_dst = me,
.encap = this->encap,
.new_encap = encap,
};
if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED)
{
return NOT_SUPPORTED;
}
}
/* update his (responder) SA */
if (this->other_spi)
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
.dst = this->other_addr,
.spi = this->other_spi,
.proto = proto_ike2ip(this->protocol),
.mark = this->mark_out,
};
kernel_ipsec_update_sa_t sa = {
.cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
.new_src = me,
.new_dst = other,
.encap = this->encap,
.new_encap = encap,
};
if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED)
{
return NOT_SUPPORTED;
}
}
/* we currently ignore the actual return values above */
return SUCCESS;
}
METHOD(child_sa_t, update, status_t,
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
bool encap)
{
child_sa_state_t old;
@ -1478,84 +1509,50 @@ METHOD(child_sa_t, update, status_t,
this->config->has_option(this->config,
OPT_PROXY_MODE);
if (!transport_proxy_mode)
{
/* update our (initiator) SA */
if (this->my_spi)
{
kernel_ipsec_sa_id_t id = {
.src = this->other_addr,
.dst = this->my_addr,
.spi = this->my_spi,
.proto = proto_ike2ip(this->protocol),
.mark = mark_in_sa(this),
};
kernel_ipsec_update_sa_t sa = {
.cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
.new_src = other,
.new_dst = me,
.encap = this->encap,
.new_encap = encap,
};
if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED)
{
set_state(this, old);
return NOT_SUPPORTED;
}
}
/* update his (responder) SA */
if (this->other_spi)
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
.dst = this->other_addr,
.spi = this->other_spi,
.proto = proto_ike2ip(this->protocol),
.mark = this->mark_out,
};
kernel_ipsec_update_sa_t sa = {
.cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
.new_src = me,
.new_dst = other,
.encap = this->encap,
.new_encap = encap,
};
if (charon->kernel->update_sa(charon->kernel, &id,
&sa) == NOT_SUPPORTED)
{
set_state(this, old);
return NOT_SUPPORTED;
}
}
}
if (!this->config->has_option(this->config, OPT_NO_POLICIES) &&
require_policy_update())
{
if (!me->ip_equals(me, this->my_addr) ||
!other->ip_equals(other, this->other_addr))
ipsec_sa_cfg_t my_sa, other_sa;
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
uint32_t manual_prio;
status_t state;
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))
{
ipsec_sa_cfg_t my_sa, other_sa;
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
uint32_t manual_prio;
/* install drop policy to avoid traffic leaks, acquires etc. */
install_policies_outbound(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_DEFAULT, manual_prio);
prepare_sa_cfg(this, &my_sa, &other_sa);
manual_prio = this->config->get_manual_prio(this->config);
/* remove old policies */
del_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio);
}
enumerator->destroy(enumerator);
/* always use high priorities, as hosts getting updated are INSTALLED */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
/* update the IPsec SAs */
state = update_sas(this, me, other, encap);
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
/* reinstall the previous policies if we can't update the SAs */
if (state == NOT_SUPPORTED)
{
install_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, manual_prio);
}
else
{
traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
/* remove old policies first */
del_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio);
/* check if we have to update a "dynamic" traffic selector */
if (!me->ip_equals(me, this->my_addr) &&
my_ts->is_host(my_ts, this->my_addr))
@ -1578,23 +1575,32 @@ METHOD(child_sa_t, update, status_t,
install_policies_internal(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT, manual_prio);
/* update fallback policies after the new policy is in place */
if (manual_prio == 0)
{
del_policies_outbound(this, this->my_addr, this->other_addr,
old_my_ts ?: my_ts,
old_other_ts ?: other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK, 0);
install_policies_outbound(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK, 0);
}
DESTROY_IF(old_my_ts);
DESTROY_IF(old_other_ts);
}
enumerator->destroy(enumerator);
/* remove the drop policy */
del_policies_outbound(this, this->my_addr, this->other_addr,
old_my_ts ?: my_ts,
old_other_ts ?: other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_DEFAULT, 0);
DESTROY_IF(old_my_ts);
DESTROY_IF(old_other_ts);
}
enumerator->destroy(enumerator);
if (state == NOT_SUPPORTED)
{
set_state(this, old);
return NOT_SUPPORTED;
}
}
else if (!transport_proxy_mode)
{
if (update_sas(this, me, other, encap) == NOT_SUPPORTED)
{
set_state(this, old);
return NOT_SUPPORTED;
}
}
@ -1655,13 +1661,6 @@ METHOD(child_sa_t, destroy, void,
del_policies_inbound(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_IPSEC, priority, manual_prio);
if (!this->trap && manual_prio == 0 && require_policy_update() &&
del_outbound)
{
del_policies_outbound(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0);
}
}
enumerator->destroy(enumerator);
}