diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 113920a63557a9497e6fb8d7efd60e2fcde1df09 (patch) | |
tree | 7ea97040bf40a91ae0523ca9d580edad42c77917 /src/libcharon/plugins/kernel_wfp | |
parent | b6869973db5f522dc9e2c20155ffd6e32152f197 (diff) | |
parent | 81c63b0eed39432878f78727f60a1e7499645199 (diff) | |
download | vyos-strongswan-113920a63557a9497e6fb8d7efd60e2fcde1df09.tar.gz vyos-strongswan-113920a63557a9497e6fb8d7efd60e2fcde1df09.zip |
Merge tag 'upstream/5.2.0'
Upstream version 5.2.0
Diffstat (limited to 'src/libcharon/plugins/kernel_wfp')
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/Makefile.am | 33 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/Makefile.in | 801 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/ipsecdump.c | 666 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c | 157 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h | 205 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c | 2551 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h | 47 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c | 77 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h | 43 | ||||
-rw-r--r-- | src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff | 26 |
10 files changed, 4606 insertions, 0 deletions
diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.am b/src/libcharon/plugins/kernel_wfp/Makefile.am new file mode 100644 index 000000000..85e5089a3 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/Makefile.am @@ -0,0 +1,33 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-kernel-wfp.la +else +plugin_LTLIBRARIES = libstrongswan-kernel-wfp.la +endif + +libstrongswan_kernel_wfp_la_SOURCES = \ + kernel_wfp_plugin.h kernel_wfp_plugin.c \ + kernel_wfp_compat.c kernel_wfp_compat.h \ + kernel_wfp_ipsec.h kernel_wfp_ipsec.c + +libstrongswan_kernel_wfp_la_LDFLAGS = -module -avoid-version +libstrongswan_kernel_wfp_la_LIBADD = -lfwpuclnt + + +noinst_PROGRAMS = ipsecdump + +ipsecdump_SOURCES = \ + ipsecdump.c +ipsecdump_LDADD = \ + libstrongswan-kernel-wfp.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la + + +EXTRA_DIST = mingw-w64-4.8.1.diff diff --git a/src/libcharon/plugins/kernel_wfp/Makefile.in b/src/libcharon/plugins/kernel_wfp/Makefile.in new file mode 100644 index 000000000..ff987f8d4 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/Makefile.in @@ -0,0 +1,801 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = ipsecdump$(EXEEXT) +subdir = src/libcharon/plugins/kernel_wfp +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ + $(top_srcdir)/m4/config/ltoptions.m4 \ + $(top_srcdir)/m4/config/ltsugar.m4 \ + $(top_srcdir)/m4/config/ltversion.m4 \ + $(top_srcdir)/m4/config/lt~obsolete.m4 \ + $(top_srcdir)/m4/macros/split-package-version.m4 \ + $(top_srcdir)/m4/macros/with.m4 \ + $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/m4/macros/add-plugin.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES) +libstrongswan_kernel_wfp_la_DEPENDENCIES = +am_libstrongswan_kernel_wfp_la_OBJECTS = kernel_wfp_plugin.lo \ + kernel_wfp_compat.lo kernel_wfp_ipsec.lo +libstrongswan_kernel_wfp_la_OBJECTS = \ + $(am_libstrongswan_kernel_wfp_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libstrongswan_kernel_wfp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libstrongswan_kernel_wfp_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@MONOLITHIC_FALSE@am_libstrongswan_kernel_wfp_la_rpath = -rpath \ +@MONOLITHIC_FALSE@ $(plugindir) +@MONOLITHIC_TRUE@am_libstrongswan_kernel_wfp_la_rpath = +PROGRAMS = $(noinst_PROGRAMS) +am_ipsecdump_OBJECTS = ipsecdump.$(OBJEXT) +ipsecdump_OBJECTS = $(am_ipsecdump_OBJECTS) +ipsecdump_DEPENDENCIES = libstrongswan-kernel-wfp.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libstrongswan_kernel_wfp_la_SOURCES) $(ipsecdump_SOURCES) +DIST_SOURCES = $(libstrongswan_kernel_wfp_la_SOURCES) \ + $(ipsecdump_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BFDLIB = @BFDLIB@ +BTLIB = @BTLIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COVERAGE_CFLAGS = @COVERAGE_CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GPERF = @GPERF@ +GPRBUILD = @GPRBUILD@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQLCFLAG = @MYSQLCFLAG@ +MYSQLCONFIG = @MYSQLCONFIG@ +MYSQLLIB = @MYSQLLIB@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_LIB = @OPENSSL_LIB@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_BUILD = @PACKAGE_VERSION_BUILD@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_REVIEW = @PACKAGE_VERSION_REVIEW@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PLUGIN_CFLAGS = @PLUGIN_CFLAGS@ +PTHREADLIB = @PTHREADLIB@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYINCLUDE = @RUBYINCLUDE@ +RUBYLIB = @RUBYLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKLIB = @SOCKLIB@ +STRIP = @STRIP@ +UNWINDLIB = @UNWINDLIB@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +aikgen_plugins = @aikgen_plugins@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +attest_plugins = @attest_plugins@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ +clearsilver_LIBS = @clearsilver_LIBS@ +cmd_plugins = @cmd_plugins@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dbusservicedir = @dbusservicedir@ +dev_headers = @dev_headers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fips_mode = @fips_mode@ +gtk_CFLAGS = @gtk_CFLAGS@ +gtk_LIBS = @gtk_LIBS@ +h_plugins = @h_plugins@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +imcvdir = @imcvdir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ +ipsecdir = @ipsecdir@ +ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ +ipsecuser = @ipsecuser@ +libdir = @libdir@ +libexecdir = @libexecdir@ +linux_headers = @linux_headers@ +localedir = @localedir@ +localstatedir = @localstatedir@ +maemo_CFLAGS = @maemo_CFLAGS@ +maemo_LIBS = @maemo_LIBS@ +manager_plugins = @manager_plugins@ +mandir = @mandir@ +medsrv_plugins = @medsrv_plugins@ +mkdir_p = @mkdir_p@ +nm_CFLAGS = @nm_CFLAGS@ +nm_LIBS = @nm_LIBS@ +nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ +oldincludedir = @oldincludedir@ +pcsclite_CFLAGS = @pcsclite_CFLAGS@ +pcsclite_LIBS = @pcsclite_LIBS@ +pdfdir = @pdfdir@ +piddir = @piddir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pki_plugins = @pki_plugins@ +plugindir = @plugindir@ +pool_plugins = @pool_plugins@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +random_device = @random_device@ +resolv_conf = @resolv_conf@ +routing_table = @routing_table@ +routing_table_prio = @routing_table_prio@ +s_plugins = @s_plugins@ +sbindir = @sbindir@ +scepclient_plugins = @scepclient_plugins@ +scripts_plugins = @scripts_plugins@ +sharedstatedir = @sharedstatedir@ +soup_CFLAGS = @soup_CFLAGS@ +soup_LIBS = @soup_LIBS@ +srcdir = @srcdir@ +starter_plugins = @starter_plugins@ +strongswan_conf = @strongswan_conf@ +strongswan_options = @strongswan_options@ +swanctldir = @swanctldir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +t_plugins = @t_plugins@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +urandom_device = @urandom_device@ +xml_CFLAGS = @xml_CFLAGS@ +xml_LIBS = @xml_LIBS@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) + +@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-wfp.la +@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-wfp.la +libstrongswan_kernel_wfp_la_SOURCES = \ + kernel_wfp_plugin.h kernel_wfp_plugin.c \ + kernel_wfp_compat.c kernel_wfp_compat.h \ + kernel_wfp_ipsec.h kernel_wfp_ipsec.c + +libstrongswan_kernel_wfp_la_LDFLAGS = -module -avoid-version +libstrongswan_kernel_wfp_la_LIBADD = -lfwpuclnt +ipsecdump_SOURCES = \ + ipsecdump.c + +ipsecdump_LDADD = \ + libstrongswan-kernel-wfp.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la + +EXTRA_DIST = mingw-w64-4.8.1.diff +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_wfp/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcharon/plugins/kernel_wfp/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libstrongswan-kernel-wfp.la: $(libstrongswan_kernel_wfp_la_OBJECTS) $(libstrongswan_kernel_wfp_la_DEPENDENCIES) $(EXTRA_libstrongswan_kernel_wfp_la_DEPENDENCIES) + $(AM_V_CCLD)$(libstrongswan_kernel_wfp_la_LINK) $(am_libstrongswan_kernel_wfp_la_rpath) $(libstrongswan_kernel_wfp_la_OBJECTS) $(libstrongswan_kernel_wfp_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +ipsecdump$(EXEEXT): $(ipsecdump_OBJECTS) $(ipsecdump_DEPENDENCIES) $(EXTRA_ipsecdump_DEPENDENCIES) + @rm -f ipsecdump$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ipsecdump_OBJECTS) $(ipsecdump_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsecdump.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_compat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_wfp_plugin.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS clean-pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ + clean-pluginLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pluginLTLIBRARIES \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libcharon/plugins/kernel_wfp/ipsecdump.c b/src/libcharon/plugins/kernel_wfp/ipsecdump.c new file mode 100644 index 000000000..7ca7df5a1 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/ipsecdump.c @@ -0,0 +1,666 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* Windows 7, for some fwpmu.h functionality */ +#define _WIN32_WINNT 0x0601 + +#include "kernel_wfp_compat.h" + +#include <library.h> + +ENUM(auth_type_names, IPSEC_AUTH_MD5, IPSEC_AUTH_AES_256, + "MD5", + "SHA1", + "SHA256", + "AES128", + "AES192", + "AES256", +); + +ENUM(auth_config_names, 0, 5, + "HMAC96", + "HMAC96", + "HMAC128", + "GMAC", + "GMAC", + "GMAC", +); + +ENUM(cipher_type_names, IPSEC_CIPHER_TYPE_DES, IPSEC_CIPHER_TYPE_AES_256, + "DES", + "3DES", + "AES128", + "AES192", + "AES256", +); + +ENUM(cipher_config_names, 1, 8, + "CBC", + "CBC", + "CBC", + "CBC", + "CBC", + "GCM", + "GCM", + "GCM", +); + +ENUM(match_type_names, FWP_MATCH_EQUAL, FWP_MATCH_NOT_EQUAL, + "equals", + "greater", + "less than", + "greater or equal than", + "less or equal than", + "in range", + "has all flags set", + "has any flags set", + "has none flags set", + "equals case insensitive", + "not equal", +); + +ENUM(traffic_type_names, IPSEC_TRAFFIC_TYPE_TRANSPORT, IPSEC_TRAFFIC_TYPE_TUNNEL, + "Transport", + "Tunnel", +); + +/** + * Print a GUID to a static buffer + */ +static char *guid2string(GUID *guid) +{ + static char buf[64]; + + snprintf(buf, sizeof(buf), + "%08x,%04x,%04x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + + return buf; +} + +/** + * Convert filter condition key GUID to some known strings + */ +static char* cond2name(GUID *guid, bool *address) +{ + struct { + GUID guid; + char *name; + bool address; + } map[] = { + { FWPM_CONDITION_IP_LOCAL_ADDRESS, "local address", TRUE}, + { FWPM_CONDITION_IP_REMOTE_ADDRESS, "remote address", TRUE}, + { FWPM_CONDITION_IP_SOURCE_ADDRESS, "source address", TRUE}, + { FWPM_CONDITION_IP_DESTINATION_ADDRESS, "destination address", TRUE}, + { FWPM_CONDITION_IP_LOCAL_PORT, "local port", FALSE}, + { FWPM_CONDITION_IP_REMOTE_PORT, "remote port", FALSE}, + { FWPM_CONDITION_IP_PROTOCOL, "protocol", FALSE}, + { FWPM_CONDITION_ICMP_CODE, "icmp code", FALSE}, + { FWPM_CONDITION_ICMP_TYPE, "icmp type", FALSE}, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (memeq(&map[i].guid, guid, sizeof(GUID))) + { + *address = map[i].address; + return map[i].name; + } + } + *address = FALSE; + return guid2string(guid); +} + +/** + * Print a host from raw data and IP version + */ +static void print_host(FWP_IP_VERSION version, void *data) +{ + host_t *host = NULL; + UINT32 ints[4]; + + switch (version) + { + case FWP_IP_VERSION_V4: + ints[0] = untoh32(data); + host = host_create_from_chunk(AF_INET, chunk_from_thing(ints[0]), 0); + break; + case FWP_IP_VERSION_V6: + ints[3] = untoh32(data); + ints[2] = untoh32(data + 4); + ints[1] = untoh32(data + 8); + ints[0] = untoh32(data + 12); + host = host_create_from_chunk(AF_INET6, chunk_from_thing(ints), 0); + break; + default: + break; + } + if (host) + { + printf("%H", host); + host->destroy(host); + } +} + +/** + * Print IPSEC_SA_AUTH_INFORMATION0 + */ +static void print_auth(IPSEC_SA_AUTH_INFORMATION0 *a) +{ + printf("%N-%N", + auth_type_names, a->authTransform.authTransformId.authType, + auth_config_names, a->authTransform.authTransformId.authConfig); +} + +/** + * Print IPSEC_SA_CIPHER_INFORMATION0 + */ +static void print_cipher(IPSEC_SA_CIPHER_INFORMATION0 *c) +{ + printf("%N-%N", + cipher_type_names, c->cipherTransform.cipherTransformId.cipherType, + cipher_config_names, c->cipherTransform.cipherTransformId.cipherConfig); +} + +/** + * Print IPsec SA transform + */ +static void list_sa(HANDLE engine, IPSEC_SA0 *sa) +{ + printf(" SPI 0x%08x\n", sa->spi); + switch (sa->saTransformType) + { + case IPSEC_TRANSFORM_AH: + printf(" AH: "); + print_auth(sa->ahInformation); + break; + case IPSEC_TRANSFORM_ESP_AUTH: + printf(" ESP: "); + print_auth(sa->espAuthInformation); + break; + case IPSEC_TRANSFORM_ESP_CIPHER: + printf(" ESP: "); + print_cipher(sa->espCipherInformation); + break; + case IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER: + printf(" ESP: "); + print_auth(&sa->espAuthAndCipherInformation->saAuthInformation); + printf(", "); + print_cipher(&sa->espAuthAndCipherInformation->saCipherInformation); + break; + default: + printf(" (Transform %d)", sa->saTransformType); + break; + } + printf("\n"); +} + +/** + * List a filter condition value, optionally as IP address + */ +static void print_value(FWP_CONDITION_VALUE0 *value, bool address) +{ + chunk_t chunk; + + switch (value->type) + { + case FWP_EMPTY: + printf("empty"); + break; + case FWP_UINT8: + printf("%u", value->uint8); + break; + case FWP_UINT16: + printf("%u", value->uint16); + break; + case FWP_UINT32: + if (address) + { + print_host(FWP_IP_VERSION_V4, &value->uint32); + } + else + { + printf("%u", value->uint32); + } + break; + case FWP_UINT64: + printf("%llu", value->uint64); + break; + case FWP_INT8: + printf("%d", value->int8); + break; + case FWP_INT16: + printf("%d", value->int16); + break; + case FWP_INT32: + printf("%d", value->int32); + break; + case FWP_INT64: + printf("%lld", value->int64); + break; + case FWP_FLOAT: + printf("%f", value->float32); + break; + case FWP_DOUBLE: + printf("%lf", value->double64); + break; + case FWP_BYTE_ARRAY16_TYPE: + if (address) + { + print_host(FWP_IP_VERSION_V6, value->byteArray16); + } + else + { + chunk = chunk_create((u_char*)value->byteArray16, 16); + printf("%#B", &chunk); + } + break; + case FWP_BYTE_BLOB_TYPE: + chunk = chunk_create(value->byteBlob->data, value->byteBlob->size); + printf("%#B", &chunk); + break; + case FWP_V4_ADDR_MASK: + print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->addr); + printf("/"); + print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->mask); + break; + case FWP_V6_ADDR_MASK: + print_host(FWP_IP_VERSION_V6, &value->v6AddrMask->addr); + printf("/%u", &value->v6AddrMask->prefixLength); + break; + case FWP_RANGE_TYPE: + print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueLow, + address); + printf(" - "); + print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueHigh, + address); + break; + default: + printf("(unsupported)"); + break; + } +} + +/** + * List a filter condition + */ +static void list_cond(HANDLE engine, FWPM_FILTER_CONDITION0 *cond) +{ + bool address; + + printf(" '%s' %N '", cond2name(&cond->fieldKey, &address), + match_type_names, cond->matchType); + print_value(&cond->conditionValue, address); + printf("'\n"); +} + +/** + * Print IPsec SA details + */ +static void list_details(HANDLE engine, IPSEC_SA_DETAILS1 *details) +{ + int i; + + printf(" %sbound SA: ", + details->saDirection == FWP_DIRECTION_INBOUND ? "In" : "Out"); + print_host(details->traffic.ipVersion, &details->traffic.localV4Address); + printf(" %s ", details->saDirection == FWP_DIRECTION_INBOUND ? "<-" : "->"); + print_host(details->traffic.ipVersion, &details->traffic.remoteV4Address); + printf("\n %N, flags: 0x%06x, lifetime: %us\n", + traffic_type_names, details->traffic.trafficType, + details->saBundle.flags, details->saBundle.lifetime.lifetimeSeconds); + if (details->udpEncapsulation) + { + printf(" UDP encap ports %u - %u\n", + details->udpEncapsulation->localUdpEncapPort, + details->udpEncapsulation->remoteUdpEncapPort); + } + for (i = 0; i < details->saBundle.numSAs; i++) + { + list_sa(engine, &details->saBundle.saList[i]); + } + printf(" Filter ID %llu\n", details->transportFilter->filterId); + for (i = 0; i < details->transportFilter->numFilterConditions; i++) + { + list_cond(engine, &details->transportFilter->filterCondition[i]); + } +} + +/** + * List installed SA contexts + */ +static bool list_contexts(HANDLE engine) +{ + HANDLE handle; + UINT32 returned; + DWORD res; + IPSEC_SA_CONTEXT1 **entries; + + res = IPsecSaContextCreateEnumHandle0(engine, NULL, &handle); + if (res != ERROR_SUCCESS) + { + fprintf(stderr, "IPsecSaContextCreateEnumHandle0(): 0x%08x\n", res); + return FALSE; + } + + while (TRUE) + { + res = IPsecSaContextEnum1(engine, handle, 1, &entries, &returned); + if (res != ERROR_SUCCESS) + { + fprintf(stderr, "IPsecSaContextEnum1(): 0x%08x\n", res); + IPsecSaContextDestroyEnumHandle0(engine, handle); + return FALSE; + } + if (returned == 0) + { + break; + } + + printf("SA context %llu:\n", entries[0]->saContextId); + list_details(engine, entries[0]->inboundSa); + list_details(engine, entries[0]->outboundSa); + + FwpmFreeMemory0((void**)&entries); + } + IPsecSaContextDestroyEnumHandle0(engine, handle); + return TRUE; +} + +const GUID FWPM_LAYER_IPSEC_KM_DEMUX_V4 = { + 0xf02b1526, 0xa459, 0x4a51, { 0xb9, 0xe3, 0x75, 0x9d, 0xe5, 0x2b, 0x9d, 0x2c } +}; +const GUID FWPM_LAYER_IPSEC_KM_DEMUX_V6 = { + 0x2f755cf6, 0x2fd4, 0x4e88, { 0xb3, 0xe4, 0xa9, 0x1b, 0xca, 0x49, 0x52, 0x35 } +}; +const GUID FWPM_LAYER_IPSEC_V4 = { + 0xeda65c74, 0x610d, 0x4bc5, { 0x94, 0x8f, 0x3c, 0x4f, 0x89, 0x55, 0x68, 0x67 } +}; +const GUID FWPM_LAYER_IPSEC_V6 = { + 0x13c48442, 0x8d87, 0x4261, { 0x9a, 0x29, 0x59, 0xd2, 0xab, 0xc3, 0x48, 0xb4 } +}; +const GUID FWPM_LAYER_IKEEXT_V4 = { + 0xb14b7bdb, 0xdbbd, 0x473e, { 0xbe, 0xd4, 0x8b, 0x47, 0x08, 0xd4, 0xf2, 0x70 } +}; +const GUID FWPM_LAYER_IKEEXT_V6 = { + 0xb64786b3, 0xf687, 0x4eb9, { 0x89, 0xd2, 0x8e, 0xf3, 0x2a, 0xcd, 0xab, 0xe2 } +}; +const GUID FWPM_LAYER_INBOUND_IPPACKET_V4 = { + 0xc86fd1bf, 0x21cd, 0x497e, { 0xa0, 0xbb, 0x17, 0x42, 0x5c, 0x88, 0x5c, 0x58 } +}; +const GUID FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD = { + 0xb5a230d0, 0xa8c0, 0x44f2, { 0x91, 0x6e, 0x99, 0x1b, 0x53, 0xde, 0xd1, 0xf7 } +}; +const GUID FWPM_LAYER_INBOUND_IPPACKET_V6 = { + 0xf52032cb, 0x991c, 0x46e7, { 0x97, 0x1d, 0x26, 0x01, 0x45, 0x9a, 0x91, 0xca } +}; +const GUID FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD = { + 0xbb24c279, 0x93b4, 0x47a2, { 0x83, 0xad, 0xae, 0x16, 0x98, 0xb5, 0x08, 0x85 } +}; +const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V4 = { + 0x1e5c9fae, 0x8a84, 0x4135, { 0xa3, 0x31, 0x95, 0x0b, 0x54, 0x22, 0x9e, 0xcd } +}; +const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD = { + 0x08e4bcb5, 0xb647, 0x48f3, { 0x95, 0x3c, 0xe5, 0xdd, 0xbd, 0x03, 0x93, 0x7e } +}; +const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6 = { + 0xa3b3ab6b, 0x3564, 0x488c, { 0x91, 0x17, 0xf3, 0x4e, 0x82, 0x14, 0x27, 0x63 } +}; +const GUID FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD = { + 0x9513d7c4, 0xa934, 0x49dc, { 0x91, 0xa7, 0x6c, 0xcb, 0x80, 0xcc, 0x02, 0xe3 } +}; +const GUID FWPM_LAYER_IPFORWARD_V4_DISCARD = { + 0x9e9ea773, 0x2fae, 0x4210, { 0x8f, 0x17, 0x34, 0x12, 0x9e, 0xf3, 0x69, 0xeb } +}; +const GUID FWPM_LAYER_IPFORWARD_V6_DISCARD = { + 0x31524a5d, 0x1dfe, 0x472f, { 0xbb, 0x93, 0x51, 0x8e, 0xe9, 0x45, 0xd8, 0xa2 } +}; +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD = { + 0xac4a9833, 0xf69d, 0x4648, { 0xb2, 0x61, 0x6d, 0xc8, 0x48, 0x35, 0xef, 0x39 } +}; +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD = { + 0x2a6ff955, 0x3b2b, 0x49d2, { 0x98, 0x48, 0xad, 0x9d, 0x72, 0xdc, 0xaa, 0xb7 } +}; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD = { + 0xc5f10551, 0xbdb0, 0x43d7, { 0xa3, 0x13, 0x50, 0xe2, 0x11, 0xf4, 0xd6, 0x8a } +}; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD = { + 0xf433df69, 0xccbd, 0x482e, { 0xb9, 0xb2, 0x57, 0x16, 0x56, 0x58, 0xc3, 0xb3 } +}; + +/** + * Convert filter layer GUID to name + */ +static char* layer2name(GUID *guid) +{ + struct { + GUID guid; + char *name; + } map[] = { + { FWPM_LAYER_IPSEC_KM_DEMUX_V4, "IPsec KM demux v4" }, + { FWPM_LAYER_IPSEC_KM_DEMUX_V6, "IPsec KM demux v6" }, + { FWPM_LAYER_IPSEC_V4, "IPsec v4" }, + { FWPM_LAYER_IPSEC_V6, "IPsec v6" }, + { FWPM_LAYER_IKEEXT_V4, "IKE ext v4" }, + { FWPM_LAYER_IKEEXT_V6, "IKE ext v6" }, + { FWPM_LAYER_INBOUND_IPPACKET_V4, "inbound v4" }, + { FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, "inbound v4 dsc" }, + { FWPM_LAYER_INBOUND_IPPACKET_V6, "inbound v6" }, + { FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, "inbound v6 dsc" }, + { FWPM_LAYER_OUTBOUND_IPPACKET_V4, "outbound v4" }, + { FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, "outbound v4 dsc" }, + { FWPM_LAYER_OUTBOUND_IPPACKET_V6, "outbound v6" }, + { FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, "outbound v6 dsc" }, + { FWPM_LAYER_IPFORWARD_V4, "forward v4" }, + { FWPM_LAYER_IPFORWARD_V4_DISCARD, "forward v4 dsc" }, + { FWPM_LAYER_IPFORWARD_V6, "forward v6" }, + { FWPM_LAYER_IPFORWARD_V6_DISCARD, "forward v6 discard" }, + { FWPM_LAYER_INBOUND_TRANSPORT_V4, "inbound transport v4" }, + { FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, "inbound transport v4 dsc" }, + { FWPM_LAYER_INBOUND_TRANSPORT_V6, "inbound transport v6" }, + { FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, "inbound v6 transport dsc" }, + { FWPM_LAYER_OUTBOUND_TRANSPORT_V4, "outbound transport v4" }, + { FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, "outbound transport v4 dsc" }, + { FWPM_LAYER_OUTBOUND_TRANSPORT_V6, "outbound transport v6" }, + { FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, "outbound transport v6 dsc" }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (memeq(&map[i].guid, guid, sizeof(GUID))) + { + return map[i].name; + } + } + return NULL; +} + +/** + * Convert filter callout GUID to name + */ +static char* callout2name(GUID *guid) +{ + struct { + GUID guid; + char *name; + } map[] = { + { FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4, "inbound transport v4" }, + { FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6, "inbound transport v6" }, + { FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4, "outbound transport v4" }, + { FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6, "outbound transport v6" }, + { FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4, "inbound tunnel v4" }, + { FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6, "inbound tunnel v6" }, + { FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4, "outbound tunnel v4" }, + { FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6, "outbound tunnel v6" }, + { FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4, "forward in tunnel v4" }, + { FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6, "forward in tunnel v6" }, + { FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4, "forward out tunnel v4" }, + { FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6, "forward out tunnel v6" }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (memeq(&map[i].guid, guid, sizeof(GUID))) + { + return map[i].name; + } + } + return guid2string(guid); +} + +/** + * Print display data with description + */ +static void print_display_data(FWPM_DISPLAY_DATA0 *data) +{ + char buf[128]; + + buf[0] = '\0'; + if (data->name) + { + wcstombs(buf, data->name, sizeof(buf)); + } + printf("%s", buf); + if (data->description) + { + buf[0] = '\0'; + wcstombs(buf, data->description, sizeof(buf)); + if (strlen(buf)) + { + printf(" (%s)", buf); + } + } +} + +/** + * List installed firewall filters + */ +static bool list_filters(HANDLE engine) +{ + HANDLE handle; + UINT32 returned; + DWORD res; + FWPM_FILTER0 **entries; + char *layer; + int i; + + res = FwpmFilterCreateEnumHandle0(engine, NULL, &handle); + if (res != ERROR_SUCCESS) + { + fprintf(stderr, "FwpmFilterCreateEnumHandle0(): 0x%08x\n", res); + return FALSE; + } + + while (TRUE) + { + res = FwpmFilterEnum0(engine, handle, 1, &entries, &returned); + if (res != ERROR_SUCCESS) + { + fprintf(stderr, "FwpmFilterEnum0(): 0x%08x\n", res); + FwpmFilterDestroyEnumHandle0(engine, handle); + return FALSE; + } + if (returned == 0) + { + break; + } + + layer = layer2name(&entries[0]->layerKey); + if (layer) + { + printf("Filter ID %llu, '", entries[0]->filterId); + print_display_data(&entries[0]->displayData); + printf("'\n"); + printf(" %s, ", layer); + if (entries[0]->effectiveWeight.type == FWP_UINT64) + { + printf("weight %016llx, ", *entries[0]->effectiveWeight.uint64); + } + + switch (entries[0]->action.type) + { + case FWP_ACTION_BLOCK: + printf("block\n"); + break; + case FWP_ACTION_PERMIT: + printf("permit\n"); + break; + case FWP_ACTION_CALLOUT_TERMINATING: + printf("callout terminating: %s\n", + callout2name(&entries[0]->action.calloutKey)); + break; + case FWP_ACTION_CALLOUT_INSPECTION: + printf("callout inspection: %s\n", + callout2name(&entries[0]->action.calloutKey)); + break; + case FWP_ACTION_CALLOUT_UNKNOWN: + printf("callout unknown: %s\n", + callout2name(&entries[0]->action.calloutKey)); + break; + default: + printf("(unknown action)\n"); + break; + } + for (i = 0; i < entries[0]->numFilterConditions; i++) + { + list_cond(engine, &entries[0]->filterCondition[i]); + } + } + FwpmFreeMemory0((void**)&entries); + } + FwpmFilterDestroyEnumHandle0(engine, handle); + return TRUE; +} + +/** + * ipsecdump main() + */ +int main(int argc, char *argv[]) +{ + FWPM_SESSION0 session = { + .displayData = { + .name = L"ipsecdump", + .description = L"strongSwan SAD/SPD dumper", + }, + }; + HANDLE engine; + DWORD res; + int code; + + library_init(NULL, "ipsecdump"); + atexit(library_deinit); + + res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine); + if (res != ERROR_SUCCESS) + { + fprintf(stderr, "FwpmEngineOpen(): 0x%08x\n", res); + return 2; + } + if (argc > 1 && streq(argv[1], "filters")) + { + code = list_filters(engine) ? 0 : 1; + } + else + { + code = list_contexts(engine) ? 0 : 1; + } + FwpmEngineClose0(engine); + return code; +} diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c new file mode 100644 index 000000000..41f85ba5c --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <library.h> + +const GUID FWPM_CONDITION_IP_REMOTE_ADDRESS = { + 0xb235ae9a, 0x1d64, 0x49b8, { 0xa4,0x4c,0x5f,0xf3,0xd9,0x09,0x50,0x45 } +}; +const GUID FWPM_CONDITION_IP_LOCAL_ADDRESS = { + 0xd9ee00de, 0xc1ef, 0x4617, { 0xbf,0xe3,0xff,0xd8,0xf5,0xa0,0x89,0x57 } +}; +const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS = { + 0xae96897e, 0x2e94, 0x4bc9, { 0xb3,0x13,0xb2,0x7e,0xe8,0x0e,0x57,0x4d } +}; +const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS = { + 0x2d79133b, 0xb390, 0x45c6, { 0x86,0x99,0xac,0xac,0xea,0xaf,0xed,0x33 } +}; +const GUID FWPM_CONDITION_IP_LOCAL_PORT = { + 0x0c1ba1af, 0x5765, 0x453f, { 0xaf,0x22,0xa8,0xf7,0x91,0xac,0x77,0x5b } +}; +const GUID FWPM_CONDITION_IP_REMOTE_PORT = { + 0xc35a604d, 0xd22b, 0x4e1a, { 0x91,0xb4,0x68,0xf6,0x74,0xee,0x67,0x4b } +}; +const GUID FWPM_CONDITION_IP_PROTOCOL = { + 0x3971ef2b, 0x623e, 0x4f9a, { 0x8c,0xb1,0x6e,0x79,0xb8,0x06,0xb9,0xa7 } +}; +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4 = { + 0x5926dfc8, 0xe3cf, 0x4426, { 0xa2,0x83,0xdc,0x39,0x3f,0x5d,0x0f,0x9d } +}; +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6 = { + 0x634a869f, 0xfc23, 0x4b90, { 0xb0,0xc1,0xbf,0x62,0x0a,0x36,0xae,0x6f } +}; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4 = { + 0x09e61aea, 0xd214, 0x46e2, { 0x9b,0x21,0xb2,0x6b,0x0b,0x2f,0x28,0xc8 } +}; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6 = { + 0xe1735bde, 0x013f, 0x4655, { 0xb3,0x51,0xa4,0x9e,0x15,0x76,0x2d,0xf0 } +}; +const GUID FWPM_LAYER_IPFORWARD_V4 = { + 0xa82acc24, 0x4ee1, 0x4ee1, { 0xb4,0x65,0xfd,0x1d,0x25,0xcb,0x10,0xa4} +}; +const GUID FWPM_LAYER_IPFORWARD_V6 = { + 0x7b964818, 0x19c7, 0x493a, { 0xb7,0x1f,0x83,0x2c,0x36,0x84,0xd2,0x8c } +}; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 = { + 0x5132900d, 0x5e84, 0x4b5f, { 0x80,0xe4,0x01,0x74,0x1e,0x81,0xff,0x10 } +}; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 = { + 0x49d3ac92, 0x2a6c, 0x4dcf, { 0x95,0x5f,0x1c,0x3b,0xe0,0x09,0xdd,0x99 } +}; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 = { + 0x4b46bf0a, 0x4523, 0x4e57, { 0xaa,0x38,0xa8,0x79,0x87,0xc9,0x10,0xd9 } +}; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 = { + 0x38d87722, 0xad83, 0x4f11, { 0xa9,0x1f,0xdf,0x0f,0xb0,0x77,0x22,0x5b } +}; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 = { + 0x191a8a46, 0x0bf8, 0x46cf, { 0xb0,0x45,0x4b,0x45,0xdf,0xa6,0xa3,0x24 } +}; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 = { + 0x80c342e3, 0x1e53, 0x4d6f, { 0x9b,0x44,0x03,0xdf,0x5a,0xee,0xe1,0x54 } +}; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 = { + 0x70a4196c, 0x835b, 0x4fb0, { 0x98,0xe8,0x07,0x5f,0x4d,0x97,0x7d,0x46 } +}; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 = { + 0xf1835363, 0xa6a5, 0x4e62, { 0xb1,0x80,0x23,0xdb,0x78,0x9d,0x8d,0xa6 } +}; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 = { + 0x28829633, 0xc4f0, 0x4e66, { 0x87,0x3f,0x84,0x4d,0xb2,0xa8,0x99,0xc7 } +}; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 = { + 0xaf50bec2, 0xc686, 0x429a, { 0x88,0x4d,0xb7,0x44,0x43,0xe7,0xb0,0xb4 } +}; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 = { + 0xfb532136, 0x15cb, 0x440b, { 0x93,0x7c,0x17,0x17,0xca,0x32,0x0c,0x40 } +}; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 = { + 0xdae640cc, 0xe021, 0x4bee, { 0x9e,0xb6,0xa4,0x8b,0x27,0x5c,0x8c,0x1d } +}; + +/** + * Load a function symbol from a loaded dll + */ +static inline void *load_function(char *dll, char *name) +{ + HANDLE handle; + void *sym = NULL; + + handle = GetModuleHandle(dll); + if (!handle) + { + return NULL; + } + sym = GetProcAddress(handle, name); + return sym; +} + +/** + * Macro that defines a stub for a function that calls the same DLL function + * + * @param dll DLL to find function in + * @param ret return type of function + * @param name function name + * @param size size of all arguments on stack + * @param ... arguments of function + */ +#define STUB(dll, ret, name, size, ...) \ +ret WINAPI name(__VA_ARGS__) \ +{ \ + static void (*fun)() = NULL; \ + if (!fun) \ + { \ + fun = load_function(#dll, #name); \ + } \ + if (fun) \ + { \ + __builtin_return(__builtin_apply(fun, __builtin_apply_args(), size)); \ + } \ + return ERROR_NOT_SUPPORTED; \ +} + +STUB(fwpuclnt, DWORD, IPsecSaContextCreate1, 40, + HANDLE engineHandle, const void *outboundTraffic, + const void *virtualIfTunnelInfo, UINT64 *inboundFilterId, UINT64 *id) + +STUB(fwpuclnt, DWORD, IPsecSaContextSetSpi0, 32, + HANDLE engineHandle, UINT64 id, const void *getSpi, UINT32 inboundSpi) + +STUB(fwpuclnt, DWORD, IPsecSaContextGetById1, 24, + HANDLE engineHandle, UINT64 id, void **saContext) + +STUB(fwpuclnt, DWORD, IPsecSaContextUpdate0, 24, + HANDLE engineHandle, UINT32 flags, const void *newValues) + +STUB(fwpuclnt, DWORD, IPsecSaContextEnum1, 40, + HANDLE engineHandle, HANDLE enumHandle, UINT32 numEntriesRequested, + void ***entries, UINT32 *numEntriesReturned) + +STUB(fwpuclnt, DWORD, FwpmNetEventSubscribe0, 40, + HANDLE engineHandle, const void *subscription, void(*callback)(), + void *context, HANDLE *eventsHandle) + +STUB(fwpuclnt, DWORD, FwpmNetEventUnsubscribe0, 16, + HANDLE engineHandle, HANDLE eventsHandle) diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h new file mode 100644 index 000000000..50a89a007 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_compat.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup kernel_wfp_compat kernel_wfp_compat + * @{ @ingroup kernel_wfp + */ + +#ifndef KERNEL_WFP_COMPAT_H_ +#define KERNEL_WFP_COMPAT_H_ + +#include <winsock2.h> +#include <windows.h> +#include <ipsectypes.h> + +/* MinGW defines CIPHERs incorrectly starting at 0 */ +#define IPSEC_CIPHER_TYPE_DES 1 +#define IPSEC_CIPHER_TYPE_3DES 2 +#define IPSEC_CIPHER_TYPE_AES_128 3 +#define IPSEC_CIPHER_TYPE_AES_192 4 +#define IPSEC_CIPHER_TYPE_AES_256 5 +#define IPSEC_CIPHER_TYPE_MAX 6 + +#include <fwpmtypes.h> +#include <fwpmu.h> +#undef interface + +/* MinGW defines TRANSFORMs incorrectly starting at 0 */ +#define IPSEC_TRANSFORM_AH 1 +#define IPSEC_TRANSFORM_ESP_AUTH 2 +#define IPSEC_TRANSFORM_ESP_CIPHER 3 +#define IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER 4 +#define IPSEC_TRANSFORM_ESP_AUTH_FW 5 +#define IPSEC_TRANSFORM_TYPE_MAX 6 + +/* missing in MinGW */ +enum { + FWPM_TUNNEL_FLAG_POINT_TO_POINT = (1<<0), + FWPM_TUNNEL_FLAG_ENABLE_VIRTUAL_IF_TUNNELING = (1<<1), +}; + +/* missing in MinGW */ +enum { + IPSEC_SA_DETAILS_UPDATE_TRAFFIC = (1<<0), + IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION = (1<<1), + IPSEC_SA_BUNDLE_UPDATE_FLAGS = (1<<2), + IPSEC_SA_BUNDLE_UPDATE_NAP_CONTEXT = (1<<3), + IPSEC_SA_BUNDLE_UPDATE_KEY_MODULE_STATE = (1<<4), + IPSEC_SA_BUNDLE_UPDATE_PEER_V4_PRIVATE_ADDRESS = (1<<5), + IPSEC_SA_BUNDLE_UPDATE_MM_SA_ID = (1<<6), +}; + +/* missing in MinGW */ +enum { + FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET = (1<<0), + FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET = (1<<1), + FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET = (1<<2), + FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET = (1<<3), + FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET = (1<<4), + FWPM_NET_EVENT_FLAG_APP_ID_SET = (1<<5), + FWPM_NET_EVENT_FLAG_USER_ID_SET = (1<<6), + FWPM_NET_EVENT_FLAG_SCOPE_ID_SET = (1<<7), + FWPM_NET_EVENT_FLAG_IP_VERSION_SET = (1<<8), + FWPM_NET_EVENT_FLAG_REAUTH_REASON_SET = (1<<9), +}; + +/* missing in MinGW */ +enum { + FWPM_FILTER_FLAG_PERSISTENT = (1<<0), + FWPM_FILTER_FLAG_BOOTTIME = (1<<1), + FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT = (1<<2), + FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT = (1<<3), + FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED = (1<<4), + FWPM_FILTER_FLAG_DISABLED = (1<<5), +}; + +/* missing in MinGW */ +enum { + IPSEC_SA_BUNDLE_FLAG_ND_SECURE = (1<< 0), + IPSEC_SA_BUNDLE_FLAG_ND_BOUNDARY = (1<< 1), + IPSEC_SA_BUNDLE_FLAG_ND_PEER_NAT_BOUNDARY = (1<< 2), + IPSEC_SA_BUNDLE_FLAG_GUARANTEE_ENCRYPTION = (1<< 3), + IPSEC_SA_BUNDLE_FLAG_NLB = (1<< 4), + IPSEC_SA_BUNDLE_FLAG_NO_MACHINE_LUID_VERIFY = (1<< 5), + IPSEC_SA_BUNDLE_FLAG_NO_IMPERSONATION_LUID_VERIFY = (1<< 6), + IPSEC_SA_BUNDLE_FLAG_NO_EXPLICIT_CRED_MATCH = (1<< 7), + IPSEC_SA_BUNDLE_FLAG_ALLOW_NULL_TARGET_NAME_MATCH = (1<< 9), + IPSEC_SA_BUNDLE_FLAG_CLEAR_DF_ON_TUNNEL = (1<<10), + IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND = (1<<11), + IPSEC_SA_BUNDLE_FLAG_ND_PEER_BOUNDARY = (1<<12), + IPSEC_SA_BUNDLE_FLAG_SUPPRESS_DUPLICATE_DELETION = (1<<13), + IPSEC_SA_BUNDLE_FLAG_PEER_SUPPORTS_GUARANTEE_ENCRYPTION = (1<<14), + IPSEC_SA_BUNDLE_FLAG_FORCE_INBOUND_CONNECTIONS = (1<<15), + IPSEC_SA_BUNDLE_FLAG_FORCE_OUTBOUND_CONNECTIONS = (1<<16), + IPSEC_SA_BUNDLE_FLAG_FORWARD_PATH_INITIATOR = (1<<17), +}; + +/* missing in some MinGW versions */ +const GUID FWPM_CONDITION_IP_REMOTE_ADDRESS; +const GUID FWPM_CONDITION_IP_LOCAL_ADDRESS; +const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS; +const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS; +const GUID FWPM_CONDITION_IP_LOCAL_PORT; +const GUID FWPM_CONDITION_IP_REMOTE_PORT; +const GUID FWPM_CONDITION_IP_PROTOCOL; +#ifndef FWPM_CONDITION_ICMP_TYPE +# define FWPM_CONDITION_ICMP_TYPE FWPM_CONDITION_IP_LOCAL_PORT +#endif +#ifndef FWPM_CONDITION_ICMP_CODE +# define FWPM_CONDITION_ICMP_CODE FWPM_CONDITION_IP_REMOTE_PORT +#endif +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V4; +const GUID FWPM_LAYER_INBOUND_TRANSPORT_V6; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V4; +const GUID FWPM_LAYER_OUTBOUND_TRANSPORT_V6; +const GUID FWPM_LAYER_IPFORWARD_V4; +const GUID FWPM_LAYER_IPFORWARD_V6; +const GUID FWPM_SUBLAYER_IPSEC_TUNNEL; +const GUID FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4; +const GUID FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4; +const GUID FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4; +const GUID FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6; + +/* integrity config, missing in some MinGW versions */ +#ifndef IPSEC_AUTH_CONFIG_HMAC_MD5_96 +enum { + IPSEC_AUTH_CONFIG_HMAC_MD5_96 = 0, + IPSEC_AUTH_CONFIG_HMAC_SHA_1_96, + IPSEC_AUTH_CONFIG_HMAC_SHA_256_128, + IPSEC_AUTH_CONFIG_GCM_AES_128, + IPSEC_AUTH_CONFIG_GCM_AES_192, + IPSEC_AUTH_CONFIG_GCM_AES_256, + IPSEC_AUTH_CONFIG_MAX +}; +#define IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 { \ + IPSEC_AUTH_MD5, IPSEC_AUTH_CONFIG_HMAC_MD5_96 } +#define IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 { \ + IPSEC_AUTH_SHA_1, IPSEC_AUTH_CONFIG_HMAC_SHA_1_96 } +#define IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128 { \ + IPSEC_AUTH_SHA_256, IPSEC_AUTH_CONFIG_HMAC_SHA_256_128 } +#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 { \ + IPSEC_AUTH_AES_128, IPSEC_AUTH_CONFIG_GCM_AES_128 } +#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 { \ + IPSEC_AUTH_AES_192, IPSEC_AUTH_CONFIG_GCM_AES_192 } +#define IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 { \ + IPSEC_AUTH_AES_256, IPSEC_AUTH_CONFIG_GCM_AES_256 } +#endif + +/* encryption config, missing in some MinGW versions */ +#ifndef IPSEC_CIPHER_CONFIG_CBC_DES +enum { + IPSEC_CIPHER_CONFIG_CBC_DES = 1, + IPSEC_CIPHER_CONFIG_CBC_3DES, + IPSEC_CIPHER_CONFIG_CBC_AES_128, + IPSEC_CIPHER_CONFIG_CBC_AES_192, + IPSEC_CIPHER_CONFIG_CBC_AES_256, + IPSEC_CIPHER_CONFIG_GCM_AES_128, + IPSEC_CIPHER_CONFIG_GCM_AES_192, + IPSEC_CIPHER_CONFIG_GCM_AES_256, + IPSEC_CIPHER_CONFIG_MAX +}; +#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 { \ + IPSEC_CIPHER_TYPE_AES_128, IPSEC_CIPHER_CONFIG_GCM_AES_128 } +#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 { \ + IPSEC_CIPHER_TYPE_AES_192, IPSEC_CIPHER_CONFIG_GCM_AES_192 } +#define IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 { \ + IPSEC_CIPHER_TYPE_AES_256, IPSEC_CIPHER_CONFIG_GCM_AES_256 } +#define IPSEC_CIPHER_TRANSFORM_ID_CBC_DES { \ + IPSEC_CIPHER_TYPE_DES, IPSEC_CIPHER_CONFIG_CBC_DES } +#define IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES { \ + IPSEC_CIPHER_TYPE_3DES, IPSEC_CIPHER_CONFIG_CBC_3DES } +#define IPSEC_CIPHER_TRANSFORM_ID_AES_128 { \ + IPSEC_CIPHER_TYPE_AES_128, IPSEC_CIPHER_CONFIG_CBC_AES_128 } +#define IPSEC_CIPHER_TRANSFORM_ID_AES_192 { \ + IPSEC_CIPHER_TYPE_AES_192, IPSEC_CIPHER_CONFIG_CBC_AES_192 } +#define IPSEC_CIPHER_TRANSFORM_ID_AES_256 { \ + IPSEC_CIPHER_TYPE_AES_256, IPSEC_CIPHER_CONFIG_CBC_AES_256 } +#endif + +DWORD WINAPI FwpmIPsecTunnelAdd0(HANDLE, UINT32, + const FWPM_PROVIDER_CONTEXT0*, const FWPM_PROVIDER_CONTEXT0*, UINT32, + const FWPM_FILTER_CONDITION0*, PSECURITY_DESCRIPTOR); + +#endif /** KERNEL_WFP_COMPAT_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c new file mode 100644 index 000000000..c788bfb10 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c @@ -0,0 +1,2551 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* Windows 7, for some fwpmu.h functionality */ +#define _WIN32_WINNT 0x0601 + +#include "kernel_wfp_compat.h" +#include "kernel_wfp_ipsec.h" + +#include <daemon.h> +#include <hydra.h> +#include <threading/mutex.h> +#include <collections/array.h> +#include <collections/hashtable.h> +#include <processing/jobs/callback_job.h> + + +typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t; + +struct private_kernel_wfp_ipsec_t { + + /** + * Public interface + */ + kernel_wfp_ipsec_t public; + + /** + * Next SPI to allocate + */ + refcount_t nextspi; + + /** + * Mix value to distribute SPI allocation randomly + */ + u_int32_t mixspi; + + /** + * IKE bypass filters, as UINT64 filter LUID + */ + array_t *bypass; + + /** + * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t + */ + hashtable_t *tsas; + + /** + * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t + */ + hashtable_t *isas; + + /** + * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t + */ + hashtable_t *osas; + + /** + * Installed routes, as route_t => route_t + */ + hashtable_t *routes; + + /** + * Installed traps, as trap_t => trap_t + */ + hashtable_t *traps; + + /** + * Mutex for accessing entries + */ + mutex_t *mutex; + + /** + * WFP session handle + */ + HANDLE handle; + + /** + * Provider charon registers as + */ + FWPM_PROVIDER0 provider; + + /** + * Event handle + */ + HANDLE event; +}; + +/** + * Security association entry + */ +typedef struct { + /** SPI for this SA */ + u_int32_t spi; + /** protocol, IPPROTO_ESP/IPPROTO_AH */ + u_int8_t protocol; + /** hard lifetime of SA */ + u_int32_t lifetime; + /** destination host address for this SPI */ + host_t *dst; + struct { + /** algorithm */ + u_int16_t alg; + /** key */ + chunk_t key; + } integ, encr; +} sa_entry_t; + +/** + * Hash function for sas lookup table + */ +static u_int hash_sa(sa_entry_t *key) +{ + return chunk_hash_inc(chunk_from_thing(key->spi), + chunk_hash(key->dst->get_address(key->dst))); +} + +/** + * equals function for sas lookup table + */ +static bool equals_sa(sa_entry_t *a, sa_entry_t *b) +{ + return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst); +} + +/** + * Security policy entry + */ +typedef struct { + /** policy source addresses */ + traffic_selector_t *src; + /** policy destinaiton addresses */ + traffic_selector_t *dst; + /** WFP allocated LUID for inbound filter ID */ + u_int64_t policy_in; + /** WFP allocated LUID for outbound filter ID */ + u_int64_t policy_out; + /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */ + u_int64_t policy_fwd_in; + /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */ + u_int64_t policy_fwd_out; + /** have installed a route for it? */ + bool route; +} sp_entry_t; + +/** + * Destroy an SP entry + */ +static void sp_entry_destroy(sp_entry_t *sp) +{ + sp->src->destroy(sp->src); + sp->dst->destroy(sp->dst); + free(sp); +} + +/** + * Collection of SA/SP database entries for a reqid + */ +typedef struct { + /** reqid of entry */ + u_int32_t reqid; + /** outer address on local host */ + host_t *local; + /** outer address on remote host */ + host_t *remote; + /** inbound SA entry */ + sa_entry_t isa; + /** outbound SA entry */ + sa_entry_t osa; + /** associated (outbound) policies, as sp_entry_t* */ + array_t *sps; + /** IPsec mode, tunnel|transport */ + ipsec_mode_t mode; + /** UDP encapsulation */ + bool encap; + /** provider context, for tunnel mode only */ + u_int64_t provider; + /** WFP allocated LUID for SA context */ + u_int64_t sa_id; +} entry_t; + +/** + * Installed route + */ +typedef struct { + /** destination net of route */ + host_t *dst; + /** prefix length of dst */ + u_int8_t mask; + /** source address for route */ + host_t *src; + /** gateway of route, NULL if directly attached */ + host_t *gtw; + /** references for route */ + u_int refs; +} route_t; + +/** + * Destroy a route_t + */ +static void destroy_route(route_t *this) +{ + this->dst->destroy(this->dst); + this->src->destroy(this->src); + DESTROY_IF(this->gtw); + free(this); +} + +/** + * Hashtable equals function for routes + */ +static bool equals_route(route_t *a, route_t *b) +{ + return a->mask == b->mask && + a->dst->ip_equals(a->dst, b->dst) && + a->src->ip_equals(a->src, b->src); +} + +/** + * Hashtable hash function for routes + */ +static u_int hash_route(route_t *route) +{ + return chunk_hash_inc(route->src->get_address(route->src), + chunk_hash_inc(route->dst->get_address(route->dst), route->mask)); +} + +/** forward declaration */ +static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry, + bool add); + +/** + * Remove policies associated to an entry from kernel + */ +static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + enumerator_t *enumerator; + sp_entry_t *sp; + + if (entry->mode == MODE_TUNNEL) + { + manage_routes(this, entry, FALSE); + } + + enumerator = array_create_enumerator(entry->sps); + while (enumerator->enumerate(enumerator, &sp)) + { + if (sp->policy_in) + { + FwpmFilterDeleteById0(this->handle, sp->policy_in); + sp->policy_in = 0; + } + if (sp->policy_out) + { + FwpmFilterDeleteById0(this->handle, sp->policy_out); + sp->policy_out = 0; + } + if (sp->policy_fwd_in) + { + FwpmFilterDeleteById0(this->handle, sp->policy_fwd_in); + sp->policy_fwd_in = 0; + } + if (sp->policy_fwd_out) + { + FwpmFilterDeleteById0(this->handle, sp->policy_fwd_out); + sp->policy_fwd_out = 0; + } + } + enumerator->destroy(enumerator); +} + +/** + * Destroy a SA/SP entry set + */ +static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + if (entry->sa_id) + { + IPsecSaContextDeleteById0(this->handle, entry->sa_id); + } + if (entry->provider) + { + FwpmProviderContextDeleteById0(this->handle, entry->provider); + } + cleanup_policies(this, entry); + array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL); + entry->local->destroy(entry->local); + entry->remote->destroy(entry->remote); + chunk_clear(&entry->isa.integ.key); + chunk_clear(&entry->isa.encr.key); + chunk_clear(&entry->osa.integ.key); + chunk_clear(&entry->osa.encr.key); + free(entry); +} + +/** + * Append/Realloc a filter condition to an existing condition set + */ +static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[], + int *count) +{ + FWPM_FILTER_CONDITION0 *cond; + + (*count)++; + *conds = realloc(*conds, *count * sizeof(*cond)); + cond = *conds + *count - 1; + memset(cond, 0, sizeof(*cond)); + + return cond; +} + +/** + * Convert an IPv4 prefix to a host order subnet mask + */ +static u_int32_t prefix2mask(u_int8_t prefix) +{ + u_int8_t netmask[4] = {}; + int i; + + for (i = 0; i < sizeof(netmask); i++) + { + if (prefix < 8) + { + netmask[i] = 0xFF << (8 - prefix); + break; + } + netmask[i] = 0xFF; + prefix -= 8; + } + return untoh32(netmask); +} + +/** + * Convert a 16-bit range to a WFP condition + */ +static void range2cond(FWPM_FILTER_CONDITION0 *cond, + u_int16_t from, u_int16_t to) +{ + if (from == to) + { + cond->matchType = FWP_MATCH_EQUAL; + cond->conditionValue.type = FWP_UINT16; + cond->conditionValue.uint16 = from; + } + else + { + cond->matchType = FWP_MATCH_RANGE; + cond->conditionValue.type = FWP_RANGE_TYPE; + cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0)); + cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16; + cond->conditionValue.rangeValue->valueLow.uint16 = from; + cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16; + cond->conditionValue.rangeValue->valueHigh.uint16 = to; + } +} + +/** + * (Re-)allocate filter conditions for given local or remote traffic selector + */ +static bool ts2condition(traffic_selector_t *ts, const GUID *target, + FWPM_FILTER_CONDITION0 *conds[], int *count) +{ + FWPM_FILTER_CONDITION0 *cond; + FWP_BYTE_ARRAY16 *addr; + FWP_RANGE0 *range; + u_int16_t from_port, to_port; + void *from, *to; + u_int8_t proto; + host_t *net; + u_int8_t prefix; + + from = ts->get_from_address(ts).ptr; + to = ts->get_to_address(ts).ptr; + from_port = ts->get_from_port(ts); + to_port = ts->get_to_port(ts); + + cond = append_condition(conds, count); + cond->fieldKey = *target; + if (ts->is_host(ts, NULL)) + { + cond->matchType = FWP_MATCH_EQUAL; + switch (ts->get_type(ts)) + { + case TS_IPV4_ADDR_RANGE: + cond->conditionValue.type = FWP_UINT32; + cond->conditionValue.uint32 = untoh32(from); + break; + case TS_IPV6_ADDR_RANGE: + cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr)); + memcpy(addr, from, sizeof(*addr)); + break; + default: + return FALSE; + } + } + else if (ts->to_subnet(ts, &net, &prefix)) + { + FWP_V6_ADDR_AND_MASK *m6; + FWP_V4_ADDR_AND_MASK *m4; + + cond->matchType = FWP_MATCH_EQUAL; + switch (net->get_family(net)) + { + case AF_INET: + cond->conditionValue.type = FWP_V4_ADDR_MASK; + cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4)); + m4->addr = untoh32(from); + m4->mask = prefix2mask(prefix); + break; + case AF_INET6: + cond->conditionValue.type = FWP_V6_ADDR_MASK; + cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6)); + memcpy(m6->addr, from, sizeof(m6->addr)); + m6->prefixLength = prefix; + break; + default: + net->destroy(net); + return FALSE; + } + net->destroy(net); + } + else + { + cond->matchType = FWP_MATCH_RANGE; + cond->conditionValue.type = FWP_RANGE_TYPE; + cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range)); + switch (ts->get_type(ts)) + { + case TS_IPV4_ADDR_RANGE: + range->valueLow.type = FWP_UINT32; + range->valueLow.uint32 = untoh32(from); + range->valueHigh.type = FWP_UINT32; + range->valueHigh.uint32 = untoh32(to); + break; + case TS_IPV6_ADDR_RANGE: + range->valueLow.type = FWP_BYTE_ARRAY16_TYPE; + range->valueLow.byteArray16 = addr = malloc(sizeof(*addr)); + memcpy(addr, from, sizeof(*addr)); + range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE; + range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr)); + memcpy(addr, to, sizeof(*addr)); + break; + default: + return FALSE; + } + } + + proto = ts->get_protocol(ts); + if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + { + cond = append_condition(conds, count); + cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL; + cond->matchType = FWP_MATCH_EQUAL; + cond->conditionValue.type = FWP_UINT8; + cond->conditionValue.uint8 = proto; + } + + if (proto == IPPROTO_ICMP) + { + if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + { + u_int8_t from_type, to_type, from_code, to_code; + + from_type = traffic_selector_icmp_type(from_port); + to_type = traffic_selector_icmp_type(to_port); + from_code = traffic_selector_icmp_code(from_port); + to_code = traffic_selector_icmp_code(to_port); + + if (from_type != 0 || to_type != 0xFF) + { + cond = append_condition(conds, count); + cond->fieldKey = FWPM_CONDITION_ICMP_TYPE; + range2cond(cond, from_type, to_type); + } + if (from_code != 0 || to_code != 0xFF) + { + cond = append_condition(conds, count); + cond->fieldKey = FWPM_CONDITION_ICMP_CODE; + range2cond(cond, from_code, to_code); + } + } + } + else if (from_port != 0 || to_port != 0xFFFF) + { + if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + { + cond = append_condition(conds, count); + cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; + range2cond(cond, from_port, to_port); + } + if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + { + cond = append_condition(conds, count); + cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + range2cond(cond, from_port, to_port); + } + } + return TRUE; +} + +/** + * Free memory associated to a single condition + */ +static void free_condition(FWP_DATA_TYPE type, void *value) +{ + FWP_RANGE0 *range; + + switch (type) + { + case FWP_BYTE_ARRAY16_TYPE: + case FWP_V4_ADDR_MASK: + case FWP_V6_ADDR_MASK: + free(value); + break; + case FWP_RANGE_TYPE: + range = value; + free_condition(range->valueLow.type, range->valueLow.sd); + free_condition(range->valueHigh.type, range->valueHigh.sd); + free(range); + break; + default: + break; + } +} + +/** + * Free memory used by a set of conditions + */ +static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd); + } + free(conds); +} + +/** + * Find the callout GUID for given parameters + */ +static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward, + GUID *layer, GUID *sublayer, GUID *callout) +{ + struct { + bool tunnel; + bool v6; + bool inbound; + bool forward; + const GUID *layer; + const GUID *sublayer; + const GUID *callout; + } map[] = { + { 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 }, + { 0, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL, + &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 }, + { 0, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 }, + { 0, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL, + &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 }, + { 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 }, + { 1, 0, 0, 1, &FWPM_LAYER_IPFORWARD_V4, + &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL, + &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 }, + { 1, 0, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 }, + { 1, 0, 1, 1, &FWPM_LAYER_IPFORWARD_V4, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 }, + { 1, 1, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 }, + { 1, 1, 0, 1, &FWPM_LAYER_IPFORWARD_V6, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 }, + { 1, 1, 1, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 }, + { 1, 1, 1, 1, &FWPM_LAYER_IPFORWARD_V6, + &FWPM_SUBLAYER_IPSEC_TUNNEL, + &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (tunnel == map[i].tunnel && + v6 == map[i].v6 && + inbound == map[i].inbound && + forward == map[i].forward) + { + *callout = *map[i].callout; + *layer = *map[i].layer; + if (map[i].sublayer) + { + *sublayer = *map[i].sublayer; + } + return TRUE; + } + } + return FALSE; +} + +/** + * Install a single policy in to the kernel + */ +static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp, + GUID *context, bool inbound, bool fwd, UINT64 *filter_id) +{ + FWPM_FILTER_CONDITION0 *conds = NULL; + traffic_selector_t *local, *remote; + const GUID *ltarget, *rtarget; + int count = 0; + bool v6; + DWORD res; + FWPM_FILTER0 filter = { + .displayData = { + .name = L"charon IPsec policy", + }, + .action = { + .type = FWP_ACTION_CALLOUT_TERMINATING, + }, + }; + + if (context) + { + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&this->provider.providerKey; + filter.providerContextKey = *context; + } + + v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE; + if (!find_callout(context != NULL, v6, inbound, fwd, + &filter.layerKey, &filter.subLayerKey, + &filter.action.calloutKey)) + { + return FALSE; + } + + if (inbound && fwd) + { + local = sp->dst; + remote = sp->src; + } + else + { + local = sp->src; + remote = sp->dst; + } + if (fwd) + { + ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS; + rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS; + } + else + { + ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS; + rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS; + } + if (!ts2condition(local, ltarget, &conds, &count) || + !ts2condition(remote, rtarget, &conds, &count)) + { + free_conditions(conds, count); + return FALSE; + } + + filter.numFilterConditions = count; + filter.filterCondition = conds; + + res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id); + free_conditions(conds, count); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "installing %s%sbound WFP filter failed: 0x%08x", + fwd ? "forward " : "", inbound ? "in" : "out", res); + return FALSE; + } + return TRUE; +} + +/** + * Install a set of policies in to the kernel + */ +static bool install_sps(private_kernel_wfp_ipsec_t *this, + entry_t *entry, GUID *context) +{ + enumerator_t *enumerator; + sp_entry_t *sp; + + enumerator = array_create_enumerator(entry->sps); + while (enumerator->enumerate(enumerator, &sp)) + { + /* inbound policy */ + if (!install_sp(this, sp, context, TRUE, FALSE, &sp->policy_in)) + { + enumerator->destroy(enumerator); + return FALSE; + } + /* outbound policy */ + if (!install_sp(this, sp, context, FALSE, FALSE, &sp->policy_out)) + { + enumerator->destroy(enumerator); + return FALSE; + } + if (context) + { + if (!sp->src->is_host(sp->src, entry->local) || + !sp->dst->is_host(sp->dst, entry->remote)) + { + /* inbound forward policy, from decapsulation */ + if (!install_sp(this, sp, context, + TRUE, TRUE, &sp->policy_fwd_in)) + { + enumerator->destroy(enumerator); + return FALSE; + } + /* outbound forward policy, to encapsulate */ + if (!install_sp(this, sp, context, + FALSE, TRUE, &sp->policy_fwd_out)) + { + enumerator->destroy(enumerator); + return FALSE; + } + } + } + } + enumerator->destroy(enumerator); + + return TRUE; +} + +/** + * Convert a chunk_t to a WFP FWP_BYTE_BLOB + */ +static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk) +{ + return (FWP_BYTE_BLOB){ + .size = chunk.len, + .data = chunk.ptr, + }; +} + +/** + * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0 + */ +static bool alg2auth(integrity_algorithm_t alg, + IPSEC_SA_AUTH_INFORMATION0 *info) +{ + struct { + integrity_algorithm_t alg; + IPSEC_AUTH_TRANSFORM_ID0 transform; + } map[] = { + { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 }, + { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 }, + { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128}, + { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 }, + { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 }, + { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (map[i].alg == alg) + { + info->authTransform.authTransformId = map[i].transform; + return TRUE; + } + } + return FALSE; +} + +/** + * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0 + */ +static bool alg2cipher(encryption_algorithm_t alg, int keylen, + IPSEC_SA_CIPHER_INFORMATION0 *info) +{ + struct { + encryption_algorithm_t alg; + int keylen; + IPSEC_CIPHER_TRANSFORM_ID0 transform; + } map[] = { + { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES }, + { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES }, + { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 }, + { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 }, + { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 }, + { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 }, + { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 }, + { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (map[i].alg == alg && map[i].keylen == keylen) + { + info->cipherTransform.cipherTransformId = map[i].transform; + return TRUE; + } + } + return FALSE; +} + +/** + * Get the integrity algorithm used for an AEAD transform + */ +static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen) +{ + struct { + encryption_algorithm_t encr; + int keylen; + integrity_algorithm_t integ; + } map[] = { + { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC }, + { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC }, + { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC }, + { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC }, + { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC }, + { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC }, + }; + int i; + + for (i = 0; i < countof(map); i++) + { + if (map[i].encr == encr && map[i].keylen == keylen) + { + return map[i].integ; + } + } + return AUTH_UNDEFINED; +} + +/** + * Install a single SA + */ +static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry, + bool inbound, sa_entry_t *sa, FWP_IP_VERSION version) +{ + IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {}; + IPSEC_SA0 ipsec = { + .spi = ntohl(sa->spi), + }; + IPSEC_SA_BUNDLE0 bundle = { + .lifetime = { + .lifetimeSeconds = inbound ? entry->isa.lifetime + : entry->osa.lifetime, + }, + .saList = &ipsec, + .numSAs = 1, + .ipVersion = version, + }; + struct { + u_int16_t alg; + chunk_t key; + } integ = {}, encr = {}; + DWORD res; + + switch (sa->protocol) + { + case IPPROTO_AH: + ipsec.saTransformType = IPSEC_TRANSFORM_AH; + ipsec.ahInformation = &info.saAuthInformation; + integ.key = sa->integ.key; + integ.alg = sa->integ.alg; + break; + case IPPROTO_ESP: + if (sa->encr.alg == ENCR_NULL || + sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC) + { + ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH; + ipsec.espAuthInformation = &info.saAuthInformation; + } + else + { + ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER; + ipsec.espAuthAndCipherInformation = &info; + encr.key = sa->encr.key; + encr.alg = sa->encr.alg; + } + if (encryption_algorithm_is_aead(sa->encr.alg)) + { + integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len); + integ.key = sa->encr.key; + } + else + { + integ.alg = sa->integ.alg; + integ.key = sa->integ.key; + } + break; + default: + return FALSE; + } + + if (integ.alg) + { + info.saAuthInformation.authKey = chunk2blob(integ.key); + if (!alg2auth(integ.alg, &info.saAuthInformation)) + { + DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP", + integrity_algorithm_names, integ.alg); + return FALSE; + } + } + if (encr.alg) + { + info.saCipherInformation.cipherKey = chunk2blob(encr.key); + if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation)) + { + DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP", + encryption_algorithm_names, encr.alg); + return FALSE; + } + } + + if (inbound) + { + res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle); + } + else + { + bundle.flags |= IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND; + res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle); + } + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x", + inbound ? "in" : "out", res); + return FALSE; + } + return TRUE; +} + +/** + * Convert an IPv6 host address to WFP representation + */ +static void host2address6(host_t *host, void *out) +{ + u_int32_t *src, *dst = out; + + src = (u_int32_t*)host->get_address(host).ptr; + + dst[0] = untoh32(&src[3]); + dst[1] = untoh32(&src[2]); + dst[2] = untoh32(&src[1]); + dst[3] = untoh32(&src[0]); +} + +/** + * Fill in traffic structure from entry addresses + */ +static bool hosts2traffic(private_kernel_wfp_ipsec_t *this, + host_t *l, host_t *r, IPSEC_TRAFFIC1 *traffic) +{ + if (l->get_family(l) != r->get_family(r)) + { + return FALSE; + } + switch (l->get_family(l)) + { + case AF_INET: + traffic->ipVersion = FWP_IP_VERSION_V4; + traffic->localV4Address = untoh32(l->get_address(l).ptr); + traffic->remoteV4Address = untoh32(r->get_address(r).ptr); + return TRUE; + case AF_INET6: + traffic->ipVersion = FWP_IP_VERSION_V6; + host2address6(l, &traffic->localV6Address); + host2address6(r, &traffic->remoteV6Address); + return TRUE; + default: + return FALSE; + } +} + +/** + * Install SAs to the kernel + */ +static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry, + IPSEC_TRAFFIC_TYPE type) +{ + IPSEC_TRAFFIC1 traffic = { + .trafficType = type, + }; + IPSEC_GETSPI1 spi = { + .inboundIpsecTraffic = { + .trafficType = type, + }, + }; + enumerator_t *enumerator; + sp_entry_t *sp; + DWORD res; + + if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT) + { + enumerator = array_create_enumerator(entry->sps); + if (enumerator->enumerate(enumerator, &sp)) + { + traffic.ipsecFilterId = sp->policy_out; + spi.inboundIpsecTraffic.ipsecFilterId = sp->policy_in; + } + else + { + enumerator->destroy(enumerator); + return FALSE; + } + enumerator->destroy(enumerator); + } + else + { + traffic.tunnelPolicyId = entry->provider; + spi.inboundIpsecTraffic.tunnelPolicyId = entry->provider; + } + + if (!hosts2traffic(this, entry->local, entry->remote, &traffic)) + { + return FALSE; + } + + res = IPsecSaContextCreate1(this->handle, &traffic, NULL, NULL, + &entry->sa_id); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res); + return FALSE; + } + + memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address, + sizeof(traffic.localV6Address)); + memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address, + sizeof(traffic.remoteV6Address)); + spi.ipVersion = traffic.ipVersion; + + res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi, + ntohl(entry->isa.spi)); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res); + IPsecSaContextDeleteById0(this->handle, entry->sa_id); + entry->sa_id = 0; + return FALSE; + } + + if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) || + !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion)) + { + IPsecSaContextDeleteById0(this->handle, entry->sa_id); + entry->sa_id = 0; + return FALSE; + } + + if (entry->encap) + { + IPSEC_V4_UDP_ENCAPSULATION0 encap = { + .localUdpEncapPort = entry->local->get_port(entry->local), + .remoteUdpEncapPort = entry->remote->get_port(entry->remote), + }; + IPSEC_SA_CONTEXT1 *ctx; + + res = IPsecSaContextGetById1(this->handle, entry->sa_id, &ctx); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "getting WFP SA for UDP encap failed: 0x%08x", res); + IPsecSaContextDeleteById0(this->handle, entry->sa_id); + entry->sa_id = 0; + return FALSE; + } + ctx->inboundSa->udpEncapsulation = &encap; + ctx->outboundSa->udpEncapsulation = &encap; + + res = IPsecSaContextUpdate0(this->handle, + IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION, ctx); + FwpmFreeMemory0((void**)&ctx); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "enable WFP UDP encap failed: 0x%08x", res); + IPsecSaContextDeleteById0(this->handle, entry->sa_id); + entry->sa_id = 0; + return FALSE; + } + } + + return TRUE; +} + +/** + * Install a transport mode SA/SP set to the kernel + */ +static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + if (install_sps(this, entry, NULL) && + install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT)) + { + return TRUE; + } + cleanup_policies(this, entry); + return FALSE; +} + +/** + * Generate a new GUID, random + */ +static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid) +{ + bool ok; + rng_t *rng; + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + return FALSE; + } + ok = rng->get_bytes(rng, sizeof(GUID), (u_int8_t*)guid); + rng->destroy(rng); + return ok; +} + +/** + * Register a dummy tunnel provider to associate tunnel filters to + */ +static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this, + entry_t *entry, GUID *guid, UINT64 *luid) +{ + DWORD res; + + IPSEC_AUTH_TRANSFORM0 transform = { + /* Create any valid proposal. This is actually not used, as we + * don't create an SA from this information. */ + .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96, + }; + IPSEC_SA_TRANSFORM0 transforms = { + .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH, + .espAuthTransform = &transform, + }; + IPSEC_PROPOSAL0 proposal = { + .lifetime = { + /* We need a valid lifetime, even if we don't create any SA + * from these values. Pick some values accepted. */ + .lifetimeSeconds = 0xFFFF, + .lifetimeKilobytes = 0xFFFFFFFF, + .lifetimePackets = 0xFFFFFFFF, + }, + .numSaTransforms = 1, + .saTransforms = &transforms, + }; + IPSEC_TUNNEL_POLICY0 policy = { + .numIpsecProposals = 1, + .ipsecProposals = &proposal, + .saIdleTimeout = { + /* not used, set to lifetime for maximum */ + .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds, + .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds, + }, + }; + FWPM_PROVIDER_CONTEXT0 qm = { + .displayData = { + .name = L"charon tunnel provider", + }, + .providerKey = (GUID*)&this->provider.providerKey, + .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT, + .ikeQmTunnelPolicy = &policy, + }; + + switch (entry->local->get_family(entry->local)) + { + case AF_INET: + policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4; + policy.tunnelEndpoints.localV4Address = + untoh32(entry->local->get_address(entry->local).ptr); + policy.tunnelEndpoints.remoteV4Address = + untoh32(entry->remote->get_address(entry->remote).ptr); + break; + case AF_INET6: + policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6; + host2address6(entry->local, &policy.tunnelEndpoints.localV6Address); + host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address); + break; + default: + return FALSE; + } + + if (!generate_guid(this, &qm.providerContextKey)) + { + return FALSE; + } + + res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res); + return FALSE; + } + *guid = qm.providerContextKey; + return TRUE; +} + +/** + * Install tunnel mode SPs to the kernel + */ +static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + GUID guid; + + if (!add_tunnel_provider(this, entry, &guid, &entry->provider)) + { + return FALSE; + } + if (!install_sps(this, entry, &guid)) + { + return FALSE; + } + return TRUE; +} + +/** + * Reduce refcount, or uninstall a route if all refs gone + */ +static bool uninstall_route(private_kernel_wfp_ipsec_t *this, + host_t *dst, u_int8_t mask, host_t *src, host_t *gtw) +{ + route_t *route, key = { + .dst = dst, + .mask = mask, + .src = src, + }; + char *name; + bool res = FALSE; + + this->mutex->lock(this->mutex); + route = this->routes->get(this->routes, &key); + if (route) + { + if (--route->refs == 0) + { + if (hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &name)) + { + res = hydra->kernel_interface->del_route(hydra->kernel_interface, + dst->get_address(dst), mask, gtw, src, name) == SUCCESS; + free(name); + } + route = this->routes->remove(this->routes, route); + if (route) + { + destroy_route(route); + } + } + else + { + res = TRUE; + } + } + this->mutex->unlock(this->mutex); + + return res; +} + +/** + * Install a single route, or refcount if exists + */ +static bool install_route(private_kernel_wfp_ipsec_t *this, + host_t *dst, u_int8_t mask, host_t *src, host_t *gtw) +{ + route_t *route, key = { + .dst = dst, + .mask = mask, + .src = src, + }; + char *name; + bool res = FALSE; + + this->mutex->lock(this->mutex); + route = this->routes->get(this->routes, &key); + if (route) + { + route->refs++; + res = TRUE; + } + else + { + if (hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &name)) + { + if (hydra->kernel_interface->add_route(hydra->kernel_interface, + dst->get_address(dst), mask, gtw, src, name) == SUCCESS) + { + INIT(route, + .dst = dst->clone(dst), + .mask = mask, + .src = src->clone(src), + .gtw = gtw ? gtw->clone(gtw) : NULL, + .refs = 1, + ); + route = this->routes->put(this->routes, route, route); + if (route) + { + destroy_route(route); + } + res = TRUE; + } + free(name); + } + } + this->mutex->unlock(this->mutex); + + return res; +} + +/** + * (Un)-install a single route + */ +static bool manage_route(private_kernel_wfp_ipsec_t *this, + host_t *local, host_t *remote, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + bool add) +{ + host_t *src, *dst, *gtw; + u_int8_t mask; + bool done; + + if (!dst_ts->to_subnet(dst_ts, &dst, &mask)) + { + return FALSE; + } + if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, + src_ts, &src, NULL) != SUCCESS) + { + dst->destroy(dst); + return FALSE; + } + gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface, + remote, -1, local); + if (add) + { + done = install_route(this, dst, mask, src, gtw); + } + else + { + done = uninstall_route(this, dst, mask, src, gtw); + } + dst->destroy(dst); + src->destroy(src); + DESTROY_IF(gtw); + + if (!done) + { + DBG1(DBG_KNL, "%sinstalling route for policy %R === %R failed", + add ? "" : "un", src_ts, dst_ts); + } + return done; +} + +/** + * (Un)-install routes for IPsec policies + */ +static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry, + bool add) +{ + enumerator_t *enumerator; + sp_entry_t *sp; + + enumerator = array_create_enumerator(entry->sps); + while (enumerator->enumerate(enumerator, &sp)) + { + if (add && sp->route) + { + continue; + } + if (!add && !sp->route) + { + continue; + } + if (manage_route(this, entry->local, entry->remote, + sp->src, sp->dst, add)) + { + sp->route = add; + } + } + enumerator->destroy(enumerator); + + return TRUE; +} + +/** + * Install a tunnel mode SA/SP set to the kernel + */ +static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + if (install_tunnel_sps(this, entry) && + manage_routes(this, entry, TRUE) && + install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL)) + { + return TRUE; + } + cleanup_policies(this, entry); + return FALSE; +} + +/** + * Install a SA/SP set to the kernel + */ +static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry) +{ + switch (entry->mode) + { + case MODE_TRANSPORT: + return install_transport(this, entry); + case MODE_TUNNEL: + return install_tunnel(this, entry); + case MODE_BEET: + default: + return FALSE; + } +} + +/** + * Installed trap entry + */ +typedef struct { + /** reqid this trap is installed for */ + u_int32_t reqid; + /** is this a forward policy trap for tunnel mode? */ + bool fwd; + /** do we have installed a route for this trap policy? */ + bool route; + /** local address of associated route */ + host_t *local; + /** remote address of associated route */ + host_t *remote; + /** src traffic selector */ + traffic_selector_t *src; + /** dst traffic selector */ + traffic_selector_t *dst; + /** LUID of installed tunnel policy filter */ + UINT64 filter_id; +} trap_t; + +/** + * Destroy a trap entry + */ +static void destroy_trap(trap_t *this) +{ + this->local->destroy(this->local); + this->remote->destroy(this->remote); + this->src->destroy(this->src); + this->dst->destroy(this->dst); + free(this); +} + +/** + * Hashtable equals function for traps + */ +static bool equals_trap(trap_t *a, trap_t *b) +{ + return a->filter_id == b->filter_id; +} + +/** + * Hashtable hash function for traps + */ +static u_int hash_trap(trap_t *trap) +{ + return chunk_hash(chunk_from_thing(trap->filter_id)); +} + +/** + * Send an acquire for an installed trap filter + */ +static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id, + traffic_selector_t *src, traffic_selector_t *dst) +{ + u_int32_t reqid = 0; + trap_t *trap, key = { + .filter_id = filter_id, + }; + + this->mutex->lock(this->mutex); + trap = this->traps->get(this->traps, &key); + if (trap) + { + reqid = trap->reqid; + } + this->mutex->unlock(this->mutex); + + if (reqid) + { + src = src ? src->clone(src) : NULL; + dst = dst ? dst->clone(dst) : NULL; + hydra->kernel_interface->acquire(hydra->kernel_interface, reqid, + src, dst); + } +} + +/** + * Create a single host traffic selector from an FWP address definition + */ +static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data, + u_int8_t protocol, u_int16_t from_port, u_int16_t to_port) +{ + ts_type_t type; + UINT32 ints[4]; + chunk_t addr; + + switch (version) + { + case FWP_IP_VERSION_V4: + ints[0] = untoh32(data); + addr = chunk_from_thing(ints[0]); + type = TS_IPV4_ADDR_RANGE; + break; + case FWP_IP_VERSION_V6: + ints[3] = untoh32(data); + ints[2] = untoh32(data + 4); + ints[1] = untoh32(data + 8); + ints[0] = untoh32(data + 12); + addr = chunk_from_thing(ints); + type = TS_IPV6_ADDR_RANGE; + break; + default: + return NULL; + } + return traffic_selector_create_from_bytes(protocol, type, addr, from_port, + addr, to_port); +} + +/** + * FwpmNetEventSubscribe0() callback + */ +static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event) +{ + private_kernel_wfp_ipsec_t *this = user; + traffic_selector_t *local = NULL, *remote = NULL; + u_int8_t protocol = 0; + u_int16_t from_local = 0, to_local = 65535; + u_int16_t from_remote = 0, to_remote = 65535; + + if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) && + (event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET)) + { + if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET) + { + from_local = to_local = event->header.localPort; + } + if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET) + { + from_remote = to_remote = event->header.remotePort; + } + if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET) + { + protocol = event->header.ipProtocol; + } + + local = addr2ts(event->header.ipVersion, + (void*)&event->header.localAddrV6, + protocol, from_local, to_local); + remote = addr2ts(event->header.ipVersion, + (void*)&event->header.remoteAddrV6, + protocol, from_remote, to_remote); + } + + switch (event->type) + { + case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP: + acquire(this, event->classifyDrop->filterId, local, remote); + break; + case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE: + case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE: + case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE: + case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP: + DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, " + "SPI 0x%08x, %s filterId %llu", local, remote, + event->ipsecDrop->failureStatus, event->ipsecDrop->spi, + event->ipsecDrop->direction ? "in" : "out", + event->ipsecDrop->filterId); + break; + case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP: + default: + break; + } + + DESTROY_IF(local); + DESTROY_IF(remote); +} + +/** + * Register for net events + */ +static bool register_events(private_kernel_wfp_ipsec_t *this) +{ + FWPM_NET_EVENT_SUBSCRIPTION0 subscription = {}; + DWORD res; + + res = FwpmNetEventSubscribe0(this->handle, &subscription, + event_callback, this, &this->event); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "registering for WFP events failed: 0x%08x", res); + return FALSE; + } + return TRUE; +} + +/** + * Install a trap policy to kernel + */ +static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) +{ + FWPM_FILTER_CONDITION0 *conds = NULL; + int count = 0; + DWORD res; + const GUID *starget, *dtarget; + UINT64 weight = 0x000000000000ff00; + FWPM_FILTER0 filter = { + .displayData = { + .name = L"charon IPsec trap", + }, + .action = { + .type = FWP_ACTION_BLOCK, + }, + .weight = { + .type = FWP_UINT64, + .uint64 = &weight, + }, + }; + + if (trap->fwd) + { + if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE) + { + filter.layerKey = FWPM_LAYER_IPFORWARD_V4; + } + else + { + filter.layerKey = FWPM_LAYER_IPFORWARD_V6; + } + starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS; + dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS; + } + else + { + if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE) + { + filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4; + } + else + { + filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6; + } + starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS; + dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS; + } + + if (!ts2condition(trap->src, starget, &conds, &count) || + !ts2condition(trap->dst, dtarget, &conds, &count)) + { + free_conditions(conds, count); + return FALSE; + } + + filter.numFilterConditions = count; + filter.filterCondition = conds; + + res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id); + free_conditions(conds, count); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res); + return FALSE; + } + return TRUE; +} + +/** + * Uninstall a trap policy from kernel + */ +static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap) +{ + DWORD res; + + res = FwpmFilterDeleteById0(this->handle, trap->filter_id); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res); + return FALSE; + } + return TRUE; +} + +/** + * Create and install a new trap entry + */ +static bool add_trap(private_kernel_wfp_ipsec_t *this, + u_int32_t reqid, bool fwd, host_t *local, host_t *remote, + traffic_selector_t *src, traffic_selector_t *dst) +{ + trap_t *trap; + + INIT(trap, + .reqid = reqid, + .fwd = fwd, + .src = src->clone(src), + .dst = dst->clone(dst), + .local = local->clone(local), + .remote = remote->clone(remote), + ); + + if (!install_trap(this, trap)) + { + destroy_trap(trap); + return FALSE; + } + + trap->route = manage_route(this, local, remote, src, dst, TRUE); + + this->mutex->lock(this->mutex); + this->traps->put(this->traps, trap, trap); + this->mutex->unlock(this->mutex); + return TRUE; +} + +/** + * Uninstall and remove a new trap entry + */ +static bool remove_trap(private_kernel_wfp_ipsec_t *this, + u_int32_t reqid, bool fwd, + traffic_selector_t *src, traffic_selector_t *dst) +{ + enumerator_t *enumerator; + trap_t *trap, *found = NULL; + + this->mutex->lock(this->mutex); + enumerator = this->traps->create_enumerator(this->traps); + while (enumerator->enumerate(enumerator, NULL, &trap)) + { + if (reqid == trap->reqid && + fwd == trap->fwd && + src->equals(src, trap->src) && + dst->equals(dst, trap->dst)) + { + this->traps->remove_at(this->traps, enumerator); + found = trap; + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + if (found) + { + if (trap->route) + { + trap->route = !manage_route(this, trap->local, trap->remote, + src, dst, FALSE); + } + uninstall_trap(this, found); + destroy_trap(found); + return TRUE; + } + return FALSE; +} + +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_wfp_ipsec_t *this) +{ + return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES; +} + +/** + * Initialize seeds for SPI generation + */ +static bool init_spi(private_kernel_wfp_ipsec_t *this) +{ + bool ok = TRUE; + rng_t *rng; + + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng) + { + return FALSE; + } + ok = rng->get_bytes(rng, sizeof(this->nextspi), (u_int8_t*)&this->nextspi); + if (ok) + { + ok = rng->get_bytes(rng, sizeof(this->mixspi), (u_int8_t*)&this->mixspi); + } + rng->destroy(rng); + return ok; +} + +/** + * Map an integer x with a one-to-one function using quadratic residues. + */ +static u_int permute(u_int x, u_int p) +{ + u_int qr; + + x = x % p; + qr = ((u_int64_t)x * x) % p; + if (x <= p / 2) + { + return qr; + } + return p - qr; +} + +METHOD(kernel_ipsec_t, get_spi, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t reqid, u_int32_t *spi) +{ + /* To avoid sequencial SPIs, we use a one-to-one permuation function on + * an incrementing counter, that is a full period PRNG for the range we + * allocate SPIs in. We add some randomness using a fixed XOR and start + * the counter at random position. This is not cryptographically safe, + * but that is actually not required. + * The selected prime should be smaller than the range we allocate SPIs + * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */ + static const u_int p = 268435399, offset = 0xc0000000; + + *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p)); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, get_cpi, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t reqid, u_int16_t *cpi) +{ + return NOT_SUPPORTED; +} + +/** + * Data for an expire callback job + */ +typedef struct { + /* backref to kernel backend */ + private_kernel_wfp_ipsec_t *this; + /* SPI of expiring SA */ + u_int32_t spi; + /* destination address of expiring SA */ + host_t *dst; + /* is this a hard expire, or a rekey request? */ + bool hard; +} expire_data_t; + +/** + * Clean up expire data + */ +static void expire_data_destroy(expire_data_t *data) +{ + data->dst->destroy(data->dst); + free(data); +} + +/** + * Callback job for SA expiration + */ +static job_requeue_t expire_job(expire_data_t *data) +{ + private_kernel_wfp_ipsec_t *this = data->this; + u_int32_t reqid = 0; + u_int8_t protocol; + entry_t *entry; + sa_entry_t key = { + .spi = data->spi, + .dst = data->dst, + }; + + if (data->hard) + { + this->mutex->lock(this->mutex); + entry = this->isas->remove(this->isas, &key); + this->mutex->unlock(this->mutex); + if (entry) + { + protocol = entry->isa.protocol; + reqid = entry->reqid; + if (entry->osa.dst) + { + key.dst = entry->osa.dst; + key.spi = entry->osa.spi; + this->osas->remove(this->osas, &key); + } + entry_destroy(this, entry); + } + } + else + { + this->mutex->lock(this->mutex); + entry = this->isas->get(this->isas, &key); + if (entry) + { + protocol = entry->isa.protocol; + reqid = entry->reqid; + } + this->mutex->unlock(this->mutex); + } + + if (reqid) + { + hydra->kernel_interface->expire(hydra->kernel_interface, + reqid, protocol, data->spi, data->hard); + } + + return JOB_REQUEUE_NONE; +} + +/** + * Schedule an expire event for an SA + */ +static void schedule_expire(private_kernel_wfp_ipsec_t *this, u_int32_t spi, + host_t *dst, u_int32_t lifetime, bool hard) +{ + expire_data_t *data; + + INIT(data, + .this = this, + .spi = spi, + .dst = dst->clone(dst), + .hard = hard, + ); + + lib->scheduler->schedule_job(lib->scheduler, (job_t*) + callback_job_create((void*)expire_job, data, + (void*)expire_data_destroy, NULL), + lifetime); +} + +METHOD(kernel_ipsec_t, add_sa, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, + u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, + u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, + bool initiator, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) +{ + host_t *local, *remote; + entry_t *entry; + + if (inbound) + { + /* comes first, create new entry */ + local = dst->clone(dst); + remote = src->clone(src); + + INIT(entry, + .reqid = reqid, + .isa = { + .spi = spi, + .dst = local, + .protocol = protocol, + .lifetime = lifetime->time.life, + .encr = { + .alg = enc_alg, + .key = chunk_clone(enc_key), + }, + .integ = { + .alg = int_alg, + .key = chunk_clone(int_key), + }, + }, + .sps = array_create(0, 0), + .local = local, + .remote = remote, + .mode = mode, + .encap = encap, + ); + + if (lifetime->time.life) + { + schedule_expire(this, spi, local, lifetime->time.life, TRUE); + } + if (lifetime->time.rekey && lifetime->time.rekey != lifetime->time.life) + { + schedule_expire(this, spi, local, lifetime->time.rekey, FALSE); + } + + this->mutex->lock(this->mutex); + this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry); + this->isas->put(this->isas, &entry->isa, entry); + this->mutex->unlock(this->mutex); + } + else + { + /* comes after inbound, update entry */ + this->mutex->lock(this->mutex); + entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)reqid); + this->mutex->unlock(this->mutex); + + if (!entry) + { + DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found " + "for reqid %u ", reqid); + return NOT_FOUND; + } + /* TODO: should we check for local/remote, mode etc.? */ + + entry->osa = (sa_entry_t){ + .spi = spi, + .dst = entry->remote, + .protocol = protocol, + .lifetime = lifetime->time.life, + .encr = { + .alg = enc_alg, + .key = chunk_clone(enc_key), + }, + .integ = { + .alg = int_alg, + .key = chunk_clone(int_key), + }, + }; + + this->mutex->lock(this->mutex); + this->osas->put(this->osas, &entry->osa, entry); + this->mutex->unlock(this->mutex); + } + + return SUCCESS; +} + +METHOD(kernel_ipsec_t, update_sa, status_t, + private_kernel_wfp_ipsec_t *this, u_int32_t spi, u_int8_t protocol, + u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst, + bool encap, bool new_encap, mark_t mark) +{ + entry_t *entry; + sa_entry_t key = { + .dst = dst, + .spi = spi, + }; + UINT64 sa_id = 0; + IPSEC_SA_CONTEXT1 *ctx; + IPSEC_V4_UDP_ENCAPSULATION0 ports; + UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC; + DWORD res; + + this->mutex->lock(this->mutex); + entry = this->osas->get(this->osas, &key); + this->mutex->unlock(this->mutex); + + if (entry) + { + /* outbound entry, nothing to do */ + return SUCCESS; + } + + this->mutex->lock(this->mutex); + entry = this->isas->get(this->isas, &key); + if (entry) + { + /* inbound entry, do update */ + sa_id = entry->sa_id; + ports.localUdpEncapPort = entry->local->get_port(entry->local); + ports.remoteUdpEncapPort = entry->remote->get_port(entry->remote); + } + this->mutex->unlock(this->mutex); + + if (!sa_id) + { + return NOT_FOUND; + } + + res = IPsecSaContextGetById1(this->handle, sa_id, &ctx); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res); + return FAILED; + } + if (!hosts2traffic(this, new_dst, new_src, &ctx->inboundSa->traffic) || + !hosts2traffic(this, new_dst, new_src, &ctx->outboundSa->traffic)) + { + FwpmFreeMemory0((void**)&ctx); + return FAILED; + } + + if (new_encap != encap) + { + if (new_encap) + { + ctx->inboundSa->udpEncapsulation = &ports; + ctx->outboundSa->udpEncapsulation = &ports; + } + else + { + ctx->inboundSa->udpEncapsulation = NULL; + ctx->outboundSa->udpEncapsulation = NULL; + } + flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION; + } + + res = IPsecSaContextUpdate0(this->handle, flags, ctx); + FwpmFreeMemory0((void**)&ctx); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res); + return FAILED; + } + + this->mutex->lock(this->mutex); + entry = this->isas->remove(this->isas, &key); + if (entry) + { + key.spi = entry->osa.spi; + key.dst = entry->osa.dst; + this->osas->remove(this->osas, &key); + + entry->local->destroy(entry->local); + entry->remote->destroy(entry->remote); + entry->local = new_dst->clone(new_dst); + entry->remote = new_src->clone(new_src); + entry->isa.dst = entry->local; + entry->osa.dst = entry->remote; + + this->isas->put(this->isas, &entry->isa, entry); + this->osas->put(this->osas, &entry->osa, entry); + + manage_routes(this, entry, FALSE); + manage_routes(this, entry, TRUE); + } + this->mutex->unlock(this->mutex); + + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_sa, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes, + u_int64_t *packets, time_t *time) +{ + /* It does not seem that WFP provides any means of getting per-SA traffic + * statistics. IPsecGetStatistics0/1() provides global stats, and + * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured + * values only. */ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, del_sa, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + entry_t *entry; + sa_entry_t key = { + .dst = dst, + .spi = spi, + }; + + this->mutex->lock(this->mutex); + entry = this->isas->remove(this->isas, &key); + this->mutex->unlock(this->mutex); + + if (entry) + { + /* keep entry until removal of outbound */ + return SUCCESS; + } + + this->mutex->lock(this->mutex); + entry = this->osas->remove(this->osas, &key); + this->mutex->unlock(this->mutex); + + if (entry) + { + entry_destroy(this, entry); + return SUCCESS; + } + + return NOT_FOUND; +} + +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_kernel_wfp_ipsec_t *this) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark, + policy_priority_t priority) +{ + status_t status = SUCCESS; + entry_t *entry; + sp_entry_t *sp; + sa_entry_t key = { + .spi = sa->esp.use ? sa->esp.spi : sa->ah.spi, + .dst = dst, + }; + + if (sa->esp.use && sa->ah.use) + { + return NOT_SUPPORTED; + } + + switch (type) + { + case POLICY_IPSEC: + break; + case POLICY_PASS: + case POLICY_DROP: + return NOT_SUPPORTED; + } + + switch (direction) + { + case POLICY_OUT: + break; + case POLICY_IN: + case POLICY_FWD: + /* not required */ + return SUCCESS; + default: + return NOT_SUPPORTED; + } + + switch (priority) + { + case POLICY_PRIORITY_DEFAULT: + break; + case POLICY_PRIORITY_ROUTED: + if (!add_trap(this, sa->reqid, FALSE, src, dst, src_ts, dst_ts)) + { + return FAILED; + } + if (sa->mode == MODE_TUNNEL) + { + if (!add_trap(this, sa->reqid, TRUE, src, dst, src_ts, dst_ts)) + { + return FAILED; + } + } + return SUCCESS; + case POLICY_PRIORITY_FALLBACK: + default: + return NOT_SUPPORTED; + } + + this->mutex->lock(this->mutex); + entry = this->osas->get(this->osas, &key); + if (entry) + { + if (sa->mode == MODE_TUNNEL || array_count(entry->sps) == 0) + { + INIT(sp, + .src = src_ts->clone(src_ts), + .dst = dst_ts->clone(dst_ts), + ); + array_insert(entry->sps, -1, sp); + if (array_count(entry->sps) == sa->policy_count) + { + if (!install(this, entry)) + { + status = FAILED; + } + } + } + else + { + /* TODO: reinstall with a filter using multiple TS? + * Filters are ANDed for a match, but we could install a filter + * with the inverse TS set using NOT-matches... */ + DBG1(DBG_KNL, "multiple transport mode traffic selectors not " + "supported by WFP"); + status = NOT_SUPPORTED; + } + } + else + { + DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi); + status = FAILED; + } + this->mutex->unlock(this->mutex); + + return status; +} + +METHOD(kernel_ipsec_t, query_policy, status_t, + private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, + time_t *use_time) +{ + /* see query_sa() for some notes */ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, del_policy, status_t, + private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority) +{ + if (direction == POLICY_OUT && priority == POLICY_PRIORITY_ROUTED) + { + if (remove_trap(this, reqid, FALSE, src_ts, dst_ts)) + { + remove_trap(this, reqid, TRUE, src_ts, dst_ts); + return SUCCESS; + } + return NOT_FOUND; + } + /* not required, as we delete the whole SA/SP set during del_sa() */ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_kernel_wfp_ipsec_t *this) +{ + return NOT_SUPPORTED; +} + +/** + * Add a bypass policy for a specific UDP port + */ +static bool add_bypass(private_kernel_wfp_ipsec_t *this, + int family, u_int16_t port, bool inbound, UINT64 *luid) +{ + FWPM_FILTER_CONDITION0 *cond, *conds = NULL; + int count = 0; + DWORD res; + UINT64 weight = 0xff00000000000000; + FWPM_FILTER0 filter = { + .displayData = { + .name = L"charon IKE bypass", + }, + .action = { + .type = FWP_ACTION_PERMIT, + }, + .weight = { + .type = FWP_UINT64, + .uint64 = &weight, + }, + }; + + switch (family) + { + case AF_INET: + filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 + : FWPM_LAYER_OUTBOUND_TRANSPORT_V4; + break; + case AF_INET6: + filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V6 + : FWPM_LAYER_OUTBOUND_TRANSPORT_V6; + break; + default: + return FALSE; + } + + cond = append_condition(&conds, &count); + cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL; + cond->matchType = FWP_MATCH_EQUAL; + cond->conditionValue.type = FWP_UINT8; + cond->conditionValue.uint8 = IPPROTO_UDP; + + cond = append_condition(&conds, &count); + cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; + cond->matchType = FWP_MATCH_EQUAL; + cond->conditionValue.type = FWP_UINT16; + cond->conditionValue.uint16 = port; + + filter.numFilterConditions = count; + filter.filterCondition = conds; + + res = FwpmFilterAdd0(this->handle, &filter, NULL, luid); + free_conditions(conds, count); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res); + return FALSE; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, bypass_socket, bool, + private_kernel_wfp_ipsec_t *this, int fd, int family) +{ + union { + struct sockaddr sa; + SOCKADDR_IN in; + SOCKADDR_IN6 in6; + } saddr; + int addrlen = sizeof(saddr); + UINT64 filter_out, filter_in = 0; + u_int16_t port; + + if (getsockname(fd, &saddr.sa, &addrlen) == SOCKET_ERROR) + { + return FALSE; + } + switch (family) + { + case AF_INET: + port = ntohs(saddr.in.sin_port); + break; + case AF_INET6: + port = ntohs(saddr.in6.sin6_port); + break; + default: + return FALSE; + } + + if (!add_bypass(this, family, port, TRUE, &filter_in) || + !add_bypass(this, family, port, FALSE, &filter_out)) + { + if (filter_in) + { + FwpmFilterDeleteById0(this->handle, filter_in); + } + return FALSE; + } + + this->mutex->lock(this->mutex); + array_insert(this->bypass, ARRAY_TAIL, &filter_in); + array_insert(this->bypass, ARRAY_TAIL, &filter_out); + this->mutex->unlock(this->mutex); + + return TRUE; +} + +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_wfp_ipsec_t *this, int fd, int family, u_int16_t port) +{ + return FALSE; +} + +METHOD(kernel_ipsec_t, destroy, void, + private_kernel_wfp_ipsec_t *this) +{ + UINT64 filter; + + while (array_remove(this->bypass, ARRAY_TAIL, &filter)) + { + FwpmFilterDeleteById0(this->handle, filter); + } + if (this->handle) + { + if (this->event) + { + FwpmNetEventUnsubscribe0(this->handle, this->event); + } + FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey); + FwpmEngineClose0(this->handle); + } + array_destroy(this->bypass); + this->tsas->destroy(this->tsas); + this->isas->destroy(this->isas); + this->osas->destroy(this->osas); + this->routes->destroy(this->routes); + this->traps->destroy(this->traps); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * Described in header. + */ +kernel_wfp_ipsec_t *kernel_wfp_ipsec_create() +{ + private_kernel_wfp_ipsec_t *this; + DWORD res; + FWPM_SESSION0 session = { + .displayData = { + .name = L"charon", + .description = L"strongSwan IKE kernel-wfp backend", + }, + }; + + INIT(this, + .public = { + .interface = { + .get_features = _get_features, + .get_spi = _get_spi, + .get_cpi = _get_cpi, + .add_sa = _add_sa, + .update_sa = _update_sa, + .query_sa = _query_sa, + .del_sa = _del_sa, + .flush_sas = _flush_sas, + .add_policy = _add_policy, + .query_policy = _query_policy, + .del_policy = _del_policy, + .flush_policies = _flush_policies, + .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, + .destroy = _destroy, + }, + }, + .provider = { + .displayData = { + .name = L"charon", + .description = L"strongSwan IKE kernel-wfp backend", + }, + .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09, + { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }}, + }, + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + .bypass = array_create(sizeof(UINT64), 2), + .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4), + .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4), + .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4), + .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4), + .traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4), + ); + + if (!init_spi(this)) + { + destroy(this); + return NULL; + } + + res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, + &this->handle); + if (res != ERROR_SUCCESS) + { + DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res); + destroy(this); + return NULL; + } + + res = FwpmProviderAdd0(this->handle, &this->provider, NULL); + if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS) + { + DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res); + destroy(this); + return NULL; + } + + if (!register_events(this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h new file mode 100644 index 000000000..d61c230e4 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup kernel_wfp_ipsec kernel_wfp_ipsec + * @{ @ingroup kernel_wfp + */ + +#ifndef KERNEL_WFP_IPSEC_H_ +#define KERNEL_WFP_IPSEC_H_ + +#include <library.h> +#include <kernel/kernel_ipsec.h> + +typedef struct kernel_wfp_ipsec_t kernel_wfp_ipsec_t; + +/** + * Windows Filter Platform based IPsec kernel backend. + */ +struct kernel_wfp_ipsec_t { + + /** + * Implements kernel_ipsec_t interface + */ + kernel_ipsec_t interface; +}; + +/** + * Create WFP kernel interface instance. + * + * @return kernel_wfp_ipsec_t instance + */ +kernel_wfp_ipsec_t *kernel_wfp_ipsec_create(); + +#endif /** KERNEL_WFP_IPSEC_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c new file mode 100644 index 000000000..e465b0a76 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "kernel_wfp_plugin.h" +#include "kernel_wfp_ipsec.h" + +#include <daemon.h> + +typedef struct private_kernel_wfp_plugin_t private_kernel_wfp_plugin_t; + +/** + * Private data of kernel-wfp plugin + */ +struct private_kernel_wfp_plugin_t { + + /** + * Implements plugin interface + */ + kernel_wfp_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_kernel_wfp_plugin_t *this) +{ + return "kernel-wfp"; +} + +METHOD(plugin_t, get_features, int, + private_kernel_wfp_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK(kernel_ipsec_register, kernel_wfp_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + PLUGIN_DEPENDS(RNG, RNG_WEAK), + PLUGIN_DEPENDS(RNG, RNG_STRONG), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_kernel_wfp_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *kernel_wfp_plugin_create() +{ + private_kernel_wfp_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h new file mode 100644 index 000000000..a538e34a1 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/kernel_wfp_plugin.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup kernel_wfp kernel_wfp + * @ingroup cplugins + * + * @defgroup kernel_wfp_plugin kernel_wfp_plugin + * @{ @ingroup kernel_wfp + */ + +#ifndef KERNEL_WFP_PLUGIN_H_ +#define KERNEL_WFP_PLUGIN_H_ + +#include <library.h> +#include <plugins/plugin.h> + +typedef struct kernel_wfp_plugin_t kernel_wfp_plugin_t; + +/** + * Windows Filter Platform based IPsec backend plugin. + */ +struct kernel_wfp_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** KERNEL_WFP_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff b/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff new file mode 100644 index 000000000..c72b94c07 --- /dev/null +++ b/src/libcharon/plugins/kernel_wfp/mingw-w64-4.8.1.diff @@ -0,0 +1,26 @@ +diff -Naur /mingw-orig/x86_64-w64-mingw32/include/fwptypes.h /mingw/x86_64-w64-mingw32/include/fwptypes.h +--- /mingw-orig/x86_64-w64-mingw32/include/fwptypes.h 2013-08-30 07:15:40 +0200 ++++ /mingw/x86_64-w64-mingw32/include/fwptypes.h 2014-01-02 16:32:26 +0100 +@@ -333,11 +333,6 @@ + } __C89_NAMELESSUNIONNAME; + } FWP_CONDITION_VALUE0; + +-typedef struct FWPM_DISPLAY_DATA0_ { +- wchar_t *name; +- wchar_t *description; +-} FWPM_DISPLAY_DATA0; +- + #endif /* WINAPI_PARTITION_DESKTOP. */ + /* Begin additional prototypes for all interfaces */ + +diff -Naur /mingw-orig/x86_64-w64-mingw32/include/iketypes.h /mingw/x86_64-w64-mingw32/include/iketypes.h +--- /mingw-orig/x86_64-w64-mingw32/include/iketypes.h 2013-08-30 07:15:40 +0200 ++++ /mingw/x86_64-w64-mingw32/include/iketypes.h 2014-01-02 16:31:12 +0100 +@@ -212,7 +212,6 @@ + FWP_BYTE_BLOB presharedKey; + UINT32 flags; + } IKEEXT_PRESHARED_KEY_AUTHENTICATION1; +-#endif + + typedef struct IKEEXT_CERTIFICATE_AUTHENTICATION0_ { + IKEEXT_CERT_CONFIG_TYPE inboundConfigType; |