kernel-wfp: Manually create a ProviderContext to attach individual filters

This gives us more flexibility than using the intransparent FwpmIPsecTunnelAdd,
and fixes the issues we have seen with trap policies. Forward filters are
still missing, but required for site-to-site tunnels.
This commit is contained in:
Martin Willi
2014-06-04 16:32:10 +02:00
parent 1ca2b1615a
commit 1678f0a999
4 changed files with 73 additions and 79 deletions
@@ -504,18 +504,6 @@ static char* layer2name(GUID *guid)
return NULL;
}
const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 = {
0x191a8a46, 0x0bf8, 0x46cf, { 0xb0,0x45,0x4b,0x45,0xdf,0xa6,0xa3,0x24 }
};
const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 = {
0x80c342e3, 0x1e53, 0x4d6f, { 0x9b,0x44,0x03,0xdf,0x5a,0xee,0xe1,0x54 }
};
const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 = {
0x70a4196c, 0x835b, 0x4fb0, { 0x98,0xe8,0x07,0x5f,0x4d,0x97,0x7d,0x46 }
};
const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 = {
0xf1835363, 0xa6a5, 0x4e62, { 0xb1,0x80,0x23,0xdb,0x78,0x9d,0x8d,0xa6 }
};
const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 = {
0x28829633, 0xc4f0, 0x4e66, { 0x87,0x3f,0x84,0x4d,0xb2,0xa8,0x99,0xc7 }
};
@@ -54,6 +54,18 @@ const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 = {
const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 = {
0x38d87722, 0xad83, 0x4f11, { 0xa9,0x1f,0xdf,0x0f,0xb0,0x77,0x22,0x5b }
};
const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 = {
0x191a8a46, 0x0bf8, 0x46cf, { 0xb0,0x45,0x4b,0x45,0xdf,0xa6,0xa3,0x24 }
};
const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 = {
0x80c342e3, 0x1e53, 0x4d6f, { 0x9b,0x44,0x03,0xdf,0x5a,0xee,0xe1,0x54 }
};
const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 = {
0x70a4196c, 0x835b, 0x4fb0, { 0x98,0xe8,0x07,0x5f,0x4d,0x97,0x7d,0x46 }
};
const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 = {
0xf1835363, 0xa6a5, 0x4e62, { 0xb1,0x80,0x23,0xdb,0x78,0x9d,0x8d,0xa6 }
};
/**
* Load a function symbol from a loaded dll
@@ -76,6 +76,16 @@ enum {
FWPM_NET_EVENT_FLAG_REAUTH_REASON_SET = (1<<9),
};
/* missing in MinGW */
enum {
FWPM_FILTER_FLAG_PERSISTENT = (1<<0),
FWPM_FILTER_FLAG_BOOTTIME = (1<<1),
FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT = (1<<2),
FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT = (1<<3),
FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED = (1<<4),
FWPM_FILTER_FLAG_DISABLED = (1<<5),
};
DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32,
const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32,
const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR);
@@ -174,10 +174,12 @@ typedef struct {
ipsec_mode_t mode;
/** UDP encapsulation */
bool encap;
/** WFP allocated LUID for inbound filter/tunnel policy ID */
/** WFP allocated LUID for inbound filter ID */
u_int64_t policy_in;
/** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
/** WFP allocated LUID for outbound filter ID */
u_int64_t policy_out;
/** provider context, for tunnel mode only */
u_int64_t provider;
/** WFP allocated LUID for SA context */
u_int64_t sa_id;
} entry_t;
@@ -232,29 +234,6 @@ static u_int hash_route(route_t *route)
static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
bool add);
/**
* Remove a transport or tunnel policy from kernel
*/
static void cleanup_policy(private_kernel_wfp_ipsec_t *this, bool transport,
u_int64_t policy)
{
if (transport)
{
FwpmFilterDeleteById0(this->handle, policy);
}
else
{
FWPM_PROVIDER_CONTEXT0 *ctx;
if (FwpmProviderContextGetById0(this->handle, policy,
&ctx) == ERROR_SUCCESS)
{
FwpmIPsecTunnelDeleteByKey0(this->handle, &ctx->providerContextKey);
FwpmFreeMemory0((void**)&ctx);
}
}
}
/**
* Remove policies associated to an entry from kernel
*/
@@ -262,12 +241,12 @@ static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
if (entry->policy_in)
{
cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_in);
FwpmFilterDeleteById0(this->handle, entry->policy_in);
entry->policy_in = 0;
}
if (entry->policy_out)
{
cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_out);
FwpmFilterDeleteById0(this->handle, entry->policy_out);
entry->policy_out = 0;
}
if (entry->mode == MODE_TUNNEL)
@@ -285,6 +264,10 @@ static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
}
if (entry->provider)
{
FwpmProviderContextDeleteById0(this->handle, entry->provider);
}
cleanup_policies(this, entry);
array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
entry->local->destroy(entry->local);
@@ -547,10 +530,10 @@ static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
}
/**
* Install transport mode SP to the kernel
* Install a single policy in to the kernel
*/
static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
entry_t *entry, bool inbound)
static bool install_sp(private_kernel_wfp_ipsec_t *this,
entry_t *entry, GUID *context, bool inbound)
{
FWPM_FILTER_CONDITION0 *conds = NULL;
int count = 0;
@@ -560,17 +543,41 @@ static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
DWORD res;
FWPM_FILTER0 filter = {
.displayData = {
.name = L"charon IPsec transport",
.name = L"charon IPsec policy",
},
.action = {
.type = FWP_ACTION_CALLOUT_TERMINATING,
.calloutKey = inbound ? FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 :
FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4,
},
.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 :
FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
};
if (context)
{
if (inbound)
{
filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4;
}
else
{
filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4;
}
filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
filter.providerKey = (GUID*)&this->provider.providerKey,
memcpy(&filter.providerContextKey, context, sizeof(GUID));
}
else
{
if (inbound)
{
filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4;
}
else
{
filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4;
}
}
enumerator = array_create_enumerator(entry->sps);
while (enumerator->enumerate(enumerator, &sp))
{
@@ -609,7 +616,8 @@ static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
free_conditions(conds, count);
if (res != ERROR_SUCCESS)
{
DBG1(DBG_KNL, "installing inbound FWP filter failed: 0x%08x", res);
DBG1(DBG_KNL, "installing %sbound FWP filter failed: 0x%08x",
inbound ? "in" : "out", res);
return FALSE;
}
return TRUE;
@@ -883,8 +891,8 @@ static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
}
else
{
traffic.tunnelPolicyId = entry->policy_in;
spi.inboundIpsecTraffic.tunnelPolicyId = entry->policy_in;
traffic.tunnelPolicyId = entry->provider;
spi.inboundIpsecTraffic.tunnelPolicyId = entry->provider;
}
if (!hosts2traffic(this, entry->local, entry->remote, &traffic))
@@ -963,8 +971,8 @@ static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
*/
static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
if (install_transport_sp(this, entry, TRUE) &&
install_transport_sp(this, entry, FALSE) &&
if (install_sp(this, entry, NULL, TRUE) &&
install_sp(this, entry, NULL, FALSE) &&
install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
{
return TRUE;
@@ -996,10 +1004,6 @@ static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
*/
static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
{
FWPM_FILTER_CONDITION0 *conds = NULL;
int count = 0;
enumerator_t *enumerator;
sp_entry_t *sp;
DWORD res;
IPSEC_AUTH_TRANSFORM0 transform = {
@@ -1031,7 +1035,7 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
.idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
},
};
FWPM_PROVIDER_CONTEXT0 *ctx, qm = {
FWPM_PROVIDER_CONTEXT0 qm = {
.displayData = {
.name = L"charon tunnel provider context",
},
@@ -1065,38 +1069,18 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
return FALSE;
}
enumerator = array_create_enumerator(entry->sps);
while (enumerator->enumerate(enumerator, &sp))
{
if (!ts2condition(sp->src, TRUE, &conds, &count) ||
!ts2condition(sp->dst, FALSE, &conds, &count))
{
free_conditions(conds, count);
enumerator->destroy(enumerator);
return FALSE;
}
}
enumerator->destroy(enumerator);
res = FwpmIPsecTunnelAdd0(this->handle, 0, NULL, &qm, count, conds, NULL);
free_conditions(conds, count);
res = FwpmProviderContextAdd0(this->handle, &qm, NULL, &entry->provider);
if (res != ERROR_SUCCESS)
{
DBG1(DBG_KNL, "installing FWP tunnel policy failed: 0x%08x", res);
DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res);
return FALSE;
}
/* to get the tunnelPolicyId LUID we have to query the context */
res = FwpmProviderContextGetByKey0(this->handle, &qm.providerContextKey,
&ctx);
if (res != ERROR_SUCCESS)
if (!install_sp(this, entry, &qm.providerContextKey, TRUE) ||
!install_sp(this, entry, &qm.providerContextKey, FALSE))
{
DBG1(DBG_KNL, "getting FWP tunnel policy context failed: 0x%08x", res);
return FALSE;
}
entry->policy_in = ctx->providerContextId;
FwpmFreeMemory0((void**)&ctx);
return TRUE;
}