diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
commit | eb841c5ef668a48782ef1154fda65cb6048f5885 (patch) | |
tree | 00dd0cb4313bf2291d94ed511fe51f0b4bc7ea7a /src/charon/plugins/stroke | |
parent | 738206039047924ae7e4762a53d121be1ca43000 (diff) | |
download | vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.tar.gz vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.zip |
- Updated to new upstream.
Diffstat (limited to 'src/charon/plugins/stroke')
20 files changed, 5633 insertions, 0 deletions
diff --git a/src/charon/plugins/stroke/Makefile.am b/src/charon/plugins/stroke/Makefile.am new file mode 100644 index 000000000..7a341102b --- /dev/null +++ b/src/charon/plugins/stroke/Makefile.am @@ -0,0 +1,19 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke + +AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" + +plugin_LTLIBRARIES = libstrongswan-stroke.la + +libstrongswan_stroke_la_SOURCES = stroke_plugin.h stroke_plugin.c \ + stroke_socket.h stroke_socket.c \ + stroke_config.h stroke_config.c \ + stroke_control.h stroke_control.c \ + stroke_cred.h stroke_cred.c \ + stroke_ca.h stroke_ca.c \ + stroke_attribute.h stroke_attribute.c \ + stroke_list.h stroke_list.c \ + stroke_shared_key.h stroke_shared_key.c + +libstrongswan_stroke_la_LDFLAGS = -module + diff --git a/src/charon/plugins/stroke/Makefile.in b/src/charon/plugins/stroke/Makefile.in new file mode 100644 index 000000000..a528377d0 --- /dev/null +++ b/src/charon/plugins/stroke/Makefile.in @@ -0,0 +1,513 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 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@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@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/charon/plugins/stroke +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_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 = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(plugindir)" +pluginLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(plugin_LTLIBRARIES) +libstrongswan_stroke_la_LIBADD = +am_libstrongswan_stroke_la_OBJECTS = stroke_plugin.lo stroke_socket.lo \ + stroke_config.lo stroke_control.lo stroke_cred.lo stroke_ca.lo \ + stroke_attribute.lo stroke_list.lo stroke_shared_key.lo +libstrongswan_stroke_la_OBJECTS = \ + $(am_libstrongswan_stroke_la_OBJECTS) +libstrongswan_stroke_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libstrongswan_stroke_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +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 = $(libstrongswan_stroke_la_SOURCES) +DIST_SOURCES = $(libstrongswan_stroke_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPSEC_ROUTING_TABLE = @IPSEC_ROUTING_TABLE@ +IPSEC_ROUTING_TABLE_PRIO = @IPSEC_ROUTING_TABLE_PRIO@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LINUX_HEADERS = @LINUX_HEADERS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +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_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gtk_CFLAGS = @gtk_CFLAGS@ +gtk_LIBS = @gtk_LIBS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +ipsecgroup = @ipsecgroup@ +ipsecuser = @ipsecuser@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libstrongswan_plugins = @libstrongswan_plugins@ +linuxdir = @linuxdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +resolv_conf = @resolv_conf@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +simreader = @simreader@ +srcdir = @srcdir@ +strongswan_conf = @strongswan_conf@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +xml_CFLAGS = @xml_CFLAGS@ +xml_LIBS = @xml_LIBS@ +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke +AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" +plugin_LTLIBRARIES = libstrongswan-stroke.la +libstrongswan_stroke_la_SOURCES = stroke_plugin.h stroke_plugin.c \ + stroke_socket.h stroke_socket.c \ + stroke_config.h stroke_config.c \ + stroke_control.h stroke_control.c \ + stroke_cred.h stroke_cred.c \ + stroke_ca.h stroke_ca.c \ + stroke_attribute.h stroke_attribute.c \ + stroke_list.h stroke_list.c \ + stroke_shared_key.h stroke_shared_key.c + +libstrongswan_stroke_la_LDFLAGS = -module +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/charon/plugins/stroke/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/charon/plugins/stroke/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 +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)" + @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(plugindir)/$$f"; \ + else :; fi; \ + done + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$p"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_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 +libstrongswan-stroke.la: $(libstrongswan_stroke_la_OBJECTS) $(libstrongswan_stroke_la_DEPENDENCIES) + $(libstrongswan_stroke_la_LINK) -rpath $(plugindir) $(libstrongswan_stroke_la_OBJECTS) $(libstrongswan_stroke_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_attribute.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_ca.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_control.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_cred.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_shared_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_socket.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(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@ mv -f $(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@ mv -f $(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 + +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; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + 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; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && 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 $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(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) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-exec-am: + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pluginLTLIBRARIES \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-pluginLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/charon/plugins/stroke/stroke_attribute.c b/src/charon/plugins/stroke/stroke_attribute.c new file mode 100644 index 000000000..71b56bc8a --- /dev/null +++ b/src/charon/plugins/stroke/stroke_attribute.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_attribute.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/mutex.h> + +#define POOL_LIMIT 16 + +typedef struct private_stroke_attribute_t private_stroke_attribute_t; + +/** + * private data of stroke_attribute + */ +struct private_stroke_attribute_t { + + /** + * public functions + */ + stroke_attribute_t public; + + /** + * list of pools, contains pool_t + */ + linked_list_t *pools; + + /** + * mutex to lock access to pools + */ + mutex_t *mutex; +}; + +typedef struct { + /** name of the pool */ + char *name; + /** base address of the pool */ + host_t *base; + /** number of entries in the pool */ + int count; + /** array of in-use flags, TODO: use bit fields */ + u_int8_t *in_use; +} pool_t; + +/** + * destroy a pool_t + */ +static void pool_destroy(pool_t *this) +{ + DESTROY_IF(this->base); + free(this->name); + free(this->in_use); + free(this); +} + +/** + * find a pool by name + */ +static pool_t *find_pool(private_stroke_attribute_t *this, char *name) +{ + enumerator_t *enumerator; + pool_t *current, *found = NULL; + + enumerator = this->pools->create_enumerator(this->pools); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(name, current->name)) + { + found = current; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * convert an pool offset to an address + */ +host_t* offset2host(pool_t *pool, int offset) +{ + chunk_t addr; + host_t *host; + u_int32_t *pos; + + if (offset > pool->count) + { + return NULL; + } + + addr = chunk_clone(pool->base->get_address(pool->base)); + if (pool->base->get_family(pool->base) == AF_INET6) + { + pos = (u_int32_t*)(addr.ptr + 12); + } + else + { + pos = (u_int32_t*)addr.ptr; + } + *pos = htonl(offset + ntohl(*pos)); + host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0); + free(addr.ptr); + return host; +} + +/** + * convert a host to a pool offset + */ +int host2offset(pool_t *pool, host_t *addr) +{ + chunk_t host, base; + u_int32_t hosti, basei; + + if (addr->get_family(addr) != pool->base->get_family(pool->base)) + { + return -1; + } + host = addr->get_address(addr); + base = pool->base->get_address(pool->base); + if (addr->get_family(addr) == AF_INET6) + { + /* only look at last /32 block */ + if (!memeq(host.ptr, base.ptr, 12)) + { + return -1; + } + host = chunk_skip(host, 12); + base = chunk_skip(base, 12); + } + hosti = ntohl(*(u_int32_t*)(host.ptr)); + basei = ntohl(*(u_int32_t*)(base.ptr)); + if (hosti > basei + pool->count) + { + return -1; + } + return hosti - basei; +} + +/** + * Implementation of attribute_provider_t.acquire_address + */ +static host_t* acquire_address(private_stroke_attribute_t *this, + char *name, identification_t *id, + auth_info_t *auth, host_t *requested) +{ + pool_t *pool; + host_t *host = NULL; + int i; + + this->mutex->lock(this->mutex); + pool = find_pool(this, name); + if (pool) + { + if (requested && !requested->is_anyaddr(requested)) + { + if (pool->count == 0) + { /* %config, give any */ + host = requested->clone(requested); + } + else + { + i = host2offset(pool, requested); + if (i >= 0 && !pool->in_use[i]) + { + pool->in_use[i] = TRUE; + host = requested->clone(requested); + } + } + } + if (!host) + { + for (i = 0; i < pool->count; i++) + { + if (!pool->in_use[i]) + { + pool->in_use[i] = TRUE; + host = offset2host(pool, i); + break; + } + } + } + } + this->mutex->unlock(this->mutex); + return host; +} + +/** + * Implementation of attribute_provider_t.release_address + */ +static bool release_address(private_stroke_attribute_t *this, + char *name, host_t *address) +{ + pool_t *pool; + bool found = FALSE; + int i; + + this->mutex->lock(this->mutex); + pool = find_pool(this, name); + if (pool) + { + if (pool->count != 0) + { + i = host2offset(pool, address); + if (i >= 0 && pool->in_use[i]) + { + pool->in_use[i] = FALSE; + found = TRUE; + } + } + } + this->mutex->unlock(this->mutex); + return found; +} + +/** + * Implementation of stroke_attribute_t.add_pool. + */ +static void add_pool(private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + if (msg->add_conn.other.sourceip_size) + { + pool_t *pool; + + if (msg->add_conn.other.sourceip) + { + u_int32_t bits; + int family; + + DBG1(DBG_CFG, "adding virtual IP address pool '%s': %s/%d", + msg->add_conn.name, msg->add_conn.other.sourceip, + msg->add_conn.other.sourceip_size); + + pool = malloc_thing(pool_t); + pool->base = host_create_from_string(msg->add_conn.other.sourceip, 0); + if (!pool->base) + { + free(pool); + DBG1(DBG_CFG, "virtual IP address invalid, discarded"); + return; + } + pool->name = strdup(msg->add_conn.name); + family = pool->base->get_family(pool->base); + bits = (family == AF_INET ? 32 : 128) - msg->add_conn.other.sourceip_size; + if (bits > POOL_LIMIT) + { + bits = POOL_LIMIT; + DBG1(DBG_CFG, "virtual IP pool to large, limiting to %s/%d", + msg->add_conn.other.sourceip, + (family == AF_INET ? 32 : 128) - bits); + } + pool->count = 1 << (bits); + pool->in_use = calloc(pool->count, sizeof(u_int8_t)); + + if (pool->count > 2) + { /* do not use first and last addresses of a block */ + pool->in_use[0] = TRUE; + pool->in_use[pool->count-1] = TRUE; + } + } + else + { /* %config, add an empty pool */ + pool = malloc_thing(pool_t); + pool->name = strdup(msg->add_conn.name); + pool->base = NULL; + pool->count = 0; + pool->in_use = NULL; + } + this->mutex->lock(this->mutex); + this->pools->insert_last(this->pools, pool); + this->mutex->unlock(this->mutex); + } +} + +/** + * Implementation of stroke_attribute_t.del_pool. + */ +static void del_pool(private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + pool_t *pool; + + this->mutex->lock(this->mutex); + enumerator = this->pools->create_enumerator(this->pools); + while (enumerator->enumerate(enumerator, &pool)) + { + if (streq(msg->del_conn.name, pool->name)) + { + this->pools->remove_at(this->pools, enumerator); + pool_destroy(pool); + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of stroke_attribute_t.destroy + */ +static void destroy(private_stroke_attribute_t *this) +{ + this->mutex->destroy(this->mutex); + this->pools->destroy_function(this->pools, (void*)pool_destroy); + free(this); +} + +/* + * see header file + */ +stroke_attribute_t *stroke_attribute_create() +{ + private_stroke_attribute_t *this = malloc_thing(private_stroke_attribute_t); + + this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *,auth_info_t *, host_t *))acquire_address; + this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *))release_address; + this->public.add_pool = (void(*)(stroke_attribute_t*, stroke_msg_t *msg))add_pool; + this->public.del_pool = (void(*)(stroke_attribute_t*, stroke_msg_t *msg))del_pool; + this->public.destroy = (void(*)(stroke_attribute_t*))destroy; + + this->pools = linked_list_create(); + this->mutex = mutex_create(MUTEX_DEFAULT); + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_attribute.h b/src/charon/plugins/stroke/stroke_attribute.h new file mode 100644 index 000000000..f871d5a13 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_attribute.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_attribute stroke_attribute + * @{ @ingroup stroke + */ + +#ifndef STROKE_ATTRIBUTE_H_ +#define STROKE_ATTRIBUTE_H_ + +#include <stroke_msg.h> +#include <config/attributes/attribute_provider.h> + +typedef struct stroke_attribute_t stroke_attribute_t; + +/** + * Stroke IKEv2 cfg attribute provider + */ +struct stroke_attribute_t { + + /** + * Implements attribute provider interface + */ + attribute_provider_t provider; + + /** + * Add a virtual IP address. + * + * @param msg stroke message + * @param end end of stroke message that contains virtual IP. + */ + void (*add_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + + /** + * Remove a virtual IP address. + * + * @param msg stroke message + */ + void (*del_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + + /** + * Destroy a stroke_attribute instance. + */ + void (*destroy)(stroke_attribute_t *this); +}; + +/** + * Create a stroke_attribute instance. + */ +stroke_attribute_t *stroke_attribute_create(); + +#endif /* STROKE_ATTRIBUTE_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_ca.c b/src/charon/plugins/stroke/stroke_ca.c new file mode 100644 index 000000000..897365eb0 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_ca.c @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_ca.h" +#include "stroke_cred.h" + +#include <utils/mutex.h> +#include <utils/linked_list.h> +#include <crypto/hashers/hasher.h> + +#include <daemon.h> + +typedef struct private_stroke_ca_t private_stroke_ca_t; + +/** + * private data of stroke_ca + */ +struct private_stroke_ca_t { + + /** + * public functions + */ + stroke_ca_t public; + + /** + * mutex to lock access to list + */ + mutex_t *mutex; + + /** + * list of starters CA sections and its certificates (ca_section_t) + */ + linked_list_t *sections; + + /** + * stroke credentials, stores our CA certificates + */ + stroke_cred_t *cred; +}; + +typedef struct ca_section_t ca_section_t; + +/** + * loaded ipsec.conf CA sections + */ +struct ca_section_t { + + /** + * name of the CA section + */ + char *name; + + /** + * reference to cert in trusted_credential_t + */ + certificate_t *cert; + + /** + * CRL URIs + */ + linked_list_t *crl; + + /** + * OCSP URIs + */ + linked_list_t *ocsp; + + /** + * Hashes of certificates issued by this CA + */ + linked_list_t *hashes; + + /** + * Base URI used for certificates from this CA + */ + char *certuribase; +}; + +/** + * create a new CA section + */ +static ca_section_t *ca_section_create(char *name, certificate_t *cert) +{ + ca_section_t *ca = malloc_thing(ca_section_t); + + ca->name = strdup(name); + ca->crl = linked_list_create(); + ca->ocsp = linked_list_create(); + ca->cert = cert; + ca->hashes = linked_list_create(); + ca->certuribase = NULL; + return ca; +} + +/** + * destroy a ca section entry + */ +static void ca_section_destroy(ca_section_t *this) +{ + this->crl->destroy_function(this->crl, free); + this->ocsp->destroy_function(this->ocsp, free); + this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy)); + free(this->certuribase); + free(this->name); + free(this); +} + +/** + * data to pass to create_inner_cdp + */ +typedef struct { + private_stroke_ca_t *this; + certificate_type_t type; + identification_t *id; +} cdp_data_t; + +/** + * destroy cdp enumerator data and unlock list + */ +static void cdp_data_destroy(cdp_data_t *data) +{ + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * inner enumerator constructor for CDP URIs + */ +static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data) +{ + public_key_t *public; + identification_t *keyid; + enumerator_t *enumerator = NULL; + linked_list_t *list; + + if (data->type == CERT_X509_OCSP_RESPONSE) + { + list = section->ocsp; + } + else + { + list = section->crl; + } + + public = section->cert->get_public_key(section->cert); + if (public) + { + if (!data->id) + { + enumerator = list->create_enumerator(list); + } + else + { + keyid = public->get_id(public, data->id->get_type(data->id)); + if (keyid && keyid->matches(keyid, data->id)) + { + enumerator = list->create_enumerator(list); + } + } + public->destroy(public); + } + return enumerator; +} + +/** + * inner enumerator constructor for "Hash and URL" + */ +static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data) +{ + enumerator_t *enumerator = NULL, *hash_enum; + identification_t *current; + + if (!data->id || !section->certuribase) + { + return NULL; + } + + hash_enum = section->hashes->create_enumerator(section->hashes); + while (hash_enum->enumerate(hash_enum, ¤t)) + { + if (current->matches(current, data->id)) + { + char *url, *hash; + + url = malloc(strlen(section->certuribase) + 40 + 1); + strcpy(url, section->certuribase); + hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr; + strncat(url, hash, 40); + free(hash); + + enumerator = enumerator_create_single(url, free); + break; + } + } + hash_enum->destroy(hash_enum); + return enumerator; +} + +/** + * Implementation of credential_set_t.create_cdp_enumerator. + */ +static enumerator_t *create_cdp_enumerator(private_stroke_ca_t *this, + certificate_type_t type, identification_t *id) +{ + cdp_data_t *data; + + switch (type) + { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */ + case CERT_X509: + case CERT_X509_CRL: + case CERT_X509_OCSP_RESPONSE: + case CERT_ANY: + break; + default: + return NULL; + } + data = malloc_thing(cdp_data_t); + data->this = this; + data->type = type; + data->id = id; + + this->mutex->lock(this->mutex); + return enumerator_create_nested(this->sections->create_enumerator(this->sections), + (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp, + data, (void*)cdp_data_destroy); +} +/** + * Implementation of stroke_ca_t.add. + */ +static void add(private_stroke_ca_t *this, stroke_msg_t *msg) +{ + certificate_t *cert; + ca_section_t *ca; + + if (msg->add_ca.cacert == NULL) + { + DBG1(DBG_CFG, "missing cacert parameter"); + return; + } + cert = this->cred->load_ca(this->cred, msg->add_ca.cacert); + if (cert) + { + ca = ca_section_create(msg->add_ca.name, cert); + if (msg->add_ca.crluri) + { + ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri)); + } + if (msg->add_ca.crluri2) + { + ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2)); + } + if (msg->add_ca.ocspuri) + { + ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri)); + } + if (msg->add_ca.ocspuri2) + { + ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2)); + } + if (msg->add_ca.certuribase) + { + ca->certuribase = strdup(msg->add_ca.certuribase); + } + this->mutex->lock(this->mutex); + this->sections->insert_last(this->sections, ca); + this->mutex->unlock(this->mutex); + DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name); + } +} + +/** + * Implementation of stroke_ca_t.del. + */ +static void del(private_stroke_ca_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + ca_section_t *ca = NULL; + + this->mutex->lock(this->mutex); + enumerator = this->sections->create_enumerator(this->sections); + while (enumerator->enumerate(enumerator, &ca)) + { + if (streq(ca->name, msg->del_ca.name)) + { + this->sections->remove_at(this->sections, enumerator); + break; + } + ca = NULL; + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + if (ca == NULL) + { + DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name); + return; + } + ca_section_destroy(ca); + /* TODO: flush cached certs */ +} + +/** + * list crl or ocsp URIs + */ +static void list_uris(linked_list_t *list, char *label, FILE *out) +{ + bool first = TRUE; + char *uri; + enumerator_t *enumerator; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, (void**)&uri)) + { + if (first) + { + fprintf(out, label); + first = FALSE; + } + else + { + fprintf(out, " "); + } + fprintf(out, "'%s'\n", uri); + } + enumerator->destroy(enumerator); +} + +/** + * Implementation of stroke_ca_t.check_for_hash_and_url. + */ +static void check_for_hash_and_url(private_stroke_ca_t *this, certificate_t* cert) +{ + ca_section_t *section; + enumerator_t *enumerator; + + hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (hasher == NULL) + { + DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported"); + return; + } + + this->mutex->lock(this->mutex); + enumerator = this->sections->create_enumerator(this->sections); + while (enumerator->enumerate(enumerator, (void**)§ion)) + { + if (section->certuribase && cert->issued_by(cert, section->cert)) + { + chunk_t hash, encoded = cert->get_encoding(cert); + hasher->allocate_hash(hasher, encoded, &hash); + section->hashes->insert_last(section->hashes, + identification_create_from_encoding(ID_CERT_DER_SHA1, hash)); + chunk_free(&hash); + chunk_free(&encoded); + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + hasher->destroy(hasher); +} + +/** + * Implementation of stroke_ca_t.list. + */ +static void list(private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out) +{ + bool first = TRUE; + ca_section_t *section; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->sections->create_enumerator(this->sections); + while (enumerator->enumerate(enumerator, (void**)§ion)) + { + certificate_t *cert = section->cert; + public_key_t *public = cert->get_public_key(cert); + + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of CA Information Sections:\n"); + first = FALSE; + } + fprintf(out, "\n"); + fprintf(out, " authname: \"%D\"\n", cert->get_subject(cert)); + + /* list authkey and keyid */ + if (public) + { + fprintf(out, " authkey: %D\n", + public->get_id(public, ID_PUBKEY_SHA1)); + fprintf(out, " keyid: %D\n", + public->get_id(public, ID_PUBKEY_INFO_SHA1)); + public->destroy(public); + } + list_uris(section->crl, " crluris: ", out); + list_uris(section->ocsp, " ocspuris: ", out); + if (section->certuribase) + { + fprintf(out, " certuribase: '%s'\n", section->certuribase); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of stroke_ca_t.destroy + */ +static void destroy(private_stroke_ca_t *this) +{ + this->sections->destroy_function(this->sections, (void*)ca_section_destroy); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * see header file + */ +stroke_ca_t *stroke_ca_create(stroke_cred_t *cred) +{ + private_stroke_ca_t *this = malloc_thing(private_stroke_ca_t); + + this->public.set.create_private_enumerator = (void*)return_null; + this->public.set.create_cert_enumerator = (void*)return_null; + this->public.set.create_shared_enumerator = (void*)return_null; + this->public.set.create_cdp_enumerator = (void*)create_cdp_enumerator; + this->public.set.cache_cert = (void*)nop; + this->public.add = (void(*)(stroke_ca_t*, stroke_msg_t *msg))add; + this->public.del = (void(*)(stroke_ca_t*, stroke_msg_t *msg))del; + this->public.list = (void(*)(stroke_ca_t*, stroke_msg_t *msg, FILE *out))list; + this->public.check_for_hash_and_url = (void(*)(stroke_ca_t*, certificate_t*))check_for_hash_and_url; + this->public.destroy = (void(*)(stroke_ca_t*))destroy; + + this->sections = linked_list_create(); + this->mutex = mutex_create(MUTEX_RECURSIVE); + this->cred = cred; + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_ca.h b/src/charon/plugins/stroke/stroke_ca.h new file mode 100644 index 000000000..882446afe --- /dev/null +++ b/src/charon/plugins/stroke/stroke_ca.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_ca stroke_ca + * @{ @ingroup stroke + */ + +#ifndef STROKE_CA_H_ +#define STROKE_CA_H_ + +#include <stroke_msg.h> + +#include "stroke_cred.h" + +typedef struct stroke_ca_t stroke_ca_t; + +/** + * ipsec.conf ca section handling. + */ +struct stroke_ca_t { + + /** + * Implements credential_set_t + */ + credential_set_t set; + + /** + * Add a CA to the set using a stroke_msg_t. + * + * @param msg stroke message containing CA info + */ + void (*add)(stroke_ca_t *this, stroke_msg_t *msg); + + /** + * Remove a CA from the set using a stroke_msg_t. + * + * @param msg stroke message containing CA info + */ + void (*del)(stroke_ca_t *this, stroke_msg_t *msg); + + /** + * List CA sections to stroke console. + * + * @param msg stroke message + */ + void (*list)(stroke_ca_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Check if a certificate can be made available through hash and URL. + * + * @param cert peer certificate + */ + void (*check_for_hash_and_url)(stroke_ca_t *this, certificate_t* cert); + + /** + * Destroy a stroke_ca instance. + */ + void (*destroy)(stroke_ca_t *this); +}; + +/** + * Create a stroke_ca instance. + */ +stroke_ca_t *stroke_ca_create(stroke_cred_t *cred); + +#endif /* STROKE_CA_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_config.c b/src/charon/plugins/stroke/stroke_config.c new file mode 100644 index 000000000..0069191b5 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_config.c @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_config.h" + +#include <daemon.h> +#include <utils/mutex.h> + +typedef struct private_stroke_config_t private_stroke_config_t; + +/** + * private data of stroke_config + */ +struct private_stroke_config_t { + + /** + * public functions + */ + stroke_config_t public; + + /** + * list of peer_cfg_t + */ + linked_list_t *list; + + /** + * mutex to lock config list + */ + mutex_t *mutex; + + /** + * ca sections + */ + stroke_ca_t *ca; + + /** + * credentials + */ + stroke_cred_t *cred; +}; + +/** + * data to pass peer_filter + */ +typedef struct { + private_stroke_config_t *this; + identification_t *me; + identification_t *other; +} peer_data_t; + +/** + * destroy id enumerator data and unlock list + */ +static void peer_data_destroy(peer_data_t *data) +{ + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * filter function for peer configs + */ +static bool peer_filter(peer_data_t *data, peer_cfg_t **in, peer_cfg_t **out) +{ + bool match_me = FALSE, match_other = FALSE; + identification_t *me, *other; + + me = (*in)->get_my_id(*in); + other = (*in)->get_other_id(*in); + + /* own ID may have wildcards in data (no IDr payload) or in config */ + match_me = (!data->me || data->me->matches(data->me, me) || + me->matches(me, data->me)); + /* others ID has wildcards in config only */ + match_other = (!data->other || data->other->matches(data->other, other)); + + if (match_me && match_other) + { + *out = *in; + return TRUE; + } + return FALSE; +} + +/** + * Implementation of backend_t.create_peer_cfg_enumerator. + */ +static enumerator_t* create_peer_cfg_enumerator(private_stroke_config_t *this, + identification_t *me, + identification_t *other) +{ + peer_data_t *data; + + data = malloc_thing(peer_data_t); + data->this = this; + data->me = me; + data->other = other; + + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->list->create_enumerator(this->list), + (void*)peer_filter, data, + (void*)peer_data_destroy); +} + +/** + * data to pass ike_filter + */ +typedef struct { + private_stroke_config_t *this; + host_t *me; + host_t *other; +} ike_data_t; + +/** + * destroy id enumerator data and unlock list + */ +static void ike_data_destroy(ike_data_t *data) +{ + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * filter function for ike configs + */ +static bool ike_filter(ike_data_t *data, peer_cfg_t **in, ike_cfg_t **out) +{ + *out = (*in)->get_ike_cfg(*in); + return TRUE; +} + +/** + * Implementation of backend_t.create_ike_cfg_enumerator. + */ +static enumerator_t* create_ike_cfg_enumerator(private_stroke_config_t *this, + host_t *me, host_t *other) +{ + ike_data_t *data; + + data = malloc_thing(ike_data_t); + data->this = this; + data->me = me; + data->other = other; + + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->list->create_enumerator(this->list), + (void*)ike_filter, data, + (void*)ike_data_destroy); +} + +/** + * implements backend_t.get_peer_cfg_by_name. + */ +static peer_cfg_t *get_peer_cfg_by_name(private_stroke_config_t *this, char *name) +{ + enumerator_t *e1, *e2; + peer_cfg_t *current, *found = NULL; + child_cfg_t *child; + + this->mutex->lock(this->mutex); + e1 = this->list->create_enumerator(this->list); + while (e1->enumerate(e1, ¤t)) + { + /* compare peer_cfgs name first */ + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + /* compare all child_cfg names otherwise */ + e2 = current->create_child_cfg_enumerator(current); + while (e2->enumerate(e2, &child)) + { + if (streq(child->get_name(child), name)) + { + found = current; + found->get_ref(found); + break; + } + } + e2->destroy(e2); + if (found) + { + break; + } + } + e1->destroy(e1); + this->mutex->unlock(this->mutex); + return found; +} + +/** + * check if a certificate has an ID + */ +static identification_t *update_peerid(certificate_t *cert, identification_t *id) +{ + if (!cert->has_subject(cert, id)) + { + DBG1(DBG_CFG, " peerid %D not confirmed by certificate, " + "defaulting to subject DN", id); + id->destroy(id); + id = cert->get_subject(cert); + return id->clone(id); + } + return id; +} + +/** + * parse a proposal string, either into ike_cfg or child_cfg + */ +static void add_proposals(private_stroke_config_t *this, char *string, + ike_cfg_t *ike_cfg, child_cfg_t *child_cfg) +{ + if (string) + { + char *single; + char *strict; + proposal_t *proposal; + protocol_id_t proto = PROTO_ESP; + + if (ike_cfg) + { + proto = PROTO_IKE; + } + strict = string + strlen(string) - 1; + if (*strict == '!') + { + *strict = '\0'; + } + else + { + strict = NULL; + } + while ((single = strsep(&string, ","))) + { + proposal = proposal_create_from_string(proto, single); + if (proposal) + { + if (ike_cfg) + { + ike_cfg->add_proposal(ike_cfg, proposal); + } + else + { + child_cfg->add_proposal(child_cfg, proposal); + } + continue; + } + DBG1(DBG_CFG, "skipped invalid proposal string: %s", single); + } + if (strict) + { + return; + } + /* add default porposal to the end if not strict */ + } + if (ike_cfg) + { + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + } + else + { + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + } +} + +/** + * Build an IKE config from a stroke message + */ +static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg) +{ + stroke_end_t tmp_end; + ike_cfg_t *ike_cfg; + char *interface; + host_t *host; + + host = host_create_from_dns(msg->add_conn.other.address, 0, 0); + if (host) + { + interface = charon->kernel_interface->get_interface( + charon->kernel_interface, host); + host->destroy(host); + if (interface) + { + DBG2(DBG_CFG, "left is other host, swapping ends"); + tmp_end = msg->add_conn.me; + msg->add_conn.me = msg->add_conn.other; + msg->add_conn.other = tmp_end; + free(interface); + } + else + { + host = host_create_from_dns(msg->add_conn.me.address, 0, 0); + if (host) + { + interface = charon->kernel_interface->get_interface( + charon->kernel_interface, host); + host->destroy(host); + if (!interface) + { + DBG1(DBG_CFG, "left nor right host is our side, " + "assuming left=local"); + } + else + { + free(interface); + } + + } + } + } + ike_cfg = ike_cfg_create(msg->add_conn.other.sendcert != CERT_NEVER_SEND, + msg->add_conn.force_encap, + msg->add_conn.me.address, + msg->add_conn.other.address); + add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL); + return ike_cfg; +} +/** + * build a peer_cfg from a stroke msg + */ +static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, + stroke_msg_t *msg, ike_cfg_t *ike_cfg) +{ + identification_t *me, *other, *peer_id = NULL; + peer_cfg_t *mediated_by = NULL; + host_t *vip = NULL; + certificate_t *cert; + unique_policy_t unique; + u_int32_t rekey = 0, reauth = 0, over, jitter; + + me = identification_create_from_string(msg->add_conn.me.id ? + msg->add_conn.me.id : msg->add_conn.me.address); + if (!me) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.me.id); + return NULL; + } + other = identification_create_from_string(msg->add_conn.other.id ? + msg->add_conn.other.id : msg->add_conn.other.address); + if (!other) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.other.id); + me->destroy(me); + return NULL; + } + + +#ifdef ME + if (msg->add_conn.ikeme.mediation && msg->add_conn.ikeme.mediated_by) + { + DBG1(DBG_CFG, "a mediation connection cannot be a" + " mediated connection at the same time, aborting"); + me->destroy(me); + other->destroy(other); + return NULL; + } + + if (msg->add_conn.ikeme.mediated_by) + { + mediated_by = charon->backends->get_peer_cfg_by_name(charon->backends, + msg->add_conn.ikeme.mediated_by); + if (!mediated_by) + { + DBG1(DBG_CFG, "mediation connection '%s' not found, aborting", + msg->add_conn.ikeme.mediated_by); + me->destroy(me); + other->destroy(other); + return NULL; + } + + if (!mediated_by->is_mediation(mediated_by)) + { + DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is" + "no mediation connection, aborting", + msg->add_conn.ikeme.mediated_by, msg->add_conn.name); + mediated_by->destroy(mediated_by); + me->destroy(me); + other->destroy(other); + return NULL; + } + } + + if (msg->add_conn.ikeme.peerid) + { + peer_id = identification_create_from_string(msg->add_conn.ikeme.peerid); + if (!peer_id) + { + DBG1(DBG_CFG, "invalid peer ID: %s\n", msg->add_conn.ikeme.peerid); + mediated_by->destroy(mediated_by); + me->destroy(me); + other->destroy(other); + return NULL; + } + } + else + { + /* no peer ID supplied, assume right ID */ + peer_id = other->clone(other); + } +#endif /* ME */ + + if (msg->add_conn.me.cert) + { + cert = this->cred->load_peer(this->cred, msg->add_conn.me.cert); + if (cert) + { + this->ca->check_for_hash_and_url(this->ca, cert); + me = update_peerid(cert, me); + cert->destroy(cert); + } + } + if (msg->add_conn.other.cert) + { + cert = this->cred->load_peer(this->cred, msg->add_conn.other.cert); + if (cert) + { + other = update_peerid(cert, other); + cert->destroy(cert); + } + } + jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100; + over = msg->add_conn.rekey.margin; + if (msg->add_conn.rekey.reauth) + { + reauth = msg->add_conn.rekey.ike_lifetime - over; + } + else + { + rekey = msg->add_conn.rekey.ike_lifetime - over; + } + if (msg->add_conn.me.sourceip_size) + { + if (msg->add_conn.me.sourceip) + { + vip = host_create_from_string(msg->add_conn.me.sourceip, 0); + } + if (!vip) + { /* if it is set to something like %poolname, request an address */ + if (msg->add_conn.me.subnets) + { /* use the same address as in subnet, if any */ + if (strchr(msg->add_conn.me.subnets, '.')) + { + vip = host_create_any(AF_INET); + } + else + { + vip = host_create_any(AF_INET6); + } + } + else + { + if (strchr(ike_cfg->get_my_addr(ike_cfg), ':')) + { + vip = host_create_any(AF_INET6); + } + else + { + vip = host_create_any(AF_INET); + } + } + } + } + switch (msg->add_conn.unique) + { + case 1: /* yes */ + case 2: /* replace */ + unique = UNIQUE_REPLACE; + break; + case 3: /* keep */ + unique = UNIQUE_KEEP; + break; + default: /* no */ + unique = UNIQUE_NO; + break; + } + if (msg->add_conn.dpd.action == 0) + { /* dpdaction=none disables DPD */ + msg->add_conn.dpd.delay = 0; + } + + /* other.sourceip is managed in stroke_attributes. If it is set, we define + * the pool name as the connection name, which the attribute provider + * uses to serve pool addresses. */ + return peer_cfg_create(msg->add_conn.name, + msg->add_conn.ikev2 ? 2 : 1, ike_cfg, me, other, + msg->add_conn.me.sendcert, unique, msg->add_conn.auth_method, + msg->add_conn.eap_type, msg->add_conn.eap_vendor, + msg->add_conn.rekey.tries, rekey, reauth, jitter, over, + msg->add_conn.mobike, msg->add_conn.dpd.delay, + vip, msg->add_conn.other.sourceip_size ? + msg->add_conn.name : msg->add_conn.other.sourceip, + msg->add_conn.ikeme.mediation, mediated_by, peer_id); +} + +/** + * fill in auth_info from stroke message + */ +static void build_auth_info(private_stroke_config_t *this, + stroke_msg_t *msg, auth_info_t *auth) +{ + identification_t *my_ca = NULL, *other_ca = NULL; + bool my_ca_same = FALSE; + bool other_ca_same = FALSE; + cert_validation_t valid; + + if (msg->add_conn.other.groups) + { + /* TODO: AC groups */ + } + + switch (msg->add_conn.crl_policy) + { + case CRL_STRICT_YES: + valid = VALIDATION_GOOD; + auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid); + break; + case CRL_STRICT_IFURI: + valid = VALIDATION_SKIPPED; + auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid); + break; + default: + break; + } + + if (msg->add_conn.me.ca) + { + if (streq(msg->add_conn.me.ca, "%same")) + { + my_ca_same = TRUE; + } + else + { + my_ca = identification_create_from_string(msg->add_conn.me.ca); + } + } + if (msg->add_conn.other.ca) + { + if (streq(msg->add_conn.other.ca, "%same")) + { + other_ca_same = TRUE; + } + else + { + other_ca = identification_create_from_string(msg->add_conn.other.ca); + } + } + if (other_ca_same && my_ca) + { + other_ca = my_ca->clone(my_ca); + } + else if (my_ca_same && other_ca) + { + my_ca = other_ca->clone(other_ca); + } + + if (other_ca) + { + DBG2(DBG_CFG, " other ca: %D", other_ca); + certificate_t *cert = charon->credentials->get_cert(charon->credentials, + CERT_X509, KEY_ANY, other_ca, TRUE); + if (cert) + { + auth->add_item(auth, AUTHZ_CA_CERT, cert); + cert->destroy(cert); + } + else + { + auth->add_item(auth, AUTHZ_CA_CERT_NAME, other_ca); + } + other_ca->destroy(other_ca); + } + if (my_ca) + { + DBG2(DBG_CFG, " my ca: %D", my_ca); + certificate_t *cert = charon->credentials->get_cert(charon->credentials, + CERT_X509, KEY_ANY, my_ca, TRUE); + if (cert) + { + auth->add_item(auth, AUTHN_CA_CERT, cert); + cert->destroy(cert); + } + else + { + auth->add_item(auth, AUTHN_CA_CERT_NAME, my_ca); + } + my_ca->destroy(my_ca); + } +} + +/** + * build a traffic selector from a stroke_end + */ +static void add_ts(private_stroke_config_t *this, + stroke_end_t *end, child_cfg_t *child_cfg, bool local) +{ + traffic_selector_t *ts; + + if (end->tohost) + { + ts = traffic_selector_create_dynamic(end->protocol, + end->port ? end->port : 0, end->port ? end->port : 65535); + child_cfg->add_traffic_selector(child_cfg, local, ts); + } + else + { + host_t *net; + + if (!end->subnets) + { + net = host_create_from_string(end->address, IKEV2_UDP_PORT); + if (net) + { + ts = traffic_selector_create_from_subnet(net, 0, end->protocol, + end->port); + child_cfg->add_traffic_selector(child_cfg, local, ts); + } + } + else + { + char *del, *start, *bits; + + start = end->subnets; + do + { + int intbits = 0; + + del = strchr(start, ','); + if (del) + { + *del = '\0'; + } + bits = strchr(start, '/'); + if (bits) + { + *bits = '\0'; + intbits = atoi(bits + 1); + } + + net = host_create_from_string(start, IKEV2_UDP_PORT); + if (net) + { + ts = traffic_selector_create_from_subnet(net, intbits, + end->protocol, end->port); + child_cfg->add_traffic_selector(child_cfg, local, ts); + } + else + { + DBG1(DBG_CFG, "invalid subnet: %s, skipped", start); + } + start = del + 1; + } + while (del); + } + } +} + +/** + * build a child config from the stroke message + */ +static child_cfg_t *build_child_cfg(private_stroke_config_t *this, + stroke_msg_t *msg) +{ + child_cfg_t *child_cfg; + action_t dpd; + + switch (msg->add_conn.dpd.action) + { /* map startes magic values to our action type */ + case 2: /* =hold */ + dpd = ACTION_ROUTE; + break; + case 3: /* =restart */ + dpd = ACTION_RESTART; + break; + default: + dpd = ACTION_NONE; + break; + } + child_cfg = child_cfg_create( + msg->add_conn.name, msg->add_conn.rekey.ipsec_lifetime, + msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin, + msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100, + msg->add_conn.me.updown, msg->add_conn.me.hostaccess, + msg->add_conn.mode, dpd, ACTION_NONE, msg->add_conn.ipcomp); + + add_ts(this, &msg->add_conn.me, child_cfg, TRUE); + add_ts(this, &msg->add_conn.other, child_cfg, FALSE); + + add_proposals(this, msg->add_conn.algorithms.esp, NULL, child_cfg); + + return child_cfg; +} + +/** + * Implementation of stroke_config_t.add. + */ +static void add(private_stroke_config_t *this, stroke_msg_t *msg) +{ + ike_cfg_t *ike_cfg, *existing_ike; + peer_cfg_t *peer_cfg, *existing; + child_cfg_t *child_cfg; + enumerator_t *enumerator; + bool use_existing = FALSE; + + ike_cfg = build_ike_cfg(this, msg); + if (!ike_cfg) + { + return; + } + peer_cfg = build_peer_cfg(this, msg, ike_cfg); + if (!peer_cfg) + { + ike_cfg->destroy(ike_cfg); + return; + } + + build_auth_info(this, msg, peer_cfg->get_auth(peer_cfg)); + enumerator = create_peer_cfg_enumerator(this, NULL, NULL); + while (enumerator->enumerate(enumerator, &existing)) + { + existing_ike = existing->get_ike_cfg(existing); + if (existing->equals(existing, peer_cfg) && + existing_ike->equals(existing_ike, peer_cfg->get_ike_cfg(peer_cfg))) + { + use_existing = TRUE; + peer_cfg->destroy(peer_cfg); + peer_cfg = existing; + peer_cfg->get_ref(peer_cfg); + DBG1(DBG_CFG, "added child to existing configuration '%s'", + peer_cfg->get_name(peer_cfg)); + break; + } + } + enumerator->destroy(enumerator); + + child_cfg = build_child_cfg(this, msg); + if (!child_cfg) + { + peer_cfg->destroy(peer_cfg); + return; + } + peer_cfg->add_child_cfg(peer_cfg, child_cfg); + + if (use_existing) + { + peer_cfg->destroy(peer_cfg); + } + else + { + /* add config to backend */ + DBG1(DBG_CFG, "added configuration '%s': %s[%D]...%s[%D]", msg->add_conn.name, + ike_cfg->get_my_addr(ike_cfg), peer_cfg->get_my_id(peer_cfg), + ike_cfg->get_other_addr(ike_cfg), peer_cfg->get_other_id(peer_cfg)); + this->mutex->lock(this->mutex); + this->list->insert_last(this->list, peer_cfg); + this->mutex->unlock(this->mutex); + } +} + +/** + * Implementation of stroke_config_t.del. + */ +static void del(private_stroke_config_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator, *children; + peer_cfg_t *peer; + child_cfg_t *child; + + this->mutex->lock(this->mutex); + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, (void**)&peer)) + { + /* remove peer config with such a name */ + if (streq(peer->get_name(peer), msg->del_conn.name)) + { + this->list->remove_at(this->list, enumerator); + peer->destroy(peer); + continue; + } + /* remove any child with such a name */ + children = peer->create_child_cfg_enumerator(peer); + while (children->enumerate(children, &child)) + { + if (streq(child->get_name(child), msg->del_conn.name)) + { + peer->remove_child_cfg(peer, enumerator); + child->destroy(child); + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + DBG1(DBG_CFG, "deleted connection '%s'", msg->del_conn.name); +} + +/** + * Implementation of stroke_config_t.destroy + */ +static void destroy(private_stroke_config_t *this) +{ + this->list->destroy_offset(this->list, offsetof(peer_cfg_t, destroy)); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * see header file + */ +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred) +{ + private_stroke_config_t *this = malloc_thing(private_stroke_config_t); + + this->public.backend.create_peer_cfg_enumerator = (enumerator_t*(*)(backend_t*, identification_t *me, identification_t *other))create_peer_cfg_enumerator; + this->public.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator; + this->public.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name; + this->public.add = (void(*)(stroke_config_t*, stroke_msg_t *msg))add; + this->public.del = (void(*)(stroke_config_t*, stroke_msg_t *msg))del; + this->public.destroy = (void(*)(stroke_config_t*))destroy; + + this->list = linked_list_create(); + this->mutex = mutex_create(MUTEX_RECURSIVE); + this->ca = ca; + this->cred = cred; + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_config.h b/src/charon/plugins/stroke/stroke_config.h new file mode 100644 index 000000000..22b493cd2 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_config.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_config stroke_config + * @{ @ingroup stroke + */ + +#ifndef STROKE_CONFIG_H_ +#define STROKE_CONFIG_H_ + +#include <config/backend.h> +#include <stroke_msg.h> +#include "stroke_ca.h" +#include "stroke_cred.h" + +typedef struct stroke_config_t stroke_config_t; + +/** + * Stroke in-memory configuration backend + */ +struct stroke_config_t { + + /** + * Implements the backend_t interface + */ + backend_t backend; + + /** + * Add a configuration to the backend. + * + * @param msg received stroke message containing config + */ + void (*add)(stroke_config_t *this, stroke_msg_t *msg); + + /** + * Remove a configuration from the backend. + * + * @param msg received stroke message containing config name + */ + void (*del)(stroke_config_t *this, stroke_msg_t *msg); + + /** + * Destroy a stroke_config instance. + */ + void (*destroy)(stroke_config_t *this); +}; + +/** + * Create a stroke_config instance. + */ +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred); + +#endif /* STROKE_CONFIG_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_control.c b/src/charon/plugins/stroke/stroke_control.c new file mode 100644 index 000000000..2956b1576 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_control.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_control.h" + +#include <daemon.h> + +typedef struct private_stroke_control_t private_stroke_control_t; + +/** + * private data of stroke_control + */ +struct private_stroke_control_t { + + /** + * public functions + */ + stroke_control_t public; +}; + + +typedef struct stroke_log_info_t stroke_log_info_t; + +/** + * helper struct to say what and where to log when using controller callback + */ +struct stroke_log_info_t { + + /** + * level to log up to + */ + level_t level; + + /** + * where to write log + */ + FILE* out; +}; + +/** + * logging to the stroke interface + */ +static bool stroke_log(stroke_log_info_t *info, signal_t signal, level_t level, + ike_sa_t *ike_sa, char *format, va_list args) +{ + if (level <= info->level) + { + if (vfprintf(info->out, format, args) < 0 || + fprintf(info->out, "\n") < 0 || + fflush(info->out) != 0) + { + return FALSE; + } + } + return TRUE; +} + +/** + * get the child_cfg with the same name as the peer cfg + */ +static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) +{ + child_cfg_t *current, *found = NULL; + enumerator_t *enumerator; + + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** + * Implementation of stroke_control_t.initiate. + */ +static void initiate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + stroke_log_info_t info; + + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, + msg->initiate.name); + if (peer_cfg == NULL) + { + DBG1(DBG_CFG, "no config named '%s'\n", msg->initiate.name); + return; + } + if (peer_cfg->get_ike_version(peer_cfg) != 2) + { + DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config", + peer_cfg->get_ike_version(peer_cfg)); + peer_cfg->destroy(peer_cfg); + return; + } + + child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name); + if (child_cfg == NULL) + { + DBG1(DBG_CFG, "no child config named '%s'\n", msg->initiate.name); + peer_cfg->destroy(peer_cfg); + return; + } + + if (msg->output_verbosity < 0) + { + charon->controller->initiate(charon->controller, peer_cfg, child_cfg, + NULL, NULL); + } + else + { + info.out = out; + info.level = msg->output_verbosity; + charon->controller->initiate(charon->controller, peer_cfg, child_cfg, + (controller_cb_t)stroke_log, &info); + } +} + +/** + * Implementation of stroke_control_t.terminate. + */ +static void terminate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + char *string, *pos = NULL, *name = NULL; + u_int32_t id = 0; + bool child; + int len; + ike_sa_t *ike_sa; + enumerator_t *enumerator; + stroke_log_info_t info; + + string = msg->terminate.name; + + len = strlen(string); + if (len < 1) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + switch (string[len-1]) + { + case '}': + child = TRUE; + pos = strchr(string, '{'); + break; + case ']': + child = FALSE; + pos = strchr(string, '['); + break; + default: + name = string; + child = FALSE; + break; + } + + if (name) + { + /* is a single name */ + } + else if (pos == string + len - 2) + { /* is name[] or name{} */ + string[len-2] = '\0'; + name = string; + } + else + { /* is name[123] or name{23} */ + string[len-1] = '\0'; + id = atoi(pos + 1); + if (id == 0) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + } + + info.out = out; + info.level = msg->output_verbosity; + + enumerator = charon->controller->create_ike_sa_enumerator(charon->controller); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + child_sa_t *child_sa; + iterator_t *children; + + if (child) + { + children = ike_sa->create_child_sa_iterator(ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if ((name && streq(name, child_sa->get_name(child_sa))) || + (id && id == child_sa->get_reqid(child_sa))) + { + id = child_sa->get_reqid(child_sa); + children->destroy(children); + enumerator->destroy(enumerator); + + charon->controller->terminate_child(charon->controller, id, + (controller_cb_t)stroke_log, &info); + return; + } + } + children->destroy(children); + } + else if ((name && streq(name, ike_sa->get_name(ike_sa))) || + (id && id == ike_sa->get_unique_id(ike_sa))) + { + id = ike_sa->get_unique_id(ike_sa); + /* unlock manager first */ + enumerator->destroy(enumerator); + + charon->controller->terminate_ike(charon->controller, id, + (controller_cb_t)stroke_log, &info); + return; + } + + } + enumerator->destroy(enumerator); + DBG1(DBG_CFG, "no such SA found"); +} + +/** + * Implementation of stroke_control_t.route. + */ +static void route(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + stroke_log_info_t info; + + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, + msg->route.name); + if (peer_cfg == NULL) + { + fprintf(out, "no config named '%s'\n", msg->route.name); + return; + } + if (peer_cfg->get_ike_version(peer_cfg) != 2) + { + peer_cfg->destroy(peer_cfg); + return; + } + + child_cfg = get_child_from_peer(peer_cfg, msg->route.name); + if (child_cfg == NULL) + { + fprintf(out, "no child config named '%s'\n", msg->route.name); + peer_cfg->destroy(peer_cfg); + return; + } + + info.out = out; + info.level = msg->output_verbosity; + charon->controller->route(charon->controller, peer_cfg, child_cfg, + (controller_cb_t)stroke_log, &info); + peer_cfg->destroy(peer_cfg); + child_cfg->destroy(child_cfg); +} + +/** + * Implementation of stroke_control_t.unroute. + */ +static void unroute(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + char *name; + ike_sa_t *ike_sa; + enumerator_t *enumerator; + stroke_log_info_t info; + + name = msg->terminate.name; + + info.out = out; + info.level = msg->output_verbosity; + + enumerator = charon->controller->create_ike_sa_enumerator(charon->controller); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + child_sa_t *child_sa; + iterator_t *children; + u_int32_t id; + + children = ike_sa->create_child_sa_iterator(ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED && + streq(name, child_sa->get_name(child_sa))) + { + id = child_sa->get_reqid(child_sa); + children->destroy(children); + enumerator->destroy(enumerator); + charon->controller->unroute(charon->controller, id, + (controller_cb_t)stroke_log, &info); + return; + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + DBG1(DBG_CFG, "no such SA found"); +} + +/** + * Implementation of stroke_control_t.destroy + */ +static void destroy(private_stroke_control_t *this) +{ + free(this); +} + +/* + * see header file + */ +stroke_control_t *stroke_control_create() +{ + private_stroke_control_t *this = malloc_thing(private_stroke_control_t); + + this->public.initiate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))initiate; + this->public.terminate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))terminate; + this->public.route = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))route; + this->public.unroute = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))unroute; + this->public.destroy = (void(*)(stroke_control_t*))destroy; + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_control.h b/src/charon/plugins/stroke/stroke_control.h new file mode 100644 index 000000000..917679209 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_control.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_control stroke_control + * @{ @ingroup stroke + */ + +#ifndef STROKE_CONTROL_H_ +#define STROKE_CONTROL_H_ + +#include <stroke_msg.h> +#include <library.h> +#include <stdio.h> + +typedef struct stroke_control_t stroke_control_t; + +/** + * Process stroke control messages + */ +struct stroke_control_t { + + /** + * Initiate a connection. + * + * @param msg stroke message + */ + void (*initiate)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Terminate a connection. + * + * @param msg stroke message + */ + void (*terminate)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Route a connection. + * + * @param msg stroke message + */ + void (*route)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Unroute a connection. + * + * @param msg stroke message + */ + void (*unroute)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Destroy a stroke_control instance. + */ + void (*destroy)(stroke_control_t *this); +}; + +/** + * Create a stroke_control instance. + */ +stroke_control_t *stroke_control_create(); + +#endif /* STROKE_CONTROL_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_cred.c b/src/charon/plugins/stroke/stroke_cred.c new file mode 100644 index 000000000..223500488 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_cred.c @@ -0,0 +1,977 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_cred.h" +#include "stroke_shared_key.h" + +#include <sys/stat.h> +#include <limits.h> + +#include <credentials/certificates/x509.h> +#include <credentials/certificates/crl.h> +#include <credentials/certificates/ac.h> +#include <utils/linked_list.h> +#include <utils/mutex.h> +#include <utils/lexparser.h> +#include <asn1/pem.h> +#include <daemon.h> + +/* configuration directories and files */ +#define CONFIG_DIR IPSEC_CONFDIR +#define IPSEC_D_DIR CONFIG_DIR "/ipsec.d" +#define PRIVATE_KEY_DIR IPSEC_D_DIR "/private" +#define CERTIFICATE_DIR IPSEC_D_DIR "/certs" +#define CA_CERTIFICATE_DIR IPSEC_D_DIR "/cacerts" +#define AA_CERTIFICATE_DIR IPSEC_D_DIR "/aacerts" +#define ATTR_CERTIFICATE_DIR IPSEC_D_DIR "/acerts" +#define OCSP_CERTIFICATE_DIR IPSEC_D_DIR "/ocspcerts" +#define CRL_DIR IPSEC_D_DIR "/crls" +#define SECRETS_FILE CONFIG_DIR "/ipsec.secrets" + +typedef struct private_stroke_cred_t private_stroke_cred_t; + +/** + * private data of stroke_cred + */ +struct private_stroke_cred_t { + + /** + * public functions + */ + stroke_cred_t public; + + /** + * list of trusted peer/signer/CA certificates (certificate_t) + */ + linked_list_t *certs; + + /** + * list of shared secrets (private_shared_key_t) + */ + linked_list_t *shared; + + /** + * list of private keys (private_key_t) + */ + linked_list_t *private; + + /** + * mutex to lock lists above + */ + mutex_t *mutex; + + /** + * cache CRLs to disk? + */ + bool cachecrl; +}; + +/** + * data to pass to various filters + */ +typedef struct { + private_stroke_cred_t *this; + identification_t *id; +} id_data_t; + +/** + * destroy id enumerator data and unlock list + */ +static void id_data_destroy(id_data_t *data) +{ + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * filter function for private key enumerator + */ +static bool private_filter(id_data_t *data, + private_key_t **in, private_key_t **out) +{ + identification_t *candidate; + id_type_t type; + + if (data->id == NULL) + { + *out = *in; + return TRUE; + } + type = data->id->get_type(data->id); + if (type == ID_KEY_ID) + { /* handle ID_KEY_ID as a ID_PUBKEY_SHA1 */ + type = ID_PUBKEY_SHA1; + } + candidate = (*in)->get_id(*in, type); + if (candidate && + chunk_equals(candidate->get_encoding(candidate), + data->id->get_encoding(data->id))) + { + *out = *in; + return TRUE; + } + return FALSE; +} + +/** + * Implements credential_set_t.create_private_enumerator + */ +static enumerator_t* create_private_enumerator(private_stroke_cred_t *this, + key_type_t type, identification_t *id) +{ + id_data_t *data; + + data = malloc_thing(id_data_t); + data->this = this; + data->id = id; + + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->private->create_enumerator(this->private), + (void*)private_filter, data, + (void*)id_data_destroy); +} + +/** + * filter function for certs enumerator + */ +static bool certs_filter(id_data_t *data, certificate_t **in, certificate_t **out) +{ + public_key_t *public; + identification_t *candidate; + certificate_t *cert = *in; + certificate_type_t type = cert->get_type(cert); + + if (type == CERT_X509_CRL || type == CERT_X509_AC) + { + return FALSE; + } + + if (data->id == NULL || cert->has_subject(cert, data->id)) + { + *out = *in; + return TRUE; + } + + public = (cert)->get_public_key(cert); + if (public) + { + candidate = public->get_id(public, data->id->get_type(data->id)); + if (candidate && data->id->equals(data->id, candidate)) + { + public->destroy(public); + *out = *in; + return TRUE; + } + public->destroy(public); + } + return FALSE; +} + +/** + * filter function for crl enumerator + */ +static bool crl_filter(id_data_t *data, certificate_t **in, certificate_t **out) +{ + certificate_t *cert = *in; + + if (cert->get_type(cert) != CERT_X509_CRL) + { + return FALSE; + } + + if (data->id == NULL || cert->has_issuer(cert, data->id)) + { + *out = *in; + return TRUE; + } + return FALSE; +} + +/** + * filter function for attribute certificate enumerator + */ +static bool ac_filter(id_data_t *data, certificate_t **in, certificate_t **out) +{ + certificate_t *cert = *in; + + if (cert->get_type(cert) != CERT_X509_AC) + { + return FALSE; + } + + if (data->id == NULL || cert->has_subject(cert, data->id)) + { + *out = *in; + return TRUE; + } + return FALSE; +} + +/** + * Implements credential_set_t.create_cert_enumerator + */ +static enumerator_t* create_cert_enumerator(private_stroke_cred_t *this, + certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + id_data_t *data; + + if (cert == CERT_X509_CRL || cert == CERT_X509_AC) + { + if (trusted) + { + return NULL; + } + data = malloc_thing(id_data_t); + data->this = this; + data->id = id; + + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->certs->create_enumerator(this->certs), + (cert == CERT_X509_CRL)? (void*)crl_filter : (void*)ac_filter, + data, (void*)id_data_destroy); + } + if (cert != CERT_X509 && cert != CERT_ANY) + { /* we only have X509 certificates. TODO: ACs? */ + return NULL; + } + data = malloc_thing(id_data_t); + data->this = this; + data->id = id; + + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->certs->create_enumerator(this->certs), + (void*)certs_filter, data, + (void*)id_data_destroy); +} + +typedef struct { + private_stroke_cred_t *this; + identification_t *me; + identification_t *other; + shared_key_type_t type; +} shared_data_t; + +/** + * free shared key enumerator data and unlock list + */ +static void shared_data_destroy(shared_data_t *data) +{ + data->this->mutex->unlock(data->this->mutex); + free(data); +} + +/** + * filter function for certs enumerator + */ +static bool shared_filter(shared_data_t *data, + stroke_shared_key_t **in, shared_key_t **out, + void **unused1, id_match_t *me, + void **unused2, id_match_t *other) +{ + id_match_t my_match, other_match; + stroke_shared_key_t *stroke = *in; + shared_key_t *shared = &stroke->shared; + + if (data->type != SHARED_ANY && shared->get_type(shared) != data->type) + { + return FALSE; + } + + my_match = stroke->has_owner(stroke, data->me); + other_match = stroke->has_owner(stroke, data->other); + if (!my_match && !other_match) + { + return FALSE; + } + *out = shared; + if (me) + { + *me = my_match; + } + if (other) + { + *other = other_match; + } + return TRUE; +} + +/** + * Implements credential_set_t.create_shared_enumerator + */ +static enumerator_t* create_shared_enumerator(private_stroke_cred_t *this, + shared_key_type_t type, identification_t *me, + identification_t *other) +{ + shared_data_t *data = malloc_thing(shared_data_t); + + data->this = this; + data->me = me; + data->other = other; + data->type = type; + this->mutex->lock(this->mutex); + return enumerator_create_filter(this->shared->create_enumerator(this->shared), + (void*)shared_filter, data, + (void*)shared_data_destroy); +} + +/** + * Add a certificate to chain + */ +static certificate_t* add_cert(private_stroke_cred_t *this, certificate_t *cert) +{ + certificate_t *current; + enumerator_t *enumerator; + bool new = TRUE; + + this->mutex->lock(this->mutex); + enumerator = this->certs->create_enumerator(this->certs); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->equals(current, cert)) + { + /* cert already in queue */ + cert->destroy(cert); + cert = current; + new = FALSE; + break; + } + } + enumerator->destroy(enumerator); + + if (new) + { + this->certs->insert_last(this->certs, cert); + } + this->mutex->unlock(this->mutex); + return cert; +} + +/** + * Implementation of stroke_cred_t.load_ca. + */ +static certificate_t* load_ca(private_stroke_cred_t *this, char *filename) +{ + certificate_t *cert; + char path[PATH_MAX]; + + if (*filename == '/') + { + snprintf(path, sizeof(path), "%s", filename); + } + else + { + snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); + } + + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, + BUILD_X509_FLAG, X509_CA, + BUILD_END); + if (cert) + { + return (certificate_t*)add_cert(this, cert); + } + return NULL; +} + +/** + * Add X.509 CRL to chain + */ +static bool add_crl(private_stroke_cred_t *this, crl_t* crl) +{ + certificate_t *current, *cert = &crl->certificate; + enumerator_t *enumerator; + bool new = TRUE, found = FALSE; + + this->mutex->lock(this->mutex); + enumerator = this->certs->create_enumerator(this->certs); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->get_type(current) == CERT_X509_CRL) + { + crl_t *crl_c = (crl_t*)current; + identification_t *authkey = crl->get_authKeyIdentifier(crl); + identification_t *authkey_c = crl_c->get_authKeyIdentifier(crl_c); + + /* if compare authorityKeyIdentifiers if available */ + if (authkey != NULL && authkey_c != NULL && + authkey->equals(authkey, authkey_c)) + { + found = TRUE; + } + else + { + identification_t *issuer = cert->get_issuer(cert); + identification_t *issuer_c = current->get_issuer(current); + + /* otherwise compare issuer distinguished names */ + if (issuer->equals(issuer, issuer_c)) + { + found = TRUE; + } + } + if (found) + { + new = cert->is_newer(cert, current); + if (new) + { + this->certs->remove_at(this->certs, enumerator); + } + else + { + cert->destroy(cert); + } + break; + } + } + } + enumerator->destroy(enumerator); + + if (new) + { + this->certs->insert_last(this->certs, cert); + } + this->mutex->unlock(this->mutex); + return new; +} + +/** + * Add X.509 attribute certificate to chain + */ +static bool add_ac(private_stroke_cred_t *this, ac_t* ac) +{ + certificate_t *cert = &ac->certificate; + + this->mutex->lock(this->mutex); + this->certs->insert_last(this->certs, cert); + this->mutex->unlock(this->mutex); + return TRUE; +} + +/** + * Implementation of stroke_cred_t.load_peer. + */ +static certificate_t* load_peer(private_stroke_cred_t *this, char *filename) +{ + certificate_t *cert; + char path[PATH_MAX]; + + if (*filename == '/') + { + snprintf(path, sizeof(path), "%s", filename); + } + else + { + snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename); + } + + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, + BUILD_X509_FLAG, 0, + BUILD_END); + if (cert) + { + cert = add_cert(this, cert); + return cert->get_ref(cert); + } + return NULL; +} + +/** + * load trusted certificates from a directory + */ +static void load_certdir(private_stroke_cred_t *this, char *path, + certificate_type_t type, x509_flag_t flag) +{ + struct stat st; + char *file; + + enumerator_t *enumerator = enumerator_create_directory(path); + + if (!enumerator) + { + DBG1(DBG_CFG, " reading directory failed"); + return; + } + + while (enumerator->enumerate(enumerator, NULL, &file, &st)) + { + certificate_t *cert; + + if (!S_ISREG(st.st_mode)) + { + /* skip special file */ + continue; + } + switch (type) + { + case CERT_X509: + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, + BUILD_X509_FLAG, flag, + BUILD_END); + if (cert) + { + add_cert(this, cert); + } + break; + case CERT_X509_CRL: + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509_CRL, + BUILD_FROM_FILE, file, + BUILD_END); + if (cert) + { + add_crl(this, (crl_t*)cert); + } + break; + case CERT_X509_AC: + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509_AC, + BUILD_FROM_FILE, file, + BUILD_END); + if (cert) + { + add_ac(this, (ac_t*)cert); + } + break; + default: + break; + } + } + enumerator->destroy(enumerator); +} + +/** + * Implementation of credential_set_t.cache_cert. + */ +static void cache_cert(private_stroke_cred_t *this, certificate_t *cert) +{ + if (cert->get_type(cert) == CERT_X509_CRL && this->cachecrl) + { + /* CRLs get written to /etc/ipsec.d/crls/authkeyId.crl */ + crl_t *crl = (crl_t*)cert; + + cert->get_ref(cert); + if (add_crl(this, crl)) + { + char buf[256]; + chunk_t chunk, hex; + identification_t *id; + + id = crl->get_authKeyIdentifier(crl); + chunk = id->get_encoding(id); + hex = chunk_to_hex(chunk, NULL, FALSE); + snprintf(buf, sizeof(buf), "%s/%s.crl", CRL_DIR, hex); + free(hex.ptr); + + chunk = cert->get_encoding(cert); + if (chunk_write(chunk, buf, 022, TRUE)) + { + DBG1(DBG_CFG, " written crl to '%s'", buf); + } + else + { + DBG1(DBG_CFG, " writing crl to '%s' failed", buf); + } + free(chunk.ptr); + } + } +} + +/** + * Implementation of stroke_cred_t.cachecrl. + */ +static void cachecrl(private_stroke_cred_t *this, bool enabled) +{ + DBG1(DBG_CFG, "crl caching to %s %s", + CRL_DIR, enabled ? "enabled" : "disabled"); + this->cachecrl = enabled; +} + + +/** + * Convert a string of characters into a binary secret + * A string between single or double quotes is treated as ASCII characters + * A string prepended by 0x is treated as HEX and prepended by 0s as Base64 + */ +static err_t extract_secret(chunk_t *secret, chunk_t *line) +{ + chunk_t raw_secret; + char delimiter = ' '; + bool quotes = FALSE; + + if (!eat_whitespace(line)) + { + return "missing secret"; + } + + if (*line->ptr == '\'' || *line->ptr == '"') + { + quotes = TRUE; + delimiter = *line->ptr; + line->ptr++; line->len--; + } + + if (!extract_token(&raw_secret, delimiter, line)) + { + if (delimiter == ' ') + { + raw_secret = *line; + } + else + { + return "missing second delimiter"; + } + } + + if (quotes) + { + /* treat as an ASCII string */ + *secret = chunk_clone(raw_secret); + return NULL; + } + /* treat 0x as hex, 0s as base64 */ + if (raw_secret.len > 2) + { + if (strncasecmp("0x", raw_secret.ptr, 2) == 0) + { + *secret = chunk_from_hex(chunk_skip(raw_secret, 2), NULL); + return NULL; + } + if (strncasecmp("0s", raw_secret.ptr, 2) == 0) + { + *secret = chunk_from_base64(chunk_skip(raw_secret, 2), NULL); + return NULL; + } + } + *secret = chunk_clone(raw_secret); + return NULL; +} + +/** + * reload ipsec.secrets + */ +static void load_secrets(private_stroke_cred_t *this) +{ + size_t bytes; + int line_nr = 0; + chunk_t chunk, src, line; + FILE *fd; + private_key_t *private; + shared_key_t *shared; + + DBG1(DBG_CFG, "loading secrets from '%s'", SECRETS_FILE); + + fd = fopen(SECRETS_FILE, "r"); + if (fd == NULL) + { + DBG1(DBG_CFG, "opening secrets file '%s' failed"); + return; + } + + /* TODO: do error checks */ + fseek(fd, 0, SEEK_END); + chunk.len = ftell(fd); + rewind(fd); + chunk.ptr = malloc(chunk.len); + bytes = fread(chunk.ptr, 1, chunk.len, fd); + fclose(fd); + src = chunk; + + this->mutex->lock(this->mutex); + while (this->shared->remove_last(this->shared, + (void**)&shared) == SUCCESS) + { + shared->destroy(shared); + } + while (this->private->remove_last(this->private, + (void**)&private) == SUCCESS) + { + private->destroy(private); + } + + while (fetchline(&src, &line)) + { + chunk_t ids, token; + shared_key_type_t type; + + line_nr++; + + if (!eat_whitespace(&line)) + { + continue; + } + if (!extract_last_token(&ids, ':', &line)) + { + DBG1(DBG_CFG, "line %d: missing ':' separator", line_nr); + goto error; + } + /* NULL terminate the ids string by replacing the : separator */ + *(ids.ptr + ids.len) = '\0'; + + if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line)) + { + DBG1(DBG_CFG, "line %d: missing token", line_nr); + goto error; + } + if (match("RSA", &token) || match("ECDSA", &token)) + { + char path[PATH_MAX]; + chunk_t filename; + chunk_t secret = chunk_empty; + private_key_t *key; + bool pgp = FALSE; + chunk_t chunk = chunk_empty; + key_type_t key_type = match("RSA", &token) ? KEY_RSA : KEY_ECDSA; + + err_t ugh = extract_value(&filename, &line); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (filename.len == 0) + { + DBG1(DBG_CFG, "line %d: empty filename", line_nr); + goto error; + } + if (*filename.ptr == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR, + filename.len, filename.ptr); + } + + /* check for optional passphrase */ + if (eat_whitespace(&line)) + { + ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh); + goto error; + } + } + + if (pem_asn1_load_file(path, &secret, &chunk, &pgp)) + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type, + BUILD_BLOB_ASN1_DER, chunk, BUILD_END); + if (key) + { + DBG1(DBG_CFG, " loaded private key file '%s'", path); + this->private->insert_last(this->private, key); + } + } + chunk_clear(&secret); + } + else if ((match("PSK", &token) && (type = SHARED_IKE)) || + (match("EAP", &token) && (type = SHARED_EAP)) || + (match("XAUTH", &token) && (type = SHARED_EAP)) || + (match("PIN", &token) && (type = SHARED_PIN))) + { + stroke_shared_key_t *shared_key; + chunk_t secret = chunk_empty; + bool any = TRUE; + + err_t ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh); + goto error; + } + shared_key = stroke_shared_key_create(type, secret); + DBG1(DBG_CFG, " loaded %N secret for %s", shared_key_type_names, type, + ids.len > 0 ? (char*)ids.ptr : "%any"); + DBG4(DBG_CFG, " secret: %#B", &secret); + + this->shared->insert_last(this->shared, shared_key); + while (ids.len > 0) + { + chunk_t id; + identification_t *peer_id; + + ugh = extract_value(&id, &ids); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (id.len == 0) + { + continue; + } + + /* NULL terminate the ID string */ + *(id.ptr + id.len) = '\0'; + + peer_id = identification_create_from_string(id.ptr); + if (peer_id == NULL) + { + DBG1(DBG_CFG, "line %d: malformed ID: %s", line_nr, id.ptr); + goto error; + } + if (peer_id->get_type(peer_id) == ID_ANY) + { + peer_id->destroy(peer_id); + continue; + } + + shared_key->add_owner(shared_key, peer_id); + any = FALSE; + } + if (any) + { + shared_key->add_owner(shared_key, + identification_create_from_encoding(ID_ANY, chunk_empty)); + } + } + else + { + DBG1(DBG_CFG, "line %d: token must be either " + "RSA, EC, PSK, EAP, or PIN", line_nr); + goto error; + } + } +error: + this->mutex->unlock(this->mutex); + chunk_clear(&chunk); +} + +/** + * load all certificates from ipsec.d + */ +static void load_certs(private_stroke_cred_t *this) +{ + DBG1(DBG_CFG, "loading ca certificates from '%s'", + CA_CERTIFICATE_DIR); + load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA); + + DBG1(DBG_CFG, "loading aa certificates from '%s'", + AA_CERTIFICATE_DIR); + load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA); + + DBG1(DBG_CFG, "loading ocsp signer certificates from '%s'", + OCSP_CERTIFICATE_DIR); + load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, X509_OCSP_SIGNER); + + DBG1(DBG_CFG, "loading attribute certificates from '%s'", + ATTR_CERTIFICATE_DIR); + load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0); + + DBG1(DBG_CFG, "loading crls from '%s'", + CRL_DIR); + load_certdir(this, CRL_DIR, CERT_X509_CRL, 0); +} + +/** + * Implementation of stroke_cred_t.reread. + */ +static void reread(private_stroke_cred_t *this, stroke_msg_t *msg) +{ + if (msg->reread.flags & REREAD_SECRETS) + { + DBG1(DBG_CFG, "rereading secrets"); + load_secrets(this); + } + if (msg->reread.flags & REREAD_CACERTS) + { + DBG1(DBG_CFG, "rereading ca certificates from '%s'", + CA_CERTIFICATE_DIR); + load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA); + } + if (msg->reread.flags & REREAD_OCSPCERTS) + { + DBG1(DBG_CFG, "rereading ocsp signer certificates from '%s'", + OCSP_CERTIFICATE_DIR); + load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, + X509_OCSP_SIGNER); + } + if (msg->reread.flags & REREAD_AACERTS) + { + DBG1(DBG_CFG, "rereading aa certificates from '%s'", + AA_CERTIFICATE_DIR); + load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA); + } + if (msg->reread.flags & REREAD_ACERTS) + { + DBG1(DBG_CFG, "rereading attribute certificates from '%s'", + ATTR_CERTIFICATE_DIR); + load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0); + } + if (msg->reread.flags & REREAD_CRLS) + { + DBG1(DBG_CFG, "rereading crls from '%s'", + CRL_DIR); + load_certdir(this, CRL_DIR, CERT_X509_CRL, 0); + } +} + +/** + * Implementation of stroke_cred_t.destroy + */ +static void destroy(private_stroke_cred_t *this) +{ + this->certs->destroy_offset(this->certs, offsetof(certificate_t, destroy)); + this->shared->destroy_offset(this->shared, offsetof(shared_key_t, destroy)); + this->private->destroy_offset(this->private, offsetof(private_key_t, destroy)); + this->mutex->destroy(this->mutex); + free(this); +} + +/* + * see header file + */ +stroke_cred_t *stroke_cred_create() +{ + private_stroke_cred_t *this = malloc_thing(private_stroke_cred_t); + + this->public.set.create_private_enumerator = (void*)create_private_enumerator; + this->public.set.create_cert_enumerator = (void*)create_cert_enumerator; + this->public.set.create_shared_enumerator = (void*)create_shared_enumerator; + this->public.set.create_cdp_enumerator = (void*)return_null; + this->public.set.cache_cert = (void*)cache_cert; + this->public.reread = (void(*)(stroke_cred_t*, stroke_msg_t *msg))reread; + this->public.load_ca = (certificate_t*(*)(stroke_cred_t*, char *filename))load_ca; + this->public.load_peer = (certificate_t*(*)(stroke_cred_t*, char *filename))load_peer; + this->public.cachecrl = (void(*)(stroke_cred_t*, bool enabled))cachecrl; + this->public.destroy = (void(*)(stroke_cred_t*))destroy; + + this->certs = linked_list_create(); + this->shared = linked_list_create(); + this->private = linked_list_create(); + this->mutex = mutex_create(MUTEX_RECURSIVE); + + load_certs(this); + load_secrets(this); + + this->cachecrl = FALSE; + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_cred.h b/src/charon/plugins/stroke/stroke_cred.h new file mode 100644 index 000000000..1b9ef986e --- /dev/null +++ b/src/charon/plugins/stroke/stroke_cred.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_cred stroke_cred + * @{ @ingroup stroke + */ + +#ifndef STROKE_CRED_H_ +#define STROKE_CRED_H_ + +#include <stroke_msg.h> +#include <credentials/credential_set.h> +#include <credentials/certificates/certificate.h> + +typedef struct stroke_cred_t stroke_cred_t; + +/** + * Stroke in-memory credential storage. + */ +struct stroke_cred_t { + + /** + * Implements credential_set_t + */ + credential_set_t set; + + /** + * Reread secrets from config files. + * + * @param msg stroke message + */ + void (*reread)(stroke_cred_t *this, stroke_msg_t *msg); + + /** + * Load a CA certificate, and serve it through the credential_set. + * + * @param filename file to load CA cert from + * @return reference to loaded certificate, or NULL + */ + certificate_t* (*load_ca)(stroke_cred_t *this, char *filename); + + /** + * Load a peer certificate and serve it rhrough the credential_set. + * + * @param filename file to load peer cert from + * @return reference to loaded certificate, or NULL + */ + certificate_t* (*load_peer)(stroke_cred_t *this, char *filename); + + /** + * Enable/Disable CRL caching to disk. + * + * @param enabled TRUE to enable, FALSE to disable + */ + void (*cachecrl)(stroke_cred_t *this, bool enabled); + + /** + * Destroy a stroke_cred instance. + */ + void (*destroy)(stroke_cred_t *this); +}; + +/** + * Create a stroke_cred instance. + */ +stroke_cred_t *stroke_cred_create(); + +#endif /* STROKE_CRED_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_list.c b/src/charon/plugins/stroke/stroke_list.c new file mode 100644 index 000000000..44699ba0a --- /dev/null +++ b/src/charon/plugins/stroke/stroke_list.c @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_list.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <credentials/certificates/x509.h> +#include <credentials/certificates/ac.h> +#include <credentials/certificates/crl.h> + +/* warning intervals for list functions */ +#define CERT_WARNING_INTERVAL 30 /* days */ +#define CRL_WARNING_INTERVAL 7 /* days */ +#define AC_WARNING_INTERVAL 1 /* day */ + +typedef struct private_stroke_list_t private_stroke_list_t; + +/** + * private data of stroke_list + */ +struct private_stroke_list_t { + + /** + * public functions + */ + stroke_list_t public; + + /** + * timestamp of daemon start + */ + time_t uptime; +}; + +/** + * log an IKE_SA to out + */ +static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) +{ + ike_sa_id_t *id = ike_sa->get_id(ike_sa); + + fprintf(out, "%12s[%d]: %N, %H[%D]...%H[%D]\n", + ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), + ike_sa_state_names, ike_sa->get_state(ike_sa), + ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa), + ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa)); + + if (all) + { + char *ike_proposal = ike_sa->get_proposal(ike_sa); + + fprintf(out, "%12s[%d]: IKE SPIs: %.16llx_i%s %.16llx_r%s", + ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), + id->get_initiator_spi(id), id->is_initiator(id) ? "*" : "", + id->get_responder_spi(id), id->is_initiator(id) ? "" : "*"); + + + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + u_int32_t rekey = ike_sa->get_statistic(ike_sa, STAT_REKEY_TIME); + u_int32_t reauth = ike_sa->get_statistic(ike_sa, STAT_REAUTH_TIME); + + if (rekey) + { + fprintf(out, ", rekeying in %V", &rekey); + } + if (reauth) + { + fprintf(out, ", reauthentication in %V", &reauth); + } + if (!rekey && !reauth) + { + fprintf(out, ", rekeying disabled"); + } + } + fprintf(out, "\n"); + + if (ike_proposal) + { + fprintf(out, "%12s[%d]: IKE proposal: %s\n", + ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), + ike_proposal); + } + } +} + +/** + * log an CHILD_SA to out + */ +static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all) +{ + u_int32_t rekey, now = time(NULL); + u_int32_t use_in, use_out, use_fwd; + encryption_algorithm_t encr_alg; + integrity_algorithm_t int_alg; + size_t encr_len, int_len; + mode_t mode; + + child_sa->get_stats(child_sa, &mode, &encr_alg, &encr_len, + &int_alg, &int_len, &rekey, &use_in, &use_out, + &use_fwd); + + fprintf(out, "%12s{%d}: %N, %N", + child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), + child_sa_state_names, child_sa->get_state(child_sa), + mode_names, mode); + + if (child_sa->get_state(child_sa) == CHILD_INSTALLED) + { + fprintf(out, ", %N SPIs: %.8x_i %.8x_o", + protocol_id_names, child_sa->get_protocol(child_sa), + htonl(child_sa->get_spi(child_sa, TRUE)), + htonl(child_sa->get_spi(child_sa, FALSE))); + + if (all) + { + fprintf(out, "\n%12s{%d}: ", child_sa->get_name(child_sa), + child_sa->get_reqid(child_sa)); + + + if (child_sa->get_protocol(child_sa) == PROTO_ESP) + { + fprintf(out, "%N", encryption_algorithm_names, encr_alg); + + if (encr_len) + { + fprintf(out, "-%d", encr_len); + } + if (int_alg != AUTH_UNDEFINED) + { + fprintf(out, "/"); + } + } + + if (int_alg != AUTH_UNDEFINED) + { + fprintf(out, "%N", integrity_algorithm_names, int_alg); + if (int_len) + { + fprintf(out, "-%d", int_len); + } + } + fprintf(out, ", rekeying "); + + if (rekey) + { + fprintf(out, "in %#V", &now, &rekey); + } + else + { + fprintf(out, "disabled"); + } + + fprintf(out, ", last use: "); + use_in = max(use_in, use_fwd); + if (use_in) + { + fprintf(out, "%ds_i ", now - use_in); + } + else + { + fprintf(out, "no_i "); + } + if (use_out) + { + fprintf(out, "%ds_o ", now - use_out); + } + else + { + fprintf(out, "no_o "); + } + } + } + + fprintf(out, "\n%12s{%d}: %#R=== %#R\n", + child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), + child_sa->get_traffic_selectors(child_sa, TRUE), + child_sa->get_traffic_selectors(child_sa, FALSE)); +} + +/** + * Implementation of stroke_list_t.status. + */ +static void status(private_stroke_list_t *this, stroke_msg_t *msg, FILE *out, bool all) +{ + enumerator_t *enumerator, *children; + iterator_t *iterator; + host_t *host; + peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; + child_cfg_t *child_cfg; + ike_sa_t *ike_sa; + char *name = NULL, *plugin; + bool found = FALSE; + time_t uptime; + + name = msg->status.name; + + if (all) + { + uptime = time(NULL) - this->uptime; + fprintf(out, "Performance:\n"); + fprintf(out, " uptime: %V, since %#T\n", &uptime, &this->uptime, FALSE); + fprintf(out, " worker threads: %d idle of %d,", + charon->processor->get_idle_threads(charon->processor), + charon->processor->get_total_threads(charon->processor)); + fprintf(out, " job queue load: %d,", + charon->processor->get_job_load(charon->processor)); + fprintf(out, " scheduled events: %d\n", + charon->scheduler->get_job_load(charon->scheduler)); + fprintf(out, " loaded plugins: "); + enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); + while (enumerator->enumerate(enumerator, &plugin)) + { + fprintf(out, "%s ", plugin); + } + enumerator->destroy(enumerator); + fprintf(out, "\n"); + + iterator = charon->kernel_interface->create_address_iterator( + charon->kernel_interface); + fprintf(out, "Listening IP addresses:\n"); + while (iterator->iterate(iterator, (void**)&host)) + { + fprintf(out, " %H\n", host); + } + iterator->destroy(iterator); + + fprintf(out, "Connections:\n"); + enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends); + while (enumerator->enumerate(enumerator, (void**)&peer_cfg)) + { + if (peer_cfg->get_ike_version(peer_cfg) != 2 || + (name && !streq(name, peer_cfg->get_name(peer_cfg)))) + { + continue; + } + + ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); + fprintf(out, "%12s: %s[%D]...%s[%D]\n", peer_cfg->get_name(peer_cfg), + ike_cfg->get_my_addr(ike_cfg), peer_cfg->get_my_id(peer_cfg), + ike_cfg->get_other_addr(ike_cfg), peer_cfg->get_other_id(peer_cfg)); + /* TODO: list CAs and groups */ + children = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (children->enumerate(children, &child_cfg)) + { + linked_list_t *my_ts, *other_ts; + my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + fprintf(out, "%12s: %#R=== %#R\n", child_cfg->get_name(child_cfg), + my_ts, other_ts); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + } + children->destroy(children); + } + enumerator->destroy(enumerator); + } + + fprintf(out, "Security Associations:\n"); + enumerator = charon->controller->create_ike_sa_enumerator(charon->controller); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + bool ike_printed = FALSE; + child_sa_t *child_sa; + iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa); + + if (name == NULL || streq(name, ike_sa->get_name(ike_sa))) + { + log_ike_sa(out, ike_sa, all); + found = TRUE; + ike_printed = TRUE; + } + + while (children->iterate(children, (void**)&child_sa)) + { + if (name == NULL || streq(name, child_sa->get_name(child_sa))) + { + if (!ike_printed) + { + log_ike_sa(out, ike_sa, all); + found = TRUE; + ike_printed = TRUE; + } + log_child_sa(out, child_sa, all); + } + } + children->destroy(children); + } + enumerator->destroy(enumerator); + + if (!found) + { + if (name) + { + fprintf(out, " no match\n"); + } + else + { + fprintf(out, " none\n"); + } + } +} + +/** + * create a unique certificate list without duplicates + * certicates having the same issuer are grouped together. + */ +static linked_list_t* create_unique_cert_list(certificate_type_t type) +{ + linked_list_t *list = linked_list_create(); + enumerator_t *enumerator = charon->credentials->create_cert_enumerator( + charon->credentials, type, KEY_ANY, + NULL, FALSE); + certificate_t *cert; + + while (enumerator->enumerate(enumerator, (void**)&cert)) + { + iterator_t *iterator = list->create_iterator(list, TRUE); + identification_t *issuer = cert->get_issuer(cert); + bool previous_same, same = FALSE, last = TRUE; + certificate_t *list_cert; + + while (iterator->iterate(iterator, (void**)&list_cert)) + { + /* exit if we have a duplicate? */ + if (list_cert->equals(list_cert, cert)) + { + last = FALSE; + break; + } + /* group certificates with same issuer */ + previous_same = same; + same = list_cert->has_issuer(list_cert, issuer); + if (previous_same && !same) + { + iterator->insert_before(iterator, (void *)cert->get_ref(cert)); + last = FALSE; + break; + } + } + iterator->destroy(iterator); + + if (last) + { + list->insert_last(list, (void *)cert->get_ref(cert)); + } + } + enumerator->destroy(enumerator); + return list; +} + +/** + * list all X.509 certificates matching the flags + */ +static void stroke_list_certs(linked_list_t *list, char *label, + x509_flag_t flags, bool utc, FILE *out) +{ + bool first = TRUE; + time_t now = time(NULL); + enumerator_t *enumerator = list->create_enumerator(list); + certificate_t *cert; + + while (enumerator->enumerate(enumerator, (void**)&cert)) + { + x509_t *x509 = (x509_t*)cert; + x509_flag_t x509_flags = x509->get_flags(x509); + + /* list only if flag is set, or flags == 0 (ignoring self-signed) */ + if ((x509_flags & flags) || (flags == (x509_flags & ~X509_SELF_SIGNED))) + { + enumerator_t *enumerator; + identification_t *altName; + bool first_altName = TRUE; + chunk_t serial = x509->get_serial(x509); + identification_t *authkey = x509->get_authKeyIdentifier(x509); + time_t notBefore, notAfter; + public_key_t *public = cert->get_public_key(cert); + + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of %s:\n", label); + first = FALSE; + } + fprintf(out, "\n"); + + /* list subjectAltNames */ + enumerator = x509->create_subjectAltName_enumerator(x509); + while (enumerator->enumerate(enumerator, (void**)&altName)) + { + if (first_altName) + { + fprintf(out, " altNames: "); + first_altName = FALSE; + } + else + { + fprintf(out, ", "); + } + fprintf(out, "%D", altName); + } + if (!first_altName) + { + fprintf(out, "\n"); + } + enumerator->destroy(enumerator); + + fprintf(out, " subject: \"%D\"\n", cert->get_subject(cert)); + fprintf(out, " issuer: \"%D\"\n", cert->get_issuer(cert)); + fprintf(out, " serial: %#B\n", &serial); + + /* list validity */ + cert->get_validity(cert, &now, ¬Before, ¬After); + fprintf(out, " validity: not before %#T, ", ¬Before, utc); + if (now < notBefore) + { + fprintf(out, "not valid yet (valid in %#V)\n", &now, ¬Before); + } + else + { + fprintf(out, "ok\n"); + } + fprintf(out, " not after %#T, ", ¬After, utc); + if (now > notAfter) + { + fprintf(out, "expired (%#V ago)\n", &now, ¬After); + } + else + { + fprintf(out, "ok"); + if (now > notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24) + { + fprintf(out, " (expires in %#V)", &now, ¬After); + } + fprintf(out, " \n"); + } + + /* list public key information */ + if (public) + { + private_key_t *private = NULL; + identification_t *id, *keyid; + + id = public->get_id(public, ID_PUBKEY_SHA1); + keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1); + + private = charon->credentials->get_private( + charon->credentials, + public->get_type(public), keyid, NULL); + fprintf(out, " pubkey: %N %d bits%s\n", + key_type_names, public->get_type(public), + public->get_keysize(public) * 8, + private ? ", has private key" : ""); + fprintf(out, " keyid: %D\n", keyid); + fprintf(out, " subjkey: %D\n", id); + DESTROY_IF(private); + public->destroy(public); + } + + /* list optional authorityKeyIdentifier */ + if (authkey) + { + fprintf(out, " authkey: %D\n", authkey); + } + } + } + enumerator->destroy(enumerator); +} + +/** + * list all X.509 attribute certificates + */ +static void stroke_list_acerts(linked_list_t *list, bool utc, FILE *out) +{ + bool first = TRUE; + time_t thisUpdate, nextUpdate, now = time(NULL); + enumerator_t *enumerator = list->create_enumerator(list); + certificate_t *cert; + + while (enumerator->enumerate(enumerator, (void**)&cert)) + { + ac_t *ac = (ac_t*)cert; + chunk_t serial = ac->get_serial(ac); + chunk_t holderSerial = ac->get_holderSerial(ac); + identification_t *holderIssuer = ac->get_holderIssuer(ac); + identification_t *authkey = ac->get_authKeyIdentifier(ac); + identification_t *entityName = cert->get_subject(cert); + + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 Attribute Certificates:\n"); + first = FALSE; + } + fprintf(out, "\n"); + + if (entityName) + { + fprintf(out, " holder: \"%D\"\n", entityName); + } + if (holderIssuer) + { + fprintf(out, " hissuer: \"%D\"\n", holderIssuer); + } + if (holderSerial.ptr) + { + fprintf(out, " hserial: %#B\n", &holderSerial); + } + fprintf(out, " issuer: \"%D\"\n", cert->get_issuer(cert)); + fprintf(out, " serial: %#B\n", &serial); + + /* list validity */ + cert->get_validity(cert, &now, &thisUpdate, &nextUpdate); + fprintf(out, " updates: this %#T\n", &thisUpdate, utc); + fprintf(out, " next %#T, ", &nextUpdate, utc); + if (now > nextUpdate) + { + fprintf(out, "expired (%#V ago)\n", &now, &nextUpdate); + } + else + { + fprintf(out, "ok"); + if (now > nextUpdate - AC_WARNING_INTERVAL * 60 * 60 * 24) + { + fprintf(out, " (expires in %#V)", &now, &nextUpdate); + } + fprintf(out, " \n"); + } + + /* list optional authorityKeyIdentifier */ + if (authkey) + { + fprintf(out, " authkey: %D\n", authkey); + } + } + enumerator->destroy(enumerator); +} + +/** + * list all X.509 CRLs + */ +static void stroke_list_crls(linked_list_t *list, bool utc, FILE *out) +{ + bool first = TRUE; + time_t thisUpdate, nextUpdate, now = time(NULL); + enumerator_t *enumerator = list->create_enumerator(list); + certificate_t *cert; + + while (enumerator->enumerate(enumerator, (void**)&cert)) + { + crl_t *crl = (crl_t*)cert; + chunk_t serial = crl->get_serial(crl); + identification_t *authkey = crl->get_authKeyIdentifier(crl); + + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 CRLs:\n"); + first = FALSE; + } + fprintf(out, "\n"); + + fprintf(out, " issuer: \"%D\"\n", cert->get_issuer(cert)); + + /* list optional crlNumber */ + if (serial.ptr) + { + fprintf(out, " serial: %#B\n", &serial); + } + + /* count the number of revoked certificates */ + { + int count = 0; + enumerator_t *enumerator = crl->create_enumerator(crl); + + while (enumerator->enumerate(enumerator, NULL, NULL, NULL)) + { + count++; + } + fprintf(out, " revoked: %d certificate%s\n", count, + (count == 1)? "" : "s"); + enumerator->destroy(enumerator); + } + + /* list validity */ + cert->get_validity(cert, &now, &thisUpdate, &nextUpdate); + fprintf(out, " updates: this %#T\n", &thisUpdate, utc); + fprintf(out, " next %#T, ", &nextUpdate, utc); + if (now > nextUpdate) + { + fprintf(out, "expired (%#V ago)\n", &now, &nextUpdate); + } + else + { + fprintf(out, "ok"); + if (now > nextUpdate - CRL_WARNING_INTERVAL * 60 * 60 * 24) + { + fprintf(out, " (expires in %#V)", &now, &nextUpdate); + } + fprintf(out, " \n"); + } + + /* list optional authorityKeyIdentifier */ + if (authkey) + { + fprintf(out, " authkey: %D\n", authkey); + } + } + enumerator->destroy(enumerator); +} + +/** + * list all OCSP responses + */ +static void stroke_list_ocsp(linked_list_t* list, bool utc, FILE *out) +{ + bool first = TRUE; + enumerator_t *enumerator = list->create_enumerator(list); + certificate_t *cert; + + while (enumerator->enumerate(enumerator, (void**)&cert)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of OCSP responses:\n"); + fprintf(out, "\n"); + first = FALSE; + } + + fprintf(out, " signer: \"%D\"\n", cert->get_issuer(cert)); + } + enumerator->destroy(enumerator); +} + +/** + * Implementation of stroke_list_t.list. + */ +static void list(private_stroke_list_t *this, stroke_msg_t *msg, FILE *out) +{ + linked_list_t *cert_list = NULL; + + if (msg->list.flags & (LIST_CERTS | LIST_CACERTS | LIST_OCSPCERTS | LIST_AACERTS)) + { + cert_list = create_unique_cert_list(CERT_X509); + } + if (msg->list.flags & LIST_CERTS) + { + stroke_list_certs(cert_list, "X.509 End Entity Certificates", + 0, msg->list.utc, out); + } + if (msg->list.flags & LIST_CACERTS) + { + stroke_list_certs(cert_list, "X.509 CA Certificates", + X509_CA, msg->list.utc, out); + } + if (msg->list.flags & LIST_OCSPCERTS) + { + stroke_list_certs(cert_list, "X.509 OCSP Signer Certificates", + X509_OCSP_SIGNER, msg->list.utc, out); + } + if (msg->list.flags & LIST_AACERTS) + { + stroke_list_certs(cert_list, "X.509 AA Certificates", + X509_AA, msg->list.utc, out); + } + if (msg->list.flags & LIST_ACERTS) + { + linked_list_t *ac_list = create_unique_cert_list(CERT_X509_AC); + + stroke_list_acerts(ac_list, msg->list.utc, out); + ac_list->destroy_offset(ac_list, offsetof(certificate_t, destroy)); + } + if (msg->list.flags & LIST_CRLS) + { + linked_list_t *crl_list = create_unique_cert_list(CERT_X509_CRL); + + stroke_list_crls(crl_list, msg->list.utc, out); + crl_list->destroy_offset(crl_list, offsetof(certificate_t, destroy)); + } + if (msg->list.flags & LIST_OCSP) + { + linked_list_t *ocsp_list = create_unique_cert_list(CERT_X509_OCSP_RESPONSE); + + stroke_list_ocsp(ocsp_list, msg->list.utc, out); + ocsp_list->destroy_offset(ocsp_list, offsetof(certificate_t, destroy)); + } + DESTROY_OFFSET_IF(cert_list, offsetof(certificate_t, destroy)); +} + +/** + * Implementation of stroke_list_t.destroy + */ +static void destroy(private_stroke_list_t *this) +{ + free(this); +} + +/* + * see header file + */ +stroke_list_t *stroke_list_create() +{ + private_stroke_list_t *this = malloc_thing(private_stroke_list_t); + + this->public.list = (void(*)(stroke_list_t*, stroke_msg_t *msg, FILE *out))list; + this->public.status = (void(*)(stroke_list_t*, stroke_msg_t *msg, FILE *out,bool))status; + this->public.destroy = (void(*)(stroke_list_t*))destroy; + + this->uptime = time(NULL); + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_list.h b/src/charon/plugins/stroke/stroke_list.h new file mode 100644 index 000000000..dabdbff39 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_list.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_list stroke_list + * @{ @ingroup stroke + */ + +#ifndef STROKE_LIST_H_ +#define STROKE_LIST_H_ + +#include <stroke_msg.h> +#include <library.h> + +typedef struct stroke_list_t stroke_list_t; + +/** + * Log status information to stroke console + */ +struct stroke_list_t { + + /** + * List certificate information to stroke console. + * + * @param msg stroke message + * @param out stroke console stream + */ + void (*list)(stroke_list_t *this, stroke_msg_t *msg, FILE *out); + + /** + * Log status information to stroke console. + * + * @param msg stroke message + * @param out stroke console stream + * @param all TRUE for "statusall" + */ + void (*status)(stroke_list_t *this, stroke_msg_t *msg, FILE *out, bool all); + + /** + * Destroy a stroke_list instance. + */ + void (*destroy)(stroke_list_t *this); +}; + +/** + * Create a stroke_list instance. + */ +stroke_list_t *stroke_list_create(); + +#endif /* STROKE_LIST_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_plugin.c b/src/charon/plugins/stroke/stroke_plugin.c new file mode 100644 index 000000000..6933fc074 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_plugin.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_plugin.h" + +#include <library.h> +#include "stroke_socket.h" + +typedef struct private_stroke_plugin_t private_stroke_plugin_t; + +/** + * private data of stroke_plugin + */ +struct private_stroke_plugin_t { + + /** + * public functions + */ + stroke_plugin_t public; + + /** + * stroke socket, receives strokes + */ + stroke_socket_t *socket; +}; + +/** + * Implementation of stroke_plugin_t.destroy + */ +static void destroy(private_stroke_plugin_t *this) +{ + this->socket->destroy(this->socket); + free(this); +} + +/* + * see header file + */ +plugin_t *plugin_create() +{ + private_stroke_plugin_t *this = malloc_thing(private_stroke_plugin_t); + + this->public.plugin.destroy = (void(*)(plugin_t*))destroy; + + this->socket = stroke_socket_create(); + if (this->socket == NULL) + { + free(this); + return NULL; + } + return &this->public.plugin; +} + diff --git a/src/charon/plugins/stroke/stroke_plugin.h b/src/charon/plugins/stroke/stroke_plugin.h new file mode 100644 index 000000000..7ea18b8af --- /dev/null +++ b/src/charon/plugins/stroke/stroke_plugin.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id: stroke.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup stroke stroke + * @ingroup cplugins + * + * @defgroup stroke_plugin stroke_plugin + * @{ @ingroup stroke + */ + +#ifndef STROKE_PLUGIN_H_ +#define STROKE_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct stroke_plugin_t stroke_plugin_t; + +/** + * strongSwan 2.x style configuration and control interface. + * + * Stroke is a home-brewed communication interface inspired by whack. It + * uses a unix socket (/var/run/charon.ctl). + */ +struct stroke_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +/** + * Instanciate stroke plugin. + */ +plugin_t *plugin_create(); + +#endif /* STROKE_PLUGIN_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_shared_key.c b/src/charon/plugins/stroke/stroke_shared_key.c new file mode 100644 index 000000000..9c21eb830 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_shared_key.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_shared_key.h" + +#include <utils/linked_list.h> + +typedef struct private_stroke_shared_key_t private_stroke_shared_key_t; + +/** + * private data of shared_key + */ +struct private_stroke_shared_key_t { + + /** + * implements shared_key_t + */ + stroke_shared_key_t public; + + /** + * type of this key + */ + shared_key_type_t type; + + /** + * data of the key + */ + chunk_t key; + + /** + * list of key owners, as identification_t + */ + linked_list_t *owners; + + /** + * reference counter + */ + refcount_t ref; +}; + +/** + * Implementation of shared_key_t.get_type. + */ +static shared_key_type_t get_type(private_stroke_shared_key_t *this) +{ + return this->type; +} + +/** + * Implementation of shared_key_t.get_ref. + */ +static private_stroke_shared_key_t* get_ref(private_stroke_shared_key_t *this) +{ + ref_get(&this->ref); + return this; +} + +/** + * Implementation of shared_key_t.get_key. + */ +static chunk_t get_key(private_stroke_shared_key_t *this) +{ + return this->key; +} + +/** + * Implementation of stroke_shared_key_t.has_owner. + */ +static id_match_t has_owner(private_stroke_shared_key_t *this, identification_t *owner) +{ + enumerator_t *enumerator; + id_match_t match, best = ID_MATCH_NONE; + identification_t *current; + + enumerator = this->owners->create_enumerator(this->owners); + while (enumerator->enumerate(enumerator, ¤t)) + { + match = owner->matches(owner, current); + if (match > best) + { + best = match; + } + } + enumerator->destroy(enumerator); + return best; +} +/** + * Implementation of stroke_shared_key_t.add_owner. + */ +static void add_owner(private_stroke_shared_key_t *this, identification_t *owner) +{ + this->owners->insert_last(this->owners, owner); +} + +/** + * Implementation of stroke_shared_key_t.destroy + */ +static void destroy(private_stroke_shared_key_t *this) +{ + if (ref_put(&this->ref)) + { + this->owners->destroy_offset(this->owners, offsetof(identification_t, destroy)); + chunk_free(&this->key); + free(this); + } +} + +/** + * create a shared key + */ +stroke_shared_key_t *stroke_shared_key_create(shared_key_type_t type, chunk_t key) +{ + private_stroke_shared_key_t *this = malloc_thing(private_stroke_shared_key_t); + + this->public.shared.get_type = (shared_key_type_t(*)(shared_key_t*))get_type; + this->public.shared.get_key = (chunk_t(*)(shared_key_t*))get_key; + this->public.shared.get_ref = (shared_key_t*(*)(shared_key_t*))get_ref; + this->public.shared.destroy = (void(*)(shared_key_t*))destroy; + this->public.add_owner = (void(*)(stroke_shared_key_t*, identification_t *owner))add_owner; + this->public.has_owner = (id_match_t(*)(stroke_shared_key_t*, identification_t *owner))has_owner; + + this->owners = linked_list_create(); + this->type = type; + this->key = key; + this->ref = 1; + + return &this->public; +} diff --git a/src/charon/plugins/stroke/stroke_shared_key.h b/src/charon/plugins/stroke/stroke_shared_key.h new file mode 100644 index 000000000..e93d8cee2 --- /dev/null +++ b/src/charon/plugins/stroke/stroke_shared_key.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_shared_key stroke_shared_key + * @{ @ingroup stroke + */ + +#ifndef STROKE_SHARED_KEY_H_ +#define STROKE_SHARED_KEY_H_ + +#include <utils/identification.h> +#include <credentials/keys/shared_key.h> + +typedef struct stroke_shared_key_t stroke_shared_key_t; + +/** + * Shared key implementation for keys read from ipsec.secrets + */ +struct stroke_shared_key_t { + + /** + * Implements the shared_key_t interface. + */ + shared_key_t shared; + + /** + * Add an owner to the key. + * + * @param owner owner to add + */ + void (*add_owner)(stroke_shared_key_t *this, identification_t *owner); + + /** + * Check if a key has a specific owner. + * + * @param owner owner to check + * @return best match found + */ + id_match_t (*has_owner)(stroke_shared_key_t *this, identification_t *owner); +}; + +/** + * Create a stroke_shared_key instance. + */ +stroke_shared_key_t *stroke_shared_key_create(shared_key_type_t type, chunk_t key); + +#endif /* STROKE_SHARED_KEY_H_ @}*/ diff --git a/src/charon/plugins/stroke/stroke_socket.c b/src/charon/plugins/stroke/stroke_socket.c new file mode 100644 index 000000000..92e295a0c --- /dev/null +++ b/src/charon/plugins/stroke/stroke_socket.c @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +#include "stroke_socket.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <processing/jobs/callback_job.h> +#include <daemon.h> + +#include "stroke_config.h" +#include "stroke_control.h" +#include "stroke_cred.h" +#include "stroke_ca.h" +#include "stroke_attribute.h" +#include "stroke_list.h" + +typedef struct stroke_job_context_t stroke_job_context_t; +typedef struct private_stroke_socket_t private_stroke_socket_t; + +/** + * private data of stroke_socket + */ +struct private_stroke_socket_t { + + /** + * public functions + */ + stroke_socket_t public; + + /** + * Unix socket to listen for strokes + */ + int socket; + + /** + * job accepting stroke messages + */ + callback_job_t *job; + + /** + * configuration backend + */ + stroke_config_t *config; + + /** + * attribute provider + */ + stroke_attribute_t *attribute; + + /** + * controller to control daemon + */ + stroke_control_t *control; + + /** + * credential set + */ + stroke_cred_t *cred; + + /** + * CA sections + */ + stroke_ca_t *ca; + + /** + * Status information logging + */ + stroke_list_t *list; +}; + +/** + * job context to pass to processing thread + */ +struct stroke_job_context_t { + + /** + * file descriptor to read from + */ + int fd; + + /** + * global stroke interface + */ + private_stroke_socket_t *this; +}; + +/** + * Helper function which corrects the string pointers + * in a stroke_msg_t. Strings in a stroke_msg sent over "wire" + * contains RELATIVE addresses (relative to the beginning of the + * stroke_msg). They must be corrected if they reach our address + * space... + */ +static void pop_string(stroke_msg_t *msg, char **string) +{ + if (*string == NULL) + { + return; + } + + /* check for sanity of string pointer and string */ + if (string < (char**)msg || + string > (char**)msg + sizeof(stroke_msg_t) || + (unsigned long)*string < (unsigned long)((char*)msg->buffer - (char*)msg) || + (unsigned long)*string > msg->length) + { + *string = "(invalid pointer in stroke msg)"; + } + else + { + *string = (char*)msg + (unsigned long)*string; + } +} + +/** + * Pop the strings of a stroke_end_t struct and log them for debugging purposes + */ +static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end) +{ + pop_string(msg, &end->address); + pop_string(msg, &end->subnets); + pop_string(msg, &end->sourceip); + pop_string(msg, &end->id); + pop_string(msg, &end->cert); + pop_string(msg, &end->ca); + pop_string(msg, &end->groups); + pop_string(msg, &end->updown); + + DBG2(DBG_CFG, " %s=%s", label, end->address); + DBG2(DBG_CFG, " %ssubnet=%s", label, end->subnets); + DBG2(DBG_CFG, " %ssourceip=%s", label, end->sourceip); + DBG2(DBG_CFG, " %sid=%s", label, end->id); + DBG2(DBG_CFG, " %scert=%s", label, end->cert); + DBG2(DBG_CFG, " %sca=%s", label, end->ca); + DBG2(DBG_CFG, " %sgroups=%s", label, end->groups); + DBG2(DBG_CFG, " %supdown=%s", label, end->updown); +} + +/** + * Add a connection to the configuration list + */ +static void stroke_add_conn(private_stroke_socket_t *this, stroke_msg_t *msg) +{ + pop_string(msg, &msg->add_conn.name); + DBG1(DBG_CFG, "received stroke: add connection '%s'", msg->add_conn.name); + + DBG2(DBG_CFG, "conn %s", msg->add_conn.name); + pop_end(msg, "left", &msg->add_conn.me); + pop_end(msg, "right", &msg->add_conn.other); + pop_string(msg, &msg->add_conn.algorithms.ike); + pop_string(msg, &msg->add_conn.algorithms.esp); + pop_string(msg, &msg->add_conn.ikeme.mediated_by); + pop_string(msg, &msg->add_conn.ikeme.peerid); + DBG2(DBG_CFG, " ike=%s", msg->add_conn.algorithms.ike); + DBG2(DBG_CFG, " esp=%s", msg->add_conn.algorithms.esp); + DBG2(DBG_CFG, " mediation=%s", msg->add_conn.ikeme.mediation ? "yes" : "no"); + DBG2(DBG_CFG, " mediated_by=%s", msg->add_conn.ikeme.mediated_by); + DBG2(DBG_CFG, " me_peerid=%s", msg->add_conn.ikeme.peerid); + + this->config->add(this->config, msg); + this->attribute->add_pool(this->attribute, msg); +} + +/** + * Delete a connection from the list + */ +static void stroke_del_conn(private_stroke_socket_t *this, stroke_msg_t *msg) +{ + pop_string(msg, &msg->del_conn.name); + DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); + + this->config->del(this->config, msg); + this->attribute->del_pool(this->attribute, msg); +} + +/** + * initiate a connection by name + */ +static void stroke_initiate(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->initiate.name); + DBG1(DBG_CFG, "received stroke: initiate '%s'", msg->initiate.name); + + this->control->initiate(this->control, msg, out); +} + +/** + * terminate a connection by name + */ +static void stroke_terminate(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->terminate.name); + DBG1(DBG_CFG, "received stroke: terminate '%s'", msg->terminate.name); + + this->control->terminate(this->control, msg, out); +} + +/** + * route a policy (install SPD entries) + */ +static void stroke_route(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->route.name); + DBG1(DBG_CFG, "received stroke: route '%s'", msg->route.name); + + this->control->route(this->control, msg, out); +} + +/** + * unroute a policy + */ +static void stroke_unroute(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->terminate.name); + DBG1(DBG_CFG, "received stroke: unroute '%s'", msg->route.name); + + this->control->unroute(this->control, msg, out); +} + +/** + * Add a ca information record to the cainfo list + */ +static void stroke_add_ca(private_stroke_socket_t *this, + stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->add_ca.name); + DBG1(DBG_CFG, "received stroke: add ca '%s'", msg->add_ca.name); + + pop_string(msg, &msg->add_ca.cacert); + pop_string(msg, &msg->add_ca.crluri); + pop_string(msg, &msg->add_ca.crluri2); + pop_string(msg, &msg->add_ca.ocspuri); + pop_string(msg, &msg->add_ca.ocspuri2); + pop_string(msg, &msg->add_ca.certuribase); + DBG2(DBG_CFG, "ca %s", msg->add_ca.name); + DBG2(DBG_CFG, " cacert=%s", msg->add_ca.cacert); + DBG2(DBG_CFG, " crluri=%s", msg->add_ca.crluri); + DBG2(DBG_CFG, " crluri2=%s", msg->add_ca.crluri2); + DBG2(DBG_CFG, " ocspuri=%s", msg->add_ca.ocspuri); + DBG2(DBG_CFG, " ocspuri2=%s", msg->add_ca.ocspuri2); + DBG2(DBG_CFG, " certuribase=%s", msg->add_ca.certuribase); + + this->ca->add(this->ca, msg); +} + +/** + * Delete a ca information record from the cainfo list + */ +static void stroke_del_ca(private_stroke_socket_t *this, + stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->del_ca.name); + DBG1(DBG_CFG, "received stroke: delete ca '%s'", msg->del_ca.name); + + this->ca->del(this->ca, msg); +} + + +/** + * show status of daemon + */ +static void stroke_status(private_stroke_socket_t *this, + stroke_msg_t *msg, FILE *out, bool all) +{ + pop_string(msg, &(msg->status.name)); + + this->list->status(this->list, msg, out, all); +} + +/** + * list various information + */ +static void stroke_list(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + if (msg->list.flags & LIST_CAINFOS) + { + this->ca->list(this->ca, msg, out); + } + this->list->list(this->list, msg, out); +} + +/** + * reread various information + */ +static void stroke_reread(private_stroke_socket_t *this, + stroke_msg_t *msg, FILE *out) +{ + this->cred->reread(this->cred, msg); +} + +/** + * purge various information + */ +static void stroke_purge(private_stroke_socket_t *this, + stroke_msg_t *msg, FILE *out) +{ + charon->credentials->flush_cache(charon->credentials, + CERT_X509_OCSP_RESPONSE); +} + +signal_t get_signal_from_logtype(char *type) +{ + if (strcasecmp(type, "any") == 0) return SIG_ANY; + else if (strcasecmp(type, "mgr") == 0) return DBG_MGR; + else if (strcasecmp(type, "ike") == 0) return DBG_IKE; + else if (strcasecmp(type, "chd") == 0) return DBG_CHD; + else if (strcasecmp(type, "job") == 0) return DBG_JOB; + else if (strcasecmp(type, "cfg") == 0) return DBG_CFG; + else if (strcasecmp(type, "knl") == 0) return DBG_KNL; + else if (strcasecmp(type, "net") == 0) return DBG_NET; + else if (strcasecmp(type, "enc") == 0) return DBG_ENC; + else if (strcasecmp(type, "lib") == 0) return DBG_LIB; + else return -1; +} + +/** + * set the verbosity debug output + */ +static void stroke_loglevel(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + signal_t signal; + + pop_string(msg, &(msg->loglevel.type)); + DBG1(DBG_CFG, "received stroke: loglevel %d for %s", + msg->loglevel.level, msg->loglevel.type); + + signal = get_signal_from_logtype(msg->loglevel.type); + if (signal < 0) + { + fprintf(out, "invalid type (%s)!\n", msg->loglevel.type); + return; + } + + charon->outlog->set_level(charon->outlog, signal, msg->loglevel.level); + charon->syslog->set_level(charon->syslog, signal, msg->loglevel.level); +} + +/** + * set various config options + */ +static void stroke_config(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + this->cred->cachecrl(this->cred, msg->config.cachecrl); +} + +/** + * destroy a job context + */ +static void stroke_job_context_destroy(stroke_job_context_t *this) +{ + if (this->fd) + { + close(this->fd); + } + free(this); +} + +/** + * process a stroke request from the socket pointed by "fd" + */ +static job_requeue_t process(stroke_job_context_t *ctx) +{ + stroke_msg_t *msg; + u_int16_t msg_length; + ssize_t bytes_read; + FILE *out; + private_stroke_socket_t *this = ctx->this; + int strokefd = ctx->fd; + + /* peek the length */ + bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK); + if (bytes_read != sizeof(msg_length)) + { + DBG1(DBG_CFG, "reading length of stroke message failed: %s", + strerror(errno)); + return JOB_REQUEUE_NONE; + } + + /* read message */ + msg = alloca(msg_length); + bytes_read = recv(strokefd, msg, msg_length, 0); + if (bytes_read != msg_length) + { + DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno)); + return JOB_REQUEUE_NONE; + } + + out = fdopen(strokefd, "w"); + if (out == NULL) + { + DBG1(DBG_CFG, "opening stroke output channel failed: %s", strerror(errno)); + return JOB_REQUEUE_NONE; + } + + DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length); + + switch (msg->type) + { + case STR_INITIATE: + stroke_initiate(this, msg, out); + break; + case STR_ROUTE: + stroke_route(this, msg, out); + break; + case STR_UNROUTE: + stroke_unroute(this, msg, out); + break; + case STR_TERMINATE: + stroke_terminate(this, msg, out); + break; + case STR_STATUS: + stroke_status(this, msg, out, FALSE); + break; + case STR_STATUS_ALL: + stroke_status(this, msg, out, TRUE); + break; + case STR_ADD_CONN: + stroke_add_conn(this, msg); + break; + case STR_DEL_CONN: + stroke_del_conn(this, msg); + break; + case STR_ADD_CA: + stroke_add_ca(this, msg, out); + break; + case STR_DEL_CA: + stroke_del_ca(this, msg, out); + break; + case STR_LOGLEVEL: + stroke_loglevel(this, msg, out); + break; + case STR_CONFIG: + stroke_config(this, msg, out); + break; + case STR_LIST: + stroke_list(this, msg, out); + break; + case STR_REREAD: + stroke_reread(this, msg, out); + break; + case STR_PURGE: + stroke_purge(this, msg, out); + break; + default: + DBG1(DBG_CFG, "received unknown stroke"); + break; + } + fclose(out); + /* fclose() closes underlying FD */ + ctx->fd = 0; + return JOB_REQUEUE_NONE; +} + +/** + * Implementation of private_stroke_socket_t.stroke_receive. + */ +static job_requeue_t receive(private_stroke_socket_t *this) +{ + struct sockaddr_un strokeaddr; + int strokeaddrlen = sizeof(strokeaddr); + int strokefd; + int oldstate; + callback_job_t *job; + stroke_job_context_t *ctx; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen); + pthread_setcancelstate(oldstate, NULL); + + if (strokefd < 0) + { + DBG1(DBG_CFG, "accepting stroke connection failed: %s", strerror(errno)); + return JOB_REQUEUE_FAIR; + } + + ctx = malloc_thing(stroke_job_context_t); + ctx->fd = strokefd; + ctx->this = this; + job = callback_job_create((callback_job_cb_t)process, + ctx, (void*)stroke_job_context_destroy, this->job); + charon->processor->queue_job(charon->processor, (job_t*)job); + + return JOB_REQUEUE_FAIR; +} + + +/** + * initialize and open stroke socket + */ +static bool open_socket(private_stroke_socket_t *this) +{ + struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; + mode_t old; + + /* set up unix socket */ + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (this->socket == -1) + { + DBG1(DBG_CFG, "could not create stroke socket"); + return FALSE; + } + + unlink(socket_addr.sun_path); + old = umask(~(S_IRWXU | S_IRWXG)); + if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + { + DBG1(DBG_CFG, "could not bind stroke socket: %s", strerror(errno)); + close(this->socket); + return FALSE; + } + umask(old); + if (chown(socket_addr.sun_path, charon->uid, charon->gid) != 0) + { + DBG1(DBG_CFG, "changing stroke socket permissions failed: %s", + strerror(errno)); + } + + if (listen(this->socket, 0) < 0) + { + DBG1(DBG_CFG, "could not listen on stroke socket: %s", strerror(errno)); + close(this->socket); + unlink(socket_addr.sun_path); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of stroke_socket_t.destroy + */ +static void destroy(private_stroke_socket_t *this) +{ + this->job->cancel(this->job); + charon->credentials->remove_set(charon->credentials, &this->ca->set); + charon->credentials->remove_set(charon->credentials, &this->cred->set); + charon->backends->remove_backend(charon->backends, &this->config->backend); + charon->attributes->remove_provider(charon->attributes, &this->attribute->provider); + this->cred->destroy(this->cred); + this->ca->destroy(this->ca); + this->config->destroy(this->config); + this->attribute->destroy(this->attribute); + this->control->destroy(this->control); + this->list->destroy(this->list); + free(this); +} + +/* + * see header file + */ +stroke_socket_t *stroke_socket_create() +{ + private_stroke_socket_t *this = malloc_thing(private_stroke_socket_t); + + this->public.destroy = (void(*)(stroke_socket_t*))destroy; + + if (!open_socket(this)) + { + free(this); + return NULL; + } + + this->cred = stroke_cred_create(); + this->attribute = stroke_attribute_create(); + this->ca = stroke_ca_create(this->cred); + this->config = stroke_config_create(this->ca, this->cred); + this->control = stroke_control_create(); + this->list = stroke_list_create(); + + charon->credentials->add_set(charon->credentials, &this->ca->set); + charon->credentials->add_set(charon->credentials, &this->cred->set); + charon->backends->add_backend(charon->backends, &this->config->backend); + charon->attributes->add_provider(charon->attributes, &this->attribute->provider); + + this->job = callback_job_create((callback_job_cb_t)receive, + this, NULL, NULL); + charon->processor->queue_job(charon->processor, (job_t*)this->job); + + return &this->public; +} + diff --git a/src/charon/plugins/stroke/stroke_socket.h b/src/charon/plugins/stroke/stroke_socket.h new file mode 100644 index 000000000..7bc96686f --- /dev/null +++ b/src/charon/plugins/stroke/stroke_socket.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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. + * + * $Id$ + */ + +/** + * @defgroup stroke_socket stroke_socket + * @{ @ingroup stroke + */ + +#ifndef STROKE_SOCKET_H_ +#define STROKE_SOCKET_H_ + +typedef struct stroke_socket_t stroke_socket_t; + +/** + * Stroke socket, opens UNIX communication socket, reads and dispatches. + */ +struct stroke_socket_t { + + /** + * Destroy a stroke_socket instance. + */ + void (*destroy)(stroke_socket_t *this); +}; + +/** + * Create a stroke_socket instance. + */ +stroke_socket_t *stroke_socket_create(); + +#endif /* STROKE_SOCKET_H_ @}*/ |