Annotation of embedaddon/strongswan/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2013 Martin Willi
! 3: * Copyright (C) 2013 revosec AG
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: /* Windows 7, for some fwpmu.h functionality */
! 17: #define _WIN32_WINNT 0x0601
! 18:
! 19: #include "kernel_wfp_compat.h"
! 20: #include "kernel_wfp_ipsec.h"
! 21:
! 22: #include <daemon.h>
! 23: #include <threading/mutex.h>
! 24: #include <collections/array.h>
! 25: #include <collections/hashtable.h>
! 26: #include <processing/jobs/callback_job.h>
! 27:
! 28: #ifndef IPPROTO_IPIP
! 29: #define IPPROTO_IPIP 4
! 30: #endif
! 31: #ifndef IPPROTO_IPV6
! 32: #define IPPROTO_IPV6 41
! 33: #endif
! 34:
! 35: typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
! 36:
! 37: struct private_kernel_wfp_ipsec_t {
! 38:
! 39: /**
! 40: * Public interface
! 41: */
! 42: kernel_wfp_ipsec_t public;
! 43:
! 44: /**
! 45: * Next SPI to allocate
! 46: */
! 47: refcount_t nextspi;
! 48:
! 49: /**
! 50: * Mix value to distribute SPI allocation randomly
! 51: */
! 52: uint32_t mixspi;
! 53:
! 54: /**
! 55: * IKE bypass filters, as UINT64 filter LUID
! 56: */
! 57: array_t *bypass;
! 58:
! 59: /**
! 60: * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
! 61: */
! 62: hashtable_t *tsas;
! 63:
! 64: /**
! 65: * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
! 66: */
! 67: hashtable_t *isas;
! 68:
! 69: /**
! 70: * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
! 71: */
! 72: hashtable_t *osas;
! 73:
! 74: /**
! 75: * Installed routes, as route_t => route_t
! 76: */
! 77: hashtable_t *routes;
! 78:
! 79: /**
! 80: * Installed traps, as trap_t => trap_t
! 81: */
! 82: hashtable_t *traps;
! 83:
! 84: /**
! 85: * Mutex for accessing entries
! 86: */
! 87: mutex_t *mutex;
! 88:
! 89: /**
! 90: * WFP session handle
! 91: */
! 92: HANDLE handle;
! 93:
! 94: /**
! 95: * Provider charon registers as
! 96: */
! 97: FWPM_PROVIDER0 provider;
! 98:
! 99: /**
! 100: * Event handle
! 101: */
! 102: HANDLE event;
! 103: };
! 104:
! 105: /**
! 106: * Security association entry
! 107: */
! 108: typedef struct {
! 109: /** SPI for this SA */
! 110: uint32_t spi;
! 111: /** protocol, IPPROTO_ESP/IPPROTO_AH */
! 112: uint8_t protocol;
! 113: /** hard lifetime of SA */
! 114: uint32_t lifetime;
! 115: /** destination host address for this SPI */
! 116: host_t *dst;
! 117: struct {
! 118: /** algorithm */
! 119: uint16_t alg;
! 120: /** key */
! 121: chunk_t key;
! 122: } integ, encr;
! 123: } sa_entry_t;
! 124:
! 125: /**
! 126: * Hash function for sas lookup table
! 127: */
! 128: static u_int hash_sa(sa_entry_t *key)
! 129: {
! 130: return chunk_hash_inc(chunk_from_thing(key->spi),
! 131: chunk_hash(key->dst->get_address(key->dst)));
! 132: }
! 133:
! 134: /**
! 135: * equals function for sas lookup table
! 136: */
! 137: static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
! 138: {
! 139: return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
! 140: }
! 141:
! 142: /**
! 143: * Security policy entry
! 144: */
! 145: typedef struct {
! 146: /** policy source addresses */
! 147: traffic_selector_t *src;
! 148: /** policy destination addresses */
! 149: traffic_selector_t *dst;
! 150: /** WFP allocated LUID for inbound filter ID */
! 151: uint64_t policy_in;
! 152: /** WFP allocated LUID for outbound filter ID */
! 153: uint64_t policy_out;
! 154: /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */
! 155: uint64_t policy_fwd_in;
! 156: /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */
! 157: uint64_t policy_fwd_out;
! 158: /** have installed a route for it? */
! 159: bool route;
! 160: } sp_entry_t;
! 161:
! 162: /**
! 163: * Destroy an SP entry
! 164: */
! 165: static void sp_entry_destroy(sp_entry_t *sp)
! 166: {
! 167: sp->src->destroy(sp->src);
! 168: sp->dst->destroy(sp->dst);
! 169: free(sp);
! 170: }
! 171:
! 172: /**
! 173: * Collection of SA/SP database entries for a reqid
! 174: */
! 175: typedef struct {
! 176: /** reqid of entry */
! 177: uint32_t reqid;
! 178: /** outer address on local host */
! 179: host_t *local;
! 180: /** outer address on remote host */
! 181: host_t *remote;
! 182: /** inbound SA entry */
! 183: sa_entry_t isa;
! 184: /** outbound SA entry */
! 185: sa_entry_t osa;
! 186: /** associated (outbound) policies, as sp_entry_t* */
! 187: array_t *sps;
! 188: /** IPsec mode, tunnel|transport */
! 189: ipsec_mode_t mode;
! 190: /** UDP encapsulation */
! 191: bool encap;
! 192: /** provider context, for tunnel mode only */
! 193: uint64_t provider;
! 194: /** WFP allocated LUID for SA context */
! 195: uint64_t sa_id;
! 196: /** WFP allocated LUID for tunnel mode IP-IPv4 inbound filter */
! 197: uint64_t ip_ipv4_in;
! 198: /** WFP allocated LUID for tunnel mode IP-IPv4 outbound filter */
! 199: uint64_t ip_ipv4_out;
! 200: /** WFP allocated LUID for tunnel mode IP-IPv6 inbound filter */
! 201: uint64_t ip_ipv6_in;
! 202: /** WFP allocated LUID for tunnel mode IP-IPv6 outbound filter */
! 203: uint64_t ip_ipv6_out;
! 204: } entry_t;
! 205:
! 206: /**
! 207: * Installed route
! 208: */
! 209: typedef struct {
! 210: /** destination net of route */
! 211: host_t *dst;
! 212: /** prefix length of dst */
! 213: uint8_t mask;
! 214: /** source address for route */
! 215: host_t *src;
! 216: /** gateway of route, NULL if directly attached */
! 217: host_t *gtw;
! 218: /** references for route */
! 219: u_int refs;
! 220: } route_t;
! 221:
! 222: /**
! 223: * Destroy a route_t
! 224: */
! 225: static void destroy_route(route_t *this)
! 226: {
! 227: this->dst->destroy(this->dst);
! 228: this->src->destroy(this->src);
! 229: DESTROY_IF(this->gtw);
! 230: free(this);
! 231: }
! 232:
! 233: /**
! 234: * Hashtable equals function for routes
! 235: */
! 236: static bool equals_route(route_t *a, route_t *b)
! 237: {
! 238: return a->mask == b->mask &&
! 239: a->dst->ip_equals(a->dst, b->dst) &&
! 240: a->src->ip_equals(a->src, b->src);
! 241: }
! 242:
! 243: /**
! 244: * Hashtable hash function for routes
! 245: */
! 246: static u_int hash_route(route_t *route)
! 247: {
! 248: return chunk_hash_inc(route->src->get_address(route->src),
! 249: chunk_hash_inc(route->dst->get_address(route->dst), route->mask));
! 250: }
! 251:
! 252: /** forward declaration */
! 253: static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
! 254: bool add);
! 255:
! 256: /**
! 257: * Remove policies associated to an entry from kernel
! 258: */
! 259: static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 260: {
! 261: enumerator_t *enumerator;
! 262: sp_entry_t *sp;
! 263:
! 264: if (entry->mode == MODE_TUNNEL)
! 265: {
! 266: manage_routes(this, entry, FALSE);
! 267: }
! 268:
! 269: enumerator = array_create_enumerator(entry->sps);
! 270: while (enumerator->enumerate(enumerator, &sp))
! 271: {
! 272: if (sp->policy_in)
! 273: {
! 274: FwpmFilterDeleteById0(this->handle, sp->policy_in);
! 275: sp->policy_in = 0;
! 276: }
! 277: if (sp->policy_out)
! 278: {
! 279: FwpmFilterDeleteById0(this->handle, sp->policy_out);
! 280: sp->policy_out = 0;
! 281: }
! 282: if (sp->policy_fwd_in)
! 283: {
! 284: FwpmFilterDeleteById0(this->handle, sp->policy_fwd_in);
! 285: sp->policy_fwd_in = 0;
! 286: }
! 287: if (sp->policy_fwd_out)
! 288: {
! 289: FwpmFilterDeleteById0(this->handle, sp->policy_fwd_out);
! 290: sp->policy_fwd_out = 0;
! 291: }
! 292: }
! 293: enumerator->destroy(enumerator);
! 294: }
! 295:
! 296: /**
! 297: * Destroy a SA/SP entry set
! 298: */
! 299: static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 300: {
! 301: if (entry->ip_ipv4_in)
! 302: {
! 303: FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_in);
! 304: }
! 305: if (entry->ip_ipv4_out)
! 306: {
! 307: FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_out);
! 308: }
! 309: if (entry->ip_ipv6_in)
! 310: {
! 311: FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_in);
! 312: }
! 313: if (entry->ip_ipv6_out)
! 314: {
! 315: FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_out);
! 316: }
! 317: if (entry->sa_id)
! 318: {
! 319: IPsecSaContextDeleteById0(this->handle, entry->sa_id);
! 320: }
! 321: if (entry->provider)
! 322: {
! 323: FwpmProviderContextDeleteById0(this->handle, entry->provider);
! 324: }
! 325: cleanup_policies(this, entry);
! 326: array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
! 327: entry->local->destroy(entry->local);
! 328: entry->remote->destroy(entry->remote);
! 329: chunk_clear(&entry->isa.integ.key);
! 330: chunk_clear(&entry->isa.encr.key);
! 331: chunk_clear(&entry->osa.integ.key);
! 332: chunk_clear(&entry->osa.encr.key);
! 333: free(entry);
! 334: }
! 335:
! 336: /**
! 337: * Append/Realloc a filter condition to an existing condition set
! 338: */
! 339: static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
! 340: int *count)
! 341: {
! 342: FWPM_FILTER_CONDITION0 *cond;
! 343:
! 344: (*count)++;
! 345: *conds = realloc(*conds, *count * sizeof(*cond));
! 346: cond = *conds + *count - 1;
! 347: memset(cond, 0, sizeof(*cond));
! 348:
! 349: return cond;
! 350: }
! 351:
! 352: /**
! 353: * Convert an IPv4 prefix to a host order subnet mask
! 354: */
! 355: static uint32_t prefix2mask(uint8_t prefix)
! 356: {
! 357: uint8_t netmask[4] = {};
! 358: int i;
! 359:
! 360: for (i = 0; i < sizeof(netmask); i++)
! 361: {
! 362: if (prefix < 8)
! 363: {
! 364: netmask[i] = 0xFF << (8 - prefix);
! 365: break;
! 366: }
! 367: netmask[i] = 0xFF;
! 368: prefix -= 8;
! 369: }
! 370: return untoh32(netmask);
! 371: }
! 372:
! 373: /**
! 374: * Convert a 16-bit range to a WFP condition
! 375: */
! 376: static void range2cond(FWPM_FILTER_CONDITION0 *cond,
! 377: uint16_t from, uint16_t to)
! 378: {
! 379: if (from == to)
! 380: {
! 381: cond->matchType = FWP_MATCH_EQUAL;
! 382: cond->conditionValue.type = FWP_UINT16;
! 383: cond->conditionValue.uint16 = from;
! 384: }
! 385: else
! 386: {
! 387: cond->matchType = FWP_MATCH_RANGE;
! 388: cond->conditionValue.type = FWP_RANGE_TYPE;
! 389: cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
! 390: cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
! 391: cond->conditionValue.rangeValue->valueLow.uint16 = from;
! 392: cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
! 393: cond->conditionValue.rangeValue->valueHigh.uint16 = to;
! 394: }
! 395: }
! 396:
! 397: /**
! 398: * (Re-)allocate filter conditions for given local or remote traffic selector
! 399: */
! 400: static bool ts2condition(traffic_selector_t *ts, const GUID *target,
! 401: FWPM_FILTER_CONDITION0 *conds[], int *count)
! 402: {
! 403: FWPM_FILTER_CONDITION0 *cond;
! 404: FWP_BYTE_ARRAY16 *addr;
! 405: FWP_RANGE0 *range;
! 406: uint16_t from_port, to_port;
! 407: void *from, *to;
! 408: uint8_t proto;
! 409: host_t *net;
! 410: uint8_t prefix;
! 411:
! 412: from = ts->get_from_address(ts).ptr;
! 413: to = ts->get_to_address(ts).ptr;
! 414: from_port = ts->get_from_port(ts);
! 415: to_port = ts->get_to_port(ts);
! 416:
! 417: cond = append_condition(conds, count);
! 418: cond->fieldKey = *target;
! 419: if (ts->is_host(ts, NULL))
! 420: {
! 421: cond->matchType = FWP_MATCH_EQUAL;
! 422: switch (ts->get_type(ts))
! 423: {
! 424: case TS_IPV4_ADDR_RANGE:
! 425: cond->conditionValue.type = FWP_UINT32;
! 426: cond->conditionValue.uint32 = untoh32(from);
! 427: break;
! 428: case TS_IPV6_ADDR_RANGE:
! 429: cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
! 430: cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
! 431: memcpy(addr, from, sizeof(*addr));
! 432: break;
! 433: default:
! 434: return FALSE;
! 435: }
! 436: }
! 437: else if (ts->to_subnet(ts, &net, &prefix))
! 438: {
! 439: FWP_V6_ADDR_AND_MASK *m6;
! 440: FWP_V4_ADDR_AND_MASK *m4;
! 441:
! 442: cond->matchType = FWP_MATCH_EQUAL;
! 443: switch (net->get_family(net))
! 444: {
! 445: case AF_INET:
! 446: cond->conditionValue.type = FWP_V4_ADDR_MASK;
! 447: cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
! 448: m4->addr = untoh32(from);
! 449: m4->mask = prefix2mask(prefix);
! 450: break;
! 451: case AF_INET6:
! 452: cond->conditionValue.type = FWP_V6_ADDR_MASK;
! 453: cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
! 454: memcpy(m6->addr, from, sizeof(m6->addr));
! 455: m6->prefixLength = prefix;
! 456: break;
! 457: default:
! 458: net->destroy(net);
! 459: return FALSE;
! 460: }
! 461: net->destroy(net);
! 462: }
! 463: else
! 464: {
! 465: cond->matchType = FWP_MATCH_RANGE;
! 466: cond->conditionValue.type = FWP_RANGE_TYPE;
! 467: cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
! 468: switch (ts->get_type(ts))
! 469: {
! 470: case TS_IPV4_ADDR_RANGE:
! 471: range->valueLow.type = FWP_UINT32;
! 472: range->valueLow.uint32 = untoh32(from);
! 473: range->valueHigh.type = FWP_UINT32;
! 474: range->valueHigh.uint32 = untoh32(to);
! 475: break;
! 476: case TS_IPV6_ADDR_RANGE:
! 477: range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
! 478: range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
! 479: memcpy(addr, from, sizeof(*addr));
! 480: range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
! 481: range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
! 482: memcpy(addr, to, sizeof(*addr));
! 483: break;
! 484: default:
! 485: return FALSE;
! 486: }
! 487: }
! 488:
! 489: proto = ts->get_protocol(ts);
! 490: if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
! 491: {
! 492: cond = append_condition(conds, count);
! 493: cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
! 494: cond->matchType = FWP_MATCH_EQUAL;
! 495: cond->conditionValue.type = FWP_UINT8;
! 496: cond->conditionValue.uint8 = proto;
! 497: }
! 498:
! 499: if (proto == IPPROTO_ICMP)
! 500: {
! 501: if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
! 502: {
! 503: uint8_t from_type, to_type, from_code, to_code;
! 504:
! 505: from_type = traffic_selector_icmp_type(from_port);
! 506: to_type = traffic_selector_icmp_type(to_port);
! 507: from_code = traffic_selector_icmp_code(from_port);
! 508: to_code = traffic_selector_icmp_code(to_port);
! 509:
! 510: if (from_type != 0 || to_type != 0xFF)
! 511: {
! 512: cond = append_condition(conds, count);
! 513: cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
! 514: range2cond(cond, from_type, to_type);
! 515: }
! 516: if (from_code != 0 || to_code != 0xFF)
! 517: {
! 518: cond = append_condition(conds, count);
! 519: cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
! 520: range2cond(cond, from_code, to_code);
! 521: }
! 522: }
! 523: }
! 524: else if (from_port != 0 || to_port != 0xFFFF)
! 525: {
! 526: if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
! 527: {
! 528: cond = append_condition(conds, count);
! 529: cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
! 530: range2cond(cond, from_port, to_port);
! 531: }
! 532: if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS)
! 533: {
! 534: cond = append_condition(conds, count);
! 535: cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
! 536: range2cond(cond, from_port, to_port);
! 537: }
! 538: }
! 539: return TRUE;
! 540: }
! 541:
! 542: /**
! 543: * Free memory associated to a single condition
! 544: */
! 545: static void free_condition(FWP_DATA_TYPE type, void *value)
! 546: {
! 547: FWP_RANGE0 *range;
! 548:
! 549: switch (type)
! 550: {
! 551: case FWP_BYTE_ARRAY16_TYPE:
! 552: case FWP_V4_ADDR_MASK:
! 553: case FWP_V6_ADDR_MASK:
! 554: free(value);
! 555: break;
! 556: case FWP_RANGE_TYPE:
! 557: range = value;
! 558: free_condition(range->valueLow.type, range->valueLow.sd);
! 559: free_condition(range->valueHigh.type, range->valueHigh.sd);
! 560: free(range);
! 561: break;
! 562: default:
! 563: break;
! 564: }
! 565: }
! 566:
! 567: /**
! 568: * Free memory used by a set of conditions
! 569: */
! 570: static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
! 571: {
! 572: int i;
! 573:
! 574: for (i = 0; i < count; i++)
! 575: {
! 576: free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
! 577: }
! 578: free(conds);
! 579: }
! 580:
! 581: /**
! 582: * Find the callout GUID for given parameters
! 583: */
! 584: static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
! 585: bool ale, GUID *layer, GUID *sublayer, GUID *callout)
! 586: {
! 587: struct {
! 588: bool tunnel;
! 589: bool v6;
! 590: bool inbound;
! 591: bool forward;
! 592: bool ale;
! 593: const GUID *layer;
! 594: const GUID *sublayer;
! 595: const GUID *callout;
! 596: } map[] = {
! 597: { 0, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL,
! 598: &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 },
! 599: { 0, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL,
! 600: &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 },
! 601: { 0, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL,
! 602: &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 },
! 603: { 0, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL,
! 604: &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 },
! 605: { 1, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
! 606: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 607: &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 },
! 608: { 1, 0, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
! 609: &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
! 610: &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 },
! 611: { 1, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4,
! 612: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 613: &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 },
! 614: { 1, 0, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
! 615: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 616: &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 },
! 617: { 1, 0, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, NULL,
! 618: &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V4 },
! 619: { 1, 0, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, NULL,
! 620: &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V4},
! 621: { 1, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
! 622: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 623: &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 },
! 624: { 1, 1, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
! 625: &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
! 626: &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 },
! 627: { 1, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6,
! 628: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 629: &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 },
! 630: { 1, 1, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
! 631: &FWPM_SUBLAYER_IPSEC_TUNNEL,
! 632: &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 },
! 633: { 1, 1, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, NULL,
! 634: &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V6 },
! 635: { 1, 1, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, NULL,
! 636: &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V6},
! 637: };
! 638: int i;
! 639:
! 640: for (i = 0; i < countof(map); i++)
! 641: {
! 642: if (tunnel == map[i].tunnel &&
! 643: v6 == map[i].v6 &&
! 644: inbound == map[i].inbound &&
! 645: forward == map[i].forward &&
! 646: ale == map[i].ale)
! 647: {
! 648: *callout = *map[i].callout;
! 649: *layer = *map[i].layer;
! 650: if (map[i].sublayer)
! 651: {
! 652: *sublayer = *map[i].sublayer;
! 653: }
! 654: return TRUE;
! 655: }
! 656: }
! 657: return FALSE;
! 658: }
! 659:
! 660: /**
! 661: * Install a single policy in to the kernel
! 662: */
! 663: static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
! 664: GUID *context, bool inbound, bool fwd, UINT64 *filter_id)
! 665: {
! 666: FWPM_FILTER_CONDITION0 *conds = NULL;
! 667: traffic_selector_t *local, *remote;
! 668: const GUID *ltarget, *rtarget;
! 669: int count = 0;
! 670: bool v6;
! 671: DWORD res;
! 672: FWPM_FILTER0 filter = {
! 673: .displayData = {
! 674: .name = L"charon IPsec policy",
! 675: },
! 676: .action = {
! 677: .type = FWP_ACTION_CALLOUT_TERMINATING,
! 678: },
! 679: };
! 680:
! 681: if (context)
! 682: {
! 683: filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
! 684: filter.providerKey = (GUID*)&this->provider.providerKey;
! 685: filter.providerContextKey = *context;
! 686: }
! 687:
! 688: v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE;
! 689: if (!find_callout(context != NULL, v6, inbound, fwd, FALSE,
! 690: &filter.layerKey, &filter.subLayerKey,
! 691: &filter.action.calloutKey))
! 692: {
! 693: return FALSE;
! 694: }
! 695:
! 696: if (inbound && fwd)
! 697: {
! 698: local = sp->dst;
! 699: remote = sp->src;
! 700: }
! 701: else
! 702: {
! 703: local = sp->src;
! 704: remote = sp->dst;
! 705: }
! 706: if (fwd)
! 707: {
! 708: ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
! 709: rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
! 710: }
! 711: else
! 712: {
! 713: ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
! 714: rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
! 715: }
! 716: if (!ts2condition(local, ltarget, &conds, &count) ||
! 717: !ts2condition(remote, rtarget, &conds, &count))
! 718: {
! 719: free_conditions(conds, count);
! 720: return FALSE;
! 721: }
! 722:
! 723: filter.numFilterConditions = count;
! 724: filter.filterCondition = conds;
! 725:
! 726: res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
! 727: free_conditions(conds, count);
! 728: if (res != ERROR_SUCCESS)
! 729: {
! 730: DBG1(DBG_KNL, "installing IPv%d %s%sbound %s WFP filter failed: 0x%08x",
! 731: v6 ? 6 : 4, fwd ? "forward " : "", inbound ? "in" : "out",
! 732: context ? "tunnel" : "transport", res);
! 733: return FALSE;
! 734: }
! 735: return TRUE;
! 736: }
! 737:
! 738: /**
! 739: * Install an IP-IP allow filter for SA specific hosts
! 740: */
! 741: static bool install_ipip_ale(private_kernel_wfp_ipsec_t *this,
! 742: host_t *local, host_t *remote, GUID *context,
! 743: bool inbound, int proto, uint64_t *filter_id)
! 744: {
! 745: traffic_selector_t *lts, *rts;
! 746: FWPM_FILTER_CONDITION0 *conds = NULL;
! 747: int count = 0;
! 748: bool v6;
! 749: DWORD res;
! 750: FWPM_FILTER0 filter = {
! 751: .displayData = {
! 752: .name = L"charon IPsec IP-in-IP ALE policy",
! 753: },
! 754: .action = {
! 755: .type = FWP_ACTION_CALLOUT_TERMINATING,
! 756: },
! 757: };
! 758:
! 759: if (context)
! 760: {
! 761: filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
! 762: filter.providerKey = (GUID*)&this->provider.providerKey;
! 763: filter.providerContextKey = *context;
! 764: }
! 765:
! 766: v6 = local->get_family(local) == AF_INET6;
! 767: if (!find_callout(TRUE, v6, inbound, FALSE, TRUE, &filter.layerKey,
! 768: &filter.subLayerKey, &filter.action.calloutKey))
! 769: {
! 770: return FALSE;
! 771: }
! 772:
! 773: lts = traffic_selector_create_from_subnet(local->clone(local),
! 774: v6 ? 128 : 32 , proto, 0, 65535);
! 775: rts = traffic_selector_create_from_subnet(remote->clone(remote),
! 776: v6 ? 128 : 32 , proto, 0, 65535);
! 777: if (!ts2condition(lts, &FWPM_CONDITION_IP_LOCAL_ADDRESS, &conds, &count) ||
! 778: !ts2condition(rts, &FWPM_CONDITION_IP_REMOTE_ADDRESS, &conds, &count))
! 779: {
! 780: free_conditions(conds, count);
! 781: lts->destroy(lts);
! 782: rts->destroy(rts);
! 783: return FALSE;
! 784: }
! 785: lts->destroy(lts);
! 786: rts->destroy(rts);
! 787:
! 788: filter.numFilterConditions = count;
! 789: filter.filterCondition = conds;
! 790:
! 791: res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
! 792: free_conditions(conds, count);
! 793: if (res != ERROR_SUCCESS)
! 794: {
! 795: DBG1(DBG_KNL, "installing IP-IPv%d %s ALE WFP filter failed: 0x%08x",
! 796: v6 ? 6 : 4, inbound ? "inbound" : "outbound", res);
! 797: return FALSE;
! 798: }
! 799: return TRUE;
! 800: }
! 801:
! 802: /**
! 803: * Install a set of policies in to the kernel
! 804: */
! 805: static bool install_sps(private_kernel_wfp_ipsec_t *this,
! 806: entry_t *entry, GUID *context)
! 807: {
! 808: enumerator_t *enumerator;
! 809: sp_entry_t *sp;
! 810: bool has_v4 = FALSE, has_v6 = FALSE;
! 811:
! 812: enumerator = array_create_enumerator(entry->sps);
! 813: while (enumerator->enumerate(enumerator, &sp))
! 814: {
! 815: switch (sp->src->get_type(sp->src))
! 816: {
! 817: case TS_IPV4_ADDR_RANGE:
! 818: has_v4 = TRUE;
! 819: break;
! 820: case TS_IPV6_ADDR_RANGE:
! 821: has_v6 = TRUE;
! 822: break;
! 823: }
! 824:
! 825: /* inbound policy */
! 826: if (!install_sp(this, sp, context, TRUE, FALSE, &sp->policy_in))
! 827: {
! 828: enumerator->destroy(enumerator);
! 829: return FALSE;
! 830: }
! 831: /* outbound policy */
! 832: if (!install_sp(this, sp, context, FALSE, FALSE, &sp->policy_out))
! 833: {
! 834: enumerator->destroy(enumerator);
! 835: return FALSE;
! 836: }
! 837:
! 838: if (context)
! 839: {
! 840: if (!sp->src->is_host(sp->src, entry->local) ||
! 841: !sp->dst->is_host(sp->dst, entry->remote))
! 842: {
! 843: /* inbound forward policy, from decapsulation */
! 844: if (!install_sp(this, sp, context, TRUE, TRUE,
! 845: &sp->policy_fwd_in))
! 846: {
! 847: enumerator->destroy(enumerator);
! 848: return FALSE;
! 849: }
! 850: /* outbound forward policy, to encapsulate */
! 851: if (!install_sp(this, sp, context, FALSE, TRUE,
! 852: &sp->policy_fwd_out))
! 853: {
! 854: enumerator->destroy(enumerator);
! 855: return FALSE;
! 856: }
! 857: }
! 858: }
! 859: }
! 860: enumerator->destroy(enumerator);
! 861:
! 862: if (context)
! 863: {
! 864: /* In tunnel mode, Windows does firewall filtering on decrypted but
! 865: * non-unwrapped packets: It sees them as IP-in-IP packets. When using
! 866: * a default-drop policy, we need to allow such packets explicitly. */
! 867: if (has_v4)
! 868: {
! 869: if (!install_ipip_ale(this, entry->local, entry->remote, context,
! 870: TRUE, IPPROTO_IPIP, &entry->ip_ipv4_in))
! 871: {
! 872: return FALSE;
! 873: }
! 874: if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
! 875: FALSE, IPPROTO_IPIP, &entry->ip_ipv4_out))
! 876: {
! 877: return FALSE;
! 878: }
! 879: }
! 880: if (has_v6)
! 881: {
! 882: if (!install_ipip_ale(this, entry->local, entry->remote, context,
! 883: TRUE, IPPROTO_IPV6, &entry->ip_ipv6_in))
! 884: {
! 885: return FALSE;
! 886: }
! 887: if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
! 888: FALSE, IPPROTO_IPV6, &entry->ip_ipv6_out))
! 889: {
! 890: return FALSE;
! 891: }
! 892: }
! 893: }
! 894: return TRUE;
! 895: }
! 896:
! 897: /**
! 898: * Convert a chunk_t to a WFP FWP_BYTE_BLOB
! 899: */
! 900: static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
! 901: {
! 902: return (FWP_BYTE_BLOB){
! 903: .size = chunk.len,
! 904: .data = chunk.ptr,
! 905: };
! 906: }
! 907:
! 908: /**
! 909: * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
! 910: */
! 911: static bool alg2auth(integrity_algorithm_t alg,
! 912: IPSEC_SA_AUTH_INFORMATION0 *info)
! 913: {
! 914: struct {
! 915: integrity_algorithm_t alg;
! 916: IPSEC_AUTH_TRANSFORM_ID0 transform;
! 917: } map[] = {
! 918: { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
! 919: { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
! 920: { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
! 921: { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
! 922: { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
! 923: { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
! 924: };
! 925: int i;
! 926:
! 927: for (i = 0; i < countof(map); i++)
! 928: {
! 929: if (map[i].alg == alg)
! 930: {
! 931: info->authTransform.authTransformId = map[i].transform;
! 932: return TRUE;
! 933: }
! 934: }
! 935: return FALSE;
! 936: }
! 937:
! 938: /**
! 939: * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
! 940: */
! 941: static bool alg2cipher(encryption_algorithm_t alg, int keylen,
! 942: IPSEC_SA_CIPHER_INFORMATION0 *info)
! 943: {
! 944: struct {
! 945: encryption_algorithm_t alg;
! 946: int keylen;
! 947: IPSEC_CIPHER_TRANSFORM_ID0 transform;
! 948: } map[] = {
! 949: { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
! 950: { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
! 951: { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
! 952: { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
! 953: { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
! 954: { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
! 955: { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
! 956: { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
! 957: };
! 958: int i;
! 959:
! 960: for (i = 0; i < countof(map); i++)
! 961: {
! 962: if (map[i].alg == alg && map[i].keylen == keylen)
! 963: {
! 964: info->cipherTransform.cipherTransformId = map[i].transform;
! 965: return TRUE;
! 966: }
! 967: }
! 968: return FALSE;
! 969: }
! 970:
! 971: /**
! 972: * Get the integrity algorithm used for an AEAD transform
! 973: */
! 974: static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
! 975: {
! 976: struct {
! 977: encryption_algorithm_t encr;
! 978: int keylen;
! 979: integrity_algorithm_t integ;
! 980: } map[] = {
! 981: { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
! 982: { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
! 983: { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
! 984: { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
! 985: { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
! 986: { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
! 987: };
! 988: int i;
! 989:
! 990: for (i = 0; i < countof(map); i++)
! 991: {
! 992: if (map[i].encr == encr && map[i].keylen == keylen)
! 993: {
! 994: return map[i].integ;
! 995: }
! 996: }
! 997: return AUTH_UNDEFINED;
! 998: }
! 999:
! 1000: /**
! 1001: * Install a single SA
! 1002: */
! 1003: static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
! 1004: bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
! 1005: {
! 1006: IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
! 1007: IPSEC_SA0 ipsec = {
! 1008: .spi = ntohl(sa->spi),
! 1009: };
! 1010: IPSEC_SA_BUNDLE0 bundle = {
! 1011: .lifetime = {
! 1012: .lifetimeSeconds = inbound ? entry->isa.lifetime
! 1013: : entry->osa.lifetime,
! 1014: },
! 1015: .saList = &ipsec,
! 1016: .numSAs = 1,
! 1017: .ipVersion = version,
! 1018: };
! 1019: struct {
! 1020: uint16_t alg;
! 1021: chunk_t key;
! 1022: } integ = {}, encr = {};
! 1023: DWORD res;
! 1024:
! 1025: switch (sa->protocol)
! 1026: {
! 1027: case IPPROTO_AH:
! 1028: ipsec.saTransformType = IPSEC_TRANSFORM_AH;
! 1029: ipsec.ahInformation = &info.saAuthInformation;
! 1030: integ.key = sa->integ.key;
! 1031: integ.alg = sa->integ.alg;
! 1032: break;
! 1033: case IPPROTO_ESP:
! 1034: if (sa->encr.alg == ENCR_NULL ||
! 1035: sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
! 1036: {
! 1037: ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
! 1038: ipsec.espAuthInformation = &info.saAuthInformation;
! 1039: }
! 1040: else
! 1041: {
! 1042: ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
! 1043: ipsec.espAuthAndCipherInformation = &info;
! 1044: encr.key = sa->encr.key;
! 1045: encr.alg = sa->encr.alg;
! 1046: }
! 1047: if (encryption_algorithm_is_aead(sa->encr.alg))
! 1048: {
! 1049: integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
! 1050: integ.key = sa->encr.key;
! 1051: }
! 1052: else
! 1053: {
! 1054: integ.alg = sa->integ.alg;
! 1055: integ.key = sa->integ.key;
! 1056: }
! 1057: break;
! 1058: default:
! 1059: return FALSE;
! 1060: }
! 1061:
! 1062: if (integ.alg)
! 1063: {
! 1064: info.saAuthInformation.authKey = chunk2blob(integ.key);
! 1065: if (!alg2auth(integ.alg, &info.saAuthInformation))
! 1066: {
! 1067: DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
! 1068: integrity_algorithm_names, integ.alg);
! 1069: return FALSE;
! 1070: }
! 1071: }
! 1072: if (encr.alg)
! 1073: {
! 1074: info.saCipherInformation.cipherKey = chunk2blob(encr.key);
! 1075: if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
! 1076: {
! 1077: DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
! 1078: encryption_algorithm_names, encr.alg);
! 1079: return FALSE;
! 1080: }
! 1081: }
! 1082:
! 1083: if (inbound)
! 1084: {
! 1085: res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
! 1086: }
! 1087: else
! 1088: {
! 1089: bundle.flags |= IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND;
! 1090: res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
! 1091: }
! 1092: if (res != ERROR_SUCCESS)
! 1093: {
! 1094: DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
! 1095: inbound ? "in" : "out", res);
! 1096: return FALSE;
! 1097: }
! 1098: return TRUE;
! 1099: }
! 1100:
! 1101: /**
! 1102: * Convert an IPv6 host address to WFP representation
! 1103: */
! 1104: static void host2address6(host_t *host, void *out)
! 1105: {
! 1106: uint32_t *src, *dst = out;
! 1107:
! 1108: src = (uint32_t*)host->get_address(host).ptr;
! 1109:
! 1110: dst[0] = untoh32(&src[3]);
! 1111: dst[1] = untoh32(&src[2]);
! 1112: dst[2] = untoh32(&src[1]);
! 1113: dst[3] = untoh32(&src[0]);
! 1114: }
! 1115:
! 1116: /**
! 1117: * Fill in traffic structure from entry addresses
! 1118: */
! 1119: static bool hosts2traffic(private_kernel_wfp_ipsec_t *this,
! 1120: host_t *l, host_t *r, IPSEC_TRAFFIC1 *traffic)
! 1121: {
! 1122: if (l->get_family(l) != r->get_family(r))
! 1123: {
! 1124: return FALSE;
! 1125: }
! 1126: switch (l->get_family(l))
! 1127: {
! 1128: case AF_INET:
! 1129: traffic->ipVersion = FWP_IP_VERSION_V4;
! 1130: traffic->localV4Address = untoh32(l->get_address(l).ptr);
! 1131: traffic->remoteV4Address = untoh32(r->get_address(r).ptr);
! 1132: return TRUE;
! 1133: case AF_INET6:
! 1134: traffic->ipVersion = FWP_IP_VERSION_V6;
! 1135: host2address6(l, &traffic->localV6Address);
! 1136: host2address6(r, &traffic->remoteV6Address);
! 1137: return TRUE;
! 1138: default:
! 1139: return FALSE;
! 1140: }
! 1141: }
! 1142:
! 1143: /**
! 1144: * Install SAs to the kernel
! 1145: */
! 1146: static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
! 1147: IPSEC_TRAFFIC_TYPE type)
! 1148: {
! 1149: IPSEC_TRAFFIC1 traffic = {
! 1150: .trafficType = type,
! 1151: };
! 1152: IPSEC_GETSPI1 spi = {
! 1153: .inboundIpsecTraffic = {
! 1154: .trafficType = type,
! 1155: },
! 1156: };
! 1157: enumerator_t *enumerator;
! 1158: sp_entry_t *sp;
! 1159: DWORD res;
! 1160:
! 1161: if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
! 1162: {
! 1163: enumerator = array_create_enumerator(entry->sps);
! 1164: if (enumerator->enumerate(enumerator, &sp))
! 1165: {
! 1166: traffic.ipsecFilterId = sp->policy_out;
! 1167: spi.inboundIpsecTraffic.ipsecFilterId = sp->policy_in;
! 1168: }
! 1169: else
! 1170: {
! 1171: enumerator->destroy(enumerator);
! 1172: return FALSE;
! 1173: }
! 1174: enumerator->destroy(enumerator);
! 1175: }
! 1176: else
! 1177: {
! 1178: traffic.tunnelPolicyId = entry->provider;
! 1179: spi.inboundIpsecTraffic.tunnelPolicyId = entry->provider;
! 1180: }
! 1181:
! 1182: if (!hosts2traffic(this, entry->local, entry->remote, &traffic))
! 1183: {
! 1184: return FALSE;
! 1185: }
! 1186:
! 1187: res = IPsecSaContextCreate1(this->handle, &traffic, NULL, NULL,
! 1188: &entry->sa_id);
! 1189: if (res != ERROR_SUCCESS)
! 1190: {
! 1191: DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
! 1192: return FALSE;
! 1193: }
! 1194:
! 1195: memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
! 1196: sizeof(traffic.localV6Address));
! 1197: memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
! 1198: sizeof(traffic.remoteV6Address));
! 1199: spi.ipVersion = traffic.ipVersion;
! 1200:
! 1201: res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
! 1202: ntohl(entry->isa.spi));
! 1203: if (res != ERROR_SUCCESS)
! 1204: {
! 1205: DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
! 1206: IPsecSaContextDeleteById0(this->handle, entry->sa_id);
! 1207: entry->sa_id = 0;
! 1208: return FALSE;
! 1209: }
! 1210:
! 1211: if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
! 1212: !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
! 1213: {
! 1214: IPsecSaContextDeleteById0(this->handle, entry->sa_id);
! 1215: entry->sa_id = 0;
! 1216: return FALSE;
! 1217: }
! 1218:
! 1219: if (entry->encap)
! 1220: {
! 1221: IPSEC_V4_UDP_ENCAPSULATION0 encap = {
! 1222: .localUdpEncapPort = entry->local->get_port(entry->local),
! 1223: .remoteUdpEncapPort = entry->remote->get_port(entry->remote),
! 1224: };
! 1225: IPSEC_SA_CONTEXT1 *ctx;
! 1226:
! 1227: res = IPsecSaContextGetById1(this->handle, entry->sa_id, &ctx);
! 1228: if (res != ERROR_SUCCESS)
! 1229: {
! 1230: DBG1(DBG_KNL, "getting WFP SA for UDP encap failed: 0x%08x", res);
! 1231: IPsecSaContextDeleteById0(this->handle, entry->sa_id);
! 1232: entry->sa_id = 0;
! 1233: return FALSE;
! 1234: }
! 1235: ctx->inboundSa->udpEncapsulation = &encap;
! 1236: ctx->outboundSa->udpEncapsulation = &encap;
! 1237:
! 1238: res = IPsecSaContextUpdate0(this->handle,
! 1239: IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION, ctx);
! 1240: FwpmFreeMemory0((void**)&ctx);
! 1241: if (res != ERROR_SUCCESS)
! 1242: {
! 1243: DBG1(DBG_KNL, "enable WFP UDP encap failed: 0x%08x", res);
! 1244: IPsecSaContextDeleteById0(this->handle, entry->sa_id);
! 1245: entry->sa_id = 0;
! 1246: return FALSE;
! 1247: }
! 1248: }
! 1249:
! 1250: return TRUE;
! 1251: }
! 1252:
! 1253: /**
! 1254: * Install a transport mode SA/SP set to the kernel
! 1255: */
! 1256: static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 1257: {
! 1258: if (install_sps(this, entry, NULL) &&
! 1259: install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
! 1260: {
! 1261: return TRUE;
! 1262: }
! 1263: cleanup_policies(this, entry);
! 1264: return FALSE;
! 1265: }
! 1266:
! 1267: /**
! 1268: * Generate a new GUID, random
! 1269: */
! 1270: static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
! 1271: {
! 1272: bool ok;
! 1273: rng_t *rng;
! 1274:
! 1275: rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
! 1276: if (!rng)
! 1277: {
! 1278: return FALSE;
! 1279: }
! 1280: ok = rng->get_bytes(rng, sizeof(GUID), (uint8_t*)guid);
! 1281: rng->destroy(rng);
! 1282: return ok;
! 1283: }
! 1284:
! 1285: /**
! 1286: * Register a dummy tunnel provider to associate tunnel filters to
! 1287: */
! 1288: static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this,
! 1289: entry_t *entry, GUID *guid, UINT64 *luid)
! 1290: {
! 1291: DWORD res;
! 1292:
! 1293: IPSEC_AUTH_TRANSFORM0 transform = {
! 1294: /* Create any valid proposal. This is actually not used, as we
! 1295: * don't create an SA from this information. */
! 1296: .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
! 1297: };
! 1298: IPSEC_SA_TRANSFORM0 transforms = {
! 1299: .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
! 1300: .espAuthTransform = &transform,
! 1301: };
! 1302: IPSEC_PROPOSAL0 proposal = {
! 1303: .lifetime = {
! 1304: /* We need a valid lifetime, even if we don't create any SA
! 1305: * from these values. Pick some values accepted. */
! 1306: .lifetimeSeconds = 0xFFFF,
! 1307: .lifetimeKilobytes = 0xFFFFFFFF,
! 1308: .lifetimePackets = 0xFFFFFFFF,
! 1309: },
! 1310: .numSaTransforms = 1,
! 1311: .saTransforms = &transforms,
! 1312: };
! 1313: IPSEC_TUNNEL_POLICY0 policy = {
! 1314: .numIpsecProposals = 1,
! 1315: .ipsecProposals = &proposal,
! 1316: .saIdleTimeout = {
! 1317: /* not used, set to lifetime for maximum */
! 1318: .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
! 1319: .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
! 1320: },
! 1321: };
! 1322: FWPM_PROVIDER_CONTEXT0 qm = {
! 1323: .displayData = {
! 1324: .name = L"charon tunnel provider",
! 1325: },
! 1326: .providerKey = (GUID*)&this->provider.providerKey,
! 1327: .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
! 1328: .ikeQmTunnelPolicy = &policy,
! 1329: };
! 1330:
! 1331: switch (entry->local->get_family(entry->local))
! 1332: {
! 1333: case AF_INET:
! 1334: policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
! 1335: policy.tunnelEndpoints.localV4Address =
! 1336: untoh32(entry->local->get_address(entry->local).ptr);
! 1337: policy.tunnelEndpoints.remoteV4Address =
! 1338: untoh32(entry->remote->get_address(entry->remote).ptr);
! 1339: break;
! 1340: case AF_INET6:
! 1341: policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
! 1342: host2address6(entry->local, &policy.tunnelEndpoints.localV6Address);
! 1343: host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address);
! 1344: break;
! 1345: default:
! 1346: return FALSE;
! 1347: }
! 1348:
! 1349: if (!generate_guid(this, &qm.providerContextKey))
! 1350: {
! 1351: return FALSE;
! 1352: }
! 1353:
! 1354: res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid);
! 1355: if (res != ERROR_SUCCESS)
! 1356: {
! 1357: DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res);
! 1358: return FALSE;
! 1359: }
! 1360: *guid = qm.providerContextKey;
! 1361: return TRUE;
! 1362: }
! 1363:
! 1364: /**
! 1365: * Install tunnel mode SPs to the kernel
! 1366: */
! 1367: static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 1368: {
! 1369: GUID guid;
! 1370:
! 1371: if (!add_tunnel_provider(this, entry, &guid, &entry->provider))
! 1372: {
! 1373: return FALSE;
! 1374: }
! 1375: if (!install_sps(this, entry, &guid))
! 1376: {
! 1377: return FALSE;
! 1378: }
! 1379: return TRUE;
! 1380: }
! 1381:
! 1382: /**
! 1383: * Reduce refcount, or uninstall a route if all refs gone
! 1384: */
! 1385: static bool uninstall_route(private_kernel_wfp_ipsec_t *this,
! 1386: host_t *dst, uint8_t mask, host_t *src, host_t *gtw)
! 1387: {
! 1388: route_t *route, key = {
! 1389: .dst = dst,
! 1390: .mask = mask,
! 1391: .src = src,
! 1392: };
! 1393: char *name;
! 1394: bool res = FALSE;
! 1395:
! 1396: this->mutex->lock(this->mutex);
! 1397: route = this->routes->get(this->routes, &key);
! 1398: if (route)
! 1399: {
! 1400: if (--route->refs == 0)
! 1401: {
! 1402: if (charon->kernel->get_interface(charon->kernel, src, &name))
! 1403: {
! 1404: res = charon->kernel->del_route(charon->kernel,
! 1405: dst->get_address(dst), mask, gtw, src,
! 1406: name, FALSE) == SUCCESS;
! 1407: free(name);
! 1408: }
! 1409: route = this->routes->remove(this->routes, route);
! 1410: if (route)
! 1411: {
! 1412: destroy_route(route);
! 1413: }
! 1414: }
! 1415: else
! 1416: {
! 1417: res = TRUE;
! 1418: }
! 1419: }
! 1420: this->mutex->unlock(this->mutex);
! 1421:
! 1422: return res;
! 1423: }
! 1424:
! 1425: /**
! 1426: * Install a single route, or refcount if exists
! 1427: */
! 1428: static bool install_route(private_kernel_wfp_ipsec_t *this,
! 1429: host_t *dst, uint8_t mask, host_t *src, host_t *gtw)
! 1430: {
! 1431: route_t *route, key = {
! 1432: .dst = dst,
! 1433: .mask = mask,
! 1434: .src = src,
! 1435: };
! 1436: char *name;
! 1437: bool res = FALSE;
! 1438:
! 1439: this->mutex->lock(this->mutex);
! 1440: route = this->routes->get(this->routes, &key);
! 1441: if (route)
! 1442: {
! 1443: route->refs++;
! 1444: res = TRUE;
! 1445: }
! 1446: else
! 1447: {
! 1448: if (charon->kernel->get_interface(charon->kernel, src, &name))
! 1449: {
! 1450: if (charon->kernel->add_route(charon->kernel, dst->get_address(dst),
! 1451: mask, gtw, src, name, FALSE) == SUCCESS)
! 1452: {
! 1453: INIT(route,
! 1454: .dst = dst->clone(dst),
! 1455: .mask = mask,
! 1456: .src = src->clone(src),
! 1457: .gtw = gtw ? gtw->clone(gtw) : NULL,
! 1458: .refs = 1,
! 1459: );
! 1460: route = this->routes->put(this->routes, route, route);
! 1461: if (route)
! 1462: {
! 1463: destroy_route(route);
! 1464: }
! 1465: res = TRUE;
! 1466: }
! 1467: free(name);
! 1468: }
! 1469: }
! 1470: this->mutex->unlock(this->mutex);
! 1471:
! 1472: return res;
! 1473: }
! 1474:
! 1475: /**
! 1476: * (Un)-install a single route
! 1477: */
! 1478: static bool manage_route(private_kernel_wfp_ipsec_t *this,
! 1479: host_t *local, host_t *remote,
! 1480: traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
! 1481: bool add)
! 1482: {
! 1483: host_t *src, *dst, *gtw;
! 1484: uint8_t mask;
! 1485: bool done;
! 1486:
! 1487: if (!dst_ts->to_subnet(dst_ts, &dst, &mask))
! 1488: {
! 1489: return FALSE;
! 1490: }
! 1491: if (charon->kernel->get_address_by_ts(charon->kernel, src_ts, &src,
! 1492: NULL) != SUCCESS)
! 1493: {
! 1494: dst->destroy(dst);
! 1495: return FALSE;
! 1496: }
! 1497: gtw = charon->kernel->get_nexthop(charon->kernel, remote, -1, local, NULL);
! 1498: if (add)
! 1499: {
! 1500: done = install_route(this, dst, mask, src, gtw);
! 1501: }
! 1502: else
! 1503: {
! 1504: done = uninstall_route(this, dst, mask, src, gtw);
! 1505: }
! 1506: dst->destroy(dst);
! 1507: src->destroy(src);
! 1508: DESTROY_IF(gtw);
! 1509:
! 1510: if (!done)
! 1511: {
! 1512: DBG1(DBG_KNL, "%sinstalling route for policy %R === %R failed",
! 1513: add ? "" : "un", src_ts, dst_ts);
! 1514: }
! 1515: return done;
! 1516: }
! 1517:
! 1518: /**
! 1519: * (Un)-install routes for IPsec policies
! 1520: */
! 1521: static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
! 1522: bool add)
! 1523: {
! 1524: enumerator_t *enumerator;
! 1525: sp_entry_t *sp;
! 1526:
! 1527: enumerator = array_create_enumerator(entry->sps);
! 1528: while (enumerator->enumerate(enumerator, &sp))
! 1529: {
! 1530: if (add && sp->route)
! 1531: {
! 1532: continue;
! 1533: }
! 1534: if (!add && !sp->route)
! 1535: {
! 1536: continue;
! 1537: }
! 1538: if (manage_route(this, entry->local, entry->remote,
! 1539: sp->src, sp->dst, add))
! 1540: {
! 1541: sp->route = add;
! 1542: }
! 1543: }
! 1544: enumerator->destroy(enumerator);
! 1545:
! 1546: return TRUE;
! 1547: }
! 1548:
! 1549: /**
! 1550: * Install a tunnel mode SA/SP set to the kernel
! 1551: */
! 1552: static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 1553: {
! 1554: if (install_tunnel_sps(this, entry) &&
! 1555: manage_routes(this, entry, TRUE) &&
! 1556: install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
! 1557: {
! 1558: return TRUE;
! 1559: }
! 1560: cleanup_policies(this, entry);
! 1561: return FALSE;
! 1562: }
! 1563:
! 1564: /**
! 1565: * Install a SA/SP set to the kernel
! 1566: */
! 1567: static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
! 1568: {
! 1569: switch (entry->mode)
! 1570: {
! 1571: case MODE_TRANSPORT:
! 1572: return install_transport(this, entry);
! 1573: case MODE_TUNNEL:
! 1574: return install_tunnel(this, entry);
! 1575: case MODE_BEET:
! 1576: default:
! 1577: return FALSE;
! 1578: }
! 1579: }
! 1580:
! 1581: /**
! 1582: * Installed trap entry
! 1583: */
! 1584: typedef struct {
! 1585: /** reqid this trap is installed for */
! 1586: uint32_t reqid;
! 1587: /** is this a forward policy trap for tunnel mode? */
! 1588: bool fwd;
! 1589: /** do we have installed a route for this trap policy? */
! 1590: bool route;
! 1591: /** local address of associated route */
! 1592: host_t *local;
! 1593: /** remote address of associated route */
! 1594: host_t *remote;
! 1595: /** src traffic selector */
! 1596: traffic_selector_t *src;
! 1597: /** dst traffic selector */
! 1598: traffic_selector_t *dst;
! 1599: /** LUID of installed tunnel policy filter */
! 1600: UINT64 filter_id;
! 1601: } trap_t;
! 1602:
! 1603: /**
! 1604: * Destroy a trap entry
! 1605: */
! 1606: static void destroy_trap(trap_t *this)
! 1607: {
! 1608: this->local->destroy(this->local);
! 1609: this->remote->destroy(this->remote);
! 1610: this->src->destroy(this->src);
! 1611: this->dst->destroy(this->dst);
! 1612: free(this);
! 1613: }
! 1614:
! 1615: /**
! 1616: * Hashtable equals function for traps
! 1617: */
! 1618: static bool equals_trap(trap_t *a, trap_t *b)
! 1619: {
! 1620: return a->filter_id == b->filter_id;
! 1621: }
! 1622:
! 1623: /**
! 1624: * Hashtable hash function for traps
! 1625: */
! 1626: static u_int hash_trap(trap_t *trap)
! 1627: {
! 1628: return chunk_hash(chunk_from_thing(trap->filter_id));
! 1629: }
! 1630:
! 1631: /**
! 1632: * Send an acquire for an installed trap filter
! 1633: */
! 1634: static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
! 1635: traffic_selector_t *src, traffic_selector_t *dst)
! 1636: {
! 1637: uint32_t reqid = 0;
! 1638: trap_t *trap, key = {
! 1639: .filter_id = filter_id,
! 1640: };
! 1641:
! 1642: this->mutex->lock(this->mutex);
! 1643: trap = this->traps->get(this->traps, &key);
! 1644: if (trap)
! 1645: {
! 1646: reqid = trap->reqid;
! 1647: }
! 1648: this->mutex->unlock(this->mutex);
! 1649:
! 1650: if (reqid)
! 1651: {
! 1652: src = src ? src->clone(src) : NULL;
! 1653: dst = dst ? dst->clone(dst) : NULL;
! 1654: charon->kernel->acquire(charon->kernel, reqid, src, dst);
! 1655: }
! 1656: }
! 1657:
! 1658: /**
! 1659: * Create a single host traffic selector from an FWP address definition
! 1660: */
! 1661: static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data,
! 1662: uint8_t protocol, uint16_t from_port, uint16_t to_port)
! 1663: {
! 1664: ts_type_t type;
! 1665: UINT32 ints[4];
! 1666: chunk_t addr;
! 1667:
! 1668: switch (version)
! 1669: {
! 1670: case FWP_IP_VERSION_V4:
! 1671: ints[0] = untoh32(data);
! 1672: addr = chunk_from_thing(ints[0]);
! 1673: type = TS_IPV4_ADDR_RANGE;
! 1674: break;
! 1675: case FWP_IP_VERSION_V6:
! 1676: ints[3] = untoh32(data);
! 1677: ints[2] = untoh32(data + 4);
! 1678: ints[1] = untoh32(data + 8);
! 1679: ints[0] = untoh32(data + 12);
! 1680: addr = chunk_from_thing(ints);
! 1681: type = TS_IPV6_ADDR_RANGE;
! 1682: break;
! 1683: default:
! 1684: return NULL;
! 1685: }
! 1686: return traffic_selector_create_from_bytes(protocol, type, addr, from_port,
! 1687: addr, to_port);
! 1688: }
! 1689:
! 1690: /**
! 1691: * FwpmNetEventSubscribe0() callback
! 1692: */
! 1693: static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event)
! 1694: {
! 1695: private_kernel_wfp_ipsec_t *this = user;
! 1696: traffic_selector_t *local = NULL, *remote = NULL;
! 1697: uint8_t protocol = 0;
! 1698: uint16_t from_local = 0, to_local = 65535;
! 1699: uint16_t from_remote = 0, to_remote = 65535;
! 1700:
! 1701: if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) &&
! 1702: (event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
! 1703: {
! 1704: if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
! 1705: {
! 1706: from_local = to_local = event->header.localPort;
! 1707: }
! 1708: if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
! 1709: {
! 1710: from_remote = to_remote = event->header.remotePort;
! 1711: }
! 1712: if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)
! 1713: {
! 1714: protocol = event->header.ipProtocol;
! 1715: }
! 1716:
! 1717: local = addr2ts(event->header.ipVersion,
! 1718: (void*)&event->header.localAddrV6,
! 1719: protocol, from_local, to_local);
! 1720: remote = addr2ts(event->header.ipVersion,
! 1721: (void*)&event->header.remoteAddrV6,
! 1722: protocol, from_remote, to_remote);
! 1723: }
! 1724:
! 1725: switch (event->type)
! 1726: {
! 1727: case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
! 1728: acquire(this, event->classifyDrop->filterId, local, remote);
! 1729: break;
! 1730: case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
! 1731: DBG1(DBG_KNL, "WFP MM failure: %R === %R, 0x%08x, filterId %llu",
! 1732: local, remote, event->ikeMmFailure->failureErrorCode,
! 1733: event->ikeMmFailure->mmFilterId);
! 1734: break;
! 1735: case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
! 1736: DBG1(DBG_KNL, "WFP QM failure: %R === %R, 0x%08x, filterId %llu",
! 1737: local, remote, event->ikeQmFailure->failureErrorCode,
! 1738: event->ikeQmFailure->qmFilterId);
! 1739: break;
! 1740: case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
! 1741: DBG1(DBG_KNL, "WFP EM failure: %R === %R, 0x%08x, filterId %llu",
! 1742: local, remote, event->ikeEmFailure->failureErrorCode,
! 1743: event->ikeEmFailure->qmFilterId);
! 1744: break;
! 1745: case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
! 1746: DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, "
! 1747: "SPI 0x%08x, %s filterId %llu", local, remote,
! 1748: event->ipsecDrop->failureStatus, event->ipsecDrop->spi,
! 1749: event->ipsecDrop->direction ? "in" : "out",
! 1750: event->ipsecDrop->filterId);
! 1751: break;
! 1752: case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
! 1753: default:
! 1754: break;
! 1755: }
! 1756:
! 1757: DESTROY_IF(local);
! 1758: DESTROY_IF(remote);
! 1759: }
! 1760:
! 1761: /**
! 1762: * Register for net events
! 1763: */
! 1764: static bool register_events(private_kernel_wfp_ipsec_t *this)
! 1765: {
! 1766: FWPM_NET_EVENT_SUBSCRIPTION0 subscription = {};
! 1767: DWORD res;
! 1768:
! 1769: res = FwpmNetEventSubscribe0(this->handle, &subscription,
! 1770: event_callback, this, &this->event);
! 1771: if (res != ERROR_SUCCESS)
! 1772: {
! 1773: DBG1(DBG_KNL, "registering for WFP events failed: 0x%08x", res);
! 1774: return FALSE;
! 1775: }
! 1776: return TRUE;
! 1777: }
! 1778:
! 1779: /**
! 1780: * Install a trap policy to kernel
! 1781: */
! 1782: static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
! 1783: {
! 1784: FWPM_FILTER_CONDITION0 *conds = NULL;
! 1785: int count = 0;
! 1786: DWORD res;
! 1787: const GUID *starget, *dtarget;
! 1788: UINT64 weight = 0x000000000000ff00;
! 1789: FWPM_FILTER0 filter = {
! 1790: .displayData = {
! 1791: .name = L"charon IPsec trap",
! 1792: },
! 1793: .action = {
! 1794: .type = FWP_ACTION_BLOCK,
! 1795: },
! 1796: .weight = {
! 1797: .type = FWP_UINT64,
! 1798: .uint64 = &weight,
! 1799: },
! 1800: };
! 1801:
! 1802: if (trap->fwd)
! 1803: {
! 1804: if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
! 1805: {
! 1806: filter.layerKey = FWPM_LAYER_IPFORWARD_V4;
! 1807: }
! 1808: else
! 1809: {
! 1810: filter.layerKey = FWPM_LAYER_IPFORWARD_V6;
! 1811: }
! 1812: starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
! 1813: dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
! 1814: }
! 1815: else
! 1816: {
! 1817: if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
! 1818: {
! 1819: filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
! 1820: }
! 1821: else
! 1822: {
! 1823: filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
! 1824: }
! 1825: starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
! 1826: dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
! 1827: }
! 1828:
! 1829: if (!ts2condition(trap->src, starget, &conds, &count) ||
! 1830: !ts2condition(trap->dst, dtarget, &conds, &count))
! 1831: {
! 1832: free_conditions(conds, count);
! 1833: return FALSE;
! 1834: }
! 1835:
! 1836: filter.numFilterConditions = count;
! 1837: filter.filterCondition = conds;
! 1838:
! 1839: res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id);
! 1840: free_conditions(conds, count);
! 1841: if (res != ERROR_SUCCESS)
! 1842: {
! 1843: DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res);
! 1844: return FALSE;
! 1845: }
! 1846: return TRUE;
! 1847: }
! 1848:
! 1849: /**
! 1850: * Uninstall a trap policy from kernel
! 1851: */
! 1852: static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
! 1853: {
! 1854: DWORD res;
! 1855:
! 1856: res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
! 1857: if (res != ERROR_SUCCESS)
! 1858: {
! 1859: DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res);
! 1860: return FALSE;
! 1861: }
! 1862: return TRUE;
! 1863: }
! 1864:
! 1865: /**
! 1866: * Create and install a new trap entry
! 1867: */
! 1868: static bool add_trap(private_kernel_wfp_ipsec_t *this,
! 1869: uint32_t reqid, bool fwd, host_t *local, host_t *remote,
! 1870: traffic_selector_t *src, traffic_selector_t *dst)
! 1871: {
! 1872: trap_t *trap;
! 1873:
! 1874: INIT(trap,
! 1875: .reqid = reqid,
! 1876: .fwd = fwd,
! 1877: .src = src->clone(src),
! 1878: .dst = dst->clone(dst),
! 1879: .local = local->clone(local),
! 1880: .remote = remote->clone(remote),
! 1881: );
! 1882:
! 1883: if (!install_trap(this, trap))
! 1884: {
! 1885: destroy_trap(trap);
! 1886: return FALSE;
! 1887: }
! 1888:
! 1889: trap->route = manage_route(this, local, remote, src, dst, TRUE);
! 1890:
! 1891: this->mutex->lock(this->mutex);
! 1892: this->traps->put(this->traps, trap, trap);
! 1893: this->mutex->unlock(this->mutex);
! 1894: return TRUE;
! 1895: }
! 1896:
! 1897: /**
! 1898: * Uninstall and remove a new trap entry
! 1899: */
! 1900: static bool remove_trap(private_kernel_wfp_ipsec_t *this,
! 1901: uint32_t reqid, bool fwd,
! 1902: traffic_selector_t *src, traffic_selector_t *dst)
! 1903: {
! 1904: enumerator_t *enumerator;
! 1905: trap_t *trap, *found = NULL;
! 1906:
! 1907: this->mutex->lock(this->mutex);
! 1908: enumerator = this->traps->create_enumerator(this->traps);
! 1909: while (enumerator->enumerate(enumerator, NULL, &trap))
! 1910: {
! 1911: if (reqid == trap->reqid &&
! 1912: fwd == trap->fwd &&
! 1913: src->equals(src, trap->src) &&
! 1914: dst->equals(dst, trap->dst))
! 1915: {
! 1916: this->traps->remove_at(this->traps, enumerator);
! 1917: found = trap;
! 1918: break;
! 1919: }
! 1920: }
! 1921: enumerator->destroy(enumerator);
! 1922: this->mutex->unlock(this->mutex);
! 1923:
! 1924: if (found)
! 1925: {
! 1926: if (trap->route)
! 1927: {
! 1928: trap->route = !manage_route(this, trap->local, trap->remote,
! 1929: src, dst, FALSE);
! 1930: }
! 1931: uninstall_trap(this, found);
! 1932: destroy_trap(found);
! 1933: return TRUE;
! 1934: }
! 1935: return FALSE;
! 1936: }
! 1937:
! 1938: METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
! 1939: private_kernel_wfp_ipsec_t *this)
! 1940: {
! 1941: return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES;
! 1942: }
! 1943:
! 1944: /**
! 1945: * Initialize seeds for SPI generation
! 1946: */
! 1947: static bool init_spi(private_kernel_wfp_ipsec_t *this)
! 1948: {
! 1949: bool ok = TRUE;
! 1950: rng_t *rng;
! 1951:
! 1952: rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
! 1953: if (!rng)
! 1954: {
! 1955: return FALSE;
! 1956: }
! 1957: ok = rng->get_bytes(rng, sizeof(this->nextspi), (uint8_t*)&this->nextspi);
! 1958: if (ok)
! 1959: {
! 1960: ok = rng->get_bytes(rng, sizeof(this->mixspi), (uint8_t*)&this->mixspi);
! 1961: }
! 1962: rng->destroy(rng);
! 1963: return ok;
! 1964: }
! 1965:
! 1966: /**
! 1967: * Map an integer x with a one-to-one function using quadratic residues.
! 1968: */
! 1969: static u_int permute(u_int x, u_int p)
! 1970: {
! 1971: u_int qr;
! 1972:
! 1973: x = x % p;
! 1974: qr = ((uint64_t)x * x) % p;
! 1975: if (x <= p / 2)
! 1976: {
! 1977: return qr;
! 1978: }
! 1979: return p - qr;
! 1980: }
! 1981:
! 1982: METHOD(kernel_ipsec_t, get_spi, status_t,
! 1983: private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
! 1984: uint8_t protocol, uint32_t *spi)
! 1985: {
! 1986: /* To avoid sequential SPIs, we use a one-to-one permutation function on
! 1987: * an incrementing counter, that is a full period PRNG for the range we
! 1988: * allocate SPIs in. We add some randomness using a fixed XOR and start
! 1989: * the counter at random position. This is not cryptographically safe,
! 1990: * but that is actually not required.
! 1991: * The selected prime should be smaller than the range we allocate SPIs
! 1992: * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */
! 1993: static const u_int p = 268435399, offset = 0xc0000000;
! 1994:
! 1995: *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p));
! 1996: return SUCCESS;
! 1997: }
! 1998:
! 1999: METHOD(kernel_ipsec_t, get_cpi, status_t,
! 2000: private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
! 2001: uint16_t *cpi)
! 2002: {
! 2003: return NOT_SUPPORTED;
! 2004: }
! 2005:
! 2006: /**
! 2007: * Data for an expire callback job
! 2008: */
! 2009: typedef struct {
! 2010: /* backref to kernel backend */
! 2011: private_kernel_wfp_ipsec_t *this;
! 2012: /* SPI of expiring SA */
! 2013: uint32_t spi;
! 2014: /* destination address of expiring SA */
! 2015: host_t *dst;
! 2016: /* is this a hard expire, or a rekey request? */
! 2017: bool hard;
! 2018: } expire_data_t;
! 2019:
! 2020: /**
! 2021: * Clean up expire data
! 2022: */
! 2023: static void expire_data_destroy(expire_data_t *data)
! 2024: {
! 2025: data->dst->destroy(data->dst);
! 2026: free(data);
! 2027: }
! 2028:
! 2029: /**
! 2030: * Callback job for SA expiration
! 2031: */
! 2032: static job_requeue_t expire_job(expire_data_t *data)
! 2033: {
! 2034: private_kernel_wfp_ipsec_t *this = data->this;
! 2035: uint8_t protocol;
! 2036: entry_t *entry = NULL;
! 2037: sa_entry_t key = {
! 2038: .spi = data->spi,
! 2039: .dst = data->dst,
! 2040: };
! 2041:
! 2042: if (data->hard)
! 2043: {
! 2044: this->mutex->lock(this->mutex);
! 2045: entry = this->isas->remove(this->isas, &key);
! 2046: this->mutex->unlock(this->mutex);
! 2047: if (entry)
! 2048: {
! 2049: protocol = entry->isa.protocol;
! 2050: if (entry->osa.dst)
! 2051: {
! 2052: key.dst = entry->osa.dst;
! 2053: key.spi = entry->osa.spi;
! 2054: this->osas->remove(this->osas, &key);
! 2055: }
! 2056: entry_destroy(this, entry);
! 2057: }
! 2058: }
! 2059: else
! 2060: {
! 2061: this->mutex->lock(this->mutex);
! 2062: entry = this->isas->get(this->isas, &key);
! 2063: if (entry)
! 2064: {
! 2065: protocol = entry->isa.protocol;
! 2066: }
! 2067: this->mutex->unlock(this->mutex);
! 2068: }
! 2069:
! 2070: if (entry)
! 2071: {
! 2072: charon->kernel->expire(charon->kernel, protocol, data->spi, data->dst,
! 2073: data->hard);
! 2074: }
! 2075:
! 2076: return JOB_REQUEUE_NONE;
! 2077: }
! 2078:
! 2079: /**
! 2080: * Schedule an expire event for an SA
! 2081: */
! 2082: static void schedule_expire(private_kernel_wfp_ipsec_t *this, uint32_t spi,
! 2083: host_t *dst, uint32_t lifetime, bool hard)
! 2084: {
! 2085: expire_data_t *data;
! 2086:
! 2087: INIT(data,
! 2088: .this = this,
! 2089: .spi = spi,
! 2090: .dst = dst->clone(dst),
! 2091: .hard = hard,
! 2092: );
! 2093:
! 2094: lib->scheduler->schedule_job(lib->scheduler, (job_t*)
! 2095: callback_job_create((void*)expire_job, data,
! 2096: (void*)expire_data_destroy, NULL),
! 2097: lifetime);
! 2098: }
! 2099:
! 2100: METHOD(kernel_ipsec_t, add_sa, status_t,
! 2101: private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
! 2102: kernel_ipsec_add_sa_t *data)
! 2103: {
! 2104: host_t *local, *remote;
! 2105: entry_t *entry;
! 2106:
! 2107: if (data->inbound)
! 2108: {
! 2109: /* comes first, create new entry */
! 2110: local = id->dst->clone(id->dst);
! 2111: remote = id->src->clone(id->src);
! 2112:
! 2113: INIT(entry,
! 2114: .reqid = data->reqid,
! 2115: .isa = {
! 2116: .spi = id->spi,
! 2117: .dst = local,
! 2118: .protocol = id->proto,
! 2119: .lifetime = data->lifetime->time.life,
! 2120: .encr = {
! 2121: .alg = data->enc_alg,
! 2122: .key = chunk_clone(data->enc_key),
! 2123: },
! 2124: .integ = {
! 2125: .alg = data->int_alg,
! 2126: .key = chunk_clone(data->int_key),
! 2127: },
! 2128: },
! 2129: .sps = array_create(0, 0),
! 2130: .local = local,
! 2131: .remote = remote,
! 2132: .mode = data->mode,
! 2133: .encap = data->encap,
! 2134: );
! 2135:
! 2136: if (data->lifetime->time.life)
! 2137: {
! 2138: schedule_expire(this, id->spi, local,
! 2139: data->lifetime->time.life, TRUE);
! 2140: }
! 2141: if (data->lifetime->time.rekey &&
! 2142: data->lifetime->time.rekey != data->lifetime->time.life)
! 2143: {
! 2144: schedule_expire(this, id->spi, local,
! 2145: data->lifetime->time.rekey, FALSE);
! 2146: }
! 2147:
! 2148: this->mutex->lock(this->mutex);
! 2149: this->tsas->put(this->tsas, (void*)(uintptr_t)data->reqid, entry);
! 2150: this->isas->put(this->isas, &entry->isa, entry);
! 2151: this->mutex->unlock(this->mutex);
! 2152: }
! 2153: else
! 2154: {
! 2155: /* comes after inbound, update entry */
! 2156: this->mutex->lock(this->mutex);
! 2157: entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)data->reqid);
! 2158: this->mutex->unlock(this->mutex);
! 2159:
! 2160: if (!entry)
! 2161: {
! 2162: DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
! 2163: "for reqid %u ", data->reqid);
! 2164: return NOT_FOUND;
! 2165: }
! 2166: /* TODO: should we check for local/remote, mode etc.? */
! 2167:
! 2168: entry->osa = (sa_entry_t){
! 2169: .spi = id->spi,
! 2170: .dst = entry->remote,
! 2171: .protocol = id->proto,
! 2172: .lifetime = data->lifetime->time.life,
! 2173: .encr = {
! 2174: .alg = data->enc_alg,
! 2175: .key = chunk_clone(data->enc_key),
! 2176: },
! 2177: .integ = {
! 2178: .alg = data->int_alg,
! 2179: .key = chunk_clone(data->int_key),
! 2180: },
! 2181: };
! 2182:
! 2183: this->mutex->lock(this->mutex);
! 2184: this->osas->put(this->osas, &entry->osa, entry);
! 2185: this->mutex->unlock(this->mutex);
! 2186: }
! 2187:
! 2188: return SUCCESS;
! 2189: }
! 2190:
! 2191: METHOD(kernel_ipsec_t, update_sa, status_t,
! 2192: private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
! 2193: kernel_ipsec_update_sa_t *data)
! 2194: {
! 2195: entry_t *entry;
! 2196: sa_entry_t key = {
! 2197: .dst = id->dst,
! 2198: .spi = id->spi,
! 2199: };
! 2200: UINT64 sa_id = 0;
! 2201: IPSEC_SA_CONTEXT1 *ctx;
! 2202: IPSEC_V4_UDP_ENCAPSULATION0 ports;
! 2203: UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC;
! 2204: DWORD res;
! 2205:
! 2206: this->mutex->lock(this->mutex);
! 2207: entry = this->osas->get(this->osas, &key);
! 2208: this->mutex->unlock(this->mutex);
! 2209:
! 2210: if (entry)
! 2211: {
! 2212: /* outbound entry, nothing to do */
! 2213: return SUCCESS;
! 2214: }
! 2215:
! 2216: this->mutex->lock(this->mutex);
! 2217: entry = this->isas->get(this->isas, &key);
! 2218: if (entry)
! 2219: {
! 2220: /* inbound entry, do update */
! 2221: sa_id = entry->sa_id;
! 2222: ports.localUdpEncapPort = entry->local->get_port(entry->local);
! 2223: ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote);
! 2224: }
! 2225: this->mutex->unlock(this->mutex);
! 2226:
! 2227: if (!sa_id)
! 2228: {
! 2229: return NOT_FOUND;
! 2230: }
! 2231:
! 2232: res = IPsecSaContextGetById1(this->handle, sa_id, &ctx);
! 2233: if (res != ERROR_SUCCESS)
! 2234: {
! 2235: DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res);
! 2236: return FAILED;
! 2237: }
! 2238: if (!hosts2traffic(this, data->new_dst, data->new_src, &ctx->inboundSa->traffic) ||
! 2239: !hosts2traffic(this, data->new_dst, data->new_src, &ctx->outboundSa->traffic))
! 2240: {
! 2241: FwpmFreeMemory0((void**)&ctx);
! 2242: return FAILED;
! 2243: }
! 2244:
! 2245: if (data->new_encap != data->encap)
! 2246: {
! 2247: if (data->new_encap)
! 2248: {
! 2249: ctx->inboundSa->udpEncapsulation = &ports;
! 2250: ctx->outboundSa->udpEncapsulation = &ports;
! 2251: }
! 2252: else
! 2253: {
! 2254: ctx->inboundSa->udpEncapsulation = NULL;
! 2255: ctx->outboundSa->udpEncapsulation = NULL;
! 2256: }
! 2257: flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION;
! 2258: }
! 2259:
! 2260: res = IPsecSaContextUpdate0(this->handle, flags, ctx);
! 2261: FwpmFreeMemory0((void**)&ctx);
! 2262: if (res != ERROR_SUCCESS)
! 2263: {
! 2264: DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res);
! 2265: return FAILED;
! 2266: }
! 2267:
! 2268: this->mutex->lock(this->mutex);
! 2269: entry = this->isas->remove(this->isas, &key);
! 2270: if (entry)
! 2271: {
! 2272: key.spi = entry->osa.spi;
! 2273: key.dst = entry->osa.dst;
! 2274: this->osas->remove(this->osas, &key);
! 2275:
! 2276: entry->local->destroy(entry->local);
! 2277: entry->remote->destroy(entry->remote);
! 2278: entry->local = data->new_dst->clone(data->new_dst);
! 2279: entry->remote = data->new_src->clone(data->new_src);
! 2280: entry->isa.dst = entry->local;
! 2281: entry->osa.dst = entry->remote;
! 2282:
! 2283: this->isas->put(this->isas, &entry->isa, entry);
! 2284: this->osas->put(this->osas, &entry->osa, entry);
! 2285:
! 2286: manage_routes(this, entry, FALSE);
! 2287: manage_routes(this, entry, TRUE);
! 2288: }
! 2289: this->mutex->unlock(this->mutex);
! 2290:
! 2291: return SUCCESS;
! 2292: }
! 2293:
! 2294: METHOD(kernel_ipsec_t, query_sa, status_t,
! 2295: private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
! 2296: kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets,
! 2297: time_t *time)
! 2298: {
! 2299: /* It does not seem that WFP provides any means of getting per-SA traffic
! 2300: * statistics. IPsecGetStatistics0/1() provides global stats, and
! 2301: * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
! 2302: * values only. */
! 2303: return NOT_SUPPORTED;
! 2304: }
! 2305:
! 2306: METHOD(kernel_ipsec_t, del_sa, status_t,
! 2307: private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
! 2308: kernel_ipsec_del_sa_t *data)
! 2309: {
! 2310: entry_t *entry;
! 2311: sa_entry_t key = {
! 2312: .dst = id->dst,
! 2313: .spi = id->spi,
! 2314: };
! 2315:
! 2316: this->mutex->lock(this->mutex);
! 2317: entry = this->isas->remove(this->isas, &key);
! 2318: this->mutex->unlock(this->mutex);
! 2319:
! 2320: if (entry)
! 2321: {
! 2322: /* keep entry until removal of outbound */
! 2323: return SUCCESS;
! 2324: }
! 2325:
! 2326: this->mutex->lock(this->mutex);
! 2327: entry = this->osas->remove(this->osas, &key);
! 2328: this->mutex->unlock(this->mutex);
! 2329:
! 2330: if (entry)
! 2331: {
! 2332: entry_destroy(this, entry);
! 2333: return SUCCESS;
! 2334: }
! 2335:
! 2336: return NOT_FOUND;
! 2337: }
! 2338:
! 2339: METHOD(kernel_ipsec_t, flush_sas, status_t,
! 2340: private_kernel_wfp_ipsec_t *this)
! 2341: {
! 2342: return NOT_SUPPORTED;
! 2343: }
! 2344:
! 2345: METHOD(kernel_ipsec_t, add_policy, status_t,
! 2346: private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
! 2347: kernel_ipsec_manage_policy_t *data)
! 2348: {
! 2349: status_t status = SUCCESS;
! 2350: entry_t *entry;
! 2351: sp_entry_t *sp;
! 2352: sa_entry_t key = {
! 2353: .spi = data->sa->esp.use ? data->sa->esp.spi : data->sa->ah.spi,
! 2354: .dst = data->dst,
! 2355: };
! 2356:
! 2357: if (data->sa->esp.use && data->sa->ah.use)
! 2358: {
! 2359: return NOT_SUPPORTED;
! 2360: }
! 2361:
! 2362: switch (data->type)
! 2363: {
! 2364: case POLICY_IPSEC:
! 2365: break;
! 2366: case POLICY_PASS:
! 2367: case POLICY_DROP:
! 2368: return NOT_SUPPORTED;
! 2369: }
! 2370:
! 2371: switch (id->dir)
! 2372: {
! 2373: case POLICY_OUT:
! 2374: break;
! 2375: case POLICY_IN:
! 2376: case POLICY_FWD:
! 2377: /* not required */
! 2378: return SUCCESS;
! 2379: default:
! 2380: return NOT_SUPPORTED;
! 2381: }
! 2382:
! 2383: switch (data->prio)
! 2384: {
! 2385: case POLICY_PRIORITY_DEFAULT:
! 2386: break;
! 2387: case POLICY_PRIORITY_ROUTED:
! 2388: if (!add_trap(this, data->sa->reqid, FALSE, data->src, data->dst,
! 2389: id->src_ts, id->dst_ts))
! 2390: {
! 2391: return FAILED;
! 2392: }
! 2393: if (data->sa->mode == MODE_TUNNEL)
! 2394: {
! 2395: if (!add_trap(this, data->sa->reqid, TRUE, data->src, data->dst,
! 2396: id->src_ts, id->dst_ts))
! 2397: {
! 2398: return FAILED;
! 2399: }
! 2400: }
! 2401: return SUCCESS;
! 2402: case POLICY_PRIORITY_FALLBACK:
! 2403: default:
! 2404: return NOT_SUPPORTED;
! 2405: }
! 2406:
! 2407: this->mutex->lock(this->mutex);
! 2408: entry = this->osas->get(this->osas, &key);
! 2409: if (entry)
! 2410: {
! 2411: if (data->sa->mode == MODE_TUNNEL || array_count(entry->sps) == 0)
! 2412: {
! 2413: INIT(sp,
! 2414: .src = id->src_ts->clone(id->src_ts),
! 2415: .dst = id->dst_ts->clone(id->dst_ts),
! 2416: );
! 2417: array_insert(entry->sps, -1, sp);
! 2418: if (array_count(entry->sps) == data->sa->policy_count)
! 2419: {
! 2420: if (!install(this, entry))
! 2421: {
! 2422: status = FAILED;
! 2423: }
! 2424: }
! 2425: }
! 2426: else
! 2427: {
! 2428: /* TODO: reinstall with a filter using multiple TS?
! 2429: * Filters are ANDed for a match, but we could install a filter
! 2430: * with the inverse TS set using NOT-matches... */
! 2431: DBG1(DBG_KNL, "multiple transport mode traffic selectors not "
! 2432: "supported by WFP");
! 2433: status = NOT_SUPPORTED;
! 2434: }
! 2435: }
! 2436: else
! 2437: {
! 2438: DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
! 2439: status = FAILED;
! 2440: }
! 2441: this->mutex->unlock(this->mutex);
! 2442:
! 2443: return status;
! 2444: }
! 2445:
! 2446: METHOD(kernel_ipsec_t, query_policy, status_t,
! 2447: private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
! 2448: kernel_ipsec_query_policy_t *data, time_t *use_time)
! 2449: {
! 2450: /* see query_sa() for some notes */
! 2451: return NOT_SUPPORTED;
! 2452: }
! 2453:
! 2454: METHOD(kernel_ipsec_t, del_policy, status_t,
! 2455: private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
! 2456: kernel_ipsec_manage_policy_t *data)
! 2457: {
! 2458: if (id->dir == POLICY_OUT && data->prio == POLICY_PRIORITY_ROUTED)
! 2459: {
! 2460: if (remove_trap(this, data->sa->reqid, FALSE, id->src_ts,
! 2461: id->dst_ts))
! 2462: {
! 2463: remove_trap(this, data->sa->reqid, TRUE, id->src_ts,
! 2464: id->dst_ts);
! 2465: return SUCCESS;
! 2466: }
! 2467: return NOT_FOUND;
! 2468: }
! 2469: /* not required, as we delete the whole SA/SP set during del_sa() */
! 2470: return SUCCESS;
! 2471: }
! 2472:
! 2473: METHOD(kernel_ipsec_t, flush_policies, status_t,
! 2474: private_kernel_wfp_ipsec_t *this)
! 2475: {
! 2476: return NOT_SUPPORTED;
! 2477: }
! 2478:
! 2479: /**
! 2480: * Add a bypass policy for a specific UDP port
! 2481: */
! 2482: static bool add_bypass(private_kernel_wfp_ipsec_t *this,
! 2483: int family, uint16_t port, bool inbound, UINT64 *luid)
! 2484: {
! 2485: FWPM_FILTER_CONDITION0 *cond, *conds = NULL;
! 2486: int count = 0;
! 2487: DWORD res;
! 2488: UINT64 weight = 0xff00000000000000;
! 2489: FWPM_FILTER0 filter = {
! 2490: .displayData = {
! 2491: .name = L"charon IKE bypass",
! 2492: },
! 2493: .action = {
! 2494: .type = FWP_ACTION_PERMIT,
! 2495: },
! 2496: .weight = {
! 2497: .type = FWP_UINT64,
! 2498: .uint64 = &weight,
! 2499: },
! 2500: };
! 2501:
! 2502: switch (family)
! 2503: {
! 2504: case AF_INET:
! 2505: filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4
! 2506: : FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
! 2507: break;
! 2508: case AF_INET6:
! 2509: filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V6
! 2510: : FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
! 2511: break;
! 2512: default:
! 2513: return FALSE;
! 2514: }
! 2515:
! 2516: cond = append_condition(&conds, &count);
! 2517: cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
! 2518: cond->matchType = FWP_MATCH_EQUAL;
! 2519: cond->conditionValue.type = FWP_UINT8;
! 2520: cond->conditionValue.uint8 = IPPROTO_UDP;
! 2521:
! 2522: cond = append_condition(&conds, &count);
! 2523: cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
! 2524: cond->matchType = FWP_MATCH_EQUAL;
! 2525: cond->conditionValue.type = FWP_UINT16;
! 2526: cond->conditionValue.uint16 = port;
! 2527:
! 2528: filter.numFilterConditions = count;
! 2529: filter.filterCondition = conds;
! 2530:
! 2531: res = FwpmFilterAdd0(this->handle, &filter, NULL, luid);
! 2532: free_conditions(conds, count);
! 2533: if (res != ERROR_SUCCESS)
! 2534: {
! 2535: DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res);
! 2536: return FALSE;
! 2537: }
! 2538: return TRUE;
! 2539: }
! 2540:
! 2541: METHOD(kernel_ipsec_t, bypass_socket, bool,
! 2542: private_kernel_wfp_ipsec_t *this, int fd, int family)
! 2543: {
! 2544: union {
! 2545: struct sockaddr sa;
! 2546: SOCKADDR_IN in;
! 2547: SOCKADDR_IN6 in6;
! 2548: } saddr;
! 2549: int addrlen = sizeof(saddr);
! 2550: UINT64 filter_out, filter_in = 0;
! 2551: uint16_t port;
! 2552:
! 2553: if (getsockname(fd, &saddr.sa, &addrlen) == SOCKET_ERROR)
! 2554: {
! 2555: return FALSE;
! 2556: }
! 2557: switch (family)
! 2558: {
! 2559: case AF_INET:
! 2560: port = ntohs(saddr.in.sin_port);
! 2561: break;
! 2562: case AF_INET6:
! 2563: port = ntohs(saddr.in6.sin6_port);
! 2564: break;
! 2565: default:
! 2566: return FALSE;
! 2567: }
! 2568:
! 2569: if (!add_bypass(this, family, port, TRUE, &filter_in) ||
! 2570: !add_bypass(this, family, port, FALSE, &filter_out))
! 2571: {
! 2572: if (filter_in)
! 2573: {
! 2574: FwpmFilterDeleteById0(this->handle, filter_in);
! 2575: }
! 2576: return FALSE;
! 2577: }
! 2578:
! 2579: this->mutex->lock(this->mutex);
! 2580: array_insert(this->bypass, ARRAY_TAIL, &filter_in);
! 2581: array_insert(this->bypass, ARRAY_TAIL, &filter_out);
! 2582: this->mutex->unlock(this->mutex);
! 2583:
! 2584: return TRUE;
! 2585: }
! 2586:
! 2587: METHOD(kernel_ipsec_t, enable_udp_decap, bool,
! 2588: private_kernel_wfp_ipsec_t *this, int fd, int family, uint16_t port)
! 2589: {
! 2590: return FALSE;
! 2591: }
! 2592:
! 2593: METHOD(kernel_ipsec_t, destroy, void,
! 2594: private_kernel_wfp_ipsec_t *this)
! 2595: {
! 2596: UINT64 filter;
! 2597:
! 2598: while (array_remove(this->bypass, ARRAY_TAIL, &filter))
! 2599: {
! 2600: FwpmFilterDeleteById0(this->handle, filter);
! 2601: }
! 2602: if (this->handle)
! 2603: {
! 2604: if (this->event)
! 2605: {
! 2606: FwpmNetEventUnsubscribe0(this->handle, this->event);
! 2607: }
! 2608: FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
! 2609: FwpmEngineClose0(this->handle);
! 2610: }
! 2611: array_destroy(this->bypass);
! 2612: this->tsas->destroy(this->tsas);
! 2613: this->isas->destroy(this->isas);
! 2614: this->osas->destroy(this->osas);
! 2615: this->routes->destroy(this->routes);
! 2616: this->traps->destroy(this->traps);
! 2617: this->mutex->destroy(this->mutex);
! 2618: free(this);
! 2619: }
! 2620:
! 2621: /*
! 2622: * Described in header.
! 2623: */
! 2624: kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
! 2625: {
! 2626: private_kernel_wfp_ipsec_t *this;
! 2627: DWORD res;
! 2628: FWPM_SESSION0 session = {
! 2629: .displayData = {
! 2630: .name = L"charon",
! 2631: .description = L"strongSwan IKE kernel-wfp backend",
! 2632: },
! 2633: };
! 2634:
! 2635: INIT(this,
! 2636: .public = {
! 2637: .interface = {
! 2638: .get_features = _get_features,
! 2639: .get_spi = _get_spi,
! 2640: .get_cpi = _get_cpi,
! 2641: .add_sa = _add_sa,
! 2642: .update_sa = _update_sa,
! 2643: .query_sa = _query_sa,
! 2644: .del_sa = _del_sa,
! 2645: .flush_sas = _flush_sas,
! 2646: .add_policy = _add_policy,
! 2647: .query_policy = _query_policy,
! 2648: .del_policy = _del_policy,
! 2649: .flush_policies = _flush_policies,
! 2650: .bypass_socket = _bypass_socket,
! 2651: .enable_udp_decap = _enable_udp_decap,
! 2652: .destroy = _destroy,
! 2653: },
! 2654: },
! 2655: .provider = {
! 2656: .displayData = {
! 2657: .name = L"charon",
! 2658: .description = L"strongSwan IKE kernel-wfp backend",
! 2659: },
! 2660: .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
! 2661: { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
! 2662: },
! 2663: .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
! 2664: .bypass = array_create(sizeof(UINT64), 2),
! 2665: .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
! 2666: .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
! 2667: .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
! 2668: .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
! 2669: .traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4),
! 2670: );
! 2671:
! 2672: if (!init_spi(this))
! 2673: {
! 2674: destroy(this);
! 2675: return NULL;
! 2676: }
! 2677:
! 2678: res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
! 2679: &this->handle);
! 2680: if (res != ERROR_SUCCESS)
! 2681: {
! 2682: DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
! 2683: destroy(this);
! 2684: return NULL;
! 2685: }
! 2686:
! 2687: res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
! 2688: if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
! 2689: {
! 2690: DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
! 2691: destroy(this);
! 2692: return NULL;
! 2693: }
! 2694:
! 2695: if (!register_events(this))
! 2696: {
! 2697: destroy(this);
! 2698: return NULL;
! 2699: }
! 2700:
! 2701: return &this->public;
! 2702: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>