diff options
Diffstat (limited to 'src/libipsec')
-rw-r--r-- | src/libipsec/Android.mk | 38 | ||||
-rw-r--r-- | src/libipsec/Makefile.am | 31 | ||||
-rw-r--r-- | src/libipsec/Makefile.in | 780 | ||||
-rw-r--r-- | src/libipsec/esp_context.c | 301 | ||||
-rw-r--r-- | src/libipsec/esp_context.h | 110 | ||||
-rw-r--r-- | src/libipsec/esp_packet.c | 468 | ||||
-rw-r--r-- | src/libipsec/esp_packet.h | 151 | ||||
-rw-r--r-- | src/libipsec/ip_packet.c | 193 | ||||
-rw-r--r-- | src/libipsec/ip_packet.h | 96 | ||||
-rw-r--r-- | src/libipsec/ipsec.c | 77 | ||||
-rw-r--r-- | src/libipsec/ipsec.h | 83 | ||||
-rw-r--r-- | src/libipsec/ipsec_event_listener.h | 48 | ||||
-rw-r--r-- | src/libipsec/ipsec_event_relay.c | 193 | ||||
-rw-r--r-- | src/libipsec/ipsec_event_relay.h | 79 | ||||
-rw-r--r-- | src/libipsec/ipsec_policy.c | 212 | ||||
-rw-r--r-- | src/libipsec/ipsec_policy.h | 140 | ||||
-rw-r--r-- | src/libipsec/ipsec_policy_mgr.c | 286 | ||||
-rw-r--r-- | src/libipsec/ipsec_policy_mgr.h | 119 | ||||
-rw-r--r-- | src/libipsec/ipsec_processor.c | 324 | ||||
-rw-r--r-- | src/libipsec/ipsec_processor.h | 115 | ||||
-rw-r--r-- | src/libipsec/ipsec_sa.c | 234 | ||||
-rw-r--r-- | src/libipsec/ipsec_sa.h | 169 | ||||
-rw-r--r-- | src/libipsec/ipsec_sa_mgr.c | 626 | ||||
-rw-r--r-- | src/libipsec/ipsec_sa_mgr.h | 167 |
24 files changed, 5040 insertions, 0 deletions
diff --git a/src/libipsec/Android.mk b/src/libipsec/Android.mk new file mode 100644 index 000000000..81f4632ef --- /dev/null +++ b/src/libipsec/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# copy-n-paste from Makefile.am +LOCAL_SRC_FILES := \ +ipsec.c ipsec.h \ +esp_context.c esp_context.h \ +esp_packet.c esp_packet.h \ +ip_packet.c ip_packet.h \ +ipsec_event_listener.h \ +ipsec_event_relay.c ipsec_event_relay.h \ +ipsec_policy.c ipsec_policy.h \ +ipsec_policy_mgr.c ipsec_policy_mgr.h \ +ipsec_processor.c ipsec_processor.h \ +ipsec_sa.c ipsec_sa.h \ +ipsec_sa_mgr.c ipsec_sa_mgr.h + +# build libipsec --------------------------------------------------------------- + +LOCAL_C_INCLUDES += \ + $(libvstr_PATH) \ + $(strongswan_PATH)/src/include \ + $(strongswan_PATH)/src/libstrongswan + +LOCAL_CFLAGS := $(strongswan_CFLAGS) + +LOCAL_MODULE := libipsec + +LOCAL_MODULE_TAGS := optional + +LOCAL_ARM_MODE := arm + +LOCAL_PRELINK_MODULE := false + +LOCAL_SHARED_LIBRARIES += libstrongswan + +include $(BUILD_SHARED_LIBRARY) + diff --git a/src/libipsec/Makefile.am b/src/libipsec/Makefile.am new file mode 100644 index 000000000..35b8d7916 --- /dev/null +++ b/src/libipsec/Makefile.am @@ -0,0 +1,31 @@ +ipseclib_LTLIBRARIES = libipsec.la + +libipsec_la_SOURCES = \ +ipsec.c ipsec.h \ +esp_context.c esp_context.h \ +esp_packet.c esp_packet.h \ +ip_packet.c ip_packet.h \ +ipsec_event_listener.h \ +ipsec_event_relay.c ipsec_event_relay.h \ +ipsec_policy.c ipsec_policy.h \ +ipsec_policy_mgr.c ipsec_policy_mgr.h \ +ipsec_processor.c ipsec_processor.h \ +ipsec_sa.c ipsec_sa.h \ +ipsec_sa_mgr.c ipsec_sa_mgr.h + +libipsec_la_LIBADD = + +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan + +EXTRA_DIST = Android.mk + +# build optional plugins +######################## + +if MONOLITHIC +SUBDIRS = +else +SUBDIRS = . +endif + diff --git a/src/libipsec/Makefile.in b/src/libipsec/Makefile.in new file mode 100644 index 000000000..6d984d8ab --- /dev/null +++ b/src/libipsec/Makefile.in @@ -0,0 +1,780 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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@ +subdir = src/libipsec +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +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/with.m4 \ + $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/m4/macros/add-plugin.m4 \ + $(top_srcdir)/configure.in +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__installdirs = "$(DESTDIR)$(ipseclibdir)" +LTLIBRARIES = $(ipseclib_LTLIBRARIES) +libipsec_la_DEPENDENCIES = +am_libipsec_la_OBJECTS = ipsec.lo esp_context.lo esp_packet.lo \ + ip_packet.lo ipsec_event_relay.lo ipsec_policy.lo \ + ipsec_policy_mgr.lo ipsec_processor.lo ipsec_sa.lo \ + ipsec_sa_mgr.lo +libipsec_la_OBJECTS = $(am_libipsec_la_OBJECTS) +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) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libipsec_la_SOURCES) +DIST_SOURCES = $(libipsec_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = . +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BFDLIB = @BFDLIB@ +BTLIB = @BTLIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLIB = @DLLIB@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +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@ +MKDIR_P = @MKDIR_P@ +MYSQLCFLAG = @MYSQLCFLAG@ +MYSQLCONFIG = @MYSQLCONFIG@ +MYSQLLIB = @MYSQLLIB@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +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@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PTHREADLIB = @PTHREADLIB@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYINCLUDE = @RUBYINCLUDE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKLIB = @SOCKLIB@ +STRIP = @STRIP@ +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_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +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@ +axis2c_CFLAGS = @axis2c_CFLAGS@ +axis2c_LIBS = @axis2c_LIBS@ +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@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dbusservicedir = @dbusservicedir@ +dev_headers = @dev_headers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +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@ +lt_ECHO = @lt_ECHO@ +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@ +openac_plugins = @openac_plugins@ +p_plugins = @p_plugins@ +pcsclite_CFLAGS = @pcsclite_CFLAGS@ +pcsclite_LIBS = @pcsclite_LIBS@ +pdfdir = @pdfdir@ +piddir = @piddir@ +pki_plugins = @pki_plugins@ +plugindir = @plugindir@ +pool_plugins = @pool_plugins@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +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@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +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@ +ipseclib_LTLIBRARIES = libipsec.la +libipsec_la_SOURCES = \ +ipsec.c ipsec.h \ +esp_context.c esp_context.h \ +esp_packet.c esp_packet.h \ +ip_packet.c ip_packet.h \ +ipsec_event_listener.h \ +ipsec_event_relay.c ipsec_event_relay.h \ +ipsec_policy.c ipsec_policy.h \ +ipsec_policy_mgr.c ipsec_policy_mgr.h \ +ipsec_processor.c ipsec_processor.h \ +ipsec_sa.c ipsec_sa.h \ +ipsec_sa_mgr.c ipsec_sa_mgr.h + +libipsec_la_LIBADD = +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan + +EXTRA_DIST = Android.mk +@MONOLITHIC_FALSE@SUBDIRS = . + +# build optional plugins +######################## +@MONOLITHIC_TRUE@SUBDIRS = +all: all-recursive + +.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/libipsec/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libipsec/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): +install-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(ipseclibdir)" || $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(ipseclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(ipseclibdir)"; \ + } + +uninstall-ipseclibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(ipseclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(ipseclibdir)/$$f"; \ + done + +clean-ipseclibLTLIBRARIES: + -test -z "$(ipseclib_LTLIBRARIES)" || rm -f $(ipseclib_LTLIBRARIES) + @list='$(ipseclib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libipsec.la: $(libipsec_la_OBJECTS) $(libipsec_la_DEPENDENCIES) + $(LINK) -rpath $(ipseclibdir) $(libipsec_la_OBJECTS) $(libipsec_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esp_context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esp_packet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_packet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_event_relay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_policy_mgr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_processor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_sa.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_sa_mgr.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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 +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(ipseclibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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-recursive + +clean-am: clean-generic clean-ipseclibLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-ipseclibLTLIBRARIES + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-ipseclibLTLIBRARIES + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ + install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic \ + clean-ipseclibLTLIBRARIES clean-libtool ctags ctags-recursive \ + 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-ipseclibLTLIBRARIES install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-ipseclibLTLIBRARIES + + +# 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/libipsec/esp_context.c b/src/libipsec/esp_context.c new file mode 100644 index 000000000..dc3ad3f8b --- /dev/null +++ b/src/libipsec/esp_context.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 <limits.h> +#include <stdint.h> + +#include "esp_context.h" + +#include <library.h> +#include <debug.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + +/** + * Should be a multiple of 8 + */ +#define ESP_DEFAULT_WINDOW_SIZE 128 + +typedef struct private_esp_context_t private_esp_context_t; + +/** + * Private additions to esp_context_t. + */ +struct private_esp_context_t { + + /** + * Public members + */ + esp_context_t public; + + /** + * Crypter used to encrypt/decrypt ESP packets + */ + crypter_t *crypter; + + /** + * Signer to authenticate ESP packets + */ + signer_t *signer; + + /** + * The highest sequence number that was successfully verified + * and authenticated, or assigned in an outbound context + */ + u_int32_t last_seqno; + + /** + * The bit in the window of the highest authenticated sequence number + */ + u_int seqno_index; + + /** + * The size of the anti-replay window (in bits) + */ + u_int window_size; + + /** + * The anti-replay window buffer + */ + chunk_t window; + + /** + * TRUE in case of an inbound ESP context + */ + bool inbound; +}; + +/** + * Set or unset a bit in the window. + */ +static inline void set_window_bit(private_esp_context_t *this, + u_int index, bool set) +{ + u_int i = index / CHAR_BIT; + + if (set) + { + this->window.ptr[i] |= 1 << (index % CHAR_BIT); + } + else + { + this->window.ptr[i] &= ~(1 << (index % CHAR_BIT)); + } +} + +/** + * Get a bit from the window. + */ +static inline bool get_window_bit(private_esp_context_t *this, u_int index) +{ + u_int i = index / CHAR_BIT; + + return this->window.ptr[i] & (1 << index % CHAR_BIT); +} + +/** + * Returns TRUE if the supplied seqno is not already marked in the window + */ +static bool check_window(private_esp_context_t *this, u_int32_t seqno) +{ + u_int offset; + + offset = this->last_seqno - seqno; + offset = (this->seqno_index - offset) % this->window_size; + return !get_window_bit(this, offset); +} + +METHOD(esp_context_t, verify_seqno, bool, + private_esp_context_t *this, u_int32_t seqno) +{ + if (!this->inbound) + { + return FALSE; + } + + if (seqno > this->last_seqno) + { /* |----------------------------------------| + * <---------^ ^ or <---------^ ^ + * WIN H S WIN H S + */ + return TRUE; + } + else if (seqno > 0 && this->window_size > this->last_seqno - seqno) + { /* |----------------------------------------| + * <---------^ or <---------^ + * WIN ^ H WIN ^ H + * S S + */ + return check_window(this, seqno); + } + else + { /* |----------------------------------------| + * ^ <---------^ + * S WIN H + */ + return FALSE; + } +} + +METHOD(esp_context_t, set_authenticated_seqno, void, + private_esp_context_t *this, u_int32_t seqno) +{ + u_int i, shift; + + if (!this->inbound) + { + return; + } + + if (seqno > this->last_seqno) + { /* shift the window to the new highest authenticated seqno */ + shift = seqno - this->last_seqno; + shift = shift < this->window_size ? shift : this->window_size; + for (i = 0; i < shift; ++i) + { + this->seqno_index = (this->seqno_index + 1) % this->window_size; + set_window_bit(this, this->seqno_index, FALSE); + } + set_window_bit(this, this->seqno_index, TRUE); + this->last_seqno = seqno; + } + else + { /* seqno is inside the window, set the corresponding window bit */ + i = this->last_seqno - seqno; + set_window_bit(this, (this->seqno_index - i) % this->window_size, TRUE); + } +} + +METHOD(esp_context_t, get_seqno, u_int32_t, + private_esp_context_t *this) +{ + return this->last_seqno; +} + +METHOD(esp_context_t, next_seqno, bool, + private_esp_context_t *this, u_int32_t *seqno) +{ + if (this->inbound || this->last_seqno == UINT32_MAX) + { /* inbound or segno would cycle */ + return FALSE; + } + *seqno = ++this->last_seqno; + return TRUE; +} + +METHOD(esp_context_t, get_signer, signer_t *, + private_esp_context_t *this) +{ + return this->signer; +} + +METHOD(esp_context_t, get_crypter, crypter_t *, + private_esp_context_t *this) +{ + return this->crypter; +} + +METHOD(esp_context_t, destroy, void, + private_esp_context_t *this) +{ + chunk_free(&this->window); + DESTROY_IF(this->crypter); + DESTROY_IF(this->signer); + free(this); +} + +/** + * Described in header. + */ +esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key, + int int_alg, chunk_t int_key, bool inbound) +{ + private_esp_context_t *this; + + INIT(this, + .public = { + .get_crypter = _get_crypter, + .get_signer = _get_signer, + .get_seqno = _get_seqno, + .next_seqno = _next_seqno, + .verify_seqno = _verify_seqno, + .set_authenticated_seqno = _set_authenticated_seqno, + .destroy = _destroy, + }, + .inbound = inbound, + .window_size = ESP_DEFAULT_WINDOW_SIZE, + ); + + switch(enc_alg) + { + case ENCR_AES_CBC: + this->crypter = lib->crypto->create_crypter(lib->crypto, enc_alg, + enc_key.len); + break; + default: + break; + } + if (!this->crypter) + { + DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption " + "algorithm"); + destroy(this); + return NULL; + } + if (!this->crypter->set_key(this->crypter, enc_key)) + { + DBG1(DBG_ESP, "failed to create ESP context: setting encryption key " + "failed"); + destroy(this); + return NULL; + } + + switch(int_alg) + { + case AUTH_HMAC_SHA1_96: + case AUTH_HMAC_SHA2_256_128: + case AUTH_HMAC_SHA2_384_192: + case AUTH_HMAC_SHA2_512_256: + this->signer = lib->crypto->create_signer(lib->crypto, int_alg); + break; + default: + break; + } + if (!this->signer) + { + DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity " + "algorithm"); + destroy(this); + return NULL; + } + if (!this->signer->set_key(this->signer, int_key)) + { + DBG1(DBG_ESP, "failed to create ESP context: setting signature key " + "failed"); + destroy(this); + return NULL; + } + + if (inbound) + { + this->window = chunk_alloc(this->window_size / CHAR_BIT + 1); + memset(this->window.ptr, 0, this->window.len); + } + return &this->public; +} + + diff --git a/src/libipsec/esp_context.h b/src/libipsec/esp_context.h new file mode 100644 index 000000000..db247dced --- /dev/null +++ b/src/libipsec/esp_context.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 esp_context esp_context + * @{ @ingroup libipsec + */ + +#ifndef ESP_CONTEXT_H_ +#define ESP_CONTEXT_H_ + +#include <library.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + +typedef struct esp_context_t esp_context_t; + +/** + * ESP context, handles sequence numbers and maintains cryptographic primitives + */ +struct esp_context_t { + + /** + * Get the crypter. + * + * @return crypter + */ + crypter_t *(*get_crypter)(esp_context_t *this); + + /** + * Get the signer. + * + * @return signer + */ + signer_t *(*get_signer)(esp_context_t *this); + + /** + * Get the current outbound ESP sequence number or the highest authenticated + * inbound sequence number. + * + * @return current sequence number, in host byte order + */ + u_int32_t (*get_seqno)(esp_context_t *this); + + /** + * Allocate the next outbound ESP sequence number. + * + * @param seqno the sequence number, in host byte order + * @return FALSE if the sequence number cycled or inbound context + */ + bool (*next_seqno)(esp_context_t *this, u_int32_t *seqno); + + /** + * Verify an ESP sequence number. Checks whether a packet with this + * sequence number was already received, using the anti-replay window. + * This operation does not modify the internal state. After the sequence + * number is successfully verified and the ESP packet is authenticated, + * set_authenticated_seqno() should be called. + * + * @param seqno the sequence number to verify, in host byte order + * @return TRUE when sequence number is valid + */ + bool (*verify_seqno)(esp_context_t *this, u_int32_t seqno); + + /** + * Adds a sequence number that was successfully verified and + * authenticated. A user MUST call verify_seqno() immediately before + * calling this method. + * + * @param seqno verified and authenticated seq number in host byte order + */ + void (*set_authenticated_seqno)(esp_context_t *this, + u_int32_t seqno); + + /** + * Destroy an esp_context_t + */ + void (*destroy)(esp_context_t *this); + +}; + +/** + * Create an esp_context_t instance + * + * @param enc_alg encryption algorithm + * @param enc_key encryption key + * @param int_alg integrity protection algorithm + * @param int_key integrity protection key + * @param inbound TRUE to create an inbound ESP context + * @return ESP context instance, or NULL if creation fails + */ +esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key, int int_alg, + chunk_t int_key, bool inbound); + +#endif /** ESP_CONTEXT_H_ @}*/ + diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c new file mode 100644 index 000000000..bfcab95eb --- /dev/null +++ b/src/libipsec/esp_packet.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "esp_packet.h" + +#include <library.h> +#include <debug.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +#include <netinet/in.h> + +typedef struct private_esp_packet_t private_esp_packet_t; + +/** + * Private additions to esp_packet_t. + */ +struct private_esp_packet_t { + + /** + * Public members + */ + esp_packet_t public; + + /** + * Raw ESP packet + */ + packet_t *packet; + + /** + * Payload of this packet + */ + ip_packet_t *payload; + + /** + * Next Header info (e.g. IPPROTO_IPIP) + */ + u_int8_t next_header; + +}; + +/** + * Forward declaration for clone() + */ +static private_esp_packet_t *esp_packet_create_internal(packet_t *packet); + +METHOD(packet_t, set_source, void, + private_esp_packet_t *this, host_t *src) +{ + return this->packet->set_source(this->packet, src); +} + +METHOD2(esp_packet_t, packet_t, get_source, host_t*, + private_esp_packet_t *this) +{ + return this->packet->get_source(this->packet); +} + +METHOD(packet_t, set_destination, void, + private_esp_packet_t *this, host_t *dst) +{ + return this->packet->set_destination(this->packet, dst); +} + +METHOD2(esp_packet_t, packet_t, get_destination, host_t*, + private_esp_packet_t *this) +{ + return this->packet->get_destination(this->packet); +} + +METHOD(packet_t, get_data, chunk_t, + private_esp_packet_t *this) +{ + return this->packet->get_data(this->packet); +} + +METHOD(packet_t, set_data, void, + private_esp_packet_t *this, chunk_t data) +{ + return this->packet->set_data(this->packet, data); +} + +METHOD(packet_t, skip_bytes, void, + private_esp_packet_t *this, size_t bytes) +{ + return this->packet->skip_bytes(this->packet, bytes); +} + +METHOD(packet_t, clone, packet_t*, + private_esp_packet_t *this) +{ + private_esp_packet_t *pkt; + + pkt = esp_packet_create_internal(this->packet->clone(this->packet)); + pkt->payload = this->payload ? this->payload->clone(this->payload) : NULL; + pkt->next_header = this->next_header; + return &pkt->public.packet; +} + +METHOD(esp_packet_t, parse_header, bool, + private_esp_packet_t *this, u_int32_t *spi) +{ + bio_reader_t *reader; + u_int32_t seq; + + reader = bio_reader_create(this->packet->get_data(this->packet)); + if (!reader->read_uint32(reader, spi) || + !reader->read_uint32(reader, &seq)) + { + DBG1(DBG_ESP, "failed to parse ESP header: invalid length"); + reader->destroy(reader); + return FALSE; + } + reader->destroy(reader); + + DBG2(DBG_ESP, "parsed ESP header with SPI %.8x [seq %u]", *spi, seq); + *spi = htonl(*spi); + return TRUE; +} + +/** + * Check padding as specified in RFC 4303 + */ +static bool check_padding(chunk_t padding) +{ + size_t i; + + for (i = 0; i < padding.len; ++i) + { + if (padding.ptr[i] != (u_int8_t)(i + 1)) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Remove the padding from the payload and set the next header info + */ +static bool remove_padding(private_esp_packet_t *this, chunk_t plaintext) +{ + u_int8_t next_header, pad_length; + chunk_t padding, payload; + bio_reader_t *reader; + + reader = bio_reader_create(plaintext); + if (!reader->read_uint8_end(reader, &next_header) || + !reader->read_uint8_end(reader, &pad_length)) + { + DBG1(DBG_ESP, "parsing ESP payload failed: invalid length"); + goto failed; + } + if (!reader->read_data_end(reader, pad_length, &padding) || + !check_padding(padding)) + { + DBG1(DBG_ESP, "parsing ESP payload failed: invalid padding"); + goto failed; + } + this->payload = ip_packet_create(reader->peek(reader)); + reader->destroy(reader); + if (!this->payload) + { + DBG1(DBG_ESP, "parsing ESP payload failed: unsupported payload"); + return FALSE; + } + this->next_header = next_header; + payload = this->payload->get_encoding(this->payload); + + DBG3(DBG_ESP, "ESP payload:\n payload %B\n padding %B\n " + "padding length = %hhu, next header = %hhu", &payload, &padding, + pad_length, this->next_header); + return TRUE; + +failed: + reader->destroy(reader); + chunk_free(&plaintext); + return FALSE; +} + +METHOD(esp_packet_t, decrypt, status_t, + private_esp_packet_t *this, esp_context_t *esp_context) +{ + bio_reader_t *reader; + u_int32_t spi, seq; + chunk_t data, iv, icv, ciphertext, plaintext; + crypter_t *crypter; + signer_t *signer; + + DESTROY_IF(this->payload); + this->payload = NULL; + + data = this->packet->get_data(this->packet); + crypter = esp_context->get_crypter(esp_context); + signer = esp_context->get_signer(esp_context); + + reader = bio_reader_create(data); + if (!reader->read_uint32(reader, &spi) || + !reader->read_uint32(reader, &seq) || + !reader->read_data(reader, crypter->get_iv_size(crypter), &iv) || + !reader->read_data_end(reader, signer->get_block_size(signer), &icv) || + reader->remaining(reader) % crypter->get_block_size(crypter)) + { + DBG1(DBG_ESP, "ESP decryption failed: invalid length"); + return PARSE_ERROR; + } + ciphertext = reader->peek(reader); + reader->destroy(reader); + + if (!esp_context->verify_seqno(esp_context, seq)) + { + DBG1(DBG_ESP, "ESP sequence number verification failed:\n " + "src %H, dst %H, SPI %.8x [seq %u]", + get_source(this), get_destination(this), spi, seq); + return VERIFY_ERROR; + } + DBG3(DBG_ESP, "ESP decryption:\n SPI %.8x [seq %u]\n IV %B\n " + "encrypted %B\n ICV %B", spi, seq, &iv, &ciphertext, &icv); + + if (!signer->get_signature(signer, chunk_create(data.ptr, 8), NULL) || + !signer->get_signature(signer, iv, NULL) || + !signer->verify_signature(signer, ciphertext, icv)) + { + DBG1(DBG_ESP, "ICV verification failed!"); + return FAILED; + } + esp_context->set_authenticated_seqno(esp_context, seq); + + if (!crypter->decrypt(crypter, ciphertext, iv, &plaintext)) + { + DBG1(DBG_ESP, "ESP decryption failed"); + return FAILED; + } + + if (!remove_padding(this, plaintext)) + { + return PARSE_ERROR; + } + return SUCCESS; +} + +/** + * Generate the padding as specified in RFC4303 + */ +static void generate_padding(chunk_t padding) +{ + size_t i; + + for (i = 0; i < padding.len; ++i) + { + padding.ptr[i] = (u_int8_t)(i + 1); + } +} + +METHOD(esp_packet_t, encrypt, status_t, + private_esp_packet_t *this, esp_context_t *esp_context, u_int32_t spi) +{ + chunk_t iv, icv, padding, payload, ciphertext, auth_data; + bio_writer_t *writer; + u_int32_t next_seqno; + size_t blocksize, plainlen; + crypter_t *crypter; + signer_t *signer; + rng_t *rng; + + this->packet->set_data(this->packet, chunk_empty); + + if (!esp_context->next_seqno(esp_context, &next_seqno)) + { + DBG1(DBG_ESP, "ESP encapsulation failed: sequence numbers cycled"); + return FAILED; + } + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_ESP, "ESP encryption failed: could not find RNG"); + return NOT_FOUND; + } + crypter = esp_context->get_crypter(esp_context); + signer = esp_context->get_signer(esp_context); + + blocksize = crypter->get_block_size(crypter); + iv.len = crypter->get_iv_size(crypter); + icv.len = signer->get_block_size(signer); + + /* plaintext = payload, padding, pad_length, next_header */ + payload = this->payload ? this->payload->get_encoding(this->payload) + : chunk_empty; + plainlen = payload.len + 2; + padding.len = blocksize - (plainlen % blocksize); + plainlen += padding.len; + + /* len = spi, seq, IV, plaintext, ICV */ + writer = bio_writer_create(2 * sizeof(u_int32_t) + iv.len + plainlen + + icv.len); + writer->write_uint32(writer, ntohl(spi)); + writer->write_uint32(writer, next_seqno); + + iv = writer->skip(writer, iv.len); + if (!rng->get_bytes(rng, iv.len, iv.ptr)) + { + DBG1(DBG_ESP, "ESP encryption failed: could not generate IV"); + writer->destroy(writer); + rng->destroy(rng); + return FAILED; + } + rng->destroy(rng); + + /* plain-/ciphertext will start here */ + ciphertext = writer->get_buf(writer); + ciphertext.ptr += ciphertext.len; + ciphertext.len = plainlen; + + writer->write_data(writer, payload); + + padding = writer->skip(writer, padding.len); + generate_padding(padding); + + writer->write_uint8(writer, padding.len); + writer->write_uint8(writer, this->next_header); + + DBG3(DBG_ESP, "ESP before encryption:\n payload = %B\n padding = %B\n " + "padding length = %hhu, next header = %hhu", &payload, &padding, + (u_int8_t)padding.len, this->next_header); + + /* encrypt the content inline */ + if (!crypter->encrypt(crypter, ciphertext, iv, NULL)) + { + DBG1(DBG_ESP, "ESP encryption failed"); + writer->destroy(writer); + return FAILED; + } + + /* calculate signature */ + auth_data = writer->get_buf(writer); + icv = writer->skip(writer, icv.len); + if (!signer->get_signature(signer, auth_data, icv.ptr)) + { + DBG1(DBG_ESP, "ESP encryption failed: signature generation failed"); + writer->destroy(writer); + return FAILED; + } + + DBG3(DBG_ESP, "ESP packet:\n SPI %.8x [seq %u]\n IV %B\n " + "encrypted %B\n ICV %B", ntohl(spi), next_seqno, &iv, + &ciphertext, &icv); + + this->packet->set_data(this->packet, writer->extract_buf(writer)); + writer->destroy(writer); + return SUCCESS; +} + +METHOD(esp_packet_t, get_next_header, u_int8_t, + private_esp_packet_t *this) +{ + return this->next_header; +} + +METHOD(esp_packet_t, get_payload, ip_packet_t*, + private_esp_packet_t *this) +{ + return this->payload; +} + +METHOD(esp_packet_t, extract_payload, ip_packet_t*, + private_esp_packet_t *this) +{ + ip_packet_t *payload; + + payload = this->payload; + this->payload = NULL; + return payload; +} + +METHOD2(esp_packet_t, packet_t, destroy, void, + private_esp_packet_t *this) +{ + DESTROY_IF(this->payload); + this->packet->destroy(this->packet); + free(this); +} + +static private_esp_packet_t *esp_packet_create_internal(packet_t *packet) +{ + private_esp_packet_t *this; + + INIT(this, + .public = { + .packet = { + .set_source = _set_source, + .get_source = _get_source, + .set_destination = _set_destination, + .get_destination = _get_destination, + .get_data = _get_data, + .set_data = _set_data, + .skip_bytes = _skip_bytes, + .clone = _clone, + .destroy = _destroy, + }, + .get_source = _get_source, + .get_destination = _get_destination, + .get_next_header = _get_next_header, + .parse_header = _parse_header, + .decrypt = _decrypt, + .encrypt = _encrypt, + .get_payload = _get_payload, + .extract_payload = _extract_payload, + .destroy = _destroy, + }, + .packet = packet, + .next_header = IPPROTO_NONE, + ); + return this; +} + +/** + * Described in header. + */ +esp_packet_t *esp_packet_create_from_packet(packet_t *packet) +{ + private_esp_packet_t *this; + + this = esp_packet_create_internal(packet); + + return &this->public; +} + +/** + * Described in header. + */ +esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst, + ip_packet_t *payload) +{ + private_esp_packet_t *this; + packet_t *packet; + + packet = packet_create_from_data(src, dst, chunk_empty); + this = esp_packet_create_internal(packet); + this->payload = payload; + if (payload) + { + this->next_header = payload->get_version(payload) == 4 ? IPPROTO_IPIP + : IPPROTO_IPV6; + } + else + { + this->next_header = IPPROTO_NONE; + } + return &this->public; +} diff --git a/src/libipsec/esp_packet.h b/src/libipsec/esp_packet.h new file mode 100644 index 000000000..a1d1602c1 --- /dev/null +++ b/src/libipsec/esp_packet.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 esp_packet esp_packet + * @{ @ingroup libipsec + */ + +#ifndef ESP_PACKET_H_ +#define ESP_PACKET_H_ + +#include "ip_packet.h" +#include "esp_context.h" + +#include <library.h> +#include <utils/host.h> +#include <utils/packet.h> + +typedef struct esp_packet_t esp_packet_t; + +/** + * ESP packet + */ +struct esp_packet_t { + + /** + * Implements packet_t interface to access the raw ESP packet + */ + packet_t packet; + + /** + * Get the source address of this packet + * + * @return source host + */ + host_t *(*get_source)(esp_packet_t *this); + + /** + * Get the destination address of this packet + * + * @return destination host + */ + host_t *(*get_destination)(esp_packet_t *this); + + /** + * Parse the packet header before decryption. Tries to read the SPI + * from the packet to find a corresponding SA. + * + * @param spi parsed SPI, in network byte order + * @return TRUE when successful, FALSE otherwise (e.g. when the + * length of the packet is invalid) + */ + bool (*parse_header)(esp_packet_t *this, u_int32_t *spi); + + /** + * Authenticate and decrypt the packet. Also verifies the sequence number + * using the supplied ESP context and updates the anti-replay window. + * + * @param esp_context ESP context of corresponding inbound IPsec SA + * @return - SUCCESS if successfully authenticated, + * decrypted and parsed + * - PARSE_ERROR if the length of the packet or the + * padding is invalid + * - VERIFY_ERROR if the sequence number + * verification failed + * - FAILED if the ICV (MAC) check or the actual + * decryption failed + */ + status_t (*decrypt)(esp_packet_t *this, esp_context_t *esp_context); + + /** + * Encapsulate and encrypt the packet. The sequence number will be generated + * using the supplied ESP context. + * + * @param esp_context ESP context of corresponding outbound IPsec SA + * @param spi SPI value to use, in network byte order + * @return - SUCCESS if encrypted + * - FAILED if sequence number cycled or any of the + * cryptographic functions failed + * - NOT_FOUND if no suitable RNG could be found + */ + status_t (*encrypt)(esp_packet_t *this, esp_context_t *esp_context, + u_int32_t spi); + + /** + * Get the next header field of a packet. + * + * @note Packet has to be in the decrypted state. + * + * @return next header field + */ + u_int8_t (*get_next_header)(esp_packet_t *this); + + /** + * Get the plaintext payload of this packet. + * + * @return plaintext payload (internal data), + * NULL if not decrypted + */ + ip_packet_t *(*get_payload)(esp_packet_t *this); + + /** + * Extract the plaintext payload from this packet. + * + * @return plaintext payload (has to be destroyed), + * NULL if not decrypted + */ + ip_packet_t *(*extract_payload)(esp_packet_t *this); + + /** + * Destroy an esp_packet_t + */ + void (*destroy)(esp_packet_t *this); + +}; + +/** + * Create an ESP packet out of data from the wire. + * + * @param packet the packet data as received, gets owned + * @return esp_packet_t instance + */ +esp_packet_t *esp_packet_create_from_packet(packet_t *packet); + +/** + * Create an ESP packet from a plaintext payload + * + * @param src source address + * @param dst destination address + * @param payload plaintext payload, gets owned + * @return esp_packet_t instance + */ +esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst, + ip_packet_t *payload); + +#endif /** ESP_PACKET_H_ @}*/ + diff --git a/src/libipsec/ip_packet.c b/src/libipsec/ip_packet.c new file mode 100644 index 000000000..096ca33a8 --- /dev/null +++ b/src/libipsec/ip_packet.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "ip_packet.h" + +#include <library.h> +#include <debug.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#ifdef HAVE_NETINET_IP6_H +#include <netinet/ip6.h> +#endif + +typedef struct private_ip_packet_t private_ip_packet_t; + +/** + * Private additions to ip_packet_t. + */ +struct private_ip_packet_t { + + /** + * Public members + */ + ip_packet_t public; + + /** + * Source address + */ + host_t *src; + + /** + * Destination address + */ + host_t *dst; + + /** + * IP packet + */ + chunk_t packet; + + /** + * IP version + */ + u_int8_t version; + + /** + * Protocol|Next Header field + */ + u_int8_t next_header; + +}; + +METHOD(ip_packet_t, get_version, u_int8_t, + private_ip_packet_t *this) +{ + return this->version; +} + +METHOD(ip_packet_t, get_source, host_t*, + private_ip_packet_t *this) +{ + return this->src; +} + +METHOD(ip_packet_t, get_destination, host_t*, + private_ip_packet_t *this) +{ + return this->dst; +} + +METHOD(ip_packet_t, get_encoding, chunk_t, + private_ip_packet_t *this) +{ + return this->packet; +} + +METHOD(ip_packet_t, get_next_header, u_int8_t, + private_ip_packet_t *this) +{ + return this->next_header; +} + +METHOD(ip_packet_t, clone, ip_packet_t*, + private_ip_packet_t *this) +{ + return ip_packet_create(this->packet); +} + +METHOD(ip_packet_t, destroy, void, + private_ip_packet_t *this) +{ + this->src->destroy(this->src); + this->dst->destroy(this->dst); + chunk_free(&this->packet); + free(this); +} + +/** + * Described in header. + */ +ip_packet_t *ip_packet_create(chunk_t packet) +{ + private_ip_packet_t *this; + u_int8_t version, next_header; + host_t *src, *dst; + + if (packet.len < 1) + { + DBG1(DBG_ESP, "IP packet too short"); + goto failed; + } + + version = (packet.ptr[0] & 0xf0) >> 4; + + switch (version) + { + case 4: + { + struct ip *ip; + + if (packet.len < sizeof(struct ip)) + { + DBG1(DBG_ESP, "IPv4 packet too short"); + goto failed; + } + ip = (struct ip*)packet.ptr; + src = host_create_from_chunk(AF_INET, + chunk_from_thing(ip->ip_src), 0); + dst = host_create_from_chunk(AF_INET, + chunk_from_thing(ip->ip_dst), 0); + next_header = ip->ip_p; + break; + } +#ifdef HAVE_NETINET_IP6_H + case 6: + { + struct ip6_hdr *ip; + + if (packet.len < sizeof(struct ip6_hdr)) + { + DBG1(DBG_ESP, "IPv6 packet too short"); + goto failed; + } + ip = (struct ip6_hdr*)packet.ptr; + src = host_create_from_chunk(AF_INET6, + chunk_from_thing(ip->ip6_src), 0); + dst = host_create_from_chunk(AF_INET6, + chunk_from_thing(ip->ip6_dst), 0); + next_header = ip->ip6_nxt; + break; + } +#endif /* HAVE_NETINET_IP6_H */ + default: + DBG1(DBG_ESP, "unsupported IP version"); + goto failed; + } + + INIT(this, + .public = { + .get_version = _get_version, + .get_source = _get_source, + .get_destination = _get_destination, + .get_next_header = _get_next_header, + .get_encoding = _get_encoding, + .clone = _clone, + .destroy = _destroy, + }, + .src = src, + .dst = dst, + .packet = packet, + .version = version, + .next_header = next_header, + ); + return &this->public; + +failed: + chunk_free(&packet); + return NULL; +} diff --git a/src/libipsec/ip_packet.h b/src/libipsec/ip_packet.h new file mode 100644 index 000000000..b4fc298ff --- /dev/null +++ b/src/libipsec/ip_packet.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 ip_packet ip_packet + * @{ @ingroup libipsec + */ + +#ifndef IP_PACKET_H_ +#define IP_PACKET_H_ + +#include <library.h> +#include <utils/host.h> +#include <utils/packet.h> + +typedef struct ip_packet_t ip_packet_t; + +/** + * IP packet + */ +struct ip_packet_t { + + /** + * IP version of this packet + * + * @return ip version + */ + u_int8_t (*get_version)(ip_packet_t *this); + + /** + * Get the source address of this packet + * + * @return source host + */ + host_t *(*get_source)(ip_packet_t *this); + + /** + * Get the destination address of this packet + * + * @return destination host + */ + host_t *(*get_destination)(ip_packet_t *this); + + /** + * Get the protocol (IPv4) or next header (IPv6) field of this packet. + * + * @return protocol|next header field + */ + u_int8_t (*get_next_header)(ip_packet_t *this); + + /** + * Get the complete IP packet (including the header) + * + * @return IP packet (internal data) + */ + chunk_t (*get_encoding)(ip_packet_t *this); + + /** + * Clone the IP packet + * + * @return clone of the packet + */ + ip_packet_t *(*clone)(ip_packet_t *this); + + /** + * Destroy an ip_packet_t + */ + void (*destroy)(ip_packet_t *this); + +}; + +/** + * Create an IP packet out of data from the wire (or decapsulated from another + * packet). + * + * @note The raw IP packet gets either owned by the new object, or destroyed, + * if the data is invalid. + * + * @param packet the IP packet (including header), gets owned + * @return ip_packet_t instance, or NULL if invalid + */ +ip_packet_t *ip_packet_create(chunk_t packet); + +#endif /** IP_PACKET_H_ @}*/ diff --git a/src/libipsec/ipsec.c b/src/libipsec/ipsec.c new file mode 100644 index 000000000..50d9163ea --- /dev/null +++ b/src/libipsec/ipsec.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec.h" + +#include <debug.h> + +typedef struct private_ipsec_t private_ipsec_t; + +/** + * Private additions to ipsec_t. + */ +struct private_ipsec_t { + + /** + * Public members of ipsec_t. + */ + ipsec_t public; +}; + +/** + * Single instance of ipsec_t. + */ +ipsec_t *ipsec; + +/** + * Described in header. + */ +void libipsec_deinit() +{ + private_ipsec_t *this = (private_ipsec_t*)ipsec; + DESTROY_IF(this->public.processor); + DESTROY_IF(this->public.events); + DESTROY_IF(this->public.policies); + DESTROY_IF(this->public.sas); + free(this); + ipsec = NULL; +} + +/** + * Described in header. + */ +bool libipsec_init() +{ + private_ipsec_t *this; + + INIT(this); + ipsec = &this->public; + + if (lib->integrity && + !lib->integrity->check(lib->integrity, "libipsec", libipsec_init)) + { + DBG1(DBG_LIB, "integrity check of libipsec failed"); + return FALSE; + } + + this->public.sas = ipsec_sa_mgr_create(); + this->public.policies = ipsec_policy_mgr_create(); + this->public.events = ipsec_event_relay_create(); + this->public.processor = ipsec_processor_create(); + return TRUE; +} + diff --git a/src/libipsec/ipsec.h b/src/libipsec/ipsec.h new file mode 100644 index 000000000..7ee49432a --- /dev/null +++ b/src/libipsec/ipsec.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 libipsec libipsec + * + * @addtogroup libipsec + * @{ + */ + +#ifndef IPSEC_H_ +#define IPSEC_H_ + +#include "ipsec_sa_mgr.h" +#include "ipsec_policy_mgr.h" +#include "ipsec_event_relay.h" +#include "ipsec_processor.h" + +#include <library.h> + +typedef struct ipsec_t ipsec_t; + +/** + * User space IPsec implementation. + */ +struct ipsec_t { + + /** + * IPsec SA manager instance + */ + ipsec_sa_mgr_t *sas; + + /** + * IPsec policy manager instance + */ + ipsec_policy_mgr_t *policies; + + /** + * Event relay instance + */ + ipsec_event_relay_t *events; + + /** + * IPsec processor instance + */ + ipsec_processor_t *processor; + +}; + +/** + * The single instance of ipsec_t. + * + * Set between calls to libipsec_init() and libipsec_deinit() calls. + */ +extern ipsec_t *ipsec; + +/** + * Initialize libipsec. + * + * @return FALSE if integrity check failed + */ +bool libipsec_init(); + +/** + * Deinitialize libipsec. + */ +void libipsec_deinit(); + +#endif /** IPSEC_H_ @}*/ diff --git a/src/libipsec/ipsec_event_listener.h b/src/libipsec/ipsec_event_listener.h new file mode 100644 index 000000000..c5c39b0f1 --- /dev/null +++ b/src/libipsec/ipsec_event_listener.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_event_listener ipsec_event_listener + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_EVENT_LISTENER_H_ +#define IPSEC_EVENT_LISTENER_H_ + +typedef struct ipsec_event_listener_t ipsec_event_listener_t; + +#include <library.h> + +/** + * Listener interface for IPsec events + * + * All methods are optional. + */ +struct ipsec_event_listener_t { + + /** + * Called when the lifetime of an IPsec SA expired + * + * @param reqid reqid of the expired SA + * @param protocol protocol of the expired SA + * @param spi spi of the expired SA + * @param hard TRUE if this is a hard expire, FALSE otherwise + */ + void (*expire)(u_int32_t reqid, u_int8_t protocol, u_int32_t spi, + bool hard); + +}; + +#endif /** IPSEC_EVENT_LISTENER_H_ @}*/ diff --git a/src/libipsec/ipsec_event_relay.c b/src/libipsec/ipsec_event_relay.c new file mode 100644 index 000000000..34222258c --- /dev/null +++ b/src/libipsec/ipsec_event_relay.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec_event_relay.h" + +#include <library.h> +#include <debug.h> +#include <threading/rwlock.h> +#include <utils/linked_list.h> +#include <utils/blocking_queue.h> +#include <processing/jobs/callback_job.h> + +typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t; + +/** + * Private additions to ipsec_event_relay_t. + */ +struct private_ipsec_event_relay_t { + + /** + * Public members + */ + ipsec_event_relay_t public; + + /** + * Registered listeners + */ + linked_list_t *listeners; + + /** + * Lock to safely access the list of listeners + */ + rwlock_t *lock; + + /** + * Blocking queue for events + */ + blocking_queue_t *queue; +}; + +/** + * Helper struct used to manage events in a queue + */ +typedef struct { + + /** + * Type of the event + */ + enum { + IPSEC_EVENT_EXPIRE, + } type; + + /** + * Reqid of the SA, if any + */ + u_int32_t reqid; + + /** + * SPI of the SA, if any + */ + u_int32_t spi; + + /** + * Additional data for specific event types + */ + union { + + struct { + /** Protocol of the SA */ + u_int8_t protocol; + /** TRUE in case of a hard expire */ + bool hard; + } expire; + + } data; + +} ipsec_event_t; + +/** + * Dequeue events and relay them to listeners + */ +static job_requeue_t handle_events(private_ipsec_event_relay_t *this) +{ + enumerator_t *enumerator; + ipsec_event_listener_t *current; + ipsec_event_t *event; + + event = this->queue->dequeue(this->queue); + + this->lock->read_lock(this->lock); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + switch (event->type) + { + case IPSEC_EVENT_EXPIRE: + if (current->expire) + { + current->expire(event->reqid, event->data.expire.protocol, + event->spi, event->data.expire.hard); + } + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return JOB_REQUEUE_DIRECT; +} + +METHOD(ipsec_event_relay_t, expire, void, + private_ipsec_event_relay_t *this, u_int32_t reqid, u_int8_t protocol, + u_int32_t spi, bool hard) +{ + ipsec_event_t *event; + + INIT(event, + .type = IPSEC_EVENT_EXPIRE, + .reqid = reqid, + .spi = spi, + .data = { + .expire = { + .protocol = protocol, + .hard = hard, + }, + }, + ); + this->queue->enqueue(this->queue, event); +} + +METHOD(ipsec_event_relay_t, register_listener, void, + private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener) +{ + this->lock->write_lock(this->lock); + this->listeners->insert_last(this->listeners, listener); + this->lock->unlock(this->lock); +} + +METHOD(ipsec_event_relay_t, unregister_listener, void, + private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener) +{ + this->lock->write_lock(this->lock); + this->listeners->remove(this->listeners, listener, NULL); + this->lock->unlock(this->lock); +} + +METHOD(ipsec_event_relay_t, destroy, void, + private_ipsec_event_relay_t *this) +{ + this->queue->destroy_function(this->queue, free); + this->listeners->destroy(this->listeners); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Described in header. + */ +ipsec_event_relay_t *ipsec_event_relay_create() +{ + private_ipsec_event_relay_t *this; + + INIT(this, + .public = { + .expire = _expire, + .register_listener = _register_listener, + .unregister_listener = _unregister_listener, + .destroy = _destroy, + }, + .listeners = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .queue = blocking_queue_create(), + ); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create((callback_job_cb_t)handle_events, this, + NULL, (callback_job_cancel_t)return_false)); + + return &this->public; +} diff --git a/src/libipsec/ipsec_event_relay.h b/src/libipsec/ipsec_event_relay.h new file mode 100644 index 000000000..c6935d546 --- /dev/null +++ b/src/libipsec/ipsec_event_relay.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_event_relay ipsec_event_relay + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_EVENT_RELAY_H_ +#define IPSEC_EVENT_RELAY_H_ + +#include "ipsec_event_listener.h" + +#include <library.h> + +typedef struct ipsec_event_relay_t ipsec_event_relay_t; + +/** + * Event relay manager. + * + * Used to notify upper layers about changes + */ +struct ipsec_event_relay_t { + + /** + * Raise an expire event. + * + * @param reqid reqid of the expired IPsec SA + * @param protocol protocol (e.g ESP) of the expired SA + * @param spi SPI of the expired SA + * @param hard TRUE for a hard expire, FALSE otherwise + */ + void (*expire)(ipsec_event_relay_t *this, u_int32_t reqid, + u_int8_t protocol, u_int32_t spi, bool hard); + + /** + * Register a listener to events raised by this manager + * + * @param listener the listener to register + */ + void (*register_listener)(ipsec_event_relay_t *this, + ipsec_event_listener_t *listener); + + /** + * Unregister a listener + * + * @param listener the listener to unregister + */ + void (*unregister_listener)(ipsec_event_relay_t *this, + ipsec_event_listener_t *listener); + + /** + * Destroy an ipsec_event_relay_t + */ + void (*destroy)(ipsec_event_relay_t *this); + +}; + +/** + * Create an ipsec_event_relay_t instance + * + * @return IPsec event relay instance + */ +ipsec_event_relay_t *ipsec_event_relay_create(); + +#endif /** IPSEC_EVENT_RELAY_H_ @}*/ diff --git a/src/libipsec/ipsec_policy.c b/src/libipsec/ipsec_policy.c new file mode 100644 index 000000000..af8ea9f9d --- /dev/null +++ b/src/libipsec/ipsec_policy.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec_policy.h" + +#include <debug.h> + +typedef struct private_ipsec_policy_t private_ipsec_policy_t; + +/** + * Private additions to ipsec_policy_t. + */ +struct private_ipsec_policy_t { + + /** + * Public members + */ + ipsec_policy_t public; + + /** + * SA source address + */ + host_t *src; + + /** + * SA destination address + */ + host_t *dst; + + /** + * Source traffic selector + */ + traffic_selector_t *src_ts; + + /** + * Destination traffic selector + */ + traffic_selector_t *dst_ts; + + /** + * If any of the two TS has a protocol selector we cache it here + */ + u_int8_t protocol; + + /** + * Traffic direction + */ + policy_dir_t direction; + + /** + * Policy type + */ + policy_type_t type; + + /** + * SA configuration + */ + ipsec_sa_cfg_t sa; + + /** + * Mark + */ + mark_t mark; + + /** + * Policy priority + */ + policy_priority_t priority; + + /** + * Reference counter + */ + refcount_t refcount; + +}; + +METHOD(ipsec_policy_t, match, bool, + private_ipsec_policy_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) +{ + return (this->direction == direction && + this->priority == priority && + this->sa.reqid == reqid && + memeq(&this->mark, &mark, sizeof(mark_t)) && + this->src_ts->equals(this->src_ts, src_ts) && + this->dst_ts->equals(this->dst_ts, dst_ts)); +} + +METHOD(ipsec_policy_t, match_packet, bool, + private_ipsec_policy_t *this, ip_packet_t *packet) +{ + u_int8_t proto = packet->get_next_header(packet); + host_t *src = packet->get_source(packet), + *dst = packet->get_destination(packet); + + return (!this->protocol || this->protocol == proto) && + this->src_ts->includes(this->src_ts, src) && + this->dst_ts->includes(this->dst_ts, dst); +} + +METHOD(ipsec_policy_t, get_source_ts, traffic_selector_t*, + private_ipsec_policy_t *this) +{ + return this->src_ts; +} + +METHOD(ipsec_policy_t, get_destination_ts, traffic_selector_t*, + private_ipsec_policy_t *this) +{ + return this->dst_ts; +} + +METHOD(ipsec_policy_t, get_reqid, u_int32_t, + private_ipsec_policy_t *this) +{ + return this->sa.reqid; +} + +METHOD(ipsec_policy_t, get_direction, policy_dir_t, + private_ipsec_policy_t *this) +{ + return this->direction; +} + +METHOD(ipsec_policy_t, get_priority, policy_priority_t, + private_ipsec_policy_t *this) +{ + return this->priority; +} + +METHOD(ipsec_policy_t, get_type, policy_type_t, + private_ipsec_policy_t *this) +{ + return this->type; +} + +METHOD(ipsec_policy_t, get_ref, ipsec_policy_t*, + private_ipsec_policy_t *this) +{ + ref_get(&this->refcount); + return &this->public; +} + +METHOD(ipsec_policy_t, destroy, void, + private_ipsec_policy_t *this) +{ + if (ref_put(&this->refcount)) + { + this->src->destroy(this->src); + this->dst->destroy(this->dst); + this->src_ts->destroy(this->src_ts); + this->dst_ts->destroy(this->dst_ts); + free(this); + } +} + +/** + * Described in header. + */ +ipsec_policy_t *ipsec_policy_create(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) +{ + private_ipsec_policy_t *this; + + INIT(this, + .public = { + .match = _match, + .match_packet = _match_packet, + .get_source_ts = _get_source_ts, + .get_destination_ts = _get_destination_ts, + .get_direction = _get_direction, + .get_priority = _get_priority, + .get_reqid = _get_reqid, + .get_type = _get_type, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .src = src->clone(src), + .dst = dst->clone(dst), + .src_ts = src_ts->clone(src_ts), + .dst_ts = dst_ts->clone(dst_ts), + .protocol = max(src_ts->get_protocol(src_ts), + dst_ts->get_protocol(dst_ts)), + .direction = direction, + .type = type, + .sa = *sa, + .mark = mark, + .priority = priority, + .refcount = 1, + ); + + return &this->public; +} diff --git a/src/libipsec/ipsec_policy.h b/src/libipsec/ipsec_policy.h new file mode 100644 index 000000000..67ad0b0ed --- /dev/null +++ b/src/libipsec/ipsec_policy.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_policy ipsec_policy + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_POLICY_H +#define IPSEC_POLICY_H + +#include "ip_packet.h" + +#include <library.h> +#include <utils/host.h> +#include <ipsec/ipsec_types.h> +#include <selectors/traffic_selector.h> + +typedef struct ipsec_policy_t ipsec_policy_t; + +/** + * IPsec Policy + */ +struct ipsec_policy_t { + + /** + * Get the source traffic selector of this policy + * + * @return the source traffic selector + */ + traffic_selector_t *(*get_source_ts)(ipsec_policy_t *this); + + /** + * Get the destination traffic selector of this policy + * + * @return the destination traffic selector + */ + traffic_selector_t *(*get_destination_ts)(ipsec_policy_t *this); + + /** + * Get the direction of this policy + * + * @return direction + */ + policy_dir_t (*get_direction)(ipsec_policy_t *this); + + /** + * Get the priority of this policy + * + * @return priority + */ + policy_priority_t (*get_priority)(ipsec_policy_t *this); + + /** + * Get the type of this policy (e.g. IPsec) + * + * @return the policy type + */ + policy_type_t (*get_type)(ipsec_policy_t *this); + + /** + * Get the reqid associated to this policy + * + * @return the reqid + */ + u_int32_t (*get_reqid)(ipsec_policy_t *this); + + /** + * Get another reference to this policy + * + * @return additional reference to the policy + */ + ipsec_policy_t *(*get_ref)(ipsec_policy_t *this); + + /** + * Check if this policy matches all given parameters + * + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @param direction traffic direction + * @param reqid reqid of the policy + * @param mark mark for this policy + * @param prioirty policy priority + * @return TRUE if policy matches all parameters + */ + bool (*match)(ipsec_policy_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); + + /** + * Check if this policy matches the given IP packet + * + * @param packet IP packet + * @return TRUE if policy matches the packet + */ + bool (*match_packet)(ipsec_policy_t *this, ip_packet_t *packet); + + /** + * Destroy an ipsec_policy_t + */ + void (*destroy)(ipsec_policy_t *this); + +}; + +/** + * Create an ipsec_policy_t instance + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of this policy + * @return ipsec policy instance + */ +ipsec_policy_t *ipsec_policy_create(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); + +#endif /** IPSEC_POLICY_H @}*/ diff --git a/src/libipsec/ipsec_policy_mgr.c b/src/libipsec/ipsec_policy_mgr.c new file mode 100644 index 000000000..41ba792c3 --- /dev/null +++ b/src/libipsec/ipsec_policy_mgr.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec_policy_mgr.h" + +#include <debug.h> +#include <threading/rwlock.h> +#include <utils/linked_list.h> + +/** Base priority for installed policies */ +#define PRIO_BASE 512 + +typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t; + +/** + * Private additions to ipsec_policy_mgr_t. + */ +struct private_ipsec_policy_mgr_t { + + /** + * Public members of ipsec_policy_mgr_t. + */ + ipsec_policy_mgr_t public; + + /** + * Installed policies (ipsec_policy_entry_t*) + */ + linked_list_t *policies; + + /** + * Lock to safely access the list of policies + */ + rwlock_t *lock; + +}; + +/** + * Helper struct to store policies in a list sorted by the same pseudo-priority + * used by the NETLINK kernel interface. + */ +typedef struct { + + /** + * Priority used to sort policies + */ + u_int32_t priority; + + /** + * The policy + */ + ipsec_policy_t *policy; + +} ipsec_policy_entry_t; + +/** + * Calculate the pseudo-priority to sort policies. This is the same algorithm + * used by the NETLINK kernel interface (i.e. high priority -> low value). + */ +static u_int32_t calculate_priority(policy_priority_t policy_priority, + traffic_selector_t *src, + traffic_selector_t *dst) +{ + u_int32_t priority = PRIO_BASE; + u_int16_t port; + u_int8_t mask, proto; + host_t *net; + + switch (policy_priority) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + break; + } + /* calculate priority based on selector size, small size = high prio */ + src->to_subnet(src, &net, &mask); + priority -= mask; + proto = src->get_protocol(src); + port = net->get_port(net); + net->destroy(net); + + dst->to_subnet(dst, &net, &mask); + priority -= mask; + proto = max(proto, dst->get_protocol(dst)); + port = max(port, net->get_port(net)); + net->destroy(net); + + priority <<= 2; /* make some room for the two flags */ + priority += port ? 0 : 2; + priority += proto ? 0 : 1; + return priority; +} + +/** + * Create a policy entry + */ +static ipsec_policy_entry_t *policy_entry_create(ipsec_policy_t *policy) +{ + ipsec_policy_entry_t *this; + + INIT(this, + .policy = policy, + .priority = calculate_priority(policy->get_priority(policy), + policy->get_source_ts(policy), + policy->get_destination_ts(policy)), + ); + return this; +} + +/** + * Destroy a policy entry + */ +static void policy_entry_destroy(ipsec_policy_entry_t *this) +{ + this->policy->destroy(this->policy); + free(this); +} + +METHOD(ipsec_policy_mgr_t, add_policy, status_t, + private_ipsec_policy_mgr_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) +{ + enumerator_t *enumerator; + ipsec_policy_entry_t *entry, *current; + ipsec_policy_t *policy; + + if (type != POLICY_IPSEC || direction == POLICY_FWD) + { /* we ignore these policies as we currently have no use for them */ + return SUCCESS; + } + + DBG2(DBG_ESP, "adding policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + + policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa, + mark, priority); + entry = policy_entry_create(policy); + + this->lock->write_lock(this->lock); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->priority >= entry->priority) + { + break; + } + } + this->policies->insert_before(this->policies, enumerator, entry); + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return SUCCESS; +} + +METHOD(ipsec_policy_mgr_t, del_policy, status_t, + private_ipsec_policy_mgr_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 policy_priority) +{ + enumerator_t *enumerator; + ipsec_policy_entry_t *current, *found = NULL; + u_int32_t priority; + + if (direction == POLICY_FWD) + { /* we ignore these policies as we currently have no use for them */ + return SUCCESS; + } + DBG2(DBG_ESP, "deleting policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + + priority = calculate_priority(policy_priority, src_ts, dst_ts); + + this->lock->write_lock(this->lock); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->priority == priority && + current->policy->match(current->policy, src_ts, dst_ts, direction, + reqid, mark, policy_priority)) + { + this->policies->remove_at(this->policies, enumerator); + found = current; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + if (found) + { + policy_entry_destroy(found); + return SUCCESS; + } + return FAILED; +} + +METHOD(ipsec_policy_mgr_t, flush_policies, status_t, + private_ipsec_policy_mgr_t *this) +{ + ipsec_policy_entry_t *entry; + + DBG2(DBG_ESP, "flushing policies"); + + this->lock->write_lock(this->lock); + while (this->policies->remove_last(this->policies, + (void**)&entry) == SUCCESS) + { + policy_entry_destroy(entry); + } + this->lock->unlock(this->lock); + return SUCCESS; +} + +METHOD(ipsec_policy_mgr_t, find_by_packet, ipsec_policy_t*, + private_ipsec_policy_mgr_t *this, ip_packet_t *packet, bool inbound) +{ + enumerator_t *enumerator; + ipsec_policy_entry_t *current; + ipsec_policy_t *found = NULL; + + this->lock->read_lock(this->lock); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + ipsec_policy_t *policy = current->policy; + + if ((inbound == (policy->get_direction(policy) == POLICY_IN)) && + policy->match_packet(policy, packet)) + { + found = policy->get_ref(policy); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return found; +} + +METHOD(ipsec_policy_mgr_t, destroy, void, + private_ipsec_policy_mgr_t *this) +{ + flush_policies(this); + this->policies->destroy(this->policies); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Described in header. + */ +ipsec_policy_mgr_t *ipsec_policy_mgr_create() +{ + private_ipsec_policy_mgr_t *this; + + INIT(this, + .public = { + .add_policy = _add_policy, + .del_policy = _del_policy, + .flush_policies = _flush_policies, + .find_by_packet = _find_by_packet, + .destroy = _destroy, + }, + .policies = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libipsec/ipsec_policy_mgr.h b/src/libipsec/ipsec_policy_mgr.h new file mode 100644 index 000000000..d3ee1074f --- /dev/null +++ b/src/libipsec/ipsec_policy_mgr.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_policy_mgr ipsec_policy_mgr + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_POLICY_MGR_H_ +#define IPSEC_POLICY_MGR_H_ + +#include "ipsec_policy.h" +#include "ip_packet.h" + +#include <library.h> +#include <utils/host.h> +#include <utils/linked_list.h> +#include <ipsec/ipsec_types.h> +#include <selectors/traffic_selector.h> + +typedef struct ipsec_policy_mgr_t ipsec_policy_mgr_t; + +/** + * IPsec policy manager + * + * The first methods are modeled after those in kernel_ipsec_t. + * + * @note Only policies of type POLICY_IPSEC are currently used, also policies + * with direction POLICY_FWD are ignored. Any packets that do not match an + * installed policy will be dropped. + */ +struct ipsec_policy_mgr_t { + + /** + * Add a policy + * + * A policy is always associated to an SA. Traffic which matches a + * policy is handled by the SA with the same reqid. + * + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param type type of policy, POLICY_(IPSEC|PASS|DROP) + * @param sa details about the SA(s) tied to this policy + * @param mark mark for this policy + * @param priority priority of this policy + * @return SUCCESS if operation completed + */ + status_t (*add_policy)(ipsec_policy_mgr_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); + + /** + * Remove a policy + * + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_(IN|OUT|FWD) + * @param reqid unique ID of the associated SA + * @param mark optional mark + * @param priority priority of the policy + * @return SUCCESS if operation completed + */ + status_t (*del_policy)(ipsec_policy_mgr_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); + + /** + * Flush all policies + * + * @return SUCCESS if operation completed + */ + status_t (*flush_policies)(ipsec_policy_mgr_t *this); + + /** + * Find the policy that matches the given IP packet best + * + * @param packet IP packet to match + * @param inbound TRUE for an inbound packet + * @return reference to the policy, or NULL if none found + */ + ipsec_policy_t *(*find_by_packet)(ipsec_policy_mgr_t *this, + ip_packet_t *packet, bool inbound); + + /** + * Destroy an ipsec_policy_mgr_t + */ + void (*destroy)(ipsec_policy_mgr_t *this); + +}; + +/** + * Create an ipsec_policy_mgr instance + * + * @return ipsec_policy_mgr + */ +ipsec_policy_mgr_t *ipsec_policy_mgr_create(); + +#endif /** IPSEC_POLICY_MGR_H_ @}*/ diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c new file mode 100644 index 000000000..a91d9e074 --- /dev/null +++ b/src/libipsec/ipsec_processor.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec.h" +#include "ipsec_processor.h" + +#include <debug.h> +#include <library.h> +#include <threading/rwlock.h> +#include <utils/blocking_queue.h> +#include <processing/jobs/callback_job.h> + +typedef struct private_ipsec_processor_t private_ipsec_processor_t; + +/** + * Private additions to ipsec_processor_t. + */ +struct private_ipsec_processor_t { + + /** + * Public members + */ + ipsec_processor_t public; + + /** + * Queue for inbound packets (esp_packet_t*) + */ + blocking_queue_t *inbound_queue; + + /** + * Queue for outbound packets (ip_packet_t*) + */ + blocking_queue_t *outbound_queue; + + /** + * Registered inbound callback + */ + struct { + ipsec_inbound_cb_t cb; + void *data; + } inbound; + + /** + * Registered outbound callback + */ + struct { + ipsec_outbound_cb_t cb; + void *data; + } outbound; + + /** + * Lock used to synchronize access to the callbacks + */ + rwlock_t *lock; +}; + +/** + * Deliver an inbound IP packet to the registered listener + */ +static void deliver_inbound(private_ipsec_processor_t *this, + esp_packet_t *packet) +{ + this->lock->read_lock(this->lock); + if (this->inbound.cb) + { + this->inbound.cb(this->inbound.data, packet->extract_payload(packet)); + } + else + { + DBG2(DBG_ESP, "no inbound callback registered, dropping packet"); + } + packet->destroy(packet); + this->lock->unlock(this->lock); +} + +/** + * Processes inbound packets + */ +static job_requeue_t process_inbound(private_ipsec_processor_t *this) +{ + esp_packet_t *packet; + ipsec_sa_t *sa; + u_int8_t next_header; + u_int32_t spi; + + packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue); + + if (!packet->parse_header(packet, &spi)) + { + packet->destroy(packet); + return JOB_REQUEUE_DIRECT; + } + + sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi, + packet->get_destination(packet)); + if (!sa) + { + DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA"); + packet->destroy(packet); + return JOB_REQUEUE_DIRECT; + } + + if (!sa->is_inbound(sa)) + { + DBG1(DBG_ESP, "error: IPsec SA is not inbound"); + packet->destroy(packet); + ipsec->sas->checkin(ipsec->sas, sa); + return JOB_REQUEUE_DIRECT; + } + + if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS) + { + ipsec->sas->checkin(ipsec->sas, sa); + packet->destroy(packet); + return JOB_REQUEUE_DIRECT; + } + ipsec->sas->checkin(ipsec->sas, sa); + + next_header = packet->get_next_header(packet); + switch (next_header) + { + case IPPROTO_IPIP: + case IPPROTO_IPV6: + { + ipsec_policy_t *policy; + ip_packet_t *ip_packet; + + ip_packet = packet->get_payload(packet); + policy = ipsec->policies->find_by_packet(ipsec->policies, + ip_packet, TRUE); + if (policy) + { /* TODO-IPSEC: update policy/sa stats? */ + deliver_inbound(this, packet); + policy->destroy(policy); + break; + } + DBG1(DBG_ESP, "discarding inbound IP packet due to policy"); + /* no matching policy found, fall-through */ + } + case IPPROTO_NONE: + /* discard dummy packets */ + /* fall-through */ + default: + packet->destroy(packet); + break; + } + return JOB_REQUEUE_DIRECT; +} + +/** + * Send an ESP packet using the registered outbound callback + */ +static void send_outbound(private_ipsec_processor_t *this, + esp_packet_t *packet) +{ + this->lock->read_lock(this->lock); + if (this->outbound.cb) + { + this->outbound.cb(this->outbound.data, packet); + } + else + { + DBG2(DBG_ESP, "no outbound callback registered, dropping packet"); + packet->destroy(packet); + } + this->lock->unlock(this->lock); +} + +/** + * Processes outbound packets + */ +static job_requeue_t process_outbound(private_ipsec_processor_t *this) +{ + ipsec_policy_t *policy; + esp_packet_t *esp_packet; + ip_packet_t *packet; + ipsec_sa_t *sa; + host_t *src, *dst; + + packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue); + + policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE); + if (!policy) + { + DBG1(DBG_ESP, "no matching outbound IPsec policy for %H == %H", + packet->get_source(packet), packet->get_destination(packet)); + packet->destroy(packet); + return JOB_REQUEUE_DIRECT; + } + + sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy), + FALSE); + if (!sa) + { /* TODO-IPSEC: send an acquire to uppper layer */ + DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, " + "dropping packet", policy->get_reqid(policy)); + packet->destroy(packet); + policy->destroy(policy); + return JOB_REQUEUE_DIRECT; + } + src = sa->get_source(sa); + dst = sa->get_destination(sa); + esp_packet = esp_packet_create_from_payload(src->clone(src), + dst->clone(dst), packet); + if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa), + sa->get_spi(sa)) != SUCCESS) + { + ipsec->sas->checkin(ipsec->sas, sa); + esp_packet->destroy(esp_packet); + policy->destroy(policy); + return JOB_REQUEUE_DIRECT; + } + /* TODO-IPSEC: update policy/sa counters? */ + ipsec->sas->checkin(ipsec->sas, sa); + policy->destroy(policy); + send_outbound(this, esp_packet); + return JOB_REQUEUE_DIRECT; +} + +METHOD(ipsec_processor_t, queue_inbound, void, + private_ipsec_processor_t *this, esp_packet_t *packet) +{ + this->inbound_queue->enqueue(this->inbound_queue, packet); +} + +METHOD(ipsec_processor_t, queue_outbound, void, + private_ipsec_processor_t *this, ip_packet_t *packet) +{ + this->outbound_queue->enqueue(this->outbound_queue, packet); +} + +METHOD(ipsec_processor_t, register_inbound, void, + private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data) +{ + this->lock->write_lock(this->lock); + this->inbound.cb = cb; + this->inbound.data = data; + this->lock->unlock(this->lock); +} + +METHOD(ipsec_processor_t, unregister_inbound, void, + private_ipsec_processor_t *this, ipsec_inbound_cb_t cb) +{ + this->lock->write_lock(this->lock); + if (this->inbound.cb == cb) + { + this->inbound.cb = NULL; + } + this->lock->unlock(this->lock); +} + +METHOD(ipsec_processor_t, register_outbound, void, + private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data) +{ + this->lock->write_lock(this->lock); + this->outbound.cb = cb; + this->outbound.data = data; + this->lock->unlock(this->lock); +} + +METHOD(ipsec_processor_t, unregister_outbound, void, + private_ipsec_processor_t *this, ipsec_outbound_cb_t cb) +{ + this->lock->write_lock(this->lock); + if (this->outbound.cb == cb) + { + this->outbound.cb = NULL; + } + this->lock->unlock(this->lock); +} + +METHOD(ipsec_processor_t, destroy, void, + private_ipsec_processor_t *this) +{ + this->inbound_queue->destroy_offset(this->inbound_queue, + offsetof(esp_packet_t, destroy)); + this->outbound_queue->destroy_offset(this->outbound_queue, + offsetof(ip_packet_t, destroy)); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Described in header. + */ +ipsec_processor_t *ipsec_processor_create() +{ + private_ipsec_processor_t *this; + + INIT(this, + .public = { + .queue_inbound = _queue_inbound, + .queue_outbound = _queue_outbound, + .register_inbound = _register_inbound, + .unregister_inbound = _unregister_inbound, + .register_outbound = _register_outbound, + .unregister_outbound = _unregister_outbound, + .destroy = _destroy, + }, + .inbound_queue = blocking_queue_create(), + .outbound_queue = blocking_queue_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this, + NULL, (callback_job_cancel_t)return_false)); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this, + NULL, (callback_job_cancel_t)return_false)); + return &this->public; +} diff --git a/src/libipsec/ipsec_processor.h b/src/libipsec/ipsec_processor.h new file mode 100644 index 000000000..0a409828b --- /dev/null +++ b/src/libipsec/ipsec_processor.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_processor ipsec_processor + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_PROCESSOR_H_ +#define IPSEC_PROCESSOR_H_ + +#include "ip_packet.h" +#include "esp_packet.h" + +typedef struct ipsec_processor_t ipsec_processor_t; + +/** + * Callback called to deliver an inbound plaintext packet. + * + * @param data data supplied during registration of the callback + * @param packet plaintext IP packet to deliver + */ +typedef void (*ipsec_inbound_cb_t)(void *data, ip_packet_t *packet); + +/** + * Callback called to send an ESP packet. + * + * @note The ESP packet currently comes without IP header (and without UDP + * header in case of UDP encapsulation) + * + * @param data data supplied during registration of the callback + * @param packet ESP packet to send + */ +typedef void (*ipsec_outbound_cb_t)(void *data, esp_packet_t *packet); + +/** + * IPsec processor + */ +struct ipsec_processor_t { + + /** + * Queue an inbound ESP packet for processing. + * + * @param packet the ESP packet to process + */ + void (*queue_inbound)(ipsec_processor_t *this, esp_packet_t *packet); + + /** + * Queue an outbound plaintext IP packet for processing. + * + * @param packet the plaintext IP packet + */ + void (*queue_outbound)(ipsec_processor_t *this, ip_packet_t *packet); + + /** + * Register the callback used to deliver inbound plaintext packets. + * + * @param cb the inbound callback function + * @param data optional data provided to callback + */ + void (*register_inbound)(ipsec_processor_t *this, ipsec_inbound_cb_t cb, + void *data); + + /** + * Unregister a previously registered inbound callback. + * + * @param cb previously registered callback function + */ + void (*unregister_inbound)(ipsec_processor_t *this, + ipsec_inbound_cb_t cb); + + /** + * Register the callback used to send outbound ESP packets. + * + * @param cb the outbound callback function + * @param data optional data provided to callback + */ + void (*register_outbound)(ipsec_processor_t *this, ipsec_outbound_cb_t cb, + void *data); + + /** + * Unregister a previously registered outbound callback. + * + * @param cb previously registered callback function + */ + void (*unregister_outbound)(ipsec_processor_t *this, + ipsec_outbound_cb_t cb); + + /** + * Destroy an ipsec_processor_t. + */ + void (*destroy)(ipsec_processor_t *this); + +}; + +/** + * Create an ipsec_processor_t instance + * + * @return IPsec processor instance + */ +ipsec_processor_t *ipsec_processor_create(); + +#endif /** IPSEC_PROCESSOR_H_ @}*/ diff --git a/src/libipsec/ipsec_sa.c b/src/libipsec/ipsec_sa.c new file mode 100644 index 000000000..cccd16404 --- /dev/null +++ b/src/libipsec/ipsec_sa.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec_sa.h" + +#include <library.h> +#include <debug.h> + +typedef struct private_ipsec_sa_t private_ipsec_sa_t; + +/** + * Private additions to ipsec_sa_t. + */ +struct private_ipsec_sa_t { + + /** + * Public members + */ + ipsec_sa_t public; + + /** + * SPI of this SA + */ + u_int32_t spi; + + /** + * Source address + */ + host_t *src; + + /** + * Destination address + */ + host_t *dst; + + /** + * Protocol + */ + u_int8_t protocol; + + /** + * Reqid of this SA + */ + u_int32_t reqid; + + /** + * Lifetime configuration + */ + lifetime_cfg_t lifetime; + + /** + * IPsec mode + */ + ipsec_mode_t mode; + + /** + * TRUE if extended sequence numbers are used + */ + bool esn; + + /** + * TRUE if this is an inbound SA + */ + bool inbound; + + /** + * ESP context + */ + esp_context_t *esp_context; +}; + +METHOD(ipsec_sa_t, get_source, host_t*, + private_ipsec_sa_t *this) +{ + return this->src; +} + +METHOD(ipsec_sa_t, get_destination, host_t*, + private_ipsec_sa_t *this) +{ + return this->dst; +} + +METHOD(ipsec_sa_t, get_spi, u_int32_t, + private_ipsec_sa_t *this) +{ + return this->spi; +} + +METHOD(ipsec_sa_t, get_reqid, u_int32_t, + private_ipsec_sa_t *this) +{ + return this->reqid; +} + +METHOD(ipsec_sa_t, get_protocol, u_int8_t, + private_ipsec_sa_t *this) +{ + return this->protocol; +} + +METHOD(ipsec_sa_t, get_lifetime, lifetime_cfg_t*, + private_ipsec_sa_t *this) +{ + return &this->lifetime; +} + +METHOD(ipsec_sa_t, is_inbound, bool, + private_ipsec_sa_t *this) +{ + return this->inbound; +} + +METHOD(ipsec_sa_t, get_esp_context, esp_context_t*, + private_ipsec_sa_t *this) +{ + return this->esp_context; +} + +METHOD(ipsec_sa_t, match_by_spi_dst, bool, + private_ipsec_sa_t *this, u_int32_t spi, host_t *dst) +{ + return this->spi == spi && this->dst->ip_equals(this->dst, dst); +} + +METHOD(ipsec_sa_t, match_by_spi_src_dst, bool, + private_ipsec_sa_t *this, u_int32_t spi, host_t *src, host_t *dst) +{ + return this->spi == spi && this->src->ip_equals(this->src, src) && + this->dst->ip_equals(this->dst, dst); +} + +METHOD(ipsec_sa_t, match_by_reqid, bool, + private_ipsec_sa_t *this, u_int32_t reqid, bool inbound) +{ + return this->reqid == reqid && this->inbound == inbound; +} + +METHOD(ipsec_sa_t, destroy, void, + private_ipsec_sa_t *this) +{ + this->src->destroy(this->src); + this->dst->destroy(this->dst); + DESTROY_IF(this->esp_context); + free(this); +} + +/** + * Described in header. + */ +ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst, + 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, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) +{ + private_ipsec_sa_t *this; + + if (protocol != IPPROTO_ESP) + { + DBG1(DBG_ESP, " IPsec SA: protocol not supported"); + return NULL; + } + if (!encap) + { + DBG1(DBG_ESP, " IPsec SA: only UDP encapsulation is supported"); + return NULL; + } + if (esn) + { + DBG1(DBG_ESP, " IPsec SA: ESN not supported"); + return NULL; + } + if (ipcomp != IPCOMP_NONE) + { + DBG1(DBG_ESP, " IPsec SA: compression not supported"); + return NULL; + } + if (mode != MODE_TUNNEL) + { + DBG1(DBG_ESP, " IPsec SA: unsupported mode"); + return NULL; + } + + INIT(this, + .public = { + .destroy = _destroy, + .get_source = _get_source, + .get_destination = _get_destination, + .get_spi = _get_spi, + .get_reqid = _get_reqid, + .get_protocol = _get_protocol, + .get_lifetime = _get_lifetime, + .is_inbound = _is_inbound, + .match_by_spi_dst = _match_by_spi_dst, + .match_by_spi_src_dst = _match_by_spi_src_dst, + .match_by_reqid = _match_by_reqid, + .get_esp_context = _get_esp_context, + }, + .spi = spi, + .src = src->clone(src), + .dst = dst->clone(dst), + .lifetime = *lifetime, + .protocol = protocol, + .reqid = reqid, + .mode = mode, + .esn = esn, + .inbound = inbound, + ); + + this->esp_context = esp_context_create(enc_alg, enc_key, int_alg, int_key, + inbound); + if (!this->esp_context) + { + destroy(this); + return NULL; + } + return &this->public; +} diff --git a/src/libipsec/ipsec_sa.h b/src/libipsec/ipsec_sa.h new file mode 100644 index 000000000..5fd03b6e4 --- /dev/null +++ b/src/libipsec/ipsec_sa.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_sa ipsec_sa + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_SA_H_ +#define IPSEC_SA_H_ + +#include "esp_context.h" + +#include <library.h> +#include <utils/host.h> +#include <selectors/traffic_selector.h> +#include <ipsec/ipsec_types.h> + +typedef struct ipsec_sa_t ipsec_sa_t; + +/** + * IPsec Security Association (SA) + */ +struct ipsec_sa_t { + + /** + * Get the source address for this SA + * + * @return source address of this SA + */ + host_t *(*get_source)(ipsec_sa_t *this); + + /** + * Get the destination address for this SA + * + * @return destination address of this SA + */ + host_t *(*get_destination)(ipsec_sa_t *this); + + /** + * Get the SPI for this SA + * + * @return SPI of this SA + */ + u_int32_t (*get_spi)(ipsec_sa_t *this); + + /** + * Get the reqid of this SA + * + * @return reqid of this SA + */ + u_int32_t (*get_reqid)(ipsec_sa_t *this); + + /** + * Get the protocol (e.g. IPPROTO_ESP) of this SA + * + * @return protocol of this SA + */ + u_int8_t (*get_protocol)(ipsec_sa_t *this); + + /** + * Returns whether this SA is inbound or outbound + * + * @return TRUE if inbound, FALSE if outbound + */ + bool (*is_inbound)(ipsec_sa_t *this); + + /** + * Get the lifetime information for this SA + * Note that this information is always relative to the time when the + * SA was installed (i.e. it is not adjusted over time) + * + * @return lifetime of this SA + */ + lifetime_cfg_t *(*get_lifetime)(ipsec_sa_t *this); + + /** + * Get the ESP context for this SA + * + * @return ESP context of this SA + */ + esp_context_t *(*get_esp_context)(ipsec_sa_t *this); + + /** + * Check if this SA matches all given parameters + * + * @param spi SPI + * @param dst destination address + * @return TRUE if this SA matches all parameters, FALSE otherwise + */ + bool (*match_by_spi_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *dst); + + /** + * Check if this SA matches all given parameters + * + * @param spi SPI + * @param src source address + * @param dst destination address + * @return TRUE if this SA matches all parameters, FALSE otherwise + */ + bool (*match_by_spi_src_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *src, + host_t *dst); + + /** + * Check if this SA matches all given parameters + * + * @param reqid reqid + * @param inbound TRUE for inbound SA, FALSE for outbound + * @return TRUE if this SA matches all parameters, FALSE otherwise + */ + bool (*match_by_reqid)(ipsec_sa_t *this, u_int32_t reqid, bool inbound); + + /** + * Destroy an ipsec_sa_t + */ + void (*destroy)(ipsec_sa_t *this); + +}; + +/** + * Create an ipsec_sa_t instance + * + * @param spi SPI for this SA + * @param src source address for this SA (gets cloned) + * @param dst destination address for this SA (gets cloned) + * @param protocol protocol for this SA (only ESP is supported) + * @param reqid reqid for this SA + * @param mark mark for this SA (ignored) + * @param tfc Traffic Flow Confidentiality (currently not supported) + * @param lifetime lifetime for this SA + * @param enc_alg encryption algorithm for this SA + * @param enc_key encryption key for this SA + * @param int_alg integrity protection algorithm + * @param int_key integrity protection key + * @param mode mode for this SA (only tunnel mode is supported) + * @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE) + * @param cpi CPI for IPcomp (ignored) + * @param encap enable UDP encapsulation (must be TRUE) + * @param esn Extended Sequence Numbers (currently not supported) + * @param inbound TRUE if this is an inbound SA, FALSE otherwise + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @return the IPsec SA, or NULL if the creation failed + */ +ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst, + 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, + bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts); + +#endif /** IPSEC_SA_H_ @}*/ diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c new file mode 100644 index 000000000..e42c77aa5 --- /dev/null +++ b/src/libipsec/ipsec_sa_mgr.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 "ipsec.h" +#include "ipsec_sa_mgr.h" + +#include <debug.h> +#include <library.h> +#include <processing/jobs/callback_job.h> +#include <threading/condvar.h> +#include <threading/mutex.h> +#include <utils/hashtable.h> +#include <utils/linked_list.h> + +typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t; + +/** + * Private additions to ipsec_sa_mgr_t. + */ +struct private_ipsec_sa_mgr_t { + + /** + * Public members of ipsec_sa_mgr_t. + */ + ipsec_sa_mgr_t public; + + /** + * Installed SAs + */ + linked_list_t *sas; + + /** + * SPIs allocated using get_spi() + */ + hashtable_t *allocated_spis; + + /** + * Mutex used to synchronize access to the SA manager + */ + mutex_t *mutex; + + /** + * RNG used to generate SPIs + */ + rng_t *rng; +}; + +/** + * Struct to keep track of locked IPsec SAs + */ +typedef struct { + + /** + * IPsec SA + */ + ipsec_sa_t *sa; + + /** + * Set if this SA is currently in use by a thread + */ + bool locked; + + /** + * Condvar used by threads to wait for this entry + */ + condvar_t *condvar; + + /** + * Number of threads waiting for this entry + */ + u_int waiting_threads; + + /** + * Set if this entry is awaiting deletion + */ + bool awaits_deletion; + +} ipsec_sa_entry_t; + +/** + * Helper struct for expiration events + */ +typedef struct { + + /** + * IPsec SA manager + */ + private_ipsec_sa_mgr_t *manager; + + /** + * Entry that expired + */ + ipsec_sa_entry_t *entry; + + /** + * 0 if this is a hard expire, otherwise the offset in s (soft->hard) + */ + u_int32_t hard_offset; + +} ipsec_sa_expired_t; + +/* + * Used for the hash table of allocated SPIs + */ +static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi) +{ + return *spi == *other_spi; +} + +static u_int spi_hash(u_int32_t *spi) +{ + return chunk_hash(chunk_from_thing(*spi)); +} + +/** + * Create an SA entry + */ +static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa) +{ + ipsec_sa_entry_t *this; + + INIT(this, + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .sa = sa, + ); + return this; +} + +/** + * Destroy an SA entry + */ +static void destroy_entry(ipsec_sa_entry_t *entry) +{ + entry->condvar->destroy(entry->condvar); + entry->sa->destroy(entry->sa); + free(entry); +} + +/** + * Makes sure an entry is safe to remove + * Must be called with this->mutex held. + * + * @return TRUE if entry can be removed, FALSE if entry is already +* being removed by another thread + */ +static bool wait_remove_entry(private_ipsec_sa_mgr_t *this, + ipsec_sa_entry_t *entry) +{ + if (entry->awaits_deletion) + { + /* this will be deleted by another thread already */ + return FALSE; + } + entry->awaits_deletion = TRUE; + while (entry->locked) + { + entry->condvar->wait(entry->condvar, this->mutex); + } + while (entry->waiting_threads > 0) + { + entry->condvar->broadcast(entry->condvar); + entry->condvar->wait(entry->condvar, this->mutex); + } + return TRUE; +} + +/** + * Waits until an is available and then locks it. + * Must only be called with this->mutex held + */ +static bool wait_for_entry(private_ipsec_sa_mgr_t *this, + ipsec_sa_entry_t *entry) +{ + while (entry->locked && !entry->awaits_deletion) + { + entry->waiting_threads++; + entry->condvar->wait(entry->condvar, this->mutex); + entry->waiting_threads--; + } + if (entry->awaits_deletion) + { + /* others may still be waiting, */ + entry->condvar->signal(entry->condvar); + return FALSE; + } + entry->locked = TRUE; + return TRUE; +} + +/** + * Flushes all entries + * Must be called with this->mutex held. + */ +static void flush_entries(private_ipsec_sa_mgr_t *this) +{ + ipsec_sa_entry_t *current; + enumerator_t *enumerator; + + DBG2(DBG_ESP, "flushing SAD"); + + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + destroy_entry(current); + } + } + enumerator->destroy(enumerator); +} + +/* + * Different match functions to find SAs in the linked list + */ +static bool match_entry_by_ptr(ipsec_sa_entry_t *item, ipsec_sa_entry_t *entry) +{ + return item == entry; +} + +static bool match_entry_by_sa_ptr(ipsec_sa_entry_t *item, ipsec_sa_t *sa) +{ + return item->sa == sa; +} + +static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t spi, + bool inbound) +{ + return item->sa->get_spi(item->sa) == spi && + item->sa->is_inbound(item->sa) == inbound; +} + +static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t spi, + host_t *src, host_t *dst) +{ + return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst); +} + +static bool match_entry_by_reqid_inbound(ipsec_sa_entry_t *item, + u_int32_t reqid, bool inbound) +{ + return item->sa->match_by_reqid(item->sa, reqid, inbound); +} + +static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t spi, + host_t *dst) +{ + return item->sa->match_by_spi_dst(item->sa, spi, dst); +} + +/** + * Remove an entry + */ +static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry) +{ + ipsec_sa_entry_t *current; + enumerator_t *enumerator; + bool removed = FALSE; + + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current == entry) + { + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + removed = TRUE; + } + break; + } + } + enumerator->destroy(enumerator); + return removed; +} + +/** + * Callback for expiration events + */ +static job_requeue_t sa_expired(ipsec_sa_expired_t *expired) +{ + private_ipsec_sa_mgr_t *this = expired->manager; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr, + NULL, expired->entry) == SUCCESS) + { + u_int32_t hard_offset = expired->hard_offset; + ipsec_sa_t *sa = expired->entry->sa; + + ipsec->events->expire(ipsec->events, sa->get_reqid(sa), + sa->get_protocol(sa), sa->get_spi(sa), + hard_offset == 0); + if (hard_offset) + { /* soft limit reached, schedule hard expire */ + expired->hard_offset = 0; + this->mutex->unlock(this->mutex); + return JOB_RESCHEDULE(hard_offset); + } + /* hard limit reached */ + if (remove_entry(this, expired->entry)) + { + destroy_entry(expired->entry); + } + } + this->mutex->unlock(this->mutex); + return JOB_REQUEUE_NONE; +} + +/** + * Schedule a job to handle IPsec SA expiration + */ +static void schedule_expiration(private_ipsec_sa_mgr_t *this, + ipsec_sa_entry_t *entry) +{ + lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa); + ipsec_sa_expired_t *expired; + callback_job_t *job; + u_int32_t timeout; + + INIT(expired, + .manager = this, + .entry = entry, + ); + + /* schedule a rekey first, a hard timeout will be scheduled then, if any */ + expired->hard_offset = lifetime->time.life - lifetime->time.rekey; + timeout = lifetime->time.rekey; + + if (lifetime->time.life <= lifetime->time.rekey || + lifetime->time.rekey == 0) + { /* no rekey, schedule hard timeout */ + expired->hard_offset = 0; + timeout = lifetime->time.life; + } + + job = callback_job_create((callback_job_cb_t)sa_expired, expired, + (callback_job_cleanup_t)free, NULL); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout); +} + +/** + * Remove all allocated SPIs + */ +static void flush_allocated_spis(private_ipsec_sa_mgr_t *this) +{ + enumerator_t *enumerator; + u_int32_t *current; + + DBG2(DBG_ESP, "flushing allocated SPIs"); + enumerator = this->allocated_spis->create_enumerator(this->allocated_spis); + while (enumerator->enumerate(enumerator, NULL, (void**)¤t)) + { + this->allocated_spis->remove_at(this->allocated_spis, enumerator); + DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current)); + free(current); + } + enumerator->destroy(enumerator); +} + +/** + * Pre-allocate an SPI for an inbound SA + */ +static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi) +{ + u_int32_t *spi_alloc; + + if (this->allocated_spis->get(this->allocated_spis, &spi) || + this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound, + NULL, spi, TRUE) == SUCCESS) + { + return FALSE; + } + spi_alloc = malloc_thing(u_int32_t); + *spi_alloc = spi; + this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc); + return TRUE; +} + +METHOD(ipsec_sa_mgr_t, get_spi, status_t, + private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol, + u_int32_t reqid, u_int32_t *spi) +{ + u_int32_t spi_new; + + DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid); + + this->mutex->lock(this->mutex); + if (!this->rng) + { + this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!this->rng) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to create RNG for SPI generation"); + return FAILED; + } + } + + do + { + if (!this->rng->get_bytes(this->rng, sizeof(spi_new), + (u_int8_t*)&spi_new)) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid); + return FAILED; + } + /* make sure the SPI is valid (not in range 0-255) */ + spi_new |= 0x00000100; + spi_new = htonl(spi_new); + } + while (!allocate_spi(this, spi_new)); + this->mutex->unlock(this->mutex); + + *spi = spi_new; + + DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, add_sa, status_t, + private_ipsec_sa_mgr_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, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts) +{ + ipsec_sa_entry_t *entry; + ipsec_sa_t *sa_new; + + DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}", + ntohl(spi), reqid); + DBG2(DBG_ESP, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg, enc_key.len * 8); + DBG2(DBG_ESP, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg, int_key.len * 8); + + sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc, + lifetime, enc_alg, enc_key, int_alg, int_key, mode, + ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts); + if (!sa_new) + { + DBG1(DBG_ESP, "failed to create SAD entry"); + return FAILED; + } + + this->mutex->lock(this->mutex); + + if (inbound) + { /* remove any pre-allocated SPIs */ + u_int32_t *spi_alloc; + + spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi); + free(spi_alloc); + } + + if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst, + NULL, spi, src, dst) == SUCCESS) + { + this->mutex->unlock(this->mutex); + DBG1(DBG_ESP, "failed to install SAD entry: already installed"); + sa_new->destroy(sa_new); + return FAILED; + } + + entry = create_entry(sa_new); + schedule_expiration(this, entry); + this->sas->insert_last(this->sas, entry); + + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, del_sa, status_t, + private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi, + u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + ipsec_sa_entry_t *current, *found = NULL; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->sas->create_enumerator(this->sas); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (match_entry_by_spi_src_dst(current, spi, src, dst)) + { + if (wait_remove_entry(this, current)) + { + this->sas->remove_at(this->sas, enumerator); + found = current; + } + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + if (found) + { + DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x", + found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi)); + destroy_entry(found); + return SUCCESS; + } + return FAILED; +} + +METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*, + private_ipsec_sa_mgr_t *this, u_int32_t reqid, bool inbound) +{ + ipsec_sa_entry_t *entry; + ipsec_sa_t *sa = NULL; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_reqid_inbound, + (void**)&entry, reqid, inbound) == SUCCESS && + wait_for_entry(this, entry)) + { + sa = entry->sa; + } + this->mutex->unlock(this->mutex); + return sa; +} + +METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*, + private_ipsec_sa_mgr_t *this, u_int32_t spi, host_t *dst) +{ + ipsec_sa_entry_t *entry; + ipsec_sa_t *sa = NULL; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_dst, + (void**)&entry, spi, dst) == SUCCESS && + wait_for_entry(this, entry)) + { + sa = entry->sa; + } + this->mutex->unlock(this->mutex); + return sa; +} + +METHOD(ipsec_sa_mgr_t, checkin, void, + private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa) +{ + ipsec_sa_entry_t *entry; + + this->mutex->lock(this->mutex); + if (this->sas->find_first(this->sas, (void*)match_entry_by_sa_ptr, + (void**)&entry, sa) == SUCCESS) + { + if (entry->locked) + { + entry->locked = FALSE; + entry->condvar->signal(entry->condvar); + } + } + this->mutex->unlock(this->mutex); +} + +METHOD(ipsec_sa_mgr_t, flush_sas, status_t, + private_ipsec_sa_mgr_t *this) +{ + this->mutex->lock(this->mutex); + flush_entries(this); + this->mutex->unlock(this->mutex); + return SUCCESS; +} + +METHOD(ipsec_sa_mgr_t, destroy, void, + private_ipsec_sa_mgr_t *this) +{ + this->mutex->lock(this->mutex); + flush_entries(this); + flush_allocated_spis(this); + this->mutex->unlock(this->mutex); + + this->allocated_spis->destroy(this->allocated_spis); + this->sas->destroy(this->sas); + + this->mutex->destroy(this->mutex); + DESTROY_IF(this->rng); + free(this); +} + +/** + * Described in header. + */ +ipsec_sa_mgr_t *ipsec_sa_mgr_create() +{ + private_ipsec_sa_mgr_t *this; + + INIT(this, + .public = { + .get_spi = _get_spi, + .add_sa = _add_sa, + .del_sa = _del_sa, + .checkout_by_spi = _checkout_by_spi, + .checkout_by_reqid = _checkout_by_reqid, + .checkin = _checkin, + .flush_sas = _flush_sas, + .destroy = _destroy, + }, + .sas = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash, + (hashtable_equals_t)spi_equals, 16), + ); + + return &this->public; +} diff --git a/src/libipsec/ipsec_sa_mgr.h b/src/libipsec/ipsec_sa_mgr.h new file mode 100644 index 000000000..303b36f0e --- /dev/null +++ b/src/libipsec/ipsec_sa_mgr.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * 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 ipsec_sa_mgr ipsec_sa_mgr + * @{ @ingroup libipsec + */ + +#ifndef IPSEC_SA_MGR_H_ +#define IPSEC_SA_MGR_H_ + +#include "ipsec_sa.h" + +#include <library.h> +#include <ipsec/ipsec_types.h> +#include <selectors/traffic_selector.h> +#include <utils/host.h> + +typedef struct ipsec_sa_mgr_t ipsec_sa_mgr_t; + +/** + * IPsec SA manager + * + * The first methods are modeled after those in kernel_ipsec_t. + */ +struct ipsec_sa_mgr_t { + + /** + * Allocate an SPI for an inbound IPsec SA + * + * @param src source address of the SA + * @param dst destination address of the SA + * @param protocol protocol of the SA (only ESP supported) + * @param reqid reqid for the SA + * @param spi the allocated SPI + * @return SUCCESS of operation successful + */ + status_t (*get_spi)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t reqid, u_int32_t *spi); + + /** + * Add a new SA + * + * @param src source address for this SA (gets cloned) + * @param dst destination address for this SA (gets cloned) + * @param spi SPI for this SA + * @param protocol protocol for this SA (only ESP is supported) + * @param reqid reqid for this SA + * @param mark mark for this SA (ignored) + * @param tfc Traffic Flow Confidentiality (not yet supported) + * @param lifetime lifetime for this SA + * @param enc_alg encryption algorithm for this SA + * @param enc_key encryption key for this SA + * @param int_alg integrity protection algorithm + * @param int_key integrity protection key + * @param mode mode for this SA (only tunnel mode is supported) + * @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE) + * @param cpi CPI for IPcomp (ignored) + * @param encap enable UDP encapsulation (must be TRUE) + * @param esn Extended Sequence Numbers (currently not supported) + * @param inbound TRUE if this is an inbound SA, FALSE otherwise + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector + * @return SUCCESS if operation completed + */ + status_t (*add_sa)(ipsec_sa_mgr_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, bool encap, bool esn, bool inbound, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts); + + /** + * Delete a previously added SA + * + * @param spi SPI of the SA + * @param src source address of the SA + * @param dst destination address of the SA + * @param protocol protocol of the SA + * @param cpi CPI for IPcomp + * @param mark optional mark + * @return SUCCESS if operation completed + */ + status_t (*del_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, + mark_t mark); + + /** + * Flush all SAs + * + * @return SUCCESS if operation completed + */ + status_t (*flush_sas)(ipsec_sa_mgr_t *this); + + /** + * Checkout an installed IPsec SA by SPI and destination address + * Can be used to find the correct SA for an inbound packet. + * + * The matching SA is locked until it is checked in using checkin(). + * If the matching SA is already checked out, this call blocks until the + * SA is checked in. + * + * Since other threads may be waiting for the checked out SA, it should be + * checked in as soon as possible after use. + * + * @param spi SPI (e.g. of an inbound packet) + * @param dst destination address (e.g. of an inbound packet) + * @return the matching IPsec SA, or NULL if none is found + */ + ipsec_sa_t *(*checkout_by_spi)(ipsec_sa_mgr_t *this, u_int32_t spi, + host_t *dst); + + /** + * Checkout an installed IPsec SA by its reqid and inbound/outbound flag. + * Can be used to find the correct SA for an outbound packet. + * + * The matching SA is locked until it is checked in using checkin(). + * If the matching SA is already checked out, this call blocks until the + * SA is checked in. + * + * Since other threads may be waiting for a checked out SA, it should be + * checked in as soon as possible after use. + * + * @param reqid reqid of the SA + * @param inbound TRUE for an inbound SA, FALSE for an outbound SA + * @return the matching IPsec SA, or NULL if none is found + */ + ipsec_sa_t *(*checkout_by_reqid)(ipsec_sa_mgr_t *this, u_int32_t reqid, + bool inbound); + + /** + * Checkin an SA after use. + * + * @param sa checked out SA + */ + void (*checkin)(ipsec_sa_mgr_t *this, ipsec_sa_t *sa); + + /** + * Destroy an ipsec_sa_mgr_t + */ + void (*destroy)(ipsec_sa_mgr_t *this); + +}; + +/** + * Create an ipsec_sa_mgr instance + * + * @return IPsec SA manager instance + */ +ipsec_sa_mgr_t *ipsec_sa_mgr_create(); + +#endif /** IPSEC_SA_MGR_H_ @}*/ |