diff options
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c')
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c | 361 |
1 files changed, 251 insertions, 110 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c index 5c956ea69..b6d7d7a58 100644 --- a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -178,6 +178,10 @@ typedef struct { u_int64_t policy_in; /** WFP allocated LUID for outbound filter ID */ u_int64_t policy_out; + /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */ + u_int64_t policy_fwd_in; + /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */ + u_int64_t policy_fwd_out; /** provider context, for tunnel mode only */ u_int64_t provider; /** WFP allocated LUID for SA context */ @@ -252,6 +256,16 @@ static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry) if (entry->mode == MODE_TUNNEL) { manage_routes(this, entry, FALSE); + if (entry->policy_fwd_in) + { + FwpmFilterDeleteById0(this->handle, entry->policy_fwd_in); + entry->policy_fwd_in = 0; + } + if (entry->policy_fwd_out) + { + FwpmFilterDeleteById0(this->handle, entry->policy_fwd_out); + entry->policy_fwd_out = 0; + } } } @@ -343,7 +357,7 @@ static void range2cond(FWPM_FILTER_CONDITION0 *cond, /** * (Re-)allocate filter conditions for given local or remote traffic selector */ -static bool ts2condition(traffic_selector_t *ts, bool local, +static bool ts2condition(traffic_selector_t *ts, const GUID *target, FWPM_FILTER_CONDITION0 *conds[], int *count) { FWPM_FILTER_CONDITION0 *cond; @@ -361,14 +375,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local, 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; - } + cond->fieldKey = *target; if (ts->is_host(ts, NULL)) { cond->matchType = FWP_MATCH_EQUAL; @@ -440,7 +447,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local, } proto = ts->get_protocol(ts); - if (proto && local) + if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) { cond = append_condition(conds, count); cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL; @@ -451,7 +458,7 @@ static bool ts2condition(traffic_selector_t *ts, bool local, if (proto == IPPROTO_ICMP) { - if (local) + if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) { u_int8_t from_type, to_type, from_code, to_code; @@ -476,16 +483,18 @@ static bool ts2condition(traffic_selector_t *ts, bool local, } else if (from_port != 0 || to_port != 0xFFFF) { - cond = append_condition(conds, count); - if (local) + if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) { + cond = append_condition(conds, count); cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; + range2cond(cond, from_port, to_port); } - else + if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS) { + cond = append_condition(conds, count); cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + range2cond(cond, from_port, to_port); } - range2cond(cond, from_port, to_port); } return TRUE; } @@ -530,16 +539,72 @@ 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 *callout) +{ + struct { + bool tunnel; + bool v6; + bool inbound; + bool forward; + const GUID *layer; + const GUID *callout; + } map[] = { + { 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 }, + { 0, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, + &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 }, + { 0, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 }, + { 0, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, + &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 }, + { 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 }, + { 1, 0, 0, 1, &FWPM_LAYER_IPFORWARD_V4, + &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 }, + { 1, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, + &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 }, + { 1, 0, 1, 1, &FWPM_LAYER_IPFORWARD_V4, + &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 }, + { 1, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 }, + { 1, 1, 0, 1, &FWPM_LAYER_IPFORWARD_V6, + &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 }, + { 1, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, + &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 }, + { 1, 1, 1, 1, &FWPM_LAYER_IPFORWARD_V6, + &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (tunnel == map[i].tunnel && + v6 == map[i].v6 && + inbound == map[i].inbound && + forward == map[i].forward) + { + *callout = *map[i].callout; + *layer = *map[i].layer; + return TRUE; + } + } + return FALSE; +} + +/** * Install a single policy in to the kernel */ -static bool install_sp(private_kernel_wfp_ipsec_t *this, - entry_t *entry, GUID *context, bool inbound) +static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp, + GUID *context, bool inbound, bool fwd, UINT64 *filter_id) { FWPM_FILTER_CONDITION0 *conds = NULL; - int count = 0; - enumerator_t *enumerator; traffic_selector_t *local, *remote; - sp_entry_t *sp; + const GUID *ltarget, *rtarget; + int count = 0; + bool v6; DWORD res; FWPM_FILTER0 filter = { .displayData = { @@ -548,78 +613,111 @@ static bool install_sp(private_kernel_wfp_ipsec_t *this, .action = { .type = FWP_ACTION_CALLOUT_TERMINATING, }, - .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)); + filter.providerKey = (GUID*)&this->provider.providerKey; + filter.providerContextKey = *context; + } + + v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE; + if (!find_callout(context != NULL, v6, inbound, fwd, + &filter.layerKey, &filter.action.calloutKey)) + { + return FALSE; + } + + if (inbound && fwd) + { + local = sp->dst; + remote = sp->src; } else { - if (inbound) - { - filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4; - } - else - { - filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4; - } + local = sp->src; + remote = sp->dst; + } + if (fwd) + { + ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS; + rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS; + } + else + { + ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS; + rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS; } + if (!ts2condition(local, ltarget, &conds, &count) || + !ts2condition(remote, rtarget, &conds, &count)) + { + free_conditions(conds, count); + return FALSE; + } + + 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 %s%sbound WFP filter failed: 0x%08x", + fwd ? "forward " : "", inbound ? "in" : "out", res); + return FALSE; + } + return TRUE; +} + +/** + * Install a set of policies in to the kernel + */ +static bool install_sps(private_kernel_wfp_ipsec_t *this, + entry_t *entry, GUID *context) +{ + enumerator_t *enumerator; + sp_entry_t *sp; enumerator = array_create_enumerator(entry->sps); while (enumerator->enumerate(enumerator, &sp)) { - if (inbound) + /* inbound policy */ + if (!install_sp(this, sp, context, TRUE, FALSE, &entry->policy_in)) { - local = sp->dst; - remote = sp->src; - } - else - { - local = sp->src; - remote = sp->dst; + enumerator->destroy(enumerator); + return FALSE; } - - if (!ts2condition(local, TRUE, &conds, &count) || - !ts2condition(remote, FALSE, &conds, &count)) + /* outbound policy */ + if (!install_sp(this, sp, context, FALSE, FALSE, &entry->policy_out)) { - free_conditions(conds, count); 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, &entry->policy_fwd_in)) + { + enumerator->destroy(enumerator); + return FALSE; + } + /* outbound forward policy, to encapsulate */ + if (!install_sp(this, sp, context, + FALSE, TRUE, &entry->policy_fwd_out)) + { + 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 %sbound FWP filter failed: 0x%08x", - inbound ? "in" : "out", res); - return FALSE; - } return TRUE; } @@ -971,8 +1069,7 @@ 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_sp(this, entry, NULL, TRUE) && - install_sp(this, entry, NULL, FALSE) && + if (install_sps(this, entry, NULL) && install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT)) { return TRUE; @@ -1000,9 +1097,10 @@ static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid) } /** - * Install tunnel mode SPs to the kernel + * Register a dummy tunnel provider to associate tunnel filters to */ -static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) +static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this, + entry_t *entry, GUID *guid, UINT64 *luid) { DWORD res; @@ -1037,7 +1135,7 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) }; FWPM_PROVIDER_CONTEXT0 qm = { .displayData = { - .name = L"charon tunnel provider context", + .name = L"charon tunnel provider", }, .providerKey = (GUID*)&this->provider.providerKey, .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT, @@ -1055,10 +1153,8 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) 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); + host2address6(entry->local, &policy.tunnelEndpoints.localV6Address); + host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address); break; default: return FALSE; @@ -1069,15 +1165,28 @@ static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) return FALSE; } - res = FwpmProviderContextAdd0(this->handle, &qm, NULL, &entry->provider); + res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid); if (res != ERROR_SUCCESS) { DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res); return FALSE; } + *guid = qm.providerContextKey; + return TRUE; +} - if (!install_sp(this, entry, &qm.providerContextKey, TRUE) || - !install_sp(this, entry, &qm.providerContextKey, FALSE)) +/** + * Install tunnel mode SPs to the kernel + */ +static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + GUID guid; + + if (!add_tunnel_provider(this, entry, &guid, &entry->provider)) + { + return FALSE; + } + if (!install_sps(this, entry, &guid)) { return FALSE; } @@ -1267,10 +1376,12 @@ static bool install(private_kernel_wfp_ipsec_t *this, entry_t *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; + /** is this a forward policy trap for tunnel mode? */ + bool fwd; + /** src traffic selector */ + traffic_selector_t *src; + /** dst traffic selector */ + traffic_selector_t *dst; /** LUID of installed tunnel policy filter */ UINT64 filter_id; } trap_t; @@ -1280,8 +1391,8 @@ typedef struct { */ static void destroy_trap(trap_t *this) { - this->local->destroy(this->local); - this->remote->destroy(this->remote); + this->src->destroy(this->src); + this->dst->destroy(this->dst); free(this); } @@ -1438,6 +1549,7 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) FWPM_FILTER_CONDITION0 *conds = NULL; int count = 0; DWORD res; + const GUID *starget, *dtarget; UINT64 weight = 0x000000000000ff00; FWPM_FILTER0 filter = { .displayData = { @@ -1452,18 +1564,35 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) }, }; - switch (trap->local->get_type(trap->local)) + if (trap->fwd) { - case TS_IPV4_ADDR_RANGE: + if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE) + { + filter.layerKey = FWPM_LAYER_IPFORWARD_V4; + } + else + { + filter.layerKey = FWPM_LAYER_IPFORWARD_V6; + } + starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS; + dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS; + } + else + { + if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE) + { filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4; - break; - case TS_IPV6_ADDR_RANGE: + } + else + { filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6; - break; + } + starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS; + dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS; } - if (!ts2condition(trap->local, TRUE, &conds, &count) || - !ts2condition(trap->remote, FALSE, &conds, &count)) + if (!ts2condition(trap->src, starget, &conds, &count) || + !ts2condition(trap->dst, dtarget, &conds, &count)) { free_conditions(conds, count); return FALSE; @@ -1476,7 +1605,7 @@ static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) free_conditions(conds, count); if (res != ERROR_SUCCESS) { - DBG1(DBG_KNL, "installing FWP trap filter failed: 0x%08x", res); + DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res); return FALSE; } return TRUE; @@ -1492,7 +1621,7 @@ static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) res = FwpmFilterDeleteById0(this->handle, trap->filter_id); if (res != ERROR_SUCCESS) { - DBG1(DBG_KNL, "uninstalling FWP trap filter failed: 0x%08x", res); + DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res); return FALSE; } return TRUE; @@ -1501,15 +1630,17 @@ static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) /** * 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) +static bool add_trap(private_kernel_wfp_ipsec_t *this, + u_int32_t reqid, bool fwd, + traffic_selector_t *src, traffic_selector_t *dst) { trap_t *trap; INIT(trap, .reqid = reqid, - .local = local->clone(local), - .remote = remote->clone(remote), + .fwd = fwd, + .src = src->clone(src), + .dst = dst->clone(dst), ); if (!install_trap(this, trap)) @@ -1526,8 +1657,9 @@ static bool add_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid, /** * 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) +static bool remove_trap(private_kernel_wfp_ipsec_t *this, + u_int32_t reqid, bool fwd, + traffic_selector_t *src, traffic_selector_t *dst) { enumerator_t *enumerator; trap_t *trap, *found = NULL; @@ -1537,8 +1669,9 @@ static bool remove_trap(private_kernel_wfp_ipsec_t *this, u_int32_t reqid, while (enumerator->enumerate(enumerator, NULL, &trap)) { if (reqid == trap->reqid && - local->equals(local, trap->local) && - remote->equals(remote, trap->remote)) + fwd == trap->fwd && + src->equals(src, trap->src) && + dst->equals(dst, trap->dst)) { this->traps->remove_at(this->traps, enumerator); found = trap; @@ -2014,11 +2147,18 @@ METHOD(kernel_ipsec_t, add_policy, status_t, case POLICY_PRIORITY_DEFAULT: break; case POLICY_PRIORITY_ROUTED: - if (add_trap(this, sa->reqid, src_ts, dst_ts)) + if (!add_trap(this, sa->reqid, FALSE, src_ts, dst_ts)) + { + return FAILED; + } + if (sa->mode == MODE_TUNNEL) { - return SUCCESS; + if (!add_trap(this, sa->reqid, TRUE, src_ts, dst_ts)) + { + return FAILED; + } } - return FAILED; + return SUCCESS; case POLICY_PRIORITY_FALLBACK: default: return NOT_SUPPORTED; @@ -2074,8 +2214,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, { if (direction == POLICY_OUT && priority == POLICY_PRIORITY_ROUTED) { - if (remove_trap(this, reqid, src_ts, dst_ts)) + if (remove_trap(this, reqid, FALSE, src_ts, dst_ts)) { + remove_trap(this, reqid, TRUE, src_ts, dst_ts); return SUCCESS; } return NOT_FOUND; @@ -2146,7 +2287,7 @@ static bool add_bypass(private_kernel_wfp_ipsec_t *this, free_conditions(conds, count); if (res != ERROR_SUCCESS) { - DBG1(DBG_KNL, "installing FWP trap filter failed: 0x%08x", res); + DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res); return FALSE; } return TRUE; |