diff --git a/HACKING b/HACKING index 830b68841..dbb347e6f 100644 --- a/HACKING +++ b/HACKING @@ -18,6 +18,7 @@ the code, you need the following tools: - lex/flex - yacc/bison - gperf + - check - optionally Doxygen To check out the master branch, use: diff --git a/NEWS b/NEWS index b4bc162d0..10c8353b8 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,14 @@ strongswan-5.0.3 any authentication. Therefore, to use this backend it has to be selected explicitly with rightauth2=xauth-noauth. +- The new charon-tkm IKEv2 daemon delegates security critical operations to a + separate process. This has the benefit that the network facing daemon has no + knowledge of keying material used to protect child SAs. Thus subverting + charon-tkm does not result in the compromise of cryptographic keys. + The extracted functionality has been implemented from scratch in a minimal TCB + (trusted computing base) in the Ada programming language. Further information + can be found at http://www.codelabs.ch/tkm/. + strongswan-5.0.2 ---------------- diff --git a/configure.in b/configure.in index 716bad5b1..82f90dab8 100644 --- a/configure.in +++ b/configure.in @@ -236,6 +236,8 @@ ARG_ENABL_SET([radattr], [enable plugin to inject and process custom RADI ARG_ENABL_SET([vstr], [enforce using the Vstr string library to replace glibc-like printf hooks.]) ARG_ENABL_SET([monolithic], [build monolithic version of libstrongswan that includes all enabled plugins. Similarly, the plugins of charon are assembled in libcharon.]) ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.]) +ARG_ENABL_SET([unit-tests], [enable unit tests using the check test framework.]) +ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.]) # =================================== # option to disable default options @@ -258,6 +260,8 @@ if test -z "$CFLAGS"; then CFLAGS="-g -O2 -Wall -Wno-format -Wno-pointer-sign" fi AC_PROG_CC +AM_PROG_CC_C_O + AC_LIB_PREFIX AC_C_BIGENDIAN @@ -894,6 +898,16 @@ AC_SUBST(dev_headers) CFLAGS="$CFLAGS -include `pwd`/config.h" +if test x$tkm = xtrue; then + AC_PATH_PROG([GPRBUILD], [gprbuild], [], [$PATH:/bin:/usr/bin:/usr/local/bin]) +fi + +if test x$unit_tests = xtrue; then + PKG_CHECK_MODULES(CHECK, [check >= 0.9.4]) + AC_SUBST(CHECK_CFLAGS) + AC_SUBST(CHECK_LIBS) +fi + # =============================================== # collect plugin list for strongSwan components # =============================================== @@ -1190,9 +1204,9 @@ AM_CONDITIONAL(USE_NM, test x$nm = xtrue) AM_CONDITIONAL(USE_TOOLS, test x$tools = xtrue) AM_CONDITIONAL(USE_SCRIPTS, test x$scripts = xtrue) AM_CONDITIONAL(USE_CONFTEST, test x$conftest = xtrue) -AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$tools = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue) -AM_CONDITIONAL(USE_LIBHYDRA, test x$charon = xtrue -o x$nm = xtrue) -AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue) +AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$tools = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue) +AM_CONDITIONAL(USE_LIBHYDRA, test x$charon = xtrue -o x$nm = xtrue -o x$tkm = xtrue) +AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue -o x$tkm = xtrue) AM_CONDITIONAL(USE_LIBIPSEC, test x$libipsec = xtrue) AM_CONDITIONAL(USE_LIBTNCIF, test x$tnc_tnccs = xtrue -o x$imcv = xtrue) AM_CONDITIONAL(USE_LIBTNCCS, test x$tnc_tnccs = xtrue) @@ -1208,6 +1222,8 @@ AM_CONDITIONAL(USE_IMCV, test x$imcv = xtrue) AM_CONDITIONAL(USE_PTS, test x$pts = xtrue) AM_CONDITIONAL(USE_TROUSERS, test x$tss = xtrousers) AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue) +AM_CONDITIONAL(UNITTESTS, test x$unit_tests = xtrue) +AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue) # ======================== # set global definitions @@ -1309,6 +1325,7 @@ AC_CONFIG_FILES([ src/libimcv/plugins/imv_os/Makefile src/charon/Makefile src/charon-nm/Makefile + src/charon-tkm/Makefile src/libcharon/Makefile src/libcharon/plugins/eap_aka/Makefile src/libcharon/plugins/eap_aka_3gpp2/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index e71f73db3..07953b0b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -100,6 +100,10 @@ if USE_INTEGRITY_TEST SUBDIRS += checksum endif +if USE_TKM + SUBDIRS += charon-tkm +endif + EXTRA_DIST = strongswan.conf install-exec-local : diff --git a/src/charon-tkm/.gitignore b/src/charon-tkm/.gitignore new file mode 100644 index 000000000..b672fdeaf --- /dev/null +++ b/src/charon-tkm/.gitignore @@ -0,0 +1 @@ +obj diff --git a/src/charon-tkm/Makefile.am b/src/charon-tkm/Makefile.am new file mode 100644 index 000000000..457e5e44e --- /dev/null +++ b/src/charon-tkm/Makefile.am @@ -0,0 +1,54 @@ +SRC = $(top_builddir)/src + +# includes relative to obj directory +INCLUDES = \ + -include $(top_builddir)/config.h \ + -I../$(SRC)/libstrongswan \ + -I../$(SRC)/libhydra \ + -I../$(SRC)/libcharon + +LIBLD = \ + -L$(SRC)/libstrongswan/.libs \ + -L$(SRC)/libhydra/.libs \ + -L$(SRC)/libcharon/.libs +LIBPT = $(SRC)/libstrongswan/.libs:$(SRC)/libhydra/.libs:$(SRC)/libcharon/.libs +LIBFL = -lstrongswan -lhydra -lcharon + +DEFS += -DPLUGINS=\""$(PLUGINS)\"" -DIPSEC_PIDDIR=\"${piddir}\" + +BUILD_OPTS = \ + -XOBJ_DIR=$(CURDIR)/obj \ + -cargs $(INCLUDES) $(DEFS) \ + -largs $(LIBLD) $(LIBFL) + +# plugins to enable +PLUGINS = \ + kernel-netlink \ + pem \ + socket-default \ + openssl \ + stroke + +all: build_charon + +build_charon: build_charon.gpr src/charon-tkm.c + @$(GPRBUILD) -p $< $(BUILD_OPTS) + +build_tests: build_tests.gpr + @$(GPRBUILD) -p $< $(BUILD_OPTS) -cargs @CHECK_CFLAGS@ -largs @CHECK_LIBS@ + +if UNITTESTS +check: build_tests + @LD_LIBRARY_PATH=$(LIBPT) obj/test_runner +else +check: + @echo "reconfigure with --enable-unit-tests" +endif + +install: build_charon + $(INSTALL) -m 755 obj/charon-tkm $(DESTDIR)$(ipsecdir) + +clean: + rm -rf obj + +EXTRA_DIST = build_charon.gpr build_common.gpr build_tests.gpr src tests diff --git a/src/charon-tkm/build_charon.gpr b/src/charon-tkm/build_charon.gpr new file mode 100644 index 000000000..b208667a3 --- /dev/null +++ b/src/charon-tkm/build_charon.gpr @@ -0,0 +1,20 @@ +with "build_common"; + +project Build_Charon is + + for Languages use ("Ada", "C"); + for Source_Dirs use ("src/**"); + for Main use ("charon-tkm"); + for Object_Dir use Build_Common.Obj_Dir; + + package Compiler is + for Default_Switches ("ada") use Build_Common.Ada_Compiler_Switches; + for Default_Switches ("c") use Build_Common.C_Compiler_Switches + & "-Werror"; + end Compiler; + + package Binder is + for Default_Switches ("ada") use Build_Common.Ada_Binder_Switches; + end Binder; + +end Build_Charon; diff --git a/src/charon-tkm/build_common.gpr b/src/charon-tkm/build_common.gpr new file mode 100644 index 000000000..ac322d713 --- /dev/null +++ b/src/charon-tkm/build_common.gpr @@ -0,0 +1,25 @@ +with "tkmrpc_client"; +with "tkmrpc_server-ees"; + +project Build_Common is + + for Source_Dirs use (); + + Obj_Dir := "obj"; + + C_Compiler_Switches := ("-W", + "-Wall", + "-Wno-unused-parameter"); + Ada_Compiler_Switches := ("-gnatwale", + "-gnatygAdISuxo", + "-gnata", + "-gnatVa", + "-gnat05", + "-gnatf", + "-fstack-check", + "-gnato", + "-g"); + + Ada_Binder_Switches := ("-E"); + +end Build_Common; diff --git a/src/charon-tkm/build_tests.gpr b/src/charon-tkm/build_tests.gpr new file mode 100644 index 000000000..032c7969e --- /dev/null +++ b/src/charon-tkm/build_tests.gpr @@ -0,0 +1,14 @@ +with "build_common"; + +project Build_Tests is + + for Languages use ("Ada", "C"); + for Source_Dirs use ("src/ees", "src/ehandler", "src/tkm", "tests"); + for Main use ("test_runner"); + for Object_Dir use Build_Common.Obj_Dir; + + package Compiler is + for Default_Switches ("c") use Build_Common.C_Compiler_Switches; + end Compiler; + +end Build_Tests; diff --git a/src/charon-tkm/src/charon-tkm.c b/src/charon-tkm/src/charon-tkm.c new file mode 100644 index 000000000..0b39058a6 --- /dev/null +++ b/src/charon-tkm/src/charon-tkm.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tkm.h" +#include "tkm_nonceg.h" +#include "tkm_diffie_hellman.h" +#include "tkm_keymat.h" +#include "tkm_listener.h" +#include "tkm_kernel_ipsec.h" +#include "tkm_public_key.h" +#include "tkm_cred.h" +#include "tkm_encoder.h" + +/** + * TKM bus listener for IKE authorize events. + */ +static tkm_listener_t *listener; + +/** + * PID file, in which charon-tkm stores its process id + */ +static char *pidfile_name = NULL; + +/** + * Global reference to PID file (required to truncate, if undeletable) + */ +static FILE *pidfile = NULL; + +/** + * Hook in library for debugging messages + */ +extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); + +/** + * Simple logging hook for library logs, using syslog output + */ +static void dbg_syslog(debug_t group, level_t level, char *fmt, ...) +{ + if (level <= 1) + { + char buffer[8192]; + va_list args; + + va_start(args, fmt); + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), fmt, args); + syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", debug_names->names[group], + buffer); + va_end(args); + } +} + +/** + * Run the daemon and handle unix signals + */ +static void run() +{ + sigset_t set; + + /* handle SIGINT and SIGTERM in this handler */ + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, NULL); + + while (TRUE) + { + int sig; + int error; + + error = sigwait(&set, &sig); + if (error) + { + DBG1(DBG_DMN, "error %d while waiting for a signal", error); + return; + } + switch (sig) + { + case SIGINT: + { + DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down"); + charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig); + return; + } + case SIGTERM: + { + DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down"); + charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig); + return; + } + default: + { + DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig); + break; + } + } + } +} + +/** + * Handle SIGSEGV/SIGILL signals raised by threads + */ +static void segv_handler(int signal) +{ + backtrace_t *backtrace; + + DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal); + backtrace = backtrace_create(2); + backtrace->log(backtrace, stderr, TRUE); + backtrace->destroy(backtrace); + + DBG1(DBG_DMN, "killing ourself, received critical signal"); + abort(); +} + +/** + * Lookup UID and GID + */ +static bool lookup_uid_gid() +{ +#ifdef IPSEC_USER + if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER)) + { + return FALSE; + } +#endif +#ifdef IPSEC_GROUP + if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP)) + { + return FALSE; + } +#endif + return TRUE; +} + +/** + * Check/create PID file, return TRUE if already running + */ +static bool check_pidfile() +{ + struct stat stb; + + if (stat(pidfile_name, &stb) == 0) + { + pidfile = fopen(pidfile_name, "r"); + if (pidfile) + { + char buf[64]; + pid_t pid = 0; + + memset(buf, 0, sizeof(buf)); + if (fread(buf, 1, sizeof(buf), pidfile)) + { + buf[sizeof(buf) - 1] = '\0'; + pid = atoi(buf); + } + fclose(pidfile); + if (pid && kill(pid, 0) == 0) + { /* such a process is running */ + return TRUE; + } + } + DBG1(DBG_DMN, "removing pidfile '%s', process not running", pidfile_name); + unlink(pidfile_name); + } + + /* create new pidfile */ + pidfile = fopen(pidfile_name, "w"); + if (pidfile) + { + ignore_result(fchown(fileno(pidfile), + charon->caps->get_uid(charon->caps), + charon->caps->get_gid(charon->caps))); + fprintf(pidfile, "%d\n", getpid()); + fflush(pidfile); + } + return FALSE; +} + +/** + * Delete/truncate the PID file + */ +static void unlink_pidfile() +{ + /* because unlinking the PID file may fail, we truncate it to ensure the + * daemon can be properly restarted. one probable cause for this is the + * combination of not running as root and the effective user lacking + * permissions on the parent dir(s) of the PID file */ + if (pidfile) + { + ignore_result(ftruncate(fileno(pidfile), 0)); + fclose(pidfile); + } + unlink(pidfile_name); +} +/** + * Main function, starts TKM backend. + */ +int main(int argc, char *argv[]) +{ + char *dmn_name; + if (argc > 0 && strlen(argv[0]) > 0) + { + dmn_name = basename(argv[0]); + } + else + { + dmn_name = "charon-tkm"; + } + + /* TKM credential set */ + tkm_cred_t *creds; + + struct sigaction action; + int status = SS_RC_INITIALIZATION_FAILED; + + /* logging for library during initialization, as we have no bus yet */ + dbg = dbg_syslog; + + /* initialize library */ + if (!library_init(NULL)) + { + library_deinit(); + exit(status); + } + + if (!libhydra_init(dmn_name)) + { + dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name); + libhydra_deinit(); + library_deinit(); + exit(status); + } + + if (!libcharon_init(dmn_name)) + { + dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name); + goto deinit; + } + + if (!lookup_uid_gid()) + { + dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting %s", dmn_name); + goto deinit; + } + + /* make sure we log to the DAEMON facility by default */ + lib->settings->set_int(lib->settings, "%s.syslog.daemon.default", + lib->settings->get_int(lib->settings, "%s.syslog.daemon.default", 1, + dmn_name), dmn_name); + charon->load_loggers(charon, NULL, FALSE); + + DBG1(DBG_DMN, "Starting charon with TKM backend (strongSwan "VERSION")"); + + /* register TKM specific plugins */ + static plugin_feature_t features[] = { + PLUGIN_REGISTER(NONCE_GEN, tkm_nonceg_create), + PLUGIN_PROVIDE(NONCE_GEN), + PLUGIN_REGISTER(DH, tkm_diffie_hellman_create), + PLUGIN_PROVIDE(DH, MODP_2048_BIT), + PLUGIN_PROVIDE(DH, MODP_3072_BIT), + PLUGIN_PROVIDE(DH, MODP_4096_BIT), + PLUGIN_REGISTER(PUBKEY, tkm_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_RSA), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA256), + PLUGIN_CALLBACK(kernel_ipsec_register, tkm_kernel_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + }; + lib->plugins->add_static_features(lib->plugins, "tkm-backend", features, + countof(features), TRUE); + + /* register TKM keymat variant */ + keymat_register_constructor(IKEV2, (keymat_constructor_t)tkm_keymat_create); + + /* initialize daemon */ + if (!charon->initialize(charon, PLUGINS)) + { + DBG1(DBG_DMN, "initialization failed - aborting %s", dmn_name); + goto deinit; + } + + /* set global pidfile name depending on daemon name */ + if (asprintf(&pidfile_name, IPSEC_PIDDIR"/%s.pid", dmn_name) < 0) + { + DBG1(DBG_DMN, "unable to set pidfile name - aborting %s", dmn_name); + goto deinit; + }; + + if (check_pidfile()) + { + DBG1(DBG_DMN, "%s already running (\"%s\" exists)", dmn_name, + pidfile_name); + goto deinit; + } + + if (!charon->caps->drop(charon->caps)) + { + DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name); + goto deinit; + } + + /* initialize TKM client */ + if (!tkm_init()) + { + DBG1(DBG_DMN, "init of TKM client failed - aborting %s", dmn_name); + goto deinit; + } + + /* register TKM authorization hook */ + listener = tkm_listener_create(); + charon->bus->add_listener(charon->bus, &listener->listener); + + /* register TKM credential set */ + creds = tkm_cred_create(); + lib->credmgr->add_set(lib->credmgr, (credential_set_t*)creds); + + /* register TKM credential encoder */ + lib->encoding->add_encoder(lib->encoding, tkm_encoder_encode); + + /* add handler for SEGV and ILL, + * INT and TERM are handled by sigwait() in run() */ + action.sa_handler = segv_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + sigaddset(&action.sa_mask, SIGTERM); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGILL, &action, NULL); + sigaction(SIGBUS, &action, NULL); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + + pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL); + + /* start daemon (i.e. the threads in the thread-pool) */ + charon->start(charon); + + /* main thread goes to run loop */ + run(); + + unlink_pidfile(); + status = 0; + charon->bus->remove_listener(charon->bus, &listener->listener); + listener->destroy(listener); + creds->destroy(creds); + lib->encoding->remove_encoder(lib->encoding, tkm_encoder_encode); + +deinit: + libcharon_deinit(); + libhydra_deinit(); + library_deinit(); + tkm_deinit(); + return status; +} diff --git a/src/charon-tkm/src/ees/ees_callbacks.c b/src/charon-tkm/src/ees/ees_callbacks.c new file mode 100644 index 000000000..2d9653837 --- /dev/null +++ b/src/charon-tkm/src/ees/ees_callbacks.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include + +#include "ees_callbacks.h" + +void charon_esa_acquire(result_type *res, const sp_id_type sp_id) +{ + DBG1(DBG_KNL, "ees: acquire received for reqid {%d}", sp_id); + hydra->kernel_interface->acquire(hydra->kernel_interface, sp_id, NULL, + NULL); + *res = TKM_OK; +} + +void charon_esa_expire(result_type *res, const sp_id_type sp_id, + const esp_spi_type spi_rem, const protocol_type protocol, + const expiry_flag_type hard) +{ + DBG1(DBG_KNL, "ees: expire received for reqid {%d}", sp_id); + hydra->kernel_interface->expire(hydra->kernel_interface, sp_id, protocol, + spi_rem, hard != 0); + *res = TKM_OK; +} diff --git a/src/charon-tkm/src/ees/ees_callbacks.h b/src/charon-tkm/src/ees/ees_callbacks.h new file mode 100644 index 000000000..b73dc6cb5 --- /dev/null +++ b/src/charon-tkm/src/ees/ees_callbacks.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-eescallbacks ees callbacks + * @{ @ingroup tkm + * + * ESP SA Event Service (EES) callbacks. + * The xfrm-proxy forwards acquire and expire events from the kernel to + * charon-tkm using the EES interface. Upon reception of an event the + * corresponding callback is executed. + */ + +#ifndef EES_CALLBACKS_H_ +#define EES_CALLBACKS_H_ + +/** + * Process Acquire event for given security policy. + */ +void charon_esa_acquire(result_type *res, const sp_id_type sp_id); + +/** + * Process Expire event for given security policy. + */ +void charon_esa_expire(result_type *res, const sp_id_type sp_id, + const esp_spi_type spi_rem, const protocol_type protocol, + const expiry_flag_type hard); + +#endif /** EES_CALLBACKS_H_ @}*/ diff --git a/src/charon-tkm/src/ees/esa_event_service.adb b/src/charon-tkm/src/ees/esa_event_service.adb new file mode 100644 index 000000000..5b5d7003b --- /dev/null +++ b/src/charon-tkm/src/ees/esa_event_service.adb @@ -0,0 +1,57 @@ +-- +-- Copyright (C) 2012 Reto Buerki +-- Copyright (C) 2012 Adrian-Ken Rueegsegger +-- 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 . +-- +-- 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. +-- + +with Anet.Sockets.Unix; +with Anet.Receivers.Stream; + +with Tkmrpc.Dispatchers.Ees; +with Tkmrpc.Process_Stream; + +pragma Elaborate_All (Anet.Receivers.Stream); +pragma Elaborate_All (Tkmrpc.Process_Stream); + +package body Esa_Event_Service +is + + package Unix_TCP_Receiver is new Anet.Receivers.Stream + (Socket_Type => Anet.Sockets.Unix.TCP_Socket_Type); + + procedure Dispatch is new Tkmrpc.Process_Stream + (Dispatch => Tkmrpc.Dispatchers.Ees.Dispatch); + + Sock : aliased Anet.Sockets.Unix.TCP_Socket_Type; + Receiver : Unix_TCP_Receiver.Receiver_Type (S => Sock'Access); + + ------------------------------------------------------------------------- + + procedure Finalize + is + begin + Receiver.Stop; + end Finalize; + + ------------------------------------------------------------------------- + + procedure Init (Address : Interfaces.C.Strings.chars_ptr) + is + Path : constant String := Interfaces.C.Strings.Value (Address); + begin + Sock.Init; + Sock.Bind (Path => Anet.Sockets.Unix.Path_Type (Path)); + Receiver.Listen (Callback => Dispatch'Access); + end Init; + +end Esa_Event_Service; diff --git a/src/charon-tkm/src/ees/esa_event_service.ads b/src/charon-tkm/src/ees/esa_event_service.ads new file mode 100644 index 000000000..f3630b7ac --- /dev/null +++ b/src/charon-tkm/src/ees/esa_event_service.ads @@ -0,0 +1,30 @@ +-- +-- Copyright (C) 2012 Reto Buerki +-- Copyright (C) 2012 Adrian-Ken Rueegsegger +-- 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 . +-- +-- 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. +-- + +with Interfaces.C.Strings; + +package Esa_Event_Service +is + + procedure Init (Address : Interfaces.C.Strings.chars_ptr); + pragma Export (C, Init, "ees_server_init"); + -- Initialize Esa Event Service (EES) with given address. + + procedure Finalize; + pragma Export (C, Finalize, "ees_server_finalize"); + -- Finalize EES. + +end Esa_Event_Service; diff --git a/src/charon-tkm/src/ees/tkmrpc-servers-ees.adb b/src/charon-tkm/src/ees/tkmrpc-servers-ees.adb new file mode 100644 index 000000000..2240065c2 --- /dev/null +++ b/src/charon-tkm/src/ees/tkmrpc-servers-ees.adb @@ -0,0 +1,65 @@ +package body Tkmrpc.Servers.Ees +is + + -------------------------------- + -- charon callback signatures -- + -------------------------------- + + procedure Charon_Esa_Acquire + (Result : out Results.Result_Type; + Sp_Id : Types.Sp_Id_Type); + pragma Import (C, Charon_Esa_Acquire, "charon_esa_acquire"); + + procedure Charon_Esa_Expire + (Result : out Results.Result_Type; + Sp_Id : Types.Sp_Id_Type; + Spi_Rem : Types.Esp_Spi_Type; + Protocol : Types.Protocol_Type; + Hard : Types.Expiry_Flag_Type); + pragma Import (C, Charon_Esa_Expire, "charon_esa_expire"); + + ------------------------------------------------------------------------- + + procedure Esa_Acquire + (Result : out Results.Result_Type; + Sp_Id : Types.Sp_Id_Type) + is + begin + Charon_Esa_Acquire (Result => Result, + Sp_Id => Sp_Id); + end Esa_Acquire; + + ------------------------------------------------------------------------- + + procedure Esa_Expire + (Result : out Results.Result_Type; + Sp_Id : Types.Sp_Id_Type; + Spi_Rem : Types.Esp_Spi_Type; + Protocol : Types.Protocol_Type; + Hard : Types.Expiry_Flag_Type) + is + begin + Charon_Esa_Expire (Result => Result, + Sp_Id => Sp_Id, + Spi_Rem => Spi_Rem, + Protocol => Protocol, + Hard => Hard); + end Esa_Expire; + + ------------------------------------------------------------------------- + + procedure Finalize + is + begin + null; + end Finalize; + + ------------------------------------------------------------------------- + + procedure Init + is + begin + null; + end Init; + +end Tkmrpc.Servers.Ees; diff --git a/src/charon-tkm/src/ehandler/eh_callbacks.c b/src/charon-tkm/src/ehandler/eh_callbacks.c new file mode 100644 index 000000000..7dca97c3e --- /dev/null +++ b/src/charon-tkm/src/ehandler/eh_callbacks.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "eh_callbacks.h" + +void charon_terminate(char *msg) +{ + DBG1(DBG_DMN, "critical TKM error, terminating!"); + DBG1(DBG_DMN, msg); + kill(0, SIGTERM); +} diff --git a/src/charon-tkm/src/ehandler/eh_callbacks.h b/src/charon-tkm/src/ehandler/eh_callbacks.h new file mode 100644 index 000000000..db325dcd2 --- /dev/null +++ b/src/charon-tkm/src/ehandler/eh_callbacks.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-ehandler exception handler + * @{ @ingroup tkm + * + * The exception handler callback is registered as global exception action in + * the Ada runtime. If an exception is raised in Ada code this callback is + * executed. + */ + +#ifndef EH_CALLBACKS_H_ +#define EH_CALLBACKS_H_ + +/** + * Log given message and terminate charon. + */ +void charon_terminate(char *msg); + +#endif /** EH_CALLBACKS_H_ @}*/ diff --git a/src/charon-tkm/src/ehandler/exception_handler.adb b/src/charon-tkm/src/ehandler/exception_handler.adb new file mode 100644 index 000000000..3f165e1cd --- /dev/null +++ b/src/charon-tkm/src/ehandler/exception_handler.adb @@ -0,0 +1,57 @@ +-- +-- Copyright (C) 2012 Reto Buerki +-- Copyright (C) 2012 Adrian-Ken Rueegsegger +-- 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 . +-- +-- 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. +-- + +with Ada.Exceptions; + +with GNAT.Exception_Actions; + +with Interfaces.C.Strings; + +package body Exception_Handler +is + + procedure Charon_Terminate (Message : Interfaces.C.Strings.chars_ptr); + pragma Import (C, Charon_Terminate, "charon_terminate"); + + procedure Bailout (Ex : Ada.Exceptions.Exception_Occurrence); + -- Signal critical condition to charon daemon. + + ------------------------------------------------------------------------- + + procedure Bailout (Ex : Ada.Exceptions.Exception_Occurrence) + is + begin + if Ada.Exceptions.Exception_Name (Ex) = "_ABORT_SIGNAL" then + + -- Ignore runtime-internal abort signal exception. + + return; + end if; + + Charon_Terminate (Message => Interfaces.C.Strings.New_String + (Ada.Exceptions.Exception_Information (Ex))); + end Bailout; + + ------------------------------------------------------------------------- + + procedure Init + is + begin + GNAT.Exception_Actions.Register_Global_Action + (Action => Bailout'Access); + end Init; + +end Exception_Handler; diff --git a/src/charon-tkm/src/ehandler/exception_handler.ads b/src/charon-tkm/src/ehandler/exception_handler.ads new file mode 100644 index 000000000..29dd3d8f4 --- /dev/null +++ b/src/charon-tkm/src/ehandler/exception_handler.ads @@ -0,0 +1,24 @@ +-- +-- Copyright (C) 2012 Reto Buerki +-- Copyright (C) 2012 Adrian-Ken Rueegsegger +-- 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 . +-- +-- 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. +-- + +package Exception_Handler +is + + procedure Init; + pragma Export (C, Init, "ehandler_init"); + -- Register last-chance exception handler. + +end Exception_Handler; diff --git a/src/charon-tkm/src/tkm/.gitignore b/src/charon-tkm/src/tkm/.gitignore new file mode 100644 index 000000000..b672fdeaf --- /dev/null +++ b/src/charon-tkm/src/tkm/.gitignore @@ -0,0 +1 @@ +obj diff --git a/src/charon-tkm/src/tkm/tkm.c b/src/charon-tkm/src/tkm/tkm.c new file mode 100644 index 000000000..a39221dc2 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include +#include + +#include "tkm.h" + +#define IKE_SOCKET "/tmp/tkm.rpc.ike" +#define EES_SOCKET "/tmp/tkm.rpc.ees" + +typedef struct private_tkm_t private_tkm_t; + +extern result_type ees_server_init(const char * const address); +extern void ees_server_finalize(void); +extern void ehandler_init(void); + +/* + * Private additions to tkm_t. + */ +struct private_tkm_t { + + /** + * Public members of tkm_t. + */ + tkm_t public; +}; + +/** + * Single instance of tkm_t. + */ +tkm_t *tkm = NULL; + +/** + * Described in header. + */ +bool tkm_init() +{ + private_tkm_t *this; + active_requests_type max_requests; + char *ikesock, *eessock; + tkm_limits_t limits; + + /* initialize TKM client library */ + tkmlib_init(); + ehandler_init(); + + ikesock = lib->settings->get_str(lib->settings, "%s.ike_socket", IKE_SOCKET, + charon->name); + if (ike_init(ikesock) != TKM_OK) + { + tkmlib_final(); + return FALSE; + } + DBG1(DBG_DMN, "connected to TKM via socket '%s'", ikesock); + + eessock = lib->settings->get_str(lib->settings, "%s.ees_socket", EES_SOCKET, + charon->name); + ees_server_init(eessock); + DBG1(DBG_DMN, "serving EES requests on socket '%s'", eessock); + + if (ike_tkm_reset() != TKM_OK) + { + ees_server_finalize(); + tkmlib_final(); + return FALSE; + } + + /* get limits from tkm */ + if (ike_tkm_limits(&max_requests, &limits[TKM_CTX_NONCE], &limits[TKM_CTX_DH], + &limits[TKM_CTX_CC], &limits[TKM_CTX_AE], + &limits[TKM_CTX_ISA], &limits[TKM_CTX_ESA]) != TKM_OK) + { + ees_server_finalize(); + tkmlib_final(); + return FALSE; + } + + INIT(this, + .public = { + .idmgr = tkm_id_manager_create(limits), + .chunk_map = tkm_chunk_map_create(), + }, + ); + tkm = &this->public; + + return TRUE; +} + +/** + * Described in header. + */ +void tkm_deinit() +{ + if (!tkm) + { + return; + } + private_tkm_t *this = (private_tkm_t*)tkm; + this->public.idmgr->destroy(this->public.idmgr); + this->public.chunk_map->destroy(this->public.chunk_map); + + ees_server_finalize(); + + tkmlib_final(); + free(this); + tkm = NULL; +} diff --git a/src/charon-tkm/src/tkm/tkm.h b/src/charon-tkm/src/tkm/tkm.h new file mode 100644 index 000000000..7c711312b --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm tkm + * + * @addtogroup tkm + * @{ + * + * Untrusted IKEv2 component used with Trusted Key Manager for IKE + * disaggregation. + * + * The untrusted IKEv2 component used in conjunction with the Trusted Key + * Manager infrastructure is implemented as a separate charon instance located + * in its own directory below the strongSwan top-level source directory + * (src/charon-tkm). This has the advantage that the TKM code is contained and + * does not mix with other strongSwan files. The charon-tkm binary startup code + * is modeled after the charon-nm instance, a special charon daemon variant to + * be used with the GNOME NetworkManager project. The major difference is the + * registration of custom TKM plugins as the final step of the startup phase. + * The charon-tkm daemon does not rely on the dynamic plugin loading mechanism + * for its core plugins, they are statically registered before entering the main + * processing loop. + * + * The following diagram shows the main components of the system and how they + * communicate. + @verbatim + + +------------+ +------------+ +------------+ + | xfrm-proxy |<-[tkm-rpc->| charon-tkm |<-[tkm-rpc]->| TKM | + +------------+ +------------+ +------------+ + ^ ^ + [Netlink | XFRM] [XFRM | Netlink] + | v + +-----------------------------------------------------------------+ + | Kernel | + +-----------------------------------------------------------------+ + + @endverbatim + * Since the charon-tkm code uses the tkm-rpc library written in Ada, the daemon + * has to be built using an Ada-aware toolchain. The integration of Ada code + * into the strongSwan codebase is explained in the TKM documentation, section + * 5.4.1: http://www.codelabs.ch/tkm#anchor-doc. + * + * The Trusted Key Manager (TKM) is a minimal Trusted Computing Base which + * implements security-critical functions of the IKEv2 protocol. + * + * The xfrm-proxy receives XFRM Acquire and Expiry events from the kernel and + * forwards them to the charon-tkm IKE daemon for further processing. + * + * The underlying concept of IKE disaggregation and the design of TKM and all + * related components, of which charon-tkm is one component, is presented in + * detail in the project documentation found at + * http://www.codelabs.ch/tkm#anchor-doc. + */ + +#ifndef TKM_H_ +#define TKM_H_ + +#include "tkm_id_manager.h" +#include "tkm_chunk_map.h" + +typedef struct tkm_t tkm_t; + +/** + * Trusted key manager context, contains tkm related globals. + */ +struct tkm_t { + + /** + * Context ID manager. + */ + tkm_id_manager_t *idmgr; + + /** + * Chunk-to-ID mappings. + */ + tkm_chunk_map_t *chunk_map; + +}; + +/** + * Initialize trusted key manager, creates "tkm" instance. + * + * @return FALSE if initialization error occured + */ +bool tkm_init(); + +/** + * Deinitialize trusted key manager, destroys "tkm" instance. + */ +void tkm_deinit(); + +/** + * Trusted key manager instance, set after tkm_init() and before tkm_deinit() + * calls. + */ +extern tkm_t *tkm; + +#endif /** TKM_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_chunk_map.c b/src/charon-tkm/src/tkm/tkm_chunk_map.c new file mode 100644 index 000000000..03ff22836 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_chunk_map.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include + +#include "tkm_chunk_map.h" + +typedef struct private_tkm_chunk_map_t private_tkm_chunk_map_t; + +/** + * Private data of tkm chunk map. + */ +struct private_tkm_chunk_map_t { + + /** + * public functions + */ + tkm_chunk_map_t public; + + /** + * Hashtable to store mappings. + */ + hashtable_t *mappings; + + /** + * rwlock for table. + */ + rwlock_t *lock; + +}; + +/** + * Entry for hashtables + */ +typedef struct { + /** Key chunk */ + chunk_t key; + /** Entry value */ + uint64_t value; +} entry_t; + +/** + * Destroy a hashtable entry + */ +static void entry_destroy(entry_t *this) +{ + chunk_free(&this->key); + free(this); +} + +METHOD(tkm_chunk_map_t, insert, void, + private_tkm_chunk_map_t * const this, const chunk_t * const data, + const uint64_t id) +{ + entry_t *entry; + INIT(entry, + .key = chunk_clone(*data), + .value = id + ); + + this->lock->write_lock(this->lock); + entry = this->mappings->put(this->mappings, (void*)&entry->key, entry); + this->lock->unlock(this->lock); + + if (entry) + { + entry_destroy(entry); + } +} + +METHOD(tkm_chunk_map_t, get_id, uint64_t, + private_tkm_chunk_map_t * const this, chunk_t *data) +{ + entry_t *entry; + this->lock->read_lock(this->lock); + entry = this->mappings->get(this->mappings, data); + this->lock->unlock(this->lock); + + if (!entry) + { + return 0; + } + + return entry->value; +} + +METHOD(tkm_chunk_map_t, remove_, bool, + private_tkm_chunk_map_t * const this, chunk_t *data) +{ + entry_t *entry; + + this->lock->write_lock(this->lock); + entry = this->mappings->remove(this->mappings, data); + this->lock->unlock(this->lock); + + if (entry) + { + entry_destroy(entry); + return TRUE; + } + else + { + return FALSE; + } +} + +METHOD(tkm_chunk_map_t, destroy, void, + private_tkm_chunk_map_t *this) +{ + entry_t *entry; + enumerator_t *enumerator; + + this->lock->write_lock(this->lock); + enumerator = this->mappings->create_enumerator(this->mappings); + while (enumerator->enumerate(enumerator, NULL, &entry)) + { + entry_destroy(entry); + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + this->mappings->destroy(this->mappings); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Hashtable hash function. + */ +static u_int hash(chunk_t *key) +{ + return chunk_hash(*key); +} + +/* + * see header file + */ +tkm_chunk_map_t *tkm_chunk_map_create() +{ + private_tkm_chunk_map_t *this; + + INIT(this, + .public = { + .insert = _insert, + .get_id = _get_id, + .remove = _remove_, + .destroy = _destroy, + }, + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .mappings = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)chunk_equals_ptr, 32), + ); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_chunk_map.h b/src/charon-tkm/src/tkm/tkm_chunk_map.h new file mode 100644 index 000000000..648257574 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_chunk_map.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-chunk-map chunk map + * @{ @ingroup tkm + */ + +#ifndef TKM_CHUNK_MAP_H_ +#define TKM_CHUNK_MAP_H_ + +#include +#include + +typedef struct tkm_chunk_map_t tkm_chunk_map_t; + +/** + * The tkm chunk map handles mappings of chunks to ids. + */ +struct tkm_chunk_map_t { + + /** + * Store new mapping for given chunk and id. + * + * @param data data associated with id + * @param id id associated with data + */ + void (*insert)(tkm_chunk_map_t * const this, const chunk_t * const data, + const uint64_t id); + + /** + * Get id for given chunk. + * + * @param data data specifying the mapping + * @return id of given chunk, 0 if not found + */ + uint64_t (*get_id)(tkm_chunk_map_t * const this, chunk_t *data); + + /** + * Remove mapping for given chunk. + * + * @param data data specifiying the mapping to remove + * @return TRUE if mapping was removed, FALSE otherwise + */ + bool (*remove)(tkm_chunk_map_t * const this, chunk_t *data); + + /** + * Destroy a tkm chunk map instance. + */ + void (*destroy)(tkm_chunk_map_t *this); + +}; + +/** + * Create a tkm chunk map instance. + */ +tkm_chunk_map_t *tkm_chunk_map_create(); + +#endif /** TKM_CHUNK_MAP_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_cred.c b/src/charon-tkm/src/tkm/tkm_cred.c new file mode 100644 index 000000000..d9517f908 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_cred.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include + +#include "tkm_private_key.h" +#include "tkm_cred.h" + +typedef struct private_tkm_cred_t private_tkm_cred_t; + +/** + * Private data of a tkm_cred_t object. + */ +struct private_tkm_cred_t { + + /** + * Public tkm_cred_t interface. + */ + tkm_cred_t public; + + /** + * In-memory credential set. + */ + mem_cred_t *creds; + + /** + * Key-id hashtable. + */ + hashtable_t *known_keys; + + /** + * rwlock for hashtable. + */ + rwlock_t *lock; + +}; + +METHOD(credential_set_t, create_private_enumerator, enumerator_t*, + private_tkm_cred_t *this, key_type_t type, identification_t *id) +{ + identification_t *entry; + + if (!id) + { + return this->known_keys->create_enumerator(this->known_keys); + } + + this->lock->write_lock(this->lock); + entry = this->known_keys->get(this->known_keys, id); + + if (!entry) + { + identification_t *clone = id->clone(id); + tkm_private_key_t *key = tkm_private_key_init(id); + + DBG1(DBG_CFG, "adding private key proxy for id '%Y'", clone); + if (!key) + { + DBG1(DBG_CFG, "unable to create private key for id '%Y'", clone); + this->lock->unlock(this->lock); + return NULL; + } + this->creds->add_key(this->creds, (private_key_t *)key); + entry = this->known_keys->put(this->known_keys, clone, clone); + } + this->lock->unlock(this->lock); + + return this->creds->set.create_private_enumerator(&this->creds->set, + type, id); +} + +METHOD(tkm_cred_t, destroy, void, + private_tkm_cred_t *this) +{ + enumerator_t *enumerator; + identification_t *entry; + + enumerator = this->known_keys->create_enumerator(this->known_keys); + while (enumerator->enumerate(enumerator, NULL, &entry)) + { + entry->destroy(entry); + } + enumerator->destroy(enumerator); + this->known_keys->destroy(this->known_keys); + + this->creds->destroy(this->creds); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Hashtable hash function. + */ +static u_int hash(identification_t *id) +{ + return chunk_hash(id->get_encoding(id)); +} + +/** + * Hashtable equals function. + */ +static bool equals(identification_t *a, identification_t *b) +{ + return a->equals(a, b); +} + +/** + * See header + */ +tkm_cred_t *tkm_cred_create() +{ + private_tkm_cred_t *this; + + INIT(this, + .public = { + .set = { + .create_shared_enumerator = (void*)return_null, + .create_private_enumerator = _create_private_enumerator, + .create_cert_enumerator = (void*)return_null, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .destroy = _destroy, + }, + .creds = mem_cred_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .known_keys = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 4), + ); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_cred.h b/src/charon-tkm/src/tkm/tkm_cred.h new file mode 100644 index 000000000..1cfb5b9c7 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_cred.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-credential credential set + * @{ @ingroup tkm + */ + +#ifndef TKM_CRED_H_ +#define TKM_CRED_H_ + +typedef struct tkm_cred_t tkm_cred_t; + +#include + +/** + * TKM in-memory credential set. + */ +struct tkm_cred_t { + + /** + * Implements credential_set_t. + */ + credential_set_t set; + + /** + * Destroy a tkm_cred_t. + */ + void (*destroy)(tkm_cred_t *this); + +}; + +/** + * Create a tkm_cred instance. + */ +tkm_cred_t *tkm_cred_create(); + +#endif /** TKM_CRED_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_diffie_hellman.c b/src/charon-tkm/src/tkm/tkm_diffie_hellman.c new file mode 100644 index 000000000..19f57de01 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_diffie_hellman.c @@ -0,0 +1,140 @@ +/* + * Copyrigth (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include + +#include "tkm.h" +#include "tkm_utils.h" +#include "tkm_diffie_hellman.h" + +#include + +typedef struct private_tkm_diffie_hellman_t private_tkm_diffie_hellman_t; + +/** + * Private data of a tkm_diffie_hellman_t object. + */ +struct private_tkm_diffie_hellman_t { + + /** + * Public tkm_diffie_hellman_t interface. + */ + tkm_diffie_hellman_t public; + + /** + * Diffie Hellman group number. + */ + u_int16_t group; + + /** + * Diffie Hellman public value. + */ + dh_pubvalue_type pubvalue; + + /** + * Context id. + */ + dh_id_type context_id; + +}; + +METHOD(diffie_hellman_t, get_my_public_value, void, + private_tkm_diffie_hellman_t *this, chunk_t *value) +{ + sequence_to_chunk(this->pubvalue.data, this->pubvalue.size, value); +} + +METHOD(diffie_hellman_t, get_shared_secret, status_t, + private_tkm_diffie_hellman_t *this, chunk_t *secret) +{ + *secret = chunk_empty; + return SUCCESS; +} + + +METHOD(diffie_hellman_t, set_other_public_value, void, + private_tkm_diffie_hellman_t *this, chunk_t value) +{ + // TODO: unvoid this function + + dh_pubvalue_type othervalue; + othervalue.size = value.len; + memcpy(&othervalue.data, value.ptr, value.len); + + ike_dh_generate_key(this->context_id, othervalue); +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_tkm_diffie_hellman_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_tkm_diffie_hellman_t *this) +{ + if (ike_dh_reset(this->context_id) != TKM_OK) + { + DBG1(DBG_LIB, "failed to reset DH context %d", this->context_id); + } + + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_DH, this->context_id); + free(this); +} + +METHOD(tkm_diffie_hellman_t, get_id, dh_id_type, + private_tkm_diffie_hellman_t *this) +{ + return this->context_id; +} + +/* + * Described in header. + */ +tkm_diffie_hellman_t *tkm_diffie_hellman_create(diffie_hellman_group_t group) +{ + private_tkm_diffie_hellman_t *this; + + INIT(this, + .public = { + .dh = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + .get_id = _get_id, + }, + .group = group, + .context_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_DH), + ); + + if (!this->context_id) + { + free(this); + return NULL; + } + + if (ike_dh_create(this->context_id, group, &this->pubvalue) != TKM_OK) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_diffie_hellman.h b/src/charon-tkm/src/tkm/tkm_diffie_hellman.h new file mode 100644 index 000000000..a144303fa --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_diffie_hellman.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-dh diffie hellman + * @{ @ingroup tkm + */ + +#ifndef TKM_DIFFIE_HELLMAN_H_ +#define TKM_DIFFIE_HELLMAN_H_ + +typedef struct tkm_diffie_hellman_t tkm_diffie_hellman_t; + +#include +#include + +/** + * diffie_hellman_t implementation using the trusted key manager. + */ +struct tkm_diffie_hellman_t { + + /** + * Implements diffie_hellman_t interface. + */ + diffie_hellman_t dh; + + /** + * Get Diffie-Hellman context id. + * + * @return id of this DH context. + */ + dh_id_type (*get_id)(tkm_diffie_hellman_t * const this); + +}; + +/** + * Creates a new tkm_diffie_hellman_t object. + * + * @param group Diffie Hellman group number to use + * @return tkm_diffie_hellman_t object, NULL if not supported + */ +tkm_diffie_hellman_t *tkm_diffie_hellman_create(diffie_hellman_group_t group); + +#endif /** TKM_DIFFIE_HELLMAN_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_encoder.c b/src/charon-tkm/src/tkm/tkm_encoder.c new file mode 100644 index 000000000..d5367ea78 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_encoder.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 Reto Buerki + * Copyright (C) 2013 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "tkm_encoder.h" + +/** + * Build the SHA1 hash of pubkey(info) ASN.1 data. + */ +static bool hash_pubkey(chunk_t pubkey, chunk_t *hash) +{ + hasher_t *hasher; + + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher || !hasher->allocate_hash(hasher, pubkey, hash)) + { + DBG1(DBG_LIB, "SHA1 hash algorithm not supported, " + "fingerprinting failed"); + DESTROY_IF(hasher); + chunk_free(&pubkey); + return FALSE; + } + hasher->destroy(hasher); + chunk_free(&pubkey); + return TRUE; +} + +/** + * Encode the public key blob into subjectPublicKeyInfo. + */ +static bool build_pub_info(chunk_t *encoding, va_list args) +{ + chunk_t blob; + + if (cred_encoding_args(args, CRED_PART_RSA_PUB_ASN1_DER, &blob, + CRED_PART_END)) + { + *encoding = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_algorithmIdentifier(OID_RSA_ENCRYPTION), + asn1_bitstring("c", blob)); + return TRUE; + } + return FALSE; +} + +/** + * Build the fingerprint of the subjectPublicKeyInfo object. + */ +static bool build_info_sha1(chunk_t *encoding, va_list args) +{ + chunk_t pubkey; + + if (build_pub_info(&pubkey, args)) + { + return hash_pubkey(pubkey, encoding); + } + return FALSE; +} + +/** + * Build the fingerprint of the subjectPublicKey object. + */ +static bool build_sha1(chunk_t *encoding, va_list args) +{ + chunk_t blob; + + if (cred_encoding_args(args, CRED_PART_RSA_PUB_ASN1_DER, &blob, + CRED_PART_END)) + { + return hash_pubkey(chunk_clone(blob), encoding); + } + return FALSE; +} + +/** + * See header. + */ +bool tkm_encoder_encode(cred_encoding_type_t type, chunk_t *encoding, + va_list args) +{ + switch (type) + { + case KEYID_PUBKEY_INFO_SHA1: + return build_info_sha1(encoding, args); + case KEYID_PUBKEY_SHA1: + return build_sha1(encoding, args); + default: + return FALSE; + } +} diff --git a/src/charon-tkm/src/tkm/tkm_encoder.h b/src/charon-tkm/src/tkm/tkm_encoder.h new file mode 100644 index 000000000..7c6a4989d --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_encoder.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Reto Buerki + * Copyright (C) 2013 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-credential-enc credential encoder + * @{ @ingroup tkm + */ + +#ifndef TKM_ENCODER_H_ +#define TKM_ENCODER_H_ + +#include + +/** + * Encoding function for TKM key fingerprints. + */ +bool tkm_encoder_encode(cred_encoding_type_t type, chunk_t *encoding, + va_list args); + +#endif /** TKM_ENCODER_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_id_manager.c b/src/charon-tkm/src/tkm/tkm_id_manager.c new file mode 100644 index 000000000..407d0a87f --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_id_manager.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 "tkm_id_manager.h" + +#include +#include +#include + +#define TKM_LIMIT 100 + +ENUM_BEGIN(tkm_context_kind_names, TKM_CTX_NONCE, TKM_CTX_ESA, + "NONCE_CONTEXT", + "DH_CONTEXT", + "CC_CONTEXT" + "ISA_CONTEXT", + "AE_CONTEXT", + "ESA_CONTEXT"); +ENUM_END(tkm_context_kind_names, TKM_CTX_ESA); + +typedef struct private_tkm_id_manager_t private_tkm_id_manager_t; + +/** + * private data of tkm_id_manager + */ +struct private_tkm_id_manager_t { + + /** + * public functions + */ + tkm_id_manager_t public; + + /** + * Per-kind array of free context ids + */ + bool* ctxids[TKM_CTX_MAX]; + + /** + * Per-kind context limits. + */ + tkm_limits_t limits; + + /** + * rwlocks for context id lists + */ + rwlock_t *locks[TKM_CTX_MAX]; + +}; + +/** + * Check if given kind is a valid context kind value. + * + * @param kind context kind to check + * @return TRUE if given kind is a valid context kind, + * FALSE otherwise + */ +static bool is_valid_kind(const tkm_context_kind_t kind) +{ + return (int)kind >= 0 && kind < TKM_CTX_MAX; +}; + +METHOD(tkm_id_manager_t, acquire_id, int, + private_tkm_id_manager_t * const this, const tkm_context_kind_t kind) +{ + int id = 0; + uint64_t j; + + if (!is_valid_kind(kind)) + { + DBG1(DBG_LIB, "tried to acquire id for invalid context kind '%d'", + kind); + return 0; + } + + this->locks[kind]->write_lock(this->locks[kind]); + for (j = 0; j < this->limits[kind]; j++) + { + if (!this->ctxids[kind][j]) + { + this->ctxids[kind][j] = true; + id = j + 1; + break; + } + } + this->locks[kind]->unlock(this->locks[kind]); + + if (!id) + { + DBG1(DBG_LIB, "acquiring %N context id failed", tkm_context_kind_names, + kind); + } + + return id; +} + +METHOD(tkm_id_manager_t, release_id, bool, + private_tkm_id_manager_t * const this, const tkm_context_kind_t kind, + const int id) +{ + const int idx = id - 1; + + if (!is_valid_kind(kind)) + { + DBG1(DBG_LIB, "tried to release id %d for invalid context kind '%d'", + id, kind); + return FALSE; + } + + this->locks[kind]->write_lock(this->locks[kind]); + this->ctxids[kind][idx] = false; + this->locks[kind]->unlock(this->locks[kind]); + + return TRUE; +} + + +METHOD(tkm_id_manager_t, destroy, void, + private_tkm_id_manager_t *this) +{ + int i; + for (i = 0; i < TKM_CTX_MAX; i++) + { + free(this->ctxids[i]); + this->locks[i]->destroy(this->locks[i]); + } + free(this); +} + +/* + * see header file + */ +tkm_id_manager_t *tkm_id_manager_create(const tkm_limits_t limits) +{ + private_tkm_id_manager_t *this; + int i; + + INIT(this, + .public = { + .acquire_id = _acquire_id, + .release_id = _release_id, + .destroy = _destroy, + }, + ); + + for (i = 0; i < TKM_CTX_MAX; i++) + { + this->limits[i] = limits[i]; + this->ctxids[i] = calloc(limits[i], sizeof(bool)); + this->locks[i] = rwlock_create(RWLOCK_TYPE_DEFAULT); + DBG2(DBG_LIB, "%N initialized, %llu slot(s)", tkm_context_kind_names, i, + limits[i]); + } + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_id_manager.h b/src/charon-tkm/src/tkm/tkm_id_manager.h new file mode 100644 index 000000000..0fc9ff8ef --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_id_manager.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-id-manager id manager + * @{ @ingroup tkm + */ + +#ifndef TKM_ID_MANAGER_H_ +#define TKM_ID_MANAGER_H_ + +#include + +typedef struct tkm_id_manager_t tkm_id_manager_t; +typedef enum tkm_context_kind_t tkm_context_kind_t; + +/** + * Trusted key manager context kinds. + */ +enum tkm_context_kind_t { + /** Nonce context */ + TKM_CTX_NONCE, + /** Diffie-Hellman context */ + TKM_CTX_DH, + /** Certificate chain context */ + TKM_CTX_CC, + /** IKE SA context */ + TKM_CTX_ISA, + /** Authenticated Endpoint context */ + TKM_CTX_AE, + /** ESP SA context */ + TKM_CTX_ESA, + + /** helper to determine the number of elements in this enum */ + TKM_CTX_MAX, +}; + +/** + * enum name for context_kind_t. + */ +extern enum_name_t *tkm_context_kind_names; + +/** + * TKM context limits. + */ +typedef uint64_t tkm_limits_t[TKM_CTX_MAX]; + +/** + * The tkm id manager hands out context ids for all context kinds (e.g. nonce). + */ +struct tkm_id_manager_t { + + /** + * Acquire new context id for a specific context kind. + * + * @param kind kind of context id to acquire + * @return context id of given kind, + * 0 if no id of given kind could be acquired + */ + int (*acquire_id)(tkm_id_manager_t * const this, + const tkm_context_kind_t kind); + + /** + * Release a previously acquired context id. + * + * @param kind kind of context id to release + * @param id id to release + * @return TRUE if id was released, FALSE otherwise + */ + bool (*release_id)(tkm_id_manager_t * const this, + const tkm_context_kind_t kind, + const int id); + + /** + * Destroy a tkm_id_manager instance. + */ + void (*destroy)(tkm_id_manager_t *this); + +}; + +/** + * Create a tkm id manager instance using the given context limits. + */ +tkm_id_manager_t *tkm_id_manager_create(const tkm_limits_t limits); + +#endif /** TKM_ID_MANAGER_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c new file mode 100644 index 000000000..69aefea97 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "tkm.h" +#include "tkm_utils.h" +#include "tkm_types.h" +#include "tkm_keymat.h" +#include "tkm_kernel_sad.h" +#include "tkm_kernel_ipsec.h" + +/** From linux/in.h */ +#ifndef IP_XFRM_POLICY +#define IP_XFRM_POLICY 17 +#endif + +typedef struct private_tkm_kernel_ipsec_t private_tkm_kernel_ipsec_t; + +/** + * Private variables and functions of TKM kernel ipsec instance. + */ +struct private_tkm_kernel_ipsec_t { + + /** + * Public tkm_kernel_ipsec interface. + */ + tkm_kernel_ipsec_t public; + + /** + * RNG used for SPI generation. + */ + rng_t *rng; + + /** + * CHILD/ESP SA database. + */ + tkm_kernel_sad_t *sad; + +}; + +METHOD(kernel_ipsec_t, get_spi, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int8_t protocol, u_int32_t reqid, u_int32_t *spi) +{ + bool result; + + if (!this->rng) + { + this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!this->rng) + { + DBG1(DBG_KNL, "unable to create RNG"); + return FAILED; + } + } + + DBG1(DBG_KNL, "getting SPI for reqid {%u}", reqid); + result = this->rng->get_bytes(this->rng, sizeof(u_int32_t), + (u_int8_t *)spi); + return result ? SUCCESS : FAILED; +} + +METHOD(kernel_ipsec_t, get_cpi, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t reqid, u_int16_t *cpi) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, add_sa, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, + u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key, + u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, + u_int16_t cpi, bool encap, bool esn, bool inbound, + traffic_selector_t* src_ts, traffic_selector_t* dst_ts) +{ + esa_info_t esa; + bool initiator; + esp_spi_type spi_loc, spi_rem; + host_t *local, *peer; + chunk_t *nonce_loc, *nonce_rem; + nc_id_type nonce_loc_id; + esa_id_type esa_id; + nonce_type nc_rem; + + if (enc_key.ptr == NULL) + { + DBG1(DBG_KNL, "Unable to get ESA information"); + return FAILED; + } + esa = *(esa_info_t *)(enc_key.ptr); + + /* only handle the case where we have both distinct ESP spi's available */ + if (esa.spi_r == spi) + { + chunk_free(&esa.nonce_i); + chunk_free(&esa.nonce_r); + return SUCCESS; + } + + /* Initiator if encr_r is passed as enc_key to the inbound add_sa call */ + initiator = esa.is_encr_r && inbound; + if (initiator) + { + spi_loc = spi; + spi_rem = esa.spi_r; + local = dst; + peer = src; + nonce_loc = &esa.nonce_i; + nonce_rem = &esa.nonce_r; + } + else + { + spi_loc = esa.spi_r; + spi_rem = spi; + local = src; + peer = dst; + nonce_loc = &esa.nonce_r; + nonce_rem = &esa.nonce_i; + } + + esa_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_ESA); + if (!this->sad->insert(this->sad, esa_id, peer, local, spi_loc, protocol)) + { + DBG1(DBG_KNL, "unable to add entry (%llu) to SAD", esa_id); + goto sad_failure; + } + + /* + * creation of first CHILD SA: + * no nonce and no dh contexts because the ones from the IKE SA are re-used + */ + nonce_loc_id = tkm->chunk_map->get_id(tkm->chunk_map, nonce_loc); + if (nonce_loc_id == 0 && esa.dh_id == 0) + { + if (ike_esa_create_first(esa_id, esa.isa_id, reqid, 1, spi_loc, spi_rem) + != TKM_OK) + { + DBG1(DBG_KNL, "child SA (%llu, first) creation failed", esa_id); + goto failure; + } + } + /* creation of child SA without PFS: no dh context */ + else if (nonce_loc_id != 0 && esa.dh_id == 0) + { + chunk_to_sequence(nonce_rem, &nc_rem, sizeof(nonce_type)); + if (ike_esa_create_no_pfs(esa_id, esa.isa_id, reqid, 1, nonce_loc_id, + nc_rem, initiator, spi_loc, spi_rem) + != TKM_OK) + { + DBG1(DBG_KNL, "child SA (%llu, no PFS) creation failed", esa_id); + goto failure; + } + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, nonce_loc_id); + } + /* creation of subsequent child SA with PFS: nonce and dh context are set */ + else + { + chunk_to_sequence(nonce_rem, &nc_rem, sizeof(nonce_type)); + if (ike_esa_create(esa_id, esa.isa_id, reqid, 1, esa.dh_id, nonce_loc_id, + nc_rem, initiator, spi_loc, spi_rem) != TKM_OK) + { + DBG1(DBG_KNL, "child SA (%llu) creation failed", esa_id); + goto failure; + } + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, nonce_loc_id); + } + if (ike_esa_select(esa_id) != TKM_OK) + { + DBG1(DBG_KNL, "error selecting new child SA (%llu)", esa_id); + if (ike_esa_reset(esa_id) != TKM_OK) + { + DBG1(DBG_KNL, "child SA (%llu) deletion failed", esa_id); + } + goto failure; + } + + DBG1(DBG_KNL, "added child SA (esa: %llu, isa: %llu, esp_spi_loc: %x, " + "esp_spi_rem: %x, role: %s)", esa_id, esa.isa_id, ntohl(spi_loc), + ntohl(spi_rem), initiator ? "initiator" : "responder"); + chunk_free(&esa.nonce_i); + chunk_free(&esa.nonce_r); + + return SUCCESS; + +failure: + this->sad->remove(this->sad, esa_id); +sad_failure: + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_ESA, esa_id); + chunk_free(&esa.nonce_i); + chunk_free(&esa.nonce_r); + return FAILED; +} + +METHOD(kernel_ipsec_t, query_sa, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes, + u_int64_t *packets) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, del_sa, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark) +{ + esa_id_type esa_id; + + esa_id = this->sad->get_esa_id(this->sad, src, dst, spi, protocol); + if (esa_id) + { + DBG1(DBG_KNL, "deleting child SA (esa: %llu, spi: %x)", esa_id, + ntohl(spi)); + if (ike_esa_reset(esa_id) != TKM_OK) + { + DBG1(DBG_KNL, "child SA (%llu) deletion failed", esa_id); + return FAILED; + } + this->sad->remove(this->sad, esa_id); + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_ESA, esa_id); + } + return SUCCESS; +} + +METHOD(kernel_ipsec_t, update_sa, status_t, + private_tkm_kernel_ipsec_t *this, u_int32_t spi, u_int8_t protocol, + u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst, + bool old_encap, bool new_encap, mark_t mark) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, flush_sas, status_t, + private_tkm_kernel_ipsec_t *this) +{ + DBG1(DBG_KNL, "flushing child SA entries"); + return SUCCESS; +} + +METHOD(kernel_ipsec_t, add_policy, status_t, + private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, + mark_t mark, policy_priority_t priority) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, query_policy, status_t, + private_tkm_kernel_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark, + u_int32_t *use_time) +{ + return NOT_SUPPORTED; +} + +METHOD(kernel_ipsec_t, del_policy, status_t, + private_tkm_kernel_ipsec_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t prio) +{ + return SUCCESS; +} + +METHOD(kernel_ipsec_t, flush_policies, status_t, + private_tkm_kernel_ipsec_t *this) +{ + return SUCCESS; +} + + +METHOD(kernel_ipsec_t, bypass_socket, bool, + private_tkm_kernel_ipsec_t *this, int fd, int family) +{ + struct xfrm_userpolicy_info policy; + u_int sol, ipsec_policy; + + switch (family) + { + case AF_INET: + sol = SOL_IP; + ipsec_policy = IP_XFRM_POLICY; + break; + case AF_INET6: + sol = SOL_IPV6; + ipsec_policy = IPV6_XFRM_POLICY; + break; + default: + return FALSE; + } + + memset(&policy, 0, sizeof(policy)); + policy.action = XFRM_POLICY_ALLOW; + policy.sel.family = family; + + policy.dir = XFRM_POLICY_OUT; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + policy.dir = XFRM_POLICY_IN; + if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", + strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_tkm_kernel_ipsec_t *this, int fd, int family, u_int16_t port) +{ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; + } + return TRUE; +} + +METHOD(kernel_ipsec_t, destroy, void, + private_tkm_kernel_ipsec_t *this) +{ + DESTROY_IF(this->rng); + DESTROY_IF(this->sad); + free(this); +} + +/* + * Described in header. + */ +tkm_kernel_ipsec_t *tkm_kernel_ipsec_create() +{ + private_tkm_kernel_ipsec_t *this; + + INIT(this, + .public = { + .interface = { + .get_spi = _get_spi, + .get_cpi = _get_cpi, + .add_sa = _add_sa, + .update_sa = _update_sa, + .query_sa = _query_sa, + .del_sa = _del_sa, + .flush_sas = _flush_sas, + .add_policy = _add_policy, + .query_policy = _query_policy, + .del_policy = _del_policy, + .flush_policies = _flush_policies, + .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, + .destroy = _destroy, + }, + }, + .sad = tkm_kernel_sad_create(), + ); + + if (!this->sad) + { + DBG1(DBG_KNL, "unable to create SAD"); + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.h b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.h new file mode 100644 index 000000000..14db21266 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-kernel-ipsec kernel ipsec + * @{ @ingroup tkm + */ + +#ifndef TKM_KERNEL_IPSEC_H_ +#define TKM_KERNEL_IPSEC_H_ + +#include + +typedef struct tkm_kernel_ipsec_t tkm_kernel_ipsec_t; + +/** + * TKM implementation of the kernel ipsec interface. + */ +struct tkm_kernel_ipsec_t { + + /** + * Implements kernel_ipsec_t interface + */ + kernel_ipsec_t interface; +}; + +/** + * Create a TKM kernel ipsec interface instance. + * + * @return tkm_kernel_ipsec_t instance + */ +tkm_kernel_ipsec_t *tkm_kernel_ipsec_create(); + +#endif /** TKM_KERNEL_IPSEC_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_kernel_sad.c b/src/charon-tkm/src/tkm/tkm_kernel_sad.c new file mode 100644 index 000000000..360a47bdc --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_kernel_sad.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "tkm_kernel_sad.h" + +typedef struct private_tkm_kernel_sad_t private_tkm_kernel_sad_t; + +/** + * Private data of tkm_kernel_sad. + */ +struct private_tkm_kernel_sad_t { + + /** + * Public functions. + */ + tkm_kernel_sad_t public; + + /** + * Linked list of SAD entries. + */ + linked_list_t *data; + + /** + * Lock used to protect SA data. + */ + mutex_t *mutex; + +}; + +typedef struct sad_entry_t sad_entry_t; + +/** + * Data structure holding all information of an SAD entry. + */ +struct sad_entry_t { + + /** + * ESA identifier. + */ + esa_id_type esa_id; + + /** + * Source address of CHILD SA. + */ + host_t *src; + + /** + * Destination address of CHILD SA. + */ + host_t *dst; + + /** + * SPI of CHILD SA. + */ + u_int32_t spi; + + /** + * Protocol of CHILD SA (ESP/AH). + */ + u_int8_t proto; + +}; + +/** + * Destroy an sad_entry_t object. + */ +static void sad_entry_destroy(sad_entry_t *entry) +{ + if (entry) + { + DESTROY_IF(entry->src); + DESTROY_IF(entry->dst); + free(entry); + } +} + +/** + * Find a list entry with given src, dst, spi and proto values. + */ +static bool sad_entry_match(sad_entry_t * const entry, const host_t * const src, + const host_t * const dst, const u_int32_t * const spi, + const u_int8_t * const proto) +{ + if (entry->src == NULL || entry->dst == NULL) + { + return FALSE; + } + + return src->ip_equals(entry->src, (host_t *)src) && + dst->ip_equals(entry->dst, (host_t *)dst) && + entry->spi == *spi && entry->proto == *proto; +} + +/** + * Compare two SAD entries for equality. + */ +static bool sad_entry_equal(sad_entry_t * const left, sad_entry_t * const right) +{ + if (left->src == NULL || left->dst == NULL || right->src == NULL || + right->dst == NULL) + { + return FALSE; + } + return left->esa_id == right->esa_id && + left->src->ip_equals(left->src, right->src) && + left->dst->ip_equals(left->dst, right->dst) && + left->spi == right->spi && left->proto == right->proto; +} + +METHOD(tkm_kernel_sad_t, insert, bool, + private_tkm_kernel_sad_t * const this, const esa_id_type esa_id, + const host_t * const src, const host_t * const dst, const u_int32_t spi, + const u_int8_t proto) +{ + status_t result; + sad_entry_t *new_entry; + + INIT(new_entry, + .esa_id = esa_id, + .src = (host_t *)src, + .dst = (host_t *)dst, + .spi = spi, + .proto = proto, + ); + + this->mutex->lock(this->mutex); + result = this->data->find_first(this->data, + (linked_list_match_t)sad_entry_equal, NULL, + new_entry); + if (result == NOT_FOUND) + { + DBG3(DBG_KNL, "inserting SAD entry (esa: %llu, src: %H, dst: %H, " + "spi: %x, proto: %u)", esa_id, src, dst, ntohl(spi), proto); + new_entry->src = src->clone((host_t *)src); + new_entry->dst = dst->clone((host_t *)dst); + this->data->insert_last(this->data, new_entry); + } + else + { + DBG1(DBG_KNL, "SAD entry with esa id %llu already exists!", esa_id); + free(new_entry); + } + this->mutex->unlock(this->mutex); + return result == NOT_FOUND; +} + +METHOD(tkm_kernel_sad_t, get_esa_id, esa_id_type, + private_tkm_kernel_sad_t * const this, const host_t * const src, + const host_t * const dst, const u_int32_t spi, const u_int8_t proto) +{ + esa_id_type id = 0; + sad_entry_t *entry = NULL; + + this->mutex->lock(this->mutex); + const status_t res = this->data->find_first(this->data, + (linked_list_match_t)sad_entry_match, + (void**)&entry, src, dst, &spi, + &proto); + if (res == SUCCESS && entry) + { + id = entry->esa_id; + DBG3(DBG_KNL, "getting ESA id of SAD entry (esa: %llu, src: %H, " + "dst: %H, spi: %x, proto: %u)", id, src, dst, ntohl(spi), + proto); + } + else + { + DBG3(DBG_KNL, "no SAD entry found"); + } + this->mutex->unlock(this->mutex); + return id; +} + +METHOD(tkm_kernel_sad_t, _remove, bool, + private_tkm_kernel_sad_t * const this, const esa_id_type esa_id) +{ + sad_entry_t *current; + bool removed = FALSE; + enumerator_t *enumerator; + + this->mutex->lock(this->mutex); + enumerator = this->data->create_enumerator(this->data); + while (enumerator->enumerate(enumerator, (void **)¤t)) + { + if (current->esa_id == esa_id) + { + this->data->remove_at(this->data, enumerator); + sad_entry_destroy(current); + removed = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (removed) + { + DBG3(DBG_KNL, "removed SAD entry (esa: %llu)", esa_id); + } + else + { + DBG1(DBG_KNL, "no SAD entry with ESA id %llu found!", esa_id); + } + this->mutex->unlock(this->mutex); + + return removed; +} + + +METHOD(tkm_kernel_sad_t, destroy, void, + private_tkm_kernel_sad_t *this) +{ + this->mutex->destroy(this->mutex); + this->data->destroy_function(this->data, (void*)sad_entry_destroy); + free(this); +} + +/* + * see header file + */ +tkm_kernel_sad_t *tkm_kernel_sad_create() +{ + private_tkm_kernel_sad_t *this; + + INIT(this, + .public = { + .insert = _insert, + .get_esa_id = _get_esa_id, + .remove = __remove, + .destroy = _destroy, + }, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .data = linked_list_create(), + ); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_kernel_sad.h b/src/charon-tkm/src/tkm/tkm_kernel_sad.h new file mode 100644 index 000000000..0194cd3bc --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_kernel_sad.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-kernel-sad kernel sad + * @{ @ingroup tkm + */ + +#ifndef TKM_KERNEL_SAD_H_ +#define TKM_KERNEL_SAD_H_ + +#include +#include + +typedef struct tkm_kernel_sad_t tkm_kernel_sad_t; + +/** + * The TKM kernel SAD (security association database) stores information about + * CHILD SAs. + */ +struct tkm_kernel_sad_t { + + /** + * Insert new SAD entry with specified parameters. + * + * @param esa_id ESP SA context identifier + * @param src source address of CHILD SA + * @param dst destination address of CHILD SA + * @param spi SPI of CHILD SA + * @param proto protocol of CHILD SA (ESP/AH) + * @return TRUE if entry was inserted, FALSE otherwise + */ + bool (*insert)(tkm_kernel_sad_t * const this, const esa_id_type esa_id, + const host_t * const src, const host_t * const dst, + const u_int32_t spi, const u_int8_t proto); + + /** + * Get ESA id for entry with given parameters. + * + * @param src source address of CHILD SA + * @param dst destination address of CHILD SA + * @param spi SPI of CHILD SA + * @param proto protocol of CHILD SA (ESP/AH) + * @return ESA id of entry if found, 0 otherwise + */ + esa_id_type (*get_esa_id)(tkm_kernel_sad_t * const this, + const host_t * const src, const host_t * const dst, + const u_int32_t spi, const u_int8_t proto); + + /** + * Remove entry with given ESA id from SAD. + * + * @param esa_id ESA identifier of entry to remove + * @return TRUE if entry was removed, FALSE otherwise + */ + bool (*remove)(tkm_kernel_sad_t * const this, const esa_id_type esa_id); + + /** + * Destroy a tkm_kernel_sad instance. + */ + void (*destroy)(tkm_kernel_sad_t *this); + +}; + +/** + * Create a TKM kernel SAD instance. + */ +tkm_kernel_sad_t *tkm_kernel_sad_create(); + +#endif /** TKM_KERNEL_SAD_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_keymat.c b/src/charon-tkm/src/tkm/tkm_keymat.c new file mode 100644 index 000000000..772fac8b0 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_keymat.c @@ -0,0 +1,511 @@ +/* + * Copyrigth (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "tkm.h" +#include "tkm_types.h" +#include "tkm_utils.h" +#include "tkm_diffie_hellman.h" +#include "tkm_keymat.h" + +typedef struct private_tkm_keymat_t private_tkm_keymat_t; + +/** + * Private data of a keymat_t object. + */ +struct private_tkm_keymat_t { + + /** + * Public tkm_keymat_t interface. + */ + tkm_keymat_t public; + + /** + * IKE_SA Role, initiator or responder. + */ + bool initiator; + + /** + * Inbound AEAD. + */ + aead_t *aead_in; + + /** + * Outbound AEAD. + */ + aead_t *aead_out; + + /** + * ISA context id. + */ + isa_id_type isa_ctx_id; + + /** + * AE context id. + */ + ae_id_type ae_ctx_id; + + /** + * AUTH payload chunk. + */ + chunk_t auth_payload; + + /** + * Peer init message chunk. + */ + chunk_t other_init_msg; + +}; + +/** + * Create AEAD transforms from given key chunks. + * + * @param in inbound AEAD transform to allocate, NULL if failed + * @param out outbound AEAD transform to allocate, NULL if failed + * @param sk_ai SK_ai key chunk + * @param sk_ar SK_ar key chunk + * @param sk_ei SK_ei key chunk + * @param sk_er SK_er key chunk + * @param enc_alg encryption algorithm to use + * @param int_alg integrity algorithm to use + * @param key_size encryption key size in bytes + * @param initiator TRUE if initiator + */ +static void aead_create_from_keys(aead_t **in, aead_t **out, + const chunk_t * const sk_ai, const chunk_t * const sk_ar, + const chunk_t * const sk_ei, const chunk_t * const sk_er, + const u_int16_t enc_alg, const u_int16_t int_alg, + const u_int16_t key_size, bool initiator) +{ + *in = *out = NULL; + signer_t *signer_i, *signer_r; + crypter_t *crypter_i, *crypter_r; + + signer_i = lib->crypto->create_signer(lib->crypto, int_alg); + signer_r = lib->crypto->create_signer(lib->crypto, int_alg); + if (signer_i == NULL || signer_r == NULL) + { + DBG1(DBG_IKE, "%N %N not supported!", + transform_type_names, INTEGRITY_ALGORITHM, + integrity_algorithm_names, int_alg); + return; + } + crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, key_size); + crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, key_size); + if (crypter_i == NULL || crypter_r == NULL) + { + signer_i->destroy(signer_i); + signer_r->destroy(signer_r); + DBG1(DBG_IKE, "%N %N (key size %d) not supported!", + transform_type_names, ENCRYPTION_ALGORITHM, + encryption_algorithm_names, enc_alg, key_size); + return; + } + + DBG4(DBG_IKE, "Sk_ai %B", sk_ai); + if (!signer_i->set_key(signer_i, *sk_ai)) + { + return; + } + DBG4(DBG_IKE, "Sk_ar %B", sk_ar); + if (!signer_r->set_key(signer_r, *sk_ar)) + { + return; + } + DBG4(DBG_IKE, "Sk_ei %B", sk_ei); + if (!crypter_i->set_key(crypter_i, *sk_ei)) + { + return; + } + DBG4(DBG_IKE, "Sk_er %B", sk_er); + if (!crypter_r->set_key(crypter_r, *sk_er)) + { + return; + } + + if (initiator) + { + *in = aead_create(crypter_r, signer_r); + *out = aead_create(crypter_i, signer_i); + } + else + { + *in = aead_create(crypter_i, signer_i); + *out = aead_create(crypter_r, signer_r); + } +} + +METHOD(keymat_t, get_version, ike_version_t, + private_tkm_keymat_t *this) +{ + return IKEV2; +} + +METHOD(keymat_t, create_dh, diffie_hellman_t*, + private_tkm_keymat_t *this, diffie_hellman_group_t group) +{ + return lib->crypto->create_dh(lib->crypto, group); +} + +METHOD(keymat_t, create_nonce_gen, nonce_gen_t*, + private_tkm_keymat_t *this) +{ + return lib->crypto->create_nonce_gen(lib->crypto); +} + +METHOD(keymat_v2_t, derive_ike_keys, bool, + private_tkm_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, + pseudo_random_function_t rekey_function, chunk_t rekey_skd) +{ + u_int16_t enc_alg, int_alg, key_size; + u_int64_t nc_id, spi_loc, spi_rem; + chunk_t *nonce, c_ai, c_ar, c_ei, c_er; + tkm_diffie_hellman_t *tkm_dh; + dh_id_type dh_id; + nonce_type nonce_rem; + result_type res; + key_type sk_ai, sk_ar, sk_ei, sk_er; + + /* Check encryption and integrity algorithms */ + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, + &key_size)) + { + DBG1(DBG_IKE, "no %N selected", transform_type_names, + ENCRYPTION_ALGORITHM); + return FALSE; + } + if (encryption_algorithm_is_aead(enc_alg)) + { + DBG1(DBG_IKE, "AEAD algorithm %N not supported", + encryption_algorithm_names, enc_alg); + return FALSE; + } + if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, NULL)) + { + DBG1(DBG_IKE, "no %N selected", transform_type_names, + INTEGRITY_ALGORITHM); + return FALSE; + } + if (!(enc_alg == ENCR_AES_CBC && key_size == 256 && + int_alg == AUTH_HMAC_SHA2_512_256)) + { + DBG1(DBG_IKE, "the TKM only supports aes256-sha512 at the moment, " + "please update your configuration"); + return FALSE; + } + + DBG2(DBG_IKE, "using %N for encryption, %N for integrity", + encryption_algorithm_names, enc_alg, integrity_algorithm_names, + int_alg); + + /* Acquire nonce context id */ + nonce = this->initiator ? &nonce_i : &nonce_r; + nc_id = tkm->chunk_map->get_id(tkm->chunk_map, nonce); + if (!nc_id) + { + DBG1(DBG_IKE, "unable to acquire context id for nonce"); + return FALSE; + } + + /* Get DH context id */ + tkm_dh = (tkm_diffie_hellman_t *)dh; + dh_id = tkm_dh->get_id(tkm_dh); + + if (this->initiator) + { + chunk_to_sequence(&nonce_r, &nonce_rem, sizeof(nonce_type)); + spi_loc = id->get_initiator_spi(id); + spi_rem = id->get_responder_spi(id); + } + else + { + chunk_to_sequence(&nonce_i, &nonce_rem, sizeof(nonce_type)); + spi_loc = id->get_responder_spi(id); + spi_rem = id->get_initiator_spi(id); + } + + if (rekey_function == PRF_UNDEFINED) + { + this->ae_ctx_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_AE); + if (!this->ae_ctx_id) + { + DBG1(DBG_IKE, "unable to acquire ae context id"); + return FALSE; + } + DBG1(DBG_IKE, "deriving IKE keys (nc: %llu, dh: %llu, spi_loc: %llx, " + "spi_rem: %llx)", nc_id, dh_id, spi_loc, spi_rem); + res = ike_isa_create(this->isa_ctx_id, this->ae_ctx_id, 1, dh_id, nc_id, + nonce_rem, this->initiator, spi_loc, spi_rem, + &sk_ai, &sk_ar, &sk_ei, &sk_er); + } + else + { + isa_info_t isa_info; + + if (rekey_skd.ptr == NULL || rekey_skd.len != sizeof(isa_info_t)) + { + DBG1(DBG_IKE, "unable to retrieve parent isa info"); + return FALSE; + } + isa_info = *((isa_info_t *)(rekey_skd.ptr)); + DBG1(DBG_IKE, "deriving IKE keys (parent_isa: %llu, ae: %llu, nc: %llu," + "dh: %llu, spi_loc: %llx, spi_rem: %llx)", isa_info.parent_isa_id, + isa_info.ae_id, nc_id, dh_id, spi_loc, spi_rem); + this->ae_ctx_id = isa_info.ae_id; + res = ike_isa_create_child(this->isa_ctx_id, isa_info.parent_isa_id, 1, + dh_id, nc_id, nonce_rem, this->initiator, + spi_loc, spi_rem, &sk_ai, &sk_ar, &sk_ei, + &sk_er); + chunk_free(&rekey_skd); + } + + if (res != TKM_OK) + { + DBG1(DBG_IKE, "key derivation failed (isa: %llu)", this->isa_ctx_id); + return FALSE; + } + + sequence_to_chunk(sk_ai.data, sk_ai.size, &c_ai); + sequence_to_chunk(sk_ar.data, sk_ar.size, &c_ar); + sequence_to_chunk(sk_ei.data, sk_ei.size, &c_ei); + sequence_to_chunk(sk_er.data, sk_er.size, &c_er); + + aead_create_from_keys(&this->aead_in, &this->aead_out, &c_ai, &c_ar, &c_ei, + &c_er, enc_alg, int_alg, key_size / 8, + this->initiator); + + chunk_clear(&c_ai); + chunk_clear(&c_ar); + chunk_clear(&c_ei); + chunk_clear(&c_er); + + if (!this->aead_in || !this->aead_out) + { + DBG1(DBG_IKE, "could not initialize AEAD transforms"); + return FALSE; + } + + /* TODO: Add failure handler (see keymat_v2.c) */ + + tkm->chunk_map->remove(tkm->chunk_map, nonce); + if (ike_nc_reset(nc_id) != TKM_OK) + { + DBG1(DBG_IKE, "failed to reset nonce context %llu", nc_id); + } + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, nc_id); + + return TRUE; +} + +METHOD(keymat_v2_t, derive_child_keys, bool, + private_tkm_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, + chunk_t *encr_r, chunk_t *integ_r) +{ + esa_info_t *esa_info_i, *esa_info_r; + dh_id_type dh_id = 0; + + if (dh) + { + dh_id = ((tkm_diffie_hellman_t *)dh)->get_id((tkm_diffie_hellman_t *)dh); + } + + INIT(esa_info_i, + .isa_id = this->isa_ctx_id, + .spi_r = proposal->get_spi(proposal), + .nonce_i = chunk_clone(nonce_i), + .nonce_r = chunk_clone(nonce_r), + .is_encr_r = FALSE, + .dh_id = dh_id, + ); + + INIT(esa_info_r, + .isa_id = this->isa_ctx_id, + .spi_r = proposal->get_spi(proposal), + .nonce_i = chunk_clone(nonce_i), + .nonce_r = chunk_clone(nonce_r), + .is_encr_r = TRUE, + .dh_id = dh_id, + ); + + DBG1(DBG_CHD, "passing on esa info (isa: %llu, spi_r: %x, dh_id: %llu)", + esa_info_i->isa_id, ntohl(esa_info_i->spi_r), esa_info_i->dh_id); + + /* store ESA info in encr_i/r, which is passed to add_sa */ + *encr_i = chunk_create((u_char *)esa_info_i, sizeof(esa_info_t)); + *encr_r = chunk_create((u_char *)esa_info_r, sizeof(esa_info_t)); + *integ_i = chunk_empty; + *integ_r = chunk_empty; + + return TRUE; +} + +METHOD(keymat_t, get_aead, aead_t*, + private_tkm_keymat_t *this, bool in) +{ + return in ? this->aead_in : this->aead_out; +} + +METHOD(keymat_v2_t, get_auth_octets, bool, + private_tkm_keymat_t *this, bool verify, chunk_t ike_sa_init, + chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets) +{ + sign_info_t *sign; + + if (verify) + { + /* store peer init message for authentication step */ + this->other_init_msg = chunk_clone(ike_sa_init); + *octets = chunk_empty; + return TRUE; + } + + INIT(sign, + .isa_id = this->isa_ctx_id, + .init_message = chunk_clone(ike_sa_init), + ); + + /* + * store signature info in AUTH octets, which is passed to the private key + * sign() operation + */ + *octets = chunk_create((u_char *)sign, sizeof(sign_info_t)); + return TRUE; +} + +METHOD(keymat_v2_t, get_skd, pseudo_random_function_t, + private_tkm_keymat_t *this, chunk_t *skd) +{ + isa_info_t *isa_info; + + INIT(isa_info, + .parent_isa_id = this->isa_ctx_id, + .ae_id = this->ae_ctx_id, + ); + + *skd = chunk_create((u_char *)isa_info, sizeof(isa_info_t)); + + /* + * remove ae context id, since control has now been handed over to the new + * IKE SA keymat + */ + this->ae_ctx_id = 0; + return PRF_HMAC_SHA2_512; +} + +METHOD(keymat_v2_t, get_psk_sig, bool, + private_tkm_keymat_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, char reserved[3], chunk_t *sig) +{ + return FALSE; +} + +METHOD(keymat_t, destroy, void, + private_tkm_keymat_t *this) +{ + if (ike_isa_reset(this->isa_ctx_id) != TKM_OK) + { + DBG1(DBG_IKE, "failed to reset ISA context %d", this->isa_ctx_id); + } + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_ISA, this->isa_ctx_id); + /* only reset ae context if set */ + if (this->ae_ctx_id != 0) + { + if (ike_ae_reset(this->ae_ctx_id) != TKM_OK) + { + DBG1(DBG_IKE, "failed to reset AE context %d", this->ae_ctx_id); + } + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_AE, this->ae_ctx_id); + } + + DESTROY_IF(this->aead_in); + DESTROY_IF(this->aead_out); + chunk_free(&this->auth_payload); + chunk_free(&this->other_init_msg); + free(this); +} + +METHOD(tkm_keymat_t, get_isa_id, isa_id_type, + private_tkm_keymat_t *this) +{ + return this->isa_ctx_id; +} + +METHOD(tkm_keymat_t, set_auth_payload, void, + private_tkm_keymat_t *this, const chunk_t * const payload) +{ + this->auth_payload = chunk_clone(*payload); +} + +METHOD(tkm_keymat_t, get_auth_payload, chunk_t*, + private_tkm_keymat_t *this) +{ + return &this->auth_payload; +} + +METHOD(tkm_keymat_t, get_peer_init_msg, chunk_t*, + private_tkm_keymat_t *this) +{ + return &this->other_init_msg; +} + +/** + * See header. + */ +tkm_keymat_t *tkm_keymat_create(bool initiator) +{ + private_tkm_keymat_t *this; + + INIT(this, + .public = { + .keymat_v2 = { + .keymat = { + .get_version = _get_version, + .create_dh = _create_dh, + .create_nonce_gen = _create_nonce_gen, + .get_aead = _get_aead, + .destroy = _destroy, + }, + .derive_ike_keys = _derive_ike_keys, + .derive_child_keys = _derive_child_keys, + .get_skd = _get_skd, + .get_auth_octets = _get_auth_octets, + .get_psk_sig = _get_psk_sig, + }, + .get_isa_id = _get_isa_id, + .set_auth_payload = _set_auth_payload, + .get_auth_payload = _get_auth_payload, + .get_peer_init_msg = _get_peer_init_msg, + }, + .initiator = initiator, + .isa_ctx_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_ISA), + .ae_ctx_id = 0, + .auth_payload = chunk_empty, + .other_init_msg = chunk_empty, + ); + + if (!this->isa_ctx_id) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_keymat.h b/src/charon-tkm/src/tkm/tkm_keymat.h new file mode 100644 index 000000000..ee90bead5 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_keymat.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-keymat keymat + * @{ @ingroup tkm + */ + +#ifndef TKM_KEYMAT_H_ +#define TKM_KEYMAT_H_ + +#include + +typedef struct tkm_keymat_t tkm_keymat_t; + +/** + * Derivation and management of sensitive keying material, TKM variant. + */ +struct tkm_keymat_t { + + /** + * Implements keymat_v2_t. + */ + keymat_v2_t keymat_v2; + + /** + * Get ISA context id. + * + * @return id of associated ISA context. + */ + isa_id_type (*get_isa_id)(tkm_keymat_t * const this); + + /** + * Set IKE AUTH payload. + * + * @param payload AUTH payload + */ + void (*set_auth_payload)(tkm_keymat_t *this, const chunk_t * const payload); + + /** + * Get IKE AUTH payload. + * + * @return AUTH payload if set, chunk_empty otherwise + */ + chunk_t* (*get_auth_payload)(tkm_keymat_t * const this); + + /** + * Get IKE init message of peer. + * + * @return init message if set, chunk_empty otherwise + */ + chunk_t* (*get_peer_init_msg)(tkm_keymat_t * const this); + +}; + +/** + * Create TKM keymat instance. + * + * @param initiator TRUE if we are the initiator + * @return keymat instance + */ +tkm_keymat_t *tkm_keymat_create(bool initiator); + +#endif /** KEYMAT_TKM_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_listener.c b/src/charon-tkm/src/tkm/tkm_listener.c new file mode 100644 index 000000000..050586456 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_listener.c @@ -0,0 +1,355 @@ +/* + * Copyrigth (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include +#include +#include + +#include "tkm.h" +#include "tkm_listener.h" +#include "tkm_keymat.h" +#include "tkm_utils.h" + +typedef struct private_tkm_listener_t private_tkm_listener_t; + +/** + * Private data of a tkm_listener_t object. + */ +struct private_tkm_listener_t { + + /** + * Public tkm_listener_t interface. + */ + tkm_listener_t public; + +}; + +/** + * Return id of remote identity. + * + * TODO: Replace this with the lookup for the remote identitiy id. + * + * Currently the reqid of the first child SA in peer config of IKE SA is + * returned. Might choose wrong reqid if IKE SA has multiple child configs + * with different reqids. + * + * @param peer_cfg Remote peer config + * @return remote identity id if found, 0 otherwise + */ +static ri_id_type get_remote_identity_id(peer_cfg_t *peer) +{ + ri_id_type remote_id = 0; + child_cfg_t *child; + enumerator_t* children; + + children = peer->create_child_cfg_enumerator(peer); + + /* pick the reqid of the first child, no need to enumerate all children. */ + children->enumerate(children, &child); + remote_id = child->get_reqid(child); + children->destroy(children); + + return remote_id; +} + +/** + * Build a TKM certificate chain context with given cc id. + * + * @param ike_sa IKE SA containing auth config to build certificate chain from + * @param cc_id Certificate chain ID + * @return TRUE if certificate chain was built successfully, + * FALSE otherwise + */ +static bool build_cert_chain(const ike_sa_t * const ike_sa, cc_id_type cc_id) +{ + auth_cfg_t *auth; + certificate_t *cert; + enumerator_t *rounds; + + DBG1(DBG_IKE, "building certificate chain context %llu for IKE SA %s", + cc_id, ike_sa->get_name((ike_sa_t *)ike_sa)); + + rounds = ike_sa->create_auth_cfg_enumerator((ike_sa_t *)ike_sa, FALSE); + while (rounds->enumerate(rounds, &auth)) + { + cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + chunk_t enc_user_cert; + ri_id_type ri_id; + certificate_type user_cert; + auth_rule_t rule; + enumerator_t *enumerator; + + /* set user certificate */ + if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_user_cert)) + { + DBG1(DBG_IKE, "unable to extract encoded user certificate"); + rounds->destroy(rounds); + return FALSE; + } + + ri_id = get_remote_identity_id(ike_sa->get_peer_cfg((ike_sa_t *)ike_sa)); + chunk_to_sequence(&enc_user_cert, &user_cert, sizeof(certificate_type)); + chunk_free(&enc_user_cert); + if (ike_cc_set_user_certificate(cc_id, ri_id, 1, user_cert) != TKM_OK) + { + DBG1(DBG_IKE, "error setting user certificate of cert chain" + " (cc_id: %llu)", cc_id); + rounds->destroy(rounds); + return FALSE; + } + + /* process intermediate CA certificates */ + enumerator = auth->create_enumerator(auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_IM_CERT) + { + chunk_t enc_im_cert; + certificate_type im_cert; + + if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_im_cert)) + { + DBG1(DBG_IKE, "unable to extract encoded intermediate CA" + " certificate"); + rounds->destroy(rounds); + enumerator->destroy(enumerator); + return FALSE; + } + + chunk_to_sequence(&enc_im_cert, &im_cert, + sizeof(certificate_type)); + chunk_free(&enc_im_cert); + if (ike_cc_add_certificate(cc_id, 1, im_cert) != TKM_OK) + { + DBG1(DBG_IKE, "error adding intermediate certificate to" + " cert chain (cc_id: %llu)", cc_id); + rounds->destroy(rounds); + enumerator->destroy(enumerator); + return FALSE; + } + } + } + enumerator->destroy(enumerator); + + /* finally add CA certificate */ + cert = auth->get(auth, AUTH_RULE_CA_CERT); + if (cert) + { + const ca_id_type ca_id = 1; + certificate_type ca_cert; + chunk_t enc_ca_cert; + + if (!cert->get_encoding(cert, CERT_ASN1_DER, &enc_ca_cert)) + { + DBG1(DBG_IKE, "unable to extract encoded CA certificate"); + rounds->destroy(rounds); + return FALSE; + } + + chunk_to_sequence(&enc_ca_cert, &ca_cert, + sizeof(certificate_type)); + chunk_free(&enc_ca_cert); + if (ike_cc_add_certificate(cc_id, 1, ca_cert) != TKM_OK) + { + DBG1(DBG_IKE, "error adding CA certificate to cert chain " + "(cc_id: %llu)", cc_id); + rounds->destroy(rounds); + return FALSE; + } + + if (ike_cc_check_ca(cc_id, ca_id) != TKM_OK) + { + DBG1(DBG_IKE, "certificate chain (cc_id: %llu) not based on" + " trusted CA (ca_id: %llu)", cc_id, ca_id); + rounds->destroy(rounds); + return FALSE; + } + + rounds->destroy(rounds); + return TRUE; + } + else + { + DBG1(DBG_IKE, "no CA certificate"); + } + } + else + { + DBG1(DBG_IKE, "no subject certificate for remote peer"); + } + } + + rounds->destroy(rounds); + return FALSE; +} + +METHOD(listener_t, alert, bool, + private_tkm_listener_t *this, ike_sa_t *ike_sa, + alert_t alert, va_list args) +{ + if (alert == ALERT_KEEP_ON_CHILD_SA_FAILURE) + { + tkm_keymat_t *keymat; + isa_id_type isa_id; + + keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa); + isa_id = keymat->get_isa_id(keymat); + + DBG1(DBG_IKE, "TKM alert listener called for ISA context %llu", isa_id); + if (ike_isa_skip_create_first(isa_id) != TKM_OK) + { + DBG1(DBG_IKE, "Skip of first child SA creation failed for ISA " + "context %llu", isa_id); + } + } + + return TRUE; +} + +METHOD(listener_t, authorize, bool, + private_tkm_listener_t *this, ike_sa_t *ike_sa, + bool final, bool *success) +{ + tkm_keymat_t *keymat; + isa_id_type isa_id; + cc_id_type cc_id; + chunk_t *auth, *other_init_msg; + signature_type signature; + init_message_type init_msg; + + if (!final) + { + return TRUE; + } + + keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa); + isa_id = keymat->get_isa_id(keymat); + DBG1(DBG_IKE, "TKM authorize listener called for ISA context %llu", isa_id); + + cc_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_CC); + if (!cc_id) + { + DBG1(DBG_IKE, "unable to acquire CC context id"); + *success = FALSE; + return TRUE; + } + if (!build_cert_chain(ike_sa, cc_id)) + { + DBG1(DBG_IKE, "unable to build certificate chain"); + *success = FALSE; + return TRUE; + } + + auth = keymat->get_auth_payload(keymat); + if (!auth->ptr) + { + DBG1(DBG_IKE, "no AUTHENTICATION data available"); + *success = FALSE; + } + + other_init_msg = keymat->get_peer_init_msg(keymat); + if (!other_init_msg->ptr) + { + DBG1(DBG_IKE, "no peer init message available"); + *success = FALSE; + } + + chunk_to_sequence(auth, &signature, sizeof(signature_type)); + chunk_to_sequence(other_init_msg, &init_msg, sizeof(init_message_type)); + + if (ike_isa_auth(isa_id, cc_id, init_msg, signature) != TKM_OK) + { + DBG1(DBG_IKE, "TKM based authentication failed" + " for ISA context %llu", isa_id); + *success = FALSE; + } + else + { + DBG1(DBG_IKE, "TKM based authentication successful" + " for ISA context %llu", isa_id); + *success = TRUE; + } + + return TRUE; +} + +METHOD(listener_t, message, bool, + private_tkm_listener_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + tkm_keymat_t *keymat; + isa_id_type isa_id; + auth_payload_t *auth_payload; + + if (!incoming || !plain || message->get_exchange_type(message) != IKE_AUTH) + { + return TRUE; + } + + keymat = (tkm_keymat_t*)ike_sa->get_keymat(ike_sa); + isa_id = keymat->get_isa_id(keymat); + DBG1(DBG_IKE, "saving AUTHENTICATION payload for authorize hook" + " (ISA context %llu)", isa_id); + + auth_payload = (auth_payload_t*)message->get_payload(message, + AUTHENTICATION); + if (auth_payload) + { + chunk_t auth_data; + + auth_data = auth_payload->get_data(auth_payload); + keymat->set_auth_payload(keymat, &auth_data); + } + else + { + DBG1(DBG_IKE, "unable to extract AUTHENTICATION payload, authorize will" + " fail"); + } + + return TRUE; +} + +METHOD(tkm_listener_t, destroy, void, + private_tkm_listener_t *this) +{ + free(this); +} + +/** + * See header + */ +tkm_listener_t *tkm_listener_create() +{ + private_tkm_listener_t *this; + + INIT(this, + .public = { + .listener = { + .authorize = _authorize, + .message = _message, + .alert = _alert, + }, + .destroy = _destroy, + }, + ); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_listener.h b/src/charon-tkm/src/tkm/tkm_listener.h new file mode 100644 index 000000000..1162a77be --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_listener.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-listener listener + * @{ @ingroup tkm + */ + +#ifndef TKM_LISTENER_H_ +#define TKM_LISTENER_H_ + +#include + +typedef struct tkm_listener_t tkm_listener_t; + +/** + * TKM bus listener. + */ +struct tkm_listener_t { + + /** + * Implements listener_t interface. + */ + listener_t listener; + + /** + * Destroy a tkm_listener_t. + */ + void (*destroy)(tkm_listener_t *this); +}; + +/** + * Create a tkm_listener instance. + * + * @return listener instance + */ +tkm_listener_t *tkm_listener_create(); + +#endif /** TKM_LISTENER_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_nonceg.c b/src/charon-tkm/src/tkm/tkm_nonceg.c new file mode 100644 index 000000000..a07326798 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_nonceg.c @@ -0,0 +1,106 @@ +/* + * Copyrigth (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include + +#include "tkm.h" +#include "tkm_nonceg.h" + +typedef struct private_tkm_nonceg_t private_tkm_nonceg_t; + +/** + * Private data of a tkm_nonceg_t object. + */ +struct private_tkm_nonceg_t { + + /** + * Public tkm_nonceg_t interface. + */ + tkm_nonceg_t public; + + /** + * Context id. + */ + nc_id_type context_id; + +}; + +METHOD(nonce_gen_t, get_nonce, bool, + private_tkm_nonceg_t *this, size_t size, u_int8_t *buffer) +{ + nonce_type nonce; + + if (ike_nc_create(this->context_id, size, &nonce) != TKM_OK) + { + return FALSE; + } + + memcpy(buffer, &nonce.data, size); + return TRUE; +} + +METHOD(nonce_gen_t, allocate_nonce, bool, + private_tkm_nonceg_t *this, size_t size, chunk_t *chunk) +{ + *chunk = chunk_alloc(size); + if (get_nonce(this, chunk->len, chunk->ptr)) + { + tkm->chunk_map->insert(tkm->chunk_map, chunk, this->context_id); + return TRUE; + } + return FALSE; +} + +METHOD(nonce_gen_t, destroy, void, + private_tkm_nonceg_t *this) +{ + free(this); +} + +METHOD(tkm_nonceg_t, get_id, nc_id_type, + private_tkm_nonceg_t *this) +{ + return this->context_id; +} + +/* + * Described in header. + */ +tkm_nonceg_t *tkm_nonceg_create() +{ + private_tkm_nonceg_t *this; + + INIT(this, + .public = { + .nonce_gen = { + .get_nonce = _get_nonce, + .allocate_nonce = _allocate_nonce, + .destroy = _destroy, + }, + .get_id = _get_id, + }, + .context_id = tkm->idmgr->acquire_id(tkm->idmgr, TKM_CTX_NONCE), + ); + + if (!this->context_id) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_nonceg.h b/src/charon-tkm/src/tkm/tkm_nonceg.h new file mode 100644 index 000000000..ceadb081f --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_nonceg.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-nonceg nonce generator + * @{ @ingroup tkm + */ + +#ifndef TKM_NONCEG_H_ +#define TKM_NONCEG_H_ + +typedef struct tkm_nonceg_t tkm_nonceg_t; + +#include +#include + +/** + * nonce_gen_t implementation using the trusted key manager. + */ +struct tkm_nonceg_t { + + /** + * Implements nonce_gen_t. + */ + nonce_gen_t nonce_gen; + + /** + * Get nonce context id. + * + * @return context id of this nonce generator. + */ + nc_id_type (*get_id)(tkm_nonceg_t * const this); + +}; + +/** + * Creates a tkm_nonceg_t instance. + * + * @return created tkm_nonceg_t + */ +tkm_nonceg_t *tkm_nonceg_create(); + +#endif /** TKM_NONCEG_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_private_key.c b/src/charon-tkm/src/tkm/tkm_private_key.c new file mode 100644 index 000000000..db57ec1c7 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_private_key.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012-2013 Reto Buerki + * Copyright (C) 2012-2013 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "tkm_utils.h" +#include "tkm_types.h" +#include "tkm_private_key.h" + +typedef struct private_tkm_private_key_t private_tkm_private_key_t; + +/** + * Private data of a tkm_private_key_t object. + */ +struct private_tkm_private_key_t { + + /** + * Public interface for this signer. + */ + tkm_private_key_t public; + + /** + * Key ID. + */ + identification_t *id; + + /** + * Key type. + */ + key_type_t key_type; + + /** + * Reference count. + */ + refcount_t ref; + +}; + +METHOD(private_key_t, get_type, key_type_t, + private_tkm_private_key_t *this) +{ + return this->key_type; +} + +METHOD(private_key_t, sign, bool, + private_tkm_private_key_t *this, signature_scheme_t scheme, + chunk_t data, chunk_t *signature) +{ + signature_type sig; + init_message_type msg; + sign_info_t sign; + isa_id_type isa_id; + + if (data.ptr == NULL) + { + DBG1(DBG_LIB, "unable to get signature information"); + return FALSE; + } + sign = *(sign_info_t *)(data.ptr); + + chunk_to_sequence(&sign.init_message, &msg, sizeof(init_message_type)); + isa_id = sign.isa_id; + chunk_free(&sign.init_message); + + if (ike_isa_sign(isa_id, 1, msg, &sig) != TKM_OK) + { + DBG1(DBG_LIB, "signature operation failed"); + return FALSE; + } + + sequence_to_chunk(sig.data, sig.size, signature); + return TRUE; +} + +METHOD(private_key_t, decrypt, bool, + private_tkm_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + return FALSE; +} + +METHOD(private_key_t, get_keysize, int, + private_tkm_private_key_t *this) +{ + return 0; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_tkm_private_key_t *this) +{ + return NULL; +} + +METHOD(private_key_t, get_encoding, bool, + private_tkm_private_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + return FALSE; +} + +METHOD(private_key_t, get_fingerprint, bool, + private_tkm_private_key_t *this, cred_encoding_type_t type, chunk_t *fp) +{ + *fp = this->id->get_encoding(this->id); + return TRUE; +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_tkm_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(private_key_t, destroy, void, + private_tkm_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + this->id->destroy(this->id); + free(this); + } +} + +/** + * See header. + */ +tkm_private_key_t *tkm_private_key_init(identification_t * const id) +{ + private_tkm_private_key_t *this; + certificate_t *cert; + public_key_t *pubkey; + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .sign = _sign, + .decrypt = _decrypt, + .get_keysize = _get_keysize, + .get_public_key = _get_public_key, + .equals = private_key_equals, + .belongs_to = private_key_belongs_to, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = private_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + .id = id->clone(id), + ); + + /* get key type from associated public key */ + cert = lib->credmgr->get_cert(lib->credmgr, CERT_ANY, KEY_ANY, id, FALSE); + if (!cert) + { + destroy(this); + return NULL; + } + + pubkey = cert->get_public_key(cert); + if (!pubkey) + { + cert->destroy(cert); + destroy(this); + return NULL; + } + this->key_type = pubkey->get_type(pubkey); + pubkey->destroy(pubkey); + cert->destroy(cert); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_private_key.h b/src/charon-tkm/src/tkm/tkm_private_key.h new file mode 100644 index 000000000..ded8300ca --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_private_key.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-privkey private key + * @{ @ingroup tkm + */ + +#ifndef TKM_PRIVATE_KEY_H_ +#define TKM_PRIVATE_KEY_H_ + +#include + +typedef struct tkm_private_key_t tkm_private_key_t; + +/** + * TKM private_key_t implementation. + */ +struct tkm_private_key_t { + + /** + * Implements private_key_t interface + */ + private_key_t key; +}; + +/** + * Initialize TKM private key with given key ID. + */ +tkm_private_key_t *tkm_private_key_init(identification_t * const id); + +#endif /** TKM_PRIVATE_KEY_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_public_key.c b/src/charon-tkm/src/tkm/tkm_public_key.c new file mode 100644 index 000000000..9ebdc29e6 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_public_key.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012-2013 Reto Buerki + * Copyright (C) 2012-2013 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_public_key.h" + +typedef struct private_tkm_public_key_t private_tkm_public_key_t; + +/** + * Private data of tkm_public_key_t object. + */ +struct private_tkm_public_key_t { + + /** + * Public interface for this signer. + */ + tkm_public_key_t public; + + /** + * ASN.1 blob of pubkey. + */ + chunk_t asn_blob; + + /** + * Key type. + */ + key_type_t key_type; + + /** + * Reference count. + */ + refcount_t ref; +}; + +METHOD(public_key_t, get_type, key_type_t, + private_tkm_public_key_t *this) +{ + return this->key_type; +} + +METHOD(public_key_t, verify, bool, + private_tkm_public_key_t *this, signature_scheme_t scheme, + chunk_t data, chunk_t signature) +{ + return TRUE; +} + +METHOD(public_key_t, encrypt_, bool, + private_tkm_public_key_t *this, encryption_scheme_t scheme, + chunk_t plain, chunk_t *crypto) +{ + return FALSE; +} + +METHOD(public_key_t, get_keysize, int, + private_tkm_public_key_t *this) +{ + return 0; +} + +METHOD(public_key_t, get_encoding, bool, + private_tkm_public_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + return NULL; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_tkm_public_key_t *this, cred_encoding_type_t type, chunk_t *fp) +{ + if (lib->encoding->get_cache(lib->encoding, type, this, fp)) + { + return TRUE; + } + switch(this->key_type) + { + case KEY_RSA: + return lib->encoding->encode(lib->encoding, type, this, fp, + CRED_PART_RSA_PUB_ASN1_DER, + this->asn_blob, CRED_PART_END); + default: + DBG1(DBG_LIB, "%N public key not supported, fingerprinting failed", + key_type_names, this->key_type); + return FALSE; + } +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_tkm_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(public_key_t, destroy, void, + private_tkm_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this); + chunk_free(&this->asn_blob); + free(this); + } +} + +/** + * See header. + */ +tkm_public_key_t *tkm_public_key_load(key_type_t type, va_list args) +{ + private_tkm_public_key_t *this; + chunk_t blob = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (!blob.ptr) + { + return NULL; + } + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .verify = _verify, + .encrypt = _encrypt_, + .equals = public_key_equals, + .get_keysize = _get_keysize, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = public_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + .asn_blob = chunk_clone(blob), + .key_type = type, + ); + + return &this->public; +} diff --git a/src/charon-tkm/src/tkm/tkm_public_key.h b/src/charon-tkm/src/tkm/tkm_public_key.h new file mode 100644 index 000000000..5b21287b7 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_public_key.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012-2013 Reto Buerki + * Copyright (C) 2012-2013 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-pubkey public key + * @{ @ingroup tkm + */ + +#ifndef TKM_PUBLIC_KEY_H_ +#define TKM_PUBLIC_KEY_H_ + +#include + +typedef struct tkm_public_key_t tkm_public_key_t; + +/** + * TKM public_key_t implementation. + */ +struct tkm_public_key_t { + + /** + * Implements the public_key_t interface + */ + public_key_t key; +}; + +/** + * Load a TKM public key. + * + * @param type type of the key + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +tkm_public_key_t *tkm_public_key_load(key_type_t type, va_list args); + +#endif /** TKM_PUBLIC_KEY_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_types.h b/src/charon-tkm/src/tkm/tkm_types.h new file mode 100644 index 000000000..cef53deb3 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_types.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-types types + * @{ @ingroup tkm + */ + +#ifndef TKM_TYPES_H_ +#define TKM_TYPES_H_ + +#include +#include + +typedef struct esa_info_t esa_info_t; + +/** + * ESP SA info data structure. + * + * This type is used to transfer ESA information from the keymat + * derive_child_keys to the kernel IPsec interface add_sa operation. This is + * necessary because the CHILD SA key derivation and installation is handled + * by a single exchange with the TKM (esa_create*) in add_sa. + * For this purpose the out parameters encr_i and encr_r of the + * derive_child_keys function are (ab)used and the data is stored in these + * data chunks. This is possible since the child SA keys are treated as opaque + * values and handed to the add_sa procedure of the kernel interface as-is + * without any processing. + */ +struct esa_info_t { + + /** + * ISA context id. + */ + isa_id_type isa_id; + + /** + * Responder SPI of child SA. + */ + esp_spi_type spi_r; + + /** + * Initiator nonce. + */ + chunk_t nonce_i; + + /** + * Responder nonce. + */ + chunk_t nonce_r; + + /** + * Flag specifying if this esa info struct is contained in encr_r. + * It is set to TRUE for encr_r and FALSE for encr_i. + */ + bool is_encr_r; + + /** + * Diffie-Hellman context id. + */ + dh_id_type dh_id; + +}; + +typedef struct isa_info_t isa_info_t; + +/** + * IKE SA info data structure. + * + * This type is used to transfer ISA information from the keymat of the parent + * SA to the keymat of the new IKE SA. For this purpose the skd data chunk is + * (ab)used. This is possible since the sk_d chunk is treated as an opaque value + * and handed to the derive_ike_keys procedure of the new keymat as-is without + * any processing. + */ +struct isa_info_t { + + /** + * Parent isa context id. + */ + isa_id_type parent_isa_id; + + /** + * Authenticated endpoint context id. + */ + ae_id_type ae_id; + +}; + +typedef struct sign_info_t sign_info_t; + +/** + * AUTH signature info data structure. + * + * This type is used to transfer an ISA context id and the initial message + * from the keymat to the TKM private key sign operation. For this purpose the + * auth octets chunk is (ab)used and the data is stored in this chunk. + * This is possible since the auth octets are treated as opaque value and handed + * to the private key sign function as-is without any processing. + */ +struct sign_info_t { + + /** + * ISA context id. + */ + isa_id_type isa_id; + + /** + * Init message. + */ + chunk_t init_message; + +}; + +#endif /** TKM_TYPES_H_ @}*/ diff --git a/src/charon-tkm/src/tkm/tkm_utils.c b/src/charon-tkm/src/tkm/tkm_utils.c new file mode 100644 index 000000000..e0692b893 --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_utils.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_utils.h" + +/* Generic variable-length sequence */ +struct sequence_type { + uint32_t size; + byte_t data[]; +}; +typedef struct sequence_type sequence_type; + +void sequence_to_chunk(const byte_t * const first, const uint32_t len, + chunk_t * const chunk) +{ + *chunk = chunk_alloc(len); + memcpy(chunk->ptr, first, len); +} + +void chunk_to_sequence(const chunk_t * const chunk, void *sequence, + const uint32_t typelen) +{ + const uint32_t seqlenmax = typelen - sizeof(uint32_t); + sequence_type *seq = sequence; + + memset(sequence, 0, typelen); + if (chunk->len > seqlenmax) + { + DBG1(DBG_LIB, "chunk too large to fit into sequence %d > %d, limiting" + " to %d bytes", chunk->len, seqlenmax, seqlenmax); + seq->size = seqlenmax; + } + else + { + seq->size = chunk->len; + } + memcpy(seq->data, chunk->ptr, seq->size); +} diff --git a/src/charon-tkm/src/tkm/tkm_utils.h b/src/charon-tkm/src/tkm/tkm_utils.h new file mode 100644 index 000000000..308c58fbb --- /dev/null +++ b/src/charon-tkm/src/tkm/tkm_utils.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tkm-utils utils + * @{ @ingroup tkm + */ + +#ifndef TKM_UTILS_H_ +#define TKM_UTILS_H_ + +#include +#include + +/** + * Convert byte sequence to chunk. + * + * @param first pointer to first byte of sequence + * @param len length of byte sequence + * @param chunk pointer to chunk struct + */ +void sequence_to_chunk(const byte_t * const first, const uint32_t len, + chunk_t * const chunk); + +/** + * Convert chunk to variable-length byte sequence. + * + * @param chunk pointer to chunk struct + * @param sequence pointer to variable-length sequence + * @param typelen length of sequence type + */ +void chunk_to_sequence(const chunk_t * const chunk, void *sequence, + const uint32_t typelen); + +#endif /** TKM_UTILS_H_ @}*/ diff --git a/src/charon-tkm/tests/.gitignore b/src/charon-tkm/tests/.gitignore new file mode 100644 index 000000000..35429f617 --- /dev/null +++ b/src/charon-tkm/tests/.gitignore @@ -0,0 +1 @@ +test_runner diff --git a/src/charon-tkm/tests/chunk_map_tests.c b/src/charon-tkm/tests/chunk_map_tests.c new file mode 100644 index 000000000..6deef9a80 --- /dev/null +++ b/src/charon-tkm/tests/chunk_map_tests.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_chunk_map.h" + +START_TEST(test_chunk_map_creation) +{ + tkm_chunk_map_t *map = NULL; + + map = tkm_chunk_map_create(); + fail_if(map == NULL, "Error creating chunk map"); + + map->destroy(map); +} +END_TEST + +START_TEST(test_chunk_map_handling) +{ + tkm_chunk_map_t *map = NULL; + const int ref = 35; + chunk_t data = chunk_from_thing(ref); + + map = tkm_chunk_map_create(); + fail_if(map == NULL, "Error creating chunk map"); + + map->insert(map, &data, 24); + fail_if(map->get_id(map, &data) != 24, "Id mismatch"); + + fail_unless(map->remove(map, &data), "Unable to remove mapping"); + fail_unless(!map->get_id(map, &data), "Error removing mapping"); + + map->destroy(map); +} +END_TEST + +TCase *make_chunk_map_tests(void) +{ + TCase *tc = tcase_create("Chunk map tests"); + tcase_add_test(tc, test_chunk_map_creation); + tcase_add_test(tc, test_chunk_map_handling); + + return tc; +} diff --git a/src/charon-tkm/tests/diffie_hellman_tests.c b/src/charon-tkm/tests/diffie_hellman_tests.c new file mode 100644 index 000000000..ffe99614d --- /dev/null +++ b/src/charon-tkm/tests/diffie_hellman_tests.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_diffie_hellman.h" + +START_TEST(test_dh_creation) +{ + tkm_diffie_hellman_t *dh = NULL; + + dh = tkm_diffie_hellman_create(MODP_768_BIT); + fail_if(dh, "MODP_768 created"); + + dh = tkm_diffie_hellman_create(MODP_4096_BIT); + fail_if(!dh, "MODP_4096 not created"); + fail_if(!dh->get_id(dh), "Invalid context id (0)"); + + dh->dh.destroy(&dh->dh); +} +END_TEST + +START_TEST(test_dh_get_my_pubvalue) +{ + tkm_diffie_hellman_t *dh = tkm_diffie_hellman_create(MODP_4096_BIT); + fail_if(!dh, "Unable to create DH"); + + chunk_t value; + dh->dh.get_my_public_value(&dh->dh, &value); + dh->dh.destroy(&dh->dh); + + fail_if(value.ptr == NULL, "Pubvalue is NULL"); + fail_if(value.len != 512, "Pubvalue size mismatch"); + + chunk_free(&value); +} +END_TEST + +TCase *make_diffie_hellman_tests(void) +{ + TCase *tc = tcase_create("Diffie-Hellman tests"); + tcase_add_test(tc, test_dh_creation); + tcase_add_test(tc, test_dh_get_my_pubvalue); + + return tc; +} diff --git a/src/charon-tkm/tests/id_manager_tests.c b/src/charon-tkm/tests/id_manager_tests.c new file mode 100644 index 000000000..15522f118 --- /dev/null +++ b/src/charon-tkm/tests/id_manager_tests.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_id_manager.h" + +static const tkm_limits_t limits = {125, 100, 55, 30, 200, 42}; + +START_TEST(test_id_mgr_creation) +{ + tkm_id_manager_t *idmgr = NULL; + + idmgr = tkm_id_manager_create(limits); + fail_if(idmgr == NULL, "Error creating tkm id manager"); + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_acquire_id) +{ + int i, id = 0; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + for (i = 0; i < TKM_CTX_MAX; i++) + { + id = idmgr->acquire_id(idmgr, i); + fail_unless(id > 0, "Error acquiring id of context kind %d", i); + + /* Reset test variable */ + id = 0; + } + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_acquire_id_invalid_kind) +{ + int id = 0; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + id = idmgr->acquire_id(idmgr, TKM_CTX_MAX); + fail_unless(id == 0, "Acquired id for invalid context kind %d", TKM_CTX_MAX); + + /* Reset test variable */ + id = 0; + + id = idmgr->acquire_id(idmgr, -1); + fail_unless(id == 0, "Acquired id for invalid context kind %d", -1); + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_acquire_id_same) +{ + int id1 = 0, id2 = 0; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + id1 = idmgr->acquire_id(idmgr, TKM_CTX_NONCE); + fail_unless(id1 > 0, "Unable to acquire first id"); + + /* Acquire another id, must be different than first */ + id2 = idmgr->acquire_id(idmgr, TKM_CTX_NONCE); + fail_unless(id2 > 0, "Unable to acquire second id"); + fail_unless(id1 != id2, "Same id received twice"); + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_release_id) +{ + int i, id = 0; + bool released = false; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + for (i = 0; i < TKM_CTX_MAX; i++) + { + id = idmgr->acquire_id(idmgr, i); + released = idmgr->release_id(idmgr, i, id); + + fail_unless(released, "Error releasing id of context kind %d", i); + + /* Reset released variable */ + released = FALSE; + } + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_release_id_invalid_kind) +{ + bool released = TRUE; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + released = idmgr->release_id(idmgr, TKM_CTX_MAX, 1); + fail_if(released, "Released id for invalid context kind %d", TKM_CTX_MAX); + + /* Reset test variable */ + released = TRUE; + + released = idmgr->release_id(idmgr, -1, 1); + fail_if(released, "Released id for invalid context kind %d", -1); + + idmgr->destroy(idmgr); +} +END_TEST + +START_TEST(test_release_id_nonexistent) +{ + bool released = FALSE; + tkm_id_manager_t *idmgr = tkm_id_manager_create(limits); + + released = idmgr->release_id(idmgr, TKM_CTX_NONCE, 1); + fail_unless(released, "Release of nonexistent id failed"); + + idmgr->destroy(idmgr); +} +END_TEST + +TCase *make_id_manager_tests(void) +{ + TCase *tc = tcase_create("Context id manager tests"); + tcase_add_test(tc, test_id_mgr_creation); + tcase_add_test(tc, test_acquire_id); + tcase_add_test(tc, test_acquire_id_invalid_kind); + tcase_add_test(tc, test_acquire_id_same); + tcase_add_test(tc, test_release_id); + tcase_add_test(tc, test_release_id_invalid_kind); + tcase_add_test(tc, test_release_id_nonexistent); + + return tc; +} diff --git a/src/charon-tkm/tests/kernel_sad_tests.c b/src/charon-tkm/tests/kernel_sad_tests.c new file mode 100644 index 000000000..11785602d --- /dev/null +++ b/src/charon-tkm/tests/kernel_sad_tests.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 + +#include "tkm_kernel_sad.h" + +START_TEST(test_sad_creation) +{ + tkm_kernel_sad_t *sad = NULL; + + sad = tkm_kernel_sad_create(); + fail_if(!sad, "Error creating tkm kernel SAD"); + + sad->destroy(sad); +} +END_TEST + +START_TEST(test_insert) +{ + host_t *addr = host_create_from_string("127.0.0.1", 1024); + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + + fail_unless(sad->insert(sad, 1, addr, addr, 42, 50), + "Error inserting SAD entry"); + + sad->destroy(sad); + addr->destroy(addr); +} +END_TEST + +START_TEST(test_insert_duplicate) +{ + host_t *addr = host_create_from_string("127.0.0.1", 1024); + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + + fail_unless(sad->insert(sad, 1, addr, addr, 42, 50), + "Error inserting SAD entry"); + fail_if(sad->insert(sad, 1, addr, addr, 42, 50), + "Expected error inserting duplicate entry"); + + sad->destroy(sad); + addr->destroy(addr); +} +END_TEST + +START_TEST(test_get_esa_id) +{ + host_t *addr = host_create_from_string("127.0.0.1", 1024); + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + fail_unless(sad->insert(sad, 23, addr, addr, 42, 50), + "Error inserting SAD entry"); + fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 23, + "Error getting esa id"); + sad->destroy(sad); + addr->destroy(addr); +} +END_TEST + +START_TEST(test_get_esa_id_nonexistent) +{ + host_t *addr = host_create_from_string("127.0.0.1", 1024); + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 0, + "Got esa id for nonexistent SAD entry"); + sad->destroy(sad); + addr->destroy(addr); +} +END_TEST + +START_TEST(test_remove) +{ + host_t *addr = host_create_from_string("127.0.0.1", 1024); + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + fail_unless(sad->insert(sad, 23, addr, addr, 42, 50), + "Error inserting SAD entry"); + fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 23, + "Error getting esa id"); + fail_unless(sad->remove(sad, 23), + "Error removing SAD entry"); + fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 0, + "Got esa id for removed SAD entry"); + sad->destroy(sad); + addr->destroy(addr); +} +END_TEST + +START_TEST(test_remove_nonexistent) +{ + tkm_kernel_sad_t *sad = tkm_kernel_sad_create(); + fail_if(sad->remove(sad, 1), + "Expected error removing nonexistent SAD entry"); + sad->destroy(sad); +} +END_TEST + +TCase *make_kernel_sad_tests(void) +{ + TCase *tc = tcase_create("Kernel SAD tests"); + tcase_add_test(tc, test_sad_creation); + tcase_add_test(tc, test_insert); + tcase_add_test(tc, test_insert_duplicate); + tcase_add_test(tc, test_get_esa_id); + tcase_add_test(tc, test_get_esa_id_nonexistent); + tcase_add_test(tc, test_remove); + tcase_add_test(tc, test_remove_nonexistent); + + return tc; +} diff --git a/src/charon-tkm/tests/keymat_tests.c b/src/charon-tkm/tests/keymat_tests.c new file mode 100644 index 000000000..2a7525d4e --- /dev/null +++ b/src/charon-tkm/tests/keymat_tests.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include +#include +#include +#include + +#include "tkm.h" +#include "tkm_nonceg.h" +#include "tkm_diffie_hellman.h" +#include "tkm_keymat.h" +#include "tkm_types.h" + +START_TEST(test_derive_ike_keys) +{ + proposal_t *proposal = proposal_create_from_string(PROTO_IKE, + "aes256-sha512-modp4096"); + fail_if(!proposal, "Unable to create proposal"); + ike_sa_id_t *ike_sa_id = ike_sa_id_create(IKEV2_MAJOR_VERSION, + 123912312312, 32312313122, TRUE); + fail_if(!ike_sa_id, "Unable to create IKE SA ID"); + + tkm_keymat_t *keymat = tkm_keymat_create(TRUE); + fail_if(!keymat, "Unable to create keymat"); + fail_if(!keymat->get_isa_id(keymat), "Invalid ISA context id (0)"); + + chunk_t nonce; + tkm_nonceg_t *ng = tkm_nonceg_create(); + fail_if(!ng, "Unable to create nonce generator"); + fail_unless(ng->nonce_gen.allocate_nonce(&ng->nonce_gen, 32, &nonce), + "Unable to allocate nonce"); + ng->nonce_gen.destroy(&ng->nonce_gen); + + tkm_diffie_hellman_t *dh = tkm_diffie_hellman_create(MODP_4096_BIT); + fail_if(!dh, "Unable to create DH"); + + /* Use the same pubvalue for both sides */ + chunk_t pubvalue; + dh->dh.get_my_public_value(&dh->dh, &pubvalue); + dh->dh.set_other_public_value(&dh->dh, pubvalue); + + fail_unless(keymat->keymat_v2.derive_ike_keys(&keymat->keymat_v2, proposal, + &dh->dh, nonce, nonce, ike_sa_id, PRF_UNDEFINED, chunk_empty), + "Key derivation failed"); + chunk_free(&nonce); + + aead_t * const aead = keymat->keymat_v2.keymat.get_aead(&keymat->keymat_v2.keymat, TRUE); + fail_if(!aead, "AEAD is NULL"); + + fail_if(aead->get_key_size(aead) != 96, "Key size mismatch %d", + aead->get_key_size(aead)); + fail_if(aead->get_block_size(aead) != 16, "Block size mismatch %d", + aead->get_block_size(aead)); + + proposal->destroy(proposal); + dh->dh.destroy(&dh->dh); + ike_sa_id->destroy(ike_sa_id); + keymat->keymat_v2.keymat.destroy(&keymat->keymat_v2.keymat); + chunk_free(&pubvalue); +} +END_TEST + +START_TEST(test_derive_child_keys) +{ + tkm_diffie_hellman_t *dh = tkm_diffie_hellman_create(MODP_4096_BIT); + fail_if(!dh, "Unable to create DH object"); + proposal_t *proposal = proposal_create_from_string(PROTO_ESP, + "aes256-sha512-modp4096"); + fail_if(!proposal, "Unable to create proposal"); + proposal->set_spi(proposal, 42); + + tkm_keymat_t *keymat = tkm_keymat_create(TRUE); + fail_if(!keymat, "Unable to create keymat"); + + chunk_t encr_i, encr_r, integ_i, integ_r; + chunk_t nonce = chunk_from_chars("test chunk"); + + fail_unless(keymat->keymat_v2.derive_child_keys(&keymat->keymat_v2, proposal, + (diffie_hellman_t *)dh, + nonce, nonce, &encr_i, + &integ_i, &encr_r, &integ_r), + "Child key derivation failed"); + + esa_info_t *info = (esa_info_t *)encr_i.ptr; + fail_if(!info, "encr_i does not contain esa information"); + fail_if(info->isa_id != keymat->get_isa_id(keymat), + "Isa context id mismatch (encr_i)"); + fail_if(info->spi_r != 42, + "SPI mismatch (encr_i)"); + fail_unless(chunk_equals(info->nonce_i, nonce), + "nonce_i mismatch (encr_i)"); + fail_unless(chunk_equals(info->nonce_r, nonce), + "nonce_r mismatch (encr_i)"); + fail_if(info->is_encr_r, + "Flag is_encr_r set for encr_i"); + fail_if(info->dh_id != dh->get_id(dh), + "DH context id mismatch (encr_i)"); + chunk_free(&info->nonce_i); + chunk_free(&info->nonce_r); + + info = (esa_info_t *)encr_r.ptr; + fail_if(!info, "encr_r does not contain esa information"); + fail_if(info->isa_id != keymat->get_isa_id(keymat), + "Isa context id mismatch (encr_r)"); + fail_if(info->spi_r != 42, + "SPI mismatch (encr_r)"); + fail_unless(chunk_equals(info->nonce_i, nonce), + "nonce_i mismatch (encr_r)"); + fail_unless(chunk_equals(info->nonce_r, nonce), + "nonce_r mismatch (encr_r)"); + fail_unless(info->is_encr_r, + "Flag is_encr_r set for encr_r"); + fail_if(info->dh_id != dh->get_id(dh), + "DH context id mismatch (encr_i)"); + chunk_free(&info->nonce_i); + chunk_free(&info->nonce_r); + + proposal->destroy(proposal); + dh->dh.destroy(&dh->dh); + keymat->keymat_v2.keymat.destroy(&keymat->keymat_v2.keymat); + chunk_free(&encr_i); + chunk_free(&encr_r); +} +END_TEST + +TCase *make_keymat_tests(void) +{ + TCase *tc = tcase_create("Keymat tests"); + tcase_add_test(tc, test_derive_ike_keys); + tcase_add_test(tc, test_derive_child_keys); + + return tc; +} diff --git a/src/charon-tkm/tests/nonceg_tests.c b/src/charon-tkm/tests/nonceg_tests.c new file mode 100644 index 000000000..3a1effab8 --- /dev/null +++ b/src/charon-tkm/tests/nonceg_tests.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include + +#include "tkm.h" +#include "tkm_nonceg.h" + +START_TEST(test_nonceg_creation) +{ + tkm_nonceg_t *ng = NULL; + + ng = tkm_nonceg_create(); + fail_if(ng == NULL, "Error creating tkm nonce generator"); + fail_if(ng->get_id(ng) == 0, "Invalid context id (0)"); + + ng->nonce_gen.destroy(&ng->nonce_gen); +} +END_TEST + +START_TEST(test_nonceg_allocate_nonce) +{ + tkm_nonceg_t *ng = tkm_nonceg_create(); + + const size_t length = 256; + u_int8_t zero[length]; + memset(zero, 0, length); + + chunk_t nonce; + const bool got_nonce = ng->nonce_gen.allocate_nonce(&ng->nonce_gen, + length, &nonce); + + fail_unless(got_nonce, "Call to allocate_nonce failed"); + fail_unless(nonce.len = length, "Allocated nonce length mismatch"); + fail_if(memcmp(nonce.ptr, zero, length) == 0, "Unable to allocate nonce"); + + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, 1); + ike_nc_reset(1); + + chunk_free(&nonce); + ng->nonce_gen.destroy(&ng->nonce_gen); +} +END_TEST + +START_TEST(test_nonceg_get_nonce) +{ + tkm_nonceg_t *ng = tkm_nonceg_create(); + + const size_t length = 128; + u_int8_t zero[length]; + memset(zero, 0, length); + + u_int8_t *buf = malloc(length + 1); + memset(buf, 0, length); + /* set end marker */ + buf[length] = 255; + + const bool got_nonce = ng->nonce_gen.get_nonce(&ng->nonce_gen, length, buf); + fail_unless(got_nonce, "Call to get_nonce failed"); + fail_if(memcmp(buf, zero, length) == 0, "Unable to get nonce"); + fail_if(buf[length] != 255, "End marker not found"); + + tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, 1); + ike_nc_reset(1); + + free(buf); + ng->nonce_gen.destroy(&ng->nonce_gen); +} +END_TEST + +TCase *make_nonceg_tests(void) +{ + TCase *tc = tcase_create("Nonce generator tests"); + tcase_add_test(tc, test_nonceg_creation); + tcase_add_test(tc, test_nonceg_allocate_nonce); + tcase_add_test(tc, test_nonceg_get_nonce); + + return tc; +} diff --git a/src/charon-tkm/tests/test_runner.c b/src/charon-tkm/tests/test_runner.c new file mode 100644 index 000000000..5ae032935 --- /dev/null +++ b/src/charon-tkm/tests/test_runner.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include +#include + +#include "tkm.h" +#include "tkm_nonceg.h" +#include "tkm_diffie_hellman.h" +#include "tkm_kernel_ipsec.h" +#include "test_runner.h" + +int main(void) +{ + library_init(NULL); + libhydra_init("test_runner"); + libcharon_init("test_runner"); + + lib->settings->set_int(lib->settings, "test_runner.filelog.stdout.default", + 1); + charon->load_loggers(charon, NULL, FALSE); + + /* Register TKM specific plugins */ + static plugin_feature_t features[] = { + PLUGIN_REGISTER(NONCE_GEN, tkm_nonceg_create), + PLUGIN_PROVIDE(NONCE_GEN), + PLUGIN_REGISTER(DH, tkm_diffie_hellman_create), + PLUGIN_PROVIDE(DH, MODP_3072_BIT), + PLUGIN_PROVIDE(DH, MODP_4096_BIT), + PLUGIN_CALLBACK(kernel_ipsec_register, tkm_kernel_ipsec_create), + PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), + }; + lib->plugins->add_static_features(lib->plugins, "tkm-tests", features, + countof(features), TRUE); + + if (!charon->initialize(charon, PLUGINS)) + { + fprintf(stderr, "Unable to init charon"); + return EXIT_FAILURE; + } + + if (!tkm_init()) + { + fprintf(stderr, "Could not connect to TKM, aborting tests\n"); + return EXIT_FAILURE; + } + + int number_failed; + Suite *s = suite_create("TKM tests"); + suite_add_tcase(s, make_id_manager_tests()); + suite_add_tcase(s, make_chunk_map_tests()); + suite_add_tcase(s, make_utility_tests()); + suite_add_tcase(s, make_nonceg_tests()); + suite_add_tcase(s, make_diffie_hellman_tests()); + suite_add_tcase(s, make_keymat_tests()); + suite_add_tcase(s, make_kernel_sad_tests()); + + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + + tkm_deinit(); + libcharon_deinit(); + libhydra_deinit(); + library_deinit(); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/charon-tkm/tests/test_runner.h b/src/charon-tkm/tests/test_runner.h new file mode 100644 index 000000000..236a7f2a6 --- /dev/null +++ b/src/charon-tkm/tests/test_runner.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 TEST_RUNNER_H_ +#define TEST_RUNNER_H_ + +#include + +TCase *make_id_manager_tests(void); +TCase *make_chunk_map_tests(void); +TCase *make_utility_tests(void); +TCase *make_nonceg_tests(void); +TCase *make_diffie_hellman_tests(void); +TCase *make_keymat_tests(void); +TCase *make_kernel_sad_tests(void); + +#endif /** TEST_RUNNER_H_ */ diff --git a/src/charon-tkm/tests/utils_tests.c b/src/charon-tkm/tests/utils_tests.c new file mode 100644 index 000000000..b3ead7633 --- /dev/null +++ b/src/charon-tkm/tests/utils_tests.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 Reto Buerki + * Copyright (C) 2012 Adrian-Ken Rueegsegger + * 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 . + * + * 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 +#include + +#include "tkm_utils.h" + +START_TEST(test_sequence_to_chunk) +{ + key_type key = {5, {0, 1, 2, 3, 4}}; + chunk_t chunk = chunk_empty; + + sequence_to_chunk(key.data, key.size, &chunk); + fail_if(chunk.len != key.size, "Chunk size mismatch"); + + uint32_t i; + for (i = 0; i < key.size; i++) + { + fail_if(chunk.ptr[i] != i, "Data mismatch"); + } + chunk_free(&chunk); +} +END_TEST + +START_TEST(test_chunk_to_sequence) +{ + chunk_t chunk = chunk_from_thing("ABCDEFGH"); + key_type key; + + chunk_to_sequence(&chunk, &key, sizeof(key_type)); + fail_if(key.size != chunk.len, "Seq size mismatch"); + + uint32_t i; + for (i = 0; i < key.size - 1; i++) + { + fail_if(key.data[i] != 65 + i, "Data mismatch (1)"); + } + fail_if(key.data[key.size - 1] != 0, "Data mismatch (2)"); +} +END_TEST + +TCase *make_utility_tests(void) +{ + TCase *tc = tcase_create("Utility tests"); + tcase_add_test(tc, test_sequence_to_chunk); + tcase_add_test(tc, test_chunk_to_sequence); + + return tc; +} diff --git a/src/ipsec/_ipsec.in b/src/ipsec/_ipsec.in index 6b406f6d9..3742b12c7 100644 --- a/src/ipsec/_ipsec.in +++ b/src/ipsec/_ipsec.in @@ -18,6 +18,9 @@ PATH="/sbin:/bin:/usr/sbin:/usr/bin:@IPSEC_SBINDIR@" export PATH +# set daemon name +[ -z "$DAEMON_NAME" ] && DAEMON_NAME="charon" + # name and version of the ipsec implementation OS_NAME=`uname -s` IPSEC_NAME="@IPSEC_NAME@" @@ -30,8 +33,8 @@ IPSEC_CONFDIR="@IPSEC_CONFDIR@" IPSEC_PIDDIR="@IPSEC_PIDDIR@" IPSEC_SCRIPT="@IPSEC_SCRIPT@" -IPSEC_STARTER_PID="${IPSEC_PIDDIR}/starter.pid" -IPSEC_CHARON_PID="${IPSEC_PIDDIR}/charon.pid" +IPSEC_STARTER_PID="${IPSEC_PIDDIR}/starter.${DAEMON_NAME}.pid" +IPSEC_CHARON_PID="${IPSEC_PIDDIR}/${DAEMON_NAME}.pid" IPSEC_STROKE="${IPSEC_DIR}/stroke" IPSEC_STARTER="${IPSEC_DIR}/starter" @@ -220,7 +223,7 @@ start) if [ -d /var/lock/subsys ]; then touch /var/lock/subsys/ipsec fi - exec $IPSEC_STARTER "$@" + exec $IPSEC_STARTER --daemon $DAEMON_NAME "$@" ;; status|statusall) op="$1" diff --git a/src/starter/confread.c b/src/starter/confread.c index 883534aad..f0f05b036 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -190,7 +190,7 @@ static void load_setup(starter_config_t *cfg, config_parsed_t *cfgp) /* verify the executables are actually available */ #ifdef START_CHARON cfg->setup.charonstart = cfg->setup.charonstart && - daemon_exists("charon", CHARON_CMD); + daemon_exists(daemon_name, cmd); #else cfg->setup.charonstart = FALSE; #endif diff --git a/src/starter/files.h b/src/starter/files.h index 96b76fdf1..76cdaa986 100644 --- a/src/starter/files.h +++ b/src/starter/files.h @@ -15,8 +15,6 @@ #ifndef _STARTER_FILES_H_ #define _STARTER_FILES_H_ -#define STARTER_PID_FILE IPSEC_PIDDIR "/starter.pid" - #define PROC_NETKEY "/proc/net/pfkey" #define PROC_KLIPS "/proc/net/pf_key" #define PROC_MODULES "/proc/modules" @@ -24,9 +22,11 @@ #define CONFIG_FILE IPSEC_CONFDIR "/ipsec.conf" #define SECRETS_FILE IPSEC_CONFDIR "/ipsec.secrets" -#define CHARON_CMD IPSEC_DIR "/charon" #define CHARON_CTL_FILE IPSEC_PIDDIR "/charon.ctl" -#define CHARON_PID_FILE IPSEC_PIDDIR "/charon.pid" + +extern char *daemon_name; +extern char *cmd; +extern char *pid_file; #define DYNIP_DIR IPSEC_PIDDIR "/dynip" diff --git a/src/starter/invokecharon.c b/src/starter/invokecharon.c index 1c93381f7..d981f6c17 100644 --- a/src/starter/invokecharon.c +++ b/src/starter/invokecharon.c @@ -46,22 +46,22 @@ void starter_charon_sigchild(pid_t pid, int status) if (status == SS_RC_LIBSTRONGSWAN_INTEGRITY || status == SS_RC_DAEMON_INTEGRITY) { - DBG1(DBG_APP, "charon has quit: integrity test of %s failed", - (status == 64) ? "libstrongswan" : "charon"); + DBG1(DBG_APP, "%s has quit: integrity test of %s failed", + daemon_name, (status == 64) ? "libstrongswan" : daemon_name); _stop_requested = 1; } else if (status == SS_RC_INITIALIZATION_FAILED) { - DBG1(DBG_APP, "charon has quit: initialization failed"); + DBG1(DBG_APP, "%s has quit: initialization failed", daemon_name); _stop_requested = 1; } if (!_stop_requested) { - DBG1(DBG_APP, "charon has died -- restart scheduled (%dsec)", - CHARON_RESTART_DELAY); + DBG1(DBG_APP, "%s has died -- restart scheduled (%dsec)", + daemon_name, CHARON_RESTART_DELAY); alarm(CHARON_RESTART_DELAY); // restart in 5 sec } - unlink(CHARON_PID_FILE); + unlink(pid_file); } } @@ -88,7 +88,8 @@ int starter_stop_charon (void) else if (i == 40) { kill(pid, SIGKILL); - DBG1(DBG_APP, "starter_stop_charon(): charon does not respond, sending KILL"); + DBG1(DBG_APP, "starter_stop_charon(): %s does not respond, sending KILL", + daemon_name); } else { @@ -98,15 +99,15 @@ int starter_stop_charon (void) } if (_charon_pid == 0) { - DBG1(DBG_APP, "charon stopped after %d ms", 200*i); + DBG1(DBG_APP, "%s stopped after %d ms", daemon_name, 200*i); return 0; } - DBG1(DBG_APP, "starter_stop_charon(): can't stop charon !!!"); + DBG1(DBG_APP, "starter_stop_charon(): can't stop %s !!!", daemon_name); return -1; } else { - DBG1(DBG_APP, "stater_stop_charon(): charon was not started..."); + DBG1(DBG_APP, "stater_stop_charon(): %s was not started...", daemon_name); } return -1; } @@ -119,7 +120,7 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) char buffer[BUF_LEN]; int argc = 1; char *arg[] = { - CHARON_CMD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + cmd, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL @@ -130,7 +131,7 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) argc = 0; arg[argc++] = "/usr/bin/gdb"; arg[argc++] = "--args"; - arg[argc++] = CHARON_CMD; + arg[argc++] = cmd; } if (!no_fork) { @@ -172,7 +173,8 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) if (_charon_pid) { - DBG1(DBG_APP, "starter_start_charon(): charon already started..."); + DBG1(DBG_APP, "starter_start_charon(): %s already started...", + daemon_name); return -1; } else @@ -203,9 +205,9 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) { /* wait for charon for a maximum of 500 x 20 ms = 10 s */ usleep(20000); - if (stat(CHARON_PID_FILE, &stb) == 0) + if (stat(pid_file, &stb) == 0) { - DBG1(DBG_APP, "charon (%d) started after %d ms", + DBG1(DBG_APP, "%s (%d) started after %d ms", daemon_name, _charon_pid, 20*(i+1)); return 0; } @@ -213,7 +215,8 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) if (_charon_pid) { /* If charon is started but with no ctl file, stop it */ - DBG1(DBG_APP, "charon too long to start... - kill kill"); + DBG1(DBG_APP, "%s too long to start... - kill kill", + daemon_name); for (i = 0; i < 20 && (pid = _charon_pid) != 0; i++) { if (i == 0) @@ -233,7 +236,7 @@ int starter_start_charon (starter_config_t *cfg, bool no_fork, bool attach_gdb) } else { - DBG1(DBG_APP, "charon refused to be started"); + DBG1(DBG_APP, "%s refused to be started", daemon_name); } return -1; } diff --git a/src/starter/starter.c b/src/starter/starter.c index ae6863fd7..917e52d68 100644 --- a/src/starter/starter.c +++ b/src/starter/starter.c @@ -12,6 +12,8 @@ * for more details. */ +#define _GNU_SOURCE + #include #include #include @@ -50,6 +52,15 @@ #define CHARON_RESTART_DELAY 5 +static const char* cmd_default = IPSEC_DIR "/charon"; +static const char* pid_file_default = IPSEC_PIDDIR "/charon.pid"; +static const char* starter_pid_file_default = IPSEC_PIDDIR "/starter.pid"; + +char *daemon_name = NULL; +char *cmd = NULL; +char *pid_file = NULL; +char *starter_pid_file = NULL; + /* logging */ static bool log_to_stderr = TRUE; static bool log_to_syslog = TRUE; @@ -162,7 +173,10 @@ static void signal_handler(int signal) { if (pid == starter_charon_pid()) { - name = " (Charon)"; + if (asprintf(&name, " (%s)", daemon_name) < 0) + { + name = NULL; + } } if (WIFSIGNALED(status)) { @@ -193,6 +207,11 @@ static void signal_handler(int signal) starter_charon_sigchild(pid, exit_status); } } + + if (name) + { + free(name); + } } break; @@ -325,11 +344,56 @@ static bool check_pid(char *pid_file) return FALSE; } +/* Set daemon name and adjust command and pid filenames accordingly */ +static bool set_daemon_name() +{ + if (!daemon_name) + { + daemon_name = "charon"; + } + + if (asprintf(&cmd, IPSEC_DIR"/%s", daemon_name) < 0) + { + cmd = (char*)cmd_default; + } + + if (asprintf(&pid_file, IPSEC_PIDDIR"/%s.pid", daemon_name) < 0) + { + pid_file = (char*)pid_file_default; + } + + if (asprintf(&starter_pid_file, IPSEC_PIDDIR"/starter.%s.pid", + daemon_name) < 0) + { + starter_pid_file = (char*)starter_pid_file_default; + } + + return TRUE; +} + +static void cleanup() +{ + if (cmd != cmd_default) + { + free(cmd); + } + + if (pid_file != pid_file_default) + { + free(pid_file); + } + + if (starter_pid_file != starter_pid_file_default) + { + free(starter_pid_file); + } +} + static void usage(char *name) { fprintf(stderr, "Usage: starter [--nofork] [--auto-update ]\n" " [--debug|--debug-more|--debug-all|--nolog]\n" - " [--attach-gdb]\n"); + " [--attach-gdb] [--daemon ]\n"); exit(LSB_RC_INVALID_ARGUMENT); } @@ -392,12 +456,22 @@ int main (int argc, char **argv) if (!auto_update) usage(argv[0]); } + else if (streq(argv[i], "--daemon") && i+1 < argc) + { + daemon_name = argv[++i]; + } else { usage(argv[0]); } } + if (!set_daemon_name()) + { + DBG1(DBG_APP, "unable to set daemon name"); + exit(LSB_RC_FAILURE); + } + init_log("ipsec_starter"); DBG1(DBG_APP, "Starting %sSwan "VERSION" IPsec [starter]...", @@ -423,13 +497,14 @@ int main (int argc, char **argv) if (getuid() != 0) { DBG1(DBG_APP, "permission denied (must be superuser)"); + cleanup(); exit(LSB_RC_NOT_ALLOWED); } - if (check_pid(CHARON_PID_FILE)) + if (check_pid(pid_file)) { - DBG1(DBG_APP, "charon is already running (%s exists) -- skipping charon start", - CHARON_PID_FILE); + DBG1(DBG_APP, "%s is already running (%s exists) -- skipping daemon start", + daemon_name, pid_file); } else { @@ -438,12 +513,14 @@ int main (int argc, char **argv) if (stat(DEV_RANDOM, &stb) != 0) { DBG1(DBG_APP, "unable to start strongSwan IPsec -- no %s!", DEV_RANDOM); + cleanup(); exit(LSB_RC_FAILURE); } if (stat(DEV_URANDOM, &stb)!= 0) { DBG1(DBG_APP, "unable to start strongSwan IPsec -- no %s!", DEV_URANDOM); + cleanup(); exit(LSB_RC_FAILURE); } @@ -455,6 +532,7 @@ int main (int argc, char **argv) { confread_free(cfg); } + cleanup(); exit(LSB_RC_INVALID_ARGUMENT); } @@ -471,11 +549,12 @@ int main (int argc, char **argv) last_reload = time_monotonic(NULL); - if (check_pid(STARTER_PID_FILE)) + if (check_pid(starter_pid_file)) { DBG1(DBG_APP, "starter is already running (%s exists) -- no fork done", - STARTER_PID_FILE); + starter_pid_file); confread_free(cfg); + cleanup(); exit(LSB_RC_SUCCESS); } @@ -515,13 +594,14 @@ int main (int argc, char **argv) break; default: confread_free(cfg); + cleanup(); exit(LSB_RC_SUCCESS); } } - /* save pid file in /var/run/starter.pid */ + /* save pid file in /var/run/starter[.daemon_name].pid */ { - FILE *fd = fopen(STARTER_PID_FILE, "w"); + FILE *fd = fopen(starter_pid_file, "w"); if (fd) { @@ -576,7 +656,8 @@ int main (int argc, char **argv) } starter_netkey_cleanup(); confread_free(cfg); - unlink(STARTER_PID_FILE); + unlink(starter_pid_file); + cleanup(); DBG1(DBG_APP, "ipsec starter stopped"); close_log(); exit(LSB_RC_SUCCESS); @@ -709,13 +790,13 @@ int main (int argc, char **argv) } /* - * Start charon + * Start daemon */ if (_action_ & FLAG_ACTION_START_CHARON) { if (cfg->setup.charonstart && !starter_charon_pid()) { - DBG2(DBG_APP, "Attempting to start charon..."); + DBG2(DBG_APP, "Attempting to start %s...", daemon_name); if (starter_start_charon(cfg, no_fork, attach_gdb)) { /* schedule next try */ @@ -807,7 +888,8 @@ int main (int argc, char **argv) /* * Wait for something to happen */ - if (pselect(0, NULL, NULL, NULL, auto_update ? &ts : NULL, + if (!_action_ && + pselect(0, NULL, NULL, NULL, auto_update ? &ts : NULL, &action.sa_mask) == 0) { /* timeout -> auto_update */ diff --git a/testing/hosts/default/etc/ld.so.conf.d/strongswan.conf b/testing/hosts/default/etc/ld.so.conf.d/strongswan.conf new file mode 100644 index 000000000..8648d0185 --- /dev/null +++ b/testing/hosts/default/etc/ld.so.conf.d/strongswan.conf @@ -0,0 +1 @@ +/usr/local/lib/ipsec diff --git a/testing/hosts/default/usr/local/bin/expect-file b/testing/hosts/default/usr/local/bin/expect-file new file mode 100755 index 000000000..6921b6638 --- /dev/null +++ b/testing/hosts/default/usr/local/bin/expect-file @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Wait until a given file appears +# +# Params: +# $1 - filename +# $2 - maximum time to wait in seconds, default is 5 seconds + +if [[ $# -lt 1 || $# -gt 2 ]] +then + echo "invalid arguments" + exit 1 +fi + +secs=$2 +[ ! $secs ] && secs=5 + +let steps=$secs*10 +for i in `seq 1 $steps` +do + # -f does not work for special files (e.g. UNIX domain sockets), use ls + # instead + ls $1 >/dev/null 2>&1 + [ $? -eq 0 ] && exit 0 + sleep 0.1 +done + +echo "File '$1' not available after $secs second(s)" +exit 1 diff --git a/testing/scripts/build-baseimage b/testing/scripts/build-baseimage index 684730ced..a5830caa9 100755 --- a/testing/scripts/build-baseimage +++ b/testing/scripts/build-baseimage @@ -16,6 +16,8 @@ INC=$INC,libxml2-dev,libtspi-dev,libsqlite3-dev,openssh-server,tcpdump,psmisc INC=$INC,openssl,vim,sqlite3,conntrack,gdb,cmake,libxerces-c2-dev,libltdl-dev INC=$INC,liblog4cxx10-dev,libboost-thread-dev,libboost-system-dev,git-core INC=$INC,less,acpid,acpi-support-base,libldns-dev,libunbound-dev,dnsutils,screen +INC=$INC,gnat,gprbuild,libahven3-dev,libxmlada4.1-dev,libgmpada3-dev +INC=$INC,libalog0.4.1-base-dev SERVICES="apache2 dbus isc-dhcp-server slapd bind9" INC=$INC,${SERVICES// /,} EXC=iptables diff --git a/testing/scripts/build-guestimages b/testing/scripts/build-guestimages index 3e0709db9..b93c868d2 100755 --- a/testing/scripts/build-guestimages +++ b/testing/scripts/build-guestimages @@ -46,6 +46,8 @@ do execute "mount $NBDPARTITION $LOOPDIR" 0 execute "cp -rf $HOSTSDIR/${host}/etc $LOOPDIR" 0 execute "cp -rf $HOSTSDIR/default/* $LOOPDIR" 0 + execute_chroot "ldconfig" 0 + if [ "$host" = "winnetou" ] then execute "mkdir $LOOPDIR/var/log/apache2/ocsp" 0 diff --git a/testing/scripts/recipes/005_anet.mk b/testing/scripts/recipes/005_anet.mk new file mode 100644 index 000000000..1ecb7b294 --- /dev/null +++ b/testing/scripts/recipes/005_anet.mk @@ -0,0 +1,19 @@ +#!/usr/bin/make + +PKG = anet +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.2.2 + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make tests && make LIBRARY_KIND=static + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make LIBRARY_KIND=static install diff --git a/testing/scripts/recipes/006_tkm-rpc.mk b/testing/scripts/recipes/006_tkm-rpc.mk new file mode 100644 index 000000000..571d590d1 --- /dev/null +++ b/testing/scripts/recipes/006_tkm-rpc.mk @@ -0,0 +1,21 @@ +#!/usr/bin/make + +PKG = tkm-rpc +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.1 + +export ADA_PROJECT_PATH=/root/libraries/lib/gnat + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make tests && make + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make install diff --git a/testing/scripts/recipes/007_x509-ada.mk b/testing/scripts/recipes/007_x509-ada.mk new file mode 100644 index 000000000..319a6e28b --- /dev/null +++ b/testing/scripts/recipes/007_x509-ada.mk @@ -0,0 +1,19 @@ +#!/usr/bin/make + +PKG = x509-ada +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.1 + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make tests && make + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make install diff --git a/testing/scripts/recipes/008_xfrm-ada.mk b/testing/scripts/recipes/008_xfrm-ada.mk new file mode 100644 index 000000000..f44a2822b --- /dev/null +++ b/testing/scripts/recipes/008_xfrm-ada.mk @@ -0,0 +1,21 @@ +#!/usr/bin/make + +PKG = xfrm-ada +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.1 + +export ADA_PROJECT_PATH=/root/libraries/lib/gnat + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make install diff --git a/testing/scripts/recipes/009_tkm.mk b/testing/scripts/recipes/009_tkm.mk new file mode 100644 index 000000000..e8ced8a61 --- /dev/null +++ b/testing/scripts/recipes/009_tkm.mk @@ -0,0 +1,21 @@ +#!/usr/bin/make + +PKG = tkm +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.1 + +export ADA_PROJECT_PATH=/root/libraries/lib/gnat + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make tests && make + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make install diff --git a/testing/scripts/recipes/005_strongswan.mk b/testing/scripts/recipes/010_strongswan.mk similarity index 95% rename from testing/scripts/recipes/005_strongswan.mk rename to testing/scripts/recipes/010_strongswan.mk index 8bac5aa07..4390bf49f 100644 --- a/testing/scripts/recipes/005_strongswan.mk +++ b/testing/scripts/recipes/010_strongswan.mk @@ -69,7 +69,10 @@ CONFIG_OPTS = \ --enable-pkcs8 \ --enable-unity \ --enable-unbound \ - --enable-ipseckey + --enable-ipseckey \ + --enable-tkm + +export ADA_PROJECT_PATH=/root/libraries/lib/gnat all: install diff --git a/testing/scripts/recipes/011_xfrm-proxy.mk b/testing/scripts/recipes/011_xfrm-proxy.mk new file mode 100644 index 000000000..aea69703d --- /dev/null +++ b/testing/scripts/recipes/011_xfrm-proxy.mk @@ -0,0 +1,21 @@ +#!/usr/bin/make + +PKG = xfrm-proxy +SRC = http://git.codelabs.ch/git/$(PKG).git +REV = v0.1 + +export ADA_PROJECT_PATH=/root/libraries/lib/gnat + +all: install + +.$(PKG)-cloned: + git clone $(SRC) $(PKG) + cd $(PKG) && git checkout $(REV) + @touch $@ + +.$(PKG)-built: .$(PKG)-cloned + cd $(PKG) && make + @touch $@ + +install: .$(PKG)-built + cd $(PKG) && make install diff --git a/testing/tests/tkm/host2host-initiator/description.txt b/testing/tests/tkm/host2host-initiator/description.txt new file mode 100644 index 000000000..467693b1e --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/description.txt @@ -0,0 +1,3 @@ +A connection between the hosts moon and sun is set up. The host +moon uses the Trusted Key Manager (TKM) and is the initiator of the +transport connection. The authentication is based on X.509 certificates. diff --git a/testing/tests/tkm/host2host-initiator/evaltest.dat b/testing/tests/tkm/host2host-initiator/evaltest.dat new file mode 100644 index 000000000..d8d44dff6 --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/evaltest.dat @@ -0,0 +1,12 @@ +moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES +sun::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES +moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES +moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES +moon::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.1 <-> 192.168.0.2 \]::YES +moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +moon::cat /tmp/tkm.log::Adding SA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES diff --git a/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/moonKey.der new file mode 100644 index 000000000..97f0963f8 Binary files /dev/null and b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/moonKey.der differ diff --git a/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/strongswanCert.der b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/strongswanCert.der new file mode 100644 index 000000000..a5a631f4b Binary files /dev/null and b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/strongswanCert.der differ diff --git a/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/tkm.conf new file mode 100644 index 000000000..2619c0089 --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/hosts/moon/etc/tkm/tkm.conf @@ -0,0 +1,21 @@ + + + moon.strongswan.org + moonCert.pem + + + transport + + 1 + 192.168.0.1 + + + sun.strongswan.org + 192.168.0.2 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/host2host-initiator/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/host2host-initiator/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..4ad0aa7db --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/hosts/sun/etc/ipsec.conf @@ -0,0 +1,20 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + +conn host-host + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=sun.strongswan.org + right=PH_IP_MOON + rightid=moon.strongswan.org + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + auto=add diff --git a/testing/tests/tkm/host2host-initiator/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/host2host-initiator/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..dc937641c --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/hosts/sun/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac xcbc stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/tkm/host2host-initiator/posttest.dat b/testing/tests/tkm/host2host-initiator/posttest.dat new file mode 100644 index 000000000..34037bc23 --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/posttest.dat @@ -0,0 +1,4 @@ +moon::DAEMON_NAME=charon-tkm ipsec stop +moon::killall tkm_keymanager +moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log +sun::ipsec stop diff --git a/testing/tests/tkm/host2host-initiator/pretest.dat b/testing/tests/tkm/host2host-initiator/pretest.dat new file mode 100644 index 000000000..7cb90ac26 --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/pretest.dat @@ -0,0 +1,10 @@ +moon::rm /etc/ipsec.secrets +moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +moon::cat /etc/ipsec.conf +moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +moon::expect-file /tmp/tkm.rpc.ike +moon::DAEMON_NAME=charon-tkm ipsec start +sun::ipsec start +sun::expect-connection host-host +moon::DAEMON_NAME=charon-tkm expect-connection conn1 +moon::DAEMON_NAME=charon-tkm ipsec up conn1 diff --git a/testing/tests/tkm/host2host-initiator/test.conf b/testing/tests/tkm/host2host-initiator/test.conf new file mode 100644 index 000000000..9647dc6a2 --- /dev/null +++ b/testing/tests/tkm/host2host-initiator/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="moon winnetou sun" + +# Corresponding block diagram +# +DIAGRAM="m-w-s.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" diff --git a/testing/tests/tkm/host2host-responder/description.txt b/testing/tests/tkm/host2host-responder/description.txt new file mode 100644 index 000000000..72eabdb6c --- /dev/null +++ b/testing/tests/tkm/host2host-responder/description.txt @@ -0,0 +1,3 @@ +A connection between the hosts moon and sun is set up. The host +moon uses the Trusted Key Manager (TKM) and is the responder of the +transport connection. The authentication is based on X.509 certificates. diff --git a/testing/tests/tkm/host2host-responder/evaltest.dat b/testing/tests/tkm/host2host-responder/evaltest.dat new file mode 100644 index 000000000..d8d44dff6 --- /dev/null +++ b/testing/tests/tkm/host2host-responder/evaltest.dat @@ -0,0 +1,12 @@ +moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES +sun::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES +moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES +moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES +moon::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.1 <-> 192.168.0.2 \]::YES +moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +moon::cat /tmp/tkm.log::Adding SA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES diff --git a/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/moonKey.der new file mode 100644 index 000000000..97f0963f8 Binary files /dev/null and b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/moonKey.der differ diff --git a/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/strongswanCert.der b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/strongswanCert.der new file mode 100644 index 000000000..a5a631f4b Binary files /dev/null and b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/strongswanCert.der differ diff --git a/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/tkm.conf new file mode 100644 index 000000000..2619c0089 --- /dev/null +++ b/testing/tests/tkm/host2host-responder/hosts/moon/etc/tkm/tkm.conf @@ -0,0 +1,21 @@ + + + moon.strongswan.org + moonCert.pem + + + transport + + 1 + 192.168.0.1 + + + sun.strongswan.org + 192.168.0.2 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/host2host-responder/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/host2host-responder/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..6681dad11 --- /dev/null +++ b/testing/tests/tkm/host2host-responder/hosts/sun/etc/ipsec.conf @@ -0,0 +1,21 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + +conn host-host + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=sun.strongswan.org + right=PH_IP_MOON + rightid=moon.strongswan.org + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + auto=add + type=transport diff --git a/testing/tests/tkm/host2host-responder/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/host2host-responder/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..dc937641c --- /dev/null +++ b/testing/tests/tkm/host2host-responder/hosts/sun/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac xcbc stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/tkm/host2host-responder/posttest.dat b/testing/tests/tkm/host2host-responder/posttest.dat new file mode 100644 index 000000000..34037bc23 --- /dev/null +++ b/testing/tests/tkm/host2host-responder/posttest.dat @@ -0,0 +1,4 @@ +moon::DAEMON_NAME=charon-tkm ipsec stop +moon::killall tkm_keymanager +moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log +sun::ipsec stop diff --git a/testing/tests/tkm/host2host-responder/pretest.dat b/testing/tests/tkm/host2host-responder/pretest.dat new file mode 100644 index 000000000..40e84453f --- /dev/null +++ b/testing/tests/tkm/host2host-responder/pretest.dat @@ -0,0 +1,10 @@ +moon::rm /etc/ipsec.secrets +moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +moon::cat /etc/ipsec.conf +moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +moon::expect-file /tmp/tkm.rpc.ike +moon::DAEMON_NAME=charon-tkm ipsec start +sun::ipsec start +sun::expect-connection host-host +moon::DAEMON_NAME=charon-tkm expect-connection conn1 +sun::ipsec up host-host diff --git a/testing/tests/tkm/host2host-responder/test.conf b/testing/tests/tkm/host2host-responder/test.conf new file mode 100644 index 000000000..9647dc6a2 --- /dev/null +++ b/testing/tests/tkm/host2host-responder/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="moon winnetou sun" + +# Corresponding block diagram +# +DIAGRAM="m-w-s.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" diff --git a/testing/tests/tkm/host2host-xfrmproxy/description.txt b/testing/tests/tkm/host2host-xfrmproxy/description.txt new file mode 100644 index 000000000..b728a317d --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/description.txt @@ -0,0 +1,5 @@ +A transport connection between the hosts moon and sun is set up. +The host moon starts the Trusted Key Manager (TKM) and the Ada XFRM +proxy, which relays XFRM kernel messages to charon. The authentication is based +on X.509 certificates. The connection is initiated by a ping from moon to +sun. diff --git a/testing/tests/tkm/host2host-xfrmproxy/evaltest.dat b/testing/tests/tkm/host2host-xfrmproxy/evaltest.dat new file mode 100644 index 000000000..7c8c6b24a --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/evaltest.dat @@ -0,0 +1,13 @@ +moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES +sun::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES +moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES +moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES +moon::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.1 <-> 192.168.0.2 \]::YES +moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +moon::cat /tmp/tkm.log::Adding SA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES +moon::cat /tmp/xfrm_proxy.log::Initiating ESA acquire for reqid 1::YES diff --git a/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/moonKey.der new file mode 100644 index 000000000..97f0963f8 Binary files /dev/null and b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/moonKey.der differ diff --git a/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der new file mode 100644 index 000000000..a5a631f4b Binary files /dev/null and b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der differ diff --git a/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/tkm.conf new file mode 100644 index 000000000..2619c0089 --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/hosts/moon/etc/tkm/tkm.conf @@ -0,0 +1,21 @@ + + + moon.strongswan.org + moonCert.pem + + + transport + + 1 + 192.168.0.1 + + + sun.strongswan.org + 192.168.0.2 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..4ad0aa7db --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/ipsec.conf @@ -0,0 +1,20 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + +conn host-host + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=sun.strongswan.org + right=PH_IP_MOON + rightid=moon.strongswan.org + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + auto=add diff --git a/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..dc937641c --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/hosts/sun/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac xcbc stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/tkm/host2host-xfrmproxy/posttest.dat b/testing/tests/tkm/host2host-xfrmproxy/posttest.dat new file mode 100644 index 000000000..99efe7b00 --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/posttest.dat @@ -0,0 +1,5 @@ +moon::DAEMON_NAME=charon-tkm ipsec stop +moon::killall xfrm_proxy +moon::killall tkm_keymanager +moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log /tmp/xfrm_proxy.log +sun::ipsec stop diff --git a/testing/tests/tkm/host2host-xfrmproxy/pretest.dat b/testing/tests/tkm/host2host-xfrmproxy/pretest.dat new file mode 100644 index 000000000..d645ddbfe --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/pretest.dat @@ -0,0 +1,12 @@ +sun::ipsec start +moon::rm /etc/ipsec.secrets +moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +moon::cat /etc/ipsec.conf +moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +moon::expect-file /tmp/tkm.rpc.ike +moon::DAEMON_NAME=charon-tkm ipsec start +moon::expect-file /tmp/tkm.rpc.ees +moon::xfrm_proxy >/tmp/xfrm_proxy.log 2>&1 & +moon::DAEMON_NAME=charon-tkm expect-connection conn1 +sun::expect-connection host-host +moon::ping -c 3 192.168.0.2 diff --git a/testing/tests/tkm/host2host-xfrmproxy/test.conf b/testing/tests/tkm/host2host-xfrmproxy/test.conf new file mode 100644 index 000000000..9647dc6a2 --- /dev/null +++ b/testing/tests/tkm/host2host-xfrmproxy/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="moon winnetou sun" + +# Corresponding block diagram +# +DIAGRAM="m-w-s.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" diff --git a/testing/tests/tkm/multiple-clients/description.txt b/testing/tests/tkm/multiple-clients/description.txt new file mode 100644 index 000000000..c8e72d51d --- /dev/null +++ b/testing/tests/tkm/multiple-clients/description.txt @@ -0,0 +1,5 @@ +Two transport connections to gateway sun are set up, one from client +carol and the other from client dave. The gateway sun uses +the Trusted Key Manager (TKM) and is the responder for both connections. The +authentication is based on X.509 certificates. In order to test the connections, +both carol and dave ping gateway sun. diff --git a/testing/tests/tkm/multiple-clients/evaltest.dat b/testing/tests/tkm/multiple-clients/evaltest.dat new file mode 100644 index 000000000..8e0042102 --- /dev/null +++ b/testing/tests/tkm/multiple-clients/evaltest.dat @@ -0,0 +1,23 @@ +sun::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*sun.strongswan.org.*carol.strongswan.org::YES +sun::ipsec stroke status 2> /dev/null::conn2.*ESTABLISHED.*sun.strongswan.org.*dave.strongswan.org::YES +carol::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*carol.strongswan.org.*sun.strongswan.org::YES +dave::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*dave.strongswan.org.*sun.strongswan.org::YES +sun::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES +sun::ipsec stroke status 2> /dev/null::conn2.*INSTALLED, TRANSPORT::YES +carol::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES +dave::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES +carol::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_req=1::YES +dave::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_req=1::YES +carol::tcpdump::IP carol.strongswan.org > sun.strongswan.org: ESP::YES +carol::tcpdump::IP sun.strongswan.org > carol.strongswan.org: ESP::YES +dave::tcpdump::IP dave.strongswan.org > sun.strongswan.org: ESP::YES +dave::tcpdump::IP sun.strongswan.org > dave.strongswan.org: ESP::YES +sun::cat /tmp/tkm.log::RSA private key '/etc/tkm/sunKey.der' loaded::YES +sun::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.2 <-> 192.168.0.100 \]::YES +sun::cat /tmp/tkm.log::Adding policy \[ 2, 192.168.0.2 <-> 192.168.0.200 \]::YES +sun::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +sun::cat /tmp/tkm.log::Checked CA certificate of CC context 2::YES +sun::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +sun::cat /tmp/tkm.log::Authentication of ISA context 2 successful::YES +sun::cat /tmp/tkm.log::Adding SA \[ 1, 192.168.0.2 <-> 192.168.0.100, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES +sun::cat /tmp/tkm.log::Adding SA \[ 2, 192.168.0.2 <-> 192.168.0.200, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES diff --git a/testing/tests/tkm/multiple-clients/hosts/carol/etc/ipsec.conf b/testing/tests/tkm/multiple-clients/hosts/carol/etc/ipsec.conf new file mode 100644 index 000000000..10ee3e89d --- /dev/null +++ b/testing/tests/tkm/multiple-clients/hosts/carol/etc/ipsec.conf @@ -0,0 +1,22 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn host-host + left=PH_IP_CAROL + leftcert=carolCert.pem + leftid=carol@strongswan.org + right=PH_IP_SUN + rightid=sun.strongswan.org + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + type=transport + auto=add diff --git a/testing/tests/tkm/multiple-clients/hosts/carol/etc/strongswan.conf b/testing/tests/tkm/multiple-clients/hosts/carol/etc/strongswan.conf new file mode 100644 index 000000000..ca23c6971 --- /dev/null +++ b/testing/tests/tkm/multiple-clients/hosts/carol/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/tkm/multiple-clients/hosts/dave/etc/ipsec.conf b/testing/tests/tkm/multiple-clients/hosts/dave/etc/ipsec.conf new file mode 100644 index 000000000..6ba0a97ce --- /dev/null +++ b/testing/tests/tkm/multiple-clients/hosts/dave/etc/ipsec.conf @@ -0,0 +1,22 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn host-host + left=PH_IP_DAVE + leftcert=daveCert.pem + leftid=dave@strongswan.org + right=PH_IP_SUN + rightid=sun.strongswan.org + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + type=transport + auto=add diff --git a/testing/tests/tkm/multiple-clients/hosts/dave/etc/strongswan.conf b/testing/tests/tkm/multiple-clients/hosts/dave/etc/strongswan.conf new file mode 100644 index 000000000..ca23c6971 --- /dev/null +++ b/testing/tests/tkm/multiple-clients/hosts/dave/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/strongswanCert.der b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/strongswanCert.der new file mode 100644 index 000000000..a5a631f4b Binary files /dev/null and b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/strongswanCert.der differ diff --git a/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/sunKey.der b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/sunKey.der new file mode 100644 index 000000000..4c47db093 Binary files /dev/null and b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/sunKey.der differ diff --git a/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/tkm.conf b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/tkm.conf new file mode 100644 index 000000000..216625e4c --- /dev/null +++ b/testing/tests/tkm/multiple-clients/hosts/sun/etc/tkm/tkm.conf @@ -0,0 +1,36 @@ + + + sun.strongswan.org + sunCert.pem + + + transport + + 1 + 192.168.0.2 + + + carol@strongswan.org + 192.168.0.100 + + + 30 + 60 + + + + transport + + 1 + 192.168.0.2 + + + dave@strongswan.org + 192.168.0.200 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/multiple-clients/posttest.dat b/testing/tests/tkm/multiple-clients/posttest.dat new file mode 100644 index 000000000..9a4a9bc9d --- /dev/null +++ b/testing/tests/tkm/multiple-clients/posttest.dat @@ -0,0 +1,5 @@ +sun::DAEMON_NAME=charon-tkm ipsec stop +sun::killall tkm_keymanager +sun::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log +carol::ipsec stop +dave::ipsec stop diff --git a/testing/tests/tkm/multiple-clients/pretest.dat b/testing/tests/tkm/multiple-clients/pretest.dat new file mode 100644 index 000000000..ec83662f5 --- /dev/null +++ b/testing/tests/tkm/multiple-clients/pretest.dat @@ -0,0 +1,14 @@ +sun::rm /etc/ipsec.secrets +sun::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +sun::cat /etc/ipsec.conf +sun::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/sunKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +sun::expect-file /tmp/tkm.rpc.ike +sun::DAEMON_NAME=charon-tkm ipsec start +carol::ipsec start +carol::expect-connection host-host +dave::ipsec start +dave::expect-connection host-host +sun::DAEMON_NAME=charon-tkm expect-connection conn1 +sun::DAEMON_NAME=charon-tkm expect-connection conn2 +carol::ipsec up host-host +dave::ipsec up host-host diff --git a/testing/tests/tkm/multiple-clients/test.conf b/testing/tests/tkm/multiple-clients/test.conf new file mode 100644 index 000000000..1dd36309d --- /dev/null +++ b/testing/tests/tkm/multiple-clients/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="carol dave sun winnetou" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d-s.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="carol dave" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="carol dave sun" diff --git a/testing/tests/tkm/net2net-initiator/description.txt b/testing/tests/tkm/net2net-initiator/description.txt new file mode 100644 index 000000000..40f2a8013 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/description.txt @@ -0,0 +1,5 @@ +A connection between the subnets behind the gateways moon and sun +is set up. The host moon uses the Trusted Key Manager (TKM) and is the +initiator of the tunnel connection. The authentication is based on X.509 +certificates. In order to test the tunnel, client alice behind gateway +moon pings client bob located behind gateway sun. diff --git a/testing/tests/tkm/net2net-initiator/evaltest.dat b/testing/tests/tkm/net2net-initiator/evaltest.dat new file mode 100644 index 000000000..8d4794f0d --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/evaltest.dat @@ -0,0 +1,12 @@ +moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun::ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TUNNEL::YES +sun::ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES +moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES +moon::cat /tmp/tkm.log::Adding policy \[ 1, 10.1.0.0/16 > 192.168.0.1 <=> 192.168.0.2 < 10.2.0.0/16 \]::YES +moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +moon::cat /tmp/tkm.log::Adding SA \[ 1, 10.1.0.0/16 > 192.168.0.1 <=> 192.168.0.2 < 10.2.0.0/16, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES diff --git a/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/moonKey.der new file mode 100644 index 000000000..97f0963f8 Binary files /dev/null and b/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/moonKey.der differ diff --git a/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/tkm.conf new file mode 100644 index 000000000..717b0a6f4 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/hosts/moon/etc/tkm/tkm.conf @@ -0,0 +1,23 @@ + + + moon.strongswan.org + moonCert.pem + + + tunnel + + 1 + 192.168.0.1 + 10.1.0.0 + + + sun.strongswan.org + 192.168.0.2 + 10.2.0.0 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/net2net-initiator/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/net2net-initiator/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..21b613d20 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/hosts/sun/etc/ipsec.conf @@ -0,0 +1,23 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn net-net + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=sun.strongswan.org + leftsubnet=10.2.0.0/16 + right=PH_IP_MOON + rightid=moon.strongswan.org + rightsubnet=10.1.0.0/16 + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + auto=add diff --git a/testing/tests/tkm/net2net-initiator/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/net2net-initiator/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..94e0b2a62 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/hosts/sun/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown + multiple_authentication = no +} diff --git a/testing/tests/tkm/net2net-initiator/posttest.dat b/testing/tests/tkm/net2net-initiator/posttest.dat new file mode 100644 index 000000000..34037bc23 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/posttest.dat @@ -0,0 +1,4 @@ +moon::DAEMON_NAME=charon-tkm ipsec stop +moon::killall tkm_keymanager +moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log +sun::ipsec stop diff --git a/testing/tests/tkm/net2net-initiator/pretest.dat b/testing/tests/tkm/net2net-initiator/pretest.dat new file mode 100644 index 000000000..f84c8fcd2 --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/pretest.dat @@ -0,0 +1,10 @@ +moon::rm /etc/ipsec.secrets +moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +moon::cat /etc/ipsec.conf +moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +moon::expect-file /tmp/tkm.rpc.ike +moon::DAEMON_NAME=charon-tkm ipsec start +sun::ipsec start +sun::expect-connection net-net +moon::DAEMON_NAME=charon-tkm expect-connection conn1 +moon::DAEMON_NAME=charon-tkm ipsec up conn1 diff --git a/testing/tests/tkm/net2net-initiator/test.conf b/testing/tests/tkm/net2net-initiator/test.conf new file mode 100644 index 000000000..afa2accbe --- /dev/null +++ b/testing/tests/tkm/net2net-initiator/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" diff --git a/testing/tests/tkm/net2net-xfrmproxy/description.txt b/testing/tests/tkm/net2net-xfrmproxy/description.txt new file mode 100644 index 000000000..b42c89c52 --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/description.txt @@ -0,0 +1,6 @@ +A connection between the subnets behind the gateways moon and sun +is set up. The host moon starts the Trusted Key Manager (TKM) and the Ada +XFRM proxy, which relays XFRM kernel messages to charon. The authentication is +based on X.509 certificates. In order to test the tunnel, client alice +behind gateway moon pings client bob located behind gateway +sun. diff --git a/testing/tests/tkm/net2net-xfrmproxy/evaltest.dat b/testing/tests/tkm/net2net-xfrmproxy/evaltest.dat new file mode 100644 index 000000000..a38dba0ee --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/evaltest.dat @@ -0,0 +1,13 @@ +moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun::ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TUNNEL::YES +sun::ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES +moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES +moon::cat /tmp/tkm.log::Adding policy \[ 1, 10.1.0.0/16 > 192.168.0.1 <=> 192.168.0.2 < 10.2.0.0/16 \]::YES +moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES +moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES +moon::cat /tmp/tkm.log::Adding SA \[ 1, 10.1.0.0/16 > 192.168.0.1 <=> 192.168.0.2 < 10.2.0.0/16, SPI_in.*, SPI_out.*, soft 30, hard 60 \]::YES +moon::cat /tmp/xfrm_proxy.log::Initiating ESA acquire for reqid 1::YES diff --git a/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/moonKey.der new file mode 100644 index 000000000..97f0963f8 Binary files /dev/null and b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/moonKey.der differ diff --git a/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der new file mode 100644 index 000000000..a5a631f4b Binary files /dev/null and b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/strongswanCert.der differ diff --git a/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/tkm.conf new file mode 100644 index 000000000..717b0a6f4 --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/hosts/moon/etc/tkm/tkm.conf @@ -0,0 +1,23 @@ + + + moon.strongswan.org + moonCert.pem + + + tunnel + + 1 + 192.168.0.1 + 10.1.0.0 + + + sun.strongswan.org + 192.168.0.2 + 10.2.0.0 + + + 30 + 60 + + + diff --git a/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..21b613d20 --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/ipsec.conf @@ -0,0 +1,23 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn net-net + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=sun.strongswan.org + leftsubnet=10.2.0.0/16 + right=PH_IP_MOON + rightid=moon.strongswan.org + rightsubnet=10.1.0.0/16 + ike=aes256-sha512-modp4096! + esp=aes256-sha512-modp4096! + auto=add diff --git a/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..94e0b2a62 --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/hosts/sun/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown + multiple_authentication = no +} diff --git a/testing/tests/tkm/net2net-xfrmproxy/posttest.dat b/testing/tests/tkm/net2net-xfrmproxy/posttest.dat new file mode 100644 index 000000000..24544307a --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/posttest.dat @@ -0,0 +1,4 @@ +moon::DAEMON_NAME=charon-tkm ipsec stop +moon::killall tkm_keymanager +moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log /tmp/xfrm_proxy.log +sun::ipsec stop diff --git a/testing/tests/tkm/net2net-xfrmproxy/pretest.dat b/testing/tests/tkm/net2net-xfrmproxy/pretest.dat new file mode 100644 index 000000000..4732a37f6 --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/pretest.dat @@ -0,0 +1,12 @@ +sun::ipsec start +moon::rm /etc/ipsec.secrets +moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd +moon::cat /etc/ipsec.conf +moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 & +moon::expect-file /tmp/tkm.rpc.ike +moon::DAEMON_NAME=charon-tkm ipsec start +moon::expect-file /tmp/tkm.rpc.ees +moon::xfrm_proxy >/tmp/xfrm_proxy.log 2>&1 & +moon::DAEMON_NAME=charon-tkm expect-connection conn1 +sun::expect-connection net-net +alice::ping -c 3 PH_IP_BOB diff --git a/testing/tests/tkm/net2net-xfrmproxy/test.conf b/testing/tests/tkm/net2net-xfrmproxy/test.conf new file mode 100644 index 000000000..afa2accbe --- /dev/null +++ b/testing/tests/tkm/net2net-xfrmproxy/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun"