2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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>.
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
16 /* Windows 7, for some fwpmu.h functionality */
17 #define _WIN32_WINNT 0x0601
19 #include "kernel_wfp_compat.h"
20 #include "kernel_wfp_ipsec.h"
24 #include <threading/mutex.h>
25 #include <collections/array.h>
26 #include <collections/hashtable.h>
27 #include <processing/jobs/callback_job.h>
30 typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t
;
32 struct private_kernel_wfp_ipsec_t
{
37 kernel_wfp_ipsec_t
public;
40 * Next SPI to allocate
45 * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
50 * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
55 * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
60 * Mutex for accessing entries
70 * Provider charon registers as
72 FWPM_PROVIDER0 provider
;
76 * Security association entry
79 /** SPI for this SA */
81 /** protocol, IPPROTO_ESP/IPPROTO_AH */
83 /** hard lifetime of SA */
85 /** destination host address for this SPI */
96 * Hash function for sas lookup table
98 static u_int
hash_sa(sa_entry_t
*key
)
100 return chunk_hash_inc(chunk_from_thing(key
->spi
),
101 chunk_hash(key
->dst
->get_address(key
->dst
)));
105 * equals function for sas lookup table
107 static bool equals_sa(sa_entry_t
*a
, sa_entry_t
*b
)
109 return a
->spi
== b
->spi
&& a
->dst
->ip_equals(a
->dst
, b
->dst
);
113 * Security policy entry
116 /** policy source addresses */
117 traffic_selector_t
*src
;
118 /** policy destinaiton addresses */
119 traffic_selector_t
*dst
;
123 * Destroy an SP entry
125 static void sp_entry_destroy(sp_entry_t
*sp
)
127 sp
->src
->destroy(sp
->src
);
128 sp
->dst
->destroy(sp
->dst
);
133 * Collection of SA/SP database entries for a reqid
136 /** reqid of entry */
138 /** outer address on local host */
140 /** outer address on remote host */
142 /** inbound SA entry */
144 /** outbound SA entry */
146 /** associated (outbound) policies, as sp_entry_t* */
148 /** IPsec mode, tunnel|transport */
150 /** UDP encapsulation */
152 /** WFP allocated LUID for inbound filter/tunnel policy ID */
154 /** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
155 u_int64_t policy_out
;
156 /** WFP allocated LUID for SA context */
161 * Remove a transport or tunnel policy from kernel
163 static void cleanup_policy(private_kernel_wfp_ipsec_t
*this, bool transport
,
168 FwpmFilterDeleteById0(this->handle
, policy
);
172 FWPM_PROVIDER_CONTEXT0
*ctx
;
174 if (FwpmProviderContextGetById0(this->handle
, policy
,
175 &ctx
) == ERROR_SUCCESS
)
177 FwpmIPsecTunnelDeleteByKey0(this->handle
, &ctx
->providerContextKey
);
178 FwpmFreeMemory0((void**)&ctx
);
184 * Remove policies associated to an entry from kernel
186 static void cleanup_policies(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
188 if (entry
->policy_in
)
190 cleanup_policy(this, entry
->mode
== MODE_TRANSPORT
, entry
->policy_in
);
191 entry
->policy_in
= 0;
193 if (entry
->policy_out
)
195 cleanup_policy(this, entry
->mode
== MODE_TRANSPORT
, entry
->policy_out
);
196 entry
->policy_out
= 0;
201 * Destroy a SA/SP entry set
203 static void entry_destroy(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
207 IPsecSaContextDeleteById0(this->handle
, entry
->sa_id
);
209 cleanup_policies(this, entry
);
210 array_destroy_function(entry
->sps
, (void*)sp_entry_destroy
, NULL
);
211 entry
->local
->destroy(entry
->local
);
212 entry
->remote
->destroy(entry
->remote
);
213 chunk_clear(&entry
->isa
.integ
.key
);
214 chunk_clear(&entry
->isa
.encr
.key
);
215 chunk_clear(&entry
->osa
.integ
.key
);
216 chunk_clear(&entry
->osa
.encr
.key
);
221 * Append/Realloc a filter condition to an existing condition set
223 static FWPM_FILTER_CONDITION0
*append_condition(FWPM_FILTER_CONDITION0
*conds
[],
226 FWPM_FILTER_CONDITION0
*cond
;
229 *conds
= realloc(*conds
, *count
* sizeof(*cond
));
230 cond
= *conds
+ *count
- 1;
231 memset(cond
, 0, sizeof(*cond
));
237 * Convert an IPv4 prefix to a host order subnet mask
239 static u_int32_t
prefix2mask(u_int8_t prefix
)
241 u_int8_t netmask
[4] = {};
244 for (i
= 0; i
< sizeof(netmask
); i
++)
248 netmask
[i
] = 0xFF << (8 - prefix
);
254 return untoh32(netmask
);
258 * Convert a 16-bit range to a WFP condition
260 static void range2cond(FWPM_FILTER_CONDITION0
*cond
,
261 u_int16_t from
, u_int16_t to
)
265 cond
->matchType
= FWP_MATCH_EQUAL
;
266 cond
->conditionValue
.type
= FWP_UINT16
;
267 cond
->conditionValue
.uint16
= from
;
271 cond
->matchType
= FWP_MATCH_RANGE
;
272 cond
->conditionValue
.type
= FWP_RANGE_TYPE
;
273 cond
->conditionValue
.rangeValue
= calloc(1, sizeof(FWP_RANGE0
));
274 cond
->conditionValue
.rangeValue
->valueLow
.type
= FWP_UINT16
;
275 cond
->conditionValue
.rangeValue
->valueLow
.uint16
= from
;
276 cond
->conditionValue
.rangeValue
->valueHigh
.type
= FWP_UINT16
;
277 cond
->conditionValue
.rangeValue
->valueHigh
.uint16
= to
;
282 * (Re-)allocate filter conditions for given local or remote traffic selector
284 static bool ts2condition(traffic_selector_t
*ts
, bool local
,
285 FWPM_FILTER_CONDITION0
*conds
[], int *count
)
287 FWPM_FILTER_CONDITION0
*cond
;
288 FWP_BYTE_ARRAY16
*addr
;
290 u_int16_t from_port
, to_port
;
296 from
= ts
->get_from_address(ts
).ptr
;
297 to
= ts
->get_to_address(ts
).ptr
;
298 from_port
= ts
->get_from_port(ts
);
299 to_port
= ts
->get_to_port(ts
);
301 cond
= append_condition(conds
, count
);
304 cond
->fieldKey
= FWPM_CONDITION_IP_LOCAL_ADDRESS
;
308 cond
->fieldKey
= FWPM_CONDITION_IP_REMOTE_ADDRESS
;
310 if (ts
->is_host(ts
, NULL
))
312 cond
->matchType
= FWP_MATCH_EQUAL
;
313 switch (ts
->get_type(ts
))
315 case TS_IPV4_ADDR_RANGE
:
316 cond
->conditionValue
.type
= FWP_UINT32
;
317 cond
->conditionValue
.uint32
= untoh32(from
);
319 case TS_IPV6_ADDR_RANGE
:
320 cond
->conditionValue
.type
= FWP_BYTE_ARRAY16_TYPE
;
321 cond
->conditionValue
.byteArray16
= addr
= malloc(sizeof(*addr
));
322 memcpy(addr
, from
, sizeof(*addr
));
328 else if (ts
->to_subnet(ts
, &net
, &prefix
))
330 FWP_V6_ADDR_AND_MASK
*m6
;
331 FWP_V4_ADDR_AND_MASK
*m4
;
333 cond
->matchType
= FWP_MATCH_EQUAL
;
334 switch (net
->get_family(net
))
337 cond
->conditionValue
.type
= FWP_V4_ADDR_MASK
;
338 cond
->conditionValue
.v4AddrMask
= m4
= calloc(1, sizeof(*m4
));
339 m4
->addr
= untoh32(from
);
340 m4
->mask
= prefix2mask(prefix
);
343 cond
->conditionValue
.type
= FWP_V6_ADDR_MASK
;
344 cond
->conditionValue
.v6AddrMask
= m6
= calloc(1, sizeof(*m6
));
345 memcpy(m6
->addr
, from
, sizeof(m6
->addr
));
346 m6
->prefixLength
= prefix
;
356 cond
->matchType
= FWP_MATCH_RANGE
;
357 cond
->conditionValue
.type
= FWP_RANGE_TYPE
;
358 cond
->conditionValue
.rangeValue
= range
= calloc(1, sizeof(*range
));
359 switch (ts
->get_type(ts
))
361 case TS_IPV4_ADDR_RANGE
:
362 range
->valueLow
.type
= FWP_UINT32
;
363 range
->valueLow
.uint32
= untoh32(from
);
364 range
->valueHigh
.type
= FWP_UINT32
;
365 range
->valueHigh
.uint32
= untoh32(to
);
367 case TS_IPV6_ADDR_RANGE
:
368 range
->valueLow
.type
= FWP_BYTE_ARRAY16_TYPE
;
369 range
->valueLow
.byteArray16
= addr
= malloc(sizeof(*addr
));
370 memcpy(addr
, from
, sizeof(*addr
));
371 range
->valueHigh
.type
= FWP_BYTE_ARRAY16_TYPE
;
372 range
->valueHigh
.byteArray16
= addr
= malloc(sizeof(*addr
));
373 memcpy(addr
, to
, sizeof(*addr
));
380 proto
= ts
->get_protocol(ts
);
383 cond
= append_condition(conds
, count
);
384 cond
->fieldKey
= FWPM_CONDITION_IP_PROTOCOL
;
385 cond
->matchType
= FWP_MATCH_EQUAL
;
386 cond
->conditionValue
.type
= FWP_UINT8
;
387 cond
->conditionValue
.uint8
= proto
;
390 if (proto
== IPPROTO_ICMP
)
394 u_int8_t from_type
, to_type
, from_code
, to_code
;
396 from_type
= traffic_selector_icmp_type(from_port
);
397 to_type
= traffic_selector_icmp_type(to_port
);
398 from_code
= traffic_selector_icmp_code(from_port
);
399 to_code
= traffic_selector_icmp_code(to_port
);
401 if (from_type
!= 0 || to_type
!= 0xFF)
403 cond
= append_condition(conds
, count
);
404 cond
->fieldKey
= FWPM_CONDITION_ICMP_TYPE
;
405 range2cond(cond
, from_type
, to_type
);
407 if (from_code
!= 0 || to_code
!= 0xFF)
409 cond
= append_condition(conds
, count
);
410 cond
->fieldKey
= FWPM_CONDITION_ICMP_CODE
;
411 range2cond(cond
, from_code
, to_code
);
415 else if (from_port
!= 0 || to_port
!= 0xFFFF)
417 cond
= append_condition(conds
, count
);
420 cond
->fieldKey
= FWPM_CONDITION_IP_LOCAL_PORT
;
424 cond
->fieldKey
= FWPM_CONDITION_IP_REMOTE_PORT
;
426 range2cond(cond
, from_port
, to_port
);
432 * Free memory associated to a single condition
434 static void free_condition(FWP_DATA_TYPE type
, void *value
)
440 case FWP_BYTE_ARRAY16_TYPE
:
441 case FWP_V4_ADDR_MASK
:
442 case FWP_V6_ADDR_MASK
:
447 free_condition(range
->valueLow
.type
, range
->valueLow
.sd
);
448 free_condition(range
->valueHigh
.type
, range
->valueHigh
.sd
);
457 * Free memory used by a set of conditions
459 static void free_conditions(FWPM_FILTER_CONDITION0
*conds
, int count
)
463 for (i
= 0; i
< count
; i
++)
465 free_condition(conds
[i
].conditionValue
.type
, conds
[i
].conditionValue
.sd
);
471 * Install transport mode SP to the kernel
473 static bool install_transport_sp(private_kernel_wfp_ipsec_t
*this,
474 entry_t
*entry
, bool inbound
)
476 FWPM_FILTER_CONDITION0
*conds
= NULL
;
478 enumerator_t
*enumerator
;
479 traffic_selector_t
*local
, *remote
;
482 FWPM_FILTER0 filter
= {
484 .name
= L
"charon IPsec transport",
487 .type
= FWP_ACTION_CALLOUT_TERMINATING
,
488 .calloutKey
= inbound ? FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4
:
489 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4
,
491 .layerKey
= inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4
:
492 FWPM_LAYER_OUTBOUND_TRANSPORT_V4
,
495 enumerator
= array_create_enumerator(entry
->sps
);
496 while (enumerator
->enumerate(enumerator
, &sp
))
509 if (!ts2condition(local
, TRUE
, &conds
, &count
) ||
510 !ts2condition(remote
, FALSE
, &conds
, &count
))
512 free_conditions(conds
, count
);
513 enumerator
->destroy(enumerator
);
517 enumerator
->destroy(enumerator
);
519 filter
.numFilterConditions
= count
;
520 filter
.filterCondition
= conds
;
524 res
= FwpmFilterAdd0(this->handle
, &filter
, NULL
, &entry
->policy_in
);
528 res
= FwpmFilterAdd0(this->handle
, &filter
, NULL
, &entry
->policy_out
);
530 free_conditions(conds
, count
);
531 if (res
!= ERROR_SUCCESS
)
533 DBG1(DBG_KNL
, "installing inbound FWP filter failed: 0x%08x", res
);
540 * Convert a chunk_t to a WFP FWP_BYTE_BLOB
542 static inline FWP_BYTE_BLOB
chunk2blob(chunk_t chunk
)
544 return (FWP_BYTE_BLOB
){
551 * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
553 static bool alg2auth(integrity_algorithm_t alg
,
554 IPSEC_SA_AUTH_INFORMATION0
*info
)
557 integrity_algorithm_t alg
;
558 IPSEC_AUTH_TRANSFORM_ID0 transform
;
560 { AUTH_HMAC_MD5_96
, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96
},
561 { AUTH_HMAC_SHA1_96
, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96
},
562 { AUTH_HMAC_SHA2_256_128
, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128
},
563 { AUTH_AES_128_GMAC
, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128
},
564 { AUTH_AES_192_GMAC
, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192
},
565 { AUTH_AES_256_GMAC
, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256
},
569 for (i
= 0; i
< countof(map
); i
++)
571 if (map
[i
].alg
== alg
)
573 info
->authTransform
.authTransformId
= map
[i
].transform
;
581 * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
583 static bool alg2cipher(encryption_algorithm_t alg
, int keylen
,
584 IPSEC_SA_CIPHER_INFORMATION0
*info
)
587 encryption_algorithm_t alg
;
589 IPSEC_CIPHER_TRANSFORM_ID0 transform
;
591 { ENCR_DES
, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES
},
592 { ENCR_3DES
, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES
},
593 { ENCR_AES_CBC
, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128
},
594 { ENCR_AES_CBC
, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192
},
595 { ENCR_AES_CBC
, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256
},
596 { ENCR_AES_GCM_ICV16
, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128
},
597 { ENCR_AES_GCM_ICV16
, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192
},
598 { ENCR_AES_GCM_ICV16
, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256
},
602 for (i
= 0; i
< countof(map
); i
++)
604 if (map
[i
].alg
== alg
&& map
[i
].keylen
== keylen
)
606 info
->cipherTransform
.cipherTransformId
= map
[i
].transform
;
614 * Get the integrity algorithm used for an AEAD transform
616 static integrity_algorithm_t
encr2integ(encryption_algorithm_t encr
, int keylen
)
619 encryption_algorithm_t encr
;
621 integrity_algorithm_t integ
;
623 { ENCR_NULL_AUTH_AES_GMAC
, 20, AUTH_AES_128_GMAC
},
624 { ENCR_NULL_AUTH_AES_GMAC
, 28, AUTH_AES_192_GMAC
},
625 { ENCR_NULL_AUTH_AES_GMAC
, 36, AUTH_AES_256_GMAC
},
626 { ENCR_AES_GCM_ICV16
, 20, AUTH_AES_128_GMAC
},
627 { ENCR_AES_GCM_ICV16
, 28, AUTH_AES_192_GMAC
},
628 { ENCR_AES_GCM_ICV16
, 36, AUTH_AES_256_GMAC
},
632 for (i
= 0; i
< countof(map
); i
++)
634 if (map
[i
].encr
== encr
&& map
[i
].keylen
== keylen
)
639 return AUTH_UNDEFINED
;
643 * Install a single SA
645 static bool install_sa(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
,
646 bool inbound
, sa_entry_t
*sa
, FWP_IP_VERSION version
)
648 IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info
= {};
650 .spi
= ntohl(sa
->spi
),
652 IPSEC_SA_BUNDLE0 bundle
= {
654 .lifetimeSeconds
= inbound ? entry
->isa
.lifetime
655 : entry
->osa
.lifetime
,
659 .ipVersion
= version
,
664 } integ
= {}, encr
= {};
667 switch (sa
->protocol
)
670 ipsec
.saTransformType
= IPSEC_TRANSFORM_AH
;
671 ipsec
.ahInformation
= &info
.saAuthInformation
;
672 integ
.key
= sa
->integ
.key
;
673 integ
.alg
= sa
->integ
.alg
;
676 if (sa
->encr
.alg
== ENCR_NULL
||
677 sa
->encr
.alg
== ENCR_NULL_AUTH_AES_GMAC
)
679 ipsec
.saTransformType
= IPSEC_TRANSFORM_ESP_AUTH
;
680 ipsec
.espAuthInformation
= &info
.saAuthInformation
;
684 ipsec
.saTransformType
= IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER
;
685 ipsec
.espAuthAndCipherInformation
= &info
;
686 encr
.key
= sa
->encr
.key
;
687 encr
.alg
= sa
->encr
.alg
;
689 if (encryption_algorithm_is_aead(sa
->encr
.alg
))
691 integ
.alg
= encr2integ(sa
->encr
.alg
, sa
->encr
.key
.len
);
692 integ
.key
= sa
->encr
.key
;
696 integ
.alg
= sa
->integ
.alg
;
697 integ
.key
= sa
->integ
.key
;
706 info
.saAuthInformation
.authKey
= chunk2blob(integ
.key
);
707 if (!alg2auth(integ
.alg
, &info
.saAuthInformation
))
709 DBG1(DBG_KNL
, "integrity algorithm %N not supported by WFP",
710 integrity_algorithm_names
, integ
.alg
);
716 info
.saCipherInformation
.cipherKey
= chunk2blob(encr
.key
);
717 if (!alg2cipher(encr
.alg
, encr
.key
.len
, &info
.saCipherInformation
))
719 DBG1(DBG_KNL
, "encryption algorithm %N not supported by WFP",
720 encryption_algorithm_names
, encr
.alg
);
727 res
= IPsecSaContextAddInbound0(this->handle
, entry
->sa_id
, &bundle
);
731 res
= IPsecSaContextAddOutbound0(this->handle
, entry
->sa_id
, &bundle
);
733 if (res
!= ERROR_SUCCESS
)
735 DBG1(DBG_KNL
, "adding %sbound WFP SA failed: 0x%08x",
736 inbound ?
"in" : "out", res
);
743 * Install SAs to the kernel
745 static bool install_sas(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
,
746 IPSEC_TRAFFIC_TYPE type
)
748 IPSEC_TRAFFIC0 traffic
= {
751 IPSEC_GETSPI1 spi
= {
752 .inboundIpsecTraffic
= {
758 if (type
== IPSEC_TRAFFIC_TYPE_TRANSPORT
)
760 traffic
.ipsecFilterId
= entry
->policy_out
;
761 spi
.inboundIpsecTraffic
.ipsecFilterId
= entry
->policy_in
;
765 traffic
.tunnelPolicyId
= entry
->policy_in
;
766 spi
.inboundIpsecTraffic
.tunnelPolicyId
= entry
->policy_in
;
769 switch (entry
->local
->get_family(entry
->local
))
772 traffic
.ipVersion
= FWP_IP_VERSION_V4
;
773 traffic
.localV4Address
=
774 untoh32(entry
->local
->get_address(entry
->local
).ptr
);
775 traffic
.remoteV4Address
=
776 untoh32(entry
->remote
->get_address(entry
->remote
).ptr
);
779 traffic
.ipVersion
= FWP_IP_VERSION_V6
;
780 memcpy(&traffic
.localV6Address
,
781 entry
->local
->get_address(entry
->local
).ptr
, 16);
782 memcpy(&traffic
.remoteV6Address
,
783 entry
->remote
->get_address(entry
->remote
).ptr
, 16);
789 res
= IPsecSaContextCreate0(this->handle
, &traffic
, NULL
, &entry
->sa_id
);
790 if (res
!= ERROR_SUCCESS
)
792 DBG1(DBG_KNL
, "creating WFP SA context failed: 0x%08x", res
);
796 memcpy(spi
.inboundIpsecTraffic
.localV6Address
, traffic
.localV6Address
,
797 sizeof(traffic
.localV6Address
));
798 memcpy(spi
.inboundIpsecTraffic
.remoteV6Address
, traffic
.remoteV6Address
,
799 sizeof(traffic
.remoteV6Address
));
800 spi
.ipVersion
= traffic
.ipVersion
;
802 res
= IPsecSaContextSetSpi0(this->handle
, entry
->sa_id
, &spi
,
803 ntohl(entry
->isa
.spi
));
804 if (res
!= ERROR_SUCCESS
)
806 DBG1(DBG_KNL
, "setting WFP SA SPI failed: 0x%08x", res
);
807 IPsecSaContextDeleteById0(this->handle
, entry
->sa_id
);
812 if (!install_sa(this, entry
, TRUE
, &entry
->isa
, spi
.ipVersion
) ||
813 !install_sa(this, entry
, FALSE
, &entry
->osa
, spi
.ipVersion
))
815 IPsecSaContextDeleteById0(this->handle
, entry
->sa_id
);
824 * Install a transport mode SA/SP set to the kernel
826 static bool install_transport(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
828 if (install_transport_sp(this, entry
, TRUE
) &&
829 install_transport_sp(this, entry
, FALSE
) &&
830 install_sas(this, entry
, IPSEC_TRAFFIC_TYPE_TRANSPORT
))
834 cleanup_policies(this, entry
);
839 * Generate a new GUID, random
841 static bool generate_guid(private_kernel_wfp_ipsec_t
*this, GUID
*guid
)
846 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
851 ok
= rng
->get_bytes(rng
, sizeof(GUID
), (u_int8_t
*)guid
);
857 * Install tunnel mode SPs to the kernel
859 static bool install_tunnel_sps(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
861 FWPM_FILTER_CONDITION0
*conds
= NULL
;
863 enumerator_t
*enumerator
;
867 IPSEC_AUTH_TRANSFORM0 transform
= {
868 /* Create any valid proposal. This is actually not used, as we
869 * don't create an SA from this information. */
870 .authTransformId
= IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96
,
872 IPSEC_SA_TRANSFORM0 transforms
= {
873 .ipsecTransformType
= IPSEC_TRANSFORM_ESP_AUTH
,
874 .espAuthTransform
= &transform
,
876 IPSEC_PROPOSAL0 proposal
= {
878 /* We need a valid lifetime, even if we don't create any SA
879 * from these values. Pick some values accepted. */
880 .lifetimeSeconds
= 0xFFFF,
881 .lifetimeKilobytes
= 0xFFFFFFFF,
882 .lifetimePackets
= 0xFFFFFFFF,
884 .numSaTransforms
= 1,
885 .saTransforms
= &transforms
,
887 IPSEC_TUNNEL_POLICY0 policy
= {
888 .numIpsecProposals
= 1,
889 .ipsecProposals
= &proposal
,
891 /* not used, set to lifetime for maximum */
892 .idleTimeoutSeconds
= proposal
.lifetime
.lifetimeSeconds
,
893 .idleTimeoutSecondsFailOver
= proposal
.lifetime
.lifetimeSeconds
,
896 FWPM_PROVIDER_CONTEXT0
*ctx
, qm
= {
898 .name
= L
"charon tunnel provider context",
900 .providerKey
= (GUID
*)&this->provider
.providerKey
,
901 .type
= FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT
,
902 .ikeQmTunnelPolicy
= &policy
,
905 switch (entry
->local
->get_family(entry
->local
))
908 policy
.tunnelEndpoints
.ipVersion
= FWP_IP_VERSION_V4
;
909 policy
.tunnelEndpoints
.localV4Address
=
910 untoh32(entry
->local
->get_address(entry
->local
).ptr
);
911 policy
.tunnelEndpoints
.remoteV4Address
=
912 untoh32(entry
->remote
->get_address(entry
->remote
).ptr
);
915 policy
.tunnelEndpoints
.ipVersion
= FWP_IP_VERSION_V6
;
916 memcpy(&policy
.tunnelEndpoints
.localV6Address
,
917 entry
->local
->get_address(entry
->local
).ptr
, 16);
918 memcpy(&policy
.tunnelEndpoints
.remoteV6Address
,
919 entry
->remote
->get_address(entry
->remote
).ptr
, 16);
925 if (!generate_guid(this, &qm
.providerContextKey
))
930 enumerator
= array_create_enumerator(entry
->sps
);
931 while (enumerator
->enumerate(enumerator
, &sp
))
933 if (!ts2condition(sp
->src
, TRUE
, &conds
, &count
) ||
934 !ts2condition(sp
->dst
, FALSE
, &conds
, &count
))
936 free_conditions(conds
, count
);
937 enumerator
->destroy(enumerator
);
941 enumerator
->destroy(enumerator
);
943 res
= FwpmIPsecTunnelAdd0(this->handle
, 0, NULL
, &qm
, count
, conds
, NULL
);
944 free_conditions(conds
, count
);
945 if (res
!= ERROR_SUCCESS
)
947 DBG1(DBG_KNL
, "installing FWP tunnel policy failed: 0x%08x", res
);
951 /* to get the tunnelPolicyId LUID we have to query the context */
952 res
= FwpmProviderContextGetByKey0(this->handle
, &qm
.providerContextKey
,
954 if (res
!= ERROR_SUCCESS
)
956 DBG1(DBG_KNL
, "getting FWP tunnel policy context failed: 0x%08x", res
);
959 entry
->policy_in
= ctx
->providerContextId
;
960 FwpmFreeMemory0((void**)&ctx
);
966 * Install a tunnel mode SA/SP set to the kernel
968 static bool install_tunnel(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
970 if (install_tunnel_sps(this, entry
) &&
971 install_sas(this, entry
, IPSEC_TRAFFIC_TYPE_TUNNEL
))
975 cleanup_policies(this, entry
);
980 * Install a SA/SP set to the kernel
982 static bool install(private_kernel_wfp_ipsec_t
*this, entry_t
*entry
)
987 return install_transport(this, entry
);
989 return install_tunnel(this, entry
);
996 METHOD(kernel_ipsec_t
, get_features
, kernel_feature_t
,
997 private_kernel_wfp_ipsec_t
*this)
999 return KERNEL_ESP_V3_TFC
;
1002 METHOD(kernel_ipsec_t
, get_spi
, status_t
,
1003 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1004 u_int8_t protocol
, u_int32_t reqid
, u_int32_t
*spi
)
1006 *spi
= ref_get(&this->nextspi
);
1010 METHOD(kernel_ipsec_t
, get_cpi
, status_t
,
1011 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1012 u_int32_t reqid
, u_int16_t
*cpi
)
1014 return NOT_SUPPORTED
;
1018 * Data for an expire callback job
1021 /* backref to kernel backend */
1022 private_kernel_wfp_ipsec_t
*this;
1023 /* SPI of expiring SA */
1025 /* destination address of expiring SA */
1027 /* is this a hard expire, or a rekey request? */
1032 * Clean up expire data
1034 static void expire_data_destroy(expire_data_t
*data
)
1036 data
->dst
->destroy(data
->dst
);
1041 * Callback job for SA expiration
1043 static job_requeue_t
expire_job(expire_data_t
*data
)
1045 private_kernel_wfp_ipsec_t
*this = data
->this;
1046 u_int32_t reqid
= 0;
1056 this->mutex
->lock(this->mutex
);
1057 entry
= this->isas
->remove(this->isas
, &key
);
1058 this->mutex
->unlock(this->mutex
);
1061 protocol
= entry
->isa
.protocol
;
1062 reqid
= entry
->reqid
;
1065 key
.dst
= entry
->osa
.dst
;
1066 key
.spi
= entry
->osa
.spi
;
1067 this->osas
->remove(this->osas
, &key
);
1069 entry_destroy(this, entry
);
1074 this->mutex
->lock(this->mutex
);
1075 entry
= this->isas
->get(this->isas
, &key
);
1078 protocol
= entry
->isa
.protocol
;
1079 reqid
= entry
->reqid
;
1081 this->mutex
->unlock(this->mutex
);
1086 hydra
->kernel_interface
->expire(hydra
->kernel_interface
,
1087 reqid
, protocol
, data
->spi
, data
->hard
);
1090 return JOB_REQUEUE_NONE
;
1094 * Schedule an expire event for an SA
1096 static void schedule_expire(private_kernel_wfp_ipsec_t
*this, u_int32_t spi
,
1097 host_t
*dst
, u_int32_t lifetime
, bool hard
)
1099 expire_data_t
*data
;
1104 .dst
= dst
->clone(dst
),
1108 lib
->scheduler
->schedule_job(lib
->scheduler
, (job_t
*)
1109 callback_job_create((void*)expire_job
, data
,
1110 (void*)expire_data_destroy
, NULL
),
1114 METHOD(kernel_ipsec_t
, add_sa
, status_t
,
1115 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1116 u_int32_t spi
, u_int8_t protocol
, u_int32_t reqid
, mark_t mark
,
1117 u_int32_t tfc
, lifetime_cfg_t
*lifetime
, u_int16_t enc_alg
, chunk_t enc_key
,
1118 u_int16_t int_alg
, chunk_t int_key
, ipsec_mode_t mode
, u_int16_t ipcomp
,
1119 u_int16_t cpi
, bool initiator
, bool encap
, bool esn
, bool inbound
,
1120 traffic_selector_t
*src_ts
, traffic_selector_t
*dst_ts
)
1122 host_t
*local
, *remote
;
1127 /* comes first, create new entry */
1128 local
= dst
->clone(dst
);
1129 remote
= src
->clone(src
);
1136 .protocol
= protocol
,
1137 .lifetime
= lifetime
->time
.life
,
1140 .key
= chunk_clone(enc_key
),
1144 .key
= chunk_clone(int_key
),
1147 .sps
= array_create(0, 0),
1154 if (lifetime
->time
.life
)
1156 schedule_expire(this, spi
, local
, lifetime
->time
.life
, TRUE
);
1158 if (lifetime
->time
.rekey
&& lifetime
->time
.rekey
!= lifetime
->time
.life
)
1160 schedule_expire(this, spi
, local
, lifetime
->time
.rekey
, FALSE
);
1163 this->mutex
->lock(this->mutex
);
1164 this->tsas
->put(this->tsas
, (void*)(uintptr_t)reqid
, entry
);
1165 this->isas
->put(this->isas
, &entry
->isa
, entry
);
1166 this->mutex
->unlock(this->mutex
);
1170 /* comes after inbound, update entry */
1171 this->mutex
->lock(this->mutex
);
1172 entry
= this->tsas
->remove(this->tsas
, (void*)(uintptr_t)reqid
);
1173 this->mutex
->unlock(this->mutex
);
1177 DBG1(DBG_KNL
, "adding outbound SA failed, no inbound SA found "
1178 "for reqid %u ", reqid
);
1181 /* TODO: should we check for local/remote, mode etc.? */
1183 entry
->osa
= (sa_entry_t
){
1185 .dst
= entry
->remote
,
1186 .protocol
= protocol
,
1187 .lifetime
= lifetime
->time
.life
,
1190 .key
= chunk_clone(enc_key
),
1194 .key
= chunk_clone(int_key
),
1198 this->mutex
->lock(this->mutex
);
1199 this->osas
->put(this->osas
, &entry
->osa
, entry
);
1200 this->mutex
->unlock(this->mutex
);
1206 METHOD(kernel_ipsec_t
, update_sa
, status_t
,
1207 private_kernel_wfp_ipsec_t
*this, u_int32_t spi
, u_int8_t protocol
,
1208 u_int16_t cpi
, host_t
*src
, host_t
*dst
, host_t
*new_src
, host_t
*new_dst
,
1209 bool encap
, bool new_encap
, mark_t mark
)
1211 return NOT_SUPPORTED
;
1214 METHOD(kernel_ipsec_t
, query_sa
, status_t
,
1215 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1216 u_int32_t spi
, u_int8_t protocol
, mark_t mark
, u_int64_t
*bytes
,
1217 u_int64_t
*packets
, time_t *time
)
1219 /* It does not seem that WFP provides any means of getting per-SA traffic
1220 * statistics. IPsecGetStatistics0/1() provides global stats, and
1221 * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
1223 return NOT_SUPPORTED
;
1226 METHOD(kernel_ipsec_t
, del_sa
, status_t
,
1227 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1228 u_int32_t spi
, u_int8_t protocol
, u_int16_t cpi
, mark_t mark
)
1236 this->mutex
->lock(this->mutex
);
1237 entry
= this->isas
->remove(this->isas
, &key
);
1238 this->mutex
->unlock(this->mutex
);
1242 /* keep entry until removal of outbound */
1246 this->mutex
->lock(this->mutex
);
1247 entry
= this->osas
->remove(this->osas
, &key
);
1248 this->mutex
->unlock(this->mutex
);
1252 entry_destroy(this, entry
);
1259 METHOD(kernel_ipsec_t
, flush_sas
, status_t
,
1260 private_kernel_wfp_ipsec_t
*this)
1262 return NOT_SUPPORTED
;
1265 METHOD(kernel_ipsec_t
, add_policy
, status_t
,
1266 private_kernel_wfp_ipsec_t
*this, host_t
*src
, host_t
*dst
,
1267 traffic_selector_t
*src_ts
, traffic_selector_t
*dst_ts
,
1268 policy_dir_t direction
, policy_type_t type
, ipsec_sa_cfg_t
*sa
, mark_t mark
,
1269 policy_priority_t priority
)
1271 status_t status
= SUCCESS
;
1275 .spi
= sa
->esp
.use ? sa
->esp
.spi
: sa
->ah
.spi
,
1279 if (sa
->esp
.use
&& sa
->ah
.use
)
1281 return NOT_SUPPORTED
;
1293 return NOT_SUPPORTED
;
1298 case POLICY_PRIORITY_DEFAULT
:
1300 case POLICY_PRIORITY_FALLBACK
:
1301 /* TODO: install fallback policy? */
1303 case POLICY_PRIORITY_ROUTED
:
1304 /* TODO: install trap policy with low prio */
1306 return NOT_SUPPORTED
;
1309 this->mutex
->lock(this->mutex
);
1310 entry
= this->osas
->get(this->osas
, &key
);
1313 if (array_count(entry
->sps
) == 0)
1316 .src
= src_ts
->clone(src_ts
),
1317 .dst
= dst_ts
->clone(dst_ts
),
1319 array_insert(entry
->sps
, -1, sp
);
1320 if (!install(this, entry
))
1327 /* TODO: reinstall with a filter using multiple TS?
1328 * Filters are ANDed for a match, but we could install a filter
1329 * with the inverse TS set using NOT-matches... */
1330 status
= NOT_SUPPORTED
;
1335 DBG1(DBG_KNL
, "adding SP failed, no SA found for SPI 0x%08x", key
.spi
);
1338 this->mutex
->unlock(this->mutex
);
1343 METHOD(kernel_ipsec_t
, query_policy
, status_t
,
1344 private_kernel_wfp_ipsec_t
*this, traffic_selector_t
*src_ts
,
1345 traffic_selector_t
*dst_ts
, policy_dir_t direction
, mark_t mark
,
1348 /* see query_sa() for some notes */
1349 return NOT_SUPPORTED
;
1352 METHOD(kernel_ipsec_t
, del_policy
, status_t
,
1353 private_kernel_wfp_ipsec_t
*this, traffic_selector_t
*src_ts
,
1354 traffic_selector_t
*dst_ts
, policy_dir_t direction
, u_int32_t reqid
,
1355 mark_t mark
, policy_priority_t priority
)
1357 /* not required, as we delete the whole SA/SP set during del_sa() */
1361 METHOD(kernel_ipsec_t
, flush_policies
, status_t
,
1362 private_kernel_wfp_ipsec_t
*this)
1364 return NOT_SUPPORTED
;
1367 METHOD(kernel_ipsec_t
, bypass_socket
, bool,
1368 private_kernel_wfp_ipsec_t
*this, int fd
, int family
)
1370 return NOT_SUPPORTED
;
1373 METHOD(kernel_ipsec_t
, enable_udp_decap
, bool,
1374 private_kernel_wfp_ipsec_t
*this, int fd
, int family
, u_int16_t port
)
1376 return NOT_SUPPORTED
;
1379 METHOD(kernel_ipsec_t
, destroy
, void,
1380 private_kernel_wfp_ipsec_t
*this)
1384 FwpmProviderDeleteByKey0(this->handle
, &this->provider
.providerKey
);
1385 FwpmEngineClose0(this->handle
);
1387 this->tsas
->destroy(this->tsas
);
1388 this->isas
->destroy(this->isas
);
1389 this->osas
->destroy(this->osas
);
1390 this->mutex
->destroy(this->mutex
);
1395 * Described in header.
1397 kernel_wfp_ipsec_t
*kernel_wfp_ipsec_create()
1399 private_kernel_wfp_ipsec_t
*this;
1401 FWPM_SESSION0 session
= {
1404 .description
= L
"strongSwan IKE kernel-wfp backend",
1411 .get_features
= _get_features
,
1412 .get_spi
= _get_spi
,
1413 .get_cpi
= _get_cpi
,
1415 .update_sa
= _update_sa
,
1416 .query_sa
= _query_sa
,
1418 .flush_sas
= _flush_sas
,
1419 .add_policy
= _add_policy
,
1420 .query_policy
= _query_policy
,
1421 .del_policy
= _del_policy
,
1422 .flush_policies
= _flush_policies
,
1423 .bypass_socket
= _bypass_socket
,
1424 .enable_udp_decap
= _enable_udp_decap
,
1425 .destroy
= _destroy
,
1431 .description
= L
"strongSwan IKE kernel-wfp backend",
1433 .providerKey
= { 0x59cdae2e, 0xf6bb, 0x4c09,
1434 { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
1436 .nextspi
= htonl(0xc0000001),
1437 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
1438 .tsas
= hashtable_create(hashtable_hash_ptr
, hashtable_equals_ptr
, 4),
1439 .isas
= hashtable_create((void*)hash_sa
, (void*)equals_sa
, 4),
1440 .osas
= hashtable_create((void*)hash_sa
, (void*)equals_sa
, 4),
1443 res
= FwpmEngineOpen0(NULL
, RPC_C_AUTHN_WINNT
, NULL
, &session
,
1445 if (res
!= ERROR_SUCCESS
)
1447 DBG1(DBG_KNL
, "opening WFP engine failed: 0x%08x", res
);
1452 res
= FwpmProviderAdd0(this->handle
, &this->provider
, NULL
);
1453 if (res
!= ERROR_SUCCESS
&& res
!= FWP_E_ALREADY_EXISTS
)
1455 DBG1(DBG_KNL
, "registering WFP provider failed: 0x%08x", res
);
1460 return &this->public;