summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2015-04-11 22:03:59 +0200
committerYves-Alexis Perez <corsac@debian.org>2015-04-11 22:30:17 +0200
commit8404fb0212f9fb77bc53b23004b829b488430700 (patch)
tree23876c7540d138f58a6a7d90793ccf9004f6afd2 /src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
parent1b7c683a32c62b6e08ad7bf5af39b9f4edd634f3 (diff)
downloadvyos-strongswan-8404fb0212f9fb77bc53b23004b829b488430700.tar.gz
vyos-strongswan-8404fb0212f9fb77bc53b23004b829b488430700.zip
Imported Upstream version 5.3.0
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c')
-rw-r--r--src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c258
1 files changed, 206 insertions, 52 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
index c788bfb10..b38ded846 100644
--- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
+++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
@@ -26,6 +26,8 @@
#include <collections/hashtable.h>
#include <processing/jobs/callback_job.h>
+#define IPPROTO_IPIP 4
+#define IPPROTO_IPV6 41
typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
@@ -188,6 +190,14 @@ typedef struct {
u_int64_t provider;
/** WFP allocated LUID for SA context */
u_int64_t sa_id;
+ /** WFP allocated LUID for tunnel mode IP-IPv4 inbound filter */
+ u_int64_t ip_ipv4_in;
+ /** WFP allocated LUID for tunnel mode IP-IPv4 outbound filter */
+ u_int64_t ip_ipv4_out;
+ /** WFP allocated LUID for tunnel mode IP-IPv6 inbound filter */
+ u_int64_t ip_ipv6_in;
+ /** WFP allocated LUID for tunnel mode IP-IPv6 outbound filter */
+ u_int64_t ip_ipv6_out;
} entry_t;
/**
@@ -285,6 +295,22 @@ static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
*/
static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
+ if (entry->ip_ipv4_in)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_in);
+ }
+ if (entry->ip_ipv4_out)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_out);
+ }
+ if (entry->ip_ipv6_in)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_in);
+ }
+ if (entry->ip_ipv6_out)
+ {
+ FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_out);
+ }
if (entry->sa_id)
{
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
@@ -553,49 +579,58 @@ static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
* Find the callout GUID for given parameters
*/
static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
- GUID *layer, GUID *sublayer, GUID *callout)
+ bool ale, GUID *layer, GUID *sublayer, GUID *callout)
{
struct {
bool tunnel;
bool v6;
bool inbound;
bool forward;
+ bool ale;
const GUID *layer;
const GUID *sublayer;
const GUID *callout;
} map[] = {
- { 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL,
- &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 },
- { 0, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL,
- &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 },
- { 0, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL,
- &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 },
- { 0, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL,
- &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 },
- { 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 },
- { 1, 0, 0, 1, &FWPM_LAYER_IPFORWARD_V4,
- &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
- &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 },
- { 1, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 },
- { 1, 0, 1, 1, &FWPM_LAYER_IPFORWARD_V4,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 },
- { 1, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 },
- { 1, 1, 0, 1, &FWPM_LAYER_IPFORWARD_V6,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 },
- { 1, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 },
- { 1, 1, 1, 1, &FWPM_LAYER_IPFORWARD_V6,
- &FWPM_SUBLAYER_IPSEC_TUNNEL,
- &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 },
+ { 0, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 },
+ { 0, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 },
+ { 0, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 },
+ { 0, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 },
+ { 1, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 },
+ { 1, 0, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
+ &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 },
+ { 1, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 },
+ { 1, 0, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 },
+ { 1, 0, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V4 },
+ { 1, 0, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V4},
+ { 1, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 },
+ { 1, 1, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
+ &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 },
+ { 1, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 },
+ { 1, 1, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
+ &FWPM_SUBLAYER_IPSEC_TUNNEL,
+ &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 },
+ { 1, 1, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V6 },
+ { 1, 1, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, NULL,
+ &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V6},
};
int i;
@@ -604,7 +639,8 @@ static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
if (tunnel == map[i].tunnel &&
v6 == map[i].v6 &&
inbound == map[i].inbound &&
- forward == map[i].forward)
+ forward == map[i].forward &&
+ ale == map[i].ale)
{
*callout = *map[i].callout;
*layer = *map[i].layer;
@@ -647,7 +683,7 @@ static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
}
v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE;
- if (!find_callout(context != NULL, v6, inbound, fwd,
+ if (!find_callout(context != NULL, v6, inbound, fwd, FALSE,
&filter.layerKey, &filter.subLayerKey,
&filter.action.calloutKey))
{
@@ -688,8 +724,73 @@ static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
free_conditions(conds, count);
if (res != ERROR_SUCCESS)
{
- DBG1(DBG_KNL, "installing %s%sbound WFP filter failed: 0x%08x",
- fwd ? "forward " : "", inbound ? "in" : "out", res);
+ DBG1(DBG_KNL, "installing IPv%d %s%sbound %s WFP filter failed: 0x%08x",
+ v6 ? 6 : 4, fwd ? "forward " : "", inbound ? "in" : "out",
+ context ? "tunnel" : "transport", res);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Install an IP-IP allow filter for SA specific hosts
+ */
+static bool install_ipip_ale(private_kernel_wfp_ipsec_t *this,
+ host_t *local, host_t *remote, GUID *context,
+ bool inbound, int proto, u_int64_t *filter_id)
+{
+ traffic_selector_t *lts, *rts;
+ FWPM_FILTER_CONDITION0 *conds = NULL;
+ int count = 0;
+ bool v6;
+ DWORD res;
+ FWPM_FILTER0 filter = {
+ .displayData = {
+ .name = L"charon IPsec IP-in-IP ALE policy",
+ },
+ .action = {
+ .type = FWP_ACTION_CALLOUT_TERMINATING,
+ },
+ };
+
+ if (context)
+ {
+ filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
+ filter.providerKey = (GUID*)&this->provider.providerKey;
+ filter.providerContextKey = *context;
+ }
+
+ v6 = local->get_family(local) == AF_INET6;
+ if (!find_callout(TRUE, v6, inbound, FALSE, TRUE, &filter.layerKey,
+ &filter.subLayerKey, &filter.action.calloutKey))
+ {
+ return FALSE;
+ }
+
+ lts = traffic_selector_create_from_subnet(local->clone(local),
+ v6 ? 128 : 32 , proto, 0, 65535);
+ rts = traffic_selector_create_from_subnet(remote->clone(remote),
+ v6 ? 128 : 32 , proto, 0, 65535);
+ if (!ts2condition(lts, &FWPM_CONDITION_IP_LOCAL_ADDRESS, &conds, &count) ||
+ !ts2condition(rts, &FWPM_CONDITION_IP_REMOTE_ADDRESS, &conds, &count))
+ {
+ free_conditions(conds, count);
+ lts->destroy(lts);
+ rts->destroy(rts);
+ return FALSE;
+ }
+ lts->destroy(lts);
+ rts->destroy(rts);
+
+ filter.numFilterConditions = count;
+ filter.filterCondition = conds;
+
+ res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
+ free_conditions(conds, count);
+ if (res != ERROR_SUCCESS)
+ {
+ DBG1(DBG_KNL, "installing IP-IPv%d %s ALE WFP filter failed: 0x%08x",
+ v6 ? 6 : 4, inbound ? "inbound" : "outbound", res);
return FALSE;
}
return TRUE;
@@ -703,10 +804,21 @@ static bool install_sps(private_kernel_wfp_ipsec_t *this,
{
enumerator_t *enumerator;
sp_entry_t *sp;
+ bool has_v4 = FALSE, has_v6 = FALSE;
enumerator = array_create_enumerator(entry->sps);
while (enumerator->enumerate(enumerator, &sp))
{
+ switch (sp->src->get_type(sp->src))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ has_v4 = TRUE;
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ has_v6 = TRUE;
+ break;
+ }
+
/* inbound policy */
if (!install_sp(this, sp, context, TRUE, FALSE, &sp->policy_in))
{
@@ -719,21 +831,22 @@ static bool install_sps(private_kernel_wfp_ipsec_t *this,
enumerator->destroy(enumerator);
return FALSE;
}
+
if (context)
{
if (!sp->src->is_host(sp->src, entry->local) ||
!sp->dst->is_host(sp->dst, entry->remote))
{
/* inbound forward policy, from decapsulation */
- if (!install_sp(this, sp, context,
- TRUE, TRUE, &sp->policy_fwd_in))
+ if (!install_sp(this, sp, context, TRUE, TRUE,
+ &sp->policy_fwd_in))
{
enumerator->destroy(enumerator);
return FALSE;
}
/* outbound forward policy, to encapsulate */
- if (!install_sp(this, sp, context,
- FALSE, TRUE, &sp->policy_fwd_out))
+ if (!install_sp(this, sp, context, FALSE, TRUE,
+ &sp->policy_fwd_out))
{
enumerator->destroy(enumerator);
return FALSE;
@@ -743,6 +856,38 @@ static bool install_sps(private_kernel_wfp_ipsec_t *this,
}
enumerator->destroy(enumerator);
+ if (context)
+ {
+ /* In tunnel mode, Windows does firewall filtering on decrypted but
+ * non-unwrapped packets: It sees them as IP-in-IP packets. When using
+ * a default-drop policy, we need to allow such packets explicitly. */
+ if (has_v4)
+ {
+ if (!install_ipip_ale(this, entry->local, entry->remote, context,
+ TRUE, IPPROTO_IPIP, &entry->ip_ipv4_in))
+ {
+ return FALSE;
+ }
+ if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
+ FALSE, IPPROTO_IPIP, &entry->ip_ipv4_out))
+ {
+ return FALSE;
+ }
+ }
+ if (has_v6)
+ {
+ if (!install_ipip_ale(this, entry->local, entry->remote, context,
+ TRUE, IPPROTO_IPV6, &entry->ip_ipv6_in))
+ {
+ return FALSE;
+ }
+ if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
+ FALSE, IPPROTO_IPV6, &entry->ip_ipv6_out))
+ {
+ return FALSE;
+ }
+ }
+ }
return TRUE;
}
@@ -1583,8 +1728,20 @@ static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event)
acquire(this, event->classifyDrop->filterId, local, remote);
break;
case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
+ DBG1(DBG_KNL, "WFP MM failure: %R === %R, 0x%08x, filterId %llu",
+ local, remote, event->ikeMmFailure->failureErrorCode,
+ event->ikeMmFailure->mmFilterId);
+ break;
case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
+ DBG1(DBG_KNL, "WFP QM failure: %R === %R, 0x%08x, filterId %llu",
+ local, remote, event->ikeQmFailure->failureErrorCode,
+ event->ikeQmFailure->qmFilterId);
+ break;
case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
+ DBG1(DBG_KNL, "WFP EM failure: %R === %R, 0x%08x, filterId %llu",
+ local, remote, event->ikeEmFailure->failureErrorCode,
+ event->ikeEmFailure->qmFilterId);
+ break;
case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, "
"SPI 0x%08x, %s filterId %llu", local, remote,
@@ -1824,7 +1981,7 @@ static u_int permute(u_int x, u_int p)
METHOD(kernel_ipsec_t, get_spi, status_t,
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
- u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
+ u_int8_t protocol, u_int32_t *spi)
{
/* To avoid sequencial SPIs, we use a one-to-one permuation function on
* an incrementing counter, that is a full period PRNG for the range we
@@ -1841,7 +1998,7 @@ METHOD(kernel_ipsec_t, get_spi, status_t,
METHOD(kernel_ipsec_t, get_cpi, status_t,
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
- u_int32_t reqid, u_int16_t *cpi)
+ u_int16_t *cpi)
{
return NOT_SUPPORTED;
}
@@ -1875,9 +2032,8 @@ static void expire_data_destroy(expire_data_t *data)
static job_requeue_t expire_job(expire_data_t *data)
{
private_kernel_wfp_ipsec_t *this = data->this;
- u_int32_t reqid = 0;
u_int8_t protocol;
- entry_t *entry;
+ entry_t *entry = NULL;
sa_entry_t key = {
.spi = data->spi,
.dst = data->dst,
@@ -1891,7 +2047,6 @@ static job_requeue_t expire_job(expire_data_t *data)
if (entry)
{
protocol = entry->isa.protocol;
- reqid = entry->reqid;
if (entry->osa.dst)
{
key.dst = entry->osa.dst;
@@ -1908,15 +2063,14 @@ static job_requeue_t expire_job(expire_data_t *data)
if (entry)
{
protocol = entry->isa.protocol;
- reqid = entry->reqid;
}
this->mutex->unlock(this->mutex);
}
- if (reqid)
+ if (entry)
{
- hydra->kernel_interface->expire(hydra->kernel_interface,
- reqid, protocol, data->spi, data->hard);
+ hydra->kernel_interface->expire(hydra->kernel_interface, protocol,
+ data->spi, data->dst, data->hard);
}
return JOB_REQUEUE_NONE;
@@ -1949,8 +2103,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode,
u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window,
- bool initiator, bool encap, bool esn, bool inbound,
- traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
+ bool initiator, bool encap, bool esn, bool inbound, bool update,
+ linked_list_t *src_ts, linked_list_t *dst_ts)
{
host_t *local, *remote;
entry_t *entry;