mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-08 00:02:03 -04:00
This allows us to have multiple CHILD_SAs for the same reqid, and brings rekeying support.
1341 lines
32 KiB
C
1341 lines
32 KiB
C
/*
|
|
* Copyright (C) 2013 Martin Willi
|
|
* Copyright (C) 2013 revosec AG
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
/* Windows 7, for some fwpmu.h functionality */
|
|
#define _WIN32_WINNT 0x0601
|
|
|
|
#include "kernel_wfp_compat.h"
|
|
#include "kernel_wfp_ipsec.h"
|
|
|
|
#include <daemon.h>
|
|
#include <threading/mutex.h>
|
|
#include <collections/array.h>
|
|
#include <collections/hashtable.h>
|
|
|
|
|
|
typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
|
|
|
|
struct private_kernel_wfp_ipsec_t {
|
|
|
|
/**
|
|
* Public interface
|
|
*/
|
|
kernel_wfp_ipsec_t public;
|
|
|
|
/**
|
|
* Next SPI to allocate
|
|
*/
|
|
refcount_t nextspi;
|
|
|
|
/**
|
|
* Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
|
|
*/
|
|
hashtable_t *tsas;
|
|
|
|
/**
|
|
* SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
|
|
*/
|
|
hashtable_t *isas;
|
|
|
|
/**
|
|
* SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
|
|
*/
|
|
hashtable_t *osas;
|
|
|
|
/**
|
|
* Mutex for accessing entries
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* WFP session handle
|
|
*/
|
|
HANDLE handle;
|
|
|
|
/**
|
|
* Provider charon registers as
|
|
*/
|
|
FWPM_PROVIDER0 provider;
|
|
};
|
|
|
|
/**
|
|
* Security association entry
|
|
*/
|
|
typedef struct {
|
|
/** SPI for this SA */
|
|
u_int32_t spi;
|
|
/** protocol, IPPROTO_ESP/IPPROTO_AH */
|
|
u_int8_t protocol;
|
|
/** destination host address for this SPI */
|
|
host_t *dst;
|
|
struct {
|
|
/** algorithm */
|
|
u_int16_t alg;
|
|
/** key */
|
|
chunk_t key;
|
|
} integ, encr;
|
|
} sa_entry_t;
|
|
|
|
/**
|
|
* Hash function for sas lookup table
|
|
*/
|
|
static u_int hash_sa(sa_entry_t *key)
|
|
{
|
|
return chunk_hash_inc(chunk_from_thing(key->spi),
|
|
chunk_hash(key->dst->get_address(key->dst)));
|
|
}
|
|
|
|
/**
|
|
* equals function for sas lookup table
|
|
*/
|
|
static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
|
|
{
|
|
return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
|
|
}
|
|
|
|
/**
|
|
* Security policy entry
|
|
*/
|
|
typedef struct {
|
|
/** policy source addresses */
|
|
traffic_selector_t *src;
|
|
/** policy destinaiton addresses */
|
|
traffic_selector_t *dst;
|
|
} sp_entry_t;
|
|
|
|
/**
|
|
* Destroy an SP entry
|
|
*/
|
|
static void sp_entry_destroy(sp_entry_t *sp)
|
|
{
|
|
sp->src->destroy(sp->src);
|
|
sp->dst->destroy(sp->dst);
|
|
free(sp);
|
|
}
|
|
|
|
/**
|
|
* Collection of SA/SP database entries for a reqid
|
|
*/
|
|
typedef struct {
|
|
/** reqid of entry */
|
|
u_int32_t reqid;
|
|
/** outer address on local host */
|
|
host_t *local;
|
|
/** outer address on remote host */
|
|
host_t *remote;
|
|
/** inbound SA entry */
|
|
sa_entry_t isa;
|
|
/** outbound SA entry */
|
|
sa_entry_t osa;
|
|
/** associated (outbound) policies, as sp_entry_t* */
|
|
array_t *sps;
|
|
/** IPsec mode, tunnel|transport */
|
|
ipsec_mode_t mode;
|
|
/** UDP encapsulation */
|
|
bool encap;
|
|
/** WFP allocated LUID for inbound filter/tunnel policy ID */
|
|
u_int64_t policy_in;
|
|
/** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
|
|
u_int64_t policy_out;
|
|
/** WFP allocated LUID for SA context */
|
|
u_int64_t sa_id;
|
|
} entry_t;
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
|
{
|
|
if (entry->sa_id)
|
|
{
|
|
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
|
}
|
|
cleanup_policies(this, entry);
|
|
array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
|
|
entry->local->destroy(entry->local);
|
|
entry->remote->destroy(entry->remote);
|
|
chunk_clear(&entry->isa.integ.key);
|
|
chunk_clear(&entry->isa.encr.key);
|
|
chunk_clear(&entry->osa.integ.key);
|
|
chunk_clear(&entry->osa.encr.key);
|
|
free(entry);
|
|
}
|
|
|
|
/**
|
|
* Append/Realloc a filter condition to an existing condition set
|
|
*/
|
|
static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
|
|
int *count)
|
|
{
|
|
FWPM_FILTER_CONDITION0 *cond;
|
|
|
|
(*count)++;
|
|
*conds = realloc(*conds, *count * sizeof(*cond));
|
|
cond = *conds + *count - 1;
|
|
memset(cond, 0, sizeof(*cond));
|
|
|
|
return cond;
|
|
}
|
|
|
|
/**
|
|
* Convert an IPv4 prefix to a host order subnet mask
|
|
*/
|
|
static u_int32_t prefix2mask(u_int8_t prefix)
|
|
{
|
|
u_int8_t netmask[4] = {};
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(netmask); i++)
|
|
{
|
|
if (prefix < 8)
|
|
{
|
|
netmask[i] = 0xFF << (8 - prefix);
|
|
break;
|
|
}
|
|
netmask[i] = 0xFF;
|
|
prefix -= 8;
|
|
}
|
|
return untoh32(netmask);
|
|
}
|
|
|
|
/**
|
|
* Convert a 16-bit range to a WFP condition
|
|
*/
|
|
static void range2cond(FWPM_FILTER_CONDITION0 *cond,
|
|
u_int16_t from, u_int16_t to)
|
|
{
|
|
if (from == to)
|
|
{
|
|
cond->matchType = FWP_MATCH_EQUAL;
|
|
cond->conditionValue.type = FWP_UINT16;
|
|
cond->conditionValue.uint16 = from;
|
|
}
|
|
else
|
|
{
|
|
cond->matchType = FWP_MATCH_RANGE;
|
|
cond->conditionValue.type = FWP_RANGE_TYPE;
|
|
cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
|
|
cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
|
|
cond->conditionValue.rangeValue->valueLow.uint16 = from;
|
|
cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
|
|
cond->conditionValue.rangeValue->valueHigh.uint16 = to;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (Re-)allocate filter conditions for given local or remote traffic selector
|
|
*/
|
|
static bool ts2condition(traffic_selector_t *ts, bool local,
|
|
FWPM_FILTER_CONDITION0 *conds[], int *count)
|
|
{
|
|
FWPM_FILTER_CONDITION0 *cond;
|
|
FWP_BYTE_ARRAY16 *addr;
|
|
FWP_RANGE0 *range;
|
|
u_int16_t from_port, to_port;
|
|
void *from, *to;
|
|
u_int8_t proto;
|
|
host_t *net;
|
|
u_int8_t prefix;
|
|
|
|
from = ts->get_from_address(ts).ptr;
|
|
to = ts->get_to_address(ts).ptr;
|
|
from_port = ts->get_from_port(ts);
|
|
to_port = ts->get_to_port(ts);
|
|
|
|
cond = append_condition(conds, count);
|
|
if (local)
|
|
{
|
|
cond->fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
|
|
}
|
|
else
|
|
{
|
|
cond->fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
|
}
|
|
if (ts->is_host(ts, NULL))
|
|
{
|
|
cond->matchType = FWP_MATCH_EQUAL;
|
|
switch (ts->get_type(ts))
|
|
{
|
|
case TS_IPV4_ADDR_RANGE:
|
|
cond->conditionValue.type = FWP_UINT32;
|
|
cond->conditionValue.uint32 = untoh32(from);
|
|
break;
|
|
case TS_IPV6_ADDR_RANGE:
|
|
cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
|
|
cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
|
|
memcpy(addr, from, sizeof(*addr));
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (ts->to_subnet(ts, &net, &prefix))
|
|
{
|
|
FWP_V6_ADDR_AND_MASK *m6;
|
|
FWP_V4_ADDR_AND_MASK *m4;
|
|
|
|
cond->matchType = FWP_MATCH_EQUAL;
|
|
switch (net->get_family(net))
|
|
{
|
|
case AF_INET:
|
|
cond->conditionValue.type = FWP_V4_ADDR_MASK;
|
|
cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
|
|
m4->addr = untoh32(from);
|
|
m4->mask = prefix2mask(prefix);
|
|
break;
|
|
case AF_INET6:
|
|
cond->conditionValue.type = FWP_V6_ADDR_MASK;
|
|
cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
|
|
memcpy(m6->addr, from, sizeof(m6->addr));
|
|
m6->prefixLength = prefix;
|
|
break;
|
|
default:
|
|
net->destroy(net);
|
|
return FALSE;
|
|
}
|
|
net->destroy(net);
|
|
}
|
|
else
|
|
{
|
|
cond->matchType = FWP_MATCH_RANGE;
|
|
cond->conditionValue.type = FWP_RANGE_TYPE;
|
|
cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
|
|
switch (ts->get_type(ts))
|
|
{
|
|
case TS_IPV4_ADDR_RANGE:
|
|
range->valueLow.type = FWP_UINT32;
|
|
range->valueLow.uint32 = untoh32(from);
|
|
range->valueHigh.type = FWP_UINT32;
|
|
range->valueHigh.uint32 = untoh32(to);
|
|
break;
|
|
case TS_IPV6_ADDR_RANGE:
|
|
range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
|
|
range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
|
|
memcpy(addr, from, sizeof(*addr));
|
|
range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
|
|
range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
|
|
memcpy(addr, to, sizeof(*addr));
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
proto = ts->get_protocol(ts);
|
|
if (proto && local)
|
|
{
|
|
cond = append_condition(conds, count);
|
|
cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
|
cond->matchType = FWP_MATCH_EQUAL;
|
|
cond->conditionValue.type = FWP_UINT8;
|
|
cond->conditionValue.uint8 = proto;
|
|
}
|
|
|
|
if (proto == IPPROTO_ICMP)
|
|
{
|
|
if (local)
|
|
{
|
|
u_int8_t from_type, to_type, from_code, to_code;
|
|
|
|
from_type = traffic_selector_icmp_type(from_port);
|
|
to_type = traffic_selector_icmp_type(to_port);
|
|
from_code = traffic_selector_icmp_code(from_port);
|
|
to_code = traffic_selector_icmp_code(to_port);
|
|
|
|
if (from_type != 0 || to_type != 0xFF)
|
|
{
|
|
cond = append_condition(conds, count);
|
|
cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
|
|
range2cond(cond, from_type, to_type);
|
|
}
|
|
if (from_code != 0 || to_code != 0xFF)
|
|
{
|
|
cond = append_condition(conds, count);
|
|
cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
|
|
range2cond(cond, from_code, to_code);
|
|
}
|
|
}
|
|
}
|
|
else if (from_port != 0 || to_port != 0xFFFF)
|
|
{
|
|
cond = append_condition(conds, count);
|
|
if (local)
|
|
{
|
|
cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
|
}
|
|
else
|
|
{
|
|
cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
|
}
|
|
range2cond(cond, from_port, to_port);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Free memory associated to a single condition
|
|
*/
|
|
static void free_condition(FWP_DATA_TYPE type, void *value)
|
|
{
|
|
FWP_RANGE0 *range;
|
|
|
|
switch (type)
|
|
{
|
|
case FWP_BYTE_ARRAY16_TYPE:
|
|
case FWP_V4_ADDR_MASK:
|
|
case FWP_V6_ADDR_MASK:
|
|
free(value);
|
|
break;
|
|
case FWP_RANGE_TYPE:
|
|
range = value;
|
|
free_condition(range->valueLow.type, range->valueLow.sd);
|
|
free_condition(range->valueHigh.type, range->valueHigh.sd);
|
|
free(range);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Free memory used by a set of conditions
|
|
*/
|
|
static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
|
|
}
|
|
free(conds);
|
|
}
|
|
|
|
/**
|
|
* Install transport mode SP to the kernel
|
|
*/
|
|
static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
|
|
entry_t *entry, bool inbound)
|
|
{
|
|
FWPM_FILTER_CONDITION0 *conds = NULL;
|
|
int count = 0;
|
|
enumerator_t *enumerator;
|
|
traffic_selector_t *local, *remote;
|
|
sp_entry_t *sp;
|
|
DWORD res;
|
|
FWPM_FILTER0 filter = {
|
|
.displayData = {
|
|
.name = L"charon IPsec transport",
|
|
},
|
|
.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,
|
|
};
|
|
|
|
enumerator = array_create_enumerator(entry->sps);
|
|
while (enumerator->enumerate(enumerator, &sp))
|
|
{
|
|
if (inbound)
|
|
{
|
|
local = sp->dst;
|
|
remote = sp->src;
|
|
}
|
|
else
|
|
{
|
|
local = sp->src;
|
|
remote = sp->dst;
|
|
}
|
|
|
|
if (!ts2condition(local, TRUE, &conds, &count) ||
|
|
!ts2condition(remote, FALSE, &conds, &count))
|
|
{
|
|
free_conditions(conds, count);
|
|
enumerator->destroy(enumerator);
|
|
return FALSE;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
filter.numFilterConditions = count;
|
|
filter.filterCondition = conds;
|
|
|
|
if (inbound)
|
|
{
|
|
res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_in);
|
|
}
|
|
else
|
|
{
|
|
res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_out);
|
|
}
|
|
free_conditions(conds, count);
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
DBG1(DBG_KNL, "installing inbound FWP filter failed: 0x%08x", res);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Convert a chunk_t to a WFP FWP_BYTE_BLOB
|
|
*/
|
|
static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
|
|
{
|
|
return (FWP_BYTE_BLOB){
|
|
.size = chunk.len,
|
|
.data = chunk.ptr,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
|
|
*/
|
|
static bool alg2auth(integrity_algorithm_t alg,
|
|
IPSEC_SA_AUTH_INFORMATION0 *info)
|
|
{
|
|
struct {
|
|
integrity_algorithm_t alg;
|
|
IPSEC_AUTH_TRANSFORM_ID0 transform;
|
|
} map[] = {
|
|
{ AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
|
|
{ AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
|
|
{ AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
|
|
{ AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
|
|
{ AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
|
|
{ AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < countof(map); i++)
|
|
{
|
|
if (map[i].alg == alg)
|
|
{
|
|
info->authTransform.authTransformId = map[i].transform;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
|
|
*/
|
|
static bool alg2cipher(encryption_algorithm_t alg, int keylen,
|
|
IPSEC_SA_CIPHER_INFORMATION0 *info)
|
|
{
|
|
struct {
|
|
encryption_algorithm_t alg;
|
|
int keylen;
|
|
IPSEC_CIPHER_TRANSFORM_ID0 transform;
|
|
} map[] = {
|
|
{ ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
|
|
{ ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
|
|
{ ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
|
|
{ ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
|
|
{ ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
|
|
{ ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
|
|
{ ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
|
|
{ ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < countof(map); i++)
|
|
{
|
|
if (map[i].alg == alg && map[i].keylen == keylen)
|
|
{
|
|
info->cipherTransform.cipherTransformId = map[i].transform;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Get the integrity algorithm used for an AEAD transform
|
|
*/
|
|
static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
|
|
{
|
|
struct {
|
|
encryption_algorithm_t encr;
|
|
int keylen;
|
|
integrity_algorithm_t integ;
|
|
} map[] = {
|
|
{ ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
|
|
{ ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
|
|
{ ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
|
|
{ ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
|
|
{ ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
|
|
{ ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < countof(map); i++)
|
|
{
|
|
if (map[i].encr == encr && map[i].keylen == keylen)
|
|
{
|
|
return map[i].integ;
|
|
}
|
|
}
|
|
return AUTH_UNDEFINED;
|
|
}
|
|
|
|
/**
|
|
* Install a single SA
|
|
*/
|
|
static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
|
|
bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
|
|
{
|
|
IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
|
|
IPSEC_SA0 ipsec = {
|
|
.spi = ntohl(sa->spi),
|
|
};
|
|
IPSEC_SA_BUNDLE0 bundle = {
|
|
.saList = &ipsec,
|
|
.numSAs = 1,
|
|
.ipVersion = version,
|
|
};
|
|
struct {
|
|
u_int16_t alg;
|
|
chunk_t key;
|
|
} integ = {}, encr = {};
|
|
DWORD res;
|
|
|
|
switch (sa->protocol)
|
|
{
|
|
case IPPROTO_AH:
|
|
ipsec.saTransformType = IPSEC_TRANSFORM_AH;
|
|
ipsec.ahInformation = &info.saAuthInformation;
|
|
integ.key = sa->integ.key;
|
|
integ.alg = sa->integ.alg;
|
|
break;
|
|
case IPPROTO_ESP:
|
|
if (sa->encr.alg == ENCR_NULL ||
|
|
sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
|
|
{
|
|
ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
|
|
ipsec.espAuthInformation = &info.saAuthInformation;
|
|
}
|
|
else
|
|
{
|
|
ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
|
|
ipsec.espAuthAndCipherInformation = &info;
|
|
encr.key = sa->encr.key;
|
|
encr.alg = sa->encr.alg;
|
|
}
|
|
if (encryption_algorithm_is_aead(sa->encr.alg))
|
|
{
|
|
integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
|
|
integ.key = sa->encr.key;
|
|
}
|
|
else
|
|
{
|
|
integ.alg = sa->integ.alg;
|
|
integ.key = sa->integ.key;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (integ.alg)
|
|
{
|
|
info.saAuthInformation.authKey = chunk2blob(integ.key);
|
|
if (!alg2auth(integ.alg, &info.saAuthInformation))
|
|
{
|
|
DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
|
|
integrity_algorithm_names, integ.alg);
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (encr.alg)
|
|
{
|
|
info.saCipherInformation.cipherKey = chunk2blob(encr.key);
|
|
if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
|
|
{
|
|
DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
|
|
encryption_algorithm_names, encr.alg);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (inbound)
|
|
{
|
|
res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
|
|
}
|
|
else
|
|
{
|
|
res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
|
|
}
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
|
|
inbound ? "in" : "out", res);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Install SAs to the kernel
|
|
*/
|
|
static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
|
|
IPSEC_TRAFFIC_TYPE type)
|
|
{
|
|
IPSEC_TRAFFIC0 traffic = {
|
|
.trafficType = type,
|
|
};
|
|
IPSEC_GETSPI1 spi = {
|
|
.inboundIpsecTraffic = {
|
|
.trafficType = type,
|
|
},
|
|
};
|
|
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))
|
|
{
|
|
case AF_INET:
|
|
traffic.ipVersion = FWP_IP_VERSION_V4;
|
|
traffic.localV4Address =
|
|
untoh32(entry->local->get_address(entry->local).ptr);
|
|
traffic.remoteV4Address =
|
|
untoh32(entry->remote->get_address(entry->remote).ptr);
|
|
break;
|
|
case AF_INET6:
|
|
traffic.ipVersion = FWP_IP_VERSION_V6;
|
|
memcpy(&traffic.localV6Address,
|
|
entry->local->get_address(entry->local).ptr, 16);
|
|
memcpy(&traffic.remoteV6Address,
|
|
entry->remote->get_address(entry->remote).ptr, 16);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
res = IPsecSaContextCreate0(this->handle, &traffic, NULL, &entry->sa_id);
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
|
|
sizeof(traffic.localV6Address));
|
|
memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
|
|
sizeof(traffic.remoteV6Address));
|
|
spi.ipVersion = traffic.ipVersion;
|
|
|
|
res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
|
|
ntohl(entry->isa.spi));
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
|
|
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
|
entry->sa_id = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
|
|
!install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
|
|
{
|
|
IPsecSaContextDeleteById0(this->handle, entry->sa_id);
|
|
entry->sa_id = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Install a transport mode SA/SP set to the kernel
|
|
*/
|
|
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) &&
|
|
install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
|
|
{
|
|
return TRUE;
|
|
}
|
|
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)
|
|
{
|
|
return FALSE;
|
|
}
|
|
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))
|
|
{
|
|
case AF_INET:
|
|
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 (!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);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Install a SA/SP set to the kernel
|
|
*/
|
|
static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
|
{
|
|
switch (entry->mode)
|
|
{
|
|
case MODE_TRANSPORT:
|
|
return install_transport(this, entry);
|
|
case MODE_TUNNEL:
|
|
return install_tunnel(this, entry);
|
|
case MODE_BEET:
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
|
|
private_kernel_wfp_ipsec_t *this)
|
|
{
|
|
return KERNEL_ESP_V3_TFC;
|
|
}
|
|
|
|
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)
|
|
{
|
|
*spi = ref_get(&this->nextspi);
|
|
return SUCCESS;
|
|
}
|
|
|
|
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)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, add_sa, status_t,
|
|
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
|
|
u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
|
|
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, bool initiator, bool encap, bool esn, bool inbound,
|
|
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
|
|
{
|
|
host_t *local, *remote;
|
|
entry_t *entry;
|
|
|
|
if (inbound)
|
|
{
|
|
/* comes first, create new entry */
|
|
local = dst->clone(dst);
|
|
remote = src->clone(src);
|
|
|
|
INIT(entry,
|
|
.reqid = reqid,
|
|
.isa = {
|
|
.spi = spi,
|
|
.dst = local,
|
|
.protocol = protocol,
|
|
.encr = {
|
|
.alg = enc_alg,
|
|
.key = chunk_clone(enc_key),
|
|
},
|
|
.integ = {
|
|
.alg = int_alg,
|
|
.key = chunk_clone(int_key),
|
|
},
|
|
},
|
|
.sps = array_create(0, 0),
|
|
.local = local,
|
|
.remote = remote,
|
|
.mode = mode,
|
|
.encap = encap,
|
|
);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry);
|
|
this->isas->put(this->isas, &entry->isa, entry);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
else
|
|
{
|
|
/* comes after inbound, update entry */
|
|
this->mutex->lock(this->mutex);
|
|
entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)reqid);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (!entry)
|
|
{
|
|
DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
|
|
"for reqid %u ", reqid);
|
|
return NOT_FOUND;
|
|
}
|
|
/* TODO: should we check for local/remote, mode etc.? */
|
|
|
|
entry->osa = (sa_entry_t){
|
|
.spi = spi,
|
|
.dst = entry->remote,
|
|
.protocol = protocol,
|
|
.encr = {
|
|
.alg = enc_alg,
|
|
.key = chunk_clone(enc_key),
|
|
},
|
|
.integ = {
|
|
.alg = int_alg,
|
|
.key = chunk_clone(int_key),
|
|
},
|
|
};
|
|
|
|
this->mutex->lock(this->mutex);
|
|
this->osas->put(this->osas, &entry->osa, entry);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, update_sa, status_t,
|
|
private_kernel_wfp_ipsec_t *this, u_int32_t spi, u_int8_t protocol,
|
|
u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
|
|
bool encap, bool new_encap, mark_t mark)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, query_sa, status_t,
|
|
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
|
|
u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes,
|
|
u_int64_t *packets, time_t *time)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, del_sa, status_t,
|
|
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
|
|
u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark)
|
|
{
|
|
entry_t *entry;
|
|
sa_entry_t key = {
|
|
.dst = dst,
|
|
.spi = spi,
|
|
};
|
|
|
|
this->mutex->lock(this->mutex);
|
|
entry = this->isas->remove(this->isas, &key);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (entry)
|
|
{
|
|
/* keep entry until removal of outbound */
|
|
return SUCCESS;
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
entry = this->osas->remove(this->osas, &key);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (entry)
|
|
{
|
|
entry_destroy(this, entry);
|
|
return SUCCESS;
|
|
}
|
|
|
|
return NOT_FOUND;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, flush_sas, status_t,
|
|
private_kernel_wfp_ipsec_t *this)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, add_policy, status_t,
|
|
private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
|
|
traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
|
|
policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
|
|
policy_priority_t priority)
|
|
{
|
|
status_t status = SUCCESS;
|
|
entry_t *entry;
|
|
sp_entry_t *sp;
|
|
sa_entry_t key = {
|
|
.spi = sa->esp.use ? sa->esp.spi : sa->ah.spi,
|
|
.dst = dst,
|
|
};
|
|
|
|
if (sa->esp.use && sa->ah.use)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
switch (direction)
|
|
{
|
|
case POLICY_OUT:
|
|
break;
|
|
case POLICY_IN:
|
|
case POLICY_FWD:
|
|
/* not required */
|
|
return SUCCESS;
|
|
default:
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
switch (priority)
|
|
{
|
|
case POLICY_PRIORITY_DEFAULT:
|
|
break;
|
|
case POLICY_PRIORITY_FALLBACK:
|
|
/* TODO: install fallback policy? */
|
|
return SUCCESS;
|
|
case POLICY_PRIORITY_ROUTED:
|
|
/* TODO: install trap policy with low prio */
|
|
default:
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
entry = this->osas->get(this->osas, &key);
|
|
if (entry)
|
|
{
|
|
if (array_count(entry->sps) == 0)
|
|
{
|
|
INIT(sp,
|
|
.src = src_ts->clone(src_ts),
|
|
.dst = dst_ts->clone(dst_ts),
|
|
);
|
|
array_insert(entry->sps, -1, sp);
|
|
if (!install(this, entry))
|
|
{
|
|
status = FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* TODO: reinstall with a filter using multiple TS?
|
|
* Filters are ANDed for a match, but we could install a filter
|
|
* with the inverse TS set using NOT-matches... */
|
|
status = NOT_SUPPORTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
|
|
status = FAILED;
|
|
}
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
return status;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, query_policy, status_t,
|
|
private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
|
|
traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
|
|
time_t *use_time)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, del_policy, status_t,
|
|
private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
|
|
traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
|
|
mark_t mark, policy_priority_t priority)
|
|
{
|
|
/* not required, as we delete the whole SA/SP set during del_sa() */
|
|
return SUCCESS;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, flush_policies, status_t,
|
|
private_kernel_wfp_ipsec_t *this)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, bypass_socket, bool,
|
|
private_kernel_wfp_ipsec_t *this, int fd, int family)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, enable_udp_decap, bool,
|
|
private_kernel_wfp_ipsec_t *this, int fd, int family, u_int16_t port)
|
|
{
|
|
return NOT_SUPPORTED;
|
|
}
|
|
|
|
METHOD(kernel_ipsec_t, destroy, void,
|
|
private_kernel_wfp_ipsec_t *this)
|
|
{
|
|
if (this->handle)
|
|
{
|
|
FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
|
|
FwpmEngineClose0(this->handle);
|
|
}
|
|
this->tsas->destroy(this->tsas);
|
|
this->isas->destroy(this->isas);
|
|
this->osas->destroy(this->osas);
|
|
this->mutex->destroy(this->mutex);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
|
|
{
|
|
private_kernel_wfp_ipsec_t *this;
|
|
DWORD res;
|
|
FWPM_SESSION0 session = {
|
|
.displayData = {
|
|
.name = L"charon",
|
|
.description = L"strongSwan IKE kernel-wfp backend",
|
|
},
|
|
};
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.interface = {
|
|
.get_features = _get_features,
|
|
.get_spi = _get_spi,
|
|
.get_cpi = _get_cpi,
|
|
.add_sa = _add_sa,
|
|
.update_sa = _update_sa,
|
|
.query_sa = _query_sa,
|
|
.del_sa = _del_sa,
|
|
.flush_sas = _flush_sas,
|
|
.add_policy = _add_policy,
|
|
.query_policy = _query_policy,
|
|
.del_policy = _del_policy,
|
|
.flush_policies = _flush_policies,
|
|
.bypass_socket = _bypass_socket,
|
|
.enable_udp_decap = _enable_udp_decap,
|
|
.destroy = _destroy,
|
|
},
|
|
},
|
|
.provider = {
|
|
.displayData = {
|
|
.name = L"charon",
|
|
.description = L"strongSwan IKE kernel-wfp backend",
|
|
},
|
|
.providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
|
|
{ 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
|
|
},
|
|
.nextspi = htonl(0xc0000001),
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
|
|
.isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
|
|
.osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
|
|
);
|
|
|
|
res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
|
|
&this->handle);
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
|
|
res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
|
|
if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
|
|
{
|
|
DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
|
|
return &this->public;
|
|
}
|