diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:41:31 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:41:31 +0000 |
commit | 774a362e87feab25f1be16fbca08269ddc7121a4 (patch) | |
tree | cf71f4e7466468ac3edc2127125f333224a9acfb /src/charon | |
parent | c54a140a445bfe7aa66721f68bb0781f26add91c (diff) | |
download | vyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.tar.gz vyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.zip |
Major new upstream release, just ran svn-upgrade for now (and wrote some
debian/changelong entries).
Diffstat (limited to 'src/charon')
172 files changed, 49661 insertions, 0 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am new file mode 100644 index 000000000..9522b6e6d --- /dev/null +++ b/src/charon/Makefile.am @@ -0,0 +1,87 @@ +# SUBDIRS = . testing + +eap_LTLIBRARIES = libeapidentity.la + +# always build EAP Identity module +libeapidentity_la_SOURCES = sa/authenticators/eap/eap_identity.h sa/authenticators/eap/eap_identity.c +libeapidentity_la_LDFLAGS = -module + +# build optional EAP modules +if BUILD_EAP_SIM + eap_LTLIBRARIES += libeapsim.la + libeapsim_la_SOURCES = sa/authenticators/eap/eap_sim.h sa/authenticators/eap/eap_sim.c + libeapsim_la_LDFLAGS = -module +endif + +ipsec_PROGRAMS = charon + +charon_SOURCES = \ +bus/bus.c bus/bus.h \ +bus/listeners/sys_logger.c bus/listeners/sys_logger.h \ +bus/listeners/file_logger.c bus/listeners/file_logger.h \ +config/connections/connection.c config/connections/connection.h \ +config/connections/local_connection_store.c config/connections/local_connection_store.h config/connections/connection_store.h \ +config/policies/policy.c config/policies/policy.h \ +config/policies/local_policy_store.c config/policies/policy_store.h config/policies/local_policy_store.h \ +config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ +config/traffic_selector.c config/traffic_selector.h \ +config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ +sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \ +sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \ +sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ +sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \ +sa/tasks/ike_init.c sa/tasks/ike_init.h \ +sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_auth.c sa/tasks/ike_auth.h \ +sa/tasks/ike_config.c sa/tasks/ike_config.h \ +sa/tasks/ike_cert.c sa/tasks/ike_cert.h \ +sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ +sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ +sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ +sa/tasks/child_create.c sa/tasks/child_create.h \ +sa/tasks/child_delete.c sa/tasks/child_delete.h \ +sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ +sa/authenticators/authenticator.c sa/authenticators/authenticator.h \ +sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \ +sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \ +sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \ +encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ +encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \ +encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \ +encoding/payloads/configuration_attribute.c encoding/payloads/transform_substructure.c \ +encoding/payloads/encryption_payload.h encoding/payloads/auth_payload.c encoding/payloads/ike_header.c \ +encoding/payloads/transform_substructure.h encoding/payloads/nonce_payload.c encoding/payloads/cert_payload.h \ +encoding/payloads/eap_payload.c encoding/payloads/ike_header.h encoding/payloads/auth_payload.h \ +encoding/payloads/ts_payload.c encoding/payloads/traffic_selector_substructure.h encoding/payloads/nonce_payload.h \ +encoding/payloads/notify_payload.c encoding/payloads/eap_payload.h encoding/payloads/notify_payload.h \ +encoding/payloads/ts_payload.h encoding/payloads/id_payload.c encoding/payloads/ke_payload.c \ +encoding/payloads/unknown_payload.c encoding/payloads/encodings.c encoding/payloads/id_payload.h \ +encoding/payloads/cp_payload.c encoding/payloads/delete_payload.c encoding/payloads/sa_payload.c \ +encoding/payloads/ke_payload.h encoding/payloads/unknown_payload.h encoding/payloads/encodings.h \ +encoding/payloads/certreq_payload.c encoding/payloads/cp_payload.h encoding/payloads/delete_payload.h \ +encoding/payloads/sa_payload.h encoding/payloads/vendor_id_payload.c encoding/payloads/certreq_payload.h \ +encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/payloads/payload.c \ +encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ +encoding/parser.c daemon.c daemon.h network/packet.c \ +network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ +queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \ +queues/jobs/process_message_job.h queues/jobs/process_message_job.c \ +queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ +queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \ +queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ +queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ +queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ +queues/jobs/acquire_job.c queues/jobs/acquire_job.h queues/jobs/rekey_ike_sa_job.c queues/jobs/rekey_ike_sa_job.h \ +queues/job_queue.c queues/event_queue.c queues/job_queue.h queues/event_queue.h \ +threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \ +threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \ +threads/thread_pool.h threads/receiver.h threads/stroke_interface.h + +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}\" -DIPSEC_EAPDIR=\"${eapdir}\" +charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread -lm -ldl + +if USE_LIBCURL + charon_LDADD += -lcurl +endif + diff --git a/src/charon/Makefile.in b/src/charon/Makefile.in new file mode 100644 index 000000000..0f2979d32 --- /dev/null +++ b/src/charon/Makefile.in @@ -0,0 +1,1878 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +# SUBDIRS = . testing + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ + +# build optional EAP modules +@BUILD_EAP_SIM_TRUE@am__append_1 = libeapsim.la +ipsec_PROGRAMS = charon$(EXEEXT) +@USE_LIBCURL_TRUE@am__append_2 = -lcurl +subdir = src/charon +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)$(eapdir)" "$(DESTDIR)$(ipsecdir)" +eapLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(eap_LTLIBRARIES) +libeapidentity_la_LIBADD = +am_libeapidentity_la_OBJECTS = eap_identity.lo +libeapidentity_la_OBJECTS = $(am_libeapidentity_la_OBJECTS) +libeapsim_la_LIBADD = +am__libeapsim_la_SOURCES_DIST = sa/authenticators/eap/eap_sim.h \ + sa/authenticators/eap/eap_sim.c +@BUILD_EAP_SIM_TRUE@am_libeapsim_la_OBJECTS = eap_sim.lo +libeapsim_la_OBJECTS = $(am_libeapsim_la_OBJECTS) +@BUILD_EAP_SIM_TRUE@am_libeapsim_la_rpath = -rpath $(eapdir) +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am_charon_OBJECTS = bus.$(OBJEXT) sys_logger.$(OBJEXT) \ + file_logger.$(OBJEXT) connection.$(OBJEXT) \ + local_connection_store.$(OBJEXT) policy.$(OBJEXT) \ + local_policy_store.$(OBJEXT) local_credential_store.$(OBJEXT) \ + traffic_selector.$(OBJEXT) proposal.$(OBJEXT) \ + configuration.$(OBJEXT) eap_authenticator.$(OBJEXT) \ + eap_method.$(OBJEXT) child_sa.$(OBJEXT) ike_sa.$(OBJEXT) \ + ike_sa_manager.$(OBJEXT) ike_sa_id.$(OBJEXT) task.$(OBJEXT) \ + ike_init.$(OBJEXT) ike_natd.$(OBJEXT) ike_auth.$(OBJEXT) \ + ike_config.$(OBJEXT) ike_cert.$(OBJEXT) ike_rekey.$(OBJEXT) \ + ike_delete.$(OBJEXT) ike_dpd.$(OBJEXT) child_create.$(OBJEXT) \ + child_delete.$(OBJEXT) child_rekey.$(OBJEXT) \ + authenticator.$(OBJEXT) rsa_authenticator.$(OBJEXT) \ + psk_authenticator.$(OBJEXT) task_manager.$(OBJEXT) \ + encryption_payload.$(OBJEXT) cert_payload.$(OBJEXT) \ + traffic_selector_substructure.$(OBJEXT) \ + transform_attribute.$(OBJEXT) \ + configuration_attribute.$(OBJEXT) \ + transform_substructure.$(OBJEXT) auth_payload.$(OBJEXT) \ + ike_header.$(OBJEXT) nonce_payload.$(OBJEXT) \ + eap_payload.$(OBJEXT) ts_payload.$(OBJEXT) \ + notify_payload.$(OBJEXT) id_payload.$(OBJEXT) \ + ke_payload.$(OBJEXT) unknown_payload.$(OBJEXT) \ + encodings.$(OBJEXT) cp_payload.$(OBJEXT) \ + delete_payload.$(OBJEXT) sa_payload.$(OBJEXT) \ + certreq_payload.$(OBJEXT) vendor_id_payload.$(OBJEXT) \ + proposal_substructure.$(OBJEXT) payload.$(OBJEXT) \ + message.$(OBJEXT) generator.$(OBJEXT) parser.$(OBJEXT) \ + daemon.$(OBJEXT) packet.$(OBJEXT) socket.$(OBJEXT) \ + job.$(OBJEXT) process_message_job.$(OBJEXT) \ + delete_ike_sa_job.$(OBJEXT) retransmit_job.$(OBJEXT) \ + initiate_job.$(OBJEXT) send_keepalive_job.$(OBJEXT) \ + rekey_child_sa_job.$(OBJEXT) delete_child_sa_job.$(OBJEXT) \ + send_dpd_job.$(OBJEXT) route_job.$(OBJEXT) \ + acquire_job.$(OBJEXT) rekey_ike_sa_job.$(OBJEXT) \ + job_queue.$(OBJEXT) event_queue.$(OBJEXT) \ + kernel_interface.$(OBJEXT) thread_pool.$(OBJEXT) \ + scheduler.$(OBJEXT) sender.$(OBJEXT) receiver.$(OBJEXT) \ + stroke_interface.$(OBJEXT) +charon_OBJECTS = $(am_charon_OBJECTS) +am__DEPENDENCIES_1 = +charon_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) +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 --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libeapidentity_la_SOURCES) $(libeapsim_la_SOURCES) \ + $(charon_SOURCES) +DIST_SOURCES = $(libeapidentity_la_SOURCES) \ + $(am__libeapsim_la_SOURCES_DIST) $(charon_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +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@ +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_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +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@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +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@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +eap_LTLIBRARIES = libeapidentity.la $(am__append_1) + +# always build EAP Identity module +libeapidentity_la_SOURCES = sa/authenticators/eap/eap_identity.h sa/authenticators/eap/eap_identity.c +libeapidentity_la_LDFLAGS = -module +@BUILD_EAP_SIM_TRUE@libeapsim_la_SOURCES = sa/authenticators/eap/eap_sim.h sa/authenticators/eap/eap_sim.c +@BUILD_EAP_SIM_TRUE@libeapsim_la_LDFLAGS = -module +charon_SOURCES = \ +bus/bus.c bus/bus.h \ +bus/listeners/sys_logger.c bus/listeners/sys_logger.h \ +bus/listeners/file_logger.c bus/listeners/file_logger.h \ +config/connections/connection.c config/connections/connection.h \ +config/connections/local_connection_store.c config/connections/local_connection_store.h config/connections/connection_store.h \ +config/policies/policy.c config/policies/policy.h \ +config/policies/local_policy_store.c config/policies/policy_store.h config/policies/local_policy_store.h \ +config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ +config/traffic_selector.c config/traffic_selector.h \ +config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ +sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \ +sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \ +sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ +sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \ +sa/tasks/ike_init.c sa/tasks/ike_init.h \ +sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_auth.c sa/tasks/ike_auth.h \ +sa/tasks/ike_config.c sa/tasks/ike_config.h \ +sa/tasks/ike_cert.c sa/tasks/ike_cert.h \ +sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ +sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ +sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ +sa/tasks/child_create.c sa/tasks/child_create.h \ +sa/tasks/child_delete.c sa/tasks/child_delete.h \ +sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ +sa/authenticators/authenticator.c sa/authenticators/authenticator.h \ +sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \ +sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \ +sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \ +encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ +encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \ +encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \ +encoding/payloads/configuration_attribute.c encoding/payloads/transform_substructure.c \ +encoding/payloads/encryption_payload.h encoding/payloads/auth_payload.c encoding/payloads/ike_header.c \ +encoding/payloads/transform_substructure.h encoding/payloads/nonce_payload.c encoding/payloads/cert_payload.h \ +encoding/payloads/eap_payload.c encoding/payloads/ike_header.h encoding/payloads/auth_payload.h \ +encoding/payloads/ts_payload.c encoding/payloads/traffic_selector_substructure.h encoding/payloads/nonce_payload.h \ +encoding/payloads/notify_payload.c encoding/payloads/eap_payload.h encoding/payloads/notify_payload.h \ +encoding/payloads/ts_payload.h encoding/payloads/id_payload.c encoding/payloads/ke_payload.c \ +encoding/payloads/unknown_payload.c encoding/payloads/encodings.c encoding/payloads/id_payload.h \ +encoding/payloads/cp_payload.c encoding/payloads/delete_payload.c encoding/payloads/sa_payload.c \ +encoding/payloads/ke_payload.h encoding/payloads/unknown_payload.h encoding/payloads/encodings.h \ +encoding/payloads/certreq_payload.c encoding/payloads/cp_payload.h encoding/payloads/delete_payload.h \ +encoding/payloads/sa_payload.h encoding/payloads/vendor_id_payload.c encoding/payloads/certreq_payload.h \ +encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/payloads/payload.c \ +encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ +encoding/parser.c daemon.c daemon.h network/packet.c \ +network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ +queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \ +queues/jobs/process_message_job.h queues/jobs/process_message_job.c \ +queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ +queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \ +queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ +queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ +queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ +queues/jobs/acquire_job.c queues/jobs/acquire_job.h queues/jobs/rekey_ike_sa_job.c queues/jobs/rekey_ike_sa_job.h \ +queues/job_queue.c queues/event_queue.c queues/job_queue.h queues/event_queue.h \ +threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \ +threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \ +threads/thread_pool.h threads/receiver.h threads/stroke_interface.h + +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}\" -DIPSEC_EAPDIR=\"${eapdir}\" +charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ + -lgmp -lpthread -lm -ldl $(am__append_2) +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/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/charon/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-eapLTLIBRARIES: $(eap_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(eapdir)" || $(mkdir_p) "$(DESTDIR)$(eapdir)" + @list='$(eap_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(eapLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(eapdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(eapLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(eapdir)/$$f"; \ + else :; fi; \ + done + +uninstall-eapLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(eap_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(eapdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(eapdir)/$$p"; \ + done + +clean-eapLTLIBRARIES: + -test -z "$(eap_LTLIBRARIES)" || rm -f $(eap_LTLIBRARIES) + @list='$(eap_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 +libeapidentity.la: $(libeapidentity_la_OBJECTS) $(libeapidentity_la_DEPENDENCIES) + $(LINK) -rpath $(eapdir) $(libeapidentity_la_LDFLAGS) $(libeapidentity_la_OBJECTS) $(libeapidentity_la_LIBADD) $(LIBS) +libeapsim.la: $(libeapsim_la_OBJECTS) $(libeapsim_la_DEPENDENCIES) + $(LINK) $(am_libeapsim_la_rpath) $(libeapsim_la_LDFLAGS) $(libeapsim_la_OBJECTS) $(libeapsim_la_LIBADD) $(LIBS) +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +charon$(EXEEXT): $(charon_OBJECTS) $(charon_DEPENDENCIES) + @rm -f charon$(EXEEXT) + $(LINK) $(charon_LDFLAGS) $(charon_OBJECTS) $(charon_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acquire_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cert_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certreq_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_create.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_rekey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_sa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration_attribute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_child_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_ike_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_identity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_method.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_sim.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encodings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encryption_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_logger.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_auth.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_cert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_dpd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_header.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_natd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_rekey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa_id.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initiate_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ke_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_connection_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_credential_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_policy_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nonce_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process_message_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proposal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proposal_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psk_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/receiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rekey_child_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rekey_ike_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/retransmit_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sa_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scheduler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_dpd_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_keepalive_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sender.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_logger.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_pool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traffic_selector.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traffic_selector_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transform_attribute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transform_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ts_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unknown_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vendor_id_payload.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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 $@ $< + +eap_identity.lo: sa/authenticators/eap/eap_identity.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_identity.lo -MD -MP -MF "$(DEPDIR)/eap_identity.Tpo" -c -o eap_identity.lo `test -f 'sa/authenticators/eap/eap_identity.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_identity.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_identity.Tpo" "$(DEPDIR)/eap_identity.Plo"; else rm -f "$(DEPDIR)/eap_identity.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_identity.c' object='eap_identity.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_identity.lo `test -f 'sa/authenticators/eap/eap_identity.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_identity.c + +eap_sim.lo: sa/authenticators/eap/eap_sim.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_sim.lo -MD -MP -MF "$(DEPDIR)/eap_sim.Tpo" -c -o eap_sim.lo `test -f 'sa/authenticators/eap/eap_sim.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_sim.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_sim.Tpo" "$(DEPDIR)/eap_sim.Plo"; else rm -f "$(DEPDIR)/eap_sim.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_sim.c' object='eap_sim.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_sim.lo `test -f 'sa/authenticators/eap/eap_sim.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_sim.c + +bus.o: bus/bus.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus.o -MD -MP -MF "$(DEPDIR)/bus.Tpo" -c -o bus.o `test -f 'bus/bus.c' || echo '$(srcdir)/'`bus/bus.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bus.Tpo" "$(DEPDIR)/bus.Po"; else rm -f "$(DEPDIR)/bus.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/bus.c' object='bus.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus.o `test -f 'bus/bus.c' || echo '$(srcdir)/'`bus/bus.c + +bus.obj: bus/bus.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus.obj -MD -MP -MF "$(DEPDIR)/bus.Tpo" -c -o bus.obj `if test -f 'bus/bus.c'; then $(CYGPATH_W) 'bus/bus.c'; else $(CYGPATH_W) '$(srcdir)/bus/bus.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bus.Tpo" "$(DEPDIR)/bus.Po"; else rm -f "$(DEPDIR)/bus.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/bus.c' object='bus.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus.obj `if test -f 'bus/bus.c'; then $(CYGPATH_W) 'bus/bus.c'; else $(CYGPATH_W) '$(srcdir)/bus/bus.c'; fi` + +sys_logger.o: bus/listeners/sys_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sys_logger.o -MD -MP -MF "$(DEPDIR)/sys_logger.Tpo" -c -o sys_logger.o `test -f 'bus/listeners/sys_logger.c' || echo '$(srcdir)/'`bus/listeners/sys_logger.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sys_logger.Tpo" "$(DEPDIR)/sys_logger.Po"; else rm -f "$(DEPDIR)/sys_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/sys_logger.c' object='sys_logger.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sys_logger.o `test -f 'bus/listeners/sys_logger.c' || echo '$(srcdir)/'`bus/listeners/sys_logger.c + +sys_logger.obj: bus/listeners/sys_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sys_logger.obj -MD -MP -MF "$(DEPDIR)/sys_logger.Tpo" -c -o sys_logger.obj `if test -f 'bus/listeners/sys_logger.c'; then $(CYGPATH_W) 'bus/listeners/sys_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/sys_logger.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sys_logger.Tpo" "$(DEPDIR)/sys_logger.Po"; else rm -f "$(DEPDIR)/sys_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/sys_logger.c' object='sys_logger.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sys_logger.obj `if test -f 'bus/listeners/sys_logger.c'; then $(CYGPATH_W) 'bus/listeners/sys_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/sys_logger.c'; fi` + +file_logger.o: bus/listeners/file_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT file_logger.o -MD -MP -MF "$(DEPDIR)/file_logger.Tpo" -c -o file_logger.o `test -f 'bus/listeners/file_logger.c' || echo '$(srcdir)/'`bus/listeners/file_logger.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/file_logger.Tpo" "$(DEPDIR)/file_logger.Po"; else rm -f "$(DEPDIR)/file_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/file_logger.c' object='file_logger.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o file_logger.o `test -f 'bus/listeners/file_logger.c' || echo '$(srcdir)/'`bus/listeners/file_logger.c + +file_logger.obj: bus/listeners/file_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT file_logger.obj -MD -MP -MF "$(DEPDIR)/file_logger.Tpo" -c -o file_logger.obj `if test -f 'bus/listeners/file_logger.c'; then $(CYGPATH_W) 'bus/listeners/file_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/file_logger.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/file_logger.Tpo" "$(DEPDIR)/file_logger.Po"; else rm -f "$(DEPDIR)/file_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/file_logger.c' object='file_logger.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o file_logger.obj `if test -f 'bus/listeners/file_logger.c'; then $(CYGPATH_W) 'bus/listeners/file_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/file_logger.c'; fi` + +connection.o: config/connections/connection.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT connection.o -MD -MP -MF "$(DEPDIR)/connection.Tpo" -c -o connection.o `test -f 'config/connections/connection.c' || echo '$(srcdir)/'`config/connections/connection.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/connection.Tpo" "$(DEPDIR)/connection.Po"; else rm -f "$(DEPDIR)/connection.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/connection.c' object='connection.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o connection.o `test -f 'config/connections/connection.c' || echo '$(srcdir)/'`config/connections/connection.c + +connection.obj: config/connections/connection.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT connection.obj -MD -MP -MF "$(DEPDIR)/connection.Tpo" -c -o connection.obj `if test -f 'config/connections/connection.c'; then $(CYGPATH_W) 'config/connections/connection.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/connection.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/connection.Tpo" "$(DEPDIR)/connection.Po"; else rm -f "$(DEPDIR)/connection.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/connection.c' object='connection.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o connection.obj `if test -f 'config/connections/connection.c'; then $(CYGPATH_W) 'config/connections/connection.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/connection.c'; fi` + +local_connection_store.o: config/connections/local_connection_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_connection_store.o -MD -MP -MF "$(DEPDIR)/local_connection_store.Tpo" -c -o local_connection_store.o `test -f 'config/connections/local_connection_store.c' || echo '$(srcdir)/'`config/connections/local_connection_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_connection_store.Tpo" "$(DEPDIR)/local_connection_store.Po"; else rm -f "$(DEPDIR)/local_connection_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/local_connection_store.c' object='local_connection_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_connection_store.o `test -f 'config/connections/local_connection_store.c' || echo '$(srcdir)/'`config/connections/local_connection_store.c + +local_connection_store.obj: config/connections/local_connection_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_connection_store.obj -MD -MP -MF "$(DEPDIR)/local_connection_store.Tpo" -c -o local_connection_store.obj `if test -f 'config/connections/local_connection_store.c'; then $(CYGPATH_W) 'config/connections/local_connection_store.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/local_connection_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_connection_store.Tpo" "$(DEPDIR)/local_connection_store.Po"; else rm -f "$(DEPDIR)/local_connection_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/local_connection_store.c' object='local_connection_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_connection_store.obj `if test -f 'config/connections/local_connection_store.c'; then $(CYGPATH_W) 'config/connections/local_connection_store.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/local_connection_store.c'; fi` + +policy.o: config/policies/policy.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT policy.o -MD -MP -MF "$(DEPDIR)/policy.Tpo" -c -o policy.o `test -f 'config/policies/policy.c' || echo '$(srcdir)/'`config/policies/policy.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/policy.Tpo" "$(DEPDIR)/policy.Po"; else rm -f "$(DEPDIR)/policy.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/policy.c' object='policy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o policy.o `test -f 'config/policies/policy.c' || echo '$(srcdir)/'`config/policies/policy.c + +policy.obj: config/policies/policy.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT policy.obj -MD -MP -MF "$(DEPDIR)/policy.Tpo" -c -o policy.obj `if test -f 'config/policies/policy.c'; then $(CYGPATH_W) 'config/policies/policy.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/policy.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/policy.Tpo" "$(DEPDIR)/policy.Po"; else rm -f "$(DEPDIR)/policy.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/policy.c' object='policy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o policy.obj `if test -f 'config/policies/policy.c'; then $(CYGPATH_W) 'config/policies/policy.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/policy.c'; fi` + +local_policy_store.o: config/policies/local_policy_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_policy_store.o -MD -MP -MF "$(DEPDIR)/local_policy_store.Tpo" -c -o local_policy_store.o `test -f 'config/policies/local_policy_store.c' || echo '$(srcdir)/'`config/policies/local_policy_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_policy_store.Tpo" "$(DEPDIR)/local_policy_store.Po"; else rm -f "$(DEPDIR)/local_policy_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/local_policy_store.c' object='local_policy_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_policy_store.o `test -f 'config/policies/local_policy_store.c' || echo '$(srcdir)/'`config/policies/local_policy_store.c + +local_policy_store.obj: config/policies/local_policy_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_policy_store.obj -MD -MP -MF "$(DEPDIR)/local_policy_store.Tpo" -c -o local_policy_store.obj `if test -f 'config/policies/local_policy_store.c'; then $(CYGPATH_W) 'config/policies/local_policy_store.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/local_policy_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_policy_store.Tpo" "$(DEPDIR)/local_policy_store.Po"; else rm -f "$(DEPDIR)/local_policy_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/local_policy_store.c' object='local_policy_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_policy_store.obj `if test -f 'config/policies/local_policy_store.c'; then $(CYGPATH_W) 'config/policies/local_policy_store.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/local_policy_store.c'; fi` + +local_credential_store.o: config/credentials/local_credential_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_credential_store.o -MD -MP -MF "$(DEPDIR)/local_credential_store.Tpo" -c -o local_credential_store.o `test -f 'config/credentials/local_credential_store.c' || echo '$(srcdir)/'`config/credentials/local_credential_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_credential_store.Tpo" "$(DEPDIR)/local_credential_store.Po"; else rm -f "$(DEPDIR)/local_credential_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/credentials/local_credential_store.c' object='local_credential_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_credential_store.o `test -f 'config/credentials/local_credential_store.c' || echo '$(srcdir)/'`config/credentials/local_credential_store.c + +local_credential_store.obj: config/credentials/local_credential_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_credential_store.obj -MD -MP -MF "$(DEPDIR)/local_credential_store.Tpo" -c -o local_credential_store.obj `if test -f 'config/credentials/local_credential_store.c'; then $(CYGPATH_W) 'config/credentials/local_credential_store.c'; else $(CYGPATH_W) '$(srcdir)/config/credentials/local_credential_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_credential_store.Tpo" "$(DEPDIR)/local_credential_store.Po"; else rm -f "$(DEPDIR)/local_credential_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/credentials/local_credential_store.c' object='local_credential_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_credential_store.obj `if test -f 'config/credentials/local_credential_store.c'; then $(CYGPATH_W) 'config/credentials/local_credential_store.c'; else $(CYGPATH_W) '$(srcdir)/config/credentials/local_credential_store.c'; fi` + +traffic_selector.o: config/traffic_selector.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector.o -MD -MP -MF "$(DEPDIR)/traffic_selector.Tpo" -c -o traffic_selector.o `test -f 'config/traffic_selector.c' || echo '$(srcdir)/'`config/traffic_selector.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector.Tpo" "$(DEPDIR)/traffic_selector.Po"; else rm -f "$(DEPDIR)/traffic_selector.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/traffic_selector.c' object='traffic_selector.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector.o `test -f 'config/traffic_selector.c' || echo '$(srcdir)/'`config/traffic_selector.c + +traffic_selector.obj: config/traffic_selector.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector.obj -MD -MP -MF "$(DEPDIR)/traffic_selector.Tpo" -c -o traffic_selector.obj `if test -f 'config/traffic_selector.c'; then $(CYGPATH_W) 'config/traffic_selector.c'; else $(CYGPATH_W) '$(srcdir)/config/traffic_selector.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector.Tpo" "$(DEPDIR)/traffic_selector.Po"; else rm -f "$(DEPDIR)/traffic_selector.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/traffic_selector.c' object='traffic_selector.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector.obj `if test -f 'config/traffic_selector.c'; then $(CYGPATH_W) 'config/traffic_selector.c'; else $(CYGPATH_W) '$(srcdir)/config/traffic_selector.c'; fi` + +proposal.o: config/proposal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal.o -MD -MP -MF "$(DEPDIR)/proposal.Tpo" -c -o proposal.o `test -f 'config/proposal.c' || echo '$(srcdir)/'`config/proposal.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal.Tpo" "$(DEPDIR)/proposal.Po"; else rm -f "$(DEPDIR)/proposal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/proposal.c' object='proposal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal.o `test -f 'config/proposal.c' || echo '$(srcdir)/'`config/proposal.c + +proposal.obj: config/proposal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal.obj -MD -MP -MF "$(DEPDIR)/proposal.Tpo" -c -o proposal.obj `if test -f 'config/proposal.c'; then $(CYGPATH_W) 'config/proposal.c'; else $(CYGPATH_W) '$(srcdir)/config/proposal.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal.Tpo" "$(DEPDIR)/proposal.Po"; else rm -f "$(DEPDIR)/proposal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/proposal.c' object='proposal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal.obj `if test -f 'config/proposal.c'; then $(CYGPATH_W) 'config/proposal.c'; else $(CYGPATH_W) '$(srcdir)/config/proposal.c'; fi` + +configuration.o: config/configuration.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.o -MD -MP -MF "$(DEPDIR)/configuration.Tpo" -c -o configuration.o `test -f 'config/configuration.c' || echo '$(srcdir)/'`config/configuration.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration.Tpo" "$(DEPDIR)/configuration.Po"; else rm -f "$(DEPDIR)/configuration.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/configuration.c' object='configuration.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.o `test -f 'config/configuration.c' || echo '$(srcdir)/'`config/configuration.c + +configuration.obj: config/configuration.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.obj -MD -MP -MF "$(DEPDIR)/configuration.Tpo" -c -o configuration.obj `if test -f 'config/configuration.c'; then $(CYGPATH_W) 'config/configuration.c'; else $(CYGPATH_W) '$(srcdir)/config/configuration.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration.Tpo" "$(DEPDIR)/configuration.Po"; else rm -f "$(DEPDIR)/configuration.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/configuration.c' object='configuration.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.obj `if test -f 'config/configuration.c'; then $(CYGPATH_W) 'config/configuration.c'; else $(CYGPATH_W) '$(srcdir)/config/configuration.c'; fi` + +eap_authenticator.o: sa/authenticators/eap_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_authenticator.o -MD -MP -MF "$(DEPDIR)/eap_authenticator.Tpo" -c -o eap_authenticator.o `test -f 'sa/authenticators/eap_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/eap_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_authenticator.Tpo" "$(DEPDIR)/eap_authenticator.Po"; else rm -f "$(DEPDIR)/eap_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap_authenticator.c' object='eap_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_authenticator.o `test -f 'sa/authenticators/eap_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/eap_authenticator.c + +eap_authenticator.obj: sa/authenticators/eap_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_authenticator.obj -MD -MP -MF "$(DEPDIR)/eap_authenticator.Tpo" -c -o eap_authenticator.obj `if test -f 'sa/authenticators/eap_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/eap_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_authenticator.Tpo" "$(DEPDIR)/eap_authenticator.Po"; else rm -f "$(DEPDIR)/eap_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap_authenticator.c' object='eap_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_authenticator.obj `if test -f 'sa/authenticators/eap_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/eap_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap_authenticator.c'; fi` + +eap_method.o: sa/authenticators/eap/eap_method.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_method.o -MD -MP -MF "$(DEPDIR)/eap_method.Tpo" -c -o eap_method.o `test -f 'sa/authenticators/eap/eap_method.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_method.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_method.Tpo" "$(DEPDIR)/eap_method.Po"; else rm -f "$(DEPDIR)/eap_method.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_method.c' object='eap_method.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_method.o `test -f 'sa/authenticators/eap/eap_method.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_method.c + +eap_method.obj: sa/authenticators/eap/eap_method.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_method.obj -MD -MP -MF "$(DEPDIR)/eap_method.Tpo" -c -o eap_method.obj `if test -f 'sa/authenticators/eap/eap_method.c'; then $(CYGPATH_W) 'sa/authenticators/eap/eap_method.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap/eap_method.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_method.Tpo" "$(DEPDIR)/eap_method.Po"; else rm -f "$(DEPDIR)/eap_method.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_method.c' object='eap_method.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_method.obj `if test -f 'sa/authenticators/eap/eap_method.c'; then $(CYGPATH_W) 'sa/authenticators/eap/eap_method.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap/eap_method.c'; fi` + +child_sa.o: sa/child_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_sa.o -MD -MP -MF "$(DEPDIR)/child_sa.Tpo" -c -o child_sa.o `test -f 'sa/child_sa.c' || echo '$(srcdir)/'`sa/child_sa.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_sa.Tpo" "$(DEPDIR)/child_sa.Po"; else rm -f "$(DEPDIR)/child_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/child_sa.c' object='child_sa.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_sa.o `test -f 'sa/child_sa.c' || echo '$(srcdir)/'`sa/child_sa.c + +child_sa.obj: sa/child_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_sa.obj -MD -MP -MF "$(DEPDIR)/child_sa.Tpo" -c -o child_sa.obj `if test -f 'sa/child_sa.c'; then $(CYGPATH_W) 'sa/child_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/child_sa.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_sa.Tpo" "$(DEPDIR)/child_sa.Po"; else rm -f "$(DEPDIR)/child_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/child_sa.c' object='child_sa.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_sa.obj `if test -f 'sa/child_sa.c'; then $(CYGPATH_W) 'sa/child_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/child_sa.c'; fi` + +ike_sa.o: sa/ike_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa.o -MD -MP -MF "$(DEPDIR)/ike_sa.Tpo" -c -o ike_sa.o `test -f 'sa/ike_sa.c' || echo '$(srcdir)/'`sa/ike_sa.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa.Tpo" "$(DEPDIR)/ike_sa.Po"; else rm -f "$(DEPDIR)/ike_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa.c' object='ike_sa.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa.o `test -f 'sa/ike_sa.c' || echo '$(srcdir)/'`sa/ike_sa.c + +ike_sa.obj: sa/ike_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa.obj -MD -MP -MF "$(DEPDIR)/ike_sa.Tpo" -c -o ike_sa.obj `if test -f 'sa/ike_sa.c'; then $(CYGPATH_W) 'sa/ike_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa.Tpo" "$(DEPDIR)/ike_sa.Po"; else rm -f "$(DEPDIR)/ike_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa.c' object='ike_sa.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa.obj `if test -f 'sa/ike_sa.c'; then $(CYGPATH_W) 'sa/ike_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa.c'; fi` + +ike_sa_manager.o: sa/ike_sa_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_manager.o -MD -MP -MF "$(DEPDIR)/ike_sa_manager.Tpo" -c -o ike_sa_manager.o `test -f 'sa/ike_sa_manager.c' || echo '$(srcdir)/'`sa/ike_sa_manager.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_manager.Tpo" "$(DEPDIR)/ike_sa_manager.Po"; else rm -f "$(DEPDIR)/ike_sa_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_manager.c' object='ike_sa_manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_manager.o `test -f 'sa/ike_sa_manager.c' || echo '$(srcdir)/'`sa/ike_sa_manager.c + +ike_sa_manager.obj: sa/ike_sa_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_manager.obj -MD -MP -MF "$(DEPDIR)/ike_sa_manager.Tpo" -c -o ike_sa_manager.obj `if test -f 'sa/ike_sa_manager.c'; then $(CYGPATH_W) 'sa/ike_sa_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_manager.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_manager.Tpo" "$(DEPDIR)/ike_sa_manager.Po"; else rm -f "$(DEPDIR)/ike_sa_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_manager.c' object='ike_sa_manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_manager.obj `if test -f 'sa/ike_sa_manager.c'; then $(CYGPATH_W) 'sa/ike_sa_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_manager.c'; fi` + +ike_sa_id.o: sa/ike_sa_id.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_id.o -MD -MP -MF "$(DEPDIR)/ike_sa_id.Tpo" -c -o ike_sa_id.o `test -f 'sa/ike_sa_id.c' || echo '$(srcdir)/'`sa/ike_sa_id.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_id.Tpo" "$(DEPDIR)/ike_sa_id.Po"; else rm -f "$(DEPDIR)/ike_sa_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_id.c' object='ike_sa_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_id.o `test -f 'sa/ike_sa_id.c' || echo '$(srcdir)/'`sa/ike_sa_id.c + +ike_sa_id.obj: sa/ike_sa_id.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_id.obj -MD -MP -MF "$(DEPDIR)/ike_sa_id.Tpo" -c -o ike_sa_id.obj `if test -f 'sa/ike_sa_id.c'; then $(CYGPATH_W) 'sa/ike_sa_id.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_id.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_id.Tpo" "$(DEPDIR)/ike_sa_id.Po"; else rm -f "$(DEPDIR)/ike_sa_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_id.c' object='ike_sa_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_id.obj `if test -f 'sa/ike_sa_id.c'; then $(CYGPATH_W) 'sa/ike_sa_id.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_id.c'; fi` + +task.o: sa/tasks/task.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task.o -MD -MP -MF "$(DEPDIR)/task.Tpo" -c -o task.o `test -f 'sa/tasks/task.c' || echo '$(srcdir)/'`sa/tasks/task.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task.Tpo" "$(DEPDIR)/task.Po"; else rm -f "$(DEPDIR)/task.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/task.c' object='task.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task.o `test -f 'sa/tasks/task.c' || echo '$(srcdir)/'`sa/tasks/task.c + +task.obj: sa/tasks/task.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task.obj -MD -MP -MF "$(DEPDIR)/task.Tpo" -c -o task.obj `if test -f 'sa/tasks/task.c'; then $(CYGPATH_W) 'sa/tasks/task.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/task.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task.Tpo" "$(DEPDIR)/task.Po"; else rm -f "$(DEPDIR)/task.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/task.c' object='task.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task.obj `if test -f 'sa/tasks/task.c'; then $(CYGPATH_W) 'sa/tasks/task.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/task.c'; fi` + +ike_init.o: sa/tasks/ike_init.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_init.o -MD -MP -MF "$(DEPDIR)/ike_init.Tpo" -c -o ike_init.o `test -f 'sa/tasks/ike_init.c' || echo '$(srcdir)/'`sa/tasks/ike_init.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_init.Tpo" "$(DEPDIR)/ike_init.Po"; else rm -f "$(DEPDIR)/ike_init.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_init.c' object='ike_init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_init.o `test -f 'sa/tasks/ike_init.c' || echo '$(srcdir)/'`sa/tasks/ike_init.c + +ike_init.obj: sa/tasks/ike_init.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_init.obj -MD -MP -MF "$(DEPDIR)/ike_init.Tpo" -c -o ike_init.obj `if test -f 'sa/tasks/ike_init.c'; then $(CYGPATH_W) 'sa/tasks/ike_init.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_init.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_init.Tpo" "$(DEPDIR)/ike_init.Po"; else rm -f "$(DEPDIR)/ike_init.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_init.c' object='ike_init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_init.obj `if test -f 'sa/tasks/ike_init.c'; then $(CYGPATH_W) 'sa/tasks/ike_init.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_init.c'; fi` + +ike_natd.o: sa/tasks/ike_natd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_natd.o -MD -MP -MF "$(DEPDIR)/ike_natd.Tpo" -c -o ike_natd.o `test -f 'sa/tasks/ike_natd.c' || echo '$(srcdir)/'`sa/tasks/ike_natd.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_natd.Tpo" "$(DEPDIR)/ike_natd.Po"; else rm -f "$(DEPDIR)/ike_natd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_natd.c' object='ike_natd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_natd.o `test -f 'sa/tasks/ike_natd.c' || echo '$(srcdir)/'`sa/tasks/ike_natd.c + +ike_natd.obj: sa/tasks/ike_natd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_natd.obj -MD -MP -MF "$(DEPDIR)/ike_natd.Tpo" -c -o ike_natd.obj `if test -f 'sa/tasks/ike_natd.c'; then $(CYGPATH_W) 'sa/tasks/ike_natd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_natd.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_natd.Tpo" "$(DEPDIR)/ike_natd.Po"; else rm -f "$(DEPDIR)/ike_natd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_natd.c' object='ike_natd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_natd.obj `if test -f 'sa/tasks/ike_natd.c'; then $(CYGPATH_W) 'sa/tasks/ike_natd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_natd.c'; fi` + +ike_auth.o: sa/tasks/ike_auth.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_auth.o -MD -MP -MF "$(DEPDIR)/ike_auth.Tpo" -c -o ike_auth.o `test -f 'sa/tasks/ike_auth.c' || echo '$(srcdir)/'`sa/tasks/ike_auth.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_auth.Tpo" "$(DEPDIR)/ike_auth.Po"; else rm -f "$(DEPDIR)/ike_auth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_auth.c' object='ike_auth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_auth.o `test -f 'sa/tasks/ike_auth.c' || echo '$(srcdir)/'`sa/tasks/ike_auth.c + +ike_auth.obj: sa/tasks/ike_auth.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_auth.obj -MD -MP -MF "$(DEPDIR)/ike_auth.Tpo" -c -o ike_auth.obj `if test -f 'sa/tasks/ike_auth.c'; then $(CYGPATH_W) 'sa/tasks/ike_auth.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_auth.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_auth.Tpo" "$(DEPDIR)/ike_auth.Po"; else rm -f "$(DEPDIR)/ike_auth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_auth.c' object='ike_auth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_auth.obj `if test -f 'sa/tasks/ike_auth.c'; then $(CYGPATH_W) 'sa/tasks/ike_auth.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_auth.c'; fi` + +ike_config.o: sa/tasks/ike_config.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_config.o -MD -MP -MF "$(DEPDIR)/ike_config.Tpo" -c -o ike_config.o `test -f 'sa/tasks/ike_config.c' || echo '$(srcdir)/'`sa/tasks/ike_config.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_config.Tpo" "$(DEPDIR)/ike_config.Po"; else rm -f "$(DEPDIR)/ike_config.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_config.c' object='ike_config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_config.o `test -f 'sa/tasks/ike_config.c' || echo '$(srcdir)/'`sa/tasks/ike_config.c + +ike_config.obj: sa/tasks/ike_config.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_config.obj -MD -MP -MF "$(DEPDIR)/ike_config.Tpo" -c -o ike_config.obj `if test -f 'sa/tasks/ike_config.c'; then $(CYGPATH_W) 'sa/tasks/ike_config.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_config.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_config.Tpo" "$(DEPDIR)/ike_config.Po"; else rm -f "$(DEPDIR)/ike_config.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_config.c' object='ike_config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_config.obj `if test -f 'sa/tasks/ike_config.c'; then $(CYGPATH_W) 'sa/tasks/ike_config.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_config.c'; fi` + +ike_cert.o: sa/tasks/ike_cert.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_cert.o -MD -MP -MF "$(DEPDIR)/ike_cert.Tpo" -c -o ike_cert.o `test -f 'sa/tasks/ike_cert.c' || echo '$(srcdir)/'`sa/tasks/ike_cert.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_cert.Tpo" "$(DEPDIR)/ike_cert.Po"; else rm -f "$(DEPDIR)/ike_cert.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_cert.c' object='ike_cert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_cert.o `test -f 'sa/tasks/ike_cert.c' || echo '$(srcdir)/'`sa/tasks/ike_cert.c + +ike_cert.obj: sa/tasks/ike_cert.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_cert.obj -MD -MP -MF "$(DEPDIR)/ike_cert.Tpo" -c -o ike_cert.obj `if test -f 'sa/tasks/ike_cert.c'; then $(CYGPATH_W) 'sa/tasks/ike_cert.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_cert.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_cert.Tpo" "$(DEPDIR)/ike_cert.Po"; else rm -f "$(DEPDIR)/ike_cert.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_cert.c' object='ike_cert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_cert.obj `if test -f 'sa/tasks/ike_cert.c'; then $(CYGPATH_W) 'sa/tasks/ike_cert.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_cert.c'; fi` + +ike_rekey.o: sa/tasks/ike_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_rekey.o -MD -MP -MF "$(DEPDIR)/ike_rekey.Tpo" -c -o ike_rekey.o `test -f 'sa/tasks/ike_rekey.c' || echo '$(srcdir)/'`sa/tasks/ike_rekey.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_rekey.Tpo" "$(DEPDIR)/ike_rekey.Po"; else rm -f "$(DEPDIR)/ike_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_rekey.c' object='ike_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_rekey.o `test -f 'sa/tasks/ike_rekey.c' || echo '$(srcdir)/'`sa/tasks/ike_rekey.c + +ike_rekey.obj: sa/tasks/ike_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_rekey.obj -MD -MP -MF "$(DEPDIR)/ike_rekey.Tpo" -c -o ike_rekey.obj `if test -f 'sa/tasks/ike_rekey.c'; then $(CYGPATH_W) 'sa/tasks/ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_rekey.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_rekey.Tpo" "$(DEPDIR)/ike_rekey.Po"; else rm -f "$(DEPDIR)/ike_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_rekey.c' object='ike_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_rekey.obj `if test -f 'sa/tasks/ike_rekey.c'; then $(CYGPATH_W) 'sa/tasks/ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_rekey.c'; fi` + +ike_delete.o: sa/tasks/ike_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_delete.o -MD -MP -MF "$(DEPDIR)/ike_delete.Tpo" -c -o ike_delete.o `test -f 'sa/tasks/ike_delete.c' || echo '$(srcdir)/'`sa/tasks/ike_delete.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_delete.Tpo" "$(DEPDIR)/ike_delete.Po"; else rm -f "$(DEPDIR)/ike_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_delete.c' object='ike_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_delete.o `test -f 'sa/tasks/ike_delete.c' || echo '$(srcdir)/'`sa/tasks/ike_delete.c + +ike_delete.obj: sa/tasks/ike_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_delete.obj -MD -MP -MF "$(DEPDIR)/ike_delete.Tpo" -c -o ike_delete.obj `if test -f 'sa/tasks/ike_delete.c'; then $(CYGPATH_W) 'sa/tasks/ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_delete.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_delete.Tpo" "$(DEPDIR)/ike_delete.Po"; else rm -f "$(DEPDIR)/ike_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_delete.c' object='ike_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_delete.obj `if test -f 'sa/tasks/ike_delete.c'; then $(CYGPATH_W) 'sa/tasks/ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_delete.c'; fi` + +ike_dpd.o: sa/tasks/ike_dpd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_dpd.o -MD -MP -MF "$(DEPDIR)/ike_dpd.Tpo" -c -o ike_dpd.o `test -f 'sa/tasks/ike_dpd.c' || echo '$(srcdir)/'`sa/tasks/ike_dpd.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_dpd.Tpo" "$(DEPDIR)/ike_dpd.Po"; else rm -f "$(DEPDIR)/ike_dpd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_dpd.c' object='ike_dpd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_dpd.o `test -f 'sa/tasks/ike_dpd.c' || echo '$(srcdir)/'`sa/tasks/ike_dpd.c + +ike_dpd.obj: sa/tasks/ike_dpd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_dpd.obj -MD -MP -MF "$(DEPDIR)/ike_dpd.Tpo" -c -o ike_dpd.obj `if test -f 'sa/tasks/ike_dpd.c'; then $(CYGPATH_W) 'sa/tasks/ike_dpd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_dpd.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_dpd.Tpo" "$(DEPDIR)/ike_dpd.Po"; else rm -f "$(DEPDIR)/ike_dpd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_dpd.c' object='ike_dpd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_dpd.obj `if test -f 'sa/tasks/ike_dpd.c'; then $(CYGPATH_W) 'sa/tasks/ike_dpd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_dpd.c'; fi` + +child_create.o: sa/tasks/child_create.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_create.o -MD -MP -MF "$(DEPDIR)/child_create.Tpo" -c -o child_create.o `test -f 'sa/tasks/child_create.c' || echo '$(srcdir)/'`sa/tasks/child_create.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_create.Tpo" "$(DEPDIR)/child_create.Po"; else rm -f "$(DEPDIR)/child_create.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_create.c' object='child_create.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_create.o `test -f 'sa/tasks/child_create.c' || echo '$(srcdir)/'`sa/tasks/child_create.c + +child_create.obj: sa/tasks/child_create.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_create.obj -MD -MP -MF "$(DEPDIR)/child_create.Tpo" -c -o child_create.obj `if test -f 'sa/tasks/child_create.c'; then $(CYGPATH_W) 'sa/tasks/child_create.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_create.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_create.Tpo" "$(DEPDIR)/child_create.Po"; else rm -f "$(DEPDIR)/child_create.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_create.c' object='child_create.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_create.obj `if test -f 'sa/tasks/child_create.c'; then $(CYGPATH_W) 'sa/tasks/child_create.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_create.c'; fi` + +child_delete.o: sa/tasks/child_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_delete.o -MD -MP -MF "$(DEPDIR)/child_delete.Tpo" -c -o child_delete.o `test -f 'sa/tasks/child_delete.c' || echo '$(srcdir)/'`sa/tasks/child_delete.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_delete.Tpo" "$(DEPDIR)/child_delete.Po"; else rm -f "$(DEPDIR)/child_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_delete.c' object='child_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_delete.o `test -f 'sa/tasks/child_delete.c' || echo '$(srcdir)/'`sa/tasks/child_delete.c + +child_delete.obj: sa/tasks/child_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_delete.obj -MD -MP -MF "$(DEPDIR)/child_delete.Tpo" -c -o child_delete.obj `if test -f 'sa/tasks/child_delete.c'; then $(CYGPATH_W) 'sa/tasks/child_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_delete.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_delete.Tpo" "$(DEPDIR)/child_delete.Po"; else rm -f "$(DEPDIR)/child_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_delete.c' object='child_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_delete.obj `if test -f 'sa/tasks/child_delete.c'; then $(CYGPATH_W) 'sa/tasks/child_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_delete.c'; fi` + +child_rekey.o: sa/tasks/child_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_rekey.o -MD -MP -MF "$(DEPDIR)/child_rekey.Tpo" -c -o child_rekey.o `test -f 'sa/tasks/child_rekey.c' || echo '$(srcdir)/'`sa/tasks/child_rekey.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_rekey.Tpo" "$(DEPDIR)/child_rekey.Po"; else rm -f "$(DEPDIR)/child_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_rekey.c' object='child_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_rekey.o `test -f 'sa/tasks/child_rekey.c' || echo '$(srcdir)/'`sa/tasks/child_rekey.c + +child_rekey.obj: sa/tasks/child_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_rekey.obj -MD -MP -MF "$(DEPDIR)/child_rekey.Tpo" -c -o child_rekey.obj `if test -f 'sa/tasks/child_rekey.c'; then $(CYGPATH_W) 'sa/tasks/child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_rekey.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_rekey.Tpo" "$(DEPDIR)/child_rekey.Po"; else rm -f "$(DEPDIR)/child_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_rekey.c' object='child_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_rekey.obj `if test -f 'sa/tasks/child_rekey.c'; then $(CYGPATH_W) 'sa/tasks/child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_rekey.c'; fi` + +authenticator.o: sa/authenticators/authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT authenticator.o -MD -MP -MF "$(DEPDIR)/authenticator.Tpo" -c -o authenticator.o `test -f 'sa/authenticators/authenticator.c' || echo '$(srcdir)/'`sa/authenticators/authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/authenticator.Tpo" "$(DEPDIR)/authenticator.Po"; else rm -f "$(DEPDIR)/authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/authenticator.c' object='authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o authenticator.o `test -f 'sa/authenticators/authenticator.c' || echo '$(srcdir)/'`sa/authenticators/authenticator.c + +authenticator.obj: sa/authenticators/authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT authenticator.obj -MD -MP -MF "$(DEPDIR)/authenticator.Tpo" -c -o authenticator.obj `if test -f 'sa/authenticators/authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/authenticator.Tpo" "$(DEPDIR)/authenticator.Po"; else rm -f "$(DEPDIR)/authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/authenticator.c' object='authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o authenticator.obj `if test -f 'sa/authenticators/authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/authenticator.c'; fi` + +rsa_authenticator.o: sa/authenticators/rsa_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_authenticator.o -MD -MP -MF "$(DEPDIR)/rsa_authenticator.Tpo" -c -o rsa_authenticator.o `test -f 'sa/authenticators/rsa_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/rsa_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_authenticator.Tpo" "$(DEPDIR)/rsa_authenticator.Po"; else rm -f "$(DEPDIR)/rsa_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/rsa_authenticator.c' object='rsa_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_authenticator.o `test -f 'sa/authenticators/rsa_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/rsa_authenticator.c + +rsa_authenticator.obj: sa/authenticators/rsa_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_authenticator.obj -MD -MP -MF "$(DEPDIR)/rsa_authenticator.Tpo" -c -o rsa_authenticator.obj `if test -f 'sa/authenticators/rsa_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/rsa_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/rsa_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_authenticator.Tpo" "$(DEPDIR)/rsa_authenticator.Po"; else rm -f "$(DEPDIR)/rsa_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/rsa_authenticator.c' object='rsa_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_authenticator.obj `if test -f 'sa/authenticators/rsa_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/rsa_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/rsa_authenticator.c'; fi` + +psk_authenticator.o: sa/authenticators/psk_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT psk_authenticator.o -MD -MP -MF "$(DEPDIR)/psk_authenticator.Tpo" -c -o psk_authenticator.o `test -f 'sa/authenticators/psk_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/psk_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/psk_authenticator.Tpo" "$(DEPDIR)/psk_authenticator.Po"; else rm -f "$(DEPDIR)/psk_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/psk_authenticator.c' object='psk_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o psk_authenticator.o `test -f 'sa/authenticators/psk_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/psk_authenticator.c + +psk_authenticator.obj: sa/authenticators/psk_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT psk_authenticator.obj -MD -MP -MF "$(DEPDIR)/psk_authenticator.Tpo" -c -o psk_authenticator.obj `if test -f 'sa/authenticators/psk_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/psk_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/psk_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/psk_authenticator.Tpo" "$(DEPDIR)/psk_authenticator.Po"; else rm -f "$(DEPDIR)/psk_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/psk_authenticator.c' object='psk_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o psk_authenticator.obj `if test -f 'sa/authenticators/psk_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/psk_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/psk_authenticator.c'; fi` + +task_manager.o: sa/task_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task_manager.o -MD -MP -MF "$(DEPDIR)/task_manager.Tpo" -c -o task_manager.o `test -f 'sa/task_manager.c' || echo '$(srcdir)/'`sa/task_manager.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task_manager.Tpo" "$(DEPDIR)/task_manager.Po"; else rm -f "$(DEPDIR)/task_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/task_manager.c' object='task_manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task_manager.o `test -f 'sa/task_manager.c' || echo '$(srcdir)/'`sa/task_manager.c + +task_manager.obj: sa/task_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task_manager.obj -MD -MP -MF "$(DEPDIR)/task_manager.Tpo" -c -o task_manager.obj `if test -f 'sa/task_manager.c'; then $(CYGPATH_W) 'sa/task_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/task_manager.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task_manager.Tpo" "$(DEPDIR)/task_manager.Po"; else rm -f "$(DEPDIR)/task_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/task_manager.c' object='task_manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task_manager.obj `if test -f 'sa/task_manager.c'; then $(CYGPATH_W) 'sa/task_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/task_manager.c'; fi` + +encryption_payload.o: encoding/payloads/encryption_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encryption_payload.o -MD -MP -MF "$(DEPDIR)/encryption_payload.Tpo" -c -o encryption_payload.o `test -f 'encoding/payloads/encryption_payload.c' || echo '$(srcdir)/'`encoding/payloads/encryption_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encryption_payload.Tpo" "$(DEPDIR)/encryption_payload.Po"; else rm -f "$(DEPDIR)/encryption_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encryption_payload.c' object='encryption_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encryption_payload.o `test -f 'encoding/payloads/encryption_payload.c' || echo '$(srcdir)/'`encoding/payloads/encryption_payload.c + +encryption_payload.obj: encoding/payloads/encryption_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encryption_payload.obj -MD -MP -MF "$(DEPDIR)/encryption_payload.Tpo" -c -o encryption_payload.obj `if test -f 'encoding/payloads/encryption_payload.c'; then $(CYGPATH_W) 'encoding/payloads/encryption_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encryption_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encryption_payload.Tpo" "$(DEPDIR)/encryption_payload.Po"; else rm -f "$(DEPDIR)/encryption_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encryption_payload.c' object='encryption_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encryption_payload.obj `if test -f 'encoding/payloads/encryption_payload.c'; then $(CYGPATH_W) 'encoding/payloads/encryption_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encryption_payload.c'; fi` + +cert_payload.o: encoding/payloads/cert_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cert_payload.o -MD -MP -MF "$(DEPDIR)/cert_payload.Tpo" -c -o cert_payload.o `test -f 'encoding/payloads/cert_payload.c' || echo '$(srcdir)/'`encoding/payloads/cert_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cert_payload.Tpo" "$(DEPDIR)/cert_payload.Po"; else rm -f "$(DEPDIR)/cert_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cert_payload.c' object='cert_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cert_payload.o `test -f 'encoding/payloads/cert_payload.c' || echo '$(srcdir)/'`encoding/payloads/cert_payload.c + +cert_payload.obj: encoding/payloads/cert_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cert_payload.obj -MD -MP -MF "$(DEPDIR)/cert_payload.Tpo" -c -o cert_payload.obj `if test -f 'encoding/payloads/cert_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cert_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cert_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cert_payload.Tpo" "$(DEPDIR)/cert_payload.Po"; else rm -f "$(DEPDIR)/cert_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cert_payload.c' object='cert_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cert_payload.obj `if test -f 'encoding/payloads/cert_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cert_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cert_payload.c'; fi` + +traffic_selector_substructure.o: encoding/payloads/traffic_selector_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector_substructure.o -MD -MP -MF "$(DEPDIR)/traffic_selector_substructure.Tpo" -c -o traffic_selector_substructure.o `test -f 'encoding/payloads/traffic_selector_substructure.c' || echo '$(srcdir)/'`encoding/payloads/traffic_selector_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector_substructure.Tpo" "$(DEPDIR)/traffic_selector_substructure.Po"; else rm -f "$(DEPDIR)/traffic_selector_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/traffic_selector_substructure.c' object='traffic_selector_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector_substructure.o `test -f 'encoding/payloads/traffic_selector_substructure.c' || echo '$(srcdir)/'`encoding/payloads/traffic_selector_substructure.c + +traffic_selector_substructure.obj: encoding/payloads/traffic_selector_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector_substructure.obj -MD -MP -MF "$(DEPDIR)/traffic_selector_substructure.Tpo" -c -o traffic_selector_substructure.obj `if test -f 'encoding/payloads/traffic_selector_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/traffic_selector_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/traffic_selector_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector_substructure.Tpo" "$(DEPDIR)/traffic_selector_substructure.Po"; else rm -f "$(DEPDIR)/traffic_selector_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/traffic_selector_substructure.c' object='traffic_selector_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector_substructure.obj `if test -f 'encoding/payloads/traffic_selector_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/traffic_selector_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/traffic_selector_substructure.c'; fi` + +transform_attribute.o: encoding/payloads/transform_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_attribute.o -MD -MP -MF "$(DEPDIR)/transform_attribute.Tpo" -c -o transform_attribute.o `test -f 'encoding/payloads/transform_attribute.c' || echo '$(srcdir)/'`encoding/payloads/transform_attribute.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_attribute.Tpo" "$(DEPDIR)/transform_attribute.Po"; else rm -f "$(DEPDIR)/transform_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_attribute.c' object='transform_attribute.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_attribute.o `test -f 'encoding/payloads/transform_attribute.c' || echo '$(srcdir)/'`encoding/payloads/transform_attribute.c + +transform_attribute.obj: encoding/payloads/transform_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_attribute.obj -MD -MP -MF "$(DEPDIR)/transform_attribute.Tpo" -c -o transform_attribute.obj `if test -f 'encoding/payloads/transform_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/transform_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_attribute.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_attribute.Tpo" "$(DEPDIR)/transform_attribute.Po"; else rm -f "$(DEPDIR)/transform_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_attribute.c' object='transform_attribute.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_attribute.obj `if test -f 'encoding/payloads/transform_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/transform_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_attribute.c'; fi` + +configuration_attribute.o: encoding/payloads/configuration_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration_attribute.o -MD -MP -MF "$(DEPDIR)/configuration_attribute.Tpo" -c -o configuration_attribute.o `test -f 'encoding/payloads/configuration_attribute.c' || echo '$(srcdir)/'`encoding/payloads/configuration_attribute.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration_attribute.Tpo" "$(DEPDIR)/configuration_attribute.Po"; else rm -f "$(DEPDIR)/configuration_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/configuration_attribute.c' object='configuration_attribute.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration_attribute.o `test -f 'encoding/payloads/configuration_attribute.c' || echo '$(srcdir)/'`encoding/payloads/configuration_attribute.c + +configuration_attribute.obj: encoding/payloads/configuration_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration_attribute.obj -MD -MP -MF "$(DEPDIR)/configuration_attribute.Tpo" -c -o configuration_attribute.obj `if test -f 'encoding/payloads/configuration_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/configuration_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/configuration_attribute.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration_attribute.Tpo" "$(DEPDIR)/configuration_attribute.Po"; else rm -f "$(DEPDIR)/configuration_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/configuration_attribute.c' object='configuration_attribute.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration_attribute.obj `if test -f 'encoding/payloads/configuration_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/configuration_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/configuration_attribute.c'; fi` + +transform_substructure.o: encoding/payloads/transform_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_substructure.o -MD -MP -MF "$(DEPDIR)/transform_substructure.Tpo" -c -o transform_substructure.o `test -f 'encoding/payloads/transform_substructure.c' || echo '$(srcdir)/'`encoding/payloads/transform_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_substructure.Tpo" "$(DEPDIR)/transform_substructure.Po"; else rm -f "$(DEPDIR)/transform_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_substructure.c' object='transform_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_substructure.o `test -f 'encoding/payloads/transform_substructure.c' || echo '$(srcdir)/'`encoding/payloads/transform_substructure.c + +transform_substructure.obj: encoding/payloads/transform_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_substructure.obj -MD -MP -MF "$(DEPDIR)/transform_substructure.Tpo" -c -o transform_substructure.obj `if test -f 'encoding/payloads/transform_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/transform_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_substructure.Tpo" "$(DEPDIR)/transform_substructure.Po"; else rm -f "$(DEPDIR)/transform_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_substructure.c' object='transform_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_substructure.obj `if test -f 'encoding/payloads/transform_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/transform_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_substructure.c'; fi` + +auth_payload.o: encoding/payloads/auth_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth_payload.o -MD -MP -MF "$(DEPDIR)/auth_payload.Tpo" -c -o auth_payload.o `test -f 'encoding/payloads/auth_payload.c' || echo '$(srcdir)/'`encoding/payloads/auth_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/auth_payload.Tpo" "$(DEPDIR)/auth_payload.Po"; else rm -f "$(DEPDIR)/auth_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/auth_payload.c' object='auth_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth_payload.o `test -f 'encoding/payloads/auth_payload.c' || echo '$(srcdir)/'`encoding/payloads/auth_payload.c + +auth_payload.obj: encoding/payloads/auth_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth_payload.obj -MD -MP -MF "$(DEPDIR)/auth_payload.Tpo" -c -o auth_payload.obj `if test -f 'encoding/payloads/auth_payload.c'; then $(CYGPATH_W) 'encoding/payloads/auth_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/auth_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/auth_payload.Tpo" "$(DEPDIR)/auth_payload.Po"; else rm -f "$(DEPDIR)/auth_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/auth_payload.c' object='auth_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth_payload.obj `if test -f 'encoding/payloads/auth_payload.c'; then $(CYGPATH_W) 'encoding/payloads/auth_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/auth_payload.c'; fi` + +ike_header.o: encoding/payloads/ike_header.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_header.o -MD -MP -MF "$(DEPDIR)/ike_header.Tpo" -c -o ike_header.o `test -f 'encoding/payloads/ike_header.c' || echo '$(srcdir)/'`encoding/payloads/ike_header.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_header.Tpo" "$(DEPDIR)/ike_header.Po"; else rm -f "$(DEPDIR)/ike_header.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ike_header.c' object='ike_header.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_header.o `test -f 'encoding/payloads/ike_header.c' || echo '$(srcdir)/'`encoding/payloads/ike_header.c + +ike_header.obj: encoding/payloads/ike_header.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_header.obj -MD -MP -MF "$(DEPDIR)/ike_header.Tpo" -c -o ike_header.obj `if test -f 'encoding/payloads/ike_header.c'; then $(CYGPATH_W) 'encoding/payloads/ike_header.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ike_header.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_header.Tpo" "$(DEPDIR)/ike_header.Po"; else rm -f "$(DEPDIR)/ike_header.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ike_header.c' object='ike_header.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_header.obj `if test -f 'encoding/payloads/ike_header.c'; then $(CYGPATH_W) 'encoding/payloads/ike_header.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ike_header.c'; fi` + +nonce_payload.o: encoding/payloads/nonce_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nonce_payload.o -MD -MP -MF "$(DEPDIR)/nonce_payload.Tpo" -c -o nonce_payload.o `test -f 'encoding/payloads/nonce_payload.c' || echo '$(srcdir)/'`encoding/payloads/nonce_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/nonce_payload.Tpo" "$(DEPDIR)/nonce_payload.Po"; else rm -f "$(DEPDIR)/nonce_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/nonce_payload.c' object='nonce_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nonce_payload.o `test -f 'encoding/payloads/nonce_payload.c' || echo '$(srcdir)/'`encoding/payloads/nonce_payload.c + +nonce_payload.obj: encoding/payloads/nonce_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nonce_payload.obj -MD -MP -MF "$(DEPDIR)/nonce_payload.Tpo" -c -o nonce_payload.obj `if test -f 'encoding/payloads/nonce_payload.c'; then $(CYGPATH_W) 'encoding/payloads/nonce_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/nonce_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/nonce_payload.Tpo" "$(DEPDIR)/nonce_payload.Po"; else rm -f "$(DEPDIR)/nonce_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/nonce_payload.c' object='nonce_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nonce_payload.obj `if test -f 'encoding/payloads/nonce_payload.c'; then $(CYGPATH_W) 'encoding/payloads/nonce_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/nonce_payload.c'; fi` + +eap_payload.o: encoding/payloads/eap_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_payload.o -MD -MP -MF "$(DEPDIR)/eap_payload.Tpo" -c -o eap_payload.o `test -f 'encoding/payloads/eap_payload.c' || echo '$(srcdir)/'`encoding/payloads/eap_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_payload.Tpo" "$(DEPDIR)/eap_payload.Po"; else rm -f "$(DEPDIR)/eap_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/eap_payload.c' object='eap_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_payload.o `test -f 'encoding/payloads/eap_payload.c' || echo '$(srcdir)/'`encoding/payloads/eap_payload.c + +eap_payload.obj: encoding/payloads/eap_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_payload.obj -MD -MP -MF "$(DEPDIR)/eap_payload.Tpo" -c -o eap_payload.obj `if test -f 'encoding/payloads/eap_payload.c'; then $(CYGPATH_W) 'encoding/payloads/eap_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/eap_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_payload.Tpo" "$(DEPDIR)/eap_payload.Po"; else rm -f "$(DEPDIR)/eap_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/eap_payload.c' object='eap_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_payload.obj `if test -f 'encoding/payloads/eap_payload.c'; then $(CYGPATH_W) 'encoding/payloads/eap_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/eap_payload.c'; fi` + +ts_payload.o: encoding/payloads/ts_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ts_payload.o -MD -MP -MF "$(DEPDIR)/ts_payload.Tpo" -c -o ts_payload.o `test -f 'encoding/payloads/ts_payload.c' || echo '$(srcdir)/'`encoding/payloads/ts_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ts_payload.Tpo" "$(DEPDIR)/ts_payload.Po"; else rm -f "$(DEPDIR)/ts_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ts_payload.c' object='ts_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ts_payload.o `test -f 'encoding/payloads/ts_payload.c' || echo '$(srcdir)/'`encoding/payloads/ts_payload.c + +ts_payload.obj: encoding/payloads/ts_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ts_payload.obj -MD -MP -MF "$(DEPDIR)/ts_payload.Tpo" -c -o ts_payload.obj `if test -f 'encoding/payloads/ts_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ts_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ts_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ts_payload.Tpo" "$(DEPDIR)/ts_payload.Po"; else rm -f "$(DEPDIR)/ts_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ts_payload.c' object='ts_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ts_payload.obj `if test -f 'encoding/payloads/ts_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ts_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ts_payload.c'; fi` + +notify_payload.o: encoding/payloads/notify_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT notify_payload.o -MD -MP -MF "$(DEPDIR)/notify_payload.Tpo" -c -o notify_payload.o `test -f 'encoding/payloads/notify_payload.c' || echo '$(srcdir)/'`encoding/payloads/notify_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/notify_payload.Tpo" "$(DEPDIR)/notify_payload.Po"; else rm -f "$(DEPDIR)/notify_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/notify_payload.c' object='notify_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify_payload.o `test -f 'encoding/payloads/notify_payload.c' || echo '$(srcdir)/'`encoding/payloads/notify_payload.c + +notify_payload.obj: encoding/payloads/notify_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT notify_payload.obj -MD -MP -MF "$(DEPDIR)/notify_payload.Tpo" -c -o notify_payload.obj `if test -f 'encoding/payloads/notify_payload.c'; then $(CYGPATH_W) 'encoding/payloads/notify_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/notify_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/notify_payload.Tpo" "$(DEPDIR)/notify_payload.Po"; else rm -f "$(DEPDIR)/notify_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/notify_payload.c' object='notify_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify_payload.obj `if test -f 'encoding/payloads/notify_payload.c'; then $(CYGPATH_W) 'encoding/payloads/notify_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/notify_payload.c'; fi` + +id_payload.o: encoding/payloads/id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT id_payload.o -MD -MP -MF "$(DEPDIR)/id_payload.Tpo" -c -o id_payload.o `test -f 'encoding/payloads/id_payload.c' || echo '$(srcdir)/'`encoding/payloads/id_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/id_payload.Tpo" "$(DEPDIR)/id_payload.Po"; else rm -f "$(DEPDIR)/id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/id_payload.c' object='id_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o id_payload.o `test -f 'encoding/payloads/id_payload.c' || echo '$(srcdir)/'`encoding/payloads/id_payload.c + +id_payload.obj: encoding/payloads/id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT id_payload.obj -MD -MP -MF "$(DEPDIR)/id_payload.Tpo" -c -o id_payload.obj `if test -f 'encoding/payloads/id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/id_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/id_payload.Tpo" "$(DEPDIR)/id_payload.Po"; else rm -f "$(DEPDIR)/id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/id_payload.c' object='id_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o id_payload.obj `if test -f 'encoding/payloads/id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/id_payload.c'; fi` + +ke_payload.o: encoding/payloads/ke_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ke_payload.o -MD -MP -MF "$(DEPDIR)/ke_payload.Tpo" -c -o ke_payload.o `test -f 'encoding/payloads/ke_payload.c' || echo '$(srcdir)/'`encoding/payloads/ke_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ke_payload.Tpo" "$(DEPDIR)/ke_payload.Po"; else rm -f "$(DEPDIR)/ke_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ke_payload.c' object='ke_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ke_payload.o `test -f 'encoding/payloads/ke_payload.c' || echo '$(srcdir)/'`encoding/payloads/ke_payload.c + +ke_payload.obj: encoding/payloads/ke_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ke_payload.obj -MD -MP -MF "$(DEPDIR)/ke_payload.Tpo" -c -o ke_payload.obj `if test -f 'encoding/payloads/ke_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ke_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ke_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ke_payload.Tpo" "$(DEPDIR)/ke_payload.Po"; else rm -f "$(DEPDIR)/ke_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ke_payload.c' object='ke_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ke_payload.obj `if test -f 'encoding/payloads/ke_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ke_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ke_payload.c'; fi` + +unknown_payload.o: encoding/payloads/unknown_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unknown_payload.o -MD -MP -MF "$(DEPDIR)/unknown_payload.Tpo" -c -o unknown_payload.o `test -f 'encoding/payloads/unknown_payload.c' || echo '$(srcdir)/'`encoding/payloads/unknown_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/unknown_payload.Tpo" "$(DEPDIR)/unknown_payload.Po"; else rm -f "$(DEPDIR)/unknown_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/unknown_payload.c' object='unknown_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unknown_payload.o `test -f 'encoding/payloads/unknown_payload.c' || echo '$(srcdir)/'`encoding/payloads/unknown_payload.c + +unknown_payload.obj: encoding/payloads/unknown_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unknown_payload.obj -MD -MP -MF "$(DEPDIR)/unknown_payload.Tpo" -c -o unknown_payload.obj `if test -f 'encoding/payloads/unknown_payload.c'; then $(CYGPATH_W) 'encoding/payloads/unknown_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/unknown_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/unknown_payload.Tpo" "$(DEPDIR)/unknown_payload.Po"; else rm -f "$(DEPDIR)/unknown_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/unknown_payload.c' object='unknown_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unknown_payload.obj `if test -f 'encoding/payloads/unknown_payload.c'; then $(CYGPATH_W) 'encoding/payloads/unknown_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/unknown_payload.c'; fi` + +encodings.o: encoding/payloads/encodings.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encodings.o -MD -MP -MF "$(DEPDIR)/encodings.Tpo" -c -o encodings.o `test -f 'encoding/payloads/encodings.c' || echo '$(srcdir)/'`encoding/payloads/encodings.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encodings.Tpo" "$(DEPDIR)/encodings.Po"; else rm -f "$(DEPDIR)/encodings.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encodings.c' object='encodings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encodings.o `test -f 'encoding/payloads/encodings.c' || echo '$(srcdir)/'`encoding/payloads/encodings.c + +encodings.obj: encoding/payloads/encodings.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encodings.obj -MD -MP -MF "$(DEPDIR)/encodings.Tpo" -c -o encodings.obj `if test -f 'encoding/payloads/encodings.c'; then $(CYGPATH_W) 'encoding/payloads/encodings.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encodings.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encodings.Tpo" "$(DEPDIR)/encodings.Po"; else rm -f "$(DEPDIR)/encodings.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encodings.c' object='encodings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encodings.obj `if test -f 'encoding/payloads/encodings.c'; then $(CYGPATH_W) 'encoding/payloads/encodings.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encodings.c'; fi` + +cp_payload.o: encoding/payloads/cp_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cp_payload.o -MD -MP -MF "$(DEPDIR)/cp_payload.Tpo" -c -o cp_payload.o `test -f 'encoding/payloads/cp_payload.c' || echo '$(srcdir)/'`encoding/payloads/cp_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cp_payload.Tpo" "$(DEPDIR)/cp_payload.Po"; else rm -f "$(DEPDIR)/cp_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cp_payload.c' object='cp_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cp_payload.o `test -f 'encoding/payloads/cp_payload.c' || echo '$(srcdir)/'`encoding/payloads/cp_payload.c + +cp_payload.obj: encoding/payloads/cp_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cp_payload.obj -MD -MP -MF "$(DEPDIR)/cp_payload.Tpo" -c -o cp_payload.obj `if test -f 'encoding/payloads/cp_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cp_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cp_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cp_payload.Tpo" "$(DEPDIR)/cp_payload.Po"; else rm -f "$(DEPDIR)/cp_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cp_payload.c' object='cp_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cp_payload.obj `if test -f 'encoding/payloads/cp_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cp_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cp_payload.c'; fi` + +delete_payload.o: encoding/payloads/delete_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_payload.o -MD -MP -MF "$(DEPDIR)/delete_payload.Tpo" -c -o delete_payload.o `test -f 'encoding/payloads/delete_payload.c' || echo '$(srcdir)/'`encoding/payloads/delete_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_payload.Tpo" "$(DEPDIR)/delete_payload.Po"; else rm -f "$(DEPDIR)/delete_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/delete_payload.c' object='delete_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_payload.o `test -f 'encoding/payloads/delete_payload.c' || echo '$(srcdir)/'`encoding/payloads/delete_payload.c + +delete_payload.obj: encoding/payloads/delete_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_payload.obj -MD -MP -MF "$(DEPDIR)/delete_payload.Tpo" -c -o delete_payload.obj `if test -f 'encoding/payloads/delete_payload.c'; then $(CYGPATH_W) 'encoding/payloads/delete_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/delete_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_payload.Tpo" "$(DEPDIR)/delete_payload.Po"; else rm -f "$(DEPDIR)/delete_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/delete_payload.c' object='delete_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_payload.obj `if test -f 'encoding/payloads/delete_payload.c'; then $(CYGPATH_W) 'encoding/payloads/delete_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/delete_payload.c'; fi` + +sa_payload.o: encoding/payloads/sa_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sa_payload.o -MD -MP -MF "$(DEPDIR)/sa_payload.Tpo" -c -o sa_payload.o `test -f 'encoding/payloads/sa_payload.c' || echo '$(srcdir)/'`encoding/payloads/sa_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sa_payload.Tpo" "$(DEPDIR)/sa_payload.Po"; else rm -f "$(DEPDIR)/sa_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/sa_payload.c' object='sa_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sa_payload.o `test -f 'encoding/payloads/sa_payload.c' || echo '$(srcdir)/'`encoding/payloads/sa_payload.c + +sa_payload.obj: encoding/payloads/sa_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sa_payload.obj -MD -MP -MF "$(DEPDIR)/sa_payload.Tpo" -c -o sa_payload.obj `if test -f 'encoding/payloads/sa_payload.c'; then $(CYGPATH_W) 'encoding/payloads/sa_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/sa_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sa_payload.Tpo" "$(DEPDIR)/sa_payload.Po"; else rm -f "$(DEPDIR)/sa_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/sa_payload.c' object='sa_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sa_payload.obj `if test -f 'encoding/payloads/sa_payload.c'; then $(CYGPATH_W) 'encoding/payloads/sa_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/sa_payload.c'; fi` + +certreq_payload.o: encoding/payloads/certreq_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT certreq_payload.o -MD -MP -MF "$(DEPDIR)/certreq_payload.Tpo" -c -o certreq_payload.o `test -f 'encoding/payloads/certreq_payload.c' || echo '$(srcdir)/'`encoding/payloads/certreq_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/certreq_payload.Tpo" "$(DEPDIR)/certreq_payload.Po"; else rm -f "$(DEPDIR)/certreq_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/certreq_payload.c' object='certreq_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o certreq_payload.o `test -f 'encoding/payloads/certreq_payload.c' || echo '$(srcdir)/'`encoding/payloads/certreq_payload.c + +certreq_payload.obj: encoding/payloads/certreq_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT certreq_payload.obj -MD -MP -MF "$(DEPDIR)/certreq_payload.Tpo" -c -o certreq_payload.obj `if test -f 'encoding/payloads/certreq_payload.c'; then $(CYGPATH_W) 'encoding/payloads/certreq_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/certreq_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/certreq_payload.Tpo" "$(DEPDIR)/certreq_payload.Po"; else rm -f "$(DEPDIR)/certreq_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/certreq_payload.c' object='certreq_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o certreq_payload.obj `if test -f 'encoding/payloads/certreq_payload.c'; then $(CYGPATH_W) 'encoding/payloads/certreq_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/certreq_payload.c'; fi` + +vendor_id_payload.o: encoding/payloads/vendor_id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vendor_id_payload.o -MD -MP -MF "$(DEPDIR)/vendor_id_payload.Tpo" -c -o vendor_id_payload.o `test -f 'encoding/payloads/vendor_id_payload.c' || echo '$(srcdir)/'`encoding/payloads/vendor_id_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/vendor_id_payload.Tpo" "$(DEPDIR)/vendor_id_payload.Po"; else rm -f "$(DEPDIR)/vendor_id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/vendor_id_payload.c' object='vendor_id_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vendor_id_payload.o `test -f 'encoding/payloads/vendor_id_payload.c' || echo '$(srcdir)/'`encoding/payloads/vendor_id_payload.c + +vendor_id_payload.obj: encoding/payloads/vendor_id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vendor_id_payload.obj -MD -MP -MF "$(DEPDIR)/vendor_id_payload.Tpo" -c -o vendor_id_payload.obj `if test -f 'encoding/payloads/vendor_id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/vendor_id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/vendor_id_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/vendor_id_payload.Tpo" "$(DEPDIR)/vendor_id_payload.Po"; else rm -f "$(DEPDIR)/vendor_id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/vendor_id_payload.c' object='vendor_id_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vendor_id_payload.obj `if test -f 'encoding/payloads/vendor_id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/vendor_id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/vendor_id_payload.c'; fi` + +proposal_substructure.o: encoding/payloads/proposal_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal_substructure.o -MD -MP -MF "$(DEPDIR)/proposal_substructure.Tpo" -c -o proposal_substructure.o `test -f 'encoding/payloads/proposal_substructure.c' || echo '$(srcdir)/'`encoding/payloads/proposal_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal_substructure.Tpo" "$(DEPDIR)/proposal_substructure.Po"; else rm -f "$(DEPDIR)/proposal_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/proposal_substructure.c' object='proposal_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal_substructure.o `test -f 'encoding/payloads/proposal_substructure.c' || echo '$(srcdir)/'`encoding/payloads/proposal_substructure.c + +proposal_substructure.obj: encoding/payloads/proposal_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal_substructure.obj -MD -MP -MF "$(DEPDIR)/proposal_substructure.Tpo" -c -o proposal_substructure.obj `if test -f 'encoding/payloads/proposal_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/proposal_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/proposal_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal_substructure.Tpo" "$(DEPDIR)/proposal_substructure.Po"; else rm -f "$(DEPDIR)/proposal_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/proposal_substructure.c' object='proposal_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal_substructure.obj `if test -f 'encoding/payloads/proposal_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/proposal_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/proposal_substructure.c'; fi` + +payload.o: encoding/payloads/payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT payload.o -MD -MP -MF "$(DEPDIR)/payload.Tpo" -c -o payload.o `test -f 'encoding/payloads/payload.c' || echo '$(srcdir)/'`encoding/payloads/payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/payload.Tpo" "$(DEPDIR)/payload.Po"; else rm -f "$(DEPDIR)/payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/payload.c' object='payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o payload.o `test -f 'encoding/payloads/payload.c' || echo '$(srcdir)/'`encoding/payloads/payload.c + +payload.obj: encoding/payloads/payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT payload.obj -MD -MP -MF "$(DEPDIR)/payload.Tpo" -c -o payload.obj `if test -f 'encoding/payloads/payload.c'; then $(CYGPATH_W) 'encoding/payloads/payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/payload.Tpo" "$(DEPDIR)/payload.Po"; else rm -f "$(DEPDIR)/payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/payload.c' object='payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o payload.obj `if test -f 'encoding/payloads/payload.c'; then $(CYGPATH_W) 'encoding/payloads/payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/payload.c'; fi` + +message.o: encoding/message.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT message.o -MD -MP -MF "$(DEPDIR)/message.Tpo" -c -o message.o `test -f 'encoding/message.c' || echo '$(srcdir)/'`encoding/message.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/message.Tpo" "$(DEPDIR)/message.Po"; else rm -f "$(DEPDIR)/message.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/message.c' object='message.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o message.o `test -f 'encoding/message.c' || echo '$(srcdir)/'`encoding/message.c + +message.obj: encoding/message.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT message.obj -MD -MP -MF "$(DEPDIR)/message.Tpo" -c -o message.obj `if test -f 'encoding/message.c'; then $(CYGPATH_W) 'encoding/message.c'; else $(CYGPATH_W) '$(srcdir)/encoding/message.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/message.Tpo" "$(DEPDIR)/message.Po"; else rm -f "$(DEPDIR)/message.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/message.c' object='message.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o message.obj `if test -f 'encoding/message.c'; then $(CYGPATH_W) 'encoding/message.c'; else $(CYGPATH_W) '$(srcdir)/encoding/message.c'; fi` + +generator.o: encoding/generator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT generator.o -MD -MP -MF "$(DEPDIR)/generator.Tpo" -c -o generator.o `test -f 'encoding/generator.c' || echo '$(srcdir)/'`encoding/generator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/generator.Tpo" "$(DEPDIR)/generator.Po"; else rm -f "$(DEPDIR)/generator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/generator.c' object='generator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o generator.o `test -f 'encoding/generator.c' || echo '$(srcdir)/'`encoding/generator.c + +generator.obj: encoding/generator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT generator.obj -MD -MP -MF "$(DEPDIR)/generator.Tpo" -c -o generator.obj `if test -f 'encoding/generator.c'; then $(CYGPATH_W) 'encoding/generator.c'; else $(CYGPATH_W) '$(srcdir)/encoding/generator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/generator.Tpo" "$(DEPDIR)/generator.Po"; else rm -f "$(DEPDIR)/generator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/generator.c' object='generator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o generator.obj `if test -f 'encoding/generator.c'; then $(CYGPATH_W) 'encoding/generator.c'; else $(CYGPATH_W) '$(srcdir)/encoding/generator.c'; fi` + +parser.o: encoding/parser.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser.o -MD -MP -MF "$(DEPDIR)/parser.Tpo" -c -o parser.o `test -f 'encoding/parser.c' || echo '$(srcdir)/'`encoding/parser.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/parser.Tpo" "$(DEPDIR)/parser.Po"; else rm -f "$(DEPDIR)/parser.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/parser.c' object='parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser.o `test -f 'encoding/parser.c' || echo '$(srcdir)/'`encoding/parser.c + +parser.obj: encoding/parser.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser.obj -MD -MP -MF "$(DEPDIR)/parser.Tpo" -c -o parser.obj `if test -f 'encoding/parser.c'; then $(CYGPATH_W) 'encoding/parser.c'; else $(CYGPATH_W) '$(srcdir)/encoding/parser.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/parser.Tpo" "$(DEPDIR)/parser.Po"; else rm -f "$(DEPDIR)/parser.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/parser.c' object='parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser.obj `if test -f 'encoding/parser.c'; then $(CYGPATH_W) 'encoding/parser.c'; else $(CYGPATH_W) '$(srcdir)/encoding/parser.c'; fi` + +packet.o: network/packet.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet.o -MD -MP -MF "$(DEPDIR)/packet.Tpo" -c -o packet.o `test -f 'network/packet.c' || echo '$(srcdir)/'`network/packet.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/packet.Tpo" "$(DEPDIR)/packet.Po"; else rm -f "$(DEPDIR)/packet.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/packet.c' object='packet.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet.o `test -f 'network/packet.c' || echo '$(srcdir)/'`network/packet.c + +packet.obj: network/packet.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet.obj -MD -MP -MF "$(DEPDIR)/packet.Tpo" -c -o packet.obj `if test -f 'network/packet.c'; then $(CYGPATH_W) 'network/packet.c'; else $(CYGPATH_W) '$(srcdir)/network/packet.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/packet.Tpo" "$(DEPDIR)/packet.Po"; else rm -f "$(DEPDIR)/packet.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/packet.c' object='packet.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet.obj `if test -f 'network/packet.c'; then $(CYGPATH_W) 'network/packet.c'; else $(CYGPATH_W) '$(srcdir)/network/packet.c'; fi` + +socket.o: network/socket.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket.o -MD -MP -MF "$(DEPDIR)/socket.Tpo" -c -o socket.o `test -f 'network/socket.c' || echo '$(srcdir)/'`network/socket.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/socket.Tpo" "$(DEPDIR)/socket.Po"; else rm -f "$(DEPDIR)/socket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/socket.c' object='socket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket.o `test -f 'network/socket.c' || echo '$(srcdir)/'`network/socket.c + +socket.obj: network/socket.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket.obj -MD -MP -MF "$(DEPDIR)/socket.Tpo" -c -o socket.obj `if test -f 'network/socket.c'; then $(CYGPATH_W) 'network/socket.c'; else $(CYGPATH_W) '$(srcdir)/network/socket.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/socket.Tpo" "$(DEPDIR)/socket.Po"; else rm -f "$(DEPDIR)/socket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/socket.c' object='socket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket.obj `if test -f 'network/socket.c'; then $(CYGPATH_W) 'network/socket.c'; else $(CYGPATH_W) '$(srcdir)/network/socket.c'; fi` + +job.o: queues/jobs/job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job.o -MD -MP -MF "$(DEPDIR)/job.Tpo" -c -o job.o `test -f 'queues/jobs/job.c' || echo '$(srcdir)/'`queues/jobs/job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job.Tpo" "$(DEPDIR)/job.Po"; else rm -f "$(DEPDIR)/job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/job.c' object='job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job.o `test -f 'queues/jobs/job.c' || echo '$(srcdir)/'`queues/jobs/job.c + +job.obj: queues/jobs/job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job.obj -MD -MP -MF "$(DEPDIR)/job.Tpo" -c -o job.obj `if test -f 'queues/jobs/job.c'; then $(CYGPATH_W) 'queues/jobs/job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job.Tpo" "$(DEPDIR)/job.Po"; else rm -f "$(DEPDIR)/job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/job.c' object='job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job.obj `if test -f 'queues/jobs/job.c'; then $(CYGPATH_W) 'queues/jobs/job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/job.c'; fi` + +process_message_job.o: queues/jobs/process_message_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT process_message_job.o -MD -MP -MF "$(DEPDIR)/process_message_job.Tpo" -c -o process_message_job.o `test -f 'queues/jobs/process_message_job.c' || echo '$(srcdir)/'`queues/jobs/process_message_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/process_message_job.Tpo" "$(DEPDIR)/process_message_job.Po"; else rm -f "$(DEPDIR)/process_message_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/process_message_job.c' object='process_message_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o process_message_job.o `test -f 'queues/jobs/process_message_job.c' || echo '$(srcdir)/'`queues/jobs/process_message_job.c + +process_message_job.obj: queues/jobs/process_message_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT process_message_job.obj -MD -MP -MF "$(DEPDIR)/process_message_job.Tpo" -c -o process_message_job.obj `if test -f 'queues/jobs/process_message_job.c'; then $(CYGPATH_W) 'queues/jobs/process_message_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/process_message_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/process_message_job.Tpo" "$(DEPDIR)/process_message_job.Po"; else rm -f "$(DEPDIR)/process_message_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/process_message_job.c' object='process_message_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o process_message_job.obj `if test -f 'queues/jobs/process_message_job.c'; then $(CYGPATH_W) 'queues/jobs/process_message_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/process_message_job.c'; fi` + +delete_ike_sa_job.o: queues/jobs/delete_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_ike_sa_job.o -MD -MP -MF "$(DEPDIR)/delete_ike_sa_job.Tpo" -c -o delete_ike_sa_job.o `test -f 'queues/jobs/delete_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_ike_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_ike_sa_job.Tpo" "$(DEPDIR)/delete_ike_sa_job.Po"; else rm -f "$(DEPDIR)/delete_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_ike_sa_job.c' object='delete_ike_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_ike_sa_job.o `test -f 'queues/jobs/delete_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_ike_sa_job.c + +delete_ike_sa_job.obj: queues/jobs/delete_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_ike_sa_job.obj -MD -MP -MF "$(DEPDIR)/delete_ike_sa_job.Tpo" -c -o delete_ike_sa_job.obj `if test -f 'queues/jobs/delete_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_ike_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_ike_sa_job.Tpo" "$(DEPDIR)/delete_ike_sa_job.Po"; else rm -f "$(DEPDIR)/delete_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_ike_sa_job.c' object='delete_ike_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_ike_sa_job.obj `if test -f 'queues/jobs/delete_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_ike_sa_job.c'; fi` + +retransmit_job.o: queues/jobs/retransmit_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT retransmit_job.o -MD -MP -MF "$(DEPDIR)/retransmit_job.Tpo" -c -o retransmit_job.o `test -f 'queues/jobs/retransmit_job.c' || echo '$(srcdir)/'`queues/jobs/retransmit_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/retransmit_job.Tpo" "$(DEPDIR)/retransmit_job.Po"; else rm -f "$(DEPDIR)/retransmit_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/retransmit_job.c' object='retransmit_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o retransmit_job.o `test -f 'queues/jobs/retransmit_job.c' || echo '$(srcdir)/'`queues/jobs/retransmit_job.c + +retransmit_job.obj: queues/jobs/retransmit_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT retransmit_job.obj -MD -MP -MF "$(DEPDIR)/retransmit_job.Tpo" -c -o retransmit_job.obj `if test -f 'queues/jobs/retransmit_job.c'; then $(CYGPATH_W) 'queues/jobs/retransmit_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/retransmit_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/retransmit_job.Tpo" "$(DEPDIR)/retransmit_job.Po"; else rm -f "$(DEPDIR)/retransmit_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/retransmit_job.c' object='retransmit_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o retransmit_job.obj `if test -f 'queues/jobs/retransmit_job.c'; then $(CYGPATH_W) 'queues/jobs/retransmit_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/retransmit_job.c'; fi` + +initiate_job.o: queues/jobs/initiate_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initiate_job.o -MD -MP -MF "$(DEPDIR)/initiate_job.Tpo" -c -o initiate_job.o `test -f 'queues/jobs/initiate_job.c' || echo '$(srcdir)/'`queues/jobs/initiate_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/initiate_job.Tpo" "$(DEPDIR)/initiate_job.Po"; else rm -f "$(DEPDIR)/initiate_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/initiate_job.c' object='initiate_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initiate_job.o `test -f 'queues/jobs/initiate_job.c' || echo '$(srcdir)/'`queues/jobs/initiate_job.c + +initiate_job.obj: queues/jobs/initiate_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initiate_job.obj -MD -MP -MF "$(DEPDIR)/initiate_job.Tpo" -c -o initiate_job.obj `if test -f 'queues/jobs/initiate_job.c'; then $(CYGPATH_W) 'queues/jobs/initiate_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/initiate_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/initiate_job.Tpo" "$(DEPDIR)/initiate_job.Po"; else rm -f "$(DEPDIR)/initiate_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/initiate_job.c' object='initiate_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initiate_job.obj `if test -f 'queues/jobs/initiate_job.c'; then $(CYGPATH_W) 'queues/jobs/initiate_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/initiate_job.c'; fi` + +send_keepalive_job.o: queues/jobs/send_keepalive_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_keepalive_job.o -MD -MP -MF "$(DEPDIR)/send_keepalive_job.Tpo" -c -o send_keepalive_job.o `test -f 'queues/jobs/send_keepalive_job.c' || echo '$(srcdir)/'`queues/jobs/send_keepalive_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_keepalive_job.Tpo" "$(DEPDIR)/send_keepalive_job.Po"; else rm -f "$(DEPDIR)/send_keepalive_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_keepalive_job.c' object='send_keepalive_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_keepalive_job.o `test -f 'queues/jobs/send_keepalive_job.c' || echo '$(srcdir)/'`queues/jobs/send_keepalive_job.c + +send_keepalive_job.obj: queues/jobs/send_keepalive_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_keepalive_job.obj -MD -MP -MF "$(DEPDIR)/send_keepalive_job.Tpo" -c -o send_keepalive_job.obj `if test -f 'queues/jobs/send_keepalive_job.c'; then $(CYGPATH_W) 'queues/jobs/send_keepalive_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_keepalive_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_keepalive_job.Tpo" "$(DEPDIR)/send_keepalive_job.Po"; else rm -f "$(DEPDIR)/send_keepalive_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_keepalive_job.c' object='send_keepalive_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_keepalive_job.obj `if test -f 'queues/jobs/send_keepalive_job.c'; then $(CYGPATH_W) 'queues/jobs/send_keepalive_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_keepalive_job.c'; fi` + +rekey_child_sa_job.o: queues/jobs/rekey_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_child_sa_job.o -MD -MP -MF "$(DEPDIR)/rekey_child_sa_job.Tpo" -c -o rekey_child_sa_job.o `test -f 'queues/jobs/rekey_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_child_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_child_sa_job.Tpo" "$(DEPDIR)/rekey_child_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_child_sa_job.c' object='rekey_child_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_child_sa_job.o `test -f 'queues/jobs/rekey_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_child_sa_job.c + +rekey_child_sa_job.obj: queues/jobs/rekey_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_child_sa_job.obj -MD -MP -MF "$(DEPDIR)/rekey_child_sa_job.Tpo" -c -o rekey_child_sa_job.obj `if test -f 'queues/jobs/rekey_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_child_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_child_sa_job.Tpo" "$(DEPDIR)/rekey_child_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_child_sa_job.c' object='rekey_child_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_child_sa_job.obj `if test -f 'queues/jobs/rekey_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_child_sa_job.c'; fi` + +delete_child_sa_job.o: queues/jobs/delete_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_child_sa_job.o -MD -MP -MF "$(DEPDIR)/delete_child_sa_job.Tpo" -c -o delete_child_sa_job.o `test -f 'queues/jobs/delete_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_child_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_child_sa_job.Tpo" "$(DEPDIR)/delete_child_sa_job.Po"; else rm -f "$(DEPDIR)/delete_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_child_sa_job.c' object='delete_child_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_child_sa_job.o `test -f 'queues/jobs/delete_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_child_sa_job.c + +delete_child_sa_job.obj: queues/jobs/delete_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_child_sa_job.obj -MD -MP -MF "$(DEPDIR)/delete_child_sa_job.Tpo" -c -o delete_child_sa_job.obj `if test -f 'queues/jobs/delete_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_child_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_child_sa_job.Tpo" "$(DEPDIR)/delete_child_sa_job.Po"; else rm -f "$(DEPDIR)/delete_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_child_sa_job.c' object='delete_child_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_child_sa_job.obj `if test -f 'queues/jobs/delete_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_child_sa_job.c'; fi` + +send_dpd_job.o: queues/jobs/send_dpd_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_dpd_job.o -MD -MP -MF "$(DEPDIR)/send_dpd_job.Tpo" -c -o send_dpd_job.o `test -f 'queues/jobs/send_dpd_job.c' || echo '$(srcdir)/'`queues/jobs/send_dpd_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_dpd_job.Tpo" "$(DEPDIR)/send_dpd_job.Po"; else rm -f "$(DEPDIR)/send_dpd_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_dpd_job.c' object='send_dpd_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_dpd_job.o `test -f 'queues/jobs/send_dpd_job.c' || echo '$(srcdir)/'`queues/jobs/send_dpd_job.c + +send_dpd_job.obj: queues/jobs/send_dpd_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_dpd_job.obj -MD -MP -MF "$(DEPDIR)/send_dpd_job.Tpo" -c -o send_dpd_job.obj `if test -f 'queues/jobs/send_dpd_job.c'; then $(CYGPATH_W) 'queues/jobs/send_dpd_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_dpd_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_dpd_job.Tpo" "$(DEPDIR)/send_dpd_job.Po"; else rm -f "$(DEPDIR)/send_dpd_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_dpd_job.c' object='send_dpd_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_dpd_job.obj `if test -f 'queues/jobs/send_dpd_job.c'; then $(CYGPATH_W) 'queues/jobs/send_dpd_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_dpd_job.c'; fi` + +route_job.o: queues/jobs/route_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT route_job.o -MD -MP -MF "$(DEPDIR)/route_job.Tpo" -c -o route_job.o `test -f 'queues/jobs/route_job.c' || echo '$(srcdir)/'`queues/jobs/route_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/route_job.Tpo" "$(DEPDIR)/route_job.Po"; else rm -f "$(DEPDIR)/route_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/route_job.c' object='route_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o route_job.o `test -f 'queues/jobs/route_job.c' || echo '$(srcdir)/'`queues/jobs/route_job.c + +route_job.obj: queues/jobs/route_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT route_job.obj -MD -MP -MF "$(DEPDIR)/route_job.Tpo" -c -o route_job.obj `if test -f 'queues/jobs/route_job.c'; then $(CYGPATH_W) 'queues/jobs/route_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/route_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/route_job.Tpo" "$(DEPDIR)/route_job.Po"; else rm -f "$(DEPDIR)/route_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/route_job.c' object='route_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o route_job.obj `if test -f 'queues/jobs/route_job.c'; then $(CYGPATH_W) 'queues/jobs/route_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/route_job.c'; fi` + +acquire_job.o: queues/jobs/acquire_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acquire_job.o -MD -MP -MF "$(DEPDIR)/acquire_job.Tpo" -c -o acquire_job.o `test -f 'queues/jobs/acquire_job.c' || echo '$(srcdir)/'`queues/jobs/acquire_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/acquire_job.Tpo" "$(DEPDIR)/acquire_job.Po"; else rm -f "$(DEPDIR)/acquire_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/acquire_job.c' object='acquire_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acquire_job.o `test -f 'queues/jobs/acquire_job.c' || echo '$(srcdir)/'`queues/jobs/acquire_job.c + +acquire_job.obj: queues/jobs/acquire_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acquire_job.obj -MD -MP -MF "$(DEPDIR)/acquire_job.Tpo" -c -o acquire_job.obj `if test -f 'queues/jobs/acquire_job.c'; then $(CYGPATH_W) 'queues/jobs/acquire_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/acquire_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/acquire_job.Tpo" "$(DEPDIR)/acquire_job.Po"; else rm -f "$(DEPDIR)/acquire_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/acquire_job.c' object='acquire_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acquire_job.obj `if test -f 'queues/jobs/acquire_job.c'; then $(CYGPATH_W) 'queues/jobs/acquire_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/acquire_job.c'; fi` + +rekey_ike_sa_job.o: queues/jobs/rekey_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_ike_sa_job.o -MD -MP -MF "$(DEPDIR)/rekey_ike_sa_job.Tpo" -c -o rekey_ike_sa_job.o `test -f 'queues/jobs/rekey_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_ike_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_ike_sa_job.Tpo" "$(DEPDIR)/rekey_ike_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_ike_sa_job.c' object='rekey_ike_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_ike_sa_job.o `test -f 'queues/jobs/rekey_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_ike_sa_job.c + +rekey_ike_sa_job.obj: queues/jobs/rekey_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_ike_sa_job.obj -MD -MP -MF "$(DEPDIR)/rekey_ike_sa_job.Tpo" -c -o rekey_ike_sa_job.obj `if test -f 'queues/jobs/rekey_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_ike_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_ike_sa_job.Tpo" "$(DEPDIR)/rekey_ike_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_ike_sa_job.c' object='rekey_ike_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_ike_sa_job.obj `if test -f 'queues/jobs/rekey_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_ike_sa_job.c'; fi` + +job_queue.o: queues/job_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job_queue.o -MD -MP -MF "$(DEPDIR)/job_queue.Tpo" -c -o job_queue.o `test -f 'queues/job_queue.c' || echo '$(srcdir)/'`queues/job_queue.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job_queue.Tpo" "$(DEPDIR)/job_queue.Po"; else rm -f "$(DEPDIR)/job_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/job_queue.c' object='job_queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job_queue.o `test -f 'queues/job_queue.c' || echo '$(srcdir)/'`queues/job_queue.c + +job_queue.obj: queues/job_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job_queue.obj -MD -MP -MF "$(DEPDIR)/job_queue.Tpo" -c -o job_queue.obj `if test -f 'queues/job_queue.c'; then $(CYGPATH_W) 'queues/job_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/job_queue.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job_queue.Tpo" "$(DEPDIR)/job_queue.Po"; else rm -f "$(DEPDIR)/job_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/job_queue.c' object='job_queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job_queue.obj `if test -f 'queues/job_queue.c'; then $(CYGPATH_W) 'queues/job_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/job_queue.c'; fi` + +event_queue.o: queues/event_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT event_queue.o -MD -MP -MF "$(DEPDIR)/event_queue.Tpo" -c -o event_queue.o `test -f 'queues/event_queue.c' || echo '$(srcdir)/'`queues/event_queue.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/event_queue.Tpo" "$(DEPDIR)/event_queue.Po"; else rm -f "$(DEPDIR)/event_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/event_queue.c' object='event_queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o event_queue.o `test -f 'queues/event_queue.c' || echo '$(srcdir)/'`queues/event_queue.c + +event_queue.obj: queues/event_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT event_queue.obj -MD -MP -MF "$(DEPDIR)/event_queue.Tpo" -c -o event_queue.obj `if test -f 'queues/event_queue.c'; then $(CYGPATH_W) 'queues/event_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/event_queue.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/event_queue.Tpo" "$(DEPDIR)/event_queue.Po"; else rm -f "$(DEPDIR)/event_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/event_queue.c' object='event_queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o event_queue.obj `if test -f 'queues/event_queue.c'; then $(CYGPATH_W) 'queues/event_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/event_queue.c'; fi` + +kernel_interface.o: threads/kernel_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT kernel_interface.o -MD -MP -MF "$(DEPDIR)/kernel_interface.Tpo" -c -o kernel_interface.o `test -f 'threads/kernel_interface.c' || echo '$(srcdir)/'`threads/kernel_interface.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/kernel_interface.Tpo" "$(DEPDIR)/kernel_interface.Po"; else rm -f "$(DEPDIR)/kernel_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/kernel_interface.c' object='kernel_interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_interface.o `test -f 'threads/kernel_interface.c' || echo '$(srcdir)/'`threads/kernel_interface.c + +kernel_interface.obj: threads/kernel_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT kernel_interface.obj -MD -MP -MF "$(DEPDIR)/kernel_interface.Tpo" -c -o kernel_interface.obj `if test -f 'threads/kernel_interface.c'; then $(CYGPATH_W) 'threads/kernel_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/kernel_interface.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/kernel_interface.Tpo" "$(DEPDIR)/kernel_interface.Po"; else rm -f "$(DEPDIR)/kernel_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/kernel_interface.c' object='kernel_interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_interface.obj `if test -f 'threads/kernel_interface.c'; then $(CYGPATH_W) 'threads/kernel_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/kernel_interface.c'; fi` + +thread_pool.o: threads/thread_pool.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread_pool.o -MD -MP -MF "$(DEPDIR)/thread_pool.Tpo" -c -o thread_pool.o `test -f 'threads/thread_pool.c' || echo '$(srcdir)/'`threads/thread_pool.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/thread_pool.Tpo" "$(DEPDIR)/thread_pool.Po"; else rm -f "$(DEPDIR)/thread_pool.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/thread_pool.c' object='thread_pool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o thread_pool.o `test -f 'threads/thread_pool.c' || echo '$(srcdir)/'`threads/thread_pool.c + +thread_pool.obj: threads/thread_pool.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread_pool.obj -MD -MP -MF "$(DEPDIR)/thread_pool.Tpo" -c -o thread_pool.obj `if test -f 'threads/thread_pool.c'; then $(CYGPATH_W) 'threads/thread_pool.c'; else $(CYGPATH_W) '$(srcdir)/threads/thread_pool.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/thread_pool.Tpo" "$(DEPDIR)/thread_pool.Po"; else rm -f "$(DEPDIR)/thread_pool.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/thread_pool.c' object='thread_pool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o thread_pool.obj `if test -f 'threads/thread_pool.c'; then $(CYGPATH_W) 'threads/thread_pool.c'; else $(CYGPATH_W) '$(srcdir)/threads/thread_pool.c'; fi` + +scheduler.o: threads/scheduler.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scheduler.o -MD -MP -MF "$(DEPDIR)/scheduler.Tpo" -c -o scheduler.o `test -f 'threads/scheduler.c' || echo '$(srcdir)/'`threads/scheduler.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/scheduler.Tpo" "$(DEPDIR)/scheduler.Po"; else rm -f "$(DEPDIR)/scheduler.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/scheduler.c' object='scheduler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scheduler.o `test -f 'threads/scheduler.c' || echo '$(srcdir)/'`threads/scheduler.c + +scheduler.obj: threads/scheduler.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scheduler.obj -MD -MP -MF "$(DEPDIR)/scheduler.Tpo" -c -o scheduler.obj `if test -f 'threads/scheduler.c'; then $(CYGPATH_W) 'threads/scheduler.c'; else $(CYGPATH_W) '$(srcdir)/threads/scheduler.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/scheduler.Tpo" "$(DEPDIR)/scheduler.Po"; else rm -f "$(DEPDIR)/scheduler.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/scheduler.c' object='scheduler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scheduler.obj `if test -f 'threads/scheduler.c'; then $(CYGPATH_W) 'threads/scheduler.c'; else $(CYGPATH_W) '$(srcdir)/threads/scheduler.c'; fi` + +sender.o: threads/sender.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sender.o -MD -MP -MF "$(DEPDIR)/sender.Tpo" -c -o sender.o `test -f 'threads/sender.c' || echo '$(srcdir)/'`threads/sender.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sender.Tpo" "$(DEPDIR)/sender.Po"; else rm -f "$(DEPDIR)/sender.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/sender.c' object='sender.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sender.o `test -f 'threads/sender.c' || echo '$(srcdir)/'`threads/sender.c + +sender.obj: threads/sender.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sender.obj -MD -MP -MF "$(DEPDIR)/sender.Tpo" -c -o sender.obj `if test -f 'threads/sender.c'; then $(CYGPATH_W) 'threads/sender.c'; else $(CYGPATH_W) '$(srcdir)/threads/sender.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sender.Tpo" "$(DEPDIR)/sender.Po"; else rm -f "$(DEPDIR)/sender.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/sender.c' object='sender.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sender.obj `if test -f 'threads/sender.c'; then $(CYGPATH_W) 'threads/sender.c'; else $(CYGPATH_W) '$(srcdir)/threads/sender.c'; fi` + +receiver.o: threads/receiver.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT receiver.o -MD -MP -MF "$(DEPDIR)/receiver.Tpo" -c -o receiver.o `test -f 'threads/receiver.c' || echo '$(srcdir)/'`threads/receiver.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/receiver.Tpo" "$(DEPDIR)/receiver.Po"; else rm -f "$(DEPDIR)/receiver.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/receiver.c' object='receiver.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o receiver.o `test -f 'threads/receiver.c' || echo '$(srcdir)/'`threads/receiver.c + +receiver.obj: threads/receiver.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT receiver.obj -MD -MP -MF "$(DEPDIR)/receiver.Tpo" -c -o receiver.obj `if test -f 'threads/receiver.c'; then $(CYGPATH_W) 'threads/receiver.c'; else $(CYGPATH_W) '$(srcdir)/threads/receiver.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/receiver.Tpo" "$(DEPDIR)/receiver.Po"; else rm -f "$(DEPDIR)/receiver.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/receiver.c' object='receiver.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o receiver.obj `if test -f 'threads/receiver.c'; then $(CYGPATH_W) 'threads/receiver.c'; else $(CYGPATH_W) '$(srcdir)/threads/receiver.c'; fi` + +stroke_interface.o: threads/stroke_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT stroke_interface.o -MD -MP -MF "$(DEPDIR)/stroke_interface.Tpo" -c -o stroke_interface.o `test -f 'threads/stroke_interface.c' || echo '$(srcdir)/'`threads/stroke_interface.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/stroke_interface.Tpo" "$(DEPDIR)/stroke_interface.Po"; else rm -f "$(DEPDIR)/stroke_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/stroke_interface.c' object='stroke_interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stroke_interface.o `test -f 'threads/stroke_interface.c' || echo '$(srcdir)/'`threads/stroke_interface.c + +stroke_interface.obj: threads/stroke_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT stroke_interface.obj -MD -MP -MF "$(DEPDIR)/stroke_interface.Tpo" -c -o stroke_interface.obj `if test -f 'threads/stroke_interface.c'; then $(CYGPATH_W) 'threads/stroke_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/stroke_interface.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/stroke_interface.Tpo" "$(DEPDIR)/stroke_interface.Po"; else rm -f "$(DEPDIR)/stroke_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/stroke_interface.c' object='stroke_interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stroke_interface.obj `if test -f 'threads/stroke_interface.c'; then $(CYGPATH_W) 'threads/stroke_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/stroke_interface.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +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; } \ + END { 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; } \ + END { 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=; \ + 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; } \ + END { 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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + 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) $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(eapdir)" "$(DESTDIR)$(ipsecdir)"; 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-eapLTLIBRARIES clean-generic clean-ipsecPROGRAMS \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-eapLTLIBRARIES install-ipsecPROGRAMS + +install-exec-am: + +install-info: install-info-am + +install-man: + +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-eapLTLIBRARIES uninstall-info-am \ + uninstall-ipsecPROGRAMS + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-eapLTLIBRARIES clean-generic clean-ipsecPROGRAMS \ + clean-libtool 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-eapLTLIBRARIES \ + install-exec install-exec-am install-info install-info-am \ + install-ipsecPROGRAMS install-man 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-eapLTLIBRARIES \ + uninstall-info-am uninstall-ipsecPROGRAMS + +# 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/bus/bus.c b/src/charon/bus/bus.c new file mode 100644 index 000000000..740663d5c --- /dev/null +++ b/src/charon/bus/bus.c @@ -0,0 +1,397 @@ +/** + * @file bus.c + * + * @brief Implementation of bus_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include "bus.h" + +#include <pthread.h> + +ENUM(signal_names, SIG_ANY, SIG_MAX, + /** should not get printed */ + "SIG_ANY", + /** debugging message types */ + "DMN", + "MGR", + "IKE", + "CHD", + "JOB", + "CFG", + "KNL", + "NET", + "ENC", + "LIB", + /** should not get printed */ + "SIG_DBG_MAX", + /** all level0 signals are AUDIT signals */ + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + /** should not get printed */ + "SIG_MAX", +); + +typedef struct active_listener_t active_listener_t; + +/** + * information for a active listener + */ +struct active_listener_t { + + /** + * associated thread + */ + pthread_t id; + + /** + * condvar to wait for a signal + */ + pthread_cond_t cond; + + /** + * state of the thread + */ + enum { + /** not registered, do not wait for thread */ + UNREGISTERED, + /** registered, if a signal occurs, wait until it is LISTENING */ + REGISTERED, + /** listening, deliver signal */ + LISTENING, + } state; + + /** + * currently processed signals type + */ + signal_t signal; + + /** + * verbosity level of the signal + */ + level_t level; + + /** + * current processed signals thread number + */ + int thread; + + /** + * currently processed signals ike_sa + */ + ike_sa_t *ike_sa; + + /** + * currently processed signals format string + */ + char *format; + + /** + * currently processed signals format varargs + */ + va_list args; + +}; + +typedef struct private_bus_t private_bus_t; + +/** + * Private data of a bus_t object. + */ +struct private_bus_t { + /** + * Public part of a bus_t object. + */ + bus_t public; + + /** + * List of registered listeners implementing the bus_t interface + */ + linked_list_t *listeners; + + /** + * List of active listeners with listener_state TRUE + */ + linked_list_t *active_listeners; + + /** + * mutex to synchronize active listeners + */ + pthread_mutex_t mutex; + + /** + * Thread local storage for a unique, simple thread ID + */ + pthread_key_t thread_id; + + /** + * Thread local storage the threads IKE_SA + */ + pthread_key_t thread_sa; + +}; + +/** + * Get a unique thread number for a calling thread. Since + * pthread_self returns large and ugly numbers, use this function + * for logging; these numbers are incremental starting at 1 + */ +static int get_thread_number(private_bus_t *this) +{ + static long current_num = 0; + static long stored_num; + + stored_num = (long)pthread_getspecific(this->thread_id); + if (stored_num == 0) + { /* first call of current thread */ + pthread_setspecific(this->thread_id, (void*)++current_num); + return current_num; + } + else + { + return stored_num; + } +} + +/** + * Implementation of bus_t.add_listener. + */ +static void add_listener(private_bus_t *this, bus_listener_t *listener) +{ + pthread_mutex_lock(&this->mutex); + this->listeners->insert_last(this->listeners, listener); + pthread_mutex_unlock(&this->mutex); +} + +/** + * Get the listener object for the calling thread + */ +static active_listener_t *get_active_listener(private_bus_t *this) +{ + active_listener_t *current, *found = NULL; + iterator_t *iterator; + + /* if the thread was here once before, we have a active_listener record */ + iterator = this->active_listeners->create_iterator(this->active_listeners, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->id == pthread_self()) + { + found = current; + break; + } + } + iterator->destroy(iterator); + + if (found == NULL) + { + /* create a new object for a never-seen thread */ + found = malloc_thing(active_listener_t); + found->id = pthread_self(); + pthread_cond_init(&found->cond, NULL); + this->active_listeners->insert_last(this->active_listeners, found); + } + + return found; +} + +/** + * Implementation of bus_t.listen. + */ +static signal_t listen_(private_bus_t *this, level_t *level, int *thread, + ike_sa_t **ike_sa, char** format, va_list* args) +{ + active_listener_t *listener; + + pthread_mutex_lock(&this->mutex); + listener = get_active_listener(this); + /* go "listening", say hello to a thread which have a signal for us */ + listener->state = LISTENING; + pthread_cond_broadcast(&listener->cond); + /* wait until it has us delivered a signal, and go back to "registered" */ + pthread_cond_wait(&listener->cond, &this->mutex); + pthread_mutex_unlock(&this->mutex); + + /* return signal values */ + *level = listener->level; + *thread = listener->thread; + *ike_sa = listener->ike_sa; + *format = listener->format; + va_copy(*args, listener->args); + va_end(listener->args); + + return listener->signal; +} + +/** + * Implementation of bus_t.set_listen_state. + */ +static void set_listen_state(private_bus_t *this, bool active) +{ + active_listener_t *listener; + + pthread_mutex_lock(&this->mutex); + + listener = get_active_listener(this); + if (active) + { + listener->state = REGISTERED; + } + else + { + listener->state = UNREGISTERED; + /* say hello to signal emitter; we are finished processing the signal */ + pthread_cond_signal(&listener->cond); + } + + pthread_mutex_unlock(&this->mutex); +} + + +/** + * Implementation of bus_t.set_sa. + */ +static void set_sa(private_bus_t *this, ike_sa_t *ike_sa) +{ + pthread_setspecific(this->thread_sa, ike_sa); +} + +/** + * Implementation of bus_t.vsignal. + */ +static void vsignal(private_bus_t *this, signal_t signal, level_t level, + char* format, va_list args) +{ + iterator_t *iterator; + bus_listener_t *listener; + active_listener_t *active_listener; + ike_sa_t *ike_sa; + long thread; + + ike_sa = pthread_getspecific(this->thread_sa); + thread = get_thread_number(this); + + pthread_mutex_lock(&this->mutex); + + /* do the job for all passive bus_listeners */ + iterator = this->listeners->create_iterator(this->listeners, TRUE); + while (iterator->iterate(iterator, (void**)&listener)) + { + va_list args_copy; + + va_copy(args_copy, args); + if (!listener->signal(listener, signal, level, thread, + ike_sa, format, args_copy)) + { + /* unregister listener if requested */ + iterator->remove(iterator); + } + va_end(args_copy); + } + iterator->destroy(iterator); + + /* wake up all active listeners */ + iterator = this->active_listeners->create_iterator(this->active_listeners, TRUE); + while (iterator->iterate(iterator, (void**)&active_listener)) + { + /* wait until it is back */ + while (active_listener->state == REGISTERED) + { + pthread_cond_wait(&active_listener->cond, &this->mutex); + } + /* if thread is listening now, give it the signal to process */ + if (active_listener->state == LISTENING) + { + active_listener->level = level; + active_listener->thread = thread; + active_listener->ike_sa = ike_sa; + active_listener->signal = signal; + active_listener->format = format; + va_copy(active_listener->args, args); + active_listener->state = REGISTERED; + pthread_cond_signal(&active_listener->cond); + } + } + + /* we must wait now until all are not in state REGISTERED, + * as they may still use our arguments */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&active_listener)) + { + while (active_listener->state == REGISTERED) + { + pthread_cond_wait(&active_listener->cond, &this->mutex); + } + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&this->mutex); +} + +/** + * Implementation of bus_t.signal. + */ +static void signal_(private_bus_t *this, signal_t signal, level_t level, + char* format, ...) +{ + va_list args; + + va_start(args, format); + vsignal(this, signal, level, format, args); + va_end(args); +} + +/** + * Implementation of bus_t.destroy. + */ +static void destroy(private_bus_t *this) +{ + this->active_listeners->destroy_function(this->active_listeners, free); + this->listeners->destroy(this->listeners); + free(this); +} + +/* + * Described in header. + */ +bus_t *bus_create() +{ + private_bus_t *this = malloc_thing(private_bus_t); + + this->public.add_listener = (void(*)(bus_t*,bus_listener_t*))add_listener; + this->public.listen = (signal_t(*)(bus_t*,level_t*,int*,ike_sa_t**,char**,va_list*))listen_; + this->public.set_listen_state = (void(*)(bus_t*,bool))set_listen_state; + this->public.set_sa = (void(*)(bus_t*,ike_sa_t*))set_sa; + this->public.signal = (void(*)(bus_t*,signal_t,level_t,char*,...))signal_; + this->public.vsignal = (void(*)(bus_t*,signal_t,level_t,char*,va_list))vsignal; + this->public.destroy = (void(*)(bus_t*)) destroy; + + this->listeners = linked_list_create(); + this->active_listeners = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + pthread_key_create(&this->thread_id, NULL); + pthread_key_create(&this->thread_sa, NULL); + + return &(this->public); +} diff --git a/src/charon/bus/bus.h b/src/charon/bus/bus.h new file mode 100644 index 000000000..200525fb7 --- /dev/null +++ b/src/charon/bus/bus.h @@ -0,0 +1,366 @@ +/** + * @file bus.h + * + * @brief Interface of bus_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef BUS_H_ +#define BUS_H_ + +typedef enum signal_t signal_t; +typedef enum level_t level_t; +typedef struct bus_listener_t bus_listener_t; +typedef struct bus_t bus_t; + +#include <stdarg.h> + +#include <sa/ike_sa.h> +#include <sa/child_sa.h> + + +/** + * @brief signals emitted by the daemon. + * + * Signaling is for different purporses. First, it allows debugging via + * "debugging signal messages", sencondly, it allows to follow certain + * mechanisms currently going on in the daemon. As we are multithreaded, + * and of multiple transactions are involved, it's not possible to follow + * one connection setup without further infrastructure. These infrastructure + * is provided by the bus and the signals the daemon emits to the bus. + * + * There are different scenarios to follow these signals, but all have + * the same scheme. First, a START signal is emitted to indicate the daemon + * has started to + * + * @ingroup bus + */ +enum signal_t { + /** pseudo signal, representing any other signal */ + SIG_ANY, + + /** debugging message from daemon main loop */ + DBG_DMN, + /** debugging message from IKE_SA_MANAGER */ + DBG_MGR, + /** debugging message from an IKE_SA */ + DBG_IKE, + /** debugging message from a CHILD_SA */ + DBG_CHD, + /** debugging message from job processing */ + DBG_JOB, + /** debugging message from configuration backends */ + DBG_CFG, + /** debugging message from kernel interface */ + DBG_KNL, + /** debugging message from networking */ + DBG_NET, + /** debugging message from message encoding/decoding */ + DBG_ENC, + /** debugging message from libstrongswan via logging hook */ + DBG_LIB, + + /** number of debug signals */ + DBG_MAX, + + /** signals for IKE_SA establishment */ + IKE_UP_START, + IKE_UP_SUCCESS, + IKE_UP_FAILED, + + /** signals for IKE_SA delete */ + IKE_DOWN_START, + IKE_DOWN_SUCCESS, + IKE_DOWN_FAILED, + + /** signals for IKE_SA rekeying */ + IKE_REKEY_START, + IKE_REKEY_SUCCESS, + IKE_REKEY_FAILED, + + /** signals for CHILD_SA establishment */ + CHILD_UP_START, + CHILD_UP_SUCCESS, + CHILD_UP_FAILED, + + /** signals for CHILD_SA delete */ + CHILD_DOWN_START, + CHILD_DOWN_SUCCESS, + CHILD_DOWN_FAILED, + + /** signals for CHILD_SA rekeying */ + CHILD_REKEY_START, + CHILD_REKEY_SUCCESS, + CHILD_REKEY_FAILED, + + /** signals for CHILD_SA routing */ + CHILD_ROUTE_START, + CHILD_ROUTE_SUCCESS, + CHILD_ROUTE_FAILED, + + /** signals for CHILD_SA routing */ + CHILD_UNROUTE_START, + CHILD_UNROUTE_SUCCESS, + CHILD_UNROUTE_FAILED, + + SIG_MAX +}; + +/** + * short names of signals using 3 chars + */ +extern enum_name_t *signal_names; + +/** + * Signal levels used to control output verbosity. + */ +enum level_t { + /** numerical levels from 0 to 4 */ + LEVEL_0 = 0, + LEVEL_1 = 1, + LEVEL_2 = 2, + LEVEL_3 = 3, + LEVEL_4 = 4, + /** absolutely silent, no signal is emitted with this level */ + LEVEL_SILENT = -1, + /** alias for numberical levels */ + LEVEL_AUDIT = LEVEL_0, + LEVEL_CTRL = LEVEL_1, + LEVEL_CTRLMORE = LEVEL_2, + LEVEL_RAW = LEVEL_3, + LEVEL_PRIVATE = LEVEL_4, +}; + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 4 +#endif /* DEBUG_LEVEL */ + +#if DEBUG_LEVEL >= 1 +/** + * @brief Log a debug message via the signal bus. + * + * @param signal signal_t signal description + * @param format printf() style format string + * @param ... printf() style agument list + */ +# define DBG1(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_1, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 2 +#define DBG2(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_2, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 3 +#define DBG3(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_3, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 4 +#define DBG4(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_4, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ + +#ifndef DBG1 +# define DBG1(...) {} +#endif /* DBG1 */ +#ifndef DBG2 +# define DBG2(...) {} +#endif /* DBG2 */ +#ifndef DBG3 +# define DBG3(...) {} +#endif /* DBG3 */ +#ifndef DBG4 +# define DBG4(...) {} +#endif /* DBG4 */ + +/** + * @brief Raise a signal for an occured event. + * + * @param sig signal_t signal description + * @param format printf() style format string + * @param ... printf() style agument list + */ +#define SIG(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_0, format, ##__VA_ARGS__) + +/** + * @brief Get the type of a signal. + * + * A signal may be a debugging signal with a specific context. They have + * a level specific for their context > 0. All audit signals use the + * type 0. This allows filtering of singals by their type. + * + * @param signal signal to get the type from + * @return type of the signal, between 0..(DBG_MAX-1) + */ +#define SIG_TYPE(sig) (sig > DBG_MAX ? SIG_ANY : sig) + + +/** + * @brief Interface for registering at the signal bus. + * + * To receive signals from the bus, the client implementing the + * bus_listener_t interface registers itself at the signal bus. + * + * @ingroup bus + */ +struct bus_listener_t { + + /** + * @brief Send a signal to a bus listener. + * + * A numerical identification for the thread is included, as the + * associated IKE_SA, if any. Signal specifies the type of + * the event occured. The format string specifies + * an additional informational or error message with a printf() like + * variable argument list. This is in the va_list form, as forwarding + * a "..." parameters to functions is not (cleanly) possible. + * The implementing signal function returns TRUE to stay registered + * to the bus, or FALSE to unregister itself. + * + * @param this listener + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param thread ID of the thread raised this signal + * @param ike_sa IKE_SA associated to the event + * @param format printf() style format string + * @param args vprintf() style va_list argument list + " @return TRUE to stay registered, FALSE to unregister + */ + bool (*signal) (bus_listener_t *this, signal_t signal, level_t level, + int thread, ike_sa_t *ike_sa, char* format, va_list args); +}; + +/** + * @brief Signal bus which sends signals to registered listeners. + * + * The signal bus is not much more than a multiplexer. A listener interested + * in receiving event signals registers at the bus. Any signals sent to + * are delivered to all registered listeners. + * To deliver signals to threads, the blocking listen() call may be used + * to wait for a signal. + * + * @ingroup bus + */ +struct bus_t { + + /** + * @brief Register a listener to the bus. + * + * A registered listener receives all signals which are sent to the bus. + * The listener is passive; the thread which emitted the signal + * processes the listener routine. + * + * @param this bus + * @param listener listener to register. + */ + void (*add_listener) (bus_t *this, bus_listener_t *listener); + + /** + * @brief Listen actively on the bus. + * + * As we are fully multithreaded, we must provide a mechanism + * for active threads to listen to the bus. With the listen() method, + * a thread waits until a signal occurs, and then processes it. + * To prevent the listen() calling thread to miss signals emitted while + * it processes a signal, registration is required. This is done through + * the set_listen_state() method, see below. + * + * @param this bus + * @param level verbosity level of the signal + * @param thread receives thread number emitted the signal + * @param ike_sa receives the IKE_SA involved in the signal, or NULL + * @param format receives the format string supplied with the signal + * @param va_list receives the variable argument list for format + * @return the emitted signal type + */ + signal_t (*listen) (bus_t *this, level_t* level, int *thread, + ike_sa_t **ike_sa, char** format, va_list* args); + + /** + * @brief Set the listening state of the calling thread. + * + * To prevent message loss for active listeners using listen(), threads + * must register themself to the bus before starting to listen(). When + * a signal occurs, the emitter waits until all threads with listen_state + * TRUE are waiting in the listen() method to process the signal. + * It is important that a thread with liste_state TRUE calls listen() + * periodically, or sets it's listening state to FALSE; otherwise + * all signal emitting threads get blocked on the bus. + * + * @param this bus + * @param active TRUE to set to listening + */ + void (*set_listen_state) (bus_t *this, bool active); + + /** + * @brief Set the IKE_SA the calling thread is using. + * + * To associate an received signal to an IKE_SA without passing it as + * parameter each time, the thread registers it's used IKE_SA each + * time it checked it out. Before checking it in, the thread unregisters + * the IKE_SA (by passing NULL). This IKE_SA is stored per-thread, so each + * thread has one IKE_SA registered (or not). + * + * @param this bus + * @param ike_sa ike_sa to register, or NULL to unregister + */ + void (*set_sa) (bus_t *this, ike_sa_t *ike_sa); + + /** + * @brief Send a signal to the bus. + * + * The signal specifies the type of the event occured. The format string + * specifies an additional informational or error message with a + * printf() like variable argument list. + * Some useful macros are available to shorten this call. + * @see SIG(), DBG1() + * + * @param this bus + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param format printf() style format string + * @param ... printf() style argument list + */ + void (*signal) (bus_t *this, signal_t signal, level_t level, char* format, ...); + + /** + * @brief Send a signal to the bus using va_list arguments. + * + * Same as bus_t.signal(), but uses va_list argument list. + * + * @param this bus + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param format printf() style format string + * @param args va_list arguments + */ + void (*vsignal) (bus_t *this, signal_t signal, level_t level, char* format, va_list args); + + /** + * @brief Destroy the signal bus. + * + * @param this bus to destroy + */ + void (*destroy) (bus_t *this); +}; + +/** + * @brief Create the signal bus which multiplexes signals to its listeners. + * + * @return signal bus instance + * + * @ingroup bus + */ +bus_t *bus_create(); + +#endif /* BUS_H_ */ diff --git a/src/charon/bus/listeners/file_logger.c b/src/charon/bus/listeners/file_logger.c new file mode 100644 index 000000000..14f9f72cf --- /dev/null +++ b/src/charon/bus/listeners/file_logger.c @@ -0,0 +1,128 @@ +/** + * @file file_logger.c + * + * @brief Implementation of file_logger_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <stdio.h> +#include <string.h> + +#include "file_logger.h" + + +typedef struct private_file_logger_t private_file_logger_t; + +/** + * Private data of a file_logger_t object + */ +struct private_file_logger_t { + + /** + * Public data. + */ + file_logger_t public; + + /** + * output file + */ + FILE *out; + + /** + * Maximum level to log + */ + level_t levels[DBG_MAX]; +}; + + +/** + * Implementation of bus_listener_t.signal. + */ +static bool signal_(private_file_logger_t *this, signal_t signal, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[SIG_TYPE(signal)]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* prepend a prefix in front of every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + fprintf(this->out, "%.2d[%N] %s\n", thread, signal_names, signal, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of file_logger_t.set_level. + */ +static void set_level(private_file_logger_t *this, signal_t signal, level_t level) +{ + if (signal == SIG_ANY) + { + int i; + for (i = 0; i < DBG_MAX; i++) + { + this->levels[i] = level; + } + } + else + { + + this->levels[SIG_TYPE(signal)] = level; + } +} + +/** + * Implementation of file_logger_t.destroy. + */ +static void destroy(private_file_logger_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +file_logger_t *file_logger_create(FILE *out) +{ + private_file_logger_t *this = malloc_thing(private_file_logger_t); + + /* public functions */ + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.set_level = (void(*)(file_logger_t*,signal_t,level_t))set_level; + this->public.destroy = (void(*)(file_logger_t*))destroy; + + /* private variables */ + this->out = out; + set_level(this, SIG_ANY, LEVEL_SILENT); + + return &this->public; +} diff --git a/src/charon/bus/listeners/file_logger.h b/src/charon/bus/listeners/file_logger.h new file mode 100644 index 000000000..d67daba25 --- /dev/null +++ b/src/charon/bus/listeners/file_logger.h @@ -0,0 +1,73 @@ +/** + * @file file_logger.h + * + * @brief Interface of file_logger_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef FILE_LOGGER_H_ +#define FILE_LOGGER_H_ + +typedef struct file_logger_t file_logger_t; + +#include <bus/bus.h> + +/** + * @brief Logger to files which implements bus_listener_t. + * + * @b Constructors: + * - file_logger_create() + * + * @ingroup listeners + */ +struct file_logger_t { + + /** + * Implements the bus_listener_t interface. + */ + bus_listener_t listener; + + /** + * @brief Set the loglevel for a signal type. + * + * @param this stream_logger_t object + * @param singal type of signal + * @param level max level to log (0..4) + */ + void (*set_level) (file_logger_t *this, signal_t signal, level_t level); + + /** + * @brief Destroys a file_logger_t object. + * + * @param this file_logger_t object + */ + void (*destroy) (file_logger_t *this); +}; + +/** + * @brief Constructor to create a file_logger_t object. + * + * @param out FILE to write to + * @return file_logger_t object + * + * @ingroup listeners + */ +file_logger_t *file_logger_create(FILE *out); + + +#endif /* FILE_LOGGER_H_ */ diff --git a/src/charon/bus/listeners/sys_logger.c b/src/charon/bus/listeners/sys_logger.c new file mode 100644 index 000000000..d26d14dc0 --- /dev/null +++ b/src/charon/bus/listeners/sys_logger.c @@ -0,0 +1,131 @@ +/** + * @file sys_logger.c + * + * @brief Implementation of sys_logger_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <stdio.h> +#include <string.h> +#include <pthread.h> + +#include "sys_logger.h" + + +typedef struct private_sys_logger_t private_sys_logger_t; + +/** + * Private data of a sys_logger_t object + */ +struct private_sys_logger_t { + + /** + * Public data. + */ + sys_logger_t public; + + /** + * syslog facility to use + */ + int facility; + + /** + * Maximum level to log + */ + level_t levels[DBG_MAX]; +}; + + +/** + * Implementation of bus_listener_t.signal. + */ +static bool signal_(private_sys_logger_t *this, signal_t signal, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[SIG_TYPE(signal)]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(this->facility|LOG_INFO, "%.2d[%N] %s\n", + thread, signal_names, signal, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of sys_logger_t.set_level. + */ +static void set_level(private_sys_logger_t *this, signal_t signal, level_t level) +{ + if (signal == SIG_ANY) + { + int i; + for (i = 0; i < DBG_MAX; i++) + { + this->levels[i] = level; + } + } + else + { + + this->levels[SIG_TYPE(signal)] = level; + } +} + +/** + * Implementation of sys_logger_t.destroy. + */ +static void destroy(private_sys_logger_t *this) +{ + closelog(); + free(this); +} + +/* + * Described in header. + */ +sys_logger_t *sys_logger_create(int facility) +{ + private_sys_logger_t *this = malloc_thing(private_sys_logger_t); + + /* public functions */ + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.set_level = (void(*)(sys_logger_t*,signal_t,level_t))set_level; + this->public.destroy = (void(*)(sys_logger_t*))destroy; + + /* private variables */ + this->facility = facility; + set_level(this, SIG_ANY, LEVEL_SILENT); + + return &this->public; +} diff --git a/src/charon/bus/listeners/sys_logger.h b/src/charon/bus/listeners/sys_logger.h new file mode 100644 index 000000000..091217313 --- /dev/null +++ b/src/charon/bus/listeners/sys_logger.h @@ -0,0 +1,75 @@ +/** + * @file sys_logger.h + * + * @brief Interface of sys_logger_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef SYS_LOGGER_H_ +#define SYS_LOGGER_H_ + +typedef struct sys_logger_t sys_logger_t; + +#include <syslog.h> + +#include <bus/bus.h> + +/** + * @brief Logger for syslog which implements bus_listener_t. + * + * @b Constructors: + * - sys_logger_create() + * + * @ingroup listeners + */ +struct sys_logger_t { + + /** + * Implements the bus_listener_t interface. + */ + bus_listener_t listener; + + /** + * @brief Set the loglevel for a signal type. + * + * @param this stream_logger_t object + * @param singal type of signal + * @param level max level to log + */ + void (*set_level) (sys_logger_t *this, signal_t signal, level_t level); + + /** + * @brief Destroys a sys_logger_t object. + * + * @param this sys_logger_t object + */ + void (*destroy) (sys_logger_t *this); +}; + +/** + * @brief Constructor to create a sys_logger_t object. + * + * @param facility syslog facility to use + * @return sys_logger_t object + * + * @ingroup listeners + */ +sys_logger_t *sys_logger_create(int facility); + + +#endif /* SYS_LOGGER_H_ */ diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c new file mode 100755 index 000000000..488ba9a5e --- /dev/null +++ b/src/charon/config/configuration.c @@ -0,0 +1,162 @@ +/** + * @file configuration.c + * + * @brief Implementation of configuration_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <stdlib.h> +#include <math.h> + +#include "configuration.h" + +#include <library.h> + +/** + * Timeout in milliseconds after that a half open IKE_SA gets deleted. + */ +#define HALF_OPEN_IKE_SA_TIMEOUT 30000 + +/** + * Retransmission uses a backoff algorithm. The timeout is calculated using + * TIMEOUT * (BASE ** try). + * When try reaches TRIES, retransmission is given up. + * + * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us: + * + * | relative | absolute + * --------------------------------------------------------- + * 4s * (1.8 ** (0 % 5)) = 4s 4s + * 4s * (1.8 ** (1 % 5)) = 7s 11s + * 4s * (1.8 ** (2 % 5)) = 13s 24s + * 4s * (1.8 ** (3 % 5)) = 23s 47s + * 4s * (1.8 ** (4 % 5)) = 42s 89s + * 4s * (1.8 ** (5 % 5)) = 76s 165s + * + * The peer is considered dead after 2min 45s when no reply comes in. + */ + +/** + * First retransmit timeout in milliseconds. + * Timeout value is increasing in each retransmit round. + */ +#define RETRANSMIT_TIMEOUT 4000 + +/** + * Base which is raised to the power of the retransmission count. + */ +#define RETRANSMIT_BASE 1.8 + +/** + * Number of retransmits done in a retransmit sequence + */ +#define RETRANSMIT_TRIES 5 + +/** + * Keepalive interval in seconds. + */ +#define KEEPALIVE_INTERVAL 20 + +/** + * retry interval in seconds. + */ +#define RETRY_INTERVAL 30 + +/** + * jitter to user for retrying + */ +#define RETRY_JITTER 20 + + +typedef struct private_configuration_t private_configuration_t; + +/** + * Private data of an configuration_t object. + */ +struct private_configuration_t { + + /** + * Public part of configuration_t object. + */ + configuration_t public; + +}; + +/** + * Implementation of configuration_t.get_retransmit_timeout. + */ +static u_int32_t get_retransmit_timeout (private_configuration_t *this, + u_int32_t retransmit_count) +{ + if (retransmit_count > RETRANSMIT_TRIES) + { + /* give up */ + return 0; + } + return (u_int32_t) + (RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); +} + +/** + * Implementation of configuration_t.get_half_open_ike_sa_timeout. + */ +static u_int32_t get_half_open_ike_sa_timeout (private_configuration_t *this) +{ + return HALF_OPEN_IKE_SA_TIMEOUT; +} + +/** + * Implementation of configuration_t.get_keepalive_interval. + */ +static u_int32_t get_keepalive_interval (private_configuration_t *this) +{ + return KEEPALIVE_INTERVAL; +} + +/** + * Implementation of configuration_t.get_retry_interval. + */ +static u_int32_t get_retry_interval (private_configuration_t *this) +{ + return RETRY_INTERVAL - (random() % RETRY_JITTER); +} + +/** + * Implementation of configuration_t.destroy. + */ +static void destroy(private_configuration_t *this) +{ + free(this); +} + +/* + * Described in header-file + */ +configuration_t *configuration_create() +{ + private_configuration_t *this = malloc_thing(private_configuration_t); + + /* public functions */ + this->public.destroy = (void(*)(configuration_t*))destroy; + this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t))get_retransmit_timeout; + this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t*)) get_half_open_ike_sa_timeout; + this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t*)) get_keepalive_interval; + this->public.get_retry_interval = (u_int32_t (*) (configuration_t*)) get_retry_interval; + + return (&this->public); +} diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h new file mode 100755 index 000000000..c1207171d --- /dev/null +++ b/src/charon/config/configuration.h @@ -0,0 +1,102 @@ +/** + * @file configuration.h + * + * @brief Interface configuration_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ + +typedef struct configuration_t configuration_t; + +#include <library.h> + +/** + * @brief The interface for various daemon related configs. + * + * @b Constructors: + * - configuration_create() + * + * @ingroup config + */ +struct configuration_t { + + /** + * @brief Returns the retransmit timeout. + * + * A return value of zero means the request should not be + * retransmitted again. + * + * @param this calling object + * @param retransmitted number of times a message was retransmitted so far + * @return time in milliseconds, when to do next retransmit + */ + u_int32_t (*get_retransmit_timeout) (configuration_t *this, + u_int32_t retransmitted); + + /** + * @brief Returns the timeout for an half open IKE_SA in ms. + * + * Half open means that the IKE_SA is still on a not established state + * + * @param this calling object + * @return timeout in milliseconds (ms) + */ + u_int32_t (*get_half_open_ike_sa_timeout) (configuration_t *this); + + /** + * @brief Returns the keepalive interval in s. + * + * The keepalive interval defines the idle time after which a + * NAT keepalive packet should be sent. + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_keepalive_interval) (configuration_t *this); + + /** + * @brief Returns the interval to retry a failed action again. + * + * In some situations, the protocol may be in a state where processing + * is not possible and an action must be retried (e.g. rekeying). + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_retry_interval) (configuration_t *this); + + /** + * @brief Destroys a configuration_t object. + * + * @param this calling object + */ + void (*destroy) (configuration_t *this); +}; + +/** + * @brief Creates a configuration backend. + * + * @return static_configuration_t object + * + * @ingroup config + */ +configuration_t *configuration_create(void); + +#endif /*CONFIGURATION_H_*/ diff --git a/src/charon/config/connections/connection.c b/src/charon/config/connections/connection.c new file mode 100644 index 000000000..ffe508992 --- /dev/null +++ b/src/charon/config/connections/connection.c @@ -0,0 +1,404 @@ +/** + * @file connection.c + * + * @brief Implementation of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> + +#include <config/connections/connection.h> +#include <utils/linked_list.h> + +ENUM(cert_policy_names, CERT_ALWAYS_SEND, CERT_NEVER_SEND, + "CERT_ALWAYS_SEND", + "CERT_SEND_IF_ASKED", + "CERT_NEVER_SEND" +); + +typedef struct private_connection_t private_connection_t; + +/** + * Private data of an connection_t object + */ +struct private_connection_t { + + /** + * Public part + */ + connection_t public; + + /** + * Number of references hold by others to this connection + */ + refcount_t refcount; + + /** + * Name of the connection + */ + char *name; + + /** + * Does charon handle this connection? Or can he ignore it? + */ + bool ikev2; + + /** + * should we send a certificate request? + */ + cert_policy_t certreq_policy; + + /** + * should we send a certificates? + */ + cert_policy_t cert_policy; + + /** + * ID of us + */ + identification_t *my_id; + + /** + * Host information of my host. + */ + host_t *my_host; + + /** + * Host information of other host. + */ + host_t *other_host; + + /** + * Interval to send DPD liveness checks on inactivity + */ + u_int32_t dpd_delay; + + /** + * Number of retransmission sequences to send bevore giving up + */ + u_int32_t keyingtries; + + /** + * Supported proposals + */ + linked_list_t *proposals; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Use full reauthentication instead of rekeying + */ + bool reauth; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; +}; + +/** + * Implementation of connection_t.get_name. + */ +static char *get_name (private_connection_t *this) +{ + return this->name; +} + +/** + * Implementation of connection_t.is_ikev2. + */ +static bool is_ikev2 (private_connection_t *this) +{ + return this->ikev2; +} + +/** + * Implementation of connection_t.get_certreq_policy. + */ +static cert_policy_t get_certreq_policy (private_connection_t *this) +{ + return this->certreq_policy; +} + +/** + * Implementation of connection_t.get_cert_policy. + */ +static cert_policy_t get_cert_policy (private_connection_t *this) +{ + return this->cert_policy; +} + +/** + * Implementation of connection_t.get_my_host. + */ +static host_t *get_my_host (private_connection_t *this) +{ + return this->my_host; +} + +/** + * Implementation of connection_t.get_other_host. + */ +static host_t *get_other_host (private_connection_t *this) +{ + return this->other_host; +} + +/** + * Implementation of connection_t.get_proposals. + */ +static linked_list_t* get_proposals(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of connection_t.select_proposal. + */ +static proposal_t *select_proposal(private_connection_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of connection_t.add_proposal. + */ +static void add_proposal(private_connection_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, proposal); +} + +/** + * Implementation of connection_t.get_dpd_delay. + */ +static u_int32_t get_dpd_delay(private_connection_t *this) +{ + return this->dpd_delay; +} + +/** + * Implementation of connection_t.get_keyingtries. + */ +static u_int32_t get_keyingtries(private_connection_t *this) +{ + return this->keyingtries; +} + +/** + * Implementation of connection_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *proposal; + algorithm_t *algo; + diffie_hellman_group_t dh_group = MODP_NONE; + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &algo)) + { + dh_group = algo->algorithm; + break; + } + } + iterator->destroy(iterator); + return dh_group; +} + +/** + * Implementation of connection_t.check_dh_group. + */ +static bool check_dh_group(private_connection_t *this, diffie_hellman_group_t dh_group) +{ + iterator_t *prop_iter, *alg_iter; + proposal_t *proposal; + algorithm_t *algo; + + prop_iter = this->proposals->create_iterator(this->proposals, TRUE); + while (prop_iter->iterate(prop_iter, (void**)&proposal)) + { + alg_iter = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (alg_iter->iterate(alg_iter, (void**)&algo)) + { + if (algo->algorithm == dh_group) + { + prop_iter->destroy(prop_iter); + alg_iter->destroy(alg_iter); + return TRUE; + } + } + alg_iter->destroy(alg_iter); + } + prop_iter->destroy(prop_iter); + return FALSE; +} +/** + * Implementation of connection_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_connection_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of connection_t.get_hard_lifetime. + */ +static u_int32_t get_hard_lifetime(private_connection_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of connection_t.get_reauth. + */ +static bool get_reauth(private_connection_t *this) +{ + return this->reauth; +} + +/** + * Implementation of connection_t.get_ref. + */ +static void get_ref(private_connection_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implementation of connection_t.destroy. + */ +static void destroy(private_connection_t *this) +{ + if (ref_put(&this->refcount)) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + free(this->name); + free(this); + } +} + +/** + * Described in header. + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_policy, + cert_policy_t certreq_policy, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, + u_int32_t soft_lifetime, u_int32_t jitter) +{ + private_connection_t *this = malloc_thing(private_connection_t); + + /* public functions */ + this->public.get_name = (char*(*)(connection_t*))get_name; + this->public.is_ikev2 = (bool(*)(connection_t*))is_ikev2; + this->public.get_cert_policy = (cert_policy_t(*)(connection_t*))get_cert_policy; + this->public.get_certreq_policy = (cert_policy_t(*)(connection_t*))get_certreq_policy; + this->public.get_my_host = (host_t*(*)(connection_t*))get_my_host; + this->public.get_other_host = (host_t*(*)(connection_t*))get_other_host; + this->public.get_proposals = (linked_list_t*(*)(connection_t*))get_proposals; + this->public.select_proposal = (proposal_t*(*)(connection_t*,linked_list_t*))select_proposal; + this->public.add_proposal = (void(*)(connection_t*, proposal_t*)) add_proposal; + this->public.get_dpd_delay = (u_int32_t(*)(connection_t*)) get_dpd_delay; + this->public.get_reauth = (bool(*)(connection_t*)) get_reauth; + this->public.get_keyingtries = (u_int32_t(*)(connection_t*)) get_keyingtries; + this->public.get_dh_group = (diffie_hellman_group_t(*)(connection_t*)) get_dh_group; + this->public.check_dh_group = (bool(*)(connection_t*,diffie_hellman_group_t)) check_dh_group; + this->public.get_soft_lifetime = (u_int32_t (*) (connection_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (connection_t *))get_hard_lifetime; + this->public.get_ref = (void(*)(connection_t*))get_ref; + this->public.destroy = (void(*)(connection_t*))destroy; + + /* private variables */ + this->refcount = 1; + this->name = strdup(name); + this->ikev2 = ikev2; + this->cert_policy = cert_policy; + this->certreq_policy = certreq_policy; + this->my_host = my_host; + this->other_host = other_host; + this->dpd_delay = dpd_delay; + this->reauth = reauth; + this->keyingtries = keyingtries; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + + this->proposals = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/connections/connection.h b/src/charon/config/connections/connection.h new file mode 100644 index 000000000..d0788876f --- /dev/null +++ b/src/charon/config/connections/connection.h @@ -0,0 +1,292 @@ +/** + * @file connection.h + * + * @brief Interface of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +typedef enum cert_policy_t cert_policy_t; +typedef struct connection_t connection_t; + +#include <library.h> +#include <utils/host.h> +#include <utils/linked_list.h> +#include <utils/identification.h> +#include <config/proposal.h> +#include <crypto/diffie_hellman.h> + + +/** + * Certificate sending policy. This is also used for certificate + * requests when using this definition for the other peer. If + * it is CERT_NEVER_SEND, a certreq is omitted, otherwise its + * included. + * + * @ingroup config + * + * @warning These definitions must be the same as in pluto/starter, + * as they are sent over the stroke socket. + */ +enum cert_policy_t { + /** always send certificates, even when not requested */ + CERT_ALWAYS_SEND = 0, + /** send certificate upon cert request */ + CERT_SEND_IF_ASKED = 1, + /** never send a certificate, even when requested */ + CERT_NEVER_SEND = 2, +}; + +/** + * enum strings for cert_policy_t + * + * @ingroup config + */ +extern enum_name_t *cert_policy_names; + +/** + * @brief A connection_t defines the rules to set up an IKE_SA. + * + * @b Constructors: + * - connection_create() + * + * @ingroup config + */ +struct connection_t { + + /** + * @brief Get my address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_my_host) (connection_t *this); + + /** + * @brief Get others address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_other_host) (connection_t *this); + + /** + * @brief Returns a list of all supported proposals. + * + * Returned list and its proposals must be destroyed after usage. + * + * @param this calling object + * @return list containing all the proposals + */ + linked_list_t *(*get_proposals) (connection_t *this); + + /** + * @brief Adds a proposal to the list. + * + * The first added proposal has the highest priority, the last + * added the lowest. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (connection_t *this, proposal_t *proposal); + + /** + * @brief Select a proposed from suggested proposals. + * + * Returned proposal must be destroyed after usage. + * + * @param this calling object + * @param proposals list of proposals to select from + * @return selected proposal, or NULL if none matches. + */ + proposal_t *(*select_proposal) (connection_t *this, linked_list_t *proposals); + + /** + * @brief Get the DPD check interval. + * + * @param this calling object + * @return dpd_delay in seconds + */ + u_int32_t (*get_dpd_delay) (connection_t *this); + + /** + * @brief Should a full reauthentication be done instead of rekeying? + * + * @param this calling object + * @return TRUE to use full reauthentication + */ + bool (*get_reauth) (connection_t *this); + + /** + * @brief Get the max number of retransmission sequences. + * + * @param this calling object + * @return max number of retransmission sequences + */ + u_int32_t (*get_keyingtries) (connection_t *this); + + /** + * @brief Get the connection name. + * + * Name must not be freed, since it points to + * internal data. + * + * @param this calling object + * @return name of the connection + */ + char* (*get_name) (connection_t *this); + + /** + * @brief Check if the connection is marked as an IKEv2 connection. + * + * Since all connections (IKEv1+2) are loaded, but charon handles + * only those marked with IKEv2, this flag can tell us if we must + * ignore a connection on initiaton. Then pluto will do it for us. + * + * @param this calling object + * @return - TRUE, if this is an IKEv2 connection + */ + bool (*is_ikev2) (connection_t *this); + + /** + * @brief Should be sent a certificate request for this connection? + * + * A certificate request contains serials of our trusted CA certificates. + * This flag says if such a request is sent on connection setup to + * the peer. It should be omitted when CERT_SEND_NEVER, sended otherwise. + * + * @param this calling object + * @return certificate request sending policy + */ + cert_policy_t (*get_certreq_policy) (connection_t *this); + + /** + * @brief Should be sent a certificate for this connection? + * + * Return the policy used to send the certificate. + * + * @param this calling object + * @return certificate sending policy + */ + cert_policy_t (*get_cert_policy) (connection_t *this); + + /** + * @brief Get the DH group to use for connection initialization. + * + * @param this calling object + * @return dh group to use for initialization + */ + diffie_hellman_group_t (*get_dh_group) (connection_t *this); + + /** + * @brief Check if a suggested dh group is acceptable. + * + * If we guess a wrong DH group for IKE_SA_INIT, the other + * peer will send us a offer. But is this acceptable for us? + * + * @param this calling object + * @return TRUE if group acceptable + */ + bool (*check_dh_group) (connection_t *this, diffie_hellman_group_t dh_group); + + /** + * @brief Get the lifetime of a connection, before IKE_SA rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (connection_t *this); + + /** + * @brief Get the lifetime of a connection, before IKE_SA gets deleted. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (connection_t *this); + + /** + * @brief Get a new reference to this connection. + * + * Get a new reference to this connection by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (connection_t *this); + + /** + * @brief Destroys a connection_t object. + * + * Decrements the internal reference counter and + * destroys the connection when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (connection_t *this); +}; + +/** + * @brief Creates a connection_t object. + * + * Supplied hosts become owned by connection, so + * do not modify or destroy them after a call to + * connection_create(). Name gets cloned internally. + * The retrasmit sequence number says how fast we give up when the peer + * does not respond. A high value may bridge-over temporary connection + * problems, a small value can detect dead peers faster. + * + * @param name connection identifier + * @param ikev2 TRUE if this is an IKEv2 connection + * @param cert_policy certificate send policy + * @param cert_req_policy certificate request send policy + * @param my_host host_t representing local address + * @param other_host host_t representing remote address + * @param dpd_delay interval of DPD liveness checks + * @param reauth use full reauthentication instead of rekeying + * @param keyingtries number of retransmit sequences to use + * @param hard_lifetime lifetime before deleting an IKE_SA + * @param soft_lifetime lifetime before rekeying an IKE_SA + * @param jitter range of randomization time + * @return connection_t object. + * + * @ingroup config + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_pol, cert_policy_t req_pol, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter); + +#endif /* CONNECTION_H_ */ diff --git a/src/charon/config/connections/connection_store.h b/src/charon/config/connections/connection_store.h new file mode 100755 index 000000000..70f209d3b --- /dev/null +++ b/src/charon/config/connections/connection_store.h @@ -0,0 +1,118 @@ +/** + * @file connection_store.h + * + * @brief Interface connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef CONNECTION_STORE_H_ +#define CONNECTION_STORE_H_ + +typedef struct connection_store_t connection_store_t; + +#include <library.h> +#include <config/connections/connection.h> +#include <utils/iterator.h> + +/** + * @brief The interface for a store of connection_t's. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct connection_store_t { + + /** + * @brief Returns a connection definition identified by two hosts. + * + * This call is usefull to get a connection identified by addresses. + * It may be used after kernel request for traffic protection. + * The returned connection gets created/cloned and therefore must + * be destroyed after usage. + * + * @param this calling object + * @param my_id own address of connection + * @param other_id others address of connection + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_hosts)(connection_store_t *this, + host_t *my_host, host_t *other_host); + + /** + * @brief Returns a connection identified by its name. + * + * This call is usefull to get a connection identified its + * name, as on an connection setup. + * + * @param this calling object + * @param name name of the connection to get + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_name) (connection_store_t *this, char *name); + + /** + * @brief Add a connection to the store. + * + * After a successful call, the connection is owned by the store and may + * not be manipulated nor destroyed. + * + * @param this calling object + * @param connection connection to add + * @return + * - SUCCESS, or + * - FAILED + */ + status_t (*add_connection) (connection_store_t *this, connection_t *connection); + + /** + * @brief Delete a connection from the store. + * + * Remove a connection from the connection store, identified + * by the connections name. + * + * @param this calling object + * @param name name of the connection to delete + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_connection) (connection_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored connections. + * + * @param this calling object + * @return iterator over all stored connections + */ + iterator_t* (*create_iterator) (connection_store_t *this); + + /** + * @brief Destroys a connection_store_t object. + * + * @param this calling object + */ + void (*destroy) (connection_store_t *this); +}; + +#endif /* CONNECTION_STORE_H_ */ diff --git a/src/charon/config/connections/local_connection_store.c b/src/charon/config/connections/local_connection_store.c new file mode 100644 index 000000000..df4ec230a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.c @@ -0,0 +1,237 @@ +/** + * @file local_connection_store.c + * + * @brief Implementation of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> + +#include "local_connection_store.h" + +#include <daemon.h> +#include <utils/linked_list.h> + + +typedef struct private_local_connection_store_t private_local_connection_store_t; + +/** + * Private data of an local_connection_store_t object + */ +struct private_local_connection_store_t { + + /** + * Public part + */ + local_connection_store_t public; + + /** + * stored connection + */ + linked_list_t *connections; + + /** + * Mutex to exclusivly access connection list + */ + pthread_mutex_t mutex; +}; + + +/** + * Implementation of connection_store_t.get_connection_by_hosts. + */ +static connection_t *get_connection_by_hosts(private_local_connection_store_t *this, host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ADDR_ANY= 0x01, + PRIO_ADDR_MATCH= 0x02 + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + connection_t *candidate; + connection_t *found = NULL; + + DBG2(DBG_CFG, "looking for connection for host pair %H...%H", + my_host, other_host); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + /* determine closest matching connection */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + host_t *candidate_my_host; + host_t *candidate_other_host; + + candidate_my_host = candidate->get_my_host(candidate); + candidate_other_host = candidate->get_other_host(candidate); + + /* my_host addresses must match*/ + if (my_host->ip_equals(my_host, candidate_my_host)) + { + prio_t prio = PRIO_UNDEFINED; + + /* exact match of peer host address or wildcard address? */ + if (other_host->ip_equals(other_host, candidate_other_host)) + { + prio |= PRIO_ADDR_MATCH; + } + else if (candidate_other_host->is_anyaddr(candidate_other_host)) + { + prio |= PRIO_ADDR_ANY; + } + + DBG2(DBG_CFG, "candidate connection \"%s\": %H...%H (prio=%d)", + candidate->get_name(candidate), + candidate_my_host, candidate_other_host, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG2(DBG_CFG, "found matching connection \"%s\": %H...%H (prio=%d)", + found->get_name(found), found->get_my_host(found), + found->get_other_host(found), best_prio); + + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of connection_store_t.get_connection_by_name. + */ +static connection_t *get_connection_by_name(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current, *found = NULL; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (strcmp(name, current->get_name(current)) == 0) + { + found = current; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + if (found) + { + /* get a new reference for it */ + found->get_ref(found); + } + return found; +} + +/** + * Implementation of connection_store_t.delete_connection. + */ +static status_t delete_connection(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove connection from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of connection_store_t.add_connection. + */ +static status_t add_connection(private_local_connection_store_t *this, connection_t *connection) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->insert_last(this->connections, connection); + pthread_mutex_unlock(&(this->mutex)); + return SUCCESS; +} + +/** + * Implementation of connection_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_connection_store_t *this) +{ + return this->connections->create_iterator_locked(this->connections, + &this->mutex); +} + +/** + * Implementation of connection_store_t.destroy. + */ +static void destroy (private_local_connection_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->destroy_offset(this->connections, offsetof(connection_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_connection_store_t * local_connection_store_create(void) +{ + private_local_connection_store_t *this = malloc_thing(private_local_connection_store_t); + + this->public.connection_store.get_connection_by_hosts = (connection_t*(*)(connection_store_t*,host_t*,host_t*))get_connection_by_hosts; + this->public.connection_store.get_connection_by_name = (connection_t*(*)(connection_store_t*,char*))get_connection_by_name; + this->public.connection_store.delete_connection = (status_t(*)(connection_store_t*,char*))delete_connection; + this->public.connection_store.add_connection = (status_t(*)(connection_store_t*,connection_t*))add_connection; + this->public.connection_store.create_iterator = (iterator_t*(*)(connection_store_t*))create_iterator; + this->public.connection_store.destroy = (void(*)(connection_store_t*))destroy; + + /* private variables */ + this->connections = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/connections/local_connection_store.h b/src/charon/config/connections/local_connection_store.h new file mode 100644 index 000000000..e78ed809a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.h @@ -0,0 +1,62 @@ +/** + * @file local_connection_store.h + * + * @brief Interface of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_CONNECTION_H_ +#define LOCAL_CONNECTION_H_ + +typedef struct local_connection_store_t local_connection_store_t; + +#include <library.h> +#include <config/connections/connection_store.h> + +/** + * @brief A connection_store_t implementation using a simple connection list. + * + * The local_connection_store_t class implements the connection_store_t interface + * as simple as possible. connection_t's are stored in an in-memory list. + * + * @b Constructors: + * - local_connection_store_create() + * + * @todo Make thread-save first + * @todo Add remove_connection method + * + * @ingroup config + */ +struct local_connection_store_t { + + /** + * Implements connection_store_t interface + */ + connection_store_t connection_store; +}; + +/** + * @brief Creates a local_connection_store_t instance. + * + * @return connection store instance. + * + * @ingroup config + */ +local_connection_store_t * local_connection_store_create(void); + +#endif /* LOCAL_CONNECTION_H_ */ diff --git a/src/charon/config/credentials/local_credential_store.c b/src/charon/config/credentials/local_credential_store.c new file mode 100644 index 000000000..b7b71b9e7 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.c @@ -0,0 +1,1363 @@ +/** + * @file local_credential_store.c + * + * @brief Implementation of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <sys/stat.h> +#include <dirent.h> +#include <string.h> +#include <pthread.h> + +#include <library.h> +#include <utils/lexparser.h> +#include <utils/linked_list.h> +#include <crypto/rsa/rsa_public_key.h> +#include <crypto/certinfo.h> +#include <crypto/x509.h> +#include <crypto/ca.h> +#include <crypto/crl.h> +#include <asn1/ttodata.h> + +#include "local_credential_store.h" + +#define PATH_BUF 256 +#define MAX_CA_PATH_LEN 7 + +typedef struct shared_key_t shared_key_t; + +/** + * Private date of a shared_key_t object + */ +struct shared_key_t { + + /** + * shared secret + */ + chunk_t secret; + + /** + * list of peer IDs + */ + linked_list_t *peers; +}; + + +/** + * Implementation of shared_key_t.destroy. + */ +static void shared_key_destroy(shared_key_t *this) +{ + this->peers->destroy_offset(this->peers, offsetof(identification_t, destroy)); + chunk_free(&this->secret); + free(this); +} + +/** + * @brief Creates a shared_key_t object. + * + * @param shared_key shared key value + * @return shared_key_t object + * + * @ingroup config + */ +static shared_key_t *shared_key_create(chunk_t secret) +{ + shared_key_t *this = malloc_thing(shared_key_t); + + /* private data */ + this->secret = chunk_clone(secret); + this->peers = linked_list_create(); + + return (this); +} + +/* ------------------------------------------------------------------------ * + * the ca_info_t object as a central control element + ++--------------------------------------------------------+ +| local_credential_store_t | ++--------------------------------------------------------+ + | | ++---------------------------+ +-------------------------+ +| linked_list_t *auth_certs | | linked_list_t *ca_infos | ++---------------------------+ +-------------------------+ + | | + | +------------------------- + + | | ca_info_t | + | +--------------------------+ ++---------------+ | char *name | +| x509_t |<--| x509_t *cacert | +----------------------+ ++---------------+ | linked_list_t *certinfos |-->| certinfo_t | +| chunk_t keyid | | linked_list_t *ocspuris | +----------------------+ ++---------------+ | crl_t *crl | | chunk_t serialNumber | + | | linked_list_t *crluris | | cert_status_t status | + | | pthread_mutex_t mutex | | time_t thisUpdate | ++---------------+ +--------------------------+ | time_t nextUpdate | +| x509_t | | | bool once | ++---------------+ | +----------------------+ +| chunk_t keyid | | | ++---------------+ +------------------------- + +----------------------+ + | | ca_info_t | | certinfo_t | + | +--------------------------+ +----------------------+ ++---------------+ | char *name | | chunk_t serialNumber | +| x509_t |<--| x509_t *cacert | | cert_status_t status | ++---------------+ | linked_list_t *certinfos | | time_t thisUpdate | +| chunk_t keyid | | linked_list_t *ocspuris | | time_t nextUpdate | ++---------------+ | crl_t *crl | | bool once | + | | linked_list_t *crluris | +----------------------+ + | | pthread_mutex_t mutex; | | + | +--------------------------+ + | | + + * ------------------------------------------------------------------------ */ + +typedef struct private_local_credential_store_t private_local_credential_store_t; + +/** + * Private data of an local_credential_store_t object + */ +struct private_local_credential_store_t { + + /** + * Public part + */ + local_credential_store_t public; + + /** + * list of shared keys + */ + linked_list_t *shared_keys; + + /** + * list of EAP keys + */ + linked_list_t *eap_keys; + + /** + * list of key_entry_t's with private keys + */ + linked_list_t *private_keys; + + /** + * list of X.509 certificates with public keys + */ + linked_list_t *certs; + + /** + * list of X.509 authority certificates with public keys + */ + linked_list_t *auth_certs; + + /** + * list of X.509 CA information records + */ + linked_list_t *ca_infos; + + /** + * enforce strict crl policy + */ + bool strict; +}; + + +/** + * Get a key from a list with shared_key_t's + */ +static status_t get_key(linked_list_t *keys, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ANY_MATCH= 0x01, + PRIO_MY_MATCH= 0x02, + PRIO_OTHER_MATCH= 0x04, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + chunk_t found = chunk_empty; + shared_key_t *shared_key; + + iterator_t *iterator = keys->create_iterator(keys, TRUE); + + while (iterator->iterate(iterator, (void**)&shared_key)) + { + iterator_t *peer_iterator; + identification_t *peer_id; + prio_t prio = PRIO_UNDEFINED; + + peer_iterator = shared_key->peers->create_iterator(shared_key->peers, TRUE); + + if (peer_iterator->get_count(peer_iterator) == 0) + { + /* this is a wildcard shared key */ + prio = PRIO_ANY_MATCH; + } + else + { + while (peer_iterator->iterate(peer_iterator, (void**)&peer_id)) + { + if (my_id->equals(my_id, peer_id)) + { + prio |= PRIO_MY_MATCH; + } + if (other_id->equals(other_id, peer_id)) + { + prio |= PRIO_OTHER_MATCH; + } + } + } + peer_iterator->destroy(peer_iterator); + + if (prio > best_prio) + { + best_prio = prio; + found = shared_key->secret; + } + } + iterator->destroy(iterator); + + if (best_prio == PRIO_UNDEFINED) + { + return NOT_FOUND; + } + else + { + *secret = chunk_clone(found); + return SUCCESS; + } +} + + +/** + * Implementation of local_credential_store_t.get_shared_key. + */ +static status_t get_shared_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->shared_keys, my_id, other_id, secret); +} + +/** + * Implementation of local_credential_store_t.get_eap_key. + */ +static status_t get_eap_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->eap_keys, my_id, other_id, secret); +} + +/** + * Implementation of credential_store_t.get_certificate. + */ +static x509_t* get_certificate(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->certs->create_iterator(this->certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (id->equals(id, current_cert->get_subject(current_cert)) || + current_cert->equals_subjectAltName(current_cert, id)) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.get_rsa_public_key. + */ +static rsa_public_key_t *get_rsa_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *cert = get_certificate(this, id); + + return (cert == NULL)? NULL:cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_trusted_public_key. + */ +static rsa_public_key_t *get_trusted_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + cert_status_t status; + err_t ugh; + + x509_t *cert = get_certificate(this, id); + + if (cert == NULL) + return NULL; + + ugh = cert->is_valid(cert, NULL); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return NULL; + } + + status = cert->get_status(cert); + if (status == CERT_REVOKED || status == CERT_UNTRUSTED || (this->strict && status != CERT_GOOD)) + { + DBG1(DBG_CFG, "certificate status: %N", cert_status_names, status); + return NULL; + } + if (status == CERT_GOOD && cert->get_until(cert) < time(NULL)) + { + DBG1(DBG_CFG, "certificate is good but crl is stale"); + return NULL; + } + + return cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_rsa_private_key. + */ +static rsa_private_key_t *get_rsa_private_key(private_local_credential_store_t *this, + rsa_public_key_t *pubkey) +{ + rsa_private_key_t *found = NULL, *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = current->clone(current); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.has_rsa_private_key. + */ +static bool has_rsa_private_key(private_local_credential_store_t *this, rsa_public_key_t *pubkey) +{ + bool found = FALSE; + rsa_private_key_t *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of credential_store_t.get_auth_certificate. + */ +static x509_t* get_auth_certificate(private_local_credential_store_t *this, + u_int auth_flags, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (current_cert->has_authority_flag(current_cert, auth_flags) + && id->equals(id, current_cert->get_subject(current_cert))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_ca_certificate_by_keyid. + */ +static x509_t* get_ca_certificate_by_keyid(private_local_credential_store_t *this, + chunk_t keyid) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + rsa_public_key_t *pubkey = current_cert->get_public_key(current_cert); + + if (current_cert->has_authority_flag(current_cert, AUTH_CA) + && chunk_equals(keyid, pubkey->get_keyid(pubkey))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_issuer. + */ +static ca_info_t* get_issuer(private_local_credential_store_t *this, const x509_t *cert) +{ + ca_info_t *found = NULL; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_cert_issuer(ca_info, cert)) + { + found = ca_info; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Find an exact copy of a certificate in a linked list + */ +static x509_t* find_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = NULL, *current_cert; + + iterator_t *iterator = certs->create_iterator(certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (cert->equals(cert, current_cert)) + { + found_cert = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found_cert; +} + +/** + * Adds crl and ocsp uris to the corresponding issuer info record + */ +static void add_uris(ca_info_t *issuer, x509_t *cert) +{ + iterator_t *iterator; + identification_t *uri; + + /* add any crl distribution points to the issuer ca info record */ + iterator = cert->create_crluri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_crluri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); + + /* add any ocsp access points to the issuer ca info record */ + iterator = cert->create_ocspuri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_ocspuri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); +} + +/** + * Implementation of credential_store_t.is_trusted + */ +static bool is_trusted(private_local_credential_store_t *this, x509_t *cert) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + x509_t *cert_to_be_trusted = cert; + + DBG2(DBG_CFG, "establishing trust in certificate:"); + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG2(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG2(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG2(DBG_CFG, "reached self-signed root ca"); + cert_to_be_trusted->set_until(cert_to_be_trusted, until); + cert_to_be_trusted->set_status(cert_to_be_trusted, CERT_GOOD); + return TRUE; + } + else + { + /* go up one step in the trust chain */ + cert = issuer_cert; + } + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Implementation of credential_store_t.verify. + */ +static bool verify(private_local_credential_store_t *this, x509_t *cert, bool *found) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + + x509_t *end_cert = cert; + x509_t *cert_copy = find_certificate(this->certs, end_cert); + + DBG2(DBG_CFG, "verifying end entity certificate:"); + + *found = (cert_copy != NULL); + if (*found) + { + DBG2(DBG_CFG, + "end entitity certificate is already in credential store"); + } + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG1(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG1(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG1(DBG_CFG, "reached self-signed root ca"); + + /* set the definite status and trust interval of the end entity certificate */ + end_cert->set_until(end_cert, until); + if (cert_copy) + { + cert_copy->set_status(cert_copy, end_cert->get_status(end_cert)); + cert_copy->set_until(cert_copy, until); + } + return TRUE; + } + else + { + time_t nextUpdate; + cert_status_t status; + certinfo_t *certinfo = certinfo_create(cert->get_serialNumber(cert)); + + certinfo->set_nextUpdate(certinfo, until); + + if (pathlen == 0) + { + /* add any crl and ocsp uris contained in the certificate under test */ + add_uris(issuer, cert); + } + + /* first check certificate revocation using ocsp */ + status = issuer->verify_by_ocsp(issuer, certinfo, &this->public.credential_store); + + /* if ocsp service is not available then fall back to crl */ + if ((status == CERT_UNDEFINED) || (status == CERT_UNKNOWN && this->strict)) + { + status = issuer->verify_by_crl(issuer, certinfo, CRL_DIR); + } + + nextUpdate = certinfo->get_nextUpdate(certinfo); + cert->set_status(cert, status); + + switch (status) + { + case CERT_GOOD: + /* set nextUpdate */ + cert->set_until(cert, nextUpdate); + + /* if status information is stale */ + if (this->strict && nextUpdate < time(NULL)) + { + DBG2(DBG_CFG, "certificate is good but status is stale"); + certinfo->destroy(certinfo); + return FALSE; + } + DBG1(DBG_CFG, "certificate is good"); + + /* with strict crl policy the public key must have the same + * lifetime as the validity of the ocsp status or crl lifetime + */ + if (this->strict && nextUpdate < until) + until = nextUpdate; + break; + case CERT_REVOKED: + { + time_t revocationTime = certinfo->get_revocationTime(certinfo); + DBG1(DBG_CFG, + "certificate was revoked on %T, reason: %N", + &revocationTime, crl_reason_names, + certinfo->get_revocationReason(certinfo)); + + /* set revocationTime */ + cert->set_until(cert, revocationTime); + + /* update status of end certificate in the credential store */ + if (cert_copy) + { + if (pathlen > 0) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + else + { + cert_copy->set_status(cert_copy, CERT_REVOKED); + cert_copy->set_until(cert_copy, + certinfo->get_revocationTime(certinfo)); + } + } + certinfo->destroy(certinfo); + return FALSE; + } + case CERT_UNKNOWN: + case CERT_UNDEFINED: + default: + DBG1(DBG_CFG, "certificate status unknown"); + if (this->strict) + { + /* update status of end certificate in the credential store */ + if (cert_copy) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + certinfo->destroy(certinfo); + return FALSE; + } + break; + } + certinfo->destroy(certinfo); + } + /* go up one step in the trust chain */ + cert = issuer_cert; + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Add a unique certificate to a linked list + */ +static x509_t* add_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = find_certificate(certs, cert); + + if (found_cert) + { + /* add the authority flags */ + found_cert->add_authority_flags(found_cert, cert->get_authority_flags(cert)); + + cert->destroy(cert); + return found_cert; + } + else + { + certs->insert_last(certs, (void*)cert); + return cert; + } +} + +/** + * Add a unique ca info record to a linked list + */ +static void add_ca_info(private_local_credential_store_t *this, ca_info_t *ca_info) +{ + ca_info_t *current_ca_info; + ca_info_t *found_ca_info = NULL; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_ca_info)) + { + if (current_ca_info->equals(current_ca_info, ca_info)) + { + found_ca_info = current_ca_info; + break; + } + } + iterator->destroy(iterator); + + if (found_ca_info) + { + current_ca_info->add_info(current_ca_info, ca_info); + ca_info->destroy(ca_info); + } + else + { + this->ca_infos->insert_last(this->ca_infos, (void*)ca_info); + } +} + +/** + * Release ca info record of a given name + */ +static status_t release_ca_info(private_local_credential_store_t *this, const char *name) +{ + status_t status = NOT_FOUND; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->equals_name_release_info(ca_info, name)) + { + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implements local_credential_store_t.add_end_certificate + */ +static x509_t* add_end_certificate(private_local_credential_store_t *this, x509_t *cert) +{ + x509_t *ret_cert = add_certificate(this->certs, cert); + + /* add crl and ocsp uris the first time the certificate is added */ + if (ret_cert == cert) + { + ca_info_t *issuer = get_issuer(this, cert); + + if (issuer) + { + add_uris(issuer, cert); + } + } + return ret_cert; +} + +/** + * Implements local_credential_store_t.add_auth_certificate + */ +static x509_t* add_auth_certificate(private_local_credential_store_t *this, x509_t *cert, u_int auth_flags) +{ + cert->add_authority_flags(cert, auth_flags); + return add_certificate(this->auth_certs, cert); +} + +/** + * Implements local_credential_store_t.create_cert_iterator + */ +static iterator_t* create_cert_iterator(private_local_credential_store_t *this) +{ + return this->certs->create_iterator(this->certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cacert_iterator + */ +static iterator_t* create_auth_cert_iterator(private_local_credential_store_t *this) +{ + return this->auth_certs->create_iterator(this->auth_certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cainfo_iterator + */ +static iterator_t* create_cainfo_iterator(private_local_credential_store_t *this) +{ + return this->ca_infos->create_iterator(this->ca_infos, TRUE); +} + +/** + * Implements local_credential_store_t.load_auth_certificates + */ +static void load_auth_certificates(private_local_credential_store_t *this, + u_int auth_flag, + const char* label, + const char* path) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + + DBG1(DBG_CFG, "loading %s certificates from '%s/'", label, path); + + dir = opendir(path); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening %s certs directory %s'", label, path); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", path, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + x509_t *cert = x509_create_from_file(file, label); + + if (cert) + { + err_t ugh = cert->is_valid(cert, NULL); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "warning: %s certificate %s", label, ugh); + } + + if (auth_flag == AUTH_CA && !cert->is_ca(cert)) + { + DBG1(DBG_CFG, " CA basic constraints flag not set, cert discarded"); + cert->destroy(cert); + } + else + { + x509_t *ret_cert; + + cert->add_authority_flags(cert, auth_flag); + + ret_cert = add_certificate(this->auth_certs, cert); + + if (auth_flag == AUTH_CA && ret_cert == cert) + { + ca_info_t *ca_info = ca_info_create(NULL, cert); + + add_ca_info(this, ca_info); + } + } + } + } + } + closedir(dir); +} + +/** + * Implements local_credential_store_t.load_ca_certificates + */ +static void load_ca_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_CA, "ca", CA_CERTIFICATE_DIR); + + /* add any crl and ocsp uris found in the ca certificates to the + * corresponding issuer info record. We can do this only after all + * ca certificates have been loaded and the ca hierarchy is known. + */ + { + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + x509_t *cacert = ca_info->get_certificate(ca_info); + ca_info_t *issuer = get_issuer(this, cacert); + + if (issuer) + { + add_uris(issuer, cacert); + } + } + iterator->destroy(iterator); + } +} + +/** + * Implements local_credential_store_t.load_ocsp_certificates + */ +static void load_ocsp_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_OCSP, "ocsp", OCSP_CERTIFICATE_DIR); +} + +/** + * Add the latest crl to the issuing ca + */ +static void add_crl(private_local_credential_store_t *this, crl_t *crl, const char *path) +{ + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + bool found = FALSE; + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_crl_issuer(ca_info, crl)) + { + char buffer[BUF_LEN]; + chunk_t uri = { buffer, 7 + strlen(path) }; + + ca_info->add_crl(ca_info, crl); + if (uri.len < BUF_LEN) + { + snprintf(buffer, BUF_LEN, "file://%s", path); + ca_info->add_crluri(ca_info, uri); + } + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (!found) + { + crl->destroy(crl); + DBG2(DBG_CFG, " no issuing ca found for this crl - discarded"); + } +} + +/** + * Implements local_credential_store_t.load_crls + */ +static void load_crls(private_local_credential_store_t *this) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + crl_t *crl; + + DBG1(DBG_CFG, "loading crls from '%s/'", CRL_DIR); + + dir = opendir(CRL_DIR); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening crl directory %s'", CRL_DIR); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", CRL_DIR, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + crl = crl_create_from_file(file); + if (crl) + { + DBG1(DBG_CFG, " crl is %s", crl->is_valid(crl)? "valid":"stale"); + add_crl(this, crl, file); + } + } + } + closedir(dir); +} + +/** + * 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 */ + if (raw_secret.len > secret->len) + return "secret larger than buffer"; + memcpy(secret->ptr, raw_secret.ptr, raw_secret.len); + secret->len = raw_secret.len; + } + else + { /* convert from HEX or Base64 to binary */ + size_t len; + err_t ugh = ttodata(raw_secret.ptr, raw_secret.len, 0, secret->ptr, secret->len, &len); + + if (ugh != NULL) + return ugh; + if (len > secret->len) + return "secret larger than buffer"; + secret->len = len; + } + return NULL; +} + +/** + * Implements local_credential_store_t.load_secrets + */ +static void load_secrets(private_local_credential_store_t *this) +{ + FILE *fd = fopen(SECRETS_FILE, "r"); + + if (fd) + { + int bytes; + int line_nr = 0; + chunk_t chunk, src, line; + + DBG1(DBG_CFG, "loading secrets from \"%s\"", SECRETS_FILE); + + 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; + + while (fetchline(&src, &line)) + { + chunk_t ids, token; + bool is_eap = FALSE; + + line_nr++; + + if (!eat_whitespace(&line)) + { + continue; + } + if (!extract_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)) + { + char path[PATH_BUF]; + chunk_t filename; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + chunk_t *passphrase = NULL; + + rsa_private_key_t *key; + + 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 (secret.len > 0) + passphrase = &secret; + } + key = rsa_private_key_create_from_file(path, passphrase); + if (key) + { + this->private_keys->insert_last(this->private_keys, (void*)key); + } + } + else if ( match("PSK", &token) || + ((match("EAP", &token) || match("XAUTH", &token)) && (is_eap = TRUE))) + { + shared_key_t *shared_key; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + + err_t ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh); + goto error; + } + + DBG1(DBG_CFG, " loading %s key for %s", + is_eap ? "EAP" : "shared", + ids.len > 0 ? (char*)ids.ptr : "%any"); + + DBG4(DBG_CFG, " secret:", secret); + + shared_key = shared_key_create(secret); + if (shared_key) + { + if (is_eap) + { + this->eap_keys->insert_last(this->eap_keys, (void*)shared_key); + } + else + { + this->shared_keys->insert_last(this->shared_keys, (void*)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->peers->insert_last(shared_key->peers, (void*)peer_id); + } + } + else if (match("PIN", &token)) + { + + } + else + { + DBG1(DBG_CFG, "line %d: token must be either " + "RSA, PSK, EAP, or PIN", line_nr, token.len); + goto error; + } + } +error: + free(chunk.ptr); + } + else + { + DBG1(DBG_CFG, "could not open file '%s'", SECRETS_FILE); + } +} + +/** + * Implementation of local_credential_store_t.destroy. + */ +static void destroy(private_local_credential_store_t *this) +{ + this->certs->destroy_offset(this->certs, offsetof(x509_t, destroy)); + this->auth_certs->destroy_offset(this->auth_certs, offsetof(x509_t, destroy)); + this->ca_infos->destroy_offset(this->ca_infos, offsetof(ca_info_t, destroy)); + this->private_keys->destroy_offset(this->private_keys, offsetof(rsa_private_key_t, destroy)); + this->shared_keys->destroy_function(this->shared_keys, (void*)shared_key_destroy); + this->eap_keys->destroy_function(this->eap_keys, (void*)shared_key_destroy); + free(this); +} + +/** + * Described in header. + */ +local_credential_store_t * local_credential_store_create(bool strict) +{ + private_local_credential_store_t *this = malloc_thing(private_local_credential_store_t); + + this->public.credential_store.get_shared_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_shared_key; + this->public.credential_store.get_eap_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_eap_key; + this->public.credential_store.get_rsa_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_rsa_public_key; + this->public.credential_store.get_rsa_private_key = (rsa_private_key_t* (*) (credential_store_t*,rsa_public_key_t*))get_rsa_private_key; + this->public.credential_store.has_rsa_private_key = (bool (*) (credential_store_t*,rsa_public_key_t*))has_rsa_private_key; + this->public.credential_store.get_trusted_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_trusted_public_key; + this->public.credential_store.get_certificate = (x509_t* (*) (credential_store_t*,identification_t*))get_certificate; + this->public.credential_store.get_auth_certificate = (x509_t* (*) (credential_store_t*,u_int,identification_t*))get_auth_certificate; + this->public.credential_store.get_ca_certificate_by_keyid = (x509_t* (*) (credential_store_t*,chunk_t))get_ca_certificate_by_keyid; + this->public.credential_store.get_issuer = (ca_info_t* (*) (credential_store_t*,const x509_t*))get_issuer; + this->public.credential_store.is_trusted = (bool (*) (credential_store_t*,x509_t*))is_trusted; + this->public.credential_store.verify = (bool (*) (credential_store_t*,x509_t*,bool*))verify; + this->public.credential_store.add_end_certificate = (x509_t* (*) (credential_store_t*,x509_t*))add_end_certificate; + this->public.credential_store.add_auth_certificate = (x509_t* (*) (credential_store_t*,x509_t*,u_int))add_auth_certificate; + this->public.credential_store.add_ca_info = (void (*) (credential_store_t*,ca_info_t*))add_ca_info; + this->public.credential_store.release_ca_info = (status_t (*) (credential_store_t*,const char*))release_ca_info; + this->public.credential_store.create_cert_iterator = (iterator_t* (*) (credential_store_t*))create_cert_iterator; + this->public.credential_store.create_auth_cert_iterator = (iterator_t* (*) (credential_store_t*))create_auth_cert_iterator; + this->public.credential_store.create_cainfo_iterator = (iterator_t* (*) (credential_store_t*))create_cainfo_iterator; + this->public.credential_store.load_ca_certificates = (void (*) (credential_store_t*))load_ca_certificates; + this->public.credential_store.load_ocsp_certificates = (void (*) (credential_store_t*))load_ocsp_certificates; + this->public.credential_store.load_crls = (void (*) (credential_store_t*))load_crls; + this->public.credential_store.load_secrets = (void (*) (credential_store_t*))load_secrets; + this->public.credential_store.destroy = (void (*) (credential_store_t*))destroy; + + /* private variables */ + this->shared_keys = linked_list_create(); + this->eap_keys = linked_list_create(); + this->private_keys = linked_list_create(); + this->certs = linked_list_create(); + this->auth_certs = linked_list_create(); + this->ca_infos = linked_list_create(); + this->strict = strict; + + return (&this->public); +} diff --git a/src/charon/config/credentials/local_credential_store.h b/src/charon/config/credentials/local_credential_store.h new file mode 100644 index 000000000..88a94d6f9 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.h @@ -0,0 +1,64 @@ +/** + * @file local_credential_store.h + * + * @brief Interface of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_CREDENTIAL_H_ +#define LOCAL_CREDENTIAL_H_ + +typedef struct local_credential_store_t local_credential_store_t; + +#include <library.h> +#include <credential_store.h> +#include <daemon.h> + + +/** + * @brief A credential_store_t implementation using simple credentail lists. + * + * The local_credential_store_t class implements the credential_store_t interface + * as simple as possible. The credentials are stored in lists, and are loaded from + * files on the disk. + * Shared secret are not handled yet, so get_shared_secret always returns NOT_FOUND. + * + * @b Constructors: + * - local_credential_store_create(bool strict) + * + * @ingroup config + */ +struct local_credential_store_t { + + /** + * Implements credential_store_t interface + */ + credential_store_t credential_store; +}; + +/** + * @brief Creates a local_credential_store_t instance. + * + * @param strict enforce a strict crl policy + * @return credential store instance. + * + * @ingroup config + */ +local_credential_store_t *local_credential_store_create(bool strict); + +#endif /* LOCAL_CREDENTIAL_H_ */ diff --git a/src/charon/config/policies/local_policy_store.c b/src/charon/config/policies/local_policy_store.c new file mode 100644 index 000000000..dd22b43a0 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.c @@ -0,0 +1,282 @@ +/** + * @file local_policy_store.c + * + * @brief Implementation of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> + +#include "local_policy_store.h" + +#include <daemon.h> +#include <utils/linked_list.h> + + +typedef struct private_local_policy_store_t private_local_policy_store_t; + +/** + * Private data of an local_policy_store_t object + */ +struct private_local_policy_store_t { + + /** + * Public part + */ + local_policy_store_t public; + + /** + * list of policy_t's + */ + linked_list_t *policies; + + /** + * Mutex to exclusivly access list + */ + pthread_mutex_t mutex; +}; + +/** + * Implementation of policy_store_t.add_policy. + */ +static void add_policy(private_local_policy_store_t *this, policy_t *policy) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->insert_last(this->policies, (void*)policy); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Check if a policy contains traffic selectors + */ +static bool contains_traffic_selectors(policy_t *policy, bool mine, + linked_list_t *ts, host_t *host) +{ + linked_list_t *selected; + bool contains = FALSE; + + if (mine) + { + selected = policy->select_my_traffic_selectors(policy, ts, host); + } + else + { + selected = policy->select_other_traffic_selectors(policy, ts, host); + } + if (selected->get_count(selected)) + { + contains = TRUE; + } + selected->destroy_offset(selected, offsetof(traffic_selector_t, destroy)); + return contains; +} + +/** + * Implementation of policy_store_t.get_policy. + */ +static policy_t *get_policy(private_local_policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED = 0x00, + PRIO_TS_MISMATCH = 0x01, + PRIO_ID_ANY = 0x02, + PRIO_ID_MATCH = PRIO_ID_ANY + MAX_WILDCARDS, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + policy_t *candidate; + policy_t *found = NULL; + traffic_selector_t *ts; + + DBG1(DBG_CFG, "searching policy for '%D'...'%D'", my_id, other_id); + iterator = my_ts->create_iterator(my_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " local TS: %R", ts); + } + iterator->destroy(iterator); + iterator = other_ts->create_iterator(other_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " remote TS: %R", ts); + } + iterator->destroy(iterator); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + + /* determine closest matching policy */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + identification_t *candidate_my_id; + identification_t *candidate_other_id; + int wildcards; + + candidate_my_id = candidate->get_my_id(candidate); + candidate_other_id = candidate->get_other_id(candidate); + + /* my_id is either %any or if set must match exactly */ + if (candidate_my_id->matches(candidate_my_id, my_id, &wildcards)) + { + prio_t prio = PRIO_UNDEFINED; + + /* wildcard match for other_id */ + if (!other_id->matches(other_id, candidate_other_id, &wildcards)) + { + continue; + } + prio = PRIO_ID_MATCH - wildcards; + + /* only accept if traffic selectors match */ + if (!contains_traffic_selectors(candidate, TRUE, my_ts, my_host) || + !contains_traffic_selectors(candidate, FALSE, other_ts, other_host)) + { + DBG2(DBG_CFG, "candidate '%s' inacceptable due traffic " + "selector mismatch", candidate->get_name(candidate)); + prio = PRIO_TS_MISMATCH; + } + + DBG2(DBG_CFG, "candidate policy '%s': '%D'...'%D' (prio=%d)", + candidate->get_name(candidate), + candidate_my_id, candidate_other_id, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG1(DBG_CFG, "found matching policy '%s': '%D'...'%D' (prio=%d)", + found->get_name(found), found->get_my_id(found), + found->get_other_id(found), best_prio); + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of policy_store_t.get_policy_by_name. + */ +static policy_t *get_policy_by_name(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current, *found = NULL; + + DBG2(DBG_CFG, "looking for policy '%s'", name); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + found = current; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + /* give out a new reference */ + found->get_ref(found); + return found; +} + +/** + * Implementation of policy_store_t.delete_policy. + */ +static status_t delete_policy(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove policy from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + /* we do not break here, as there may be multipe policies */ + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of policy_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_policy_store_t *this) +{ + return this->policies->create_iterator_locked(this->policies, + &this->mutex); +} + +/** + * Implementation of policy_store_t.destroy. + */ +static void destroy(private_local_policy_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->destroy_offset(this->policies, offsetof(policy_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_policy_store_t *local_policy_store_create(void) +{ + private_local_policy_store_t *this = malloc_thing(private_local_policy_store_t); + + this->public.policy_store.add_policy = (void (*) (policy_store_t*,policy_t*))add_policy; + this->public.policy_store.get_policy = (policy_t* (*) (policy_store_t*,identification_t*,identification_t*, + linked_list_t*,linked_list_t*,host_t*,host_t*))get_policy; + this->public.policy_store.get_policy_by_name = (policy_t* (*) (policy_store_t*,char*))get_policy_by_name; + this->public.policy_store.delete_policy = (status_t (*) (policy_store_t*,char*))delete_policy; + this->public.policy_store.create_iterator = (iterator_t* (*) (policy_store_t*))create_iterator; + this->public.policy_store.destroy = (void (*) (policy_store_t*))destroy; + + /* private variables */ + this->policies = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/policies/local_policy_store.h b/src/charon/config/policies/local_policy_store.h new file mode 100644 index 000000000..01d5d2d60 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.h @@ -0,0 +1,60 @@ +/** + * @file local_policy_store.h + * + * @brief Interface of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef LOCAL_POLICY_STORE_H_ +#define LOCAL_POLICY_STORE_H_ + +typedef struct local_policy_store_t local_policy_store_t; + +#include <library.h> +#include <config/policies/policy_store.h> + + +/** + * @brief A policy_store_t implementation using a simple policy lists. + * + * The local_policy_store_t class implements the policy_store_t interface + * as simple as possible. The policies are stored in a in-memory list. + * + * @b Constructors: + * - local_policy_store_create() + * + * @ingroup config + */ +struct local_policy_store_t { + + /** + * Implements policy_store_t interface + */ + policy_store_t policy_store; +}; + +/** + * @brief Creates a local_policy_store_t instance. + * + * @return policy store instance. + * + * @ingroup config + */ +local_policy_store_t *local_policy_store_create(void); + +#endif /* LOCAL_POLICY_STORE_H_ */ diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c new file mode 100644 index 000000000..363d1609f --- /dev/null +++ b/src/charon/config/policies/policy.c @@ -0,0 +1,635 @@ +/** + * @file policy.c + * + * @brief Implementation of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <time.h> +#include <string.h> +#include <unistd.h> + +#include "policy.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> + +ENUM(dpd_action_names, DPD_NONE, DPD_RESTART, + "DPD_NONE", + "DPD_CLEAR", + "DPD_ROUTE", + "DPD_RESTART" +); + +ENUM(mode_names, MODE_TRANSPORT, MODE_BEET, + "TRANSPORT", + "TUNNEL", + "2", + "3", + "BEET" +); + +typedef struct private_policy_t private_policy_t; + +/** + * Private data of an policy_t object + */ +struct private_policy_t { + + /** + * Public part + */ + policy_t public; + + /** + * Number of references hold by others to this policy + */ + refcount_t refcount; + + /** + * Name of the policy, used to query it + */ + char *name; + + /** + * id to use to identify us + */ + identification_t *my_id; + + /** + * allowed id for other + */ + identification_t *other_id; + + /** + * virtual IP to use locally + */ + host_t *my_virtual_ip; + + /** + * virtual IP to use remotly + */ + host_t *other_virtual_ip; + + /** + * Method to use for own authentication data + */ + auth_method_t auth_method; + + /** + * EAP type to use for peer authentication + */ + eap_type_t eap_type; + + /** + * we have a cert issued by this CA + */ + identification_t *my_ca; + + /** + * we require the other end to have a cert issued by this CA + */ + identification_t *other_ca; + + /** + * updown script + */ + char *updown; + + /** + * allow host access + */ + bool hostaccess; + + /** + * list for all proposals + */ + linked_list_t *proposals; + + /** + * list for traffic selectors for my site + */ + linked_list_t *my_ts; + + /** + * list for traffic selectors for others site + */ + linked_list_t *other_ts; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; + + /** + * What to do with an SA when other peer seams to be dead? + */ + bool dpd_action; + + /** + * Mode to propose for a initiated CHILD: tunnel/transport + */ + mode_t mode; +}; + +/** + * Implementation of policy_t.get_name + */ +static char *get_name(private_policy_t *this) +{ + return this->name; +} + +/** + * Implementation of policy_t.get_my_id + */ +static identification_t *get_my_id(private_policy_t *this) +{ + return this->my_id; +} + +/** + * Implementation of policy_t.get_other_id + */ +static identification_t *get_other_id(private_policy_t *this) +{ + return this->other_id; +} + +/** + * Implementation of policy_t.get_my_ca + */ +static identification_t *get_my_ca(private_policy_t *this) +{ + return this->my_ca; +} + +/** + * Implementation of policy_t.get_other_ca + */ +static identification_t *get_other_ca(private_policy_t *this) +{ + return this->other_ca; +} + +/** + * Implementation of connection_t.auth_method_t. + */ +static auth_method_t get_auth_method(private_policy_t *this) +{ + return this->auth_method; +} + +/** + * Implementation of connection_t.get_eap_type. + */ +static eap_type_t get_eap_type(private_policy_t *this) +{ + return this->eap_type; +} + +/** + * Get traffic selectors, with wildcard-address update + */ +static linked_list_t *get_traffic_selectors(private_policy_t *this, + linked_list_t *list, host_t *host) +{ + iterator_t *iterator; + traffic_selector_t *current; + linked_list_t *result = linked_list_create(); + + iterator = list->create_iterator(list, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + current = current->clone(current); + if (host) + { + current->set_address(current, host); + } + + result->insert_last(result, (void*)current); + } + iterator->destroy(iterator); + return result; +} + +/** + * Implementation of policy_t.get_my_traffic_selectors + */ +static linked_list_t *get_my_traffic_selectors(private_policy_t *this, host_t *me) +{ + return get_traffic_selectors(this, this->my_ts, me); +} + +/** + * Implementation of policy_t.get_other_traffic_selectors + */ +static linked_list_t *get_other_traffic_selectors(private_policy_t *this, host_t *other) +{ + return get_traffic_selectors(this, this->other_ts, other); +} + +/** + * Narrow traffic selectors, with wildcard-address update in "stored". + */ +static linked_list_t *select_traffic_selectors(private_policy_t *this, + linked_list_t *stored, + linked_list_t *supplied, + host_t *host) +{ + iterator_t *supplied_iter, *stored_iter, *i1, *i2; + traffic_selector_t *supplied_ts, *stored_ts, *selected_ts, *ts1, *ts2; + linked_list_t *selected = linked_list_create(); + + DBG2(DBG_CFG, "selecting traffic selectors"); + + stored_iter = stored->create_iterator(stored, TRUE); + supplied_iter = supplied->create_iterator(supplied, TRUE); + + /* iterate over all stored selectors */ + while (stored_iter->iterate(stored_iter, (void**)&stored_ts)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + stored_ts = stored_ts->clone(stored_ts); + if (host) + { + stored_ts->set_address(stored_ts, host); + } + + supplied_iter->reset(supplied_iter); + /* iterate over all supplied traffic selectors */ + while (supplied_iter->iterate(supplied_iter, (void**)&supplied_ts)) + { + DBG2(DBG_CFG, "stored %R <=> %R received", + stored_ts, supplied_ts); + + selected_ts = stored_ts->get_subset(stored_ts, supplied_ts); + if (selected_ts) + { + /* got a match, add to list */ + selected->insert_last(selected, (void*)selected_ts); + + DBG2(DBG_CFG, "found traffic selector for %s: %R", + stored == this->my_ts ? "us" : "other", selected_ts); + } + } + stored_ts->destroy(stored_ts); + } + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + /* remove any redundant traffic selectors in the list */ + i1 = selected->create_iterator(selected, TRUE); + i2 = selected->create_iterator(selected, TRUE); + while (i1->iterate(i1, (void**)&ts1)) + { + while (i2->iterate(i2, (void**)&ts2)) + { + if (ts1 != ts2) + { + if (ts2->is_contained_in(ts2, ts1)) + { + i2->remove(i2); + ts2->destroy(ts2); + i1->reset(i1); + break; + } + if (ts1->is_contained_in(ts1, ts2)) + { + i1->remove(i1); + ts1->destroy(ts1); + i2->reset(i2); + break; + } + } + } + } + i1->destroy(i1); + i2->destroy(i2); + + return selected; +} + +/** + * Implementation of private_policy_t.select_my_traffic_selectors + */ +static linked_list_t *select_my_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t *me) +{ + return select_traffic_selectors(this, this->my_ts, supplied, me); +} + +/** + * Implementation of private_policy_t.select_other_traffic_selectors + */ +static linked_list_t *select_other_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t* other) +{ + return select_traffic_selectors(this, this->other_ts, supplied, other); +} + +/** + * Implementation of policy_t.get_proposal_iterator + */ +static linked_list_t *get_proposals(private_policy_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of policy_t.select_proposal + */ +static proposal_t *select_proposal(private_policy_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of policy_t.add_authorities + */ +static void add_authorities(private_policy_t *this, identification_t *my_ca, identification_t *other_ca) +{ + this->my_ca = my_ca; + this->other_ca = other_ca; +} + +/** + * Implementation of policy_t.get_updown + */ +static char* get_updown(private_policy_t *this) +{ + return this->updown; +} + +/** + * Implementation of policy_t.get_hostaccess + */ +static bool get_hostaccess(private_policy_t *this) +{ + return this->hostaccess; +} + +/** + * Implements policy_t.get_dpd_action + */ +static dpd_action_t get_dpd_action(private_policy_t *this) +{ + return this->dpd_action; +} + +/** + * Implementation of policy_t.add_my_traffic_selector + */ +static void add_my_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->my_ts->insert_last(this->my_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_other_traffic_selector + */ +static void add_other_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->other_ts->insert_last(this->other_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_proposal + */ +static void add_proposal(private_policy_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, (void*)proposal); +} + +/** + * Implementation of policy_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_policy_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of policy_t.get_hard_lifetime + */ +static u_int32_t get_hard_lifetime(private_policy_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of policy_t.get_mode. + */ +static mode_t get_mode(private_policy_t *this) +{ + return this->mode; +} + +/** + * Implementation of policy_t.get_virtual_ip. + */ +static host_t* get_virtual_ip(private_policy_t *this, host_t *suggestion) +{ + if (suggestion == NULL) + { + if (this->my_virtual_ip) + { + return this->my_virtual_ip->clone(this->my_virtual_ip); + } + return NULL; + } + if (this->other_virtual_ip) + { + return this->other_virtual_ip->clone(this->other_virtual_ip); + } + if (suggestion->is_anyaddr(suggestion)) + { + return NULL; + } + return suggestion->clone(suggestion); +} + +/** + * Implements policy_t.get_ref. + */ +static void get_ref(private_policy_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implements policy_t.destroy. + */ +static void destroy(private_policy_t *this) +{ + if (ref_put(&this->refcount)) + { + + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); + this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); + + /* delete certification authorities */ + DESTROY_IF(this->my_ca); + DESTROY_IF(this->other_ca); + + /* delete updown script */ + if (this->updown) + { + free(this->updown); + } + + /* delete ids */ + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + DESTROY_IF(this->my_virtual_ip); + DESTROY_IF(this->other_virtual_ip); + + free(this->name); + free(this); + } +} + +/* + * Described in header-file + */ +policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action) +{ + private_policy_t *this = malloc_thing(private_policy_t); + + /* public functions */ + this->public.get_name = (char* (*) (policy_t*))get_name; + this->public.get_my_id = (identification_t* (*) (policy_t*))get_my_id; + this->public.get_other_id = (identification_t* (*) (policy_t*))get_other_id; + this->public.get_my_ca = (identification_t* (*) (policy_t*))get_my_ca; + this->public.get_other_ca = (identification_t* (*) (policy_t*))get_other_ca; + this->public.get_auth_method = (auth_method_t (*) (policy_t*)) get_auth_method; + this->public.get_eap_type = (eap_type_t (*) (policy_t*)) get_eap_type; + this->public.get_my_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_other_traffic_selectors; + this->public.select_my_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_my_traffic_selectors; + this->public.select_other_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_other_traffic_selectors; + this->public.get_proposals = (linked_list_t* (*) (policy_t*))get_proposals; + this->public.select_proposal = (proposal_t* (*) (policy_t*,linked_list_t*))select_proposal; + this->public.add_my_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_my_traffic_selector; + this->public.add_other_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_other_traffic_selector; + this->public.add_proposal = (void (*) (policy_t*,proposal_t*))add_proposal; + this->public.add_authorities = (void (*) (policy_t*,identification_t*,identification_t*))add_authorities; + this->public.get_updown = (char* (*) (policy_t*))get_updown; + this->public.get_hostaccess = (bool (*) (policy_t*))get_hostaccess; + this->public.get_dpd_action = (dpd_action_t (*) (policy_t*))get_dpd_action; + this->public.get_soft_lifetime = (u_int32_t (*) (policy_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (policy_t *))get_hard_lifetime; + this->public.get_mode = (mode_t (*) (policy_t *))get_mode; + this->public.get_virtual_ip = (host_t* (*)(policy_t*,host_t*))get_virtual_ip; + this->public.get_ref = (void (*) (policy_t*))get_ref; + this->public.destroy = (void (*) (policy_t*))destroy; + + /* apply init values */ + this->name = strdup(name); + this->my_id = my_id; + this->other_id = other_id; + this->my_virtual_ip = my_virtual_ip; + this->other_virtual_ip = other_virtual_ip; + this->auth_method = auth_method; + this->eap_type = eap_type; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + this->updown = (updown == NULL) ? NULL : strdup(updown); + this->hostaccess = hostaccess; + this->dpd_action = dpd_action; + this->mode = mode; + + /* initialize private members*/ + this->refcount = 1; + this->my_ca = NULL; + this->other_ca = NULL; + this->proposals = linked_list_create(); + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h new file mode 100644 index 000000000..d8916b29e --- /dev/null +++ b/src/charon/config/policies/policy.h @@ -0,0 +1,413 @@ +/** + * @file policy.h + * + * @brief Interface of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef POLICY_H_ +#define POLICY_H_ + +typedef enum dpd_action_t dpd_action_t; +typedef struct policy_t policy_t; + +#include <library.h> +#include <utils/identification.h> +#include <config/traffic_selector.h> +#include <config/proposal.h> +#include <sa/authenticators/authenticator.h> +#include <sa/authenticators/eap/eap_method.h> + + +/** + * @brief Actions to take when a peer does not respond (dead peer detected). + * + * These values are the same as in pluto/starter, so do not modify them! + * + * @ingroup config + */ +enum dpd_action_t { + /** DPD disabled */ + DPD_NONE, + /** remove CHILD_SA without replacement */ + DPD_CLEAR, + /** route the CHILD_SA to resetup when needed */ + DPD_ROUTE, + /** restart CHILD_SA in a new IKE_SA, immediately */ + DPD_RESTART, +}; + +/** + * enum names for dpd_action_t. + */ +extern enum_name_t *dpd_action_names; + +/** + * @brief Mode of an IPsec SA. + * + * These are equal to those defined in XFRM, so don't change. + * + * @ingroup config + */ +enum mode_t { + /** transport mode, no inner address */ + MODE_TRANSPORT = 0, + /** tunnel mode, inner and outer addresses */ + MODE_TUNNEL = 1, + /** BEET mode, tunnel mode but fixed, bound inner addresses */ + MODE_BEET = 4, +}; + +/** + * enum names for mode_t. + */ +extern enum_name_t *mode_names; + +/** + * @brief A policy_t defines the policies to apply to CHILD_SAs. + * + * The given two IDs identify a policy. These rules define how + * child SAs may be set up and which traffic may be IPsec'ed. + * + * @b Constructors: + * - policy_create() + * + * @ingroup config + */ +struct policy_t { + + /** + * @brief Get the name of the policy. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return policy's name + */ + char *(*get_name) (policy_t *this); + + /** + * @brief Get own id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own id + */ + identification_t *(*get_my_id) (policy_t *this); + + /** + * @brief Get peer id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other id + */ + identification_t *(*get_other_id) (policy_t *this); + + /** + * @brief Get own ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own ca + */ + identification_t *(*get_my_ca) (policy_t *this); + + /** + * @brief Get peer ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other ca + */ + identification_t *(*get_other_ca) (policy_t *this); + + /** + * @brief Get the authentication method to use. + * + * @param this calling object + * @return authentication method + */ + auth_method_t (*get_auth_method) (policy_t *this); + + /** + * @brief Get the EAP type to use for peer authentication. + * + * @param this calling object + * @return authentication method + */ + eap_type_t (*get_eap_type) (policy_t *this); + + /** + * @brief Get configured traffic selectors for our site. + * + * Returns a list with all traffic selectors for the local + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_my_traffic_selectors) (policy_t *this, host_t *me); + + /** + * @brief Get configured traffic selectors for others site. + * + * Returns a list with all traffic selectors for the remote + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_other_traffic_selectors) (policy_t *this, host_t* other); + + /** + * @brief Select traffic selectors from a supplied list for local site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @param me host address used by us + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_my_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *me); + + /** + * @brief Select traffic selectors from a supplied list for remote site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_other_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *other); + + /** + * @brief Get the list of internally stored proposals. + * + * policy_t does store proposals for AH/ESP, IKE proposals are in + * the connection_t. + * Resulting list and all of its proposals must be freed after usage. + * + * @param this calling object + * @return lists with proposals + */ + linked_list_t *(*get_proposals) (policy_t *this); + + /** + * @brief Select a proposal from a supplied list. + * + * Returned propsal is newly created and must be destroyed after usage. + * + * @param this calling object + * @param proposals list from from wich proposals are selected + * @return selected proposal, or NULL if nothing matches + */ + proposal_t *(*select_proposal) (policy_t *this, linked_list_t *proposals); + + /** + * @brief Add a traffic selector to the list for local site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_my_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a traffic selector to the list for remote site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_other_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a proposal to the list. + * + * The proposals are stored by priority, first added + * is the most prefered. + * After add, proposal is owned by policy. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (policy_t *this, proposal_t *proposal); + + /** + * @brief Add certification authorities. + * + * @param this calling object + * @param my_ca issuer of my certificate + * @param other_ca required issuer of the peer's certificate + */ + void (*add_authorities) (policy_t *this, identification_t *my_ca, identification_t *other_ca); + + /** + * @brief Get updown script + * + * @param this calling object + * @return path to updown script + */ + char* (*get_updown) (policy_t *this); + + /** + * @brief Get hostaccess flag + * + * @param this calling object + * @return value of hostaccess flag + */ + bool (*get_hostaccess) (policy_t *this); + + /** + * @brief What should be done with a CHILD_SA, when other peer does not respond. + * + * @param this calling object + * @return dpd action + */ + dpd_action_t (*get_dpd_action) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before SA gets deleted. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (policy_t *this); + + /** + * @brief Get the mode to use for the CHILD_SA, tunnel, transport or BEET. + * + * @param this policy + * @return lifetime in seconds + */ + mode_t (*get_mode) (policy_t *this); + + /** + * @brief Get a virtual IP for the local or the remote host. + * + * By supplying NULL as IP, an IP for the local host is requested. It + * may be %any or specific. + * By supplying %any as host, an IP from the pool is selected to be + * served to the peer. + * If a specified host is supplied, it is checked if this address + * is acceptable to serve to the peer. If so, it is returned. Otherwise, + * an alternative IP is returned. + * In any mode, this call may return NULL indicating virtual IP should + * not be used. + * + * @param this policy + * @param suggestion NULL, %any or specific, see description + * @return clone of an IP to use, or NULL + */ + host_t* (*get_virtual_ip) (policy_t *this, host_t *suggestion); + + /** + * @brief Get a new reference. + * + * Get a new reference to this policy by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (policy_t *this); + + /** + * @brief Destroys the policy object. + * + * Decrements the internal reference counter and + * destroys the policy when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (policy_t *this); +}; + +/** + * @brief Create a configuration object for IKE_AUTH and later. + * + * name-string gets cloned, ID's not. + * Virtual IPs are used if they are != NULL. A %any host means the virtual + * IP should be obtained from the other peer. + * Lifetimes are in seconds. To prevent to peers to start rekeying at the + * same time, a jitter may be specified. Rekeying of an SA starts at + * (soft_lifetime - random(0, jitter)). After a successful rekeying, + * the hard_lifetime limit counter is reset. You should specify + * hard_lifetime > soft_lifetime > jitter. + * After a call to create, a reference is obtained (refcount = 1). + * + * @param name name of the policy + * @param my_id identification_t for ourselves + * @param other_id identification_t for the remote guy + * @param my_virtual_ip virtual IP for local host, or NULL + * @param other_virtual_ip virtual IP for remote host, or NULL + * @param auth_method Authentication method to use for our(!) auth data + * @param eap_type EAP type to use for peer authentication + * @param hard_lifetime lifetime before deleting an SA + * @param soft_lifetime lifetime before rekeying an SA + * @param jitter range of randomization time + * @param updown updown script to execute on up/down event + * @param hostaccess allow access to the host itself (used by the updown script) + * @param mode mode to propose for CHILD_SA, transport, tunnel or BEET + * @param dpd_action what to to with a CHILD_SA when other peer does not respond + * @return policy_t object + * + * @ingroup config + */ +policy_t *policy_create(char *name, + identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action); + +#endif /* POLICY_H_ */ diff --git a/src/charon/config/policies/policy_store.h b/src/charon/config/policies/policy_store.h new file mode 100755 index 000000000..cd8870953 --- /dev/null +++ b/src/charon/config/policies/policy_store.h @@ -0,0 +1,119 @@ +/** + * @file policy_store.h + * + * @brief Interface policy_store_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef POLICY_STORE_H_ +#define POLICY_STORE_H_ + +typedef struct policy_store_t policy_store_t; + +#include <library.h> +#include <config/policies/policy.h> +#include <utils/linked_list.h> + + +/** + * @brief The interface for a store of policy_t's. + * + * The store uses reference counting to manage their lifetime. Call + * destroy() for a policy which is returned from the store after usage. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct policy_store_t { + + /** + * @brief Returns a policy identified by two IDs and a set of traffic selectors. + * + * other_id must be fully qualified. my_id may be %any, as the + * other peer may not include an IDr Request. + * + * @param this calling object + * @param my_id own ID of the policy + * @param other_id others ID of the policy + * @param my_ts traffic selectors requested for local host + * @param other_ts traffic selectors requested for remote host + * @param my_host host to use for wilcards in TS compare + * @param other_host host to use for wildcards in TS compare + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy) (policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t* other_host); + + /** + * @brief Returns a policy identified by a connection name. + * + * @param this calling object + * @param name name of the policy + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy_by_name) (policy_store_t *this, char *name); + + /** + * @brief Add a policy to the list. + * + * The policy is owned by the store after the call. Do + * not modify nor free. + * + * @param this calling object + * @param policy policy to add + */ + void (*add_policy) (policy_store_t *this, policy_t *policy); + + /** + * @brief Delete a policy from the store. + * + * Remove a policy from the store identified by its name. + * + * @param this calling object + * @param policy policy to add + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_policy) (policy_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored policies. + * + * @param this calling object + * @return iterator over all stored policies + */ + iterator_t* (*create_iterator) (policy_store_t *this); + + /** + * @brief Destroys a policy_store_t object. + * + * @param this calling object + */ + void (*destroy) (policy_store_t *this); +}; + +#endif /*POLICY_STORE_H_*/ diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c new file mode 100644 index 000000000..dcab8cbdd --- /dev/null +++ b/src/charon/config/proposal.c @@ -0,0 +1,641 @@ +/** + * @file proposal.c + * + * @brief Implementation of proposal_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> + +#include "proposal.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> +#include <utils/lexparser.h> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + + +ENUM(protocol_id_names, PROTO_NONE, PROTO_ESP, + "PROTO_NONE", + "IKE", + "AH", + "ESP", +); + +ENUM_BEGIN(transform_type_names, UNDEFINED_TRANSFORM_TYPE, UNDEFINED_TRANSFORM_TYPE, + "UNDEFINED_TRANSFORM_TYPE"); +ENUM_NEXT(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS, UNDEFINED_TRANSFORM_TYPE, + "ENCRYPTION_ALGORITHM", + "PSEUDO_RANDOM_FUNCTION", + "INTEGRITY_ALGORITHM", + "DIFFIE_HELLMAN_GROUP", + "EXTENDED_SEQUENCE_NUMBERS"); +ENUM_END(transform_type_names, EXTENDED_SEQUENCE_NUMBERS); + +ENUM(extended_sequence_numbers_names, NO_EXT_SEQ_NUMBERS, EXT_SEQ_NUMBERS, + "NO_EXT_SEQ_NUMBERS", + "EXT_SEQ_NUMBERS", +); + +typedef struct private_proposal_t private_proposal_t; + +/** + * Private data of an proposal_t object + */ +struct private_proposal_t { + + /** + * Public part + */ + proposal_t public; + + /** + * protocol (ESP or AH) + */ + protocol_id_t protocol; + + /** + * priority ordered list of encryption algorithms + */ + linked_list_t *encryption_algos; + + /** + * priority ordered list of integrity algorithms + */ + linked_list_t *integrity_algos; + + /** + * priority ordered list of pseudo random functions + */ + linked_list_t *prf_algos; + + /** + * priority ordered list of dh groups + */ + linked_list_t *dh_groups; + + /** + * priority ordered list of extended sequence number flags + */ + linked_list_t *esns; + + /** + * senders SPI + */ + u_int64_t spi; +}; + +/** + * Add algorithm/keysize to a algorithm list + */ +static void add_algo(linked_list_t *list, u_int16_t algo, size_t key_size) +{ + algorithm_t *algo_key; + + algo_key = malloc_thing(algorithm_t); + algo_key->algorithm = algo; + algo_key->key_size = key_size; + list->insert_last(list, (void*)algo_key); +} + +/** + * Implements proposal_t.add_algorithm + */ +static void add_algorithm(private_proposal_t *this, transform_type_t type, u_int16_t algo, size_t key_size) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + add_algo(this->encryption_algos, algo, key_size); + break; + case INTEGRITY_ALGORITHM: + add_algo(this->integrity_algos, algo, key_size); + break; + case PSEUDO_RANDOM_FUNCTION: + add_algo(this->prf_algos, algo, key_size); + break; + case DIFFIE_HELLMAN_GROUP: + add_algo(this->dh_groups, algo, 0); + break; + case EXTENDED_SEQUENCE_NUMBERS: + add_algo(this->esns, algo, 0); + break; + default: + break; + } +} + +/** + * Implements proposal_t.get_algorithm. + */ +static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo) +{ + linked_list_t *list; + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = this->encryption_algos; + break; + case INTEGRITY_ALGORITHM: + list = this->integrity_algos; + break; + case PSEUDO_RANDOM_FUNCTION: + list = this->prf_algos; + break; + case DIFFIE_HELLMAN_GROUP: + list = this->dh_groups; + break; + case EXTENDED_SEQUENCE_NUMBERS: + list = this->esns; + break; + default: + return FALSE; + } + if (list->get_first(list, (void**)algo) != SUCCESS) + { + return FALSE; + } + return TRUE; +} + +/** + * Implements proposal_t.create_algorithm_iterator. + */ +static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform_type_t type) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + return this->encryption_algos->create_iterator(this->encryption_algos, TRUE); + case INTEGRITY_ALGORITHM: + return this->integrity_algos->create_iterator(this->integrity_algos, TRUE); + case PSEUDO_RANDOM_FUNCTION: + return this->prf_algos->create_iterator(this->prf_algos, TRUE); + case DIFFIE_HELLMAN_GROUP: + return this->dh_groups->create_iterator(this->dh_groups, TRUE); + case EXTENDED_SEQUENCE_NUMBERS: + return this->esns->create_iterator(this->esns, TRUE); + default: + break; + } + return NULL; +} + +/** + * Find a matching alg/keysize in two linked lists + */ +static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add, u_int16_t *alg, size_t *key_size) +{ + iterator_t *first_iter, *second_iter; + algorithm_t *first_alg, *second_alg; + + /* if in both are zero algorithms specified, we HAVE a match */ + if (first->get_count(first) == 0 && second->get_count(second) == 0) + { + *add = FALSE; + return TRUE; + } + + first_iter = first->create_iterator(first, TRUE); + second_iter = second->create_iterator(second, TRUE); + /* compare algs, order of algs in "first" is preferred */ + while (first_iter->iterate(first_iter, (void**)&first_alg)) + { + second_iter->reset(second_iter); + while (second_iter->iterate(second_iter, (void**)&second_alg)) + { + if (first_alg->algorithm == second_alg->algorithm && + first_alg->key_size == second_alg->key_size) + { + /* ok, we have an algorithm */ + *alg = first_alg->algorithm; + *key_size = first_alg->key_size; + *add = TRUE; + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return TRUE; + } + } + } + /* no match in all comparisons */ + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return FALSE; +} + +/** + * Implements proposal_t.select. + */ +static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t *other) +{ + proposal_t *selected; + u_int16_t algo; + size_t key_size; + bool add; + + DBG2(DBG_CFG, "selecting proposal:"); + + /* check protocol */ + if (this->protocol != other->protocol) + { + DBG2(DBG_CFG, " protocol mismatch, skipping"); + return NULL; + } + + selected = proposal_create(this->protocol); + + /* select encryption algorithm */ + if (select_algo(this->encryption_algos, other->encryption_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, ENCRYPTION_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable ENCRYPTION_ALGORITHM found, skipping"); + return NULL; + } + /* select integrity algorithm */ + if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable INTEGRITY_ALGORITHM found, skipping"); + return NULL; + } + /* select prf algorithm */ + if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, PSEUDO_RANDOM_FUNCTION, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable PSEUDO_RANDOM_FUNCTION found, skipping"); + return NULL; + } + /* select a DH-group */ + if (select_algo(this->dh_groups, other->dh_groups, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, DIFFIE_HELLMAN_GROUP, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable DIFFIE_HELLMAN_GROUP found, skipping"); + return NULL; + } + /* select if we use ESNs */ + if (select_algo(this->esns, other->esns, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, EXTENDED_SEQUENCE_NUMBERS, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable EXTENDED_SEQUENCE_NUMBERS found, skipping"); + return NULL; + } + DBG2(DBG_CFG, " proposal matches"); + + /* apply SPI from "other" */ + selected->set_spi(selected, other->spi); + + /* everything matched, return new proposal */ + return selected; +} + +/** + * Implements proposal_t.get_protocols. + */ +static protocol_id_t get_protocol(private_proposal_t *this) +{ + return this->protocol; +} + +/** + * Implements proposal_t.set_spi. + */ +static void set_spi(private_proposal_t *this, u_int64_t spi) +{ + this->spi = spi; +} + +/** + * Implements proposal_t.get_spi. + */ +static u_int64_t get_spi(private_proposal_t *this) +{ + return this->spi; +} + +/** + * Clone a algorithm list + */ +static void clone_algo_list(linked_list_t *list, linked_list_t *clone_list) +{ + algorithm_t *algo, *clone_algo; + iterator_t *iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&algo)) + { + clone_algo = malloc_thing(algorithm_t); + memcpy(clone_algo, algo, sizeof(algorithm_t)); + clone_list->insert_last(clone_list, (void*)clone_algo); + } + iterator->destroy(iterator); +} + +/** + * Implements proposal_t.clone + */ +static proposal_t *clone_(private_proposal_t *this) +{ + private_proposal_t *clone = (private_proposal_t*)proposal_create(this->protocol); + + clone_algo_list(this->encryption_algos, clone->encryption_algos); + clone_algo_list(this->integrity_algos, clone->integrity_algos); + clone_algo_list(this->prf_algos, clone->prf_algos); + clone_algo_list(this->dh_groups, clone->dh_groups); + clone_algo_list(this->esns, clone->esns); + + clone->spi = this->spi; + + return &clone->public; +} + +static status_t add_string_algo(private_proposal_t *this, chunk_t alg) +{ + if (strncmp(alg.ptr, "null", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_NULL, 0); + } + else if (strncmp(alg.ptr, "aes128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + } + else if (strncmp(alg.ptr, "aes192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + } + else if (strncmp(alg.ptr, "aes256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + } + else if (strncmp(alg.ptr, "3des", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + } + /* blowfish only uses some predefined key sizes yet */ + else if (strncmp(alg.ptr, "blowfish128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 128); + } + else if (strncmp(alg.ptr, "blowfish192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 192); + } + else if (strncmp(alg.ptr, "blowfish256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + } + else if (strncmp(alg.ptr, "sha", alg.len) == 0 || + strncmp(alg.ptr, "sha1", alg.len) == 0) + { + /* sha means we use SHA for both, PRF and AUTH */ + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + } + } + else if (strncmp(alg.ptr, "sha256", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + } + } + else if (strncmp(alg.ptr, "sha384", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + } + } + else if (strncmp(alg.ptr, "sha512", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + } + } + else if (strncmp(alg.ptr, "md5", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + } + } + else if (strncmp(alg.ptr, "modp768", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_768_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1024", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1536", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + } + else if (strncmp(alg.ptr, "modp2048", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + } + else if (strncmp(alg.ptr, "modp4096", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + } + else if (strncmp(alg.ptr, "modp8192", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + } + else + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implements proposal_t.destroy. + */ +static void destroy(private_proposal_t *this) +{ + this->encryption_algos->destroy_function(this->encryption_algos, free); + this->integrity_algos->destroy_function(this->integrity_algos, free); + this->prf_algos->destroy_function(this->prf_algos, free); + this->dh_groups->destroy_function(this->dh_groups, free); + this->esns->destroy_function(this->esns, free); + free(this); +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create(protocol_id_t protocol) +{ + private_proposal_t *this = malloc_thing(private_proposal_t); + + this->public.add_algorithm = (void (*)(proposal_t*,transform_type_t,u_int16_t,size_t))add_algorithm; + this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,transform_type_t))create_algorithm_iterator; + this->public.get_algorithm = (bool (*)(proposal_t*,transform_type_t,algorithm_t**))get_algorithm; + this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal; + this->public.get_protocol = (protocol_id_t(*)(proposal_t*))get_protocol; + this->public.set_spi = (void(*)(proposal_t*,u_int64_t))set_spi; + this->public.get_spi = (u_int64_t(*)(proposal_t*))get_spi; + this->public.clone = (proposal_t*(*)(proposal_t*))clone_; + this->public.destroy = (void(*)(proposal_t*))destroy; + + this->spi = 0; + this->protocol = protocol; + + this->encryption_algos = linked_list_create(); + this->integrity_algos = linked_list_create(); + this->prf_algos = linked_list_create(); + this->dh_groups = linked_list_create(); + this->esns = linked_list_create(); + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_default(protocol_id_t protocol) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + + switch (protocol) + { + case PROTO_IKE: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + break; + case PROTO_ESP: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + case PROTO_AH: + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + default: + break; + } + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + chunk_t string = {(void*)algs, strlen(algs)}; + chunk_t alg; + status_t status = SUCCESS; + + eat_whitespace(&string); + if (string.len < 1) + { + destroy(this); + return NULL; + } + + /* get all tokens, separated by '-' */ + while (extract_token(&alg, '-', &string)) + { + status |= add_string_algo(this, alg); + } + if (string.len) + { + status |= add_string_algo(this, string); + } + if (status != SUCCESS) + { + destroy(this); + return NULL; + } + + if (protocol == PROTO_AH || protocol == PROTO_ESP) + { + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + } + return &this->public; +} diff --git a/src/charon/config/proposal.h b/src/charon/config/proposal.h new file mode 100644 index 000000000..abcb40999 --- /dev/null +++ b/src/charon/config/proposal.h @@ -0,0 +1,266 @@ +/** + * @file proposal.h + * + * @brief Interface of proposal_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef PROPOSAL_H_ +#define PROPOSAL_H_ + +typedef enum protocol_id_t protocol_id_t; +typedef enum transform_type_t transform_type_t; +typedef enum extended_sequence_numbers_t extended_sequence_numbers_t; +typedef struct algorithm_t algorithm_t; +typedef struct proposal_t proposal_t; + +#include <library.h> +#include <utils/identification.h> +#include <utils/linked_list.h> +#include <utils/host.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <crypto/diffie_hellman.h> +#include <config/traffic_selector.h> + +/** + * Protocol ID of a proposal. + * + * @ingroup config + */ +enum protocol_id_t { + PROTO_NONE = 0, + PROTO_IKE = 1, + PROTO_AH = 2, + PROTO_ESP = 3, +}; + +/** + * enum names for protocol_id_t + * + * @ingroup config + */ +extern enum_name_t *protocol_id_names; + + +/** + * Type of a transform, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum transform_type_t { + UNDEFINED_TRANSFORM_TYPE = 241, + ENCRYPTION_ALGORITHM = 1, + PSEUDO_RANDOM_FUNCTION = 2, + INTEGRITY_ALGORITHM = 3, + DIFFIE_HELLMAN_GROUP = 4, + EXTENDED_SEQUENCE_NUMBERS = 5 +}; + +/** + * enum names for transform_type_t. + * + * @ingroup config + */ +extern enum_name_t *transform_type_names; + + +/** + * Extended sequence numbers, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum extended_sequence_numbers_t { + NO_EXT_SEQ_NUMBERS = 0, + EXT_SEQ_NUMBERS = 1 +}; + +/** + * enum strings for extended_sequence_numbers_t. + * + * @ingroup config + */ +extern enum_name_t *extended_sequence_numbers_names; + + + +/** + * Struct used to store different kinds of algorithms. The internal + * lists of algorithms contain such structures. + */ +struct algorithm_t { + /** + * Value from an encryption_algorithm_t/integrity_algorithm_t/... + */ + u_int16_t algorithm; + + /** + * the associated key size in bits, or zero if not needed + */ + u_int16_t key_size; +}; + +/** + * @brief Stores a set of algorithms used for an SA. + * + * A proposal stores algorithms for a specific + * protocol. It can store algorithms for one protocol. + * Proposals with multiple protocols are not supported, + * as it's not specified in RFC4301 anymore. + * + * @b Constructors: + * - proposal_create() + * + * @ingroup config + */ +struct proposal_t { + + /** + * @brief Add an algorithm to the proposal. + * + * The algorithms are stored by priority, first added + * is the most preferred. + * Key size is only needed for encryption algorithms + * with variable key size (such as AES). Must be set + * to zero if key size is not specified. + * The alg parameter accepts encryption_algorithm_t, + * integrity_algorithm_t, dh_group_number_t and + * extended_sequence_numbers_t. + * + * @param this calling object + * @param type kind of algorithm + * @param alg identifier for algorithm + * @param key_size key size to use + */ + void (*add_algorithm) (proposal_t *this, transform_type_t type, u_int16_t alg, size_t key_size); + + /** + * @brief Get an iterator over algorithms for a specifc algo type. + * + * @param this calling object + * @param type kind of algorithm + * @return iterator over algorithm_t's + */ + iterator_t *(*create_algorithm_iterator) (proposal_t *this, transform_type_t type); + + /** + * @brief Get the algorithm for a type to use. + * + * If there are multiple algorithms, only the first is returned. + * Result is still owned by proposal, do not modify! + * + * @param this calling object + * @param type kind of algorithm + * @param[out] algo pointer which receives algorithm and key size + * @return TRUE if algorithm of this kind available + */ + bool (*get_algorithm) (proposal_t *this, transform_type_t type, algorithm_t** algo); + + /** + * @brief Compare two proposal, and select a matching subset. + * + * If the proposals are for the same protocols (AH/ESP), they are + * compared. If they have at least one algorithm of each type + * in common, a resulting proposal of this kind is created. + * + * @param this calling object + * @param other proposal to compair agains + * @return + * - selected proposal, if possible + * - NULL, if proposals don't match + */ + proposal_t *(*select) (proposal_t *this, proposal_t *other); + + /** + * @brief Get the protocol ID of the proposal. + * + * @param this calling object + * @return protocol of the proposal + */ + protocol_id_t (*get_protocol) (proposal_t *this); + + /** + * @brief Get the SPI of the proposal. + * + * @param this calling object + * @return spi for proto + */ + u_int64_t (*get_spi) (proposal_t *this); + + /** + * @brief Set the SPI of the proposal. + * + * @param this calling object + * @param spi spi to set for proto + */ + void (*set_spi) (proposal_t *this, u_int64_t spi); + + /** + * @brief Clone a proposal. + * + * @param this proposal to clone + * @return clone of it + */ + proposal_t *(*clone) (proposal_t *this); + + /** + * @brief Destroys the proposal object. + * + * @param this calling object + */ + void (*destroy) (proposal_t *this); +}; + +/** + * @brief Create a child proposal for AH, ESP or IKE. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create(protocol_id_t protocol); + +/** + * @brief Create a default proposal if nothing further specified. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_default(protocol_id_t protocol); + +/** + * @brief Create a proposal from a string identifying the algorithms. + * + * The string is in the same form as a in the ipsec.conf file. + * E.g.: aes128-sha2_256-modp2048 + * 3des-md5 + * An additional '!' at the end of the string forces this proposal, + * without it the peer may choose another algorithm we support. + * + * @param protocol protocol, such as PROTO_ESP + * @param algs algorithms as string + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs); + +#endif /* PROPOSAL_H_ */ diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c new file mode 100644 index 000000000..2fb012e16 --- /dev/null +++ b/src/charon/config/traffic_selector.c @@ -0,0 +1,795 @@ +/** + * @file traffic_selector.c + * + * @brief Implementation of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <arpa/inet.h> +#include <string.h> +#include <netdb.h> +#include <stdio.h> +#include <printf.h> + +#include "traffic_selector.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/identification.h> + +ENUM(ts_type_name, TS_IPV4_ADDR_RANGE, TS_IPV6_ADDR_RANGE, + "TS_IPV4_ADDR_RANGE", + "TS_IPV6_ADDR_RANGE", +); + +typedef struct private_traffic_selector_t private_traffic_selector_t; + +/** + * Private data of an traffic_selector_t object + */ +struct private_traffic_selector_t { + + /** + * Public part + */ + traffic_selector_t public; + + /** + * Type of address + */ + ts_type_t type; + + /** + * IP protocol (UDP, TCP, ICMP, ...) + */ + u_int8_t protocol; + + /** + * narrow this traffic selector to hosts external ip + * if set, from and to have no meaning until set_address() is called + */ + bool dynamic; + + /** + * begin of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char from[0]; + /** IPv4 address */ + u_int32_t from4[1]; + /** IPv6 address */ + u_int32_t from6[4]; + }; + + /** + * end of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char to[0]; + /** IPv4 address */ + u_int32_t to4[1]; + /** IPv6 address */ + u_int32_t to6[4]; + }; + + /** + * begin of port range + */ + u_int16_t from_port; + + /** + * end of port range + */ + u_int16_t to_port; +}; + +/** + * calculate to "to"-address for the "from" address and a subnet size + */ +static void calc_range(private_traffic_selector_t *this, u_int8_t netbits) +{ + int byte; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go through the from address, starting at the tail. While we + * have not processed the bits belonging to the host, set them to 1 on + * the to address. If we reach the bits for the net, copy them from "from". */ + for (byte = size - 1; byte >=0; byte--) + { + u_char mask = 0x00; + int shift; + + shift = (byte+1) * 8 - netbits; + if (shift > 0) + { + mask = 1 << shift; + if (mask != 0xFF) + { + mask--; + } + } + this->to[byte] = this->from[byte] | mask; + } +} + +/** + * calculate to subnet size from "to"- and "from"-address + */ +static u_int8_t calc_netbits(private_traffic_selector_t *this) +{ + int byte, bit; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go trough all bits of the addresses, begging in the front. + * As longer as they equal, the subnet gets larger */ + for (byte = 0; byte < size; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if ((1<<bit & this->from[byte]) != (1<<bit & this->to[byte])) + { + return ((7 - bit) + (byte * 8)); + } + } + } + /* single host, netmask is 32/128 */ + return (size * 8); +} + +/** + * internal generic constructor + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts_type_t type, u_int16_t from_port, u_int16_t to_port); + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_traffic_selector_t *this = *((private_traffic_selector_t**)(args[0])); + char addr_str[INET6_ADDRSTRLEN] = ""; + char *serv_proto = NULL; + u_int8_t mask; + bool has_proto; + bool has_ports; + size_t written = 0; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + if (this->type == TS_IPV4_ADDR_RANGE) + { + inet_ntop(AF_INET, &this->from4, addr_str, sizeof(addr_str)); + } + else + { + inet_ntop(AF_INET6, &this->from6, addr_str, sizeof(addr_str)); + } + mask = calc_netbits(this); + + written += fprintf(stream, "%s/%d", addr_str, mask); + + /* check if we have protocol and/or port selectors */ + has_proto = this->protocol != 0; + has_ports = !(this->from_port == 0 && this->to_port == 0xFFFF); + + if (!has_proto && !has_ports) + { + return written; + } + + written += fprintf(stream, "["); + + /* build protocol string */ + if (has_proto) + { + struct protoent *proto = getprotobynumber(this->protocol); + + if (proto) + { + written += fprintf(stream, "%s", proto->p_name); + serv_proto = proto->p_name; + } + else + { + written += fprintf(stream, "%d", this->protocol); + } + } + + if (has_proto && has_ports) + { + written += fprintf(stream, "/"); + } + + /* build port string */ + if (has_ports) + { + if (this->from_port == this->to_port) + { + struct servent *serv = getservbyport(htons(this->from_port), serv_proto); + + if (serv) + { + written += fprintf(stream, "%s", serv->s_name); + } + else + { + written += fprintf(stream, "%d", this->from_port); + } + } + else + { + written += fprintf(stream, "%d-%d", this->from_port, this->to_port); + } + } + + written += fprintf(stream, "]"); + + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_TRAFFIC_SELECTOR, print, arginfo_ptr); +} + +/** + * implements traffic_selector_t.get_subset + */ +static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type == other->type && (this->protocol == other->protocol || + this->protocol == 0 || other->protocol == 0)) + { + u_int16_t from_port, to_port; + u_char *from, *to; + u_int8_t protocol; + size_t size; + private_traffic_selector_t *new_ts; + + /* calculate the maximum port range allowed for both */ + from_port = max(this->from_port, other->from_port); + to_port = min(this->to_port, other->to_port); + if (from_port > to_port) + { + return NULL; + } + /* select protocol, which is not zero */ + protocol = max(this->protocol, other->protocol); + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + size = sizeof(this->from4); + break; + case TS_IPV6_ADDR_RANGE: + size = sizeof(this->from6); + break; + default: + return NULL; + } + + /* get higher from-address */ + if (memcmp(this->from, other->from, size) > 0) + { + from = this->from; + } + else + { + from = other->from; + } + /* get lower to-address */ + if (memcmp(this->to, other->to, size) > 0) + { + to = other->to; + } + else + { + to = this->to; + } + /* if "from" > "to", we don't have a match */ + if (memcmp(from, to, size) > 0) + { + return NULL; + } + + /* we have a match in protocol, port, and address: return it... */ + new_ts = traffic_selector_create(protocol, this->type, from_port, to_port); + new_ts->type = this->type; + memcpy(new_ts->from, from, size); + memcpy(new_ts->to, to, size); + + return &new_ts->public; + } + return NULL; +} + +/** + * implements traffic_selector_t.equals + */ +static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type != other->type) + { + return FALSE; + } + if (!(this->from_port == other->from_port && + this->to_port == other->to_port && + this->protocol == other->protocol)) + { + return FALSE; + } + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + if (memeq(this->from4, other->from4, sizeof(this->from4))) + { + return TRUE; + } + break; + case TS_IPV6_ADDR_RANGE: + if (memeq(this->from6, other->from6, sizeof(this->from6))) + { + return TRUE; + } + break; + default: + break; + } + return FALSE; +} + +/** + * Implements traffic_selector_t.get_from_address. + */ +static chunk_t get_from_address(private_traffic_selector_t *this) +{ + chunk_t from = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + from.len = sizeof(this->from4); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from4, from.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + from.len = sizeof(this->from6); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from6, from.len); + break; + } + } + return from; +} + +/** + * Implements traffic_selector_t.get_to_address. + */ +static chunk_t get_to_address(private_traffic_selector_t *this) +{ + chunk_t to = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + to.len = sizeof(this->to4); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to4, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + to.len = sizeof(this->to6); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to6, to.len); + break; + } + } + return to; +} + +/** + * Implements traffic_selector_t.get_from_port. + */ +static u_int16_t get_from_port(private_traffic_selector_t *this) +{ + return this->from_port; +} + +/** + * Implements traffic_selector_t.get_to_port. + */ +static u_int16_t get_to_port(private_traffic_selector_t *this) +{ + return this->to_port; +} + +/** + * Implements traffic_selector_t.get_type. + */ +static ts_type_t get_type(private_traffic_selector_t *this) +{ + return this->type; +} + +/** + * Implements traffic_selector_t.get_protocol. + */ +static u_int8_t get_protocol(private_traffic_selector_t *this) +{ + return this->protocol; +} + +/** + * Implements traffic_selector_t.is_host. + */ +static bool is_host(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + return TRUE; + } + + if (host) + { + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + if (memeq(addr.ptr, this->from, addr.len) && + memeq(addr.ptr, this->to, addr.len)) + { + return TRUE; + } + } + } + else + { + size_t length = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + if (memeq(this->from, this->to, length)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Implements traffic_selector_t.set_address. + */ +static void set_address(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + this->type = host->get_family(host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + + chunk_t from = host->get_address(host); + memcpy(this->from, from.ptr, from.len); + memcpy(this->to, from.ptr, from.len); + } +} + +/** + * Implements traffic_selector_t.is_contained_in. + */ +static bool is_contained_in(private_traffic_selector_t *this, + private_traffic_selector_t *other) +{ + private_traffic_selector_t *subset; + bool contained_in = FALSE; + + subset = (private_traffic_selector_t*)get_subset(this, other); + + if (subset) + { + if (equals(subset, this)) + { + contained_in = TRUE; + } + free(subset); + } + return contained_in; +} + +/** + * Implements traffic_selector_t.includes. + */ +static bool includes(private_traffic_selector_t *this, host_t *host) +{ + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + + return memcmp(this->from, addr.ptr, addr.len) <= 0 && + memcmp(this->to, addr.ptr, addr.len) >= 0; + } + + return FALSE; +} + +/** + * Implements traffic_selector_t.clone. + */ +static traffic_selector_t *clone_(private_traffic_selector_t *this) +{ + private_traffic_selector_t *clone; + + clone = traffic_selector_create(this->protocol, this->type, + this->from_port, this->to_port); + + clone->dynamic = this->dynamic; + switch (clone->type) + { + case TS_IPV4_ADDR_RANGE: + { + memcpy(clone->from4, this->from4, sizeof(this->from4)); + memcpy(clone->to4, this->to4, sizeof(this->to4)); + return &clone->public; + } + case TS_IPV6_ADDR_RANGE: + { + memcpy(clone->from6, this->from6, sizeof(this->from6)); + memcpy(clone->to6, this->to6, sizeof(this->to6)); + return &clone->public; + } + default: + { + /* unreachable */ + return &clone->public; + } + } +} + +/** + * Implements traffic_selector_t.destroy. + */ +static void destroy(private_traffic_selector_t *this) +{ + free(this); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, + ts_type_t type, + chunk_t from, u_int16_t from_port, + chunk_t to, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (from.len != 4 || to.len != 4) + { + free(this); + return NULL; + } + memcpy(this->from4, from.ptr, from.len); + memcpy(this->to4, to.ptr, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (from.len != 16 || to.len != 16) + { + free(this); + return NULL; + } + memcpy(this->from6, from.ptr, from.len); + memcpy(this->to6, to.ptr, to.len); + break; + } + default: + { + free(this); + return NULL; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, + u_int8_t netbits, u_int8_t protocol, u_int16_t port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, 0, 0, 65535); + + switch (net->get_family(net)) + { + case AF_INET: + { + chunk_t from; + + this->type = TS_IPV4_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from4, from.ptr, from.len); + if (this->from4[0] == 0) + { + /* use /0 for 0.0.0.0 */ + this->to4[0] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + case AF_INET6: + { + chunk_t from; + + this->type = TS_IPV6_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from6, from.ptr, from.len); + if (this->from6[0] == 0 && this->from6[1] == 0 && + this->from6[2] == 0 && this->from6[3] == 0) + { + /* use /0 for ::0 */ + this->to6[0] = ~0; + this->to6[1] = ~0; + this->to6[2] = ~0; + this->to6[3] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + default: + { + free(this); + return NULL; + } + } + if (port) + { + this->from_port = port; + this->to_port = port; + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + this->type = type; + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (inet_pton(AF_INET, from_addr, (struct in_addr*)this->from4) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET, to_addr, (struct in_addr*)this->to4) < 0) + { + free(this); + return NULL; + } + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (inet_pton(AF_INET6, from_addr, (struct in6_addr*)this->from6) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET6, to_addr, (struct in6_addr*)this->to6) < 0) + { + free(this); + return NULL; + } + break; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + memset(this->from6, 0, sizeof(this->from6)); + memset(this->to6, 0xFF, sizeof(this->to6)); + + this->dynamic = TRUE; + + return &this->public; +} + +/* + * see declaration + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, + ts_type_t type, u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = malloc_thing(private_traffic_selector_t); + + /* public functions */ + this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset; + this->public.equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals; + this->public.get_from_address = (chunk_t(*)(traffic_selector_t*))get_from_address; + this->public.get_to_address = (chunk_t(*)(traffic_selector_t*))get_to_address; + this->public.get_from_port = (u_int16_t(*)(traffic_selector_t*))get_from_port; + this->public.get_to_port = (u_int16_t(*)(traffic_selector_t*))get_to_port; + this->public.get_type = (ts_type_t(*)(traffic_selector_t*))get_type; + this->public.get_protocol = (u_int8_t(*)(traffic_selector_t*))get_protocol; + this->public.is_host = (bool(*)(traffic_selector_t*,host_t*))is_host; + this->public.is_contained_in = (bool(*)(traffic_selector_t*,traffic_selector_t*))is_contained_in; + this->public.includes = (bool(*)(traffic_selector_t*,host_t*))includes; + this->public.set_address = (void(*)(traffic_selector_t*,host_t*))set_address; + this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_; + this->public.destroy = (void(*)(traffic_selector_t*))destroy; + + this->from_port = from_port; + this->to_port = to_port; + this->protocol = protocol; + this->type = type; + this->dynamic = FALSE; + + return this; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/config/traffic_selector.h b/src/charon/config/traffic_selector.h new file mode 100644 index 000000000..0e798fc6a --- /dev/null +++ b/src/charon/config/traffic_selector.h @@ -0,0 +1,312 @@ +/** + * @file traffic_selector.h + * + * @brief Interface of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef TRAFFIC_SELECTOR_H_ +#define TRAFFIC_SELECTOR_H_ + +typedef enum ts_type_t ts_type_t; +typedef struct traffic_selector_t traffic_selector_t; + +#include <library.h> +#include <utils/host.h> + +/** + * Traffic selector types. + * + * @ingroup config + */ +enum ts_type_t { + + /** + * A range of IPv4 addresses, represented by two four (4) octet + * values. The first value is the beginning IPv4 address + * (inclusive) and the second value is the ending IPv4 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV4_ADDR_RANGE = 7, + + /** + * A range of IPv6 addresses, represented by two sixteen (16) + * octet values. The first value is the beginning IPv6 address + * (inclusive) and the second value is the ending IPv6 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV6_ADDR_RANGE = 8 +}; + +/** + * enum names for ts_type_t + */ +extern enum_name_t *ts_type_name; + +/** + * @brief Object representing a traffic selector entry. + * + * A traffic selector defines an range of addresses + * and a range of ports. IPv6 is not fully supported yet. + * + * @b Constructors: + * - traffic_selector_create_from_bytes() + * - traffic_selector_create_from_string() + * + * @todo Add IPv6 support + * + * @ingroup config + */ +struct traffic_selector_t { + + /** + * @brief Compare two traffic selectors, and create a new one + * which is the largest subset of both (subnet & port). + * + * Resulting traffic_selector is newly created and must be destroyed. + * + * @param this first to compare + * @param other second to compare + * @return + * - created subset of them + * - or NULL if no match between this and other + */ + traffic_selector_t *(*get_subset) (traffic_selector_t *this, + traffic_selector_t *other); + + /** + * @brief Clone a traffic selector. + * + * @param this traffic selector to clone + * @return clone of it + */ + traffic_selector_t *(*clone) (traffic_selector_t *this); + + /** + * @brief Get starting address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_from_address) (traffic_selector_t *this); + + /** + * @brief Get ending address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_to_address) (traffic_selector_t *this); + + /** + * @brief Get starting port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_from_port) (traffic_selector_t *this); + + /** + * @brief Get ending port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_to_port) (traffic_selector_t *this); + + /** + * @brief Get the type of the traffic selector. + * + * @param this called object + * @return ts_type_t specifying the type + */ + ts_type_t (*get_type) (traffic_selector_t *this); + + /** + * @brief Get the protocol id of this ts. + * + * @param this called object + * @return protocol id + */ + u_int8_t (*get_protocol) (traffic_selector_t *this); + + /** + * @brief Check if the traffic selector is for a single host. + * + * Traffic selector may describe the end of *-to-host tunnel. In this + * case, the address range is a single address equal to the hosts + * peer address. + * If host is NULL, the traffic selector is checked if it is a single host, + * but not a specific one. + * + * @param this called object + * @param host host_t specifying the address range + */ + bool (*is_host) (traffic_selector_t *this, host_t* host); + + /** + * @brief Update the address of a traffic selector. + * + * Update the address range of a traffic selector, if it is + * constructed with the traffic_selector_create_dynamic(). + * + * @param this called object + * @param host host_t specifying the address + */ + void (*set_address) (traffic_selector_t *this, host_t* host); + + /** + * @brief Compare two traffic selectors for equality. + * + * @param this first to compare + * @param other second to compare with first + * @return pointer to a string. + */ + bool (*equals) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a traffic selector is contained completly in another. + * + * contains() allows to check if multiple traffic selectors are redundant. + * + * @param this ts that is contained in another + * @param other ts that contains this + * @return TRUE if other contains this completly, FALSE otherwise + */ + bool (*is_contained_in) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a specific host is included in the address range of + * this traffic selector. + * + * @param this called object + * @param host the host to check + */ + bool (*includes) (traffic_selector_t *this, host_t *host); + + /** + * @brief Destroys the ts object + * + * @param this called object + */ + void (*destroy) (traffic_selector_t *this); +}; + +/** + * @brief Create a new traffic selector using human readable params. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_addr start of address range as string + * @param from_port port number in host order + * @param to_addr end of address range as string + * @param to_port port number in host order + * @return + * - traffic_selector_t object + * - NULL if invalid address strings/protocol + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port); + +/** + * @brief Create a new traffic selector using data read from the net. + * + * There exists a mix of network and host order in the params. + * But the parser gives us this data in this format, so we + * don't have to convert twice. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_address start of address range, network order + * @param from_port port number, host order + * @param to_address end of address range as string, network + * @param to_port port number, host order + * @return traffic_selector_t object + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_bytes( + u_int8_t protocol, ts_type_t type, + chunk_t from_address, u_int16_t from_port, + chunk_t to_address, u_int16_t to_port); + +/** + * @brief Create a new traffic selector defining a whole subnet. + * + * In most cases, definition of a traffic selector for full subnets + * is sufficient. This constructor creates a traffic selector for + * all protocols, all ports and the address range specified by the + * subnet. + * Additionally, a protocol and a port may be specified. Port ranges + * are not supported via this constructor. + * + * @param net subnet to use + * @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation + * @return + * - traffic_selector_t object + * - NULL if address family of net not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_subnet( + host_t *net, u_int8_t netbits, + u_int8_t protocol, u_int16_t port); + +/** + * @brief Create a traffic selector for host-to-host cases. + * + * For host2host or virtual IP setups, the traffic selectors gets + * created at runtime using the external/virtual IP. Using this constructor, + * a call to set_address() sets this traffic selector to the supplied host. + * + * + * @param protocol upper layer protocl to allow + * @param type family type + * @param from_port start of allowed port range + * @param to_port end of range + * @return + * - traffic_selector_t object + * - NULL if type not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port); + +#endif /* TRAFFIC_SELECTOR_H_ */ + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/daemon.c b/src/charon/daemon.c new file mode 100644 index 000000000..7671aea86 --- /dev/null +++ b/src/charon/daemon.c @@ -0,0 +1,529 @@ +/** + * @file daemon.c + * + * @brief Implementation of daemon_t and main of IKEv2-Daemon. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdio.h> +#include <signal.h> +#include <pthread.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#ifdef HAVE_BACKTRACE +# include <execinfo.h> +#endif /* HAVE_BACKTRACE */ + +#include "daemon.h" + +#include <library.h> +#include <crypto/ca.h> +#include <utils/fetcher.h> +#include <config/credentials/local_credential_store.h> +#include <config/connections/local_connection_store.h> +#include <config/policies/local_policy_store.h> +#include <sa/authenticators/eap/eap_method.h> + + +typedef struct private_daemon_t private_daemon_t; + +/** + * Private additions to daemon_t, contains threads and internal functions. + */ +struct private_daemon_t { + /** + * Public members of daemon_t. + */ + daemon_t public; + + /** + * Signal set used for signal handling. + */ + sigset_t signal_set; + + /** + * The thread_id of main-thread. + */ + pthread_t main_thread_id; +}; + +/** + * One and only instance of the daemon. + */ +daemon_t *charon; + +/** + * hook in library for debugging messages + */ +extern void (*dbg) (int level, char *fmt, ...); + +/** + * Logging hook for library logs, spreads debug message over bus + */ +static void dbg_bus(int level, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + charon->bus->vsignal(charon->bus, DBG_LIB, level, fmt, args); + va_end(args); +} + +/** + * Logging hook for library logs, using stderr output + */ +static void dbg_stderr(int level, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "00[LIB] "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} + +/** + * Run the daemon and handle unix signals + */ +static void run(private_daemon_t *this) +{ + /* reselect signals for this thread */ + sigemptyset(&(this->signal_set)); + sigaddset(&(this->signal_set), SIGINT); + sigaddset(&(this->signal_set), SIGHUP); + sigaddset(&(this->signal_set), SIGTERM); + pthread_sigmask(SIG_BLOCK, &(this->signal_set), 0); + + while(TRUE) + { + int signal_number; + int error; + + error = sigwait(&(this->signal_set), &signal_number); + if(error) + { + DBG1(DBG_DMN, "error %d while waiting for a signal", error); + return; + } + switch (signal_number) + { + case SIGHUP: + { + DBG1(DBG_DMN, "signal of type SIGHUP received. Ignored"); + break; + } + case SIGINT: + { + DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down"); + return; + } + case SIGTERM: + DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down"); + return; + default: + { + DBG1(DBG_DMN, "unknown signal %d received. Ignored", signal_number); + break; + } + } + } +} + +/** + * Clean up all daemon resources + */ +static void destroy(private_daemon_t *this) +{ + /* destruction is a non trivial task, we need to follow + * a strict order to prevent threading issues! + * Kill active threads first, except the sender, as + * the killed IKE_SA want to send delete messages. + */ + /* we don't want to receive anything anymore... */ + DESTROY_IF(this->public.receiver); + /* ignore all incoming user requests */ + DESTROY_IF(this->public.stroke); + /* stop scheduing jobs */ + DESTROY_IF(this->public.scheduler); + /* stop processing jobs */ + DESTROY_IF(this->public.thread_pool); + /* shut down manager with all IKE SAs */ + DESTROY_IF(this->public.ike_sa_manager); + /* all child SAs should be down now, so kill kernel interface */ + DESTROY_IF(this->public.kernel_interface); + /* destroy other infrastructure */ + DESTROY_IF(this->public.job_queue); + DESTROY_IF(this->public.event_queue); + DESTROY_IF(this->public.configuration); + DESTROY_IF(this->public.credentials); + DESTROY_IF(this->public.connections); + DESTROY_IF(this->public.policies); + sched_yield(); + /* we hope the sender could send the outstanding deletes, but + * we shut down here at any cost */ + DESTROY_IF(this->public.sender); + DESTROY_IF(this->public.socket); + /* before destroying bus with its listeners, rehook library logs */ + dbg = dbg_stderr; + DESTROY_IF(this->public.bus); + DESTROY_IF(this->public.outlog); + DESTROY_IF(this->public.syslog); + DESTROY_IF(this->public.authlog); + free(this); +} + +/** + * Enforce daemon shutdown, with a given reason to do so. + */ +static void kill_daemon(private_daemon_t *this, char *reason) +{ + /* we send SIGTERM, so the daemon can cleanly shut down */ + DBG1(DBG_DMN, "killing daemon: %s", reason); + if (this->main_thread_id == pthread_self()) + { + /* initialization failed, terminate daemon */ + destroy(this); + unlink(PID_FILE); + exit(-1); + } + else + { + DBG1(DBG_DMN, "sending SIGTERM to ourself"); + raise(SIGTERM); + /* thread must die, since he produced a ciritcal failure and can't continue */ + pthread_exit(NULL); + } +} + +/** + * Initialize the daemon, optional with a strict crl policy + */ +static void initialize(private_daemon_t *this, bool strict, bool syslog, + level_t levels[]) +{ + credential_store_t* credentials; + signal_t signal; + + /* for uncritical pseudo random numbers */ + srandom(time(NULL) + getpid()); + + /* setup bus and it's listeners first to enable log output */ + this->public.bus = bus_create(); + this->public.outlog = file_logger_create(stdout); + this->public.syslog = sys_logger_create(LOG_DAEMON); + this->public.authlog = sys_logger_create(LOG_AUTHPRIV); + this->public.bus->add_listener(this->public.bus, &this->public.syslog->listener); + this->public.bus->add_listener(this->public.bus, &this->public.outlog->listener); + this->public.bus->add_listener(this->public.bus, &this->public.authlog->listener); + this->public.authlog->set_level(this->public.authlog, SIG_ANY, LEVEL_AUDIT); + /* set up hook to log dbg message in library via charons message bus */ + dbg = dbg_bus; + + /* apply loglevels */ + for (signal = 0; signal < DBG_MAX; signal++) + { + if (syslog) + { + this->public.syslog->set_level(this->public.syslog, + signal, levels[signal]); + } + else + { + this->public.outlog->set_level(this->public.outlog, + signal, levels[signal]); + } + } + + DBG1(DBG_DMN, "starting charon (strongSwan Version %s)", VERSION); + + this->public.configuration = configuration_create(); + this->public.socket = socket_create(IKEV2_UDP_PORT, IKEV2_NATT_PORT); + this->public.ike_sa_manager = ike_sa_manager_create(); + this->public.job_queue = job_queue_create(); + this->public.event_queue = event_queue_create(); + this->public.connections = (connection_store_t*)local_connection_store_create(); + this->public.policies = (policy_store_t*)local_policy_store_create(); + this->public.credentials = (credential_store_t*)local_credential_store_create(strict); + + /* initialize fetcher_t class */ + fetcher_initialize(); + + /* load secrets, ca certificates and crls */ + credentials = this->public.credentials; + credentials->load_ca_certificates(credentials); + credentials->load_ocsp_certificates(credentials); + credentials->load_crls(credentials); + credentials->load_secrets(credentials); + + /* start building threads, we are multi-threaded NOW */ + this->public.stroke = stroke_create(); + this->public.sender = sender_create(); + this->public.receiver = receiver_create(); + this->public.scheduler = scheduler_create(); + this->public.kernel_interface = kernel_interface_create(); + this->public.thread_pool = thread_pool_create(NUMBER_OF_WORKING_THREADS); +} + +/** + * Handle SIGSEGV/SIGILL signals raised by threads + */ +void signal_handler(int signal) +{ +#ifdef HAVE_BACKTRACE + void *array[20]; + size_t size; + char **strings; + size_t i; + + size = backtrace(array, 20); + strings = backtrace_symbols(array, size); + + DBG1(DBG_DMN, "thread %u received %s. Dumping %d frames from stack:", + pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL", size); + + for (i = 0; i < size; i++) + { + DBG1(DBG_DMN, " %s", strings[i]); + } + free (strings); +#else /* !HAVE_BACKTRACE */ + DBG1(DBG_DMN, "thread %u received %s", + pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL"); +#endif /* HAVE_BACKTRACE */ + DBG1(DBG_DMN, "killing ourself hard after SIGSEGV"); + raise(SIGKILL); +} + +/** + * Create the daemon. + */ +private_daemon_t *daemon_create(void) +{ + private_daemon_t *this = malloc_thing(private_daemon_t); + struct sigaction action; + + /* assign methods */ + this->public.kill = (void (*) (daemon_t*,char*))kill_daemon; + + /* NULL members for clean destruction */ + this->public.socket = NULL; + this->public.ike_sa_manager = NULL; + this->public.job_queue = NULL; + this->public.event_queue = NULL; + this->public.configuration = NULL; + this->public.credentials = NULL; + this->public.connections = NULL; + this->public.policies = NULL; + this->public.sender= NULL; + this->public.receiver = NULL; + this->public.scheduler = NULL; + this->public.kernel_interface = NULL; + this->public.thread_pool = NULL; + this->public.stroke = NULL; + this->public.bus = NULL; + this->public.outlog = NULL; + this->public.syslog = NULL; + this->public.authlog = NULL; + + this->main_thread_id = pthread_self(); + + /* setup signal handling for all threads */ + sigemptyset(&(this->signal_set)); + sigaddset(&(this->signal_set), SIGSEGV); + sigaddset(&(this->signal_set), SIGINT); + sigaddset(&(this->signal_set), SIGHUP); + sigaddset(&(this->signal_set), SIGTERM); + pthread_sigmask(SIG_BLOCK, &(this->signal_set), 0); + + /* setup SIGSEGV handler for all threads */ + action.sa_handler = signal_handler; + action.sa_mask = this->signal_set; + action.sa_flags = 0; + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGILL, &action, NULL); + return this; +} + +/** + * print command line usage and exit + */ +static void usage(const char *msg) +{ + if (msg != NULL && *msg != '\0') + { + fprintf(stderr, "%s\n", msg); + } + fprintf(stderr, "Usage: charon\n" + " [--help]\n" + " [--version]\n" + " [--strictcrlpolicy]\n" + " [--cachecrls]\n" + " [--crlcheckinterval <interval>]\n" + " [--eapdir <dir>]\n" + " [--use-syslog]\n" + " [--debug-<type> <level>]\n" + " <type>: log context type (dmn|mgr|ike|chd|job|cfg|knl|net|enc|lib)\n" + " <level>: log verbosity (-1 = silent, 0 = audit, 1 = control,\n" + " 2 = controlmore, 3 = raw, 4 = private)\n" + "\n" + ); + exit(msg == NULL? 0 : 1); +} + +/** + * Main function, manages the daemon. + */ +int main(int argc, char *argv[]) +{ + u_int crl_check_interval = 0; + bool strict_crl_policy = FALSE; + bool cache_crls = FALSE; + bool use_syslog = FALSE; + char *eapdir = IPSEC_EAPDIR; + + private_daemon_t *private_charon; + FILE *pid_file; + struct stat stb; + linked_list_t *list; + host_t *host; + level_t levels[DBG_MAX]; + int signal; + + /* use CTRL loglevel for default */ + for (signal = 0; signal < DBG_MAX; signal++) + { + levels[signal] = LEVEL_CTRL; + } + + /* handle arguments */ + for (;;) + { + struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "use-syslog", no_argument, NULL, 'l' }, + { "strictcrlpolicy", no_argument, NULL, 'r' }, + { "cachecrls", no_argument, NULL, 'C' }, + { "crlcheckinterval", required_argument, NULL, 'x' }, + { "eapdir", required_argument, NULL, 'e' }, + /* TODO: handle "debug-all" */ + { "debug-dmn", required_argument, &signal, DBG_DMN }, + { "debug-mgr", required_argument, &signal, DBG_MGR }, + { "debug-ike", required_argument, &signal, DBG_IKE }, + { "debug-chd", required_argument, &signal, DBG_CHD }, + { "debug-job", required_argument, &signal, DBG_JOB }, + { "debug-cfg", required_argument, &signal, DBG_CFG }, + { "debug-knl", required_argument, &signal, DBG_KNL }, + { "debug-net", required_argument, &signal, DBG_NET }, + { "debug-enc", required_argument, &signal, DBG_ENC }, + { "debug-lib", required_argument, &signal, DBG_LIB }, + { 0,0,0,0 } + }; + + int c = getopt_long(argc, argv, "", long_opts, NULL); + switch (c) + { + case EOF: + break; + case 'h': + usage(NULL); + break; + case 'v': + printf("Linux strongSwan %s\n", VERSION); + exit(0); + case 'l': + use_syslog = TRUE; + continue; + case 'r': + strict_crl_policy = TRUE; + continue; + case 'C': + cache_crls = TRUE; + continue; + case 'x': + crl_check_interval = atoi(optarg); + continue; + case 'e': + eapdir = optarg; + continue; + case 0: + /* option is in signal */ + levels[signal] = atoi(optarg); + continue; + default: + usage(""); + break; + } + break; + } + + private_charon = daemon_create(); + charon = (daemon_t*)private_charon; + + /* initialize daemon */ + initialize(private_charon, strict_crl_policy, use_syslog, levels); + + /* load pluggable EAP modules */ + eap_method_load(eapdir); + + /* set cache_crls and crl_check_interval options */ + ca_info_set_options(cache_crls, crl_check_interval); + + /* check/setup PID file */ + if (stat(PID_FILE, &stb) == 0) + { + DBG1(DBG_DMN, "charon already running (\""PID_FILE"\" exists)"); + destroy(private_charon); + exit(-1); + } + pid_file = fopen(PID_FILE, "w"); + if (pid_file) + { + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + } + + /* log socket info */ + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + DBG1(DBG_NET, "listening on %d addresses:", list->get_count(list)); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + DBG1(DBG_NET, " %H", host); + host->destroy(host); + } + list->destroy(list); + + /* run daemon */ + run(private_charon); + + eap_method_unload(); + fetcher_finalize(); + /* normal termination, cleanup and exit */ + destroy(private_charon); + unlink(PID_FILE); + + return 0; +} diff --git a/src/charon/daemon.h b/src/charon/daemon.h new file mode 100644 index 000000000..420262474 --- /dev/null +++ b/src/charon/daemon.h @@ -0,0 +1,403 @@ +/** + * @file daemon.h + * + * @brief Interface of daemon_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef DAEMON_H_ +#define DAEMON_H_ + +typedef struct daemon_t daemon_t; + +#include <credential_store.h> + +#include <threads/sender.h> +#include <threads/receiver.h> +#include <threads/scheduler.h> +#include <threads/kernel_interface.h> +#include <threads/thread_pool.h> +#include <threads/stroke_interface.h> +#include <network/socket.h> +#include <bus/bus.h> +#include <bus/listeners/file_logger.h> +#include <bus/listeners/sys_logger.h> +#include <sa/ike_sa_manager.h> +#include <queues/job_queue.h> +#include <queues/event_queue.h> +#include <config/configuration.h> +#include <config/connections/connection_store.h> +#include <config/policies/policy_store.h> + +/** + * @defgroup charon charon + * + * @brief IKEv2 keying daemon. + * + * @section Architecture + * + * All IKEv2 stuff is handled in charon. It uses a newer and more flexible + * architecture than pluto. Charon uses a thread-pool, which allows parallel + * execution SA-management. Beside the thread-pool, there are some special purpose + * threads which do their job for the common health of the daemon. + @verbatim + +------+ + | E Q | + | v u |---+ +------+ +------+ + | e e | | | | | IKE- | + | n u | +-----------+ | |--| SA | + | t e | | | | I M | +------+ + +------------+ | - | | Scheduler | | K a | + | receiver | +------+ | | | E n | +------+ + +----+-------+ +-----------+ | - a | | IKE- | + | | +------+ | | S g |--| SA | + +-------+--+ +-----| J Q |---+ +------------+ | A e | +------+ + -| socket | | o u | | | | - r | + +-------+--+ | b e | | Thread- | | | + | | - u | | Pool | | | + +----+-------+ | e |------| |---| | + | sender | +------+ +------------+ +------+ + +------------+ + + @endverbatim + * The thread-pool is the heart of the architecture. It processes jobs from a + * (fully synchronized) job-queue. Mostly, a job is associated with a specific + * IKE SA. These IKE SAs are synchronized, only one thread can work one an IKE SA. + * This makes it unnecesary to use further synchronisation methods once a IKE SA + * is checked out. The (rather complex) synchronization of IKE SAs is completely + * done in the IKE SA manager. + * The sceduler is responsible for event firing. It waits until a event in the + * (fully synchronized) event-queue is ready for processing and pushes the event + * down to the job-queue. A thread form the pool will pick it up as quick as + * possible. Every thread can queue events or jobs. Furter, an event can place a + * packet in the sender. The sender thread waits for those packets and sends + * them over the wire, via the socket. The receiver does exactly the opposite of + * the sender. It waits on the socket, reads in packets an places them on the + * job-queue for further processing by a thread from the pool. + * There are even more threads, not drawn in the upper scheme. The stroke thread + * is responsible for reading and processessing commands from another process. The + * kernel interface thread handles communication from and to the kernel via a + * netlink socket. It waits for kernel events and processes them appropriately. + */ + +/** + * @defgroup config config + * + * Classes implementing configuration related things. + * + * @ingroup charon + */ + +/** + * @defgroup encoding encoding + * + * Classes used to encode and decode IKEv2 messages. + * + * @ingroup charon + */ + + /** + * @defgroup payloads payloads + * + * Classes representing specific IKEv2 payloads. + * + * @ingroup encoding + */ + +/** + * @defgroup network network + * + * Classes for network relevant stuff. + * + * @ingroup charon + */ + +/** + * @defgroup queues queues + * + * Different kind of queues + * (thread save lists). + * + * @ingroup charon + */ + +/** + * @defgroup jobs jobs + * + * Jobs used in job queue and event queue. + * + * @ingroup queues + */ + +/** + * @defgroup sa sa + * + * Security associations for IKE and IPSec, + * and some helper classes. + * + * @ingroup charon + */ + +/** + * @defgroup tasks tasks + * + * Tasks process and build message payloads. They are used to create + * and process multiple exchanges. + * + * @ingroup sa + */ + +/** + * @defgroup authenticators authenticators + * + * Authenticator classes to prove identity of peer. + * + * @ingroup sa + */ + +/** + * @defgroup eap eap + * + * EAP authentication module interface and it's implementations. + * + * @ingroup authenticators + */ + +/** + * @defgroup threads threads + * + * Threaded classes, which will do their job alone. + * + * @ingroup charon + */ + +/** + * @defgroup bus bus + * + * Signaling bus and its listeners. + * + * @ingroup charon + */ + +/** + * Name of the daemon. + * + * @ingroup charon + */ +#define DAEMON_NAME "charon" + +/** + * @brief Number of threads in the thread pool. + * + * There are several other threads, this defines + * only the number of threads in thread_pool_t. + * + * @ingroup charon + */ +#define NUMBER_OF_WORKING_THREADS 4 + +/** + * UDP Port on which the daemon will listen for incoming traffic. + * + * @ingroup charon + */ +#define IKEV2_UDP_PORT 500 + +/** + * UDP Port to which the daemon will float to if NAT is detected. + * + * @ingroup charon + */ +#define IKEV2_NATT_PORT 4500 + +/** + * PID file, in which charon stores its process id + * + * @ingroup charon + */ +#define PID_FILE IPSEC_PIDDIR "/charon.pid" + +/** + * Configuration directory + * + * @ingroup charon + */ +#define CONFIG_DIR IPSEC_CONFDIR + +/** + * Directory of IPsec relevant files + * + * @ingroup charon + */ +#define IPSEC_D_DIR CONFIG_DIR "/ipsec.d" + +/** + * Default directory for private keys + * + * @ingroup charon + */ +#define PRIVATE_KEY_DIR IPSEC_D_DIR "/private" + +/** + * Default directory for end entity certificates + * + * @ingroup charon + */ +#define CERTIFICATE_DIR IPSEC_D_DIR "/certs" + +/** + * Default directory for trusted CA certificates + * + * @ingroup charon + */ +#define CA_CERTIFICATE_DIR IPSEC_D_DIR "/cacerts" + +/** + * Default directory for OCSP signing certificates + * + * @ingroup charon + */ +#define OCSP_CERTIFICATE_DIR IPSEC_D_DIR "/ocspcerts" + +/** + * Default directory for CRLs + * + * @ingroup charon + */ +#define CRL_DIR IPSEC_D_DIR "/crls" + +/** + * Secrets files + * + * @ingroup charon + */ +#define SECRETS_FILE CONFIG_DIR "/ipsec.secrets" + +/** + * @brief Main class of daemon, contains some globals. + * + * @ingroup charon + */ +struct daemon_t { + /** + * A socket_t instance. + */ + socket_t *socket; + + /** + * A job_queue_t instance. + */ + job_queue_t *job_queue; + + /** + * A event_queue_t instance. + */ + event_queue_t *event_queue; + + /** + * A ike_sa_manager_t instance. + */ + ike_sa_manager_t *ike_sa_manager; + + /** + * A configuration_t instance. + */ + configuration_t *configuration; + + /** + * A connection_store_t instance. + */ + connection_store_t *connections; + + /** + * A policy_store_t instance. + */ + policy_store_t *policies; + + /** + * A credential_store_t instance. + */ + credential_store_t *credentials; + + /** + * The Sender-Thread. + */ + sender_t *sender; + + /** + * The Receiver-Thread. + */ + receiver_t *receiver; + + /** + * The Scheduler-Thread. + */ + scheduler_t *scheduler; + + /** + * The Thread pool managing the worker threads. + */ + thread_pool_t *thread_pool; + + /** + * The signaling bus. + */ + bus_t *bus; + + /** + * A bus listener logging to stdout + */ + file_logger_t *outlog; + + /** + * A bus listener logging to syslog + */ + sys_logger_t *syslog; + + /** + * A bus listener logging most important events + */ + sys_logger_t *authlog; + + /** + * Kernel Interface to communicate with kernel + */ + kernel_interface_t *kernel_interface; + + /** + * IPC interface, as whack in pluto + */ + stroke_t *stroke; + + /** + * @brief Shut down the daemon. + * + * @param this the daemon to kill + * @param reason describtion why it will be killed + */ + void (*kill) (daemon_t *this, char *reason); +}; + +/** + * The one and only instance of the daemon. + */ +extern daemon_t *charon; + +#endif /*DAEMON_H_*/ diff --git a/src/charon/encoding/generator.c b/src/charon/encoding/generator.c new file mode 100644 index 000000000..efa845bb3 --- /dev/null +++ b/src/charon/encoding/generator.c @@ -0,0 +1,1063 @@ +/** + * @file generator.c + * + * @brief Implementation of generator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include <stdio.h> + + +#include "generator.h" + +#include <library.h> +#include <daemon.h> +#include <utils/linked_list.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/proposal_substructure.h> +#include <encoding/payloads/transform_substructure.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <encoding/payloads/vendor_id_payload.h> +#include <encoding/payloads/cp_payload.h> +#include <encoding/payloads/configuration_attribute.h> +#include <encoding/payloads/eap_payload.h> + + +typedef struct private_generator_t private_generator_t; + +/** + * Private part of a generator_t object. + */ +struct private_generator_t { + /** + * Public part of a generator_t object. + */ + generator_t public; + + /** + * Generates a U_INT-Field type and writes it to buffer. + * + * @param this private_generator_t object + * @param int_type type of U_INT field (U_INT_4, U_INT_8, etc.) + * ATTRIBUTE_TYPE is also generated in this function + * @param offset offset of value in data struct + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @return + * - SUCCESS + * - FAILED if allignment is wrong + */ + void (*generate_u_int_type) (private_generator_t *this,encoding_type_t int_type,u_int32_t offset); + + /** + * Get size of current buffer in bytes. + * + * @param this private_generator_t object + * @return Size of buffer in bytes + */ + size_t (*get_current_buffer_size) (private_generator_t *this); + + /** + * Get free space of current buffer in bytes. + * + * @param this private_generator_t object + * @return space in buffer in bytes + */ + size_t (*get_current_buffer_space) (private_generator_t *this); + + /** + * Get length of data in buffer (in bytes). + * + * @param this private_generator_t object + * @return length of data in bytes + */ + size_t (*get_current_data_length) (private_generator_t *this); + + /** + * Get current offset in buffer (in bytes). + * + * @param this private_generator_t object + * @return offset in bytes + */ + u_int32_t (*get_current_buffer_offset) (private_generator_t *this); + + /** + * Generates a RESERVED BIT field or a RESERVED BYTE field and writes + * it to the buffer. + * + * @param this private_generator_t object + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @param bits number of bits to generate + */ + void (*generate_reserved_field) (private_generator_t *this,int bits); + + /** + * Generates a FLAG field. + * + * @param this private_generator_t object + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @param offset offset of flag value in data struct + */ + void (*generate_flag) (private_generator_t *this,u_int32_t offset); + + /** + * Writes the current buffer content into a chunk_t. + * + * Memory of specific chunk_t gets allocated. + * + * @param this calling private_generator_t object + * @param data pointer of chunk_t to write to + */ + void (*write_chunk) (private_generator_t *this,chunk_t *data); + + /** + * Generates a bytestream from a chunk_t. + * + * @param this private_generator_t object + * @param offset offset of chunk_t value in data struct + */ + void (*generate_from_chunk) (private_generator_t *this,u_int32_t offset); + + /** + * Makes sure enough space is available in buffer to store amount of bits. + * + * If buffer is to small to hold the specific amount of bits it + * is increased using reallocation function of allocator. + * + * @param this calling private_generator_t object + * @param bits number of bits to make available in buffer + */ + void (*make_space_available) (private_generator_t *this,size_t bits); + + /** + * Writes a specific amount of byte into the buffer. + * + * If buffer is to small to hold the specific amount of bytes it + * is increased. + * + * @param this calling private_generator_t object + * @param bytes pointer to bytes to write + * @param number_of_bytes number of bytes to write into buffer + */ + void (*write_bytes_to_buffer) (private_generator_t *this,void * bytes,size_t number_of_bytes); + + + /** + * Writes a specific amount of byte into the buffer at a specific offset. + * + * @warning buffer size is not check to hold the data if offset is to large. + * + * @param this calling private_generator_t object + * @param bytes pointer to bytes to write + * @param number_of_bytes number of bytes to write into buffer + * @param offset offset to write the data into + */ + void (*write_bytes_to_buffer_at_offset) (private_generator_t *this,void * bytes,size_t number_of_bytes,u_int32_t offset); + + /** + * Buffer used to generate the data into. + */ + u_int8_t *buffer; + + /** + * Current write position in buffer (one byte aligned). + */ + u_int8_t *out_position; + + /** + * Position of last byte in buffer. + */ + u_int8_t *roof_position; + + /** + * Current bit writing to in current byte (between 0 and 7). + */ + size_t current_bit; + + /** + * Associated data struct to read informations from. + */ + void * data_struct; + + /* + * Last payload length position offset in the buffer. + */ + u_int32_t last_payload_length_position_offset; + + /** + * Offset of the header length field in the buffer. + */ + u_int32_t header_length_position_offset; + + /** + * Last SPI size. + */ + u_int8_t last_spi_size; + + /** + * Attribute format of the last generated transform attribute. + * + * Used to check if a variable value field is used or not for + * the transform attribute value. + */ + bool attribute_format; + + /** + * Depending on the value of attribute_format this field is used + * to hold the length of the transform attribute in bytes. + */ + u_int16_t attribute_length; +}; + +/** + * Implementation of private_generator_t.get_current_buffer_size. + */ +static size_t get_current_buffer_size (private_generator_t *this) +{ + return ((this->roof_position) - (this->buffer)); +} + +/** + * Implementation of private_generator_t.get_current_buffer_space. + */ +static size_t get_current_buffer_space (private_generator_t *this) +{ + /* we know, one byte more */ + size_t space = (this->roof_position) - (this->out_position); + return (space); +} + +/** + * Implementation of private_generator_t.get_current_data_length. + */ +static size_t get_current_data_length (private_generator_t *this) +{ + return (this->out_position - this->buffer); +} + +/** + * Implementation of private_generator_t.get_current_buffer_offset. + */ +static u_int32_t get_current_buffer_offset (private_generator_t *this) +{ + return (this->out_position - this->buffer); +} + +/** + * Implementation of private_generator_t.generate_u_int_type. + */ +static void generate_u_int_type (private_generator_t *this,encoding_type_t int_type,u_int32_t offset) +{ + size_t number_of_bits = 0; + + /* find out number of bits of each U_INT type to check for enough space + in buffer */ + switch (int_type) + { + case U_INT_4: + number_of_bits = 4; + break; + case TS_TYPE: + case U_INT_8: + number_of_bits = 8; + break; + case U_INT_16: + case CONFIGURATION_ATTRIBUTE_LENGTH: + number_of_bits = 16; + break; + case U_INT_32: + number_of_bits = 32; + break; + case U_INT_64: + number_of_bits = 64; + break; + case ATTRIBUTE_TYPE: + number_of_bits = 15; + break; + case IKE_SPI: + number_of_bits = 64; + break; + + default: + DBG1(DBG_ENC, "U_INT Type %N is not supported", + encoding_type_names, int_type); + + return; + } + /* U_INT Types of multiple then 8 bits must be aligned */ + if (((number_of_bits % 8) == 0) && (this->current_bit != 0)) + { + DBG1(DBG_ENC, "U_INT Type %N is not 8 Bit aligned", + encoding_type_names, int_type); + /* current bit has to be zero for values multiple of 8 bits */ + return; + } + + /* make sure enough space is available in buffer */ + this->make_space_available(this,number_of_bits); + /* now handle each u int type differently */ + switch (int_type) + { + case U_INT_4: + { + if (this->current_bit == 0) + { + /* highval of current byte in buffer has to be set to the new value*/ + u_int8_t high_val = *((u_int8_t *)(this->data_struct + offset)) << 4; + /* lowval in buffer is not changed */ + u_int8_t low_val = *(this->out_position) & 0x0F; + /* highval is set, low_val is not changed */ + *(this->out_position) = high_val | low_val; + DBG3(DBG_ENC, " => %d", *(this->out_position)); + /* write position is not changed, just bit position is moved */ + this->current_bit = 4; + } + else if (this->current_bit == 4) + { + /* highval in buffer is not changed */ + u_int high_val = *(this->out_position) & 0xF0; + /* lowval of current byte in buffer has to be set to the new value*/ + u_int low_val = *((u_int8_t *)(this->data_struct + offset)) & 0x0F; + *(this->out_position) = high_val | low_val; + DBG3(DBG_ENC, " => %d", *(this->out_position)); + this->out_position++; + this->current_bit = 0; + + } + else + { + DBG1(DBG_ENC, "U_INT_4 Type is not 4 Bit aligned"); + /* 4 Bit integers must have a 4 bit alignment */ + return; + }; + break; + } + case TS_TYPE: + case U_INT_8: + { + /* 8 bit values are written as they are */ + *this->out_position = *((u_int8_t *)(this->data_struct + offset)); + DBG3(DBG_ENC, " => %d", *(this->out_position)); + this->out_position++; + break; + + } + case ATTRIBUTE_TYPE: + { + /* attribute type must not change first bit uf current byte ! */ + if (this->current_bit != 1) + { + DBG1(DBG_ENC, "ATTRIBUTE FORMAT flag is not set"); + /* first bit has to be set! */ + return; + } + /* get value of attribute format flag */ + u_int8_t attribute_format_flag = *(this->out_position) & 0x80; + /* get attribute type value as 16 bit integer*/ + u_int16_t int16_val = *((u_int16_t*)(this->data_struct + offset)); + /* unset most significant bit */ + int16_val &= 0x7FFF; + if (attribute_format_flag) + { + int16_val |= 0x8000; + } + int16_val = htons(int16_val); + DBG3(DBG_ENC, " => %d", int16_val); + /* write bytes to buffer (set bit is overwritten)*/ + this->write_bytes_to_buffer(this,&int16_val,sizeof(u_int16_t)); + this->current_bit = 0; + break; + + } + case U_INT_16: + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + u_int16_t int16_val = htons(*((u_int16_t*)(this->data_struct + offset))); + DBG3(DBG_ENC, " => %b", (void*)&int16_val, sizeof(int16_val)); + this->write_bytes_to_buffer(this,&int16_val,sizeof(u_int16_t)); + break; + } + case U_INT_32: + { + u_int32_t int32_val = htonl(*((u_int32_t*)(this->data_struct + offset))); + DBG3(DBG_ENC, " => %b", (void*)&int32_val, sizeof(int32_val)); + this->write_bytes_to_buffer(this,&int32_val,sizeof(u_int32_t)); + break; + } + case U_INT_64: + { + /* 64 bit integers are written as two 32 bit integers */ + u_int32_t int32_val_low = htonl(*((u_int32_t*)(this->data_struct + offset))); + u_int32_t int32_val_high = htonl(*((u_int32_t*)(this->data_struct + offset) + 1)); + DBG3(DBG_ENC, " => %b %b", + (void*)&int32_val_low, sizeof(int32_val_low), + (void*)&int32_val_high, sizeof(int32_val_high)); + /* TODO add support for big endian machines */ + this->write_bytes_to_buffer(this,&int32_val_high,sizeof(u_int32_t)); + this->write_bytes_to_buffer(this,&int32_val_low,sizeof(u_int32_t)); + break; + } + + case IKE_SPI: + { + /* 64 bit are written as they come :-) */ + this->write_bytes_to_buffer(this,(this->data_struct + offset),sizeof(u_int64_t)); + DBG3(DBG_ENC, " => %b", (void*)(this->data_struct + offset), sizeof(u_int64_t)); + break; + } + default: + { + DBG1(DBG_ENC, "U_INT Type %N is not supported", + encoding_type_names, int_type); + return; + } + } +} + +/** + * Implementation of private_generator_t.generate_reserved_field. + */ +static void generate_reserved_field(private_generator_t *this,int bits) +{ + /* only one bit or 8 bit fields are supported */ + if ((bits != 1) && (bits != 8)) + { + DBG1(DBG_ENC, "reserved field of %d bits cannot be generated", bits); + return ; + } + /* make sure enough space is available in buffer */ + this->make_space_available(this,bits); + + if (bits == 1) + { + /* one bit processing */ + u_int8_t reserved_bit = ~(1 << (7 - this->current_bit)); + *(this->out_position) = *(this->out_position) & reserved_bit; + if (this->current_bit == 0) + { + /* memory must be zero */ + *(this->out_position) = 0x00; + } + + + this->current_bit++; + if (this->current_bit >= 8) + { + this->current_bit = this->current_bit % 8; + this->out_position++; + } + } + else + { + /* one byte processing*/ + if (this->current_bit > 0) + { + DBG1(DBG_ENC, "reserved field cannot be written cause " + "alignement of current bit is %d", this->current_bit); + return; + } + *(this->out_position) = 0x00; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.generate_flag. + */ +static void generate_flag (private_generator_t *this,u_int32_t offset) +{ + /* value of current flag */ + u_int8_t flag_value; + /* position of flag in current byte */ + u_int8_t flag; + + /* if the value in the data_struct is TRUE, flag_value is set to 1, 0 otherwise */ + flag_value = (*((bool *) (this->data_struct + offset))) ? 1 : 0; + /* get flag position */ + flag = (flag_value << (7 - this->current_bit)); + + /* make sure one bit is available in buffer */ + this->make_space_available(this,1); + if (this->current_bit == 0) + { + /* memory must be zero */ + *(this->out_position) = 0x00; + } + + *(this->out_position) = *(this->out_position) | flag; + + + DBG3(DBG_ENC, " => %d", *(this->out_position)); + + this->current_bit++; + if (this->current_bit >= 8) + { + this->current_bit = this->current_bit % 8; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.generate_from_chunk. + */ +static void generate_from_chunk (private_generator_t *this,u_int32_t offset) +{ + if (this->current_bit != 0) + { + DBG1(DBG_ENC, "can not generate a chunk at Bitpos %d", this->current_bit); + return ; + } + + /* position in buffer */ + chunk_t *attribute_value = (chunk_t *)(this->data_struct + offset); + + DBG3(DBG_ENC, " => %B", attribute_value); + + /* use write_bytes_to_buffer function to do the job */ + this->write_bytes_to_buffer(this,attribute_value->ptr,attribute_value->len); +} + +/** + * Implementation of private_generator_t.make_space_available. + */ +static void make_space_available (private_generator_t *this, size_t bits) +{ + while (((this->get_current_buffer_space(this) * 8) - this->current_bit) < bits) + { + /* must increase buffer */ + size_t old_buffer_size = this->get_current_buffer_size(this); + size_t new_buffer_size = old_buffer_size + GENERATOR_DATA_BUFFER_INCREASE_VALUE; + size_t out_position_offset = ((this->out_position) - (this->buffer)); + + DBG2(DBG_ENC, "increased gen buffer from %d to %d byte", + old_buffer_size, new_buffer_size); + + /* Reallocate space for new buffer */ + this->buffer = realloc(this->buffer,new_buffer_size); + + this->out_position = (this->buffer + out_position_offset); + this->roof_position = (this->buffer + new_buffer_size); + } +} + +/** + * Implementation of private_generator_t.write_bytes_to_buffer. + */ +static void write_bytes_to_buffer (private_generator_t *this,void * bytes, size_t number_of_bytes) +{ + int i; + u_int8_t *read_position = (u_int8_t *) bytes; + + this->make_space_available(this,number_of_bytes * 8); + + for (i = 0; i < number_of_bytes; i++) + { + *(this->out_position) = *(read_position); + read_position++; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.write_bytes_to_buffer_at_offset. + */ +static void write_bytes_to_buffer_at_offset (private_generator_t *this,void * bytes,size_t number_of_bytes,u_int32_t offset) +{ + int i; + u_int8_t *read_position = (u_int8_t *) bytes; + u_int8_t *write_position; + u_int32_t free_space_after_offset = (this->get_current_buffer_size(this) - offset); + + /* check first if enough space for new data is available */ + if (number_of_bytes > free_space_after_offset) + { + this->make_space_available(this,(number_of_bytes - free_space_after_offset) * 8); + } + + write_position = this->buffer + offset; + for (i = 0; i < number_of_bytes; i++) + { + *(write_position) = *(read_position); + read_position++; + write_position++; + } +} + +/** + * Implementation of private_generator_t.write_to_chunk. + */ +static void write_to_chunk (private_generator_t *this,chunk_t *data) +{ + size_t data_length = this->get_current_data_length(this); + u_int32_t header_length_field = data_length; + + /* write length into header length field */ + if (this->header_length_position_offset > 0) + { + u_int32_t int32_val = htonl(header_length_field); + this->write_bytes_to_buffer_at_offset(this,&int32_val,sizeof(u_int32_t),this->header_length_position_offset); + } + + if (this->current_bit > 0) + data_length++; + data->ptr = malloc(data_length); + memcpy(data->ptr,this->buffer,data_length); + data->len = data_length; + + DBG3(DBG_ENC, "generated data of this generator %B", data); +} + +/** + * Implementation of private_generator_t.generate_payload. + */ +static void generate_payload (private_generator_t *this,payload_t *payload) +{ + int i; + this->data_struct = payload; + size_t rule_count; + encoding_rule_t *rules; + payload_type_t payload_type; + u_int8_t *payload_start; + + /* get payload type */ + payload_type = payload->get_type(payload); + /* spi size has to get reseted */ + this->last_spi_size = 0; + + payload_start = this->out_position; + + DBG2(DBG_ENC, "generating payload of type %N", + payload_type_names, payload_type); + + /* each payload has its own encoding rules */ + payload->get_encoding_rules(payload,&rules,&rule_count); + + for (i = 0; i < rule_count;i++) + { + DBG2(DBG_ENC, " generating rule %d %N", + i, encoding_type_names, rules[i].type); + switch (rules[i].type) + { + /* all u int values, IKE_SPI,TS_TYPE and ATTRIBUTE_TYPE are generated in generate_u_int_type */ + case U_INT_4: + case U_INT_8: + case U_INT_16: + case U_INT_32: + case U_INT_64: + case IKE_SPI: + case TS_TYPE: + case ATTRIBUTE_TYPE: + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + this->generate_u_int_type(this,rules[i].type,rules[i].offset); + break; + } + case RESERVED_BIT: + { + this->generate_reserved_field(this,1); + break; + } + case RESERVED_BYTE: + { + this->generate_reserved_field(this,8); + break; + } + case FLAG: + { + this->generate_flag(this,rules[i].offset); + break; + } + case PAYLOAD_LENGTH: + { + /* position of payload lenght field is temporary stored */ + this->last_payload_length_position_offset = this->get_current_buffer_offset(this); + /* payload length is generated like an U_INT_16 */ + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + break; + } + case HEADER_LENGTH: + { + /* position of header length field is temporary stored */ + this->header_length_position_offset = this->get_current_buffer_offset(this); + /* header length is generated like an U_INT_32 */ + this->generate_u_int_type(this,U_INT_32,rules[i].offset); + break; + } + case SPI_SIZE: + /* spi size is handled as 8 bit unsigned integer */ + this->generate_u_int_type(this,U_INT_8,rules[i].offset); + /* last spi size is temporary stored */ + this->last_spi_size = *((u_int8_t *)(this->data_struct + rules[i].offset)); + break; + case ADDRESS: + { + /* the Address value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + break; + } + case SPI: + { + /* the SPI value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + break; + } + case KEY_EXCHANGE_DATA: + case NOTIFICATION_DATA: + case NONCE_DATA: + case ID_DATA: + case AUTH_DATA: + case CERT_DATA: + case CERTREQ_DATA: + case SPIS: + case CONFIGURATION_ATTRIBUTE_VALUE: + case VID_DATA: + case EAP_DATA: + { + u_int32_t payload_length_position_offset; + u_int16_t length_of_payload; + u_int16_t header_length = 0; + u_int16_t length_in_network_order; + + switch(rules[i].type) + { + case KEY_EXCHANGE_DATA: + header_length = KE_PAYLOAD_HEADER_LENGTH; + break; + case NOTIFICATION_DATA: + header_length = NOTIFY_PAYLOAD_HEADER_LENGTH + this->last_spi_size ; + break; + case NONCE_DATA: + header_length = NONCE_PAYLOAD_HEADER_LENGTH; + break; + case ID_DATA: + header_length = ID_PAYLOAD_HEADER_LENGTH; + break; + case AUTH_DATA: + header_length = AUTH_PAYLOAD_HEADER_LENGTH; + break; + case CERT_DATA: + header_length = CERT_PAYLOAD_HEADER_LENGTH; + break; + case CERTREQ_DATA: + header_length = CERTREQ_PAYLOAD_HEADER_LENGTH; + break; + case SPIS: + header_length = DELETE_PAYLOAD_HEADER_LENGTH; + break; + case VID_DATA: + header_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH; + break; + case CONFIGURATION_ATTRIBUTE_VALUE: + header_length = CONFIGURATION_ATTRIBUTE_HEADER_LENGTH; + break; + case EAP_DATA: + header_length = EAP_PAYLOAD_HEADER_LENGTH; + break; + default: + break; + } + + /* the data value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + + payload_length_position_offset = this->last_payload_length_position_offset; + + + /* Length of payload is calculated */ + length_of_payload = header_length + ((chunk_t *)(this->data_struct + rules[i].offset))->len; + + length_in_network_order = htons(length_of_payload); + this->write_bytes_to_buffer_at_offset(this,&length_in_network_order,sizeof(u_int16_t),payload_length_position_offset); + break; + } + case PROPOSALS: + { + /* before iterative generate the transforms, store the current payload length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + /* Length of SA_PAYLOAD is calculated */ + u_int16_t length_of_sa_payload = SA_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + /* proposals are stored in a linked list and so accessed */ + linked_list_t *proposals = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_proposal; + + /* create forward iterator */ + iterator = proposals->create_iterator(proposals,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->iterate(iterator, (void**)¤t_proposal)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_proposal); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_sa_payload += (after_generate_position_offset - before_generate_position_offset); + } + iterator->destroy(iterator); + + int16_val = htons(length_of_sa_payload); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + break; + } + case TRANSFORMS: + { + /* before iterative generate the transforms, store the current length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_proposal = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH + this->last_spi_size; + u_int16_t int16_val; + linked_list_t *transforms = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_transform; + + /* create forward iterator */ + iterator = transforms->create_iterator(transforms,TRUE); + while (iterator->iterate(iterator, (void**)¤t_transform)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_transform); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_proposal += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_proposal); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + + break; + } + case TRANSFORM_ATTRIBUTES: + { + /* before iterative generate the transform attributes, store the current length position */ + u_int32_t transform_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_transform = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + u_int16_t int16_val; + linked_list_t *transform_attributes =*((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_attribute; + + /* create forward iterator */ + iterator = transform_attributes->create_iterator(transform_attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_attribute); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_transform += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_transform); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),transform_length_position_offset); + + break; + } + case CONFIGURATION_ATTRIBUTES: + { + /* before iterative generate the configuration attributes, store the current length position */ + u_int32_t configurations_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_configurations = CP_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + linked_list_t *configuration_attributes =*((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_attribute; + + /* create forward iterator */ + iterator = configuration_attributes->create_iterator(configuration_attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_attribute); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_configurations += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_configurations); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),configurations_length_position_offset); + + break; + } + case ATTRIBUTE_FORMAT: + { + this->generate_flag(this,rules[i].offset); + /* Attribute format is a flag which is stored in context*/ + this->attribute_format = *((bool *) (this->data_struct + rules[i].offset)); + break; + } + + case ATTRIBUTE_LENGTH_OR_VALUE: + { + if (this->attribute_format == FALSE) + { + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + /* this field hold the length of the attribute */ + this->attribute_length = *((u_int16_t *)(this->data_struct + rules[i].offset)); + } + else + { + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + } + break; + } + case ATTRIBUTE_VALUE: + { + if (this->attribute_format == FALSE) + { + DBG2(DBG_ENC, "attribute value has not fixed size"); + /* the attribute value is generated */ + this->generate_from_chunk(this,rules[i].offset); + } + break; + } + case TRAFFIC_SELECTORS: + { + /* before iterative generate the traffic_selectors, store the current payload length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + /* Length of SA_PAYLOAD is calculated */ + u_int16_t length_of_ts_payload = TS_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + /* traffic selectors are stored in a linked list and so accessed */ + linked_list_t *traffic_selectors = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_traffic_selector_substructure; + + /* create forward iterator */ + iterator = traffic_selectors->create_iterator(traffic_selectors,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->iterate(iterator, (void **)¤t_traffic_selector_substructure)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_traffic_selector_substructure); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_ts_payload += (after_generate_position_offset - before_generate_position_offset); + } + iterator->destroy(iterator); + + int16_val = htons(length_of_ts_payload); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + break; + } + + case ENCRYPTED_DATA: + { + this->generate_from_chunk(this, rules[i].offset); + break; + } + default: + DBG1(DBG_ENC, "field type %N is not supported", + encoding_type_names, rules[i].type); + return; + } + } + DBG2(DBG_ENC, "generating %N payload finished", + payload_type_names, payload_type); + DBG3(DBG_ENC, "generated data for this payload %b", + payload_start, this->out_position-payload_start); +} + +/** + * Implementation of generator_t.destroy. + */ +static status_t destroy(private_generator_t *this) +{ + free(this->buffer); + free(this); + return SUCCESS; +} + +/* + * Described in header + */ +generator_t *generator_create() +{ + private_generator_t *this; + + this = malloc_thing(private_generator_t); + + /* initiate public functions */ + this->public.generate_payload = (void(*)(generator_t*, payload_t *)) generate_payload; + this->public.destroy = (void(*)(generator_t*)) destroy; + this->public.write_to_chunk = (void (*) (generator_t *,chunk_t *)) write_to_chunk; + + + /* initiate private functions */ + this->get_current_buffer_size = get_current_buffer_size; + this->get_current_buffer_space = get_current_buffer_space; + this->get_current_data_length = get_current_data_length; + this->get_current_buffer_offset = get_current_buffer_offset; + this->generate_u_int_type = generate_u_int_type; + this->generate_reserved_field = generate_reserved_field; + this->generate_flag = generate_flag; + this->generate_from_chunk = generate_from_chunk; + this->make_space_available = make_space_available; + this->write_bytes_to_buffer = write_bytes_to_buffer; + this->write_bytes_to_buffer_at_offset = write_bytes_to_buffer_at_offset; + + + /* allocate memory for buffer */ + this->buffer = malloc(GENERATOR_DATA_BUFFER_SIZE); + + /* initiate private variables */ + this->out_position = this->buffer; + this->roof_position = this->buffer + GENERATOR_DATA_BUFFER_SIZE; + this->data_struct = NULL; + this->current_bit = 0; + this->last_payload_length_position_offset = 0; + this->header_length_position_offset = 0; + + return &(this->public); +} diff --git a/src/charon/encoding/generator.h b/src/charon/encoding/generator.h new file mode 100644 index 000000000..8eff957cc --- /dev/null +++ b/src/charon/encoding/generator.h @@ -0,0 +1,102 @@ +/** + * @file generator.h + * + * @brief Interface of generator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef GENERATOR_H_ +#define GENERATOR_H_ + +typedef struct generator_t generator_t; + +#include <library.h> +#include <encoding/payloads/encodings.h> +#include <encoding/payloads/payload.h> + +/** + * Generating is done in a data buffer. + * This is thehe start size of this buffer in bytes. + * + * @ingroup enconding + */ +#define GENERATOR_DATA_BUFFER_SIZE 500 + +/** + * Number of bytes to increase the buffer, if it is to small. + * + * @ingroup enconding + */ +#define GENERATOR_DATA_BUFFER_INCREASE_VALUE 500 + + +/** + * @brief A generator_t class used to generate IKEv2 payloads. + * + * After creation, multiple payloads can be generated with the generate_payload + * method. The generated bytes are appended. After all payloads are added, + * the write_to_chunk method writes out all generated data since + * the creation of the generator. After that, the generator must be destroyed. + * The generater uses a set of encoding rules, which it can get from + * the supplied payload. With this rules, the generater can generate + * the payload and all substructures automatically. + * + * @b Constructor: + * - generator_create() + * + * @ingroup encoding + */ +struct generator_t { + + /** + * @brief Generates a specific payload from given payload object. + * + * Remember: Header and substructures are also handled as payloads. + * + * @param this generator_t object + * @param[in] payload interface payload_t implementing object + */ + void (*generate_payload) (generator_t *this,payload_t *payload); + + /** + * @brief Writes all generated data of the generator to a chunk. + * + * @param this generator_t object + * @param[out] data chunk to write the data to + */ + void (*write_to_chunk) (generator_t *this,chunk_t *data); + + /** + * @brief Destroys a generator_t object. + * + * @param this generator_t object + */ + void (*destroy) (generator_t *this); +}; + +/** + * @brief Constructor to create a generator. + * + * @return generator_t object. + * + * @ingroup encoding + */ +generator_t *generator_create(void); + +#endif /*GENERATOR_H_*/ diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c new file mode 100644 index 000000000..5f3f91f8b --- /dev/null +++ b/src/charon/encoding/message.c @@ -0,0 +1,1316 @@ +/** + * @file message.c + * + * @brief Implementation of message_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <string.h> +#include <printf.h> + +#include "message.h" + +#include <library.h> +#include <daemon.h> +#include <sa/ike_sa_id.h> +#include <encoding/generator.h> +#include <encoding/parser.h> +#include <utils/linked_list.h> +#include <encoding/payloads/encodings.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/unknown_payload.h> + +/** + * Max number of notify payloads per IKEv2 Message + */ +#define MAX_NOTIFY_PAYLOADS 20 + + +typedef struct payload_rule_t payload_rule_t; + +/** + * A payload rule defines the rules for a payload + * in a specific message rule. It defines if and how + * many times a payload must/can occur in a message + * and if it must be encrypted. + */ +struct payload_rule_t { + /** + * Payload type. + */ + payload_type_t payload_type; + + /** + * Minimal occurence of this payload. + */ + size_t min_occurence; + + /** + * Max occurence of this payload. + */ + size_t max_occurence; + + /** + * TRUE if payload must be encrypted + */ + bool encrypted; + + /** + * If this payload occurs, the message rule is + * fullfilled in any case. This applies e.g. to + * notify_payloads. + */ + bool sufficient; +}; + +typedef struct message_rule_t message_rule_t; + +/** + * A message rule defines the kind of a message, + * if it has encrypted contents and a list + * of payload rules. + * + */ +struct message_rule_t { + /** + * Type of message. + */ + exchange_type_t exchange_type; + + /** + * Is message a request or response. + */ + bool is_request; + + /** + * Message contains encrypted content. + */ + bool encrypted_content; + + /** + * Number of payload rules which will follow + */ + size_t payload_rule_count; + + /** + * Pointer to first payload rule + */ + payload_rule_t *payload_rules; +}; + +/** + * Message rule for IKE_SA_INIT from initiator. + */ +static payload_rule_t ike_sa_init_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,FALSE,FALSE}, + {SECURITY_ASSOCIATION,1,1,FALSE,FALSE}, + {KEY_EXCHANGE,1,1,FALSE,FALSE}, + {NONCE,1,1,FALSE,FALSE}, + {VENDOR_ID,0,10,FALSE,FALSE}, +}; + +/** + * Message rule for IKE_SA_INIT from responder. + */ +static payload_rule_t ike_sa_init_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,FALSE,TRUE}, + {SECURITY_ASSOCIATION,1,1,FALSE,FALSE}, + {KEY_EXCHANGE,1,1,FALSE,FALSE}, + {NONCE,1,1,FALSE,FALSE}, + {VENDOR_ID,0,10,FALSE,FALSE}, +}; + +/** + * Message rule for IKE_AUTH from initiator. + */ +static payload_rule_t ike_auth_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {EXTENSIBLE_AUTHENTICATION,0,1,TRUE,TRUE}, + {AUTHENTICATION,0,1,TRUE,TRUE}, + {ID_INITIATOR,1,1,TRUE,FALSE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {CERTIFICATE_REQUEST,0,1,TRUE,FALSE}, + {ID_RESPONDER,0,1,TRUE,FALSE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,1,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,1,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for IKE_AUTH from responder. + */ +static payload_rule_t ike_auth_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE}, + {EXTENSIBLE_AUTHENTICATION,0,1,TRUE,TRUE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {ID_RESPONDER,0,1,TRUE,FALSE}, + {AUTHENTICATION,0,1,TRUE,FALSE}, + {SECURITY_ASSOCIATION,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + + +/** + * Message rule for INFORMATIONAL from initiator. + */ +static payload_rule_t informational_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {DELETE,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, + +}; + +/** + * Message rule for INFORMATIONAL from responder. + */ +static payload_rule_t informational_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {DELETE,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for CREATE_CHILD_SA from initiator. + */ +static payload_rule_t create_child_sa_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {NONCE,1,1,TRUE,FALSE}, + {KEY_EXCHANGE,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for CREATE_CHILD_SA from responder. + */ +static payload_rule_t create_child_sa_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {NONCE,1,1,TRUE,FALSE}, + {KEY_EXCHANGE,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + + +/** + * Message rules, defines allowed payloads. + */ +static message_rule_t message_rules[] = { + {IKE_SA_INIT,TRUE,FALSE,(sizeof(ike_sa_init_i_payload_rules)/sizeof(payload_rule_t)),ike_sa_init_i_payload_rules}, + {IKE_SA_INIT,FALSE,FALSE,(sizeof(ike_sa_init_r_payload_rules)/sizeof(payload_rule_t)),ike_sa_init_r_payload_rules}, + {IKE_AUTH,TRUE,TRUE,(sizeof(ike_auth_i_payload_rules)/sizeof(payload_rule_t)),ike_auth_i_payload_rules}, + {IKE_AUTH,FALSE,TRUE,(sizeof(ike_auth_r_payload_rules)/sizeof(payload_rule_t)),ike_auth_r_payload_rules}, + {INFORMATIONAL,TRUE,TRUE,(sizeof(informational_i_payload_rules)/sizeof(payload_rule_t)),informational_i_payload_rules}, + {INFORMATIONAL,FALSE,TRUE,(sizeof(informational_r_payload_rules)/sizeof(payload_rule_t)),informational_r_payload_rules}, + {CREATE_CHILD_SA,TRUE,TRUE,(sizeof(create_child_sa_i_payload_rules)/sizeof(payload_rule_t)),create_child_sa_i_payload_rules}, + {CREATE_CHILD_SA,FALSE,TRUE,(sizeof(create_child_sa_r_payload_rules)/sizeof(payload_rule_t)),create_child_sa_r_payload_rules}, +}; + + +typedef struct private_message_t private_message_t; + +/** + * Private data of an message_t object. + */ +struct private_message_t { + + /** + * Public part of a message_t object. + */ + message_t public; + + /** + * Minor version of message. + */ + u_int8_t major_version; + + /** + * Major version of message. + */ + u_int8_t minor_version; + + /** + * First Payload in message. + */ + payload_type_t first_payload; + + /** + * Assigned exchange type. + */ + exchange_type_t exchange_type; + + /** + * TRUE if message is a request, FALSE if a reply. + */ + bool is_request; + + /** + * Message ID of this message. + */ + u_int32_t message_id; + + /** + * ID of assigned IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Assigned UDP packet, stores incoming packet or last generated one. + */ + packet_t *packet; + + /** + * Linked List where payload data are stored in. + */ + linked_list_t *payloads; + + /** + * Assigned parser to parse Header and Body of this message. + */ + parser_t *parser; + + /** + * The message rule for this message instance + */ + message_rule_t *message_rule; +}; + +/** + * Implementation of private_message_t.set_message_rule. + */ +static status_t set_message_rule(private_message_t *this) +{ + int i; + + for (i = 0; i < (sizeof(message_rules) / sizeof(message_rule_t)); i++) + { + if ((this->exchange_type == message_rules[i].exchange_type) && + (this->is_request == message_rules[i].is_request)) + { + /* found rule for given exchange_type*/ + this->message_rule = &(message_rules[i]); + return SUCCESS; + } + } + this->message_rule = NULL; + return NOT_FOUND; +} + +/** + * Implementation of private_message_t.get_payload_rule. + */ +static status_t get_payload_rule(private_message_t *this, payload_type_t payload_type, payload_rule_t **payload_rule) +{ + int i; + + for (i = 0; i < this->message_rule->payload_rule_count;i++) + { + if (this->message_rule->payload_rules[i].payload_type == payload_type) + { + *payload_rule = &(this->message_rule->payload_rules[i]); + return SUCCESS; + } + } + + *payload_rule = NULL; + return NOT_FOUND; +} + +/** + * Implementation of message_t.set_ike_sa_id. + */ +static void set_ike_sa_id (private_message_t *this,ike_sa_id_t *ike_sa_id) +{ + DESTROY_IF(this->ike_sa_id); + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); +} + +/** + * Implementation of message_t.get_ike_sa_id. + */ +static ike_sa_id_t* get_ike_sa_id (private_message_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of message_t.set_message_id. + */ +static void set_message_id (private_message_t *this,u_int32_t message_id) +{ + this->message_id = message_id; +} + +/** + * Implementation of message_t.get_message_id. + */ +static u_int32_t get_message_id (private_message_t *this) +{ + return this->message_id; +} + +/** + * Implementation of message_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_message_t *this) +{ + return (this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); +} + +/** + * Implementation of message_t.get_responder_spi. + */ +static u_int64_t get_responder_spi (private_message_t *this) +{ + return (this->ike_sa_id->get_responder_spi(this->ike_sa_id)); +} + +/** + * Implementation of message_t.set_major_version. + */ +static void set_major_version (private_message_t *this,u_int8_t major_version) +{ + this->major_version = major_version; +} + + +/** + * Implementation of message_t.set_major_version. + */ +static u_int8_t get_major_version (private_message_t *this) +{ + return this->major_version; +} + +/** + * Implementation of message_t.set_minor_version. + */ +static void set_minor_version (private_message_t *this,u_int8_t minor_version) +{ + this->minor_version = minor_version; +} + +/** + * Implementation of message_t.get_minor_version. + */ +static u_int8_t get_minor_version (private_message_t *this) +{ + return this->minor_version; +} + +/** + * Implementation of message_t.set_exchange_type. + */ +static void set_exchange_type (private_message_t *this,exchange_type_t exchange_type) +{ + this->exchange_type = exchange_type; +} + +/** + * Implementation of message_t.get_exchange_type. + */ +static exchange_type_t get_exchange_type (private_message_t *this) +{ + return this->exchange_type; +} + +/** + * Implementation of message_t.set_request. + */ +static void set_request (private_message_t *this,bool request) +{ + this->is_request = request; +} + +/** + * Implementation of message_t.get_request. + */ +static exchange_type_t get_request (private_message_t *this) +{ + return this->is_request; +} + +/** + * Is this message in an encoded form? + */ +static bool is_encoded(private_message_t *this) +{ + chunk_t data = this->packet->get_data(this->packet); + + if (data.ptr == NULL) + { + return FALSE; + } + return TRUE; +} + +/** + * Implementation of message_t.add_payload. + */ +static void add_payload(private_message_t *this, payload_t *payload) +{ + payload_t *last_payload, *first_payload; + + if ((this->is_request && payload->get_type(payload) == ID_INITIATOR) || + (!this->is_request && payload->get_type(payload) == ID_RESPONDER)) + { + /* HOTD: insert ID payload in the beginning to respect RFC */ + if (this->payloads->get_first(this->payloads, + (void **)&first_payload) == SUCCESS) + { + payload->set_next_type(payload, first_payload->get_type(first_payload)); + } + else + { + payload->set_next_type(payload, NO_PAYLOAD); + } + this->first_payload = payload->get_type(payload); + this->payloads->insert_first(this->payloads, payload); + } + else + { + if (this->payloads->get_count(this->payloads) > 0) + { + this->payloads->get_last(this->payloads,(void **) &last_payload); + last_payload->set_next_type(last_payload, payload->get_type(payload)); + } + else + { + this->first_payload = payload->get_type(payload); + } + payload->set_next_type(payload, NO_PAYLOAD); + this->payloads->insert_last(this->payloads, payload); + } + + DBG2(DBG_ENC ,"added payload of type %N to message", + payload_type_names, payload->get_type(payload)); +} + +/** + * Implementation of message_t.add_notify. + */ +static void add_notify(private_message_t *this, bool flush, notify_type_t type, + chunk_t data) +{ + notify_payload_t *notify; + payload_t *payload; + + if (flush) + { + while (this->payloads->remove_last(this->payloads, + (void**)&payload) == SUCCESS) + { + payload->destroy(payload); + } + } + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + notify->set_notification_data(notify, data); + add_payload(this, (payload_t*)notify); +} + +/** + * Implementation of message_t.set_source. + */ +static void set_source(private_message_t *this, host_t *host) +{ + this->packet->set_source(this->packet, host); +} + +/** + * Implementation of message_t.set_destination. + */ +static void set_destination(private_message_t *this, host_t *host) +{ + this->packet->set_destination(this->packet, host); +} + +/** + * Implementation of message_t.get_source. + */ +static host_t* get_source(private_message_t *this) +{ + return this->packet->get_source(this->packet); +} + +/** + * Implementation of message_t.get_destination. + */ +static host_t * get_destination(private_message_t *this) +{ + return this->packet->get_destination(this->packet); +} + +/** + * Implementation of message_t.get_payload_iterator. + */ +static iterator_t *get_payload_iterator(private_message_t *this) +{ + return this->payloads->create_iterator(this->payloads, TRUE); +} + +/** + * Implementation of message_t.get_payload. + */ +static payload_t *get_payload(private_message_t *this, payload_type_t type) +{ + payload_t *current, *found = NULL; + iterator_t *iterator; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_type(current) == type) + { + found = current; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_message_t *this = *((private_message_t**)(args[0])); + iterator_t *iterator; + payload_t *payload; + bool first = TRUE; + size_t total_written = 0; + size_t written; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + written = fprintf(stream, "%N %s [", + exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + if (written < 0) + { + return written; + } + total_written += written; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (!first) + { + written = fprintf(stream, " "); + if (written < 0) + { + return written; + } + total_written += written; + } + else + { + first = FALSE; + } + written = fprintf(stream, "%N", payload_type_short_names, + payload->get_type(payload)); + if (written < 0) + { + return written; + } + total_written += written; + } + iterator->destroy(iterator); + written = fprintf(stream, "]"); + if (written < 0) + { + return written; + } + total_written += written; + return total_written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_MESSAGE, print, arginfo_ptr); +} + +/** + * Implementation of private_message_t.encrypt_payloads. + */ +static status_t encrypt_payloads (private_message_t *this,crypter_t *crypter, signer_t* signer) +{ + encryption_payload_t *encryption_payload = NULL; + status_t status; + linked_list_t *all_payloads; + + if (!this->message_rule->encrypted_content) + { + DBG2(DBG_ENC, "message doesn't have to be encrypted"); + /* message contains no content to encrypt */ + return SUCCESS; + } + + DBG2(DBG_ENC, "copy all payloads to a temporary list"); + all_payloads = linked_list_create(); + + /* first copy all payloads in a temporary list */ + while (this->payloads->get_count(this->payloads) > 0) + { + void *current_payload; + this->payloads->remove_first(this->payloads,¤t_payload); + all_payloads->insert_last(all_payloads,current_payload); + } + + encryption_payload = encryption_payload_create(); + + DBG2(DBG_ENC, "check each payloads if they have to get encrypted"); + while (all_payloads->get_count(all_payloads) > 0) + { + payload_rule_t *payload_rule; + payload_t *current_payload; + bool to_encrypt = FALSE; + + all_payloads->remove_first(all_payloads,(void **)¤t_payload); + + status = get_payload_rule(this, + current_payload->get_type(current_payload),&payload_rule); + /* for payload types which are not found in supported payload list, + * it is presumed that they don't have to be encrypted */ + if ((status == SUCCESS) && (payload_rule->encrypted)) + { + DBG2(DBG_ENC, "payload %N gets encrypted", + payload_type_names, current_payload->get_type(current_payload)); + to_encrypt = TRUE; + } + + if (to_encrypt) + { + DBG2(DBG_ENC, "insert payload %N to encryption payload", + payload_type_names, current_payload->get_type(current_payload)); + encryption_payload->add_payload(encryption_payload,current_payload); + } + else + { + DBG2(DBG_ENC, "insert payload %N unencrypted", + payload_type_names ,current_payload->get_type(current_payload)); + add_payload(this, (payload_t*)encryption_payload); + } + } + + status = SUCCESS; + DBG2(DBG_ENC, "encrypting encryption payload"); + encryption_payload->set_transforms(encryption_payload, crypter,signer); + status = encryption_payload->encrypt(encryption_payload); + DBG2(DBG_ENC, "add encrypted payload to payload list"); + add_payload(this, (payload_t*)encryption_payload); + + all_payloads->destroy(all_payloads); + + return status; +} + +/** + * Implementation of message_t.generate. + */ +static status_t generate(private_message_t *this, crypter_t *crypter, signer_t* signer, packet_t **packet) +{ + generator_t *generator; + ike_header_t *ike_header; + payload_t *payload, *next_payload; + iterator_t *iterator; + status_t status; + chunk_t packet_data; + + if (is_encoded(this)) + { + /* already generated, return a new packet clone */ + *packet = this->packet->clone(this->packet); + return SUCCESS; + } + + DBG1(DBG_ENC, "generating %M", this); + + if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) + { + DBG1(DBG_ENC, "exchange type is not defined"); + return INVALID_STATE; + } + + if (this->packet->get_source(this->packet) == NULL || + this->packet->get_destination(this->packet) == NULL) + { + DBG1(DBG_ENC, "%s not defined", + !this->packet->get_source(this->packet) ? "source" : "destination"); + return INVALID_STATE; + } + + /* set the rules for this messge */ + status = set_message_rule(this); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "no message rules specified for this message type"); + return NOT_SUPPORTED; + } + + /* going to encrypt all content which have to be encrypted */ + status = encrypt_payloads(this, crypter, signer); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "payload encryption failed"); + return status; + } + + /* build ike header */ + ike_header = ike_header_create(); + + ike_header->set_exchange_type(ike_header, this->exchange_type); + ike_header->set_message_id(ike_header, this->message_id); + ike_header->set_response_flag(ike_header, !this->is_request); + ike_header->set_initiator_flag(ike_header, this->ike_sa_id->is_initiator(this->ike_sa_id)); + ike_header->set_initiator_spi(ike_header, this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); + ike_header->set_responder_spi(ike_header, this->ike_sa_id->get_responder_spi(this->ike_sa_id)); + + generator = generator_create(); + + payload = (payload_t*)ike_header; + + + /* generate every payload expect last one, this is doen later*/ + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while(iterator->iterate(iterator, (void**)&next_payload)) + { + payload->set_next_type(payload, next_payload->get_type(next_payload)); + generator->generate_payload(generator, payload); + payload = next_payload; + } + iterator->destroy(iterator); + + /* last payload has no next payload*/ + payload->set_next_type(payload, NO_PAYLOAD); + + generator->generate_payload(generator, payload); + + ike_header->destroy(ike_header); + + /* build packet */ + generator->write_to_chunk(generator, &packet_data); + generator->destroy(generator); + + /* if last payload is of type encrypted, integrity checksum if necessary */ + if (payload->get_type(payload) == ENCRYPTED) + { + DBG2(DBG_ENC, "build signature on whole message"); + encryption_payload_t *encryption_payload = (encryption_payload_t*)payload; + status = encryption_payload->build_signature(encryption_payload, packet_data); + if (status != SUCCESS) + { + return status; + } + } + + this->packet->set_data(this->packet, packet_data); + + /* clone packet for caller */ + *packet = this->packet->clone(this->packet); + + DBG2(DBG_ENC, "message generated successfully"); + return SUCCESS; +} + +/** + * Implementation of message_t.get_packet. + */ +static packet_t *get_packet (private_message_t *this) +{ + if (this->packet == NULL) + { + return NULL; + } + return this->packet->clone(this->packet); +} + +/** + * Implementation of message_t.get_packet_data. + */ +static chunk_t get_packet_data (private_message_t *this) +{ + if (this->packet == NULL) + { + return chunk_empty; + } + return chunk_clone(this->packet->get_data(this->packet)); +} + +/** + * Implementation of message_t.parse_header. + */ +static status_t parse_header(private_message_t *this) +{ + ike_header_t *ike_header; + status_t status; + + DBG2(DBG_ENC, "parsing header of message"); + + this->parser->reset_context(this->parser); + status = this->parser->parse_payload(this->parser,HEADER,(payload_t **) &ike_header); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "header could not be parsed"); + return status; + + } + + /* verify payload */ + status = ike_header->payload_interface.verify(&(ike_header->payload_interface)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "header verification failed"); + ike_header->destroy(ike_header); + return status; + } + + if (this->ike_sa_id != NULL) + { + this->ike_sa_id->destroy(this->ike_sa_id); + } + + this->ike_sa_id = ike_sa_id_create(ike_header->get_initiator_spi(ike_header), + ike_header->get_responder_spi(ike_header), + ike_header->get_initiator_flag(ike_header)); + + this->exchange_type = ike_header->get_exchange_type(ike_header); + this->message_id = ike_header->get_message_id(ike_header); + this->is_request = (!(ike_header->get_response_flag(ike_header))); + this->major_version = ike_header->get_maj_version(ike_header); + this->minor_version = ike_header->get_min_version(ike_header); + this->first_payload = ike_header->payload_interface.get_next_type(&(ike_header->payload_interface)); + + DBG2(DBG_ENC, "parsed a %N %s", exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + + ike_header->destroy(ike_header); + + /* get the rules for this messge */ + status = set_message_rule(this); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "no message rules specified for a %N %s", + exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + } + + return status; +} + +/** + * Implementation of private_message_t.decrypt_and_verify_payloads. + */ +static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, signer_t* signer) +{ + bool current_payload_was_encrypted = FALSE; + payload_t *previous_payload = NULL; + int payload_number = 1; + iterator_t *iterator; + payload_t *current_payload; + status_t status; + + iterator = this->payloads->create_iterator(this->payloads,TRUE); + + /* process each payload and decrypt a encryption payload */ + while(iterator->iterate(iterator, (void**)¤t_payload)) + { + payload_rule_t *payload_rule; + payload_type_t current_payload_type; + + /* needed to check */ + current_payload_type = current_payload->get_type(current_payload); + + DBG2(DBG_ENC, "process payload of type %N", + payload_type_names, current_payload_type); + + if (current_payload_type == ENCRYPTED) + { + encryption_payload_t *encryption_payload; + payload_t *current_encrypted_payload; + + encryption_payload = (encryption_payload_t*)current_payload; + + DBG2(DBG_ENC, "found an encryption payload"); + + if (payload_number != this->payloads->get_count(this->payloads)) + { + /* encrypted payload is not last one */ + DBG1(DBG_ENC, "encrypted payload is not last payload"); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + /* decrypt */ + encryption_payload->set_transforms(encryption_payload, crypter, signer); + DBG2(DBG_ENC, "verify signature of encryption payload"); + status = encryption_payload->verify_signature(encryption_payload, + this->packet->get_data(this->packet)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "encryption payload signature invalid"); + iterator->destroy(iterator); + return FAILED; + } + DBG2(DBG_ENC, "decrypting content of encryption payload"); + status = encryption_payload->decrypt(encryption_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "encrypted payload could not be decrypted and parsed"); + iterator->destroy(iterator); + return PARSE_ERROR; + } + + /* needed later to find out if a payload was encrypted */ + current_payload_was_encrypted = TRUE; + + /* check if there are payloads contained in the encryption payload */ + if (encryption_payload->get_payload_count(encryption_payload) == 0) + { + DBG2(DBG_ENC, "encrypted payload is empty"); + /* remove the encryption payload, is not needed anymore */ + iterator->remove(iterator); + /* encrypted payload contains no other payload */ + current_payload_type = NO_PAYLOAD; + } + else + { + /* encryption_payload is replaced with first payload contained in encryption_payload */ + encryption_payload->remove_first_payload(encryption_payload, ¤t_encrypted_payload); + iterator->replace(iterator,NULL,(void *) current_encrypted_payload); + current_payload_type = current_encrypted_payload->get_type(current_encrypted_payload); + } + + /* is the current paylad the first in the message? */ + if (previous_payload == NULL) + { + /* yes, set the first payload type of the message to the current type */ + this->first_payload = current_payload_type; + } + else + { + /* no, set the next_type of the previous payload to the current type */ + previous_payload->set_next_type(previous_payload, current_payload_type); + } + + /* all encrypted payloads are added to the payload list */ + while (encryption_payload->get_payload_count(encryption_payload) > 0) + { + encryption_payload->remove_first_payload(encryption_payload, ¤t_encrypted_payload); + DBG2(DBG_ENC, "insert unencrypted payload of type %N at end of list", + payload_type_names, current_encrypted_payload->get_type(current_encrypted_payload)); + this->payloads->insert_last(this->payloads,current_encrypted_payload); + } + + /* encryption payload is processed, payloads are moved. Destroy it. */ + encryption_payload->destroy(encryption_payload); + } + + /* we allow unknown payloads of any type and don't bother if it was encrypted. Not our problem. */ + if (current_payload_type != UNKNOWN_PAYLOAD && current_payload_type != NO_PAYLOAD) + { + /* get the ruleset for found payload */ + status = get_payload_rule(this, current_payload_type, &payload_rule); + if (status != SUCCESS) + { + /* payload is not allowed */ + DBG1(DBG_ENC, "payload type %N not allowed", + payload_type_names, current_payload_type); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + + /* check if the payload was encrypted, and if it should been have encrypted */ + if (payload_rule->encrypted != current_payload_was_encrypted) + { + /* payload was not encrypted, but should have been. or vice-versa */ + DBG1(DBG_ENC, "payload type %N should be %s!", + payload_type_names, current_payload_type, + (payload_rule->encrypted) ? "encrypted" : "not encrypted"); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + } + /* advance to the next payload */ + payload_number++; + /* is stored to set next payload in case of found encryption payload */ + previous_payload = current_payload; + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of private_message_t.verify. + */ +static status_t verify(private_message_t *this) +{ + int i; + iterator_t *iterator; + payload_t *current_payload; + size_t total_found_payloads = 0; + + DBG2(DBG_ENC, "verifying message structure"); + + iterator = this->payloads->create_iterator(this->payloads,TRUE); + /* check for payloads with wrong count*/ + for (i = 0; i < this->message_rule->payload_rule_count;i++) + { + size_t found_payloads = 0; + + /* check all payloads for specific rule */ + iterator->reset(iterator); + + while(iterator->iterate(iterator,(void **)¤t_payload)) + { + payload_type_t current_payload_type; + + current_payload_type = current_payload->get_type(current_payload); + if (current_payload_type == UNKNOWN_PAYLOAD) + { + /* unknown payloads are ignored, IF they are not critical */ + unknown_payload_t *unknown_payload = (unknown_payload_t*)current_payload; + if (unknown_payload->is_critical(unknown_payload)) + { + DBG1(DBG_ENC, "%N is not supported, but its critical!", + payload_type_names, current_payload_type); + iterator->destroy(iterator); + return NOT_SUPPORTED; + } + } + else if (current_payload_type == this->message_rule->payload_rules[i].payload_type) + { + found_payloads++; + total_found_payloads++; + DBG2(DBG_ENC, "found payload of type %N", + payload_type_names, this->message_rule->payload_rules[i].payload_type); + + /* as soon as ohe payload occures more then specified, the verification fails */ + if (found_payloads > this->message_rule->payload_rules[i].max_occurence) + { + DBG1(DBG_ENC, "payload of type %N more than %d times (%d) occured in current message", + payload_type_names, current_payload_type, + this->message_rule->payload_rules[i].max_occurence, found_payloads); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + } + } + + if (found_payloads < this->message_rule->payload_rules[i].min_occurence) + { + DBG1(DBG_ENC, "payload of type %N not occured %d times (%d)", + payload_type_names, this->message_rule->payload_rules[i].payload_type, + this->message_rule->payload_rules[i].min_occurence, found_payloads); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + if ((this->message_rule->payload_rules[i].sufficient) && (this->payloads->get_count(this->payloads) == total_found_payloads)) + { + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of message_t.parse_body. + */ +static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t *signer) +{ + status_t status = SUCCESS; + payload_type_t current_payload_type; + + current_payload_type = this->first_payload; + + DBG2(DBG_ENC, "parsing body of message, first payload is %N", + payload_type_names, current_payload_type); + + /* parse payload for payload, while there are more available */ + while ((current_payload_type != NO_PAYLOAD)) + { + payload_t *current_payload; + + DBG2(DBG_ENC, "starting parsing a %N payload", + payload_type_names, current_payload_type); + + /* parse current payload */ + status = this->parser->parse_payload(this->parser,current_payload_type,(payload_t **) ¤t_payload); + + if (status != SUCCESS) + { + DBG1(DBG_ENC, "payload type %N could not be parsed", + payload_type_names, current_payload_type); + return PARSE_ERROR; + } + + DBG2(DBG_ENC, "verifying payload of type %N", + payload_type_names, current_payload_type); + + /* verify it, stop parsig if its invalid */ + status = current_payload->verify(current_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "%N payload verification failed", + payload_type_names, current_payload_type); + current_payload->destroy(current_payload); + return VERIFY_ERROR; + } + + DBG2(DBG_ENC, "%N payload verified. Adding to payload list", + payload_type_names, current_payload_type); + this->payloads->insert_last(this->payloads,current_payload); + + /* an encryption payload is the last one, so STOP here. decryption is done later */ + if (current_payload_type == ENCRYPTED) + { + DBG2(DBG_ENC, "%N payload found. Stop parsing", + payload_type_names, current_payload_type); + break; + } + + /* get next payload type */ + current_payload_type = current_payload->get_next_type(current_payload); + } + + if (current_payload_type == ENCRYPTED) + { + status = decrypt_payloads(this,crypter,signer); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "could not decrypt payloads"); + return status; + } + } + + status = verify(this); + if (status != SUCCESS) + { + return status; + } + + DBG1(DBG_ENC, "parsed %M", this); + + return SUCCESS; +} + +/** + * Implementation of message_t.destroy. + */ +static void destroy (private_message_t *this) +{ + DESTROY_IF(this->ike_sa_id); + this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + this->packet->destroy(this->packet); + this->parser->destroy(this->parser); + free(this); +} + +/* + * Described in Header-File + */ +message_t *message_create_from_packet(packet_t *packet) +{ + private_message_t *this = malloc_thing(private_message_t); + + /* public functions */ + this->public.set_major_version = (void(*)(message_t*, u_int8_t))set_major_version; + this->public.get_major_version = (u_int8_t(*)(message_t*))get_major_version; + this->public.set_minor_version = (void(*)(message_t*, u_int8_t))set_minor_version; + this->public.get_minor_version = (u_int8_t(*)(message_t*))get_minor_version; + this->public.set_message_id = (void(*)(message_t*, u_int32_t))set_message_id; + this->public.get_message_id = (u_int32_t(*)(message_t*))get_message_id; + this->public.get_initiator_spi = (u_int64_t(*)(message_t*))get_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(message_t*))get_responder_spi; + this->public.set_ike_sa_id = (void(*)(message_t*, ike_sa_id_t *))set_ike_sa_id; + this->public.get_ike_sa_id = (ike_sa_id_t*(*)(message_t*))get_ike_sa_id; + this->public.set_exchange_type = (void(*)(message_t*, exchange_type_t))set_exchange_type; + this->public.get_exchange_type = (exchange_type_t(*)(message_t*))get_exchange_type; + this->public.set_request = (void(*)(message_t*, bool))set_request; + this->public.get_request = (bool(*)(message_t*))get_request; + this->public.add_payload = (void(*)(message_t*,payload_t*))add_payload; + this->public.add_notify = (void(*)(message_t*,bool,notify_type_t,chunk_t))add_notify; + this->public.generate = (status_t (*) (message_t *,crypter_t*,signer_t*,packet_t**)) generate; + this->public.set_source = (void (*) (message_t*,host_t*)) set_source; + this->public.get_source = (host_t * (*) (message_t*)) get_source; + this->public.set_destination = (void (*) (message_t*,host_t*)) set_destination; + this->public.get_destination = (host_t * (*) (message_t*)) get_destination; + this->public.get_payload_iterator = (iterator_t * (*) (message_t *)) get_payload_iterator; + this->public.get_payload = (payload_t * (*) (message_t *, payload_type_t)) get_payload; + this->public.parse_header = (status_t (*) (message_t *)) parse_header; + this->public.parse_body = (status_t (*) (message_t *,crypter_t*,signer_t*)) parse_body; + this->public.get_packet = (packet_t * (*) (message_t*)) get_packet; + this->public.get_packet_data = (chunk_t (*) (message_t *this)) get_packet_data; + this->public.destroy = (void(*)(message_t*))destroy; + + /* private values */ + this->exchange_type = EXCHANGE_TYPE_UNDEFINED; + this->is_request = TRUE; + this->ike_sa_id = NULL; + this->first_payload = NO_PAYLOAD; + this->message_id = 0; + + /* private values */ + if (packet == NULL) + { + packet = packet_create(); + } + this->message_rule = NULL; + this->packet = packet; + this->payloads = linked_list_create(); + + /* parser is created from data of packet */ + this->parser = parser_create(this->packet->get_data(this->packet)); + + return (&this->public); +} + +/* + * Described in Header. + */ +message_t *message_create() +{ + return message_create_from_packet(NULL); +} diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h new file mode 100644 index 000000000..73c2e05c6 --- /dev/null +++ b/src/charon/encoding/message.h @@ -0,0 +1,390 @@ +/** + * @file message.h + * + * @brief Interface of message_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef MESSAGE_H_ +#define MESSAGE_H_ + +typedef struct message_t message_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <network/packet.h> +#include <encoding/payloads/ike_header.h> +#include <encoding/payloads/notify_payload.h> +#include <utils/linked_list.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> + +/** + * @brief This class is used to represent an IKEv2-Message. + * + * The message handles parsing and generation of payloads + * via parser_t/generator_t. Encryption is done transparently + * via the encryption_payload_t. A set of rules for messages + * and payloads does check parsed messages. + * + * @b Constructors: + * - message_create() + * - message_create_from_packet() + * - message_create_notify_reply() + * + * @ingroup encoding + */ +struct message_t { + + /** + * @brief Sets the IKE major version of the message. + * + * @param this message_t object + * @param major_version major version to set + */ + void (*set_major_version) (message_t *this,u_int8_t major_version); + + /** + * @brief Gets the IKE major version of the message. + * + * @param this message_t object + * @return major version of the message + */ + u_int8_t (*get_major_version) (message_t *this); + + /** + * @brief Sets the IKE minor version of the message. + * + * @param this message_t object + * @param minor_version minor version to set + */ + void (*set_minor_version) (message_t *this,u_int8_t minor_version); + + /** + * @brief Gets the IKE minor version of the message. + * + * @param this message_t object + * @return minor version of the message + */ + u_int8_t (*get_minor_version) (message_t *this); + + /** + * @brief Sets the Message ID of the message. + * + * @param this message_t object + * @param message_id message_id to set + */ + void (*set_message_id) (message_t *this,u_int32_t message_id); + + /** + * @brief Gets the Message ID of the message. + * + * @param this message_t object + * @return message_id type of the message + */ + u_int32_t (*get_message_id) (message_t *this); + + /** + * @brief Gets the initiator SPI of the message. + * + * @param this message_t object + * @return initiator spi of the message + */ + u_int64_t (*get_initiator_spi) (message_t *this); + + /** + * @brief Gets the responder SPI of the message. + * + * @param this message_t object + * @return responder spi of the message + */ + u_int64_t (*get_responder_spi) (message_t *this); + + /** + * @brief Sets the IKE_SA ID of the message. + * + * ike_sa_id gets cloned. + * + * @param this message_t object + * @param ike_sa_id ike_sa_id to set + */ + void (*set_ike_sa_id) (message_t *this, ike_sa_id_t * ike_sa_id); + + /** + * @brief Gets the IKE_SA ID of the message. + * + * The ike_sa_id points to the message internal id, do not modify. + * + * @param this message_t object + * @return ike_sa_id of message + */ + ike_sa_id_t *(*get_ike_sa_id) (message_t *this); + + /** + * @brief Sets the exchange type of the message. + * + * @param this message_t object + * @param exchange_type exchange_type to set + */ + void (*set_exchange_type) (message_t *this,exchange_type_t exchange_type); + + /** + * @brief Gets the exchange type of the message. + * + * @param this message_t object + * @return exchange type of the message + */ + exchange_type_t (*get_exchange_type) (message_t *this); + + /** + * @brief Sets the request flag. + * + * @param this message_t object + * @param original_initiator TRUE if message is a request, FALSE if it is a reply + */ + void (*set_request) (message_t *this,bool request); + + /** + * @brief Gets request flag. + * + * @param this message_t object + * @return TRUE if message is a request, FALSE if it is a reply + */ + bool (*get_request) (message_t *this); + + /** + * @brief Append a payload to the message. + * + * If the payload must be encrypted is not specified here. Encryption + * of payloads is evaluated via internal rules for the messages and + * is done before generation. The order of payloads may change, since + * all payloads to encrypt are added to the encryption payload, which is + * always the last one. + * + * @param this message_t object + * @param payload payload to append + */ + void (*add_payload) (message_t *this, payload_t *payload); + + /** + * @brief Build a notify payload and add it to the message. + * + * This is a helper method to create notify messages or add + * notify payload to messages. The flush parameter specifies if existing + * payloads should get removed before appending the notify. + * + * @param this message_t object + * @param flush TRUE to remove existing payloads + * @param type type of the notify + * @param data a chunk of data to add to the notify, gets cloned + */ + void (*add_notify) (message_t *this, bool flush, notify_type_t type, + chunk_t data); + + /** + * @brief Parses header of message. + * + * Begins parisng of a message created via message_create_from_packet(). + * The parsing context is stored, so a subsequent call to parse_body() + * will continue the parsing process. + * + * @param this message_t object + * @return + * - SUCCESS if header could be parsed + * - PARSE_ERROR if corrupted/invalid data found + * - FAILED if consistence check of header failed + */ + status_t (*parse_header) (message_t *this); + + /** + * @brief Parses body of message. + * + * The body gets not only parsed, but rather it gets verified. + * All payloads are verified if they are allowed to exist in the message + * of this type and if their own structure is ok. + * If there are encrypted payloads, they get decrypted via the supplied + * crypter. Also the message integrity gets verified with the supplied + * signer. + * Crypter/signer can be omitted (by passing NULL) when no encryption + * payload is expected. + * + * @param this message_t object + * @param crypter crypter to decrypt encryption payloads + * @param signer signer to verifiy a message with an encryption payload + * @return + * - SUCCESS if parsing successful + * - NOT_SUPPORTED if ciritcal unknown payloads found + * - NOT_SUPPORTED if message type is not supported! + * - PARSE_ERROR if message parsing failed + * - VERIFY_ERROR if message verification failed (bad syntax) + * - FAILED if integrity check failed + * - INVALID_STATE if crypter/signer not supplied, but needed + */ + status_t (*parse_body) (message_t *this, crypter_t *crypter, signer_t *signer); + + /** + * @brief Generates the UDP packet of specific message. + * + * Payloads which must be encrypted are generated first and added to + * an encryption payload. This encryption payload will get encrypted via + * the supplied crypter. Then all other payloads and the header get generated. + * After that, the checksum is added to the encryption payload over the full + * message. + * Crypter/signer can be omitted (by passing NULL) when no encryption + * payload is expected. + * Generation is only done once, multiple calls will just return a packet copy. + * + * @param this message_t object + * @param crypter crypter to use when a payload must be encrypted + * @param signer signer to build a mac + * @param packet copy of generated packet + * @return + * - SUCCESS if packet could be generated + * - INVALID_STATE if exchange type is currently not set + * - NOT_FOUND if no rules found for message generation + * - INVALID_STATE if crypter/signer not supplied but needed. + */ + status_t (*generate) (message_t *this, crypter_t *crypter, signer_t *signer, packet_t **packet); + + /** + * @brief Gets the source host informations. + * + * @warning Returned host_t object is not getting cloned, + * do not destroy nor modify. + * + * @param this message_t object + * @return host_t object representing source host + */ + host_t * (*get_source) (message_t *this); + + /** + * @brief Sets the source host informations. + * + * @warning host_t object is not getting cloned and gets destroyed by + * message_t.destroy or next call of message_t.set_source. + * + * @param this message_t object + * @param host host_t object representing source host + */ + void (*set_source) (message_t *this, host_t *host); + + /** + * @brief Gets the destination host informations. + * + * @warning Returned host_t object is not getting cloned, + * do not destroy nor modify. + * + * @param this message_t object + * @return host_t object representing destination host + */ + host_t * (*get_destination) (message_t *this); + + /** + * @brief Sets the destination host informations. + * + * @warning host_t object is not getting cloned and gets destroyed by + * message_t.destroy or next call of message_t.set_destination. + * + * @param this message_t object + * @param host host_t object representing destination host + */ + void (*set_destination) (message_t *this, host_t *host); + + /** + * @brief Returns an iterator on all stored payloads. + * + * @warning Don't insert payloads over this iterator. + * Use add_payload() instead. + * + * @param this message_t object + * @return iterator_t object which has to get destroyd by the caller + */ + iterator_t * (*get_payload_iterator) (message_t *this); + + /** + * @brief Find a payload of a spicific type. + * + * Returns the first occurance. + * + * @param this message_t object + * @param type type of the payload to find + * @return payload, or NULL if no such payload found + */ + payload_t* (*get_payload) (message_t *this, payload_type_t type); + + /** + * @brief Returns a clone of the internal stored packet_t object. + * + * @param this message_t object + * @return packet_t object as clone of internal one + */ + packet_t * (*get_packet) (message_t *this); + + /** + * @brief Returns a clone of the internal stored packet_t data. + * + * @param this message_t object + * @return clone of the internal stored packet_t data. + */ + chunk_t (*get_packet_data) (message_t *this); + + /** + * @brief Destroys a message and all including objects. + * + * @param this message_t object + */ + void (*destroy) (message_t *this); +}; + +/** + * @brief Creates an message_t object from a incoming UDP Packet. + * + * @warning the given packet_t object is not copied and gets + * destroyed in message_t's destroy call. + * + * @warning Packet is not parsed in here! + * + * - exchange_type is set to NOT_SET + * - original_initiator is set to TRUE + * - is_request is set to TRUE + * Call message_t.parse_header afterwards. + * + * @param packet packet_t object which is assigned to message + * @return message_t object + * + * @ingroup encoding + */ +message_t * message_create_from_packet(packet_t *packet); + + +/** + * @brief Creates an empty message_t object. + * + * - exchange_type is set to NOT_SET + * - original_initiator is set to TRUE + * - is_request is set to TRUE + * + * @return message_t object + * + * @ingroup encoding + */ +message_t * message_create(void); + +#endif /*MESSAGE_H_*/ diff --git a/src/charon/encoding/parser.c b/src/charon/encoding/parser.c new file mode 100644 index 000000000..d7caf7099 --- /dev/null +++ b/src/charon/encoding/parser.c @@ -0,0 +1,1048 @@ +/** + * @file parser.c + * + * @brief Implementation of parser_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <arpa/inet.h> +#include <string.h> + +#include "parser.h" + +#include <library.h> +#include <daemon.h> +#include <utils/linked_list.h> +#include <encoding/payloads/encodings.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/proposal_substructure.h> +#include <encoding/payloads/transform_substructure.h> +#include <encoding/payloads/transform_attribute.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <encoding/payloads/vendor_id_payload.h> +#include <encoding/payloads/cp_payload.h> +#include <encoding/payloads/configuration_attribute.h> +#include <encoding/payloads/eap_payload.h> +#include <encoding/payloads/unknown_payload.h> + + +typedef struct private_parser_t private_parser_t; + +/** + * Private data stored in a context. + * + * Contains pointers and counters to store current state. + */ +struct private_parser_t { + /** + * Public members, see parser_t. + */ + parser_t public; + + /** + * @brief Parse a 4-Bit unsigned integer from the current parsing position. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint4) (private_parser_t *this, int rule_number, u_int8_t *output_pos); + + /** + * @brief Parse a 8-Bit unsigned integer from the current parsing position. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint8) (private_parser_t *this, int rule_number, u_int8_t *output_pos); + + /** + * @brief Parse a 15-Bit unsigned integer from the current parsing position. + * + * This is a special case used for ATTRIBUTE_TYPE. + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint15) (private_parser_t *this, int rule_number, u_int16_t *output_pos); + + /** + * @brief Parse a 16-Bit unsigned integer from the current parsing position. + * + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint16) (private_parser_t *this, int rule_number, u_int16_t *output_pos); + + /** + * @brief Parse a 32-Bit unsigned integer from the current parsing position. + * + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint32) (private_parser_t *this, int rule_number, u_int32_t *output_pos); + + /** + * @brief Parse a 64-Bit unsigned integer from the current parsing position. + * + * @todo add support for big-endian machines. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint64) (private_parser_t *this, int rule_number, u_int64_t *output_pos); + + /** + * @brief Parse a given amount of bytes and writes them to a specific location + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @param bytes number of bytes to parse + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_bytes) (private_parser_t *this, int rule_number, u_int8_t *output_pos,size_t bytes); + + /** + * @brief Parse a single Bit from the current parsing position + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_bit) (private_parser_t *this, int rule_number, bool *output_pos); + + /** + * @brief Parse substructures in a list + * + * This function calls the parser recursively to parse contained substructures + * in a linked_list_t. The list must already be created. Payload defines + * the type of the substructures. parsing is continued until the specified length + * is completely parsed. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer of a linked_list where substructures are added + * @param payload_type type of the contained substructures to parse + * @param length number of bytes to parse in this list + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_list) (private_parser_t *this, int rule_number, linked_list_t **output_pos, payload_type_t payload_ype, size_t length); + + /** + * @brief Parse data from current parsing position in a chunk. + * + * This function clones length number of bytes to output_pos, without + * modifiyng them. Space will be allocated and must be freed by caller. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer of a chunk which will point to the allocated data + * @param length number of bytes to clone + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_chunk) (private_parser_t *this, int rule_number, chunk_t *output_pos, size_t length); + + /** + * Current bit for reading in input data. + */ + u_int8_t bit_pos; + + /** + * Current byte for reading in input data. + */ + u_int8_t *byte_pos; + + /** + * Input data to parse. + */ + u_int8_t *input; + + /** + * Roof of input, used for length-checking. + */ + u_int8_t *input_roof; + + /** + * Set of encoding rules for this parsing session. + */ + encoding_rule_t *rules; +}; + +/** + * Implementation of private_parser_t.parse_uint4. + */ +static status_t parse_uint4(private_parser_t *this, int rule_number, u_int8_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + switch (this->bit_pos) + { + case 0: + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos) >> 4; + } + this->bit_pos = 4; + break; + case 4: + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos) & 0x0F; + } + this->bit_pos = 0; + this->byte_pos++; + break; + default: + DBG2(DBG_ENC, " found rule %d %N on bitpos %d", + rule_number, encoding_type_names, + this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + if (output_pos != NULL) + { + DBG3(DBG_ENC, " => %d", *output_pos); + } + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint8. + */ +static status_t parse_uint8(private_parser_t *this, int rule_number, u_int8_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", + rule_number, encoding_type_names, + this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos); + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos++; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint15. + */ +static status_t parse_uint15(private_parser_t *this, int rule_number, u_int16_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int16_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos != 1) + { + DBG2(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohs(*((u_int16_t*)this->byte_pos)) & ~0x8000; + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 2; + this->bit_pos = 0; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint16. + */ +static status_t parse_uint16(private_parser_t *this, int rule_number, u_int16_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int16_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohs(*((u_int16_t*)this->byte_pos)); + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 2; + + return SUCCESS; +} +/** + * Implementation of private_parser_t.parse_uint32. + */ +static status_t parse_uint32(private_parser_t *this, int rule_number, u_int32_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int32_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohl(*((u_int32_t*)this->byte_pos)); + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 4; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint64. + */ +static status_t parse_uint64(private_parser_t *this, int rule_number, u_int64_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int64_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + /* assuming little endian host order */ + *(output_pos + 1) = ntohl(*((u_int32_t*)this->byte_pos)); + *output_pos = ntohl(*(((u_int32_t*)this->byte_pos) + 1)); + + DBG3(DBG_ENC, " => %b", (void*)output_pos, sizeof(u_int64_t)); + } + this->byte_pos += 8; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_bytes. + */ +static status_t parse_bytes (private_parser_t *this, int rule_number, u_int8_t *output_pos,size_t bytes) +{ + if (this->byte_pos + bytes > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + /* caller interested in result ? */ + if (output_pos != NULL) + { + memcpy(output_pos,this->byte_pos,bytes); + + DBG3(DBG_ENC, " => %b", (void*)output_pos, bytes); + } + this->byte_pos += bytes; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_bit. + */ +static status_t parse_bit(private_parser_t *this, int rule_number, bool *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + u_int8_t mask; + mask = 0x01 << (7 - this->bit_pos); + *output_pos = *this->byte_pos & mask; + + if (*output_pos) + { + /* set to a "clean", comparable true */ + *output_pos = TRUE; + } + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->bit_pos = (this->bit_pos + 1) % 8; + if (this->bit_pos == 0) + { + this->byte_pos++; + } + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_list. + */ +static status_t parse_list(private_parser_t *this, int rule_number, linked_list_t **output_pos, payload_type_t payload_type, size_t length) +{ + linked_list_t * list = *output_pos; + + if (length < 0) + { + DBG1(DBG_ENC, " invalid length for rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + while (length > 0) + { + u_int8_t *pos_before = this->byte_pos; + payload_t *payload; + status_t status; + DBG2(DBG_ENC, " %d bytes left, parsing recursively %N", + length, payload_type_names, payload_type); + status = this->public.parse_payload((parser_t*)this, payload_type, &payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, " parsing of a %N substructure failed", + payload_type_names, payload_type); + return status; + } + list->insert_last(list, payload); + length -= this->byte_pos - pos_before; + } + *output_pos = list; + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_chunk. + */ +static status_t parse_chunk(private_parser_t *this, int rule_number, chunk_t *output_pos, size_t length) +{ + if (this->byte_pos + length > this->input_roof) + { + DBG1(DBG_ENC, " not enough input (%d bytes) to parse rule %d %N", + length, rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + if (output_pos != NULL) + { + output_pos->len = length; + output_pos->ptr = malloc(length); + memcpy(output_pos->ptr, this->byte_pos, length); + } + this->byte_pos += length; + DBG3(DBG_ENC, " => %b", (void*)output_pos->ptr, length); + + return SUCCESS; +} + +/** + * Implementation of parser_t.parse_payload. + */ +static status_t parse_payload(private_parser_t *this, payload_type_t payload_type, payload_t **payload) +{ + payload_t *pld; + void *output; + size_t rule_count, payload_length = 0, spi_size = 0, attribute_length = 0; + u_int16_t ts_type = 0; + bool attribute_format = FALSE; + int rule_number; + encoding_rule_t *rule; + + /* create instance of the payload to parse */ + pld = payload_create(payload_type); + + DBG2(DBG_ENC, "parsing %N payload, %d bytes left", + payload_type_names, payload_type, this->input_roof - this->byte_pos); + + DBG3(DBG_ENC, "parsing payload from %b", + this->byte_pos, this->input_roof-this->byte_pos); + + if (pld->get_type(pld) == UNKNOWN_PAYLOAD) + { + DBG1(DBG_ENC, " payload type %d is unknown, handling as %N", + payload_type, payload_type_names, UNKNOWN_PAYLOAD); + } + + /* base pointer for output, avoids casting in every rule */ + output = pld; + + /* parse the payload with its own rulse */ + pld->get_encoding_rules(pld, &(this->rules), &rule_count); + for (rule_number = 0; rule_number < rule_count; rule_number++) + { + rule = &(this->rules[rule_number]); + DBG2(DBG_ENC, " parsing rule %d %N", + rule_number, encoding_type_names, rule->type); + switch (rule->type) + { + case U_INT_4: + { + if (this->parse_uint4(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_8: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_16: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_32: + { + if (this->parse_uint32(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_64: + { + if (this->parse_uint64(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case IKE_SPI: + { + if (this->parse_bytes(this, rule_number, output + rule->offset,8) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case RESERVED_BIT: + { + if (this->parse_bit(this, rule_number, NULL) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case RESERVED_BYTE: + { + if (this->parse_uint8(this, rule_number, NULL) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case FLAG: + { + if (this->parse_bit(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case PAYLOAD_LENGTH: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + payload_length = *(u_int16_t*)(output + rule->offset); + break; + } + case HEADER_LENGTH: + { + if (this->parse_uint32(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case SPI_SIZE: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + spi_size = *(u_int8_t*)(output + rule->offset); + break; + } + case SPI: + { + if (this->parse_chunk(this, rule_number, output + rule->offset, spi_size) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case PROPOSALS: + { + size_t proposals_length = payload_length - SA_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, PROPOSAL_SUBSTRUCTURE, proposals_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRANSFORMS: + { + size_t transforms_length = payload_length - spi_size - PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRANSFORM_SUBSTRUCTURE, transforms_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRANSFORM_ATTRIBUTES: + { + size_t transform_a_length = payload_length - TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRANSFORM_ATTRIBUTE, transform_a_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CONFIGURATION_ATTRIBUTES: + { + size_t configuration_attributes_length = payload_length - CP_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, CONFIGURATION_ATTRIBUTE, configuration_attributes_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ATTRIBUTE_FORMAT: + { + if (this->parse_bit(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_format = *(bool*)(output + rule->offset); + break; + } + case ATTRIBUTE_TYPE: + { + if (this->parse_uint15(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_format = *(bool*)(output + rule->offset); + break; + } + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_length = *(u_int16_t*)(output + rule->offset); + break; + } + case ATTRIBUTE_LENGTH_OR_VALUE: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_length = *(u_int16_t*)(output + rule->offset); + break; + } + case ATTRIBUTE_VALUE: + { + if (attribute_format == FALSE) + { + if (this->parse_chunk(this, rule_number, output + rule->offset, attribute_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + } + break; + } + case NONCE_DATA: + { + size_t nonce_length = payload_length - NONCE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, nonce_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ID_DATA: + { + size_t data_length = payload_length - ID_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case AUTH_DATA: + { + size_t data_length = payload_length - AUTH_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CERT_DATA: + { + size_t data_length = payload_length - CERT_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CERTREQ_DATA: + { + size_t data_length = payload_length - CERTREQ_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case EAP_DATA: + { + size_t data_length = payload_length - EAP_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case SPIS: + { + size_t data_length = payload_length - DELETE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case VID_DATA: + { + size_t data_length = payload_length - VENDOR_ID_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CONFIGURATION_ATTRIBUTE_VALUE: + { + size_t data_length = attribute_length; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case KEY_EXCHANGE_DATA: + { + size_t keydata_length = payload_length - KE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, keydata_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case NOTIFICATION_DATA: + { + size_t notify_length = payload_length - NOTIFY_PAYLOAD_HEADER_LENGTH - spi_size; + if (this->parse_chunk(this, rule_number, output + rule->offset, notify_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ENCRYPTED_DATA: + { + size_t data_length = payload_length - ENCRYPTION_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TS_TYPE: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + ts_type = *(u_int8_t*)(output + rule->offset); + break; + } + case ADDRESS: + { + size_t address_length = (ts_type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + if (this->parse_chunk(this, rule_number, output + rule->offset,address_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRAFFIC_SELECTORS: + { + size_t traffic_selectors_length = payload_length - TS_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRAFFIC_SELECTOR_SUBSTRUCTURE, traffic_selectors_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case UNKNOWN_PAYLOAD: + { + size_t unknown_payload_data_length = payload_length - UNKNOWN_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, unknown_payload_data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + default: + { + DBG1(DBG_ENC, " no rule to parse rule %d %N", + rule_number, encoding_type_names, rule->type); + pld->destroy(pld); + return PARSE_ERROR; + } + } + /* process next rulue */ + rule++; + } + + *payload = pld; + DBG2(DBG_ENC, "parsing %N payload finished", + payload_type_names, payload_type); + return SUCCESS; +} + +/** + * Implementation of parser_t.get_remaining_byte_count. + */ +static int get_remaining_byte_count (private_parser_t *this) +{ + int count = (this->input_roof - this->byte_pos); + return count; +} + +/** + * Implementation of parser_t.reset_context. + */ +static void reset_context (private_parser_t *this) +{ + this->byte_pos = this->input; + this->bit_pos = 0; +} + +/** + * Implementation of parser_t.destroy. + */ +static void destroy(private_parser_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +parser_t *parser_create(chunk_t data) +{ + private_parser_t *this = malloc_thing(private_parser_t); + + this->public.parse_payload = (status_t(*)(parser_t*,payload_type_t,payload_t**)) parse_payload; + this->public.reset_context = (void(*)(parser_t*)) reset_context; + this->public.get_remaining_byte_count = (int (*) (parser_t *))get_remaining_byte_count; + this->public.destroy = (void(*)(parser_t*)) destroy; + + this->parse_uint4 = parse_uint4; + this->parse_uint8 = parse_uint8; + this->parse_uint15 = parse_uint15; + this->parse_uint16 = parse_uint16; + this->parse_uint32 = parse_uint32; + this->parse_uint64 = parse_uint64; + this->parse_bytes = parse_bytes; + this->parse_bit = parse_bit; + this->parse_list = parse_list; + this->parse_chunk = parse_chunk; + + this->input = data.ptr; + this->byte_pos = data.ptr; + this->bit_pos = 0; + this->input_roof = data.ptr + data.len; + + return (parser_t*)this; +} diff --git a/src/charon/encoding/parser.h b/src/charon/encoding/parser.h new file mode 100644 index 000000000..e9978524c --- /dev/null +++ b/src/charon/encoding/parser.h @@ -0,0 +1,95 @@ +/** + * @file parser.h + * + * @brief Interface of parser_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef PARSER_H_ +#define PARSER_H_ + +typedef struct parser_t parser_t; + +#include <library.h> +#include <encoding/payloads/encodings.h> +#include <encoding/payloads/payload.h> + +/** + * @brief A parser_t class to parse IKEv2 payloads. + * + * A parser is used for parsing one chunk of data. Multiple + * payloads can be parsed out of the chunk using parse_payload. + * The parser remains the state until destroyed. + * + * @b Constructors: + * - parser_create() + * + * @ingroup encoding + */ +struct parser_t { + + /** + * @brief Parses the next payload. + * + * @warning Caller is responsible for freeing allocated payload. + * + * Rules for parsing are described in the payload definition. + * + * @param this parser_t bject + * @param payload_type payload type to parse + * @param[out] payload pointer where parsed payload was allocated + * @return + * - SUCCESSFUL if succeeded, + * - PARSE_ERROR if corrupted/invalid data found + */ + status_t (*parse_payload) (parser_t *this, payload_type_t payload_type, payload_t **payload); + + /** + * Gets the remaining byte count which is not currently parsed. + * + * @param parser parser_t object + */ + int (*get_remaining_byte_count) (parser_t *this); + + /** + * @brief Resets the current parser context. + * + * @param parser parser_t object + */ + void (*reset_context) (parser_t *this); + + /** + * @brief Destroys a parser_t object. + * + * @param parser parser_t object + */ + void (*destroy) (parser_t *this); +}; + +/** + * @brief Constructor to create a parser_t object. + * + * @param data chunk of data to parse with this parser_t object + * @return parser_t object + * + * @ingroup encoding + */ +parser_t *parser_create(chunk_t data); + +#endif /*PARSER_H_*/ diff --git a/src/charon/encoding/payloads/auth_payload.c b/src/charon/encoding/payloads/auth_payload.c new file mode 100644 index 000000000..256d6c8a4 --- /dev/null +++ b/src/charon/encoding/payloads/auth_payload.c @@ -0,0 +1,265 @@ +/** + * @file auth_payload.h + * + * @brief Implementation of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "auth_payload.h" + +#include <encoding/payloads/encodings.h> + + +typedef struct private_auth_payload_t private_auth_payload_t; + +/** + * Private data of an auth_payload_t object. + * + */ +struct private_auth_payload_t { + + /** + * Public auth_payload_t interface. + */ + auth_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Method of the AUTH Data. + */ + u_int8_t auth_method; + + /** + * The contained auth data value. + */ + chunk_t auth_data; +}; + +/** + * Encoding rules to parse or generate a AUTH payload + * + * The defined offsets are the positions in a object of type + * private_auth_payload_t. + */ +encoding_rule_t auth_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_auth_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_auth_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_auth_payload_t, payload_length)}, + /* 1 Byte AUTH type*/ + { U_INT_8, offsetof(private_auth_payload_t, auth_method) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some auth data bytes, length is defined in PAYLOAD_LENGTH */ + { AUTH_DATA, offsetof(private_auth_payload_t, auth_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Auth Method ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Authentication Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_auth_payload_t *this) +{ + if (this->auth_method == 0 || + (this->auth_method >= 4 && this->auth_method <= 200)) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of auth_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_auth_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = auth_payload_encodings; + *rule_count = sizeof(auth_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_auth_payload_t *this) +{ + return AUTHENTICATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_auth_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_auth_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_auth_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of auth_payload_t.set_auth_method. + */ +static void set_auth_method (private_auth_payload_t *this, auth_method_t method) +{ + this->auth_method = method; +} + +/** + * Implementation of auth_payload_t.get_auth_method. + */ +static auth_method_t get_auth_method (private_auth_payload_t *this) +{ + return (this->auth_method); +} + +/** + * Implementation of auth_payload_t.set_data. + */ +static void set_data (private_auth_payload_t *this, chunk_t data) +{ + if (this->auth_data.ptr != NULL) + { + chunk_free(&(this->auth_data)); + } + this->auth_data.ptr = clalloc(data.ptr,data.len); + this->auth_data.len = data.len; + this->payload_length = AUTH_PAYLOAD_HEADER_LENGTH + this->auth_data.len; +} + +/** + * Implementation of auth_payload_t.get_data. + */ +static chunk_t get_data (private_auth_payload_t *this) +{ + return (this->auth_data); +} + +/** + * Implementation of auth_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_auth_payload_t *this) +{ + chunk_t cloned_data; + if (this->auth_data.ptr == NULL) + { + return (this->auth_data); + } + cloned_data.ptr = clalloc(this->auth_data.ptr,this->auth_data.len); + cloned_data.len = this->auth_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and auth_payload_t.destroy. + */ +static void destroy(private_auth_payload_t *this) +{ + if (this->auth_data.ptr != NULL) + { + chunk_free(&(this->auth_data)); + } + + free(this); +} + +/* + * Described in header + */ +auth_payload_t *auth_payload_create() +{ + private_auth_payload_t *this = malloc_thing(private_auth_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (auth_payload_t *)) destroy; + this->public.set_auth_method = (void (*) (auth_payload_t *,auth_method_t)) set_auth_method; + this->public.get_auth_method = (auth_method_t (*) (auth_payload_t *)) get_auth_method; + this->public.set_data = (void (*) (auth_payload_t *,chunk_t)) set_data; + this->public.get_data_clone = (chunk_t (*) (auth_payload_t *)) get_data_clone; + this->public.get_data = (chunk_t (*) (auth_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =AUTH_PAYLOAD_HEADER_LENGTH; + this->auth_data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/auth_payload.h b/src/charon/encoding/payloads/auth_payload.h new file mode 100644 index 000000000..2db82ec0b --- /dev/null +++ b/src/charon/encoding/payloads/auth_payload.h @@ -0,0 +1,121 @@ +/** + * @file auth_payload.h + * + * @brief Interface of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef AUTH_PAYLOAD_H_ +#define AUTH_PAYLOAD_H_ + +typedef struct auth_payload_t auth_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <sa/authenticators/authenticator.h> + +/** + * Length of a auth payload without the auth data in bytes. + * + * @ingroup payloads + */ +#define AUTH_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 AUTH payload. + * + * The AUTH payload format is described in RFC section 3.8. + * + * @b Constructors: + * - auth_payload_create() + * + * @ingroup payloads + */ +struct auth_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the AUTH method. + * + * @param this calling auth_payload_t object + * @param method auth_method_t to use + */ + void (*set_auth_method) (auth_payload_t *this, auth_method_t method); + + /** + * @brief Get the AUTH method. + * + * @param this calling auth_payload_t object + * @return auth_method_t used + */ + auth_method_t (*get_auth_method) (auth_payload_t *this); + + /** + * @brief Set the AUTH data. + * + * Data are getting cloned. + * + * @param this calling auth_payload_t object + * @param data AUTH data as chunk_t + */ + void (*set_data) (auth_payload_t *this, chunk_t data); + + /** + * @brief Get the AUTH data. + * + * Returned data are a copy of the internal one. + * + * @param this calling auth_payload_t object + * @return AUTH data as chunk_t + */ + chunk_t (*get_data_clone) (auth_payload_t *this); + + /** + * @brief Get the AUTH data. + * + * Returned data are NOT copied + * + * @param this calling auth_payload_t object + * @return AUTH data as chunk_t + */ + chunk_t (*get_data) (auth_payload_t *this); + + /** + * @brief Destroys an auth_payload_t object. + * + * @param this auth_payload_t object to destroy + */ + void (*destroy) (auth_payload_t *this); +}; + +/** + * @brief Creates an empty auth_payload_t object. + * + * @return auth_payload_t object + * + * @ingroup payloads + */ +auth_payload_t *auth_payload_create(void); + + +#endif /* AUTH_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/cert_payload.c b/src/charon/encoding/payloads/cert_payload.c new file mode 100644 index 000000000..c456f4936 --- /dev/null +++ b/src/charon/encoding/payloads/cert_payload.c @@ -0,0 +1,290 @@ +/** + * @file cert_payload.c + * + * @brief Implementation of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "cert_payload.h" + + +ENUM(cert_encoding_names, CERT_NONE, CERT_OCSP_CONTENT, + "CERT_NONE", + "CERT_PKCS7_WRAPPED_X509", + "CERT_PGP", + "CERT_DNS_SIGNED_KEY", + "CERT_X509_SIGNATURE", + "CERT_X509_KEY_EXCHANGE", + "CERT_KERBEROS_TOKENS", + "CERT_CRL", + "CERT_ARL", + "CERT_SPKI", + "CERT_X509_ATTRIBUTE", + "CERT_RAW_RSA_KEY", + "CERT_X509_HASH_AND_URL", + "CERT_X509_HASH_AND_URL_BUNDLE", + "CERT_OCSP_CONTENT", +); + +typedef struct private_cert_payload_t private_cert_payload_t; + +/** + * Private data of an cert_payload_t object. + * + */ +struct private_cert_payload_t { + /** + * Public cert_payload_t interface. + */ + cert_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Encoding of the CERT Data. + */ + u_int8_t cert_encoding; + + /** + * The contained cert data value. + */ + chunk_t cert_data; +}; + +/** + * Encoding rules to parse or generate a CERT payload + * + * The defined offsets are the positions in a object of type + * private_cert_payload_t. + * + */ +encoding_rule_t cert_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_cert_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_cert_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_cert_payload_t, payload_length)}, + /* 1 Byte CERT type*/ + { U_INT_8, offsetof(private_cert_payload_t, cert_encoding) }, + /* some cert data bytes, length is defined in PAYLOAD_LENGTH */ + { CERT_DATA, offsetof(private_cert_payload_t, cert_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certificate Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_cert_payload_t *this) +{ + if ((this->cert_encoding == 0) || + ((this->cert_encoding >= CERT_ROOF) && (this->cert_encoding <= 200))) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of cert_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_cert_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = cert_payload_encodings; + *rule_count = sizeof(cert_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_cert_payload_t *this) +{ + return CERTIFICATE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_cert_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_cert_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_cert_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of cert_payload_t.set_cert_encoding. + */ +static void set_cert_encoding (private_cert_payload_t *this, cert_encoding_t encoding) +{ + this->cert_encoding = encoding; +} + +/** + * Implementation of cert_payload_t.get_cert_encoding. + */ +static cert_encoding_t get_cert_encoding (private_cert_payload_t *this) +{ + return (this->cert_encoding); +} + +/** + * Implementation of cert_payload_t.set_data. + */ +static void set_data (private_cert_payload_t *this, chunk_t data) +{ + if (this->cert_data.ptr != NULL) + { + chunk_free(&(this->cert_data)); + } + this->cert_data.ptr = clalloc(data.ptr,data.len); + this->cert_data.len = data.len; + this->payload_length = CERT_PAYLOAD_HEADER_LENGTH + this->cert_data.len; +} + +/** + * Implementation of cert_payload_t.get_data. + */ +static chunk_t get_data (private_cert_payload_t *this) +{ + return (this->cert_data); +} + +/** + * Implementation of cert_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_cert_payload_t *this) +{ + chunk_t cloned_data; + if (this->cert_data.ptr == NULL) + { + return (this->cert_data); + } + cloned_data.ptr = clalloc(this->cert_data.ptr,this->cert_data.len); + cloned_data.len = this->cert_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and cert_payload_t.destroy. + */ +static void destroy(private_cert_payload_t *this) +{ + if (this->cert_data.ptr != NULL) + { + chunk_free(&(this->cert_data)); + } + + free(this); +} + +/* + * Described in header + */ +cert_payload_t *cert_payload_create() +{ + private_cert_payload_t *this = malloc_thing(private_cert_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t*))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t*,encoding_rule_t**, size_t*))get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t*))get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t*))get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t*,payload_type_t))set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t*))get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t*))destroy; + + /* public functions */ + this->public.destroy = (void (*) (cert_payload_t*))destroy; + this->public.set_cert_encoding = (void (*) (cert_payload_t*,cert_encoding_t))set_cert_encoding; + this->public.get_cert_encoding = (cert_encoding_t (*) (cert_payload_t*))get_cert_encoding; + this->public.set_data = (void (*) (cert_payload_t*,chunk_t))set_data; + this->public.get_data_clone = (chunk_t (*) (cert_payload_t*))get_data_clone; + this->public.get_data = (chunk_t (*) (cert_payload_t*))get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = CERT_PAYLOAD_HEADER_LENGTH; + this->cert_data = chunk_empty; + + return (&(this->public)); +} + +/* + * Described in header + */ +cert_payload_t *cert_payload_create_from_x509(x509_t *cert) +{ + cert_payload_t *this = cert_payload_create(); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, cert->get_certificate(cert)); + return this; +} diff --git a/src/charon/encoding/payloads/cert_payload.h b/src/charon/encoding/payloads/cert_payload.h new file mode 100644 index 000000000..bcb961398 --- /dev/null +++ b/src/charon/encoding/payloads/cert_payload.h @@ -0,0 +1,166 @@ +/** + * @file cert_payload.h + * + * @brief Interface of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CERT_PAYLOAD_H_ +#define CERT_PAYLOAD_H_ + +typedef enum cert_encoding_t cert_encoding_t; +typedef struct cert_payload_t cert_payload_t; + +#include <library.h> +#include <crypto/x509.h> +#include <encoding/payloads/payload.h> + +/** + * Length of a cert payload without the cert data in bytes. + * + * @ingroup payloads + */ +#define CERT_PAYLOAD_HEADER_LENGTH 5 + +/** + * @brief Certificate encoding, as described in IKEv2 RFC section 3.6 + * + * @ingroup payloads + */ +enum cert_encoding_t { + CERT_NONE = 0, + CERT_PKCS7_WRAPPED_X509 = 1, + CERT_PGP = 2, + CERT_DNS_SIGNED_KEY = 3, + CERT_X509_SIGNATURE = 4, + CERT_KERBEROS_TOKEN = 6, + CERT_CRL = 7, + CERT_ARL = 8, + CERT_SPKI = 9, + CERT_X509_ATTRIBUTE = 10, + CERT_RAW_RSA_KEY = 11, + CERT_X509_HASH_AND_URL = 12, + CERT_X509_HASH_AND_URL_BUNDLE = 13, + CERT_OCSP_CONTENT = 14, /* from RFC 4806 */ + CERT_ROOF = 15 +}; + +/** + * string mappings for cert_encoding_t. + * + * @ingroup payloads + */ +extern enum_name_t *cert_encoding_names; + +/** + * @brief Class representing an IKEv2 CERT payload. + * + * The CERT payload format is described in RFC section 3.6. + * This is just a dummy implementation to fullfill the standards + * requirements. A full implementation would offer setters/getters + * for the different encoding types. + * + * @b Constructors: + * - cert_payload_create() + * + * @todo Implement setters/getters for the different certificate encodings. + * + * @ingroup payloads + */ +struct cert_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the CERT encoding. + * + * @param this calling cert_payload_t object + * @param encoding CERT encoding + */ + void (*set_cert_encoding) (cert_payload_t *this, cert_encoding_t encoding); + + /** + * @brief Get the CERT encoding. + * + * @param this calling cert_payload_t object + * @return Encoding of the CERT + */ + cert_encoding_t (*get_cert_encoding) (cert_payload_t *this); + + /** + * @brief Set the CERT data. + * + * Data are getting cloned. + * + * @param this calling cert_payload_t object + * @param data CERT data as chunk_t + */ + void (*set_data) (cert_payload_t *this, chunk_t data); + + /** + * @brief Get the CERT data. + * + * Returned data are a copy of the internal one. + * + * @param this calling cert_payload_t object + * @return CERT data as chunk_t + */ + chunk_t (*get_data_clone) (cert_payload_t *this); + + /** + * @brief Get the CERT data. + * + * Returned data are NOT copied. + * + * @param this calling cert_payload_t object + * @return CERT data as chunk_t + */ + chunk_t (*get_data) (cert_payload_t *this); + + /** + * @brief Destroys an cert_payload_t object. + * + * @param this cert_payload_t object to destroy + */ + void (*destroy) (cert_payload_t *this); +}; + +/** + * @brief Creates an empty cert_payload_t object. + * + * @return cert_payload_t object + * + * @ingroup payloads + */ +cert_payload_t *cert_payload_create(void); + +/** + * @brief Creates a cert_payload_t object with an X.509 certificate. + * + * @param cert X.509 certificate + * @return cert_payload_t object + * + * @ingroup payloads + */ +cert_payload_t *cert_payload_create_from_x509(x509_t *cert); + +#endif /* CERT_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/certreq_payload.c b/src/charon/encoding/payloads/certreq_payload.c new file mode 100644 index 000000000..46663811a --- /dev/null +++ b/src/charon/encoding/payloads/certreq_payload.c @@ -0,0 +1,335 @@ +/** + * @file certreq_payload.c + * + * @brief Implementation of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> +#include <string.h> + +#include <daemon.h> +#include <crypto/hashers/hasher.h> +#include <crypto/ca.h> + +#include "certreq_payload.h" + + +typedef struct private_certreq_payload_t private_certreq_payload_t; + +/** + * Private data of an certreq_payload_t object. + * + */ +struct private_certreq_payload_t { + /** + * Public certreq_payload_t interface. + */ + certreq_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Encoding of the CERT Data. + */ + u_int8_t cert_encoding; + + /** + * The contained certreq data value. + */ + chunk_t certreq_data; +}; + +/** + * Encoding rules to parse or generate a CERTREQ payload + * + * The defined offsets are the positions in a object of type + * private_certreq_payload_t. + * + */ +encoding_rule_t certreq_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_certreq_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_certreq_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_certreq_payload_t, payload_length)}, + /* 1 Byte CERTREQ type*/ + { U_INT_8, offsetof(private_certreq_payload_t, cert_encoding)}, + /* some certreq data bytes, length is defined in PAYLOAD_LENGTH */ + { CERTREQ_DATA, offsetof(private_certreq_payload_t, certreq_data)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certification Authority ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_certreq_payload_t *this) +{ + if ((this->cert_encoding == 0) || + ((this->cert_encoding >= CERT_ROOF) && (this->cert_encoding <= 200))) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of certreq_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_certreq_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = certreq_payload_encodings; + *rule_count = sizeof(certreq_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_certreq_payload_t *this) +{ + return CERTIFICATE_REQUEST; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_certreq_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_certreq_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_certreq_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of certreq_payload_t.set_cert_encoding. + */ +static void set_cert_encoding (private_certreq_payload_t *this, cert_encoding_t encoding) +{ + this->cert_encoding = encoding; +} + +/** + * Implementation of certreq_payload_t.get_cert_encoding. + */ +static cert_encoding_t get_cert_encoding (private_certreq_payload_t *this) +{ + return (this->cert_encoding); +} + +/** + * Implementation of certreq_payload_t.set_data. + */ +static void set_data (private_certreq_payload_t *this, chunk_t data) +{ + if (this->certreq_data.ptr != NULL) + { + chunk_free(&(this->certreq_data)); + } + this->certreq_data.ptr = clalloc(data.ptr,data.len); + this->certreq_data.len = data.len; + this->payload_length = CERTREQ_PAYLOAD_HEADER_LENGTH + this->certreq_data.len; +} + +/** + * Implementation of certreq_payload_t.get_data. + */ +static chunk_t get_data (private_certreq_payload_t *this) +{ + return (this->certreq_data); +} + +/** + * Implementation of certreq_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_certreq_payload_t *this) +{ + chunk_t cloned_data; + if (this->certreq_data.ptr == NULL) + { + return (this->certreq_data); + } + cloned_data.ptr = clalloc(this->certreq_data.ptr,this->certreq_data.len); + cloned_data.len = this->certreq_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and certreq_payload_t.destroy. + */ +static void destroy(private_certreq_payload_t *this) +{ + if (this->certreq_data.ptr != NULL) + { + chunk_free(&(this->certreq_data)); + } + + free(this); +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create() +{ + private_certreq_payload_t *this = malloc_thing(private_certreq_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t*))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t*,encoding_rule_t**,size_t*))get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t*))get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t*))get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t*,payload_type_t))set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t*))get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t*))destroy; + + /* public functions */ + this->public.destroy = (void (*) (certreq_payload_t*)) destroy; + this->public.set_cert_encoding = (void (*) (certreq_payload_t*,cert_encoding_t))set_cert_encoding; + this->public.get_cert_encoding = (cert_encoding_t (*) (certreq_payload_t*))get_cert_encoding; + this->public.set_data = (void (*) (certreq_payload_t*,chunk_t))set_data; + this->public.get_data_clone = (chunk_t (*) (certreq_payload_t*))get_data_clone; + this->public.get_data = (chunk_t (*) (certreq_payload_t*))get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =CERTREQ_PAYLOAD_HEADER_LENGTH; + this->certreq_data = chunk_empty; + + return (&(this->public)); +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create_from_cacert(identification_t *id) +{ + x509_t *cacert; + rsa_public_key_t *pubkey; + chunk_t keyid; + certreq_payload_t *this; + + cacert = charon->credentials->get_auth_certificate(charon->credentials, AUTH_CA, id); + if (cacert == NULL) + { + /* no such CA cert */ + return NULL; + } + + this = certreq_payload_create(); + pubkey = cacert->get_public_key(cacert); + keyid = pubkey->get_keyid(pubkey); + + DBG2(DBG_IKE, "requesting certificate issued by '%D'", id); + DBG2(DBG_IKE, " with keyid %#B", &keyid); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, keyid); + return this; +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create_from_cacerts(void) +{ + certreq_payload_t *this; + chunk_t keyids; + u_char *pos; + ca_info_t *cainfo; + + iterator_t *iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + int count = iterator->get_count(iterator); + + if (count == 0) + { + iterator->destroy(iterator); + return NULL; + } + + this = certreq_payload_create(); + keyids = chunk_alloc(count * HASH_SIZE_SHA1); + pos = keyids.ptr; + + while (iterator->iterate(iterator, (void**)&cainfo)) + { + x509_t *cacert = cainfo->get_certificate(cainfo); + chunk_t keyid = cacert->get_keyid(cacert); + + DBG2(DBG_IKE, "requesting certificate issued by '%D'", cacert->get_subject(cacert)); + DBG2(DBG_IKE, " with keyid %#B", &keyid); + memcpy(pos, keyid.ptr, keyid.len); + pos += HASH_SIZE_SHA1; + } + iterator->destroy(iterator); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, keyids); + free(keyids.ptr); + return this; +} diff --git a/src/charon/encoding/payloads/certreq_payload.h b/src/charon/encoding/payloads/certreq_payload.h new file mode 100644 index 000000000..2985fdae1 --- /dev/null +++ b/src/charon/encoding/payloads/certreq_payload.h @@ -0,0 +1,144 @@ +/** + * @file certreq_payload.h + * + * @brief Interface of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CERTREQ_PAYLOAD_H_ +#define CERTREQ_PAYLOAD_H_ + +typedef struct certreq_payload_t certreq_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/cert_payload.h> + +/** + * Length of a CERTREQ payload without the CERTREQ data in bytes. + * + * @ingroup payloads + */ +#define CERTREQ_PAYLOAD_HEADER_LENGTH 5 + + +/** + * @brief Class representing an IKEv2 CERTREQ payload. + * + * The CERTREQ payload format is described in RFC section 3.7. + * This is just a dummy implementation to fullfill the standards + * requirements. A full implementation would offer setters/getters + * for the different encoding types. + * + * @b Constructors: + * - certreq_payload_create() + * + * @todo Implement payload functionality. + * + * @ingroup payloads + */ +struct certreq_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the CERT encoding. + * + * @param this calling certreq_payload_t object + * @param encoding CERT encoding + */ + void (*set_cert_encoding) (certreq_payload_t *this, cert_encoding_t encoding); + + /** + * @brief Get the CERT encoding. + * + * @param this calling certreq_payload_t object + * @return Encoding of the CERT + */ + cert_encoding_t (*get_cert_encoding) (certreq_payload_t *this); + + /** + * @brief Set the CERTREQ data. + * + * Data are getting cloned. + * + * @param this calling certreq_payload_t object + * @param data CERTREQ data as chunk_t + */ + void (*set_data) (certreq_payload_t *this, chunk_t data); + + /** + * @brief Get the CERTREQ data. + * + * Returned data are a copy of the internal one. + * + * @param this calling certreq_payload_t object + * @return CERTREQ data as chunk_t + */ + chunk_t (*get_data_clone) (certreq_payload_t *this); + + /** + * @brief Get the CERTREQ data. + * + * Returned data are NOT copied. + * + * @param this calling certreq_payload_t object + * @return CERTREQ data as chunk_t + */ + chunk_t (*get_data) (certreq_payload_t *this); + + /** + * @brief Destroys an certreq_payload_t object. + * + * @param this certreq_payload_t object to destroy + */ + void (*destroy) (certreq_payload_t *this); +}; + +/** + * @brief Creates an empty certreq_payload_t object. + * + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create(void); + +/** + * @brief Creates a certreq_payload_t object from a ca certificate + * + * @param id subject distinguished name of CA certificate + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create_from_cacert(identification_t *id); + +/** + * @brief Creates a certreq_payload_t object from all ca certificates + * + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create_from_cacerts(void); + +#endif /* CERTREQ_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/configuration_attribute.c b/src/charon/encoding/payloads/configuration_attribute.c new file mode 100644 index 000000000..0aa82169f --- /dev/null +++ b/src/charon/encoding/payloads/configuration_attribute.c @@ -0,0 +1,313 @@ +/** + * @file configuration_attribute.c + * + * @brief Implementation of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "configuration_attribute.h" + +#include <encoding/payloads/encodings.h> +#include <library.h> +#include <daemon.h> + + +typedef struct private_configuration_attribute_t private_configuration_attribute_t; + +/** + * Private data of an configuration_attribute_t object. + * + */ +struct private_configuration_attribute_t { + /** + * Public configuration_attribute_t interface. + */ + configuration_attribute_t public; + + /** + * Type of the attribute. + */ + u_int16_t attribute_type; + + /** + * Length of the attribute. + */ + u_int16_t attribute_length; + + /** + * Attribute value as chunk. + */ + chunk_t attribute_value; +}; + +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, + "INTERNAL_IP4_ADDRESS", + "INTERNAL_IP4_NETMASK", + "INTERNAL_IP4_DNS", + "INTERNAL_IP4_NBNS", + "INTERNAL_ADDRESS_EXPIRY", + "INTERNAL_IP4_DHCP", + "APPLICATION_VERSION", + "INTERNAL_IP6_ADDRESS"); +ENUM_NEXT(configuration_attribute_type_names, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS, + "INTERNAL_IP6_DNS", + "INTERNAL_IP6_NBNS", + "INTERNAL_IP6_DHCP", + "INTERNAL_IP4_SUBNET", + "SUPPORTED_ATTRIBUTES", + "INTERNAL_IP6_SUBNET"); +ENUM_END(configuration_attribute_type_names, INTERNAL_IP6_SUBNET); + +/** + * Encoding rules to parse or generate a configuration attribute. + * + * The defined offsets are the positions in a object of type + * private_configuration_attribute_t. + * + */ +encoding_rule_t configuration_attribute_encodings[] = { + + { RESERVED_BIT, 0 }, + /* type of the attribute as 15 bit unsigned integer */ + { ATTRIBUTE_TYPE, offsetof(private_configuration_attribute_t, attribute_type) }, + /* Length of attribute value */ + { CONFIGURATION_ATTRIBUTE_LENGTH, offsetof(private_configuration_attribute_t, attribute_length)}, + /* Value of attribute if attribute format flag is zero */ + { CONFIGURATION_ATTRIBUTE_VALUE, offsetof(private_configuration_attribute_t, attribute_value)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !R| Attribute Type ! Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ Value ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_configuration_attribute_t *this) +{ + bool failed = FALSE; + + if (this->attribute_length != this->attribute_value.len) + { + DBG1(DBG_ENC, "invalid attribute length"); + return FAILED; + } + + switch (this->attribute_type) + { + case INTERNAL_IP4_ADDRESS: + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_NBNS: + case INTERNAL_ADDRESS_EXPIRY: + case INTERNAL_IP4_DHCP: + if (this->attribute_length != 0 && this->attribute_length != 4) + { + failed = TRUE; + } + break; + case INTERNAL_IP4_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 8) + { + failed = TRUE; + } + break; + case INTERNAL_IP6_ADDRESS: + case INTERNAL_IP6_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 17) + { + failed = TRUE; + } + break; + case INTERNAL_IP6_DNS: + case INTERNAL_IP6_NBNS: + case INTERNAL_IP6_DHCP: + if (this->attribute_length != 0 && this->attribute_length != 16) + { + failed = TRUE; + } + break; + case SUPPORTED_ATTRIBUTES: + if (this->attribute_length % 2) + { + failed = TRUE; + } + break; + case APPLICATION_VERSION: + /* any length acceptable */ + break; + default: + DBG1(DBG_ENC, "unknown attribute type %N", + configuration_attribute_type_names, this->attribute_type); + return FAILED; + } + + if (failed) + { + DBG1(DBG_ENC, "invalid attribute length %d for %N", + this->attribute_length, configuration_attribute_type_names, + this->attribute_type); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_configuration_attribute_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = configuration_attribute_encodings; + *rule_count = sizeof(configuration_attribute_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_configuration_attribute_t *this) +{ + return CONFIGURATION_ATTRIBUTE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_configuration_attribute_t *this) +{ + return (NO_PAYLOAD); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_configuration_attribute_t *this,payload_type_t type) +{ +} + +/** + * Implementation of configuration_attribute_t.get_length. + */ +static size_t get_length(private_configuration_attribute_t *this) +{ + return (this->attribute_value.len + CONFIGURATION_ATTRIBUTE_HEADER_LENGTH); +} + +/** + * Implementation of configuration_attribute_t.set_value. + */ +static void set_value(private_configuration_attribute_t *this, chunk_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + chunk_free(&(this->attribute_value)); + } + + this->attribute_value.ptr = clalloc(value.ptr,value.len); + this->attribute_value.len = value.len; + + this->attribute_length = this->attribute_value.len; +} + +/** + * Implementation of configuration_attribute_t.get_value. + */ +static chunk_t get_value (private_configuration_attribute_t *this) +{ + return this->attribute_value; +} + +/** + * Implementation of configuration_attribute_t.set_type. + */ +static void set_attribute_type (private_configuration_attribute_t *this, u_int16_t type) +{ + this->attribute_type = type & 0x7FFF; +} + +/** + * Implementation of configuration_attribute_t.get_type. + */ +static u_int16_t get_attribute_type (private_configuration_attribute_t *this) +{ + return this->attribute_type; +} + +/** + * Implementation of configuration_attribute_t.get_length. + */ +static u_int16_t get_attribute_length (private_configuration_attribute_t *this) +{ + return this->attribute_length; +} + + +/** + * Implementation of configuration_attribute_t.destroy and payload_t.destroy. + */ +static void destroy(private_configuration_attribute_t *this) +{ + if (this->attribute_value.ptr != NULL) + { + free(this->attribute_value.ptr); + } + free(this); +} + +/* + * Described in header. + */ +configuration_attribute_t *configuration_attribute_create() +{ + private_configuration_attribute_t *this = malloc_thing(private_configuration_attribute_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.set_value = (void (*) (configuration_attribute_t *,chunk_t)) set_value; + this->public.get_value = (chunk_t (*) (configuration_attribute_t *)) get_value; + this->public.set_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type; + this->public.get_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length; + this->public.destroy = (void (*) (configuration_attribute_t *)) destroy; + + /* set default values of the fields */ + this->attribute_type = 0; + this->attribute_value = chunk_empty; + this->attribute_length = 0; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/configuration_attribute.h b/src/charon/encoding/payloads/configuration_attribute.h new file mode 100644 index 000000000..5c4f65b14 --- /dev/null +++ b/src/charon/encoding/payloads/configuration_attribute.h @@ -0,0 +1,147 @@ +/** + * @file configuration_attribute.h + * + * @brief Interface of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CONFIGURATION_ATTRIBUTE_H_ +#define CONFIGURATION_ATTRIBUTE_H_ + +typedef enum configuration_attribute_type_t configuration_attribute_type_t; +typedef struct configuration_attribute_t configuration_attribute_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + + +/** + * Configuration attribute header length in bytes. + * + * @ingroup payloads + */ +#define CONFIGURATION_ATTRIBUTE_HEADER_LENGTH 4 + +/** + * Type of the attribute, as in IKEv2 RFC 3.15.1. + * + * @ingroup payloads + */ +enum configuration_attribute_type_t { + INTERNAL_IP4_ADDRESS = 1, + INTERNAL_IP4_NETMASK = 2, + INTERNAL_IP4_DNS = 3, + INTERNAL_IP4_NBNS = 4, + INTERNAL_ADDRESS_EXPIRY = 5, + INTERNAL_IP4_DHCP = 6, + APPLICATION_VERSION = 7, + INTERNAL_IP6_ADDRESS = 8, + INTERNAL_IP6_DNS = 10, + INTERNAL_IP6_NBNS = 11, + INTERNAL_IP6_DHCP = 12, + INTERNAL_IP4_SUBNET = 13, + SUPPORTED_ATTRIBUTES = 14, + INTERNAL_IP6_SUBNET = 15 +}; + +/** + * enum names for configuration_attribute_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *configuration_attribute_type_names; + +/** + * @brief Class representing an IKEv2-CONFIGURATION Attribute. + * + * The CONFIGURATION ATTRIBUTE format is described in RFC section 3.15.1. + * + * @b Constructors: + * - configuration_attribute_create() + * + * @ingroup payloads + */ +struct configuration_attribute_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling configuration_attribute_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_value) (configuration_attribute_t *this); + + /** + * @brief Sets the value of the attribute. + * + * @warning Value is getting copied. + * + * @param this calling configuration_attribute_t object + * @param value chunk_t pointing to the value to set + */ + void (*set_value) (configuration_attribute_t *this, chunk_t value); + + /** + * @brief Sets the type of the attribute. + * + * @param this calling configuration_attribute_t object + * @param type type to set (most significant bit is set to zero) + */ + void (*set_type) (configuration_attribute_t *this, u_int16_t type); + + /** + * @brief get the type of the attribute. + * + * @param this calling configuration_attribute_t object + * @return type of the value + */ + u_int16_t (*get_type) (configuration_attribute_t *this); + + /** + * @brief get the length of an attribute. + * + * @param this calling configuration_attribute_t object + * @return type of the value + */ + u_int16_t (*get_length) (configuration_attribute_t *this); + + /** + * @brief Destroys an configuration_attribute_t object. + * + * @param this configuration_attribute_t object to destroy + */ + void (*destroy) (configuration_attribute_t *this); +}; + +/** + * @brief Creates an empty configuration_attribute_t object. + * + * @return created configuration_attribute_t object + * + * @ingroup payloads + */ +configuration_attribute_t *configuration_attribute_create(void); + +#endif /* CONFIGURATION_ATTRIBUTE_H_*/ diff --git a/src/charon/encoding/payloads/cp_payload.c b/src/charon/encoding/payloads/cp_payload.c new file mode 100644 index 000000000..380ed9681 --- /dev/null +++ b/src/charon/encoding/payloads/cp_payload.c @@ -0,0 +1,277 @@ +/** + * @file cp_payload.c + * + * @brief Implementation of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "cp_payload.h" + +#include <encoding/payloads/encodings.h> +#include <utils/linked_list.h> + +ENUM(config_type_names, CFG_REQUEST, CFG_ACK, + "CFG_REQUEST", + "CFG_REPLY", + "CFG_SET", + "CFG_ACK", +); + +typedef struct private_cp_payload_t private_cp_payload_t; + +/** + * Private data of an cp_payload_t object. + * + */ +struct private_cp_payload_t { + /** + * Public cp_payload_t interface. + */ + cp_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Configuration Attributes in this payload are stored in a linked_list_t. + */ + linked_list_t * attributes; + + /** + * Config Type. + */ + u_int8_t config_type; +}; + +/** + * Encoding rules to parse or generate a IKEv2-CP Payload + * + * The defined offsets are the positions in a object of type + * private_cp_payload_t. + * + */ +encoding_rule_t cp_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_cp_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_cp_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole CP payload*/ + { PAYLOAD_LENGTH, offsetof(private_cp_payload_t, payload_length) }, + /* Proposals are stored in a proposal substructure, + offset points to a linked_list_t pointer */ + { U_INT_8, offsetof(private_cp_payload_t, config_type) }, + { RESERVED_BYTE,0 }, + { RESERVED_BYTE,0 }, + { RESERVED_BYTE,0 }, + { CONFIGURATION_ATTRIBUTES, offsetof(private_cp_payload_t, attributes) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! CFG Type ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Configuration Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_cp_payload_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + configuration_attribute_t *attribute; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while(iterator->iterate(iterator, (void**)&attribute)) + { + status = attribute->payload_interface.verify(&attribute->payload_interface); + if (status != SUCCESS) + { + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_cp_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = cp_payload_encodings; + *rule_count = sizeof(cp_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_cp_payload_t *this) +{ + return CONFIGURATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_cp_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_cp_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_cp_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_attribute; + size_t length = CP_PAYLOAD_HEADER_LENGTH; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_cp_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of cp_payload_t.create_configuration_attribute_iterator. + */ +static iterator_t *create_attribute_iterator (private_cp_payload_t *this) +{ + return this->attributes->create_iterator(this->attributes, TRUE); +} + +/** + * Implementation of cp_payload_t.add_proposal_substructure. + */ +static void add_configuration_attribute (private_cp_payload_t *this,configuration_attribute_t *attribute) +{ + this->attributes->insert_last(this->attributes,(void *) attribute); + compute_length(this); +} + +/** + * Implementation of cp_payload_t.set_config_type. + */ +static void set_config_type (private_cp_payload_t *this,config_type_t config_type) +{ + this->config_type = config_type; +} + +/** + * Implementation of cp_payload_t.get_config_type. + */ +static config_type_t get_config_type (private_cp_payload_t *this) +{ + return this->config_type; +} + +/** + * Implementation of payload_t.destroy and cp_payload_t.destroy. + */ +static void destroy(private_cp_payload_t *this) +{ + this->attributes->destroy_offset(this->attributes, + offsetof(configuration_attribute_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +cp_payload_t *cp_payload_create() +{ + private_cp_payload_t *this = malloc_thing(private_cp_payload_t); + + /* public interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_attribute_iterator = (iterator_t* (*) (cp_payload_t *)) create_attribute_iterator; + this->public.add_configuration_attribute = (void (*) (cp_payload_t *,configuration_attribute_t *)) add_configuration_attribute; + this->public.set_config_type = (void (*) (cp_payload_t *, config_type_t)) set_config_type; + this->public.get_config_type = (config_type_t (*) (cp_payload_t *)) get_config_type; + this->public.destroy = (void (*) (cp_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = CP_PAYLOAD_HEADER_LENGTH; + + this->attributes = linked_list_create(); + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/cp_payload.h b/src/charon/encoding/payloads/cp_payload.h new file mode 100644 index 000000000..27ff41005 --- /dev/null +++ b/src/charon/encoding/payloads/cp_payload.h @@ -0,0 +1,132 @@ +/** + * @file cp_payload.h + * + * @brief Interface of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef CP_PAYLOAD_H_ +#define CP_PAYLOAD_H_ + +typedef enum config_type_t config_type_t; +typedef struct cp_payload_t cp_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/configuration_attribute.h> +#include <utils/linked_list.h> + +/** + * CP_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define CP_PAYLOAD_HEADER_LENGTH 8 + +/** + * Config Type of an Configuration Payload. + * + * @ingroup payloads + */ +enum config_type_t { + CFG_REQUEST = 1, + CFG_REPLY = 2, + CFG_SET = 3, + CFG_ACK = 4, +}; + +/** + * enum name for config_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *config_type_names; + +/** + * @brief Class representing an IKEv2-CP Payload. + * + * The CP Payload format is described in RFC section 3.15. + * + * @b Constructors: + * - cp_payload_create() + * + * @ingroup payloads + */ +struct cp_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored configuration_attribute_t objects. + * + * When deleting an attribute using this iterator, the length of this + * configuration_attribute_t has to be refreshed by calling get_length()! + * + * @param this calling cp_payload_t object + * @return created iterator_t object + */ + iterator_t *(*create_attribute_iterator) (cp_payload_t *this); + + /** + * @brief Adds a configuration_attribute_t object to this object. + * + * The added configuration_attribute_t object is getting destroyed in + * destroy function of cp_payload_t. + * + * @param this calling cp_payload_t object + * @param attribute configuration_attribute_t object to add + */ + void (*add_configuration_attribute) (cp_payload_t *this, configuration_attribute_t *attribute); + + /** + * @brief Set the config type. + * + * @param this calling cp_payload_t object + * @param config_type config_type_t to set + */ + void (*set_config_type) (cp_payload_t *this,config_type_t config_type); + + /** + * @brief Get the config type. + * + * @param this calling cp_payload_t object + * @return config_type_t + */ + config_type_t (*get_config_type) (cp_payload_t *this); + + /** + * @brief Destroys an cp_payload_t object. + * + * @param this cp_payload_t object to destroy + */ + void (*destroy) (cp_payload_t *this); +}; + +/** + * @brief Creates an empty cp_payload_t object + * + * @return cp_payload_t object + * + * @ingroup payloads + */ +cp_payload_t *cp_payload_create(void); + +#endif /*CP_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/delete_payload.c b/src/charon/encoding/payloads/delete_payload.c new file mode 100644 index 000000000..1d42a3af2 --- /dev/null +++ b/src/charon/encoding/payloads/delete_payload.c @@ -0,0 +1,299 @@ +/** + * @file delete_payload.c + * + * @brief Implementation of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "delete_payload.h" + + +typedef struct private_delete_payload_t private_delete_payload_t; + +/** + * Private data of an delete_payload_t object. + * + */ +struct private_delete_payload_t { + /** + * Public delete_payload_t interface. + */ + delete_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Protocol ID. + */ + u_int8_t protocol_id; + + /** + * SPI Size. + */ + u_int8_t spi_size; + + /** + * Number of SPI's. + */ + u_int16_t spi_count; + + /** + * The contained SPI's. + */ + chunk_t spis; + + /** + * List containing u_int32_t spis + */ + linked_list_t *spi_list; +}; + +/** + * Encoding rules to parse or generate a DELETE payload + * + * The defined offsets are the positions in a object of type + * private_delete_payload_t. + * + */ +encoding_rule_t delete_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_delete_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_delete_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_delete_payload_t, payload_length)}, + { U_INT_8, offsetof(private_delete_payload_t, protocol_id) }, + { U_INT_8, offsetof(private_delete_payload_t, spi_size) }, + { U_INT_16, offsetof(private_delete_payload_t, spi_count) }, + /* some delete data bytes, length is defined in PAYLOAD_LENGTH */ + { SPIS, offsetof(private_delete_payload_t, spis) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Protocol ID ! SPI Size ! # of SPIs ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Security Parameter Index(es) (SPI) ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_delete_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi_size != 4) + { + return FAILED; + } + break; + case PROTO_IKE: + case 0: + /* IKE deletion has no spi assigned! */ + if (this->spi_size != 0) + { + return FAILED; + } + break; + default: + return FAILED; + } + if (this->spis.len != (this->spi_count * this->spi_size)) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of delete_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_delete_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = delete_payload_encodings; + *rule_count = sizeof(delete_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_delete_payload_t *this) +{ + return DELETE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_delete_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_delete_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_delete_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of delete_payload_t.get_protocol_id. + */ +static protocol_id_t get_protocol_id (private_delete_payload_t *this) +{ + return (this->protocol_id); +} + +/** + * Implementation of delete_payload_t.add_spi. + */ +static void add_spi(private_delete_payload_t *this, u_int32_t spi) +{ + /* only add SPIs if AH|ESP, ignore others */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) + { + this->spi_count += 1; + this->spis.len += this->spi_size; + this->spis.ptr = realloc(this->spis.ptr, this->spis.len); + *(u_int32_t*)(this->spis.ptr + (this->spis.len / this->spi_size - 1)) = spi; + if (this->spi_list) + { + /* reset SPI iterator list */ + this->spi_list->destroy(this->spi_list); + this->spi_list = NULL; + } + } +} + +/** + * Implementation of delete_payload_t.create_spi_iterator. + */ +static iterator_t* create_spi_iterator(private_delete_payload_t *this) +{ + int i; + + if (this->spi_list == NULL) + { + this->spi_list = linked_list_create(); + /* only parse SPIs if AH|ESP */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) + { + for (i = 0; i < this->spi_count; i++) + { + this->spi_list->insert_last(this->spi_list, this->spis.ptr + i * + this->spi_size); + } + } + } + return this->spi_list->create_iterator(this->spi_list, TRUE); +} + +/** + * Implementation of payload_t.destroy and delete_payload_t.destroy. + */ +static void destroy(private_delete_payload_t *this) +{ + if (this->spis.ptr != NULL) + { + chunk_free(&this->spis); + } + if (this->spi_list) + { + this->spi_list->destroy(this->spi_list); + } + free(this); +} + +/* + * Described in header + */ +delete_payload_t *delete_payload_create(protocol_id_t protocol_id) +{ + private_delete_payload_t *this = malloc_thing(private_delete_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (delete_payload_t *)) destroy; + this->public.get_protocol_id = (protocol_id_t (*) (delete_payload_t *)) get_protocol_id; + this->public.add_spi = (void (*) (delete_payload_t *,u_int32_t))add_spi; + this->public.create_spi_iterator = (iterator_t* (*) (delete_payload_t *)) create_spi_iterator; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = DELETE_PAYLOAD_HEADER_LENGTH; + this->protocol_id = protocol_id; + this->spi_size = protocol_id == PROTO_AH || protocol_id == PROTO_ESP ? 4 : 0; + this->spi_count = 0; + this->spis = chunk_empty; + this->spi_list = NULL; + + return (&this->public); +} diff --git a/src/charon/encoding/payloads/delete_payload.h b/src/charon/encoding/payloads/delete_payload.h new file mode 100644 index 000000000..508f7fba2 --- /dev/null +++ b/src/charon/encoding/payloads/delete_payload.h @@ -0,0 +1,102 @@ +/** + * @file delete_payload.h + * + * @brief Interface of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef DELETE_PAYLOAD_H_ +#define DELETE_PAYLOAD_H_ + +typedef struct delete_payload_t delete_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/proposal_substructure.h> + +/** + * Length of a delete payload without the SPI in bytes. + * + * @ingroup payloads + */ +#define DELETE_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 DELETE payload. + * + * The DELETE payload format is described in RFC section 3.11. + * + * @b Constructors: + * - delete_payload_create() + * + * @todo Implement better setter/getters + * + * @ingroup payloads + */ +struct delete_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the protocol ID. + * + * @param this calling delete_payload_t object + * @return protocol ID + */ + protocol_id_t (*get_protocol_id) (delete_payload_t *this); + + /** + * @brief Add an SPI to the list of deleted SAs. + * + * @param this calling delete_payload_t object + * @param spi spi to add + */ + void (*add_spi) (delete_payload_t *this, u_int32_t spi); + + /** + * @brief Get an iterator over the SPIs. + * + * The iterate() function returns a pointer to a u_int32_t SPI. + * + * @param this calling delete_payload_t object + * @return iterator over SPIs + */ + iterator_t *(*create_spi_iterator) (delete_payload_t *this); + + /** + * @brief Destroys an delete_payload_t object. + * + * @param this delete_payload_t object to destroy + */ + void (*destroy) (delete_payload_t *this); +}; + +/** + * @brief Creates an empty delete_payload_t object. + * + * @param protocol_id protocol, such as AH|ESP + * @return delete_payload_t object + * + * @ingroup payloads + */ +delete_payload_t *delete_payload_create(protocol_id_t protocol_id); + +#endif /* DELETE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/eap_payload.c b/src/charon/encoding/payloads/eap_payload.c new file mode 100644 index 000000000..79ab32fe5 --- /dev/null +++ b/src/charon/encoding/payloads/eap_payload.c @@ -0,0 +1,331 @@ +/** + * @file eap_payload.c + * + * @brief Implementation of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "eap_payload.h" + +#include <daemon.h> + +typedef struct private_eap_payload_t private_eap_payload_t; + +/** + * Private data of an eap_payload_t object. + * + */ +struct private_eap_payload_t { + /** + * Public eap_payload_t interface. + */ + eap_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * EAP message data, if available + */ + chunk_t data; +}; + +/** + * Encoding rules to parse or generate a EAP payload. + * + * The defined offsets are the positions in a object of type + * private_eap_payload_t. + * + */ +encoding_rule_t eap_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_eap_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_eap_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_eap_payload_t, payload_length) }, + /* chunt to data, starting at "code" */ + { EAP_DATA, offsetof(private_eap_payload_t, data) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Code ! Identifier ! Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Type ! Type_Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_eap_payload_t *this) +{ + u_int16_t length; + u_int8_t code; + + if (this->data.len < 4) + { + DBG1(DBG_ENC, "EAP payloads EAP message too short (%d)", this->data.len); + return FAILED; + } + code = *this->data.ptr; + length = htons(*(u_int16_t*)(this->data.ptr + 2)); + if (this->data.len != length) + { + DBG1(DBG_ENC, "EAP payload length (%d) does not match contained message length (%d)", + this->data.len, length); + return FAILED; + } + switch (code) + { + case EAP_REQUEST: + case EAP_RESPONSE: + { + if (this->data.len < 4) + { + DBG1(DBG_ENC, "EAP Request/Response does not have any data"); + return FAILED; + } + break; + } + case EAP_SUCCESS: + case EAP_FAILURE: + { + if (this->data.len != 4) + { + DBG1(DBG_ENC, "EAP Success/Failure has data"); + return FAILED; + } + break; + } + default: + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of eap_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_eap_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = eap_payload_encodings; + *rule_count = sizeof(eap_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_eap_payload_t *this) +{ + return EXTENSIBLE_AUTHENTICATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_eap_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_eap_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_eap_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of eap_payload_t.get_data. + */ +static chunk_t get_data(private_eap_payload_t *this) +{ + return this->data; +} + +/** + * Implementation of eap_payload_t.set_data. + */ +static void set_data(private_eap_payload_t *this, chunk_t data) +{ + chunk_free(&this->data); + this->data = chunk_clone(data); + this->payload_length = this->data.len + 4; +} + +/** + * Implementation of eap_payload_t.get_code. + */ +static eap_code_t get_code(private_eap_payload_t *this) +{ + if (this->data.len > 0) + { + return *this->data.ptr; + } + /* should not happen, as it is verified */ + return 0; +} + +/** + * Implementation of eap_payload_t.get_identifier. + */ +static u_int8_t get_identifier(private_eap_payload_t *this) +{ + if (this->data.len > 1) + { + return *(this->data.ptr + 1); + } + /* should not happen, as it is verified */ + return 0; +} + +/** + * Implementation of eap_payload_t.get_type. + */ +static eap_type_t get_type(private_eap_payload_t *this) +{ + if (this->data.len > 4) + { + return *(this->data.ptr + 4); + } + return 0; +} + +/** + * Implementation of payload_t.destroy and eap_payload_t.destroy. + */ +static void destroy(private_eap_payload_t *this) +{ + chunk_free(&this->data); + free(this); +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create() +{ + private_eap_payload_t *this = malloc_thing(private_eap_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (eap_payload_t *)) destroy; + this->public.get_data = (chunk_t (*) (eap_payload_t*))get_data; + this->public.set_data = (void (*) (eap_payload_t *,chunk_t))set_data; + this->public.get_code = (eap_code_t (*) (eap_payload_t*))get_code; + this->public.get_identifier = (u_int8_t (*) (eap_payload_t*))get_identifier; + this->public.get_type = (eap_type_t (*) (eap_payload_t*))get_type; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = EAP_PAYLOAD_HEADER_LENGTH; + this->data = chunk_empty; + + return &(this->public); +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_data(chunk_t data) +{ + eap_payload_t *this = eap_payload_create(); + + this->set_data(this, data); + return this; +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_code(eap_code_t code) +{ + eap_payload_t *this = eap_payload_create(); + chunk_t data = chunk_alloca(4); + + *(data.ptr + 0) = code; + *(data.ptr + 1) = 0; + *(u_int16_t*)(data.ptr + 2) = htons(data.len); + + this->set_data(this, data); + return this; +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_nak() +{ + eap_payload_t *this = eap_payload_create(); + chunk_t data = chunk_alloca(5); + + *(data.ptr + 0) = EAP_RESPONSE; + *(data.ptr + 1) = 0; + *(u_int16_t*)(data.ptr + 2) = htons(data.len); + *(data.ptr + 4) = EAP_NAK; + + this->set_data(this, data); + return this; +} diff --git a/src/charon/encoding/payloads/eap_payload.h b/src/charon/encoding/payloads/eap_payload.h new file mode 100644 index 000000000..13c0ade80 --- /dev/null +++ b/src/charon/encoding/payloads/eap_payload.h @@ -0,0 +1,149 @@ +/** + * @file eap_payload.h + * + * @brief Interface of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef EAP_PAYLOAD_H_ +#define EAP_PAYLOAD_H_ + +typedef struct eap_payload_t eap_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <sa/authenticators/eap/eap_method.h> + +/** + * Length of a EAP payload without the EAP Message in bytes. + * + * @ingroup payloads + */ +#define EAP_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Class representing an IKEv2 EAP payload. + * + * The EAP payload format is described in RFC section 3.16. + * + * @b Constructors: + * - eap_payload_create() + * + * @ingroup payloads + */ +struct eap_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the contained EAP data. + * + * This contains the FULL EAP message starting with "code". + * Chunk gets cloned. + * + * @param this calling eap_payload_t object + * @param message EAP data + */ + void (*set_data) (eap_payload_t *this, chunk_t data); + + /** + * @brief Get the contained EAP data. + * + * This contains the FULL EAP message starting with "code". + * + * @param this calling eap_payload_t object + * @return EAP data (pointer to internal data) + */ + chunk_t (*get_data) (eap_payload_t *this); + + /** + * @brief Get the EAP code. + * + * @param this calling eap_payload_t object + * @return EAP message as chunk_t + */ + eap_code_t (*get_code) (eap_payload_t *this); + + /** + * @brief Get the EAP identifier. + * + * @param this calling eap_payload_t object + * @return unique identifier + */ + u_int8_t (*get_identifier) (eap_payload_t *this); + + /** + * @brief Get the EAP method type. + * + * @param this calling eap_payload_t object + * @return EAP method type + */ + eap_type_t (*get_type) (eap_payload_t *this); + + /** + * @brief Destroys an eap_payload_t object. + * + * @param this eap_payload_t object to destroy + */ + void (*destroy) (eap_payload_t *this); +}; + +/** + * @brief Creates an empty eap_payload_t object. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create(void); + +/** + * @brief Creates an eap_payload_t object with data. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_data(chunk_t data); + +/** + * @brief Creates an eap_payload_t object with a code. + * + * Could should be either EAP_SUCCESS/EAP_FAILURE, use + * constructor above otherwise. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_code(eap_code_t code); + +/** + * @brief Creates an eap_payload_t EAP_RESPONSE containing an EAP_NAK. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_nak(); + +#endif /* EAP_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/encodings.c b/src/charon/encoding/payloads/encodings.c new file mode 100644 index 000000000..55a7cf132 --- /dev/null +++ b/src/charon/encoding/payloads/encodings.c @@ -0,0 +1,66 @@ +/** + * @file encodings.c + * + * @brief String mappings of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "encodings.h" + +ENUM(encoding_type_names, U_INT_4, ENCRYPTED_DATA, + "U_INT_4", + "U_INT_8", + "U_INT_16", + "U_INT_32", + "U_INT_64", + "RESERVED_BIT", + "RESERVED_BYTE", + "FLAG", + "PAYLOAD_LENGTH", + "HEADER_LENGTH", + "SPI_SIZE", + "SPI", + "KEY_EXCHANGE_DATA", + "NOTIFICATION_DATA", + "PROPOSALS", + "TRANSFORMS", + "TRANSFORM_ATTRIBUTES", + "CONFIGURATION_ATTRIBUTES", + "CONFIGURATION_ATTRIBUTE_VALUE", + "ATTRIBUTE_FORMAT", + "ATTRIBUTE_TYPE", + "ATTRIBUTE_LENGTH_OR_VALUE", + "CONFIGURATION_ATTRIBUTE_LENGTH", + "ATTRIBUTE_VALUE", + "TRAFFIC_SELECTORS", + "TS_TYPE", + "ADDRESS", + "NONCE_DATA", + "ID_DATA", + "AUTH_DATA", + "CERT_DATA", + "CERTREQ_DATA", + "EAP_DATA", + "SPIS", + "VID_DATA", + "UNKNOWN_DATA", + "IKE_SPI", + "ENCRYPTED_DATA", +); diff --git a/src/charon/encoding/payloads/encodings.h b/src/charon/encoding/payloads/encodings.h new file mode 100644 index 000000000..5e07fbfab --- /dev/null +++ b/src/charon/encoding/payloads/encodings.h @@ -0,0 +1,537 @@ +/** + * @file encodings.h + * + * @brief Definition of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef ENCODINGS_H_ +#define ENCODINGS_H_ + +typedef enum encoding_type_t encoding_type_t; +typedef struct encoding_rule_t encoding_rule_t; + +#include <library.h> + +/** + * @brief All different kinds of encoding types. + * + * Each field of an IKEv2-Message (in header or payload) + * which has to be parsed or generated differently has its own + * type defined here. + * + * Header is parsed like a payload and gets its one payload_id + * from PRIVATE USE space. Also the substructures + * of specific payload types get their own payload_id + * from PRIVATE_USE space. See IKEv2-Draft for more informations. + * + * @ingroup payloads + */ +enum encoding_type_t { + + /** + * Representing a 4 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 4 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 4 bit forward afterwards. + */ + U_INT_4, + + /** + * Representing a 8 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 8 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 8 bit forward afterwards. + */ + U_INT_8, + + /** + * Representing a 16 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + U_INT_16, + + /** + * Representing a 32 Bit unsigned int value. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 32 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 32 bit forward afterwards. + */ + U_INT_32, + + /** + * Representing a 64 Bit unsigned int value. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 64 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 64 bit forward afterwards. + */ + U_INT_64, + + /** + * @brief represents a RESERVED_BIT used in FLAG-Bytes. + * + * When generating, the next bit is set to zero and the current write + * position is moved one bit forward. + * No value is read from the associated data struct. + * The current write position is moved 1 bit forward afterwards. + * + * When parsing, the current read pointer is moved one bit forward. + * No value is written to the associated data struct. + * The current read pointer is moved 1 bit forward afterwards. + */ + RESERVED_BIT, + + /** + * @brief represents a RESERVED_BYTE. + * + * When generating, the next byte is set to zero and the current write + * position is moved one byte forward. + * No value is read from the associated data struct. + * The current write position is moved 1 byte forward afterwards. + * + * When parsing, the current read pointer is moved one byte forward. + * No value is written to the associated data struct. + * The current read pointer is moved 1 byte forward afterwards. + */ + RESERVED_BYTE, + + /** + * Representing a 1 Bit flag. + * + * When generation, the next bit is set to 1 if the associated value + * in the data struct is TRUE, 0 otherwise. The current write position + * is moved 1 bit forward afterwards. + * + * When parsing, the next bit is read and stored in the associated data + * struct. 0 means FALSE, 1 means TRUE, The current read pointer + * is moved 1 bit forward afterwards + */ + FLAG, + + /** + * Representating a length field of a payload. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + PAYLOAD_LENGTH, + + /** + * Representating a length field of a header. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 32 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 32 bit forward afterwards. + */ + HEADER_LENGTH, + + /** + * Representating a spi size field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 8 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 8 bit forward afterwards. + */ + SPI_SIZE, + + /** + * Representating a spi field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing SPI_SIZE bytes are read and written into the chunk pointing to. + */ + SPI, + + /** + * Representating a Key Exchange Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + KEY_EXCHANGE_DATA, + + /** + * Representating a Notification field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - spi size - 8) bytes are read and written into the chunk pointing to. + */ + NOTIFICATION_DATA, + + /** + * Representating one or more proposal substructures. + * + * The offset points to a linked_list_t pointer. + * + * When generating the proposal_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed proposal_substructure_t objects have + * to be stored in the pointed linked_list. + */ + PROPOSALS, + + /** + * Representating one or more transform substructures. + * + * The offset points to a linked_list_t pointer. + * + * When generating the transform_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed transform_substructure_t objects have + * to be stored in the pointed linked_list. + */ + TRANSFORMS, + + /** + * Representating one or more Attributes of a transform substructure. + * + * The offset points to a linked_list_t pointer. + * + * When generating the transform_attribute_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed transform_attribute_t objects have + * to be stored in the pointed linked_list. + */ + TRANSFORM_ATTRIBUTES, + + /** + * Representating one or more Attributes of a configuration payload. + * + * The offset points to a linked_list_t pointer. + * + * When generating the configuration_attribute_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed configuration_attribute_t objects have + * to be stored in the pointed linked_list. + */ + CONFIGURATION_ATTRIBUTES, + + /** + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + CONFIGURATION_ATTRIBUTE_VALUE, + + /** + * Representing a 1 Bit flag specifying the format of a transform attribute. + * + * When generation, the next bit is set to 1 if the associated value + * in the data struct is TRUE, 0 otherwise. The current write position + * is moved 1 bit forward afterwards. + * + * When parsing, the next bit is read and stored in the associated data + * struct. 0 means FALSE, 1 means TRUE, The current read pointer + * is moved 1 bit forward afterwards. + */ + ATTRIBUTE_FORMAT, + /** + * Representing a 15 Bit unsigned int value used as attribute type + * in an attribute transform. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 15 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 15 bit forward afterwards. + */ + ATTRIBUTE_TYPE, + + /** + * Depending on the field of type ATTRIBUTE_FORMAT + * this field contains the length or the value of an transform attribute. + * Its stored in a 16 unsigned integer field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + ATTRIBUTE_LENGTH_OR_VALUE, + + /** + * This field contains the length or the value of an configuration attribute. + * Its stored in a 16 unsigned integer field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + CONFIGURATION_ATTRIBUTE_LENGTH, + + /** + * Depending on the field of type ATTRIBUTE_FORMAT + * this field is available or missing and so parsed/generated + * or not parsed/not generated. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing SPI_SIZE bytes are read and written into the chunk pointing to. + */ + ATTRIBUTE_VALUE, + + /** + * Representating one or more Traffic selectors of a TS payload. + * + * The offset points to a linked_list_t pointer. + * + * When generating the traffic_selector_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed traffic_selector_substructure_t objects have + * to be stored in the pointed linked_list. + */ + TRAFFIC_SELECTORS, + + /** + * Representating a Traffic selector type field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + TS_TYPE, + + /** + * Representating an address field in a traffic selector. + * + * Depending on the last field of type TS_TYPE + * this field is either 4 or 16 byte long. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing 4 or 16 bytes are read and written into the chunk pointing to. + */ + ADDRESS, + + /** + * Representating a Nonce Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + NONCE_DATA, + + /** + * Representating a ID Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + ID_DATA, + + /** + * Representating a AUTH Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + AUTH_DATA, + + /** + * Representating a CERT Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 5) bytes are read and written into the chunk pointing to. + */ + CERT_DATA, + + /** + * Representating a CERTREQ Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 5) bytes are read and written into the chunk pointing to. + */ + CERTREQ_DATA, + + /** + * Representating an EAP message field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + EAP_DATA, + + /** + * Representating the SPIS field in a DELETE payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + SPIS, + + /** + * Representating the VID DATA field in a VENDOR ID payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + VID_DATA, + + /** + * Representating the DATA of an unknown payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + UNKNOWN_DATA, + + /** + * Representating an IKE_SPI field in an IKEv2 Header. + * + * When generating the value of the u_int64_t pointing to + * is written (host and networ order is not changed). + * + * When parsing 8 bytes are read and written into the u_int64_t pointing to. + */ + IKE_SPI, + + /** + * Representing the encrypted data body of a encryption payload. + */ + ENCRYPTED_DATA, +}; + +/** + * enum name for encoding_type_t + * + * @ingroup payloads + */ +extern enum_name_t *encoding_type_names; + +/** + * An encoding rule is a mapping of a specific encoding type to + * a location in the data struct where the current field is stored to + * or read from. + * + * For examples see files in this directory. + * + * This rules are used by parser and generator. + * + * @ingroup payloads + */ +struct encoding_rule_t { + + /** + * Encoding type. + */ + encoding_type_t type; + + /** + * Offset in the data struct. + * + * When parsing, data are written to this offset of the + * data struct. + * + * When generating, data are read from this offset in the + * data struct. + */ + u_int32_t offset; +}; + +#endif /*ENCODINGS_H_*/ diff --git a/src/charon/encoding/payloads/encryption_payload.c b/src/charon/encoding/payloads/encryption_payload.c new file mode 100644 index 000000000..23b6e8d9f --- /dev/null +++ b/src/charon/encoding/payloads/encryption_payload.c @@ -0,0 +1,646 @@ +/** + * @file encryption_payload.c + * + * @brief Implementation of encryption_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> +#include <string.h> + +#include "encryption_payload.h" + +#include <daemon.h> +#include <encoding/payloads/encodings.h> +#include <utils/linked_list.h> +#include <encoding/generator.h> +#include <encoding/parser.h> +#include <utils/iterator.h> +#include <utils/randomizer.h> +#include <crypto/signers/signer.h> + + +typedef struct private_encryption_payload_t private_encryption_payload_t; + +/** + * Private data of an encryption_payload_t' Object. + * + */ +struct private_encryption_payload_t { + + /** + * Public encryption_payload_t interface. + */ + encryption_payload_t public; + + /** + * There is no next payload for an encryption payload, + * since encryption payload MUST be the last one. + * next_payload means here the first payload of the + * contained, encrypted payload. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload + */ + u_int16_t payload_length; + + /** + * Chunk containing the iv, data, padding, + * and (an eventually not calculated) signature. + */ + chunk_t encrypted; + + /** + * Chunk containing the data in decrypted (unpadded) form. + */ + chunk_t decrypted; + + /** + * Signer set by set_signer. + */ + signer_t *signer; + + /** + * Crypter, supplied by encrypt/decrypt + */ + crypter_t *crypter; + + /** + * Contained payloads of this encrpytion_payload. + */ + linked_list_t *payloads; +}; + +/** + * Encoding rules to parse or generate a IKEv2-Encryption Payload. + * + * The defined offsets are the positions in a object of type + * private_encryption_payload_t. + * + */ +encoding_rule_t encryption_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_encryption_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_encryption_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole encryption payload*/ + { PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) }, + /* encrypted data, stored in a chunk. contains iv, data, padding */ + { ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Initialization Vector ! + ! (length is block size for encryption algorithm) ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Encrypted IKE Payloads ! + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! Padding (0-255 octets) ! + +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + ! ! Pad Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ Integrity Checksum Data ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_encryption_payload_t *this) +{ + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_encryption_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = encryption_payload_encodings; + *rule_count = sizeof(encryption_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_encryption_payload_t *this) +{ + return ENCRYPTED; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_encryption_payload_t *this) +{ + /* returns first contained payload here */ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_encryption_payload_t *this, payload_type_t type) +{ + /* set next type is not allowed, since this payload MUST be the last one + * and so nothing is done in here*/ +} + +/** + * (re-)compute the lenght of the whole payload + */ +static void compute_length(private_encryption_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_payload; + size_t block_size, length = 0; + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* count payload length */ + while (iterator->iterate(iterator, (void **) ¤t_payload)) + { + length += current_payload->get_length(current_payload); + } + iterator->destroy(iterator); + + if (this->crypter && this->signer) + { + /* append one byte for padding length */ + length++; + /* append padding */ + block_size = this->crypter->get_block_size(this->crypter); + length += block_size - length % block_size; + /* add iv */ + length += block_size; + /* add signature */ + length += this->signer->get_block_size(this->signer); + } + length += ENCRYPTION_PAYLOAD_HEADER_LENGTH; + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_encryption_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of payload_t.create_payload_iterator. + */ +static iterator_t *create_payload_iterator (private_encryption_payload_t *this, bool forward) +{ + return (this->payloads->create_iterator(this->payloads, forward)); +} + +/** + * Implementation of payload_t.add_payload. + */ +static void add_payload(private_encryption_payload_t *this, payload_t *payload) +{ + payload_t *last_payload; + if (this->payloads->get_count(this->payloads) > 0) + { + this->payloads->get_last(this->payloads,(void **) &last_payload); + last_payload->set_next_type(last_payload, payload->get_type(payload)); + } + else + { + this->next_payload = payload->get_type(payload); + } + payload->set_next_type(payload, NO_PAYLOAD); + this->payloads->insert_last(this->payloads, (void*)payload); + compute_length(this); +} + +/** + * Implementation of encryption_payload_t.remove_first_payload. + */ +static status_t remove_first_payload(private_encryption_payload_t *this, payload_t **payload) +{ + return this->payloads->remove_first(this->payloads, (void**)payload); +} + +/** + * Implementation of encryption_payload_t.get_payload_count. + */ +static size_t get_payload_count(private_encryption_payload_t *this) +{ + return this->payloads->get_count(this->payloads); +} + +/** + * Generate payload before encryption. + */ +static void generate(private_encryption_payload_t *this) +{ + payload_t *current_payload, *next_payload; + generator_t *generator; + iterator_t *iterator; + + /* recalculate length before generating */ + compute_length(this); + + /* create iterator */ + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* get first payload */ + if (iterator->iterate(iterator, (void**)¤t_payload)) + { + this->next_payload = current_payload->get_type(current_payload); + } + else + { + /* no paylads? */ + DBG2(DBG_ENC, "generating contained payloads, but none available"); + free(this->decrypted.ptr); + this->decrypted = chunk_empty; + iterator->destroy(iterator); + return; + } + + generator = generator_create(); + + /* build all payload, except last */ + while(iterator->iterate(iterator, (void**)&next_payload)) + { + current_payload->set_next_type(current_payload, next_payload->get_type(next_payload)); + generator->generate_payload(generator, current_payload); + current_payload = next_payload; + } + iterator->destroy(iterator); + + /* build last payload */ + current_payload->set_next_type(current_payload, NO_PAYLOAD); + generator->generate_payload(generator, current_payload); + + /* free already generated data */ + free(this->decrypted.ptr); + + generator->write_to_chunk(generator, &(this->decrypted)); + generator->destroy(generator); + DBG2(DBG_ENC, "successfully generated content in encryption payload"); +} + +/** + * Implementation of encryption_payload_t.encrypt. + */ +static status_t encrypt(private_encryption_payload_t *this) +{ + chunk_t iv, padding, to_crypt, result; + randomizer_t *randomizer; + status_t status; + size_t block_size; + + if (this->signer == NULL || this->crypter == NULL) + { + DBG1(DBG_ENC, "could not encrypt, signer/crypter not set"); + return INVALID_STATE; + } + + /* for random data in iv and padding */ + randomizer = randomizer_create(); + + /* build payload chunk */ + generate(this); + + DBG2(DBG_ENC, "encrypting payloads"); + DBG3(DBG_ENC, "data to encrypt %B", &this->decrypted); + + /* build padding */ + block_size = this->crypter->get_block_size(this->crypter); + padding.len = block_size - ((this->decrypted.len + 1) % block_size); + status = randomizer->allocate_pseudo_random_bytes(randomizer, padding.len, &padding); + if (status != SUCCESS) + { + randomizer->destroy(randomizer); + return status; + } + + /* concatenate payload data, padding, padding len */ + to_crypt.len = this->decrypted.len + padding.len + 1; + to_crypt.ptr = malloc(to_crypt.len); + + memcpy(to_crypt.ptr, this->decrypted.ptr, this->decrypted.len); + memcpy(to_crypt.ptr + this->decrypted.len, padding.ptr, padding.len); + *(to_crypt.ptr + to_crypt.len - 1) = padding.len; + + /* build iv */ + iv.len = block_size; + status = randomizer->allocate_pseudo_random_bytes(randomizer, iv.len, &iv); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + chunk_free(&to_crypt); + chunk_free(&padding); + return status; + } + + DBG3(DBG_ENC, "data before encryption with padding %B", &to_crypt); + + /* encrypt to_crypt chunk */ + free(this->encrypted.ptr); + status = this->crypter->encrypt(this->crypter, to_crypt, iv, &result); + free(padding.ptr); + free(to_crypt.ptr); + if (status != SUCCESS) + { + DBG2(DBG_ENC, "encryption failed"); + free(iv.ptr); + return status; + } + DBG3(DBG_ENC, "data after encryption %B", &result); + + /* build encrypted result with iv and signature */ + this->encrypted.len = iv.len + result.len + this->signer->get_block_size(this->signer); + free(this->encrypted.ptr); + this->encrypted.ptr = malloc(this->encrypted.len); + + /* fill in result, signature is left out */ + memcpy(this->encrypted.ptr, iv.ptr, iv.len); + memcpy(this->encrypted.ptr + iv.len, result.ptr, result.len); + + free(result.ptr); + free(iv.ptr); + DBG3(DBG_ENC, "data after encryption with IV and (invalid) signature %B", + &this->encrypted); + + return SUCCESS; +} + +/** + * Parse the payloads after decryption. + */ +static status_t parse(private_encryption_payload_t *this) +{ + parser_t *parser; + status_t status; + payload_type_t current_payload_type; + + /* build a parser on the decrypted data */ + parser = parser_create(this->decrypted); + + current_payload_type = this->next_payload; + /* parse all payloads */ + while (current_payload_type != NO_PAYLOAD) + { + payload_t *current_payload; + + status = parser->parse_payload(parser, current_payload_type, (payload_t**)¤t_payload); + if (status != SUCCESS) + { + parser->destroy(parser); + return PARSE_ERROR; + } + + status = current_payload->verify(current_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "%N verification failed", + payload_type_names, current_payload->get_type(current_payload)); + current_payload->destroy(current_payload); + parser->destroy(parser); + return VERIFY_ERROR; + } + + /* get next payload type */ + current_payload_type = current_payload->get_next_type(current_payload); + + this->payloads->insert_last(this->payloads,current_payload); + } + parser->destroy(parser); + DBG2(DBG_ENC, "succesfully parsed content of encryption payload"); + return SUCCESS; +} + +/** + * Implementation of encryption_payload_t.encrypt. + */ +static status_t decrypt(private_encryption_payload_t *this) +{ + chunk_t iv, concatenated; + u_int8_t padding_length; + status_t status; + + DBG2(DBG_ENC, "decrypting encryption payload"); + DBG3(DBG_ENC, "data before decryption with IV and (invalid) signature %B", + &this->encrypted); + + if (this->signer == NULL || this->crypter == NULL) + { + DBG1(DBG_ENC, "could not decrypt, no crypter/signer set"); + return INVALID_STATE; + } + + /* get IV */ + iv.len = this->crypter->get_block_size(this->crypter); + + iv.ptr = this->encrypted.ptr; + + /* point concatenated to data + padding + padding_length*/ + concatenated.ptr = this->encrypted.ptr + iv.len; + concatenated.len = this->encrypted.len - iv.len - this->signer->get_block_size(this->signer); + + /* check the size of input: + * concatenated must be at least on block_size of crypter + */ + if (concatenated.len < iv.len) + { + DBG1(DBG_ENC, "could not decrypt, invalid input"); + return FAILED; + } + + /* free previus data, if any */ + free(this->decrypted.ptr); + + DBG3(DBG_ENC, "data before decryption %B", &concatenated); + + status = this->crypter->decrypt(this->crypter, concatenated, iv, &(this->decrypted)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "could not decrypt, decryption failed"); + return FAILED; + } + DBG3(DBG_ENC, "data after decryption with padding %B", &this->decrypted); + + + /* get padding length, sits just bevore signature */ + padding_length = *(this->decrypted.ptr + this->decrypted.len - 1); + /* add one byte to the padding length, since the padding_length field is not included */ + padding_length++; + this->decrypted.len -= padding_length; + + /* check size again */ + if (padding_length > concatenated.len || this->decrypted.len < 0) + { + DBG1(DBG_ENC, "decryption failed, invalid padding length found. Invalid key?"); + /* decryption failed :-/ */ + return FAILED; + } + + /* free padding */ + this->decrypted.ptr = realloc(this->decrypted.ptr, this->decrypted.len); + DBG3(DBG_ENC, "data after decryption without padding %B", &this->decrypted); + DBG2(DBG_ENC, "decryption successful, trying to parse content"); + return parse(this); +} + +/** + * Implementation of encryption_payload_t.set_transforms. + */ +static void set_transforms(private_encryption_payload_t *this, crypter_t* crypter, signer_t* signer) +{ + this->signer = signer; + this->crypter = crypter; +} + +/** + * Implementation of encryption_payload_t.build_signature. + */ +static status_t build_signature(private_encryption_payload_t *this, chunk_t data) +{ + chunk_t data_without_sig = data; + chunk_t sig; + + if (this->signer == NULL) + { + DBG1(DBG_ENC, "unable to build signature, no signer set"); + return INVALID_STATE; + } + + sig.len = this->signer->get_block_size(this->signer); + data_without_sig.len -= sig.len; + sig.ptr = data.ptr + data_without_sig.len; + DBG2(DBG_ENC, "building signature"); + this->signer->get_signature(this->signer, data_without_sig, sig.ptr); + return SUCCESS; +} + +/** + * Implementation of encryption_payload_t.verify_signature. + */ +static status_t verify_signature(private_encryption_payload_t *this, chunk_t data) +{ + chunk_t sig, data_without_sig; + bool valid; + + if (this->signer == NULL) + { + DBG1(DBG_ENC, "unable to verify signature, no signer set"); + return INVALID_STATE; + } + /* find signature in data chunk */ + sig.len = this->signer->get_block_size(this->signer); + if (data.len <= sig.len) + { + DBG1(DBG_ENC, "unable to verify signature, invalid input"); + return FAILED; + } + sig.ptr = data.ptr + data.len - sig.len; + + /* verify it */ + data_without_sig.len = data.len - sig.len; + data_without_sig.ptr = data.ptr; + valid = this->signer->verify_signature(this->signer, data_without_sig, sig); + + if (!valid) + { + DBG1(DBG_ENC, "signature verification failed"); + return FAILED; + } + + DBG2(DBG_ENC, "signature verification successful"); + return SUCCESS; +} + +/** + * Implementation of payload_t.destroy. + */ +static void destroy(private_encryption_payload_t *this) +{ + this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + free(this->encrypted.ptr); + free(this->decrypted.ptr); + free(this); +} + +/* + * Described in header + */ +encryption_payload_t *encryption_payload_create() +{ + private_encryption_payload_t *this = malloc_thing(private_encryption_payload_t); + + /* payload_t interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_payload_iterator = (iterator_t * (*) (encryption_payload_t *,bool)) create_payload_iterator; + this->public.add_payload = (void (*) (encryption_payload_t *,payload_t *)) add_payload; + this->public.remove_first_payload = (status_t (*)(encryption_payload_t*, payload_t **)) remove_first_payload; + this->public.get_payload_count = (size_t (*)(encryption_payload_t*)) get_payload_count; + + this->public.encrypt = (status_t (*) (encryption_payload_t *)) encrypt; + this->public.decrypt = (status_t (*) (encryption_payload_t *)) decrypt; + this->public.set_transforms = (void (*) (encryption_payload_t*,crypter_t*,signer_t*)) set_transforms; + this->public.build_signature = (status_t (*) (encryption_payload_t*, chunk_t)) build_signature; + this->public.verify_signature = (status_t (*) (encryption_payload_t*, chunk_t)) verify_signature; + this->public.destroy = (void (*) (encryption_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = ENCRYPTION_PAYLOAD_HEADER_LENGTH; + this->encrypted = chunk_empty; + this->decrypted = chunk_empty; + this->signer = NULL; + this->crypter = NULL; + this->payloads = linked_list_create(); + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/encryption_payload.h b/src/charon/encoding/payloads/encryption_payload.h new file mode 100644 index 000000000..7cf53619f --- /dev/null +++ b/src/charon/encoding/payloads/encryption_payload.h @@ -0,0 +1,197 @@ +/** + * @file encryption_payload.h + * + * @brief Interface of encryption_payload_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef ENCRYPTION_PAYLOAD_H_ +#define ENCRYPTION_PAYLOAD_H_ + +typedef struct encryption_payload_t encryption_payload_t; + +#include <library.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <encoding/payloads/payload.h> +#include <utils/linked_list.h> + +/** + * Encrpytion payload length in bytes without IV and following data. + * + * @ingroup payloads + */ +#define ENCRYPTION_PAYLOAD_HEADER_LENGTH 4 + + +/** + * @brief The encryption payload as described in RFC section 3.14. + * + * Before any crypt/decrypt/sign/verify operation can occur, + * the transforms must be set. After that, a parsed encryption payload + * can be decrypted, which also will parse the contained payloads. + * Encryption is done the same way, added payloads will get generated + * and then encrypted. + * For signature building, there is the FULL packet needed. Meaning it + * must be builded after generation of all payloads and the encryption + * of the encryption payload. + * Signature verificatin is done before decryption. + * + * @b Constructors: + * - encryption_payload_create() + * + * @ingroup payloads + */ +struct encryption_payload_t { + /** + * Implements payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator for all contained payloads. + * + * @warning iterator_t object has to get destroyed by the caller. + * + * @param this calling encryption_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * return created iterator_t object + */ + iterator_t *(*create_payload_iterator) (encryption_payload_t *this, bool forward); + + /** + * @brief Adds a payload to this encryption payload. + * + * @param this calling encryption_payload_t object + * @param payload payload_t object to add + */ + void (*add_payload) (encryption_payload_t *this, payload_t *payload); + + /** + * @brief Reove the last payload in the contained payload list. + * + * @param this calling encryption_payload_t object + * @param[out] payload removed payload + * @return + * - SUCCESS, or + * - NOT_FOUND if list empty + */ + status_t (*remove_first_payload) (encryption_payload_t *this, payload_t **payload); + + /** + * @brief Get the number of payloads. + * + * @param this calling encryption_payload_t object + * @return number of contained payloads + */ + size_t (*get_payload_count) (encryption_payload_t *this); + + /** + * @brief Set transforms to use. + * + * To decryption, encryption, signature building and verifying, + * the payload needs a crypter and a signer object. + * + * @warning Do NOT call this function again after encryption, since + * the signer must be the same while encrypting and signature building! + * + * @param this calling encryption_payload_t + * @param crypter crypter_t to use for data de-/encryption + * @param signer signer_t to use for data signing/verifying + */ + void (*set_transforms) (encryption_payload_t *this, crypter_t *crypter, signer_t *signer); + + /** + * @brief Generate and encrypt contained payloads. + * + * This function generates the content for added payloads + * and encrypts them. Signature is not built, since we need + * additional data (the full message). + * + * @param this calling encryption_payload_t + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set + */ + status_t (*encrypt) (encryption_payload_t *this); + + /** + * @brief Decrypt and parse contained payloads. + * + * This function decrypts the contained data. After, + * the payloads are parsed internally and are accessible + * via the iterator. + * + * @param this calling encryption_payload_t + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set, or + * - FAILED if data is invalid + */ + status_t (*decrypt) (encryption_payload_t *this); + + /** + * @brief Build the signature. + * + * The signature is built over the FULL message, so the header + * and every payload (inclusive this one) must already be generated. + * The generated message is supplied via the data paramater. + * + * @param this calling encryption_payload_t + * @param data chunk contains the already generated message + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set + */ + status_t (*build_signature) (encryption_payload_t *this, chunk_t data); + + /** + * @brief Verify the signature. + * + * Since the signature is built over the full message, we need + * this data to do the verification. The message data + * is supplied via the data argument. + * + * @param this calling encryption_payload_t + * @param data chunk contains the message + * @return + * - SUCCESS, or + * - FAILED if signature invalid, or + * - INVALID_STATE if transforms not set + */ + status_t (*verify_signature) (encryption_payload_t *this, chunk_t data); + + /** + * @brief Destroys an encryption_payload_t object. + * + * @param this encryption_payload_t object to destroy + */ + void (*destroy) (encryption_payload_t *this); +}; + +/** + * @brief Creates an empty encryption_payload_t object. + * + * @return encryption_payload_t object + * + * @ingroup payloads + */ +encryption_payload_t *encryption_payload_create(void); + + +#endif /*ENCRYPTION_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/id_payload.c b/src/charon/encoding/payloads/id_payload.c new file mode 100644 index 000000000..74c0ce870 --- /dev/null +++ b/src/charon/encoding/payloads/id_payload.c @@ -0,0 +1,323 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "id_payload.h" + +#include <daemon.h> +#include <encoding/payloads/encodings.h> + +typedef struct private_id_payload_t private_id_payload_t; + +/** + * Private data of an id_payload_t object. + * + */ +struct private_id_payload_t { + /** + * Public id_payload_t interface. + */ + id_payload_t public; + + /** + * TRUE if this ID payload is of type IDi, FALSE for IDr. + */ + bool is_initiator; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Type of the ID Data. + */ + u_int8_t id_type; + + /** + * The contained id data value. + */ + chunk_t id_data; +}; + +/** + * Encoding rules to parse or generate a ID payload + * + * The defined offsets are the positions in a object of type + * private_id_payload_t. + * + */ +encoding_rule_t id_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_id_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_id_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_id_payload_t, payload_length) }, + /* 1 Byte ID type*/ + { U_INT_8, offsetof(private_id_payload_t, id_type) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some id data bytes, length is defined in PAYLOAD_LENGTH */ + { ID_DATA, offsetof(private_id_payload_t, id_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ID Type ! RESERVED | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Identification Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_id_payload_t *this) +{ + if ((this->id_type == 0) || + (this->id_type == 4) || + ((this->id_type >= 6) && (this->id_type <= 8)) || + ((this->id_type >= 12) && (this->id_type <= 200))) + { + /* reserved IDs */ + DBG1(DBG_ENC, "received ID with reserved type %d", this->id_type); + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of id_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_id_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = id_payload_encodings; + *rule_count = sizeof(id_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_id_payload_t *this) +{ + if (this->is_initiator) + { + return ID_INITIATOR; + } + else + { + return ID_RESPONDER; + } +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_id_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_id_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_id_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of id_payload_t.set_type. + */ +static void set_id_type (private_id_payload_t *this, id_type_t type) +{ + this->id_type = type; +} + +/** + * Implementation of id_payload_t.get_id_type. + */ +static id_type_t get_id_type (private_id_payload_t *this) +{ + return (this->id_type); +} + +/** + * Implementation of id_payload_t.set_data. + */ +static void set_data (private_id_payload_t *this, chunk_t data) +{ + if (this->id_data.ptr != NULL) + { + chunk_free(&(this->id_data)); + } + this->id_data.ptr = clalloc(data.ptr,data.len); + this->id_data.len = data.len; + this->payload_length = ID_PAYLOAD_HEADER_LENGTH + this->id_data.len; +} + + +/** + * Implementation of id_payload_t.get_data_clone. + */ +static chunk_t get_data (private_id_payload_t *this) +{ + return (this->id_data); +} + +/** + * Implementation of id_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_id_payload_t *this) +{ + chunk_t cloned_data; + if (this->id_data.ptr == NULL) + { + return (this->id_data); + } + cloned_data.ptr = clalloc(this->id_data.ptr,this->id_data.len); + cloned_data.len = this->id_data.len; + return cloned_data; +} + +/** + * Implementation of id_payload_t.get_initiator. + */ +static bool get_initiator (private_id_payload_t *this) +{ + return (this->is_initiator); +} + +/** + * Implementation of id_payload_t.set_initiator. + */ +static void set_initiator (private_id_payload_t *this,bool is_initiator) +{ + this->is_initiator = is_initiator; +} + +/** + * Implementation of id_payload_t.get_identification. + */ +static identification_t *get_identification (private_id_payload_t *this) +{ + return identification_create_from_encoding(this->id_type,this->id_data); +} + +/** + * Implementation of payload_t.destroy and id_payload_t.destroy. + */ +static void destroy(private_id_payload_t *this) +{ + if (this->id_data.ptr != NULL) + { + chunk_free(&(this->id_data)); + } + free(this); +} + +/* + * Described in header. + */ +id_payload_t *id_payload_create(bool is_initiator) +{ + private_id_payload_t *this = malloc_thing(private_id_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (id_payload_t *)) destroy; + this->public.set_id_type = (void (*) (id_payload_t *,id_type_t)) set_id_type; + this->public.get_id_type = (id_type_t (*) (id_payload_t *)) get_id_type; + this->public.set_data = (void (*) (id_payload_t *,chunk_t)) set_data; + this->public.get_data = (chunk_t (*) (id_payload_t *)) get_data; + this->public.get_data_clone = (chunk_t (*) (id_payload_t *)) get_data_clone; + + this->public.get_initiator = (bool (*) (id_payload_t *)) get_initiator; + this->public.set_initiator = (void (*) (id_payload_t *,bool)) set_initiator; + this->public.get_identification = (identification_t * (*) (id_payload_t *this)) get_identification; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =ID_PAYLOAD_HEADER_LENGTH; + this->id_data = chunk_empty; + this->is_initiator = is_initiator; + + return (&(this->public)); +} + +/* + * Described in header. + */ +id_payload_t *id_payload_create_from_identification(bool is_initiator,identification_t *identification) +{ + id_payload_t *this= id_payload_create(is_initiator); + this->set_data(this,identification->get_encoding(identification)); + this->set_id_type(this,identification->get_type(identification)); + return this; +} diff --git a/src/charon/encoding/payloads/id_payload.h b/src/charon/encoding/payloads/id_payload.h new file mode 100644 index 000000000..b67d85d2e --- /dev/null +++ b/src/charon/encoding/payloads/id_payload.h @@ -0,0 +1,172 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef ID_PAYLOAD_H_ +#define ID_PAYLOAD_H_ + +typedef struct id_payload_t id_payload_t; + +#include <library.h> +#include <utils/identification.h> +#include <encoding/payloads/payload.h> + +/** + * Length of a id payload without the data in bytes. + * + * @ingroup payloads + */ +#define ID_PAYLOAD_HEADER_LENGTH 8 + +/** + * Object representing an IKEv2 ID payload. + * + * The ID payload format is described in RFC section 3.5. + * + * @b Constructors: + * - id_payload_create_from_identification() + * - id_payload_create() + * + * @ingroup payloads + */ +struct id_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the ID type. + * + * @param this calling id_payload_t object + * @param type Type of ID + */ + void (*set_id_type) (id_payload_t *this, id_type_t type); + + /** + * @brief Get the ID type. + * + * @param this calling id_payload_t object + * @return type of the ID + */ + id_type_t (*get_id_type) (id_payload_t *this); + + /** + * @brief Set the ID data. + * + * Data are getting cloned. + * + * @param this calling id_payload_t object + * @param data ID data as chunk_t + */ + void (*set_data) (id_payload_t *this, chunk_t data); + + /** + * @brief Get the ID data. + * + * Returned data are a copy of the internal one + * + * @param this calling id_payload_t object + * @return ID data as chunk_t + */ + chunk_t (*get_data_clone) (id_payload_t *this); + + /** + * @brief Get the ID data. + * + * Returned data are NOT copied. + * + * @param this calling id_payload_t object + * @return ID data as chunk_t + */ + chunk_t (*get_data) (id_payload_t *this); + + /** + * @brief Creates an identification object of this id payload. + * + * Returned object has to get destroyed by the caller. + * + * @param this calling id_payload_t object + * @return identification_t object + */ + identification_t *(*get_identification) (id_payload_t *this); + + /** + * @brief Get the type of ID payload (IDi or IDr). + * + * @param this calling id_payload_t object + * @return + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + */ + bool (*get_initiator) (id_payload_t *this); + + /** + * @brief Set the type of ID payload (IDi or IDr). + * + * @param this calling id_payload_t object + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + */ + void (*set_initiator) (id_payload_t *this,bool is_initiator); + + /** + * @brief Destroys an id_payload_t object. + * + * @param this id_payload_t object to destroy + */ + void (*destroy) (id_payload_t *this); +}; + +/** + * @brief Creates an empty id_payload_t object. + * + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + * @return id_payload_t object + * + * @ingroup payloads + */ +id_payload_t *id_payload_create(bool is_initiator); + +/** + * @brief Creates an id_payload_t from an existing identification_t object. + * + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * @param identification identification_t object + * @return id_payload_t object + * + * @ingroup payloads + */ +id_payload_t *id_payload_create_from_identification(bool is_initiator,identification_t *identification); + + + +#endif /* ID_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/ike_header.c b/src/charon/encoding/payloads/ike_header.c new file mode 100644 index 000000000..b1b4fbf87 --- /dev/null +++ b/src/charon/encoding/payloads/ike_header.c @@ -0,0 +1,406 @@ +/** + * @file ike_header.c + * + * @brief Implementation of ike_header_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +/* offsetof macro */ +#include <stddef.h> + +#include "ike_header.h" + +#include <encoding/payloads/encodings.h> + + +typedef struct private_ike_header_t private_ike_header_t; + +/** + * Private data of an ike_header_t object. + * + */ +struct private_ike_header_t { + /** + * Public interface. + */ + ike_header_t public; + + /** + * SPI of the initiator. + */ + u_int64_t initiator_spi; + + /** + * SPI of the responder. + */ + u_int64_t responder_spi; + + /** + * Next payload type. + */ + u_int8_t next_payload; + /** + * IKE major version. + */ + u_int8_t maj_version; + + /** + * IKE minor version. + */ + u_int8_t min_version; + + /** + * Exchange type . + */ + u_int8_t exchange_type; + + /** + * Flags of the Message. + * + */ + struct { + /** + * Sender is initiator of the associated IKE_SA_INIT-Exchange. + */ + bool initiator; + + /** + * Is protocol supporting higher version? + */ + bool version; + + /** + * TRUE, if this is a response, FALSE if its a Request. + */ + bool response; + } flags; + + /** + * Associated Message-ID. + */ + u_int32_t message_id; + + /** + * Length of the whole IKEv2-Message (header and all payloads). + */ + u_int32_t length; +}; + +ENUM_BEGIN(exchange_type_names, EXCHANGE_TYPE_UNDEFINED, EXCHANGE_TYPE_UNDEFINED, + "EXCHANGE_TYPE_UNDEFINED"); +ENUM_NEXT(exchange_type_names, IKE_SA_INIT, INFORMATIONAL, EXCHANGE_TYPE_UNDEFINED, + "IKE_SA_INIT", + "IKE_AUTH", + "CREATE_CHILD_SA", + "INFORMATIONAL"); +ENUM_END(exchange_type_names, INFORMATIONAL); + +/** + * Encoding rules to parse or generate a IKEv2-Header. + * + * The defined offsets are the positions in a object of type + * ike_header_t. + * + */ +encoding_rule_t ike_header_encodings[] = { + /* 8 Byte SPI, stored in the field initiator_spi */ + { IKE_SPI, offsetof(private_ike_header_t, initiator_spi) }, + /* 8 Byte SPI, stored in the field responder_spi */ + { IKE_SPI, offsetof(private_ike_header_t, responder_spi) }, + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ike_header_t, next_payload) }, + /* 4 Bit major version, stored in the field maj_version */ + { U_INT_4, offsetof(private_ike_header_t, maj_version) }, + /* 4 Bit minor version, stored in the field min_version */ + { U_INT_4, offsetof(private_ike_header_t, min_version) }, + /* 8 Bit for the exchange type */ + { U_INT_8, offsetof(private_ike_header_t, exchange_type) }, + /* 2 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* 3 Bit flags, stored in the fields response, version and initiator */ + { FLAG, offsetof(private_ike_header_t, flags.response) }, + { FLAG, offsetof(private_ike_header_t, flags.version) }, + { FLAG, offsetof(private_ike_header_t, flags.initiator) }, + /* 3 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* 4 Byte message id, stored in the field message_id */ + { U_INT_32, offsetof(private_ike_header_t, message_id) }, + /* 4 Byte length fied, stored in the field length */ + { HEADER_LENGTH, offsetof(private_ike_header_t, length) } +}; + + +/* 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! IKE_SA Initiator's SPI ! + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! IKE_SA Responder's SPI ! + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Message ID ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ike_header_t *this) +{ + if ((this->exchange_type < IKE_SA_INIT) || (this->exchange_type > INFORMATIONAL)) + { + /* unsupported exchange type */ + return FAILED; + } + if (this->initiator_spi == 0) + { + /* initiator spi not set */ + return FAILED; + } + + /* verification of version is not done in here */ + + return SUCCESS; +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(payload_t *this,payload_type_t type) +{ + ((private_ike_header_t *)this)->next_payload = type; +} +/** + * Implementation of ike_header_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi(private_ike_header_t *this) +{ + return this->initiator_spi; +} + +/** + * Implementation of ike_header_t.set_initiator_spi. + */ +static void set_initiator_spi(private_ike_header_t *this, u_int64_t initiator_spi) +{ + this->initiator_spi = initiator_spi; +} + +/** + * Implementation of ike_header_t.get_responder_spi. + */ +static u_int64_t get_responder_spi(private_ike_header_t *this) +{ + return this->responder_spi; +} + +/** + * Implementation of ike_header_t.set_responder_spi. + */ +static void set_responder_spi(private_ike_header_t *this, u_int64_t responder_spi) +{ + this->responder_spi = responder_spi; +} + +/** + * Implementation of ike_header_t.get_maj_version. + */ +static u_int8_t get_maj_version(private_ike_header_t *this) +{ + return this->maj_version; +} + +/** + * Implementation of ike_header_t.get_min_version. + */ +static u_int8_t get_min_version(private_ike_header_t *this) +{ + return this->min_version; +} + +/** + * Implementation of ike_header_t.get_response_flag. + */ +static bool get_response_flag(private_ike_header_t *this) +{ + return this->flags.response; +} + +/** + * Implementation of ike_header_t.set_response_flag. + */ +static void set_response_flag(private_ike_header_t *this, bool response) +{ + this->flags.response = response; +} + +/** + * Implementation of ike_header_t.get_version_flag. + */ +static bool get_version_flag(private_ike_header_t *this) +{ + return this->flags.version; +} + +/** + * Implementation of ike_header_t.get_initiator_flag. + */ +static bool get_initiator_flag(private_ike_header_t *this) +{ + return this->flags.initiator; +} + +/** + * Implementation of ike_header_t.set_initiator_flag. + */ +static void set_initiator_flag(private_ike_header_t *this, bool initiator) +{ + this->flags.initiator = initiator; +} + +/** + * Implementation of ike_header_t.get_exchange_type. + */ +static u_int8_t get_exchange_type(private_ike_header_t *this) +{ + return this->exchange_type; +} + +/** + * Implementation of ike_header_t.set_exchange_type. + */ +static void set_exchange_type(private_ike_header_t *this, u_int8_t exchange_type) +{ + this->exchange_type = exchange_type; +} + +/** + * Implements ike_header_t's get_message_id function. + * See #ike_header_t.get_message_id for description. + */ +static u_int32_t get_message_id(private_ike_header_t *this) +{ + return this->message_id; +} + +/** + * Implementation of ike_header_t.set_message_id. + */ +static void set_message_id(private_ike_header_t *this, u_int32_t message_id) +{ + this->message_id = message_id; +} + +/** + * Implementation of ike_header_t.destroy and payload_t.destroy. + */ +static void destroy(ike_header_t *this) +{ + free(this); +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ike_header_encodings; + *rule_count = sizeof(ike_header_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(payload_t *this) +{ + return HEADER; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(payload_t *this) +{ + return (((private_ike_header_t*)this)->next_payload); +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(payload_t *this) +{ + return (((private_ike_header_t*)this)->length); +} + +/* + * Described in header. + */ +ike_header_t *ike_header_create() +{ + private_ike_header_t *this = malloc_thing(private_ike_header_t); + + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = get_encoding_rules; + this->public.payload_interface.get_length = get_length; + this->public.payload_interface.get_next_type = get_next_type; + this->public.payload_interface.set_next_type = set_next_type; + this->public.payload_interface.get_type = get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + this->public.destroy = destroy; + + this->public.get_initiator_spi = (u_int64_t (*) (ike_header_t*))get_initiator_spi; + this->public.set_initiator_spi = (void (*) (ike_header_t*,u_int64_t))set_initiator_spi; + this->public.get_responder_spi = (u_int64_t (*) (ike_header_t*))get_responder_spi; + this->public.set_responder_spi = (void (*) (ike_header_t *,u_int64_t))set_responder_spi; + this->public.get_maj_version = (u_int8_t (*) (ike_header_t*))get_maj_version; + this->public.get_min_version = (u_int8_t (*) (ike_header_t*))get_min_version; + this->public.get_response_flag = (bool (*) (ike_header_t*))get_response_flag; + this->public.set_response_flag = (void (*) (ike_header_t*,bool))set_response_flag; + this->public.get_version_flag = (bool (*) (ike_header_t*))get_version_flag; + this->public.get_initiator_flag = (bool (*) (ike_header_t*))get_initiator_flag; + this->public.set_initiator_flag = (void (*) (ike_header_t*,bool))set_initiator_flag; + this->public.get_exchange_type = (u_int8_t (*) (ike_header_t*))get_exchange_type; + this->public.set_exchange_type = (void (*) (ike_header_t*,u_int8_t))set_exchange_type; + this->public.get_message_id = (u_int32_t (*) (ike_header_t*))get_message_id; + this->public.set_message_id = (void (*) (ike_header_t*,u_int32_t))set_message_id; + + /* set default values of the fields */ + this->initiator_spi = 0; + this->responder_spi = 0; + this->next_payload = 0; + this->maj_version = IKE_MAJOR_VERSION; + this->min_version = IKE_MINOR_VERSION; + this->exchange_type = EXCHANGE_TYPE_UNDEFINED; + this->flags.initiator = TRUE; + this->flags.version = HIGHER_VERSION_SUPPORTED_FLAG; + this->flags.response = FALSE; + this->message_id = 0; + this->length = IKE_HEADER_LENGTH; + + return (ike_header_t*)this; +} diff --git a/src/charon/encoding/payloads/ike_header.h b/src/charon/encoding/payloads/ike_header.h new file mode 100644 index 000000000..95c20f810 --- /dev/null +++ b/src/charon/encoding/payloads/ike_header.h @@ -0,0 +1,260 @@ +/** + * @file ike_header.h + * + * @brief Interface of ike_header_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef IKE_HEADER_H_ +#define IKE_HEADER_H_ + +typedef enum exchange_type_t exchange_type_t; +typedef struct ike_header_t ike_header_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + +/** + * Major Version of IKEv2. + * + * @ingroup payloads + */ +#define IKE_MAJOR_VERSION 2 + +/** + * Minor Version of IKEv2. + * + * @ingroup payloads + */ +#define IKE_MINOR_VERSION 0 + +/** + * Flag in IKEv2-Header. Always 0. + * + * @ingroup payloads + */ +#define HIGHER_VERSION_SUPPORTED_FLAG 0 + +/** + * Length of IKE Header in Bytes. + * + * @ingroup payloads + */ +#define IKE_HEADER_LENGTH 28 + +/** + * @brief Different types of IKE-Exchanges. + * + * See Draft for different types. + * + * @ingroup payloads + */ +enum exchange_type_t{ + + /** + * EXCHANGE_TYPE_UNDEFINED. In private space, since not a official message type. + */ + EXCHANGE_TYPE_UNDEFINED = 240, + + /** + * IKE_SA_INIT. + */ + IKE_SA_INIT = 34, + + /** + * IKE_AUTH. + */ + IKE_AUTH = 35, + + /** + * CREATE_CHILD_SA. + */ + CREATE_CHILD_SA = 36, + + /** + * INFORMATIONAL. + */ + INFORMATIONAL = 37 +}; + +/** + * enum name for exchange_type_t + * + * @ingroup payloads + */ +extern enum_name_t *exchange_type_names; + +/** + * @brief An object of this type represents an IKEv2 header and is used to + * generate and parse IKEv2 headers. + * + * The header format of an IKEv2-Message is compatible to the + * ISAKMP-Header format to allow implementations supporting + * both versions of the IKE-protocol. + * + * @b Constructors: + * - ike_header_create() + * + * @ingroup payloads + */ +struct ike_header_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the initiator spi. + * + * @param this ike_header_t object + * @return initiator_spi + */ + u_int64_t (*get_initiator_spi) (ike_header_t *this); + + /** + * @brief Set the initiator spi. + * + * @param this ike_header_t object + * @param initiator_spi initiator_spi + */ + void (*set_initiator_spi) (ike_header_t *this, u_int64_t initiator_spi); + + /** + * @brief Get the responder spi. + * + * @param this ike_header_t object + * @return responder_spi + */ + u_int64_t (*get_responder_spi) (ike_header_t *this); + + /** + * @brief Set the responder spi. + * + * @param this ike_header_t object + * @param responder_spi responder_spi + */ + void (*set_responder_spi) (ike_header_t *this, u_int64_t responder_spi); + + /** + * @brief Get the major version. + * + * @param this ike_header_t object + * @return major version + */ + u_int8_t (*get_maj_version) (ike_header_t *this); + + /** + * @brief Get the minor version. + * + * @param this ike_header_t object + * @return minor version + */ + u_int8_t (*get_min_version) (ike_header_t *this); + + /** + * @brief Get the response flag. + * + * @param this ike_header_t object + * @return response flag + */ + bool (*get_response_flag) (ike_header_t *this); + + /** + * @brief Set the response flag- + * + * @param this ike_header_t object + * @param response response flag + * + */ + void (*set_response_flag) (ike_header_t *this, bool response); + /** + * @brief Get "higher version supported"-flag. + * + * @param this ike_header_t object + * @return version flag + */ + bool (*get_version_flag) (ike_header_t *this); + + /** + * @brief Get the initiator flag. + * + * @param this ike_header_t object + * @return initiator flag + */ + bool (*get_initiator_flag) (ike_header_t *this); + + /** + * @brief Set the initiator flag. + * + * @param this ike_header_t object + * @param initiator initiator flag + * + */ + void (*set_initiator_flag) (ike_header_t *this, bool initiator); + + /** + * @brief Get the exchange type. + * + * @param this ike_header_t object + * @return exchange type + */ + u_int8_t (*get_exchange_type) (ike_header_t *this); + + /** + * @brief Set the exchange type. + * + * @param this ike_header_t object + * @param exchange_type exchange type + */ + void (*set_exchange_type) (ike_header_t *this, u_int8_t exchange_type); + + /** + * @brief Get the message id. + * + * @param this ike_header_t object + * @return message id + */ + u_int32_t (*get_message_id) (ike_header_t *this); + + /** + * @brief Set the message id. + * + * @param this ike_header_t object + * @param initiator_spi message id + */ + void (*set_message_id) (ike_header_t *this, u_int32_t message_id); + + /** + * @brief Destroys a ike_header_t object. + * + * @param this ike_header_t object to destroy + */ + void (*destroy) (ike_header_t *this); +}; + +/** + * @brief Create an ike_header_t object + * + * @return ike_header_t object + * + * @ingroup payloads + */ +ike_header_t *ike_header_create(void); + +#endif /*IKE_HEADER_H_*/ diff --git a/src/charon/encoding/payloads/ke_payload.c b/src/charon/encoding/payloads/ke_payload.c new file mode 100644 index 000000000..8926b15f9 --- /dev/null +++ b/src/charon/encoding/payloads/ke_payload.c @@ -0,0 +1,277 @@ +/** + * @file ke_payload.c + * + * @brief Implementation of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "ke_payload.h" + +#include <encoding/payloads/encodings.h> + + +typedef struct private_ke_payload_t private_ke_payload_t; + +/** + * Private data of an ke_payload_t object. + * + */ +struct private_ke_payload_t { + /** + * Public ke_payload_t interface. + */ + ke_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * DH Group Number. + */ + u_int16_t dh_group_number; + + /** + * Key Exchange Data of this KE payload. + */ + chunk_t key_exchange_data; +}; + +/** + * Encoding rules to parse or generate a IKEv2-KE Payload. + * + * The defined offsets are the positions in a object of type + * private_ke_payload_t. + * + */ +encoding_rule_t ke_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ke_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_ke_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_ke_payload_t, payload_length) }, + /* DH Group number as 16 bit field*/ + { U_INT_16, offsetof(private_ke_payload_t, dh_group_number) }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* Key Exchange Data is from variable size */ + { KEY_EXCHANGE_DATA, offsetof(private_ke_payload_t, key_exchange_data)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! DH Group # ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Key Exchange Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ke_payload_t *this) +{ + /* dh group is not verified in here */ + return SUCCESS; +} + +/** + * Implementation of payload_t.destroy. + */ +static void destroy(private_ke_payload_t *this) +{ + if (this->key_exchange_data.ptr != NULL) + { + free(this->key_exchange_data.ptr); + } + free(this); +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_ke_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ke_payload_encodings; + *rule_count = sizeof(ke_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_ke_payload_t *this) +{ + return KEY_EXCHANGE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_ke_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_ke_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_ke_payload_t *this) +{ + size_t length = KE_PAYLOAD_HEADER_LENGTH; + if (this->key_exchange_data.ptr != NULL) + { + length += this->key_exchange_data.len; + } + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ke_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of ke_payload_t.get_key_exchange_data. + */ +static chunk_t get_key_exchange_data(private_ke_payload_t *this) +{ + return (this->key_exchange_data); +} + +/** + * Implementation of ke_payload_t.set_key_exchange_data. + */ +static void set_key_exchange_data(private_ke_payload_t *this, chunk_t key_exchange_data) +{ + /* destroy existing data first */ + if (this->key_exchange_data.ptr != NULL) + { + /* free existing value */ + free(this->key_exchange_data.ptr); + this->key_exchange_data.ptr = NULL; + this->key_exchange_data.len = 0; + + } + + this->key_exchange_data = chunk_clone(key_exchange_data); + compute_length(this); +} + +/** + * Implementation of ke_payload_t.get_dh_group_number. + */ +static diffie_hellman_group_t get_dh_group_number(private_ke_payload_t *this) +{ + return this->dh_group_number; +} + +/** + * Implementation of ke_payload_t.set_dh_group_number. + */ +static void set_dh_group_number(private_ke_payload_t *this, diffie_hellman_group_t dh_group_number) +{ + this->dh_group_number = dh_group_number; +} + +/* + * Described in header + */ +ke_payload_t *ke_payload_create() +{ + private_ke_payload_t *this = malloc_thing(private_ke_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_key_exchange_data = (chunk_t (*) (ke_payload_t *)) get_key_exchange_data; + this->public.set_key_exchange_data = (void (*) (ke_payload_t *,chunk_t)) set_key_exchange_data; + this->public.get_dh_group_number = (diffie_hellman_group_t (*) (ke_payload_t *)) get_dh_group_number; + this->public.set_dh_group_number =(void (*) (ke_payload_t *,diffie_hellman_group_t)) set_dh_group_number; + this->public.destroy = (void (*) (ke_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = KE_PAYLOAD_HEADER_LENGTH; + this->key_exchange_data = chunk_empty; + this->dh_group_number = MODP_NONE; + + return &this->public; +} + +/* + * Described in header + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *dh) +{ + private_ke_payload_t *this = (private_ke_payload_t*)ke_payload_create(); + + dh->get_my_public_value(dh, &this->key_exchange_data); + this->dh_group_number = dh->get_dh_group(dh); + compute_length(this); + + return &this->public; +} diff --git a/src/charon/encoding/payloads/ke_payload.h b/src/charon/encoding/payloads/ke_payload.h new file mode 100644 index 000000000..52be8ffe3 --- /dev/null +++ b/src/charon/encoding/payloads/ke_payload.h @@ -0,0 +1,121 @@ +/** + * @file ke_payload.h + * + * @brief Interface of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef KE_PAYLOAD_H_ +#define KE_PAYLOAD_H_ + +typedef struct ke_payload_t ke_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/transform_substructure.h> +#include <utils/linked_list.h> +#include <crypto/diffie_hellman.h> + +/** + * KE payload length in bytes without any key exchange data. + * + * @ingroup payloads + */ +#define KE_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2-KE Payload. + * + * The KE Payload format is described in RFC section 3.4. + * + * @b Constructors: + * - ke_payload_create() + * + * @ingroup payloads + */ +struct ke_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set key exchange data of this KE payload. + * + * @warning Returned data are not copied. + * + * @param this calling ke_payload_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_key_exchange_data) (ke_payload_t *this); + + /** + * @brief Sets the key exchange data of this KE payload. + * + * @warning Value is getting copied. + * + * @param this calling ke_payload_t object + * @param key_exchange_data chunk_t pointing to the value to set + */ + void (*set_key_exchange_data) (ke_payload_t *this, chunk_t key_exchange_data); + + /** + * @brief Gets the Diffie-Hellman Group Number of this KE payload. + * + * @param this calling ke_payload_t object + * @return DH Group Number of this payload + */ + diffie_hellman_group_t (*get_dh_group_number) (ke_payload_t *this); + + /** + * @brief Sets the Diffie-Hellman Group Number of this KE payload. + * + * @param this calling ke_payload_t object + * @param dh_group_number DH Group to set + */ + void (*set_dh_group_number) (ke_payload_t *this, diffie_hellman_group_t dh_group_number); + + /** + * @brief Destroys an ke_payload_t object. + * + * @param this ke_payload_t object to destroy + */ + void (*destroy) (ke_payload_t *this); +}; + +/** + * @brief Creates an empty ke_payload_t object + * + * @return ke_payload_t object + * + * @ingroup payloads + */ +ke_payload_t *ke_payload_create(void); + +/** + * @brief Creates a ke_payload_t from a diffie_hellman_t + * + * @param diffie_hellman diffie hellman object containing group and key + * @return ke_payload_t object + * + * @ingroup payloads + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *diffie_hellman); + +#endif /* KE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/nonce_payload.c b/src/charon/encoding/payloads/nonce_payload.c new file mode 100644 index 000000000..8e1fc505e --- /dev/null +++ b/src/charon/encoding/payloads/nonce_payload.c @@ -0,0 +1,232 @@ +/** + * @file nonce_payload.h + * + * @brief Implementation of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +/* offsetof macro */ +#include <stddef.h> + +#include "nonce_payload.h" + +#include <encoding/payloads/encodings.h> + + +typedef struct private_nonce_payload_t private_nonce_payload_t; + +/** + * Private data of an nonce_payload_t object. + * + */ +struct private_nonce_payload_t { + /** + * Public nonce_payload_t interface. + */ + nonce_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained nonce value. + */ + chunk_t nonce; +}; + +/** + * Encoding rules to parse or generate a nonce payload + * + * The defined offsets are the positions in a object of type + * private_nonce_payload_t. + * + */ +encoding_rule_t nonce_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_nonce_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_nonce_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole nonce payload*/ + { PAYLOAD_LENGTH, offsetof(private_nonce_payload_t, payload_length) }, + /* some nonce bytes, lenth is defined in PAYLOAD_LENGTH */ + { NONCE_DATA, offsetof(private_nonce_payload_t, nonce) } +}; + +/* 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Nonce Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_nonce_payload_t *this) +{ + if ((this->nonce.len < 16) || ((this->nonce.len > 256))) + { + /* nonce length is wrong */ + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of nonce_payload_t.set_nonce. + */ +static status_t set_nonce(private_nonce_payload_t *this, chunk_t nonce) +{ + this->nonce.ptr = clalloc(nonce.ptr, nonce.len); + this->nonce.len = nonce.len; + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH + nonce.len; + return SUCCESS; +} + +/** + * Implementation of nonce_payload_t.get_nonce. + */ +static chunk_t get_nonce(private_nonce_payload_t *this) +{ + chunk_t nonce; + nonce.ptr = clalloc(this->nonce.ptr,this->nonce.len); + nonce.len = this->nonce.len; + return nonce; +} + +/** + * Implementation of nonce_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_nonce_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = nonce_payload_encodings; + *rule_count = sizeof(nonce_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_nonce_payload_t *this) +{ + return NONCE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_nonce_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_nonce_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_nonce_payload_t *this) +{ + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH + this->nonce.len; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_nonce_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of payload_t.destroy and nonce_payload_t.destroy. + */ +static void destroy(private_nonce_payload_t *this) +{ + if (this->nonce.ptr != NULL) + { + free(this->nonce.ptr); + } + + free(this); +} + +/* + * Described in header + */ +nonce_payload_t *nonce_payload_create() +{ + private_nonce_payload_t *this = malloc_thing(private_nonce_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (nonce_payload_t *)) destroy; + this->public.set_nonce = (void (*) (nonce_payload_t *,chunk_t)) set_nonce; + this->public.get_nonce = (chunk_t (*) (nonce_payload_t *)) get_nonce; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH; + this->nonce.ptr = NULL; + this->nonce.len = 0; + + return (&(this->public)); +} + + diff --git a/src/charon/encoding/payloads/nonce_payload.h b/src/charon/encoding/payloads/nonce_payload.h new file mode 100644 index 000000000..96d83b028 --- /dev/null +++ b/src/charon/encoding/payloads/nonce_payload.h @@ -0,0 +1,99 @@ +/** + * @file nonce_payload.h + * + * @brief Interface of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef NONCE_PAYLOAD_H_ +#define NONCE_PAYLOAD_H_ + +typedef struct nonce_payload_t nonce_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + +/** + * Nonce size in bytes for nonces sending to other peer. + * + * @warning Nonce size MUST be between 16 and 256 bytes. + * + * @ingroup payloads + */ +#define NONCE_SIZE 16 + +/** + * Length of a nonce payload without a nonce in bytes. + * + * @ingroup payloads + */ +#define NONCE_PAYLOAD_HEADER_LENGTH 4 + +/** + * Object representing an IKEv2 Nonce payload. + * + * The Nonce payload format is described in RFC section 3.3. + * + * @b Constructors: + * - nonce_payload_create() + * + * @ingroup payloads + */ +struct nonce_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the nonce value. + * + * @param this calling nonce_payload_t object + * @param nonce chunk containing the nonce, will be cloned + */ + void (*set_nonce) (nonce_payload_t *this, chunk_t nonce); + + /** + * @brief Get the nonce value. + * + * @param this calling nonce_payload_t object + * @return a chunk containing the cloned nonce + */ + chunk_t (*get_nonce) (nonce_payload_t *this); + + /** + * @brief Destroys an nonce_payload_t object. + * + * @param this nonce_payload_t object to destroy + */ + void (*destroy) (nonce_payload_t *this); +}; + +/** + * @brief Creates an empty nonce_payload_t object + * + * @return nonce_payload_t object + * + * @ingroup payloads + */ + +nonce_payload_t *nonce_payload_create(void); + + +#endif /*NONCE_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c new file mode 100644 index 000000000..a04901a90 --- /dev/null +++ b/src/charon/encoding/payloads/notify_payload.c @@ -0,0 +1,481 @@ +/** + * @file notify_payload.c + * + * @brief Implementation of notify_payload_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "notify_payload.h" + +#include <daemon.h> +#include <encoding/payloads/encodings.h> +#include <crypto/hashers/hasher.h> + +ENUM_BEGIN(notify_type_names, UNSUPPORTED_CRITICAL_PAYLOAD, UNSUPPORTED_CRITICAL_PAYLOAD, + "UNSUPPORTED_CRITICAL_PAYLOAD"); +ENUM_NEXT(notify_type_names, INVALID_IKE_SPI, INVALID_MAJOR_VERSION, UNSUPPORTED_CRITICAL_PAYLOAD, + "INVALID_IKE_SPI", + "INVALID_MAJOR_VERSION"); +ENUM_NEXT(notify_type_names, INVALID_SYNTAX, INVALID_SYNTAX, INVALID_MAJOR_VERSION, + "INVALID_SYNTAX"); +ENUM_NEXT(notify_type_names, INVALID_MESSAGE_ID, INVALID_MESSAGE_ID, INVALID_SYNTAX, + "INVALID_MESSAGE_ID"); +ENUM_NEXT(notify_type_names, INVALID_SPI, INVALID_SPI, INVALID_MESSAGE_ID, + "INVALID_SPI"); +ENUM_NEXT(notify_type_names, NO_PROPOSAL_CHOSEN, NO_PROPOSAL_CHOSEN, INVALID_SPI, + "NO_PROPOSAL_CHOSEN"); +ENUM_NEXT(notify_type_names, INVALID_KE_PAYLOAD, INVALID_KE_PAYLOAD, NO_PROPOSAL_CHOSEN, + "INVALID_KE_PAYLOAD"); +ENUM_NEXT(notify_type_names, AUTHENTICATION_FAILED, AUTHENTICATION_FAILED, INVALID_KE_PAYLOAD, + "AUTHENTICATION_FAILED"); +ENUM_NEXT(notify_type_names, SINGLE_PAIR_REQUIRED, INVALID_SELECTORS, AUTHENTICATION_FAILED, + "SINGLE_PAIR_REQUIRED", + "NO_ADDITIONAL_SAS", + "INTERNAL_ADDRESS_FAILURE", + "FAILED_CP_REQUIRED", + "TS_UNACCEPTABLE", + "INVALID_SELECTORS"); +ENUM_NEXT(notify_type_names, INITIAL_CONTACT, AUTH_LIFETIME, INVALID_SELECTORS, + "INITIAL_CONTACT", + "SET_WINDOW_SIZE", + "ADDITIONAL_TS_POSSIBLE", + "IPCOMP_SUPPORTED", + "NAT_DETECTION_SOURCE_IP", + "NAT_DETECTION_DESTINATION_IP", + "COOKIE", + "USE_TRANSPORT_MODE", + "HTTP_CERT_LOOKUP_SUPPORTED", + "REKEY_SA", + "ESP_TFC_PADDING_NOT_SUPPORTED", + "NON_FIRST_FRAGMENTS_ALSO", + "MOBIKE_SUPPORTED", + "ADDITIONAL_IP4_ADDRESS", + "ADDITIONAL_IP6_ADDRESS", + "NO_ADDITIONAL_ADDRESSES", + "UPDATE_SA_ADDRESSES", + "COOKIE2", + "NO_NATS_ALLOWED", + "AUTH_LIFETIME"); +ENUM_NEXT(notify_type_names, EAP_ONLY_AUTHENTICATION, EAP_ONLY_AUTHENTICATION, AUTH_LIFETIME, + "EAP_ONLY_AUTHENTICATION"); +ENUM_END(notify_type_names, EAP_ONLY_AUTHENTICATION); + +typedef struct private_notify_payload_t private_notify_payload_t; + +/** + * Private data of an notify_payload_t object. + * + */ +struct private_notify_payload_t { + /** + * Public notify_payload_t interface. + */ + notify_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Protocol id. + */ + u_int8_t protocol_id; + + /** + * Spi size. + */ + u_int8_t spi_size; + + /** + * Notify message type. + */ + u_int16_t notify_type; + + /** + * Security parameter index (spi). + */ + chunk_t spi; + + /** + * Notification data. + */ + chunk_t notification_data; +}; + +/** + * Encoding rules to parse or generate a IKEv2-Notify Payload. + * + * The defined offsets are the positions in a object of type + * private_notify_payload_t. + * + */ +encoding_rule_t notify_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_notify_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_notify_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_notify_payload_t, payload_length) }, + /* Protocol ID as 8 bit field*/ + { U_INT_8, offsetof(private_notify_payload_t, protocol_id) }, + /* SPI Size as 8 bit field*/ + { SPI_SIZE, offsetof(private_notify_payload_t, spi_size) }, + /* Notify message type as 16 bit field*/ + { U_INT_16, offsetof(private_notify_payload_t, notify_type) }, + /* SPI as variable length field*/ + { SPI, offsetof(private_notify_payload_t, spi) }, + /* Key Exchange Data is from variable size */ + { NOTIFICATION_DATA, offsetof(private_notify_payload_t, notification_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Protocol ID ! SPI Size ! Notify Message Type ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Security Parameter Index (SPI) ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Notification Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_notify_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_NONE: + case PROTO_IKE: + case PROTO_AH: + case PROTO_ESP: + break; + default: + DBG1(DBG_ENC, "Unknown protocol (%d)", this->protocol_id); + return FAILED; + } + + switch (this->notify_type) + { + case INVALID_KE_PAYLOAD: + { + /* check notification data */ + diffie_hellman_group_t dh_group; + if (this->notification_data.len != 2) + { + DBG1(DBG_ENC, "invalid notify data length for %N (%d)", + notify_type_names, this->notify_type, + this->notification_data.len); + return FAILED; + } + dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr)); + switch (dh_group) + { + case MODP_768_BIT: + case MODP_1024_BIT: + case MODP_1536_BIT: + case MODP_2048_BIT: + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_6144_BIT: + case MODP_8192_BIT: + break; + default: + DBG1(DBG_ENC, "Bad DH group (%d)", dh_group); + return FAILED; + } + break; + } + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: + { + if (this->notification_data.len != HASH_SIZE_SHA1) + { + DBG1(DBG_ENC, "invalid %N notify length", + notify_type_names, this->notify_type); + return FAILED; + } + break; + } + case INVALID_SYNTAX: + case INVALID_MAJOR_VERSION: + case NO_PROPOSAL_CHOSEN: + { + if (this->notification_data.len != 0) + { + DBG1(DBG_ENC, "invalid %N notify", + notify_type_names, this->notify_type); + return FAILED; + } + break; + } + default: + /* TODO: verify */ + break; + } + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_notify_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = notify_payload_encodings; + *rule_count = sizeof(notify_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_notify_payload_t *this) +{ + return NOTIFY; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_notify_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_notify_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the payloads length. + */ +static void compute_length (private_notify_payload_t *this) +{ + size_t length = NOTIFY_PAYLOAD_HEADER_LENGTH; + if (this->notification_data.ptr != NULL) + { + length += this->notification_data.len; + } + if (this->spi.ptr != NULL) + { + length += this->spi.len; + } + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_notify_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of notify_payload_t.get_protocol_id. + */ +static u_int8_t get_protocol_id(private_notify_payload_t *this) +{ + return this->protocol_id; +} + +/** + * Implementation of notify_payload_t.set_protocol_id. + */ +static void set_protocol_id(private_notify_payload_t *this, u_int8_t protocol_id) +{ + this->protocol_id = protocol_id; +} + +/** + * Implementation of notify_payload_t.get_notify_type. + */ +static notify_type_t get_notify_type(private_notify_payload_t *this) +{ + return this->notify_type; +} + +/** + * Implementation of notify_payload_t.set_notify_type. + */ +static void set_notify_type(private_notify_payload_t *this, u_int16_t notify_type) +{ + this->notify_type = notify_type; +} + +/** + * Implementation of notify_payload_t.get_spi. + */ +static u_int32_t get_spi(private_notify_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi.len == 4) + { + return *((u_int32_t*)this->spi.ptr); + } + default: + break; + } + return 0; +} + +/** + * Implementation of notify_payload_t.set_spi. + */ +static void set_spi(private_notify_payload_t *this, u_int32_t spi) +{ + chunk_free(&this->spi); + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + this->spi = chunk_alloc(4); + *((u_int32_t*)this->spi.ptr) = spi; + break; + default: + break; + } + this->spi_size = this->spi.len; + compute_length(this); +} + +/** + * Implementation of notify_payload_t.get_notification_data. + */ +static chunk_t get_notification_data(private_notify_payload_t *this) +{ + return (this->notification_data); +} + +/** + * Implementation of notify_payload_t.set_notification_data. + */ +static status_t set_notification_data(private_notify_payload_t *this, chunk_t notification_data) +{ + chunk_free(&this->notification_data); + if (notification_data.len > 0) + { + this->notification_data = chunk_clone(notification_data); + } + compute_length(this); + return SUCCESS; +} + +/** + * Implementation of notify_payload_t.destroy and notify_payload_t.destroy. + */ +static status_t destroy(private_notify_payload_t *this) +{ + chunk_free(&this->notification_data); + chunk_free(&this->spi); + free(this); + return SUCCESS; +} + +/* + * Described in header + */ +notify_payload_t *notify_payload_create() +{ + private_notify_payload_t *this = malloc_thing(private_notify_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_protocol_id = (u_int8_t (*) (notify_payload_t *)) get_protocol_id; + this->public.set_protocol_id = (void (*) (notify_payload_t *,u_int8_t)) set_protocol_id; + this->public.get_notify_type = (notify_type_t (*) (notify_payload_t *)) get_notify_type; + this->public.set_notify_type = (void (*) (notify_payload_t *,notify_type_t)) set_notify_type; + this->public.get_spi = (u_int32_t (*) (notify_payload_t *)) get_spi; + this->public.set_spi = (void (*) (notify_payload_t *,u_int32_t)) set_spi; + this->public.get_notification_data = (chunk_t (*) (notify_payload_t *)) get_notification_data; + this->public.set_notification_data = (void (*) (notify_payload_t *,chunk_t)) set_notification_data; + this->public.destroy = (void (*) (notify_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = NOTIFY_PAYLOAD_HEADER_LENGTH; + this->protocol_id = 0; + this->notify_type = 0; + this->spi.ptr = NULL; + this->spi.len = 0; + this->spi_size = 0; + this->notification_data.ptr = NULL; + this->notification_data.len = 0; + + return &this->public; +} + +/* + * Described in header. + */ +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t notify_type) +{ + notify_payload_t *notify = notify_payload_create(); + + notify->set_notify_type(notify,notify_type); + notify->set_protocol_id(notify,protocol_id); + + return notify; +} diff --git a/src/charon/encoding/payloads/notify_payload.h b/src/charon/encoding/payloads/notify_payload.h new file mode 100644 index 000000000..431932631 --- /dev/null +++ b/src/charon/encoding/payloads/notify_payload.h @@ -0,0 +1,224 @@ +/** + * @file notify_payload.h + * + * @brief Interface of notify_payload_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef NOTIFY_PAYLOAD_H_ +#define NOTIFY_PAYLOAD_H_ + +typedef enum notify_type_t notify_type_t; +typedef struct notify_payload_t notify_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/proposal_substructure.h> +#include <utils/linked_list.h> + +/** + * Notify payload length in bytes without any spi and notification data. + * + * @ingroup payloads + */ +#define NOTIFY_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Notify message types. + * + * See IKEv2 RFC 3.10.1. + * + * @ingroup payloads + */ +enum notify_type_t { + /* notify error messages */ + UNSUPPORTED_CRITICAL_PAYLOAD = 1, + INVALID_IKE_SPI = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_SYNTAX = 7, + INVALID_MESSAGE_ID = 9, + INVALID_SPI = 11, + NO_PROPOSAL_CHOSEN = 14, + INVALID_KE_PAYLOAD = 17, + AUTHENTICATION_FAILED = 24, + SINGLE_PAIR_REQUIRED = 34, + NO_ADDITIONAL_SAS = 35, + INTERNAL_ADDRESS_FAILURE = 36, + FAILED_CP_REQUIRED = 37, + TS_UNACCEPTABLE = 38, + INVALID_SELECTORS = 39, + /* notify status messages */ + INITIAL_CONTACT = 16384, + SET_WINDOW_SIZE = 16385, + ADDITIONAL_TS_POSSIBLE = 16386, + IPCOMP_SUPPORTED = 16387, + NAT_DETECTION_SOURCE_IP = 16388, + NAT_DETECTION_DESTINATION_IP = 16389, + COOKIE = 16390, + USE_TRANSPORT_MODE = 16391, + HTTP_CERT_LOOKUP_SUPPORTED = 16392, + REKEY_SA = 16393, + ESP_TFC_PADDING_NOT_SUPPORTED = 16394, + NON_FIRST_FRAGMENTS_ALSO = 16395, + /* mobike extension, RFC4555 */ + MOBIKE_SUPPORTED = 16396, + ADDITIONAL_IP4_ADDRESS = 16397, + ADDITIONAL_IP6_ADDRESS = 16398, + NO_ADDITIONAL_ADDRESSES = 16399, + UPDATE_SA_ADDRESSES = 16400, + COOKIE2 = 16401, + NO_NATS_ALLOWED = 16402, + /* repeated authentication extension, RFC4478 */ + AUTH_LIFETIME = 16403, + /* draft-eronen-ipsec-ikev2-eap-auth, not assigned by IANA yet */ + EAP_ONLY_AUTHENTICATION = 40960, + /* BEET mode, not even a draft yet. private use */ + USE_BEET_MODE = 40961, +}; + +/** + * enum name for notify_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *notify_type_names; + +/** + * @brief Class representing an IKEv2-Notify Payload. + * + * The Notify Payload format is described in Draft section 3.10. + * + * @b Constructors: + * - notify_payload_create() + * - notify_payload_create_from_protocol_and_type() + * + * @todo Build specified constructor/getter for notify's + * + * @ingroup payloads + */ +struct notify_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Gets the protocol id of this payload. + * + * @param this calling notify_payload_t object + * @return protocol id of this payload + */ + u_int8_t (*get_protocol_id) (notify_payload_t *this); + + /** + * @brief Sets the protocol id of this payload. + * + * @param this calling notify_payload_t object + * @param protocol_id protocol id to set + */ + void (*set_protocol_id) (notify_payload_t *this, u_int8_t protocol_id); + + /** + * @brief Gets the notify message type of this payload. + * + * @param this calling notify_payload_t object + * @return notify message type of this payload + */ + notify_type_t (*get_notify_type) (notify_payload_t *this); + + /** + * @brief Sets notify message type of this payload. + * + * @param this calling notify_payload_t object + * @param type notify message type to set + */ + void (*set_notify_type) (notify_payload_t *this, notify_type_t type); + + /** + * @brief Returns the currently set spi of this payload. + * + * This is only valid for notifys with protocol AH|ESP + * + * @param this calling notify_payload_t object + * @return SPI value + */ + u_int32_t (*get_spi) (notify_payload_t *this); + + /** + * @brief Sets the spi of this payload. + * + * This is only valid for notifys with protocol AH|ESP + * + * @param this calling notify_payload_t object + * @param spi SPI value + */ + void (*set_spi) (notify_payload_t *this, u_int32_t spi); + + /** + * @brief Returns the currently set notification data of payload. + * + * @warning Returned data are not copied. + * + * @param this calling notify_payload_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_notification_data) (notify_payload_t *this); + + /** + * @brief Sets the notification data of this payload. + * + * @warning Value is getting copied. + * + * @param this calling notify_payload_t object + * @param notification_data chunk_t pointing to the value to set + */ + void (*set_notification_data) (notify_payload_t *this, chunk_t notification_data); + + /** + * @brief Destroys an notify_payload_t object. + * + * @param this notify_payload_t object to destroy + */ + void (*destroy) (notify_payload_t *this); +}; + +/** + * @brief Creates an empty notify_payload_t object + * + * @return created notify_payload_t object + * + * @ingroup payloads + */ +notify_payload_t *notify_payload_create(void); + +/** + * @brief Creates an notify_payload_t object of specific type for specific protocol id. + * + * @param protocol_id protocol id (IKE, AH or ESP) + * @param type notify type (see notify_type_t) + * @return notify_payload_t object + * + * @ingroup payloads + */ +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t type); + + +#endif /*NOTIFY_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/payload.c b/src/charon/encoding/payloads/payload.c new file mode 100644 index 000000000..3bd4cdb13 --- /dev/null +++ b/src/charon/encoding/payloads/payload.c @@ -0,0 +1,161 @@ +/** + * @file payload.c + * + * @brief Generic constructor to the payload_t interface. + * + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "payload.h" + +#include <encoding/payloads/ike_header.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <encoding/payloads/vendor_id_payload.h> +#include <encoding/payloads/cp_payload.h> +#include <encoding/payloads/configuration_attribute.h> +#include <encoding/payloads/eap_payload.h> +#include <encoding/payloads/unknown_payload.h> + + +ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD, + "NO_PAYLOAD"); +ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NO_PAYLOAD, + "SECURITY_ASSOCIATION", + "KEY_EXCHANGE", + "ID_INITIATOR", + "ID_RESPONDER", + "CERTIFICATE", + "CERTIFICATE_REQUEST", + "AUTHENTICATION", + "NONCE", + "NOTIFY", + "DELETE", + "VENDOR_ID", + "TRAFFIC_SELECTOR_INITIATOR", + "TRAFFIC_SELECTOR_RESPONDER", + "ENCRYPTED", + "CONFIGURATION", + "EXTENSIBLE_AUTHENTICATION"); +ENUM_NEXT(payload_type_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION, + "HEADER", + "PROPOSAL_SUBSTRUCTURE", + "TRANSFORM_SUBSTRUCTURE", + "TRANSFORM_ATTRIBUTE", + "TRAFFIC_SELECTOR_SUBSTRUCTURE", + "CONFIGURATION_ATTRIBUTE", + "UNKNOWN_PAYLOAD"); +ENUM_END(payload_type_names, UNKNOWN_PAYLOAD); + +/* short forms of payload names */ +ENUM_BEGIN(payload_type_short_names, NO_PAYLOAD, NO_PAYLOAD, + "--"); +ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NO_PAYLOAD, + "SA", + "KE", + "IDi", + "IDr", + "CERT", + "CERTREQ", + "AUTH", + "No", + "N", + "D", + "V", + "TSi", + "TSr", + "E", + "CP", + "EAP"); +ENUM_NEXT(payload_type_short_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION, + "HDR", + "PROP", + "TRANS", + "TRANSATTR", + "TSSUB", + "CPATTR", + "??"); +ENUM_END(payload_type_short_names, UNKNOWN_PAYLOAD); + +/* + * see header + */ +payload_t *payload_create(payload_type_t type) +{ + switch (type) + { + case HEADER: + return (payload_t*)ike_header_create(); + case SECURITY_ASSOCIATION: + return (payload_t*)sa_payload_create(); + case PROPOSAL_SUBSTRUCTURE: + return (payload_t*)proposal_substructure_create(); + case TRANSFORM_SUBSTRUCTURE: + return (payload_t*)transform_substructure_create(); + case TRANSFORM_ATTRIBUTE: + return (payload_t*)transform_attribute_create(); + case NONCE: + return (payload_t*)nonce_payload_create(); + case ID_INITIATOR: + return (payload_t*)id_payload_create(TRUE); + case ID_RESPONDER: + return (payload_t*)id_payload_create(FALSE); + case AUTHENTICATION: + return (payload_t*)auth_payload_create(); + case CERTIFICATE: + return (payload_t*)cert_payload_create(); + case CERTIFICATE_REQUEST: + return (payload_t*)certreq_payload_create(); + case TRAFFIC_SELECTOR_SUBSTRUCTURE: + return (payload_t*)traffic_selector_substructure_create(); + case TRAFFIC_SELECTOR_INITIATOR: + return (payload_t*)ts_payload_create(TRUE); + case TRAFFIC_SELECTOR_RESPONDER: + return (payload_t*)ts_payload_create(FALSE); + case KEY_EXCHANGE: + return (payload_t*)ke_payload_create(); + case NOTIFY: + return (payload_t*)notify_payload_create(); + case DELETE: + return (payload_t*)delete_payload_create(0); + case VENDOR_ID: + return (payload_t*)vendor_id_payload_create(); + case CONFIGURATION: + return (payload_t*)cp_payload_create(); + case CONFIGURATION_ATTRIBUTE: + return (payload_t*)configuration_attribute_create(); + case EXTENSIBLE_AUTHENTICATION: + return (payload_t*)eap_payload_create(); + case ENCRYPTED: + return (payload_t*)encryption_payload_create(); + default: + return (payload_t*)unknown_payload_create(); + } +} + diff --git a/src/charon/encoding/payloads/payload.h b/src/charon/encoding/payloads/payload.h new file mode 100644 index 000000000..9a8c2f482 --- /dev/null +++ b/src/charon/encoding/payloads/payload.h @@ -0,0 +1,282 @@ +/** + * @file payload.h + * + * @brief Interface payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef PAYLOAD_H_ +#define PAYLOAD_H_ + +typedef enum payload_type_t payload_type_t; +typedef struct payload_t payload_t; + +#include <library.h> +#include <encoding/payloads/encodings.h> + + +/** + * @brief Payload-Types of a IKEv2-Message. + * + * Header and substructures are also defined as + * payload types with values from PRIVATE USE space. + * + * @ingroup payloads + */ +enum payload_type_t{ + + /** + * End of payload list in next_payload + */ + NO_PAYLOAD = 0, + + /** + * The security association (SA) payload containing proposals. + */ + SECURITY_ASSOCIATION = 33, + + /** + * The key exchange (KE) payload containing diffie-hellman values. + */ + KEY_EXCHANGE = 34, + + /** + * Identification for the original initiator (IDi). + */ + ID_INITIATOR = 35, + + /** + * Identification for the original responder (IDr). + */ + ID_RESPONDER = 36, + + /** + * Certificate payload with certificates (CERT). + */ + CERTIFICATE = 37, + + /** + * Certificate request payload (CERTREQ). + */ + CERTIFICATE_REQUEST = 38, + + /** + * Authentication payload contains auth data (AUTH). + */ + AUTHENTICATION = 39, + + /** + * Nonces, for initator and responder (Ni, Nr, N) + */ + NONCE = 40, + + /** + * Notif paylaod (N). + */ + NOTIFY = 41, + + /** + * Delete payload (D) + */ + DELETE = 42, + + /** + * Vendor id paylpoad (V). + */ + VENDOR_ID = 43, + + /** + * Traffic selector for the original initiator (TSi). + */ + TRAFFIC_SELECTOR_INITIATOR = 44, + + /** + * Traffic selector for the original responser (TSr). + */ + TRAFFIC_SELECTOR_RESPONDER = 45, + + /** + * Encryption payload, contains other payloads (E). + */ + ENCRYPTED = 46, + + /** + * Configuration payload (CP). + */ + CONFIGURATION = 47, + + /** + * Extensible authentication payload (EAP). + */ + EXTENSIBLE_AUTHENTICATION = 48, + + /** + * Header has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle IKEv2-Header like a payload. + */ + HEADER = 140, + + /** + * PROPOSAL_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a proposal substructure like a payload. + */ + PROPOSAL_SUBSTRUCTURE = 141, + + /** + * TRANSFORM_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform substructure like a payload. + */ + TRANSFORM_SUBSTRUCTURE = 142, + + /** + * TRANSFORM_ATTRIBUTE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform attribute like a payload. + */ + TRANSFORM_ATTRIBUTE = 143, + + /** + * TRAFFIC_SELECTOR_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform selector like a payload. + */ + TRAFFIC_SELECTOR_SUBSTRUCTURE = 144, + + /** + * CONFIGURATION_ATTRIBUTE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform attribute like a payload. + */ + CONFIGURATION_ATTRIBUTE = 145, + + /** + * A unknown payload has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a unknown payload. + */ + UNKNOWN_PAYLOAD = 146, +}; + + +/** + * enum names for payload_type_t. + */ +extern enum_name_t *payload_type_names; + +/** + * enum names for payload_type_t in a short form. + */ +extern enum_name_t *payload_type_short_names; + +/** + * @brief Generic interface for all payload types (incl.header and substructures). + * + * To handle all kinds of payloads on a generic way, this interface must + * be implemented by every payload. This allows parser_t/generator_t a simple + * handling of all payloads. + * + * @b Constructors: + * - payload_create() with the payload to instantiate. + * + * @ingroup payloads + */ +struct payload_t { + + /** + * @brief Get encoding rules for this payload. + * + * @param this calling object + * @param[out] rules location to store pointer of first rule + * @param[out] rule_count location to store number of rules + */ + void (*get_encoding_rules) (payload_t *this, encoding_rule_t **rules, size_t *rule_count); + + /** + * @brief Get type of payload. + * + * @param this calling object + * @return type of this payload + */ + payload_type_t (*get_type) (payload_t *this); + + /** + * @brief Get type of next payload or NO_PAYLOAD (0) if this is the last one. + * + * @param this calling object + * @return type of next payload + */ + payload_type_t (*get_next_type) (payload_t *this); + + /** + * @brief Set type of next payload. + * + * @param this calling object + * @param type type of next payload + */ + void (*set_next_type) (payload_t *this,payload_type_t type); + + /** + * @brief Get length of payload. + * + * @param this calling object + * @return length of this payload + */ + size_t (*get_length) (payload_t *this); + + /** + * @brief Verifies payload structure and makes consistence check. + * + * @param this calling object + * @return + * - SUCCESS + * - FAILED if consistence not given + */ + status_t (*verify) (payload_t *this); + + /** + * @brief Destroys a payload and all included substructures. + * + * @param this payload to destroy + */ + void (*destroy) (payload_t *this); +}; + +/** + * @brief Create an empty payload. + * + * Useful for the parser, who wants a generic constructor for all payloads. + * It supports all payload_t methods. If a payload type is not known, + * an unknwon_paylod is created with the chunk of data in it. + * + * @param type type of the payload to create + * @return payload_t object + */ +payload_t *payload_create(payload_type_t type); + +#endif /*PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/proposal_substructure.c b/src/charon/encoding/payloads/proposal_substructure.c new file mode 100644 index 000000000..182d2b6e8 --- /dev/null +++ b/src/charon/encoding/payloads/proposal_substructure.c @@ -0,0 +1,603 @@ +/** + * @file proposal_substructure.h + * + * @brief Implementation of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "proposal_substructure.h" + +#include <encoding/payloads/encodings.h> +#include <encoding/payloads/transform_substructure.h> +#include <library.h> +#include <utils/linked_list.h> +#include <daemon.h> + + +/** + * IKEv1 Value for a proposal payload. + */ +#define PROPOSAL_TYPE_VALUE 2 + + +typedef struct private_proposal_substructure_t private_proposal_substructure_t; + +/** + * Private data of an proposal_substructure_t object. + * + */ +struct private_proposal_substructure_t { + /** + * Public proposal_substructure_t interface. + */ + proposal_substructure_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Length of this payload. + */ + u_int16_t proposal_length; + + /** + * Proposal number. + */ + u_int8_t proposal_number; + + /** + * Protocol ID. + */ + u_int8_t protocol_id; + + /** + * SPI size of the following SPI. + */ + u_int8_t spi_size; + + /** + * Number of transforms. + */ + u_int8_t transforms_count; + + /** + * SPI is stored as chunk. + */ + chunk_t spi; + + /** + * Transforms are stored in a linked_list_t. + */ + linked_list_t * transforms; +}; + +/** + * Encoding rules to parse or generate a Proposal substructure. + * + * The defined offsets are the positions in a object of type + * private_proposal_substructure_t. + */ +encoding_rule_t proposal_substructure_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_proposal_substructure_t, next_payload) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* Length of the whole proposal substructure payload*/ + { PAYLOAD_LENGTH, offsetof(private_proposal_substructure_t, proposal_length) }, + /* proposal number is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, proposal_number) }, + /* protocol ID is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, protocol_id) }, + /* SPI Size has its own type */ + { SPI_SIZE, offsetof(private_proposal_substructure_t, spi_size) }, + /* Number of transforms is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, transforms_count) }, + /* SPI is a chunk of variable size*/ + { SPI, offsetof(private_proposal_substructure_t, spi) }, + /* Transforms are stored in a transform substructure, + offset points to a linked_list_t pointer */ + { TRANSFORMS, offsetof(private_proposal_substructure_t, transforms) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! 0 (last) or 2 ! RESERVED ! Proposal Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Proposal # ! Protocol ID ! SPI Size !# of Transforms! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ SPI (variable) ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ <Transforms> ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_proposal_substructure_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + payload_t *current_transform; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 2)) + { + /* must be 0 or 2 */ + DBG1(DBG_ENC, "inconsistent next payload"); + return FAILED; + } + if (this->transforms_count != this->transforms->get_count(this->transforms)) + { + /* must be the same! */ + DBG1(DBG_ENC, "transform count invalid"); + return FAILED; + } + + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi.len != 4) + { + DBG1(DBG_ENC, "invalid SPI length in %N proposal", + protocol_id_names, this->protocol_id); + return FAILED; + } + break; + case PROTO_IKE: + if (this->spi.len != 0 && this->spi.len != 8) + { + DBG1(DBG_ENC, "invalid SPI length in IKE proposal"); + return FAILED; + } + break; + default: + DBG1(DBG_ENC, "invalid proposal protocol (%d)", this->protocol_id); + return FAILED; + } + if ((this->protocol_id == 0) || (this->protocol_id >= 4)) + { + /* reserved are not supported */ + DBG1(DBG_ENC, "invalid protocol"); + return FAILED; + } + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + while(iterator->iterate(iterator, (void**)¤t_transform)) + { + status = current_transform->verify(current_transform); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "TRANSFORM_SUBSTRUCTURE verification failed"); + break; + } + } + iterator->destroy(iterator); + + /* proposal number is checked in SA payload */ + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_proposal_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = proposal_substructure_encodings; + *rule_count = sizeof(proposal_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_proposal_substructure_t *this) +{ + return PROPOSAL_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_proposal_substructure_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_proposal_substructure_t *this,payload_type_t type) +{ +} + +/** + * (re-)compute the length of the payload. + */ +static void compute_length(private_proposal_substructure_t *this) +{ + iterator_t *iterator; + payload_t *current_transform; + size_t transforms_count = 0; + size_t length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH; + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + while (iterator->iterate(iterator, (void**)¤t_transform)) + { + length += current_transform->get_length(current_transform); + transforms_count++; + } + iterator->destroy(iterator); + + length += this->spi.len; + this->transforms_count = transforms_count; + this->proposal_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_proposal_substructure_t *this) +{ + compute_length(this); + return this->proposal_length; +} + +/** + * Implementation of proposal_substructure_t.create_transform_substructure_iterator. + */ +static iterator_t *create_transform_substructure_iterator (private_proposal_substructure_t *this,bool forward) +{ + return (this->transforms->create_iterator(this->transforms,forward)); +} + +/** + * Implementation of proposal_substructure_t.add_transform_substructure. + */ +static void add_transform_substructure (private_proposal_substructure_t *this,transform_substructure_t *transform) +{ + status_t status; + if (this->transforms->get_count(this->transforms) > 0) + { + transform_substructure_t *last_transform; + status = this->transforms->get_last(this->transforms,(void **) &last_transform); + /* last transform is now not anymore last one */ + last_transform->set_is_last_transform(last_transform,FALSE); + + } + transform->set_is_last_transform(transform,TRUE); + + this->transforms->insert_last(this->transforms,(void *) transform); + compute_length(this); +} + +/** + * Implementation of proposal_substructure_t.proposal_substructure_t. + */ +static void set_is_last_proposal (private_proposal_substructure_t *this, bool is_last) +{ + this->next_payload = (is_last) ? 0: PROPOSAL_TYPE_VALUE; +} + +/** + * Implementation of proposal_substructure_t.set_proposal_number. + */ +static void set_proposal_number(private_proposal_substructure_t *this,u_int8_t proposal_number) +{ + this->proposal_number = proposal_number; +} + +/** + * Implementation of proposal_substructure_t.get_proposal_number. + */ +static u_int8_t get_proposal_number (private_proposal_substructure_t *this) +{ + return (this->proposal_number); +} + +/** + * Implementation of proposal_substructure_t.set_protocol_id. + */ +static void set_protocol_id(private_proposal_substructure_t *this,u_int8_t protocol_id) +{ + this->protocol_id = protocol_id; +} + +/** + * Implementation of proposal_substructure_t.get_protocol_id. + */ +static u_int8_t get_protocol_id(private_proposal_substructure_t *this) +{ + return (this->protocol_id); +} + +/** + * Implementation of proposal_substructure_t.set_spi. + */ +static void set_spi(private_proposal_substructure_t *this, chunk_t spi) +{ + /* first delete already set spi value */ + if (this->spi.ptr != NULL) + { + free(this->spi.ptr); + this->spi.ptr = NULL; + this->spi.len = 0; + compute_length(this); + } + + this->spi.ptr = clalloc(spi.ptr,spi.len); + this->spi.len = spi.len; + this->spi_size = spi.len; + compute_length(this); +} + +/** + * Implementation of proposal_substructure_t.get_spi. + */ +static chunk_t get_spi(private_proposal_substructure_t *this) +{ + chunk_t spi; + spi.ptr = this->spi.ptr; + spi.len = this->spi.len; + + return spi; +} + +/** + * Implementation of proposal_substructure_t.get_transform_count. + */ +static size_t get_transform_count (private_proposal_substructure_t *this) +{ + return this->transforms->get_count(this->transforms); +} + +/** + * Implementation of proposal_substructure_t.get_spi_size. + */ +static size_t get_spi_size (private_proposal_substructure_t *this) +{ + return this->spi.len; +} + +/** + * Implementation of proposal_substructure_t.get_proposal. + */ +proposal_t* get_proposal(private_proposal_substructure_t *this) +{ + iterator_t *iterator; + transform_substructure_t *transform; + proposal_t *proposal; + u_int64_t spi; + + proposal = proposal_create(this->protocol_id); + + iterator = this->transforms->create_iterator(this->transforms, TRUE); + while (iterator->iterate(iterator, (void**)&transform)) + { + transform_type_t transform_type; + u_int16_t transform_id; + u_int16_t key_length = 0; + + transform_type = transform->get_transform_type(transform); + transform_id = transform->get_transform_id(transform); + transform->get_key_length(transform, &key_length); + + proposal->add_algorithm(proposal, transform_type, transform_id, key_length); + } + iterator->destroy(iterator); + + switch (this->spi.len) + { + case 4: + spi = *((u_int32_t*)this->spi.ptr); + break; + case 8: + spi = *((u_int64_t*)this->spi.ptr); + break; + default: + spi = 0; + } + proposal->set_spi(proposal, spi); + + return proposal; +} + +/** + * Implementation of proposal_substructure_t.clone. + */ +static private_proposal_substructure_t* clone_(private_proposal_substructure_t *this) +{ + private_proposal_substructure_t *clone; + iterator_t *transforms; + transform_substructure_t *current_transform; + + clone = (private_proposal_substructure_t *) proposal_substructure_create(); + clone->next_payload = this->next_payload; + clone->proposal_number = this->proposal_number; + clone->protocol_id = this->protocol_id; + clone->spi_size = this->spi_size; + if (this->spi.ptr != NULL) + { + clone->spi.ptr = clalloc(this->spi.ptr,this->spi.len); + clone->spi.len = this->spi.len; + } + + transforms = this->transforms->create_iterator(this->transforms,FALSE); + while (transforms->iterate(transforms, (void**)¤t_transform)) + { + current_transform = current_transform->clone(current_transform); + clone->public.add_transform_substructure(&clone->public, current_transform); + } + transforms->destroy(transforms); + + return clone; +} + +/** + * Implements payload_t's and proposal_substructure_t's destroy function. + * See #payload_s.destroy or proposal_substructure_s.destroy for description. + */ +static void destroy(private_proposal_substructure_t *this) +{ + this->transforms->destroy_offset(this->transforms, + offsetof(transform_substructure_t, destroy)); + chunk_free(&this->spi); + free(this); +} + +/* + * Described in header. + */ +proposal_substructure_t *proposal_substructure_create() +{ + private_proposal_substructure_t *this = malloc_thing(private_proposal_substructure_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + + /* public functions */ + this->public.create_transform_substructure_iterator = (iterator_t* (*) (proposal_substructure_t *,bool)) create_transform_substructure_iterator; + this->public.add_transform_substructure = (void (*) (proposal_substructure_t *,transform_substructure_t *)) add_transform_substructure; + this->public.set_proposal_number = (void (*) (proposal_substructure_t *,u_int8_t))set_proposal_number; + this->public.get_proposal_number = (u_int8_t (*) (proposal_substructure_t *)) get_proposal_number; + this->public.set_protocol_id = (void (*) (proposal_substructure_t *,u_int8_t))set_protocol_id; + this->public.get_protocol_id = (u_int8_t (*) (proposal_substructure_t *)) get_protocol_id; + this->public.set_is_last_proposal = (void (*) (proposal_substructure_t *,bool)) set_is_last_proposal; + this->public.get_proposal = (proposal_t* (*) (proposal_substructure_t*))get_proposal; + this->public.set_spi = (void (*) (proposal_substructure_t *,chunk_t))set_spi; + this->public.get_spi = (chunk_t (*) (proposal_substructure_t *)) get_spi; + this->public.get_transform_count = (size_t (*) (proposal_substructure_t *)) get_transform_count; + this->public.get_spi_size = (size_t (*) (proposal_substructure_t *)) get_spi_size; + this->public.clone = (proposal_substructure_t * (*) (proposal_substructure_t *)) clone_; + this->public.destroy = (void (*) (proposal_substructure_t *)) destroy; + + /* set default values of the fields */ + this->next_payload = NO_PAYLOAD; + this->proposal_length = 0; + this->proposal_number = 0; + this->protocol_id = 0; + this->transforms_count = 0; + this->spi_size = 0; + this->spi.ptr = NULL; + this->spi.len = 0; + + this->transforms = linked_list_create(); + + return (&(this->public)); +} + +/* + * Described in header. + */ +proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t *proposal) +{ + private_proposal_substructure_t *this = (private_proposal_substructure_t*) + proposal_substructure_create(); + iterator_t *iterator; + algorithm_t *algo; + transform_substructure_t *transform; + + /* encryption algorithm is only availble in ESP */ + iterator = proposal->create_algorithm_iterator(proposal, ENCRYPTION_ALGORITHM); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(ENCRYPTION_ALGORITHM, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* integrity algorithms */ + iterator = proposal->create_algorithm_iterator(proposal, INTEGRITY_ALGORITHM); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(INTEGRITY_ALGORITHM, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* prf algorithms */ + iterator = proposal->create_algorithm_iterator(proposal, PSEUDO_RANDOM_FUNCTION); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(PSEUDO_RANDOM_FUNCTION, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* dh groups */ + iterator = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(DIFFIE_HELLMAN_GROUP, algo->algorithm, 0); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* extended sequence numbers */ + iterator = proposal->create_algorithm_iterator(proposal, EXTENDED_SEQUENCE_NUMBERS); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(EXTENDED_SEQUENCE_NUMBERS, + algo->algorithm, 0); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* add SPI, if necessary */ + switch (proposal->get_protocol(proposal)) + { + case PROTO_AH: + case PROTO_ESP: + this->spi_size = this->spi.len = 4; + this->spi.ptr = malloc(this->spi_size); + *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal); + break; + case PROTO_IKE: + if (proposal->get_spi(proposal)) + { /* IKE only uses SPIS when rekeying, but on initial setup */ + this->spi_size = this->spi.len = 8; + this->spi.ptr = malloc(this->spi_size); + *((u_int64_t*)this->spi.ptr) = proposal->get_spi(proposal); + } + break; + default: + break; + } + this->proposal_number = 0; + this->protocol_id = proposal->get_protocol(proposal); + + return &this->public; +} diff --git a/src/charon/encoding/payloads/proposal_substructure.h b/src/charon/encoding/payloads/proposal_substructure.h new file mode 100644 index 000000000..93a8d7b2f --- /dev/null +++ b/src/charon/encoding/payloads/proposal_substructure.h @@ -0,0 +1,206 @@ +/** + * @file proposal_substructure.h + * + * @brief Interface of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef PROPOSAL_SUBSTRUCTURE_H_ +#define PROPOSAL_SUBSTRUCTURE_H_ + +typedef struct proposal_substructure_t proposal_substructure_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/transform_substructure.h> +#include <config/proposal.h> +#include <utils/linked_list.h> + + +/** + * Length of the proposal substructure header (without spi). + * + * @ingroup payloads + */ +#define PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2-PROPOSAL SUBSTRUCTURE. + * + * The PROPOSAL SUBSTRUCTURE format is described in RFC section 3.3.1. + * + * @b Constructors: + * - proposal_substructure_create() + * + * @ingroup payloads + */ +struct proposal_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored transform_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * When deleting any transform over this iterator, call + * get_size to make sure the length and number values are ok. + * + * @param this calling proposal_substructure_t object + * @param forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_transform_substructure_iterator) (proposal_substructure_t *this, bool forward); + + /** + * @brief Adds a transform_substructure_t object to this object. + * + * @warning The added transform_substructure_t object is + * getting destroyed in destroy function of proposal_substructure_t. + * + * @param this calling proposal_substructure_t object + * @param transform transform_substructure_t object to add + */ + void (*add_transform_substructure) (proposal_substructure_t *this,transform_substructure_t *transform); + + /** + * @brief Sets the proposal number of current proposal. + * + * @param this calling proposal_substructure_t object + * @param id proposal number to set + */ + void (*set_proposal_number) (proposal_substructure_t *this,u_int8_t proposal_number); + + /** + * @brief get proposal number of current proposal. + * + * @param this calling proposal_substructure_t object + * @return proposal number of current proposal substructure. + */ + u_int8_t (*get_proposal_number) (proposal_substructure_t *this); + + /** + * @brief get the number of transforms in current proposal. + * + * @param this calling proposal_substructure_t object + * @return transform count in current proposal + */ + size_t (*get_transform_count) (proposal_substructure_t *this); + + /** + * @brief get size of the set spi in bytes. + * + * @param this calling proposal_substructure_t object + * @return size of the spi in bytes + */ + size_t (*get_spi_size) (proposal_substructure_t *this); + + /** + * @brief Sets the protocol id of current proposal. + * + * @param this calling proposal_substructure_t object + * @param id protocol id to set + */ + void (*set_protocol_id) (proposal_substructure_t *this,u_int8_t protocol_id); + + /** + * @brief get protocol id of current proposal. + * + * @param this calling proposal_substructure_t object + * @return protocol id of current proposal substructure. + */ + u_int8_t (*get_protocol_id) (proposal_substructure_t *this); + + /** + * @brief Sets the next_payload field of this substructure + * + * If this is the last proposal, next payload field is set to 0, + * otherwise to 2 + * + * @param this calling proposal_substructure_t object + * @param is_last When TRUE, next payload field is set to 0, otherwise to 2 + */ + void (*set_is_last_proposal) (proposal_substructure_t *this, bool is_last); + + /** + * @brief Returns the currently set SPI of this proposal. + * + * @warning Returned data are not copied + * + * @param this calling proposal_substructure_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_spi) (proposal_substructure_t *this); + + /** + * @brief Sets the SPI of the current proposal. + * + * @warning SPI is getting copied + * + * @param this calling proposal_substructure_t object + * @param spi chunk_t pointing to the value to set + */ + void (*set_spi) (proposal_substructure_t *this, chunk_t spi); + + /** + * @brief Get a proposal_t from the propsal_substructure_t. + * + * @param this calling proposal_substructure_t object + * @return proposal_t + */ + proposal_t * (*get_proposal) (proposal_substructure_t *this); + + /** + * @brief Clones an proposal_substructure_t object. + * + * @param this proposal_substructure_t object to clone + * @return cloned object + */ + proposal_substructure_t* (*clone) (proposal_substructure_t *this); + + /** + * @brief Destroys an proposal_substructure_t object. + * + * @param this proposal_substructure_t object to destroy + */ + void (*destroy) (proposal_substructure_t *this); +}; + +/** + * @brief Creates an empty proposal_substructure_t object + * + * @return proposal_substructure_t object + * + * @ingroup payloads + */ +proposal_substructure_t *proposal_substructure_create(void); + +/** + * @brief Creates a proposal_substructure_t from a proposal_t. + * + * @param proposal proposal to build a substruct out of it + * @return proposal_substructure_t object + * + * @ingroup payloads + */ +proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t *proposal); + + +#endif /*PROPOSAL_SUBSTRUCTURE_H_*/ diff --git a/src/charon/encoding/payloads/sa_payload.c b/src/charon/encoding/payloads/sa_payload.c new file mode 100644 index 000000000..e264b2123 --- /dev/null +++ b/src/charon/encoding/payloads/sa_payload.c @@ -0,0 +1,375 @@ +/** + * @file sa_payload.c + * + * @brief Implementation of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "sa_payload.h" + +#include <encoding/payloads/encodings.h> +#include <utils/linked_list.h> +#include <daemon.h> + + +typedef struct private_sa_payload_t private_sa_payload_t; + +/** + * Private data of an sa_payload_t object. + * + */ +struct private_sa_payload_t { + /** + * Public sa_payload_t interface. + */ + sa_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Proposals in this payload are stored in a linked_list_t. + */ + linked_list_t * proposals; +}; + +/** + * Encoding rules to parse or generate a IKEv2-SA Payload + * + * The defined offsets are the positions in a object of type + * private_sa_payload_t. + * + */ +encoding_rule_t sa_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_sa_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_sa_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole SA payload*/ + { PAYLOAD_LENGTH, offsetof(private_sa_payload_t, payload_length) }, + /* Proposals are stored in a proposal substructure, + offset points to a linked_list_t pointer */ + { PROPOSALS, offsetof(private_sa_payload_t, proposals) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ <Proposals> ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_sa_payload_t *this) +{ + int expected_number = 1, current_number; + status_t status = SUCCESS; + iterator_t *iterator; + proposal_substructure_t *current_proposal; + bool first = TRUE; + + /* check proposal numbering */ + iterator = this->proposals->create_iterator(this->proposals,TRUE); + + while(iterator->iterate(iterator, (void**)¤t_proposal)) + { + current_number = current_proposal->get_proposal_number(current_proposal); + if (current_number < expected_number) + { + if (current_number != (expected_number + 1)) + { + DBG1(DBG_ENC, "proposal number is %d, excepted %d or %d", + current_number, expected_number, expected_number + 1); + status = FAILED; + break; + } + } + else if (current_number < expected_number) + { + /* must not be smaller then proceeding one */ + DBG1(DBG_ENC, "proposal number smaller than that of previous proposal"); + status = FAILED; + break; + } + + status = current_proposal->payload_interface.verify(&(current_proposal->payload_interface)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "PROPOSAL_SUBSTRUCTURE verification failed"); + break; + } + first = FALSE; + expected_number = current_number; + } + + iterator->destroy(iterator); + return status; +} + + +/** + * Implementation of payload_t.destroy and sa_payload_t.destroy. + */ +static status_t destroy(private_sa_payload_t *this) +{ + this->proposals->destroy_offset(this->proposals, + offsetof(proposal_substructure_t, destroy)); + free(this); + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_sa_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = sa_payload_encodings; + *rule_count = sizeof(sa_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_sa_payload_t *this) +{ + return SECURITY_ASSOCIATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_sa_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_sa_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute length of the payload. + */ +static void compute_length (private_sa_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_proposal; + size_t length = SA_PAYLOAD_HEADER_LENGTH; + + iterator = this->proposals->create_iterator(this->proposals,TRUE); + while (iterator->iterate(iterator, (void **)¤t_proposal)) + { + length += current_proposal->get_length(current_proposal); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_sa_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of sa_payload_t.create_proposal_substructure_iterator. + */ +static iterator_t *create_proposal_substructure_iterator (private_sa_payload_t *this,bool forward) +{ + return this->proposals->create_iterator(this->proposals,forward); +} + +/** + * Implementation of sa_payload_t.add_proposal_substructure. + */ +static void add_proposal_substructure(private_sa_payload_t *this,proposal_substructure_t *proposal) +{ + status_t status; + u_int proposal_count = this->proposals->get_count(this->proposals); + + if (proposal_count > 0) + { + proposal_substructure_t *last_proposal; + status = this->proposals->get_last(this->proposals,(void **) &last_proposal); + /* last transform is now not anymore last one */ + last_proposal->set_is_last_proposal(last_proposal, FALSE); + } + proposal->set_is_last_proposal(proposal, TRUE); + proposal->set_proposal_number(proposal, proposal_count + 1); + this->proposals->insert_last(this->proposals,(void *) proposal); + compute_length(this); +} + +/** + * Implementation of sa_payload_t.add_proposal. + */ +static void add_proposal(private_sa_payload_t *this, proposal_t *proposal) +{ + proposal_substructure_t *substructure; + + substructure = proposal_substructure_create_from_proposal(proposal); + add_proposal_substructure(this, substructure); +} + +/** + * Implementation of sa_payload_t.get_proposals. + */ +static linked_list_t *get_proposals(private_sa_payload_t *this) +{ + int struct_number = 0; + int ignore_struct_number = 0; + iterator_t *iterator; + proposal_substructure_t *proposal_struct; + linked_list_t *proposal_list; + + /* this list will hold our proposals */ + proposal_list = linked_list_create(); + + /* we do not support proposals split up to two proposal substructures, as + * AH+ESP bundles are not supported in RFC4301 anymore. + * To handle such structures safely, we just skip proposals with multiple + * protocols. + */ + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void **)&proposal_struct)) + { + proposal_t *proposal; + + /* check if a proposal has a single protocol */ + if (proposal_struct->get_proposal_number(proposal_struct) == struct_number) + { + if (ignore_struct_number < struct_number) + { + /* remova an already added, if first of series */ + proposal_list->remove_last(proposal_list, (void**)&proposal); + proposal->destroy(proposal); + ignore_struct_number = struct_number; + } + continue; + } + struct_number++; + proposal = proposal_struct->get_proposal(proposal_struct); + if (proposal) + { + proposal_list->insert_last(proposal_list, proposal); + } + } + iterator->destroy(iterator); + return proposal_list; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create() +{ + private_sa_payload_t *this = malloc_thing(private_sa_payload_t); + + /* public interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_proposal_substructure_iterator = (iterator_t* (*) (sa_payload_t *,bool)) create_proposal_substructure_iterator; + this->public.add_proposal_substructure = (void (*) (sa_payload_t *,proposal_substructure_t *)) add_proposal_substructure; + this->public.add_proposal = (void (*) (sa_payload_t*,proposal_t*))add_proposal; + this->public.get_proposals = (linked_list_t* (*) (sa_payload_t *)) get_proposals; + this->public.destroy = (void (*) (sa_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = SA_PAYLOAD_HEADER_LENGTH; + this->proposals = linked_list_create(); + return &this->public; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create_from_proposal_list(linked_list_t *proposals) +{ + iterator_t *iterator; + proposal_t *proposal; + sa_payload_t *sa_payload = sa_payload_create(); + + /* add every payload from the list */ + iterator = proposals->create_iterator(proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + add_proposal((private_sa_payload_t*)sa_payload, proposal); + } + iterator->destroy(iterator); + + return sa_payload; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create_from_proposal(proposal_t *proposal) +{ + sa_payload_t *sa_payload = sa_payload_create(); + + add_proposal((private_sa_payload_t*)sa_payload, proposal); + + return sa_payload; +} diff --git a/src/charon/encoding/payloads/sa_payload.h b/src/charon/encoding/payloads/sa_payload.h new file mode 100644 index 000000000..67d687857 --- /dev/null +++ b/src/charon/encoding/payloads/sa_payload.h @@ -0,0 +1,141 @@ +/** + * @file sa_payload.h + * + * @brief Interface of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef SA_PAYLOAD_H_ +#define SA_PAYLOAD_H_ + +typedef struct sa_payload_t sa_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/proposal_substructure.h> +#include <utils/linked_list.h> + +/** + * SA_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define SA_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Class representing an IKEv2-SA Payload. + * + * The SA Payload format is described in RFC section 3.3. + * + * @b Constructors: + * - sa_payload_create() + * - sa_payload_create_from_ike_proposals() + * - sa_payload_create_from_proposal() + * + * @todo Add support of algorithms without specified keylength in get_proposals and get_ike_proposals. + * + * @ingroup payloads + */ +struct sa_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored proposal_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When deleting an proposal using this iterator, + * the length of this transform substructure has to be refreshed + * by calling get_length()! + * + * @param this calling sa_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_proposal_substructure_iterator) (sa_payload_t *this, bool forward); + + /** + * @brief Adds a proposal_substructure_t object to this object. + * + * @warning The added proposal_substructure_t object is + * getting destroyed in destroy function of sa_payload_t. + * + * @param this calling sa_payload_t object + * @param proposal proposal_substructure_t object to add + */ + void (*add_proposal_substructure) (sa_payload_t *this,proposal_substructure_t *proposal); + + /** + * @brief Gets the proposals in this payload as a list. + * + * @return a list containing proposal_t s + */ + linked_list_t *(*get_proposals) (sa_payload_t *this); + + /** + * @brief Add a child proposal (AH/ESP) to the payload. + * + * @param proposal child proposal to add to the payload + */ + void (*add_proposal) (sa_payload_t *this, proposal_t *proposal); + + /** + * @brief Destroys an sa_payload_t object. + * + * @param this sa_payload_t object to destroy + */ + void (*destroy) (sa_payload_t *this); +}; + +/** + * @brief Creates an empty sa_payload_t object + * + * @return created sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create(void); + +/** + * @brief Creates a sa_payload_t object from a list of proposals. + * + * @param proposals list of proposals to build the payload from + * @return sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create_from_proposal_list(linked_list_t *proposals); + +/** + * @brief Creates a sa_payload_t object from a single proposal. + * + * This is only for convenience. Use sa_payload_create_from_proposal_list + * if you want to add more than one proposal. + * + * @param proposal proposal from which the payload should be built. + * @return sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create_from_proposal(proposal_t *proposal); + +#endif /*SA_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/traffic_selector_substructure.c b/src/charon/encoding/payloads/traffic_selector_substructure.c new file mode 100644 index 000000000..573139bf3 --- /dev/null +++ b/src/charon/encoding/payloads/traffic_selector_substructure.c @@ -0,0 +1,283 @@ +/** + * @file traffic_selector_substructure.c + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "traffic_selector_substructure.h" + +#include <encoding/payloads/encodings.h> +#include <utils/linked_list.h> + + +typedef struct private_traffic_selector_substructure_t private_traffic_selector_substructure_t; + +/** + * Private data of an traffic_selector_substructure_t object. + * + */ +struct private_traffic_selector_substructure_t { + /** + * Public traffic_selector_substructure_t interface. + */ + traffic_selector_substructure_t public; + + /** + * Type of traffic selector. + */ + u_int8_t ts_type; + + /** + * IP Protocol ID. + */ + u_int8_t ip_protocol_id; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Start port number. + */ + u_int16_t start_port; + + /** + * End port number. + */ + u_int16_t end_port; + + /** + * Starting address. + */ + chunk_t starting_address; + + /** + * Ending address. + */ + chunk_t ending_address; +}; + +/** + * Encoding rules to parse or generate a TS payload + * + * The defined offsets are the positions in a object of type + * private_traffic_selector_substructure_t. + * + */ +encoding_rule_t traffic_selector_substructure_encodings[] = { + /* 1 Byte next ts type*/ + { TS_TYPE, offsetof(private_traffic_selector_substructure_t, ts_type) }, + /* 1 Byte IP protocol id*/ + { U_INT_8, offsetof(private_traffic_selector_substructure_t, ip_protocol_id) }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_traffic_selector_substructure_t, payload_length) }, + /* 2 Byte start port*/ + { U_INT_16, offsetof(private_traffic_selector_substructure_t, start_port) }, + /* 2 Byte end port*/ + { U_INT_16, offsetof(private_traffic_selector_substructure_t, end_port) }, + /* starting address is either 4 or 16 byte */ + { ADDRESS, offsetof(private_traffic_selector_substructure_t, starting_address) }, + /* ending address is either 4 or 16 byte */ + { ADDRESS, offsetof(private_traffic_selector_substructure_t, ending_address) } + +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! TS Type !IP Protocol ID*| Selector Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Start Port* | End Port* | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Starting Address* ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Ending Address* ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_traffic_selector_substructure_t *this) +{ + if (this->start_port > this->end_port) + { + return FAILED; + } + switch (this->ts_type) + { + case TS_IPV4_ADDR_RANGE: + { + if ((this->starting_address.len != 4) || + (this->ending_address.len != 4)) + { + /* ipv4 address must be 4 bytes long */ + return FAILED; + } + break; + } + case TS_IPV6_ADDR_RANGE: + { + if ((this->starting_address.len != 16) || + (this->ending_address.len != 16)) + { + /* ipv6 address must be 16 bytes long */ + return FAILED; + } + break; + } + default: + { + /* not supported ts type */ + return FAILED; + } + } + + return SUCCESS; +} + +/** + * Implementation of traffic_selector_substructure_t.get_encoding_rules. + */ +static void get_encoding_rules(private_traffic_selector_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = traffic_selector_substructure_encodings; + *rule_count = sizeof(traffic_selector_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_traffic_selector_substructure_t *this) +{ + return TRAFFIC_SELECTOR_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_traffic_selector_substructure_t *this) +{ + return 0; +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_traffic_selector_substructure_t *this,payload_type_t type) +{ + +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_traffic_selector_substructure_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of traffic_selector_substructure_t.get_traffic_selector. + */ +static traffic_selector_t *get_traffic_selector(private_traffic_selector_substructure_t *this) +{ + traffic_selector_t *ts; + ts = traffic_selector_create_from_bytes(this->ip_protocol_id, this->ts_type, + this->starting_address, this->start_port, + this->ending_address, this->end_port); + return ts; +} + +/** + * recompute length field of the payload + */ +void compute_length(private_traffic_selector_substructure_t *this) +{ + this->payload_length = TRAFFIC_SELECTOR_HEADER_LENGTH + + this->ending_address.len + this->starting_address.len; +} + +/** + * Implementation of payload_t.destroy and traffic_selector_substructure_t.destroy. + */ +static void destroy(private_traffic_selector_substructure_t *this) +{ + free(this->starting_address.ptr); + free(this->ending_address.ptr); + free(this); +} + +/* + * Described in header + */ +traffic_selector_substructure_t *traffic_selector_substructure_create() +{ + private_traffic_selector_substructure_t *this = malloc_thing(private_traffic_selector_substructure_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_traffic_selector = (traffic_selector_t* (*)(traffic_selector_substructure_t*))get_traffic_selector; + this->public.destroy = (void (*) (traffic_selector_substructure_t *)) destroy; + + /* private variables */ + this->payload_length = TRAFFIC_SELECTOR_HEADER_LENGTH; + this->start_port = 0; + this->end_port = 0; + this->starting_address = chunk_empty; + this->ending_address = chunk_empty; + this->ip_protocol_id = 0; + /* must be set to be valid */ + this->ts_type = TS_IPV4_ADDR_RANGE; + + return (&(this->public)); +} + +/* + * Described in header + */ +traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector(traffic_selector_t *traffic_selector) +{ + private_traffic_selector_substructure_t *this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create(); + this->ts_type = traffic_selector->get_type(traffic_selector); + this->ip_protocol_id = traffic_selector->get_protocol(traffic_selector); + this->start_port = traffic_selector->get_from_port(traffic_selector); + this->end_port = traffic_selector->get_to_port(traffic_selector); + this->starting_address = traffic_selector->get_from_address(traffic_selector); + this->ending_address = traffic_selector->get_to_address(traffic_selector); + + compute_length(this); + + return &(this->public); +} diff --git a/src/charon/encoding/payloads/traffic_selector_substructure.h b/src/charon/encoding/payloads/traffic_selector_substructure.h new file mode 100644 index 000000000..14efccc89 --- /dev/null +++ b/src/charon/encoding/payloads/traffic_selector_substructure.h @@ -0,0 +1,172 @@ +/** + * @file traffic_selector_substructure.h + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ +#define TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ + +typedef struct traffic_selector_substructure_t traffic_selector_substructure_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <utils/host.h> +#include <config/traffic_selector.h> + +/** + * Length of a TRAFFIC SELECTOR SUBSTRUCTURE without start and end address. + * + * @ingroup payloads + */ +#define TRAFFIC_SELECTOR_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 TRAFFIC SELECTOR. + * + * The TRAFFIC SELECTOR format is described in RFC section 3.13.1. + * + * @b Constructors: + * - traffic_selector_substructure_create() + * - traffic_selector_substructure_create_from_traffic_selector() + * + * @ingroup payloads + */ +struct traffic_selector_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the type of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @return type of traffic selector + * + */ + ts_type_t (*get_ts_type) (traffic_selector_substructure_t *this); + + /** + * @brief Set the type of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @param ts_type type of traffic selector + */ + void (*set_ts_type) (traffic_selector_substructure_t *this,ts_type_t ts_type); + + /** + * @brief Get the IP protocol ID of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @return type of traffic selector + * + */ + u_int8_t (*get_protocol_id) (traffic_selector_substructure_t *this); + + /** + * @brief Set the IP protocol ID of Traffic selector + * + * @param this calling traffic_selector_substructure_t object + * @param protocol_id protocol ID of traffic selector + */ + void (*set_protocol_id) (traffic_selector_substructure_t *this,u_int8_t protocol_id); + + /** + * @brief Get the start port and address as host_t object. + * + * Returned host_t object has to get destroyed by the caller. + * + * @param this calling traffic_selector_substructure_t object + * @return start host as host_t object + * + */ + host_t *(*get_start_host) (traffic_selector_substructure_t *this); + + /** + * @brief Set the start port and address as host_t object. + * + * @param this calling traffic_selector_substructure_t object + * @param start_host start host as host_t object + */ + void (*set_start_host) (traffic_selector_substructure_t *this,host_t *start_host); + + /** + * @brief Get the end port and address as host_t object. + * + * Returned host_t object has to get destroyed by the caller. + * + * @param this calling traffic_selector_substructure_t object + * @return end host as host_t object + * + */ + host_t *(*get_end_host) (traffic_selector_substructure_t *this); + + /** + * @brief Set the end port and address as host_t object. + * + * @param this calling traffic_selector_substructure_t object + * @param end_host end host as host_t object + */ + void (*set_end_host) (traffic_selector_substructure_t *this,host_t *end_host); + + /** + * @brief Get a traffic_selector_t from this substructure. + * + * @warning traffic_selector_t must be destroyed after usage. + * + * @param this calling traffic_selector_substructure_t object + * @return contained traffic_selector_t + */ + traffic_selector_t *(*get_traffic_selector) (traffic_selector_substructure_t *this); + + /** + * @brief Destroys an traffic_selector_substructure_t object. + * + * @param this traffic_selector_substructure_t object to destroy + */ + void (*destroy) (traffic_selector_substructure_t *this); +}; + +/** + * @brief Creates an empty traffic_selector_substructure_t object. + * + * TS type is set to default TS_IPV4_ADDR_RANGE! + * + * @return traffic_selector_substructure_t object + * + * @ingroup payloads + */ +traffic_selector_substructure_t *traffic_selector_substructure_create(void); + +/** + * @brief Creates an initialized traffif selector substructure using + * the values from a traffic_selector_t. + * + * @param traffic_selector traffic_selector_t to use for initialization + * @return traffic_selector_substructure_t object + * + * @ingroup payloads + */ +traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector(traffic_selector_t *traffic_selector); + + +#endif /* /TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ */ diff --git a/src/charon/encoding/payloads/transform_attribute.c b/src/charon/encoding/payloads/transform_attribute.c new file mode 100644 index 000000000..066885c55 --- /dev/null +++ b/src/charon/encoding/payloads/transform_attribute.c @@ -0,0 +1,332 @@ +/** + * @file transform_attribute.c + * + * @brief Implementation of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> +#include <stddef.h> + +#include "transform_attribute.h" + +#include <encoding/payloads/encodings.h> +#include <library.h> + +typedef struct private_transform_attribute_t private_transform_attribute_t; + +/** + * Private data of an transform_attribute_t object. + * + */ +struct private_transform_attribute_t { + /** + * Public transform_attribute_t interface. + */ + transform_attribute_t public; + + /** + * Attribute Format Flag. + * + * - TRUE means value is stored in attribute_length_or_value + * - FALSE means value is stored in attribute_value + */ + bool attribute_format; + + /** + * Type of the attribute. + */ + u_int16_t attribute_type; + + /** + * Attribute Length if attribute_format is 0, attribute Value otherwise. + */ + u_int16_t attribute_length_or_value; + + /** + * Attribute value as chunk if attribute_format is 0 (FALSE). + */ + chunk_t attribute_value; +}; + + +ENUM_BEGIN(transform_attribute_type_name, ATTRIBUTE_UNDEFINED, ATTRIBUTE_UNDEFINED, + "ATTRIBUTE_UNDEFINED"); +ENUM_NEXT(transform_attribute_type_name, KEY_LENGTH, KEY_LENGTH, ATTRIBUTE_UNDEFINED, + "KEY_LENGTH"); +ENUM_END(transform_attribute_type_name, KEY_LENGTH); + +/** + * Encoding rules to parse or generate a Transform attribute. + * + * The defined offsets are the positions in a object of type + * private_transform_attribute_t. + * + */ +encoding_rule_t transform_attribute_encodings[] = { + /* Flag defining the format of this payload */ + { ATTRIBUTE_FORMAT, offsetof(private_transform_attribute_t, attribute_format) }, + /* type of the attribute as 15 bit unsigned integer */ + { ATTRIBUTE_TYPE, offsetof(private_transform_attribute_t, attribute_type) }, + /* Length or value, depending on the attribute format flag */ + { ATTRIBUTE_LENGTH_OR_VALUE, offsetof(private_transform_attribute_t, attribute_length_or_value) }, + /* Value of attribute if attribute format flag is zero */ + { ATTRIBUTE_VALUE, offsetof(private_transform_attribute_t, attribute_value) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !A! Attribute Type ! AF=0 Attribute Length ! + !F! ! AF=1 Attribute Value ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! AF=0 Attribute Value ! + ! AF=1 Not Transmitted ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_transform_attribute_t *this) +{ + if (this->attribute_type != KEY_LENGTH) + { + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_transform_attribute_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = transform_attribute_encodings; + *rule_count = sizeof(transform_attribute_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_transform_attribute_t *this) +{ + return TRANSFORM_ATTRIBUTE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_transform_attribute_t *this) +{ + return (NO_PAYLOAD); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_transform_attribute_t *this,payload_type_t type) +{ +} + +/** + * Implementation of transform_attribute_t.get_length. + */ +static size_t get_length(private_transform_attribute_t *this) +{ + if (this->attribute_format == TRUE) + { + /*Attribute size is only 4 byte */ + return 4; + } + return (this->attribute_length_or_value + 4); +} + +/** + * Implementation of transform_attribute_t.set_value_chunk. + */ +static void set_value_chunk(private_transform_attribute_t *this, chunk_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + free(this->attribute_value.ptr); + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + } + + if (value.len > 2) + { + this->attribute_value.ptr = clalloc(value.ptr,value.len); + this->attribute_value.len = value.len; + this->attribute_length_or_value = value.len; + /* attribute has not a fixed length */ + this->attribute_format = FALSE; + } + else + { + memcpy(&(this->attribute_length_or_value),value.ptr,value.len); + } +} + +/** + * Implementation of transform_attribute_t.set_value. + */ +static void set_value(private_transform_attribute_t *this, u_int16_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + free(this->attribute_value.ptr); + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + } + this->attribute_length_or_value = value; +} + +/** + * Implementation of transform_attribute_t.get_value_chunk. + */ +static chunk_t get_value_chunk (private_transform_attribute_t *this) +{ + chunk_t value; + + if (this->attribute_format == FALSE) + { + value.ptr = this->attribute_value.ptr; + value.len = this->attribute_value.len; + } + else + { + value.ptr = (void *) &(this->attribute_length_or_value); + value.len = 2; + } + + return value; +} + +/** + * Implementation of transform_attribute_t.get_value. + */ +static u_int16_t get_value (private_transform_attribute_t *this) +{ + return this->attribute_length_or_value; +} + + +/** + * Implementation of transform_attribute_t.set_attribute_type. + */ +static void set_attribute_type (private_transform_attribute_t *this, u_int16_t type) +{ + this->attribute_type = type & 0x7FFF; +} + +/** + * Implementation of transform_attribute_t.get_attribute_type. + */ +static u_int16_t get_attribute_type (private_transform_attribute_t *this) +{ + return this->attribute_type; +} + +/** + * Implementation of transform_attribute_t.clone. + */ +static transform_attribute_t * clone(private_transform_attribute_t *this) +{ + private_transform_attribute_t *new_clone; + + new_clone = (private_transform_attribute_t *) transform_attribute_create(); + + new_clone->attribute_format = this->attribute_format; + new_clone->attribute_type = this->attribute_type; + new_clone->attribute_length_or_value = this->attribute_length_or_value; + + if (!new_clone->attribute_format) + { + new_clone->attribute_value.ptr = clalloc(this->attribute_value.ptr,this->attribute_value.len); + new_clone->attribute_value.len = this->attribute_value.len; + } + + return (transform_attribute_t *) new_clone; +} + +/** + * Implementation of transform_attribute_t.destroy and payload_t.destroy. + */ +static void destroy(private_transform_attribute_t *this) +{ + if (this->attribute_value.ptr != NULL) + { + free(this->attribute_value.ptr); + } + free(this); +} + +/* + * Described in header. + */ +transform_attribute_t *transform_attribute_create() +{ + private_transform_attribute_t *this = malloc_thing(private_transform_attribute_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.set_value_chunk = (void (*) (transform_attribute_t *,chunk_t)) set_value_chunk; + this->public.set_value = (void (*) (transform_attribute_t *,u_int16_t)) set_value; + this->public.get_value_chunk = (chunk_t (*) (transform_attribute_t *)) get_value_chunk; + this->public.get_value = (u_int16_t (*) (transform_attribute_t *)) get_value; + this->public.set_attribute_type = (void (*) (transform_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_attribute_type = (u_int16_t (*) (transform_attribute_t *)) get_attribute_type; + this->public.clone = (transform_attribute_t * (*) (transform_attribute_t *)) clone; + this->public.destroy = (void (*) (transform_attribute_t *)) destroy; + + /* set default values of the fields */ + this->attribute_format = TRUE; + this->attribute_type = 0; + this->attribute_length_or_value = 0; + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + return (&(this->public)); +} + +/* + * Described in header. + */ +transform_attribute_t *transform_attribute_create_key_length(u_int16_t key_length) +{ + transform_attribute_t *attribute = transform_attribute_create(); + attribute->set_attribute_type(attribute,KEY_LENGTH); + attribute->set_value(attribute,key_length); + return attribute; +} diff --git a/src/charon/encoding/payloads/transform_attribute.h b/src/charon/encoding/payloads/transform_attribute.h new file mode 100644 index 000000000..30583b23f --- /dev/null +++ b/src/charon/encoding/payloads/transform_attribute.h @@ -0,0 +1,154 @@ +/** + * @file transform_attribute.h + * + * @brief Interface of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef TRANSFORM_ATTRIBUTE_H_ +#define TRANSFORM_ATTRIBUTE_H_ + +typedef enum transform_attribute_type_t transform_attribute_type_t; +typedef struct transform_attribute_t transform_attribute_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + + +/** + * Type of the attribute, as in IKEv2 RFC 3.3.5. + * + * @ingroup payloads + */ +enum transform_attribute_type_t { + ATTRIBUTE_UNDEFINED = 16384, + KEY_LENGTH = 14 +}; + +/** + * enum name for transform_attribute_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *transform_attribute_type_names; + +/** + * @brief Class representing an IKEv2- TRANSFORM Attribute. + * + * The TRANSFORM ATTRIBUTE format is described in RFC section 3.3.5. + * + * @ingroup payloads + */ +struct transform_attribute_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling transform_attribute_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_value_chunk) (transform_attribute_t *this); + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling transform_attribute_t object + * @return value + */ + u_int16_t (*get_value) (transform_attribute_t *this); + + /** + * @brief Sets the value of the attribute. + * + * @warning Value is getting copied. + * + * @param this calling transform_attribute_t object + * @param value chunk_t pointing to the value to set + */ + void (*set_value_chunk) (transform_attribute_t *this, chunk_t value); + + /** + * @brief Sets the value of the attribute. + * + * @param this calling transform_attribute_t object + * @param value value to set + */ + void (*set_value) (transform_attribute_t *this, u_int16_t value); + + /** + * @brief Sets the type of the attribute. + * + * @param this calling transform_attribute_t object + * @param type type to set (most significant bit is set to zero) + */ + void (*set_attribute_type) (transform_attribute_t *this, u_int16_t type); + + /** + * @brief get the type of the attribute. + * + * @param this calling transform_attribute_t object + * @return type of the value + */ + u_int16_t (*get_attribute_type) (transform_attribute_t *this); + + /** + * @brief Clones an transform_attribute_t object. + * + * @param this transform_attribute_t object to clone + * @return cloned transform_attribute_t object + */ + transform_attribute_t * (*clone) (transform_attribute_t *this); + + /** + * @brief Destroys an transform_attribute_t object. + * + * @param this transform_attribute_t object to destroy + */ + void (*destroy) (transform_attribute_t *this); +}; + +/** + * @brief Creates an empty transform_attribute_t object. + * + * @return transform_attribute_t object + * + * @ingroup payloads + */ +transform_attribute_t *transform_attribute_create(void); + +/** + * @brief Creates an transform_attribute_t of type KEY_LENGTH. + * + * @param key_length key length in bytes + * @return transform_attribute_t object + * + * @ingroup payloads + */ +transform_attribute_t *transform_attribute_create_key_length(u_int16_t key_length); + + +#endif /*TRANSFORM_ATTRIBUTE_H_*/ diff --git a/src/charon/encoding/payloads/transform_substructure.c b/src/charon/encoding/payloads/transform_substructure.c new file mode 100644 index 000000000..d64d6c754 --- /dev/null +++ b/src/charon/encoding/payloads/transform_substructure.c @@ -0,0 +1,409 @@ +/** + * @file transform_substructure.h + * + * @brief Implementation of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "transform_substructure.h" + +#include <encoding/payloads/transform_attribute.h> +#include <encoding/payloads/encodings.h> +#include <library.h> +#include <utils/linked_list.h> +#include <daemon.h> + + +typedef struct private_transform_substructure_t private_transform_substructure_t; + +/** + * Private data of an transform_substructure_t object. + * + */ +struct private_transform_substructure_t { + /** + * Public transform_substructure_t interface. + */ + transform_substructure_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + + /** + * Length of this payload. + */ + u_int16_t transform_length; + + + /** + * Type of the transform. + */ + u_int8_t transform_type; + + /** + * Transform ID. + */ + u_int16_t transform_id; + + /** + * Transforms Attributes are stored in a linked_list_t. + */ + linked_list_t *attributes; +}; + + +/** + * Encoding rules to parse or generate a Transform substructure. + * + * The defined offsets are the positions in a object of type + * private_transform_substructure_t. + * + */ +encoding_rule_t transform_substructure_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_transform_substructure_t, next_payload) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* Length of the whole transform substructure*/ + { PAYLOAD_LENGTH, offsetof(private_transform_substructure_t, transform_length) }, + /* transform type is a number of 8 bit */ + { U_INT_8, offsetof(private_transform_substructure_t, transform_type) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* tranform ID is a number of 8 bit */ + { U_INT_16, offsetof(private_transform_substructure_t, transform_id) }, + /* Attributes are stored in a transform attribute, + offset points to a linked_list_t pointer */ + { TRANSFORM_ATTRIBUTES, offsetof(private_transform_substructure_t, attributes) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! 0 (last) or 3 ! RESERVED ! Transform Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !Transform Type ! RESERVED ! Transform ID ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Transform Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_transform_substructure_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + payload_t *current_attributes; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 3)) + { + /* must be 0 or 3 */ + DBG1(DBG_ENC, "inconsistent next payload"); + return FAILED; + } + + switch (this->transform_type) + { + case ENCRYPTION_ALGORITHM: + case PSEUDO_RANDOM_FUNCTION: + case INTEGRITY_ALGORITHM: + case DIFFIE_HELLMAN_GROUP: + case EXTENDED_SEQUENCE_NUMBERS: + /* we don't check transform ID, we want to reply + * cleanly with NO_PROPOSAL_CHOSEN or so if we don't support it */ + break; + default: + { + DBG1(DBG_ENC, "invalid transform type: %d", this->transform_type); + return FAILED; + } + } + iterator = this->attributes->create_iterator(this->attributes,TRUE); + + while(iterator->iterate(iterator, (void**)¤t_attributes)) + { + status = current_attributes->verify(current_attributes); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "TRANSFORM_ATTRIBUTE verification failed"); + } + } + iterator->destroy(iterator); + + /* proposal number is checked in SA payload */ + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_transform_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = transform_substructure_encodings; + *rule_count = sizeof(transform_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_transform_substructure_t *this) +{ + return TRANSFORM_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_transform_substructure_t *this) +{ + return (this->next_payload); +} + +/** + * recompute the length of the payload. + */ +static void compute_length (private_transform_substructure_t *this) +{ + iterator_t *iterator; + payload_t *current_attribute; + size_t length = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->transform_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_transform_substructure_t *this) +{ + compute_length(this); + return this->transform_length; +} + +/** + * Implementation of transform_substructure_t.create_transform_attribute_iterator. + */ +static iterator_t *create_transform_attribute_iterator (private_transform_substructure_t *this,bool forward) +{ + return this->attributes->create_iterator(this->attributes,forward); +} + +/** + * Implementation of transform_substructure_t.add_transform_attribute. + */ +static void add_transform_attribute (private_transform_substructure_t *this,transform_attribute_t *attribute) +{ + this->attributes->insert_last(this->attributes,(void *) attribute); + compute_length(this); +} + +/** + * Implementation of transform_substructure_t.set_is_last_transform. + */ +static void set_is_last_transform (private_transform_substructure_t *this, bool is_last) +{ + this->next_payload = (is_last) ? 0: TRANSFORM_TYPE_VALUE; +} + +/** + * Implementation of transform_substructure_t.get_is_last_transform. + */ +static bool get_is_last_transform (private_transform_substructure_t *this) +{ + return ((this->next_payload == TRANSFORM_TYPE_VALUE) ? FALSE : TRUE); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_transform_substructure_t *this,payload_type_t type) +{ +} + +/** + * Implementation of transform_substructure_t.set_transform_type. + */ +static void set_transform_type (private_transform_substructure_t *this,u_int8_t type) +{ + this->transform_type = type; +} + +/** + * Implementation of transform_substructure_t.get_transform_type. + */ +static u_int8_t get_transform_type (private_transform_substructure_t *this) +{ + return this->transform_type; +} + +/** + * Implementation of transform_substructure_t.set_transform_id. + */ +static void set_transform_id (private_transform_substructure_t *this,u_int16_t id) +{ + this->transform_id = id; +} + +/** + * Implementation of transform_substructure_t.get_transform_id. + */ +static u_int16_t get_transform_id (private_transform_substructure_t *this) +{ + return this->transform_id; +} + +/** + * Implementation of transform_substructure_t.clone. + */ +static transform_substructure_t *clone_(private_transform_substructure_t *this) +{ + private_transform_substructure_t *clone; + iterator_t *attributes; + transform_attribute_t *current_attribute; + + clone = (private_transform_substructure_t *) transform_substructure_create(); + clone->next_payload = this->next_payload; + clone->transform_type = this->transform_type; + clone->transform_id = this->transform_id; + + attributes = this->attributes->create_iterator(this->attributes, FALSE); + while (attributes->iterate(attributes, (void**)¤t_attribute)) + { + current_attribute = current_attribute->clone(current_attribute); + clone->public.add_transform_attribute(&clone->public, current_attribute); + } + attributes->destroy(attributes); + + return &clone->public; +} + + +/** + * Implementation of transform_substructure_t.get_key_length. + */ +static status_t get_key_length(private_transform_substructure_t *this, u_int16_t *key_length) +{ + iterator_t *attributes; + transform_attribute_t *current_attribute; + + attributes = this->attributes->create_iterator(this->attributes, TRUE); + while (attributes->iterate(attributes, (void**)¤t_attribute)) + { + if (current_attribute->get_attribute_type(current_attribute) == KEY_LENGTH) + { + *key_length = current_attribute->get_value(current_attribute); + attributes->destroy(attributes); + return SUCCESS; + } + } + attributes->destroy(attributes); + return FAILED; +} + + +/** + * Implementation of transform_substructure_t.destroy and payload_t.destroy. + */ +static void destroy(private_transform_substructure_t *this) +{ + this->attributes->destroy_offset(this->attributes, + offsetof(transform_attribute_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +transform_substructure_t *transform_substructure_create() +{ + private_transform_substructure_t *this = malloc_thing(private_transform_substructure_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_transform_attribute_iterator = (iterator_t * (*) (transform_substructure_t *,bool)) create_transform_attribute_iterator; + this->public.add_transform_attribute = (void (*) (transform_substructure_t *,transform_attribute_t *)) add_transform_attribute; + this->public.set_is_last_transform = (void (*) (transform_substructure_t *,bool)) set_is_last_transform; + this->public.get_is_last_transform = (bool (*) (transform_substructure_t *)) get_is_last_transform; + this->public.set_transform_type = (void (*) (transform_substructure_t *,u_int8_t)) set_transform_type; + this->public.get_transform_type = (u_int8_t (*) (transform_substructure_t *)) get_transform_type; + this->public.set_transform_id = (void (*) (transform_substructure_t *,u_int16_t)) set_transform_id; + this->public.get_transform_id = (u_int16_t (*) (transform_substructure_t *)) get_transform_id; + this->public.get_key_length = (status_t (*) (transform_substructure_t *,u_int16_t *)) get_key_length; + this->public.clone = (transform_substructure_t* (*) (transform_substructure_t *)) clone_; + this->public.destroy = (void (*) (transform_substructure_t *)) destroy; + + /* set default values of the fields */ + this->next_payload = NO_PAYLOAD; + this->transform_length = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + this->transform_id = 0; + this->transform_type = 0; + this->attributes = linked_list_create(); + + return (&(this->public)); +} + +/* + * Described in header + */ +transform_substructure_t *transform_substructure_create_type(transform_type_t transform_type, u_int16_t transform_id, u_int16_t key_length) +{ + transform_substructure_t *transform = transform_substructure_create(); + + transform->set_transform_type(transform,transform_type); + transform->set_transform_id(transform,transform_id); + + /* a keylength attribute is only created for variable length algos */ + if (transform_type == ENCRYPTION_ALGORITHM && + (transform_id == ENCR_AES_CBC || + transform_id == ENCR_IDEA || + transform_id == ENCR_CAST || + transform_id == ENCR_BLOWFISH)) + { + transform_attribute_t *attribute = transform_attribute_create_key_length(key_length); + transform->add_transform_attribute(transform,attribute); + } + + return transform; +} diff --git a/src/charon/encoding/payloads/transform_substructure.h b/src/charon/encoding/payloads/transform_substructure.h new file mode 100644 index 000000000..97f587d5d --- /dev/null +++ b/src/charon/encoding/payloads/transform_substructure.h @@ -0,0 +1,198 @@ +/** + * @file transform_substructure.h + * + * @brief Interface of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef TRANSFORM_SUBSTRUCTURE_H_ +#define TRANSFORM_SUBSTRUCTURE_H_ + +typedef struct transform_substructure_t transform_substructure_t; + +#include <library.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/transform_attribute.h> +#include <utils/linked_list.h> +#include <crypto/diffie_hellman.h> +#include <crypto/signers/signer.h> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <config/proposal.h> + + +/** + * IKEv1 Value for a transform payload. + * + * @ingroup payloads + */ +#define TRANSFORM_TYPE_VALUE 3 + +/** + * Length of the transform substructure header in bytes. + * + * @ingroup payloads + */ +#define TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH 8 + + +/** + * @brief Class representing an IKEv2- TRANSFORM SUBSTRUCTURE. + * + * The TRANSFORM SUBSTRUCTURE format is described in RFC section 3.3.2. + * + * @ingroup payloads + */ +struct transform_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored transform_attribute_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When deleting an transform attribute using this iterator, + * the length of this transform substructure has to be refreshed + * by calling get_length()! + * + * @param this calling transform_substructure_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object. + */ + iterator_t * (*create_transform_attribute_iterator) (transform_substructure_t *this, bool forward); + + /** + * @brief Adds a transform_attribute_t object to this object. + * + * @warning The added proposal_substructure_t object is + * getting destroyed in destroy function of transform_substructure_t. + * + * @param this calling transform_substructure_t object + * @param proposal transform_attribute_t object to add + */ + void (*add_transform_attribute) (transform_substructure_t *this,transform_attribute_t *attribute); + + /** + * @brief Sets the next_payload field of this substructure + * + * If this is the last transform, next payload field is set to 0, + * otherwise to 3 + * + * @param this calling transform_substructure_t object + * @param is_last When TRUE, next payload field is set to 0, otherwise to 3 + */ + void (*set_is_last_transform) (transform_substructure_t *this, bool is_last); + + /** + * @brief Checks if this is the last transform. + * + * @param this calling transform_substructure_t object + * @return TRUE if this is the last Transform, FALSE otherwise + */ + bool (*get_is_last_transform) (transform_substructure_t *this); + + /** + * @brief Sets transform type of the current transform substructure. + * + * @param this calling transform_substructure_t object + * @param type type value to set + */ + void (*set_transform_type) (transform_substructure_t *this,u_int8_t type); + + /** + * @brief get transform type of the current transform. + * + * @param this calling transform_substructure_t object + * @return Transform type of current transform substructure. + */ + u_int8_t (*get_transform_type) (transform_substructure_t *this); + + /** + * @brief Sets transform id of the current transform substructure. + * + * @param this calling transform_substructure_t object + * @param id transform id to set + */ + void (*set_transform_id) (transform_substructure_t *this,u_int16_t id); + + /** + * @brief get transform id of the current transform. + * + * @param this calling transform_substructure_t object + * @return Transform id of current transform substructure. + */ + u_int16_t (*get_transform_id) (transform_substructure_t *this); + + /** + * @brief get transform id of the current transform. + * + * @param this calling transform_substructure_t object + * @param key_length The key length is written to this location + * @return + * - SUCCESS if a key length attribute is contained + * - FAILED if no key length attribute is part of this + * transform or key length uses more then 16 bit! + */ + status_t (*get_key_length) (transform_substructure_t *this,u_int16_t *key_length); + + /** + * @brief Clones an transform_substructure_t object. + * + * @param this transform_substructure_t object to clone + * @return cloned transform_substructure_t object + */ + transform_substructure_t* (*clone) (transform_substructure_t *this); + + /** + * @brief Destroys an transform_substructure_t object. + * + * @param this transform_substructure_t object to destroy + */ + void (*destroy) (transform_substructure_t *this); +}; + +/** + * @brief Creates an empty transform_substructure_t object. + * + * @return created transform_substructure_t object + * + * @ingroup payloads + */ +transform_substructure_t *transform_substructure_create(void); + +/** + * @brief Creates an empty transform_substructure_t object. + * + * The key length is used for the transport types ENCRYPTION_ALGORITHM, + * PSEUDO_RANDOM_FUNCTION, INTEGRITY_ALGORITHM. For all + * other transport types the key_length parameter is not used + * + * @param transform_type type of transform to create + * @param transform_id transform id specifying the specific algorithm of a transform type + * @param key_length Key length for key lenght attribute + * @return transform_substructure_t object + * + * @ingroup payloads + */ +transform_substructure_t *transform_substructure_create_type(transform_type_t transform_type, u_int16_t transform_id, u_int16_t key_length); + +#endif /*TRANSFORM_SUBSTRUCTURE_H_*/ diff --git a/src/charon/encoding/payloads/ts_payload.c b/src/charon/encoding/payloads/ts_payload.c new file mode 100644 index 000000000..ae89919f6 --- /dev/null +++ b/src/charon/encoding/payloads/ts_payload.c @@ -0,0 +1,341 @@ +/** + * @file ts_payload.c + * + * @brief Implementation of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "ts_payload.h" + +#include <encoding/payloads/encodings.h> +#include <utils/linked_list.h> + +typedef struct private_ts_payload_t private_ts_payload_t; + +/** + * Private data of an ts_payload_t object. + * + */ +struct private_ts_payload_t { + /** + * Public ts_payload_t interface. + */ + ts_payload_t public; + + /** + * TRUE if this TS payload is of type TSi, FALSE for TSr. + */ + bool is_initiator; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Number of traffic selectors + */ + u_int8_t number_of_traffic_selectors; + + /** + * Contains the traffic selectors of type traffic_selector_substructure_t. + */ + linked_list_t *traffic_selectors; +}; + +/** + * Encoding rules to parse or generate a TS payload + * + * The defined offsets are the positions in a object of type + * private_ts_payload_t. + * + */ +encoding_rule_t ts_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ts_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_ts_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_ts_payload_t, payload_length)}, + /* 1 Byte TS type*/ + { U_INT_8, offsetof(private_ts_payload_t, number_of_traffic_selectors) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some ts data bytes, length is defined in PAYLOAD_LENGTH */ + { TRAFFIC_SELECTORS, offsetof(private_ts_payload_t, traffic_selectors) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Number of TSs ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ <Traffic Selectors> ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ts_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_traffic_selector; + status_t status = SUCCESS; + + if (this->number_of_traffic_selectors != (this->traffic_selectors->get_count(this->traffic_selectors))) + { + /* must be the same */ + return FAILED; + } + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors,TRUE); + while(iterator->iterate(iterator, (void**)¤t_traffic_selector)) + { + status = current_traffic_selector->verify(current_traffic_selector); + if (status != SUCCESS) + { + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implementation of ts_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_ts_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ts_payload_encodings; + *rule_count = sizeof(ts_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_ts_payload_t *this) +{ + if (this->is_initiator) + { + return TRAFFIC_SELECTOR_INITIATOR; + } + else + { + return TRAFFIC_SELECTOR_RESPONDER; + } +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_ts_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_ts_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length (private_ts_payload_t *this) +{ + iterator_t *iterator; + size_t ts_count = 0; + size_t length = TS_PAYLOAD_HEADER_LENGTH; + payload_t *current_traffic_selector; + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors,TRUE); + while (iterator->iterate(iterator, (void**)¤t_traffic_selector)) + { + length += current_traffic_selector->get_length(current_traffic_selector); + ts_count++; + } + iterator->destroy(iterator); + + this->number_of_traffic_selectors= ts_count; + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ts_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of ts_payload_t.get_initiator. + */ +static bool get_initiator (private_ts_payload_t *this) +{ + return (this->is_initiator); +} + +/** + * Implementation of ts_payload_t.set_initiator. + */ +static void set_initiator (private_ts_payload_t *this,bool is_initiator) +{ + this->is_initiator = is_initiator; +} + +/** + * Implementation of ts_payload_t.add_traffic_selector_substructure. + */ +static void add_traffic_selector_substructure (private_ts_payload_t *this,traffic_selector_substructure_t *traffic_selector) +{ + this->traffic_selectors->insert_last(this->traffic_selectors,traffic_selector); + this->number_of_traffic_selectors = this->traffic_selectors->get_count(this->traffic_selectors); +} + +/** + * Implementation of ts_payload_t.create_traffic_selector_substructure_iterator. + */ +static iterator_t * create_traffic_selector_substructure_iterator (private_ts_payload_t *this, bool forward) +{ + return this->traffic_selectors->create_iterator(this->traffic_selectors,forward); +} + +/** + * Implementation of ts_payload_t.get_traffic_selectors. + */ +static linked_list_t *get_traffic_selectors(private_ts_payload_t *this) +{ + traffic_selector_t *ts; + iterator_t *iterator; + traffic_selector_substructure_t *ts_substructure; + linked_list_t *ts_list = linked_list_create(); + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors, TRUE); + while (iterator->iterate(iterator, (void**)&ts_substructure)) + { + ts = ts_substructure->get_traffic_selector(ts_substructure); + ts_list->insert_last(ts_list, (void*)ts); + } + iterator->destroy(iterator); + + return ts_list; +} + +/** + * Implementation of payload_t.destroy and ts_payload_t.destroy. + */ +static void destroy(private_ts_payload_t *this) +{ + this->traffic_selectors->destroy_offset(this->traffic_selectors, + offsetof(payload_t, destroy)); + free(this); +} + +/* + * Described in header + */ +ts_payload_t *ts_payload_create(bool is_initiator) +{ + private_ts_payload_t *this = malloc_thing(private_ts_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (ts_payload_t *)) destroy; + this->public.get_initiator = (bool (*) (ts_payload_t *)) get_initiator; + this->public.set_initiator = (void (*) (ts_payload_t *,bool)) set_initiator; + this->public.add_traffic_selector_substructure = (void (*) (ts_payload_t *,traffic_selector_substructure_t *)) add_traffic_selector_substructure; + this->public.create_traffic_selector_substructure_iterator = (iterator_t* (*) (ts_payload_t *,bool)) create_traffic_selector_substructure_iterator; + this->public.get_traffic_selectors = (linked_list_t *(*) (ts_payload_t *)) get_traffic_selectors; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =TS_PAYLOAD_HEADER_LENGTH; + this->is_initiator = is_initiator; + this->number_of_traffic_selectors = 0; + this->traffic_selectors = linked_list_create(); + + return &(this->public); +} + +/* + * Described in header + */ +ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, linked_list_t *traffic_selectors) +{ + iterator_t *iterator; + traffic_selector_t *ts; + traffic_selector_substructure_t *ts_substructure; + private_ts_payload_t *this; + + this = (private_ts_payload_t*)ts_payload_create(is_initiator); + + iterator = traffic_selectors->create_iterator(traffic_selectors, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + ts_substructure = traffic_selector_substructure_create_from_traffic_selector(ts); + this->public.add_traffic_selector_substructure(&(this->public), ts_substructure); + } + iterator->destroy(iterator); + + return &(this->public); +} + diff --git a/src/charon/encoding/payloads/ts_payload.h b/src/charon/encoding/payloads/ts_payload.h new file mode 100644 index 000000000..1addee22c --- /dev/null +++ b/src/charon/encoding/payloads/ts_payload.h @@ -0,0 +1,153 @@ +/** + * @file ts_payload.h + * + * @brief Interface of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef TS_PAYLOAD_H_ +#define TS_PAYLOAD_H_ + +typedef struct ts_payload_t ts_payload_t; + +#include <library.h> +#include <utils/linked_list.h> +#include <config/traffic_selector.h> +#include <encoding/payloads/payload.h> +#include <encoding/payloads/traffic_selector_substructure.h> + +/** + * Length of a TS payload without the Traffic selectors. + * + * @ingroup payloads + */ +#define TS_PAYLOAD_HEADER_LENGTH 8 + + +/** + * @brief Class representing an IKEv2 TS payload. + * + * The TS payload format is described in RFC section 3.13. + * + * @b Constructors: + * - ts_payload_create() + * - ts_payload_create_from_traffic_selectors() + * + * @ingroup payloads + */ +struct ts_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the type of TSpayload (TSi or TSr). + * + * @param this calling id_payload_t object + * @return + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + */ + bool (*get_initiator) (ts_payload_t *this); + + /** + * @brief Set the type of TS payload (TSi or TSr). + * + * @param this calling id_payload_t object + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + */ + void (*set_initiator) (ts_payload_t *this,bool is_initiator); + + /** + * @brief Adds a traffic_selector_substructure_t object to this object. + * + * @warning The added traffic_selector_substructure_t object is + * getting destroyed in destroy function of ts_payload_t. + * + * @param this calling ts_payload_t object + * @param traffic_selector traffic_selector_substructure_t object to add + */ + void (*add_traffic_selector_substructure) (ts_payload_t *this,traffic_selector_substructure_t *traffic_selector); + + /** + * @brief Creates an iterator of stored traffic_selector_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When removing an traffic_selector_substructure_t object + * using this iterator, the length of this payload + * has to get refreshed by calling payload_t.get_length! + * + * @param this calling ts_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_traffic_selector_substructure_iterator) (ts_payload_t *this, bool forward); + + /** + * @brief Get a list of nested traffic selectors as traffic_selector_t. + * + * Resulting list and its traffic selectors must be destroyed after usage + * + * @param this calling ts_payload_t object + * @return list of traffic selectors + */ + linked_list_t *(*get_traffic_selectors) (ts_payload_t *this); + + /** + * @brief Destroys an ts_payload_t object. + * + * @param this ts_payload_t object to destroy + */ + void (*destroy) (ts_payload_t *this); +}; + +/** + * @brief Creates an empty ts_payload_t object. + * + * + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + * @return ts_payload_t object + * + * @ingroup payloads + */ +ts_payload_t *ts_payload_create(bool is_initiator); + +/** + * @brief Creates ts_payload with a list of traffic_selector_t + * + * + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + * @param traffic_selectors list of traffic selectors to include + * @return ts_payload_t object + * + * @ingroup payloads + */ +ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, linked_list_t *traffic_selectors); + + +#endif /* TS_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/unknown_payload.c b/src/charon/encoding/payloads/unknown_payload.c new file mode 100644 index 000000000..bbe736085 --- /dev/null +++ b/src/charon/encoding/payloads/unknown_payload.c @@ -0,0 +1,208 @@ +/** + * @file unknown_payload.c + * + * @brief Implementation of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "unknown_payload.h" + + + +typedef struct private_unknown_payload_t private_unknown_payload_t; + +/** + * Private data of an unknown_payload_t object. + */ +struct private_unknown_payload_t { + + /** + * Public unknown_payload_t interface. + */ + unknown_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained data. + */ + chunk_t data; +}; + +/** + * Encoding rules to parse an payload which is not further specified. + * + * The defined offsets are the positions in a object of type + * private_unknown_payload_t. + * + */ +encoding_rule_t unknown_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_unknown_payload_t, next_payload)}, + /* the critical bit */ + { FLAG, offsetof(private_unknown_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_unknown_payload_t, payload_length)}, + /* some unknown data bytes, length is defined in PAYLOAD_LENGTH */ + { UNKNOWN_DATA, offsetof(private_unknown_payload_t, data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Data of any type ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_unknown_payload_t *this) +{ + /* can't do any checks, so we assume its good */ + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_unknown_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = unknown_payload_encodings; + *rule_count = sizeof(unknown_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_unknown_payload_t *this) +{ + return UNKNOWN_PAYLOAD; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_unknown_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_unknown_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_unknown_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of unknown_payload_t.get_data. + */ +static bool is_critical(private_unknown_payload_t *this) +{ + return this->critical; +} + +/** + * Implementation of unknown_payload_t.get_data. + */ +static chunk_t get_data (private_unknown_payload_t *this) +{ + return (this->data); +} + +/** + * Implementation of payload_t.destroy and unknown_payload_t.destroy. + */ +static void destroy(private_unknown_payload_t *this) +{ + if (this->data.ptr != NULL) + { + chunk_free(&(this->data)); + } + + free(this); +} + +/* + * Described in header + */ +unknown_payload_t *unknown_payload_create() +{ + private_unknown_payload_t *this = malloc_thing(private_unknown_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (unknown_payload_t *)) destroy; + this->public.is_critical = (bool (*) (unknown_payload_t *)) is_critical; + this->public.get_data = (chunk_t (*) (unknown_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = UNKNOWN_PAYLOAD_HEADER_LENGTH; + this->data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/unknown_payload.h b/src/charon/encoding/payloads/unknown_payload.h new file mode 100644 index 000000000..8d13a03a3 --- /dev/null +++ b/src/charon/encoding/payloads/unknown_payload.h @@ -0,0 +1,95 @@ +/** + * @file unknown_payload.h + * + * @brief Interface of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef UNKNOWN_PAYLOAD_H_ +#define UNKNOWN_PAYLOAD_H_ + +typedef struct unknown_payload_t unknown_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + +/** + * Header length of the unknown payload. + * + * @ingroup payloads + */ +#define UNKNOWN_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Payload which can't be processed further. + * + * When the parser finds an unknown payload, he builds an instance of + * this class. This allows further processing of this payload, such as + * a check for the critical bit in the header. + * + * @b Constructors: + * - unknown_payload_create() + * + * @ingroup payloads + */ +struct unknown_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the raw data of this payload, without + * the generic payload header. + * + * Returned data are NOT copied and must not be freed. + * + * @param this calling unknown_payload_t object + * @return data as chunk_t + */ + chunk_t (*get_data) (unknown_payload_t *this); + + /** + * @brief Get the critical flag. + * + * @param this calling unknown_payload_t object + * @return TRUE if payload is critical, FALSE if not + */ + bool (*is_critical) (unknown_payload_t *this); + + /** + * @brief Destroys an unknown_payload_t object. + * + * @param this unknown_payload_t object to destroy + */ + void (*destroy) (unknown_payload_t *this); +}; + +/** + * @brief Creates an empty unknown_payload_t object. + * + * @return unknown_payload_t object + * + * @ingroup payloads + */ +unknown_payload_t *unknown_payload_create(void); + + +#endif /* UNKNOWN_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/vendor_id_payload.c b/src/charon/encoding/payloads/vendor_id_payload.c new file mode 100644 index 000000000..e3a4d2e1f --- /dev/null +++ b/src/charon/encoding/payloads/vendor_id_payload.c @@ -0,0 +1,228 @@ +/** + * @file vendor_id_payload.c + * + * @brief Implementation of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stddef.h> + +#include "vendor_id_payload.h" + + +typedef struct private_vendor_id_payload_t private_vendor_id_payload_t; + +/** + * Private data of an vendor_id_payload_t object. + * + */ +struct private_vendor_id_payload_t { + /** + * Public vendor_id_payload_t interface. + */ + vendor_id_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained vendor_id data value. + */ + chunk_t vendor_id_data; +}; + +/** + * Encoding rules to parse or generate a VENDOR ID payload + * + * The defined offsets are the positions in a object of type + * private_vendor_id_payload_t. + * + */ +encoding_rule_t vendor_id_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_vendor_id_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_vendor_id_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_vendor_id_payload_t, payload_length)}, + /* some vendor_id data bytes, length is defined in PAYLOAD_LENGTH */ + { VID_DATA, offsetof(private_vendor_id_payload_t, vendor_id_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certificate Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_vendor_id_payload_t *this) +{ + return SUCCESS; +} + +/** + * Implementation of vendor_id_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_vendor_id_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = vendor_id_payload_encodings; + *rule_count = sizeof(vendor_id_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_vendor_id_payload_t *this) +{ + return VENDOR_ID; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_vendor_id_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_vendor_id_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_vendor_id_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of vendor_id_payload_t.set_data. + */ +static void set_data (private_vendor_id_payload_t *this, chunk_t data) +{ + if (this->vendor_id_data.ptr != NULL) + { + chunk_free(&(this->vendor_id_data)); + } + this->vendor_id_data.ptr = clalloc(data.ptr,data.len); + this->vendor_id_data.len = data.len; + this->payload_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH + this->vendor_id_data.len; +} + +/** + * Implementation of vendor_id_payload_t.get_data. + */ +static chunk_t get_data (private_vendor_id_payload_t *this) +{ + return (this->vendor_id_data); +} + +/** + * Implementation of vendor_id_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_vendor_id_payload_t *this) +{ + chunk_t cloned_data; + if (this->vendor_id_data.ptr == NULL) + { + return (this->vendor_id_data); + } + cloned_data.ptr = clalloc(this->vendor_id_data.ptr,this->vendor_id_data.len); + cloned_data.len = this->vendor_id_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and vendor_id_payload_t.destroy. + */ +static void destroy(private_vendor_id_payload_t *this) +{ + if (this->vendor_id_data.ptr != NULL) + { + chunk_free(&(this->vendor_id_data)); + } + free(this); +} + +/* + * Described in header + */ +vendor_id_payload_t *vendor_id_payload_create() +{ + private_vendor_id_payload_t *this = malloc_thing(private_vendor_id_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (vendor_id_payload_t *)) destroy; + this->public.set_data = (void (*) (vendor_id_payload_t *,chunk_t)) set_data; + this->public.get_data_clone = (chunk_t (*) (vendor_id_payload_t *)) get_data_clone; + this->public.get_data = (chunk_t (*) (vendor_id_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH; + this->vendor_id_data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/vendor_id_payload.h b/src/charon/encoding/payloads/vendor_id_payload.h new file mode 100644 index 000000000..c7eebc155 --- /dev/null +++ b/src/charon/encoding/payloads/vendor_id_payload.h @@ -0,0 +1,104 @@ +/** + * @file vendor_id_payload.h + * + * @brief Interface of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef VENDOR_ID_PAYLOAD_H_ +#define VENDOR_ID_PAYLOAD_H_ + +typedef struct vendor_id_payload_t vendor_id_payload_t; + +#include <library.h> +#include <encoding/payloads/payload.h> + +/** + * Length of a VENDOR ID payload without the VID data in bytes. + * + * @ingroup payloads + */ +#define VENDOR_ID_PAYLOAD_HEADER_LENGTH 4 + + +/** + * @brief Class representing an IKEv2 VENDOR ID payload. + * + * The VENDOR ID payload format is described in RFC section 3.12. + * + * @b Constructors: + * - vendor_id_payload_create() + * + * @ingroup payloads + */ +struct vendor_id_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the VID data. + * + * Data are getting cloned. + * + * @param this calling vendor_id_payload_t object + * @param data VID data as chunk_t + */ + void (*set_data) (vendor_id_payload_t *this, chunk_t data); + + /** + * @brief Get the VID data. + * + * Returned data are a copy of the internal one. + * + * @param this calling vendor_id_payload_t object + * @return VID data as chunk_t + */ + chunk_t (*get_data_clone) (vendor_id_payload_t *this); + + /** + * @brief Get the VID data. + * + * Returned data are NOT copied. + * + * @param this calling vendor_id_payload_t object + * @return VID data as chunk_t + */ + chunk_t (*get_data) (vendor_id_payload_t *this); + + /** + * @brief Destroys an vendor_id_payload_t object. + * + * @param this vendor_id_payload_t object to destroy + */ + void (*destroy) (vendor_id_payload_t *this); +}; + +/** + * @brief Creates an empty vendor_id_payload_t object. + * + * @return vendor_id_payload_t object + * + * @ingroup payloads + */ +vendor_id_payload_t *vendor_id_payload_create(void); + + +#endif /* VENDOR_ID_PAYLOAD_H_ */ diff --git a/src/charon/network/packet.c b/src/charon/network/packet.c new file mode 100644 index 000000000..f2fa91569 --- /dev/null +++ b/src/charon/network/packet.c @@ -0,0 +1,168 @@ +/** + * @file packet.c + * + * @brief Implementation of packet_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "packet.h" + + +typedef struct private_packet_t private_packet_t; + +/** + * Private data of an packet_t object. + */ +struct private_packet_t { + + /** + * Public part of a packet_t object. + */ + packet_t public; + + /** + * source address + */ + host_t *source; + + /** + * destination address + */ + host_t *destination; + + /** + * message data + */ + chunk_t data; +}; + +/** + * Implements packet_t.get_source + */ +static void set_source(private_packet_t *this, host_t *source) +{ + DESTROY_IF(this->source); + this->source = source; +} + +/** + * Implements packet_t.set_destination + */ +static void set_destination(private_packet_t *this, host_t *destination) +{ + DESTROY_IF(this->destination); + this->destination = destination; +} + +/** + * Implements packet_t.get_source + */ +static host_t *get_source(private_packet_t *this) +{ + return this->source; +} + +/** + * Implements packet_t.get_destination + */ +static host_t *get_destination(private_packet_t *this) +{ + return this->destination; +} + +/** + * Implements packet_t.get_data + */ +static chunk_t get_data(private_packet_t *this) +{ + return this->data; +} + +/** + * Implements packet_t.set_data + */ +static void set_data(private_packet_t *this, chunk_t data) +{ + free(this->data.ptr); + this->data = data; +} + +/** + * Implements packet_t.destroy. + */ +static void destroy(private_packet_t *this) +{ + if (this->source != NULL) + { + this->source->destroy(this->source); + } + if (this->destination != NULL) + { + this->destination->destroy(this->destination); + } + free(this->data.ptr); + free(this); +} + +/** + * Implements packet_t.clone. + */ +static packet_t *clone_(private_packet_t *this) +{ + private_packet_t *other = (private_packet_t*)packet_create(); + + if (this->destination != NULL) + { + other->destination = this->destination->clone(this->destination); + } + if (this->source != NULL) + { + other->source = this->source->clone(this->source); + } + if (this->data.ptr != NULL) + { + other->data.ptr = clalloc(this->data.ptr,this->data.len); + other->data.len = this->data.len; + } + return &(other->public); +} + +/* + * Documented in header + */ +packet_t *packet_create(void) +{ + private_packet_t *this = malloc_thing(private_packet_t); + + this->public.set_data = (void(*) (packet_t *,chunk_t)) set_data; + this->public.get_data = (chunk_t(*) (packet_t *)) get_data; + this->public.set_source = (void(*) (packet_t *,host_t*)) set_source; + this->public.get_source = (host_t*(*) (packet_t *)) get_source; + this->public.set_destination = (void(*) (packet_t *,host_t*)) set_destination; + this->public.get_destination = (host_t*(*) (packet_t *)) get_destination; + this->public.clone = (packet_t*(*) (packet_t *))clone_; + this->public.destroy = (void(*) (packet_t *)) destroy; + + this->destination = NULL; + this->source = NULL; + this->data = chunk_empty; + + return &(this->public); +} diff --git a/src/charon/network/packet.h b/src/charon/network/packet.h new file mode 100644 index 000000000..acf953032 --- /dev/null +++ b/src/charon/network/packet.h @@ -0,0 +1,134 @@ +/** + * @file packet.h + * + * @brief Interface of packet_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef PACKET_H_ +#define PACKET_H_ + +typedef struct packet_t packet_t; + +#include <library.h> +#include <utils/host.h> + +/** + * @brief Abstraction of an UDP-Packet, contains data, sender and receiver. + * + * @b Constructors: + * - packet_create() + * + * @ingroup network + */ +struct packet_t { + + /** + * @brief Set the source address. + * + * Set host_t is now owned by packet_t, it will destroy + * it if necessary. + * + * @param this calling object + * @param source address to set as source + */ + void (*set_source) (packet_t *packet, host_t *source); + + /** + * @brief Set the destination address. + * + * Set host_t is now owned by packet_t, it will destroy + * it if necessary. + * + * @param this calling object + * @param source address to set as destination + */ + void (*set_destination) (packet_t *packet, host_t *destination); + + /** + * @brief Get the source address. + * + * Set host_t is still owned by packet_t, clone it + * if needed. + * + * @param this calling object + * @return source address + */ + host_t *(*get_source) (packet_t *packet); + + /** + * @brief Get the destination address. + * + * Set host_t is still owned by packet_t, clone it + * if needed. + * + * @param this calling object + * @return destination address + */ + host_t *(*get_destination) (packet_t *packet); + + /** + * @brief Get the data from the packet. + * + * The data pointed by the chunk is still owned + * by the packet. Clone it if needed. + * + * @param this calling object + * @return chunk containing the data + */ + chunk_t (*get_data) (packet_t *packet); + + /** + * @brief Set the data in the packet. + * + * Supplied chunk data is now owned by the + * packet. It will free it. + * + * @param this calling object + * @param data chunk with data to set + */ + void (*set_data) (packet_t *packet, chunk_t data); + + /** + * @brief Clones a packet_t object. + * + * @param packet calling object + * @param clone pointer to a packet_t object pointer where the new object is stored + */ + packet_t* (*clone) (packet_t *packet); + + /** + * @brief Destroy the packet, freeing contained data. + * + * @param packet packet to destroy + */ + void (*destroy) (packet_t *packet); +}; + +/** + * @brief create an empty packet + * + * @return packet_t object + * + * @ingroup network + */ +packet_t *packet_create(void); + + +#endif /*PACKET_H_*/ diff --git a/src/charon/network/socket.c b/src/charon/network/socket.c new file mode 100644 index 000000000..00ba22d5a --- /dev/null +++ b/src/charon/network/socket.c @@ -0,0 +1,755 @@ +/** + * @file socket.c + * + * @brief Implementation of socket_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <linux/ipsec.h> +#include <linux/filter.h> +#include <net/if.h> + +#include "socket.h" + +#include <daemon.h> + +/* constants for packet handling */ +#define IP_LEN sizeof(struct iphdr) +#define IP6_LEN sizeof(struct ip6_hdr) +#define UDP_LEN sizeof(struct udphdr) +#define MARKER_LEN sizeof(u_int32_t) + +/* offsets for packet handling */ +#define IP_PROTO_OFFSET 9 +#define IP6_PROTO_OFFSET 6 +#define IKE_VERSION_OFFSET 17 +#define IKE_LENGTH_OFFSET 24 + +/* from linux/in.h */ +#ifndef IP_IPSEC_POLICY +#define IP_IPSEC_POLICY 16 +#endif /*IP_IPSEC_POLICY*/ + +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif /*UDP_ENCAP*/ + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif /*UDP_ENCAP_ESPINUDP*/ + +/* needed for older kernel headers */ +#ifndef IPV6_2292PKTINFO +#define IPV6_2292PKTINFO 2 +#endif /*IPV6_2292PKTINFO*/ + +/* missing on uclibc */ +#ifndef IPV6_IPSEC_POLICY +#define IPV6_IPSEC_POLICY 34 +#endif /*IPV6_IPSEC_POLICY*/ + +typedef struct private_socket_t private_socket_t; + +/** + * Private data of an socket_t object + */ +struct private_socket_t{ + /** + * public functions + */ + socket_t public; + + /** + * regular port + */ + int port; + + /** + * port used for nat-t + */ + int natt_port; + + /** + * raw receiver socket for IPv4 + */ + int recv4; + + /** + * raw receiver socket for IPv6 + */ + int recv6; + + /** + * send socket on regular port for IPv4 + */ + int send4; + + /** + * send socket on regular port for IPv6 + */ + int send6; + + /** + * send socket on nat-t port for IPv4 + */ + int send4_natt; + + /** + * send socket on nat-t port for IPv6 + */ + int send6_natt; +}; + +/** + * implementation of socket_t.receive + */ +static status_t receiver(private_socket_t *this, packet_t **packet) +{ + char buffer[MAX_PACKET]; + chunk_t data; + packet_t *pkt; + struct udphdr *udp; + host_t *source = NULL, *dest = NULL; + int bytes_read = 0; + int data_offset, oldstate; + fd_set rfds; + + FD_ZERO(&rfds); + + if (this->recv4) + { + FD_SET(this->recv4, &rfds); + } + if (this->recv6) + { + FD_SET(this->recv6, &rfds); + } + + DBG2(DBG_NET, "waiting for data on raw sockets"); + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + if (select(max(this->recv4, this->recv6) + 1, &rfds, NULL, NULL, NULL) <= 0) + { + pthread_setcancelstate(oldstate, NULL); + return FAILED; + } + pthread_setcancelstate(oldstate, NULL); + + if (this->recv4 && FD_ISSET(this->recv4, &rfds)) + { + /* IPv4 raw sockets return the IP header. We read src/dest + * information directly from the raw header */ + struct iphdr *ip; + struct sockaddr_in src, dst; + + bytes_read = recv(this->recv4, buffer, MAX_PACKET, 0); + if (bytes_read < 0) + { + DBG1(DBG_NET, "error reading from IPv4 socket: %m"); + return FAILED; + } + DBG3(DBG_NET, "received IPv4 packet %b", buffer, bytes_read); + + /* read source/dest from raw IP/UDP header */ + if (bytes_read < IP_LEN + UDP_LEN + MARKER_LEN) + { + DBG1(DBG_NET, "received IPv4 packet too short (%d bytes)", + bytes_read); + return FAILED; + } + ip = (struct iphdr*) buffer; + udp = (struct udphdr*) (buffer + IP_LEN); + src.sin_family = AF_INET; + src.sin_addr.s_addr = ip->saddr; + src.sin_port = udp->source; + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = ip->daddr; + dst.sin_port = udp->dest; + source = host_create_from_sockaddr((sockaddr_t*)&src); + dest = host_create_from_sockaddr((sockaddr_t*)&dst); + + pkt = packet_create(); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest); + data_offset = IP_LEN + UDP_LEN; + /* remove non esp marker */ + if (dest->get_port(dest) == this->natt_port) + { + data_offset += MARKER_LEN; + } + /* fill in packet */ + data.len = bytes_read - data_offset; + data.ptr = malloc(data.len); + memcpy(data.ptr, buffer + data_offset, data.len); + pkt->set_data(pkt, data); + } + else if (this->recv6 && FD_ISSET(this->recv6, &rfds)) + { + /* IPv6 raw sockets return no IP header. We must query + * src/dest via socket options/ancillary data */ + struct msghdr msg; + struct cmsghdr *cmsgptr; + struct sockaddr_in6 src, dst; + struct iovec iov; + char ancillary[64]; + + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + bytes_read = recvmsg(this->recv6, &msg, 0); + if (bytes_read < 0) + { + DBG1(DBG_NET, "error reading from IPv6 socket: %m"); + return FAILED; + } + DBG3(DBG_NET, "received IPv6 packet %b", buffer, bytes_read); + + if (bytes_read < IP_LEN + UDP_LEN + MARKER_LEN) + { + DBG3(DBG_NET, "received IPv6 packet too short (%d bytes)", + bytes_read); + return FAILED; + } + + /* read ancillary data to get destination address */ + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) + { + if (cmsgptr->cmsg_len == 0) + { + DBG1(DBG_NET, "error reading IPv6 ancillary data"); + return FAILED; + } + if (cmsgptr->cmsg_level == SOL_IPV6 && + cmsgptr->cmsg_type == IPV6_2292PKTINFO) + { + struct in6_pktinfo *pktinfo; + pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsgptr); + + memset(&dst, 0, sizeof(dst)); + memcpy(&dst.sin6_addr, &pktinfo->ipi6_addr, sizeof(dst.sin6_addr)); + dst.sin6_family = AF_INET6; + udp = (struct udphdr*) (buffer); + dst.sin6_port = udp->dest; + src.sin6_port = udp->source; + dest = host_create_from_sockaddr((sockaddr_t*)&dst); + } + } + /* ancillary data missing? */ + if (dest == NULL) + { + DBG1(DBG_NET, "error reading IPv6 packet header"); + return FAILED; + } + + source = host_create_from_sockaddr((sockaddr_t*)&src); + + pkt = packet_create(); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest); + data_offset = UDP_LEN; + /* remove non esp marker */ + if (dest->get_port(dest) == this->natt_port) + { + data_offset += MARKER_LEN; + } + /* fill in packet */ + data.len = bytes_read - data_offset; + data.ptr = malloc(data.len); + memcpy(data.ptr, buffer + data_offset, data.len); + pkt->set_data(pkt, data); + } + else + { + /* oops, shouldn't happen */ + return FAILED; + } + + /* return packet */ + *packet = pkt; + return SUCCESS; +} + +/** + * implementation of socket_t.send + */ +status_t sender(private_socket_t *this, packet_t *packet) +{ + int sport, skt, family; + ssize_t bytes_sent; + chunk_t data, marked; + host_t *src, *dst; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + data = packet->get_data(packet); + + DBG2(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + /* send data */ + sport = src->get_port(src); + family = dst->get_family(dst); + if (sport == this->port) + { + if (family == AF_INET) + { + skt = this->send4; + } + else + { + skt = this->send6; + } + } + else if (sport == this->natt_port) + { + if (family == AF_INET) + { + skt = this->send4_natt; + } + else + { + skt = this->send6_natt; + } + /* NAT keepalives without marker */ + if (data.len != 1 || data.ptr[0] != 0xFF) + { + /* add non esp marker to packet */ + if (data.len > MAX_PACKET - MARKER_LEN) + { + DBG1(DBG_NET, "unable to send packet: it's too big (%d bytes)", + data.len); + return FAILED; + } + marked = chunk_alloc(data.len + MARKER_LEN); + memset(marked.ptr, 0, MARKER_LEN); + memcpy(marked.ptr + MARKER_LEN, data.ptr, data.len); + /* let the packet do the clean up for us */ + packet->set_data(packet, marked); + data = marked; + } + } + else + { + DBG1(DBG_NET, "unable to locate a send socket for port %d", sport); + return FAILED; + } + + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_name = dst->get_sockaddr(dst);; + msg.msg_namelen = *dst->get_sockaddr_len(dst); + iov.iov_base = data.ptr; + iov.iov_len = data.len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + if (!dst->is_anyaddr(dst)) + { + if (family == AF_INET) + { + char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + struct in_pktinfo *pktinfo; + struct sockaddr_in *sin; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + memset(pktinfo, 0, sizeof(struct in_pktinfo)); + sin = (struct sockaddr_in*)src->get_sockaddr(src); + memcpy(&pktinfo->ipi_spec_dst, &sin->sin_addr, sizeof(struct in_addr)); + } + else + { + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 *sin; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_IPV6; + cmsg->cmsg_type = IPV6_2292PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memset(pktinfo, 0, sizeof(struct in6_pktinfo)); + sin = (struct sockaddr_in6*)src->get_sockaddr(src); + memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr)); + } + } + + bytes_sent = sendmsg(skt, &msg, 0); + + if (bytes_sent != data.len) + { + DBG1(DBG_NET, "error writing to socket: %m"); + return FAILED; + } + return SUCCESS; +} + +/** + * open a socket to send packets + */ +static int open_send_socket(private_socket_t *this, int family, u_int16_t port) +{ + int on = TRUE; + int type = UDP_ENCAP_ESPINUDP; + struct sockaddr_storage addr; + u_int sol, ipsec_policy; + struct sadb_x_policy policy; + int skt; + + memset(&addr, 0, sizeof(addr)); + /* precalculate constants depending on address family */ + switch (family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(port); + sol = SOL_IP; + ipsec_policy = IP_IPSEC_POLICY; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any)); + sin6->sin6_port = htons(port); + sol = SOL_IPV6; + ipsec_policy = IPV6_IPSEC_POLICY; + break; + } + default: + return 0; + } + + skt = socket(family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 0) + { + DBG1(DBG_NET, "could not open send socket: %m"); + return 0; + } + + if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + { + DBG1(DBG_NET, "unable to set SO_REUSEADDR on send socket: %m"); + close(skt); + return 0; + } + + /* bypass outgoung IKE traffic on send socket */ + memset(&policy, 0, sizeof(policy)); + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; + + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on send socket: %m"); + close(skt); + return 0; + } + + /* We don't receive packets on the send socket, but we need a INBOUND policy. + * Otherwise, UDP decapsulation does not work!!! */ + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on send socket: %m"); + close(skt); + return 0; + } + + /* bind the send socket */ + if (bind(skt, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + DBG1(DBG_NET, "unable to bind send socket: %m"); + close(skt); + return 0; + } + + if (family == AF_INET) + { + /* enable UDP decapsulation globally, only for one socket needed */ + if (setsockopt(skt, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_NET, "unable to set UDP_ENCAP: %m; NAT-T may fail"); + } + } + + return skt; +} + +/** + * open a socket to receive packets + */ +static int open_recv_socket(private_socket_t *this, int family) +{ + int skt; + int on = TRUE; + u_int proto_offset, ip_len, sol, ipsec_policy, udp_header, ike_header; + struct sadb_x_policy policy; + + /* precalculate constants depending on address family */ + switch (family) + { + case AF_INET: + proto_offset = IP_PROTO_OFFSET; + ip_len = IP_LEN; + sol = SOL_IP; + ipsec_policy = IP_IPSEC_POLICY; + break; + case AF_INET6: + proto_offset = IP6_PROTO_OFFSET; + ip_len = 0; /* IPv6 raw sockets contain no IP header */ + sol = SOL_IPV6; + ipsec_policy = IPV6_IPSEC_POLICY; + break; + default: + return 0; + } + udp_header = ip_len; + ike_header = ip_len + UDP_LEN; + + /* This filter code filters out all non-IKEv2 traffic on + * a SOCK_RAW IP_PROTP_UDP socket. Handling of other + * IKE versions is done in pluto. + */ + struct sock_filter ikev2_filter_code[] = + { + /* Destination Port must be either port or natt_port */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, udp_header + 2), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->port, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->natt_port, 5, 12), + /* port */ + /* IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ike_header + IKE_VERSION_OFFSET), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 10), + /* packet length is length in IKEv2 header + ip header + udp header */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header + IKE_LENGTH_OFFSET), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, ip_len + UDP_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* natt_port */ + /* nat-t: check for marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 5), + /* nat-t: IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ike_header + MARKER_LEN + IKE_VERSION_OFFSET), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 3), + /* nat-t: packet length is length in IKEv2 header + ip header + udp header + non esp marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header + MARKER_LEN + IKE_LENGTH_OFFSET), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, ip_len + UDP_LEN + MARKER_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* packet doesn't match, ignore */ + BPF_STMT(BPF_RET+BPF_K, 0), + }; + + /* Filter struct to use with setsockopt */ + struct sock_fprog ikev2_filter = { + sizeof(ikev2_filter_code) / sizeof(struct sock_filter), + ikev2_filter_code + }; + + /* set up a raw socket */ + skt = socket(family, SOCK_RAW, IPPROTO_UDP); + if (skt < 0) + { + DBG1(DBG_NET, "unable to create raw socket: %m"); + return 0; + } + + if (setsockopt(skt, SOL_SOCKET, SO_ATTACH_FILTER, + &ikev2_filter, sizeof(ikev2_filter)) < 0) + { + DBG1(DBG_NET, "unable to attach IKEv2 filter to raw socket: %m"); + close(skt); + return 0; + } + + if (family == AF_INET6 && + /* we use IPV6_2292PKTINFO, as IPV6_PKTINFO is defined as + * 2 or 50 depending on kernel header version */ + setsockopt(skt, sol, IPV6_2292PKTINFO, &on, sizeof(on)) < 0) + { + DBG1(DBG_NET, "unable to set IPV6_PKTINFO on raw socket: %m"); + close(skt); + return 0; + } + + /* bypass incomining IKE traffic on this socket */ + memset(&policy, 0, sizeof(policy)); + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on raw socket: %m"); + close(skt); + return 0; + } + + return skt; +} + +/** + * implementation of socket_t.destroy + */ +static void destroy(private_socket_t *this) +{ + if (this->recv4) + { + close(this->recv4); + } + if (this->recv6) + { + close(this->recv6); + } + if (this->send4) + { + close(this->send4); + } + if (this->send6) + { + close(this->send6); + } + if (this->send4_natt) + { + close(this->send4_natt); + } + if (this->send6_natt) + { + close(this->send6_natt); + } + free(this); +} + +/* + * See header for description + */ +socket_t *socket_create(u_int16_t port, u_int16_t natt_port) +{ + private_socket_t *this = malloc_thing(private_socket_t); + + /* public functions */ + this->public.send = (status_t(*)(socket_t*, packet_t*))sender; + this->public.receive = (status_t(*)(socket_t*, packet_t**))receiver; + this->public.destroy = (void(*)(socket_t*)) destroy; + + this->port = port; + this->natt_port = natt_port; + this->recv4 = 0; + this->recv6 = 0; + this->send4 = 0; + this->send6 = 0; + this->send4_natt = 0; + this->send6_natt = 0; + + this->recv4 = open_recv_socket(this, AF_INET); + if (this->recv4 == 0) + { + DBG1(DBG_NET, "could not open IPv4 receive socket, IPv4 disabled"); + } + else + { + this->send4 = open_send_socket(this, AF_INET, this->port); + if (this->send4 == 0) + { + DBG1(DBG_NET, "could not open IPv4 send socket, IPv4 disabled"); + close(this->recv4); + } + else + { + this->send4_natt = open_send_socket(this, AF_INET, this->natt_port); + if (this->send4_natt == 0) + { + DBG1(DBG_NET, "could not open IPv4 NAT-T send socket"); + } + } + } + + this->recv6 = open_recv_socket(this, AF_INET6); + if (this->recv6 == 0) + { + DBG1(DBG_NET, "could not open IPv6 receive socket, IPv6 disabled"); + } + else + { + this->send6 = open_send_socket(this, AF_INET6, this->port); + if (this->send6 == 0) + { + DBG1(DBG_NET, "could not open IPv6 send socket, IPv6 disabled"); + close(this->recv6); + } + else + { + this->send6_natt = open_send_socket(this, AF_INET6, this->natt_port); + if (this->send6_natt == 0) + { + DBG1(DBG_NET, "could not open IPv6 NAT-T send socket"); + } + } + } + + if (!(this->send4 || this->send6) || !(this->recv4 || this->recv6)) + { + DBG1(DBG_NET, "could not create any sockets"); + destroy(this); + charon->kill(charon, "socket initialization failed"); + } + + return (socket_t*)this; +} diff --git a/src/charon/network/socket.h b/src/charon/network/socket.h new file mode 100644 index 000000000..ef60fa7b6 --- /dev/null +++ b/src/charon/network/socket.h @@ -0,0 +1,112 @@ +/** + * @file socket.h + * + * @brief Interface for socket_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef SOCKET_H_ +#define SOCKET_H_ + +typedef struct socket_t socket_t; + +#include <library.h> +#include <network/packet.h> +#include <utils/host.h> +#include <utils/linked_list.h> + +/** + * @brief Maximum size of a packet. + * + * 3000 Bytes should be sufficient, see IKEv2 RFC. + * + * @ingroup network + */ +#define MAX_PACKET 3000 + +/** + * @brief Abstraction of all sockets (IPv6/IPv6 send/receive). + * + * All available sockets are bound and the receive function + * reads from them. To allow binding of other daemons (pluto) to + * UDP/500, this implementation uses RAW sockets. An installed + * "Linux socket filter" filters out all non-IKEv2 traffic and handles + * just IKEv2 messages. An other daemon (pluto) must handle all traffic + * seperatly, e.g. ignore IKEv2 traffic, since charon handles that. + * + * @b Constructors: + * - socket_create() + * + * @ingroup network + */ +struct socket_t { + + /** + * @brief Receive a packet. + * + * Reads a packet from the socket and sets source/dest + * appropriately. + * + * @param this socket_t object to work on + * @param packet pinter gets address from allocated packet_t + * @return + * - SUCCESS when packet successfully received + * - FAILED when unable to receive + */ + status_t (*receive) (socket_t *this, packet_t **packet); + + /** + * @brief Send a packet. + * + * Sends a packet to the net using destination from the packet. + * Packet is sent using default routing mechanisms, thus the + * source address in packet is ignored. + * + * @param this socket_t object to work on + * @param packet[out] packet_t to send + * @return + * - SUCCESS when packet successfully sent + * - FAILED when unable to send + */ + status_t (*send) (socket_t *this, packet_t *packet); + + /** + * @brief Destroy sockets. + * + * close sockets and destroy socket_t object + * + * @param this socket_t to destroy + */ + void (*destroy) (socket_t *this); +}; + +/** + * @brief Create a socket_t, wich binds multiple sockets. + * + * @param port port to bind socket to + * @param natt_port port to float to in NAT-T + * @return socket_t object + * + * @ingroup network + */ +socket_t *socket_create(u_int16_t port, u_int16_t natt_port); + + +#endif /*SOCKET_H_*/ diff --git a/src/charon/queues/event_queue.c b/src/charon/queues/event_queue.c new file mode 100644 index 000000000..40bcb1ed8 --- /dev/null +++ b/src/charon/queues/event_queue.c @@ -0,0 +1,290 @@ +/** + * @file event_queue.c + * + * @brief Implementation of event_queue_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <pthread.h> +#include <stdlib.h> + +#include "event_queue.h" + +#include <library.h> +#include <utils/linked_list.h> + + + +typedef struct event_t event_t; + +/** + * Event containing a job and a schedule time + */ +struct event_t { + /** + * Time to fire the event. + */ + timeval_t time; + + /** + * Every event has its assigned job. + */ + job_t * job; +}; + +/** + * destroy an event and its job + */ +static void event_destroy(event_t *event) +{ + event->job->destroy(event->job); + free(event); +} + +typedef struct private_event_queue_t private_event_queue_t; + +/** + * Private Variables and Functions of event_queue_t class. + */ +struct private_event_queue_t { + /** + * Public part. + */ + event_queue_t public; + + /** + * The events are stored in a linked list of type linked_list_t. + */ + linked_list_t *list; + + /** + * Access to linked_list is locked through this mutex. + */ + pthread_mutex_t mutex; + + /** + * If the queue is empty or an event has not to be fired + * a thread has to wait. + * + * This condvar is used to wake up such a thread. + */ + pthread_cond_t condvar; +}; + +/** + * Returns the difference of to timeval structs in milliseconds + */ +static long time_difference(struct timeval *end_time, struct timeval *start_time) +{ + time_t s; + suseconds_t us; + + s = (end_time->tv_sec - start_time->tv_sec); + us = (end_time->tv_usec - start_time->tv_usec); + return ((s * 1000) + us/1000); +} + +/** + * Implements event_queue_t.get_count + */ +static int get_count(private_event_queue_t *this) +{ + int count; + pthread_mutex_lock(&(this->mutex)); + count = this->list->get_count(this->list); + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * Implements event_queue_t.get + */ +static job_t *get(private_event_queue_t *this) +{ + timespec_t timeout; + timeval_t current_time; + event_t * next_event; + job_t *job; + int oldstate; + + pthread_mutex_lock(&(this->mutex)); + + while (TRUE) + { + while(this->list->get_count(this->list) == 0) + { + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_wait( &(this->condvar), &(this->mutex)); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + + this->list->get_first(this->list, (void **)&next_event); + + gettimeofday(¤t_time, NULL); + long difference = time_difference(¤t_time,&(next_event->time)); + if (difference <= 0) + { + timeout.tv_sec = next_event->time.tv_sec; + timeout.tv_nsec = next_event->time.tv_usec * 1000; + + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_timedwait(&(this->condvar), &(this->mutex), &timeout); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + else + { + /* event available */ + this->list->remove_first(this->list, (void **)&next_event); + job = next_event->job; + free(next_event); + break; + } + } + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); + + return job; +} + +/** + * Implements function add_absolute of event_queue_t. + * See #event_queue_s.add_absolute for description. + */ +static void add_absolute(private_event_queue_t *this, job_t *job, timeval_t time) +{ + event_t *event; + event_t *current_event; + iterator_t *iterator; + + /* create event */ + event = malloc_thing(event_t); + event->time = time; + event->job = job; + + pthread_mutex_lock(&(this->mutex)); + + /* while just used to break out */ + while(TRUE) + { + if (this->list->get_count(this->list) == 0) + { + this->list->insert_first(this->list,event); + break; + } + + /* check last entry */ + this->list->get_last(this->list,(void **) ¤t_event); + + if (time_difference(&(event->time), &(current_event->time)) >= 0) + { + /* my event has to be fired after the last event in list */ + this->list->insert_last(this->list,event); + break; + } + + /* check first entry */ + this->list->get_first(this->list,(void **) ¤t_event); + + if (time_difference(&(event->time), &(current_event->time)) < 0) + { + /* my event has to be fired before the first event in list */ + this->list->insert_first(this->list,event); + break; + } + + iterator = this->list->create_iterator(this->list,TRUE); + iterator->iterate(iterator, (void**)¤t_event); + /* first element has not to be checked (already done) */ + while(iterator->iterate(iterator, (void**)¤t_event)) + { + if (time_difference(&(event->time), &(current_event->time)) <= 0) + { + /* my event has to be fired before the current event in list */ + iterator->insert_before(iterator,event); + break; + } + } + iterator->destroy(iterator); + break; + } + + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implements event_queue_t.add_relative. + */ +static void add_relative(event_queue_t *this, job_t *job, u_int32_t ms) +{ + timeval_t current_time; + timeval_t time; + + time_t s = ms / 1000; + suseconds_t us = (ms - s * 1000) * 1000; + + gettimeofday(¤t_time, NULL); + + time.tv_usec = (current_time.tv_usec + us) % 1000000; + time.tv_sec = current_time.tv_sec + (current_time.tv_usec + us)/1000000 + s; + + this->add_absolute(this, job, time); +} + + +/** + * Implements event_queue_t.destroy. + */ +static void event_queue_destroy(private_event_queue_t *this) +{ + this->list->destroy_function(this->list, (void*)event_destroy); + free(this); +} + +/* + * Documented in header + */ +event_queue_t *event_queue_create() +{ + private_event_queue_t *this = malloc_thing(private_event_queue_t); + + this->public.get_count = (int (*) (event_queue_t *event_queue)) get_count; + this->public.get = (job_t *(*) (event_queue_t *event_queue)) get; + this->public.add_absolute = (void (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute; + this->public.add_relative = (void (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative; + this->public.destroy = (void (*) (event_queue_t *event_queue)) event_queue_destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + pthread_cond_init(&(this->condvar), NULL); + + return (&this->public); +} diff --git a/src/charon/queues/event_queue.h b/src/charon/queues/event_queue.h new file mode 100644 index 000000000..cd275123b --- /dev/null +++ b/src/charon/queues/event_queue.h @@ -0,0 +1,118 @@ +/** + * @file event_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef EVENT_QUEUE_H_ +#define EVENT_QUEUE_H_ + +typedef struct event_queue_t event_queue_t; + +#include <sys/time.h> + +#include <library.h> +#include <queues/jobs/job.h> + +/** + * @brief Event-Queue used to store timed events. + * + * Added events are sorted. The get method blocks until + * the time is elapsed to process the next event. The get + * method is called from the scheduler_t thread, which + * will add the jobs to to job_queue_t for further processing. + * + * Although the event-queue is based on a linked_list_t + * all access functions are thread-save implemented. + * + * @b Constructors: + * - event_queue_create() + * + * @ingroup queues + */ +struct event_queue_t { + + /** + * @brief Returns number of events in queue. + * + * @param event_queue calling object + * @return number of events in queue + */ + int (*get_count) (event_queue_t *event_queue); + + /** + * @brief Get the next job from the event-queue. + * + * If no event is pending, this function blocks until a job can be returned. + * + * @param event_queue calling object + * @param[out] job pointer to a job pointer where to job is returned to + * @return next job + */ + job_t *(*get) (event_queue_t *event_queue); + + /** + * @brief Adds a event to the queue, using a relative time. + * + * This function is non blocking and adds a job_t at a specific time to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param event_queue calling object + * @param[in] job job to add to the queue (job is not copied) + * @param[in] time relative time, when the event has to get fired + */ + void (*add_relative) (event_queue_t *event_queue, job_t *job, u_int32_t ms); + + /** + * @brief Adds a event to the queue, using an absolute time. + * + * This function is non blocking and adds a job_t at a specific time to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param event_queue calling object + * @param[in] job job to add to the queue (job is not copied) + * @param[in] time absolute time, when the event has to get fired + */ + void (*add_absolute) (event_queue_t *event_queue, job_t *job, timeval_t time); + + /** + * @brief Destroys a event_queue object. + * + * @warning The caller of this function has to make sure + * that no thread is going to add or get an event from the event_queue + * after calling this function. + * + * @param event_queue calling object + */ + void (*destroy) (event_queue_t *event_queue); +}; + +/** + * @brief Creates an empty event_queue. + * + * @returns event_queue_t object + * + * @ingroup queues + */ +event_queue_t *event_queue_create(void); + +#endif /*EVENT_QUEUE_H_*/ diff --git a/src/charon/queues/job_queue.c b/src/charon/queues/job_queue.c new file mode 100644 index 000000000..2310ca6ff --- /dev/null +++ b/src/charon/queues/job_queue.c @@ -0,0 +1,139 @@ +/** + * @file job_queue.c + * + * @brief Implementation of job_queue_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <pthread.h> + +#include "job_queue.h" + +#include <utils/linked_list.h> + + +typedef struct private_job_queue_t private_job_queue_t; + +/** + * @brief Private Variables and Functions of job_queue class + * + */ +struct private_job_queue_t { + + /** + * public members + */ + job_queue_t public; + + /** + * The jobs are stored in a linked list + */ + linked_list_t *list; + + /** + * access to linked_list is locked through this mutex + */ + pthread_mutex_t mutex; + + /** + * If the queue is empty a thread has to wait + * This condvar is used to wake up such a thread + */ + pthread_cond_t condvar; +}; + + +/** + * implements job_queue_t.get_count + */ +static int get_count(private_job_queue_t *this) +{ + int count; + pthread_mutex_lock(&(this->mutex)); + count = this->list->get_count(this->list); + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * implements job_queue_t.get + */ +static job_t *get(private_job_queue_t *this) +{ + int oldstate; + job_t *job; + pthread_mutex_lock(&(this->mutex)); + /* go to wait while no jobs available */ + while(this->list->get_count(this->list) == 0) + { + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_wait( &(this->condvar), &(this->mutex)); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + this->list->remove_first(this->list, (void **)&job); + pthread_mutex_unlock(&(this->mutex)); + return job; +} + +/** + * implements function job_queue_t.add + */ +static void add(private_job_queue_t *this, job_t *job) +{ + pthread_mutex_lock(&(this->mutex)); + this->list->insert_last(this->list,job); + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * implements job_queue_t.destroy + */ +static void job_queue_destroy (private_job_queue_t *this) +{ + this->list->destroy_offset(this->list, offsetof(job_t, destroy)); + free(this); +} + +/* + * + * Documented in header + */ +job_queue_t *job_queue_create(void) +{ + private_job_queue_t *this = malloc_thing(private_job_queue_t); + + this->public.get_count = (int(*)(job_queue_t*))get_count; + this->public.get = (job_t*(*)(job_queue_t*))get; + this->public.add = (void(*)(job_queue_t*, job_t*))add; + this->public.destroy = (void(*)(job_queue_t*))job_queue_destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + pthread_cond_init(&(this->condvar), NULL); + + return (&this->public); +} diff --git a/src/charon/queues/job_queue.h b/src/charon/queues/job_queue.h new file mode 100644 index 000000000..c971ba514 --- /dev/null +++ b/src/charon/queues/job_queue.h @@ -0,0 +1,100 @@ +/** + * @file job_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef JOB_QUEUE_H_ +#define JOB_QUEUE_H_ + +typedef struct job_queue_t job_queue_t; + +#include <library.h> +#include <queues/jobs/job.h> + +/** + * @brief The job queue stores jobs, which will be processed by the thread_pool_t. + * + * Jobs are added from various sources, from the threads and + * from the event_queue_t. + * Although the job-queue is based on a linked_list_t + * all access functions are thread-save implemented. + * + * @b Constructors: + * - job_queue_create() + * + * @ingroup queues + */ +struct job_queue_t { + + /** + * @brief Returns number of jobs in queue. + * + * @param job_queue_t calling object + * @returns number of items in queue + */ + int (*get_count) (job_queue_t *job_queue); + + /** + * @brief Get the next job from the queue. + * + * If the queue is empty, this function blocks until a job can be returned. + * After using, the returned job has to get destroyed by the caller. + * + * @param job_queue_t calling object + * @param[out] job pointer to a job pointer where to job is returned to + * @return next job + */ + job_t *(*get) (job_queue_t *job_queue); + + /** + * @brief Adds a job to the queue. + * + * This function is non blocking and adds a job_t to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param job_queue_t calling object + * @param job job to add to the queue (job is not copied) + */ + void (*add) (job_queue_t *job_queue, job_t *job); + + /** + * @brief Destroys a job_queue object. + * + * @warning The caller of this function has to make sure + * that no thread is going to add or get a job from the job_queue + * after calling this function. + * + * @param job_queue_t calling object + */ + void (*destroy) (job_queue_t *job_queue); +}; + +/** + * @brief Creates an empty job_queue. + * + * @return job_queue_t object + * + * @ingroup queues + */ +job_queue_t *job_queue_create(void); + +#endif /*JOB_QUEUE_H_*/ diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c new file mode 100644 index 000000000..b4ffb258d --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.c @@ -0,0 +1,98 @@ +/** + * @file acquire_job.c + * + * @brief Implementation of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include "acquire_job.h" + +#include <daemon.h> + + +typedef struct private_acquire_job_t private_acquire_job_t; + +/** + * Private data of an acquire_job_t object. + */ +struct private_acquire_job_t { + /** + * Public acquire_job_t interface. + */ + acquire_job_t public; + + /** + * reqid of the child to rekey + */ + u_int32_t reqid; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_acquire_job_t *this) +{ + return ACQUIRE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_acquire_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for acquiring", + this->reqid); + return DESTROY_ME; + } + ike_sa->acquire(ike_sa, this->reqid); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_acquire_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +acquire_job_t *acquire_job_create(u_int32_t reqid) +{ + private_acquire_job_t *this = malloc_thing(private_acquire_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/acquire_job.h b/src/charon/queues/jobs/acquire_job.h new file mode 100644 index 000000000..54f1b9b5b --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.h @@ -0,0 +1,60 @@ +/** + * @file acquire_job.h + * + * @brief Interface of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef ACQUIRE_JOB_H_ +#define ACQUIRE_JOB_H_ + +typedef struct acquire_job_t acquire_job_t; + +#include <library.h> +#include <queues/jobs/job.h> + +/** + * @brief Class representing an ACQUIRE Job. + * + * This job initiates a CHILD SA on kernel request. + * + * @b Constructors: + * - acquire_job_create() + * + * @ingroup jobs + */ +struct acquire_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ACQUIRE. + * + * We use the reqid to find the routed CHILD_SA. + * + * @param reqid reqid of the CHILD_SA to acquire + * @return acquire_job_t object + * + * @ingroup jobs + */ +acquire_job_t *acquire_job_create(u_int32_t reqid); + +#endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c new file mode 100644 index 000000000..f694696b0 --- /dev/null +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -0,0 +1,113 @@ +/** + * @file delete_child_sa_job.c + * + * @brief Implementation of delete_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include "delete_child_sa_job.h" + +#include <daemon.h> + + +typedef struct private_delete_child_sa_job_t private_delete_child_sa_job_t; + +/** + * Private data of an delete_child_sa_job_t object. + */ +struct private_delete_child_sa_job_t { + /** + + * Public delete_child_sa_job_t interface. + */ + delete_child_sa_job_t public; + + /** + * reqid of the CHILD_SA + */ + u_int32_t reqid; + + /** + * protocol of the CHILD_SA (ESP/AH) + */ + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_delete_child_sa_job_t *this) +{ + return DELETE_CHILD_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_delete_child_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG1(DBG_JOB, "CHILD_SA with reqid %d not found for delete", + this->reqid); + return DESTROY_ME; + } + ike_sa->delete_child_sa(ike_sa, this->protocol, this->spi); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_delete_child_sa_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) +{ + private_delete_child_sa_job_t *this = malloc_thing(private_delete_child_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/delete_child_sa_job.h b/src/charon/queues/jobs/delete_child_sa_job.h new file mode 100644 index 000000000..9c2e4fa4d --- /dev/null +++ b/src/charon/queues/jobs/delete_child_sa_job.h @@ -0,0 +1,68 @@ +/** + * @file delete_child_sa_job.h + * + * @brief Interface of delete_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef DELETE_CHILD_SA_JOB_H_ +#define DELETE_CHILD_SA_JOB_H_ + +typedef struct delete_child_sa_job_t delete_child_sa_job_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <queues/jobs/job.h> +#include <config/proposal.h> + + +/** + * @brief Class representing an DELETE_CHILD_SA Job. + * + * This job initiates the delete of a CHILD SA. + * + * @b Constructors: + * - delete_child_sa_job_create() + * + * @ingroup jobs + */ +struct delete_child_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type DELETE_CHILD_SA. + * + * The CHILD_SA is identified by its reqid, protocol (AH/ESP) and its + * inbound SPI. + * + * @param reqid reqid of the CHILD_SA, as used in kernel + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA + * @return delete_child_sa_job_t object + * + * @ingroup jobs + */ +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi); + +#endif /* DELETE_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/delete_ike_sa_job.c b/src/charon/queues/jobs/delete_ike_sa_job.c new file mode 100644 index 000000000..706155aa6 --- /dev/null +++ b/src/charon/queues/jobs/delete_ike_sa_job.c @@ -0,0 +1,126 @@ +/** + * @file delete_ike_sa_job.c + * + * @brief Implementation of delete_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "delete_ike_sa_job.h" + +#include <daemon.h> + +typedef struct private_delete_ike_sa_job_t private_delete_ike_sa_job_t; + +/** + * Private data of an delete_ike_sa_job_t Object + */ +struct private_delete_ike_sa_job_t { + /** + * public delete_ike_sa_job_t interface + */ + delete_ike_sa_job_t public; + + /** + * ID of the ike_sa to delete + */ + ike_sa_id_t *ike_sa_id; + + /** + * Should the IKE_SA be deleted if it is in ESTABLISHED state? + */ + bool delete_if_established; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_delete_ike_sa_job_t *this) +{ + return DELETE_IKE_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_delete_ike_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (this->delete_if_established) + { + if (ike_sa->delete(ike_sa) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + else + { + /* destroy only if not ESTABLISHED */ + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + else + { + DBG1(DBG_JOB, "deleting half open IKE_SA after timeout"); + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_delete_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established) +{ + private_delete_ike_sa_job_t *this = malloc_thing(private_delete_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t *)) destroy;; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->delete_if_established = delete_if_established; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/delete_ike_sa_job.h b/src/charon/queues/jobs/delete_ike_sa_job.h new file mode 100644 index 000000000..43701a354 --- /dev/null +++ b/src/charon/queues/jobs/delete_ike_sa_job.h @@ -0,0 +1,66 @@ +/** + * @file delete_ike_sa_job.h + * + * @brief Interface of delete_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef DELETE_IKE_SA_JOB_H_ +#define DELETE_IKE_SA_JOB_H_ + +typedef struct delete_ike_sa_job_t delete_ike_sa_job_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <queues/jobs/job.h> + + +/** + * @brief Class representing an DELETE_IKE_SA Job. + * + * This job is responsible for deleting established or half open IKE_SAs. + * A half open IKE_SA is every IKE_SA which hasn't reache the SA_ESTABLISHED + * state. + * + * @b Constructors: + * - delete_ike_sa_job_create() + * + * @ingroup jobs + */ +struct delete_ike_sa_job_t { + + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type DELETE_IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to delete + * @param delete_if_established should the IKE_SA be deleted if it is established? + * @return created delete_ike_sa_job_t object + * + * @ingroup jobs + */ +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established); + +#endif /* DELETE_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/initiate_job.c b/src/charon/queues/jobs/initiate_job.c new file mode 100644 index 000000000..af50663d6 --- /dev/null +++ b/src/charon/queues/jobs/initiate_job.c @@ -0,0 +1,112 @@ +/** + * @file initiate_job.c + * + * @brief Implementation of initiate_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include <stdlib.h> + +#include "initiate_job.h" + +#include <daemon.h> + +typedef struct private_initiate_job_t private_initiate_job_t; + +/** + * Private data of an initiate_job_t Object + */ +struct private_initiate_job_t { + /** + * public initiate_job_t interface + */ + initiate_job_t public; + + /** + * associated connection to initiate + */ + connection_t *connection; + + /** + * associated policy to initiate + */ + policy_t *policy; +}; + +/** + * Implements initiate_job_t.get_type. + */ +static job_type_t get_type(private_initiate_job_t *this) +{ + return INITIATE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_initiate_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); + + if (ike_sa->initiate(ike_sa, this->connection, this->policy) != SUCCESS) + { + DBG1(DBG_JOB, "initiation failed, going to delete IKE_SA"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_initiate_job_t *this) +{ + this->connection->destroy(this->connection); + this->policy->destroy(this->policy); + free(this); +} + +/* + * Described in header + */ +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy) +{ + private_initiate_job_t *this = malloc_thing(private_initiate_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->connection = connection; + this->policy = policy; + + return &this->public; +} diff --git a/src/charon/queues/jobs/initiate_job.h b/src/charon/queues/jobs/initiate_job.h new file mode 100644 index 000000000..af1dd9ece --- /dev/null +++ b/src/charon/queues/jobs/initiate_job.h @@ -0,0 +1,61 @@ +/** + * @file initiate_job.h + * + * @brief Interface of initiate_job_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef INITIATE_IKE_SA_JOB_H_ +#define INITIATE_IKE_SA_JOB_H_ + +typedef struct initiate_job_t initiate_job_t; + +#include <library.h> +#include <queues/jobs/job.h> +#include <config/connections/connection.h> +#include <config/policies/policy.h> + +/** + * @brief Class representing an INITIATE_IKE_SA Job. + * + * This job is created if an IKE_SA should be iniated. + * + * @b Constructors: + * - initiate_job_create() + * + * @ingroup jobs + */ +struct initiate_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type INITIATE_IKE_SA. + * + * @param connection connection_t to initialize + * @param policy policy to set up + * @return initiate_job_t object + * + * @ingroup jobs + */ +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy); + +#endif /*INITIATE_IKE_SA_JOB_H_*/ diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c new file mode 100644 index 000000000..d32d1bc61 --- /dev/null +++ b/src/charon/queues/jobs/job.c @@ -0,0 +1,39 @@ +/** + * @file job.c + * + * @brief Interface additions to job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "job.h" + +ENUM(job_type_names, PROCESS_MESSAGE, SEND_DPD, + "PROCESS_MESSAGE", + "RETRANSMIT", + "INITIATE", + "ROUTE", + "ACQUIRE", + "DELETE_IKE_SA", + "DELETE_CHILD_SA", + "REKEY_CHILD_SA", + "REKEY_IKE_SA", + "SEND_KEEPALIVE", + "SEND_DPD", +); diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h new file mode 100644 index 000000000..28632672d --- /dev/null +++ b/src/charon/queues/jobs/job.h @@ -0,0 +1,165 @@ +/** + * @file job.h + * + * @brief Interface job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef JOB_H_ +#define JOB_H_ + +typedef enum job_type_t job_type_t; +typedef struct job_t job_t; + +#include <library.h> + +/** + * @brief Definition of the various job types. + * + * @ingroup jobs + */ +enum job_type_t { + /** + * Process an incoming IKEv2-Message. + * + * Job is implemented in class process_message_job_t + */ + PROCESS_MESSAGE, + + /** + * Retransmit an IKEv2-Message. + * + * Job is implemented in class retransmit_job_t + */ + RETRANSMIT, + + /** + * Set up a CHILD_SA, optional with an IKE_SA. + * + * Job is implemented in class initiate_job_t + */ + INITIATE, + + /** + * Install SPD entries. + * + * Job is implemented in class route_job_t + */ + ROUTE, + + /** + * React on a acquire message from the kernel (e.g. setup CHILD_SA) + * + * Job is implemented in class acquire_job_t + */ + ACQUIRE, + + /** + * Delete an IKE_SA. + * + * Job is implemented in class delete_ike_sa_job_t + */ + DELETE_IKE_SA, + + /** + * Delete a CHILD_SA. + * + * Job is implemented in class delete_child_sa_job_t + */ + DELETE_CHILD_SA, + + /** + * Rekey a CHILD_SA. + * + * Job is implemented in class rekey_child_sa_job_t + */ + REKEY_CHILD_SA, + + /** + * Rekey an IKE_SA. + * + * Job is implemented in class rekey_ike_sa_job_t + */ + REKEY_IKE_SA, + + /** + * Send a keepalive packet. + * + * Job is implemented in class type send_keepalive_job_t + */ + SEND_KEEPALIVE, + + /** + * Send a DPD packet. + * + * Job is implemented in class type send_dpd_job_t + */ + SEND_DPD +}; + +/** + * enum name for job_type_t + * + * @ingroup jobs + */ +extern enum_name_t *job_type_names; + + +/** + * @brief Job-Interface as it is stored in the job queue. + * + * A job consists of a job-type and one or more assigned values. + * + * @b Constructors: + * - None, use specific implementation of the interface. + * + * @ingroup jobs + */ +struct job_t { + + /** + * @brief get type of job. + * + * @param this calling object + * @return type of this job + */ + job_type_t (*get_type) (job_t *this); + + /** + * @brief Execute a job. + * + * Call the internall job routine to process the + * job. If this method returns DESTROY_ME, the job + * must be destroyed by the caller. + * + * @param this calling object + * @return status of job execution + */ + status_t (*execute) (job_t *this); + + /** + * @brief Destroys a job_t object + * + * @param job_t calling object + */ + void (*destroy) (job_t *job); +}; + + +#endif /* JOB_H_ */ diff --git a/src/charon/queues/jobs/process_message_job.c b/src/charon/queues/jobs/process_message_job.c new file mode 100644 index 000000000..ee7484bbd --- /dev/null +++ b/src/charon/queues/jobs/process_message_job.c @@ -0,0 +1,106 @@ +/** + * @file process_message_job.h + * + * @brief Implementation of process_message_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "process_message_job.h" + +#include <daemon.h> + +typedef struct private_process_message_job_t private_process_message_job_t; + +/** + * Private data of an process_message_job_t Object + */ +struct private_process_message_job_t { + /** + * public process_message_job_t interface + */ + process_message_job_t public; + + /** + * Message associated with this job + */ + message_t *message; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_process_message_job_t *this) +{ + return PROCESS_MESSAGE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_process_message_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_message(charon->ike_sa_manager, + this->message); + if (ike_sa) + { + DBG1(DBG_NET, "received packet: from %#H to %#H", + this->message->get_source(this->message), + this->message->get_destination(this->message)); + if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_process_message_job_t *this) +{ + this->message->destroy(this->message); + free(this); +} + +/* + * Described in header + */ +process_message_job_t *process_message_job_create(message_t *message) +{ + private_process_message_job_t *this = malloc_thing(private_process_message_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void(*)(job_t*))destroy; + + /* private variables */ + this->message = message; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/process_message_job.h b/src/charon/queues/jobs/process_message_job.h new file mode 100644 index 000000000..2e60a298c --- /dev/null +++ b/src/charon/queues/jobs/process_message_job.h @@ -0,0 +1,58 @@ +/** + * @file process_message_job.h + * + * @brief Interface of process_message_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef PROCESS_MESSAGE_JOB_H_ +#define PROCESS_MESSAGE_JOB_H_ + +typedef struct process_message_job_t process_message_job_t; + +#include <library.h> +#include <encoding/message.h> +#include <queues/jobs/job.h> + +/** + * @brief Class representing an PROCESS_MESSAGE job. + * + * @b Constructors: + * - process_message_job_create() + * + * @ingroup jobs + */ +struct process_message_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type PROCESS_MESSAGE. + * + * @param message message to process + * @return created process_message_job_t object + * + * @ingroup jobs + */ +process_message_job_t *process_message_job_create(message_t *message); + +#endif /*PROCESS_MESSAGE_JOB_H_*/ diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c new file mode 100644 index 000000000..3422b614d --- /dev/null +++ b/src/charon/queues/jobs/rekey_child_sa_job.c @@ -0,0 +1,112 @@ +/** + * @file rekey_child_sa_job.c + * + * @brief Implementation of rekey_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include "rekey_child_sa_job.h" + +#include <daemon.h> + + +typedef struct private_rekey_child_sa_job_t private_rekey_child_sa_job_t; + +/** + * Private data of an rekey_child_sa_job_t object. + */ +struct private_rekey_child_sa_job_t { + /** + * Public rekey_child_sa_job_t interface. + */ + rekey_child_sa_job_t public; + + /** + * reqid of the child to rekey + */ + u_int32_t reqid; + + /** + * protocol of the CHILD_SA (ESP/AH) + */ + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_rekey_child_sa_job_t *this) +{ + return REKEY_CHILD_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_rekey_child_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for rekeying", + this->reqid); + return DESTROY_ME; + } + ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_rekey_child_sa_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) +{ + private_rekey_child_sa_job_t *this = malloc_thing(private_rekey_child_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/rekey_child_sa_job.h b/src/charon/queues/jobs/rekey_child_sa_job.h new file mode 100644 index 000000000..19e1b5d32 --- /dev/null +++ b/src/charon/queues/jobs/rekey_child_sa_job.h @@ -0,0 +1,65 @@ +/** + * @file rekey_child_sa_job.h + * + * @brief Interface of rekey_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef REKEY_CHILD_SA_JOB_H_ +#define REKEY_CHILD_SA_JOB_H_ + +typedef struct rekey_child_sa_job_t rekey_child_sa_job_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <queues/jobs/job.h> +#include <config/proposal.h> + +/** + * @brief Class representing an REKEY_CHILD_SA Job. + * + * This job initiates the rekeying of a CHILD SA. + * + * @b Constructors: + * - rekey_child_sa_job_create() + * + * @ingroup jobs + */ +struct rekey_child_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type REKEY_CHILD_SA. + * + * The CHILD_SA is identified by its protocol (AH/ESP) and its + * inbound SPI. + * + * @param reqid reqid of the CHILD_SA to rekey + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA + * @return rekey_child_sa_job_t object + * + * @ingroup jobs + */ +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, protocol_id_t protocol, u_int32_t spi); + +#endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.c b/src/charon/queues/jobs/rekey_ike_sa_job.c new file mode 100644 index 000000000..2539d997e --- /dev/null +++ b/src/charon/queues/jobs/rekey_ike_sa_job.c @@ -0,0 +1,120 @@ +/** + * @file rekey_ike_sa_job.c + * + * @brief Implementation of rekey_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include "rekey_ike_sa_job.h" + +#include <daemon.h> + + +typedef struct private_rekey_ike_sa_job_t private_rekey_ike_sa_job_t; + +/** + * Private data of an rekey_ike_sa_job_t object. + */ +struct private_rekey_ike_sa_job_t { + /** + * Public rekey_ike_sa_job_t interface. + */ + rekey_ike_sa_job_t public; + + /** + * ID of the IKE_SA to rekey + */ + ike_sa_id_t *ike_sa_id; + + /** + * force reauthentication of the peer (full IKE_SA setup) + */ + bool reauth; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_rekey_ike_sa_job_t *this) +{ + return REKEY_IKE_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_rekey_ike_sa_job_t *this) +{ + ike_sa_t *ike_sa; + status_t status = SUCCESS; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "IKE_SA %J to rekey not found", this->ike_sa_id); + return DESTROY_ME; + } + + if (this->reauth) + { + ike_sa->reestablish(ike_sa); + } + else + { + status = ike_sa->rekey(ike_sa); + } + + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_rekey_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth) +{ + private_rekey_ike_sa_job_t *this = malloc_thing(private_rekey_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->reauth = reauth; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.h b/src/charon/queues/jobs/rekey_ike_sa_job.h new file mode 100644 index 000000000..f3e336fb3 --- /dev/null +++ b/src/charon/queues/jobs/rekey_ike_sa_job.h @@ -0,0 +1,60 @@ +/** + * @file rekey_ike_sa_job.h + * + * @brief Interface of rekey_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef REKEY_IKE_SA_JOB_H_ +#define REKEY_IKE_SA_JOB_H_ + +typedef struct rekey_ike_sa_job_t rekey_ike_sa_job_t; + +#include <library.h> +#include <sa/ike_sa_id.h> +#include <queues/jobs/job.h> + +/** + * @brief Class representing an REKEY_IKE_SA Job. + * + * This job initiates the rekeying of an IKE_SA. + * + * @b Constructors: + * - rekey_ike_sa_job_create() + * + * @ingroup jobs + */ +struct rekey_ike_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type REKEY_IKE_SA. + * + * @param ike_sa_id ID of the IKE_SA to rekey + * @param reauth TRUE to reauthenticate peer, FALSE for rekeying only + * @return rekey_ike_sa_job_t object + * + * @ingroup jobs + */ +rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth); + +#endif /* REKEY_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/retransmit_job.c b/src/charon/queues/jobs/retransmit_job.c new file mode 100644 index 000000000..5bfa20dfd --- /dev/null +++ b/src/charon/queues/jobs/retransmit_job.c @@ -0,0 +1,109 @@ +/** + * @file retransmit_job.c + * + * @brief Implementation of retransmit_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "retransmit_job.h" + +#include <daemon.h> + +typedef struct private_retransmit_job_t private_retransmit_job_t; + +/** + * Private data of an retransmit_job_t Object. + */ +struct private_retransmit_job_t { + /** + * Public retransmit_job_t interface. + */ + retransmit_job_t public; + + /** + * Message ID of the request to resend. + */ + u_int32_t message_id; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_retransmit_job_t *this) +{ + return RETRANSMIT; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_retransmit_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (ike_sa->retransmit(ike_sa, this->message_id) == DESTROY_ME) + { + /* retransmitted to many times, giving up */ + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_retransmit_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header. + */ +retransmit_job_t *retransmit_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id) +{ + private_retransmit_job_t *this = malloc_thing(private_retransmit_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->message_id = message_id; + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &this->public; +} diff --git a/src/charon/queues/jobs/retransmit_job.h b/src/charon/queues/jobs/retransmit_job.h new file mode 100644 index 000000000..19e29b909 --- /dev/null +++ b/src/charon/queues/jobs/retransmit_job.h @@ -0,0 +1,64 @@ +/** + * @file retransmit_job.h + * + * @brief Interface of retransmit_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef RETRANSMIT_JOB_H_ +#define RETRANSMIT_JOB_H_ + +typedef struct retransmit_job_t retransmit_job_t; + +#include <library.h> +#include <queues/jobs/job.h> +#include <sa/ike_sa_id.h> + +/** + * @brief Class representing an retransmit Job. + * + * This job is scheduled every time a request is sent over the + * wire. If the response to the request is not received at schedule + * time, the retransmission will be initiated. + * + * @b Constructors: + * - retransmit_job_create() + * + * @ingroup jobs + */ +struct retransmit_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type retransmit. + * + * @param message_id message_id of the request to resend + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t + * @return retransmit_job_t object + * + * @ingroup jobs + */ +retransmit_job_t *retransmit_job_create(u_int32_t message_id, + ike_sa_id_t *ike_sa_id); + +#endif /* RETRANSMIT_JOB_H_ */ diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c new file mode 100644 index 000000000..bb6281dcc --- /dev/null +++ b/src/charon/queues/jobs/route_job.c @@ -0,0 +1,125 @@ +/** + * @file route_job.c + * + * @brief Implementation of route_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 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. + */ + + +#include <stdlib.h> + +#include "route_job.h" + +#include <daemon.h> + +typedef struct private_route_job_t private_route_job_t; + +/** + * Private data of an route_job_t Object + */ +struct private_route_job_t { + /** + * public route_job_t interface + */ + route_job_t public; + + /** + * associated connection to route + */ + connection_t *connection; + + /** + * associated policy to route + */ + policy_t *policy; + + /** + * route or unroute? + */ + bool route; +}; + +/** + * Implements route_job_t.get_type. + */ +static job_type_t get_type(private_route_job_t *this) +{ + return ROUTE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_route_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); + if (this->route) + { + if (ike_sa->route(ike_sa, this->connection, this->policy) != SUCCESS) + { + DBG1(DBG_JOB, "routing failed"); + } + } + else + { + if (ike_sa->unroute(ike_sa, this->policy) == DESTROY_ME) + { + DBG1(DBG_JOB, "removing IKE_SA, as last routed CHILD_SA unrouted"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_route_job_t *this) +{ + this->connection->destroy(this->connection); + this->policy->destroy(this->policy); + free(this); +} + +/* + * Described in header + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route) +{ + private_route_job_t *this = malloc_thing(private_route_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->connection = connection; + this->policy = policy; + this->route = route; + + return &this->public; +} diff --git a/src/charon/queues/jobs/route_job.h b/src/charon/queues/jobs/route_job.h new file mode 100644 index 000000000..2743a70ab --- /dev/null +++ b/src/charon/queues/jobs/route_job.h @@ -0,0 +1,59 @@ +/** + * @file route_job.h + * + * @brief Interface of route_job_t. + */ + +/* + * Copyright (C) 2005-2006 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. + */ + +#ifndef ROUTE_JOB_H_ +#define ROUTE_JOB_H_ + +typedef struct route_job_t route_job_t; + +#include <library.h> +#include <queues/jobs/job.h> +#include <config/policies/policy.h> +#include <config/connections/connection.h> + +/** + * @brief Class representing an ROUTE Job. + * + * @b Constructors: + * - route_job_create() + * + * @ingroup jobs + */ +struct route_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ROUTE. + * + * @param connection connection used for routing + * @param policy policy to set up + * @param route TRUE to route, FALSE to unroute + * @return route_job_t object + * + * @ingroup jobs + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route); + +#endif /*ROUTE_JOB_H_*/ diff --git a/src/charon/queues/jobs/send_dpd_job.c b/src/charon/queues/jobs/send_dpd_job.c new file mode 100644 index 000000000..7294d78d5 --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.c @@ -0,0 +1,110 @@ +/** + * @file send_dpd_job.c + * + * @brief Implementation of send_dpd_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include <stdlib.h> + +#include "send_dpd_job.h" + +#include <sa/ike_sa.h> +#include <daemon.h> + + +typedef struct private_send_dpd_job_t private_send_dpd_job_t; + +/** + * Private data of an send_dpd_job_t Object + */ +struct private_send_dpd_job_t { + /** + * public send_dpd_job_t interface + */ + send_dpd_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements send_dpd_job_t.get_type. + */ +static job_type_t get_type(private_send_dpd_job_t *this) +{ + return SEND_DPD; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_dpd_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + return DESTROY_ME; + } + + if (ike_sa->send_dpd(ike_sa) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_dpd_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_dpd_job_t *this = malloc_thing(private_send_dpd_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_dpd_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/send_dpd_job.h b/src/charon/queues/jobs/send_dpd_job.h new file mode 100644 index 000000000..f3900f9a2 --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.h @@ -0,0 +1,68 @@ +/** + * @file send_dpd_job.h + * + * @brief Interface of send_dpd_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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. + */ + +#ifndef SEND_DPD_JOB_H_ +#define SEND_DPD_JOB_H_ + +typedef struct send_dpd_job_t send_dpd_job_t; + +#include <library.h> +#include <queues/jobs/job.h> +#include <config/connections/connection.h> +#include <sa/ike_sa_id.h> + +/** + * @brief Class representing a SEND_DPD Job. + * + * Job to periodically send a Dead Peer Detection (DPD) request, + * ie. an IKE request with no payloads other than the encrypted payload + * required by the syntax. + * + * @b Constructors: + * - send_dpd_job_create() + * + * @ingroup jobs + */ +struct send_dpd_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_dpd_job_t object. + * + * @param this send_dpd_job_t object to destroy + */ + void (*destroy) (send_dpd_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_DPD. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_DPD_JOB_H_*/ diff --git a/src/charon/queues/jobs/send_keepalive_job.c b/src/charon/queues/jobs/send_keepalive_job.c new file mode 100644 index 000000000..1c1cb288e --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.c @@ -0,0 +1,103 @@ +/** + * @file send_keepalive_job.c + * + * @brief Implementation of send_keepalive_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include <stdlib.h> + +#include "send_keepalive_job.h" + +#include <sa/ike_sa.h> +#include <daemon.h> + + +typedef struct private_send_keepalive_job_t private_send_keepalive_job_t; + +/** + * Private data of an send_keepalive_job_t Object + */ +struct private_send_keepalive_job_t { + /** + * public send_keepalive_job_t interface + */ + send_keepalive_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements send_keepalive_job_t.get_type. + */ +static job_type_t get_type(private_send_keepalive_job_t *this) +{ + return SEND_KEEPALIVE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_keepalive_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + return DESTROY_ME; + } + ike_sa->send_keepalive(ike_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_keepalive_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_keepalive_job_t *this = malloc_thing(private_send_keepalive_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_keepalive_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/send_keepalive_job.h b/src/charon/queues/jobs/send_keepalive_job.h new file mode 100644 index 000000000..c7d05be65 --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.h @@ -0,0 +1,67 @@ +/** + * @file send_keepalive_job.h + * + * @brief Interface of send_keepalive_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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. + */ + +#ifndef SEND_KEEPALIVE_JOB_H_ +#define SEND_KEEPALIVE_JOB_H_ + +typedef struct send_keepalive_job_t send_keepalive_job_t; + +#include <library.h> +#include <queues/jobs/job.h> +#include <config/connections/connection.h> +#include <sa/ike_sa_id.h> + +/** + * @brief Class representing a SEND_KEEPALIVE Job. + * + * This job will send a NAT keepalive packet if the IKE SA is still alive, + * and reinsert itself into the event queue. + * + * @b Constructors: + * - send_keepalive_job_create() + * + * @ingroup jobs + */ +struct send_keepalive_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_keepalive_job_t object. + * + * @param this send_keepalive_job_t object to destroy + */ + void (*destroy) (send_keepalive_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_KEEPALIVE. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_KEEPALIVE_JOB_H_*/ diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c new file mode 100644 index 000000000..707aae9ad --- /dev/null +++ b/src/charon/sa/authenticators/authenticator.c @@ -0,0 +1,56 @@ +/** + * @file authenticator.c + * + * @brief Generic constructor for authenticators. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> + +#include "authenticator.h" + +#include <sa/authenticators/rsa_authenticator.h> +#include <sa/authenticators/psk_authenticator.h> +#include <sa/authenticators/eap_authenticator.h> + + +ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS, + "RSA signature", + "pre-shared key", + "DSS signature"); +ENUM_NEXT(auth_method_names, AUTH_EAP, AUTH_EAP, AUTH_DSS, + "EAP"); +ENUM_END(auth_method_names, AUTH_EAP); + +/* + * Described in header. + */ +authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method) +{ + switch (auth_method) + { + case AUTH_RSA: + return (authenticator_t*)rsa_authenticator_create(ike_sa); + case AUTH_PSK: + return (authenticator_t*)psk_authenticator_create(ike_sa); + case AUTH_EAP: + return (authenticator_t*)eap_authenticator_create(ike_sa); + default: + return NULL; + } +} diff --git a/src/charon/sa/authenticators/authenticator.h b/src/charon/sa/authenticators/authenticator.h new file mode 100644 index 000000000..c7b0fc81a --- /dev/null +++ b/src/charon/sa/authenticators/authenticator.h @@ -0,0 +1,139 @@ +/** + * @file authenticator.h + * + * @brief Interface of authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef AUTHENTICATOR_H_ +#define AUTHENTICATOR_H_ + +typedef enum auth_method_t auth_method_t; +typedef struct authenticator_t authenticator_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <encoding/payloads/auth_payload.h> + +/** + * Method to use for authentication. + * + * @ingroup authenticators + */ +enum auth_method_t { + /** + * Computed as specified in section 2.15 of RFC using + * an RSA private key over a PKCS#1 padded hash. + */ + AUTH_RSA = 1, + + /** + * Computed as specified in section 2.15 of RFC using the + * shared key associated with the identity in the ID payload + * and the negotiated prf function + */ + AUTH_PSK = 2, + + /** + * Computed as specified in section 2.15 of RFC using a + * DSS private key over a SHA-1 hash. + */ + AUTH_DSS = 3, + + /** + * EAP authentication. This value is never negotiated and therefore + * a value from private use. + */ + AUTH_EAP = 201, +}; + +/** + * enum names for auth_method_t. + * + * @ingroup authenticators + */ +extern enum_name_t *auth_method_names; + +/** + * @brief Authenticator interface implemented by the various authenticators. + * + * Currently the following two AUTH methods are supported: + * - shared key message integrity code (AUTH_PSK) + * - RSA digital signature (AUTH_RSA) + * + * @b Constructors: + * - authenticator_create() + * + * @ingroup authenticators + */ +struct authenticator_t { + + /** + * @brief Verify a received authentication payload. + * + * @param this calling object + * @param ike_sa_init binary representation of received ike_sa_init + * @param my_nonce the sent nonce + * @param auth_payload authentication payload to verify + * + * @return + * - SUCCESS, + * - FAILED if verification failed + * - INVALID_ARG if auth_method does not match + * - NOT_FOUND if credentials not found + */ + status_t (*verify) (authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload); + + /** + * @brief Build an authentication payload to send to the other peer. + * + * @param this calling object + * @param ike_sa_init binary representation of sent ike_sa_init + * @param other_nonce the received nonce + * @param[out] auth_payload the resulting authentication payload + * + * @return + * - SUCCESS, + * - NOT_FOUND if the data for AUTH method could not be found + */ + status_t (*build) (authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload); + + /** + * @brief Destroys a authenticator_t object. + * + * @param this calling object + */ + void (*destroy) (authenticator_t *this); +}; + +/** + * @brief Creates an authenticator for the specified auth method. + * + * @param ike_sa associated ike_sa + * @param auth_method authentication method to use for build()/verify() + * + * @return authenticator_t object + * + * @ingroup authenticators + */ +authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method); + +#endif /* AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_identity.c b/src/charon/sa/authenticators/eap/eap_identity.c new file mode 100644 index 000000000..12a8bf7cc --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_identity.c @@ -0,0 +1,135 @@ +/** + * @file eap_identity.c + * + * @brief Implementation of eap_identity_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#include "eap_identity.h" + +#include <daemon.h> +#include <library.h> + +typedef struct private_eap_identity_t private_eap_identity_t; + +/** + * Private data of an eap_identity_t object. + */ +struct private_eap_identity_t { + + /** + * Public authenticator_t interface. + */ + eap_identity_t public; + + /** + * ID of the peer + */ + identification_t *peer; +}; + +/** + * Implementation of eap_method_t.process for the peer + */ +static status_t process(private_eap_identity_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t id, hdr; + + hdr = chunk_alloca(5); + id = this->peer->get_encoding(this->peer); + + *(hdr.ptr + 0) = EAP_RESPONSE; + *(hdr.ptr + 1) = in->get_identifier(in); + *(u_int16_t*)(hdr.ptr + 2) = htons(hdr.len + id.len); + *(hdr.ptr + 4) = EAP_IDENTITY; + + *out = eap_payload_create_data(chunk_cata("cc", hdr, id)); + return SUCCESS; + +} + +/** + * Implementation of eap_method_t.initiate for the peer + */ +static status_t initiate(private_eap_identity_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_identity_t *this) +{ + return EAP_IDENTITY; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_identity_t *this, chunk_t *msk) +{ + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_identity_t *this) +{ + return FALSE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_identity_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +eap_identity_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_identity_t *this; + + if (role != EAP_PEER) + { + return NULL; + } + + this = malloc_thing(private_eap_identity_t); + + /* public functions */ + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->peer = peer; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_identity.h b/src/charon/sa/authenticators/eap/eap_identity.h new file mode 100644 index 000000000..20f0f0b67 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_identity.h @@ -0,0 +1,59 @@ +/** + * @file eap_identity.h + * + * @brief Interface of eap_identity_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef EAP_IDENTITY_H_ +#define EAP_IDENTITY_H_ + +typedef struct eap_identity_t eap_identity_t; + +#include <sa/authenticators/eap/eap_method.h> + +/** + * @brief Implementation of the eap_method_t interface using EAP Identity. + * + * @b Constructors: + * - eap_identity_create() + * - eap_client_create() using eap_method EAP_IDENTITY + * + * @ingroup eap + */ +struct eap_identity_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP Identity. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_identity_t object + * + * @ingroup eap + */ +eap_identity_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_IDENTITY_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c new file mode 100644 index 000000000..a4d8abb58 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -0,0 +1,245 @@ +/** + * @file eap_method.c + * + * @brief Generic constructor for eap_methods. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> +#include <sys/stat.h> +#include <dirent.h> +#include <error.h> +#include <dlfcn.h> + +#include "eap_method.h" + +#include <daemon.h> +#include <library.h> +#include <utils/linked_list.h> +#include <utils/identification.h> + + +ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD, + "EAP_IDENTITY", + "EAP_NOTIFICATION", + "EAP_NAK", + "EAP_MD5", + "EAP_ONE_TIME_PASSWORD", + "EAP_TOKEN_CARD"); +ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_TOKEN_CARD, + "EAP_SIM"); +ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM, + "EAP_AKA"); +ENUM_END(eap_type_names, EAP_AKA); + +ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, + "EAP_REQUEST", + "EAP_RESPONSE", + "EAP_SUCCESS", + "EAP_FAILURE", +); + +ENUM(eap_role_names, EAP_SERVER, EAP_PEER, + "EAP_SERVER", + "EAP_PEER", +); + + +typedef struct module_entry_t module_entry_t; + +/** + * Representation of a loaded module: EAP type, library handle, constructor + */ +struct module_entry_t { + eap_type_t type; + void *handle; + eap_constructor_t constructor; +}; + +/** List of module_entry_t's */ +static linked_list_t *modules = NULL; + +/** + * unload modules at daemon shutdown + */ +void eap_method_unload() +{ + if (modules) + { + module_entry_t *entry; + + while (modules->remove_last(modules, (void**)&entry) == SUCCESS) + { + DBG2(DBG_CFG, "unloaded module for %s", eap_type_names, entry->type); + dlclose(entry->handle); + free(entry); + } + modules->destroy(modules); + modules = NULL; + } +} + +/** + * Load EAP modules at daemon startup + */ +void eap_method_load(char *directory) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + + eap_method_unload(); + modules = linked_list_create(); + + if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR)) + { + DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); + return; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory); + return; + } + + dir = opendir(directory); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); + return; + } + + DBG1(DBG_CFG, "loading EAP modules from '%s'", directory); + + while ((entry = readdir(dir)) != NULL) + { + char file[256]; + module_entry_t module, *loaded_module; + eap_method_t *method; + identification_t *id; + char *ending; + + snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name); + + if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG)) + { + DBG2(DBG_CFG, " skipping %s, doesn't look like a file", + entry->d_name); + continue; + } + ending = entry->d_name + strlen(entry->d_name) - 3; + if (ending <= entry->d_name || !streq(ending, ".so")) + { + /* skip anything which does not look like a library */ + DBG2(DBG_CFG, " skipping %s, doesn't look like a library", + entry->d_name); + continue; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name); + continue; + } + + /* try to load the library */ + module.handle = dlopen(file, RTLD_LAZY); + if (module.handle == NULL) + { + DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name, + dlerror()); + continue; + } + module.constructor = dlsym(module.handle, "eap_create"); + if (module.constructor == NULL) + { + DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped", + entry->d_name); + dlclose(module.handle); + continue; + } + + /* get the type implemented in the method, create an instance for it */ + id = identification_create_from_string("john@doe.xyz"); + method = module.constructor(EAP_SERVER, id, id); + if (method == NULL) + { + method = module.constructor(EAP_PEER, id, id); + } + id->destroy(id); + if (method == NULL) + { + DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped", + entry->d_name); + dlclose(module.handle); + continue; + } + module.type = method->get_type(method); + method->destroy(method); + + DBG1(DBG_CFG, " loaded EAP method %N successfully from %s", + eap_type_names, module.type, entry->d_name); + + loaded_module = malloc_thing(module_entry_t); + memcpy(loaded_module, &module, sizeof(module)); + modules->insert_last(modules, loaded_module); + } + closedir(dir); +} + +/* + * Described in header. + */ +eap_method_t *eap_method_create(eap_type_t type, eap_role_t role, + identification_t *server, + identification_t *peer) +{ + eap_method_t *method = NULL; + iterator_t *iterator; + module_entry_t *entry; + + iterator = modules->create_iterator(modules, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (entry->type == type) + { + method = entry->constructor(role, server, peer); + if (method) + { + break; + } + } + } + iterator->destroy(iterator); + + if (method == NULL) + { + DBG1(DBG_CFG, "no EAP module found for %N %N", + eap_type_names, type, eap_role_names, role); + } + return method; +} diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h new file mode 100644 index 000000000..d43dc001f --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -0,0 +1,242 @@ +/** + * @file eap_method.h + * + * @brief Interface eap_method_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef EAP_METHOD_H_ +#define EAP_METHOD_H_ + +typedef struct eap_method_t eap_method_t; +typedef enum eap_role_t eap_role_t; +typedef enum eap_type_t eap_type_t; +typedef enum eap_code_t eap_code_t; + +#include <library.h> +#include <utils/identification.h> +#include <encoding/payloads/eap_payload.h> + +/** + * Role of an eap_method, SERVER or PEER (client) + * + * @ingroup eap + */ +enum eap_role_t { + EAP_SERVER, + EAP_PEER, +}; +/** + * enum names for eap_role_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_role_names; + +/** + * EAP types, defines the EAP method implementation + * + * @ingroup eap + */ +enum eap_type_t { + EAP_IDENTITY = 1, + EAP_NOTIFICATION = 2, + EAP_NAK = 3, + EAP_MD5 = 4, + EAP_ONE_TIME_PASSWORD = 5, + EAP_TOKEN_CARD = 6, + EAP_SIM = 18, + EAP_AKA = 23, +}; + +/** + * enum names for eap_type_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_type_names; + +/** + * EAP code, type of an EAP message + * + * @ingroup eap + */ +enum eap_code_t { + EAP_REQUEST = 1, + EAP_RESPONSE = 2, + EAP_SUCCESS = 3, + EAP_FAILURE = 4, +}; + +/** + * enum names for eap_code_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_code_names; + + +/** + * @brief Interface of an EAP method for server and client side. + * + * An EAP method initiates an EAP exchange and processes requests and + * responses. An EAP method may need multiple exchanges before succeeding, and + * the eap_authentication may use multiple EAP methods to authenticate a peer. + * To accomplish these requirements, all EAP methods have their own + * implementation while the eap_authenticatior uses one or more of these + * EAP methods. Sending of EAP(SUCCESS/FAILURE) message is not the job + * of the method, the eap_authenticator does this. + * An EAP method may establish a MSK, this is used the complete the + * authentication. Even if a mutual EAP method is used, the traditional + * AUTH payloads are required. Only these include the nonces and messages from + * ike_sa_init and therefore prevent man in the middle attacks. + * + * @b Constructors: + * - eap_method_create() + * + * @ingroup eap + */ +struct eap_method_t { + + /** + * @brief Initiate the EAP exchange. + * + * initiate() is only useable for server implementations, as clients only + * reply to server requests. + * A eap_payload is created in "out" if result is NEED_MORE. + * + * @param this calling object + * @param out eap_payload to send to the client + * @return + * - NEED_MORE, if an other exchange is required + * - FAILED, if unable to create eap request payload + */ + status_t (*initiate) (eap_method_t *this, eap_payload_t **out); + + /** + * @brief Process a received EAP message. + * + * A eap_payload is created in "out" if result is NEED_MORE. + * + * @param this calling object + * @param in eap_payload response received + * @param out created eap_payload to send + * @return + * - NEED_MORE, if an other exchange is required + * - FAILED, if EAP method failed + * - SUCCESS, if EAP method succeeded + */ + status_t (*process) (eap_method_t *this, eap_payload_t *in, + eap_payload_t **out); + + /** + * @brief Get the EAP type implemented in this method. + * + * @param this calling object + * @return type of the EAP method + */ + eap_type_t (*get_type) (eap_method_t *this); + + /** + * @brief Check if this EAP method authenticates the server. + * + * Some EAP methods provide mutual authentication and + * allow authentication using only EAP, if the peer supports it. + * + * @param this calling object + * @return TRUE if methods provides mutual authentication + */ + bool (*is_mutual) (eap_method_t *this); + + /** + * @brief Get the MSK established by this EAP method. + * + * Not all EAP methods establish a shared secret. + * + * @param this calling object + * @param msk chunk receiving internal stored MSK + * @return + * - SUCCESS, or + * - FAILED, if MSK not established (yet) + */ + status_t (*get_msk) (eap_method_t *this, chunk_t *msk); + + /** + * @brief Destroys a eap_method_t object. + * + * @param this calling object + */ + void (*destroy) (eap_method_t *this); +}; + +/** + * @brief Creates an EAP method for a specific type and role. + * + * @param eap_type EAP type to use + * @param role role of the eap_method, server or peer + * @param server ID of acting server + * @param peer ID of involved peer (client) + * @return eap_method_t object + * + * @ingroup eap + */ +eap_method_t *eap_method_create(eap_type_t eap_type, eap_role_t role, + identification_t *server, identification_t *peer); + +/** + * @brief (Re-)Load all EAP modules in the EAP modules directory. + * + * For security reasons, the directory and all it's modules must be owned + * by root and must not be writeable by someone else. + * + * @param dir directory of the EAP modules + * + * @ingroup eap + */ +void eap_method_load(char *directory); + +/** + * @brief Unload all loaded EAP modules + * + * @ingroup eap + */ +void eap_method_unload(); + +/** + * @brief Constructor definition for a pluggable EAP module. + * + * Each EAP module must define a constructor function which will return + * an initialized object with the methods defined in eap_method_t. The + * constructor must be named eap_create() and it's signature must be equal + * to that of eap_constructor_t. + * A module may implement only a single role. If it does not support the role + * requested, NULL should be returned. Multiple modules are allowed of the + * same EAP type to support seperate implementations of peer/server. + * + * @param role role the module will play, peer or server + * @param server ID of the server to use for credential lookup + * @param peer ID of the peer to use for credential lookup + * @return implementation of the eap_method_t interface + * + * @ingroup eap + */ +typedef eap_method_t *(*eap_constructor_t)(eap_role_t role, + identification_t *server, + identification_t *peer); + +#endif /* EAP_METHOD_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_sim.c b/src/charon/sa/authenticators/eap/eap_sim.c new file mode 100644 index 000000000..3dc59fb6b --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_sim.c @@ -0,0 +1,703 @@ +/** + * @file eap_sim.c + * + * @brief Implementation of eap_sim_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#include "eap_sim.h" + +#include <dlfcn.h> + +#include <daemon.h> +#include <library.h> + +#define MAX_TRIES 3 + +ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR, + "SIM_START", + "SIM_CHALLENGE", + "SIM_NOTIFICATION", + "SIM_13", + "SIM_CLIENT_ERROR", +); + +ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, + "AT_END", + "AT_0", + "AT_RAND", + "AT_AUTN", + "AT_RES", + "AT_AUTS", + "AT_5", + "AT_PADDING", + "AT_NONCE_MT", + "AT_8", + "AT_9", + "AT_PERMANENT_ID_REQ", + "AT_MAC", + "AT_NOTIFICATION", + "AT_ANY_ID_REQ", + "AT_IDENTITY", + "AT_VERSION_LIST", + "AT_SELECTED_VERSION", + "AT_FULLAUTH_ID_REQ", + "AT_18", + "AT_COUNTER", + "AT_COUNTER_TOO_SMALL", + "AT_NONCE_S", + "AT_CLIENT_ERROR_CODE"); +ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, + "AT_IV", + "AT_ENCR_DATA", + "AT_131", + "AT_NEXT_PSEUDONYM", + "AT_NEXT_REAUTH_ID", + "AT_CHECKCODE", + "AT_RESULT_IND"); +ENUM_END(sim_attribute_names, AT_RESULT_IND); + + +typedef struct private_eap_sim_t private_eap_sim_t; + +/** + * Private data of an eap_sim_t object. + */ +struct private_eap_sim_t { + + /** + * Public authenticator_t interface. + */ + eap_sim_t public; + + /** + * ID of ourself + */ + identification_t *peer; + + /** + * SIM cardreader function loaded from library + */ + sim_algo_t alg; + + /** + * handle of the loaded library + */ + void *handle; + + /** + * how many times we try to authenticate + */ + int tries; + + /** + * version this implementation uses + */ + chunk_t version; + + /** + * version list received from server + */ + chunk_t version_list; + + /** + * Nonce value used in AT_NONCE_MT + */ + chunk_t nonce; + + /** + * k_encr key derived from MK + */ + chunk_t k_encr; + + /** + * k_auth key derived from MK, used for AT_MAC verification + */ + chunk_t k_auth; + + /** + * MSK, used for EAP-SIM based IKEv2 authentication + */ + chunk_t msk; + + /** + * EMSK, extendes MSK for further uses + */ + chunk_t emsk; +}; + +/** length of the AT_NONCE_MT nonce value */ +#define NONCE_LEN 16 +/** length of the AT_MAC value */ +#define MAC_LEN 16 +/** length of the AT_RAND value */ +#define RAND_LEN 16 +/** length of the k_encr key */ +#define KENCR_LEN 16 +/** length of the k_auth key */ +#define KAUTH_LEN 16 +/** length of the MSK */ +#define MSK_LEN 64 +/** length of the EMSK */ +#define EMSK_LEN 64 + +/* client error codes used in AT_CLIENT_ERROR_CODE */ +char client_error_general_buf[] = {0x00, 0x01}; +char client_error_unsupported_buf[] = {0x00, 0x02}; +char client_error_insufficient_buf[] = {0x00, 0x03}; +char client_error_notfresh_buf[] = {0x00, 0x04}; +chunk_t client_error_general = chunk_from_buf(client_error_general_buf); +chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf); +chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf); +chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf); + +/** + * Read EAP and EAP-SIM header, return SIM type + */ +static sim_subtype_t read_header(chunk_t *message) +{ + sim_subtype_t type; + + if (message->len < 8) + { + *message = chunk_empty; + return 0; + } + type = *(message->ptr + 5); + *message = chunk_skip(*message, 8); + return type; +} + +/** + * read the next attribute from the chunk data + */ +static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data) +{ + sim_attribute_t attribute; + size_t length; + + DBG3(DBG_IKE, "reading attribute from %B", message); + + if (message->len < 2) + { + return AT_END; + } + attribute = *message->ptr++; + length = *message->ptr++ * 4 - 2; + message->len -= 2; + DBG3(DBG_IKE, "found attribute %N with length %d", + sim_attribute_names, attribute, length); + + if (length > message->len) + { + return AT_END; + } + data->len = length; + data->ptr = message->ptr; + *message = chunk_skip(*message, length); + return attribute; +} + +/** + * Build an EAP-SIM payload using a variable length attribute list. + * The variable argument takes a sim_attribute_t followed by its data in a chunk. + */ +static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier, + sim_subtype_t type, ...) +{ + chunk_t message = chunk_alloca(512); + chunk_t pos = message; + eap_payload_t *payload; + va_list args; + sim_attribute_t attr; + u_int8_t *mac_pos = NULL; + chunk_t mac_data = chunk_empty; + + /* write EAP header, skip length bytes */ + *pos.ptr++ = EAP_RESPONSE; + *pos.ptr++ = identifier; + pos.ptr += 2; + pos.len -= 4; + /* write SIM header with type and subtype, zero reserved bytes */ + *pos.ptr++ = EAP_SIM; + *pos.ptr++ = type; + *pos.ptr++ = 0; + *pos.ptr++ = 0; + pos.len -= 4; + + va_start(args, type); + while ((attr = va_arg(args, sim_attribute_t)) != AT_END) + { + chunk_t data = va_arg(args, chunk_t); + + DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data); + + /* write attribute header */ + *pos.ptr++ = attr; + pos.len--; + + switch (attr) + { + case AT_CLIENT_ERROR_CODE: + case AT_SELECTED_VERSION: + { + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_IDENTITY: + { + /* align up to four byte */ + if (data.len % 4) + { + chunk_t tmp = chunk_alloca((data.len/4)*4 + 4); + memset(tmp.ptr, 0, tmp.len); + memcpy(tmp.ptr, data.ptr, data.len); + data = tmp; + } + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + /* actual length in bytes */ + *(u_int16_t*)pos.ptr = htons(data.len); + pos = chunk_skip(pos, sizeof(u_int16_t)); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_NONCE_MT: + { + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + memset(pos.ptr, 0, 2); + pos = chunk_skip(pos, 2); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_MAC: + { + *pos.ptr++ = 5; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + mac_pos = pos.ptr; + memset(mac_pos, 0, MAC_LEN); + pos = chunk_skip(pos, MAC_LEN); + mac_data = data; + break; + } + case AT_RAND: + { + *pos.ptr++ = data.len/4 + 1; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + default: + DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped", + sim_attribute_names, attr); + break; + } + } + va_end(args); + + /* calculate message length, write into header */ + message.len = pos.ptr - message.ptr; + *(u_int16_t*)(message.ptr + 2) = htons(message.len); + + /* create MAC if AT_MAC attribte was included. Append supplied va_arg + * chunk mac_data to "to-sign" chunk */ + if (mac_pos) + { + signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + mac_data = chunk_cata("cc", message, mac_data); + signer->get_signature(signer, mac_data, mac_pos); + DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b", + &mac_data, mac_pos, MAC_LEN); + signer->destroy(signer); + } + + payload = eap_payload_create_data(message); + + DBG3(DBG_IKE, "created EAP message %B", &message); + return payload; +} + +/** + * process an EAP-SIM/Request/Start message + */ +static status_t process_start(private_eap_sim_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + chunk_t message, data; + sim_attribute_t attribute, include_id = AT_END; + u_int8_t identifier; + + identifier = in->get_identifier(in); + message = in->get_data(in); + read_header(&message); + + while ((attribute = read_attribute(&message, &data)) != AT_END) + { + switch (attribute) + { + case AT_VERSION_LIST: + { + /* check if server supports our implementation */ + bool found = FALSE; + if (data.len > 2) + { + /* read actual length first */ + data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2); + data = chunk_skip(data, 2); + chunk_free(&this->version_list); + this->version_list = chunk_clone(data); + while (data.len >= this->version.len) + { + if (memeq(data.ptr, this->version.ptr, this->version.len)) + { + found = TRUE; + break; + } + data = chunk_skip(data, this->version.len); + } + } + if (!found) + { + DBG1(DBG_IKE, "server does not support EAP_SIM " + "version number %#B", &this->version); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_unsupported, + AT_END); + return NEED_MORE; + } + break; + } + case AT_PERMANENT_ID_REQ: + case AT_FULLAUTH_ID_REQ: + case AT_ANY_ID_REQ: + /* only include AT_IDENTITY if requested */ + include_id = AT_IDENTITY; + break; + default: + DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", + sim_attribute_names, attribute); + break; + } + } + + /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */ + *out = build_payload(this, identifier, SIM_START, + AT_SELECTED_VERSION, this->version, + AT_NONCE_MT, this->nonce, + include_id, this->peer->get_encoding(this->peer), + AT_END); + return NEED_MORE; +} + +/** + * process an EAP-SIM/Request/Challenge message + */ +static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + chunk_t message, data, tmp, kcs, kc, sreses, sres, mk; + sim_attribute_t attribute; + u_int8_t identifier, i; + chunk_t mac = chunk_empty, rands = chunk_empty; + signer_t *signer; + hasher_t *hasher; + prf_t *prf; + + if (this->tries-- <= 0) + { + /* give up without notification. This hack is required as some buggy + * server implementations won't respect our client-error. */ + return FAILED; + } + + identifier = in->get_identifier(in); + message = in->get_data(in); + read_header(&message); + + while ((attribute = read_attribute(&message, &data)) != AT_END) + { + switch (attribute) + { + case AT_RAND: + { + rands = chunk_skip(data, 2); + break; + } + case AT_MAC: + { + /* backup MAC, zero it inline for later verification */ + data = chunk_skip(data, 2); + mac = chunk_clonea(data); + memset(data.ptr, 0, data.len); + break; + } + default: + DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", + sim_attribute_names, attribute); + break; + } + } + + /* excepting two or three RAND, each 16 bytes. We require two valid + * and different RANDs */ + if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) || + memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN)) + { + DBG1(DBG_IKE, "no valid AT_RAND received"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_insufficient, + AT_END); + return FAILED; + } + if (mac.len != MAC_LEN) + { + DBG1(DBG_IKE, "no valid AT_MAC received"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + + /* get two or three KCs/SRESes from SIM using RANDs */ + kcs = kc = chunk_alloca(rands.len / 2); + sreses = sres = chunk_alloca(rands.len / 4); + while (rands.len > 0) + { + int kc_len = kc.len, sres_len = sres.len; + + if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len)) + { + DBG1(DBG_IKE, "unable to get triplets from SIM"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b", + rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len); + kc = chunk_skip(kc, kc_len); + sres = chunk_skip(sres, sres_len); + rands = chunk_skip(rands, RAND_LEN); + } + + /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs, + this->nonce, this->version_list, this->version); + hasher = hasher_create(HASH_SHA1); + mk = chunk_alloca(hasher->get_hash_size(hasher)); + hasher->get_hash(hasher, tmp, mk.ptr); + hasher->destroy(hasher); + DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk); + + /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf() + * FIPS PRF has 320 bit block size, we need 160 byte for keys + * => run prf four times */ + prf = prf_create(PRF_FIPS_SHA1_160); + prf->set_key(prf, mk); + tmp = chunk_alloca(prf->get_block_size(prf) * 4); + for (i = 0; i < 4; i++) + { + prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i); + } + prf->destroy(prf); + chunk_free(&this->k_encr); + chunk_free(&this->k_auth); + chunk_free(&this->msk); + chunk_free(&this->emsk); + chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth, + MSK_LEN, &this->msk, EMSK_LEN, &this->emsk); + DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B", + &this->k_encr, &this->k_auth, &this->msk, &this->emsk); + + /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */ + signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + tmp = chunk_cata("cc", in->get_data(in), this->nonce); + if (!signer->verify_signature(signer, tmp, mac)) + { + DBG1(DBG_IKE, "AT_MAC verification failed"); + signer->destroy(signer); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + signer->destroy(signer); + + /* build response, AT_MAC is built over "EAP packet | n*SRES" */ + *out = build_payload(this, identifier, SIM_CHALLENGE, + AT_MAC, sreses, + AT_END); + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.process for the peer + */ +static status_t process(private_eap_sim_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + sim_subtype_t type; + chunk_t message; + + message = in->get_data(in); + type = read_header(&message); + + switch (type) + { + case SIM_START: + return process_start(this, in, out); + case SIM_CHALLENGE: + return process_challenge(this, in, out); + default: + DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N", + sim_subtype_names, type); + *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, AT_END); + return NEED_MORE; + } +} + +/** + * Implementation of eap_method_t.initiate for the peer + */ +static status_t initiate(private_eap_sim_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_sim_t *this) +{ + return EAP_SIM; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_sim_t *this, chunk_t *msk) +{ + if (this->msk.ptr) + { + *msk = this->msk; + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_sim_t *this) +{ + return TRUE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_sim_t *this) +{ + dlclose(this->handle); + chunk_free(&this->nonce); + chunk_free(&this->version_list); + chunk_free(&this->k_auth); + chunk_free(&this->k_encr); + chunk_free(&this->msk); + chunk_free(&this->emsk); + free(this); +} + +/* + * Described in header. + */ +eap_sim_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_sim_t *this; + randomizer_t *randomizer; + static char version[] = {0x00,0x01}; + + if (role != EAP_PEER) + { + return NULL; + } + this = malloc_thing(private_eap_sim_t); + + this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY); + if (this->handle == NULL) + { + DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB); + free(this); + return NULL; + } + this->alg = dlsym(this->handle, SIM_READER_ALG); + if (this->alg == NULL) + { + DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'", + SIM_READER_ALG, SIM_READER_LIB); + dlclose(this->handle); + free(this); + return NULL; + } + + randomizer = randomizer_create(); + if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN, + &this->nonce)) + { + DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM"); + randomizer->destroy(randomizer); + free(this); + return NULL; + } + randomizer->destroy(randomizer); + + /* public functions */ + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->peer = peer; + this->tries = MAX_TRIES; + this->version.ptr = version; + this->version.len = sizeof(version); + this->version_list = chunk_empty; + this->k_auth = chunk_empty; + this->k_encr = chunk_empty; + this->msk = chunk_empty; + this->emsk = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_sim.h b/src/charon/sa/authenticators/eap/eap_sim.h new file mode 100644 index 000000000..10640babe --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_sim.h @@ -0,0 +1,141 @@ +/** + * @file eap_sim.h + * + * @brief Interface of eap_sim_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef EAP_SIM_H_ +#define EAP_SIM_H_ + +typedef struct eap_sim_t eap_sim_t; +typedef enum sim_subtype_t sim_subtype_t; +typedef enum sim_attribute_t sim_attribute_t; + +#include <sa/authenticators/eap/eap_method.h> + +/** + * Subtypes of SIM messages + */ +enum sim_subtype_t { + SIM_START = 10, + SIM_CHALLENGE = 11, + SIM_NOTIFICATION = 12, + SIM_CLIENT_ERROR = 14, +}; + +/** + * enum names for sim_subtype_t + */ +extern enum_name_t *sim_subtype_names; + +enum sim_attribute_t { + /** defines the end of attribute list */ + AT_END = -1, + AT_RAND = 1, + AT_AUTN = 2, + AT_RES = 3, + AT_AUTS = 4, + AT_PADDING = 6, + AT_NONCE_MT = 7, + AT_PERMANENT_ID_REQ = 10, + AT_MAC = 11, + AT_NOTIFICATION = 12, + AT_ANY_ID_REQ = 13, + AT_IDENTITY = 14, + AT_VERSION_LIST = 15, + AT_SELECTED_VERSION = 16, + AT_FULLAUTH_ID_REQ = 17, + AT_COUNTER = 19, + AT_COUNTER_TOO_SMALL = 20, + AT_NONCE_S = 21, + AT_CLIENT_ERROR_CODE = 22, + AT_IV = 129, + AT_ENCR_DATA = 130, + AT_NEXT_PSEUDONYM = 132, + AT_NEXT_REAUTH_ID = 133, + AT_CHECKCODE = 134, + AT_RESULT_IND = 135, +}; + +/** + * enum names for sim_subtype_t + */ +extern enum_name_t *sim_attribute_names; + +/** + * @brief Cardreaders SIM function. + * + * @param rand RAND to run algo with + * @param rand_length length of value in rand + * @param sres buffer to get SRES + * @param sres_length size of buffer in sres, returns bytes written to SRES + * @param kc buffer to get Kc + * @param kc_length size of buffer in Kc, returns bytes written to Kc + * @return zero on success + */ +typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length, + unsigned char *sres, int *sres_length, + unsigned char *kc, int *kc_length); + +#ifndef SIM_READER_LIB +/** the library containing the cardreader with the SIM function */ +#error SIM_READER_LIB not specified, use --with-sim-reader option +#endif /* SIM_READER_LIB */ + +#ifndef SIM_READER_ALG +/** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */ +#define SIM_READER_ALG "sim_run_alg" +#endif /* SIM_READER_ALG */ + + +/** + * @brief Implementation of the eap_method_t interface using EAP-SIM. + * + * This EAP-SIM client implementation uses another pluggable library to + * access the SIM card. This module is specified using the SIM_READER_LIB + * definition. The function to run the algorithm has the sim_algo_t type and + * is named as SIM_READER_ALG is defined. + * + * @b Constructors: + * - eap_create() of this module + * - eap_client_create() using eap_method EAP_SIM + * + * @ingroup eap + */ +struct eap_sim_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP-SIM. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_sim_t object + * + * @ingroup eap + */ +eap_sim_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_SIM_H_ */ diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c new file mode 100644 index 000000000..6c8ca8d8f --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -0,0 +1,360 @@ +/** + * @file eap_authenticator.c + * + * @brief Implementation of eap_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> + +#include "eap_authenticator.h" + +#include <daemon.h> +#include <config/policies/policy.h> +#include <sa/authenticators/eap/eap_method.h> + +typedef struct private_eap_authenticator_t private_eap_authenticator_t; + +/** + * Private data of an eap_authenticator_t object. + */ +struct private_eap_authenticator_t { + + /** + * Public authenticator_t interface. + */ + eap_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Role of this authenticator, PEER or SERVER + */ + eap_role_t role; + + /** + * Current EAP method processing + */ + eap_method_t *method; + + /** + * MSK used to build and verify auth payload + */ + chunk_t msk; +}; + +extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf_skp, prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + chunk_t auth_data, recv_auth_data; + identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa); + + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, this->msk, + other_id, this->ike_sa->get_auth_verify(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + + recv_auth_data = auth_payload->get_data(auth_payload); + if (!chunk_equals(auth_data, recv_auth_data)) + { + DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed"); + chunk_free(&auth_data); + return FAILED; + } + chunk_free(&auth_data); + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_EAP); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t auth_data; + identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa); + + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_EAP); + + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, this->msk, + my_id, this->ike_sa->get_auth_build(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); + (*auth_payload)->set_data(*auth_payload, auth_data); + chunk_free(&auth_data); + + return SUCCESS; +} + +/** + * Implementation of eap_authenticator_t.initiate + */ +static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, + eap_payload_t **out) +{ + /* if initiate() is called, role is always server */ + this->role = EAP_SERVER; + + if (type == 0) + { + DBG1(DBG_IKE, + "client requested EAP authentication, but configuration forbids it"); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + + DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); + this->method = eap_method_create(type, this->role, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + + if (this->method == NULL) + { + DBG1(DBG_IKE, "configured EAP server method %N not supported, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + if (this->method->initiate(this->method, out) != NEED_MORE) + { + DBG1(DBG_IKE, "failed to initiate %N, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + return NEED_MORE; +} + +/** + * Processing method for a peer + */ +static status_t process_peer(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + eap_type_t type = in->get_type(in); + + if (type == EAP_IDENTITY) + { + eap_method_t *method = eap_method_create(type, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); + + if (method == NULL || method->process(method, in, out) != SUCCESS) + { + DBG1(DBG_IKE, "EAP server requested %N, but unable to process", + eap_type_names, type); + DESTROY_IF(method); + return FAILED; + } + + DBG1(DBG_IKE, "EAP server requested %N, sending IKE identity", + eap_type_names, type); + + method->destroy(method); + return NEED_MORE; + } + + /* create an eap_method for the first call */ + if (this->method == NULL) + { + DBG1(DBG_IKE, "EAP server requested %N authentication", + eap_type_names, type); + this->method = eap_method_create(type, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); + if (this->method == NULL) + { + DBG1(DBG_IKE, "EAP server requested unsupported " + "EAP method %N, sending EAP_NAK", eap_type_names, type); + *out = eap_payload_create_nak(); + return NEED_MORE; + } + } + + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + DBG1(DBG_IKE, "EAP method %N succeded", + eap_type_names, this->method->get_type(this->method)); + return SUCCESS; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } +} + +/** + * Processing method for a server + */ +static status_t process_server(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + DBG1(DBG_IKE, "EAP method %N succeded, MSK established", + eap_type_names, this->method->get_type(this->method)); + this->msk = chunk_clone(this->msk); + *out = eap_payload_create_code(EAP_SUCCESS); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established", + eap_type_names, this->method->get_type(this->method)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed for peer %D", + eap_type_names, this->method->get_type(this->method), + this->ike_sa->get_other_id(this->ike_sa)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } +} + +/** + * Implementation of eap_authenticator_t.process + */ +static status_t process(private_eap_authenticator_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + eap_code_t code = in->get_code(in); + + switch (this->role) + { + case EAP_SERVER: + { + switch (code) + { + case EAP_RESPONSE: + { + return process_server(this, in, out); + } + default: + { + DBG1(DBG_IKE, "received %N, sending %N", + eap_code_names, code, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + } + } + case EAP_PEER: + { + switch (code) + { + case EAP_REQUEST: + { + return process_peer(this, in, out); + } + case EAP_SUCCESS: + { + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + this->msk = chunk_clone(this->msk); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N has no MSK established", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } + case EAP_FAILURE: + default: + { + DBG1(DBG_IKE, "received %N, EAP authentication failed", + eap_code_names, code); + return FAILED; + } + } + } + default: + { + return FAILED; + } + } +} + +/** + * Implementation of authenticator_t.is_mutual. + */ +static bool is_mutual(private_eap_authenticator_t *this) +{ + if (this->method) + { + return this->method->is_mutual(this->method); + } + return FALSE; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_eap_authenticator_t *this) +{ + DESTROY_IF(this->method); + chunk_free(&this->msk); + free(this); +} + +/* + * Described in header. + */ +eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa) +{ + private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + this->public.is_mutual = (bool(*)(eap_authenticator_t*))is_mutual; + this->public.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,eap_payload_t**))initiate; + this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process; + + /* private data */ + this->ike_sa = ike_sa; + this->role = EAP_PEER; + this->method = NULL; + this->msk = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h new file mode 100644 index 000000000..ffa162343 --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -0,0 +1,156 @@ +/** + * @file eap_authenticator.h + * + * @brief Interface of eap_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef EAP_AUTHENTICATOR_H_ +#define EAP_AUTHENTICATOR_H_ + +typedef struct eap_authenticator_t eap_authenticator_t; + +#include <sa/authenticators/authenticator.h> +#include <encoding/payloads/eap_payload.h> + +/** + * @brief Implementation of the authenticator_t interface using AUTH_EAP. + * + * Authentication using EAP involves the most complex authenticator. It stays + * alive over multiple ike_auth transactions and handles multiple EAP + * messages. + * EAP authentication must be clearly distinguished between using + * mutual EAP methods and using methods not providing server authentication. + * If no mutual authentication is used, the server must prove it's identity + * by traditional AUTH methods (RSA, psk). Only when the EAP method is mutual, + * the client should accept an EAP-only authentication. + * RFC4306 does always use traditional authentiction, EAP only authentication + * is described in the internet draft draft-eronen-ipsec-ikev2-eap-auth-05.txt. + * + * @verbatim + ike_sa_init + -------------------------> + <------------------------- + followed by multiple ike_auth: + + +--------+ +--------+ + | EAP | ID, SA, TS, N(EAP_ONLY) | EAP | + | client | ---------------------------> | server | + | | ID, [AUTH,] EAP | | AUTH payload is + | | <--------------------------- | | only included if + | | EAP | | authentication + | | ---------------------------> | | is not mutual. + | | EAP | | + | | <--------------------------- | | + | | EAP | | + | | ---------------------------> | | + | | EAP(SUCCESS) | | + | | <--------------------------- | | + | | AUTH | | If EAP establishes + | | ---------------------------> | | a session key, AUTH + | | AUTH, SA, TS | | payloads use this + | | <--------------------------- | | key, not SK_pi/pr + +--------+ +--------+ + + @endverbatim + * @b Constructors: + * - eap_authenticator_create() + * - authenticator_create() using auth_method AUTH_EAP + * + * @ingroup authenticators + */ +struct eap_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; + + /** + * @brief Check if the EAP method was/is mutual and secure. + * + * RFC4306 proposes to authenticate the EAP responder (server) by standard + * IKEv2 methods (RSA, psk). Not all, but some EAP methods + * provide mutual authentication, which would result in a redundant + * authentication. If the client supports EAP_ONLY_AUTHENTICATION, and + * the the server provides mutual authentication, authentication using + * RSA/PSK may be omitted. If the server did not include a traditional + * AUTH payload, the client must verify that the server initiated mutual + * EAP authentication before it can trust the server. + * + * @param this calling object + * @return TRUE, if no AUTH payload required, FALSE otherwise + */ + bool (*is_mutual) (eap_authenticator_t* this); + + /** + * @brief Initiate the EAP exchange. + * + * The server initiates EAP exchanges, so the client never calls + * this method. If initiate() returns NEED_MORE, the EAP authentication + * process started. In any case, a payload is created in "out". + * + * @param this calling object + * @param type EAP method to use to authenticate client + * @param out created initiaal EAP message to send + * @return + * - FAILED, if initiation failed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*initiate) (eap_authenticator_t* this, eap_type_t type, + eap_payload_t **out); + + /** + * @brief Process an EAP message. + * + * After receiving an EAP message "in", the peer/server processes + * the payload and creates a reply/subsequent request. + * The server side always returns NEED_MORE if another EAP message + * is excepted from the client, SUCCESS if EAP exchange completed and + * "out" is EAP_SUCCES, or FAILED if the EAP exchange failed with + * a EAP_FAILURE payload in "out". Anyway, a payload in "out" is always + * created. + * The peer (client) side only creates a "out" payload if result is + * NEED_MORE, a SUCCESS/FAILED is returned whenever a + * EAP_SUCCESS/EAP_FAILURE message is received in "in". + * If a SUCCESS is returned (on any side), the EAP authentication was + * successful and the AUTH payload can be exchanged. + * + * @param this calling object + * @param in received EAP message + * @param out created EAP message to send + * @return + * - FAILED, if authentication/EAP exchange failed + * - SUCCESS, if authentication completed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*process) (eap_authenticator_t* this, + eap_payload_t *in, eap_payload_t **out); +}; + +/** + * @brief Creates an authenticator for AUTH_EAP. + * + * @param ike_sa associated ike_sa + * @return eap_authenticator_t object + * + * @ingroup authenticators + */ +eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa); + +#endif /* EAP_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c new file mode 100644 index 000000000..43aec0971 --- /dev/null +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -0,0 +1,204 @@ +/** + * @file psk_authenticator.c + * + * @brief Implementation of psk_authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> + +#include "psk_authenticator.h" + +#include <config/policies/policy.h> +#include <daemon.h> + +/** + * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +#define IKEV2_KEY_PAD "Key Pad for IKEv2" +#define IKEV2_KEY_PAD_LENGTH 17 + + +typedef struct private_psk_authenticator_t private_psk_authenticator_t; + +/** + * Private data of an psk_authenticator_t object. + */ +struct private_psk_authenticator_t { + + /** + * Public authenticator_t interface. + */ + psk_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; +}; + +/** + * Builds the octets to be signed as described in section 2.15 of RFC 4306 + */ +chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf) +{ + u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00}; + chunk_t id_header = chunk_from_buf(id_header_buf); + chunk_t id_with_header, id_prfd, id_encoding; + + id_header_buf[0] = id->get_type(id); + id_encoding = id->get_encoding(id); + + id_with_header = chunk_cat("cc", id_header, id_encoding); + prf->allocate_bytes(prf, id_with_header, &id_prfd); + chunk_free(&id_with_header); + + return chunk_cat("ccm", ike_sa_init, nonce, id_prfd); +} + +/** + * Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf_skp, prf_t *prf) +{ + chunk_t key_pad, key, auth_data, octets; + + octets = build_tbs_octets(ike_sa_init, nonce, id, prf_skp); + /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */ + key_pad.ptr = IKEV2_KEY_PAD; + key_pad.len = IKEV2_KEY_PAD_LENGTH; + prf->set_key(prf, secret); + prf->allocate_bytes(prf, key_pad, &key); + prf->set_key(prf, key); + prf->allocate_bytes(prf, octets, &auth_data); + DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets); + DBG3(DBG_IKE, "secret %B", &secret); + DBG3(DBG_IKE, "keypad %B", &key_pad); + DBG3(DBG_IKE, "prf(secret, keypad) %B", &key); + DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &auth_data); + chunk_free(&octets); + chunk_free(&key); + + return auth_data; +} + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + status_t status; + chunk_t auth_data, recv_auth_data, shared_key; + identification_t *my_id, *other_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); + status = charon->credentials->get_shared_key(charon->credentials, my_id, + other_id, &shared_key); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); + return status; + } + + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, shared_key, + other_id, this->ike_sa->get_auth_verify(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + chunk_free(&shared_key); + + recv_auth_data = auth_payload->get_data(auth_payload); + if (auth_data.len != recv_auth_data.len || + !memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len)) + { + DBG1(DBG_IKE, "PSK MAC verification failed"); + chunk_free(&auth_data); + return FAILED; + } + chunk_free(&auth_data); + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_PSK); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t shared_key; + chunk_t auth_data; + status_t status; + identification_t *my_id, *other_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_PSK); + status = charon->credentials->get_shared_key(charon->credentials, my_id, + other_id, &shared_key); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); + return status; + } + + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, shared_key, + my_id, this->ike_sa->get_auth_build(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + DBG2(DBG_IKE, "successfully created shared key MAC"); + chunk_free(&shared_key); + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); + (*auth_payload)->set_data(*auth_payload, auth_data); + + chunk_free(&auth_data); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_psk_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa) +{ + private_psk_authenticator_t *this = malloc_thing(private_psk_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + /* private data */ + this->ike_sa = ike_sa; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/psk_authenticator.h b/src/charon/sa/authenticators/psk_authenticator.h new file mode 100644 index 000000000..c1c5bcaac --- /dev/null +++ b/src/charon/sa/authenticators/psk_authenticator.h @@ -0,0 +1,57 @@ +/** + * @file psk_authenticator.h + * + * @brief Interface of psk_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef PSK_AUTHENTICATOR_H_ +#define PSK_AUTHENTICATOR_H_ + +typedef struct psk_authenticator_t psk_authenticator_t; + +#include <sa/authenticators/authenticator.h> + +/** + * @brief Implementation of the authenticator_t interface using AUTH_PSK. + * + * @b Constructors: + * - psk_authenticator_create() + * - authenticator_create() using auth_method AUTH_PSK + * + * @ingroup authenticators + */ +struct psk_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; +}; + +/** + * @brief Creates an authenticator for AUTH_PSK. + * + * @param ike_sa associated ike_sa + * @return psk_authenticator_t object + * + * @ingroup authenticators + */ +psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa); + +#endif /* PSK_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/rsa_authenticator.c b/src/charon/sa/authenticators/rsa_authenticator.c new file mode 100644 index 000000000..dfa01e332 --- /dev/null +++ b/src/charon/sa/authenticators/rsa_authenticator.c @@ -0,0 +1,180 @@ +/** + * @file rsa_authenticator.c + * + * @brief Implementation of rsa_authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> + +#include "rsa_authenticator.h" + +#include <config/policies/policy.h> +#include <daemon.h> + + +typedef struct private_rsa_authenticator_t private_rsa_authenticator_t; + +/** + * Private data of an rsa_authenticator_t object. + */ +struct private_rsa_authenticator_t { + + /** + * Public authenticator_t interface. + */ + rsa_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; +}; + +/** + * Function implemented in psk_authenticator.c + */ +extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + status_t status; + chunk_t auth_data, octets; + rsa_public_key_t *public_key; + identification_t *other_id; + + other_id = this->ike_sa->get_other_id(this->ike_sa); + + if (auth_payload->get_auth_method(auth_payload) != AUTH_RSA) + { + return INVALID_ARG; + } + auth_data = auth_payload->get_data(auth_payload); + public_key = charon->credentials->get_trusted_public_key(charon->credentials, + other_id); + if (public_key == NULL) + { + DBG1(DBG_IKE, "no RSA public key found for '%D'", other_id); + return NOT_FOUND; + } + octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, + this->ike_sa->get_auth_verify(this->ike_sa)); + status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data); + chunk_free(&octets); + + if (status != SUCCESS) + { + DBG1(DBG_IKE, "RSA signature verification failed"); + return status; + } + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_RSA); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t chunk; + chunk_t octets; + chunk_t auth_data; + status_t status; + rsa_public_key_t *my_pubkey; + rsa_private_key_t *my_key; + identification_t *my_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_RSA); + DBG2(DBG_IKE, "looking for RSA public key belonging to '%D'", my_id); + + my_pubkey = charon->credentials->get_rsa_public_key(charon->credentials, my_id); + if (my_pubkey == NULL) + { + DBG1(DBG_IKE, "no RSA public key found for '%D'", my_id); + return NOT_FOUND; + } + DBG2(DBG_IKE, "matching RSA public key found"); + chunk = my_pubkey->get_keyid(my_pubkey); + DBG2(DBG_IKE, "looking for RSA private key with keyid %#B", &chunk); + my_key = charon->credentials->get_rsa_private_key(charon->credentials, my_pubkey); + if (my_key == NULL) + { + DBG1(DBG_IKE, "no RSA private key found with for %D with keyid %#B", + my_id, &chunk); + return NOT_FOUND; + } + DBG2(DBG_IKE, "matching RSA private key found"); + + octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, + this->ike_sa->get_auth_build(this->ike_sa)); + status = my_key->build_emsa_pkcs1_signature(my_key, HASH_SHA1, octets, &auth_data); + chunk_free(&octets); + + if (status != SUCCESS) + { + my_key->destroy(my_key); + DBG1(DBG_IKE, "build signature of SHA1 hash failed"); + return status; + } + DBG2(DBG_IKE, "successfully signed with RSA private key"); + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_RSA); + (*auth_payload)->set_data(*auth_payload, auth_data); + + my_key->destroy(my_key); + chunk_free(&auth_data); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_rsa_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa) +{ + private_rsa_authenticator_t *this = malloc_thing(private_rsa_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + /* private data */ + this->ike_sa = ike_sa; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/rsa_authenticator.h b/src/charon/sa/authenticators/rsa_authenticator.h new file mode 100644 index 000000000..cc5cc0150 --- /dev/null +++ b/src/charon/sa/authenticators/rsa_authenticator.h @@ -0,0 +1,57 @@ +/** + * @file rsa_authenticator.h + * + * @brief Interface of rsa_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef RSA_AUTHENTICATOR_H_ +#define RSA_AUTHENTICATOR_H_ + +typedef struct rsa_authenticator_t rsa_authenticator_t; + +#include <sa/authenticators/authenticator.h> + +/** + * @brief Implementation of the authenticator_t interface using AUTH_RSA. + * + * @b Constructors: + * - rsa_authenticator_create() + * - authenticator_create() using auth_method AUTH_RSA + * + * @ingroup authenticators + */ +struct rsa_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; +}; + +/** + * @brief Creates an authenticator for AUTH_RSA. + * + * @param ike_sa associated ike_sa + * @return rsa_authenticator_t object + * + * @ingroup authenticators + */ +rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa); + +#endif /* RSA_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c new file mode 100644 index 000000000..19131389d --- /dev/null +++ b/src/charon/sa/child_sa.c @@ -0,0 +1,1130 @@ +/** + * @file child_sa.c + * + * @brief Implementation of child_sa_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#define _GNU_SOURCE +#include "child_sa.h" + +#include <stdio.h> +#include <string.h> +#include <printf.h> + +#include <daemon.h> + +ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DELETING, + "CREATED", + "ROUTED", + "INSTALLED", + "REKEYING", + "DELETING", +); + +typedef struct sa_policy_t sa_policy_t; + +/** + * Struct used to store information for a policy. This + * is needed since we must provide all this information + * for deleting a policy... + */ +struct sa_policy_t { + /** + * Traffic selector for us + */ + traffic_selector_t *my_ts; + + /** + * Traffic selector for other + */ + traffic_selector_t *other_ts; +}; + +typedef struct private_child_sa_t private_child_sa_t; + +/** + * Private data of a child_sa_t bject. + */ +struct private_child_sa_t { + /** + * Public interface of child_sa_t. + */ + child_sa_t public; + + struct { + /** address of peer */ + host_t *addr; + /** id of peer */ + identification_t *id; + /** actual used SPI, 0 if unused */ + u_int32_t spi; + } me, other; + + /** + * Allocated SPI for a ESP proposal candidates + */ + u_int32_t alloc_esp_spi; + + /** + * Allocated SPI for a AH proposal candidates + */ + u_int32_t alloc_ah_spi; + + /** + * Protocol used to protect this SA, ESP|AH + */ + protocol_id_t protocol; + + /** + * List containing sa_policy_t objects + */ + linked_list_t *policies; + + /** + * Seperate list for local traffic selectors + */ + linked_list_t *my_ts; + + /** + * Seperate list for remote traffic selectors + */ + linked_list_t *other_ts; + + /** + * reqid used for this child_sa + */ + u_int32_t reqid; + + /** + * encryption algorithm used for this SA + */ + algorithm_t encryption; + + /** + * integrity protection algorithm used for this SA + */ + algorithm_t integrity; + + /** + * time, on which SA was installed + */ + time_t install_time; + + /** + * absolute time when rekeying is sceduled + */ + time_t rekey_time; + + /** + * state of the CHILD_SA + */ + child_sa_state_t state; + + /** + * Specifies if NAT traversal is used + */ + bool use_natt; + + /** + * mode this SA uses, tunnel/transport + */ + mode_t mode; + + /** + * virtual IP assinged to local host + */ + host_t *virtual_ip; + + /** + * policy used to create this child + */ + policy_t *policy; +}; + +/** + * Implementation of child_sa_t.get_name. + */ +static char *get_name(private_child_sa_t *this) +{ + return this->policy->get_name(this->policy);; +} + +/** + * Implements child_sa_t.get_reqid + */ +static u_int32_t get_reqid(private_child_sa_t *this) +{ + return this->reqid; +} + +/** + * Implements child_sa_t.get_spi + */ +u_int32_t get_spi(private_child_sa_t *this, bool inbound) +{ + if (inbound) + { + return this->me.spi; + } + return this->other.spi; +} + +/** + * Implements child_sa_t.get_protocol + */ +protocol_id_t get_protocol(private_child_sa_t *this) +{ + return this->protocol; +} + +/** + * Implements child_sa_t.get_state + */ +static child_sa_state_t get_state(private_child_sa_t *this) +{ + return this->state; +} + +/** + * Implements child_sa_t.get_policy + */ +static policy_t* get_policy(private_child_sa_t *this) +{ + return this->policy; +} + +/** + * Run the up/down script + */ +static void updown(private_child_sa_t *this, bool up) +{ + sa_policy_t *policy; + iterator_t *iterator; + char *script; + + script = this->policy->get_updown(this->policy); + + if (script == NULL) + { + return; + } + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + char command[1024]; + char *ifname = NULL; + char *my_client, *other_client, *my_client_mask, *other_client_mask; + char *pos, *virtual_ip; + FILE *shell; + + /* get subnet/bits from string */ + asprintf(&my_client, "%R", policy->my_ts); + pos = strchr(my_client, '/'); + *pos = '\0'; + my_client_mask = pos + 1; + pos = strchr(my_client_mask, '['); + if (pos) + { + *pos = '\0'; + } + asprintf(&other_client, "%R", policy->other_ts); + pos = strchr(other_client, '/'); + *pos = '\0'; + other_client_mask = pos + 1; + pos = strchr(other_client_mask, '['); + if (pos) + { + *pos = '\0'; + } + + if (this->virtual_ip) + { + asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", + this->virtual_ip); + } + else + { + asprintf(&virtual_ip, ""); + } + + ifname = charon->kernel_interface->get_interface(charon->kernel_interface, + this->me.addr); + + /* build the command with all env variables. + * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing + */ + snprintf(command, sizeof(command), + "2>&1 " + "PLUTO_VERSION='1.1' " + "PLUTO_VERB='%s%s%s' " + "PLUTO_CONNECTION='%s' " + "PLUTO_INTERFACE='%s' " + "PLUTO_REQID='%u' " + "PLUTO_ME='%H' " + "PLUTO_MY_ID='%D' " + "PLUTO_MY_CLIENT='%s/%s' " + "PLUTO_MY_CLIENT_NET='%s' " + "PLUTO_MY_CLIENT_MASK='%s' " + "PLUTO_MY_PORT='%u' " + "PLUTO_MY_PROTOCOL='%u' " + "PLUTO_PEER='%H' " + "PLUTO_PEER_ID='%D' " + "PLUTO_PEER_CLIENT='%s/%s' " + "PLUTO_PEER_CLIENT_NET='%s' " + "PLUTO_PEER_CLIENT_MASK='%s' " + "PLUTO_PEER_PORT='%u' " + "PLUTO_PEER_PROTOCOL='%u' " + "%s" + "%s" + "%s", + up ? "up" : "down", + policy->my_ts->is_host(policy->my_ts, + this->me.addr) ? "-host" : "-client", + this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6", + this->policy->get_name(this->policy), + ifname ? ifname : "(unknown)", + this->reqid, + this->me.addr, + this->me.id, + my_client, my_client_mask, + my_client, my_client_mask, + policy->my_ts->get_from_port(policy->my_ts), + policy->my_ts->get_protocol(policy->my_ts), + this->other.addr, + this->other.id, + other_client, other_client_mask, + other_client, other_client_mask, + policy->other_ts->get_from_port(policy->other_ts), + policy->other_ts->get_protocol(policy->other_ts), + virtual_ip, + this->policy->get_hostaccess(this->policy) ? + "PLUTO_HOST_ACCESS='1' " : "", + script); + free(ifname); + free(my_client); + free(other_client); + free(virtual_ip); + + shell = popen(command, "r"); + + if (shell == NULL) + { + DBG1(DBG_CHD, "could not execute updown script '%s'", script); + return; + } + + while (TRUE) + { + char resp[128]; + + if (fgets(resp, sizeof(resp), shell) == NULL) + { + if (ferror(shell)) + { + DBG1(DBG_CHD, "error reading output from updown script"); + return; + } + else + { + break; + } + } + else + { + char *e = resp + strlen(resp); + if (e > resp && e[-1] == '\n') + { /* trim trailing '\n' */ + e[-1] = '\0'; + } + DBG1(DBG_CHD, "updown: %s", resp); + } + } + pclose(shell); + } + iterator->destroy(iterator); +} + +/** + * Implements child_sa_t.set_state + */ +static void set_state(private_child_sa_t *this, child_sa_state_t state) +{ + this->state = state; + if (state == CHILD_INSTALLED) + { + updown(this, TRUE); + } +} + +/** + * Allocate SPI for a single proposal + */ +static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) +{ + protocol_id_t protocol = proposal->get_protocol(proposal); + + if (protocol == PROTO_AH) + { + /* get a new spi for AH, if not already done */ + if (this->alloc_ah_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_AH, this->reqid, + &this->alloc_ah_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_ah_spi); + } + if (protocol == PROTO_ESP) + { + /* get a new spi for ESP, if not already done */ + if (this->alloc_esp_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_ESP, this->reqid, + &this->alloc_esp_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_esp_spi); + } + return SUCCESS; +} + + +/** + * Implements child_sa_t.alloc + */ +static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) +{ + iterator_t *iterator; + proposal_t *proposal; + + /* iterator through proposals to update spis */ + iterator = proposals->create_iterator(proposals, TRUE); + while(iterator->iterate(iterator, (void**)&proposal)) + { + if (alloc_proposal(this, proposal) != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +static status_t install(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus, bool mine) +{ + u_int32_t spi, soft, hard;; + algorithm_t *enc_algo, *int_algo; + algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; + algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; + host_t *src; + host_t *dst; + natt_conf_t *natt; + status_t status; + + this->protocol = proposal->get_protocol(proposal); + + /* now we have to decide which spi to use. Use self allocated, if "mine", + * or the one in the proposal, if not "mine" (others). Additionally, + * source and dest host switch depending on the role */ + if (mine) + { + /* if we have allocated SPIs for AH and ESP, we must delete the unused + * one. */ + if (this->protocol == PROTO_ESP) + { + this->me.spi = this->alloc_esp_spi; + if (this->alloc_ah_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, + this->alloc_ah_spi, PROTO_AH); + } + } + else + { + this->me.spi = this->alloc_ah_spi; + if (this->alloc_esp_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, + this->alloc_esp_spi, PROTO_ESP); + } + } + spi = this->me.spi; + dst = this->me.addr; + src = this->other.addr; + } + else + { + this->other.spi = proposal->get_spi(proposal); + spi = this->other.spi; + src = this->me.addr; + dst = this->other.addr; + } + + DBG2(DBG_CHD, "adding %s %N SA", mine ? "inbound" : "outbound", + protocol_id_names, this->protocol); + + /* select encryption algo */ + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo)) + { + DBG2(DBG_CHD, " using %N for encryption", + encryption_algorithm_names, enc_algo->algorithm); + } + else + { + enc_algo = &enc_algo_none; + } + + /* select integrity algo */ + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo)) + { + DBG2(DBG_CHD, " using %N for integrity", + integrity_algorithm_names, int_algo->algorithm); + } + else + { + int_algo = &int_algo_none; + } + + /* setup nat-t */ + if (this->use_natt) + { + natt = alloca(sizeof(natt_conf_t)); + natt->sport = src->get_port(src); + natt->dport = dst->get_port(dst); + } + else + { + natt = NULL; + } + + soft = this->policy->get_soft_lifetime(this->policy); + hard = this->policy->get_hard_lifetime(this->policy); + + /* send SA down to the kernel */ + DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, spi, this->protocol, + this->reqid, mine ? soft : 0, + hard, enc_algo, int_algo, + prf_plus, natt, mode, mine); + + this->encryption = *enc_algo; + this->integrity = *int_algo; + this->install_time = time(NULL); + this->rekey_time = soft; + + return status; +} + +static status_t add(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus) +{ + u_int32_t outbound_spi, inbound_spi; + + /* backup outbound spi, as alloc overwrites it */ + outbound_spi = proposal->get_spi(proposal); + + /* get SPIs inbound SAs */ + if (alloc_proposal(this, proposal) != SUCCESS) + { + return FAILED; + } + inbound_spi = proposal->get_spi(proposal); + + /* install inbound SAs */ + if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + /* install outbound SAs, restore spi*/ + proposal->set_spi(proposal, outbound_spi); + if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + proposal->set_spi(proposal, inbound_spi); + + return SUCCESS; +} + +static status_t update(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus) +{ + u_int32_t inbound_spi; + + /* backup received spi, as install() overwrites it */ + inbound_spi = proposal->get_spi(proposal); + + /* install outbound SAs */ + if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + + /* restore spi */ + proposal->set_spi(proposal, inbound_spi); + /* install inbound SAs */ + if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + return SUCCESS; +} + +static status_t add_policies(private_child_sa_t *this, + linked_list_t *my_ts_list, + linked_list_t *other_ts_list, mode_t mode) +{ + iterator_t *my_iter, *other_iter; + traffic_selector_t *my_ts, *other_ts; + /* use low prio for ROUTED policies */ + bool high_prio = (this->state != CHILD_CREATED); + + /* iterate over both lists */ + my_iter = my_ts_list->create_iterator(my_ts_list, TRUE); + other_iter = other_ts_list->create_iterator(other_ts_list, TRUE); + while (my_iter->iterate(my_iter, (void**)&my_ts)) + { + other_iter->reset(other_iter); + while (other_iter->iterate(other_iter, (void**)&other_ts)) + { + /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ + status_t status; + sa_policy_t *policy; + + if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) + { + DBG2(DBG_CHD, + "CHILD_SA policy uses two different IP families, ignored"); + continue; + } + + /* only set up policies if protocol matches, or if one is zero (any) */ + if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts) && + my_ts->get_protocol(my_ts) && other_ts->get_protocol(other_ts)) + { + DBG2(DBG_CHD, + "CHILD_SA policy uses two different protocols, ignored"); + continue; + } + + /* install 3 policies: out, in and forward */ + status = charon->kernel_interface->add_policy(charon->kernel_interface, + this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT, + this->protocol, this->reqid, high_prio, mode, FALSE); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN, + this->protocol, this->reqid, high_prio, mode, FALSE); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD, + this->protocol, this->reqid, high_prio, mode, FALSE); + + if (status != SUCCESS) + { + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + return status; + } + + /* store policy to delete/update them later */ + policy = malloc_thing(sa_policy_t); + policy->my_ts = my_ts->clone(my_ts); + policy->other_ts = other_ts->clone(other_ts); + this->policies->insert_last(this->policies, (void*)policy); + /* add to separate list to query them via get_*_traffic_selectors() */ + this->my_ts->insert_last(this->my_ts, (void*)policy->my_ts); + this->other_ts->insert_last(this->other_ts, (void*)policy->other_ts); + } + } + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + + /* switch to routed state if no SAD entry set up */ + if (this->state == CHILD_CREATED) + { + this->state = CHILD_ROUTED; + } + /* needed to update hosts */ + this->mode = mode; + return SUCCESS; +} + +/** + * Implementation of child_sa_t.get_my_traffic_selectors. + */ +static linked_list_t *get_my_traffic_selectors(private_child_sa_t *this) +{ + return this->my_ts; +} + +/** + * Implementation of child_sa_t.get_my_traffic_selectors. + */ +static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this) +{ + return this->other_ts; +} + +/** + * Implementation of child_sa_t.get_use_time + */ +static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time) +{ + iterator_t *iterator; + sa_policy_t *policy; + status_t status = FAILED; + + *use_time = UNDEFINED_TIME; + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + if (inbound) + { + time_t in = UNDEFINED_TIME, fwd = UNDEFINED_TIME; + + status = charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_IN, (u_int32_t*)&in); + status |= charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_FWD, (u_int32_t*)&fwd); + *use_time = max(in, fwd); + } + else + { + status = charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->my_ts, policy->other_ts, + POLICY_OUT, (u_int32_t*)use_time); + } + } + iterator->destroy(iterator); + return status; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_child_sa_t *this = *((private_child_sa_t**)(args[0])); + iterator_t *iterator; + sa_policy_t *policy; + u_int32_t now, rekeying; + u_int32_t use, use_in, use_fwd; + status_t status; + size_t written = 0; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + now = time(NULL); + + written += fprintf(stream, "%12s{%d}: %N, %N", + this->policy->get_name(this->policy), this->reqid, + child_sa_state_names, this->state, + mode_names, this->mode); + + if (this->state == CHILD_INSTALLED) + { + written += fprintf(stream, ", %N SPIs: 0x%0x_i 0x%0x_o", + protocol_id_names, this->protocol, + htonl(this->me.spi), htonl(this->other.spi)); + + if (info->alt) + { + written += fprintf(stream, "\n%12s{%d}: ", + this->policy->get_name(this->policy), + this->reqid); + + if (this->protocol == PROTO_ESP) + { + written += fprintf(stream, "%N", encryption_algorithm_names, + this->encryption.algorithm); + + if (this->encryption.key_size) + { + written += fprintf(stream, "-%d", this->encryption.key_size); + } + written += fprintf(stream, "/"); + } + + written += fprintf(stream, "%N", integrity_algorithm_names, + this->integrity.algorithm); + if (this->integrity.key_size) + { + written += fprintf(stream, "-%d", this->integrity.key_size); + } + written += fprintf(stream, ", rekeying "); + + /* calculate rekey times */ + if (this->rekey_time) + { + rekeying = this->install_time + this->rekey_time - now; + written += fprintf(stream, "in %ds", rekeying); + } + else + { + written += fprintf(stream, "disabled"); + } + } + } + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ", + this->policy->get_name(this->policy), this->reqid, + policy->my_ts, policy->other_ts); + + /* query time of last policy use */ + + /* inbound: POLICY_IN or POLICY_FWD */ + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_IN, &use_in); + use_in = (status == SUCCESS)? use_in : 0; + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_FWD, &use_fwd); + use_fwd = (status == SUCCESS)? use_fwd : 0; + use = max(use_in, use_fwd); + if (use) + { + written += fprintf(stream, "%ds_i ", now - use); + } + else + { + written += fprintf(stream, "no_i "); + } + + /* outbound: POLICY_OUT */ + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->my_ts, policy->other_ts, POLICY_OUT, &use); + if (status == SUCCESS && use) + { + written += fprintf(stream, "%ds_o ", now - use); + } + else + { + written += fprintf(stream, "no_o "); + } + } + iterator->destroy(iterator); + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CHILD_SA, print, arginfo_ptr); +} + +/** + * Update the host adress/port of a SA + */ +static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + int my_changes, int other_changes, bool mine) +{ + host_t *src, *dst, *new_src, *new_dst; + int src_changes, dst_changes; + status_t status; + u_int32_t spi; + + if (mine) + { + src = this->other.addr; + dst = this->me.addr; + new_src = new_other; + new_dst = new_me; + src_changes = other_changes; + dst_changes = my_changes; + spi = this->other.spi; + } + else + { + src = this->me.addr; + dst = this->other.addr; + new_src = new_me; + new_dst = new_other; + src_changes = my_changes; + dst_changes = other_changes; + spi = this->me.spi; + } + + DBG2(DBG_CHD, "updating %N SA 0x%x, from %#H..#H to %#H..%#H", + protocol_id_names, this->protocol, ntohl(spi), src, dst, new_src, new_dst); + + status = charon->kernel_interface->update_sa(charon->kernel_interface, + dst, spi, this->protocol, + new_src, new_dst, + src_changes, dst_changes); + + if (status != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Update the host adress/port of a policy + */ +static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other) +{ + iterator_t *iterator; + sa_policy_t *policy; + status_t status; + /* we always use high priorities, as hosts getting updated are INSTALLED */ + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + status = charon->kernel_interface->add_policy( + charon->kernel_interface, + new_me, new_other, + policy->my_ts, policy->other_ts, + POLICY_OUT, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other_ts, policy->my_ts, + POLICY_IN, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other_ts, policy->my_ts, + POLICY_FWD, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + if (status != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.update_hosts. + */ +static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + host_diff_t my_changes, host_diff_t other_changes) +{ + if (!my_changes && !other_changes) + { + return SUCCESS; + } + + /* update our (initator) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS) + { + return FAILED; + } + + /* update his (responder) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS) + { + return FAILED; + } + + /* update policies */ + if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR) + { + if (update_policy_hosts(this, new_me, new_other) != SUCCESS) + { + return FAILED; + } + } + + /* update hosts */ + if (my_changes) + { + this->me.addr->destroy(this->me.addr); + this->me.addr = new_me->clone(new_me); + } + + if (other_changes) + { + this->other.addr->destroy(this->other.addr); + this->other.addr = new_other->clone(new_other); + } + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.set_virtual_ip. + */ +static void set_virtual_ip(private_child_sa_t *this, host_t *ip) +{ + this->virtual_ip = ip->clone(ip); +} + +/** + * Implementation of child_sa_t.destroy. + */ +static void destroy(private_child_sa_t *this) +{ + sa_policy_t *policy; + + if (this->state == CHILD_DELETING || this->state == CHILD_INSTALLED) + { + updown(this, FALSE); + } + + /* delete SAs in the kernel, if they are set up */ + if (this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->me.spi, this->protocol); + } + if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->alloc_esp_spi, PROTO_ESP); + } + if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->alloc_ah_spi, PROTO_AH); + } + if (this->other.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other.addr, this->other.spi, this->protocol); + } + + /* delete all policies in the kernel */ + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + /* let rekeyed policies, as they are used by another child_sa */ + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->my_ts, policy->other_ts, + POLICY_OUT); + + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_IN); + + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_FWD); + policy->my_ts->destroy(policy->my_ts); + policy->other_ts->destroy(policy->other_ts); + free(policy); + } + this->policies->destroy(this->policies); + + this->my_ts->destroy(this->my_ts); + this->other_ts->destroy(this->other_ts); + this->me.addr->destroy(this->me.addr); + this->other.addr->destroy(this->other.addr); + this->me.id->destroy(this->me.id); + this->other.id->destroy(this->other.id); + this->policy->destroy(this->policy); + DESTROY_IF(this->virtual_ip); + free(this); +} + +/* + * Described in header. + */ +child_sa_t * child_sa_create(host_t *me, host_t* other, + identification_t *my_id, identification_t *other_id, + policy_t *policy, u_int32_t rekey, bool use_natt) +{ + static u_int32_t reqid = 0; + private_child_sa_t *this = malloc_thing(private_child_sa_t); + + /* public functions */ + this->public.get_name = (char*(*)(child_sa_t*))get_name; + this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; + this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; + this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; + this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; + this->public.add = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))add; + this->public.update = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))update; + this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_diff_t,host_diff_t))update_hosts; + this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,mode_t))add_policies; + this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors; + this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; + this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; + this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; + this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy; + this->public.set_virtual_ip = (void(*)(child_sa_t*,host_t*))set_virtual_ip; + this->public.destroy = (void(*)(child_sa_t*))destroy; + + /* private data */ + this->me.addr = me->clone(me); + this->other.addr = other->clone(other); + this->me.id = my_id->clone(my_id); + this->other.id = other_id->clone(other_id); + this->me.spi = 0; + this->other.spi = 0; + this->alloc_ah_spi = 0; + this->alloc_esp_spi = 0; + this->use_natt = use_natt; + this->state = CHILD_CREATED; + /* reuse old reqid if we are rekeying an existing CHILD_SA */ + this->reqid = rekey ? rekey : ++reqid; + this->encryption.algorithm = ENCR_UNDEFINED; + this->encryption.key_size = 0; + this->integrity.algorithm = AUTH_UNDEFINED; + this->encryption.key_size = 0; + this->policies = linked_list_create(); + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + this->protocol = PROTO_NONE; + this->mode = MODE_TUNNEL; + this->virtual_ip = NULL; + this->policy = policy; + policy->get_ref(policy); + + return &this->public; +} diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h new file mode 100644 index 000000000..216e56659 --- /dev/null +++ b/src/charon/sa/child_sa.h @@ -0,0 +1,298 @@ +/** + * @file child_sa.h + * + * @brief Interface of child_sa_t. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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. + */ + + +#ifndef CHILD_SA_H_ +#define CHILD_SA_H_ + +typedef enum child_sa_state_t child_sa_state_t; +typedef struct child_sa_t child_sa_t; + +#include <library.h> +#include <crypto/prf_plus.h> +#include <encoding/payloads/proposal_substructure.h> +#include <config/proposal.h> +#include <config/policies/policy.h> + +/** + * Where we should start with reqid enumeration + */ +#define REQID_START 2000000000 + +/** + * @brief States of a CHILD_SA + */ +enum child_sa_state_t { + + /** + * Just created, uninstalled CHILD_SA + */ + CHILD_CREATED, + + /** + * Installed SPD, but no SAD entries + */ + CHILD_ROUTED, + + /** + * Installed an in-use CHILD_SA + */ + CHILD_INSTALLED, + + /** + * CHILD_SA which is rekeying + */ + CHILD_REKEYING, + + /** + * CHILD_SA in progress of delete + */ + CHILD_DELETING, +}; + +/** + * enum strings for child_sa_state_t. + */ +extern enum_name_t *child_sa_state_names; + +/** + * @brief Represents an IPsec SAs between two hosts. + * + * A child_sa_t contains two SAs. SAs for both + * directions are managed in one child_sa_t object. Both + * SAs and the policies have the same reqid. + * + * The procedure for child sa setup is as follows: + * - A gets SPIs for a proposal via child_sa_t.alloc + * - A send the updated proposal to B + * - B selects a suitable proposal + * - B calls child_sa_t.add to add and update the selected proposal + * - B sends the updated proposal to A + * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal + * + * Once SAs are set up, policies can be added using add_policies. + * + * + * @b Constructors: + * - child_sa_create() + * + * @ingroup sa + */ +struct child_sa_t { + + /** + * @brief Get the name of the policy this CHILD_SA uses. + * + * @param this calling object + * @return name + */ + char* (*get_name) (child_sa_t *this); + + /** + * @brief Get the reqid of the CHILD SA. + * + * Every CHILD_SA has a reqid. The kernel uses this ID to + * identify it. + * + * @param this calling object + * @return reqid of the CHILD SA + */ + u_int32_t (*get_reqid)(child_sa_t *this); + + /** + * @brief Get the SPI of this CHILD_SA. + * + * Set the boolean parameter inbound to TRUE to + * get the SPI for which we receive packets, use + * FALSE to get those we use for sending packets. + * + * @param this calling object + * @param inbound TRUE to get inbound SPI, FALSE for outbound. + * @return spi of the CHILD SA + */ + u_int32_t (*get_spi) (child_sa_t *this, bool inbound); + + /** + * @brief Get the protocol which this CHILD_SA uses to protect traffic. + * + * @param this calling object + * @return AH | ESP + */ + protocol_id_t (*get_protocol) (child_sa_t *this); + + /** + * @brief Allocate SPIs for given proposals. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If a proposal contains more + * than one protocol, for each protocol an SPI is + * allocated. SPIs are stored internally and written + * back to the proposal. + * + * @param this calling object + * @param proposals list of proposals for which SPIs are allocated + */ + status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); + + /** + * @brief Install the kernel SAs for a proposal, without previous SPI allocation. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param mode mode for the CHILD_SA + * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED + */ + status_t (*add)(child_sa_t *this, proposal_t *proposal, mode_t mode, + prf_plus_t *prf_plus); + + /** + * @brief Install the kernel SAs for a proposal, after SPIs have been allocated. + * + * Updates an SA, for which SPIs are already allocated via alloc(). + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param mode mode for the CHILD_SA + * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED + */ + status_t (*update)(child_sa_t *this, proposal_t *proposal, mode_t mode, + prf_plus_t *prf_plus); + + /** + * @brief Update the hosts in the kernel SAs and policies + * + * @warning only call this after update() has been called. + * + * @param this calling object + * @param new_me the new local host + * @param new_other the new remote host + * @param my_diff differences to apply for me + * @param other_diff differences to apply for other + * @return SUCCESS or FAILED + */ + status_t (*update_hosts)(child_sa_t *this, host_t *new_me, host_t *new_other, + host_diff_t my_diff, host_diff_t other_diff); + + /** + * @brief Install the policies using some traffic selectors. + * + * Supplied lists of traffic_selector_t's specify the policies + * to use for this child sa. + * + * @param this calling object + * @param my_ts traffic selectors for local site + * @param other_ts traffic selectors for remote site + * @param mode mode for the SA: tunnel/transport + * @return SUCCESS or FAILED + */ + status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list, + linked_list_t *other_ts_list, mode_t mode); + + /** + * @brief Get the traffic selectors of added policies of local host. + * + * @param this calling object + * @return list of traffic selectors + */ + linked_list_t* (*get_my_traffic_selectors) (child_sa_t *this); + + /** + * @brief Get the traffic selectors of added policies of remote host. + * + * @param this calling object + * @return list of traffic selectors + */ + linked_list_t* (*get_other_traffic_selectors) (child_sa_t *this); + + /** + * @brief Get the time of this child_sa_t's last use (i.e. last use of any of its policies) + * + * @param this calling object + * @param inbound query for in- or outbound usage + * @param use_time the time + * @return SUCCESS or FAILED + */ + status_t (*get_use_time) (child_sa_t *this, bool inbound, time_t *use_time); + + /** + * @brief Get the state of the CHILD_SA. + * + * @param this calling object + */ + child_sa_state_t (*get_state) (child_sa_t *this); + + /** + * @brief Set the state of the CHILD_SA. + * + * @param this calling object + */ + void (*set_state) (child_sa_t *this, child_sa_state_t state); + + /** + * @brief Get the policy used to set up this child sa. + * + * @param this calling object + * @return policy + */ + policy_t* (*get_policy) (child_sa_t *this); + + /** + * @brief Set the virtual IP used received from IRAS. + * + * To allow proper setup of firewall rules, the virtual IP is required + * for filtering. + * + * @param this calling object + * @param ip own virtual IP + */ + void (*set_virtual_ip) (child_sa_t *this, host_t *ip); + + /** + * @brief Destroys a child_sa. + * + * @param this calling object + */ + void (*destroy) (child_sa_t *this); +}; + +/** + * @brief Constructor to create a new child_sa_t. + * + * @param me own address + * @param other remote address + * @param my_id id of own peer + * @param other_id id of remote peer + * @param policy policy this CHILD_SA instantiates + * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise + * @param use_natt TRUE if NAT traversal is used + * @return child_sa_t object + * + * @ingroup sa + */ +child_sa_t * child_sa_create(host_t *me, host_t *other, + identification_t *my_id, identification_t* other_id, + policy_t *policy, u_int32_t reqid, bool use_natt); + +#endif /*CHILD_SA_H_*/ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c new file mode 100644 index 000000000..68aba3064 --- /dev/null +++ b/src/charon/sa/ike_sa.c @@ -0,0 +1,2032 @@ +/** + * @file ike_sa.c + * + * @brief Implementation of ike_sa_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <sys/time.h> +#include <string.h> +#include <printf.h> +#include <sys/stat.h> + +#include "ike_sa.h" + +#include <library.h> +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/lexparser.h> +#include <crypto/diffie_hellman.h> +#include <crypto/prf_plus.h> +#include <crypto/crypters/crypter.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <encoding/payloads/transform_substructure.h> +#include <encoding/payloads/transform_attribute.h> +#include <encoding/payloads/ts_payload.h> +#include <sa/task_manager.h> +#include <sa/tasks/ike_init.h> +#include <sa/tasks/ike_natd.h> +#include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_config.h> +#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_rekey.h> +#include <sa/tasks/ike_delete.h> +#include <sa/tasks/ike_dpd.h> +#include <sa/tasks/child_create.h> +#include <sa/tasks/child_delete.h> +#include <sa/tasks/child_rekey.h> +#include <queues/jobs/retransmit_job.h> +#include <queues/jobs/delete_ike_sa_job.h> +#include <queues/jobs/send_dpd_job.h> +#include <queues/jobs/send_keepalive_job.h> +#include <queues/jobs/rekey_ike_sa_job.h> +#include <queues/jobs/route_job.h> +#include <queues/jobs/initiate_job.h> + + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif + +ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING, + "CREATED", + "CONNECTING", + "ESTABLISHED", + "REKEYING", + "DELETING", +); + +typedef struct private_ike_sa_t private_ike_sa_t; + +/** + * Private data of an ike_sa_t object. + */ +struct private_ike_sa_t { + + /** + * Public members + */ + ike_sa_t public; + + /** + * Identifier for the current IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * unique numerical ID for this IKE_SA. + */ + u_int32_t unique_id; + + /** + * Current state of the IKE_SA + */ + ike_sa_state_t state; + + /** + * connection used to establish this IKE_SA. + */ + connection_t *connection; + + /** + * Peer and authentication information to establish IKE_SA. + */ + policy_t *policy; + + /** + * Juggles tasks to process messages + */ + task_manager_t *task_manager; + + /** + * Address of local host + */ + host_t *my_host; + + /** + * Address of remote host + */ + host_t *other_host; + + /** + * Identification used for us + */ + identification_t *my_id; + + /** + * Identification used for other + */ + identification_t *other_id; + + /** + * Linked List containing the child sa's of the current IKE_SA. + */ + linked_list_t *child_sas; + + /** + * crypter for inbound traffic + */ + crypter_t *crypter_in; + + /** + * crypter for outbound traffic + */ + crypter_t *crypter_out; + + /** + * Signer for inbound traffic + */ + signer_t *signer_in; + + /** + * Signer for outbound traffic + */ + signer_t *signer_out; + + /** + * Multi purpose prf, set key, use it, forget it + */ + prf_t *prf; + + /** + * Prf function for derivating keymat child SAs + */ + prf_t *child_prf; + + /** + * PRF to build outging authentication data + */ + prf_t *auth_build; + + /** + * PRF to verify incoming authentication data + */ + prf_t *auth_verify; + + /** + * NAT status of local host. + */ + bool nat_here; + + /** + * NAT status of remote host. + */ + bool nat_there; + + /** + * Virtual IP on local host, if any + */ + host_t *my_virtual_ip; + + /** + * Virtual IP on remote host, if any + */ + host_t *other_virtual_ip; + + /** + * List of DNS servers installed by us + */ + linked_list_t *dns_servers; + + /** + * Timestamps for this IKE_SA + */ + struct { + /** last IKE message received */ + u_int32_t inbound; + /** last IKE message sent */ + u_int32_t outbound; + /** when IKE_SA became established */ + u_int32_t established; + /** when IKE_SA gets rekeyed */ + u_int32_t rekey; + /** when IKE_SA gets deleted */ + u_int32_t delete; + } time; + + /** + * how many times we have retried so far (keyingtries) + */ + u_int32_t keyingtry; +}; + +/** + * get the time of the latest traffic processed by the kernel + */ +static time_t get_use_time(private_ike_sa_t* this, bool inbound) +{ + iterator_t *iterator; + child_sa_t *child_sa; + time_t latest = 0, use_time; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS) + { + latest = max(latest, use_time); + } + } + iterator->destroy(iterator); + + if (inbound) + { + return max(this->time.inbound, latest); + } + else + { + return max(this->time.outbound, latest); + } +} + +/** + * Implementation of ike_sa_t.get_unique_id + */ +static u_int32_t get_unique_id(private_ike_sa_t *this) +{ + return this->unique_id; +} + +/** + * Implementation of ike_sa_t.get_name. + */ +static char *get_name(private_ike_sa_t *this) +{ + if (this->connection) + { + return this->connection->get_name(this->connection); + } + return "(unnamed)"; +} + +/** + * Implementation of ike_sa_t.get_connection + */ +static connection_t* get_connection(private_ike_sa_t *this) +{ + return this->connection; +} + +/** + * Implementation of ike_sa_t.set_connection + */ +static void set_connection(private_ike_sa_t *this, connection_t *connection) +{ + this->connection = connection; + connection->get_ref(connection); +} + +/** + * Implementation of ike_sa_t.get_policy + */ +static policy_t *get_policy(private_ike_sa_t *this) +{ + return this->policy; +} + +/** + * Implementation of ike_sa_t.set_policy + */ +static void set_policy(private_ike_sa_t *this, policy_t *policy) +{ + policy->get_ref(policy); + this->policy = policy; +} + +/** + * Implementation of ike_sa_t.get_my_host. + */ +static host_t *get_my_host(private_ike_sa_t *this) +{ + return this->my_host; +} + +/** + * Implementation of ike_sa_t.set_my_host. + */ +static void set_my_host(private_ike_sa_t *this, host_t *me) +{ + DESTROY_IF(this->my_host); + this->my_host = me; +} + +/** + * Implementation of ike_sa_t.get_other_host. + */ +static host_t *get_other_host(private_ike_sa_t *this) +{ + return this->other_host; +} + +/** + * Implementation of ike_sa_t.set_other_host. + */ +static void set_other_host(private_ike_sa_t *this, host_t *other) +{ + DESTROY_IF(this->other_host); + this->other_host = other; +} + +/** + * Implementation of ike_sa_t.send_dpd + */ +static status_t send_dpd(private_ike_sa_t *this) +{ + send_dpd_job_t *job; + time_t diff, delay; + + delay = this->connection->get_dpd_delay(this->connection); + + if (delay == 0) + { + /* DPD disabled */ + return SUCCESS; + } + + if (this->task_manager->busy(this->task_manager)) + { + /* an exchange is in the air, no need to start a DPD check */ + diff = 0; + } + else + { + /* check if there was any inbound traffic */ + time_t last_in, now; + last_in = get_use_time(this, TRUE); + now = time(NULL); + diff = now - last_in; + if (diff >= delay) + { + /* to long ago, initiate dead peer detection */ + task_t *task; + + task = (task_t*)ike_dpd_create(TRUE); + diff = 0; + DBG1(DBG_IKE, "sending DPD request"); + + this->task_manager->queue_task(this->task_manager, task); + this->task_manager->initiate(this->task_manager); + } + } + /* recheck in "interval" seconds */ + job = send_dpd_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (delay - diff) * 1000); + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.send_keepalive + */ +static void send_keepalive(private_ike_sa_t *this) +{ + send_keepalive_job_t *job; + time_t last_out, now, diff, interval; + + last_out = get_use_time(this, FALSE); + now = time(NULL); + + diff = now - last_out; + interval = charon->configuration->get_keepalive_interval(charon->configuration); + + if (diff >= interval) + { + packet_t *packet; + chunk_t data; + + packet = packet_create(); + packet->set_source(packet, this->my_host->clone(this->my_host)); + packet->set_destination(packet, this->other_host->clone(this->other_host)); + data.ptr = malloc(1); + data.ptr[0] = 0xFF; + data.len = 1; + packet->set_data(packet, data); + charon->sender->send(charon->sender, packet); + DBG1(DBG_IKE, "sending keep alive"); + diff = 0; + } + job = send_keepalive_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (interval - diff) * 1000); +} + +/** + * Implementation of ike_sa_t.get_state. + */ +static ike_sa_state_t get_state(private_ike_sa_t *this) +{ + return this->state; +} + +/** + * Implementation of ike_sa_t.set_state. + */ +static void set_state(private_ike_sa_t *this, ike_sa_state_t state) +{ + DBG1(DBG_IKE, "IKE_SA state change: %N => %N", + ike_sa_state_names, this->state, + ike_sa_state_names, state); + + switch (state) + { + case IKE_ESTABLISHED: + { + if (this->state == IKE_CONNECTING) + { + job_t *job; + u_int32_t now = time(NULL); + u_int32_t soft, hard; + bool reauth; + + this->time.established = now; + /* start DPD checks */ + send_dpd(this); + + /* schedule rekeying/reauthentication */ + soft = this->connection->get_soft_lifetime(this->connection); + hard = this->connection->get_hard_lifetime(this->connection); + reauth = this->connection->get_reauth(this->connection); + DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds", + reauth ? "reauthentication": "rekeying", soft, hard); + + if (soft) + { + this->time.rekey = now + soft; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth); + charon->event_queue->add_relative(charon->event_queue, job, + soft * 1000); + } + + if (hard) + { + this->time.delete = now + hard; + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->event_queue->add_relative(charon->event_queue, job, + hard * 1000); + } + } + break; + } + case IKE_DELETING: + { + /* delete may fail if a packet gets lost, so set a timeout */ + job_t *job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->event_queue->add_relative(charon->event_queue, job, + charon->configuration->get_half_open_ike_sa_timeout( + charon->configuration)); + break; + } + default: + break; + } + + this->state = state; +} + +/** + * Implementation of ike_sa_t.reset + */ +static void reset(private_ike_sa_t *this) +{ + /* the responder ID is reset, as peer may choose another one */ + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0); + } + + set_state(this, IKE_CREATED); + + this->task_manager->reset(this->task_manager); +} + +/** + * Update connection host, as addresses may change (NAT) + */ +static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) +{ + iterator_t *iterator = NULL; + child_sa_t *child_sa = NULL; + host_diff_t my_diff, other_diff; + + if (this->my_host->is_anyaddr(this->my_host) || + this->other_host->is_anyaddr(this->other_host)) + { + /* on first received message */ + this->my_host->destroy(this->my_host); + this->my_host = me->clone(me); + this->other_host->destroy(this->other_host); + this->other_host = other->clone(other); + return; + } + + my_diff = me->get_differences(me, this->my_host); + other_diff = other->get_differences(other, this->other_host); + + if (!my_diff && !other_diff) + { + return; + } + + if (my_diff) + { + this->my_host->destroy(this->my_host); + this->my_host = me->clone(me); + } + + if (!this->nat_here) + { + /* update without restrictions if we are not NATted */ + if (other_diff) + { + this->other_host->destroy(this->other_host); + this->other_host = other->clone(other); + } + } + else + { + /* if we are natted, only port may change */ + if (other_diff & HOST_DIFF_ADDR) + { + return; + } + else if (other_diff & HOST_DIFF_PORT) + { + this->other_host->set_port(this->other_host, other->get_port(other)); + } + } + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_sa->update_hosts(child_sa, this->my_host, this->other_host, + my_diff, other_diff); + } + iterator->destroy(iterator); +} + +/** + * Implementation of ike_sa_t.generate + */ +static status_t generate_message(private_ike_sa_t *this, message_t *message, + packet_t **packet) +{ + this->time.outbound = time(NULL); + message->set_ike_sa_id(message, this->ike_sa_id); + message->set_destination(message, this->other_host->clone(this->other_host)); + message->set_source(message, this->my_host->clone(this->my_host)); + return message->generate(message, this->crypter_out, this->signer_out, packet); +} + +/** + * send a notify back to the sender + */ +static void send_notify_response(private_ike_sa_t *this, message_t *request, + notify_type_t type) +{ + message_t *response; + packet_t *packet; + + response = message_create(); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, request->get_message_id(request)); + response->add_notify(response, FALSE, type, chunk_empty); + if (this->my_host->is_anyaddr(this->my_host)) + { + this->my_host->destroy(this->my_host); + this->my_host = request->get_destination(request); + this->my_host = this->my_host->clone(this->my_host); + } + if (this->other_host->is_anyaddr(this->other_host)) + { + this->other_host->destroy(this->other_host); + this->other_host = request->get_source(request); + this->other_host = this->other_host->clone(this->other_host); + } + if (generate_message(this, response, &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + } + response->destroy(response); +} + +/** + * Implementation of ike_sa_t.process_message. + */ +static status_t process_message(private_ike_sa_t *this, message_t *message) +{ + status_t status; + bool is_request; + + is_request = message->get_request(message); + + status = message->parse_body(message, this->crypter_in, this->signer_in); + if (status != SUCCESS) + { + + if (is_request) + { + switch (status) + { + case NOT_SUPPORTED: + DBG1(DBG_IKE, "ciritcal unknown payloads found"); + if (is_request) + { + send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD); + } + break; + case PARSE_ERROR: + DBG1(DBG_IKE, "message parsing failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case VERIFY_ERROR: + DBG1(DBG_IKE, "message verification failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case FAILED: + DBG1(DBG_IKE, "integrity check failed"); + /* ignored */ + break; + case INVALID_STATE: + DBG1(DBG_IKE, "found encrypted message, but no keys available"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + default: + break; + } + } + DBG1(DBG_IKE, "%N %s with message ID %d processing failed", + exchange_type_names, message->get_exchange_type(message), + message->get_request(message) ? "request" : "response", + message->get_message_id(message)); + return status; + } + else + { + host_t *me, *other; + + me = message->get_destination(message); + other = message->get_source(message); + + /* if this IKE_SA is virgin, we check for a connection */ + if (this->connection == NULL) + { + job_t *job; + this->connection = charon->connections->get_connection_by_hosts( + charon->connections, me, other); + if (this->connection == NULL) + { + /* no connection found for these hosts, destroy */ + DBG1(DBG_IKE, "no connection found for %H...%H, sending %N", + me, other, notify_type_names, NO_PROPOSAL_CHOSEN); + send_notify_response(this, message, NO_PROPOSAL_CHOSEN); + return DESTROY_ME; + } + /* add a timeout if peer does not establish it completely */ + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, FALSE); + charon->event_queue->add_relative(charon->event_queue, job, + charon->configuration->get_half_open_ike_sa_timeout( + charon->configuration)); + } + + /* check if message is trustworthy, and update connection information */ + if (this->state == IKE_CREATED || + message->get_exchange_type(message) != IKE_SA_INIT) + { + update_hosts(this, me, other); + this->time.inbound = time(NULL); + } + return this->task_manager->process_message(this->task_manager, message); + } +} + +/** + * apply the connection/policy information to this IKE_SA + */ +static void apply_config(private_ike_sa_t *this, + connection_t *connection, policy_t *policy) +{ + host_t *me, *other; + identification_t *my_id, *other_id; + + if (this->connection == NULL && this->policy == NULL) + { + this->connection = connection; + connection->get_ref(connection); + this->policy = policy; + policy->get_ref(policy); + + me = connection->get_my_host(connection); + other = connection->get_other_host(connection); + my_id = policy->get_my_id(policy); + other_id = policy->get_other_id(policy); + set_my_host(this, me->clone(me)); + set_other_host(this, other->clone(other)); + DESTROY_IF(this->my_id); + DESTROY_IF(this->other_id); + this->my_id = my_id->clone(my_id); + this->other_id = other_id->clone(other_id); + } +} + +/** + * Implementation of ike_sa_t.initiate. + */ +static status_t initiate(private_ike_sa_t *this, + connection_t *connection, policy_t *policy) +{ + task_t *task; + + if (this->state == IKE_CREATED) + { + /* if we aren't established/establishing, do so */ + apply_config(this, connection, policy); + + if (this->other_host->is_anyaddr(this->other_host)) + { + SIG(IKE_UP_START, "initiating IKE_SA"); + SIG(IKE_UP_FAILED, "unable to initiate to %%any"); + return DESTROY_ME; + } + + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + } + + task = (task_t*)child_create_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.acquire. + */ +static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) +{ + policy_t *policy; + iterator_t *iterator; + child_sa_t *current, *child_sa = NULL; + task_t *task; + child_create_t *child_create; + + if (this->state == IKE_DELETING) + { + SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); + SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + "IKE_SA is deleting", reqid); + return FAILED; + } + + /* find CHILD_SA */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_reqid(current) == reqid) + { + child_sa = current; + break; + } + } + iterator->destroy(iterator); + if (!child_sa) + { + SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); + SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + "CHILD_SA not found", reqid); + return FAILED; + } + + policy = child_sa->get_policy(child_sa); + + if (this->state == IKE_CREATED) + { + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + } + + child_create = child_create_create(&this->public, policy); + child_create->use_reqid(child_create, reqid); + this->task_manager->queue_task(this->task_manager, (task_t*)child_create); + + return this->task_manager->initiate(this->task_manager); +} + +/** + * compare two lists of traffic selectors for equality + */ +static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2) +{ + bool equals = TRUE; + iterator_t *i1, *i2; + traffic_selector_t *t1, *t2; + + if (l1->get_count(l1) != l2->get_count(l2)) + { + return FALSE; + } + + i1 = l1->create_iterator(l1, TRUE); + i2 = l2->create_iterator(l2, TRUE); + while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2)) + { + if (!t1->equals(t1, t2)) + { + equals = FALSE; + break; + } + } + i1->destroy(i1); + i2->destroy(i2); + return equals; +} + +/** + * Implementation of ike_sa_t.route. + */ +static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy) +{ + child_sa_t *child_sa = NULL; + iterator_t *iterator; + linked_list_t *my_ts, *other_ts; + status_t status; + + SIG(CHILD_ROUTE_START, "routing CHILD_SA"); + + /* check if not already routed*/ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + linked_list_t *my_ts_conf, *other_ts_conf; + + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + iterator->destroy(iterator); + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + SIG(CHILD_ROUTE_FAILED, "CHILD_SA with such a policy already routed"); + return FAILED; + } + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + } + } + iterator->destroy(iterator); + + switch (this->state) + { + case IKE_DELETING: + case IKE_REKEYING: + SIG(CHILD_ROUTE_FAILED, + "unable to route CHILD_SA, as its IKE_SA gets deleted"); + return FAILED; + case IKE_CREATED: + /* apply connection information, we need it to acquire */ + apply_config(this, connection, policy); + break; + case IKE_CONNECTING: + case IKE_ESTABLISHED: + default: + break; + } + + /* install kernel policies */ + child_sa = child_sa_create(this->my_host, this->other_host, + this->my_id, this->other_id, policy, FALSE, 0); + + my_ts = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts = policy->get_other_traffic_selectors(policy, this->other_host); + status = child_sa->add_policies(child_sa, my_ts, other_ts, + policy->get_mode(policy)); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + this->child_sas->insert_last(this->child_sas, child_sa); + SIG(CHILD_ROUTE_SUCCESS, "CHILD_SA routed"); + return status; +} + +/** + * Implementation of ike_sa_t.unroute. + */ +static status_t unroute(private_ike_sa_t *this, policy_t *policy) +{ + iterator_t *iterator; + child_sa_t *child_sa = NULL; + bool found = FALSE; + linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf; + + SIG(CHILD_UNROUTE_START, "unrouting CHILD_SA"); + + /* find CHILD_SA in ROUTED state */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + iterator->remove(iterator); + SIG(CHILD_UNROUTE_SUCCESS, "CHILD_SA unrouted"); + child_sa->destroy(child_sa); + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + found = TRUE; + break; + } + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + } + } + iterator->destroy(iterator); + + if (!found) + { + SIG(CHILD_UNROUTE_FAILED, "CHILD_SA to unroute not found"); + return FAILED; + } + /* if we are not established, and we have no more routed childs, remove whole SA */ + if (this->state == IKE_CREATED && + this->child_sas->get_count(this->child_sas) == 0) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.retransmit. + */ +static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) +{ + this->time.outbound = time(NULL); + if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) + { + policy_t *policy; + child_sa_t* child_sa; + linked_list_t *to_route, *to_restart; + iterator_t *iterator; + + /* send a proper signal to brief interested bus listeners */ + switch (this->state) + { + case IKE_CONNECTING: + { + /* retry IKE_SA_INIT if we have multiple keyingtries */ + u_int32_t tries = this->connection->get_keyingtries(this->connection); + this->keyingtry++; + if (tries == 0 || tries > this->keyingtry) + { + SIG(IKE_UP_FAILED, "peer not responding, trying again " + "(%d/%d) in background ", this->keyingtry + 1, tries); + reset(this); + return this->task_manager->initiate(this->task_manager); + } + SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); + break; + } + case IKE_REKEYING: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + break; + case IKE_DELETING: + SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); + break; + default: + break; + } + + /* summarize how we have to handle each child */ + to_route = linked_list_create(); + to_restart = linked_list_create(); + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + policy = child_sa->get_policy(child_sa); + + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + /* reroute routed CHILD_SAs */ + to_route->insert_last(to_route, policy); + } + else + { + /* use DPD action for established CHILD_SAs */ + switch (policy->get_dpd_action(policy)) + { + case DPD_ROUTE: + to_route->insert_last(to_route, policy); + break; + case DPD_RESTART: + to_restart->insert_last(to_restart, policy); + break; + default: + break; + } + } + } + iterator->destroy(iterator); + + /* create a new IKE_SA if we have to route or to restart */ + if (to_route->get_count(to_route) || to_restart->get_count(to_restart)) + { + private_ike_sa_t *new; + task_t *task; + + new = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new( + charon->ike_sa_manager, TRUE); + + apply_config(new, this->connection, this->policy); + /* use actual used host, not the wildcarded one in connection */ + new->other_host->destroy(new->other_host); + new->other_host = this->other_host->clone(this->other_host); + + /* install routes */ + while (to_route->remove_last(to_route, (void**)&policy) == SUCCESS) + { + route(new, new->connection, policy); + } + + /* restart children */ + if (to_restart->get_count(to_restart)) + { + task = (task_t*)ike_init_create(&new->public, TRUE, NULL); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_natd_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_cert_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_config_create(&new->public, new->policy); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_auth_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + + while (to_restart->remove_last(to_restart, (void**)&policy) == SUCCESS) + { + task = (task_t*)child_create_create(&new->public, policy); + new->task_manager->queue_task(new->task_manager, task); + } + new->task_manager->initiate(new->task_manager); + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public); + } + to_route->destroy(to_route); + to_restart->destroy(to_restart); + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.get_prf. + */ +static prf_t *get_prf(private_ike_sa_t *this) +{ + return this->prf; +} + +/** + * Implementation of ike_sa_t.get_prf. + */ +static prf_t *get_child_prf(private_ike_sa_t *this) +{ + return this->child_prf; +} + +/** + * Implementation of ike_sa_t.get_auth_bild + */ +static prf_t *get_auth_build(private_ike_sa_t *this) +{ + return this->auth_build; +} + +/** + * Implementation of ike_sa_t.get_auth_verify + */ +static prf_t *get_auth_verify(private_ike_sa_t *this) +{ + return this->auth_verify; +} + +/** + * Implementation of ike_sa_t.get_id. + */ +static ike_sa_id_t* get_id(private_ike_sa_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of ike_sa_t.get_my_id. + */ +static identification_t* get_my_id(private_ike_sa_t *this) +{ + return this->my_id; +} + +/** + * Implementation of ike_sa_t.set_my_id. + */ +static void set_my_id(private_ike_sa_t *this, identification_t *me) +{ + DESTROY_IF(this->my_id); + this->my_id = me; +} + +/** + * Implementation of ike_sa_t.get_other_id. + */ +static identification_t* get_other_id(private_ike_sa_t *this) +{ + return this->other_id; +} + +/** + * Implementation of ike_sa_t.set_other_id. + */ +static void set_other_id(private_ike_sa_t *this, identification_t *other) +{ + DESTROY_IF(this->other_id); + this->other_id = other; +} + +/** + * Implementation of ike_sa_t.derive_keys. + */ +static status_t derive_keys(private_ike_sa_t *this, + proposal_t *proposal, chunk_t secret, + chunk_t nonce_i, chunk_t nonce_r, + bool initiator, prf_t *child_prf, prf_t *old_prf) +{ + prf_plus_t *prf_plus; + chunk_t skeyseed, key, nonces, prf_plus_seed; + algorithm_t *algo; + size_t key_size; + crypter_t *crypter_i, *crypter_r; + signer_t *signer_i, *signer_r; + prf_t *prf_i, *prf_r; + u_int8_t spi_i_buf[sizeof(u_int64_t)], spi_r_buf[sizeof(u_int64_t)]; + chunk_t spi_i = chunk_from_buf(spi_i_buf); + chunk_t spi_r = chunk_from_buf(spi_r_buf); + + /* Create SAs general purpose PRF first, we may use it here */ + if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no PSEUDO_RANDOM_FUNCTION");; + return FAILED; + } + this->prf = prf_create(algo->algorithm); + if (this->prf == NULL) + { + DBG1(DBG_IKE, "key derivation failed: PSEUDO_RANDOM_FUNCTION " + "%N not supported!", pseudo_random_function_names, algo->algorithm); + return FAILED; + } + + DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); + nonces = chunk_cat("cc", nonce_i, nonce_r); + *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); + *((u_int64_t*)spi_r.ptr) = this->ike_sa_id->get_responder_spi(this->ike_sa_id); + prf_plus_seed = chunk_cat("ccc", nonces, spi_i, spi_r); + + /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) + * + * if we are rekeying, SKEYSEED is built on another way + */ + if (child_prf == NULL) /* not rekeying */ + { + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + this->prf->set_key(this->prf, nonces); + this->prf->allocate_bytes(this->prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + this->prf->set_key(this->prf, skeyseed); + chunk_free(&skeyseed); + chunk_free(&secret); + prf_plus = prf_plus_create(this->prf, prf_plus_seed); + } + else + { + /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) + * use OLD SAs PRF functions for both prf_plus and prf */ + secret = chunk_cat("mc", secret, nonces); + child_prf->allocate_bytes(child_prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + old_prf->set_key(old_prf, skeyseed); + chunk_free(&skeyseed); + chunk_free(&secret); + prf_plus = prf_plus_create(old_prf, prf_plus_seed); + } + chunk_free(&nonces); + chunk_free(&prf_plus_seed); + + /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ + + /* SK_d is used for generating CHILD_SA key mat => child_prf */ + proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); + this->child_prf = prf_create(algo->algorithm); + key_size = this->child_prf->get_key_size(this->child_prf); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_d secret %B", &key); + this->child_prf->set_key(this->child_prf, key); + chunk_free(&key); + + /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ + if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no INTEGRITY_ALGORITHM"); + return FAILED; + } + signer_i = signer_create(algo->algorithm); + signer_r = signer_create(algo->algorithm); + if (signer_i == NULL || signer_r == NULL) + { + DBG1(DBG_IKE, "key derivation failed: INTEGRITY_ALGORITHM " + "%N not supported!", integrity_algorithm_names ,algo->algorithm); + return FAILED; + } + key_size = signer_i->get_key_size(signer_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ai secret %B", &key); + signer_i->set_key(signer_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ar secret %B", &key); + signer_r->set_key(signer_r, key); + chunk_free(&key); + + if (initiator) + { + this->signer_in = signer_r; + this->signer_out = signer_i; + } + else + { + this->signer_in = signer_i; + this->signer_out = signer_r; + } + + /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no ENCRYPTION_ALGORITHM"); + return FAILED; + } + crypter_i = crypter_create(algo->algorithm, algo->key_size / 8); + crypter_r = crypter_create(algo->algorithm, algo->key_size / 8); + if (crypter_i == NULL || crypter_r == NULL) + { + DBG1(DBG_IKE, "key derivation failed: ENCRYPTION_ALGORITHM " + "%N (key size %d) not supported!", + encryption_algorithm_names, algo->algorithm, algo->key_size); + return FAILED; + } + key_size = crypter_i->get_key_size(crypter_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ei secret %B", &key); + crypter_i->set_key(crypter_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_er secret %B", &key); + crypter_r->set_key(crypter_r, key); + chunk_free(&key); + + if (initiator) + { + this->crypter_in = crypter_r; + this->crypter_out = crypter_i; + } + else + { + this->crypter_in = crypter_i; + this->crypter_out = crypter_r; + } + + /* SK_pi/SK_pr used for authentication => prf_auth_i, prf_auth_r */ + proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); + prf_i = prf_create(algo->algorithm); + prf_r = prf_create(algo->algorithm); + + key_size = prf_i->get_key_size(prf_i); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pi secret %B", &key); + prf_i->set_key(prf_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pr secret %B", &key); + prf_r->set_key(prf_r, key); + chunk_free(&key); + + if (initiator) + { + this->auth_verify = prf_r; + this->auth_build = prf_i; + } + else + { + this->auth_verify = prf_i; + this->auth_build = prf_r; + } + + /* all done, prf_plus not needed anymore */ + prf_plus->destroy(prf_plus); + + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.add_child_sa. + */ +static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) +{ + this->child_sas->insert_last(this->child_sas, child_sa); +} + +/** + * Implementation of ike_sa_t.get_child_sa. + */ +static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound) +{ + iterator_t *iterator; + child_sa_t *current, *found = NULL; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_spi(current, inbound) == spi && + current->get_protocol(current) == protocol) + { + found = current; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of ike_sa_t.create_child_sa_iterator. + */ +static iterator_t* create_child_sa_iterator(private_ike_sa_t *this) +{ + return this->child_sas->create_iterator(this->child_sas, TRUE); +} + +/** + * Implementation of ike_sa_t.rekey_child_sa. + */ +static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + child_sa_t *child_sa; + child_rekey_t *child_rekey; + + child_sa = get_child_sa(this, protocol, spi, TRUE); + if (child_sa) + { + child_rekey = child_rekey_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_rekey->task); + return this->task_manager->initiate(this->task_manager); + } + return FAILED; +} + +/** + * Implementation of ike_sa_t.delete_child_sa. + */ +static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + child_sa_t *child_sa; + child_delete_t *child_delete; + + child_sa = get_child_sa(this, protocol, spi, TRUE); + if (child_sa) + { + child_delete = child_delete_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_delete->task); + return this->task_manager->initiate(this->task_manager); + } + return FAILED; +} + +/** + * Implementation of ike_sa_t.destroy_child_sa. + */ +static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi) +{ + iterator_t *iterator; + child_sa_t *child_sa; + status_t status = NOT_FOUND; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_protocol(child_sa) == protocol && + child_sa->get_spi(child_sa, TRUE) == spi) + { + child_sa->destroy(child_sa); + iterator->remove(iterator); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of public_ike_sa_t.delete. + */ +static status_t delete_(private_ike_sa_t *this) +{ + ike_delete_t *ike_delete; + + switch (this->state) + { + case IKE_ESTABLISHED: + DBG1(DBG_IKE, "deleting IKE_SA"); + /* do not log when rekeyed */ + case IKE_REKEYING: + ike_delete = ike_delete_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, &ike_delete->task); + return this->task_manager->initiate(this->task_manager); + default: + DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification", + ike_sa_state_names, this->state); + break; + } + return DESTROY_ME; +} + +/** + * Implementation of ike_sa_t.rekey. + */ +static status_t rekey(private_ike_sa_t *this) +{ + ike_rekey_t *ike_rekey; + + ike_rekey = ike_rekey_create(&this->public, TRUE); + + this->task_manager->queue_task(this->task_manager, &ike_rekey->task); + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.reestablish + */ +static void reestablish(private_ike_sa_t *this) +{ + private_ike_sa_t *other; + iterator_t *iterator; + child_sa_t *child_sa; + policy_t *policy; + task_t *task; + job_t *job; + + other = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new( + charon->ike_sa_manager, TRUE); + + apply_config(other, this->connection, this->policy); + other->other_host->destroy(other->other_host); + other->other_host = this->other_host->clone(this->other_host); + + if (this->state == IKE_ESTABLISHED) + { + task = (task_t*)ike_init_create(&other->public, TRUE, NULL); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_natd_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_cert_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_config_create(&other->public, other->policy); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_auth_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + } + + other->task_manager->adopt_tasks(other->task_manager, this->task_manager); + + /* Create task for established children, adopt routed children directly */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while(iterator->iterate(iterator, (void**)&child_sa)) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_ROUTED: + { + iterator->remove(iterator); + other->child_sas->insert_first(other->child_sas, child_sa); + break; + } + default: + { + policy = child_sa->get_policy(child_sa); + task = (task_t*)child_create_create(&other->public, policy); + other->task_manager->queue_task(other->task_manager, task); + break; + } + } + } + iterator->destroy(iterator); + + other->task_manager->initiate(other->task_manager); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public); + + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->job_queue->add(charon->job_queue, job); +} + +/** + * Implementation of ike_sa_t.inherit. + */ +static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) +{ + child_sa_t *child_sa; + host_t *ip; + + /* apply hosts and ids */ + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + this->my_host = other->my_host->clone(other->my_host); + this->other_host = other->other_host->clone(other->other_host); + this->my_id = other->my_id->clone(other->my_id); + this->other_id = other->other_id->clone(other->other_id); + + /* apply virtual assigned IPs... */ + if (other->my_virtual_ip) + { + this->my_virtual_ip = other->my_virtual_ip; + other->my_virtual_ip = NULL; + } + if (other->other_virtual_ip) + { + this->other_virtual_ip = other->other_virtual_ip; + other->other_virtual_ip = NULL; + } + + /* ... and DNS servers */ + while (other->dns_servers->remove_last(other->dns_servers, + (void**)&ip) == SUCCESS) + { + this->dns_servers->insert_first(this->dns_servers, ip); + } + + /* adopt all children */ + while (other->child_sas->remove_last(other->child_sas, + (void**)&child_sa) == SUCCESS) + { + this->child_sas->insert_first(this->child_sas, (void*)child_sa); + } + + /* move pending tasks to the new IKE_SA */ + this->task_manager->adopt_tasks(this->task_manager, other->task_manager); + + /* we have to initate here, there may be new tasks to handle */ + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.is_natt_enabled. + */ +static bool is_natt_enabled(private_ike_sa_t *this) +{ + return this->nat_here || this->nat_there; +} + +/** + * Implementation of ike_sa_t.enable_natt. + */ +static void enable_natt(private_ike_sa_t *this, bool local) +{ + if (local) + { + DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives"); + this->nat_here = TRUE; + send_keepalive(this); + } + else + { + DBG1(DBG_IKE, "remote host is behind NAT"); + this->nat_there = TRUE; + } +} + +/** + * Implementation of ike_sa_t.set_virtual_ip + */ +static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip) +{ + if (local) + { + DBG1(DBG_IKE, "installing new virtual IP %H", ip); + if (this->my_virtual_ip) + { + DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, + this->my_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + if (charon->kernel_interface->add_ip(charon->kernel_interface, ip, + this->my_host) == SUCCESS) + { + this->my_virtual_ip = ip->clone(ip); + } + else + { + DBG1(DBG_IKE, "installing virtual IP %H failed", ip); + this->my_virtual_ip = NULL; + } + } + else + { + DESTROY_IF(this->other_virtual_ip); + this->other_virtual_ip = ip->clone(ip); + } +} + +/** + * Implementation of ike_sa_t.get_virtual_ip + */ +static host_t* get_virtual_ip(private_ike_sa_t *this, bool local) +{ + if (local) + { + return this->my_virtual_ip; + } + else + { + return this->other_virtual_ip; + } +} + +/** + * Implementation of ike_sa_t.remove_dns_server + */ +static void remove_dns_servers(private_ike_sa_t *this) +{ + FILE *file; + struct stat stats; + chunk_t contents, line, orig_line, token; + char string[INET6_ADDRSTRLEN]; + host_t *ip; + iterator_t *iterator; + + if (this->dns_servers->get_count(this->dns_servers) == 0) + { + /* don't touch anything if we have no nameservers installed */ + return; + } + + file = fopen(RESOLV_CONF, "r"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca((size_t)stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE); + while (fetchline(&contents, &line)) + { + bool found = FALSE; + orig_line = line; + if (extract_token(&token, ' ', &line) && + strncasecmp(token.ptr, "nameserver", token.len) == 0) + { + if (!extract_token(&token, ' ', &line)) + { + token = line; + } + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&ip)) + { + snprintf(string, sizeof(string), "%H", ip); + if (strlen(string) == token.len && + strncmp(token.ptr, string, token.len) == 0) + { + iterator->remove(iterator); + ip->destroy(ip); + found = TRUE; + break; + } + } + } + + if (!found) + { + /* write line untouched back to file */ + fwrite(orig_line.ptr, orig_line.len, 1, file); + fprintf(file, "\n"); + } + } + iterator->destroy(iterator); + fclose(file); +} + +/** + * Implementation of ike_sa_t.add_dns_server + */ +static void add_dns_server(private_ike_sa_t *this, host_t *dns) +{ + FILE *file; + struct stat stats; + chunk_t contents; + + DBG1(DBG_IKE, "installing DNS server %H", dns); + + file = fopen(RESOLV_CONF, "a+"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca(stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n", + dns, this->other_id) < 0) + { + DBG1(DBG_IKE, "unable to write DNS configuration: %m"); + } + else + { + this->dns_servers->insert_last(this->dns_servers, dns->clone(dns)); + } + fwrite(contents.ptr, contents.len, 1, file); + + fclose(file); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + int written = 0; + bool reauth = FALSE; + private_ike_sa_t *this = *((private_ike_sa_t**)(args[0])); + + if (this->connection) + { + reauth = this->connection->get_reauth(this->connection); + } + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this), + this->unique_id, ike_sa_state_names, this->state, + this->my_host, this->my_id, this->other_host, + this->other_id); + written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds", + get_name(this), this->unique_id, this->ike_sa_id, + this->connection && reauth? "reauthentication":"rekeying", + this->time.rekey - time(NULL)); + + if (info->alt) + { + + } + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IKE_SA, print, arginfo_ptr); +} + +/** + * Implementation of ike_sa_t.destroy. + */ +static void destroy(private_ike_sa_t *this) +{ + this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); + + DESTROY_IF(this->crypter_in); + DESTROY_IF(this->crypter_out); + DESTROY_IF(this->signer_in); + DESTROY_IF(this->signer_out); + DESTROY_IF(this->prf); + DESTROY_IF(this->child_prf); + DESTROY_IF(this->auth_verify); + DESTROY_IF(this->auth_build); + + if (this->my_virtual_ip) + { + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, this->my_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + DESTROY_IF(this->other_virtual_ip); + + remove_dns_servers(this); + this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy)); + + DESTROY_IF(this->my_host); + DESTROY_IF(this->other_host); + DESTROY_IF(this->my_id); + DESTROY_IF(this->other_id); + + DESTROY_IF(this->connection); + DESTROY_IF(this->policy); + + this->ike_sa_id->destroy(this->ike_sa_id); + this->task_manager->destroy(this->task_manager); + free(this); +} + +/* + * Described in header. + */ +ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) +{ + private_ike_sa_t *this = malloc_thing(private_ike_sa_t); + static u_int32_t unique_id = 0; + + /* Public functions */ + this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state; + this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state; + this->public.get_name = (char*(*)(ike_sa_t*))get_name; + this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; + this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate; + this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route; + this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute; + this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire; + this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection; + this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection; + this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy; + this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy; + this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; + this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; + this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host; + this->public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host; + this->public.set_other_host = (void(*)(ike_sa_t*,host_t*)) set_other_host; + this->public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id; + this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id; + this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; + this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id; + this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit; + this->public.delete = (status_t(*)(ike_sa_t*))delete_; + this->public.destroy = (void(*)(ike_sa_t*))destroy; + this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; + this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; + this->public.get_prf = (prf_t *(*) (ike_sa_t *)) get_prf; + this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf; + this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify; + this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build; + this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; + this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; + this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; + this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator; + this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa; + this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa; + this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; + this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; + this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; + this->public.rekey = (status_t(*)(ike_sa_t*))rekey; + this->public.reestablish = (void(*)(ike_sa_t*))reestablish; + this->public.inherit = (status_t(*)(ike_sa_t*,ike_sa_t*))inherit; + this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message; + this->public.reset = (void(*)(ike_sa_t*))reset; + this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id; + this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip; + this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip; + this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server; + + /* initialize private fields */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->child_sas = linked_list_create(); + this->my_host = host_create_any(AF_INET); + this->other_host = host_create_any(AF_INET); + this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); + this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); + this->crypter_in = NULL; + this->crypter_out = NULL; + this->signer_in = NULL; + this->signer_out = NULL; + this->prf = NULL; + this->auth_verify = NULL; + this->auth_build = NULL; + this->child_prf = NULL; + this->nat_here = FALSE; + this->nat_there = FALSE; + this->state = IKE_CREATED; + this->time.inbound = this->time.outbound = time(NULL); + this->time.established = 0; + this->time.rekey = 0; + this->time.delete = 0; + this->connection = NULL; + this->policy = NULL; + this->task_manager = task_manager_create(&this->public); + this->unique_id = ++unique_id; + this->my_virtual_ip = NULL; + this->other_virtual_ip = NULL; + this->dns_servers = linked_list_create(); + this->keyingtry = 0; + + return &this->public; +} diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h new file mode 100644 index 000000000..604ec94a9 --- /dev/null +++ b/src/charon/sa/ike_sa.h @@ -0,0 +1,649 @@ +/** + * @file ike_sa.h + * + * @brief Interface of ike_sa_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef IKE_SA_H_ +#define IKE_SA_H_ + +typedef enum ike_sa_state_t ike_sa_state_t; +typedef struct ike_sa_t ike_sa_t; + +#include <library.h> +#include <encoding/message.h> +#include <encoding/payloads/proposal_substructure.h> +#include <sa/ike_sa_id.h> +#include <sa/child_sa.h> +#include <sa/tasks/task.h> +#include <config/configuration.h> +#include <utils/randomizer.h> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <config/connections/connection.h> +#include <config/policies/policy.h> +#include <config/proposal.h> + +/** + * @brief State of an IKE_SA. + * + * An IKE_SA passes various states in its lifetime. A newly created + * SA is in the state CREATED. + * @verbatim + +----------------+ + ¦ SA_CREATED ¦ + +----------------+ + ¦ + on initiate()---> ¦ <----- on IKE_SA_INIT received + V + +----------------+ + ¦ SA_CONNECTING ¦ + +----------------+ + ¦ + ¦ <----- on IKE_AUTH successfully completed + V + +----------------+ + ¦ SA_ESTABLISHED ¦-------------------------+ <-- on rekeying + +----------------+ ¦ + ¦ V + on delete()---> ¦ <----- on IKE_SA +-------------+ + ¦ delete request ¦ SA_REKEYING ¦ + ¦ received +-------------+ + V ¦ + +----------------+ ¦ + ¦ SA_DELETING ¦<------------------------+ <-- after rekeying + +----------------+ + ¦ + ¦ <----- after delete() acknowledged + ¦ + \V/ + X + / \ + @endverbatim + * + * @ingroup sa + */ +enum ike_sa_state_t { + + /** + * IKE_SA just got created, but is not initiating nor responding yet. + */ + IKE_CREATED, + + /** + * IKE_SA gets initiated actively or passively + */ + IKE_CONNECTING, + + /** + * IKE_SA is fully established + */ + IKE_ESTABLISHED, + + /** + * IKE_SA rekeying in progress + */ + IKE_REKEYING, + + /** + * IKE_SA is in progress of deletion + */ + IKE_DELETING, +}; + +/** + * enum names for ike_sa_state_t. + */ +extern enum_name_t *ike_sa_state_names; + +/** + * @brief Class ike_sa_t representing an IKE_SA. + * + * An IKE_SA contains crypto information related to a connection + * with a peer. It contains multiple IPsec CHILD_SA, for which + * it is responsible. All traffic is handled by an IKE_SA, using + * the task manager and its tasks. + * + * @b Constructors: + * - ike_sa_create() + * + * @ingroup sa + */ +struct ike_sa_t { + + /** + * @brief Get the id of the SA. + * + * Returned ike_sa_id_t object is not getting cloned! + * + * @param this calling object + * @return ike_sa's ike_sa_id_t + */ + ike_sa_id_t* (*get_id) (ike_sa_t *this); + + /** + * @brief Get the numerical ID uniquely defining this IKE_SA. + * + * @param this calling object + * @return unique ID + */ + u_int32_t (*get_unique_id) (ike_sa_t *this); + + /** + * @brief Get the state of the IKE_SA. + * + * @param this calling object + * @return state of the IKE_SA + */ + ike_sa_state_t (*get_state) (ike_sa_t *this); + + /** + * @brief Set the state of the IKE_SA. + * + * @param this calling object + * @param state state to set for the IKE_SA + */ + void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa); + + /** + * @brief Get the name of the connection this IKE_SA uses. + * + * @param this calling object + * @return name + */ + char* (*get_name) (ike_sa_t *this); + + /** + * @brief Get the own host address. + * + * @param this calling object + * @return host address + */ + host_t* (*get_my_host) (ike_sa_t *this); + + /** + * @brief Set the own host address. + * + * @param this calling object + * @param me host address + */ + void (*set_my_host) (ike_sa_t *this, host_t *me); + + /** + * @brief Get the other peers host address. + * + * @param this calling object + * @return host address + */ + host_t* (*get_other_host) (ike_sa_t *this); + + /** + * @brief Set the others host address. + * + * @param this calling object + * @param other host address + */ + void (*set_other_host) (ike_sa_t *this, host_t *other); + + /** + * @brief Get the own identification. + * + * @param this calling object + * @return identification + */ + identification_t* (*get_my_id) (ike_sa_t *this); + + /** + * @brief Set the own identification. + * + * @param this calling object + * @param me identification + */ + void (*set_my_id) (ike_sa_t *this, identification_t *me); + + /** + * @brief Get the other peers identification. + * + * @param this calling object + * @return identification + */ + identification_t* (*get_other_id) (ike_sa_t *this); + + /** + * @brief Set the other peers identification. + * + * @param this calling object + * @param other identification + */ + void (*set_other_id) (ike_sa_t *this, identification_t *other); + + /** + * @brief Get the connection used by this IKE_SA. + * + * @param this calling object + * @return connection + */ + connection_t* (*get_connection) (ike_sa_t *this); + + /** + * @brief Set the connection to use with this IKE_SA. + * + * @param this calling object + * @param connection connection to use + */ + void (*set_connection) (ike_sa_t *this, connection_t* connection); + + /** + * @brief Get the policy used by this IKE_SA. + * + * @param this calling object + * @return policy + */ + policy_t* (*get_policy) (ike_sa_t *this); + + /** + * @brief Set the policy to use with this IKE_SA. + * + * @param this calling object + * @param policy policy to use + */ + void (*set_policy) (ike_sa_t *this, policy_t *policy); + + /** + * @brief Initiate a new connection. + * + * The policy/connection is owned by the IKE_SA after the call, so + * do not modify or destroy it. + * + * @param this calling object + * @param connection connection to initiate + * @param policy policy to set up + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*initiate) (ike_sa_t *this, connection_t *connection, policy_t *policy); + + /** + * @brief Route a policy in the kernel. + * + * Installs the policies in the kernel. If traffic matches, + * the kernel requests connection setup from the IKE_SA via acquire(). + * + * @param this calling object + * @param connection connection definition used for routing + * @param policy policy to route + * @return + * - SUCCESS if routed successfully + * - FAILED if routing failed + */ + status_t (*route) (ike_sa_t *this, connection_t *connection, policy_t *policy); + + /** + * @brief Unroute a policy in the kernel previously routed. + * + * @param this calling object + * @param policy policy to route + * @return + * - SUCCESS if route removed + * - DESTROY_ME if last route was removed from + * an IKE_SA which was not established + */ + status_t (*unroute) (ike_sa_t *this, policy_t *policy); + + /** + * @brief Acquire connection setup for a policy. + * + * If an installed policy raises an acquire, the kernel calls + * this function to establish the CHILD_SA (and maybe the IKE_SA). + * + * @param this calling object + * @param reqid reqid of the CHILD_SA the policy belongs to. + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*acquire) (ike_sa_t *this, u_int32_t reqid); + + /** + * @brief Initiates the deletion of an IKE_SA. + * + * Sends a delete message to the remote peer and waits for + * its response. If the response comes in, or a timeout occurs, + * the IKE SA gets deleted. + * + * @param this calling object + * @return + * - SUCCESS if deletion is initialized + * - INVALID_STATE, if the IKE_SA is not in + * an established state and can not be + * delete (but destroyed). + */ + status_t (*delete) (ike_sa_t *this); + + /** + * @brief Processes a incoming IKEv2-Message. + * + * Message processing may fail. If a critical failure occurs, + * process_message() return DESTROY_ME. Then the caller must + * destroy the IKE_SA immediatly, as it is unusable. + * + * @param this calling object + * @param message message to process + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted + */ + status_t (*process_message) (ike_sa_t *this, message_t *message); + + /** + * @brief Generate a IKE message to send it to the peer. + * + * This method generates all payloads in the message and encrypts/signs + * the packet. + * + * @param this calling object + * @param message message to generate + * @param packet generated output packet + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted + */ + status_t (*generate_message) (ike_sa_t *this, message_t *message, + packet_t **packet); + + /** + * @brief Retransmits a request. + * + * @param this calling object + * @param message_id ID of the request to retransmit + * @return + * - SUCCESS + * - NOT_FOUND if request doesn't have to be retransmited + */ + status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); + + /** + * @brief Sends a DPD request to the peer. + * + * To check if a peer is still alive, periodic + * empty INFORMATIONAL messages are sent if no + * other traffic was received. + * + * @param this calling object + * @return + * - SUCCESS + * - DESTROY_ME, if peer did not respond + */ + status_t (*send_dpd) (ike_sa_t *this); + + /** + * @brief Sends a keep alive packet. + * + * To refresh NAT tables in a NAT router + * between the peers, periodic empty + * UDP packets are sent if no other traffic + * was sent. + * + * @param this calling object + */ + void (*send_keepalive) (ike_sa_t *this); + + /** + * @brief Check if NAT traversal is enabled for this IKE_SA. + * + * @param this calling object + * @return TRUE if NAT traversal enabled + */ + bool (*is_natt_enabled) (ike_sa_t *this); + + /** + * @brief Enable NAT detection for this IKE_SA. + * + * If a Network address translation is detected with + * NAT_DETECTION notifys, a SA must switch to ports + * 4500. To enable this behavior, call enable_natt(). + * It is relevant which peer is NATted, this is specified + * with the "local" parameter. Call it twice when both + * are NATted. + * + * @param this calling object + * @param local TRUE, if we are NATted, FALSE if other + */ + void (*enable_natt) (ike_sa_t *this, bool local); + + /** + * @brief Derive all keys and create the transforms for IKE communication. + * + * Keys are derived using the diffie hellman secret, nonces and internal + * stored SPIs. + * Key derivation differs when an IKE_SA is set up to replace an + * existing IKE_SA (rekeying). The SK_d key from the old IKE_SA + * is included in the derivation process. + * + * @param this calling object + * @param proposal proposal which contains algorithms to use + * @param secret secret derived from DH exchange, gets freed + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @param initiator TRUE if initiator, FALSE otherwise + * @param child_prf PRF with SK_d key when rekeying, NULL otherwise + * @param old_prf general purpose PRF of old SA when rekeying + */ + status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret, + chunk_t nonce_i, chunk_t nonce_r, + bool initiator, prf_t *child_prf, prf_t *old_prf); + + /** + * @brief Get the multi purpose prf. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf) (ike_sa_t *this); + + /** + * @brief Get the prf-object, which is used to derive keys for child SAs. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_child_prf) (ike_sa_t *this); + + /** + * @brief Get the prf to build outgoing authentication data. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_auth_build) (ike_sa_t *this); + + /** + * @brief Get the prf to verify incoming authentication data. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_auth_verify) (ike_sa_t *this); + + /** + * @brief Associates a child SA to this IKE SA + * + * @param this calling object + * @param child_sa child_sa to add + */ + void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); + + /** + * @brief Get a CHILD_SA identified by protocol and SPI. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi SPI of the CHILD_SA + * @param inbound TRUE if SPI is inbound, FALSE if outbound + * @return child_sa, or NULL if none found + */ + child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound); + + /** + * @brief Create an iterator over all CHILD_SAs. + * + * @param this calling object + * @return iterator + */ + iterator_t* (*create_child_sa_iterator) (ike_sa_t *this); + + /** + * @brief Rekey the CHILD SA with the specified reqid. + * + * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if rekeying initiated + */ + status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Close the CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA, deletes it and + * notify's the remote peer about the delete. The associated + * states and policies in the kernel get deleted, if they exist. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if delete message sent + */ + status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Destroy a CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA and destroys it. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS + */ + status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Rekey the IKE_SA. + * + * Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA. + * + * @param this calling object + * @return - SUCCESS, if IKE_SA rekeying initiated + */ + status_t (*rekey) (ike_sa_t *this); + + /** + * @brief Restablish the IKE_SA. + * + * Create a completely new IKE_SA with authentication, recreates all children + * within the IKE_SA, but lets the old IKE_SA untouched. + * + * @param this calling object + */ + void (*reestablish) (ike_sa_t *this); + + /** + * @brief Set the virtual IP to use for this IKE_SA and its children. + * + * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same + * lifetime as the IKE_SA. + * + * @param this calling object + */ + void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); + + /** + * @brief Get the virtual IP configured. + * + * @param this calling object + * @param local TRUE to get local virtual IP, FALSE for remote + */ + host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); + + /** + * @brief Add a DNS server to the system. + * + * An IRAS may send a DNS server. To use it, it is installed on the + * system. The DNS entry has a lifetime until the IKE_SA gets closed. + * + * @param this calling object + * @param dns DNS server to install on the system + */ + void (*add_dns_server) (ike_sa_t *this, host_t *dns); + + /** + * @brief Inherit all attributes of other to this after rekeying. + * + * When rekeying is completed, all CHILD_SAs, the virtual IP and all + * outstanding tasks are moved from other to this. + * As this call may initiate inherited tasks, a status is returned. + * + * @param this calling object + * @param other other task to inherit from + * @return DESTROY_ME if initiation of inherited task failed + */ + status_t (*inherit) (ike_sa_t *this, ike_sa_t *other); + + /** + * @brief Reset the IKE_SA, useable when initiating fails + * + * @param this calling object + */ + void (*reset) (ike_sa_t *this); + + /** + * @brief Destroys a ike_sa_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_t *this); +}; + +/** + * @brief Creates an ike_sa_t object with a specific ID. + * + * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA + * @return ike_sa_t object + * + * @ingroup sa + */ +ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id); + +#endif /* IKE_SA_H_ */ diff --git a/src/charon/sa/ike_sa_id.c b/src/charon/sa/ike_sa_id.c new file mode 100644 index 000000000..c143fc0ba --- /dev/null +++ b/src/charon/sa/ike_sa_id.c @@ -0,0 +1,215 @@ +/** + * @file ike_sa_id.c + * + * @brief Implementation of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "ike_sa_id.h" + +#include <printf.h> +#include <stdio.h> + + +typedef struct private_ike_sa_id_t private_ike_sa_id_t; + +/** + * Private data of an ike_sa_id_t object. + */ +struct private_ike_sa_id_t { + /** + * Public interface of ike_sa_id_t. + */ + ike_sa_id_t public; + + /** + * SPI of Initiator. + */ + u_int64_t initiator_spi; + + /** + * SPI of Responder. + */ + u_int64_t responder_spi; + + /** + * Role for specific IKE_SA. + */ + bool is_initiator_flag; +}; + +/** + * Implementation of ike_sa_id_t.set_responder_spi. + */ +static void set_responder_spi (private_ike_sa_id_t *this, u_int64_t responder_spi) +{ + this->responder_spi = responder_spi; +} + +/** + * Implementation of ike_sa_id_t.set_initiator_spi. + */ +static void set_initiator_spi(private_ike_sa_id_t *this, u_int64_t initiator_spi) +{ + this->initiator_spi = initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_ike_sa_id_t *this) +{ + return this->initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_responder_spi. + */ +static u_int64_t get_responder_spi (private_ike_sa_id_t *this) +{ + return this->responder_spi; +} + +/** + * Implementation of ike_sa_id_t.equals. + */ +static bool equals (private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + if (other == NULL) + { + return FALSE; + } + if ((this->is_initiator_flag == other->is_initiator_flag) && + (this->initiator_spi == other->initiator_spi) && + (this->responder_spi == other->responder_spi)) + { + /* private_ike_sa_id's are equal */ + return TRUE; + } + else + { + /* private_ike_sa_id's are not equal */ + return FALSE; + } +} + +/** + * Implementation of ike_sa_id_t.replace_values. + */ +static void replace_values(private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + this->initiator_spi = other->initiator_spi; + this->responder_spi = other->responder_spi; + this->is_initiator_flag = other->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.is_initiator. + */ +static bool is_initiator(private_ike_sa_id_t *this) +{ + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.switch_initiator. + */ +static bool switch_initiator(private_ike_sa_id_t *this) +{ + if (this->is_initiator_flag) + { + this->is_initiator_flag = FALSE; + } + else + { + this->is_initiator_flag = TRUE; + } + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.clone. + */ +static ike_sa_id_t* clone_(private_ike_sa_id_t *this) +{ + return ike_sa_id_create(this->initiator_spi, this->responder_spi, this->is_initiator_flag); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_ike_sa_id_t *this = *((private_ike_sa_id_t**)(args[0])); + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + return fprintf(stream, "0x%0llx_i%s 0x%0llx_r%s", + this->initiator_spi, + this->is_initiator_flag ? "*" : "", + this->responder_spi, + this->is_initiator_flag ? "" : "*"); +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IKE_SA_ID, print, arginfo_ptr); +} + +/** + * Implementation of ike_sa_id_t.destroy. + */ +static void destroy(private_ike_sa_id_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiator_flag) +{ + private_ike_sa_id_t *this = malloc_thing(private_ike_sa_id_t); + + /* public functions */ + this->public.set_responder_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_responder_spi; + this->public.set_initiator_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(ike_sa_id_t*)) get_responder_spi; + this->public.get_initiator_spi = (u_int64_t(*)(ike_sa_id_t*)) get_initiator_spi; + this->public.equals = (bool(*)(ike_sa_id_t*,ike_sa_id_t*)) equals; + this->public.replace_values = (void(*)(ike_sa_id_t*,ike_sa_id_t*)) replace_values; + this->public.is_initiator = (bool(*)(ike_sa_id_t*)) is_initiator; + this->public.switch_initiator = (bool(*)(ike_sa_id_t*)) switch_initiator; + this->public.clone = (ike_sa_id_t*(*)(ike_sa_id_t*)) clone_; + this->public.destroy = (void(*)(ike_sa_id_t*))destroy; + + /* private data */ + this->initiator_spi = initiator_spi; + this->responder_spi = responder_spi; + this->is_initiator_flag = is_initiator_flag; + + return &this->public; +} diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h new file mode 100644 index 000000000..0606b7222 --- /dev/null +++ b/src/charon/sa/ike_sa_id.h @@ -0,0 +1,147 @@ +/** + * @file ike_sa_id.h + * + * @brief Interface of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + + +#ifndef IKE_SA_ID_H_ +#define IKE_SA_ID_H_ + +typedef struct ike_sa_id_t ike_sa_id_t; + +#include <library.h> + + +/** + * @brief An object of type ike_sa_id_t is used to identify an IKE_SA. + * + * An IKE_SA is identified by its initiator and responder spi's. + * Additionaly it contains the role of the actual running IKEv2-Daemon + * for the specific IKE_SA (original initiator or responder). + * + * @b Constructors: + * - ike_sa_id_create() + * + * @ingroup sa + */ +struct ike_sa_id_t { + + /** + * @brief Set the SPI of the responder. + * + * This function is called when a request or reply of a IKE_SA_INIT is received. + * + * @param this calling object + * @param responder_spi SPI of responder to set + */ + void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi); + + /** + * @brief Set the SPI of the initiator. + * + * @param this calling object + * @param initiator_spi SPI to set + */ + void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi); + + /** + * @brief Get the initiator SPI. + * + * @param this calling object + * @return SPI of the initiator + */ + u_int64_t (*get_initiator_spi) (ike_sa_id_t *this); + + /** + * @brief Get the responder SPI. + * + * @param this calling object + * @return SPI of the responder + */ + u_int64_t (*get_responder_spi) (ike_sa_id_t *this); + + /** + * @brief Check if two ike_sa_id_t objects are equal. + * + * Two ike_sa_id_t objects are equal if both SPI values and the role matches. + * + * @param this calling object + * @param other ike_sa_id_t object to check if equal + * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise + */ + bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Replace all values of a given ike_sa_id_t object with values. + * from another ike_sa_id_t object. + * + * After calling this function, both objects are equal. + * + * @param this calling object + * @param other ike_sa_id_t object from which values will be taken + */ + void (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Get the initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator + */ + bool (*is_initiator) (ike_sa_id_t *this); + + /** + * @brief Switche the original initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator after switch, FALSE otherwise + */ + bool (*switch_initiator) (ike_sa_id_t *this); + + /** + * @brief Clones a given ike_sa_id_t object. + * + * @param this calling object + * @return cloned ike_sa_id_t object + */ + ike_sa_id_t *(*clone) (ike_sa_id_t *this); + + /** + * @brief Destroys an ike_sa_id_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_id_t *this); +}; + +/** + * @brief Creates an ike_sa_id_t object with specific SPI's and defined role. + * + * @param initiator_spi initiators SPI + * @param responder_spi responders SPI + * @param is_initiaor TRUE if we are the original initiator + * @return ike_sa_id_t object + * + * @ingroup sa + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiaor); + +#endif /*IKE_SA_ID_H_*/ diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c new file mode 100644 index 000000000..791ef805e --- /dev/null +++ b/src/charon/sa/ike_sa_manager.c @@ -0,0 +1,914 @@ +/** + * @file ike_sa_manager.c + * + * @brief Implementation of ike_sa_mananger_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <pthread.h> +#include <string.h> + +#include "ike_sa_manager.h" + +#include <daemon.h> +#include <sa/ike_sa_id.h> +#include <bus/bus.h> +#include <utils/linked_list.h> + +typedef struct entry_t entry_t; + +/** + * An entry in the linked list, contains IKE_SA, locking and lookup data. + */ +struct entry_t { + + /** + * Number of threads waiting for this ike_sa_t object. + */ + int waiting_threads; + + /** + * Condvar where threads can wait until ike_sa_t object is free for use again. + */ + pthread_cond_t condvar; + + /** + * Is this ike_sa currently checked out? + */ + bool checked_out; + + /** + * Does this SA drives out new threads? + */ + bool driveout_new_threads; + + /** + * Does this SA drives out waiting threads? + */ + bool driveout_waiting_threads; + + /** + * Identifiaction of an IKE_SA (SPIs). + */ + ike_sa_id_t *ike_sa_id; + + /** + * The contained ike_sa_t object. + */ + ike_sa_t *ike_sa; + + /** + * hash of the IKE_SA_INIT message, used to detect retransmissions + */ + chunk_t init_hash; + + /** + * message ID currently processing, if any + */ + u_int32_t message_id; +}; + +/** + * Implementation of entry_t.destroy. + */ +static status_t entry_destroy(entry_t *this) +{ + /* also destroy IKE SA */ + this->ike_sa->destroy(this->ike_sa); + this->ike_sa_id->destroy(this->ike_sa_id); + chunk_free(&this->init_hash); + free(this); + return SUCCESS; +} + +/** + * Creates a new entry for the ike_sa_t list. + */ +static entry_t *entry_create(ike_sa_id_t *ike_sa_id) +{ + entry_t *this = malloc_thing(entry_t); + + this->waiting_threads = 0; + pthread_cond_init(&this->condvar, NULL); + + /* we set checkout flag when we really give it out */ + this->checked_out = FALSE; + this->driveout_new_threads = FALSE; + this->driveout_waiting_threads = FALSE; + this->message_id = -1; + this->init_hash = chunk_empty; + + /* ike_sa_id is always cloned */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + /* create new ike_sa */ + this->ike_sa = ike_sa_create(ike_sa_id); + + return this; +} + + +typedef struct private_ike_sa_manager_t private_ike_sa_manager_t; + +/** + * Additional private members of ike_sa_manager_t. + */ +struct private_ike_sa_manager_t { + /** + * Public interface of ike_sa_manager_t. + */ + ike_sa_manager_t public; + + /** + * Lock for exclusivly accessing the manager. + */ + pthread_mutex_t mutex; + + /** + * Linked list with entries for the ike_sa_t objects. + */ + linked_list_t *ike_sa_list; + + /** + * A randomizer, to get random SPIs for our side + */ + randomizer_t *randomizer; + + /** + * SHA1 hasher for IKE_SA_INIT retransmit detection + */ + hasher_t *hasher; +}; + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_id. + */ +static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + /* create iterator over list of ike_sa's */ + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) + { + DBG2(DBG_MGR, "found entry by both SPIs"); + *entry = current; + status = SUCCESS; + break; + } + if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 || + current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0) + { + /* seems to be a half ready ike_sa */ + if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == + ike_sa_id->get_initiator_spi(ike_sa_id)) && + (current->ike_sa_id->is_initiator(ike_sa_id) == + ike_sa_id->is_initiator(current->ike_sa_id))) + { + DBG2(DBG_MGR, "found entry by initiator SPI"); + *entry = current; + status = SUCCESS; + break; + } + } + } + + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_sa. + */ +static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + /* only pointers are compared */ + if (current->ike_sa == ike_sa) + { + DBG2(DBG_MGR, "found entry by pointer"); + *entry = current; + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implementation of private_ike_sa_manager_s.delete_entry. + */ +static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current == entry) + { + /* mark it, so now new threads can get this entry */ + entry->driveout_new_threads = TRUE; + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_broadcast(&(entry->condvar)); + /* they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + + DBG2(DBG_MGR, "found entry by pointer, deleting it"); + iterator->remove(iterator); + entry_destroy(entry); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Wait until no other thread is using an IKE_SA, return FALSE if entry not + * acquireable + */ +static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) +{ + if (entry->driveout_new_threads) + { + /* we are not allowed to get this */ + return FALSE; + } + while (entry->checked_out && !entry->driveout_waiting_threads) + { + /* so wait until we can get it for us. + * we register us as waiting. */ + entry->waiting_threads++; + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + entry->waiting_threads--; + } + /* hm, a deletion request forbids us to get this SA, get next one */ + if (entry->driveout_waiting_threads) + { + /* we must signal here, others may be waiting on it, too */ + pthread_cond_signal(&(entry->condvar)); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of private_ike_sa_manager_t.get_next_spi. + */ +static u_int64_t get_next_spi(private_ike_sa_manager_t *this) +{ + u_int64_t spi; + + this->randomizer->get_pseudo_random_bytes(this->randomizer, sizeof(spi), + (u_int8_t*)&spi); + return spi; +} + +/** + * Implementation of of ike_sa_manager.checkout. + */ +static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + ike_sa_t *ike_sa = NULL; + entry_t *entry; + + DBG2(DBG_MGR, "checkout IKE_SA: %J, %d IKE_SAs in manager", + ike_sa_id, this->ike_sa_list->get_count(this->ike_sa_list)); + + pthread_mutex_lock(&(this->mutex)); + if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + } + } + pthread_mutex_unlock(&this->mutex); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_new. + */ +static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) +{ + entry_t *entry; + ike_sa_id_t *id; + + if (initiator) + { + id = ike_sa_id_create(get_next_spi(this), 0, TRUE); + } + else + { + id = ike_sa_id_create(0, get_next_spi(this), FALSE); + } + entry = entry_create(id); + pthread_mutex_lock(&this->mutex); + this->ike_sa_list->insert_last(this->ike_sa_list, entry); + entry->checked_out = TRUE; + pthread_mutex_unlock(&this->mutex); + DBG2(DBG_MGR, "created IKE_SA: %J, %d IKE_SAs in manager", + id, this->ike_sa_list->get_count(this->ike_sa_list)); + return entry->ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, + message_t *message) +{ + entry_t *entry; + ike_sa_t *ike_sa = NULL; + ike_sa_id_t *id = message->get_ike_sa_id(message); + id = id->clone(id); + id->switch_initiator(id); + + DBG2(DBG_MGR, "checkout IKE_SA: %J by message, %d IKE_SAs in manager", + id, this->ike_sa_list->get_count(this->ike_sa_list)); + + if (message->get_request(message) && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ + iterator_t *iterator; + chunk_t data, hash; + + data = message->get_packet_data(message); + this->hasher->allocate_hash(this->hasher, data, &hash); + chunk_free(&data); + + pthread_mutex_lock(&this->mutex); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (chunk_equals(hash, entry->init_hash)) + { + if (entry->message_id == 0) + { + iterator->destroy(iterator); + pthread_mutex_unlock(&this->mutex); + chunk_free(&hash); + id->destroy(id); + DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing"); + return NULL; + } + else if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA checked out by hash"); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + ike_sa = entry->ike_sa; + } + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&this->mutex); + + if (ike_sa == NULL) + { + if (id->get_responder_spi(id) == 0 && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* no IKE_SA found, create a new one */ + id->set_responder_spi(id, get_next_spi(this)); + entry = entry_create(id); + + pthread_mutex_lock(&this->mutex); + this->ike_sa_list->insert_last(this->ike_sa_list, entry); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + pthread_mutex_unlock(&this->mutex); + entry->init_hash = hash; + ike_sa = entry->ike_sa; + } + else + { + DBG1(DBG_MGR, "ignoring message for %J, no such IKE_SA", id); + } + } + else + { + chunk_free(&hash); + } + id->destroy(id); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; + } + + pthread_mutex_lock(&(this->mutex)); + if (get_entry_by_id(this, id, &entry) == SUCCESS) + { + /* only check out if we are not processing this request */ + if (message->get_request(message) && + message->get_message_id(message) == entry->message_id) + { + DBG1(DBG_MGR, "ignoring request with ID %d, already processing", + entry->message_id); + } + else if (wait_for_entry(this, entry)) + { + ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa); + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + if (ike_id->get_responder_spi(ike_id) == 0) + { + ike_id->set_responder_spi(ike_id, id->get_responder_spi(id)); + } + ike_sa = entry->ike_sa; + } + } + pthread_mutex_unlock(&this->mutex); + id->destroy(id); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this, + host_t *my_host, host_t *other_host, + identification_t *my_id, + identification_t *other_id) +{ + iterator_t *iterator; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + identification_t *found_my_id, *found_other_id; + host_t *found_my_host, *found_other_host; + int wc; + + if (!wait_for_entry(this, entry)) + { + continue; + } + + if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) + { + /* skip IKE_SA which are not useable */ + continue; + } + + found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); + found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); + found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); + found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); + + if (found_my_id->get_type(found_my_id) == ID_ANY && + found_other_id->get_type(found_other_id) == ID_ANY) + { + /* IKE_SA has no IDs yet, so we can't use it */ + continue; + } + + /* compare ID and hosts. Supplied ID may contain wildcards, and IP + * may be %any. */ + if ((found_my_host->is_anyaddr(found_my_host) || + my_host->ip_equals(my_host, found_my_host)) && + (found_other_host->is_anyaddr(found_other_host) || + other_host->ip_equals(other_host, found_other_host)) && + found_my_id->matches(found_my_id, my_id, &wc) && + found_other_id->matches(found_other_id, other_id, &wc)) + { + /* looks good, we take this one */ + DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]", + my_host, other_host, my_id, other_id); + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + } + } + iterator->destroy(iterator); + + if (!ike_sa) + { + u_int64_t initiator_spi; + entry_t *new_entry; + ike_sa_id_t *new_ike_sa_id; + + initiator_spi = get_next_spi(this); + new_ike_sa_id = ike_sa_id_create(0, 0, TRUE); + new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi); + + /* create entry */ + new_entry = entry_create(new_ike_sa_id); + DBG2(DBG_MGR, "created IKE_SA: %J", new_ike_sa_id); + new_ike_sa_id->destroy(new_ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id); + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + pthread_mutex_unlock(&(this->mutex)); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, + bool child) +{ + iterator_t *iterator, *children; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (wait_for_entry(this, entry)) + { + /* look for a child with such a reqid ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (child_sa->get_reqid(child_sa) == id) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a unique id */ + { + if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_name. + */ +static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, + bool child) +{ + iterator_t *iterator, *children; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (wait_for_entry(this, entry)) + { + /* look for a child with such a policy name ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (streq(child_sa->get_name(child_sa), name)) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a connection name */ + { + if (streq(entry->ike_sa->get_name(entry->ike_sa), name)) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Iterator hook for iterate, gets ike_sas instead of entries + */ +static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in, + ike_sa_t **out) +{ + /* check out entry */ + if (wait_for_entry(this, in)) + { + *out = in->ike_sa; + return TRUE; + } + return FALSE; +} + +/** + * Implementation of ike_sa_manager_t.create_iterator. + */ +static iterator_t *create_iterator(private_ike_sa_manager_t* this) +{ + iterator_t *iterator = this->ike_sa_list->create_iterator_locked( + this->ike_sa_list, &this->mutex); + /* register hook to iterator over ike_sas, not entries */ + iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); + return iterator; +} + +/** + * Implementation of ike_sa_manager_t.checkin. + */ +static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* to check the SA back in, we look for the pointer of the ike_sa + * in all entries. + * We can't search by SPI's since the MAY have changed (e.g. on reception + * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... + */ + status_t retval; + entry_t *entry; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + + DBG2(DBG_MGR, "checkin IKE_SA: %J", ike_sa_id); + + pthread_mutex_lock(&(this->mutex)); + + /* look for the entry */ + if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* ike_sa_id must be updated */ + entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); + /* signal waiting threads */ + entry->checked_out = FALSE; + entry->message_id = -1; + DBG2(DBG_MGR, "check-in of IKE_SA successful."); + pthread_cond_signal(&(entry->condvar)); + retval = SUCCESS; + } + else + { + DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA"); + /* this SA is no more, this REALLY should not happen */ + retval = NOT_FOUND; + } + + DBG2(DBG_MGR, "%d IKE_SAs in manager now", + this->ike_sa_list->get_count(this->ike_sa_list)); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, NULL); + return retval; +} + + +/** + * Implementation of ike_sa_manager_t.checkin_and_destroy. + */ +static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + entry_t *entry; + status_t retval; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + DBG2(DBG_MGR, "checkin and destroy IKE_SA: %J", ike_sa_id); + + pthread_mutex_lock(&(this->mutex)); + + if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* drive out waiting threads, as we are in hurry */ + entry->driveout_waiting_threads = TRUE; + + delete_entry(this, entry); + + DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); + retval = SUCCESS; + } + else + { + DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + charon->bus->set_sa(charon->bus, ike_sa); + return retval; +} + +/** + * Implementation of ike_sa_manager_t.get_half_open_count. + */ +static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) +{ + iterator_t *iterator; + entry_t *entry; + int count = 0; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + /* we check if we have a responder CONNECTING IKE_SA without checkout */ + if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING) + { + /* if we have a host, we have wait until no other uses the IKE_SA */ + if (ip) + { + if (wait_for_entry(this, entry) && ip->ip_equals(ip, + entry->ike_sa->get_other_host(entry->ike_sa))) + { + count++; + } + } + else + { + count++; + } + } + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * Implementation of ike_sa_manager_t.destroy. + */ +static void destroy(private_ike_sa_manager_t *this) +{ + /* destroy all list entries */ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); + /* Step 1: drive out all waiting threads */ + DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's"); + iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + /* do not accept new threads, drive out waiting threads */ + entry->driveout_new_threads = TRUE; + entry->driveout_waiting_threads = TRUE; + } + DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); + /* Step 2: wait until all are gone */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&entry)) + { + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_broadcast(&(entry->condvar)); + /* go sleeping until they are gone */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + } + DBG2(DBG_MGR, "delete all IKE_SA's"); + /* Step 3: initiate deletion of all IKE_SAs */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&entry)) + { + entry->ike_sa->delete(entry->ike_sa); + } + iterator->destroy(iterator); + + DBG2(DBG_MGR, "destroy all entries"); + /* Step 4: destroy all entries */ + list->destroy_function(list, (void*)entry_destroy); + pthread_mutex_unlock(&(this->mutex)); + + this->randomizer->destroy(this->randomizer); + this->hasher->destroy(this->hasher); + + free(this); +} + +/* + * Described in header. + */ +ike_sa_manager_t *ike_sa_manager_create() +{ + private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); + + /* assign public functions */ + this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; + this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; + this->public.checkout_new = (ike_sa_t*(*)(ike_sa_manager_t*,bool))checkout_new; + this->public.checkout_by_message = (ike_sa_t*(*)(ike_sa_manager_t*,message_t*))checkout_by_message; + this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer; + this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; + this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; + this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; + this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; + this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; + this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; + + /* initialize private variables */ + this->ike_sa_list = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + this->randomizer = randomizer_create(); + this->hasher = hasher_create(HASH_SHA1); + + return &this->public; +} diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h new file mode 100644 index 000000000..1125e5d16 --- /dev/null +++ b/src/charon/sa/ike_sa_manager.h @@ -0,0 +1,231 @@ +/** + * @file ike_sa_manager.h + * + * @brief Interface of ike_sa_manager_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef IKE_SA_MANAGER_H_ +#define IKE_SA_MANAGER_H_ + +typedef struct ike_sa_manager_t ike_sa_manager_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <encoding/message.h> + +/** + * @brief The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. + * + * To avoid access from multiple threads, IKE_SAs must be checked out from + * the manager, and checked in after usage. + * The manager also handles deletion of SAs. + * + * @todo checking of double-checkouts from the same threads would be nice. + * This could be done by comparing thread-ids via pthread_self()... + * + * @todo Managing of ike_sa_t objects in a hash table instead of linked list. + * + * @b Constructors: + * - ike_sa_manager_create() + * + * @ingroup sa + */ +struct ike_sa_manager_t { + + /** + * @brief Checkout an existing IKE_SA. + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @returns + * - checked out IKE_SA if found + * - NULL, if specified IKE_SA is not found. + */ + ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id); + + /** + * @brief Create and check out a new IKE_SA. + * + * @param this the manager object + * @param initiator TRUE for initiator, FALSE otherwise + * @returns created andchecked out IKE_SA + */ + ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator); + + /** + * @brief Checkout an IKE_SA by a message. + * + * In some situations, it is necessary that the manager knows the + * message to use for the checkout. This has the folloing reasons: + * + * 1. If the targeted IKE_SA is already processing a message, we do not + * check it out if the message ID is the same. + * 2. If it is an IKE_SA_INIT request, we have to check if it is a + * retransmission. If so, we have to drop the message, we would + * create another unneded IKE_SA for each retransmitted packet. + * + * A call to checkout_by_message() returns a (maybe new created) IKE_SA. + * If processing the message does not make sense (for the reasons above), + * NULL is returned. + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @returns + * - checked out/created IKE_SA + * - NULL to not process message further + */ + ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message); + + /** + * @brief Checkout an existing IKE_SA by hosts and identifications. + * + * Allows the lookup of an IKE_SA by user IDs and hosts. It returns the + * first found occurence, if there are multiple candidates. Supplied IDs + * may contain wildcards, hosts may be %any. + * If no IKE_SA is found, a new one is created. This is also the case when + * the found IKE_SA is in the DELETING state. + * + * @param this the manager object + * @param my_host address of our host + * @param other_id address of remote host + * @param my_id ID used by us + * @param other_id ID used by remote + * @return checked out/created IKE_SA + */ + ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this, + host_t *my_host, host_t* other_host, + identification_t *my_id, + identification_t *other_id); + + /** + * @brief Check out an IKE_SA a unique ID. + * + * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. + * These checkout function uses, depending + * on the child parameter, the unique ID of the IKE_SA or the reqid + * of one of a IKE_SAs CHILD_SA. + * + * @param this the manager object + * @param id unique ID of the object + * @param child TRUE to use CHILD, FALSE to use IKE_SA + * @return + * - checked out IKE_SA, if found + * - NULL, if not found + */ + ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id, + bool child); + + /** + * @brief Check out an IKE_SA by the policy/connection name. + * + * Check out the IKE_SA by the connections name or by a CHILD_SAs policy + * name. + * + * @param this the manager object + * @param name name of the connection/policy + * @param child TRUE to use policy name, FALSE to use conn name + * @return + * - checked out IKE_SA, if found + * - NULL, if not found + */ + ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name, + bool child); + + /** + * @brief Create an iterator over all stored IKE_SAs. + * + * The avoid synchronization issues, the iterator locks access + * to the manager exclusively, until it gets destroyed. + * This iterator is for reading only! Writing will corrupt the manager. + * + * @param this the manager object + * @return iterator over all IKE_SAs. + */ + iterator_t *(*create_iterator) (ike_sa_manager_t* this); + + /** + * @brief Checkin the SA after usage. + * + * @warning the SA pointer MUST NOT be used after checkin! + * The SA must be checked out again! + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @param ike_sa checked out SA + * @returns + * - SUCCESS if checked in + * - NOT_FOUND when not found (shouldn't happen!) + */ + status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Destroy a checked out SA. + * + * The IKE SA is destroyed without notification of the remote peer. + * Use this only if the other peer doesn't respond or behaves not + * as predicted. + * Checking in and destruction is an atomic operation (for the IKE_SA), + * so this can be called if the SA is in a "unclean" state, without the + * risk that another thread can get the SA. + * + * @param this the manager object + * @param ike_sa SA to delete + * @returns + * - SUCCESS if found + * - NOT_FOUND when no such SA is available + */ + status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Get the number of IKE_SAs which are in the connecting state. + * + * To prevent the server from resource exhaustion, cookies and other + * mechanisms are used. The number of half open IKE_SAs is a good + * indicator to see if a peer is flooding the server. + * If a host is supplied, only the number of half open IKE_SAs initiated + * from this IP are counted. + * Only SAs for which we are the responder are counted. + * + * @param this the manager object + * @param ip NULL for all, IP for half open IKE_SAs with IP + * @return number of half open IKE_SAs + */ + int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); + + /** + * @brief Destroys the manager with all associated SAs. + * + * Threads will be driven out, so all SAs can be deleted cleanly. + * + * @param this the manager object + */ + void (*destroy) (ike_sa_manager_t *this); +}; + +/** + * @brief Create a manager. + * + * @returns ike_sa_manager_t object + * + * @ingroup sa + */ +ike_sa_manager_t *ike_sa_manager_create(void); + +#endif /*IKE_SA_MANAGER_H_*/ diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c new file mode 100644 index 000000000..844300735 --- /dev/null +++ b/src/charon/sa/task_manager.c @@ -0,0 +1,854 @@ +/** + * @file task_manager.c + * + * @brief Implementation of task_manager_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#include "task_manager.h" + +#include <daemon.h> +#include <sa/tasks/ike_init.h> +#include <sa/tasks/ike_natd.h> +#include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_rekey.h> +#include <sa/tasks/ike_delete.h> +#include <sa/tasks/ike_config.h> +#include <sa/tasks/ike_dpd.h> +#include <sa/tasks/child_create.h> +#include <sa/tasks/child_rekey.h> +#include <sa/tasks/child_delete.h> +#include <encoding/payloads/delete_payload.h> +#include <queues/jobs/retransmit_job.h> + +typedef struct exchange_t exchange_t; + +/** + * An exchange in the air, used do detect and handle retransmission + */ +struct exchange_t { + + /** + * Message ID used for this transaction + */ + u_int32_t mid; + + /** + * generated packet for retransmission + */ + packet_t *packet; +}; + +typedef struct private_task_manager_t private_task_manager_t; + +/** + * private data of the task manager + */ +struct private_task_manager_t { + + /** + * public functions + */ + task_manager_t public; + + /** + * associated IKE_SA we are serving + */ + ike_sa_t *ike_sa; + + /** + * Exchange we are currently handling as responder + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * packet for retransmission + */ + packet_t *packet; + + } responding; + + /** + * Exchange we are currently handling as initiator + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * how many times we have retransmitted so far + */ + u_int retransmitted; + + /** + * packet for retransmission + */ + packet_t *packet; + + /** + * type of the initated exchange + */ + exchange_type_t type; + + } initiating; + + /** + * List of queued tasks not yet in action + */ + linked_list_t *queued_tasks; + + /** + * List of active tasks, initiated by ourselve + */ + linked_list_t *active_tasks; + + /** + * List of tasks initiated by peer + */ + linked_list_t *passive_tasks; +}; + +/** + * flush all tasks in the task manager + */ +static void flush(private_task_manager_t *this) +{ + task_t *task; + + this->queued_tasks->destroy_offset(this->queued_tasks, + offsetof(task_t, destroy)); + this->passive_tasks->destroy_offset(this->passive_tasks, + offsetof(task_t, destroy)); + + /* emmit outstanding signals for tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + switch (task->get_type(task)) + { + case IKE_AUTH: + SIG(IKE_UP_FAILED, "establishing IKE_SA failed"); + break; + case IKE_DELETE: + SIG(IKE_DOWN_FAILED, "IKE_SA deleted"); + break; + case IKE_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed"); + break; + case CHILD_CREATE: + SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed"); + break; + case CHILD_DELETE: + SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed"); + break; + case CHILD_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed"); + break; + default: + break; + } + task->destroy(task); + } + this->queued_tasks = linked_list_create(); + this->passive_tasks = linked_list_create(); +} + +/** + * move a task of a specific type from the queue to the active list + */ +static bool activate_task(private_task_manager_t *this, task_type_t type) +{ + iterator_t *iterator; + task_t *task; + bool found = FALSE; + + iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + if (task->get_type(task) == type) + { + DBG2(DBG_IKE, " activating %N task", task_type_names, type); + iterator->remove(iterator); + this->active_tasks->insert_last(this->active_tasks, task); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of task_manager_t.retransmit + */ +static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) +{ + if (message_id == this->initiating.mid) + { + u_int32_t timeout; + job_t *job; + + timeout = charon->configuration->get_retransmit_timeout( + charon->configuration, this->initiating.retransmitted); + if (timeout == 0) + { + DBG1(DBG_IKE, "giving up after %d retransmits", + this->initiating.retransmitted - 1); + return DESTROY_ME; + } + + if (this->initiating.retransmitted) + { + DBG1(DBG_IKE, "retransmit %d of request with message ID %d", + this->initiating.retransmitted, message_id); + } + this->initiating.retransmitted++; + + charon->sender->send(charon->sender, + this->initiating.packet->clone(this->initiating.packet)); + job = (job_t*)retransmit_job_create(this->initiating.mid, + this->ike_sa->get_id(this->ike_sa)); + charon->event_queue->add_relative(charon->event_queue, job, timeout); + } + return SUCCESS; +} + +/** + * build a request using the active task list + * Implementation of task_manager_t.initiate + */ +static status_t build_request(private_task_manager_t *this) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + status_t status; + exchange_type_t exchange = 0; + + if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) + { + DBG2(DBG_IKE, "delaying task initiation, exchange in progress"); + /* do not initiate if we already have a message in the air */ + return SUCCESS; + } + + if (this->active_tasks->get_count(this->active_tasks) == 0) + { + DBG2(DBG_IKE, "activating new tasks"); + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_CREATED: + if (activate_task(this, IKE_INIT)) + { + exchange = IKE_SA_INIT; + activate_task(this, IKE_NATD); + activate_task(this, IKE_CERT); + activate_task(this, IKE_AUTHENTICATE); + activate_task(this, IKE_CONFIG); + activate_task(this, CHILD_CREATE); + } + break; + case IKE_ESTABLISHED: + if (activate_task(this, CHILD_CREATE)) + { + exchange = CREATE_CHILD_SA; + activate_task(this, IKE_CONFIG); + break; + } + if (activate_task(this, CHILD_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, CHILD_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, IKE_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DEADPEER)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_REKEYING: + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_DELETING: + default: + break; + } + } + else + { + DBG2(DBG_IKE, "reinitiating already active tasks"); + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); + switch (task->get_type(task)) + { + case IKE_INIT: + exchange = IKE_SA_INIT; + break; + case IKE_AUTHENTICATE: + exchange = IKE_AUTH; + break; + default: + continue; + } + break; + } + iterator->destroy(iterator); + } + + if (exchange == 0) + { + DBG2(DBG_IKE, "nothing to initiate"); + /* nothing to do yet... */ + return SUCCESS; + } + + message = message_create(); + message->set_message_id(message, this->initiating.mid); + message->set_exchange_type(message, exchange); + this->initiating.type = exchange; + this->initiating.retransmitted = 0; + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + message->destroy(message); + flush(this); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + DESTROY_IF(this->initiating.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->initiating.packet); + message->destroy(message); + if (status != SUCCESS) + { + /* message generation failed. There is nothing more to do than to + * close the SA */ + flush(this); + return DESTROY_ME; + } + + return retransmit(this, this->initiating.mid); +} + +/** + * handle an incoming response message + */ +static status_t process_response(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task; + + if (message->get_exchange_type(message) != this->initiating.type) + { + DBG1(DBG_IKE, "received %N response, but expected %N", + exchange_type_names, message->get_exchange_type(message), + exchange_type_names, this->initiating.type); + return DESTROY_ME; + } + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + this->initiating.mid++; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + + return build_request(this); +} + +/** + * handle exchange collisions + */ +static void handle_collisions(private_task_manager_t *this, task_t *task) +{ + iterator_t *iterator; + task_t *active; + task_type_t type; + + type = task->get_type(task); + + /* do we have to check */ + if (type == IKE_REKEY || type == CHILD_REKEY || + type == CHILD_DELETE || type == IKE_DELETE) + { + /* find an exchange collision, and notify these tasks */ + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&active)) + { + switch (active->get_type(active)) + { + case IKE_REKEY: + if (type == IKE_REKEY || type == IKE_DELETE) + { + ike_rekey_t *rekey = (ike_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + case CHILD_REKEY: + if (type == CHILD_REKEY || type == CHILD_DELETE) + { + child_rekey_t *rekey = (child_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + default: + continue; + } + iterator->destroy(iterator); + return; + } + iterator->destroy(iterator); + } + /* destroy task if not registered in any active task */ + task->destroy(task); +} + +/** + * build a response depending on the "passive" task list + */ +static status_t build_response(private_task_manager_t *this, + exchange_type_t exchange) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + bool delete = FALSE; + status_t status; + + message = message_create(); + message->set_exchange_type(message, exchange); + message->set_message_id(message, this->responding.mid); + message->set_request(message, FALSE); + + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + handle_collisions(this, task); + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* destroy IKE_SA, but SEND response first */ + delete = TRUE; + break; + } + if (delete) + { + break; + } + } + iterator->destroy(iterator); + + /* remove resonder SPI if IKE_SA_INIT failed */ + if (delete && exchange == IKE_SA_INIT) + { + ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, 0); + } + + /* message complete, send it */ + DESTROY_IF(this->responding.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->responding.packet); + message->destroy(message); + if (status != SUCCESS) + { + return DESTROY_ME; + } + + charon->sender->send(charon->sender, + this->responding.packet->clone(this->responding.packet)); + if (delete) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * handle an incoming request message + */ +static status_t process_request(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task = NULL; + exchange_type_t exchange; + payload_t *payload; + notify_payload_t *notify; + + exchange = message->get_exchange_type(message); + + /* create tasks depending on request type */ + switch (exchange) + { + case IKE_SA_INIT: + { + task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_natd_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_auth_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_config_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)child_create_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case CREATE_CHILD_SA: + { + bool notify_found = FALSE, ts_found = FALSE; + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY: + { + /* if we find a rekey notify, its CHILD_SA rekeying */ + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == REKEY_SA && + (notify->get_protocol_id(notify) == PROTO_AH || + notify->get_protocol_id(notify) == PROTO_ESP)) + { + notify_found = TRUE; + } + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + case TRAFFIC_SELECTOR_RESPONDER: + { + /* if we don't find a TS, its IKE rekeying */ + ts_found = TRUE; + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + if (ts_found) + { + if (notify_found) + { + task = (task_t*)child_rekey_create(this->ike_sa, NULL); + } + else + { + task = (task_t*)child_create_create(this->ike_sa, NULL); + } + } + else + { + task = (task_t*)ike_rekey_create(this->ike_sa, FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case INFORMATIONAL: + { + delete_payload_t *delete; + + delete = (delete_payload_t*)message->get_payload(message, DELETE); + if (delete) + { + if (delete->get_protocol_id(delete) == PROTO_IKE) + { + task = (task_t*)ike_delete_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + else + { + task = (task_t*)child_delete_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + } + else + { + task = (task_t*)ike_dpd_create(FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + break; + } + default: + break; + } + + /* let the tasks process the message */ + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs at least another call to build() */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + return build_response(this, exchange); +} + +/** + * Implementation of task_manager_t.process_message + */ +static status_t process_message(private_task_manager_t *this, message_t *msg) +{ + u_int32_t mid = msg->get_message_id(msg); + + if (msg->get_request(msg)) + { + if (mid == this->responding.mid) + { + if (process_request(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + this->responding.mid++; + } + else if ((mid == this->responding.mid - 1) && this->responding.packet) + { + DBG1(DBG_IKE, "received retransmit of request with ID %d, " + "retransmitting response", mid); + charon->sender->send(charon->sender, + this->responding.packet->clone(this->responding.packet)); + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->responding.mid); + } + } + else + { + if (mid == this->initiating.mid) + { + if (process_response(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->initiating.mid); + return SUCCESS; + } + } + return SUCCESS; +} + +/** + * Implementation of task_manager_t.queue_task + */ +static void queue_task(private_task_manager_t *this, task_t *task) +{ + DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); + this->queued_tasks->insert_last(this->queued_tasks, task); +} + +/** + * Implementation of task_manager_t.adopt_tasks + */ +static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other) +{ + task_t *task; + + /* move queued tasks from other to this */ + while (other->queued_tasks->remove_last(other->queued_tasks, + (void**)&task) == SUCCESS) + { + DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } + + /* reset active tasks and move them to others queued tasks */ + while (other->active_tasks->remove_last(other->active_tasks, + (void**)&task) == SUCCESS) + { + DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.busy + */ +static bool busy(private_task_manager_t *this) +{ + return (this->active_tasks->get_count(this->active_tasks) > 0); +} + +/** + * Implementation of task_manager_t.reset + */ +static void reset(private_task_manager_t *this) +{ + task_t *task; + + /* reset message counters and retransmit packets */ + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->responding.mid = 0; + this->initiating.mid = -1; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + + /* reset active tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.destroy + */ +static void destroy(private_task_manager_t *this) +{ + flush(this); + + this->active_tasks->destroy(this->active_tasks); + this->queued_tasks->destroy(this->queued_tasks); + this->passive_tasks->destroy(this->passive_tasks); + + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + free(this); +} + +/* + * see header file + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa) +{ + private_task_manager_t *this = malloc_thing(private_task_manager_t); + + this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message; + this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task; + this->public.initiate = (status_t(*)(task_manager_t*))build_request; + this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit; + this->public.reset = (void(*)(task_manager_t*))reset; + this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks; + this->public.busy = (bool(*)(task_manager_t*))busy; + this->public.destroy = (void(*)(task_manager_t*))destroy; + + this->ike_sa = ike_sa; + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->responding.mid = 0; + this->initiating.mid = 0; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + this->queued_tasks = linked_list_create(); + this->active_tasks = linked_list_create(); + this->passive_tasks = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h new file mode 100644 index 000000000..c766d4a65 --- /dev/null +++ b/src/charon/sa/task_manager.h @@ -0,0 +1,144 @@ +/** + * @file task_manager.h + * + * @brief Interface of task_manager_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef TASK_MANAGER_H_ +#define TASK_MANAGER_H_ + +typedef struct task_manager_t task_manager_t; + +#include <library.h> +#include <encoding/message.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief The task manager, juggles task and handles message exchanges. + * + * On incoming requests, the task manager creates new tasks on demand and + * juggles the request through all available tasks. Each task inspects the + * request and adds payloads as necessary to the response. + * On outgoing requests, the task manager delivers the request through the tasks + * to build it, the response gets processed by each task to complete. + * The task manager has an internal Queue to store task which should get + * completed. + * For the initial IKE_SA setup, several tasks are queued: One for the + * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup + * and maybe one for virtual IP assignement. + * + * @b Constructors: + * - task_manager_create() + * + * @ingroup sa + */ +struct task_manager_t { + + /** + * @brief Process an incoming message. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - DESTROY_ME if IKE_SA must be closed + * - SUCCESS otherwise + */ + status_t (*process_message) (task_manager_t *this, message_t *message); + + /** + * @brief Initiate an exchange with the currently queued tasks. + * + * @param this calling object + */ + status_t (*initiate) (task_manager_t *this); + + /** + * @brief Queue a task in the manager. + * + * @param this calling object + * @param task task to queue + */ + void (*queue_task) (task_manager_t *this, task_t *task); + + /** + * @brief Retransmit a request if it hasn't been acknowledged yet. + * + * A return value of INVALID_STATE means that the message was already + * acknowledged and has not to be retransmitted. A return value of SUCCESS + * means retransmission was required and the message has been resent. + * + * @param this calling object + * @param message_id ID of the message to retransmit + * @return + * - INVALID_STATE if retransmission not required + * - SUCCESS if retransmission sent + */ + status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); + + /** + * @brief Migrate all tasks from other to this. + * + * To rekey or reestablish an IKE_SA completely, all queued or active + * tasks should get migrated to the new IKE_SA. + * + * @param this manager which gets all tasks + * @param other manager which gives away its tasks + */ + void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); + + /** + * @brief Reset message ID counters of the task manager. + * + * The IKEv2 protocol requires to restart exchanges with message IDs + * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method + * resets the message IDs and resets all active tasks using the migrate() + * method. + * + * @param this calling object + * @param other manager which gives away its tasks + */ + void (*reset) (task_manager_t *this); + + /** + * @brief Check if we are currently waiting for a reply. + * + * @param this calling object + * @return TRUE if we are waiting, FALSE otherwise + */ + bool (*busy) (task_manager_t *this); + + /** + * @brief Destroy the task_manager_t. + * + * @param this calling object + */ + void (*destroy) (task_manager_t *this); +}; + +/** + * @brief Create an instance of the task manager. + * + * @param ike_sa IKE_SA to manage. + * + * @ingroup sa + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa); + +#endif /* TASK_MANAGER_H_ */ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c new file mode 100644 index 000000000..781d679f2 --- /dev/null +++ b/src/charon/sa/tasks/child_create.c @@ -0,0 +1,804 @@ +/** + * @file child_create.c + * + * @brief Implementation of the child_create task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "child_create.h" + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/notify_payload.h> + + +typedef struct private_child_create_t private_child_create_t; + +/** + * Private members of a child_create_t task. + */ +struct private_child_create_t { + + /** + * Public methods and task_t interface. + */ + child_create_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * policy to create the CHILD_SA from + */ + policy_t *policy; + + /** + * list of proposal candidates + */ + linked_list_t *proposals; + + /** + * selected proposal to use for CHILD_SA + */ + proposal_t *proposal; + + /** + * traffic selectors for initiators side + */ + linked_list_t *tsi; + + /** + * traffic selectors for responders side + */ + linked_list_t *tsr; + + /** + * mode the new CHILD_SA uses (transport/tunnel/beet) + */ + mode_t mode; + + /** + * reqid to use if we are rekeying + */ + u_int32_t reqid; + + /** + * CHILD_SA which gets established + */ + child_sa_t *child_sa; + + /** + * successfully established the CHILD? + */ + bool established; +}; + +/** + * get the nonce from a message + */ +static status_t get_nonce(message_t *message, chunk_t *nonce) +{ + nonce_payload_t *payload; + + payload = (nonce_payload_t*)message->get_payload(message, NONCE); + if (payload == NULL) + { + return FAILED; + } + *nonce = payload->get_nonce(payload); + return NEED_MORE; +} + +/** + * generate a new nonce to include in a CREATE_CHILD_SA message + */ +static status_t generate_nonce(chunk_t *nonce) +{ + status_t status; + randomizer_t *randomizer = randomizer_create(); + + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + return FAILED; + } + return SUCCESS; +} + +/** + * Check a list of traffic selectors if any selector belongs to host + */ +static bool ts_list_is_host(linked_list_t *list, host_t *host) +{ + traffic_selector_t *ts; + bool is_host = TRUE; + iterator_t *iterator = list->create_iterator(list, TRUE); + + while (is_host && iterator->iterate(iterator, (void**)&ts)) + { + is_host = is_host && ts->is_host(ts, host); + } + iterator->destroy(iterator); + return is_host; +} + +/** + * Install a CHILD_SA for usage + */ +static status_t select_and_install(private_child_create_t *this) +{ + prf_plus_t *prf_plus; + status_t status; + chunk_t nonce_i, nonce_r, seed; + linked_list_t *my_ts, *other_ts; + host_t *me, *other, *other_vip, *my_vip; + + if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL) + { + SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message"); + return FAILED; + } + + if (this->initiator) + { + nonce_i = this->my_nonce; + nonce_r = this->other_nonce; + my_ts = this->tsi; + other_ts = this->tsr; + } + else + { + nonce_r = this->my_nonce; + nonce_i = this->other_nonce; + my_ts = this->tsr; + other_ts = this->tsi; + } + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); + other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); + + this->proposal = this->policy->select_proposal(this->policy, this->proposals); + + if (this->proposal == NULL) + { + SIG(CHILD_UP_FAILED, "no acceptable proposal found"); + return FAILED; + } + + if (this->initiator && my_vip) + { /* if we have a virtual IP, shorten our TS to the minimum */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts, + my_vip); + /* to setup firewall rules correctly, CHILD_SA needs the virtual IP */ + this->child_sa->set_virtual_ip(this->child_sa, my_vip); + } + else + { /* shorten in the host2host case only */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, + my_ts, me); + } + if (other_vip) + { /* if other has a virtual IP, shorten it's traffic selectors to it */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other_vip); + } + else + { /* use his host for the host2host case */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other); + } + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + if (this->initiator) + { + this->tsi = my_ts; + this->tsr = other_ts; + } + else + { + this->tsr = my_ts; + this->tsi = other_ts; + } + + if (this->tsi->get_count(this->tsi) == 0 || + this->tsr->get_count(this->tsr) == 0) + { + SIG(CHILD_UP_FAILED, "no acceptable traffic selectors found"); + return FAILED; + } + + if (!this->initiator) + { + /* check if requested mode is acceptable, downgrade if required */ + switch (this->mode) + { + case MODE_TRANSPORT: + if (!ts_list_is_host(this->tsi, other) || + !ts_list_is_host(this->tsr, me)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); + } + else if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, connection NATed"); + } + break; + case MODE_BEET: + if (!ts_list_is_host(this->tsi, NULL) || + !ts_list_is_host(this->tsr, NULL)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); + } + break; + default: + break; + } + } + + seed = chunk_cata("cc", nonce_i, nonce_r); + prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + + if (this->initiator) + { + status = this->child_sa->update(this->child_sa, this->proposal, + this->mode, prf_plus); + } + else + { + status = this->child_sa->add(this->child_sa, this->proposal, + this->mode, prf_plus); + } + prf_plus->destroy(prf_plus); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel"); + return status; + } + + status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts, + this->mode); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel"); + return status; + } + /* add to IKE_SA, and remove from task */ + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + this->established = TRUE; + return SUCCESS; +} + +/** + * build the payloads for the message + */ +static void build_payloads(private_child_create_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + nonce_payload_t *nonce_payload; + + /* add SA payload */ + if (this->initiator) + { + sa_payload = sa_payload_create_from_proposal_list(this->proposals); + } + else + { + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + /* add nonce payload if not in IKE_AUTH */ + if (message->get_exchange_type(message) == CREATE_CHILD_SA) + { + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + message->add_payload(message, (payload_t*)nonce_payload); + } + + /* add TSi/TSr payloads */ + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); + message->add_payload(message, (payload_t*)ts_payload); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); + message->add_payload(message, (payload_t*)ts_payload); + + /* add a notify if we are not in tunnel mode */ + switch (this->mode) + { + case MODE_TRANSPORT: + message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty); + break; + case MODE_BEET: + message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty); + break; + default: + break; + } +} + +/** + * Read payloads from message + */ +static void process_payloads(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + notify_payload_t *notify_payload; + + /* defaults to TUNNEL mode */ + this->mode = MODE_TUNNEL; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + sa_payload = (sa_payload_t*)payload; + this->proposals = sa_payload->get_proposals(sa_payload); + break; + case TRAFFIC_SELECTOR_INITIATOR: + ts_payload = (ts_payload_t*)payload; + this->tsi = ts_payload->get_traffic_selectors(ts_payload); + break; + case TRAFFIC_SELECTOR_RESPONDER: + ts_payload = (ts_payload_t*)payload; + this->tsr = ts_payload->get_traffic_selectors(ts_payload); + break; + case NOTIFY: + notify_payload = (notify_payload_t*)payload; + switch (notify_payload ->get_notify_type(notify_payload )) + { + case USE_TRANSPORT_MODE: + this->mode = MODE_TRANSPORT; + break; + case USE_BEET_MODE: + this->mode = MODE_BEET; + break; + default: + break; + } + break; + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_create_t *this, message_t *message) +{ + host_t *me, *other, *vip; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + case IKE_AUTH: + if (!message->get_payload(message, ID_INITIATOR)) + { + /* send only in the first request, not in subsequent EAP */ + return NEED_MORE; + } + break; + default: + break; + } + + SIG(CHILD_UP_START, "establishing CHILD_SA"); + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + vip = this->policy->get_virtual_ip(this->policy, NULL); + + if (vip) + { /* propose a 0.0.0.0/0 subnet when we use virtual ip */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL); + vip->destroy(vip); + } + else + { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, me); + } + this->tsr = this->policy->get_other_traffic_selectors(this->policy, other); + this->proposals = this->policy->get_proposals(this->policy); + this->mode = this->policy->get_mode(this->policy); + + this->child_sa = child_sa_create(me, other, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel"); + return FAILED; + } + + build_payloads(this, message); + + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->tsi = NULL; + this->tsr = NULL; + this->proposals = NULL; + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + case IKE_AUTH: + if (message->get_payload(message, ID_INITIATOR) == NULL) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + process_payloads(this, message); + + if (this->tsi == NULL || this->tsr == NULL) + { + DBG1(DBG_IKE, "TS payload missing in message"); + return NEED_MORE; + } + + this->policy = charon->policies->get_policy(charon->policies, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->tsr, this->tsi, + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); + + if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL) + { + this->ike_sa->set_policy(this->ike_sa, this->policy); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + case IKE_AUTH: + if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) + { + SIG(CHILD_UP_FAILED, "unable to create CHILD_SA while rekeying IKE_SA"); + message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); + return SUCCESS; + } + + if (this->policy == NULL) + { + SIG(CHILD_UP_FAILED, "no acceptable policy found"); + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (select_and_install(this) != SUCCESS) + { + message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); + return SUCCESS; + } + + build_payloads(this, message); + + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + case IKE_AUTH: + if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + /* handle notify errors related to CHILD_SA only */ + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + { + SIG(CHILD_UP_FAILED, "received %N notify, no CHILD_SA built", + notify_type_names, type); + iterator->destroy(iterator); + /* an error in CHILD_SA creation is not critical */ + return SUCCESS; + } + default: + break; + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + if (select_and_install(this) == SUCCESS) + { + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_create_t *this) +{ + return CHILD_CREATE; +} + +/** + * Implementation of child_create_t.use_reqid + */ +static void use_reqid(private_child_create_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** + * Implementation of child_create_t.get_child + */ +static child_sa_t* get_child(private_child_create_t *this) +{ + return this->child_sa; +} + +/** + * Implementation of child_create_t.get_lower_nonce + */ +static chunk_t get_lower_nonce(private_child_create_t *this) +{ + if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, + min(this->my_nonce.len, this->other_nonce.len)) < 0) + { + return this->my_nonce; + } + else + { + return this->other_nonce; + } +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + DESTROY_IF(this->child_sa); + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + this->ike_sa = ike_sa; + this->proposals = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; + this->established = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_create_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + if (!this->established) + { + DESTROY_IF(this->child_sa); + } + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + DESTROY_IF(this->policy); + free(this); +} + +/* + * Described in header. + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_child_create_t *this = malloc_thing(private_child_create_t); + + this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child; + this->public.get_lower_nonce = (chunk_t(*)(child_create_t*))get_lower_nonce; + this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy->get_ref(policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->proposals = NULL; + this->proposal = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; + this->established = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h new file mode 100644 index 000000000..200d37457 --- /dev/null +++ b/src/charon/sa/tasks/child_create.h @@ -0,0 +1,88 @@ +/** + * @file child_create.h + * + * @brief Interface child_create_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef CHILD_CREATE_H_ +#define CHILD_CREATE_H_ + +typedef struct child_create_t child_create_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <config/policies/policy.h> + +/** + * @brief Task of type CHILD_CREATE, established a new CHILD_SA. + * + * This task may be included in the IKE_AUTH message or in a separate + * CREATE_CHILD_SA exchange. + * + * @b Constructors: + * - child_create_create() + * + * @ingroup tasks + */ +struct child_create_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Use a specific reqid for the CHILD_SA. + * + * When this task is used for rekeying, the same reqid is used + * for the new CHILD_SA. + * + * @param this calling object + * @param reqid reqid to use + */ + void (*use_reqid) (child_create_t *this, u_int32_t reqid); + + /** + * @brief Get the lower of the two nonces, used for rekey collisions. + * + * @param this calling object + * @return lower nonce + */ + chunk_t (*get_lower_nonce) (child_create_t *this); + + /** + * @brief Get the CHILD_SA established/establishing by this task. + * + * @param this calling object + * @return child_sa + */ + child_sa_t* (*get_child) (child_create_t *this); +}; + +/** + * @brief Create a new child_create task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy if task initiator, NULL if responder + * @return child_create task to handle by the task_manager + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* CHILD_CREATE_H_ */ diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c new file mode 100644 index 000000000..23d509de5 --- /dev/null +++ b/src/charon/sa/tasks/child_delete.c @@ -0,0 +1,292 @@ +/** + * @file child_delete.c + * + * @brief Implementation of the child_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 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. + */ + +#include "child_delete.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_child_delete_t private_child_delete_t; + +/** + * Private members of a child_delete_t task. + */ +struct private_child_delete_t { + + /** + * Public methods and task_t interface. + */ + child_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * CHILD_SAs which get deleted + */ + linked_list_t *child_sas; +}; + +/** + * build the delete payloads from the listed child_sas + */ +static void build_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *iterator; + delete_payload_t *ah = NULL, *esp = NULL; + u_int32_t spi; + child_sa_t *child_sa; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + spi = child_sa->get_spi(child_sa, TRUE); + switch (child_sa->get_protocol(child_sa)) + { + case PROTO_ESP: + if (esp == NULL) + { + esp = delete_payload_create(PROTO_ESP); + message->add_payload(message, (payload_t*)esp); + } + esp->add_spi(esp, spi); + break; + case PROTO_AH: + if (ah == NULL) + { + ah = delete_payload_create(PROTO_AH); + message->add_payload(message, (payload_t*)ah); + } + ah->add_spi(ah, spi); + break; + default: + break; + } + child_sa->set_state(child_sa, CHILD_DELETING); + } + iterator->destroy(iterator); +} + +/** + * read in payloads and find the children to delete + */ +static void process_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *payloads, *spis; + payload_t *payload; + delete_payload_t *delete_payload; + u_int32_t *spi; + protocol_id_t protocol; + child_sa_t *child_sa; + + payloads = message->get_payload_iterator(message); + while (payloads->iterate(payloads, (void**)&payload)) + { + if (payload->get_type(payload) == DELETE) + { + delete_payload = (delete_payload_t*)payload; + protocol = delete_payload->get_protocol_id(delete_payload); + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + spis = delete_payload->create_spi_iterator(delete_payload); + while (spis->iterate(spis, (void**)&spi)) + { + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + *spi, FALSE); + if (child_sa == NULL) + { + DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, " + "but no such SA", protocol_id_names, protocol, ntohl(*spi)); + continue; + } + DBG2(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x", + protocol_id_names, protocol, ntohl(*spi)); + + switch (child_sa->get_state(child_sa)) + { + case CHILD_REKEYING: + /* we reply as usual, rekeying will fail */ + break; + case CHILD_DELETING: + /* we don't send back a delete if we initiated ourself */ + if (!this->initiator) + { + this->ike_sa->destroy_child_sa(this->ike_sa, + protocol, *spi); + continue; + } + default: + break; + } + + this->child_sas->insert_last(this->child_sas, child_sa); + } + spis->destroy(spis); + } + } + payloads->destroy(payloads); +} + +/** + * destroy the children listed in this->child_sas + */ +static void destroy_children(private_child_delete_t *this) +{ + iterator_t *iterator; + child_sa_t *child_sa; + protocol_id_t protocol; + u_int32_t spi; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + spi = child_sa->get_spi(child_sa, TRUE); + protocol = child_sa->get_protocol(child_sa); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_delete_t *this, message_t *message) +{ + build_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_delete_t *this, message_t *message) +{ + /* flush the list before adding new SAs */ + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); + + process_payloads(this, message); + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_delete_t *this, message_t *message) +{ + process_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_delete_t *this, message_t *message) +{ + /* if we are rekeying, we send an empty informational */ + if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING) + { + build_payloads(this, message); + } + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_delete_t *this) +{ + return CHILD_DELETE; +} + +/** + * Implementation of child_delete_t.get_child + */ +static child_sa_t* get_child(private_child_delete_t *this) +{ + child_sa_t *child_sa = NULL; + this->child_sas->get_first(this->child_sas, (void**)&child_sa); + return child_sa; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_delete_t *this) +{ + this->child_sas->destroy(this->child_sas); + free(this); +} + +/* + * Described in header. + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_delete_t *this = malloc_thing(private_child_delete_t); + + this->public.get_child = (child_sa_t*(*)(child_delete_t*))get_child; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + this->ike_sa = ike_sa; + this->child_sas = linked_list_create(); + + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + this->child_sas->insert_last(this->child_sas, child_sa); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + return &this->public; +} diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h new file mode 100644 index 000000000..a7e676a50 --- /dev/null +++ b/src/charon/sa/tasks/child_delete.h @@ -0,0 +1,66 @@ +/** + * @file child_delete.h + * + * @brief Interface child_delete_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef CHILD_DELETE_H_ +#define CHILD_DELETE_H_ + +typedef struct child_delete_t child_delete_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <sa/child_sa.h> + +/** + * @brief Task of type child_delete, delete a CHILD_SA. + * + * @b Constructors: + * - child_delete_create() + * + * @ingroup tasks + */ +struct child_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Get the CHILD_SA to delete by this task. + * + * @param this calling object + * @return child_sa + */ + child_sa_t* (*get_child) (child_delete_t *this); +}; + +/** + * @brief Create a new child_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa CHILD_SA to delete, or NULL as responder + * @return child_delete task to handle by the task_manager + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_DELETE_H_ */ diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c new file mode 100644 index 000000000..745895dbb --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.c @@ -0,0 +1,346 @@ +/** + * @file child_rekey.c + * + * @brief Implementation of the child_rekey task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "child_rekey.h" + +#include <daemon.h> +#include <encoding/payloads/notify_payload.h> +#include <sa/tasks/child_create.h> +#include <sa/tasks/child_delete.h> +#include <queues/jobs/rekey_child_sa_job.h> + + +typedef struct private_child_rekey_t private_child_rekey_t; + +/** + * Private members of a child_rekey_t task. + */ +struct private_child_rekey_t { + + /** + * Public methods and task_t interface. + */ + child_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the CHILD_CREATE task which is reused to simplify rekeying + */ + child_create_t *child_create; + + /** + * CHILD_SA which gets rekeyed + */ + child_sa_t *child_sa; + + /** + * colliding task, may be delete or rekey + */ + task_t *collision; +}; + +/** + * find a child using the REKEY_SA notify + */ +static void find_child(private_child_rekey_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + notify_payload_t *notify; + u_int32_t spi; + protocol_id_t protocol; + + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + + notify = (notify_payload_t*)payload; + protocol = notify->get_protocol_id(notify); + spi = notify->get_spi(notify); + + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + break; + + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_rekey_t *this, message_t *message) +{ + notify_payload_t *notify; + protocol_id_t protocol; + u_int32_t spi, reqid; + + /* we just need the rekey notify ... */ + protocol = this->child_sa->get_protocol(this->child_sa); + spi = this->child_sa->get_spi(this->child_sa, TRUE); + notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); + notify->set_spi(notify, spi); + message->add_payload(message, (payload_t*)notify); + + /* ... our CHILD_CREATE task does the hard work for us. */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_rekey_t *this, message_t *message) +{ + /* let the CHILD_CREATE task process the message */ + this->child_create->task.process(&this->child_create->task, message); + + find_child(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_rekey_t *this, message_t *message) +{ + u_int32_t reqid; + + if (this->child_sa == NULL || + this->child_sa->get_state(this->child_sa) == CHILD_DELETING) + { + DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + /* let the CHILD_CREATE task build the response */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + /* rekeying failed, reuse old child */ + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + return SUCCESS; + } + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_rekey_t *this, message_t *message) +{ + protocol_id_t protocol; + u_int32_t spi; + child_sa_t *to_delete; + + this->child_create->task.process(&this->child_create->task, message); + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + /* establishing new child failed, reuse old. but not when we + * recieved a delete in the meantime */ + if (!(this->collision && + this->collision->get_type(this->collision) == CHILD_DELETE)) + { + job_t *job; + u_int32_t retry = charon->configuration->get_retry_interval( + charon->configuration); + job = (job_t*)rekey_child_sa_job_create( + this->child_sa->get_reqid(this->child_sa), + this->child_sa->get_protocol(this->child_sa), + this->child_sa->get_spi(this->child_sa, TRUE)); + DBG1(DBG_IKE, "CHILD_SA rekeying failed, " + "trying again in %d seconds", retry); + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); + } + return SUCCESS; + } + + to_delete = this->child_sa; + + /* check for rekey collisions */ + if (this->collision && + this->collision->get_type(this->collision) == CHILD_REKEY) + { + chunk_t this_nonce, other_nonce; + private_child_rekey_t *other = (private_child_rekey_t*)this->collision; + + this_nonce = this->child_create->get_lower_nonce(this->child_create); + other_nonce = other->child_create->get_lower_nonce(other->child_create); + + /* if we have the lower nonce, delete rekeyed SA. If not, delete + * the redundant. */ + if (memcmp(this_nonce.ptr, other_nonce.ptr, + min(this_nonce.len, other_nonce.len)) < 0) + { + DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child"); + } + else + { + DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child"); + to_delete = this->child_create->get_child(this->child_create); + if (to_delete == NULL) + { + /* ooops, should not happen, fallback */ + to_delete = this->child_sa; + } + } + } + + spi = to_delete->get_spi(to_delete, TRUE); + protocol = to_delete->get_protocol(to_delete); + if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_rekey_t *this) +{ + return CHILD_REKEY; +} + +/** + * Implementation of child_rekey_t.collide + */ +static void collide(private_child_rekey_t *this, task_t *other) +{ + /* the task manager only detects exchange collision, but not if + * the collision is for the same child. we check it here. */ + if (other->get_type(other) == CHILD_REKEY) + { + private_child_rekey_t *rekey = (private_child_rekey_t*)other; + if (rekey == NULL || rekey->child_sa != this->child_sa) + { + /* not the same child => no collision */ + return; + } + } + else if (other->get_type(other) == CHILD_DELETE) + { + child_delete_t *del = (child_delete_t*)other; + if (del == NULL || del->get_child(del) != this->child_sa) + { + /* not the same child => no collision */ + return; + } + } + else + { + /* any other task is not critical for collisisions, ignore */ + return; + } + DESTROY_IF(this->collision); + this->collision = other; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) +{ + this->child_create->task.migrate(&this->child_create->task, ike_sa); + DESTROY_IF(this->collision); + + this->ike_sa = ike_sa; + this->collision = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_rekey_t *this) +{ + this->child_create->task.destroy(&this->child_create->task); + DESTROY_IF(this->collision); + free(this); +} + +/* + * Described in header. + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_rekey_t *this = malloc_thing(private_child_rekey_t); + policy_t *policy; + + this->public.collide = (void (*)(child_rekey_t*,task_t*))collide; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy = child_sa->get_policy(child_sa); + this->child_create = child_create_create(ike_sa, policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + this->child_create = child_create_create(ike_sa, NULL); + } + + this->ike_sa = ike_sa; + this->child_sa = child_sa; + this->collision = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h new file mode 100644 index 000000000..3515f0c3f --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.h @@ -0,0 +1,70 @@ +/** + * @file child_rekey.h + * + * @brief Interface child_rekey_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef CHILD_REKEY_H_ +#define CHILD_REKEY_H_ + +typedef struct child_rekey_t child_rekey_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/child_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA. + * + * @b Constructors: + * - child_rekey_create() + * + * @ingroup tasks + */ +struct child_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Register a rekeying task which collides with this one + * + * If two peers initiate rekeying at the same time, the collision must + * be handled gracefully. The task manager is aware of what exchanges + * are going on and notifies the outgoing task by passing the incoming. + * + * @param this task initated by us + * @param other incoming task + */ + void (*collide)(child_rekey_t* this, task_t *other); +}; + +/** + * @brief Create a new CHILD_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa child_sa to rekey, NULL if responder + * @return child_rekey task to handle by the task_manager + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_REKEY_H_ */ diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c new file mode 100644 index 000000000..541e1bb37 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.c @@ -0,0 +1,750 @@ +/** + * @file ike_auth.c + * + * @brief Implementation of the ike_auth task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_auth.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/eap_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <sa/authenticators/eap_authenticator.h> + + + +typedef struct private_ike_auth_t private_ike_auth_t; + +/** + * Private members of a ike_auth_t task. + */ +struct private_ike_auth_t { + + /** + * Public methods and task_t interface. + */ + ike_auth_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Nonce chosen by us in ike_init + */ + chunk_t my_nonce; + + /** + * Nonce chosen by peer in ike_init + */ + chunk_t other_nonce; + + /** + * IKE_SA_INIT message sent by us + */ + packet_t *my_packet; + + /** + * IKE_SA_INIT message sent by peer + */ + packet_t *other_packet; + + /** + * EAP authenticator when using EAP + */ + eap_authenticator_t *eap_auth; + + /** + * EAP payload received and ready to process + */ + eap_payload_t *eap_payload; + + /** + * has the peer been authenticated successfully? + */ + bool peer_authenticated; +}; + +/** + * build the AUTH payload + */ +static status_t build_auth(private_ike_auth_t *this, message_t *message) +{ + authenticator_t *auth; + auth_payload_t *auth_payload; + policy_t *policy; + auth_method_t method; + status_t status; + + /* create own authenticator and add auth payload */ + policy = this->ike_sa->get_policy(this->ike_sa); + if (!policy) + { + SIG(IKE_UP_FAILED, "unable to authenticate, no policy found"); + return FAILED; + } + method = policy->get_auth_method(policy); + + auth = authenticator_create(this->ike_sa, method); + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "configured authentication method %N not supported", + auth_method_names, method); + return FAILED; + } + + status = auth->build(auth, this->my_packet->get_data(this->my_packet), + this->other_nonce, &auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "generating authentication data failed"); + return FAILED; + } + message->add_payload(message, (payload_t*)auth_payload); + return SUCCESS; +} + +/** + * build ID payload(s) + */ +static status_t build_id(private_ike_auth_t *this, message_t *message) +{ + identification_t *me, *other; + id_payload_t *id; + policy_t *policy; + + me = this->ike_sa->get_my_id(this->ike_sa); + other = this->ike_sa->get_other_id(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + + if (me->contains_wildcards(me)) + { + me = policy->get_my_id(policy); + if (me->contains_wildcards(me)) + { + SIG(IKE_UP_FAILED, "negotiation of own ID failed"); + return FAILED; + } + this->ike_sa->set_my_id(this->ike_sa, me->clone(me)); + } + + id = id_payload_create_from_identification(this->initiator, me); + message->add_payload(message, (payload_t*)id); + + /* as initiator, include other ID if it does not contain wildcards */ + if (this->initiator && !other->contains_wildcards(other)) + { + id = id_payload_create_from_identification(FALSE, other); + message->add_payload(message, (payload_t*)id); + } + return SUCCESS; +} + +/** + * process AUTH payload + */ +static status_t process_auth(private_ike_auth_t *this, message_t *message) +{ + auth_payload_t *auth_payload; + authenticator_t *auth; + auth_method_t auth_method; + status_t status; + + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); + + if (auth_payload == NULL) + { + /* AUTH payload is missing, client wants to use EAP authentication */ + return NOT_FOUND; + } + + auth_method = auth_payload->get_auth_method(auth_payload); + auth = authenticator_create(this->ike_sa, auth_method); + + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "authentication method %N used by %D not " + "supported", auth_method_names, auth_method, + this->ike_sa->get_other_id(this->ike_sa)); + return NOT_SUPPORTED; + } + status = auth->verify(auth, this->other_packet->get_data(this->other_packet), + this->my_nonce, auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, auth_method); + return FAILED; + } + return SUCCESS; +} + +/** + * process ID payload(s) + */ +static status_t process_id(private_ike_auth_t *this, message_t *message) +{ + identification_t *id; + id_payload_t *idr, *idi; + + idi = (id_payload_t*)message->get_payload(message, ID_INITIATOR); + idr = (id_payload_t*)message->get_payload(message, ID_RESPONDER); + + if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL)) + { + SIG(IKE_UP_FAILED, "ID payload missing in message"); + return FAILED; + } + + if (this->initiator) + { + id = idr->get_identification(idr); + this->ike_sa->set_other_id(this->ike_sa, id); + } + else + { + id = idi->get_identification(idi); + this->ike_sa->set_other_id(this->ike_sa, id); + if (idr) + { + id = idr->get_identification(idr); + this->ike_sa->set_my_id(this->ike_sa, id); + } + } + return SUCCESS; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from our message + */ +static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message) +{ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->my_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + if (this->ike_sa->generate_message(this->ike_sa, message, + &this->my_packet) != SUCCESS) + { + return FAILED; + } + return NEED_MORE; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from others message + */ +static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message) +{ + /* we collect the needed information in the IKE_SA_INIT exchange */ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->other_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + this->other_packet = message->get_packet(message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build to create AUTH payload from EAP data + */ +static status_t build_auth_eap(private_ike_auth_t *this, message_t *message) +{ + authenticator_t *auth; + auth_payload_t *auth_payload; + + auth = (authenticator_t*)this->eap_auth; + if (auth->build(auth, this->my_packet->get_data(this->my_packet), + this->other_nonce, &auth_payload) != SUCCESS) + { + SIG(IKE_UP_FAILED, "generating authentication data failed"); + if (!this->initiator) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + } + return FAILED; + } + message->add_payload(message, (payload_t*)auth_payload); + if (!this->initiator) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process to verify AUTH payload after EAP + */ +static status_t process_auth_eap(private_ike_auth_t *this, message_t *message) +{ + auth_payload_t *auth_payload; + authenticator_t *auth; + + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); + this->peer_authenticated = FALSE; + + if (auth_payload) + { + auth = (authenticator_t*)this->eap_auth; + if (auth->verify(auth, this->other_packet->get_data(this->other_packet), + this->my_nonce, auth_payload) == SUCCESS) + { + this->peer_authenticated = TRUE; + } + } + + if (!this->peer_authenticated) + { + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, AUTH_EAP); + if (this->initiator) + { + return FAILED; + } + return NEED_MORE; + } + if (this->initiator) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for EAP exchanges + */ +static status_t process_eap_i(private_ike_auth_t *this, message_t *message) +{ + eap_payload_t *eap; + + eap = (eap_payload_t*)message->get_payload(message, EXTENSIBLE_AUTHENTICATION); + if (eap == NULL) + { + SIG(IKE_UP_FAILED, "EAP payload missing"); + return FAILED; + } + switch (this->eap_auth->process(this->eap_auth, eap, &eap)) + { + case NEED_MORE: + this->eap_payload = eap; + return NEED_MORE; + case SUCCESS: + /* EAP exchange completed, now create and process AUTH */ + this->eap_payload = NULL; + this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap; + return NEED_MORE; + default: + this->eap_payload = NULL; + SIG(IKE_UP_FAILED, "failed to authenticate against %D using EAP", + this->ike_sa->get_other_id(this->ike_sa)); + return FAILED; + } +} + +/** + * Implementation of task_t.process for EAP exchanges + */ +static status_t process_eap_r(private_ike_auth_t *this, message_t *message) +{ + this->eap_payload = (eap_payload_t*)message->get_payload(message, + EXTENSIBLE_AUTHENTICATION); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for EAP exchanges + */ +static status_t build_eap_i(private_ike_auth_t *this, message_t *message) +{ + message->add_payload(message, (payload_t*)this->eap_payload); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for EAP exchanges + */ +static status_t build_eap_r(private_ike_auth_t *this, message_t *message) +{ + status_t status = NEED_MORE; + eap_payload_t *eap; + + if (this->eap_payload == NULL) + { + SIG(IKE_UP_FAILED, "EAP payload missing"); + return FAILED; + } + + switch (this->eap_auth->process(this->eap_auth, this->eap_payload, &eap)) + { + case NEED_MORE: + + break; + case SUCCESS: + /* EAP exchange completed, now create and process AUTH */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap; + break; + default: + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, AUTH_EAP); + status = FAILED; + break; + } + message->add_payload(message, (payload_t*)eap); + return status; +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_auth_t *this, message_t *message) +{ + policy_t *policy; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + if (build_id(this, message) != SUCCESS) + { + return FAILED; + } + + policy = this->ike_sa->get_policy(this->ike_sa); + if (policy->get_auth_method(policy) == AUTH_EAP) + { + this->eap_auth = eap_authenticator_create(this->ike_sa); + } + else + { + if (build_auth(this, message) != SUCCESS) + { + return FAILED; + } + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_auth_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + if (process_id(this, message) != SUCCESS) + { + return NEED_MORE; + } + + switch (process_auth(this, message)) + { + case SUCCESS: + this->peer_authenticated = TRUE; + break; + case NOT_FOUND: + /* use EAP if no AUTH payload found */ + this->eap_auth = eap_authenticator_create(this->ike_sa); + break; + default: + break; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_auth_t *this, message_t *message) +{ + policy_t *policy; + eap_type_t eap_type; + eap_payload_t *eap_payload; + status_t status; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + policy = this->ike_sa->get_policy(this->ike_sa); + if (policy == NULL) + { + SIG(IKE_UP_FAILED, "no acceptable policy found"); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + if (build_id(this, message) != SUCCESS || + build_auth(this, message) != SUCCESS) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + /* use "traditional" authentication if we could authenticate peer */ + if (this->peer_authenticated) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + + if (this->eap_auth == NULL) + { + /* peer not authenticated, nor does it want to use EAP */ + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + /* initiate EAP authenitcation */ + eap_type = policy->get_eap_type(policy); + status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_payload); + message->add_payload(message, (payload_t*)eap_payload); + if (status != NEED_MORE) + { + SIG(IKE_UP_FAILED, "unable to initiate EAP authentication"); + return FAILED; + } + + /* switch to EAP methods */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_r; + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_auth_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + /* these are errors, but are not critical as only the + * CHILD_SA won't get build, but IKE_SA establishes anyway */ + break; + default: + { + if (type < 16383) + { + SIG(IKE_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + DBG1(DBG_IKE, "received %N notify", + notify_type_names, type); + break; + } + } + } + } + iterator->destroy(iterator); + + if (process_id(this, message) != SUCCESS || + process_auth(this, message) != SUCCESS) + { + return FAILED; + } + + if (this->eap_auth) + { + /* switch to EAP authentication methods */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_i; + return process_eap_i(this, message); + } + + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_auth_t *this) +{ + return IKE_AUTHENTICATE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + if (this->eap_auth) + { + this->eap_auth->authenticator_interface.destroy( + &this->eap_auth->authenticator_interface); + } + + this->my_packet = NULL; + this->other_packet = NULL; + this->peer_authenticated = FALSE; + this->eap_auth = NULL; + this->eap_payload = NULL; + this->ike_sa = ike_sa; + if (this->initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_auth_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + if (this->eap_auth) + { + this->eap_auth->authenticator_interface.destroy( + &this->eap_auth->authenticator_interface); + } + free(this); +} + +/* + * Described in header. + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_auth_t *this = malloc_thing(private_ike_auth_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->my_packet = NULL; + this->other_packet = NULL; + this->peer_authenticated = FALSE; + this->eap_auth = NULL; + this->eap_payload = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h new file mode 100644 index 000000000..d7326c988 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.h @@ -0,0 +1,64 @@ +/** + * @file ike_auth.h + * + * @brief Interface ike_auth_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_AUTH_H_ +#define IKE_AUTH_H_ + +typedef struct ike_auth_t ike_auth_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_auth, authenticates an IKE_SA using authenticators. + * + * The ike_auth task authenticates the IKE_SA using the IKE_AUTH + * exchange. It processes and build IDi and IDr payloads and also + * handles AUTH payloads. The AUTH payloads are passed to authenticator_t's, + * which do the actual authentication process. If the ike_auth task is used + * with EAP authentication, it stays alive over multiple exchanges until + * EAP has completed. + * + * @b Constructors: + * - ike_auth_create() + * + * @ingroup tasks + */ +struct ike_auth_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new task of type IKE_AUTHENTICATE. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the initator of an exchange + * @return ike_auth task to handle by the task_manager + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_AUTH_H_ */ diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c new file mode 100644 index 000000000..160600742 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.c @@ -0,0 +1,370 @@ +/** + * @file ike_cert.c + * + * @brief Implementation of the ike_cert task. + * + */ + +/* + * Copyright (C) 2006-2007 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. + */ + +#include "ike_cert.h" + +#include <daemon.h> +#include <sa/ike_sa.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> + + +typedef struct private_ike_cert_t private_ike_cert_t; + +/** + * Private members of a ike_cert_t task. + */ +struct private_ike_cert_t { + + /** + * Public methods and task_t interface. + */ + ike_cert_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * list of CA cert hashes requested, items point to 20 byte chunk + */ + linked_list_t *cas; + + /** + * have we seen a certificate request? + */ + bool certreq_seen; +}; + +/** + * read certificate requests + */ +static void process_certreqs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE_REQUEST) + { + certreq_payload_t *certreq = (certreq_payload_t*)payload; + cert_encoding_t encoding; + chunk_t keyids, keyid; + + this->certreq_seen = TRUE; + + encoding = certreq->get_cert_encoding(certreq); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certreq payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + keyids = certreq->get_data(certreq); + + while (keyids.len >= HASH_SIZE_SHA1) + { + keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1); + keyid = chunk_clone(keyid); + this->cas->insert_last(this->cas, keyid.ptr); + keyids = chunk_skip(keyids, HASH_SIZE_SHA1); + } + } + } + iterator->destroy(iterator); +} + +/** + * import certificates + */ +static void process_certs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE) + { + cert_encoding_t encoding; + x509_t *cert; + chunk_t cert_data; + bool found; + cert_payload_t *cert_payload = (cert_payload_t*)payload; + + encoding = cert_payload->get_cert_encoding(cert_payload); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certificate payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + cert_data = cert_payload->get_data_clone(cert_payload); + cert = x509_create_from_chunk(cert_data, 0); + if (cert) + { + if (charon->credentials->verify(charon->credentials, + cert, &found)) + { + DBG2(DBG_IKE, "received end entity certificate is trusted, " + "added to store"); + if (!found) + { + charon->credentials->add_end_certificate( + charon->credentials, cert); + } + else + { + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "received end entity certificate is not " + "trusted, discarded"); + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "parsing of received certificate failed, discarded"); + chunk_free(&cert_data); + } + } + } + iterator->destroy(iterator); +} + +/** + * build certificate requests + */ +static void build_certreqs(private_ike_cert_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + identification_t *ca; + certreq_payload_t *certreq; + + connection = this->ike_sa->get_connection(this->ike_sa); + + if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND) + { + policy = this->ike_sa->get_policy(this->ike_sa); + + if (policy) + { + ca = policy->get_other_ca(policy); + + if (ca && ca->get_type(ca) != ID_ANY) + { + certreq = certreq_payload_create_from_cacert(ca); + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + + if (certreq) + { + message->add_payload(message, (payload_t*)certreq); + } + } +} + +/** + * add certificates to message + */ +static void build_certs(private_ike_cert_t *this, message_t *message) +{ + policy_t *policy; + connection_t *connection; + x509_t *cert; + cert_payload_t *payload; + + policy = this->ike_sa->get_policy(this->ike_sa); + connection = this->ike_sa->get_connection(this->ike_sa); + + if (policy && policy->get_auth_method(policy) == AUTH_RSA) + { + switch (connection->get_cert_policy(connection)) + { + case CERT_NEVER_SEND: + break; + case CERT_SEND_IF_ASKED: + if (!this->certreq_seen) + { + break; + } + /* FALL */ + case CERT_ALWAYS_SEND: + { + /* TODO: respect CA cert request */ + cert = charon->credentials->get_certificate(charon->credentials, + policy->get_my_id(policy)); + if (cert) + { + payload = cert_payload_create_from_x509(cert); + message->add_payload(message, (payload_t*)payload); + } + } + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + build_certreqs(this, message); + build_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + process_certreqs(this, message); + process_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + build_certreqs(this, message); + return NEED_MORE; + } + + build_certs(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + process_certreqs(this, message); + return NEED_MORE; + } + + process_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_cert_t *this) +{ + return IKE_CERT; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->cas->destroy_function(this->cas, free); + this->cas = linked_list_create(); + this->certreq_seen = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_cert_t *this) +{ + this->cas->destroy_function(this->cas, free); + free(this); +} + +/* + * Described in header. + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_cert_t *this = malloc_thing(private_ike_cert_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->cas = linked_list_create(); + this->certreq_seen = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h new file mode 100644 index 000000000..ba0283953 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.h @@ -0,0 +1,61 @@ +/** + * @file ike_cert.h + * + * @brief Interface ike_cert_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_CERT_H_ +#define IKE_CERT_H_ + +typedef struct ike_cert_t ike_cert_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_cert, exchanges certificates and + * certificate requests. + * + * @b Constructors: + * - ike_cert_create() + * + * @ingroup tasks + */ +struct ike_cert_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_cert task. + * + * The initiator parameter means the original initiator, not the initiator + * of the certificate request. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_cert task to handle by the task_manager + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_CERT_H_ */ diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c new file mode 100644 index 000000000..ce29b9220 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.c @@ -0,0 +1,428 @@ +/** + * @file ike_config.c + * + * @brief Implementation of the ike_config task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_config.h" + +#include <daemon.h> +#include <encoding/payloads/cp_payload.h> + +typedef struct private_ike_config_t private_ike_config_t; + +/** + * Private members of a ike_config_t task. + */ +struct private_ike_config_t { + + /** + * Public methods and task_t interface. + */ + ike_config_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * associated policy with virtual IP configuration + */ + policy_t *policy; + + /** + * virtual ip + */ + host_t *virtual_ip; + + /** + * list of DNS servers + */ + linked_list_t *dns; +}; + +/** + * build configuration payloads and attributes + */ +static void build_payloads(private_ike_config_t *this, message_t *message, + config_type_t type) +{ + cp_payload_t *cp; + configuration_attribute_t *ca; + chunk_t chunk, prefix; + + if (!this->virtual_ip) + { + return; + } + + cp = cp_payload_create(); + cp->set_config_type(cp, type); + + ca = configuration_attribute_create(); + + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + chunk = this->virtual_ip->get_address(this->virtual_ip); + } + } + else + { + ca->set_type(ca, INTERNAL_IP6_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + prefix = chunk_alloca(1); + *prefix.ptr = 64; + chunk = this->virtual_ip->get_address(this->virtual_ip); + chunk = chunk_cata("cc", chunk, prefix); + } + } + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + + /* we currently always add a DNS request if we request an IP */ + if (this->initiator) + { + ca = configuration_attribute_create(); + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + cp->add_configuration_attribute(cp, ca); + } + else + { + host_t *ip; + iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE); + while (iterator->iterate(iterator, (void**)&ip)) + { + ca = configuration_attribute_create(); + if (ip->get_family(ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + chunk = ip->get_address(ip); + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + } + iterator->destroy(iterator); + } + message->add_payload(message, (payload_t*)cp); +} + +/** + * process a single configuration attribute + */ +static void process_attribute(private_ike_config_t *this, + configuration_attribute_t *ca) +{ + host_t *ip; + chunk_t addr; + int family = AF_INET6; + + switch (ca->get_type(ca)) + { + case INTERNAL_IP4_ADDRESS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_ADDRESS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + /* skip prefix byte in IPv6 payload*/ + if (family == AF_INET6) + { + addr.len--; + } + ip = host_create_from_chunk(family, addr, 0); + } + if (ip && !this->virtual_ip) + { + this->virtual_ip = ip; + } + break; + } + case INTERNAL_IP4_DNS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_DNS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + ip = host_create_from_chunk(family, addr, 0); + } + if (ip) + { + this->dns->insert_last(this->dns, ip); + } + break; + } + case INTERNAL_IP4_NBNS: + case INTERNAL_IP6_NBNS: + /* TODO */ + default: + DBG1(DBG_IKE, "ignoring %N config attribute", + configuration_attribute_type_names, + ca->get_type(ca)); + break; + } +} + +/** + * Scan for configuration payloads and attributes + */ +static void process_payloads(private_ike_config_t *this, message_t *message) +{ + iterator_t *iterator, *attributes; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CONFIGURATION) + { + cp_payload_t *cp = (cp_payload_t*)payload; + configuration_attribute_t *ca; + switch (cp->get_config_type(cp)) + { + case CFG_REQUEST: + case CFG_REPLY: + { + attributes = cp->create_attribute_iterator(cp); + while (attributes->iterate(attributes, (void**)&ca)) + { + process_attribute(this, ca); + } + attributes->destroy(attributes); + break; + } + default: + DBG1(DBG_IKE, "ignoring %N config payload", + config_type_names, cp->get_config_type(cp)); + break; + } + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, ID_INITIATOR)) + { + this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL); + + build_payloads(this, message, CFG_REQUEST); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, ID_INITIATOR)) + { + process_payloads(this, message); + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, EXTENSIBLE_AUTHENTICATION) == NULL) + { + this->policy = this->ike_sa->get_policy(this->ike_sa); + + if (this->policy && this->virtual_ip) + { + host_t *ip; + + DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); + ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip); + if (ip == NULL || ip->is_anyaddr(ip)) + { + DBG1(DBG_IKE, "not assigning a virtual IP to peer"); + return SUCCESS; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip); + this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip); + + this->virtual_ip->destroy(this->virtual_ip); + this->virtual_ip = ip; + + /* DNS testing values + if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + ip->destroy(ip); + ip = host_create_from_string("10.3.0.1", 0); + this->dns->insert_last(this->dns, ip); + ip = host_create_from_string("10.3.0.2", 0); + this->dns->insert_last(this->dns, ip); + } */ + + build_payloads(this, message, CFG_REPLY); + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + !message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + host_t *ip; + + DESTROY_IF(this->virtual_ip); + this->virtual_ip = NULL; + + process_payloads(this, message); + + if (this->virtual_ip) + { + this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + + while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + if (!ip->is_anyaddr(ip)) + { + this->ike_sa->add_dns_server(this->ike_sa, ip); + } + ip->destroy(ip); + } + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_config_t *this) +{ + return IKE_CONFIG; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + + this->ike_sa = ike_sa; + this->virtual_ip = NULL; + this->dns = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_config_t *this) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_ike_config_t *this = malloc_thing(private_ike_config_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->virtual_ip = NULL; + this->dns = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h new file mode 100644 index 000000000..0c9b961b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.h @@ -0,0 +1,59 @@ +/** + * @file ike_config.h + * + * @brief Interface ike_config_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_CONFIG_H_ +#define IKE_CONFIG_H_ + +typedef struct ike_config_t ike_config_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> +#include <config/policies/policy.h> + +/** + * @brief Task of type IKE_CONFIG, sets up a virtual IP and other + * configurations for an IKE_SA. + * + * @b Constructors: + * - ike_config_create() + * + * @ingroup tasks + */ +struct ike_config_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_config task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy for the initiator, NULL for the responder + * @return ike_config task to handle by the task_manager + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* IKE_CONFIG_H_ */ diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c new file mode 100644 index 000000000..9c4fdac0e --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.c @@ -0,0 +1,172 @@ +/** + * @file ike_delete.c + * + * @brief Implementation of the ike_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 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. + */ + +#include "ike_delete.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_ike_delete_t private_ike_delete_t; + +/** + * Private members of a ike_delete_t task. + */ +struct private_ike_delete_t { + + /** + * Public methods and task_t interface. + */ + ike_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * are we responding to a delete, but have initated our own? + */ + bool simultaneous; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_delete_t *this, message_t *message) +{ + delete_payload_t *delete_payload; + + delete_payload = delete_payload_create(PROTO_IKE); + message->add_payload(message, (payload_t*)delete_payload); + + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_delete_t *this, message_t *message) +{ + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_delete_t *this, message_t *message) +{ + /* we don't even scan the payloads, as the message wouldn't have + * come so far without being correct */ + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_DELETING: + this->simultaneous = TRUE; + break; + case IKE_ESTABLISHED: + DBG1(DBG_IKE, "deleting IKE_SA on request"); + break; + case IKE_REKEYING: + DBG1(DBG_IKE, "initiated rekeying, but received delete for IKE_SA"); + break; + default: + break; + } + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_delete_t *this, message_t *message) +{ + if (this->simultaneous) + { + /* wait for peers response for our delete request, but set a timeout */ + return SUCCESS; + } + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_delete_t *this) +{ + return IKE_DELETE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->simultaneous = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_delete_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_delete_t *this = malloc_thing(private_ike_delete_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->simultaneous = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h new file mode 100644 index 000000000..e8ec5ebbe --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.h @@ -0,0 +1,57 @@ +/** + * @file ike_delete.h + * + * @brief Interface ike_delete_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_DELETE_H_ +#define IKE_DELETE_H_ + +typedef struct ike_delete_t ike_delete_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_delete, delete an IKE_SA. + * + * @b Constructors: + * - ike_delete_create() + * + * @ingroup tasks + */ +struct ike_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if we initiate the delete + * @return ike_delete task to handle by the task_manager + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_DELETE_H_ */ diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c new file mode 100644 index 000000000..1cb05c45c --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.c @@ -0,0 +1,106 @@ +/** + * @file ike_dpd.c + * + * @brief Implementation of the ike_dpd task. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#include "ike_dpd.h" + +#include <daemon.h> + + +typedef struct private_ike_dpd_t private_ike_dpd_t; + +/** + * Private members of a ike_dpd_t task. + */ +struct private_ike_dpd_t { + + /** + * Public methods and task_t interface. + */ + ike_dpd_t public; +}; + +/** + * Implementation of task_t.build for initiator + * Implementation of task_t.process for responder + */ +static status_t return_need_more(private_ike_dpd_t *this, message_t *message) +{ + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + * Implementation of task_t.build for responder + */ +static status_t return_success(private_ike_dpd_t *this, message_t *message) +{ + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_dpd_t *this) +{ + return IKE_DEADPEER; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa) +{ + +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_dpd_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_dpd_t *ike_dpd_create(bool initiator) +{ + private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_success; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_success; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more; + } + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h new file mode 100644 index 000000000..531b0502d --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.h @@ -0,0 +1,58 @@ +/** + * @file ike_dpd.h + * + * @brief Interface ike_dpd_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_DPD_H_ +#define IKE_DPD_H_ + +typedef struct ike_dpd_t ike_dpd_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_dpd, detects dead peers. + * + * The DPD task actually does nothing, as a DPD has no associated payloads. + * + * @b Constructors: + * - ike_dpd_create() + * + * @ingroup tasks + */ +struct ike_dpd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_dpd task. + * + * @param initiator TRUE if thask is the original initator + * @return ike_dpd task to handle by the task_manager + */ +ike_dpd_t *ike_dpd_create(bool initiator); + +#endif /* IKE_DPD_H_ */ diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c new file mode 100644 index 000000000..0b493666a --- /dev/null +++ b/src/charon/sa/tasks/ike_init.c @@ -0,0 +1,598 @@ +/** + * @file ike_init.c + * + * @brief Implementation of the ike_init task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_init.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/nonce_payload.h> + +/** maximum retries to do with cookies/other dh groups */ +#define MAX_RETRIES 5 + +typedef struct private_ike_init_t private_ike_init_t; + +/** + * Private members of a ike_init_t task. + */ +struct private_ike_init_t { + + /** + * Public methods and task_t interface. + */ + ike_init_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Connection established by this IKE_SA + */ + connection_t *connection; + + /** + * diffie hellman group to use + */ + diffie_hellman_group_t dh_group; + + /** + * Diffie hellman object used to generate public DH value. + */ + diffie_hellman_t *diffie_hellman; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * Negotiated proposal used for IKE_SA + */ + proposal_t *proposal; + + /** + * Old IKE_SA which gets rekeyed + */ + ike_sa_t *old_sa; + + /** + * cookie received from responder + */ + chunk_t cookie; + + /** + * retries done so far after failure (cookie or bad dh group) + */ + u_int retry; +}; + +/** + * build the payloads for the message + */ +static void build_payloads(private_ike_init_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + linked_list_t *proposal_list; + ike_sa_id_t *id; + proposal_t *proposal; + iterator_t *iterator; + + id = this->ike_sa->get_id(this->ike_sa); + + this->connection = this->ike_sa->get_connection(this->ike_sa); + + if (this->initiator) + { + proposal_list = this->connection->get_proposals(this->connection); + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + iterator = proposal_list->create_iterator(proposal_list, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + proposal->set_spi(proposal, id->get_initiator_spi(id)); + } + iterator->destroy(iterator); + } + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); + } + else + { + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + this->proposal->set_spi(this->proposal, id->get_responder_spi(id)); + } + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + message->add_payload(message, (payload_t*)nonce_payload); + + ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + message->add_payload(message, (payload_t*)ke_payload); +} + +/** + * Read payloads from message + */ +static void process_payloads(private_ike_init_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + { + sa_payload_t *sa_payload = (sa_payload_t*)payload; + linked_list_t *proposal_list; + + proposal_list = sa_payload->get_proposals(sa_payload); + this->proposal = this->connection->select_proposal( + this->connection, proposal_list); + proposal_list->destroy_offset(proposal_list, + offsetof(proposal_t, destroy)); + break; + } + case KEY_EXCHANGE: + { + ke_payload_t *ke_payload = (ke_payload_t*)payload; + diffie_hellman_group_t dh_group; + chunk_t key_data; + + dh_group = ke_payload->get_dh_group_number(ke_payload); + + if (this->initiator) + { + if (dh_group != this->dh_group) + { + DBG1(DBG_IKE, "received a DH group not requested (%N)", + diffie_hellman_group_names, dh_group); + break; + } + } + else + { + this->dh_group = dh_group; + if (!this->connection->check_dh_group(this->connection, + dh_group)) + { + break; + } + this->diffie_hellman = diffie_hellman_create(dh_group); + } + if (this->diffie_hellman) + { + key_data = ke_payload->get_key_exchange_data(ke_payload); + this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data); + } + break; + } + case NONCE: + { + nonce_payload_t *nonce_payload = (nonce_payload_t*)payload; + this->other_nonce = nonce_payload->get_nonce(nonce_payload); + break; + } + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + status_t status; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_START, "initiating IKE_SA to %H", + this->connection->get_other_host(this->connection)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + if (this->retry++ >= MAX_RETRIES) + { + SIG(IKE_UP_FAILED, "giving up after %d retries", MAX_RETRIES); + return FAILED; + } + + /* if the DH group is set via use_dh_group(), we already have a DH object */ + if (!this->diffie_hellman) + { + this->dh_group = this->connection->get_dh_group(this->connection); + this->diffie_hellman = diffie_hellman_create(this->dh_group); + if (this->diffie_hellman == NULL) + { + SIG(IKE_UP_FAILED, "configured DH group %N not supported", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + } + + /* generate nonce only when we are trying the first time */ + if (this->my_nonce.ptr == NULL) + { + randomizer = randomizer_create(); + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "error generating random nonce value"); + return FAILED; + } + } + + if (this->cookie.ptr) + { + message->add_notify(message, FALSE, COOKIE, this->cookie); + } + + build_payloads(this, message); + + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA", + message->get_source(message)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + randomizer = randomizer_create(); + if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce) != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + } + randomizer->destroy(randomizer); + + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + + /* check if we have everything we need */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "received proposals inacceptable"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + chunk_t chunk; + u_int16_t dh_enc; + + SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)", + diffie_hellman_group_names, this->dh_group); + this->dh_group = this->connection->get_dh_group(this->connection); + dh_enc = htons(this->dh_group); + chunk.ptr = (u_int8_t*)&dh_enc; + chunk.len = sizeof(dh_enc); + message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk); + DBG1(DBG_IKE, "requesting DH group %N", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + + + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + /* Apply SPI if we are rekeying */ + id = this->ike_sa->get_id(this->ike_sa); + id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, child_prf, prf); + } + else + { + /* setup crypto keys */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + build_payloads(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + iterator_t *iterator; + payload_t *payload; + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case INVALID_KE_PAYLOAD: + { + chunk_t data; + diffie_hellman_group_t old_dh_group; + + old_dh_group = this->dh_group; + data = notify->get_notification_data(notify); + this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + + DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested" + " %N", diffie_hellman_group_names, old_dh_group, + diffie_hellman_group_names, this->dh_group); + if (!this->connection->check_dh_group(this->connection, + this->dh_group)) + { + DBG1(DBG_IKE, "requested DH group %N not acceptable, " + "giving up", diffie_hellman_group_names, + this->dh_group); + iterator->destroy(iterator); + return FAILED; + } + + this->ike_sa->reset(this->ike_sa); + + iterator->destroy(iterator); + return NEED_MORE; + } + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: + /* skip, handled in ike_natd_t */ + break; + case COOKIE: + { + chunk_free(&this->cookie); + this->cookie = chunk_clone(notify->get_notification_data(notify)); + this->ike_sa->reset(this->ike_sa); + iterator->destroy(iterator); + DBG1(DBG_IKE, "received %N notify", notify_type_names, type); + return NEED_MORE; + } + default: + { + if (type < 16383) + { + SIG(IKE_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + DBG1(DBG_IKE, "received %N notify", + notify_type_names, type); + break; + } + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + /* check if we have everything */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "peers proposal selection invalid"); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + SIG(IKE_UP_FAILED, "peers DH group selection invalid"); + return FAILED; + } + + /* Apply SPI if we are rekeying */ + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, child_prf, prf); + } + else + { + /* setup crypto keys for a new SA */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_init_t *this) +{ + return IKE_INIT; +} + +/** + * Implementation of task_t.get_type + */ +static chunk_t get_lower_nonce(private_ike_init_t *this) +{ + if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, + min(this->my_nonce.len, this->other_nonce.len)) < 0) + { + return this->my_nonce; + } + else + { + return this->other_nonce; + } +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->other_nonce); + + this->ike_sa = ike_sa; + this->proposal = NULL; + this->diffie_hellman = diffie_hellman_create(this->dh_group); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_init_t *this) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + chunk_free(&this->cookie); + free(this); +} + +/* + * Described in header. + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) +{ + private_ike_init_t *this = malloc_thing(private_ike_init_t); + + this->public.get_lower_nonce = (chunk_t(*)(ike_init_t*))get_lower_nonce; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->dh_group = MODP_NONE; + this->diffie_hellman = NULL; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->cookie = chunk_empty; + this->proposal = NULL; + this->connection = NULL; + this->old_sa = old_sa; + this->retry = 0; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h new file mode 100644 index 000000000..f60c096e8 --- /dev/null +++ b/src/charon/sa/tasks/ike_init.h @@ -0,0 +1,68 @@ +/** + * @file ike_init.h + * + * @brief Interface ike_init_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_INIT_H_ +#define IKE_INIT_H_ + +typedef struct ike_init_t ike_init_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type IKE_INIT, creates an IKE_SA without authentication. + * + * The authentication of is handle in the ike_auth task. + * + * @b Constructors: + * - ike_init_create() + * + * @ingroup tasks + */ +struct ike_init_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Get the lower of the two nonces, used for rekey collisions. + * + * @param this calling object + * @return lower nonce + */ + chunk_t (*get_lower_nonce) (ike_init_t *this); +}; + +/** + * @brief Create a new IKE_INIT task. + * + * @param ike_sa IKE_SA this task works for (new one when rekeying) + * @param initiator TRUE if thask is the original initator + * @param old_sa old IKE_SA when we are rekeying + * @return ike_init task to handle by the task_manager + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa); + +#endif /* IKE_INIT_H_ */ diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c new file mode 100644 index 000000000..50b5d652b --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.c @@ -0,0 +1,371 @@ +/** + * @file ike_natd.c + * + * @brief Implementation of the ike_natd task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_natd.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/notify_payload.h> + + +typedef struct private_ike_natd_t private_ike_natd_t; + +/** + * Private members of a ike_natd_t task. + */ +struct private_ike_natd_t { + + /** + * Public methods and task_t interface. + */ + ike_natd_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Hasher used to build NAT detection hashes + */ + hasher_t *hasher; + + /** + * Did we process any NAT detection notifys for a source address? + */ + bool src_seen; + + /** + * Did we process any NAT detection notifys for a destination address? + */ + bool dst_seen; + + /** + * Have we found a matching source address NAT hash? + */ + bool src_matched; + + /** + * Have we found a matching destination address NAT hash? + */ + bool dst_matched; +}; + + +/** + * Build NAT detection hash for a host + */ +static chunk_t generate_natd_hash(private_ike_natd_t *this, + ike_sa_id_t *ike_sa_id, host_t *host) +{ + chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; + chunk_t natd_hash; + u_int64_t spi_i, spi_r; + u_int16_t port; + + /* prepare all requred chunks */ + spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); + spi_r = ike_sa_id->get_responder_spi(ike_sa_id); + spi_i_chunk.ptr = (void*)&spi_i; + spi_i_chunk.len = sizeof(spi_i); + spi_r_chunk.ptr = (void*)&spi_r; + spi_r_chunk.len = sizeof(spi_r); + port = htons(host->get_port(host)); + port_chunk.ptr = (void*)&port; + port_chunk.len = sizeof(port); + addr_chunk = host->get_address(host); + + /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ + natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); + this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash); + DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); + DBG3(DBG_IKE, "natd_hash %B", &natd_hash); + + chunk_free(&natd_chunk); + return natd_hash; +} + +/** + * Build a NAT detection notify payload. + */ +static notify_payload_t *build_natd_payload(private_ike_natd_t *this, + notify_type_t type, host_t *host) +{ + chunk_t hash; + notify_payload_t *notify; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + hash = generate_natd_hash(this, ike_sa_id, host); + notify->set_notification_data(notify, hash); + chunk_free(&hash); + + return notify; +} + +/** + * read notifys from message and evaluate them + */ +static void process_payloads(private_ike_natd_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + notify_payload_t *notify; + chunk_t hash, src_hash, dst_hash; + ike_sa_id_t *ike_sa_id; + host_t *me, *other; + + /* Precompute NAT-D hashes for incoming NAT notify comparison */ + ike_sa_id = message->get_ike_sa_id(message); + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + dst_hash = generate_natd_hash(this, ike_sa_id, me); + src_hash = generate_natd_hash(this, ike_sa_id, other); + + DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); + DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case NAT_DETECTION_DESTINATION_IP: + { + this->dst_seen = TRUE; + if (!this->dst_matched) + { + hash = notify->get_notification_data(notify); + DBG3(DBG_IKE, "received dst_hash %B", &hash); + if (chunk_equals(hash, dst_hash)) + { + this->dst_matched = TRUE; + } + } + break; + } + case NAT_DETECTION_SOURCE_IP: + { + this->src_seen = TRUE; + if (!this->src_matched) + { + hash = notify->get_notification_data(notify); + DBG3(DBG_IKE, "received src_hash %B", &hash); + if (chunk_equals(hash, src_hash)) + { + this->src_matched = TRUE; + } + } + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + chunk_free(&src_hash); + chunk_free(&dst_hash); + + if (this->src_seen && this->dst_seen) + { + if (!this->dst_matched) + { + this->ike_sa->enable_natt(this->ike_sa, TRUE); + } + if (!this->src_matched) + { + this->ike_sa->enable_natt(this->ike_sa, FALSE); + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + host_t *me, *other; + + me = this->ike_sa->get_my_host(this->ike_sa); + me->set_port(me, IKEV2_NATT_PORT); + other = this->ike_sa->get_other_host(this->ike_sa); + other->set_port(other, IKEV2_NATT_PORT); + } + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + linked_list_t *list; + host_t *host; + + /* include one notify if our address is defined, all addresses otherwise */ + host = this->ike_sa->get_my_host(this->ike_sa); + if (host->is_anyaddr(host)) + { + /* TODO: we could get the src address from netlink!? */ + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + host->destroy(host); + message->add_payload(message, (payload_t*)notify); + } + list->destroy(list); + } + else + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + message->add_payload(message, (payload_t*)notify); + } + + host = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); + message->add_payload(message, (payload_t*)notify); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + host_t *me, *other; + + /* only add notifies on successfull responses. */ + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + return SUCCESS; + } + + if (this->src_seen && this->dst_seen) + { + /* initiator seems to support NAT detection, add response */ + me = this->ike_sa->get_my_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); + message->add_payload(message, (payload_t*)notify); + + other = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); + message->add_payload(message, (payload_t*)notify); + } + return SUCCESS; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_natd_t *this) +{ + return IKE_NATD; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_natd_t *this) +{ + this->hasher->destroy(this->hasher); + free(this); +} + +/* + * Described in header. + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_natd_t *this = malloc_thing(private_ike_natd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->hasher = hasher_create(HASH_SHA1); + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h new file mode 100644 index 000000000..8d0cb58b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.h @@ -0,0 +1,57 @@ +/** + * @file ike_natd.h + * + * @brief Interface ike_natd_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_NATD_H_ +#define IKE_NATD_H_ + +typedef struct ike_natd_t ike_natd_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange. + * + * @b Constructors: + * - ike_natd_create() + * + * @ingroup tasks + */ +struct ike_natd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_natd task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_natd task to handle by the task_manager + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_NATD_H_ */ diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c new file mode 100644 index 000000000..a33e7ee34 --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.c @@ -0,0 +1,329 @@ +/** + * @file ike_rekey.c + * + * @brief Implementation of the ike_rekey task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_rekey.h" + +#include <daemon.h> +#include <encoding/payloads/notify_payload.h> +#include <sa/tasks/ike_init.h> +#include <queues/jobs/delete_ike_sa_job.h> +#include <queues/jobs/rekey_ike_sa_job.h> + + +typedef struct private_ike_rekey_t private_ike_rekey_t; + +/** + * Private members of a ike_rekey_t task. + */ +struct private_ike_rekey_t { + + /** + * Public methods and task_t interface. + */ + ike_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * New IKE_SA which replaces the current one + */ + ike_sa_t *new_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the IKE_INIT task which is reused to simplify rekeying + */ + ike_init_t *ike_init; + + /** + * colliding task detected by the task manager + */ + task_t *collision; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + TRUE); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa); + this->ike_init->task.build(&this->ike_init->task, message); + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + iterator_t *iterator; + child_sa_t *child_sa; + + if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) + { + DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting"); + return NEED_MORE; + } + + iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_CREATED: + case CHILD_REKEYING: + case CHILD_DELETING: + /* we do not allow rekeying while we have children in-progress */ + DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open"); + iterator->destroy(iterator); + return NEED_MORE; + default: + break; + } + } + iterator->destroy(iterator); + + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + FALSE); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa); + this->ike_init->task.process(&this->ike_init->task, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_rekey_t *this, message_t *message) +{ + if (this->new_sa == NULL) + { + /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */ + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) + { + return SUCCESS; + } + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_rekey_t *this, message_t *message) +{ + job_t *job; + ike_sa_id_t *to_delete; + + if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED) + { + /* rekeying failed, fallback to old SA */ + if (!(this->collision && + this->collision->get_type(this->collision) == IKE_DELETE)) + { + job_t *job; + u_int32_t retry = charon->configuration->get_retry_interval( + charon->configuration); + job = (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), FALSE); + DBG1(DBG_IKE, "IKE_SA rekeying failed, " + "trying again in %d seconds", retry); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); + } + return SUCCESS; + } + + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + to_delete = this->ike_sa->get_id(this->ike_sa); + + /* check for collisions */ + if (this->collision && + this->collision->get_type(this->collision) == IKE_REKEY) + { + chunk_t this_nonce, other_nonce; + host_t *host; + private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision; + + this_nonce = this->ike_init->get_lower_nonce(this->ike_init); + other_nonce = other->ike_init->get_lower_nonce(other->ike_init); + + /* if we have the lower nonce, delete rekeyed SA. If not, delete + * the redundant. */ + if (memcmp(this_nonce.ptr, other_nonce.ptr, + min(this_nonce.len, other_nonce.len)) < 0) + { + DBG1(DBG_IKE, "IKE_SA rekey collision won, deleting rekeyed IKE_SA"); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa); + } + else + { + DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant IKE_SA"); + /* apply host for a proper delete */ + host = this->ike_sa->get_my_host(this->ike_sa); + this->new_sa->set_my_host(this->new_sa, host->clone(host)); + host = this->ike_sa->get_other_host(this->ike_sa); + this->new_sa->set_other_host(this->new_sa, host->clone(host)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + to_delete = this->new_sa->get_id(this->new_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + /* inherit to other->new_sa in destroy() */ + this->new_sa = other->new_sa; + other->new_sa = NULL; + } + } + + job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE); + charon->job_queue->add(charon->job_queue, job); + + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_rekey_t *this) +{ + return IKE_REKEY; +} + +static void collide(private_ike_rekey_t* this, task_t *other) +{ + DESTROY_IF(this->collision); + this->collision = other; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) +{ + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + if (this->new_sa) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + DESTROY_IF(this->collision); + + this->collision = NULL; + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_rekey_t *this) +{ + if (this->new_sa) + { + if (this->new_sa->get_state(this->new_sa) == IKE_ESTABLISHED && + this->new_sa->inherit(this->new_sa, this->ike_sa) != DESTROY_ME) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + } + else + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + } + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + DESTROY_IF(this->collision); + free(this); +} + +/* + * Described in header. + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t); + + this->public.collide = (void(*)(ike_rekey_t*,task_t*))collide; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; + this->initiator = initiator; + this->collision = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h new file mode 100644 index 000000000..125422efd --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.h @@ -0,0 +1,69 @@ +/** + * @file ike_rekey.h + * + * @brief Interface ike_rekey_t. + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#ifndef IKE_REKEY_H_ +#define IKE_REKEY_H_ + +typedef struct ike_rekey_t ike_rekey_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type IKE_REKEY, rekey an established IKE_SA. + * + * @b Constructors: + * - ike_rekey_create() + * + * @ingroup tasks + */ +struct ike_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Register a rekeying task which collides with this one. + * + * If two peers initiate rekeying at the same time, the collision must + * be handled gracefully. The task manager is aware of what exchanges + * are going on and notifies the outgoing task by passing the incoming. + * + * @param this task initated by us + * @param other incoming task + */ + void (*collide)(ike_rekey_t* this, task_t *other); +}; + +/** + * @brief Create a new IKE_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE for initiator, FALSE for responder + * @return IKE_REKEY task to handle by the task_manager + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_REKEY_H_ */ diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c new file mode 100644 index 000000000..68d8ebf0c --- /dev/null +++ b/src/charon/sa/tasks/task.c @@ -0,0 +1,38 @@ +/** + * @file task.c + * + * @brief Enum values for task types + * + */ + +/* + * Copyright (C) 2007 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. + */ + +#include "task.h" + +ENUM(task_type_names, IKE_INIT, CHILD_REKEY, + "IKE_INIT", + "IKE_NATD", + "IKE_AUTHENTICATE", + "IKE_CERT", + "IKE_CONFIG", + "IKE_DPD", + "IKE_REKEY", + "IKE_DELETE", + "IKE_DEADPEER", + "CHILD_CREATE", + "CHILD_DELETE", + "CHILD_REKEY", +); diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h new file mode 100644 index 000000000..128d7db4a --- /dev/null +++ b/src/charon/sa/tasks/task.h @@ -0,0 +1,151 @@ +/** + * @file task.h + * + * @brief Interface task_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef TASK_H_ +#define TASK_H_ + +typedef enum task_type_t task_type_t; +typedef struct task_t task_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <encoding/message.h> + +/** + * @brief Different kinds of tasks. + * + * @ingroup tasks + */ +enum task_type_t { + /** establish an unauthenticated IKE_SA */ + IKE_INIT, + /** detect NAT situation */ + IKE_NATD, + /** authenticate the initiated IKE_SA */ + IKE_AUTHENTICATE, + /** exchange certificates and requests */ + IKE_CERT, + /** Configuration payloads, virtual IP and such */ + IKE_CONFIG, + /** DPD detection */ + IKE_DEADPEER, + /** rekey an IKE_SA */ + IKE_REKEY, + /** delete an IKE_SA */ + IKE_DELETE, + /** liveness check */ + IKE_DPD, + /** establish a CHILD_SA within an IKE_SA */ + CHILD_CREATE, + /** delete an established CHILD_SA */ + CHILD_DELETE, + /** rekey an CHILD_SA */ + CHILD_REKEY, +}; + +/** + * enum names for task_type_t. + */ +extern enum_name_t *task_type_names; + +/** + * @brief Interface for a task, an operation handled within exchanges. + * + * A task is an elemantary operation. It may be handled by a single or by + * multiple exchanges. An exchange may even complete multiple tasks. + * A task has a build() and an process() operation. The build() operation + * creates payloads and adds it to the message. The process() operation + * inspects a message and handles its payloads. An initiator of an exchange + * first calls build() to build the request, and processes the response message + * with the process() method. + * A responder does the opposite; it calls process() first to handle an incoming + * request and secondly calls build() to build an appropriate response. + * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates + * that the task completed, even when the task completed unsuccesfully. The + * manager then removes the task from the list. A NEED_MORE is returned when + * the task needs further build()/process() calls to complete, the manager + * leaves the taks in the queue. A returned FAILED indicates a critical failure. + * The manager closes the IKE_SA whenever a task returns FAILED. + * + * @b Constructors: + * - None, use implementations specific constructors + * + * @ingroup tasks + */ +struct task_t { + + /** + * @brief Build a request or response message for this task. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*build) (task_t *this, message_t *message); + + /** + * @brief Process a request or response message for this task. + * + * @param this calling object + * @param message message to read payloads from + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*process) (task_t *this, message_t *message); + + /** + * @brief Get the type of the task implementation. + * + * @param this calling object + */ + task_type_t (*get_type) (task_t *this); + + /** + * @brief Migrate a task to a new IKE_SA. + * + * After migrating a task, it goes back to a state where it can be + * used again to initate an exchange. This is useful when a task + * has to get migrated to a new IKE_SA. + * A special usage is when a INVALID_KE_PAYLOAD is received. A call + * to reset resets the task, but uses another DH group for the next + * try. + * The ike_sa is the new IKE_SA this task belongs to and operates on. + * + * @param this calling object + * @param ike_sa new IKE_SA this task works for + */ + void (*migrate) (task_t *this, ike_sa_t *ike_sa); + + /** + * @brief Destroys a task_t object. + * + * @param this calling object + */ + void (*destroy) (task_t *this); +}; + +#endif /* TASK_H_ */ diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c new file mode 100644 index 000000000..4a70d2ecf --- /dev/null +++ b/src/charon/threads/kernel_interface.c @@ -0,0 +1,1964 @@ +/** + * @file kernel_interface.c + * + * @brief Implementation of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2003 Herbert Xu. + * + * Based on xfrm code from pluto. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> +#include <linux/udp.h> +#include <pthread.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <net/if.h> +#include <sys/ioctl.h> + +#include "kernel_interface.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <queues/jobs/delete_child_sa_job.h> +#include <queues/jobs/rekey_child_sa_job.h> +#include <queues/jobs/acquire_job.h> + +/** kernel level protocol identifiers */ +#define KERNEL_ESP 50 +#define KERNEL_AH 51 + +/** default priority of installed policies */ +#define PRIO_LOW 3000 +#define PRIO_HIGH 2000 + +#define BUFFER_SIZE 1024 + +/** + * returns a pointer to the first rtattr following the nlmsghdr *nlh and the + * 'usual' netlink data x like 'struct xfrm_usersa_info' + */ +#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x)))) +/** + * returns a pointer to the next rtattr following rta. + * !!! do not use this to parse messages. use RTA_NEXT and RTA_OK instead !!! + */ +#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +/** + * returns the total size of attached rta data + * (after 'usual' netlink data x like 'struct xfrm_usersa_info') + */ +#define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) + +typedef struct kernel_algorithm_t kernel_algorithm_t; + +/** + * Mapping from the algorithms defined in IKEv2 to + * kernel level algorithm names and their key length + */ +struct kernel_algorithm_t { + /** + * Identifier specified in IKEv2 + */ + int ikev2_id; + + /** + * Name of the algorithm, as used as kernel identifier + */ + char *name; + + /** + * Key length in bits, if fixed size + */ + u_int key_size; +}; +#define END_OF_LIST -1 + +/** + * Algorithms for encryption + */ +kernel_algorithm_t encryption_algs[] = { +/* {ENCR_DES_IV64, "***", 0}, */ + {ENCR_DES, "des", 64}, + {ENCR_3DES, "des3_ede", 192}, +/* {ENCR_RC5, "***", 0}, */ +/* {ENCR_IDEA, "***", 0}, */ + {ENCR_CAST, "cast128", 0}, + {ENCR_BLOWFISH, "blowfish", 0}, +/* {ENCR_3IDEA, "***", 0}, */ +/* {ENCR_DES_IV32, "***", 0}, */ + {ENCR_NULL, "cipher_null", 0}, + {ENCR_AES_CBC, "aes", 0}, +/* {ENCR_AES_CTR, "***", 0}, */ + {END_OF_LIST, NULL, 0}, +}; + +/** + * Algorithms for integrity protection + */ +kernel_algorithm_t integrity_algs[] = { + {AUTH_HMAC_MD5_96, "md5", 128}, + {AUTH_HMAC_SHA1_96, "sha1", 160}, + {AUTH_HMAC_SHA2_256_128, "sha256", 256}, + {AUTH_HMAC_SHA2_384_192, "sha384", 384}, + {AUTH_HMAC_SHA2_512_256, "sha512", 512}, +/* {AUTH_DES_MAC, "***", 0}, */ +/* {AUTH_KPDK_MD5, "***", 0}, */ +/* {AUTH_AES_XCBC_96, "***", 0}, */ + {END_OF_LIST, NULL, 0}, +}; + +/** + * Look up a kernel algorithm name and its key size + */ +char* lookup_algorithm(kernel_algorithm_t *kernel_algo, + algorithm_t *ikev2_algo, u_int *key_size) +{ + while (kernel_algo->ikev2_id != END_OF_LIST) + { + if (ikev2_algo->algorithm == kernel_algo->ikev2_id) + { + /* match, evaluate key length */ + if (ikev2_algo->key_size) + { /* variable length */ + *key_size = ikev2_algo->key_size; + } + else + { /* fixed length */ + *key_size = kernel_algo->key_size; + } + return kernel_algo->name; + } + kernel_algo++; + } + return NULL; +} + +typedef struct route_entry_t route_entry_t; + +/** + * installed routing entry + */ +struct route_entry_t { + + /** Index of the interface the route is bound to */ + int if_index; + + /** Source ip of the route */ + host_t *src_ip; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * destroy an route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + this->src_ip->destroy(this->src_ip); + chunk_free(&this->dst_net); + free(this); +} + +typedef struct policy_entry_t policy_entry_t; + +/** + * installed kernel policy. + */ +struct policy_entry_t { + + /** direction of this policy: in, out, forward */ + u_int8_t direction; + + /** reqid of the policy */ + u_int32_t reqid; + + /** parameters of installed policy */ + struct xfrm_selector sel; + + /** associated route installed for this policy */ + route_entry_t *route; + + /** by how many CHILD_SA's this policy is used */ + u_int refcount; +}; + +typedef struct vip_entry_t vip_entry_t; + +/** + * Installed virtual ip + */ +struct vip_entry_t { + /** Index of the interface the ip is bound to */ + u_int8_t if_index; + + /** The ip address */ + host_t *ip; + + /** Number of times this IP is used */ + u_int refcount; +}; + +/** + * destroy a vip_entry_t object + */ +static void vip_entry_destroy(vip_entry_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +typedef struct address_entry_t address_entry_t; + +/** + * an address found on the system, containg address and interface info + */ +struct address_entry_t { + + /** address of this entry */ + host_t *host; + + /** interface index */ + int ifindex; + + /** name of the index */ + char ifname[IFNAMSIZ]; +}; + +/** + * destroy an address entry + */ +static void address_entry_destroy(address_entry_t *this) +{ + this->host->destroy(this->host); + free(this); +} + +typedef struct private_kernel_interface_t private_kernel_interface_t; + +/** + * Private variables and functions of kernel_interface class. + */ +struct private_kernel_interface_t { + /** + * Public part of the kernel_interface_t object. + */ + kernel_interface_t public; + + /** + * List of installed policies (kernel_entry_t) + */ + linked_list_t *policies; + + /** + * Mutex locks access to policies + */ + pthread_mutex_t policies_mutex; + + /** + * List of installed virtual IPs. (vip_entry_t) + */ + linked_list_t *vips; + + /** + * Mutex to lock access to vips. + */ + pthread_mutex_t vips_mutex; + + /** + * netlink xfrm socket to receive acquire and expire events + */ + int socket_xfrm_events; + + /** + * Netlink xfrm socket (IPsec) + */ + int socket_xfrm; + + /** + * Netlink rt socket (routing) + */ + int socket_rt; + + /** + * Thread receiving events from kernel + */ + pthread_t event_thread; +}; + +/** + * convert a host_t to a struct xfrm_address + */ +static void host2xfrm(host_t *host, xfrm_address_t *xfrm) +{ + chunk_t chunk = host->get_address(host); + memcpy(xfrm, chunk.ptr, min(chunk.len, sizeof(xfrm_address_t))); +} + +/** + * convert a traffic selector address range to subnet and its mask. + */ +static void ts2subnet(traffic_selector_t* ts, + xfrm_address_t *net, u_int8_t *mask) +{ + /* there is no way to do this cleanly, as the address range may + * be anything else but a subnet. We use from_addr as subnet + * and try to calculate a usable subnet mask. + */ + int byte, bit; + bool found = FALSE; + chunk_t from, to; + size_t size = (ts->get_type(ts) == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + from = ts->get_from_address(ts); + to = ts->get_to_address(ts); + + *mask = (size * 8); + /* go trough all bits of the addresses, beginning in the front. + * as long as they are equal, the subnet gets larger + */ + for (byte = 0; byte < size; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if ((1<<bit & from.ptr[byte]) != (1<<bit & to.ptr[byte])) + { + *mask = ((7 - bit) + (byte * 8)); + found = TRUE; + break; + } + } + if (found) + { + break; + } + } + memcpy(net, from.ptr, from.len); + chunk_free(&from); + chunk_free(&to); +} + +/** + * convert a traffic selector port range to port/portmask + */ +static void ts2ports(traffic_selector_t* ts, + u_int16_t *port, u_int16_t *mask) +{ + /* linux does not seem to accept complex portmasks. Only + * any or a specific port is allowed. We set to any, if we have + * a port range, or to a specific, if we have one port only. + */ + u_int16_t from, to; + + from = ts->get_from_port(ts); + to = ts->get_to_port(ts); + + if (from == to) + { + *port = htons(from); + *mask = ~0; + } + else + { + *port = 0; + *mask = 0; + } +} + +/** + * convert a pair of traffic_selectors to a xfrm_selector + */ +static struct xfrm_selector ts2selector(traffic_selector_t *src, + traffic_selector_t *dst) +{ + struct xfrm_selector sel; + + memset(&sel, 0, sizeof(sel)); + sel.family = src->get_type(src) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + /* src or dest proto may be "any" (0), use more restrictive one */ + sel.proto = max(src->get_protocol(src), dst->get_protocol(dst)); + ts2subnet(dst, &sel.daddr, &sel.prefixlen_d); + ts2subnet(src, &sel.saddr, &sel.prefixlen_s); + ts2ports(dst, &sel.dport, &sel.dport_mask); + ts2ports(src, &sel.sport, &sel.sport_mask); + sel.ifindex = 0; + sel.user = 0; + + return sel; +} + +/** + * Creates an rtattr and adds it to the netlink message + */ +static void add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, + size_t buflen) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen) + { + DBG1(DBG_KNL, "unable to add attribute, buffer too small"); + return; + } + + rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data.len); + memcpy(RTA_DATA(rta), data.ptr, data.len); + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; +} + +/** + * Receives events from kernel + */ +static void receive_events(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[512]; + struct nlmsghdr *hdr; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(addr); + int len; + + hdr = (struct nlmsghdr*)response; + len = recvfrom(this->socket_xfrm_events, response, sizeof(response), + 0, (struct sockaddr*)&addr, &addr_len); + if (len < 0) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + charon->kill(charon, "unable to receive netlink events"); + } + + if (!NLMSG_OK(hdr, len)) + { + /* bad netlink message */ + continue; + } + + if (addr.nl_pid != 0) + { + /* not from kernel. not interested, try another one */ + continue; + } + + /* we handle ACQUIRE and EXPIRE messages directly */ + if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) + { + u_int32_t reqid = 0; + job_t *job; + struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_user_acquire); + size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl); + if (RTA_OK(rtattr, rtsize)) + { + if (rtattr->rta_type == XFRMA_TMPL) + { + struct xfrm_user_tmpl* tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rtattr); + reqid = tmpl->reqid; + } + } + if (reqid == 0) + { + DBG1(DBG_KNL, "received a XFRM_MSG_ACQUIRE, but no reqid found"); + } + else + { + DBG2(DBG_KNL, "received a XFRM_MSG_ACQUIRE"); + DBG1(DBG_KNL, "creating acquire job for CHILD_SA with reqid %d", + reqid); + job = (job_t*)acquire_job_create(reqid); + charon->job_queue->add(charon->job_queue, job); + } + } + else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) + { + job_t *job; + protocol_id_t protocol; + u_int32_t spi, reqid; + struct xfrm_user_expire *expire; + + expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); + protocol = expire->state.id.proto == KERNEL_ESP ? + PROTO_ESP : PROTO_AH; + spi = expire->state.id.spi; + reqid = expire->state.reqid; + + DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE"); + DBG1(DBG_KNL, "creating %s job for %N CHILD_SA 0x%x (reqid %d)", + expire->hard ? "delete" : "rekey", protocol_id_names, + protocol, ntohl(spi), reqid); + if (expire->hard) + { + job = (job_t*)delete_child_sa_job_create(reqid, protocol, spi); + } + else + { + job = (job_t*)rekey_child_sa_job_create(reqid, protocol, spi); + } + charon->job_queue->add(charon->job_queue, job); + } + } +} + +/** + * send a netlink message and wait for a reply + */ +static status_t netlink_send(int socket, struct nlmsghdr *in, + struct nlmsghdr **out, size_t *out_len) +{ + int len, addr_len; + struct sockaddr_nl addr; + chunk_t result = chunk_empty, tmp; + struct nlmsghdr *msg, peek; + + static int seq = 200; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + + pthread_mutex_lock(&mutex); + + in->nlmsg_seq = ++seq; + in->nlmsg_pid = getpid(); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + while (TRUE) + { + len = sendto(socket, in, in->nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + + if (len != in->nlmsg_len) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + pthread_mutex_unlock(&mutex); + DBG1(DBG_KNL, "error sending to netlink socket: %m"); + return FAILED; + } + break; + } + + while (TRUE) + { + char buf[1024]; + tmp.len = sizeof(buf); + tmp.ptr = buf; + msg = (struct nlmsghdr*)tmp.ptr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0; + addr_len = sizeof(addr); + + len = recvfrom(socket, tmp.ptr, tmp.len, 0, + (struct sockaddr*)&addr, &addr_len); + + if (len < 0) + { + if (errno == EINTR) + { + DBG1(DBG_IKE, "got interrupted"); + /* interrupted, try again */ + continue; + } + DBG1(DBG_IKE, "error reading from netlink socket: %m"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (!NLMSG_OK(msg, len)) + { + DBG1(DBG_IKE, "received corrupted netlink message"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (msg->nlmsg_seq != seq) + { + DBG1(DBG_IKE, "received invalid netlink sequence number"); + if (msg->nlmsg_seq < seq) + { + continue; + } + pthread_mutex_unlock(&mutex); + return FAILED; + } + + tmp.len = len; + result = chunk_cata("cc", result, tmp); + + /* NLM_F_MULTI flag does not seem to be set correctly, we use sequence + * numbers to detect multi header messages */ + len = recvfrom(socket, &peek, sizeof(peek), MSG_PEEK | MSG_DONTWAIT, + (struct sockaddr*)&addr, &addr_len); + + if (len == sizeof(peek) && peek.nlmsg_seq == seq) + { + /* seems to be multipart */ + continue; + } + break; + } + + *out_len = result.len; + *out = (struct nlmsghdr*)clalloc(result.ptr, result.len); + + pthread_mutex_unlock(&mutex); + + return SUCCESS; +} + +/** + * send a netlink message and wait for its acknowlegde + */ +static status_t netlink_send_ack(int socket, struct nlmsghdr *in) +{ + struct nlmsghdr *out, *hdr; + size_t len; + + if (netlink_send(socket, in, &out, &len) != SUCCESS) + { + return FAILED; + } + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case NLMSG_ERROR: + { + struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(hdr); + + if (err->error) + { + DBG1(DBG_KNL, "received netlink error: %s (%d)", + strerror(-err->error), -err->error); + free(out); + return FAILED; + } + free(out); + return SUCCESS; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + DBG1(DBG_KNL, "netlink request not acknowlegded"); + free(out); + return FAILED; +} + +/** + * Create a list of local addresses. + */ +static linked_list_t *create_address_list(private_kernel_interface_t *this) +{ + char request[BUFFER_SIZE]; + struct nlmsghdr *out, *hdr; + struct rtgenmsg *msg; + size_t len; + linked_list_t *list; + + DBG2(DBG_IKE, "getting local address list"); + + list = linked_list_create(); + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)&request; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + hdr->nlmsg_type = RTM_GETADDR; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT; + msg = (struct rtgenmsg*)NLMSG_DATA(hdr); + msg->rtgen_family = AF_UNSPEC; + + if (netlink_send(this->socket_rt, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case RTM_NEWADDR: + { + struct ifaddrmsg* msg = (struct ifaddrmsg*)(NLMSG_DATA(hdr)); + struct rtattr *rta = IFA_RTA(msg); + size_t rtasize = IFA_PAYLOAD (hdr); + host_t *host = NULL; + char *name = NULL; + chunk_t local = chunk_empty, address = chunk_empty; + + while(RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFA_LOCAL: + local.ptr = RTA_DATA(rta); + local.len = RTA_PAYLOAD(rta); + break; + case IFA_ADDRESS: + address.ptr = RTA_DATA(rta); + address.len = RTA_PAYLOAD(rta); + break; + case IFA_LABEL: + name = RTA_DATA(rta); + break; + } + rta = RTA_NEXT(rta, rtasize); + } + + /* For PPP interfaces, we need the IFA_LOCAL address, + * IFA_ADDRESS is the peers address. But IFA_LOCAL is + * not included in all cases, so fallback to IFA_ADDRESS. */ + if (local.ptr) + { + host = host_create_from_chunk(msg->ifa_family, local, 0); + } + else if (address.ptr) + { + host = host_create_from_chunk(msg->ifa_family, address, 0); + } + + if (host) + { + address_entry_t *entry; + + entry = malloc_thing(address_entry_t); + entry->host = host; + entry->ifindex = msg->ifa_index; + if (name) + { + memcpy(entry->ifname, name, IFNAMSIZ); + } + else + { + strcpy(entry->ifname, "(unknown)"); + } + list->insert_last(list, entry); + } + hdr = NLMSG_NEXT(hdr, len); + continue; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + free(out); + } + else + { + DBG1(DBG_IKE, "unable to get local address list"); + } + + return list; +} + +/** + * Implements kernel_interface_t.create_address_list. + */ +static linked_list_t *create_address_list_public(private_kernel_interface_t *this) +{ + linked_list_t *result, *list; + address_entry_t *entry; + + result = linked_list_create(); + list = create_address_list(this); + while (list->remove_last(list, (void**)&entry) == SUCCESS) + { + result->insert_last(result, entry->host); + free(entry); + } + list->destroy(list); + + return result; +} + +/** + * implementation of kernel_interface_t.get_interface_name + */ +static char *get_interface_name(private_kernel_interface_t *this, host_t* ip) +{ + linked_list_t *list; + address_entry_t *entry; + char *name = NULL; + + DBG2(DBG_IKE, "getting interface name for %H", ip); + + list = create_address_list(this); + while (!name && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ip->ip_equals(ip, entry->host)) + { + name = strdup(entry->ifname); + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (name) + { + DBG2(DBG_IKE, "%H is on interface %s", ip, name); + } + else + { + DBG2(DBG_IKE, "%H is not a local address", ip); + } + return name; +} + +/** + * Tries to find an ip address of a local interface that is included in the + * supplied traffic selector. + */ +static status_t get_address_by_ts(private_kernel_interface_t *this, + traffic_selector_t *ts, host_t **ip) +{ + address_entry_t *entry; + host_t *host; + int family; + linked_list_t *list; + bool found = FALSE; + + DBG2(DBG_IKE, "getting a local address in traffic selector %R", ts); + + /* if we have a family which includes localhost, we do not + * search for an IP, we use the default */ + family = ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + + if (family == AF_INET) + { + host = host_create_from_string("127.0.0.1", 0); + } + else + { + host = host_create_from_string("::1", 0); + } + + if (ts->includes(ts, host)) + { + *ip = host_create_any(family); + host->destroy(host); + DBG2(DBG_IKE, "using host %H", *ip); + return SUCCESS; + } + host->destroy(host); + + list = create_address_list(this); + while (!found && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ts->includes(ts, entry->host)) + { + found = TRUE; + *ip = entry->host->clone(entry->host); + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (!found) + { + DBG1(DBG_IKE, "no local address found in traffic selector %R", ts); + return FAILED; + } + DBG2(DBG_IKE, "using host %H", *ip); + return SUCCESS; +} + +/** + * get the interface of a local address + */ +static int get_interface_index(private_kernel_interface_t *this, host_t* ip) +{ + linked_list_t *list; + address_entry_t *entry; + int ifindex = 0; + + DBG2(DBG_IKE, "getting iface for %H", ip); + + list = create_address_list(this); + while (!ifindex && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ip->ip_equals(ip, entry->host)) + { + ifindex = entry->ifindex; + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (ifindex == 0) + { + DBG1(DBG_IKE, "unable to get interface for %H", ip); + } + return ifindex; +} + +/** + * Manages the creation and deletion of ip addresses on an interface. + * By setting the appropriate nlmsg_type, the ip will be set or unset. + */ +static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type, + int flags, int if_index, host_t *ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct ifaddrmsg *msg; + chunk_t chunk; + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + msg = (struct ifaddrmsg*)NLMSG_DATA(hdr); + msg->ifa_family = ip->get_family(ip); + msg->ifa_flags = 0; + msg->ifa_prefixlen = 8 * chunk.len; + msg->ifa_scope = RT_SCOPE_UNIVERSE; + msg->ifa_index = if_index; + + add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request)); + + return netlink_send_ack(this->socket_rt, hdr); +} + +/** + * Manages source routes in the routing table. + * By setting the appropriate nlmsg_type, the route added or r. + */ +static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type, + int flags, route_entry_t *route) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct rtmsg *msg; + chunk_t chunk; + + /* if route is 0.0.0.0/0, we can't install it, as it would + * overwrite the default route. Instead, we add two routes: + * 0.0.0.0/1 and 128.0.0.0/1 + * TODO: use metrics instead */ + if (route->prefixlen == 0) + { + route_entry_t half; + status_t status; + + half.dst_net = chunk_alloca(route->dst_net.len); + memset(half.dst_net.ptr, 0, half.dst_net.len); + half.src_ip = route->src_ip; + half.if_index = route->if_index; + half.prefixlen = 1; + + status = manage_srcroute(this, nlmsg_type, flags, &half); + half.dst_net.ptr[0] |= 0x80; + status = manage_srcroute(this, nlmsg_type, flags, &half); + return status; + } + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = (struct rtmsg*)NLMSG_DATA(hdr); + msg->rtm_family = route->src_ip->get_family(route->src_ip); + msg->rtm_dst_len = route->prefixlen; + msg->rtm_table = RT_TABLE_MAIN; + msg->rtm_protocol = RTPROT_STATIC; + msg->rtm_type = RTN_UNICAST; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + + add_attribute(hdr, RTA_DST, route->dst_net, sizeof(request)); + chunk = route->src_ip->get_address(route->src_ip); + add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); + chunk.ptr = (char*)&route->if_index; + chunk.len = sizeof(route->if_index); + add_attribute(hdr, RTA_OIF, chunk, sizeof(request)); + + return netlink_send_ack(this->socket_rt, hdr); +} + + +/** + * Implementation of kernel_interface_t.add_ip. + */ +static status_t add_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *iface_ip) +{ + int targetif; + vip_entry_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip); + + targetif = get_interface_index(this, iface_ip); + if (targetif == 0) + { + DBG1(DBG_KNL, "unable to add virtual IP %H, no iface found for %H", + virtual_ip, iface_ip); + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount++; + iterator->destroy(iterator); + DBG2(DBG_KNL, "virtual IP %H already added to iface %d reusing it", + virtual_ip, targetif); + return SUCCESS; + } + } + iterator->destroy(iterator); + + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + targetif, virtual_ip) == SUCCESS) + { + listed = malloc_thing(vip_entry_t); + listed->ip = virtual_ip->clone(virtual_ip); + listed->if_index = targetif; + listed->refcount = 1; + this->vips->insert_last(this->vips, listed); + DBG2(DBG_KNL, "virtual IP %H added to iface %d", + virtual_ip, targetif); + return SUCCESS; + } + + DBG2(DBG_KNL, "unable to add virtual IP %H to iface %d", + virtual_ip, targetif); + return FAILED; +} + +/** + * Implementation of kernel_interface_t.del_ip. + */ +static status_t del_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *iface_ip) +{ + int targetif; + vip_entry_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip); + + targetif = get_interface_index(this, iface_ip); + if (targetif == 0) + { + DBG1(DBG_KNL, "unable to delete virtual IP %H, no iface found for %H", + virtual_ip, iface_ip); + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount--; + if (listed->refcount == 0) + { + iterator->remove(iterator); + vip_entry_destroy(listed); + iterator->destroy(iterator); + return manage_ipaddr(this, RTM_DELADDR, 0, targetif, virtual_ip); + } + iterator->destroy(iterator); + DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", + virtual_ip); + return SUCCESS; + } + } + iterator->destroy(iterator); + + DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); + return FAILED; +} + +/** + * Implementation of kernel_interface_t.get_spi. + */ +static status_t get_spi(private_kernel_interface_t *this, + host_t *src, host_t *dst, + protocol_id_t protocol, u_int32_t reqid, + u_int32_t *spi) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *out; + struct xfrm_userspi_info *userspi; + u_int32_t received_spi = 0; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "getting SPI for reqid %d", reqid); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info)); + + userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); + host2xfrm(src, &userspi->info.saddr); + host2xfrm(dst, &userspi->info.id.daddr); + userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + userspi->info.mode = TRUE; /* tunnel mode */ + userspi->info.reqid = reqid; + userspi->info.family = src->get_family(src); + userspi->min = 0xc0000000; + userspi->max = 0xcFFFFFFF; + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + struct xfrm_usersa_info* usersa = NLMSG_DATA(hdr); + received_spi = usersa->id.spi; + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + + DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + free(out); + } + + if (received_spi == 0) + { + DBG1(DBG_KNL, "unable to get SPI for reqid %d", reqid); + return FAILED; + } + + DBG2(DBG_KNL, "got SPI 0x%x for reqid %d", received_spi, reqid); + + *spi = received_spi; + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.add_sa. + */ +static status_t add_sa(private_kernel_interface_t *this, + host_t *src, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t reqid, + u_int64_t expire_soft, u_int64_t expire_hard, + algorithm_t *enc_alg, algorithm_t *int_alg, + prf_plus_t *prf_plus, natt_conf_t *natt, mode_t mode, + bool replace) +{ + unsigned char request[BUFFER_SIZE]; + char *alg_name; + u_int key_size; + struct nlmsghdr *hdr; + struct xfrm_usersa_info *sa; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "adding SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr); + host2xfrm(src, &sa->saddr); + host2xfrm(dst, &sa->id.daddr); + sa->id.spi = spi; + sa->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa->family = src->get_family(src); + sa->mode = mode; + sa->replay_window = 32; + sa->reqid = reqid; + /* we currently do not expire SAs by volume/packet count */ + sa->lft.soft_byte_limit = XFRM_INF; + sa->lft.hard_byte_limit = XFRM_INF; + sa->lft.soft_packet_limit = XFRM_INF; + sa->lft.hard_packet_limit = XFRM_INF; + /* we use lifetimes since added, not since used */ + sa->lft.soft_add_expires_seconds = expire_soft; + sa->lft.hard_add_expires_seconds = expire_hard; + sa->lft.soft_use_expires_seconds = 0; + sa->lft.hard_use_expires_seconds = 0; + + struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); + + if (enc_alg->algorithm != ENCR_UNDEFINED) + { + rthdr->rta_type = XFRMA_ALG_CRYPT; + alg_name = lookup_algorithm(encryption_algs, enc_alg, &key_size); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + encryption_algorithm_names, enc_alg->algorithm); + return FAILED; + } + DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg->algorithm, key_size); + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + } + + if (int_alg->algorithm != AUTH_UNDEFINED) + { + rthdr->rta_type = XFRMA_ALG_AUTH; + alg_name = lookup_algorithm(integrity_algs, int_alg, &key_size); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + integrity_algorithm_names, int_alg->algorithm); + return FAILED; + } + DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg->algorithm, key_size); + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + } + + /* TODO: add IPComp here */ + + if (natt) + { + rthdr->rta_type = XFRMA_ENCAP; + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_encap_tmpl* encap = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); + encap->encap_type = UDP_ENCAP_ESPINUDP; + encap->encap_sport = htons(natt->sport); + encap->encap_dport = htons(natt->dport); + memset(&encap->encap_oa, 0, sizeof (xfrm_address_t)); + /* encap_oa could probably be derived from the + * traffic selectors [rfc4306, p39]. In the netlink kernel implementation + * pluto does the same as we do here but it uses encap_oa in the + * pfkey implementation. BUT as /usr/src/linux/net/key/af_key.c indicates + * the kernel ignores it anyway + * -> does that mean that NAT-T encap doesn't work in transport mode? + * No. The reason the kernel ignores NAT-OA is that it recomputes + * (or, rather, just ignores) the checksum. If packets pass + * the IPsec checks it marks them "checksum ok" so OA isn't needed. */ + rthdr = XFRM_RTA_NEXT(rthdr); + } + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to add SAD entry with SPI 0x%x", spi); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.update_sa. + */ +static status_t update_sa(private_kernel_interface_t *this, + host_t *src, host_t *dst, + host_t *new_src, host_t *new_dst, + host_diff_t src_changes, host_diff_t dst_changes, + u_int32_t spi, protocol_id_t protocol) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *out = NULL; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa = NULL; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + if (sa == NULL) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + DBG2(DBG_KNL, "updating SAD entry with SPI 0x%x", spi); + + hdr = out; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDSA; + + if (src_changes & HOST_DIFF_ADDR) + { + host2xfrm(new_src, &sa->saddr); + } + + if (dst_changes & HOST_DIFF_ADDR) + { + hdr->nlmsg_type = XFRM_MSG_NEWSA; + host2xfrm(new_dst, &sa->id.daddr); + } + + if (src_changes & HOST_DIFF_PORT || dst_changes & HOST_DIFF_PORT) + { + struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_usersa_info); + size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_usersa_info); + while (RTA_OK(rtattr, rtsize)) + { + if (rtattr->rta_type == XFRMA_ENCAP) + { + struct xfrm_encap_tmpl* encap; + encap = (struct xfrm_encap_tmpl*)RTA_DATA(rtattr); + encap->encap_sport = ntohs(new_src->get_port(new_src)); + encap->encap_dport = ntohs(new_dst->get_port(new_dst)); + break; + } + rtattr = RTA_NEXT(rtattr, rtsize); + } + } + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to update SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + free(out); + + if (dst_changes & HOST_DIFF_ADDR) + { + return this->public.del_sa(&this->public, dst, spi, protocol); + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.query_sa. + */ +static status_t query_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol, + u_int32_t *use_time) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa = NULL; + size_t len; + + DBG2(DBG_KNL, "querying SAD entry with SPI 0x%x", spi); + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (sa == NULL) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + *use_time = sa->curlft.use_time; + free (out); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.del_sa. + */ +static status_t del_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "deleting SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to delete SAD entry with SPI 0x%x", spi); + return FAILED; + } + DBG2(DBG_KNL, "deleted SAD entry with SPI 0x%x", spi); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.add_policy. + */ +static status_t add_policy(private_kernel_interface_t *this, + host_t *src, host_t *dst, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, protocol_id_t protocol, + u_int32_t reqid, bool high_prio, mode_t mode, + bool update) +{ + iterator_t *iterator; + policy_entry_t *current, *policy; + bool found = FALSE; + unsigned char request[BUFFER_SIZE]; + struct xfrm_userpolicy_info *policy_info; + struct nlmsghdr *hdr; + + /* create a policy */ + policy = malloc_thing(policy_entry_t); + memset(policy, 0, sizeof(policy_entry_t)); + policy->sel = ts2selector(src_ts, dst_ts); + policy->direction = direction; + + /* find the policy, which matches EXACTLY */ + pthread_mutex_lock(&this->policies_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (memcmp(¤t->sel, &policy->sel, sizeof(struct xfrm_selector)) == 0 && + policy->direction == current->direction) + { + /* use existing policy */ + if (!update) + { + current->refcount++; + DBG2(DBG_KNL, "policy %R===%R already exists, increasing ", + "refcount", src_ts, dst_ts); + } + free(policy); + policy = current; + found = TRUE; + break; + } + } + iterator->destroy(iterator); + if (!found) + { /* apply the new one, if we have no such policy */ + this->policies->insert_last(this->policies, policy); + policy->refcount = 1; + } + + DBG2(DBG_KNL, "adding policy %R===%R", src_ts, dst_ts); + + memset(&request, 0, sizeof(request)); + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + policy_info = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + policy_info->sel = policy->sel; + policy_info->dir = policy->direction; + /* calculate priority based on source selector size, small size = high prio */ + policy_info->priority = high_prio ? PRIO_HIGH : PRIO_LOW; + policy_info->priority -= policy->sel.prefixlen_s * 10; + policy_info->priority -= policy->sel.proto ? 2 : 0; + policy_info->priority -= policy->sel.sport_mask ? 1 : 0; + policy_info->action = XFRM_POLICY_ALLOW; + policy_info->share = XFRM_SHARE_ANY; + pthread_mutex_unlock(&this->policies_mutex); + + /* policies don't expire */ + policy_info->lft.soft_byte_limit = XFRM_INF; + policy_info->lft.soft_packet_limit = XFRM_INF; + policy_info->lft.hard_byte_limit = XFRM_INF; + policy_info->lft.hard_packet_limit = XFRM_INF; + policy_info->lft.soft_add_expires_seconds = 0; + policy_info->lft.hard_add_expires_seconds = 0; + policy_info->lft.soft_use_expires_seconds = 0; + policy_info->lft.hard_use_expires_seconds = 0; + + struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); + rthdr->rta_type = XFRMA_TMPL; + + rthdr->rta_len = sizeof(struct xfrm_user_tmpl); + rthdr->rta_len = RTA_LENGTH(rthdr->rta_len); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + tmpl->reqid = reqid; + tmpl->id.proto = (protocol == PROTO_AH) ? KERNEL_AH : KERNEL_ESP; + tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; + tmpl->mode = mode; + tmpl->family = src->get_family(src); + + host2xfrm(src, &tmpl->saddr); + host2xfrm(dst, &tmpl->id.daddr); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to add policy %R===%R", src_ts, dst_ts); + return FAILED; + } + + /* install a route, if: + * - we are NOT updating a policy + * - this is a forward policy (to just get one for each child) + * - we are in tunnel mode + * - we are not using IPv6 (does not work correctly yet!) + */ + if (policy->route == NULL && direction == POLICY_FWD && + mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6) + { + policy->route = malloc_thing(route_entry_t); + if (get_address_by_ts(this, dst_ts, &policy->route->src_ip) == SUCCESS) + { + policy->route->if_index = get_interface_index(this, dst); + policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len); + policy->route->prefixlen = policy->sel.prefixlen_s; + + if (manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + policy->route) != SUCCESS) + { + DBG1(DBG_KNL, "unable to install source route for %H", + policy->route->src_ip); + route_entry_destroy(policy->route); + policy->route = NULL; + } + } + else + { + free(policy->route); + policy->route = NULL; + } + } + + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.query_policy. + */ +static status_t query_policy(private_kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, u_int32_t *use_time) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_userpolicy_id *policy_id; + struct xfrm_userpolicy_info *policy = NULL; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying policy %R===%R", src_ts, dst_ts); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel = ts2selector(src_ts, dst_ts); + policy_id->dir = direction; + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWPOLICY: + { + policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying policy failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (policy == NULL) + { + DBG2(DBG_KNL, "unable to query policy %R===%R", src_ts, dst_ts); + free(out); + return FAILED; + } + *use_time = (time_t)policy->curlft.use_time; + + free(out); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.del_policy. + */ +static status_t del_policy(private_kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction) +{ + policy_entry_t *current, policy, *to_delete = NULL; + route_entry_t *route; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct xfrm_userpolicy_id *policy_id; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting policy %R===%R", src_ts, dst_ts); + + /* create a policy */ + memset(&policy, 0, sizeof(policy_entry_t)); + policy.sel = ts2selector(src_ts, dst_ts); + policy.direction = direction; + + /* find the policy */ + pthread_mutex_lock(&this->policies_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (memcmp(¤t->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 && + policy.direction == current->direction) + { + to_delete = current; + if (--to_delete->refcount > 0) + { + /* is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + iterator->destroy(iterator); + pthread_mutex_unlock(&this->policies_mutex); + return SUCCESS; + } + /* remove if last reference */ + iterator->remove(iterator); + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&this->policies_mutex); + if (!to_delete) + { + DBG1(DBG_KNL, "deleting policy %R===%R failed, not found", src_ts, dst_ts); + return NOT_FOUND; + } + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel = to_delete->sel; + policy_id->dir = direction; + + route = to_delete->route; + free(to_delete); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete policy %R===%R", src_ts, dst_ts); + return FAILED; + } + + if (route) + { + if (manage_srcroute(this, RTM_DELROUTE, 0, route) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R===%R", src_ts, dst_ts); + } + route_entry_destroy(route); + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.destroy. + */ +static void destroy(private_kernel_interface_t *this) +{ + pthread_cancel(this->event_thread); + pthread_join(this->event_thread, NULL); + close(this->socket_xfrm_events); + close(this->socket_xfrm); + close(this->socket_rt); + this->vips->destroy(this->vips); + this->policies->destroy(this->policies); + free(this); +} + +/* + * Described in header. + */ +kernel_interface_t *kernel_interface_create() +{ + private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); + struct sockaddr_nl addr; + + /* public functions */ + this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; + this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,natt_conf_t*,mode_t,bool))add_sa; + this->public.update_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_diff_t,host_diff_t))update_sa; + this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_sa; + this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,bool))add_policy; + this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy; + this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy; + + this->public.get_interface = (char*(*)(kernel_interface_t*,host_t*))get_interface_name; + this->public.create_address_list = (linked_list_t*(*)(kernel_interface_t*))create_address_list_public; + this->public.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip; + this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip; + this->public.destroy = (void(*)(kernel_interface_t*)) destroy; + + /* private members */ + this->vips = linked_list_create(); + this->policies = linked_list_create(); + pthread_mutex_init(&this->policies_mutex,NULL); + pthread_mutex_init(&this->vips_mutex,NULL); + + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + /* create and bind XFRM socket */ + this->socket_xfrm = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm <= 0) + { + charon->kill(charon, "unable to create XFRM netlink socket"); + } + + if (bind(this->socket_xfrm, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind XFRM netlink socket"); + } + + /* create and bind RT socket */ + this->socket_rt = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->socket_rt <= 0) + { + charon->kill(charon, "unable to create RT netlink socket"); + } + + if (bind(this->socket_rt, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind RT netlink socket"); + } + + /* create and bind XFRM socket for ACQUIRE & EXPIRE */ + addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; + this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm_events <= 0) + { + charon->kill(charon, "unable to create XFRM event socket"); + } + + if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind XFRM event socket"); + } + + /* create a thread receiving ACQUIRE & EXPIRE events */ + if (pthread_create(&this->event_thread, NULL, + (void*(*)(void*))receive_events, this)) + { + charon->kill(charon, "unable to create xfrm event dispatcher thread"); + } + + return &this->public; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h new file mode 100644 index 000000000..34b06f594 --- /dev/null +++ b/src/charon/threads/kernel_interface.h @@ -0,0 +1,331 @@ +/** + * @file kernel_interface.h + * + * @brief Interface of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef KERNEL_INTERFACE_H_ +#define KERNEL_INTERFACE_H_ + +typedef struct natt_conf_t natt_conf_t; +typedef enum policy_dir_t policy_dir_t; +typedef struct kernel_interface_t kernel_interface_t; + +#include <utils/host.h> +#include <crypto/prf_plus.h> +#include <encoding/payloads/proposal_substructure.h> + +/** + * Configuration for NAT-T + */ +struct natt_conf_t { + /** source port to use for UDP-encapsulated packets */ + u_int16_t sport; + /** dest port to use for UDP-encapsulated packets */ + u_int16_t dport; +}; + +/** + * Direction of a policy. These are equal to those + * defined in xfrm.h, but we want to stay implementation + * neutral here. + */ +enum policy_dir_t { + /** Policy for inbound traffic */ + POLICY_IN = 0, + /** Policy for outbound traffic */ + POLICY_OUT = 1, + /** Policy for forwarded traffic */ + POLICY_FWD = 2, +}; + +/** + * @brief Interface to the kernel. + * + * The kernel interface handles the communication with the kernel + * for SA and policy management. It allows setup of these, and provides + * further the handling of kernel events. + * Policy information are cached in the interface. This is necessary to do + * reference counting. The Linux kernel does not allow the same policy + * installed twice, but we need this as CHILD_SA exist multiple times + * when rekeying. Thats why we do reference counting of policies. + * + * @b Constructors: + * - kernel_interface_create() + * + * @ingroup threads + */ +struct kernel_interface_t { + + /** + * @brief Get a SPI from the kernel. + * + * @warning get_spi() implicitely creates an SA with + * the allocated SPI, therefore the replace flag + * in add_sa() must be set when installing this SA. + * + * @param this calling object + * @param src source address of SA + * @param dst destination address of SA + * @param protocol protocol for SA (ESP/AH) + * @param reqid unique ID for this SA + * @param[out] spi allocated spi + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*get_spi)(kernel_interface_t *this, host_t *src, host_t *dst, + protocol_id_t protocol, u_int32_t reqid, u_int32_t *spi); + + /** + * @brief Add an SA to the SAD. + * + * add_sa() may update an already allocated + * SPI (via get_spi). In this case, the replace + * flag must be set. + * This function does install a single SA for a + * single protocol in one direction. The kernel-interface + * gets the keys itself from the PRF, as we don't know + * his algorithms and key sizes. + * + * @param this calling object + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param reqid unique ID for this SA + * @param expire_soft lifetime in seconds before rekeying + * @param expire_hard lieftime in seconds before delete + * @param enc_alg Algorithm to use for encryption (ESP only) + * @param int_alg Algorithm to use for integrity protection + * @param prf_plus PRF to derive keys from + * @param natt NAT-T Configuration, or NULL of no NAT-T used + * @param mode mode of the SA (tunnel, transport) + * @param replace Should an already installed SA be updated? + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_sa) (kernel_interface_t *this, + host_t *src, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t reqid, + u_int64_t expire_soft, u_int64_t expire_hard, + algorithm_t *enc_alg, algorithm_t *int_alg, + prf_plus_t *prf_plus, natt_conf_t *natt, + mode_t mode, bool update); + + /** + * @brief Update the hosts on an installed SA. + * + * We cannot directly update the destination address as the kernel + * requires the spi, the protocol AND the destination address (and family) + * to identify SAs. Therefore if the destination address changed we + * create a new SA and delete the old one. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI of the SA + * @param protocol protocol for this SA (ESP/AH) + * @param new_src new source address for this SA + * @param new_dst new destination address for this SA + * @param src_changes changes in src + * @param dst_changes changes in dst + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*update_sa)(kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol, + host_t *new_src, host_t *new_dst, + host_diff_t src_changes, host_diff_t dst_changes); + + /** + * @brief Query the use time of an SA. + * + * The use time of an SA is not the time of the last usage, but + * the time of the first usage of the SA. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param[out] use_time the time of this SA's last use + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*query_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t *use_time); + + /** + * @brief Delete a previusly installed SA from the SAD. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol); + + /** + * @brief Add a policy to the SPD. + * + * A policy is always associated to an SA. Traffic which matches a + * policy is handled by the SA with the same reqid. + * If the update flag is set, the policy is updated with the new + * src/dst addresses. + * If the update flag is not set, but a such policy is already in the + * kernel, the reference count to this policy is increased. + * + * @param this calling object + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @param protocol protocol to use to protect traffic (AH/ESP) + * @param reqid uniqe ID of an SA to use to enforce policy + * @param high_prio if TRUE, uses a higher priority than any with FALSE + * @param mode mode of SA (tunnel, transport) + * @param update update an existing policy, if TRUE + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_policy) (kernel_interface_t *this, + host_t *src, host_t *dst, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, protocol_id_t protocol, + u_int32_t reqid, bool high_prio, + mode_t mode, bool update); + + /** + * @brief Query the use time of a policy. + * + * The use time of a policy is the time the policy was used + * for the last time. + * + * @param this calling object + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @param[out] use_time the time of this SA's last use + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*query_policy) (kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, u_int32_t *use_time); + + /** + * @brief Remove a policy from the SPD. + * + * The kernel interface implements reference counting for policies. + * If the same policy is installed multiple times (in the case of rekeying), + * the reference counter is increased. del_policy() decreases the ref counter + * and removes the policy only when no more references are available. + * + * @param this calling object + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_policy) (kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction); + + /** + * @brief Get the interface name of a local address. + * + * @param this calling object + * @param host address to get interface name from + * @return allocated interface name, or NULL if not found + */ + char* (*get_interface) (kernel_interface_t *this, host_t *host); + + /** + * @brief Creates a list of all local addresses. + * + * @param this calling object + * @return allocated list with host_t objects + */ + linked_list_t *(*create_address_list) (kernel_interface_t *this); + + /** + * @brief Add a virtual IP to an interface. + * + * Virtual IPs are attached to an interface. If an IP is added multiple + * times, the IP is refcounted and not removed until del_ip() was called + * as many times as add_ip(). + * The virtual IP is attached to the interface where the iface_ip is found. + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param iface_ip IP of an interface to attach virtual IP + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *iface_ip); + + /** + * @brief Remove a virtual IP from an interface. + * + * The kernel interface uses refcounting, see add_ip(). + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param iface_ip IP of an interface to remove virtual IP from + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *iface_ip); + + /** + * @brief Destroys a kernel_interface object. + * + * @param kernel_interface_t calling object + */ + void (*destroy) (kernel_interface_t *kernel_interface); +}; + +/** + * @brief Creates an object of type kernel_interface_t. + * + * @ingroup threads + */ +kernel_interface_t *kernel_interface_create(void); + +#endif /*KERNEL_INTERFACE_H_*/ diff --git a/src/charon/threads/receiver.c b/src/charon/threads/receiver.c new file mode 100644 index 000000000..7195c162d --- /dev/null +++ b/src/charon/threads/receiver.c @@ -0,0 +1,372 @@ +/** + * @file receiver.c + * + * @brief Implementation of receiver_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <pthread.h> + +#include "receiver.h" + +#include <daemon.h> +#include <network/socket.h> +#include <network/packet.h> +#include <queues/job_queue.h> +#include <queues/jobs/job.h> +#include <queues/jobs/process_message_job.h> + +/** length of the full cookie, including time (u_int32_t + SHA1()) */ +#define COOKIE_LENGTH 24 +/** lifetime of a cookie, in seconds */ +#define COOKIE_LIFETIME 10 +/** how many times to reuse the secret */ +#define COOKIE_REUSE 10000 +/** require cookies after half open IKE_SAs */ +#define COOKIE_TRESHOLD 10 +/** how many half open IKE_SAs per peer before blocking */ +#define BLOCK_TRESHOLD 5 +/** length of the secret to use for cookie calculation */ +#define SECRET_LENGTH 16 + +typedef struct private_receiver_t private_receiver_t; + +/** + * Private data of a receiver_t object. + */ +struct private_receiver_t { + /** + * Public part of a receiver_t object. + */ + receiver_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * current secret to use for cookie calculation + */ + char secret[SECRET_LENGTH]; + + /** + * previous secret used to verify older cookies + */ + char secret_old[SECRET_LENGTH]; + + /** + * how many times we have used "secret" so far + */ + u_int32_t secret_used; + + /** + * time we did the cookie switch + */ + u_int32_t secret_switch; + + /** + * time offset to use, hides our system time + */ + u_int32_t secret_offset; + + /** + * the randomizer to use for secret generation + */ + randomizer_t *randomizer; + + /** + * hasher to use for cookie calculation + */ + hasher_t *hasher; +}; + +/** + * send a notify back to the sender + */ +static void send_notify(message_t *request, notify_type_t type, chunk_t data) +{ + if (request->get_request(request) && + request->get_exchange_type(request) == IKE_SA_INIT) + { + message_t *response; + host_t *src, *dst; + packet_t *packet; + ike_sa_id_t *ike_sa_id; + + response = message_create(); + dst = request->get_source(request); + src = request->get_destination(request); + response->set_source(response, src->clone(src)); + response->set_destination(response, dst->clone(dst)); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, 0); + ike_sa_id = request->get_ike_sa_id(request); + ike_sa_id->switch_initiator(ike_sa_id); + response->set_ike_sa_id(response, ike_sa_id); + response->add_notify(response, FALSE, type, data); + if (response->generate(response, NULL, NULL, &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + response->destroy(response); + } + } +} + +/** + * build a cookie + */ +static chunk_t cookie_build(private_receiver_t *this, message_t *message, + u_int32_t t, chunk_t secret) +{ + u_int64_t spi = message->get_initiator_spi(message); + host_t *ip = message->get_source(message); + chunk_t input, hash = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + + /* COOKIE = t | sha1( IPi | SPIi | t | secret ) */ + input = chunk_cata("cccc", ip->get_address(ip), chunk_from_thing(spi), + chunk_from_thing(t), secret); + this->hasher->get_hash(this->hasher, input, hash.ptr); + return chunk_cat("cc", chunk_from_thing(t), hash); +} + +/** + * verify a received cookie + */ +static bool cookie_verify(private_receiver_t *this, message_t *message, + chunk_t cookie) +{ + u_int32_t t, now; + chunk_t reference; + chunk_t secret; + + now = time(NULL); + t = *(u_int32_t*)cookie.ptr; + + if (cookie.len != COOKIE_LENGTH || + t < now - this->secret_offset - COOKIE_LIFETIME) + { + DBG2(DBG_NET, "received cookie lifetime expired, rejecting"); + return FALSE; + } + + /* check if cookie is derived from old_secret */ + if (t + this->secret_offset > this->secret_switch) + { + secret = chunk_from_thing(this->secret); + } + else + { + secret = chunk_from_thing(this->secret_old); + } + + /* compare own calculation against received */ + reference = cookie_build(this, message, t, secret); + if (chunk_equals(reference, cookie)) + { + chunk_free(&reference); + return TRUE; + } + chunk_free(&reference); + return FALSE; +} + +/** + * check if cookies are required, and if so, a valid cookie is included + */ +static bool cookie_required(private_receiver_t *this, message_t *message) +{ + bool failed = FALSE; + + if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, + NULL) >= COOKIE_TRESHOLD) + { + /* check for a cookie. We don't use our parser here and do it + * quick and dirty for performance reasons. + * we assume to cookie is the first payload (which is a MUST), and + * the cookies SPI length is zero. */ + packet_t *packet = message->get_packet(message); + chunk_t data = packet->get_data(packet); + if (data.len < + IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH + COOKIE_LENGTH || + *(data.ptr + 16) != NOTIFY || + *(u_int16_t*)(data.ptr + IKE_HEADER_LENGTH + 6) != htons(COOKIE)) + { + /* no cookie found */ + failed = TRUE; + } + else + { + data.ptr += IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH; + data.len = COOKIE_LENGTH; + if (!cookie_verify(this, message, data)) + { + DBG2(DBG_NET, "found cookie, but content invalid"); + failed = TRUE; + } + } + packet->destroy(packet); + } + return failed; +} + +/** + * check if peer has to many half open IKE_SAs + */ +static bool peer_to_aggressive(private_receiver_t *this, message_t *message) +{ + if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, + message->get_source(message)) >= BLOCK_TRESHOLD) + { + return TRUE; + } + return FALSE; +} + +/** + * Implementation of receiver_t.receive_packets. + */ +static void receive_packets(private_receiver_t *this) +{ + packet_t *packet; + message_t *message; + job_t *job; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + DBG1(DBG_NET, "receiver thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + /* read in a packet */ + if (charon->socket->receive(charon->socket, &packet) != SUCCESS) + { + DBG1(DBG_NET, "receiving from socket failed!"); + continue; + } + + /* parse message header */ + message = message_create_from_packet(packet); + if (message->parse_header(message) != SUCCESS) + { + DBG1(DBG_NET, "received invalid IKE header from %H, ignored", + packet->get_source(packet)); + message->destroy(message); + continue; + } + + /* check IKE major version */ + if (message->get_major_version(message) != IKE_MAJOR_VERSION) + { + DBG1(DBG_NET, "received unsupported IKE version %d.%d from %H, " + "sending INVALID_MAJOR_VERSION", message->get_major_version(message), + message->get_minor_version(message), packet->get_source(packet)); + send_notify(message, INVALID_MAJOR_VERSION, chunk_empty); + message->destroy(message); + continue; + } + + if (message->get_request(message) && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* check for cookies */ + if (cookie_required(this, message)) + { + u_int32_t now = time(NULL); + chunk_t cookie = cookie_build(this, message, now - this->secret_offset, + chunk_from_thing(this->secret)); + + DBG2(DBG_NET, "received packet from: %#H to %#H", + message->get_source(message), + message->get_destination(message)); + DBG2(DBG_NET, "sending COOKIE notify to %H", + message->get_source(message)); + send_notify(message, COOKIE, cookie); + chunk_free(&cookie); + if (++this->secret_used > COOKIE_REUSE) + { + /* create new cookie */ + DBG1(DBG_NET, "generating new cookie secret after %d uses", + this->secret_used); + memcpy(this->secret_old, this->secret, SECRET_LENGTH); + this->randomizer->get_pseudo_random_bytes(this->randomizer, + SECRET_LENGTH, this->secret); + this->secret_switch = now; + this->secret_used = 0; + } + message->destroy(message); + continue; + } + + /* check if peer has not too many IKE_SAs half open */ + if (peer_to_aggressive(this, message)) + { + DBG1(DBG_NET, "ignoring IKE_SA setup from %H, " + "peer to aggressive", message->get_source(message)); + message->destroy(message); + continue; + } + } + job = (job_t *)process_message_job_create(message); + charon->job_queue->add(charon->job_queue, job); + } +} + +/** + * Implementation of receiver_t.destroy. + */ +static void destroy(private_receiver_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + this->randomizer->destroy(this->randomizer); + this->hasher->destroy(this->hasher); + free(this); +} + +/* + * Described in header. + */ +receiver_t *receiver_create() +{ + private_receiver_t *this = malloc_thing(private_receiver_t); + u_int32_t now = time(NULL); + + this->public.destroy = (void(*)(receiver_t*)) destroy; + + this->randomizer = randomizer_create(); + this->hasher = hasher_create(HASH_SHA1); + this->secret_switch = now; + this->secret_offset = random() % now; + this->secret_used = 0; + this->randomizer->get_pseudo_random_bytes(this->randomizer, SECRET_LENGTH, + this->secret); + memcpy(this->secret_old, this->secret, SECRET_LENGTH); + + if (pthread_create(&this->assigned_thread, NULL, + (void*)receive_packets, this) != 0) + { + free(this); + charon->kill(charon, "unable to create receiver thread"); + } + + return &this->public; +} diff --git a/src/charon/threads/receiver.h b/src/charon/threads/receiver.h new file mode 100644 index 000000000..68d9136c0 --- /dev/null +++ b/src/charon/threads/receiver.h @@ -0,0 +1,81 @@ +/** + * @file receiver.h + * + * @brief Interface of receiver_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef RECEIVER_H_ +#define RECEIVER_H_ + +typedef struct receiver_t receiver_t; + +#include <library.h> +#include <utils/host.h> + +/** + * @brief Receives packets from the socket and adds them to the job queue. + * + * The receiver starts a thread, wich reads on the blocking socket. A received + * packet is preparsed and a process_message_job is queued in the job queue. + * + * To endure DoS attacks, cookies are enabled when to many IKE_SAs are half + * open. The calculation of cookies is slightly different from the proposed + * method in RFC4306. We do not include a nonce, because we think the advantage + * we gain does not justify the overhead to parse the whole message. + * Instead of VersionIdOfSecret, we include a timestamp. This allows us to + * find out wich key was used for cookie creation. Further, we can set a + * lifetime for the cookie, which allows us to reuse the secret for a longer + * time. + * COOKIE = time | sha1( IPi | SPIi | time | secret ) + * + * The secret is changed after a certain amount of cookies sent. The old + * secret is stored to allow a clean migration between secret changes. + * + * Further, the number of half-initiated IKE_SAs is limited per peer. This + * mades it impossible for a peer to flood the server with its real IP address. + * + * @b Constructors: + * - receiver_create() + * + * @ingroup threads + */ +struct receiver_t { + + /** + * @brief Destroys a receiver_t object. + * + * @param receiver receiver object + */ + void (*destroy) (receiver_t *receiver); +}; + +/** + * @brief Create a receiver_t object. + * + * The receiver thread will start working, get data + * from the socket and add those packets to the job queue. + * + * @return receiver_t object + * + * @ingroup threads + */ +receiver_t * receiver_create(void); + +#endif /*RECEIVER_H_*/ diff --git a/src/charon/threads/scheduler.c b/src/charon/threads/scheduler.c new file mode 100644 index 000000000..74091e3a3 --- /dev/null +++ b/src/charon/threads/scheduler.c @@ -0,0 +1,102 @@ +/** + * @file scheduler.c + * + * @brief Implementation of scheduler_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <pthread.h> + +#include "scheduler.h" + +#include <daemon.h> +#include <queues/job_queue.h> + + +typedef struct private_scheduler_t private_scheduler_t; + +/** + * Private data of a scheduler_t object. + */ +struct private_scheduler_t { + /** + * Public part of a scheduler_t object. + */ + scheduler_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; +}; + +/** + * Implementation of private_scheduler_t.get_events. + */ +static void get_events(private_scheduler_t * this) +{ + job_t *current_job; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + DBG1(DBG_JOB, "scheduler thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + DBG2(DBG_JOB, "waiting for next event..."); + /* get a job, this block until one is available */ + current_job = charon->event_queue->get(charon->event_queue); + /* queue the job in the job queue, workers will eat them */ + DBG2(DBG_JOB, "got event, adding job %N to job-queue", + job_type_names, current_job->get_type(current_job)); + charon->job_queue->add(charon->job_queue, current_job); + } +} + +/** + * Implementation of scheduler_t.destroy. + */ +static void destroy(private_scheduler_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + free(this); +} + +/* + * Described in header. + */ +scheduler_t * scheduler_create() +{ + private_scheduler_t *this = malloc_thing(private_scheduler_t); + + this->public.destroy = (void(*)(scheduler_t*)) destroy; + + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))get_events, this) != 0) + { + /* thread could not be created */ + free(this); + charon->kill(charon, "unable to create scheduler thread"); + } + + return &(this->public); +} diff --git a/src/charon/threads/scheduler.h b/src/charon/threads/scheduler.h new file mode 100644 index 000000000..daecce3c6 --- /dev/null +++ b/src/charon/threads/scheduler.h @@ -0,0 +1,68 @@ +/** + * @file scheduler.h + * + * @brief Interface of scheduler_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef SCHEDULER_H_ +#define SCHEDULER_H_ + +typedef struct scheduler_t scheduler_t; + +#include <library.h> + +/** + * @brief The scheduler thread is responsible for timed events. + * + * The scheduler thread takes out jobs from the event-queue and adds them + * to the job-queue. + * + * Starts a thread which does the work, since event-queue is blocking. + * + * @b Constructors: + * - scheduler_create() + * + * @ingroup threads + */ +struct scheduler_t { + + /** + * @brief Destroys a scheduler object. + * + * @param scheduler calling object + */ + void (*destroy) (scheduler_t *scheduler); +}; + +/** + * @brief Create a scheduler with its associated thread. + * + * The thread will start to get jobs form the event queue + * and adds them to the job queue. + * + * @return + * - scheduler_t object + * - NULL if thread could not be started + * + * @ingroup threads + */ +scheduler_t * scheduler_create(void); + +#endif /*SCHEDULER_H_*/ diff --git a/src/charon/threads/sender.c b/src/charon/threads/sender.c new file mode 100644 index 000000000..c1cd0a68c --- /dev/null +++ b/src/charon/threads/sender.c @@ -0,0 +1,149 @@ +/** + * @file sender.c + * + * @brief Implementation of sender_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <pthread.h> + +#include "sender.h" + +#include <daemon.h> +#include <network/socket.h> + + +typedef struct private_sender_t private_sender_t; + +/** + * Private data of a sender_t object. + */ +struct private_sender_t { + /** + * Public part of a sender_t object. + */ + sender_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * The packets are stored in a linked list + */ + linked_list_t *list; + + /** + * mutex to synchronize access to list + */ + pthread_mutex_t mutex; + + /** + * condvar to signal for packets in list + */ + pthread_cond_t condvar; +}; + +/** + * implements sender_t.send + */ +static void send_(private_sender_t *this, packet_t *packet) +{ + host_t *src, *dst; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + pthread_mutex_lock(&this->mutex); + this->list->insert_last(this->list, packet); + pthread_mutex_unlock(&this->mutex); + pthread_cond_signal(&this->condvar); +} + +/** + * Implementation of private_sender_t.send_packets. + */ +static void send_packets(private_sender_t * this) +{ + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + DBG1(DBG_NET, "sender thread running, thread_ID: %06u", (int)pthread_self()); + + while (TRUE) + { + packet_t *packet; + int oldstate; + + pthread_mutex_lock(&this->mutex); + /* go to wait while no packets available */ + while (this->list->get_count(this->list) == 0) + { + /* add cleanup handler, wait for packet, remove cleanup handler */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&this->mutex); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + pthread_cond_wait(&this->condvar, &this->mutex); + + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + this->list->remove_first(this->list, (void**)&packet); + pthread_mutex_unlock(&this->mutex); + + charon->socket->send(charon->socket, packet); + packet->destroy(packet); + } +} + +/** + * Implementation of sender_t.destroy. + */ +static void destroy(private_sender_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + this->list->destroy_offset(this->list, offsetof(packet_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +sender_t * sender_create() +{ + private_sender_t *this = malloc_thing(private_sender_t); + + this->public.send = (void(*)(sender_t*,packet_t*))send_; + this->public.destroy = (void(*)(sender_t*)) destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + pthread_cond_init(&this->condvar, NULL); + + if (pthread_create(&this->assigned_thread, NULL, + (void*)send_packets, this) != 0) + { + charon->kill(charon, "unable to create sender thread"); + } + + return &(this->public); +} diff --git a/src/charon/threads/sender.h b/src/charon/threads/sender.h new file mode 100644 index 000000000..4f42f6f9e --- /dev/null +++ b/src/charon/threads/sender.h @@ -0,0 +1,74 @@ +/** + * @file sender.h + * + * @brief Interface of sender_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef SENDER_H_ +#define SENDER_H_ + +typedef struct sender_t sender_t; + +#include <library.h> +#include <network/packet.h> + +/** + * @brief Thread responsible for sending packets over the socket. + * + * @b Constructors: + * - sender_create() + * + * @ingroup threads + */ +struct sender_t { + + /** + * @brief Send a packet over the network. + * + * This function is non blocking and adds the packet to a queue. + * Whenever the sender thread things it's good to send the packet, + * it'll do so. + * + * @param this calling object + * @param packet packet to send + */ + void (*send) (sender_t *this, packet_t *packet); + + /** + * @brief Destroys a sender object. + * + * @param this calling object + */ + void (*destroy) (sender_t *this); +}; + +/** + * @brief Create the sender thread. + * + * The thread will start to work, getting packets + * from its queue and sends them out. + * + * @return created sender object + * + * @ingroup threads + */ +sender_t * sender_create(void); + +#endif /*SENDER_H_*/ diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c new file mode 100755 index 000000000..a9074debb --- /dev/null +++ b/src/charon/threads/stroke_interface.c @@ -0,0 +1,1456 @@ +/** + * @file stroke.c + * + * @brief Implementation of stroke_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#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 <dirent.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> + +#include "stroke_interface.h" + +#include <library.h> +#include <stroke.h> +#include <daemon.h> +#include <crypto/x509.h> +#include <crypto/ca.h> +#include <crypto/crl.h> +#include <queues/jobs/initiate_job.h> +#include <queues/jobs/route_job.h> +#include <utils/leak_detective.h> + +#define IKE_PORT 500 +#define PATH_BUF 256 + + +struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; + + +typedef struct private_stroke_t private_stroke_t; + +/** + * Private data of an stroke_t object. + */ +struct private_stroke_t { + + /** + * Public part of stroke_t object. + */ + stroke_t public; + + /** + * Output stream (stroke console) + */ + FILE *out; + + /** + * Unix socket to listen for strokes + */ + int socket; + + /** + * Thread which reads from the Socket + */ + pthread_t assigned_thread; +}; + +/** + * 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; + } +} + +/** + * Load end entitity certificate + */ +static x509_t* load_end_certificate(const char *filename, identification_t **idp) +{ + char path[PATH_BUF]; + x509_t *cert; + + if (*filename == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%s", filename); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename); + } + + cert = x509_create_from_file(path, "end entity"); + + if (cert) + { + identification_t *id = *idp; + identification_t *subject = cert->get_subject(cert); + + err_t ugh = cert->is_valid(cert, NULL); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "warning: certificate %s", ugh); + } + if (!id->equals(id, subject) && !cert->equals_subjectAltName(cert, id)) + { + id->destroy(id); + id = subject; + *idp = id->clone(id); + } + return charon->credentials->add_end_certificate(charon->credentials, cert); + } + return NULL; +} + +/** + * Load ca certificate + */ +static x509_t* load_ca_certificate(const char *filename) +{ + char path[PATH_BUF]; + x509_t *cert; + + if (*filename == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%s", filename); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); + } + + cert = x509_create_from_file(path, "ca"); + + if (cert) + { + if (cert->is_ca(cert)) + { + return charon->credentials->add_auth_certificate(charon->credentials, cert, AUTH_CA); + } + else + { + DBG1(DBG_CFG, " CA basic constraints flag not set, cert discarded"); + cert->destroy(cert); + } + } + return NULL; +} + +/** + * Add a connection to the configuration list + */ +static void stroke_add_conn(stroke_msg_t *msg, FILE *out) +{ + connection_t *connection; + policy_t *policy; + identification_t *my_id, *other_id; + identification_t *my_ca = NULL; + identification_t *other_ca = NULL; + bool my_ca_same = FALSE; + bool other_ca_same =FALSE; + host_t *my_host, *other_host, *my_subnet, *other_subnet; + host_t *my_vip = NULL, *other_vip = NULL; + proposal_t *proposal; + traffic_selector_t *my_ts, *other_ts; + char *interface; + + pop_string(msg, &msg->add_conn.name); + pop_string(msg, &msg->add_conn.me.address); + pop_string(msg, &msg->add_conn.other.address); + pop_string(msg, &msg->add_conn.me.subnet); + pop_string(msg, &msg->add_conn.other.subnet); + pop_string(msg, &msg->add_conn.me.sourceip); + pop_string(msg, &msg->add_conn.other.sourceip); + pop_string(msg, &msg->add_conn.me.id); + pop_string(msg, &msg->add_conn.other.id); + pop_string(msg, &msg->add_conn.me.cert); + pop_string(msg, &msg->add_conn.other.cert); + pop_string(msg, &msg->add_conn.me.ca); + pop_string(msg, &msg->add_conn.other.ca); + pop_string(msg, &msg->add_conn.me.updown); + pop_string(msg, &msg->add_conn.other.updown); + pop_string(msg, &msg->add_conn.algorithms.ike); + pop_string(msg, &msg->add_conn.algorithms.esp); + + DBG1(DBG_CFG, "received stroke: add connection '%s'", msg->add_conn.name); + + DBG2(DBG_CFG, "conn %s", msg->add_conn.name); + DBG2(DBG_CFG, " left=%s", msg->add_conn.me.address); + DBG2(DBG_CFG, " right=%s", msg->add_conn.other.address); + DBG2(DBG_CFG, " leftsubnet=%s", msg->add_conn.me.subnet); + DBG2(DBG_CFG, " rightsubnet=%s", msg->add_conn.other.subnet); + DBG2(DBG_CFG, " leftsourceip=%s", msg->add_conn.me.sourceip); + DBG2(DBG_CFG, " rightsourceip=%s", msg->add_conn.other.sourceip); + DBG2(DBG_CFG, " leftid=%s", msg->add_conn.me.id); + DBG2(DBG_CFG, " rightid=%s", msg->add_conn.other.id); + DBG2(DBG_CFG, " leftcert=%s", msg->add_conn.me.cert); + DBG2(DBG_CFG, " rightcert=%s", msg->add_conn.other.cert); + DBG2(DBG_CFG, " leftca=%s", msg->add_conn.me.ca); + DBG2(DBG_CFG, " rightca=%s", msg->add_conn.other.ca); + DBG2(DBG_CFG, " ike=%s", msg->add_conn.algorithms.ike); + DBG2(DBG_CFG, " esp=%s", msg->add_conn.algorithms.esp); + + my_host = msg->add_conn.me.address? + host_create_from_string(msg->add_conn.me.address, IKE_PORT) : NULL; + if (my_host == NULL) + { + DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.me.address); + return; + } + + other_host = msg->add_conn.other.address ? + host_create_from_string(msg->add_conn.other.address, IKE_PORT) : NULL; + if (other_host == NULL) + { + DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.other.address); + my_host->destroy(my_host); + return; + } + + interface = charon->kernel_interface->get_interface(charon->kernel_interface, + other_host); + if (interface) + { + stroke_end_t tmp_end; + host_t *tmp_host; + + DBG2(DBG_CFG, "left is other host, swapping ends\n"); + + tmp_host = my_host; + my_host = other_host; + other_host = tmp_host; + + tmp_end = msg->add_conn.me; + msg->add_conn.me = msg->add_conn.other; + msg->add_conn.other = tmp_end; + free(interface); + } + if (!interface) + { + interface = charon->kernel_interface->get_interface( + charon->kernel_interface, my_host); + if (!interface) + { + DBG1(DBG_CFG, "left nor right host is our side, aborting\n"); + goto destroy_hosts; + } + free(interface); + } + + my_id = identification_create_from_string(msg->add_conn.me.id ? + msg->add_conn.me.id : msg->add_conn.me.address); + if (my_id == NULL) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.me.id); + goto destroy_hosts; + } + + other_id = identification_create_from_string(msg->add_conn.other.id ? + msg->add_conn.other.id : msg->add_conn.other.address); + if (other_id == NULL) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.other.id); + my_id->destroy(my_id); + goto destroy_hosts; + } + + my_subnet = host_create_from_string(msg->add_conn.me.subnet ? + msg->add_conn.me.subnet : msg->add_conn.me.address, IKE_PORT); + if (my_subnet == NULL) + { + DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet); + goto destroy_ids; + } + + other_subnet = host_create_from_string(msg->add_conn.other.subnet ? + msg->add_conn.other.subnet : msg->add_conn.other.address, IKE_PORT); + if (other_subnet == NULL) + { + DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet); + my_subnet->destroy(my_subnet); + goto destroy_ids; + } + + if (msg->add_conn.me.virtual_ip) + { + my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0); + } + other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0); + + if (msg->add_conn.me.tohost) + { + my_ts = traffic_selector_create_dynamic(msg->add_conn.me.protocol, + my_host->get_family(my_host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE, + msg->add_conn.me.port ? msg->add_conn.me.port : 0, + msg->add_conn.me.port ? msg->add_conn.me.port : 65535); + } + else + { + my_ts = traffic_selector_create_from_subnet(my_subnet, + msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 0, + msg->add_conn.me.protocol, msg->add_conn.me.port); + } + my_subnet->destroy(my_subnet); + + if (msg->add_conn.other.tohost) + { + other_ts = traffic_selector_create_dynamic(msg->add_conn.other.protocol, + other_host->get_family(other_host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE, + msg->add_conn.other.port ? msg->add_conn.other.port : 0, + msg->add_conn.other.port ? msg->add_conn.other.port : 65535); + } + else + { + other_ts = traffic_selector_create_from_subnet(other_subnet, + msg->add_conn.other.subnet ? msg->add_conn.other.subnet_mask : 0, + msg->add_conn.other.protocol, msg->add_conn.other.port); + } + other_subnet->destroy(other_subnet); + + 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 (msg->add_conn.me.cert) + { + x509_t *cert = load_end_certificate(msg->add_conn.me.cert, &my_id); + + if (my_ca == NULL && !my_ca_same && cert) + { + identification_t *issuer = cert->get_issuer(cert); + + my_ca = issuer->clone(issuer); + } + } + if (msg->add_conn.other.cert) + { + x509_t *cert = load_end_certificate(msg->add_conn.other.cert, &other_id); + + if (other_ca == NULL && !other_ca_same && cert) + { + identification_t *issuer = cert->get_issuer(cert); + + other_ca = issuer->clone(issuer); + } + } + 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 (my_ca == NULL) + { + my_ca = identification_create_from_string("%any"); + } + if (other_ca == NULL) + { + other_ca = identification_create_from_string("%any"); + } + DBG2(DBG_CFG, " my ca: '%D'", my_ca); + DBG2(DBG_CFG, " other ca:'%D'", other_ca); + DBG2(DBG_CFG, " updown: '%s'", msg->add_conn.me.updown); + + connection = connection_create(msg->add_conn.name, + msg->add_conn.ikev2, + msg->add_conn.me.sendcert, + msg->add_conn.other.sendcert, + my_host, other_host, + msg->add_conn.dpd.delay, + msg->add_conn.rekey.reauth, + msg->add_conn.rekey.tries, + msg->add_conn.rekey.ike_lifetime, + msg->add_conn.rekey.ike_lifetime - msg->add_conn.rekey.margin, + msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100); + + if (msg->add_conn.algorithms.ike) + { + char *proposal_string; + char *strict = msg->add_conn.algorithms.ike + strlen(msg->add_conn.algorithms.ike) - 1; + + if (*strict == '!') + *strict = '\0'; + else + strict = NULL; + + while ((proposal_string = strsep(&msg->add_conn.algorithms.ike, ","))) + { + proposal = proposal_create_from_string(PROTO_IKE, proposal_string); + if (proposal == NULL) + { + DBG1(DBG_CFG, "invalid IKE proposal string: %s", proposal_string); + my_id->destroy(my_id); + other_id->destroy(other_id); + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); + my_ca->destroy(my_ca); + other_ca->destroy(other_ca); + connection->destroy(connection); + return; + } + connection->add_proposal(connection, proposal); + } + if (!strict) + { + proposal = proposal_create_default(PROTO_IKE); + connection->add_proposal(connection, proposal); + } + } + else + { + proposal = proposal_create_default(PROTO_IKE); + connection->add_proposal(connection, proposal); + } + + policy = policy_create(msg->add_conn.name, my_id, other_id, my_vip, other_vip, + msg->add_conn.auth_method, msg->add_conn.eap_type, + 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, msg->add_conn.dpd.action); + policy->add_my_traffic_selector(policy, my_ts); + policy->add_other_traffic_selector(policy, other_ts); + policy->add_authorities(policy, my_ca, other_ca); + + if (msg->add_conn.algorithms.esp) + { + char *proposal_string; + char *strict = msg->add_conn.algorithms.esp + strlen(msg->add_conn.algorithms.esp) - 1; + + if (*strict == '!') + *strict = '\0'; + else + strict = NULL; + + while ((proposal_string = strsep(&msg->add_conn.algorithms.esp, ","))) + { + proposal = proposal_create_from_string(PROTO_ESP, proposal_string); + if (proposal == NULL) + { + DBG1(DBG_CFG, "invalid ESP proposal string: %s", proposal_string); + policy->destroy(policy); + connection->destroy(connection); + return; + } + policy->add_proposal(policy, proposal); + } + if (!strict) + { + proposal = proposal_create_default(PROTO_ESP); + policy->add_proposal(policy, proposal); + } + } + else + { + proposal = proposal_create_default(PROTO_ESP); + policy->add_proposal(policy, proposal); + } + + /* add to global connection list */ + charon->connections->add_connection(charon->connections, connection); + DBG1(DBG_CFG, "added connection '%s': %H[%D]...%H[%D]", + msg->add_conn.name, my_host, my_id, other_host, other_id); + /* add to global policy list */ + charon->policies->add_policy(charon->policies, policy); + + return; + + /* mopping up after parsing errors */ + +destroy_ids: + my_id->destroy(my_id); + other_id->destroy(other_id); + +destroy_hosts: + my_host->destroy(my_host); + other_host->destroy(other_host); +} + +/** + * Delete a connection from the list + */ +static void stroke_del_conn(stroke_msg_t *msg, FILE *out) +{ + status_t status; + + pop_string(msg, &(msg->del_conn.name)); + DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); + + status = charon->connections->delete_connection(charon->connections, + msg->del_conn.name); + charon->policies->delete_policy(charon->policies, msg->del_conn.name); + if (status == SUCCESS) + { + fprintf(out, "deleted connection '%s'\n", msg->del_conn.name); + } + else + { + fprintf(out, "no connection named '%s'\n", msg->del_conn.name); + } +} + +/** + * initiate a connection by name + */ +static void stroke_initiate(stroke_msg_t *msg, FILE *out) +{ + initiate_job_t *job; + connection_t *connection; + policy_t *policy; + ike_sa_t *init_ike_sa = NULL; + signal_t signal; + + pop_string(msg, &(msg->initiate.name)); + DBG1(DBG_CFG, "received stroke: initiate '%s'", msg->initiate.name); + + connection = charon->connections->get_connection_by_name(charon->connections, + msg->initiate.name); + if (connection == NULL) + { + if (msg->output_verbosity >= 0) + { + fprintf(out, "no connection named '%s'\n", msg->initiate.name); + } + return; + } + if (!connection->is_ikev2(connection)) + { + connection->destroy(connection); + return; + } + + policy = charon->policies->get_policy_by_name(charon->policies, + msg->initiate.name); + if (policy == NULL) + { + if (msg->output_verbosity >= 0) + { + fprintf(out, "no policy named '%s'\n", msg->initiate.name); + } + connection->destroy(connection); + return; + } + + job = initiate_job_create(connection, policy); + charon->bus->set_listen_state(charon->bus, TRUE); + charon->job_queue->add(charon->job_queue, (job_t*)job); + while (TRUE) + { + level_t level; + int thread; + ike_sa_t *ike_sa; + char* format; + va_list args; + + signal = charon->bus->listen(charon->bus, &level, &thread, &ike_sa, &format, &args); + + if ((init_ike_sa == NULL || ike_sa == init_ike_sa) && + level <= msg->output_verbosity) + { + if (vfprintf(out, format, args) < 0 || + fprintf(out, "\n") < 0 || + fflush(out)) + { + charon->bus->set_listen_state(charon->bus, FALSE); + break; + } + } + + switch (signal) + { + case CHILD_UP_SUCCESS: + case CHILD_UP_FAILED: + case IKE_UP_FAILED: + if (ike_sa == init_ike_sa) + { + charon->bus->set_listen_state(charon->bus, FALSE); + return; + } + continue; + case CHILD_UP_START: + case IKE_UP_START: + if (init_ike_sa == NULL) + { + init_ike_sa = ike_sa; + } + continue; + default: + continue; + } + } +} + +/** + * route/unroute a policy (install SPD entries) + */ +static void stroke_route(stroke_msg_t *msg, FILE *out, bool route) +{ + route_job_t *job; + connection_t *connection; + policy_t *policy; + + pop_string(msg, &(msg->route.name)); + DBG1(DBG_CFG, "received stroke: %s '%s'", + route ? "route" : "unroute", msg->route.name); + + /* we wouldn't need a connection, but we only want to route policies + * whose connections are keyexchange=ikev2. */ + connection = charon->connections->get_connection_by_name(charon->connections, + msg->route.name); + if (connection == NULL) + { + fprintf(out, "no connection named '%s'\n", msg->route.name); + return; + } + if (!connection->is_ikev2(connection)) + { + connection->destroy(connection); + return; + } + + policy = charon->policies->get_policy_by_name(charon->policies, + msg->route.name); + if (policy == NULL) + { + fprintf(out, "no policy named '%s'\n", msg->route.name); + connection->destroy(connection); + return; + } + fprintf(out, "%s policy '%s'\n", + route ? "routing" : "unrouting", msg->route.name); + job = route_job_create(connection, policy, route); + charon->job_queue->add(charon->job_queue, (job_t*)job); +} + +/** + * terminate a connection by name + */ +static void stroke_terminate(stroke_msg_t *msg, FILE *out) +{ + char *string, *pos = NULL, *name = NULL; + u_int32_t id = 0; + bool child; + int len; + status_t status = SUCCESS;; + ike_sa_t *ike_sa; + + pop_string(msg, &(msg->terminate.name)); + string = msg->terminate.name; + DBG1(DBG_CFG, "received stroke: terminate '%s'", string); + + 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) + { /* must be a single name */ + DBG1(DBG_CFG, "check out by single name '%s'", name); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + name, child); + } + else if (pos == string + len - 2) + { /* must be name[] or name{} */ + string[len-2] = '\0'; + DBG1(DBG_CFG, "check out by name '%s'", string); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + string, child); + } + else + { /* must be name[123] or name{23} */ + string[len-1] = '\0'; + id = atoi(pos + 1); + if (id == 0) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + DBG1(DBG_CFG, "check out by id '%d'", id); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + id, child); + } + if (ike_sa == NULL) + { + DBG1(DBG_CFG, "no such IKE_SA found"); + return; + } + + if (!child) + { + status = ike_sa->delete(ike_sa); + } + else + { + child_sa_t *child_sa; + iterator_t *iterator = ike_sa->create_child_sa_iterator(ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if ((id && id == child_sa->get_reqid(child_sa)) || + (string && streq(string, child_sa->get_name(child_sa)))) + { + u_int32_t spi = child_sa->get_spi(child_sa, TRUE); + protocol_id_t proto = child_sa->get_protocol(child_sa); + + status = ike_sa->delete_child_sa(ike_sa, proto, spi); + break; + } + } + iterator->destroy(iterator); + } + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + return; + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); +} + +/** + * Add a ca information record to the cainfo list + */ +static void stroke_add_ca(stroke_msg_t *msg, FILE *out) +{ + x509_t *cacert; + ca_info_t *ca_info; + + pop_string(msg, &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); + + DBG1(DBG_CFG, "received stroke: add ca '%s'", msg->add_ca.name); + + 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); + + if (msg->add_ca.cacert == NULL) + { + DBG1(DBG_CFG, "missing cacert parameter\n"); + return; + } + + cacert = load_ca_certificate(msg->add_ca.cacert); + + if (cacert == NULL) + { + return; + } + ca_info = ca_info_create(msg->add_ca.name, cacert); + + if (msg->add_ca.crluri) + { + chunk_t uri = { msg->add_ca.crluri, strlen(msg->add_ca.crluri) }; + + ca_info->add_crluri(ca_info, uri); + } + if (msg->add_ca.crluri2) + { + chunk_t uri = { msg->add_ca.crluri2, strlen(msg->add_ca.crluri2) }; + + ca_info->add_crluri(ca_info, uri); + } + if (msg->add_ca.ocspuri) + { + chunk_t uri = { msg->add_ca.ocspuri, strlen(msg->add_ca.ocspuri) }; + + ca_info->add_ocspuri(ca_info, uri); + } + if (msg->add_ca.ocspuri2) + { + chunk_t uri = { msg->add_ca.ocspuri2, strlen(msg->add_ca.ocspuri2) }; + + ca_info->add_ocspuri(ca_info, uri); + } + charon->credentials->add_ca_info(charon->credentials, ca_info); + DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name); + +} + +/** + * Delete a ca information record from the cainfo list + */ +static void stroke_del_ca(stroke_msg_t *msg, FILE *out) +{ + status_t status; + + pop_string(msg, &(msg->del_ca.name)); + DBG1(DBG_CFG, "received stroke: delete ca '%s'", msg->del_ca.name); + + status = charon->credentials->release_ca_info(charon->credentials, + msg->del_ca.name); + + if (status == SUCCESS) + { + fprintf(out, "deleted ca '%s'\n", msg->del_ca.name); + } + else + { + fprintf(out, "no ca named '%s'\n", msg->del_ca.name); + } +} + +/** + * show status of daemon + */ +static void stroke_statusall(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + linked_list_t *list; + host_t *host; + connection_t *connection; + policy_t *policy; + ike_sa_t *ike_sa; + char *name = NULL; + + leak_detective_status(out); + + fprintf(out, "Performance:\n"); + fprintf(out, " worker threads: %d idle of %d,", + charon->thread_pool->get_idle_threads(charon->thread_pool), + charon->thread_pool->get_pool_size(charon->thread_pool)); + fprintf(out, " job queue load: %d,", + charon->job_queue->get_count(charon->job_queue)); + fprintf(out, " scheduled events: %d\n", + charon->event_queue->get_count(charon->event_queue)); + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + + fprintf(out, "Listening on %d IP addresses:\n", list->get_count(list)); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + fprintf(out, " %H\n", host); + host->destroy(host); + } + list->destroy(list); + + if (msg->status.name) + { + pop_string(msg, &(msg->status.name)); + name = msg->status.name; + } + + iterator = charon->connections->create_iterator(charon->connections); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Connections:\n"); + } + while (iterator->iterate(iterator, (void**)&connection)) + { + if (connection->is_ikev2(connection) + && (name == NULL || streq(name, connection->get_name(connection)))) + { + fprintf(out, "%12s: %H...%H\n", + connection->get_name(connection), + connection->get_my_host(connection), + connection->get_other_host(connection)); + } + } + iterator->destroy(iterator); + + iterator = charon->policies->create_iterator(charon->policies); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Policies:\n"); + } + while (iterator->iterate(iterator, (void**)&policy)) + { + if (name == NULL || streq(name, policy->get_name(policy))) + { + fprintf(out, "%12s: '%D'...'%D'\n", + policy->get_name(policy), + policy->get_my_id(policy), + policy->get_other_id(policy)); + } + } + iterator->destroy(iterator); + + iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Security Associations:\n"); + } + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + bool ike_sa_printed = FALSE; + child_sa_t *child_sa; + iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa); + + /* print IKE_SA */ + if (name == NULL || strncmp(name, ike_sa->get_name(ike_sa), strlen(name)) == 0) + { + fprintf(out, "%#K\n", ike_sa); + ike_sa_printed = TRUE; + } + + while (children->iterate(children, (void**)&child_sa)) + { + bool child_sa_match = name == NULL || + strncmp(name, child_sa->get_name(child_sa), strlen(name)) == 0; + + /* print IKE_SA if its name differs from the CHILD_SA's name */ + if (!ike_sa_printed && child_sa_match) + { + fprintf(out, "%#K\n", ike_sa); + ike_sa_printed = TRUE; + } + + /* print CHILD_SA */ + if (child_sa_match) + { + fprintf(out, "%#P\n", child_sa); + } + } + children->destroy(children); + } + iterator->destroy(iterator); +} + +/** + * show status of daemon + */ +static void stroke_status(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + ike_sa_t *ike_sa; + char *name = NULL; + + if (msg->status.name) + { + pop_string(msg, &(msg->status.name)); + name = msg->status.name; + } + + iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager); + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + bool ike_sa_printed = FALSE; + child_sa_t *child_sa; + iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa); + + /* print IKE_SA */ + if (name == NULL || strncmp(name, ike_sa->get_name(ike_sa), strlen(name)) == 0) + { + fprintf(out, "%K\n", ike_sa); + ike_sa_printed = TRUE; + } + + while (children->iterate(children, (void**)&child_sa)) + { + bool child_sa_match = name == NULL || + strncmp(name, child_sa->get_name(child_sa), strlen(name)) == 0; + + /* print IKE_SA if its name differs from the CHILD_SA's name */ + if (!ike_sa_printed && child_sa_match) + { + fprintf(out, "%K\n", ike_sa); + ike_sa_printed = TRUE; + } + + /* print CHILD_SA */ + if (child_sa_match) + { + fprintf(out, "%P\n", child_sa); + } + } + children->destroy(children); + } + iterator->destroy(iterator); +} + +/** + * list all authority certificates matching a specified flag + */ +static void list_auth_certificates(u_int flag, const char *label, bool utc, FILE *out) +{ + bool first = TRUE; + x509_t *cert; + + iterator_t *iterator = charon->credentials->create_auth_cert_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void**)&cert)) + { + if (cert->has_authority_flag(cert, flag)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 %s Certificates:\n", label); + fprintf(out, "\n"); + first = FALSE; + } + fprintf(out, "%#Q\n", cert, utc); + } + } + iterator->destroy(iterator); +} + +/** + * list various information + */ +static void stroke_list(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + + if (msg->list.flags & LIST_CERTS) + { + x509_t *cert; + + iterator = charon->credentials->create_cert_iterator(charon->credentials); + if (iterator->get_count(iterator)) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 End Entity Certificates:\n"); + fprintf(out, "\n"); + } + while (iterator->iterate(iterator, (void**)&cert)) + { + fprintf(out, "%#Q", cert, msg->list.utc); + if (charon->credentials->has_rsa_private_key( + charon->credentials, cert->get_public_key(cert))) + { + fprintf(out, ", has private key"); + } + fprintf(out, "\n"); + + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_CACERTS) + { + list_auth_certificates(AUTH_CA, "CA", msg->list.utc, out); + } + if (msg->list.flags & LIST_CAINFOS) + { + ca_info_t *ca_info; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + if (iterator->get_count(iterator)) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 CA Information Records:\n"); + fprintf(out, "\n"); + } + while (iterator->iterate(iterator, (void**)&ca_info)) + { + fprintf(out, "%#W", ca_info, msg->list.utc); + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_CRLS) + { + ca_info_t *ca_info; + bool first = TRUE; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + if (ca_info->has_crl(ca_info)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 CRLs:\n"); + fprintf(out, "\n"); + first = FALSE; + } + ca_info->list_crl(ca_info, out, msg->list.utc); + } + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_OCSPCERTS) + { + list_auth_certificates(AUTH_OCSP, "OCSP", msg->list.utc, out); + } + if (msg->list.flags & LIST_OCSP) + { + ca_info_t *ca_info; + bool first = TRUE; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + if (ca_info->has_certinfos(ca_info)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of OCSP responses:\n"); + first = FALSE; + } + fprintf(out, "\n"); + ca_info->list_certinfos(ca_info, out, msg->list.utc); + } + } + iterator->destroy(iterator); + } +} + +/** + * reread various information + */ +static void stroke_reread(stroke_msg_t *msg, FILE *out) +{ + if (msg->reread.flags & REREAD_CACERTS) + { + charon->credentials->load_ca_certificates(charon->credentials); + } + if (msg->reread.flags & REREAD_OCSPCERTS) + { + charon->credentials->load_ocsp_certificates(charon->credentials); + } + if (msg->reread.flags & REREAD_CRLS) + { + charon->credentials->load_crls(charon->credentials); + } +} + +/** + * purge various information + */ +static void stroke_purge(stroke_msg_t *msg, FILE *out) +{ + if (msg->purge.flags & PURGE_OCSP) + { + iterator_t *iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + ca_info_t *ca_info; + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + ca_info->purge_ocsp(ca_info); + } + iterator->destroy(iterator); + } +} + +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(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); +} + +/** + * process a stroke request from the socket pointed by "fd" + */ +static void stroke_process(int *fd) +{ + stroke_msg_t *msg; + u_int16_t msg_length; + ssize_t bytes_read; + FILE *out; + int strokefd = *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"); + close(strokefd); + return; + } + + /* read message */ + msg = malloc(msg_length); + bytes_read = recv(strokefd, msg, msg_length, 0); + if (bytes_read != msg_length) + { + DBG1(DBG_CFG, "reading stroke message failed: %m"); + close(strokefd); + return; + } + + out = fdopen(dup(strokefd), "w"); + if (out == NULL) + { + DBG1(DBG_CFG, "opening stroke output channel failed: %m"); + close(strokefd); + free(msg); + return; + } + + DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length); + + switch (msg->type) + { + case STR_INITIATE: + stroke_initiate(msg, out); + break; + case STR_ROUTE: + stroke_route(msg, out, TRUE); + break; + case STR_UNROUTE: + stroke_route(msg, out, FALSE); + break; + case STR_TERMINATE: + stroke_terminate(msg, out); + break; + case STR_STATUS: + stroke_status(msg, out); + break; + case STR_STATUS_ALL: + stroke_statusall(msg, out); + break; + case STR_ADD_CONN: + stroke_add_conn(msg, out); + break; + case STR_DEL_CONN: + stroke_del_conn(msg, out); + break; + case STR_ADD_CA: + stroke_add_ca(msg, out); + break; + case STR_DEL_CA: + stroke_del_ca(msg, out); + break; + case STR_LOGLEVEL: + stroke_loglevel(msg, out); + break; + case STR_LIST: + stroke_list(msg, out); + break; + case STR_REREAD: + stroke_reread(msg, out); + break; + case STR_PURGE: + stroke_purge(msg, out); + break; + default: + DBG1(DBG_CFG, "received unknown stroke"); + } + fclose(out); + close(strokefd); + free(msg); +} + +/** + * Implementation of private_stroke_t.stroke_receive. + */ +static void stroke_receive(private_stroke_t *this) +{ + struct sockaddr_un strokeaddr; + int strokeaddrlen = sizeof(strokeaddr); + int strokefd; + int oldstate; + pthread_t thread; + + /* ignore sigpipe. writing over the pipe back to the console + * only fails if SIGPIPE is ignored. */ + signal(SIGPIPE, SIG_IGN); + + /* disable cancellation by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (TRUE) + { + /* wait for connections, but allow thread to terminate */ + 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: %m"); + continue; + } + + /* handle request asynchronously */ + if (pthread_create(&thread, NULL, (void*(*)(void*))stroke_process, (void*)&strokefd) != 0) + { + DBG1(DBG_CFG, "failed to spawn stroke thread: %m"); + } + /* detach so the thread terminates cleanly */ + pthread_detach(thread); + } +} + +/** + * Implementation of stroke_t.destroy. + */ +static void destroy(private_stroke_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + + close(this->socket); + unlink(socket_addr.sun_path); + free(this); +} + +/* + * Described in header-file + */ +stroke_t *stroke_create() +{ + private_stroke_t *this = malloc_thing(private_stroke_t); + mode_t old; + + /* public functions */ + this->public.destroy = (void (*)(stroke_t*))destroy; + + /* set up unix socket */ + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (this->socket == -1) + { + DBG1(DBG_CFG, "could not create whack socket"); + free(this); + return NULL; + } + + old = umask(~S_IRWXU); + if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + { + DBG1(DBG_CFG, "could not bind stroke socket: %m"); + close(this->socket); + free(this); + return NULL; + } + umask(old); + + if (listen(this->socket, 0) < 0) + { + DBG1(DBG_CFG, "could not listen on stroke socket: %m"); + close(this->socket); + unlink(socket_addr.sun_path); + free(this); + return NULL; + } + + /* start a thread reading from the socket */ + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))stroke_receive, this) != 0) + { + DBG1(DBG_CFG, "could not spawn stroke thread"); + close(this->socket); + unlink(socket_addr.sun_path); + free(this); + return NULL; + } + + return (&this->public); +} diff --git a/src/charon/threads/stroke_interface.h b/src/charon/threads/stroke_interface.h new file mode 100644 index 000000000..0def5167e --- /dev/null +++ b/src/charon/threads/stroke_interface.h @@ -0,0 +1,61 @@ +/** + * @file stroke.h + * + * @brief Interface of stroke_t. + * + */ + +/* + * Copyright (C) 2006 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. + */ + +#ifndef STROKE_INTERFACE_H_ +#define STROKE_INTERFACE_H_ + +typedef struct stroke_t stroke_t; + +/** + * @brief Stroke is a configuration and control interface which + * allows other processes to modify charons behavior. + * + * stroke_t allows config manipulation (as whack in pluto). + * Messages of type stroke_msg_t's are sent over a unix socket + * (/var/run/charon.ctl). + * + * @b Constructors: + * - stroke_create() + * + * @ingroup threads + */ +struct stroke_t { + + /** + * @brief Destroy a stroke_t instance. + * + * @param this stroke_t objec to destroy + */ + void (*destroy) (stroke_t *this); +}; + + +/** + * @brief Create the stroke interface and listen on the socket. + * + * @return stroke_t object + * + * @ingroup threads + */ +stroke_t *stroke_create(void); + +#endif /* STROKE_INTERFACE_H_ */ diff --git a/src/charon/threads/thread_pool.c b/src/charon/threads/thread_pool.c new file mode 100644 index 000000000..052b5aab9 --- /dev/null +++ b/src/charon/threads/thread_pool.c @@ -0,0 +1,181 @@ +/** + * @file thread_pool.c + * + * @brief Implementation of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <stdlib.h> +#include <pthread.h> +#include <string.h> +#include <errno.h> + +#include "thread_pool.h" + +#include <daemon.h> +#include <queues/job_queue.h> + + +typedef struct private_thread_pool_t private_thread_pool_t; + +/** + * @brief Private data of thread_pool_t class. + */ +struct private_thread_pool_t { + /** + * Public thread_pool_t interface. + */ + thread_pool_t public; + + /** + * Number of running threads. + */ + u_int pool_size; + + /** + * Number of threads waiting for work + */ + u_int idle_threads; + + /** + * Array of thread ids. + */ + pthread_t *threads; +} ; + +/** + * Implementation of private_thread_pool_t.process_jobs. + */ +static void process_jobs(private_thread_pool_t *this) +{ + job_t *job; + status_t status; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + DBG1(DBG_JOB, "worker thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + /* TODO: should be atomic, but is not mission critical */ + this->idle_threads++; + job = charon->job_queue->get(charon->job_queue); + this->idle_threads--; + + status = job->execute(job); + + if (status == DESTROY_ME) + { + job->destroy(job); + } + } +} + +/** + * Implementation of thread_pool_t.get_pool_size. + */ +static u_int get_pool_size(private_thread_pool_t *this) +{ + return this->pool_size; +} + +/** + * Implementation of thread_pool_t.get_idle_threads. + */ +static u_int get_idle_threads(private_thread_pool_t *this) +{ + return this->idle_threads; +} + +/** + * Implementation of thread_pool_t.destroy. + */ +static void destroy(private_thread_pool_t *this) +{ + int current; + /* flag thread for termination */ + for (current = 0; current < this->pool_size; current++) + { + DBG1(DBG_JOB, "cancelling worker thread #%d", current+1); + pthread_cancel(this->threads[current]); + } + + /* wait for all threads */ + for (current = 0; current < this->pool_size; current++) { + if (pthread_join(this->threads[current], NULL) == 0) + { + DBG1(DBG_JOB, "worker thread #%d terminated", current+1); + } + else + { + DBG1(DBG_JOB, "could not terminate worker thread #%d", current+1); + } + } + + /* free mem */ + free(this->threads); + free(this); +} + +/* + * Described in header. + */ +thread_pool_t *thread_pool_create(size_t pool_size) +{ + int current; + private_thread_pool_t *this = malloc_thing(private_thread_pool_t); + + /* fill in public fields */ + this->public.destroy = (void(*)(thread_pool_t*))destroy; + this->public.get_pool_size = (u_int(*)(thread_pool_t*))get_pool_size; + this->public.get_idle_threads = (u_int(*)(thread_pool_t*))get_idle_threads; + + /* initialize member */ + this->pool_size = pool_size; + this->idle_threads = 0; + this->threads = malloc(sizeof(pthread_t) * pool_size); + + /* try to create as many threads as possible, up to pool_size */ + for (current = 0; current < pool_size; current++) + { + if (pthread_create(&(this->threads[current]), NULL, + (void*(*)(void*))process_jobs, this) == 0) + { + DBG1(DBG_JOB, "created worker thread #%d", current+1); + } + else + { + /* creation failed, is it the first one? */ + if (current == 0) + { + free(this->threads); + free(this); + charon->kill(charon, "could not create any worker threads"); + } + /* not all threads could be created, but at least one :-/ */ + DBG1(DBG_JOB, "could only create %d from requested %d threads!", + current, pool_size); + this->pool_size = current; + break; + } + } + return (thread_pool_t*)this; +} diff --git a/src/charon/threads/thread_pool.h b/src/charon/threads/thread_pool.h new file mode 100644 index 000000000..8e1989bda --- /dev/null +++ b/src/charon/threads/thread_pool.h @@ -0,0 +1,87 @@ +/** + * @file thread_pool.h + * + * @brief Interface of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#ifndef THREAD_POOL_H_ +#define THREAD_POOL_H_ + +typedef struct thread_pool_t thread_pool_t; + +#include <stdlib.h> + +#include <library.h> + +/** + * @brief A thread_pool consists of a pool of threads processing jobs from the job queue. + * + * Current implementation uses as many threads as specified in constructor. + * A more improved version would dynamically increase thread count if necessary. + * + * @b Constructors: + * - thread_pool_create() + * + * @todo Add support for dynamic thread handling + * + * @ingroup threads + */ +struct thread_pool_t { + + /** + * @brief Return currently instanciated thread count. + * + * @param thread_pool calling object + * @return size of thread pool + */ + u_int (*get_pool_size) (thread_pool_t *thread_pool); + + /** + * @brief Get the number of threads currently waiting for work. + * + * @param thread_pool calling object + * @return number of idle threads + */ + u_int (*get_idle_threads) (thread_pool_t *thread_pool); + + /** + * @brief Destroy a thread_pool_t object. + * + * Sends cancellation request to all threads and AWAITS their termination. + * + * @param thread_pool calling object + */ + void (*destroy) (thread_pool_t *thread_pool); +}; + +/** + * @brief Create the thread pool using using pool_size of threads. + * + * @param pool_size desired pool size + * @return + * - thread_pool_t object if one ore more threads could be started, or + * - NULL if no threads could be created + * + * @ingroup threads + */ +thread_pool_t *thread_pool_create(size_t pool_size); + + +#endif /*THREAD_POOL_H_*/ |