kernel-wfp: Add support for trap policies and acquires
This commit is contained in:
@@ -62,6 +62,20 @@ enum {
|
||||
IPSEC_SA_BUNDLE_UPDATE_MM_SA_ID = (1<<6),
|
||||
};
|
||||
|
||||
/* missing in MinGW */
|
||||
enum {
|
||||
FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET = (1<<0),
|
||||
FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET = (1<<1),
|
||||
FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET = (1<<2),
|
||||
FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET = (1<<3),
|
||||
FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET = (1<<4),
|
||||
FWPM_NET_EVENT_FLAG_APP_ID_SET = (1<<5),
|
||||
FWPM_NET_EVENT_FLAG_USER_ID_SET = (1<<6),
|
||||
FWPM_NET_EVENT_FLAG_SCOPE_ID_SET = (1<<7),
|
||||
FWPM_NET_EVENT_FLAG_IP_VERSION_SET = (1<<8),
|
||||
FWPM_NET_EVENT_FLAG_REAUTH_REASON_SET = (1<<9),
|
||||
};
|
||||
|
||||
DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32,
|
||||
const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32,
|
||||
const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR);
|
||||
|
||||
@@ -71,6 +71,11 @@ struct private_kernel_wfp_ipsec_t {
|
||||
*/
|
||||
hashtable_t *routes;
|
||||
|
||||
/**
|
||||
* Installed traps, as trap_t => trap_t
|
||||
*/
|
||||
hashtable_t *traps;
|
||||
|
||||
/**
|
||||
* Mutex for accessing entries
|
||||
*/
|
||||
@@ -1272,12 +1277,155 @@ static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installed trap entry
|
||||
*/
|
||||
typedef struct {
|
||||
/** reqid this trap is installed for */
|
||||
u_int32_t reqid;
|
||||
/** local traffic selector */
|
||||
traffic_selector_t *local;
|
||||
/** remote traffic selector */
|
||||
traffic_selector_t *remote;
|
||||
/** LUID of installed tunnel policy filter */
|
||||
UINT64 filter_id;
|
||||
} trap_t;
|
||||
|
||||
/**
|
||||
* Destroy a trap entry
|
||||
*/
|
||||
static void destroy_trap(trap_t *this)
|
||||
{
|
||||
this->local->destroy(this->local);
|
||||
this->remote->destroy(this->remote);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashtable equals function for traps
|
||||
*/
|
||||
static bool equals_trap(trap_t *a, trap_t *b)
|
||||
{
|
||||
return a->filter_id == b->filter_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashtable hash function for traps
|
||||
*/
|
||||
static u_int hash_trap(trap_t *trap)
|
||||
{
|
||||
return chunk_hash(chunk_from_thing(trap->filter_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an acquire for an installed trap filter
|
||||
*/
|
||||
static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
|
||||
traffic_selector_t *src, traffic_selector_t *dst)
|
||||
{
|
||||
u_int32_t reqid = 0;
|
||||
trap_t *trap, key = {
|
||||
.filter_id = filter_id,
|
||||
};
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
trap = this->traps->get(this->traps, &key);
|
||||
if (trap)
|
||||
{
|
||||
reqid = trap->reqid;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (reqid)
|
||||
{
|
||||
hydra->kernel_interface->acquire(hydra->kernel_interface, reqid,
|
||||
src->clone(src), dst->clone(dst));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single host traffic selector from an FWP address definition
|
||||
*/
|
||||
static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data,
|
||||
u_int8_t protocol, u_int16_t from_port, u_int16_t to_port)
|
||||
{
|
||||
ts_type_t type;
|
||||
UINT32 ints[4];
|
||||
chunk_t addr;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case FWP_IP_VERSION_V4:
|
||||
ints[0] = untoh32(data);
|
||||
addr = chunk_from_thing(ints[0]);
|
||||
type = TS_IPV4_ADDR_RANGE;
|
||||
break;
|
||||
case FWP_IP_VERSION_V6:
|
||||
ints[3] = untoh32(data);
|
||||
ints[2] = untoh32(data + 4);
|
||||
ints[1] = untoh32(data + 8);
|
||||
ints[0] = untoh32(data + 12);
|
||||
addr = chunk_from_thing(ints);
|
||||
type = TS_IPV6_ADDR_RANGE;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return traffic_selector_create_from_bytes(protocol, type, addr, from_port,
|
||||
addr, to_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* FwpmNetEventSubscribe0() callback
|
||||
*/
|
||||
static void event_callback(private_kernel_wfp_ipsec_t *this,
|
||||
const FWPM_NET_EVENT1 *event)
|
||||
{
|
||||
traffic_selector_t *local = NULL, *remote = NULL;
|
||||
u_int8_t protocol = 0;
|
||||
u_int16_t from_local = 0, to_local = 65535;
|
||||
u_int16_t from_remote = 0, to_remote = 65535;
|
||||
|
||||
if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) &&
|
||||
(event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
|
||||
{
|
||||
if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
|
||||
{
|
||||
from_local = to_local = event->header.localPort;
|
||||
}
|
||||
if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
|
||||
{
|
||||
from_remote = to_remote = event->header.remotePort;
|
||||
}
|
||||
if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)
|
||||
{
|
||||
protocol = event->header.ipProtocol;
|
||||
}
|
||||
|
||||
local = addr2ts(event->header.ipVersion,
|
||||
(void*)&event->header.localAddrV6,
|
||||
protocol, from_local, to_local);
|
||||
remote = addr2ts(event->header.ipVersion,
|
||||
(void*)&event->header.remoteAddrV6,
|
||||
protocol, from_remote, to_remote);
|
||||
}
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
|
||||
acquire(this, event->classifyDrop->filterId, local, remote);
|
||||
break;
|
||||
case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
|
||||
case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
|
||||
case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
|
||||
case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
|
||||
case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DESTROY_IF(local);
|
||||
DESTROY_IF(remote);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1298,6 +1446,133 @@ static bool register_events(private_kernel_wfp_ipsec_t *this)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a trap policy to kernel
|
||||
*/
|
||||
static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
|
||||
{
|
||||
FWPM_FILTER_CONDITION0 *conds = NULL;
|
||||
int count = 0;
|
||||
DWORD res;
|
||||
UINT64 weight = 0x000000000000ff00;
|
||||
FWPM_FILTER0 filter = {
|
||||
.displayData = {
|
||||
.name = L"charon IPsec trap",
|
||||
},
|
||||
.action = {
|
||||
.type = FWP_ACTION_BLOCK,
|
||||
},
|
||||
.weight = {
|
||||
.type = FWP_UINT64,
|
||||
.uint64 = &weight,
|
||||
},
|
||||
};
|
||||
|
||||
switch (trap->local->get_type(trap->local))
|
||||
{
|
||||
case TS_IPV4_ADDR_RANGE:
|
||||
filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
|
||||
break;
|
||||
case TS_IPV6_ADDR_RANGE:
|
||||
filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ts2condition(trap->local, TRUE, &conds, &count) ||
|
||||
!ts2condition(trap->remote, FALSE, &conds, &count))
|
||||
{
|
||||
free_conditions(conds, count);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
filter.numFilterConditions = count;
|
||||
filter.filterCondition = conds;
|
||||
|
||||
res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id);
|
||||
free_conditions(conds, count);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "installing FWP trap filter failed: 0x%08x", res);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall a trap policy from kernel
|
||||
*/
|
||||
static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
|
||||
{
|
||||
DWORD res;
|
||||
|
||||
res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
DBG1(DBG_KNL, "uninstalling FWP trap filter failed: 0x%08x", res);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and install a new trap entry
|
||||
*/
|
||||
static bool add_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
|
||||
traffic_selector_t *local, traffic_selector_t *remote)
|
||||
{
|
||||
trap_t *trap;
|
||||
|
||||
INIT(trap,
|
||||
.reqid = reqid,
|
||||
.local = local->clone(local),
|
||||
.remote = remote->clone(remote),
|
||||
);
|
||||
|
||||
if (!install_trap(this, trap))
|
||||
{
|
||||
destroy_trap(trap);
|
||||
return FALSE;
|
||||
}
|
||||
this->mutex->lock(this->mutex);
|
||||
this->traps->put(this->traps, trap, trap);
|
||||
this->mutex->unlock(this->mutex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall and remove a new trap entry
|
||||
*/
|
||||
static bool remove_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid,
|
||||
traffic_selector_t *local, traffic_selector_t *remote)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
trap_t *trap, *found = NULL;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
enumerator = this->traps->create_enumerator(this->traps);
|
||||
while (enumerator->enumerate(enumerator, NULL, &trap))
|
||||
{
|
||||
if (reqid == trap->reqid &&
|
||||
local->equals(local, trap->local) &&
|
||||
remote->equals(remote, trap->remote))
|
||||
{
|
||||
this->traps->remove_at(this->traps, enumerator);
|
||||
found = trap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (found)
|
||||
{
|
||||
uninstall_trap(this, found);
|
||||
destroy_trap(found);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
|
||||
private_kernel_wfp_ipsec_t *this)
|
||||
{
|
||||
@@ -1755,7 +2030,11 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
case POLICY_PRIORITY_DEFAULT:
|
||||
break;
|
||||
case POLICY_PRIORITY_ROUTED:
|
||||
/* TODO: install trap policy with low prio */
|
||||
if (add_trap(this, sa->reqid, src_ts, dst_ts))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
case POLICY_PRIORITY_FALLBACK:
|
||||
default:
|
||||
return NOT_SUPPORTED;
|
||||
@@ -1809,6 +2088,14 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
|
||||
mark_t mark, policy_priority_t priority)
|
||||
{
|
||||
if (direction == POLICY_OUT && priority == POLICY_PRIORITY_ROUTED)
|
||||
{
|
||||
if (remove_trap(this, reqid, src_ts, dst_ts))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
/* not required, as we delete the whole SA/SP set during del_sa() */
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -1956,6 +2243,7 @@ METHOD(kernel_ipsec_t, destroy, void,
|
||||
this->isas->destroy(this->isas);
|
||||
this->osas->destroy(this->osas);
|
||||
this->routes->destroy(this->routes);
|
||||
this->traps->destroy(this->traps);
|
||||
this->mutex->destroy(this->mutex);
|
||||
free(this);
|
||||
}
|
||||
@@ -2008,6 +2296,7 @@ kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
|
||||
.isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
|
||||
.osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
|
||||
.routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
|
||||
.traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4),
|
||||
);
|
||||
|
||||
if (!init_spi(this))
|
||||
|
||||
Reference in New Issue
Block a user