05798fdeec4494f08ed97f525465b0d4688bd7d3
[strongswan.git] / src / libcharon / plugins / kernel_wfp / kernel_wfp_ipsec.c
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 <hydra.h>
24 #include <threading/mutex.h>
25 #include <collections/array.h>
26 #include <collections/hashtable.h>
27 #include <processing/jobs/callback_job.h>
28
29
30 typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
31
32 struct private_kernel_wfp_ipsec_t {
33
34 /**
35 * Public interface
36 */
37 kernel_wfp_ipsec_t public;
38
39 /**
40 * Next SPI to allocate
41 */
42 refcount_t nextspi;
43
44 /**
45 * Mix value to distribute SPI allocation randomly
46 */
47 u_int32_t mixspi;
48
49 /**
50 * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
51 */
52 hashtable_t *tsas;
53
54 /**
55 * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
56 */
57 hashtable_t *isas;
58
59 /**
60 * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
61 */
62 hashtable_t *osas;
63
64 /**
65 * Installed routes, as route_t => route_t
66 */
67 hashtable_t *routes;
68
69 /**
70 * Mutex for accessing entries
71 */
72 mutex_t *mutex;
73
74 /**
75 * WFP session handle
76 */
77 HANDLE handle;
78
79 /**
80 * Provider charon registers as
81 */
82 FWPM_PROVIDER0 provider;
83 };
84
85 /**
86 * Security association entry
87 */
88 typedef struct {
89 /** SPI for this SA */
90 u_int32_t spi;
91 /** protocol, IPPROTO_ESP/IPPROTO_AH */
92 u_int8_t protocol;
93 /** hard lifetime of SA */
94 u_int32_t lifetime;
95 /** destination host address for this SPI */
96 host_t *dst;
97 struct {
98 /** algorithm */
99 u_int16_t alg;
100 /** key */
101 chunk_t key;
102 } integ, encr;
103 } sa_entry_t;
104
105 /**
106 * Hash function for sas lookup table
107 */
108 static u_int hash_sa(sa_entry_t *key)
109 {
110 return chunk_hash_inc(chunk_from_thing(key->spi),
111 chunk_hash(key->dst->get_address(key->dst)));
112 }
113
114 /**
115 * equals function for sas lookup table
116 */
117 static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
118 {
119 return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
120 }
121
122 /**
123 * Security policy entry
124 */
125 typedef struct {
126 /** policy source addresses */
127 traffic_selector_t *src;
128 /** policy destinaiton addresses */
129 traffic_selector_t *dst;
130 } sp_entry_t;
131
132 /**
133 * Destroy an SP entry
134 */
135 static void sp_entry_destroy(sp_entry_t *sp)
136 {
137 sp->src->destroy(sp->src);
138 sp->dst->destroy(sp->dst);
139 free(sp);
140 }
141
142 /**
143 * Collection of SA/SP database entries for a reqid
144 */
145 typedef struct {
146 /** reqid of entry */
147 u_int32_t reqid;
148 /** outer address on local host */
149 host_t *local;
150 /** outer address on remote host */
151 host_t *remote;
152 /** inbound SA entry */
153 sa_entry_t isa;
154 /** outbound SA entry */
155 sa_entry_t osa;
156 /** associated (outbound) policies, as sp_entry_t* */
157 array_t *sps;
158 /** IPsec mode, tunnel|transport */
159 ipsec_mode_t mode;
160 /** UDP encapsulation */
161 bool encap;
162 /** WFP allocated LUID for inbound filter/tunnel policy ID */
163 u_int64_t policy_in;
164 /** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
165 u_int64_t policy_out;
166 /** WFP allocated LUID for SA context */
167 u_int64_t sa_id;
168 } entry_t;
169
170 /**
171 * Installed route
172 */
173 typedef struct {
174 /** destination net of route */
175 host_t *dst;
176 /** prefix length of dst */
177 u_int8_t mask;
178 /** source address for route */
179 host_t *src;
180 /** gateway of route, NULL if directly attached */
181 host_t *gtw;
182 /** references for route */
183 u_int refs;
184 } route_t;
185
186 /**
187 * Destroy a route_t
188 */
189 static void destroy_route(route_t *this)
190 {
191 this->dst->destroy(this->dst);
192 this->src->destroy(this->src);
193 DESTROY_IF(this->gtw);
194 free(this);
195 }
196
197 /**
198 * Hashtable equals function for routes
199 */
200 static bool equals_route(route_t *a, route_t *b)
201 {
202 return a->mask == b->mask &&
203 a->dst->ip_equals(a->dst, b->dst) &&
204 a->src->ip_equals(a->src, b->src);
205 }
206
207 /**
208 * Hashtable hash function for routes
209 */
210 static u_int hash_route(route_t *route)
211 {
212 return chunk_hash_inc(route->src->get_address(route->src),
213 chunk_hash_inc(route->dst->get_address(route->dst), route->mask));
214 }
215
216 /** forward declaration */
217 static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
218 bool add);
219
220 /**
221 * Remove a transport or tunnel policy from kernel
222 */
223 static void cleanup_policy(private_kernel_wfp_ipsec_t *this, bool transport,
224 u_int64_t policy)
225 {
226 if (transport)
227 {
228 FwpmFilterDeleteById0(this->handle, policy);
229 }
230 else
231 {
232 FWPM_PROVIDER_CONTEXT0 *ctx;
233
234 if (FwpmProviderContextGetById0(this->handle, policy,
235 &ctx) == ERROR_SUCCESS)
236 {
237 FwpmIPsecTunnelDeleteByKey0(this->handle, &ctx->providerContextKey);
238 FwpmFreeMemory0((void**)&ctx);
239 }
240 }
241 }
242
243 /**
244 * Remove policies associated to an entry from kernel
245 */
246 static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
247 {
248 if (entry->policy_in)
249 {
250 cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_in);
251 entry->policy_in = 0;
252 }
253 if (entry->policy_out)
254 {
255 cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_out);
256 entry->policy_out = 0;
257 }
258 if (entry->mode == MODE_TUNNEL)
259 {
260 manage_routes(this, entry, FALSE);
261 }
262 }
263
264 /**
265 * Destroy a SA/SP entry set
266 */
267 static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
268 {
269 if (entry->sa_id)
270 {
271 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
272 }
273 cleanup_policies(this, entry);
274 array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
275 entry->local->destroy(entry->local);
276 entry->remote->destroy(entry->remote);
277 chunk_clear(&entry->isa.integ.key);
278 chunk_clear(&entry->isa.encr.key);
279 chunk_clear(&entry->osa.integ.key);
280 chunk_clear(&entry->osa.encr.key);
281 free(entry);
282 }
283
284 /**
285 * Append/Realloc a filter condition to an existing condition set
286 */
287 static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
288 int *count)
289 {
290 FWPM_FILTER_CONDITION0 *cond;
291
292 (*count)++;
293 *conds = realloc(*conds, *count * sizeof(*cond));
294 cond = *conds + *count - 1;
295 memset(cond, 0, sizeof(*cond));
296
297 return cond;
298 }
299
300 /**
301 * Convert an IPv4 prefix to a host order subnet mask
302 */
303 static u_int32_t prefix2mask(u_int8_t prefix)
304 {
305 u_int8_t netmask[4] = {};
306 int i;
307
308 for (i = 0; i < sizeof(netmask); i++)
309 {
310 if (prefix < 8)
311 {
312 netmask[i] = 0xFF << (8 - prefix);
313 break;
314 }
315 netmask[i] = 0xFF;
316 prefix -= 8;
317 }
318 return untoh32(netmask);
319 }
320
321 /**
322 * Convert a 16-bit range to a WFP condition
323 */
324 static void range2cond(FWPM_FILTER_CONDITION0 *cond,
325 u_int16_t from, u_int16_t to)
326 {
327 if (from == to)
328 {
329 cond->matchType = FWP_MATCH_EQUAL;
330 cond->conditionValue.type = FWP_UINT16;
331 cond->conditionValue.uint16 = from;
332 }
333 else
334 {
335 cond->matchType = FWP_MATCH_RANGE;
336 cond->conditionValue.type = FWP_RANGE_TYPE;
337 cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
338 cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
339 cond->conditionValue.rangeValue->valueLow.uint16 = from;
340 cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
341 cond->conditionValue.rangeValue->valueHigh.uint16 = to;
342 }
343 }
344
345 /**
346 * (Re-)allocate filter conditions for given local or remote traffic selector
347 */
348 static bool ts2condition(traffic_selector_t *ts, bool local,
349 FWPM_FILTER_CONDITION0 *conds[], int *count)
350 {
351 FWPM_FILTER_CONDITION0 *cond;
352 FWP_BYTE_ARRAY16 *addr;
353 FWP_RANGE0 *range;
354 u_int16_t from_port, to_port;
355 void *from, *to;
356 u_int8_t proto;
357 host_t *net;
358 u_int8_t prefix;
359
360 from = ts->get_from_address(ts).ptr;
361 to = ts->get_to_address(ts).ptr;
362 from_port = ts->get_from_port(ts);
363 to_port = ts->get_to_port(ts);
364
365 cond = append_condition(conds, count);
366 if (local)
367 {
368 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
369 }
370 else
371 {
372 cond->fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
373 }
374 if (ts->is_host(ts, NULL))
375 {
376 cond->matchType = FWP_MATCH_EQUAL;
377 switch (ts->get_type(ts))
378 {
379 case TS_IPV4_ADDR_RANGE:
380 cond->conditionValue.type = FWP_UINT32;
381 cond->conditionValue.uint32 = untoh32(from);
382 break;
383 case TS_IPV6_ADDR_RANGE:
384 cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
385 cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
386 memcpy(addr, from, sizeof(*addr));
387 break;
388 default:
389 return FALSE;
390 }
391 }
392 else if (ts->to_subnet(ts, &net, &prefix))
393 {
394 FWP_V6_ADDR_AND_MASK *m6;
395 FWP_V4_ADDR_AND_MASK *m4;
396
397 cond->matchType = FWP_MATCH_EQUAL;
398 switch (net->get_family(net))
399 {
400 case AF_INET:
401 cond->conditionValue.type = FWP_V4_ADDR_MASK;
402 cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
403 m4->addr = untoh32(from);
404 m4->mask = prefix2mask(prefix);
405 break;
406 case AF_INET6:
407 cond->conditionValue.type = FWP_V6_ADDR_MASK;
408 cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
409 memcpy(m6->addr, from, sizeof(m6->addr));
410 m6->prefixLength = prefix;
411 break;
412 default:
413 net->destroy(net);
414 return FALSE;
415 }
416 net->destroy(net);
417 }
418 else
419 {
420 cond->matchType = FWP_MATCH_RANGE;
421 cond->conditionValue.type = FWP_RANGE_TYPE;
422 cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
423 switch (ts->get_type(ts))
424 {
425 case TS_IPV4_ADDR_RANGE:
426 range->valueLow.type = FWP_UINT32;
427 range->valueLow.uint32 = untoh32(from);
428 range->valueHigh.type = FWP_UINT32;
429 range->valueHigh.uint32 = untoh32(to);
430 break;
431 case TS_IPV6_ADDR_RANGE:
432 range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
433 range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
434 memcpy(addr, from, sizeof(*addr));
435 range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
436 range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
437 memcpy(addr, to, sizeof(*addr));
438 break;
439 default:
440 return FALSE;
441 }
442 }
443
444 proto = ts->get_protocol(ts);
445 if (proto && local)
446 {
447 cond = append_condition(conds, count);
448 cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
449 cond->matchType = FWP_MATCH_EQUAL;
450 cond->conditionValue.type = FWP_UINT8;
451 cond->conditionValue.uint8 = proto;
452 }
453
454 if (proto == IPPROTO_ICMP)
455 {
456 if (local)
457 {
458 u_int8_t from_type, to_type, from_code, to_code;
459
460 from_type = traffic_selector_icmp_type(from_port);
461 to_type = traffic_selector_icmp_type(to_port);
462 from_code = traffic_selector_icmp_code(from_port);
463 to_code = traffic_selector_icmp_code(to_port);
464
465 if (from_type != 0 || to_type != 0xFF)
466 {
467 cond = append_condition(conds, count);
468 cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
469 range2cond(cond, from_type, to_type);
470 }
471 if (from_code != 0 || to_code != 0xFF)
472 {
473 cond = append_condition(conds, count);
474 cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
475 range2cond(cond, from_code, to_code);
476 }
477 }
478 }
479 else if (from_port != 0 || to_port != 0xFFFF)
480 {
481 cond = append_condition(conds, count);
482 if (local)
483 {
484 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
485 }
486 else
487 {
488 cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
489 }
490 range2cond(cond, from_port, to_port);
491 }
492 return TRUE;
493 }
494
495 /**
496 * Free memory associated to a single condition
497 */
498 static void free_condition(FWP_DATA_TYPE type, void *value)
499 {
500 FWP_RANGE0 *range;
501
502 switch (type)
503 {
504 case FWP_BYTE_ARRAY16_TYPE:
505 case FWP_V4_ADDR_MASK:
506 case FWP_V6_ADDR_MASK:
507 free(value);
508 break;
509 case FWP_RANGE_TYPE:
510 range = value;
511 free_condition(range->valueLow.type, range->valueLow.sd);
512 free_condition(range->valueHigh.type, range->valueHigh.sd);
513 free(range);
514 break;
515 default:
516 break;
517 }
518 }
519
520 /**
521 * Free memory used by a set of conditions
522 */
523 static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
524 {
525 int i;
526
527 for (i = 0; i < count; i++)
528 {
529 free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
530 }
531 free(conds);
532 }
533
534 /**
535 * Install transport mode SP to the kernel
536 */
537 static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
538 entry_t *entry, bool inbound)
539 {
540 FWPM_FILTER_CONDITION0 *conds = NULL;
541 int count = 0;
542 enumerator_t *enumerator;
543 traffic_selector_t *local, *remote;
544 sp_entry_t *sp;
545 DWORD res;
546 FWPM_FILTER0 filter = {
547 .displayData = {
548 .name = L"charon IPsec transport",
549 },
550 .action = {
551 .type = FWP_ACTION_CALLOUT_TERMINATING,
552 .calloutKey = inbound ? FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 :
553 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4,
554 },
555 .layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 :
556 FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
557 };
558
559 enumerator = array_create_enumerator(entry->sps);
560 while (enumerator->enumerate(enumerator, &sp))
561 {
562 if (inbound)
563 {
564 local = sp->dst;
565 remote = sp->src;
566 }
567 else
568 {
569 local = sp->src;
570 remote = sp->dst;
571 }
572
573 if (!ts2condition(local, TRUE, &conds, &count) ||
574 !ts2condition(remote, FALSE, &conds, &count))
575 {
576 free_conditions(conds, count);
577 enumerator->destroy(enumerator);
578 return FALSE;
579 }
580 }
581 enumerator->destroy(enumerator);
582
583 filter.numFilterConditions = count;
584 filter.filterCondition = conds;
585
586 if (inbound)
587 {
588 res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_in);
589 }
590 else
591 {
592 res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_out);
593 }
594 free_conditions(conds, count);
595 if (res != ERROR_SUCCESS)
596 {
597 DBG1(DBG_KNL, "installing inbound FWP filter failed: 0x%08x", res);
598 return FALSE;
599 }
600 return TRUE;
601 }
602
603 /**
604 * Convert a chunk_t to a WFP FWP_BYTE_BLOB
605 */
606 static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
607 {
608 return (FWP_BYTE_BLOB){
609 .size = chunk.len,
610 .data = chunk.ptr,
611 };
612 }
613
614 /**
615 * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
616 */
617 static bool alg2auth(integrity_algorithm_t alg,
618 IPSEC_SA_AUTH_INFORMATION0 *info)
619 {
620 struct {
621 integrity_algorithm_t alg;
622 IPSEC_AUTH_TRANSFORM_ID0 transform;
623 } map[] = {
624 { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
625 { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
626 { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
627 { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
628 { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
629 { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
630 };
631 int i;
632
633 for (i = 0; i < countof(map); i++)
634 {
635 if (map[i].alg == alg)
636 {
637 info->authTransform.authTransformId = map[i].transform;
638 return TRUE;
639 }
640 }
641 return FALSE;
642 }
643
644 /**
645 * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
646 */
647 static bool alg2cipher(encryption_algorithm_t alg, int keylen,
648 IPSEC_SA_CIPHER_INFORMATION0 *info)
649 {
650 struct {
651 encryption_algorithm_t alg;
652 int keylen;
653 IPSEC_CIPHER_TRANSFORM_ID0 transform;
654 } map[] = {
655 { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
656 { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
657 { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
658 { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
659 { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
660 { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
661 { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
662 { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
663 };
664 int i;
665
666 for (i = 0; i < countof(map); i++)
667 {
668 if (map[i].alg == alg && map[i].keylen == keylen)
669 {
670 info->cipherTransform.cipherTransformId = map[i].transform;
671 return TRUE;
672 }
673 }
674 return FALSE;
675 }
676
677 /**
678 * Get the integrity algorithm used for an AEAD transform
679 */
680 static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
681 {
682 struct {
683 encryption_algorithm_t encr;
684 int keylen;
685 integrity_algorithm_t integ;
686 } map[] = {
687 { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
688 { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
689 { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
690 { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
691 { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
692 { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
693 };
694 int i;
695
696 for (i = 0; i < countof(map); i++)
697 {
698 if (map[i].encr == encr && map[i].keylen == keylen)
699 {
700 return map[i].integ;
701 }
702 }
703 return AUTH_UNDEFINED;
704 }
705
706 /**
707 * Install a single SA
708 */
709 static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
710 bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
711 {
712 IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
713 IPSEC_SA0 ipsec = {
714 .spi = ntohl(sa->spi),
715 };
716 IPSEC_SA_BUNDLE0 bundle = {
717 .lifetime = {
718 .lifetimeSeconds = inbound ? entry->isa.lifetime
719 : entry->osa.lifetime,
720 },
721 .saList = &ipsec,
722 .numSAs = 1,
723 .ipVersion = version,
724 };
725 struct {
726 u_int16_t alg;
727 chunk_t key;
728 } integ = {}, encr = {};
729 DWORD res;
730
731 switch (sa->protocol)
732 {
733 case IPPROTO_AH:
734 ipsec.saTransformType = IPSEC_TRANSFORM_AH;
735 ipsec.ahInformation = &info.saAuthInformation;
736 integ.key = sa->integ.key;
737 integ.alg = sa->integ.alg;
738 break;
739 case IPPROTO_ESP:
740 if (sa->encr.alg == ENCR_NULL ||
741 sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
742 {
743 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
744 ipsec.espAuthInformation = &info.saAuthInformation;
745 }
746 else
747 {
748 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
749 ipsec.espAuthAndCipherInformation = &info;
750 encr.key = sa->encr.key;
751 encr.alg = sa->encr.alg;
752 }
753 if (encryption_algorithm_is_aead(sa->encr.alg))
754 {
755 integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
756 integ.key = sa->encr.key;
757 }
758 else
759 {
760 integ.alg = sa->integ.alg;
761 integ.key = sa->integ.key;
762 }
763 break;
764 default:
765 return FALSE;
766 }
767
768 if (integ.alg)
769 {
770 info.saAuthInformation.authKey = chunk2blob(integ.key);
771 if (!alg2auth(integ.alg, &info.saAuthInformation))
772 {
773 DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
774 integrity_algorithm_names, integ.alg);
775 return FALSE;
776 }
777 }
778 if (encr.alg)
779 {
780 info.saCipherInformation.cipherKey = chunk2blob(encr.key);
781 if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
782 {
783 DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
784 encryption_algorithm_names, encr.alg);
785 return FALSE;
786 }
787 }
788
789 if (inbound)
790 {
791 res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
792 }
793 else
794 {
795 res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
796 }
797 if (res != ERROR_SUCCESS)
798 {
799 DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
800 inbound ? "in" : "out", res);
801 return FALSE;
802 }
803 return TRUE;
804 }
805
806 /**
807 * Fill in traffic structure from entry addresses
808 */
809 static bool hosts2traffic(private_kernel_wfp_ipsec_t *this,
810 host_t *l, host_t *r, IPSEC_TRAFFIC1 *traffic)
811 {
812 if (l->get_family(l) != r->get_family(r))
813 {
814 return FALSE;
815 }
816 switch (l->get_family(l))
817 {
818 case AF_INET:
819 traffic->ipVersion = FWP_IP_VERSION_V4;
820 traffic->localV4Address = untoh32(l->get_address(l).ptr);
821 traffic->remoteV4Address = untoh32(r->get_address(r).ptr);
822 return TRUE;
823 case AF_INET6:
824 traffic->ipVersion = FWP_IP_VERSION_V6;
825 memcpy(&traffic->localV6Address, l->get_address(l).ptr, 16);
826 memcpy(&traffic->remoteV6Address, r->get_address(r).ptr, 16);
827 return TRUE;
828 default:
829 return FALSE;
830 }
831 }
832
833 /**
834 * Install SAs to the kernel
835 */
836 static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
837 IPSEC_TRAFFIC_TYPE type)
838 {
839 IPSEC_TRAFFIC1 traffic = {
840 .trafficType = type,
841 };
842 IPSEC_GETSPI1 spi = {
843 .inboundIpsecTraffic = {
844 .trafficType = type,
845 },
846 };
847 DWORD res;
848
849 if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
850 {
851 traffic.ipsecFilterId = entry->policy_out;
852 spi.inboundIpsecTraffic.ipsecFilterId = entry->policy_in;
853 }
854 else
855 {
856 traffic.tunnelPolicyId = entry->policy_in;
857 spi.inboundIpsecTraffic.tunnelPolicyId = entry->policy_in;
858 }
859
860 if (!hosts2traffic(this, entry->local, entry->remote, &traffic))
861 {
862 return FALSE;
863 }
864
865 res = IPsecSaContextCreate1(this->handle, &traffic, NULL, NULL,
866 &entry->sa_id);
867 if (res != ERROR_SUCCESS)
868 {
869 DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
870 return FALSE;
871 }
872
873 memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
874 sizeof(traffic.localV6Address));
875 memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
876 sizeof(traffic.remoteV6Address));
877 spi.ipVersion = traffic.ipVersion;
878
879 res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
880 ntohl(entry->isa.spi));
881 if (res != ERROR_SUCCESS)
882 {
883 DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
884 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
885 entry->sa_id = 0;
886 return FALSE;
887 }
888
889 if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
890 !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
891 {
892 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
893 entry->sa_id = 0;
894 return FALSE;
895 }
896
897 return TRUE;
898 }
899
900 /**
901 * Install a transport mode SA/SP set to the kernel
902 */
903 static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
904 {
905 if (install_transport_sp(this, entry, TRUE) &&
906 install_transport_sp(this, entry, FALSE) &&
907 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
908 {
909 return TRUE;
910 }
911 cleanup_policies(this, entry);
912 return FALSE;
913 }
914
915 /**
916 * Generate a new GUID, random
917 */
918 static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
919 {
920 bool ok;
921 rng_t *rng;
922
923 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
924 if (!rng)
925 {
926 return FALSE;
927 }
928 ok = rng->get_bytes(rng, sizeof(GUID), (u_int8_t*)guid);
929 rng->destroy(rng);
930 return ok;
931 }
932
933 /**
934 * Install tunnel mode SPs to the kernel
935 */
936 static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
937 {
938 FWPM_FILTER_CONDITION0 *conds = NULL;
939 int count = 0;
940 enumerator_t *enumerator;
941 sp_entry_t *sp;
942 DWORD res;
943
944 IPSEC_AUTH_TRANSFORM0 transform = {
945 /* Create any valid proposal. This is actually not used, as we
946 * don't create an SA from this information. */
947 .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
948 };
949 IPSEC_SA_TRANSFORM0 transforms = {
950 .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
951 .espAuthTransform = &transform,
952 };
953 IPSEC_PROPOSAL0 proposal = {
954 .lifetime = {
955 /* We need a valid lifetime, even if we don't create any SA
956 * from these values. Pick some values accepted. */
957 .lifetimeSeconds = 0xFFFF,
958 .lifetimeKilobytes = 0xFFFFFFFF,
959 .lifetimePackets = 0xFFFFFFFF,
960 },
961 .numSaTransforms = 1,
962 .saTransforms = &transforms,
963 };
964 IPSEC_TUNNEL_POLICY0 policy = {
965 .numIpsecProposals = 1,
966 .ipsecProposals = &proposal,
967 .saIdleTimeout = {
968 /* not used, set to lifetime for maximum */
969 .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
970 .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
971 },
972 };
973 FWPM_PROVIDER_CONTEXT0 *ctx, qm = {
974 .displayData = {
975 .name = L"charon tunnel provider context",
976 },
977 .providerKey = (GUID*)&this->provider.providerKey,
978 .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
979 .ikeQmTunnelPolicy = &policy,
980 };
981
982 switch (entry->local->get_family(entry->local))
983 {
984 case AF_INET:
985 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
986 policy.tunnelEndpoints.localV4Address =
987 untoh32(entry->local->get_address(entry->local).ptr);
988 policy.tunnelEndpoints.remoteV4Address =
989 untoh32(entry->remote->get_address(entry->remote).ptr);
990 break;
991 case AF_INET6:
992 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
993 memcpy(&policy.tunnelEndpoints.localV6Address,
994 entry->local->get_address(entry->local).ptr, 16);
995 memcpy(&policy.tunnelEndpoints.remoteV6Address,
996 entry->remote->get_address(entry->remote).ptr, 16);
997 break;
998 default:
999 return FALSE;
1000 }
1001
1002 if (!generate_guid(this, &qm.providerContextKey))
1003 {
1004 return FALSE;
1005 }
1006
1007 enumerator = array_create_enumerator(entry->sps);
1008 while (enumerator->enumerate(enumerator, &sp))
1009 {
1010 if (!ts2condition(sp->src, TRUE, &conds, &count) ||
1011 !ts2condition(sp->dst, FALSE, &conds, &count))
1012 {
1013 free_conditions(conds, count);
1014 enumerator->destroy(enumerator);
1015 return FALSE;
1016 }
1017 }
1018 enumerator->destroy(enumerator);
1019
1020 res = FwpmIPsecTunnelAdd0(this->handle, 0, NULL, &qm, count, conds, NULL);
1021 free_conditions(conds, count);
1022 if (res != ERROR_SUCCESS)
1023 {
1024 DBG1(DBG_KNL, "installing FWP tunnel policy failed: 0x%08x", res);
1025 return FALSE;
1026 }
1027
1028 /* to get the tunnelPolicyId LUID we have to query the context */
1029 res = FwpmProviderContextGetByKey0(this->handle, &qm.providerContextKey,
1030 &ctx);
1031 if (res != ERROR_SUCCESS)
1032 {
1033 DBG1(DBG_KNL, "getting FWP tunnel policy context failed: 0x%08x", res);
1034 return FALSE;
1035 }
1036 entry->policy_in = ctx->providerContextId;
1037 FwpmFreeMemory0((void**)&ctx);
1038
1039 return TRUE;
1040 }
1041
1042 /**
1043 * Reduce refcount, or uninstall a route if all refs gone
1044 */
1045 static bool uninstall_route(private_kernel_wfp_ipsec_t *this,
1046 host_t *dst, u_int8_t mask, host_t *src, host_t *gtw)
1047 {
1048 route_t *route, key = {
1049 .dst = dst,
1050 .mask = mask,
1051 .src = src,
1052 };
1053 char *name;
1054 bool res = FALSE;
1055
1056 this->mutex->lock(this->mutex);
1057 route = this->routes->get(this->routes, &key);
1058 if (route)
1059 {
1060 if (--route->refs == 0)
1061 {
1062 if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
1063 src, &name))
1064 {
1065 res = hydra->kernel_interface->del_route(hydra->kernel_interface,
1066 dst->get_address(dst), mask, gtw, src, name) == SUCCESS;
1067 free(name);
1068 }
1069 route = this->routes->remove(this->routes, route);
1070 if (route)
1071 {
1072 destroy_route(route);
1073 }
1074 }
1075 else
1076 {
1077 res = TRUE;
1078 }
1079 }
1080 this->mutex->unlock(this->mutex);
1081
1082 return res;
1083 }
1084
1085 /**
1086 * Install a single route, or refcount if exists
1087 */
1088 static bool install_route(private_kernel_wfp_ipsec_t *this,
1089 host_t *dst, u_int8_t mask, host_t *src, host_t *gtw)
1090 {
1091 route_t *route, key = {
1092 .dst = dst,
1093 .mask = mask,
1094 .src = src,
1095 };
1096 char *name;
1097 bool res = FALSE;
1098
1099 this->mutex->lock(this->mutex);
1100 route = this->routes->get(this->routes, &key);
1101 if (route)
1102 {
1103 route->refs++;
1104 res = TRUE;
1105 }
1106 else
1107 {
1108 if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
1109 src, &name))
1110 {
1111 if (hydra->kernel_interface->add_route(hydra->kernel_interface,
1112 dst->get_address(dst), mask, gtw, src, name) == SUCCESS)
1113 {
1114 INIT(route,
1115 .dst = dst->clone(dst),
1116 .mask = mask,
1117 .src = src->clone(src),
1118 .gtw = gtw ? gtw->clone(gtw) : NULL,
1119 .refs = 1,
1120 );
1121 route = this->routes->put(this->routes, route, route);
1122 if (route)
1123 {
1124 destroy_route(route);
1125 }
1126 res = TRUE;
1127 }
1128 free(name);
1129 }
1130 }
1131 this->mutex->unlock(this->mutex);
1132
1133 return res;
1134 }
1135
1136 /**
1137 * (Un)-install routes for IPsec policies
1138 */
1139 static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
1140 bool add)
1141 {
1142 enumerator_t *enumerator;
1143 host_t *src, *dst, *gtw;
1144 sp_entry_t *sp;
1145 u_int8_t mask;
1146
1147 enumerator = array_create_enumerator(entry->sps);
1148 while (enumerator->enumerate(enumerator, &sp))
1149 {
1150 if (!sp->dst->to_subnet(sp->dst, &dst, &mask))
1151 {
1152 continue;
1153 }
1154 if (hydra->kernel_interface->get_address_by_ts( hydra->kernel_interface,
1155 sp->src, &src, NULL) != SUCCESS)
1156 {
1157 dst->destroy(dst);
1158 continue;
1159 }
1160 gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface,
1161 entry->remote, entry->local);
1162 if (add)
1163 {
1164 if (!install_route(this, dst, mask, src, gtw))
1165 {
1166 DBG1(DBG_KNL, "installing route for policy %R === %R failed",
1167 sp->src, sp->dst);
1168 }
1169 }
1170 else
1171 {
1172 if (!uninstall_route(this, dst, mask, src, gtw))
1173 {
1174 DBG1(DBG_KNL, "uninstalling route for policy %R === %R failed",
1175 sp->src, sp->dst);
1176 }
1177 }
1178 dst->destroy(dst);
1179 src->destroy(src);
1180 DESTROY_IF(gtw);
1181 }
1182 enumerator->destroy(enumerator);
1183
1184 return TRUE;
1185 }
1186
1187 /**
1188 * Install a tunnel mode SA/SP set to the kernel
1189 */
1190 static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1191 {
1192 if (install_tunnel_sps(this, entry) &&
1193 manage_routes(this, entry, TRUE) &&
1194 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
1195 {
1196 return TRUE;
1197 }
1198 cleanup_policies(this, entry);
1199 return FALSE;
1200 }
1201
1202 /**
1203 * Install a SA/SP set to the kernel
1204 */
1205 static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1206 {
1207 switch (entry->mode)
1208 {
1209 case MODE_TRANSPORT:
1210 return install_transport(this, entry);
1211 case MODE_TUNNEL:
1212 return install_tunnel(this, entry);
1213 case MODE_BEET:
1214 default:
1215 return FALSE;
1216 }
1217 }
1218
1219 METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
1220 private_kernel_wfp_ipsec_t *this)
1221 {
1222 return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES;
1223 }
1224
1225 /**
1226 * Initialize seeds for SPI generation
1227 */
1228 static bool init_spi(private_kernel_wfp_ipsec_t *this)
1229 {
1230 bool ok = TRUE;
1231 rng_t *rng;
1232
1233 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
1234 if (!rng)
1235 {
1236 return FALSE;
1237 }
1238 ok = rng->get_bytes(rng, sizeof(this->nextspi), (u_int8_t*)&this->nextspi);
1239 if (ok)
1240 {
1241 ok = rng->get_bytes(rng, sizeof(this->mixspi), (u_int8_t*)&this->mixspi);
1242 }
1243 rng->destroy(rng);
1244 return ok;
1245 }
1246
1247 /**
1248 * Map an integer x with a one-to-one function using quadratic residues.
1249 */
1250 static u_int permute(u_int x, u_int p)
1251 {
1252 u_int qr;
1253
1254 x = x % p;
1255 qr = ((u_int64_t)x * x) % p;
1256 if (x <= p / 2)
1257 {
1258 return qr;
1259 }
1260 return p - qr;
1261 }
1262
1263 METHOD(kernel_ipsec_t, get_spi, status_t,
1264 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1265 u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
1266 {
1267 /* To avoid sequencial SPIs, we use a one-to-one permuation function on
1268 * an incrementing counter, that is a full period PRNG for the range we
1269 * allocate SPIs in. We add some randomness using a fixed XOR and start
1270 * the counter at random position. This is not cryptographically safe,
1271 * but that is actually not required.
1272 * The selected prime should be smaller than the range we allocate SPIs
1273 * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */
1274 static const u_int p = 268435399, offset = 0xc0000000;
1275
1276 *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p));
1277 return SUCCESS;
1278 }
1279
1280 METHOD(kernel_ipsec_t, get_cpi, status_t,
1281 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1282 u_int32_t reqid, u_int16_t *cpi)
1283 {
1284 return NOT_SUPPORTED;
1285 }
1286
1287 /**
1288 * Data for an expire callback job
1289 */
1290 typedef struct {
1291 /* backref to kernel backend */
1292 private_kernel_wfp_ipsec_t *this;
1293 /* SPI of expiring SA */
1294 u_int32_t spi;
1295 /* destination address of expiring SA */
1296 host_t *dst;
1297 /* is this a hard expire, or a rekey request? */
1298 bool hard;
1299 } expire_data_t;
1300
1301 /**
1302 * Clean up expire data
1303 */
1304 static void expire_data_destroy(expire_data_t *data)
1305 {
1306 data->dst->destroy(data->dst);
1307 free(data);
1308 }
1309
1310 /**
1311 * Callback job for SA expiration
1312 */
1313 static job_requeue_t expire_job(expire_data_t *data)
1314 {
1315 private_kernel_wfp_ipsec_t *this = data->this;
1316 u_int32_t reqid = 0;
1317 u_int8_t protocol;
1318 entry_t *entry;
1319 sa_entry_t key = {
1320 .spi = data->spi,
1321 .dst = data->dst,
1322 };
1323
1324 if (data->hard)
1325 {
1326 this->mutex->lock(this->mutex);
1327 entry = this->isas->remove(this->isas, &key);
1328 this->mutex->unlock(this->mutex);
1329 if (entry)
1330 {
1331 protocol = entry->isa.protocol;
1332 reqid = entry->reqid;
1333 if (entry->osa.dst)
1334 {
1335 key.dst = entry->osa.dst;
1336 key.spi = entry->osa.spi;
1337 this->osas->remove(this->osas, &key);
1338 }
1339 entry_destroy(this, entry);
1340 }
1341 }
1342 else
1343 {
1344 this->mutex->lock(this->mutex);
1345 entry = this->isas->get(this->isas, &key);
1346 if (entry)
1347 {
1348 protocol = entry->isa.protocol;
1349 reqid = entry->reqid;
1350 }
1351 this->mutex->unlock(this->mutex);
1352 }
1353
1354 if (reqid)
1355 {
1356 hydra->kernel_interface->expire(hydra->kernel_interface,
1357 reqid, protocol, data->spi, data->hard);
1358 }
1359
1360 return JOB_REQUEUE_NONE;
1361 }
1362
1363 /**
1364 * Schedule an expire event for an SA
1365 */
1366 static void schedule_expire(private_kernel_wfp_ipsec_t *this, u_int32_t spi,
1367 host_t *dst, u_int32_t lifetime, bool hard)
1368 {
1369 expire_data_t *data;
1370
1371 INIT(data,
1372 .this = this,
1373 .spi = spi,
1374 .dst = dst->clone(dst),
1375 .hard = hard,
1376 );
1377
1378 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
1379 callback_job_create((void*)expire_job, data,
1380 (void*)expire_data_destroy, NULL),
1381 lifetime);
1382 }
1383
1384 METHOD(kernel_ipsec_t, add_sa, status_t,
1385 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1386 u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
1387 u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
1388 u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
1389 u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound,
1390 traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
1391 {
1392 host_t *local, *remote;
1393 entry_t *entry;
1394
1395 if (inbound)
1396 {
1397 /* comes first, create new entry */
1398 local = dst->clone(dst);
1399 remote = src->clone(src);
1400
1401 INIT(entry,
1402 .reqid = reqid,
1403 .isa = {
1404 .spi = spi,
1405 .dst = local,
1406 .protocol = protocol,
1407 .lifetime = lifetime->time.life,
1408 .encr = {
1409 .alg = enc_alg,
1410 .key = chunk_clone(enc_key),
1411 },
1412 .integ = {
1413 .alg = int_alg,
1414 .key = chunk_clone(int_key),
1415 },
1416 },
1417 .sps = array_create(0, 0),
1418 .local = local,
1419 .remote = remote,
1420 .mode = mode,
1421 .encap = encap,
1422 );
1423
1424 if (lifetime->time.life)
1425 {
1426 schedule_expire(this, spi, local, lifetime->time.life, TRUE);
1427 }
1428 if (lifetime->time.rekey && lifetime->time.rekey != lifetime->time.life)
1429 {
1430 schedule_expire(this, spi, local, lifetime->time.rekey, FALSE);
1431 }
1432
1433 this->mutex->lock(this->mutex);
1434 this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry);
1435 this->isas->put(this->isas, &entry->isa, entry);
1436 this->mutex->unlock(this->mutex);
1437 }
1438 else
1439 {
1440 /* comes after inbound, update entry */
1441 this->mutex->lock(this->mutex);
1442 entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)reqid);
1443 this->mutex->unlock(this->mutex);
1444
1445 if (!entry)
1446 {
1447 DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
1448 "for reqid %u ", reqid);
1449 return NOT_FOUND;
1450 }
1451 /* TODO: should we check for local/remote, mode etc.? */
1452
1453 entry->osa = (sa_entry_t){
1454 .spi = spi,
1455 .dst = entry->remote,
1456 .protocol = protocol,
1457 .lifetime = lifetime->time.life,
1458 .encr = {
1459 .alg = enc_alg,
1460 .key = chunk_clone(enc_key),
1461 },
1462 .integ = {
1463 .alg = int_alg,
1464 .key = chunk_clone(int_key),
1465 },
1466 };
1467
1468 this->mutex->lock(this->mutex);
1469 this->osas->put(this->osas, &entry->osa, entry);
1470 this->mutex->unlock(this->mutex);
1471 }
1472
1473 return SUCCESS;
1474 }
1475
1476 METHOD(kernel_ipsec_t, update_sa, status_t,
1477 private_kernel_wfp_ipsec_t *this, u_int32_t spi, u_int8_t protocol,
1478 u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
1479 bool encap, bool new_encap, mark_t mark)
1480 {
1481 return NOT_SUPPORTED;
1482 }
1483
1484 METHOD(kernel_ipsec_t, query_sa, status_t,
1485 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1486 u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes,
1487 u_int64_t *packets, time_t *time)
1488 {
1489 /* It does not seem that WFP provides any means of getting per-SA traffic
1490 * statistics. IPsecGetStatistics0/1() provides global stats, and
1491 * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
1492 * values only. */
1493 return NOT_SUPPORTED;
1494 }
1495
1496 METHOD(kernel_ipsec_t, del_sa, status_t,
1497 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1498 u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark)
1499 {
1500 entry_t *entry;
1501 sa_entry_t key = {
1502 .dst = dst,
1503 .spi = spi,
1504 };
1505
1506 this->mutex->lock(this->mutex);
1507 entry = this->isas->remove(this->isas, &key);
1508 this->mutex->unlock(this->mutex);
1509
1510 if (entry)
1511 {
1512 /* keep entry until removal of outbound */
1513 return SUCCESS;
1514 }
1515
1516 this->mutex->lock(this->mutex);
1517 entry = this->osas->remove(this->osas, &key);
1518 this->mutex->unlock(this->mutex);
1519
1520 if (entry)
1521 {
1522 entry_destroy(this, entry);
1523 return SUCCESS;
1524 }
1525
1526 return NOT_FOUND;
1527 }
1528
1529 METHOD(kernel_ipsec_t, flush_sas, status_t,
1530 private_kernel_wfp_ipsec_t *this)
1531 {
1532 return NOT_SUPPORTED;
1533 }
1534
1535 METHOD(kernel_ipsec_t, add_policy, status_t,
1536 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1537 traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
1538 policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
1539 policy_priority_t priority)
1540 {
1541 status_t status = SUCCESS;
1542 entry_t *entry;
1543 sp_entry_t *sp;
1544 sa_entry_t key = {
1545 .spi = sa->esp.use ? sa->esp.spi : sa->ah.spi,
1546 .dst = dst,
1547 };
1548
1549 if (sa->esp.use && sa->ah.use)
1550 {
1551 return NOT_SUPPORTED;
1552 }
1553
1554 switch (type)
1555 {
1556 case POLICY_IPSEC:
1557 break;
1558 case POLICY_PASS:
1559 case POLICY_DROP:
1560 return NOT_SUPPORTED;
1561 }
1562
1563 switch (direction)
1564 {
1565 case POLICY_OUT:
1566 break;
1567 case POLICY_IN:
1568 case POLICY_FWD:
1569 /* not required */
1570 return SUCCESS;
1571 default:
1572 return NOT_SUPPORTED;
1573 }
1574
1575 switch (priority)
1576 {
1577 case POLICY_PRIORITY_DEFAULT:
1578 break;
1579 case POLICY_PRIORITY_ROUTED:
1580 /* TODO: install trap policy with low prio */
1581 case POLICY_PRIORITY_FALLBACK:
1582 default:
1583 return NOT_SUPPORTED;
1584 }
1585
1586 this->mutex->lock(this->mutex);
1587 entry = this->osas->get(this->osas, &key);
1588 if (entry)
1589 {
1590 if (array_count(entry->sps) == 0)
1591 {
1592 INIT(sp,
1593 .src = src_ts->clone(src_ts),
1594 .dst = dst_ts->clone(dst_ts),
1595 );
1596 array_insert(entry->sps, -1, sp);
1597 if (!install(this, entry))
1598 {
1599 status = FAILED;
1600 }
1601 }
1602 else
1603 {
1604 /* TODO: reinstall with a filter using multiple TS?
1605 * Filters are ANDed for a match, but we could install a filter
1606 * with the inverse TS set using NOT-matches... */
1607 status = NOT_SUPPORTED;
1608 }
1609 }
1610 else
1611 {
1612 DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
1613 status = FAILED;
1614 }
1615 this->mutex->unlock(this->mutex);
1616
1617 return status;
1618 }
1619
1620 METHOD(kernel_ipsec_t, query_policy, status_t,
1621 private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
1622 traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
1623 time_t *use_time)
1624 {
1625 /* see query_sa() for some notes */
1626 return NOT_SUPPORTED;
1627 }
1628
1629 METHOD(kernel_ipsec_t, del_policy, status_t,
1630 private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
1631 traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
1632 mark_t mark, policy_priority_t priority)
1633 {
1634 /* not required, as we delete the whole SA/SP set during del_sa() */
1635 return SUCCESS;
1636 }
1637
1638 METHOD(kernel_ipsec_t, flush_policies, status_t,
1639 private_kernel_wfp_ipsec_t *this)
1640 {
1641 return NOT_SUPPORTED;
1642 }
1643
1644 METHOD(kernel_ipsec_t, bypass_socket, bool,
1645 private_kernel_wfp_ipsec_t *this, int fd, int family)
1646 {
1647 return NOT_SUPPORTED;
1648 }
1649
1650 METHOD(kernel_ipsec_t, enable_udp_decap, bool,
1651 private_kernel_wfp_ipsec_t *this, int fd, int family, u_int16_t port)
1652 {
1653 return NOT_SUPPORTED;
1654 }
1655
1656 METHOD(kernel_ipsec_t, destroy, void,
1657 private_kernel_wfp_ipsec_t *this)
1658 {
1659 if (this->handle)
1660 {
1661 FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
1662 FwpmEngineClose0(this->handle);
1663 }
1664 this->tsas->destroy(this->tsas);
1665 this->isas->destroy(this->isas);
1666 this->osas->destroy(this->osas);
1667 this->routes->destroy(this->routes);
1668 this->mutex->destroy(this->mutex);
1669 free(this);
1670 }
1671
1672 /*
1673 * Described in header.
1674 */
1675 kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
1676 {
1677 private_kernel_wfp_ipsec_t *this;
1678 DWORD res;
1679 FWPM_SESSION0 session = {
1680 .displayData = {
1681 .name = L"charon",
1682 .description = L"strongSwan IKE kernel-wfp backend",
1683 },
1684 };
1685
1686 INIT(this,
1687 .public = {
1688 .interface = {
1689 .get_features = _get_features,
1690 .get_spi = _get_spi,
1691 .get_cpi = _get_cpi,
1692 .add_sa = _add_sa,
1693 .update_sa = _update_sa,
1694 .query_sa = _query_sa,
1695 .del_sa = _del_sa,
1696 .flush_sas = _flush_sas,
1697 .add_policy = _add_policy,
1698 .query_policy = _query_policy,
1699 .del_policy = _del_policy,
1700 .flush_policies = _flush_policies,
1701 .bypass_socket = _bypass_socket,
1702 .enable_udp_decap = _enable_udp_decap,
1703 .destroy = _destroy,
1704 },
1705 },
1706 .provider = {
1707 .displayData = {
1708 .name = L"charon",
1709 .description = L"strongSwan IKE kernel-wfp backend",
1710 },
1711 .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
1712 { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
1713 },
1714 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
1715 .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
1716 .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
1717 .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
1718 .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
1719 );
1720
1721 if (!init_spi(this))
1722 {
1723 destroy(this);
1724 return NULL;
1725 }
1726
1727 res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
1728 &this->handle);
1729 if (res != ERROR_SUCCESS)
1730 {
1731 DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
1732 destroy(this);
1733 return NULL;
1734 }
1735
1736 res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
1737 if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
1738 {
1739 DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
1740 destroy(this);
1741 return NULL;
1742 }
1743
1744 return &this->public;
1745 }