kernel-wfp: Add support for tunnel mode connections
This commit is contained in:
@@ -45,4 +45,14 @@
|
|||||||
#define IPSEC_TRANSFORM_ESP_AUTH_FW 5
|
#define IPSEC_TRANSFORM_ESP_AUTH_FW 5
|
||||||
#define IPSEC_TRANSFORM_TYPE_MAX 6
|
#define IPSEC_TRANSFORM_TYPE_MAX 6
|
||||||
|
|
||||||
|
/* missing in MinGW */
|
||||||
|
enum {
|
||||||
|
FWPM_TUNNEL_FLAG_POINT_TO_POINT = (1<<0),
|
||||||
|
FWPM_TUNNEL_FLAG_ENABLE_VIRTUAL_IF_TUNNELING = (1<<1),
|
||||||
|
};
|
||||||
|
|
||||||
|
DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32,
|
||||||
|
const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32,
|
||||||
|
const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR);
|
||||||
|
|
||||||
#endif /** KERNEL_WFP_COMPAT_H_ @}*/
|
#endif /** KERNEL_WFP_COMPAT_H_ @}*/
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ typedef struct {
|
|||||||
bool encap;
|
bool encap;
|
||||||
/** WFP allocated LUID for inbound filter/tunnel policy ID */
|
/** WFP allocated LUID for inbound filter/tunnel policy ID */
|
||||||
u_int64_t policy_in;
|
u_int64_t policy_in;
|
||||||
/** WFP allocated LUID for outbound filter/tunnel policy ID */
|
/** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
|
||||||
u_int64_t policy_out;
|
u_int64_t policy_out;
|
||||||
/** WFP allocated LUID for SA context */
|
/** WFP allocated LUID for SA context */
|
||||||
u_int64_t sa_id;
|
u_int64_t sa_id;
|
||||||
@@ -181,6 +181,46 @@ static entry_t *entry_create(u_int32_t reqid, host_t *local, host_t *remote,
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
entry->policy_in = 0;
|
||||||
|
}
|
||||||
|
if (entry->policy_out)
|
||||||
|
{
|
||||||
|
cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_out);
|
||||||
|
entry->policy_out = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a SA/SP entry set
|
* Destroy a SA/SP entry set
|
||||||
*/
|
*/
|
||||||
@@ -190,14 +230,7 @@ static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
|||||||
{
|
{
|
||||||
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
||||||
}
|
}
|
||||||
if (entry->policy_in)
|
cleanup_policies(this, entry);
|
||||||
{
|
|
||||||
FwpmFilterDeleteById0(this->handle, entry->policy_in);
|
|
||||||
}
|
|
||||||
if (entry->policy_out)
|
|
||||||
{
|
|
||||||
FwpmFilterDeleteById0(this->handle, entry->policy_out);
|
|
||||||
}
|
|
||||||
array_destroy(entry->sas);
|
array_destroy(entry->sas);
|
||||||
array_destroy(entry->sps);
|
array_destroy(entry->sps);
|
||||||
entry->local->destroy(entry->local);
|
entry->local->destroy(entry->local);
|
||||||
@@ -662,10 +695,10 @@ static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a single transport mode SA
|
* Install a single SA
|
||||||
*/
|
*/
|
||||||
static bool install_transport_sa(private_kernel_wfp_ipsec_t *this,
|
static bool install_sa(private_kernel_wfp_ipsec_t *this,
|
||||||
entry_t *entry, sa_entry_t *sa, FWP_IP_VERSION version)
|
entry_t *entry, sa_entry_t *sa, FWP_IP_VERSION version)
|
||||||
{
|
{
|
||||||
IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
|
IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
|
||||||
IPSEC_SA0 ipsec = {
|
IPSEC_SA0 ipsec = {
|
||||||
@@ -758,18 +791,17 @@ static bool install_transport_sa(private_kernel_wfp_ipsec_t *this,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install transport mode SAs to the kernel
|
* Install SAs to the kernel
|
||||||
*/
|
*/
|
||||||
static bool install_transport_sas(private_kernel_wfp_ipsec_t *this,
|
static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
|
||||||
entry_t *entry)
|
IPSEC_TRAFFIC_TYPE type)
|
||||||
{
|
{
|
||||||
IPSEC_TRAFFIC0 traffic = {
|
IPSEC_TRAFFIC0 traffic = {
|
||||||
.trafficType = IPSEC_TRAFFIC_TYPE_TRANSPORT,
|
.trafficType = type,
|
||||||
};
|
};
|
||||||
IPSEC_GETSPI1 spi = {
|
IPSEC_GETSPI1 spi = {
|
||||||
.inboundIpsecTraffic = {
|
.inboundIpsecTraffic = {
|
||||||
.trafficType = IPSEC_TRAFFIC_TYPE_TRANSPORT,
|
.trafficType = type,
|
||||||
.ipsecFilterId = entry->policy_in,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
sa_entry_t *sa;
|
sa_entry_t *sa;
|
||||||
@@ -777,6 +809,17 @@ static bool install_transport_sas(private_kernel_wfp_ipsec_t *this,
|
|||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
DWORD res;
|
DWORD res;
|
||||||
|
|
||||||
|
if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
|
||||||
|
{
|
||||||
|
traffic.ipsecFilterId = entry->policy_out;
|
||||||
|
spi.inboundIpsecTraffic.ipsecFilterId = entry->policy_in;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
traffic.tunnelPolicyId = entry->policy_in;
|
||||||
|
spi.inboundIpsecTraffic.tunnelPolicyId = entry->policy_in;
|
||||||
|
}
|
||||||
|
|
||||||
switch (entry->local->get_family(entry->local))
|
switch (entry->local->get_family(entry->local))
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
@@ -797,7 +840,6 @@ static bool install_transport_sas(private_kernel_wfp_ipsec_t *this,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
traffic.ipsecFilterId = entry->policy_out;
|
|
||||||
res = IPsecSaContextCreate0(this->handle, &traffic, NULL, &entry->sa_id);
|
res = IPsecSaContextCreate0(this->handle, &traffic, NULL, &entry->sa_id);
|
||||||
if (res != ERROR_SUCCESS)
|
if (res != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
@@ -838,7 +880,7 @@ static bool install_transport_sas(private_kernel_wfp_ipsec_t *this,
|
|||||||
enumerator = array_create_enumerator(entry->sas);
|
enumerator = array_create_enumerator(entry->sas);
|
||||||
while (enumerator->enumerate(enumerator, &sa))
|
while (enumerator->enumerate(enumerator, &sa))
|
||||||
{
|
{
|
||||||
if (!install_transport_sa(this, entry, sa, spi.ipVersion))
|
if (!install_sa(this, entry, sa, spi.ipVersion))
|
||||||
{
|
{
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
||||||
@@ -858,20 +900,157 @@ static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
|||||||
{
|
{
|
||||||
if (install_transport_sp(this, entry, TRUE) &&
|
if (install_transport_sp(this, entry, TRUE) &&
|
||||||
install_transport_sp(this, entry, FALSE) &&
|
install_transport_sp(this, entry, FALSE) &&
|
||||||
install_transport_sas(this, entry))
|
install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
|
||||||
{
|
{
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
if (entry->policy_in)
|
cleanup_policies(this, entry);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new GUID, random
|
||||||
|
*/
|
||||||
|
static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
rng_t *rng;
|
||||||
|
|
||||||
|
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||||
|
if (!rng)
|
||||||
{
|
{
|
||||||
FwpmFilterDeleteById0(this->handle, entry->policy_in);
|
return FALSE;
|
||||||
entry->policy_in = 0;
|
|
||||||
}
|
}
|
||||||
if (entry->policy_out)
|
ok = rng->get_bytes(rng, sizeof(GUID), (u_int8_t*)guid);
|
||||||
|
rng->destroy(rng);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install tunnel mode SPs to the kernel
|
||||||
|
*/
|
||||||
|
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 = {
|
||||||
|
/* Create any valid proposal. This is actually not used, as we
|
||||||
|
* don't create an SA from this information. */
|
||||||
|
.authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
|
||||||
|
};
|
||||||
|
IPSEC_SA_TRANSFORM0 transforms = {
|
||||||
|
.ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
|
||||||
|
.espAuthTransform = &transform,
|
||||||
|
};
|
||||||
|
IPSEC_PROPOSAL0 proposal = {
|
||||||
|
.lifetime = {
|
||||||
|
/* We need a valid lifetime, even if we don't create any SA
|
||||||
|
* from these values. Pick some values accepted. */
|
||||||
|
.lifetimeSeconds = 0xFFFF,
|
||||||
|
.lifetimeKilobytes = 0xFFFFFFFF,
|
||||||
|
.lifetimePackets = 0xFFFFFFFF,
|
||||||
|
},
|
||||||
|
.numSaTransforms = 1,
|
||||||
|
.saTransforms = &transforms,
|
||||||
|
};
|
||||||
|
IPSEC_TUNNEL_POLICY0 policy = {
|
||||||
|
.numIpsecProposals = 1,
|
||||||
|
.ipsecProposals = &proposal,
|
||||||
|
.saIdleTimeout = {
|
||||||
|
/* not used, set to lifetime for maximum */
|
||||||
|
.idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
|
||||||
|
.idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
FWPM_PROVIDER_CONTEXT0 *ctx, qm = {
|
||||||
|
.displayData = {
|
||||||
|
.name = L"charon tunnel provider context",
|
||||||
|
},
|
||||||
|
.providerKey = (GUID*)&this->provider.providerKey,
|
||||||
|
.type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
|
||||||
|
.ikeQmTunnelPolicy = &policy,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (entry->local->get_family(entry->local))
|
||||||
{
|
{
|
||||||
FwpmFilterDeleteById0(this->handle, entry->policy_out);
|
case AF_INET:
|
||||||
entry->policy_out = 0;
|
policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
|
||||||
|
policy.tunnelEndpoints.localV4Address =
|
||||||
|
untoh32(entry->local->get_address(entry->local).ptr);
|
||||||
|
policy.tunnelEndpoints.remoteV4Address =
|
||||||
|
untoh32(entry->remote->get_address(entry->remote).ptr);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
|
||||||
|
memcpy(&policy.tunnelEndpoints.localV6Address,
|
||||||
|
entry->local->get_address(entry->local).ptr, 16);
|
||||||
|
memcpy(&policy.tunnelEndpoints.remoteV6Address,
|
||||||
|
entry->remote->get_address(entry->remote).ptr, 16);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!generate_guid(this, &qm.providerContextKey))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enumerator = array_create_enumerator(entry->sps);
|
||||||
|
while (enumerator->enumerate(enumerator, &sp))
|
||||||
|
{
|
||||||
|
if (sp->direction != POLICY_IN)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* TODO: verify OUT/FORWARD matches to IN? */
|
||||||
|
if (!ts2condition(sp->dst, TRUE, &conds, &count) ||
|
||||||
|
!ts2condition(sp->src, 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);
|
||||||
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
DBG1(DBG_KNL, "installing FWP tunnel policy 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)
|
||||||
|
{
|
||||||
|
DBG1(DBG_KNL, "getting FWP tunnel policy context failed: 0x%08x", res);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
entry->policy_in = ctx->providerContextId;
|
||||||
|
FwpmFreeMemory0((void**)&ctx);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install a tunnel mode SA/SP set to the kernel
|
||||||
|
*/
|
||||||
|
static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
||||||
|
{
|
||||||
|
if (install_tunnel_sps(this, entry) &&
|
||||||
|
install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
cleanup_policies(this, entry);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -885,6 +1064,7 @@ static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
|||||||
case MODE_TRANSPORT:
|
case MODE_TRANSPORT:
|
||||||
return install_transport(this, entry);
|
return install_transport(this, entry);
|
||||||
case MODE_TUNNEL:
|
case MODE_TUNNEL:
|
||||||
|
return install_tunnel(this, entry);
|
||||||
case MODE_BEET:
|
case MODE_BEET:
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|||||||
Reference in New Issue
Block a user