aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/plugins
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2008-03-13 14:14:44 +0000
committerMartin Willi <martin@strongswan.org>2008-03-13 14:14:44 +0000
commit552cc11b1f017ce4962fca741f567d098f768574 (patch)
tree2835ae64c435191e04b5a265b1509c40a2e6766a /src/charon/plugins
parent2df655134ca29f7a0b7d90ef4783f85eff1ddfd3 (diff)
downloadstrongswan-552cc11b1f017ce4962fca741f567d098f768574.tar.bz2
strongswan-552cc11b1f017ce4962fca741f567d098f768574.tar.xz
merged the modularization branch (credentials) back to trunk
Diffstat (limited to 'src/charon/plugins')
-rw-r--r--src/charon/plugins/dbus/Makefile.am11
-rw-r--r--src/charon/plugins/dbus/dbus.c422
-rw-r--r--src/charon/plugins/dbus/dbus.h50
-rw-r--r--src/charon/plugins/eap_aka/Makefile.am11
-rw-r--r--src/charon/plugins/eap_aka/eap_aka.c1557
-rw-r--r--src/charon/plugins/eap_aka/eap_aka.h83
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_plugin.c52
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_plugin.h49
-rw-r--r--src/charon/plugins/eap_identity/Makefile.am10
-rw-r--r--src/charon/plugins/eap_identity/eap_identity.c125
-rw-r--r--src/charon/plugins/eap_identity/eap_identity.h51
-rw-r--r--src/charon/plugins/eap_identity/eap_identity_plugin.c48
-rw-r--r--src/charon/plugins/eap_identity/eap_identity_plugin.h49
-rw-r--r--src/charon/plugins/eap_md5/Makefile.am10
-rw-r--r--src/charon/plugins/eap_md5/eap_md5.c300
-rw-r--r--src/charon/plugins/eap_md5/eap_md5.h59
-rw-r--r--src/charon/plugins/eap_md5/eap_md5_plugin.c52
-rw-r--r--src/charon/plugins/eap_md5/eap_md5_plugin.h49
-rw-r--r--src/charon/plugins/eap_sim/Makefile.am13
-rw-r--r--src/charon/plugins/eap_sim/eap_sim.c1150
-rw-r--r--src/charon/plugins/eap_sim/eap_sim.h111
-rw-r--r--src/charon/plugins/eap_sim/eap_sim_file.c283
-rw-r--r--src/charon/plugins/eap_sim/eap_sim_plugin.c52
-rw-r--r--src/charon/plugins/eap_sim/eap_sim_plugin.h49
-rw-r--r--src/charon/plugins/med_db/Makefile.am10
-rw-r--r--src/charon/plugins/med_db/med_db_creds.c211
-rw-r--r--src/charon/plugins/med_db/med_db_creds.h55
-rw-r--r--src/charon/plugins/med_db/med_db_plugin.c88
-rw-r--r--src/charon/plugins/med_db/med_db_plugin.h49
-rw-r--r--src/charon/plugins/sql/Makefile.am10
-rw-r--r--src/charon/plugins/sql/config.sql73
-rw-r--r--src/charon/plugins/sql/cred.sql24
-rw-r--r--src/charon/plugins/sql/sql_config.c538
-rw-r--r--src/charon/plugins/sql/sql_config.h55
-rw-r--r--src/charon/plugins/sql/sql_plugin.c89
-rw-r--r--src/charon/plugins/sql/sql_plugin.h49
-rw-r--r--src/charon/plugins/sql/test.sql47
-rw-r--r--src/charon/plugins/stroke/Makefile.am10
-rwxr-xr-xsrc/charon/plugins/stroke/stroke.c3335
-rw-r--r--src/charon/plugins/stroke/stroke.h52
-rw-r--r--src/charon/plugins/unit_tester/Makefile.am17
-rw-r--r--src/charon/plugins/unit_tester/tests.h33
-rw-r--r--src/charon/plugins/unit_tester/tests/test_auth_info.c142
-rw-r--r--src/charon/plugins/unit_tester/tests/test_curl.c44
-rw-r--r--src/charon/plugins/unit_tester/tests/test_enumerator.c214
-rw-r--r--src/charon/plugins/unit_tester/tests/test_fips_prf.c61
-rw-r--r--src/charon/plugins/unit_tester/tests/test_mutex.c100
-rw-r--r--src/charon/plugins/unit_tester/tests/test_mysql.c90
-rw-r--r--src/charon/plugins/unit_tester/tests/test_sqlite.c94
-rw-r--r--src/charon/plugins/unit_tester/unit_tester.c118
-rw-r--r--src/charon/plugins/unit_tester/unit_tester.h51
-rw-r--r--src/charon/plugins/xml/Makefile.am10
-rw-r--r--src/charon/plugins/xml/schema.xml400
-rw-r--r--src/charon/plugins/xml/xml.c749
-rw-r--r--src/charon/plugins/xml/xml.h52
55 files changed, 11516 insertions, 0 deletions
diff --git a/src/charon/plugins/dbus/Makefile.am b/src/charon/plugins/dbus/Makefile.am
new file mode 100644
index 000000000..ccfada4ee
--- /dev/null
+++ b/src/charon/plugins/dbus/Makefile.am
@@ -0,0 +1,11 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon ${dbus_CFLAGS}
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-dbus.la
+
+libcharon_dbus_la_SOURCES = dbus.h dbus.c
+libcharon_dbus_la_LDFLAGS = -module
+libcharon_dbus_la_LIBADD = ${dbus_LIBS}
+
diff --git a/src/charon/plugins/dbus/dbus.c b/src/charon/plugins/dbus/dbus.c
new file mode 100644
index 000000000..ac29db773
--- /dev/null
+++ b/src/charon/plugins/dbus/dbus.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <NetworkManager/NetworkManager.h>
+#include <NetworkManager/NetworkManagerVPN.h>
+#include <stdlib.h>
+
+#include "dbus.h"
+
+#include <library.h>
+#include <daemon.h>
+#include <processing/jobs/callback_job.h>
+
+
+#define NM_DBUS_SERVICE_STRONG "org.freedesktop.NetworkManager.strongswan"
+#define NM_dbus_STRONG "org.freedesktop.NetworkManager.strongswan"
+#define NM_DBUS_PATH_STRONG "/org/freedesktop/NetworkManager/strongswan"
+
+typedef struct private_dbus_t private_dbus_t;
+
+/**
+ * Private data of an dbus_t object.
+ */
+struct private_dbus_t {
+
+ /**
+ * Public part of dbus_t object.
+ */
+ dbus_t public;
+
+ /**
+ * DBUS connection
+ */
+ DBusConnection* conn;
+
+ /**
+ * error value used here and there
+ */
+ DBusError err;
+
+ /**
+ * state of the daemon
+ */
+ NMVPNState state;
+
+ /**
+ * job accepting stroke messages
+ */
+ callback_job_t *job;
+
+ /**
+ * name of the currently active connection
+ */
+ char *name;
+};
+
+/**
+ * set daemon state and send StateChange signal to the bus
+ */
+static void set_state(private_dbus_t *this, NMVPNState state)
+{
+ DBusMessage* msg;
+
+ msg = dbus_message_new_signal(NM_DBUS_PATH_STRONG, NM_dbus_STRONG, NM_DBUS_VPN_SIGNAL_STATE_CHANGE);
+
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &this->state,
+ DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID) ||
+ !dbus_connection_send(this->conn, msg, NULL))
+ {
+ DBG1(DBG_CFG, "unable to send DBUS StateChange signal");
+ }
+ dbus_connection_flush(this->conn);
+ dbus_message_unref(msg);
+ this->state = state;
+}
+
+
+/**
+ * get the child_cfg with the same name as the peer cfg
+ */
+static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
+{
+ child_cfg_t *current, *found = NULL;
+ iterator_t *iterator;
+
+ iterator = peer_cfg->create_child_cfg_iterator(peer_cfg);
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (streq(current->get_name(current), name))
+ {
+ found = current;
+ found->get_ref(found);
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+
+/**
+ * process NetworkManagers startConnection method call
+ */
+static bool start_connection(private_dbus_t *this, DBusMessage* msg)
+{
+ DBusMessage *reply, *signal;
+ char *name, *user, **data, **passwords, **routes;
+ int data_count, passwords_count, routes_count;
+ u_int32_t me, other, p2p, netmask, mss;
+ char *dev, *domain, *banner;
+ const dbus_int32_t array[] = {};
+ const dbus_int32_t *varray = array;
+ peer_cfg_t *peer_cfg;
+ child_cfg_t *child_cfg;
+ status_t status = FAILED;
+
+ dbus_error_free(&this->err);
+
+ if (!dbus_message_get_args(msg, &this->err,
+ DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &user,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &passwords, &passwords_count,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &data, &data_count,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &routes, &routes_count,
+ DBUS_TYPE_INVALID))
+ {
+ return FALSE;
+ }
+ set_state(this, NM_VPN_STATE_STARTING);
+
+ peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
+ if (peer_cfg)
+ {
+ free(this->name);
+ this->name = strdup(peer_cfg->get_name(peer_cfg));
+ child_cfg = get_child_from_peer(peer_cfg, name);
+ if (child_cfg)
+ {
+ status = charon->controller->initiate(charon->controller,
+ peer_cfg, child_cfg, controller_cb_empty, NULL);
+ }
+ else
+ {
+ peer_cfg->destroy(peer_cfg);
+ }
+ }
+ reply = dbus_message_new_method_return(msg);
+ dbus_connection_send(this->conn, reply, NULL);
+ dbus_message_unref(reply);
+
+ if (status == SUCCESS)
+ {
+
+ set_state(this, NM_VPN_STATE_STARTED);
+ signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG,
+ NM_dbus_STRONG,
+ NM_DBUS_VPN_SIGNAL_IP4_CONFIG);
+ me = other = p2p = mss = netmask = 0;
+ dev = domain = banner = "";
+ if (dbus_message_append_args(signal,
+ DBUS_TYPE_UINT32, &other,
+ DBUS_TYPE_STRING, &dev,
+ DBUS_TYPE_UINT32, &me,
+ DBUS_TYPE_UINT32, &p2p,
+ DBUS_TYPE_UINT32, &netmask,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
+ DBUS_TYPE_UINT32, &mss,
+ DBUS_TYPE_STRING, &domain,
+ DBUS_TYPE_STRING, &banner, DBUS_TYPE_INVALID))
+ {
+ dbus_connection_send(this->conn, signal, NULL);
+ }
+ dbus_message_unref(signal);
+ }
+ else
+ {
+ set_state(this, NM_VPN_STATE_STOPPED);
+ }
+
+ dbus_connection_flush(this->conn);
+ return TRUE;
+}
+
+/**
+ * process NetworkManagers stopConnection method call
+ */
+static bool stop_connection(private_dbus_t *this, DBusMessage* msg)
+{
+ u_int32_t id;
+ iterator_t *iterator;
+ ike_sa_t *ike_sa;
+
+ if (this->name == NULL)
+ {
+ return FALSE;
+ }
+
+ dbus_error_free(&this->err);
+
+ set_state(this, NM_VPN_STATE_STOPPING);
+
+ iterator = charon->controller->create_ike_sa_iterator(charon->controller);
+ while (iterator->iterate(iterator, (void**)&ike_sa))
+ {
+ child_sa_t *child_sa;
+ iterator_t *children;
+
+ if (this->name && streq(this->name, ike_sa->get_name(ike_sa)))
+ {
+ id = ike_sa->get_unique_id(ike_sa);
+ iterator->destroy(iterator);
+ charon->controller->terminate_ike(charon->controller, id, NULL, NULL);
+ set_state(this, NM_VPN_STATE_STOPPED);
+ return TRUE;;
+ }
+ children = ike_sa->create_child_sa_iterator(ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (this->name && streq(this->name, child_sa->get_name(child_sa)))
+ {
+ id = child_sa->get_reqid(child_sa);
+ children->destroy(children);
+ iterator->destroy(iterator);
+ charon->controller->terminate_child(charon->controller, id, NULL, NULL);
+ set_state(this, NM_VPN_STATE_STOPPED);
+ return TRUE;
+ }
+ }
+ children->destroy(children);
+ }
+ iterator->destroy(iterator);
+ set_state(this, NM_VPN_STATE_STOPPED);
+ return TRUE;
+}
+
+/**
+ * process NetworkManagers getState method call
+ */
+static bool get_state(private_dbus_t *this, DBusMessage* msg)
+{
+ DBusMessage* reply;
+ reply = dbus_message_new_method_return(msg);
+ if (!reply || !dbus_message_append_args(reply,
+ DBUS_TYPE_UINT32, &this->state,
+ DBUS_TYPE_INVALID))
+ {
+ return FALSE;
+ }
+ dbus_connection_send(this->conn, reply, NULL);
+ return TRUE;
+}
+
+/**
+ * Handle incoming messages
+ */
+static DBusHandlerResult message_handler(DBusConnection *con, DBusMessage *msg,
+ private_dbus_t *this)
+{
+ bool handled;
+
+ if (dbus_message_is_method_call(msg, NM_dbus_STRONG,
+ "startConnection"))
+ {
+ handled = start_connection(this, msg);
+ }
+ else if (dbus_message_is_method_call(msg, NM_dbus_STRONG,
+ "stopConnection"))
+ {
+ handled = stop_connection(this, msg);
+ }
+ else if (dbus_message_is_method_call(msg, NM_dbus_STRONG,
+ "getState"))
+ {
+ handled = get_state(this, msg);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "ignoring DBUS message %s.%s",
+ dbus_message_get_interface(msg), dbus_message_get_member(msg));
+ handled = FALSE;
+ }
+
+ if (handled)
+ {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/**
+ * Handle received signals
+
+static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg,
+ private_dbus_t *this)
+{
+ bool handled;
+
+ if (dbus_message_is_signal(msg, NM_dbus, "VPNConnectionStateChange"))
+ {
+ NMVPNState state;
+ char *name;
+
+ if (dbus_message_get_args(msg, &this->err, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID))
+ {
+ DBG1(DBG_CFG, "got state %d for %s", state, name);
+ }
+ handled = TRUE;
+ }
+ else
+ {
+ DBG1(DBG_CFG, "ignoring DBUS signal %s.%s",
+ dbus_message_get_interface(msg), dbus_message_get_member(msg));
+ handled = FALSE;
+ }
+ if (handled)
+ {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+} */
+
+/**
+ * dispatcher function processed by a seperate thread
+ */
+static job_requeue_t dispatch(private_dbus_t *this)
+{
+ if (dbus_connection_read_write_dispatch(this->conn, -1))
+ {
+ return JOB_REQUEUE_DIRECT;
+ }
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Implementation of interface_t.destroy.
+ */
+static void destroy(private_dbus_t *this)
+{
+ this->job->cancel(this->job);
+ dbus_connection_close(this->conn);
+ dbus_error_free(&this->err);
+ dbus_shutdown();
+ free(this->name);
+ free(this);
+}
+
+/*
+ * Described in header file
+ */
+plugin_t *plugin_create()
+{
+ int ret;
+ DBusObjectPathVTable v = {NULL, (void*)&message_handler, NULL, NULL, NULL, NULL};
+ private_dbus_t *this = malloc_thing(private_dbus_t);
+
+ this->public.plugin.destroy = (void (*)(plugin_t*))destroy;
+
+ dbus_error_init(&this->err);
+ this->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &this->err);
+ if (dbus_error_is_set(&this->err))
+ {
+ DBG1(DBG_CFG, "unable to open DBUS connection: %s", this->err.message);
+ charon->kill(charon, "DBUS initialization failed");
+ }
+ dbus_connection_set_exit_on_disconnect(this->conn, FALSE);
+
+ ret = dbus_bus_request_name(this->conn, NM_DBUS_SERVICE_STRONG,
+ DBUS_NAME_FLAG_REPLACE_EXISTING , &this->err);
+ if (dbus_error_is_set(&this->err))
+ {
+ DBG1(DBG_CFG, "unable to set DBUS name: %s", this->err.message);
+ charon->kill(charon, "unable to set DBUS name");
+ }
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ {
+ charon->kill(charon, "DBUS name already owned");
+ }
+ if (!dbus_connection_register_object_path(this->conn, NM_DBUS_PATH_STRONG, &v, this))
+ {
+ charon->kill(charon, "unable to register DBUS message handler");
+ }
+ /*
+ if (!dbus_connection_add_filter(this->conn, (void*)signal_handler, this, NULL))
+ {
+ charon->kill(charon, "unable to register DBUS signal handler");
+ }
+
+ dbus_bus_add_match(this->conn, "type='signal', "
+ "interface='" NM_dbus_VPN "',"
+ "path='" NM_DBUS_PATH_VPN "'", &this->err);
+ if (dbus_error_is_set (&this->err))
+ {
+ charon->kill(charon, "unable to add DBUS signal match");
+ }*/
+
+ this->name = NULL;
+ this->state = NM_VPN_STATE_INIT;
+ set_state(this, NM_VPN_STATE_STOPPED);
+
+ this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
+ charon->processor->queue_job(charon->processor, (job_t*)this->job);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/dbus/dbus.h b/src/charon/plugins/dbus/dbus.h
new file mode 100644
index 000000000..e5bc2d20c
--- /dev/null
+++ b/src/charon/plugins/dbus/dbus.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup dbus dbus
+ * @ingroup cplugins
+ *
+ * @defgroup dbus_i dbus
+ * @{ @ingroup dbus
+ */
+
+#ifndef DBUS_H_
+#define DBUS_H_
+
+#include <plugins/plugin.h>
+
+typedef struct dbus_t dbus_t;
+
+/**
+ * NetworkManager DBUS control plugin.
+ *
+ * This plugin uses a DBUS connection. It is designed to work in conjuction
+ * with NetworkManager to configure and control the daemon.
+ */
+struct dbus_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a dbus plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* DBUS_H_ @}*/
diff --git a/src/charon/plugins/eap_aka/Makefile.am b/src/charon/plugins/eap_aka/Makefile.am
new file mode 100644
index 000000000..c938716f9
--- /dev/null
+++ b/src/charon/plugins/eap_aka/Makefile.am
@@ -0,0 +1,11 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-eapaka.la
+
+libcharon_eapaka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c eap_aka.h eap_aka.c
+libcharon_eapaka_la_LDFLAGS = -module
+libcharon_eapaka_la_LIBADD = -lgmp
+
diff --git a/src/charon/plugins/eap_aka/eap_aka.c b/src/charon/plugins/eap_aka/eap_aka.c
new file mode 100644
index 000000000..3dd842b9e
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka.c
@@ -0,0 +1,1557 @@
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+
+/* The EAP-AKA method uses it's own simple parser for processing EAP-AKA
+ * payloads, as the IKEv2 parser is not suitable for that job. There are
+ * two simple methods for parsing payloads, read_header() and read_attribute().
+ * Every EAP-AKA payload consists of a header and a list of attributes. Those
+ * functions mentioned read the data and return the type of the found
+ * attribute/EAP-AKA-type. For generating a EAP-AKA message, we have a
+ * build_aka_payload(), which builds the whole message from a variable
+ * argument list containing its attributes.
+ * The processing of messages is split up in various functions:
+ * - peer_process() - General processing multiplexer for the peer
+ * - peer_process_challenge() - Specific AKA-Challenge processor
+ * - peer_process_notification() - Processing of AKA-Notification
+ * - server_process() - General processing multiplexer for the server
+ * - peer_process_challenge() - Processing of a received Challenge response
+ * - peer_process_synchronize() - Process a sequence number synchronization
+ * - server_initiate() - Initiation method for the server, calls
+ * - server_initiate_challenge() - Initiation of AKA-Challenge
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <gmp.h>
+
+#include "eap_aka.h"
+
+#include <daemon.h>
+#include <library.h>
+#include <utils/randomizer.h>
+#include <crypto/hashers/hasher.h>
+
+/* Use test vectors specified in S.S0055
+#define TEST_VECTORS */
+
+#define RAND_LENGTH 16
+#define RES_LENGTH 16
+#define SQN_LENGTH 6
+#define K_LENGTH 16
+#define MAC_LENGTH 8
+#define CK_LENGTH 16
+#define IK_LENGTH 16
+#define AK_LENGTH 6
+#define AMF_LENGTH 2
+#define FMK_LENGTH 4
+#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH)
+#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH)
+#define PAYLOAD_LENGTH 64
+#define MK_LENGTH 20
+#define MSK_LENGTH 64
+#define EMSK_LENGTH 64
+#define KAUTH_LENGTH 16
+#define KENCR_LENGTH 16
+#define AT_MAC_LENGTH 16
+
+#define F1 0x42
+#define F1STAR 0x43
+#define F2 0x44
+#define F3 0x45
+#define F4 0x46
+#define F5 0x47
+#define F5STAR 0x48
+
+typedef enum aka_subtype_t aka_subtype_t;
+typedef enum aka_attribute_t aka_attribute_t;
+
+/**
+ * Subtypes of AKA messages
+ */
+enum aka_subtype_t {
+ AKA_CHALLENGE = 1,
+ AKA_AUTHENTICATION_REJECT = 2,
+ AKA_SYNCHRONIZATION_FAILURE = 4,
+ AKA_IDENTITY = 5,
+ AKA_NOTIFICATION = 12,
+ AKA_REAUTHENTICATION = 13,
+ AKA_CLIENT_ERROR = 14,
+};
+
+/**
+ * Attribute types in AKA messages
+ */
+enum aka_attribute_t {
+ /** defines the end of attribute list */
+ AT_END = -1,
+ AT_RAND = 1,
+ AT_AUTN = 2,
+ AT_RES = 3,
+ AT_AUTS = 4,
+ AT_PADDING = 6,
+ AT_NONCE_MT = 7,
+ AT_PERMANENT_ID_REQ = 10,
+ AT_MAC = 11,
+ AT_NOTIFICATION = 12,
+ AT_ANY_ID_REQ = 13,
+ AT_IDENTITY = 14,
+ AT_VERSION_LIST = 15,
+ AT_SELECTED_VERSION = 16,
+ AT_FULLAUTH_ID_REQ = 17,
+ AT_COUNTER = 19,
+ AT_COUNTER_TOO_SMALL = 20,
+ AT_NONCE_S = 21,
+ AT_CLIENT_ERROR_CODE = 22,
+ AT_IV = 129,
+ AT_ENCR_DATA = 130,
+ AT_NEXT_PSEUDONYM = 132,
+ AT_NEXT_REAUTH_ID = 133,
+ AT_CHECKCODE = 134,
+ AT_RESULT_IND = 135,
+};
+
+ENUM_BEGIN(aka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
+ "AKA_CHALLENGE",
+ "AKA_AUTHENTICATION_REJECT",
+ "AKA_3",
+ "AKA_SYNCHRONIZATION_FAILURE",
+ "AKA_IDENTITY");
+ENUM_NEXT(aka_subtype_names, AKA_NOTIFICATION, AKA_CLIENT_ERROR, AKA_IDENTITY,
+ "AKA_NOTIFICATION",
+ "AKA_REAUTHENTICATION",
+ "AKA_CLIENT_ERROR");
+ENUM_END(aka_subtype_names, AKA_CLIENT_ERROR);
+
+
+ENUM_BEGIN(aka_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
+ "AT_END",
+ "AT_0",
+ "AT_RAND",
+ "AT_AUTN",
+ "AT_RES",
+ "AT_AUTS",
+ "AT_5",
+ "AT_PADDING",
+ "AT_NONCE_MT",
+ "AT_8",
+ "AT_9",
+ "AT_PERMANENT_ID_REQ",
+ "AT_MAC",
+ "AT_NOTIFICATION",
+ "AT_ANY_ID_REQ",
+ "AT_IDENTITY",
+ "AT_VERSION_LIST",
+ "AT_SELECTED_VERSION",
+ "AT_FULLAUTH_ID_REQ",
+ "AT_18",
+ "AT_COUNTER",
+ "AT_COUNTER_TOO_SMALL",
+ "AT_NONCE_S",
+ "AT_CLIENT_ERROR_CODE");
+ENUM_NEXT(aka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
+ "AT_IV",
+ "AT_ENCR_DATA",
+ "AT_131",
+ "AT_NEXT_PSEUDONYM",
+ "AT_NEXT_REAUTH_ID",
+ "AT_CHECKCODE",
+ "AT_RESULT_IND");
+ENUM_END(aka_attribute_names, AT_RESULT_IND);
+
+
+typedef struct private_eap_aka_t private_eap_aka_t;
+
+/**
+ * Private data of an eap_aka_t object.
+ */
+struct private_eap_aka_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_aka_t public;
+
+ /**
+ * ID of the server
+ */
+ identification_t *server;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * SHA11 hasher
+ */
+ hasher_t *sha1;
+
+ /**
+ * SHA1_NOFINAL hasher for G() function
+ */
+ hasher_t *sha1_nof;
+
+ /**
+ * MAC function used in EAP-AKA
+ */
+ signer_t *signer;
+
+ /**
+ * pseudo random function used in EAP-aka
+ */
+ prf_t *prf;
+
+ /**
+ * Key for EAP MAC
+ */
+ chunk_t k_auth;
+
+ /**
+ * Key for EAP encryption
+ */
+ chunk_t k_encr;
+
+ /**
+ * MSK
+ */
+ chunk_t msk;
+
+ /**
+ * Extendend MSK
+ */
+ chunk_t emsk;
+
+ /**
+ * Expected result from client XRES
+ */
+ chunk_t xres;
+
+ /**
+ * Shared secret K from ipsec.conf (padded)
+ */
+ chunk_t k;
+
+ /**
+ * random value RAND generated by server
+ */
+ chunk_t rand;
+};
+
+/** Family key, as proposed in S.S0055 */
+static u_int8_t fmk_buf[] = {0x41, 0x48, 0x41, 0x47};
+static chunk_t fmk = chunk_from_buf(fmk_buf);
+
+/** Authentication management field */
+static u_int8_t amf_buf[] = {0x00, 0x01};
+static chunk_t amf = chunk_from_buf(amf_buf);
+
+/** AT_CLIENT_ERROR_CODE AKA attribute */
+static u_int8_t client_error_code_buf[] = {0, 0};
+static chunk_t client_error_code = chunk_from_buf(client_error_code_buf);
+
+/** previously used sqn by peer, next one must be greater */
+static u_int8_t peer_sqn_buf[6];
+static chunk_t peer_sqn = chunk_from_buf(peer_sqn_buf);
+
+/** set SQN to the current time */
+static void update_sqn(u_int8_t *sqn, time_t offset)
+{
+ timeval_t time;
+ gettimeofday(&time, NULL);
+ /* set sqb_sqn to an integer containing seconds followed by most
+ * significant useconds */
+ time.tv_sec = htonl(time.tv_sec + offset);
+ /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */
+ time.tv_usec <<= 12;
+ time.tv_usec = htonl(time.tv_usec);
+ memcpy(sqn, &time.tv_sec, 4);
+ memcpy(sqn + 4, &time.tv_usec, 2);
+}
+
+/** initialize peers SQN to the current system time at startup */
+static void __attribute__ ((constructor))init_sqn(void)
+{
+ update_sqn(peer_sqn_buf, 0);
+}
+
+/**
+ * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1
+ */
+static u_int8_t g[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2d
+};
+
+/**
+ * Predefined random bits from the RAND Corporation book
+ */
+static u_int8_t a[] = {
+ 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11,
+ 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49,
+ 0x3f, 0x4c, 0x63, 0x65
+};
+
+/**
+ * Predefined random bits from the RAND Corporation book
+ */
+static u_int8_t b[] = {
+ 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51,
+ 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e,
+ 0x7e, 0xec, 0x45, 0xe0
+};
+
+/**
+ * Multiplicate two mpz_t with bits interpreted as polynoms.
+ */
+static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b)
+{
+ mpz_t bm, rm;
+ int current = 0, shifted = 0, shift;
+
+ mpz_init_set(bm, b);
+ mpz_init_set_ui(rm, 0);
+ /* scan through a, for each found bit: */
+ while ((current = mpz_scan1(a, current)) != ULONG_MAX)
+ {
+ /* XOR shifted b into r */
+ shift = current - shifted;
+ mpz_mul_2exp(bm, bm, shift);
+ shifted += shift;
+ mpz_xor(rm, rm, bm);
+ current++;
+ }
+
+ mpz_swap(r, rm);
+ mpz_clear(rm);
+ mpz_clear(bm);
+}
+
+/**
+ * Calculate the sum of a + b interpreted as polynoms.
+ */
+static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b)
+{
+ /* addition of polynominals is just the XOR */
+ mpz_xor(res, a, b);
+}
+
+/**
+ * Calculate the remainder of a/b interpreted as polynoms.
+ */
+static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b)
+{
+ /* Example:
+ * a = 10001010
+ * b = 00000101
+ */
+ int a_bit, b_bit, diff;
+ mpz_t bm, am;
+
+ mpz_init_set(am, a);
+ mpz_init(bm);
+
+ a_bit = mpz_sizeinbase(a, 2);
+ b_bit = mpz_sizeinbase(b, 2);
+
+ /* don't do anything if b > a */
+ if (a_bit >= b_bit)
+ {
+ /* shift b left to align up most signaficant "1" to a:
+ * a = 10001010
+ * b = 10100000
+ */
+ mpz_mul_2exp(bm, b, a_bit - b_bit);
+ do
+ {
+ /* XOR b into a, this kills the most significant "1":
+ * a = 00101010
+ */
+ mpz_xor(am, am, bm);
+ /* find the next most significant "1" in a, and align up b:
+ * a = 00101010
+ * b = 00101000
+ */
+ diff = a_bit - mpz_sizeinbase(am, 2);
+ mpz_div_2exp(bm, bm, diff);
+ a_bit -= diff;
+ }
+ while (b_bit <= mpz_sizeinbase(bm, 2));
+ /* While b is not shifted to its original value */
+ }
+ /* after another iteration:
+ * a = 00000010
+ * which is the polynomial modulo
+ */
+
+ mpz_swap(r, am);
+ mpz_clear(am);
+ mpz_clear(bm);
+}
+
+/**
+ * Step 4 of the various fx() functions:
+ * Polynomial whiten calculations
+ */
+static void step4(private_eap_aka_t *this, u_int8_t x[])
+{
+ mpz_t xm, am, bm, gm;
+
+ mpz_init(xm);
+ mpz_init(am);
+ mpz_init(bm);
+ mpz_init(gm);
+
+ mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x);
+ mpz_import(am, sizeof(a), 1, 1, 1, 0, a);
+ mpz_import(bm, sizeof(b), 1, 1, 1, 0, b);
+ mpz_import(gm, sizeof(g), 1, 1, 1, 0, g);
+
+ mpz_mul_poly(xm, am, xm);
+ mpz_add_poly(xm, bm, xm);
+ mpz_mod_poly(xm, xm, gm);
+
+ mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm);
+
+ mpz_clear(xm);
+ mpz_clear(am);
+ mpz_clear(bm);
+ mpz_clear(gm);
+}
+
+/**
+ * Implementation of the G() function based on SHA1
+ */
+static void g_sha1(private_eap_aka_t *this,
+ u_int8_t t[], chunk_t c, u_int8_t res[])
+{
+ u_int8_t buf[64];
+
+ if (c.len < sizeof(buf))
+ {
+ /* pad c with zeros */
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, c.ptr, c.len);
+ c.ptr = buf;
+ c.len = sizeof(buf);
+ }
+ else
+ {
+ /* not more than 512 bits can be G()-ed */
+ c.len = sizeof(buf);
+ }
+
+ /* calculate the special (HASH_SHA1_STATE) hash*/
+ this->sha1_nof->get_hash(this->sha1_nof, c, res);
+}
+
+/**
+ * Step 3 of the various fx() functions:
+ * XOR the key into the SHA1 IV
+ */
+static void step3(private_eap_aka_t *this,
+ chunk_t k, chunk_t payload, u_int8_t h[])
+{
+ u_int8_t iv[] = {
+ 0x67,0x45,0x23,0x01,0xEF,0xCD,0xAB,0x89,0x98,0xBA,
+ 0xDC,0xFE,0x10,0x32,0x54,0x76,0xC3,0xD2,0xE1,0xF0,
+ };
+
+ /* XOR key into IV */
+ memxor(iv, k.ptr, k.len);
+
+ /* hash it with the G() function defined in FIPS 186-2 from fips_prf.h */
+ g_sha1(this, iv, payload, h);
+}
+
+/**
+ * Calculation function for f2(), f3(), f4()
+ */
+static void fx(private_eap_aka_t *this,
+ u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[])
+{
+ chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
+ u_int8_t h[HASH_SIZE_SHA1];
+ u_int8_t i;
+
+ for (i = 0; i < 2; i++)
+ {
+ memset(payload.ptr, 0x5c, payload.len);
+ payload.ptr[11] ^= f;
+ memxor(payload.ptr + 12, fmk.ptr, fmk.len);
+ memxor(payload.ptr + 24, rand.ptr, rand.len);
+
+ payload.ptr[3] ^= i;
+ payload.ptr[19] ^= i;
+ payload.ptr[35] ^= i;
+ payload.ptr[51] ^= i;
+
+ step3(this, k, payload, h);
+ step4(this, h);
+ memcpy(out + i * 8, h, 8);
+ }
+}
+
+/**
+ * Calculation function of f1() and f1star()
+ */
+static void f1x(private_eap_aka_t *this,
+ u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn,
+ chunk_t amf, u_int8_t mac[])
+{
+ /* generate MAC = f1(FMK, SQN, RAND, AMF)
+ * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit
+ * payload which gets hashed
+ */
+ chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
+ u_int8_t h[HASH_SIZE_SHA1];
+
+ memset(payload.ptr, 0x5c, PAYLOAD_LENGTH);
+ payload.ptr[11] ^= f;
+ memxor(payload.ptr + 12, fmk.ptr, fmk.len);
+ memxor(payload.ptr + 16, rand.ptr, rand.len);
+ memxor(payload.ptr + 34, sqn.ptr, sqn.len);
+ memxor(payload.ptr + 42, amf.ptr, amf.len);
+
+ step3(this, k, payload, h);
+ step4(this, h);
+ memcpy(mac, h, MAC_LENGTH);
+}
+
+/**
+ * Calculation function of f5() and f5star()
+ */
+static void f5x(private_eap_aka_t *this,
+ u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[])
+{
+ chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
+ u_int8_t h[HASH_SIZE_SHA1];
+
+ memset(payload.ptr, 0x5c, payload.len);
+ payload.ptr[11] ^= f;
+ memxor(payload.ptr + 12, fmk.ptr, fmk.len);
+ memxor(payload.ptr + 16, rand.ptr, rand.len);
+
+ step3(this, k, payload, h);
+ step4(this, h);
+ memcpy(ak, h, AK_LENGTH);
+}
+
+/**
+ * Calculate the MAC from a RAND, SQN, AMF value using K
+ */
+static void f1(private_eap_aka_t *this, chunk_t k, chunk_t rand, chunk_t sqn,
+ chunk_t amf, u_int8_t mac[])
+{
+ f1x(this, F1, k, rand, sqn, amf, mac);
+ DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH);
+}
+
+/**
+ * Calculate the MACS from a RAND, SQN, AMF value using K
+ */
+static void f1star(private_eap_aka_t *this, chunk_t k, chunk_t rand,
+ chunk_t sqn, chunk_t amf, u_int8_t macs[])
+{
+ f1x(this, F1STAR, k, rand, sqn, amf, macs);
+ DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH);
+}
+
+/**
+ * Calculate RES from RAND using K
+ */
+static void f2(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t res[])
+{
+ fx(this, F2, k, rand, res);
+ DBG3(DBG_IKE, "RES %b", res, RES_LENGTH);
+}
+
+/**
+ * Calculate CK from RAND using K
+ */
+static void f3(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ck[])
+{
+ fx(this, F3, k, rand, ck);
+ DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH);
+}
+
+/**
+ * Calculate IK from RAND using K
+ */
+static void f4(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ik[])
+{
+ fx(this, F4, k, rand, ik);
+ DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH);
+}
+
+/**
+ * Calculate AK from a RAND using K
+ */
+static void f5(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ak[])
+{
+ f5x(this, F5, k, rand, ak);
+ DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH);
+}
+
+/**
+ * Calculate AKS from a RAND using K
+ */
+static void f5star(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t aks[])
+{
+ f5x(this, F5STAR, k, rand, aks);
+ DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH);
+}
+
+/**
+ * derive the keys needed for EAP_AKA
+ */
+static bool derive_keys(private_eap_aka_t *this, identification_t *id)
+{
+ chunk_t ck, ik, mk, identity, tmp;
+
+ ck = chunk_alloca(CK_LENGTH);
+ ik = chunk_alloca(IK_LENGTH);
+ mk = chunk_alloca(MK_LENGTH);
+ identity = id->get_encoding(id);
+
+ /* MK = SHA1( Identity | IK | CK ) */
+ f3(this, this->k, this->rand, ck.ptr);
+ f4(this, this->k, this->rand, ik.ptr);
+ DBG3(DBG_IKE, "Identity %B", &identity);
+ tmp = chunk_cata("ccc", identity, ik, ck);
+ DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp);
+ this->sha1->get_hash(this->sha1, tmp, mk.ptr);
+
+ /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0)
+ * FIPS PRF has 320 bit block size, we need 160 byte for keys
+ * => run prf four times */
+ this->prf->set_key(this->prf, mk);
+ tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4);
+ this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr);
+ this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1);
+ this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2);
+ this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3);
+ chunk_free(&this->k_encr);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth,
+ 64, &this->msk, 64, &this->emsk);
+ DBG3(DBG_IKE, "MK %B", &mk);
+ DBG3(DBG_IKE, "PRF res %B", &tmp);
+ DBG3(DBG_IKE, "K_encr %B", &this->k_encr);
+ DBG3(DBG_IKE, "K_auth %B", &this->k_auth);
+ DBG3(DBG_IKE, "MSK %B", &this->msk);
+ DBG3(DBG_IKE, "EMSK %B", &this->emsk);
+ return TRUE;
+}
+
+/*
+ * Get a shared key from ipsec.secrets.
+ * We use the standard keys as used in preshared key authentication. As
+ * these keys have an undefined length, we:
+ * - strip them if they are longer
+ * - fill them up with '\0' if they are shorter
+ */
+static status_t load_key(identification_t *me, identification_t *other, chunk_t *k)
+{
+ shared_key_t *shared;
+ chunk_t key;
+
+ shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
+ me, other);
+ if (shared == NULL)
+ {
+ return NOT_FOUND;
+ }
+ key = shared->get_key(shared);
+ chunk_free(k);
+ *k = chunk_alloc(K_LENGTH);
+ memset(k->ptr, '\0', k->len);
+ memcpy(k->ptr, key.ptr, min(key.len, k->len));
+ shared->destroy(shared);
+ return SUCCESS;
+}
+
+/**
+ * skip EAP_AKA header in message and returns its AKA subtype
+ */
+static aka_subtype_t read_header(chunk_t *message)
+{
+ aka_subtype_t type;
+
+ if (message->len < 8)
+ {
+ *message = chunk_empty;
+ return 0;
+ }
+ type = *(message->ptr + 5);
+ *message = chunk_skip(*message, 8);
+ return type;
+}
+
+/**
+ * read the next attribute from the chunk data
+ */
+static aka_attribute_t read_attribute(chunk_t *data, chunk_t *attr_data)
+{
+ aka_attribute_t attribute;
+ size_t length;
+
+ DBG3(DBG_IKE, "reading attribute from %B", data);
+
+ if (data->len < 2)
+ {
+ return AT_END;
+ }
+ /* read attribute and length */
+ attribute = *data->ptr++;
+ length = *data->ptr++ * 4 - 2;
+ data->len -= 2;
+ DBG3(DBG_IKE, "found attribute %N with length %d",
+ aka_attribute_names, attribute, length);
+ if (length > data->len)
+ {
+ return AT_END;
+ }
+ /* apply attribute value to attr_data */
+ attr_data->len = length;
+ attr_data->ptr = data->ptr;
+ /* update data to point to next attribute */
+ *data = chunk_skip(*data, length);
+ return attribute;
+}
+
+/**
+ * Build an AKA payload from different attributes.
+ * The variable argument takes an aka_attribute_t
+ * followed by its data in a chunk.
+ */
+static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code,
+ u_int8_t identifier, aka_subtype_t type, ...)
+{
+ chunk_t message = chunk_alloca(512); /* is enought for all current messages */
+ chunk_t pos = message;
+ eap_payload_t *payload;
+ va_list args;
+ aka_attribute_t attr;
+ u_int8_t *mac_pos = NULL;
+
+ /* write EAP header, skip length bytes */
+ *pos.ptr++ = code;
+ *pos.ptr++ = identifier;
+ pos.ptr += 2;
+ pos.len -= 4;
+ /* write AKA header with type and subtype, null reserved bytes */
+ *pos.ptr++ = EAP_AKA;
+ *pos.ptr++ = type;
+ *pos.ptr++ = 0;
+ *pos.ptr++ = 0;
+ pos.len -= 4;
+
+ va_start(args, type);
+ while ((attr = va_arg(args, aka_attribute_t)) != AT_END)
+ {
+ chunk_t data = va_arg(args, chunk_t);
+
+ DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data);
+
+ /* write attribute header */
+ *pos.ptr++ = attr;
+ pos.len--;
+
+ switch (attr)
+ {
+ case AT_RES:
+ {
+ /* attribute length in 4byte words */
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ /* RES length in bits */
+ *(u_int16_t*)pos.ptr = htons(data.len * 8);
+ pos = chunk_skip(pos, sizeof(u_int16_t));
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_AUTN:
+ case AT_RAND:
+ {
+ *pos.ptr++ = data.len/4 + 1; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_MAC:
+ {
+ *pos.ptr++ = 5; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ mac_pos = pos.ptr;
+ /* MAC is calculated over message including zeroed AT_MAC attribute */
+ memset(mac_pos, 0, AT_MAC_LENGTH);
+ pos.ptr += AT_MAC_LENGTH;
+ pos.len -= AT_MAC_LENGTH;
+ break;
+ }
+ default:
+ {
+ /* length is data length in 4-bytes + 1 for header */
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ }
+ }
+ }
+ va_end(args);
+
+ /* calculate message length, write into header */
+ message.len = pos.ptr - message.ptr;
+ *(u_int16_t*)(message.ptr + 2) = htons(message.len);
+
+ /* create MAC if AT_MAC attribte was included */
+ if (mac_pos)
+ {
+ this->signer->set_key(this->signer, this->k_auth);
+ DBG3(DBG_IKE, "AT_MAC signature of %B", &message);
+ DBG3(DBG_IKE, "using key %B", &this->k_auth);
+ this->signer->get_signature(this->signer, message, mac_pos);
+ DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH);
+ }
+
+ /* payload constructor takes data with some bytes skipped */
+ payload = eap_payload_create_data(message);
+
+ DBG3(DBG_IKE, "created EAP message %B", &message);
+ return payload;
+}
+
+/**
+ * Initiate a AKA-Challenge using SQN
+ */
+static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn,
+ eap_payload_t **out)
+{
+ randomizer_t *randomizer;
+ status_t status;
+ chunk_t mac, ak, autn;
+
+ mac = chunk_alloca(MAC_LENGTH);
+ ak = chunk_alloca(AK_LENGTH);
+ chunk_free(&this->rand);
+ chunk_free(&this->xres);
+
+ /* generate RAND:
+ * we use our standard randomizer, not f0() proposed in S.S0055
+ */
+ randomizer = randomizer_create();
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, RAND_LENGTH, &this->rand);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed");
+ return FAILED;
+ }
+
+# ifdef TEST_VECTORS
+ /* Test vector for RAND */
+ u_int8_t test_rand[] = {
+ 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f,
+ 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e,
+ };
+ memcpy(this->rand.ptr, test_rand, this->rand.len);
+# endif /* TEST_VECTORS */
+
+ /* Get the shared key K: */
+ if (load_key(this->server, this->peer, &this->k) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate "
+ "with EAP-AKA", this->server, this->peer);
+ return FAILED;
+ }
+
+# ifdef TEST_VECTORS
+ /* Test vector for K */
+ u_int8_t test_k[] = {
+ 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c,
+ 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d,
+ };
+ memcpy(this->k.ptr, test_k, this->k.len);
+# endif /* TEST_VECTORS */
+
+ /* generate MAC */
+ f1(this, this->k, this->rand, sqn, amf, mac.ptr);
+
+ /* generate AK */
+ f5(this, this->k, this->rand, ak.ptr);
+
+ /* precalculate XRES as expected from client */
+ this->xres = chunk_alloc(RES_LENGTH);
+ f2(this, this->k, this->rand, this->xres.ptr);
+
+ /* calculate AUTN = (SQN xor AK) || AMF || MAC */
+ autn = chunk_cata("ccc", sqn, amf, mac);
+ memxor(autn.ptr, ak.ptr, ak.len);
+ DBG3(DBG_IKE, "AUTN %B", &autn);
+
+
+ /* derive K_encr, K_auth, MSK, EMSK */
+ derive_keys(this, this->peer);
+
+ /* build payload */
+ *out = build_aka_payload(this, EAP_REQUEST, 0, AKA_CHALLENGE,
+ AT_RAND, this->rand, AT_AUTN, autn, AT_MAC,
+ chunk_empty, AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.initiate for an EAP_AKA server
+ */
+static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out)
+{
+ chunk_t sqn = chunk_alloca(SQN_LENGTH);
+
+ /* we use an offset of 3 minutes to tolerate clock inaccuracy
+ * without the need to synchronize sequence numbers */
+ update_sqn(sqn.ptr, 180);
+
+# ifdef TEST_VECTORS
+ /* Test vector for SQN */
+ u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01};
+ memcpy(sqn.ptr, test_sqn, sqn.len);
+# endif /* TEST_VECTORS */
+
+ return server_initiate_challenge(this, sqn, out);
+}
+
+static status_t server_process_synchronize(private_eap_aka_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf;
+ u_int i;
+
+ message = in->get_data(in);
+ pos = message;
+ read_header(&pos);
+
+ /* iterate over attributes */
+ while (TRUE)
+ {
+ aka_attribute_t attribute = read_attribute(&pos, &attr);
+ switch (attribute)
+ {
+ case AT_END:
+ break;
+ case AT_AUTS:
+ auts = attr;
+ continue;
+ default:
+ if (attribute >= 0 && attribute <= 127)
+ {
+ DBG1(DBG_IKE, "found non skippable attribute %N",
+ aka_attribute_names, attribute);
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "ignoring skippable attribute %N",
+ aka_attribute_names, attribute);
+ continue;
+ }
+ break;
+ }
+
+ if (auts.len != AUTS_LENGTH)
+ {
+ DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS");
+ return FAILED;
+ }
+
+ chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs);
+ aks = chunk_alloca(AK_LENGTH);
+ f5star(this, this->k, this->rand, aks.ptr);
+ /* decrypt serial number by XORing AKS */
+ memxor(sqn.ptr, aks.ptr, aks.len);
+
+ /* verify MACS */
+ xmacs = chunk_alloca(MAC_LENGTH);
+ amf = chunk_alloca(AMF_LENGTH);
+ /* an AMF of zero is used for MACS calculation */
+ memset(amf.ptr, 0, amf.len);
+ f1star(this, this->k, this->rand, sqn, amf, xmacs.ptr);
+ if (!chunk_equals(macs, xmacs))
+ {
+ DBG1(DBG_IKE, "received MACS does not match XMACS");
+ DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs);
+ return FAILED;
+ }
+
+ /* retry the challenge with the received SQN + 1*/
+ for (i = SQN_LENGTH - 1; i >= 0; i--)
+ {
+ if (++sqn.ptr[i] != 0)
+ {
+ break;
+ }
+ }
+ return server_initiate_challenge(this, sqn, out);
+}
+
+/**
+ * process an AKA_Challenge response
+ */
+static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t *in)
+{
+ chunk_t attr, res = chunk_empty, at_mac = chunk_empty, pos, message;
+
+ message = in->get_data(in);
+ pos = message;
+ read_header(&pos);
+
+ /* iterate over attributes */
+ while (TRUE)
+ {
+ aka_attribute_t attribute = read_attribute(&pos, &attr);
+ switch (attribute)
+ {
+ case AT_END:
+ break;
+ case AT_RES:
+ res = attr;
+ if (attr.len == 2 + RES_LENGTH &&
+ *(u_int16_t*)attr.ptr == htons(RES_LENGTH * 8))
+ {
+ res = chunk_skip(attr, 2);
+ }
+ continue;
+
+ case AT_MAC:
+ attr = chunk_skip(attr, 2);
+ at_mac = chunk_clonea(attr);
+ /* zero MAC in message for MAC verification */
+ memset(attr.ptr, 0, attr.len);
+ continue;
+ default:
+ if (attribute >= 0 && attribute <= 127)
+ {
+ DBG1(DBG_IKE, "found non skippable attribute %N",
+ aka_attribute_names, attribute);
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "ignoring skippable attribute %N",
+ aka_attribute_names, attribute);
+ continue;
+ }
+ break;
+ }
+
+ /* verify EAP message MAC AT_MAC */
+ {
+ this->signer->set_key(this->signer, this->k_auth);
+ DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
+ DBG3(DBG_IKE, "using key %B", &this->k_auth);
+ if (!this->signer->verify_signature(this->signer, message, at_mac))
+ {
+ DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed");
+ return FAILED;
+ }
+ }
+
+ /* compare received RES against stored precalculated XRES */
+ if (!chunk_equals(res, this->xres))
+ {
+ DBG1(DBG_IKE, "received RES does not match XRES");
+ DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres);
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of eap_method_t.process for EAP_AKA servers
+ */
+static status_t server_process(private_eap_aka_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message;
+ aka_subtype_t type;
+
+ message = in->get_data(in);
+ type = read_header(&message);
+
+ DBG3(DBG_IKE, "received EAP message %B", &message);
+
+ switch (type)
+ {
+ case AKA_CHALLENGE:
+ {
+ return server_process_challenge(this, in);
+ }
+ case AKA_AUTHENTICATION_REJECT:
+ case AKA_CLIENT_ERROR:
+ {
+ DBG1(DBG_IKE, "received %N, authentication failed",
+ aka_subtype_names, type);
+ return FAILED;
+ }
+ case AKA_SYNCHRONIZATION_FAILURE:
+ {
+ DBG1(DBG_IKE, "received %N, retrying with received SQN",
+ aka_subtype_names, type);
+ return server_process_synchronize(this, in, out);
+ }
+ default:
+ DBG1(DBG_IKE, "received unknown AKA subtype %N, authentication failed",
+ aka_subtype_names, type);
+ return FAILED;
+ }
+}
+
+/**
+ * Process an incoming AKA-Challenge client side
+ */
+static status_t peer_process_challenge(private_eap_aka_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t attr = chunk_empty;
+ chunk_t autn = chunk_empty, at_mac = chunk_empty;
+ chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos;
+ u_int8_t identifier;
+
+ ak = chunk_alloca(AK_LENGTH);
+ xmac = chunk_alloca(MAC_LENGTH);
+ res = chunk_alloca(RES_LENGTH);
+ chunk_free(&this->rand);
+
+ message = in->get_data(in);
+ pos = message;
+ read_header(&pos);
+ identifier = in->get_identifier(in);
+
+ DBG3(DBG_IKE, "reading attributes from %B", &pos);
+
+ /* iterate over attributes */
+ while (TRUE)
+ {
+ aka_attribute_t attribute = read_attribute(&pos, &attr);
+ switch (attribute)
+ {
+ case AT_END:
+ break;
+ case AT_RAND:
+ this->rand = chunk_clone(chunk_skip(attr, 2));
+ continue;
+ case AT_AUTN:
+ autn = chunk_skip(attr, 2);
+ continue;
+ case AT_MAC:
+ attr = chunk_skip(attr, 2);
+ at_mac = chunk_clonea(attr);
+ /* set MAC in message to zero for own MAC verification */
+ memset(attr.ptr, 0, attr.len);
+ continue;
+ default:
+ if (attribute >= 0 && attribute <= 127)
+ {
+ /* non skippable attribute, abort */
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
+ DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d",
+ aka_attribute_names, attribute,
+ aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
+ return NEED_MORE;
+ }
+ DBG1(DBG_IKE, "ignoring skippable attribute %N",
+ aka_attribute_names, attribute);
+ continue;
+ }
+ break;
+ }
+
+ if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH)
+ {
+ /* required attributes wrong/not found, abort */
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
+ DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d",
+ aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
+ return NEED_MORE;
+ }
+
+ DBG3(DBG_IKE, "using autn %B", &autn);
+ /* split up AUTN = SQN xor AK | AMF | MAC */
+ chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac);
+
+ /* Get the shared key K: */
+ chunk_free(&this->k);
+ if (load_key(this->peer, this->server, &this->k) != SUCCESS)
+ {
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier,
+ AKA_AUTHENTICATION_REJECT, AT_END);
+ DBG3(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate "
+ "with EAP-AKA, sending %N", this->peer, this->server,
+ aka_subtype_names, AKA_AUTHENTICATION_REJECT);
+ return NEED_MORE;
+ }
+ DBG3(DBG_IKE, "using K %B", &this->k);
+# ifdef TEST_VECTORS
+ /* Test vector for K */
+ u_int8_t test_k[] = {
+ 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c,
+ 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d,
+ };
+ memcpy(this->k.ptr, test_k, this->k.len);
+# endif /* TEST_VECTORS */
+
+ /* calculate anonymity key AK */
+ f5(this, this->k, this->rand, ak.ptr);
+ DBG3(DBG_IKE, "using rand %B", &this->rand);
+ DBG3(DBG_IKE, "using ak %B", &ak);
+ /* XOR AK into SQN to decrypt it */
+
+ sqn = chunk_clonea(sqn_ak);
+
+ DBG3(DBG_IKE, "using ak xor sqn %B", &sqn_ak);
+ memxor(sqn.ptr, ak.ptr, sqn.len);
+ DBG3(DBG_IKE, "using sqn %B", &sqn);
+
+ /* calculate expected MAC and compare against received one */
+ f1(this, this->k, this->rand, sqn, amf, xmac.ptr);
+ if (!chunk_equals(mac, xmac))
+ {
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier,
+ AKA_AUTHENTICATION_REJECT, AT_END);
+ DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N",
+ aka_subtype_names, AKA_AUTHENTICATION_REJECT);
+ DBG3(DBG_IKE, "MAC %B\nXMAC %B", &mac, &xmac);
+ return NEED_MORE;
+ }
+
+#if SEQ_CHECK
+ if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0)
+ {
+ /* sequence number invalid. send AUTS */
+ chunk_t auts, macs, aks, amf;
+
+ macs = chunk_alloca(MAC_LENGTH);
+ aks = chunk_alloca(AK_LENGTH);
+ amf = chunk_alloca(AMF_LENGTH);
+
+ /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */
+ memset(amf.ptr, 0, amf.len);
+ /* AKS = f5*(RAND) */
+ f5star(this, this->k, this->rand, aks.ptr);
+ /* MACS = f1*(RAND) */
+ f1star(this, this->k, this->rand, peer_sqn, amf, macs.ptr);
+ /* AUTS = SQN xor AKS | MACS */
+ memxor(aks.ptr, peer_sqn.ptr, aks.len);
+ auts = chunk_cata("cc", aks, macs);
+
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier,
+ AKA_SYNCHRONIZATION_FAILURE,
+ AT_AUTS, auts, AT_END);
+ DBG1(DBG_IKE, "received SQN invalid, sending %N",
+ aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
+ DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn);
+ return NEED_MORE;
+ }
+#endif /* SEQ_CHECK */
+
+ /* derive K_encr, K_auth, MSK, EMSK */
+ derive_keys(this, this->peer);
+
+ /* verify EAP message MAC AT_MAC */
+ DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
+ DBG3(DBG_IKE, "using key %B", &this->k_auth);
+ if (!this->signer->verify_signature(this->signer, message, at_mac))
+ {
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
+ DBG1(DBG_IKE, "MAC in AT_MAC attribute verification "
+ "failed, sending %N %d", aka_attribute_names,
+ AT_CLIENT_ERROR_CODE, 0);
+ return NEED_MORE;
+ }
+
+ /* update stored SQN to the received one */
+ memcpy(peer_sqn.ptr, sqn.ptr, sqn.len);
+
+ /* calculate RES */
+ f2(this, this->k, this->rand, res.ptr);
+
+ /* build response */
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE,
+ AT_RES, res, AT_MAC, chunk_empty, AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * Process an incoming AKA-Notification as client
+ */
+static status_t peer_process_notification(private_eap_aka_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, pos, attr;
+ u_int8_t identifier;
+
+ message = in->get_data(in);
+ pos = message;
+ read_header(&pos);
+ identifier = in->get_identifier(in);
+
+ DBG3(DBG_IKE, "reading attributes from %B", &pos);
+
+ /* iterate over attributes */
+ while (TRUE)
+ {
+ aka_attribute_t attribute = read_attribute(&pos, &attr);
+ switch (attribute)
+ {
+ case AT_END:
+ break;
+ case AT_NOTIFICATION:
+ {
+ u_int16_t code;
+
+ if (attr.len != 2)
+ {
+ DBG1(DBG_IKE, "received invalid AKA notification, ignored");
+ continue;
+ }
+ code = ntohs(*(u_int16_t*)attr.ptr);
+ switch (code)
+ {
+ case 0:
+ DBG1(DBG_IKE, "received AKA notification 'general "
+ "failure after authentication' (%d)", code);
+ return FAILED;
+ case 16384:
+ DBG1(DBG_IKE, "received AKA notification 'general "
+ "failure' (%d)", code);
+ return FAILED;
+ case 32768:
+ DBG1(DBG_IKE, "received AKA notification 'successfully "
+ "authenticated' (%d)", code);
+ continue;
+ case 1026:
+ DBG1(DBG_IKE, "received AKA notification 'access "
+ "temporarily denied' (%d)", code);
+ return FAILED;
+ case 1031:
+ DBG1(DBG_IKE, "received AKA notification 'not "
+ "subscribed to service' (%d)", code);
+ return FAILED;
+ default:
+ DBG1(DBG_IKE, "received AKA notification code %d, "
+ "ignored", code);
+ continue;
+ }
+ }
+ default:
+ if (attribute >= 0 && attribute <= 127)
+ {
+ DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N",
+ aka_attribute_names, attribute, aka_subtype_names,
+ AKA_NOTIFICATION);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "ignoring skippable attribute %N",
+ aka_attribute_names, attribute);
+ }
+ continue;
+ }
+ break;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.process for an EAP_AKA peer
+ */
+static status_t peer_process(private_eap_aka_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ aka_subtype_t type;
+ chunk_t message;
+ u_int8_t identifier;
+
+ message = in->get_data(in);
+ type = read_header(&message);
+ identifier = in->get_identifier(in);
+
+ DBG3(DBG_IKE, "received EAP message %B", &message);
+
+ switch (type)
+ {
+ case AKA_CHALLENGE:
+ {
+ return peer_process_challenge(this, in, out);
+ }
+ case AKA_NOTIFICATION:
+ {
+ return peer_process_notification(this, in, out);
+ }
+ default:
+ {
+ *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
+ DBG1(DBG_IKE, "received unsupported %N request, sending %N %d",
+ aka_subtype_names, type,
+ aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
+ return NEED_MORE;
+ }
+ }
+}
+
+/**
+ * Implementation of eap_method_t.initiate for an EAP AKA peer
+ */
+static status_t peer_initiate(private_eap_aka_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_aka_t *this, u_int32_t *vendor)
+{
+ *vendor = 0;
+ return EAP_AKA;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_aka_t *this, chunk_t *msk)
+{
+ if (this->msk.ptr)
+ {
+ *msk = this->msk;
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_aka_t *this)
+{
+ return TRUE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_aka_t *this)
+{
+ DESTROY_IF(this->sha1);
+ DESTROY_IF(this->sha1_nof);
+ DESTROY_IF(this->signer);
+ DESTROY_IF(this->prf);
+ chunk_free(&this->k_encr);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ chunk_free(&this->xres);
+ chunk_free(&this->k);
+ chunk_free(&this->rand);
+ free(this);
+}
+
+/**
+ * generic constructor used by client & server
+ */
+static private_eap_aka_t *eap_aka_create_generic(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_aka_t *this = malloc_thing(private_eap_aka_t);
+
+ this->public.eap_method_interface.initiate = NULL;
+ this->public.eap_method_interface.process = NULL;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ /* private data */
+ this->server = server;
+ this->peer = peer;
+ this->k_encr = chunk_empty;
+ this->k_auth = chunk_empty;
+ this->msk = chunk_empty;
+ this->emsk = chunk_empty;
+ this->xres = chunk_empty;
+ this->k = chunk_empty;
+ this->rand = chunk_empty;
+
+ this->sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ this->sha1_nof = lib->crypto->create_hasher(lib->crypto, HASH_SHA1_NOFINAL);
+ this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128);
+ this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160);
+
+ if (!this->sha1 || !this->sha1_nof || !this->signer || !this->prf)
+ {
+ DBG1(DBG_IKE, "unable to initiate EAP-AKA, FIPS-PRF/SHA1 not supported");
+ DESTROY_IF(this->sha1);
+ DESTROY_IF(this->sha1_nof);
+ DESTROY_IF(this->signer);
+ DESTROY_IF(this->prf);
+ destroy(this);
+ return NULL;
+ }
+ return this;
+}
+
+/*
+ * Described in header.
+ */
+eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer)
+{
+ private_eap_aka_t *this = eap_aka_create_generic(server, peer);
+
+ if (this)
+ {
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process;
+ }
+ return (eap_aka_t*)this;
+}
+
+/*
+ * Described in header.
+ */
+eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer)
+{
+ private_eap_aka_t *this = eap_aka_create_generic(server, peer);
+
+ if (this)
+ {
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process;
+ }
+ return (eap_aka_t*)this;
+}
+
diff --git a/src/charon/plugins/eap_aka/eap_aka.h b/src/charon/plugins/eap_aka/eap_aka.h
new file mode 100644
index 000000000..1ee8496b2
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_aka_i eap_aka
+ * @{ @ingroup eap_aka
+ */
+
+#ifndef EAP_AKA_H_
+#define EAP_AKA_H_
+
+typedef struct eap_aka_t eap_aka_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/** check SEQ values as client for validity, disabled by default */
+#ifndef SEQ_CHECK
+# define SEQ_CHECK 0
+#endif
+
+/**
+ * Implementation of the eap_method_t interface using EAP-AKA.
+ *
+ * EAP-AKA uses 3rd generation mobile phone standard authentication
+ * mechanism for authentication. It is a mutual authentication
+ * mechanism which establishs a shared key and therefore supports EAP_ONLY
+ * authentication. This implementation follows the standard of the
+ * 3GPP2 (S.S0055) and not the one of 3GGP.
+ * The shared key used for authentication is from ipsec.secrets. The
+ * peers ID is used to query it.
+ * The AKA mechanism uses sequence numbers to detect replay attacks. The
+ * peer stores the sequence number normally in a USIM and accepts
+ * incremental sequence numbers (incremental for lifetime of the USIM). To
+ * prevent a complex sequence number management, this implementation uses
+ * a sequence number derived from time. It is initialized to the startup
+ * time of the daemon. As long as the (UTC) time of the system is not
+ * turned back while the daemon is not running, this method is secure.
+ * To enable time based SEQs, #define SEQ_CHECK as 1. Default is to accept
+ * any SEQ numbers. This allows an attacker to do replay attacks. But since
+ * the server has proven his identity via IKE, such an attack is only
+ * possible between server and AAA (if any).
+ */
+struct eap_aka_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * Creates the server implementation of the EAP method EAP-AKA.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_aka_t object
+ */
+eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer);
+
+/**
+ * Creates the peer implementation of the EAP method EAP-AKA.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_aka_t object
+ */
+eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer);
+
+#endif /* EAP_AKA_H_ @}*/
diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.c b/src/charon/plugins/eap_aka/eap_aka_plugin.c
new file mode 100644
index 000000000..1975d6fd3
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka_plugin.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_aka_plugin.h"
+
+#include "eap_aka.h"
+
+#include <daemon.h>
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(eap_aka_plugin_t *this)
+{
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_aka_create_server);
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_aka_create_peer);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ eap_aka_plugin_t *this = malloc_thing(eap_aka_plugin_t);
+
+ this->plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_SERVER,
+ (eap_constructor_t)eap_aka_create_server);
+ charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_PEER,
+ (eap_constructor_t)eap_aka_create_peer);
+
+ return &this->plugin;
+}
+
diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.h b/src/charon/plugins/eap_aka/eap_aka_plugin.h
new file mode 100644
index 000000000..07fcc8294
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_aka eap_aka
+ * @ingroup cplugins
+ *
+ * @defgroup eap_aka_plugin eap_aka_plugin
+ * @{ @ingroup eap_aka
+ */
+
+#ifndef EAP_AKA_PLUGIN_H_
+#define EAP_AKA_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_aka_plugin_t eap_aka_plugin_t;
+
+/**
+ * EAP-AKA plugin
+ */
+struct eap_aka_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a eap_aka_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* EAP_AKA_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/eap_identity/Makefile.am b/src/charon/plugins/eap_identity/Makefile.am
new file mode 100644
index 000000000..1ce2426f2
--- /dev/null
+++ b/src/charon/plugins/eap_identity/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-eapidentity.la
+libcharon_eapidentity_la_SOURCES = \
+ eap_identity_plugin.h eap_identity_plugin.c eap_identity.h eap_identity.c
+libcharon_eapidentity_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/eap_identity/eap_identity.c b/src/charon/plugins/eap_identity/eap_identity.c
new file mode 100644
index 000000000..f6835c7f2
--- /dev/null
+++ b/src/charon/plugins/eap_identity/eap_identity.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_identity.h"
+
+#include <daemon.h>
+#include <library.h>
+
+typedef struct private_eap_identity_t private_eap_identity_t;
+
+/**
+ * Private data of an eap_identity_t object.
+ */
+struct private_eap_identity_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_identity_t public;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+};
+
+/**
+ * Implementation of eap_method_t.process for the peer
+ */
+static status_t process(private_eap_identity_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t id, hdr;
+
+ hdr = chunk_alloca(5);
+ id = this->peer->get_encoding(this->peer);
+
+ *(hdr.ptr + 0) = EAP_RESPONSE;
+ *(hdr.ptr + 1) = in->get_identifier(in);
+ *(u_int16_t*)(hdr.ptr + 2) = htons(hdr.len + id.len);
+ *(hdr.ptr + 4) = EAP_IDENTITY;
+
+ *out = eap_payload_create_data(chunk_cata("cc", hdr, id));
+ return SUCCESS;
+
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the peer
+ */
+static status_t initiate(private_eap_identity_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_identity_t *this, u_int32_t *vendor)
+{
+ *vendor = 0;
+ return EAP_IDENTITY;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_identity_t *this, chunk_t *msk)
+{
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_identity_t *this)
+{
+ return FALSE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_identity_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_identity_t *eap_identity_create_peer(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_identity_t *this = malloc_thing(private_eap_identity_t);
+
+ /* public functions */
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ /* private data */
+ this->peer = peer;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_identity/eap_identity.h b/src/charon/plugins/eap_identity/eap_identity.h
new file mode 100644
index 000000000..5d297f798
--- /dev/null
+++ b/src/charon/plugins/eap_identity/eap_identity.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_identity_i eap_identity
+ * @{ @ingroup eap_identity
+ */
+
+#ifndef EAP_IDENTITY_H_
+#define EAP_IDENTITY_H_
+
+typedef struct eap_identity_t eap_identity_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Implementation of the eap_method_t interface using EAP Identity.
+ */
+struct eap_identity_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * Creates the EAP method EAP Identity, acting as peer.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_identity_t object
+ */
+eap_identity_t *eap_identity_create_peer(identification_t *server,
+ identification_t *peer);
+
+#endif /* EAP_IDENTITY_H_ @}*/
diff --git a/src/charon/plugins/eap_identity/eap_identity_plugin.c b/src/charon/plugins/eap_identity/eap_identity_plugin.c
new file mode 100644
index 000000000..22a884a3e
--- /dev/null
+++ b/src/charon/plugins/eap_identity/eap_identity_plugin.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_identity_plugin.h"
+
+#include "eap_identity.h"
+
+#include <daemon.h>
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(eap_identity_plugin_t *this)
+{
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_identity_create_peer);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ eap_identity_plugin_t *this = malloc_thing(eap_identity_plugin_t);
+
+ this->plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ charon->eap->add_method(charon->eap, EAP_IDENTITY, 0, EAP_PEER,
+ (eap_constructor_t)eap_identity_create_peer);
+
+ return &this->plugin;
+}
+
diff --git a/src/charon/plugins/eap_identity/eap_identity_plugin.h b/src/charon/plugins/eap_identity/eap_identity_plugin.h
new file mode 100644
index 000000000..d41c14114
--- /dev/null
+++ b/src/charon/plugins/eap_identity/eap_identity_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_identity eap_identity
+ * @ingroup cplugins
+ *
+ * @defgroup eap_identity_plugin eap_identity_plugin
+ * @{ @ingroup eap_identity
+ */
+
+#ifndef EAP_IDENTITY_PLUGIN_H_
+#define EAP_IDENTITY_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_identity_plugin_t eap_identity_plugin_t;
+
+/**
+ * EAP-IDENTITY plugin.
+ */
+struct eap_identity_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a eap_identity_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* EAP_IDENTITY_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/eap_md5/Makefile.am b/src/charon/plugins/eap_md5/Makefile.am
new file mode 100644
index 000000000..2d6d68f15
--- /dev/null
+++ b/src/charon/plugins/eap_md5/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-eapmd5.la
+
+libcharon_eapmd5_la_SOURCES = eap_md5_plugin.h eap_md5_plugin.c eap_md5.h eap_md5.c
+libcharon_eapmd5_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/eap_md5/eap_md5.c b/src/charon/plugins/eap_md5/eap_md5.c
new file mode 100644
index 000000000..702042f37
--- /dev/null
+++ b/src/charon/plugins/eap_md5/eap_md5.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_md5.h"
+
+#include <daemon.h>
+#include <library.h>
+#include <crypto/hashers/hasher.h>
+
+typedef struct private_eap_md5_t private_eap_md5_t;
+
+/**
+ * Private data of an eap_md5_t object.
+ */
+struct private_eap_md5_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_md5_t public;
+
+ /**
+ * ID of the server
+ */
+ identification_t *server;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * challenge sent by the server
+ */
+ chunk_t challenge;
+
+ /**
+ * EAP message identififier
+ */
+ u_int8_t identifier;
+};
+
+typedef struct eap_md5_header_t eap_md5_header_t;
+
+/**
+ * packed eap MD5 header struct
+ */
+struct eap_md5_header_t {
+ /** EAP code (REQUEST/RESPONSE) */
+ u_int8_t code;
+ /** unique message identifier */
+ u_int8_t identifier;
+ /** length of whole message */
+ u_int16_t length;
+ /** EAP type */
+ u_int8_t type;
+ /** length of value (challenge) */
+ u_int8_t value_size;
+ /** actual value */
+ u_int8_t value[];
+} __attribute__((__packed__));
+
+#define CHALLENGE_LEN 16
+#define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t))
+
+/**
+ * Hash the challenge string, create response
+ */
+static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response)
+{
+ shared_key_t *shared;
+ chunk_t concat;
+ hasher_t *hasher;
+
+ shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
+ this->server, this->peer);
+ if (shared == NULL)
+ {
+ DBG1(DBG_IKE, "no EAP key found for hosts '%D' - '%D'",
+ this->server, this->peer);
+ return NOT_FOUND;
+ }
+ concat = chunk_cata("ccc", chunk_from_thing(this->identifier),
+ shared->get_key(shared), this->challenge);
+ shared->destroy(shared);
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
+ if (hasher == NULL)
+ {
+ DBG1(DBG_IKE, "EAP-MD5 failed, MD5 not supported");
+ return FAILED;
+ }
+ hasher->allocate_hash(hasher, concat, response);
+ hasher->destroy(hasher);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the peer
+ */
+static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the server
+ */
+static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out)
+{
+ randomizer_t *randomizer;
+ status_t status;
+ eap_md5_header_t *req;
+
+ randomizer = randomizer_create();
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, CHALLENGE_LEN,
+ &this->challenge);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ req = alloca(PAYLOAD_LEN);
+ req->length = htons(PAYLOAD_LEN);
+ req->code = EAP_REQUEST;
+ req->identifier = this->identifier;
+ req->type = EAP_MD5;
+ req->value_size = this->challenge.len;
+ memcpy(req->value, this->challenge.ptr, this->challenge.len);
+
+ *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.process for the peer
+ */
+static status_t process_peer(private_eap_md5_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t response;
+ chunk_t data;
+ eap_md5_header_t *req;
+
+ this->identifier = in->get_identifier(in);
+ data = in->get_data(in);
+ this->challenge = chunk_clone(chunk_skip(data, 6));
+ if (data.len < 6 || this->challenge.len < *(data.ptr + 5))
+ {
+ DBG1(DBG_IKE, "received invalid EAP-MD5 message");
+ return FAILED;
+ }
+ if (hash_challenge(this, &response) != SUCCESS)
+ {
+ return FAILED;
+ }
+ req = alloca(PAYLOAD_LEN);
+ req->length = htons(PAYLOAD_LEN);
+ req->code = EAP_RESPONSE;
+ req->identifier = this->identifier;
+ req->type = EAP_MD5;
+ req->value_size = response.len;
+ memcpy(req->value, response.ptr, response.len);
+ chunk_free(&response);
+
+ *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.process for the server
+ */
+static status_t process_server(private_eap_md5_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t response, expected;
+ chunk_t data;
+
+ if (this->identifier != in->get_identifier(in))
+ {
+ DBG1(DBG_IKE, "received invalid EAP-MD5 message");
+ return FAILED;
+ }
+ if (hash_challenge(this, &expected) != SUCCESS)
+ {
+ return FAILED;
+ }
+ data = in->get_data(in);
+ response = chunk_skip(data, 6);
+
+ if (response.len < expected.len ||
+ !memeq(response.ptr, expected.ptr, expected.len))
+ {
+ chunk_free(&expected);
+ DBG1(DBG_IKE, "EAP-MD5 verification failed");
+ return FAILED;
+ }
+ chunk_free(&expected);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor)
+{
+ *vendor = 0;
+ return EAP_MD5;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_md5_t *this, chunk_t *msk)
+{
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_md5_t *this)
+{
+ return FALSE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_md5_t *this)
+{
+ chunk_free(&this->challenge);
+ free(this);
+}
+
+/**
+ * Generic constructor
+ */
+static private_eap_md5_t *eap_md5_create_generic(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_md5_t *this = malloc_thing(private_eap_md5_t);
+
+ this->public.eap_method_interface.initiate = NULL;
+ this->public.eap_method_interface.process = NULL;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ /* private data */
+ this->peer = peer;
+ this->server = server;
+ this->challenge = chunk_empty;
+ this->identifier = random();
+
+ return this;
+}
+
+/*
+ * see header
+ */
+eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
+{
+ private_eap_md5_t *this = eap_md5_create_generic(server, peer);
+
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
+
+ return &this->public;
+}
+
+/*
+ * see header
+ */
+eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
+{
+ private_eap_md5_t *this = eap_md5_create_generic(server, peer);
+
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_md5/eap_md5.h b/src/charon/plugins/eap_md5/eap_md5.h
new file mode 100644
index 000000000..bcd505c59
--- /dev/null
+++ b/src/charon/plugins/eap_md5/eap_md5.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_md5_i eap_md5
+ * @{ @ingroup eap_md5
+ */
+
+#ifndef EAP_MD5_H_
+#define EAP_MD5_H_
+
+typedef struct eap_md5_t eap_md5_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Implementation of the eap_method_t interface using EAP-MD5 (CHAP).
+ */
+struct eap_md5_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * Creates the EAP method EAP-MD5 acting as server.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_md5_t object
+ */
+eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer);
+
+/**
+ * Creates the EAP method EAP-MD5 acting as peer.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_md5_t object
+ */
+eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer);
+
+#endif /* EAP_MD5_H_ @}*/
diff --git a/src/charon/plugins/eap_md5/eap_md5_plugin.c b/src/charon/plugins/eap_md5/eap_md5_plugin.c
new file mode 100644
index 000000000..d00bf7165
--- /dev/null
+++ b/src/charon/plugins/eap_md5/eap_md5_plugin.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_md5_plugin.h"
+
+#include "eap_md5.h"
+
+#include <daemon.h>
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(eap_md5_plugin_t *this)
+{
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_md5_create_server);
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_md5_create_peer);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ eap_md5_plugin_t *this = malloc_thing(eap_md5_plugin_t);
+
+ this->plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ charon->eap->add_method(charon->eap, EAP_MD5, 0, EAP_SERVER,
+ (eap_constructor_t)eap_md5_create_server);
+ charon->eap->add_method(charon->eap, EAP_MD5, 0, EAP_PEER,
+ (eap_constructor_t)eap_md5_create_peer);
+
+ return &this->plugin;
+}
+
diff --git a/src/charon/plugins/eap_md5/eap_md5_plugin.h b/src/charon/plugins/eap_md5/eap_md5_plugin.h
new file mode 100644
index 000000000..8387a209b
--- /dev/null
+++ b/src/charon/plugins/eap_md5/eap_md5_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_md5 eap_md5
+ * @ingroup cplugins
+ *
+ * @defgroup eap_md5_plugin eap_md5_plugin
+ * @{ @ingroup eap_md5
+ */
+
+#ifndef EAP_MD5_PLUGIN_H_
+#define EAP_MD5_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_md5_plugin_t eap_md5_plugin_t;
+
+/**
+ * EAP-MD5 plugin
+ */
+struct eap_md5_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a eap_md5_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* EAP_MD5_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/eap_sim/Makefile.am b/src/charon/plugins/eap_sim/Makefile.am
new file mode 100644
index 000000000..549e92afa
--- /dev/null
+++ b/src/charon/plugins/eap_sim/Makefile.am
@@ -0,0 +1,13 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DSIM_READER_LIB=\"${simreader}\"
+
+plugin_LTLIBRARIES = libcharon-eapsim.la libeapsim-file.la
+
+libcharon_eapsim_la_SOURCES = eap_sim_plugin.h eap_sim_plugin.c eap_sim.h eap_sim.c
+libcharon_eapsim_la_LDFLAGS = -module
+
+libeapsim_file_la_SOURCES = eap_sim_file.c
+libeapsim_file_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/eap_sim/eap_sim.c b/src/charon/plugins/eap_sim/eap_sim.c
new file mode 100644
index 000000000..70651c91d
--- /dev/null
+++ b/src/charon/plugins/eap_sim/eap_sim.c
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_sim.h"
+
+#include <dlfcn.h>
+
+#include <daemon.h>
+#include <library.h>
+
+#define MAX_TRIES 3
+
+/* number of triplets for one authentication */
+#define TRIPLET_COUNT 3
+
+typedef enum sim_subtype_t sim_subtype_t;
+
+/**
+ * Subtypes of SIM messages
+ */
+enum sim_subtype_t {
+ SIM_START = 10,
+ SIM_CHALLENGE = 11,
+ SIM_NOTIFICATION = 12,
+ SIM_CLIENT_ERROR = 14,
+};
+
+ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
+ "SIM_START",
+ "SIM_CHALLENGE",
+ "SIM_NOTIFICATION",
+ "SIM_13",
+ "SIM_CLIENT_ERROR",
+);
+
+typedef enum sim_attribute_t sim_attribute_t;
+
+/**
+ * Attributes in SIM messages
+ */
+enum sim_attribute_t {
+ /** defines the end of attribute list */
+ AT_END = -1,
+ AT_RAND = 1,
+ AT_AUTN = 2,
+ AT_RES = 3,
+ AT_AUTS = 4,
+ AT_PADDING = 6,
+ AT_NONCE_MT = 7,
+ AT_PERMANENT_ID_REQ = 10,
+ AT_MAC = 11,
+ AT_NOTIFICATION = 12,
+ AT_ANY_ID_REQ = 13,
+ AT_IDENTITY = 14,
+ AT_VERSION_LIST = 15,
+ AT_SELECTED_VERSION = 16,
+ AT_FULLAUTH_ID_REQ = 17,
+ AT_COUNTER = 19,
+ AT_COUNTER_TOO_SMALL = 20,
+ AT_NONCE_S = 21,
+ AT_CLIENT_ERROR_CODE = 22,
+ AT_IV = 129,
+ AT_ENCR_DATA = 130,
+ AT_NEXT_PSEUDONYM = 132,
+ AT_NEXT_REAUTH_ID = 133,
+ AT_CHECKCODE = 134,
+ AT_RESULT_IND = 135,
+};
+
+ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
+ "AT_END",
+ "AT_0",
+ "AT_RAND",
+ "AT_AUTN",
+ "AT_RES",
+ "AT_AUTS",
+ "AT_5",
+ "AT_PADDING",
+ "AT_NONCE_MT",
+ "AT_8",
+ "AT_9",
+ "AT_PERMANENT_ID_REQ",
+ "AT_MAC",
+ "AT_NOTIFICATION",
+ "AT_ANY_ID_REQ",
+ "AT_IDENTITY",
+ "AT_VERSION_LIST",
+ "AT_SELECTED_VERSION",
+ "AT_FULLAUTH_ID_REQ",
+ "AT_18",
+ "AT_COUNTER",
+ "AT_COUNTER_TOO_SMALL",
+ "AT_NONCE_S",
+ "AT_CLIENT_ERROR_CODE");
+ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
+ "AT_IV",
+ "AT_ENCR_DATA",
+ "AT_131",
+ "AT_NEXT_PSEUDONYM",
+ "AT_NEXT_REAUTH_ID",
+ "AT_CHECKCODE",
+ "AT_RESULT_IND");
+ENUM_END(sim_attribute_names, AT_RESULT_IND);
+
+
+typedef struct private_eap_sim_t private_eap_sim_t;
+
+/**
+ * Private data of an eap_sim_t object.
+ */
+struct private_eap_sim_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_sim_t public;
+
+ /**
+ * ID of ourself
+ */
+ identification_t *peer;
+
+ /**
+ * hashing function
+ */
+ hasher_t *hasher;
+
+ /**
+ * prf
+ */
+ prf_t *prf;
+
+ /**
+ * MAC function
+ */
+ signer_t *signer;
+
+ /**
+ * SIM cardreader function loaded from library
+ */
+ sim_algo_t alg;
+
+ /**
+ * libraries get_triplet() function returning a triplet
+ */
+ sim_get_triplet_t get_triplet;
+
+ /**
+ * handle of the loaded library
+ */
+ void *handle;
+
+ /**
+ * how many times we try to authenticate
+ */
+ int tries;
+
+ /**
+ * unique EAP identifier
+ */
+ u_int8_t identifier;
+
+ /**
+ * EAP message type this role sends
+ */
+ u_int8_t type;
+
+ /**
+ * version this implementation uses
+ */
+ chunk_t version;
+
+ /**
+ * version list received from server
+ */
+ chunk_t version_list;
+
+ /**
+ * Nonce value used in AT_NONCE_MT
+ */
+ chunk_t nonce;
+
+ /**
+ * concatenated SRES values
+ */
+ chunk_t sreses;
+
+ /**
+ * k_encr key derived from MK
+ */
+ chunk_t k_encr;
+
+ /**
+ * k_auth key derived from MK, used for AT_MAC verification
+ */
+ chunk_t k_auth;
+
+ /**
+ * MSK, used for EAP-SIM based IKEv2 authentication
+ */
+ chunk_t msk;
+
+ /**
+ * EMSK, extendes MSK for further uses
+ */
+ chunk_t emsk;
+};
+
+/** length of the AT_NONCE_MT nonce value */
+#define NONCE_LEN 16
+/** length of the AT_MAC value */
+#define MAC_LEN 16
+/** length of the AT_RAND value */
+#define RAND_LEN 16
+/** length of Kc */
+#define KC_LEN 8
+/** length of SRES */
+#define SRES_LEN 4
+/** length of the k_encr key */
+#define KENCR_LEN 16
+/** length of the k_auth key */
+#define KAUTH_LEN 16
+/** length of the MSK */
+#define MSK_LEN 64
+/** length of the EMSK */
+#define EMSK_LEN 64
+
+static char version[] = {0x00,0x01};
+/* client error codes used in AT_CLIENT_ERROR_CODE */
+char client_error_general_buf[] = {0x00, 0x01};
+char client_error_unsupported_buf[] = {0x00, 0x02};
+char client_error_insufficient_buf[] = {0x00, 0x03};
+char client_error_notfresh_buf[] = {0x00, 0x04};
+chunk_t client_error_general = chunk_from_buf(client_error_general_buf);
+chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf);
+chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf);
+chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf);
+
+/**
+ * Read EAP and EAP-SIM header, return SIM type
+ */
+static sim_subtype_t read_header(chunk_t *message)
+{
+ sim_subtype_t type;
+
+ if (message->len < 8)
+ {
+ *message = chunk_empty;
+ return 0;
+ }
+ type = *(message->ptr + 5);
+ *message = chunk_skip(*message, 8);
+ return type;
+}
+
+/**
+ * read the next attribute from the chunk data
+ */
+static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
+{
+ sim_attribute_t attribute;
+ size_t length;
+
+ DBG3(DBG_IKE, "reading attribute from %B", message);
+
+ if (message->len < 2)
+ {
+ return AT_END;
+ }
+ attribute = *message->ptr++;
+ length = *message->ptr++ * 4 - 2;
+ message->len -= 2;
+ DBG3(DBG_IKE, "found attribute %N with length %d",
+ sim_attribute_names, attribute, length);
+
+ if (length > message->len)
+ {
+ return AT_END;
+ }
+ data->len = length;
+ data->ptr = message->ptr;
+ *message = chunk_skip(*message, length);
+ return attribute;
+}
+
+/**
+ * Build an EAP-SIM payload using a variable length attribute list.
+ * The variable argument takes a sim_attribute_t followed by its data in a chunk.
+ */
+static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier,
+ sim_subtype_t type, ...)
+{
+ chunk_t message = chunk_alloca(512);
+ chunk_t pos = message;
+ eap_payload_t *payload;
+ va_list args;
+ sim_attribute_t attr;
+ u_int8_t *mac_pos = NULL;
+ chunk_t mac_data = chunk_empty;
+
+ /* write EAP header, skip length bytes */
+ *pos.ptr++ = this->type;
+ *pos.ptr++ = identifier;
+ pos.ptr += 2;
+ pos.len -= 4;
+ /* write SIM header with type and subtype, zero reserved bytes */
+ *pos.ptr++ = EAP_SIM;
+ *pos.ptr++ = type;
+ *pos.ptr++ = 0;
+ *pos.ptr++ = 0;
+ pos.len -= 4;
+
+ va_start(args, type);
+ while ((attr = va_arg(args, sim_attribute_t)) != AT_END)
+ {
+ chunk_t data = va_arg(args, chunk_t);
+
+ DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data);
+
+ /* write attribute header */
+ *pos.ptr++ = attr;
+ pos.len--;
+
+ switch (attr)
+ {
+ case AT_CLIENT_ERROR_CODE:
+ case AT_SELECTED_VERSION:
+ {
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_IDENTITY:
+ case AT_VERSION_LIST:
+ {
+ u_int16_t act_len = data.len;
+ /* align up to four byte */
+ if (data.len % 4)
+ {
+ chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
+ memset(tmp.ptr, 0, tmp.len);
+ memcpy(tmp.ptr, data.ptr, data.len);
+ data = tmp;
+ }
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ /* actual length in bytes */
+ *(u_int16_t*)pos.ptr = htons(act_len);
+ pos = chunk_skip(pos, sizeof(u_int16_t));
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_NONCE_MT:
+ {
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ memset(pos.ptr, 0, 2);
+ pos = chunk_skip(pos, 2);
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_MAC:
+ {
+ *pos.ptr++ = 5; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ mac_pos = pos.ptr;
+ memset(mac_pos, 0, MAC_LEN);
+ pos = chunk_skip(pos, MAC_LEN);
+ mac_data = data;
+ break;
+ }
+ case AT_RAND:
+ {
+ *pos.ptr++ = data.len/4 + 1; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
+ sim_attribute_names, attr);
+ break;
+ }
+ }
+ va_end(args);
+
+ /* calculate message length, write into header */
+ message.len = pos.ptr - message.ptr;
+ *(u_int16_t*)(message.ptr + 2) = htons(message.len);
+
+ /* create MAC if AT_MAC attribte was included. Append supplied va_arg
+ * chunk mac_data to "to-sign" chunk */
+ if (mac_pos)
+ {
+ this->signer->set_key(this->signer, this->k_auth);
+ mac_data = chunk_cata("cc", message, mac_data);
+ this->signer->get_signature(this->signer, mac_data, mac_pos);
+ DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b",
+ &mac_data, mac_pos, MAC_LEN);
+ }
+
+ payload = eap_payload_create_data(message);
+
+ DBG3(DBG_IKE, "created EAP message %B", &message);
+ return payload;
+}
+
+/**
+ * process an EAP-SIM/Request/Start message
+ */
+static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
+ eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute, include_id = AT_END;
+ u_int8_t identifier;
+
+ identifier = in->get_identifier(in);
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_VERSION_LIST:
+ {
+ /* check if server supports our implementation */
+ bool found = FALSE;
+ if (data.len > 2)
+ {
+ /* read actual length first */
+ data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2);
+ data = chunk_skip(data, 2);
+ chunk_free(&this->version_list);
+ this->version_list = chunk_clone(data);
+ while (data.len >= this->version.len)
+ {
+ if (memeq(data.ptr, this->version.ptr, this->version.len))
+ {
+ found = TRUE;
+ break;
+ }
+ data = chunk_skip(data, this->version.len);
+ }
+ }
+ if (!found)
+ {
+ DBG1(DBG_IKE, "server does not support EAP_SIM "
+ "version number %#B", &this->version);
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_unsupported,
+ AT_END);
+ return NEED_MORE;
+ }
+ break;
+ }
+ case AT_PERMANENT_ID_REQ:
+ case AT_FULLAUTH_ID_REQ:
+ case AT_ANY_ID_REQ:
+ /* only include AT_IDENTITY if requested */
+ include_id = AT_IDENTITY;
+ break;
+ case AT_NOTIFICATION:
+ {
+ u_int16_t code = 0;
+ if (data.len == 2)
+ {
+ code = ntohs(*(u_int16_t*)data.ptr);
+ }
+ if (code <= 32767) /* no success bit */
+ {
+ DBG1(DBG_IKE, "received %N error %d",
+ sim_attribute_names, attribute, code);
+ *out = build_payload(this,
+ in->get_identifier(in), SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N code %d",
+ sim_attribute_names, attribute, code);
+ }
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+
+ /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
+ *out = build_payload(this, identifier, SIM_START,
+ AT_SELECTED_VERSION, this->version,
+ AT_NONCE_MT, this->nonce,
+ include_id, this->peer->get_encoding(this->peer),
+ AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * derive EAP keys from kc
+ */
+static void derive_keys(private_eap_sim_t *this, chunk_t kcs)
+{
+ chunk_t tmp, mk;
+ int i;
+
+ /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+ tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
+ this->nonce, this->version_list, this->version);
+ mk = chunk_alloca(this->hasher->get_hash_size(this->hasher));
+ this->hasher->get_hash(this->hasher, tmp, mk.ptr);
+ DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
+
+ /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
+ * FIPS PRF has 320 bit block size, we need 160 byte for keys
+ * => run prf four times */
+ this->prf->set_key(this->prf, mk);
+ tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4);
+ for (i = 0; i < 4; i++)
+ {
+ this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
+ }
+ chunk_free(&this->k_encr);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
+ MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
+ DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
+ &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
+}
+
+/**
+ * process an EAP-SIM/Request/Challenge message
+ */
+static status_t peer_process_challenge(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, data, tmp, kcs, kc, sreses, sres;
+ sim_attribute_t attribute;
+ u_int8_t identifier;
+ chunk_t mac = chunk_empty, rands = chunk_empty;
+
+ if (this->tries-- <= 0)
+ {
+ /* give up without notification. This hack is required as some buggy
+ * server implementations won't respect our client-error. */
+ return FAILED;
+ }
+
+ identifier = in->get_identifier(in);
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_RAND:
+ {
+ rands = chunk_skip(data, 2);
+ break;
+ }
+ case AT_MAC:
+ {
+ /* backup MAC, zero it inline for later verification */
+ data = chunk_skip(data, 2);
+ mac = chunk_clonea(data);
+ memset(data.ptr, 0, data.len);
+ break;
+ }
+ case AT_NOTIFICATION:
+ {
+ u_int16_t code = 0;
+ if (data.len == 2)
+ {
+ code = ntohs(*(u_int16_t*)data.ptr);
+ }
+ if (code <= 32767) /* no success bit */
+ {
+ DBG1(DBG_IKE, "received %N error %d",
+ sim_attribute_names, attribute, code);
+ *out = build_payload(this,
+ in->get_identifier(in), SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N code %d",
+ sim_attribute_names, attribute, code);
+ }
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+
+ /* excepting two or three RAND, each 16 bytes. We require two valid
+ * and different RANDs */
+ if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) ||
+ memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN))
+ {
+ DBG1(DBG_IKE, "no valid AT_RAND received");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_insufficient,
+ AT_END);
+ return NEED_MORE;
+ }
+ if (mac.len != MAC_LEN)
+ {
+ DBG1(DBG_IKE, "no valid AT_MAC received");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+
+ /* get two or three KCs/SRESes from SIM using RANDs */
+ kcs = kc = chunk_alloca(rands.len / 2);
+ sreses = sres = chunk_alloca(rands.len / 4);
+ while (rands.len > 0)
+ {
+ int kc_len = kc.len, sres_len = sres.len;
+
+ if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len))
+ {
+ DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
+ rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len);
+ kc = chunk_skip(kc, kc_len);
+ sres = chunk_skip(sres, sres_len);
+ rands = chunk_skip(rands, RAND_LEN);
+ }
+
+ derive_keys(this, kcs);
+
+ /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
+ this->signer->set_key(this->signer, this->k_auth);
+ tmp = chunk_cata("cc", in->get_data(in), this->nonce);
+ if (!this->signer->verify_signature(this->signer, tmp, mac))
+ {
+ DBG1(DBG_IKE, "AT_MAC verification failed");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+
+ /* build response, AT_MAC is built over "EAP packet | n*SRES" */
+ *out = build_payload(this, identifier, SIM_CHALLENGE,
+ AT_MAC, sreses,
+ AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * process an EAP-SIM/Response/Challenge message
+ */
+static status_t server_process_challenge(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute;
+ chunk_t mac = chunk_empty, tmp;
+
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_MAC:
+ /* MAC has two reserved bytes */
+ if (data.len == MAC_LEN + 2)
+ { /* clone and zero MAC for verification */
+ mac = chunk_clonea(chunk_skip(data, 2));
+ memset(data.ptr, 0, data.len);
+ }
+ break;
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+ if (!mac.ptr)
+ {
+ DBG1(DBG_IKE, "no valid AT_MAC attribute received");
+ return FAILED;
+ }
+ /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */
+ this->signer->set_key(this->signer, this->k_auth);
+ tmp = chunk_cata("cc", in->get_data(in), this->sreses);
+ if (!this->signer->verify_signature(this->signer, tmp, mac))
+ {
+ DBG1(DBG_IKE, "AT_MAC verification failed");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * process an EAP-SIM/Response/Start message
+ */
+static status_t server_process_start(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute;
+ bool supported = FALSE;
+ chunk_t rands, rand, kcs, kc, sreses, sres;
+ char id[64];
+ int len, i, rand_len, kc_len, sres_len;
+
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_NONCE_MT:
+ if (data.len == NONCE_LEN + 2)
+ {
+ this->nonce = chunk_clone(chunk_skip(data, 2));
+ }
+ break;
+ case AT_SELECTED_VERSION:
+ if (chunk_equals(data, this->version))
+ {
+ supported = TRUE;
+ }
+ break;
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+ if (!supported || !this->nonce.ptr)
+ {
+ DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
+ return FAILED;
+ }
+ len = snprintf(id, sizeof(id), "%D", this->peer);
+ if (len > sizeof(id) || len < 0)
+ {
+ return FAILED;
+ }
+
+ /* read triplets from provider */
+ rand = rands = chunk_alloca(RAND_LEN * TRIPLET_COUNT);
+ kc = kcs = chunk_alloca(KC_LEN * TRIPLET_COUNT);
+ sres = sreses = chunk_alloca(SRES_LEN * TRIPLET_COUNT);
+ rands.len = 0;
+ kcs.len = 0;
+ sreses.len = 0;
+ for (i = 0; i < TRIPLET_COUNT; i++)
+ {
+ rand_len = RAND_LEN;
+ kc_len = KC_LEN;
+ sres_len = SRES_LEN;
+ if (this->get_triplet(id, rand.ptr, &rand_len, sres.ptr, &sres_len,
+ kc.ptr, &kc_len))
+ {
+ DBG1(DBG_IKE, "getting EAP-SIM triplet %d failed", i);
+ return FAILED;
+ }
+ rands.len += rand_len;
+ kcs.len += kc_len;
+ sreses.len += sres_len;
+ rand = chunk_skip(rand, rand_len);
+ kc = chunk_skip(kc, kc_len);
+ sres = chunk_skip(sres, sres_len);
+ }
+ derive_keys(this, kcs);
+
+ /* build MAC over "EAP packet | NONCE_MT" */
+ *out = build_payload(this, this->identifier++, SIM_CHALLENGE, AT_RAND,
+ rands, AT_MAC, this->nonce, AT_END);
+ this->sreses = chunk_clone(sreses);
+ return NEED_MORE;
+}
+
+/**
+ * process an EAP-SIM/Request/Notification message
+ */
+static status_t peer_process_notification(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute;
+
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_NOTIFICATION:
+ {
+ u_int16_t code = 0;
+ if (data.len == 2)
+ {
+ code = ntohs(*(u_int16_t*)data.ptr);
+ }
+ if (code <= 32767) /* no success bit */
+ {
+ DBG1(DBG_IKE, "received %N error %d",
+ sim_attribute_names, attribute, code);
+ *out = build_payload(this,
+ in->get_identifier(in), SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N code %d",
+ sim_attribute_names, attribute, code);
+ }
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+ /* reply with empty notification */
+ *out = build_payload(this, in->get_identifier(in), SIM_NOTIFICATION, AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * Process a client error
+ */
+static status_t server_process_client_error(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute;
+
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ if (attribute == AT_CLIENT_ERROR_CODE)
+ {
+ u_int16_t code = 0;
+ if (data.len == 2)
+ {
+ code = ntohs(*(u_int16_t*)data.ptr);
+ }
+ DBG1(DBG_IKE, "received %N error %d",
+ sim_attribute_names, attribute, code);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ }
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.process for the peer
+ */
+static status_t peer_process(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ sim_subtype_t type;
+ chunk_t message;
+
+ message = in->get_data(in);
+ type = read_header(&message);
+
+ switch (type)
+ {
+ case SIM_START:
+ return peer_process_start(this, in, out);
+ case SIM_CHALLENGE:
+ return peer_process_challenge(this, in, out);
+ case SIM_NOTIFICATION:
+ return peer_process_notification(this, in, out);
+ default:
+ DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
+ sim_subtype_names, type);
+ *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general, AT_END);
+ return NEED_MORE;
+ }
+}
+
+/**
+ * Implementation of eap_method_t.process for the server
+ */
+static status_t server_process(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ sim_subtype_t type;
+ chunk_t message;
+
+ message = in->get_data(in);
+ type = read_header(&message);
+
+ switch (type)
+ {
+ case SIM_START:
+ return server_process_start(this, in, out);
+ case SIM_CHALLENGE:
+ return server_process_challenge(this, in, out);
+ case SIM_CLIENT_ERROR:
+ return server_process_client_error(this, in, out);
+ default:
+ DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
+ sim_subtype_names, type);
+ return FAILED;
+ }
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the peer
+ */
+static status_t peer_initiate(private_eap_sim_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the server
+ */
+static status_t server_initiate(private_eap_sim_t *this, eap_payload_t **out)
+{
+ /* version_list to derive MK, no padding */
+ this->version_list = chunk_clone(this->version);
+ /* build_payloads adds padding itself */
+ *out = build_payload(this, this->identifier++, SIM_START,
+ AT_VERSION_LIST, this->version, AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_sim_t *this, u_int32_t *vendor)
+{
+ *vendor = 0;
+ return EAP_SIM;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_sim_t *this, chunk_t *msk)
+{
+ if (this->msk.ptr)
+ {
+ *msk = this->msk;
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_sim_t *this)
+{
+ return TRUE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_sim_t *this)
+{
+ dlclose(this->handle);
+ DESTROY_IF(this->hasher);
+ DESTROY_IF(this->prf);
+ DESTROY_IF(this->signer);
+ chunk_free(&this->nonce);
+ chunk_free(&this->sreses);
+ chunk_free(&this->version_list);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->k_encr);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ free(this);
+}
+
+/**
+ * Generic constructor for both roles
+ */
+eap_sim_t *eap_sim_create_generic(eap_role_t role, identification_t *server,
+ identification_t *peer)
+{
+ private_eap_sim_t *this;
+ randomizer_t *randomizer;
+ void *symbol;
+ char *name;
+
+ this = malloc_thing(private_eap_sim_t);
+ this->alg = NULL;
+ this->get_triplet = NULL;
+ this->nonce = chunk_empty;
+ this->sreses = chunk_empty;
+ this->peer = peer;
+ this->tries = MAX_TRIES;
+ this->version.ptr = version;
+ this->version.len = sizeof(version);
+ this->version_list = chunk_empty;
+ this->k_auth = chunk_empty;
+ this->k_encr = chunk_empty;
+ this->msk = chunk_empty;
+ this->emsk = chunk_empty;
+ this->identifier = random();
+
+ this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
+ if (this->handle == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
+ free(this);
+ return NULL;
+ }
+ switch (role)
+ {
+ case EAP_PEER:
+ name = SIM_READER_ALG;
+ break;
+ case EAP_SERVER:
+ name = SIM_READER_GET_TRIPLET;
+ break;
+ default:
+ free(this);
+ return NULL;
+ }
+ symbol = dlsym(this->handle, name);
+ if (symbol == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open SIM function '%s' in '%s'",
+ name, SIM_READER_LIB);
+ dlclose(this->handle);
+ free(this);
+ return NULL;
+ }
+ switch (role)
+ {
+ case EAP_SERVER:
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process;
+ this->get_triplet = symbol;
+ this->type = EAP_REQUEST;
+ break;
+ case EAP_PEER:
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process;
+ this->alg = symbol;
+ this->type = EAP_RESPONSE;
+ randomizer = randomizer_create();
+ if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
+ &this->nonce))
+ {
+ DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");
+ randomizer->destroy(randomizer);
+ free(this);
+ return NULL;
+ }
+ randomizer->destroy(randomizer);
+ break;
+ default:
+ free(this);
+ return NULL;
+ }
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160);
+ this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128);
+ if (!this->hasher || !this->prf || !this->signer)
+ {
+ DBG1(DBG_IKE, "initiating EAP-SIM failed, FIPS-PRF/SHA1 not supported");
+ destroy(this);
+ return NULL;
+ }
+ return &this->public;
+}
+
+/*
+ * Described in header.
+ */
+eap_sim_t *eap_sim_create_server(identification_t *server,
+ identification_t *peer)
+{
+ return eap_sim_create_generic(EAP_SERVER, server, peer);
+}
+
+/*
+ * Described in header.
+ */
+eap_sim_t *eap_sim_create_peer(identification_t *server,
+ identification_t *peer)
+{
+ return eap_sim_create_generic(EAP_PEER, server, peer);
+}
+
diff --git a/src/charon/plugins/eap_sim/eap_sim.h b/src/charon/plugins/eap_sim/eap_sim.h
new file mode 100644
index 000000000..65020aa64
--- /dev/null
+++ b/src/charon/plugins/eap_sim/eap_sim.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup eap_sim_i eap_sim
+ * @{ @ingroup eap_sim
+ */
+
+#ifndef EAP_SIM_H_
+#define EAP_SIM_H_
+
+typedef struct eap_sim_t eap_sim_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/** the library containing with the triplet functions */
+#ifndef SIM_READER_LIB
+#error SIM_READER_LIB not specified, use --with-sim-reader option
+#endif /* SIM_READER_LIB */
+
+/**
+ * Cardreaders SIM function.
+ *
+ * @param rand RAND to run algo with
+ * @param rand_length length of value in rand
+ * @param sres buffer to get SRES
+ * @param sres_length size of buffer in sres, returns bytes written to SRES
+ * @param kc buffer to get Kc
+ * @param kc_length size of buffer in Kc, returns bytes written to Kc
+ * @return zero on success
+ */
+typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length,
+ unsigned char *sres, int *sres_length,
+ unsigned char *kc, int *kc_length);
+
+#ifndef SIM_READER_ALG
+/** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */
+#define SIM_READER_ALG "sim_run_alg"
+#endif /* SIM_READER_ALG */
+
+/**
+ * Function to get a SIM triplet.
+ *
+ * @param identity identity (imsi) to get a triplet for
+ * @param rand buffer to get RAND
+ * @param rand_length size of buffer in rand, returns bytes written to RAND
+ * @param sres buffer to get SRES
+ * @param sres_length size of buffer in sres, returns bytes written to SRES
+ * @param kc buffer to get Kc
+ * @param kc_length size of buffer in Kc, returns bytes written to Kc
+ * @return zero on success
+ */
+typedef int (*sim_get_triplet_t)(char *identity,
+ unsigned char *rand, int *rand_length,
+ unsigned char *sres, int *sres_length,
+ unsigned char *kc, int *kc_length);
+
+#ifndef SIM_READER_GET_TRIPLET
+/** the SIM_READER_LIB's get-triplet function, uses sim_get_triplet_t signature */
+#define SIM_READER_GET_TRIPLET "sim_get_triplet"
+#endif /* SIM_READER_GET_TRIPLET */
+
+/**
+ * Implementation of the eap_method_t interface using EAP-SIM.
+ *
+ * This EAP-SIM client implementation uses another pluggable library to
+ * access the SIM card/triplet provider. This module is specified using the
+ * SIM_READER_LIB definition. It has to privde a sim_run_alg() function to
+ * calculate a triplet (client), and/or a sim_get_triplet() function to get
+ * a triplet (server). These functions are named to the SIM_READER_ALG and
+ * the SIM_READER_GET_TRIPLET definitions.
+ */
+struct eap_sim_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * Creates the EAP method EAP-SIM acting as server.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_sim_t object
+ */
+eap_sim_t *eap_sim_create_server(identification_t *server, identification_t *peer);
+
+/**
+ * Creates the EAP method EAP-SIM acting as peer.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_sim_t object
+ */
+eap_sim_t *eap_sim_create_peer(identification_t *server, identification_t *peer);
+
+#endif /* EAP_SIM_H_ @}*/
diff --git a/src/charon/plugins/eap_sim/eap_sim_file.c b/src/charon/plugins/eap_sim/eap_sim_file.c
new file mode 100644
index 000000000..7040a5ace
--- /dev/null
+++ b/src/charon/plugins/eap_sim/eap_sim_file.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <daemon.h>
+
+#define IMSI_LEN 64
+#define RAND_LEN 16
+#define SRES_LEN 4
+#define KC_LEN 8
+
+typedef struct triplet_t triplet_t;
+
+struct triplet_t {
+ unsigned char imsi[IMSI_LEN];
+ unsigned char rand[RAND_LEN];
+ unsigned char sres[SRES_LEN];
+ unsigned char kc[KC_LEN];
+};
+
+static triplet_t *triplets = NULL;
+static int triplet_count = 0;
+
+#define TRIPLET_FILE IPSEC_CONFDIR "/ipsec.d/triplets.dat"
+
+/**
+ * convert a single HEX char to its integer value
+ */
+static int hexchr(char chr)
+{
+ switch (chr)
+ {
+ case '0'...'9':
+ return chr - '0';
+ case 'A'...'F':
+ return 10 + chr - 'A';
+ case 'a'...'f':
+ return 10 + chr - 'a';
+ }
+ return 0;
+}
+
+/**
+ * convert a HEX string into a char array bin, limited by array length len
+ */
+static void hex2bin(char *hex, unsigned char *bin, size_t len)
+{
+ char *pos;
+ int i, even = 1;
+
+ pos = hex - 1;
+ /* find the end, as we convert bottom up */
+ while (TRUE)
+ {
+ switch (*(pos+1))
+ {
+ case '0'...'9':
+ case 'A'...'F':
+ case 'a'...'f':
+ pos++;
+ continue;
+ }
+ break;
+ }
+ /* convert two hex chars into a single bin byte */
+ for (i = 0; pos >= hex && i < len; pos--)
+ {
+ if (even)
+ {
+ bin[len - 1 - i] = hexchr(*pos);
+ }
+ else
+ {
+ bin[len - 1 - i] |= 16 * hexchr(*pos);
+ i++;
+ }
+ even = !even;
+ }
+}
+
+/**
+ * free up allocated triplets
+ */
+static void __attribute__ ((destructor)) free_triplets()
+{
+ free(triplets);
+}
+
+/**
+ * read the triplets from the file, using freeradius triplet file syntax:
+ * http://www.freeradius.org/radiusd/doc/rlm_sim_triplets
+ */
+static void __attribute__ ((constructor)) read_triplets()
+{
+ char line[512], *data[4], *pos;
+ FILE *file;
+ int i, nr = 0;
+ triplet_t *triplet;
+
+ file = fopen(TRIPLET_FILE, "r");
+ if (file == NULL)
+ {
+ DBG1(DBG_CFG, "opening triplet file %s failed: %s",
+ TRIPLET_FILE, strerror(errno));
+ return;
+ }
+
+ if (triplets)
+ {
+ free(triplets);
+ triplets = NULL;
+ triplet_count = 0;
+ }
+
+ /* read line by line */
+ while (fgets(line, sizeof(line), file))
+ {
+ nr++;
+ /* skip comments, empty lines */
+ switch (line[0])
+ {
+ case '\n':
+ case '\r':
+ case '#':
+ case '\0':
+ continue;
+ default:
+ break;
+ }
+ /* read comma separated values */
+ pos = line;
+ for (i = 0; i < 4; i++)
+ {
+ data[i] = pos;
+ pos = strchr(pos, ',');
+ if (pos)
+ {
+ *pos = '\0';
+ pos++;
+ }
+ else if (i != 3)
+ {
+ DBG1(DBG_CFG, "error in triplet file, line %d", nr);
+ fclose(file);
+ return;
+ }
+ }
+ /* allocate new triplet */
+ triplet_count++;
+ triplets = realloc(triplets, triplet_count * sizeof(triplet_t));
+ triplet = &triplets[triplet_count - 1];
+ memset(triplet, 0, sizeof(triplet_t));
+
+ /* convert/copy triplet data */
+ for (i = 0; i < IMSI_LEN - 1; i++)
+ {
+ switch (data[0][i])
+ {
+ case '\n':
+ case '\r':
+ case '\0':
+ break;
+ default:
+ triplet->imsi[i] = data[0][i];
+ continue;
+ }
+ break;
+ }
+ hex2bin(data[1], triplet->rand, RAND_LEN);
+ hex2bin(data[2], triplet->sres, SRES_LEN);
+ hex2bin(data[3], triplet->kc, KC_LEN);
+
+ DBG4(DBG_CFG, "triplet: imsi %b\nrand %b\nsres %b\nkc %b",
+ triplet->imsi, IMSI_LEN, triplet->rand, RAND_LEN,
+ triplet->sres, SRES_LEN, triplet->kc, KC_LEN);
+ }
+ fclose(file);
+ DBG2(DBG_CFG, "read %d triplets from %s", triplet_count, TRIPLET_FILE);
+}
+
+/**
+ * Run the sim algorithm, see eap_sim.h
+ */
+int sim_run_alg(const unsigned char *rand, int rand_length,
+ unsigned char *sres, int *sres_length,
+ unsigned char *kc, int *kc_length)
+{
+ int current;
+
+ if (rand_length != RAND_LEN ||
+ *sres_length < SRES_LEN ||
+ *kc_length < KC_LEN)
+ {
+ return 1;
+ }
+
+ for (current = 0; current < triplet_count; current++)
+ {
+ if (memcmp(triplets[current].rand, rand, RAND_LEN) == 0)
+ {
+ memcpy(sres, triplets[current].sres, SRES_LEN);
+ memcpy(kc, triplets[current].kc, KC_LEN);
+ *sres_length = SRES_LEN;
+ *kc_length = KC_LEN;
+ return 0;
+ }
+ }
+ return 2;
+}
+
+/**
+ * Get a single triplet, see_eap_sim.h
+ */
+int sim_get_triplet(char *imsi,
+ unsigned char *rand, int *rand_length,
+ unsigned char *sres, int *sres_length,
+ unsigned char *kc, int *kc_length)
+{
+ int current;
+ triplet_t *triplet;
+ static int skip = -1;
+
+ DBG2(DBG_CFG, "getting triplet for %s", imsi);
+
+ if (*rand_length < RAND_LEN ||
+ *sres_length < SRES_LEN ||
+ *kc_length < KC_LEN)
+ {
+ return 1;
+ }
+ if (triplet_count == 0)
+ {
+ return 2;
+ }
+ for (current = 0; current < triplet_count; current++)
+ {
+ triplet = &triplets[current];
+
+ if (streq(imsi, triplet->imsi))
+ {
+ /* skip triplet if already used */
+ if (skip >= current)
+ {
+ continue;
+ }
+ *rand_length = RAND_LEN;
+ *sres_length = SRES_LEN;
+ *kc_length = KC_LEN;
+ memcpy(rand, triplet->rand, RAND_LEN);
+ memcpy(sres, triplet->sres, SRES_LEN);
+ memcpy(kc, triplet->kc, KC_LEN);
+ /* remember used triplet */
+ skip = current;
+ return 0;
+ }
+ }
+ if (skip > -1)
+ {
+ /* no triplet left, reuse triplets */
+ skip = -1;
+ return sim_get_triplet(imsi, rand, rand_length,
+ sres, sres_length, kc, kc_length);
+ }
+ return 2;
+}
+
diff --git a/src/charon/plugins/eap_sim/eap_sim_plugin.c b/src/charon/plugins/eap_sim/eap_sim_plugin.c
new file mode 100644
index 000000000..8fbad4211
--- /dev/null
+++ b/src/charon/plugins/eap_sim/eap_sim_plugin.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "eap_sim_plugin.h"
+
+#include "eap_sim.h"
+
+#include <daemon.h>
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(eap_sim_plugin_t *this)
+{
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_sim_create_server);
+ charon->eap->remove_method(charon->eap,
+ (eap_constructor_t)eap_sim_create_peer);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ eap_sim_plugin_t *this = malloc_thing(eap_sim_plugin_t);
+
+ this->plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ charon->eap->add_method(charon->eap, EAP_SIM, 0, EAP_SERVER,
+ (eap_constructor_t)eap_sim_create_server);
+ charon->eap->add_method(charon->eap, EAP_SIM, 0, EAP_PEER,
+ (eap_constructor_t)eap_sim_create_peer);
+
+ return &this->plugin;
+}
+
diff --git a/src/charon/plugins/eap_sim/eap_sim_plugin.h b/src/charon/plugins/eap_sim/eap_sim_plugin.h
new file mode 100644
index 000000000..e142b8991
--- /dev/null
+++ b/src/charon/plugins/eap_sim/eap_sim_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup eap_sim eap_sim
+ * @ingroup cplugins
+ *
+ * @defgroup eap_sim_plugin eap_sim_plugin
+ * @{ @ingroup eap_sim
+ */
+
+#ifndef EAP_SIM_PLUGIN_H_
+#define EAP_SIM_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_sim_plugin_t eap_sim_plugin_t;
+
+/**
+ * EAP-sim plugin
+ */
+struct eap_sim_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a eap_sim_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* EAP_SIM_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/med_db/Makefile.am b/src/charon/plugins/med_db/Makefile.am
new file mode 100644
index 000000000..f7608da05
--- /dev/null
+++ b/src/charon/plugins/med_db/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-med-db.la
+libcharon_med_db_la_SOURCES = med_db_plugin.h med_db_plugin.c \
+ med_db_creds.h med_db_creds.c
+libcharon_med_db_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/med_db/med_db_creds.c b/src/charon/plugins/med_db/med_db_creds.c
new file mode 100644
index 000000000..f8aed2597
--- /dev/null
+++ b/src/charon/plugins/med_db/med_db_creds.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "med_db_creds.h"
+
+#include <daemon.h>
+#include <library.h>
+#include <utils/enumerator.h>
+
+typedef struct private_med_db_creds_t private_med_db_creds_t;
+
+/**
+ * Private data of an med_db_creds_t object
+ */
+struct private_med_db_creds_t {
+
+ /**
+ * Public part
+ */
+ med_db_creds_t public;
+
+ /**
+ * underlying database handle
+ */
+ database_t *db;
+};
+
+/**
+ * data passed between enumerate calls
+ */
+typedef struct {
+ /** current shared key */
+ shared_key_t *current;
+} data_t;
+
+typedef struct private_shared_key_t private_shared_key_t;
+/**
+ * shared key implementation
+ */
+struct private_shared_key_t {
+ /** implements shared_key_t*/
+ shared_key_t public;
+ /** data of the key */
+ chunk_t key;
+ /** reference counter */
+ refcount_t ref;
+};
+
+/**
+ * Destroy allocated data_t struct
+ */
+static void data_destroy(data_t *this)
+{
+ DESTROY_IF(this->current);
+ free(this);
+}
+
+/**
+ * Implementation of shared_key_t.get_type.
+ */
+static shared_key_type_t get_type(private_shared_key_t *this)
+{
+ return SHARED_IKE;
+}
+
+/**
+ * Implementation of shared_key_t.get_ref.
+ */
+static private_shared_key_t* get_ref(private_shared_key_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implementation of shared_key_t.destroy
+ */
+static void shared_key_destroy(private_shared_key_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ chunk_free(&this->key);
+ free(this);
+ }
+}
+
+/**
+ * Implementation of shared_key_t.get_key.
+ */
+static chunk_t get_key(private_shared_key_t *this)
+{
+ return this->key;
+}
+
+/**
+ * create a shared key
+ */
+static shared_key_t *shared_key_create(chunk_t key)
+{
+ private_shared_key_t *this = malloc_thing(private_shared_key_t);
+
+ this->public.get_type = (shared_key_type_t(*)(shared_key_t*))get_type;
+ this->public.get_key = (chunk_t(*)(shared_key_t*))get_key;
+ this->public.get_ref = (shared_key_t*(*)(shared_key_t*))get_ref;
+ this->public.destroy = (void(*)(shared_key_t*))shared_key_destroy;
+
+ this->key = chunk_clone(key);
+ this->ref = 1;
+ return &this->public;
+}
+
+/**
+ * filter for enumerator, returns for each SQL result a shared key and match
+ */
+static bool filter(data_t *this, chunk_t *chunk, shared_key_t **out,
+ void **unused1, id_match_t *match_me,
+ void **unused2, id_match_t *match_other)
+{
+ DESTROY_IF(this->current);
+ this->current = shared_key_create(*chunk);
+ *out = this->current;
+ /* we have unique matches only, but do not compare own ID */
+ if (match_me)
+ {
+ *match_me = ID_MATCH_ANY;
+ }
+ if (match_other)
+ {
+ *match_other = ID_MATCH_PERFECT;
+ }
+ return TRUE;
+}
+
+
+/**
+ * Implements credential_set_t.create_shared_enumerator
+ */
+static enumerator_t* create_shared_enumerator(private_med_db_creds_t *this,
+ shared_key_type_t type, identification_t *me,
+ identification_t *other)
+{
+ enumerator_t *enumerator;
+ data_t *data;
+
+ if (type != SHARED_IKE)
+ {
+ return NULL;
+ }
+ enumerator = this->db->query(this->db,
+ "SELECT Psk FROM Peer WHERE PeerId = ?",
+ DB_BLOB, other->get_encoding(other),
+ DB_BLOB);
+ if (enumerator)
+ {
+ data = malloc_thing(data_t);
+ data->current = NULL;
+ return enumerator_create_filter(enumerator, (void*)filter,
+ data, (void*)data_destroy);
+ }
+ return NULL;
+}
+
+/**
+ * returns null
+ */
+static void *return_null()
+{
+ return NULL;
+}
+
+/**
+ * Implementation of backend_t.destroy.
+ */
+static void destroy(private_med_db_creds_t *this)
+{
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+med_db_creds_t *med_db_creds_create(database_t *db)
+{
+ private_med_db_creds_t *this = malloc_thing(private_med_db_creds_t);
+
+ this->public.set.create_private_enumerator = (void*)return_null;
+ this->public.set.create_cert_enumerator = (void*)return_null;
+ this->public.set.create_shared_enumerator = (void*)create_shared_enumerator;
+ this->public.set.create_cdp_enumerator = (void*)return_null;
+
+ this->public.destroy = (void (*)(med_db_creds_t*))destroy;
+
+ this->db = db;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/med_db/med_db_creds.h b/src/charon/plugins/med_db/med_db_creds.h
new file mode 100644
index 000000000..1665b8718
--- /dev/null
+++ b/src/charon/plugins/med_db/med_db_creds.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup med_db_creds_i med_db_creds
+ * @{ @ingroup med_db_creds
+ */
+
+#ifndef MED_DB_CREDS_H_
+#define MED_DB_CREDS_H_
+
+#include <credentials/credential_set.h>
+#include <database/database.h>
+
+typedef struct med_db_creds_t med_db_creds_t;
+
+/**
+ * Mediation credentials database.
+ */
+struct med_db_creds_t {
+
+ /**
+ * Implements credential_set_t interface
+ */
+ credential_set_t set;
+
+ /**
+ * Destroy the credentials databse.
+ */
+ void (*destroy)(med_db_creds_t *this);
+};
+
+/**
+ * Create the med_db credentials db.
+ *
+ * @param database underlying database
+ * @return credential set implementation on that database
+ */
+med_db_creds_t *med_db_creds_create(database_t *database);
+
+#endif /* MED_DB_CREDS_H_ @}*/
diff --git a/src/charon/plugins/med_db/med_db_plugin.c b/src/charon/plugins/med_db/med_db_plugin.c
new file mode 100644
index 000000000..033ec91c7
--- /dev/null
+++ b/src/charon/plugins/med_db/med_db_plugin.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "med_db_plugin.h"
+
+#include "med_db_creds.h"
+
+#include <daemon.h>
+
+typedef struct private_med_db_plugin_t private_med_db_plugin_t;
+
+/**
+ * private data of med_db plugin
+ */
+struct private_med_db_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ med_db_plugin_t public;
+
+ /**
+ * database connection instance
+ */
+ database_t *db;
+
+ /**
+ * med_db credential set instance
+ */
+ med_db_creds_t *creds;
+};
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(private_med_db_plugin_t *this)
+{
+ charon->credentials->remove_set(charon->credentials, &this->creds->set);
+ this->creds->destroy(this->creds);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ char *uri;
+ private_med_db_plugin_t *this = malloc_thing(private_med_db_plugin_t);
+
+ this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ uri = lib->settings->get_str(lib->settings, "plugins.med_db.database", NULL);
+ if (!uri)
+ {
+ DBG1(DBG_CFG, "mediation database URI not defined, skipped");
+ free(this);
+ return NULL;
+ }
+
+ if (this->db == NULL)
+ {
+ DBG1(DBG_CFG, "opening mediation server database failed");
+ free(this);
+ return NULL;
+ }
+
+ this->creds = med_db_creds_create(this->db);
+
+ charon->credentials->add_set(charon->credentials, &this->creds->set);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/med_db/med_db_plugin.h b/src/charon/plugins/med_db/med_db_plugin.h
new file mode 100644
index 000000000..42d55adda
--- /dev/null
+++ b/src/charon/plugins/med_db/med_db_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup med_db med_db
+ * @ingroup cplugins
+ *
+ * @defgroup med_db_plugin med_db_plugin
+ * @{ @ingroup med_db
+ */
+
+#ifndef MED_DB_PLUGIN_H_
+#define MED_DB_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct med_db_plugin_t med_db_plugin_t;
+
+/**
+ * Mediation server database plugin.
+ */
+struct med_db_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a med_db_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* MED_DB_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/sql/Makefile.am b/src/charon/plugins/sql/Makefile.am
new file mode 100644
index 000000000..813c601eb
--- /dev/null
+++ b/src/charon/plugins/sql/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-sql.la
+libcharon_sql_la_SOURCES = sql_plugin.h sql_plugin.c \
+ sql_config.h sql_config.c
+libcharon_sql_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/sql/config.sql b/src/charon/plugins/sql/config.sql
new file mode 100644
index 000000000..64aaea7d7
--- /dev/null
+++ b/src/charon/plugins/sql/config.sql
@@ -0,0 +1,73 @@
+
+DROP TABLE IF EXISTS ike_configs;
+CREATE TABLE ike_configs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ certreq INTEGER,
+ force_encap INTEGER,
+ local TEXT,
+ remote TEXT
+);
+
+DROP TABLE IF EXISTS child_configs;
+CREATE TABLE child_configs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT,
+ lifetime INTEGER,
+ rekeytime INTEGER,
+ jitter INTEGER,
+ updown TEXT,
+ hostaccess INTEGER,
+ mode INTEGER
+);
+
+DROP TABLE IF EXISTS peer_config_child_config;
+CREATE TABLE peer_config_child_config (
+ peer_cfg INTEGER,
+ child_cfg INTEGER
+);
+
+DROP TABLE IF EXISTS traffic_selectors;
+CREATE TABLE traffic_selectors (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ type INTEGER,
+ protocol INTEGER,
+ start_addr TEXT,
+ end_addr TEXT,
+ start_port INTEGER,
+ end_port INTEGER
+);
+
+DROP TABLE IF EXISTS child_config_traffic_selector;
+CREATE TABLE child_config_traffic_selector (
+ child_cfg INTEGER,
+ traffic_selector INTEGER,
+ kind INTEGER
+);
+
+DROP TABLE IF EXISTS peer_configs;
+CREATE TABLE peer_configs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT,
+ ike_version INTEGER,
+ ike_cfg INTEGER,
+ local_id TEXT,
+ remote_id TEXT,
+ cert_policy INTEGER,
+ auth_method INTEGER,
+ eap_type INTEGER,
+ eap_vendor INTEGER,
+ keyingtries INTEGER,
+ rekeytime INTEGER,
+ reauthtime INTEGER,
+ jitter INTEGER,
+ overtime INTEGER,
+ mobike INTEGER,
+ dpd_delay INTEGER,
+ dpd_action INTEGER,
+ local_vip TEXT,
+ remote_vip TEXT,
+ mediation INTEGER,
+ mediated_by INTEGER,
+ peer_id TEXT
+);
+
diff --git a/src/charon/plugins/sql/cred.sql b/src/charon/plugins/sql/cred.sql
new file mode 100644
index 000000000..4b53e4e4b
--- /dev/null
+++ b/src/charon/plugins/sql/cred.sql
@@ -0,0 +1,24 @@
+
+DROP TABLE IF EXISTS shared_secrets;
+CREATE TABLE shared_secrets (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ type INTEGER,
+ local TEXT,
+ remote TEXT
+);
+
+DROP TABLE IF EXISTS certificates;
+CREATE TABLE certificates (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ type INTEGER,
+ subject TEXT,
+ data BLOB,
+);
+
+DROP TABLE IF EXISTS private_keys;
+CREATE TABLE private_keys (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ type INTEGER,
+ keyid BLOB,
+ data BLOB,
+);
diff --git a/src/charon/plugins/sql/sql_config.c b/src/charon/plugins/sql/sql_config.c
new file mode 100644
index 000000000..eaa9da5ef
--- /dev/null
+++ b/src/charon/plugins/sql/sql_config.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2006-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include <string.h>
+
+#include "sql_config.h"
+
+#include <daemon.h>
+
+typedef struct private_sql_config_t private_sql_config_t;
+
+/**
+ * Private data of an sql_config_t object
+ */
+struct private_sql_config_t {
+
+ /**
+ * Public part
+ */
+ sql_config_t public;
+
+ /**
+ * database connection
+ */
+ database_t *db;
+};
+
+/**
+ * forward declaration
+ */
+static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
+ identification_t *me, identification_t *other);
+
+/**
+ * build a traffic selector from a SQL query
+ */
+static traffic_selector_t *build_traffic_selector(private_sql_config_t *this,
+ enumerator_t *e, bool *local)
+{
+ int type, protocol, start_port, end_port;
+ char *start_addr, *end_addr;
+ traffic_selector_t *ts;
+ enum {
+ TS_LOCAL = 0,
+ TS_REMOTE = 1,
+ TS_LOCAL_DYNAMIC = 2,
+ TS_REMOTE_DYNAMIC = 3,
+ } kind;
+
+ while (e->enumerate(e, &kind, &type, &protocol,
+ &start_addr, &end_addr, &start_port, &end_port))
+ {
+ *local = FALSE;
+ switch (kind)
+ {
+ case TS_LOCAL:
+ *local = TRUE;
+ /* FALL */
+ case TS_REMOTE:
+ ts = traffic_selector_create_from_string(protocol, type,
+ start_addr, start_port, end_addr, end_port);
+ break;
+ case TS_LOCAL_DYNAMIC:
+ *local = TRUE;
+ /* FALL */
+ case TS_REMOTE_DYNAMIC:
+ ts = traffic_selector_create_dynamic(protocol, type,
+ start_port, end_port);
+ break;
+ default:
+ continue;
+ }
+ if (ts)
+ {
+ return ts;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Add traffic selectors to a child config
+ */
+static void add_traffic_selectors(private_sql_config_t *this,
+ child_cfg_t *child, int id)
+{
+ enumerator_t *e;
+ traffic_selector_t *ts;
+ bool local;
+
+ e = this->db->query(this->db,
+ "SELECT kind, type, protocol, "
+ "start_addr, end_addr, start_port, end_port "
+ "FROM traffic_selectors JOIN child_config_traffic_selector "
+ "ON id = traffic_selector WHERE child_cfg = ?",
+ DB_INT, id,
+ DB_INT, DB_INT, DB_INT,
+ DB_TEXT, DB_TEXT, DB_INT, DB_INT);
+ if (e)
+ {
+ while ((ts = build_traffic_selector(this, e, &local)))
+ {
+ child->add_traffic_selector(child, local, ts);
+ }
+ e->destroy(e);
+ }
+}
+
+/**
+ * build a Child configuration from a SQL query
+ */
+static child_cfg_t *build_child_cfg(private_sql_config_t *this, enumerator_t *e)
+{
+ int id, lifetime, rekeytime, jitter, hostaccess, mode;
+ char *name, *updown;
+ child_cfg_t *child_cfg;
+
+ if (e->enumerate(e, &id, &name, &lifetime, &rekeytime, &jitter,
+ &updown, &hostaccess, &mode))
+ {
+ child_cfg = child_cfg_create(name, lifetime, rekeytime, jitter,
+ updown, hostaccess, mode);
+ /* TODO: read proposal from db */
+ child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
+ add_traffic_selectors(this, child_cfg, id);
+ return child_cfg;
+ }
+ return NULL;
+}
+
+/**
+ * Add child configs to peer config
+ */
+static void add_child_cfgs(private_sql_config_t *this, peer_cfg_t *peer, int id)
+{
+ enumerator_t *e;
+ child_cfg_t *child_cfg;
+
+ e = this->db->query(this->db,
+ "SELECT id, name, lifetime, rekeytime, jitter, "
+ "updown, hostaccess, mode "
+ "FROM child_configs JOIN peer_config_child_config ON id = child_cfg "
+ "WHERE peer_cfg = ?",
+ DB_INT, id,
+ DB_INT, DB_TEXT, DB_INT, DB_INT, DB_INT,
+ DB_TEXT, DB_INT, DB_INT);
+ if (e)
+ {
+ while ((child_cfg = build_child_cfg(this, e)))
+ {
+ peer->add_child_cfg(peer, child_cfg);
+ }
+ e->destroy(e);
+ }
+}
+
+/**
+ * build a ike configuration from a SQL query
+ */
+static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e,
+ host_t *my_host, host_t *other_host)
+{
+ int certreq, force_encap;
+ char *local, *remote;
+
+ while (e->enumerate(e, &certreq, &force_encap, &local, &remote))
+ {
+ host_t *me, *other;
+ ike_cfg_t *ike_cfg;
+
+ me = host_create_from_string(local, 500);
+ if (!me)
+ {
+ continue;
+ }
+ if (my_host && !me->is_anyaddr(me) &&
+ !me->ip_equals(me, my_host))
+ {
+ me->destroy(me);
+ continue;
+ }
+ other = host_create_from_string(remote, 500);
+ if (!other)
+ {
+ me->destroy(me);
+ continue;
+ }
+ if (other_host && !other->is_anyaddr(other) &&
+ !other->ip_equals(other, other_host))
+ {
+ me->destroy(me);
+ other->destroy(other);
+ continue;
+ }
+ ike_cfg = ike_cfg_create(certreq, force_encap, me, other);
+ /* TODO: read proposal from db */
+ ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
+ return ike_cfg;
+ }
+ return NULL;
+}
+
+/**
+ * Query a IKE config by its id
+ */
+static ike_cfg_t* get_ike_cfg_by_id(private_sql_config_t *this, int id)
+{
+ enumerator_t *e;
+ ike_cfg_t *ike_cfg = NULL;
+
+ e = this->db->query(this->db,
+ "SELECT certreq, force_encap, local, remote "
+ "FROM ike_configs WHERE id = ?",
+ DB_INT, id,
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT);
+ if (e)
+ {
+ ike_cfg = build_ike_cfg(this, e, NULL, NULL);
+ e->destroy(e);
+ }
+ return ike_cfg;
+}
+
+/**
+ * Query a peer config by its id
+ */
+static peer_cfg_t *get_peer_cfg_by_id(private_sql_config_t *this, int id)
+{
+ enumerator_t *e;
+ peer_cfg_t *peer_cfg = NULL;
+
+ e = this->db->query(this->db,
+ "SELECT id, name, ike_cfg, local_id, remote_id, cert_policy, "
+ "auth_method, eap_type, eap_vendor, keyingtries, "
+ "rekeytime, reauthtime, jitter, overtime, mobike, "
+ "dpd_delay, dpd_action, local_vip, remote_vip, "
+ "mediation, mediated_by, peer_id "
+ "FROM peer_configs WHERE id = ?",
+ DB_INT, id,
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT,
+ DB_INT, DB_INT, DB_TEXT);
+ if (e)
+ {
+ peer_cfg = build_peer_cfg(this, e, NULL, NULL);
+ e->destroy(e);
+ }
+ return peer_cfg;
+}
+
+/**
+ * build a peer configuration from a SQL query
+ */
+static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
+ identification_t *me, identification_t *other)
+{
+ int id, ike_cfg, cert_policy, auth_method, eap_type, eap_vendor,
+ keyingtries, rekeytime, reauthtime, jitter, overtime, mobike,
+ dpd_delay, dpd_action, mediation, mediated_by;
+ char *local_id, *remote_id, *local_vip, *remote_vip, *peer_id, *name;
+
+ while (e->enumerate(e, &id, &name, &ike_cfg, &local_id, &remote_id, &cert_policy,
+ &auth_method, &eap_type, &eap_vendor, &keyingtries,
+ &rekeytime, &reauthtime, &jitter, &overtime, &mobike,
+ &dpd_delay, &dpd_action, &local_vip, &remote_vip,
+ &mediation, &mediated_by, &peer_id))
+ {
+ ike_cfg_t *ike;
+ peer_cfg_t *peer_cfg, *mediated_cfg;
+ identification_t *my_id, *other_id, *peer;
+ host_t *my_vip, *other_vip;
+
+ my_id = identification_create_from_string(local_id);
+ if (!my_id)
+ {
+ continue;
+ }
+ if (me && !me->matches(me, my_id))
+ {
+ my_id->destroy(my_id);
+ continue;
+ }
+ other_id = identification_create_from_string(remote_id);
+ if (!other_id)
+ {
+ my_id->destroy(my_id);
+ continue;
+ }
+ if (other && !other->matches(other, other_id))
+ {
+ other_id->destroy(other_id);
+ my_id->destroy(my_id);
+ continue;
+ }
+ ike = get_ike_cfg_by_id(this, ike_cfg);
+ mediated_cfg = mediated_by ? get_peer_cfg_by_id(this, mediated_by) : NULL;
+ peer = peer_id ? identification_create_from_string(peer_id) : NULL;
+ my_vip = local_vip ? host_create_from_string(local_vip, 0) : NULL;
+ other_vip = remote_vip ? host_create_from_string(remote_vip, 0) : NULL;
+
+ if (ike)
+ {
+ peer_cfg = peer_cfg_create(
+ name, 2, ike, my_id, other_id, cert_policy,
+ auth_method, eap_type, eap_vendor, keyingtries,
+ rekeytime, reauthtime, jitter, overtime, mobike,
+ dpd_delay, dpd_action, my_vip, other_vip,
+ mediation, mediated_cfg, peer);
+ add_child_cfgs(this, peer_cfg, id);
+ return peer_cfg;
+ }
+ DESTROY_IF(ike);
+ DESTROY_IF(mediated_cfg);
+ DESTROY_IF(peer);
+ DESTROY_IF(my_vip);
+ DESTROY_IF(other_vip);
+ DESTROY_IF(my_id);
+ DESTROY_IF(other_id);
+ }
+ return NULL;
+}
+
+/**
+ * implements backend_t.get_peer_cfg_by_name.
+ */
+static peer_cfg_t *get_peer_cfg_by_name(private_sql_config_t *this, char *name)
+{
+ enumerator_t *e;
+ peer_cfg_t *peer_cfg = NULL;
+
+ e = this->db->query(this->db,
+ "SELECT id, name, ike_cfg, local_id, remote_id, cert_policy, "
+ "auth_method, eap_type, eap_vendor, keyingtries, "
+ "rekeytime, reauthtime, jitter, overtime, mobike, "
+ "dpd_delay, dpd_action, local_vip, remote_vip, "
+ "mediation, mediated_by, peer_id "
+ "FROM peer_configs WHERE ike_version = ? AND name = ?",
+ DB_INT, 2, DB_TEXT, name,
+ DB_INT, DB_TEXT, DB_INT, DB_TEXT, DB_TEXT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT,
+ DB_INT, DB_INT, DB_TEXT);
+ if (e)
+ {
+ peer_cfg = build_peer_cfg(this, e, NULL, NULL);
+ e->destroy(e);
+ }
+ return peer_cfg;
+}
+
+typedef struct {
+ /** implements enumerator */
+ enumerator_t public;
+ /** reference to context */
+ private_sql_config_t *this;
+ /** filtering own host */
+ host_t *me;
+ /** filtering remote host */
+ host_t *other;
+ /** inner SQL enumerator */
+ enumerator_t *inner;
+ /** currently enumerated peer config */
+ ike_cfg_t *current;
+} ike_enumerator_t;
+
+/**
+ * Implementation of ike_enumerator_t.public.enumerate
+ */
+static bool ike_enumerator_enumerate(ike_enumerator_t *this, ike_cfg_t **cfg)
+{
+ DESTROY_IF(this->current);
+ this->current = build_ike_cfg(this->this, this->inner, this->me, this->other);
+ if (this->current)
+ {
+ *cfg = this->current;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of ike_enumerator_t.public.destroy
+ */
+static void ike_enumerator_destroy(ike_enumerator_t *this)
+{
+ DESTROY_IF(this->current);
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * Implementation of backend_t.create_ike_cfg_enumerator.
+ */
+static enumerator_t* create_ike_cfg_enumerator(private_sql_config_t *this,
+ host_t *me, host_t *other)
+{
+ ike_enumerator_t *e = malloc_thing(ike_enumerator_t);
+
+ e->this = this;
+ e->me = me;
+ e->other = other;
+ e->current = NULL;
+ e->public.enumerate = (void*)ike_enumerator_enumerate;
+ e->public.destroy = (void*)ike_enumerator_destroy;
+
+ e->inner = this->db->query(this->db,
+ "SELECT certreq, force_encap, local, remote "
+ "FROM ike_configs",
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT);
+ if (!e->inner)
+ {
+ free(e);
+ return NULL;
+ }
+ return &e->public;
+}
+
+
+typedef struct {
+ /** implements enumerator */
+ enumerator_t public;
+ /** reference to context */
+ private_sql_config_t *this;
+ /** filtering own identity */
+ identification_t *me;
+ /** filtering remote identity */
+ identification_t *other;
+ /** inner SQL enumerator */
+ enumerator_t *inner;
+ /** currently enumerated peer config */
+ peer_cfg_t *current;
+} peer_enumerator_t;
+
+/**
+ * Implementation of peer_enumerator_t.public.enumerate
+ */
+static bool peer_enumerator_enumerate(peer_enumerator_t *this, peer_cfg_t **cfg)
+{
+ DESTROY_IF(this->current);
+ this->current = build_peer_cfg(this->this, this->inner, this->me, this->other);
+ if (this->current)
+ {
+ *cfg = this->current;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of peer_enumerator_t.public.destroy
+ */
+static void peer_enumerator_destroy(peer_enumerator_t *this)
+{
+ DESTROY_IF(this->current);
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
+/**
+ * Implementation of backend_t.create_peer_cfg_enumerator.
+ */
+static enumerator_t* create_peer_cfg_enumerator(private_sql_config_t *this,
+ identification_t *me,
+ identification_t *other)
+{
+ peer_enumerator_t *e = malloc_thing(peer_enumerator_t);
+
+ e->this = this;
+ e->me = me;
+ e->other = other;
+ e->current = NULL;
+ e->public.enumerate = (void*)peer_enumerator_enumerate;
+ e->public.destroy = (void*)peer_enumerator_destroy;
+
+ /* TODO: only get configs whose IDs match exactly or contain wildcards */
+ e->inner = this->db->query(this->db,
+ "SELECT id, name, ike_cfg, local_id, remote_id, cert_policy, "
+ "auth_method, eap_type, eap_vendor, keyingtries, "
+ "rekeytime, reauthtime, jitter, overtime, mobike, "
+ "dpd_delay, dpd_action, local_vip, remote_vip, "
+ "mediation, mediated_by, peer_id "
+ "FROM peer_configs WHERE ike_version = ? ",
+ DB_INT, 2,
+ DB_INT, DB_TEXT, DB_INT, DB_TEXT, DB_TEXT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_INT, DB_INT, DB_INT,
+ DB_INT, DB_INT, DB_TEXT, DB_TEXT,
+ DB_INT, DB_INT, DB_TEXT);
+ if (!e->inner)
+ {
+ free(e);
+ return NULL;
+ }
+ return &e->public;
+}
+
+/**
+ * Implementation of sql_config_t.destroy.
+ */
+static void destroy(private_sql_config_t *this)
+{
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+sql_config_t *sql_config_create(database_t *db)
+{
+ private_sql_config_t *this = malloc_thing(private_sql_config_t);
+
+ this->public.backend.create_peer_cfg_enumerator = (enumerator_t*(*)(backend_t*, identification_t *me, identification_t *other))create_peer_cfg_enumerator;
+ this->public.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator;
+ this->public.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name;
+ this->public.destroy = (void(*)(sql_config_t*))destroy;
+
+ this->db = db;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/sql/sql_config.h b/src/charon/plugins/sql/sql_config.h
new file mode 100644
index 000000000..829d80da8
--- /dev/null
+++ b/src/charon/plugins/sql/sql_config.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup sql_config_i sql_config
+ * @{ @ingroup sql_config
+ */
+
+#ifndef SQL_CONFIG_H_
+#define SQL_CONFIG_H_
+
+#include <config/backend.h>
+#include <database/database.h>
+
+typedef struct sql_config_t sql_config_t;
+
+/**
+ * SQL database configuration backend.
+ */
+struct sql_config_t {
+
+ /**
+ * Implements backend_t interface
+ */
+ backend_t backend;
+
+ /**
+ * Destry the backend.
+ */
+ void (*destroy)(sql_config_t *this);
+};
+
+/**
+ * Create a sql_config backend instance.
+ *
+ * @param db underlying database
+ * @return backend instance
+ */
+sql_config_t *sql_config_create(database_t *db);
+
+#endif /* SQL_CONFIG_H_ @}*/
diff --git a/src/charon/plugins/sql/sql_plugin.c b/src/charon/plugins/sql/sql_plugin.c
new file mode 100644
index 000000000..8ee6400ba
--- /dev/null
+++ b/src/charon/plugins/sql/sql_plugin.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "sql_plugin.h"
+
+#include <daemon.h>
+#include "sql_config.h"
+
+typedef struct private_sql_plugin_t private_sql_plugin_t;
+
+/**
+ * private data of sql plugin
+ */
+struct private_sql_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ sql_plugin_t public;
+
+ /**
+ * database connection instance
+ */
+ database_t *db;
+
+ /**
+ * configuration backend
+ */
+ sql_config_t *config;
+};
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(private_sql_plugin_t *this)
+{
+ charon->backends->remove_backend(charon->backends, &this->config->backend);
+ this->config->destroy(this->config);
+ this->db->destroy(this->db);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ char *uri;
+ private_sql_plugin_t *this;
+
+ uri = lib->settings->get_str(lib->settings, "charon.plugins.sql.database", NULL);
+ if (!uri)
+ {
+ DBG1(DBG_CFG, "SQL plugin database URI not set");
+ return NULL;
+ }
+
+ this = malloc_thing(private_sql_plugin_t);
+
+ this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ this->db = lib->db->create(lib->db, uri);
+ if (!this->db)
+ {
+ DBG1(DBG_CFG, "SQL plugin failed to connect to database");
+ free(this);
+ return NULL;
+ }
+ this->config = sql_config_create(this->db);
+
+ charon->backends->add_backend(charon->backends, &this->config->backend);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/sql/sql_plugin.h b/src/charon/plugins/sql/sql_plugin.h
new file mode 100644
index 000000000..978df3744
--- /dev/null
+++ b/src/charon/plugins/sql/sql_plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup sql sql
+ * @ingroup cplugins
+ *
+ * @defgroup sql_plugin sql_plugin
+ * @{ @ingroup sql
+ */
+
+#ifndef SQL_PLUGIN_H_
+#define SQL_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct sql_plugin_t sql_plugin_t;
+
+/**
+ * SQL database configuration plugin
+ */
+struct sql_plugin_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a sql_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* SQL_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/sql/test.sql b/src/charon/plugins/sql/test.sql
new file mode 100644
index 000000000..ec5b401c5
--- /dev/null
+++ b/src/charon/plugins/sql/test.sql
@@ -0,0 +1,47 @@
+
+INSERT INTO ike_configs (
+ certreq, force_encap, local, remote
+) VALUES (
+ 0, 0, '0.0.0.0', '152.96.52.150'
+);
+
+INSERT INTO child_configs (
+ name, lifetime, rekeytime, jitter, updown, hostaccess, mode
+) VALUES (
+ 'sqltest', 500, 400, 50, NULL, 1, 1
+);
+
+INSERT INTO peer_config_child_config (
+ peer_cfg, child_cfg
+) VALUES (
+ 1, 1
+);
+
+INSERT INTO traffic_selectors (
+ type, protocol
+) values (
+ 7, 0
+);
+
+INSERT INTO child_config_traffic_selector (
+ child_cfg, traffic_selector, kind
+) VALUES (
+ 1, 1, 2
+);
+
+INSERT INTO child_config_traffic_selector (
+ child_cfg, traffic_selector, kind
+) VALUES (
+ 1, 1, 3
+);
+
+INSERT INTO peer_configs (
+ name, ike_version, ike_cfg, local_id, remote_id, cert_policy, auth_method,
+ eap_type, eap_vendor, keyingtries, rekeytime, reauthtime, jitter, overtime,
+ mobike, dpd_delay, dpd_action, local_vip, remote_vip,
+ mediation, mediated_by, peer_id
+) VALUES (
+ 'sqltest', 2, 1, 'C=CH, O=Linux strongSwan, CN=martin', 'sidv0150.hsr.ch', 0, 0,
+ 0, 0, 0, 500, 2000, 20, 20,
+ 1, 120, 0, NULL, NULL, 0, 0, NULL
+);
diff --git a/src/charon/plugins/stroke/Makefile.am b/src/charon/plugins/stroke/Makefile.am
new file mode 100644
index 000000000..b2aeec132
--- /dev/null
+++ b/src/charon/plugins/stroke/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke
+
+AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\"
+
+plugin_LTLIBRARIES = libcharon-stroke.la
+
+libcharon_stroke_la_SOURCES = stroke.h stroke.c
+libcharon_stroke_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/stroke/stroke.c b/src/charon/plugins/stroke/stroke.c
new file mode 100755
index 000000000..9d112844d
--- /dev/null
+++ b/src/charon/plugins/stroke/stroke.c
@@ -0,0 +1,3335 @@
+/*
+ * Copyright (C) 2007 Tobias Brunner
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "stroke.h"
+
+/* stroke message format definition */
+#include <stroke_msg.h>
+
+#include <library.h>
+#include <daemon.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ocsp_request.h>
+#include <credentials/certificates/ocsp_response.h>
+#include <control/controller.h>
+#include <utils/lexparser.h>
+#include <asn1/ttodata.h>
+#include <asn1/pem.h>
+#include <utils/mutex.h>
+#include <processing/jobs/callback_job.h>
+#include <credentials/credential_set.h>
+
+/* configuration directories and files */
+#define CONFIG_DIR IPSEC_CONFDIR
+#define IPSEC_D_DIR CONFIG_DIR "/ipsec.d"
+#define PRIVATE_KEY_DIR IPSEC_D_DIR "/private"
+#define CERTIFICATE_DIR IPSEC_D_DIR "/certs"
+#define CA_CERTIFICATE_DIR IPSEC_D_DIR "/cacerts"
+#define AA_CERTIFICATE_DIR IPSEC_D_DIR "/aacerts"
+#define ATTR_CERTIFICATE_DIR IPSEC_D_DIR "/acerts"
+#define OCSP_CERTIFICATE_DIR IPSEC_D_DIR "/ocspcerts"
+#define CRL_DIR IPSEC_D_DIR "/crls"
+#define SECRETS_FILE CONFIG_DIR "/ipsec.secrets"
+
+/* warning intervals for list functions */
+#define CERT_WARNING_INTERVAL 30 /* days */
+#define CRL_WARNING_INTERVAL 7 /* days */
+
+typedef struct private_stroke_t private_stroke_t;
+typedef struct stroke_credentials_t stroke_credentials_t;
+typedef struct ca_creds_t ca_creds_t;
+typedef struct creds_t creds_t;
+typedef struct ca_section_t ca_section_t;
+typedef struct configs_t configs_t;
+
+/**
+ * loaded ipsec.conf CA sections
+ */
+struct ca_section_t {
+
+ /**
+ * name of the CA section
+ */
+ char *name;
+
+ /**
+ * reference to cert in trusted_credential_t
+ */
+ certificate_t *cert;
+
+ /**
+ * CRL URIs
+ */
+ linked_list_t *crl;
+
+ /**
+ * OCSP URIs
+ */
+ linked_list_t *ocsp;
+};
+
+/**
+ * private credentail_set_t implementation for CA sections
+ */
+struct ca_creds_t {
+ /**
+ * implements credential set
+ */
+ credential_set_t set;
+
+ /**
+ * list of starters CA sections and its certificates (ca_section_t)
+ */
+ linked_list_t *sections;
+
+ /**
+ * mutex to lock sections list
+ */
+ mutex_t *mutex;
+
+};
+
+/**
+ * private credential_set_t implementation for trusted certificates and keys
+ */
+struct creds_t {
+ /**
+ * implements credential set
+ */
+ credential_set_t set;
+
+ /**
+ * list of trusted peer/signer/CA certificates (certificate_t)
+ */
+ linked_list_t *certs;
+
+ /**
+ * list of shared secrets (private_shared_key_t)
+ */
+ linked_list_t *shared;
+
+ /**
+ * list of private keys (private_key_t)
+ */
+ linked_list_t *private;
+
+ /**
+ * mutex to lock lists above
+ */
+ mutex_t *mutex;
+};
+
+
+typedef struct private_shared_key_t private_shared_key_t;
+/**
+ * private data of shared_key
+ */
+struct private_shared_key_t {
+
+ /**
+ * implements shared_key_t
+ */
+ shared_key_t public;
+
+ /**
+ * type of this key
+ */
+ shared_key_type_t type;
+
+ /**
+ * data of the key
+ */
+ chunk_t key;
+
+ /**
+ * list of key owners, as identification_t
+ */
+ linked_list_t *owners;
+
+ /**
+ * reference counter
+ */
+ refcount_t ref;
+};
+
+
+/**
+ * configuration backend including peer_cfg list
+ */
+struct configs_t {
+
+ /**
+ * implements backend_t interface
+ */
+ backend_t backend;
+
+ /**
+ * list of peer_cfg_t
+ */
+ linked_list_t *list;
+
+ /**
+ * mutex to lock config list
+ */
+ mutex_t *mutex;
+};
+
+/**
+ * Private data of an stroke_t object.
+ */
+struct private_stroke_t {
+
+ /**
+ * Public part of stroke_t object.
+ */
+ stroke_t public;
+
+ /**
+ * Unix socket to listen for strokes
+ */
+ int socket;
+
+ /**
+ * job accepting stroke messages
+ */
+ callback_job_t *job;
+
+ /**
+ * CA credentials
+ */
+ ca_creds_t ca_creds;
+
+ /**
+ * other credentials
+ */
+ creds_t creds;
+
+ /**
+ * configuration backend
+ */
+ configs_t configs;
+};
+
+typedef struct stroke_log_info_t stroke_log_info_t;
+
+/**
+ * helper struct to say what and where to log when using controller callback
+ */
+struct stroke_log_info_t {
+
+ /**
+ * level to log up to
+ */
+ level_t level;
+
+ /**
+ * where to write log
+ */
+ FILE* out;
+};
+
+/**
+ * create a new CA section
+ */
+static ca_section_t *ca_section_create(char *name, certificate_t *cert)
+{
+ ca_section_t *ca = malloc_thing(ca_section_t);
+
+ ca->name = strdup(name);
+ ca->crl = linked_list_create();
+ ca->ocsp = linked_list_create();
+ ca->cert = cert;
+ return ca;
+}
+
+/**
+ * destroy a ca section entry
+ */
+static void ca_section_destroy(ca_section_t *this)
+{
+ this->crl->destroy_function(this->crl, free);
+ this->ocsp->destroy_function(this->ocsp, free);
+ free(this->name);
+ free(this);
+}
+
+/**
+ * another return NULL
+ */
+static void* return_null()
+{
+ return NULL;
+}
+
+/**
+ * data to pass to create_inner_cdp
+ */
+typedef struct {
+ ca_creds_t *this;
+ certificate_type_t type;
+ identification_t *id;
+} cdp_data_t;
+
+/**
+ * destroy cdp enumerator data and unlock list
+ */
+static void cdp_data_destroy(cdp_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ free(data);
+}
+
+/**
+ * inner enumerator constructor for CDP URIs
+ */
+static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
+{
+ public_key_t *public;
+ identification_t *keyid;
+ enumerator_t *enumerator = NULL;
+ linked_list_t *list;
+
+ if (data->type == CERT_X509_OCSP_RESPONSE)
+ {
+ list = section->ocsp;
+ }
+ else
+ {
+ list = section->crl;
+ }
+
+ public = section->cert->get_public_key(section->cert);
+ if (public)
+ {
+ if (!data->id)
+ {
+ enumerator = list->create_enumerator(list);
+ }
+ else
+ {
+ keyid = public->get_id(public, data->id->get_type(data->id));
+ if (keyid && keyid->matches(keyid, data->id))
+ {
+ enumerator = list->create_enumerator(list);
+ }
+ }
+ public->destroy(public);
+ }
+ return enumerator;
+}
+
+/**
+ * Implementation of ca_creds_t.set.create_cdp_enumerator.
+ */
+static enumerator_t *create_cdp_enumerator(ca_creds_t *this,
+ certificate_type_t type, identification_t *id)
+{
+ cdp_data_t *data;
+
+ switch (type)
+ { /* we serve CRLs and OCSP responders */
+ case CERT_X509_CRL:
+ case CERT_X509_OCSP_RESPONSE:
+ case CERT_ANY:
+ break;
+ default:
+ return NULL;
+ }
+ data = malloc_thing(cdp_data_t);
+ data->this = this;
+ data->type = type;
+ data->id = id;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_nested(this->sections->create_enumerator(this->sections),
+ (void*)create_inner_cdp, data,
+ (void*)cdp_data_destroy);
+}
+
+/**
+ * data to pass to various filters
+ */
+typedef struct {
+ creds_t *this;
+ identification_t *id;
+} id_data_t;
+
+/**
+ * destroy id enumerator data and unlock list
+ */
+static void id_data_destroy(id_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ free(data);
+}
+
+
+/**
+ * filter function for private key enumerator
+ */
+static bool private_filter(id_data_t *data,
+ private_key_t **in, private_key_t **out)
+{
+ identification_t *candidate;
+
+ if (data->id == NULL)
+ {
+ *out = *in;
+ return TRUE;
+ }
+ candidate = (*in)->get_id(*in, data->id->get_type(data->id));
+ if (candidate && data->id->equals(data->id, candidate))
+ {
+ *out = *in;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements creds_t.set.create_private_enumerator
+ */
+static enumerator_t* create_private_enumerator(creds_t *this,
+ key_type_t type, identification_t *id)
+{
+ id_data_t *data;
+
+ if (type != KEY_RSA && type != KEY_ANY)
+ { /* we only have RSA keys */
+ return NULL;
+ }
+ data = malloc_thing(id_data_t);
+ data->this = this;
+ data->id = id;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->private->create_enumerator(this->private),
+ (void*)private_filter, data,
+ (void*)id_data_destroy);
+}
+
+/**
+ * filter function for certs enumerator
+ */
+static bool certs_filter(id_data_t *data, certificate_t **in, certificate_t **out)
+{
+ public_key_t *public;
+ identification_t *candidate;
+ certificate_t *cert = *in;
+
+ if (cert->get_type(cert) == CERT_X509_CRL)
+ {
+ return FALSE;
+ }
+
+ if (data->id == NULL || cert->has_subject(cert, data->id))
+ {
+ *out = *in;
+ return TRUE;
+ }
+
+ public = (cert)->get_public_key(cert);
+ if (public)
+ {
+ candidate = public->get_id(public, data->id->get_type(data->id));
+ if (candidate && data->id->equals(data->id, candidate))
+ {
+ public->destroy(public);
+ *out = *in;
+ return TRUE;
+ }
+ public->destroy(public);
+ }
+ return FALSE;
+}
+
+/**
+ * filter function for crl enumerator
+ */
+static bool crl_filter(id_data_t *data, certificate_t **in, certificate_t **out)
+{
+ certificate_t *cert = *in;
+
+ if (cert->get_type(cert) != CERT_X509_CRL)
+ {
+ return FALSE;
+ }
+
+ if (data->id == NULL || cert->has_issuer(cert, data->id))
+ {
+ *out = *in;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements creds_t.set.create_cert_enumerator
+ */
+static enumerator_t* create_cert_enumerator(creds_t *this,
+ certificate_type_t cert, key_type_t key,
+ identification_t *id, bool trusted)
+{
+ id_data_t *data;
+
+ if (cert == CERT_X509_CRL)
+ {
+ data = malloc_thing(id_data_t);
+ data->this = this;
+ data->id = id;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->certs->create_enumerator(this->certs),
+ (void*)crl_filter, data,
+ (void*)id_data_destroy);
+ }
+ if (cert != CERT_X509 && cert != CERT_ANY)
+ { /* we only have X509 certificates. TODO: ACs? */
+ return NULL;
+ }
+ if (key != KEY_RSA && key != KEY_ANY)
+ { /* we only have RSA keys */
+ return NULL;
+ }
+ data = malloc_thing(id_data_t);
+ data->this = this;
+ data->id = id;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->certs->create_enumerator(this->certs),
+ (void*)certs_filter, data,
+ (void*)id_data_destroy);
+}
+
+/**
+ * Implementation of shared_key_t.get_type.
+ */
+static shared_key_type_t get_type(private_shared_key_t *this)
+{
+ return this->type;
+}
+
+/**
+ * Implementation of shared_key_t.get_ref.
+ */
+static private_shared_key_t* get_ref(private_shared_key_t *this)
+{
+ ref_get(&this->ref);
+ return this;
+}
+
+/**
+ * Implementation of shared_key_t.destroy
+ */
+static void shared_key_destroy(private_shared_key_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ this->owners->destroy_offset(this->owners, offsetof(identification_t, destroy));
+ chunk_free(&this->key);
+ free(this);
+ }
+}
+
+/**
+ * Implementation of shared_key_t.get_key.
+ */
+static chunk_t get_key(private_shared_key_t *this)
+{
+ return this->key;
+}
+
+/**
+ * create a shared key
+ */
+static private_shared_key_t *shared_key_create(shared_key_type_t type, chunk_t key)
+{
+ private_shared_key_t *this = malloc_thing(private_shared_key_t);
+
+ this->public.get_type = (shared_key_type_t(*)(shared_key_t*))get_type;
+ this->public.get_key = (chunk_t(*)(shared_key_t*))get_key;
+ this->public.get_ref = (shared_key_t*(*)(shared_key_t*))get_ref;
+ this->public.destroy = (void(*)(shared_key_t*))shared_key_destroy;
+
+ this->owners = linked_list_create();
+ this->type = type;
+ this->key = key;
+ this->ref = 1;
+ return this;
+}
+
+/**
+ * Check if a key has such an owner
+ */
+static id_match_t has_owner(private_shared_key_t *this, identification_t *owner)
+{
+ enumerator_t *enumerator;
+ id_match_t match, best = ID_MATCH_NONE;
+ identification_t *current;
+
+ enumerator = this->owners->create_enumerator(this->owners);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ match = owner->matches(owner, current);
+ if (match > best)
+ {
+ best = match;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return best;
+}
+
+typedef struct {
+ creds_t *this;
+ identification_t *me;
+ identification_t *other;
+ shared_key_type_t type;
+} shared_data_t;
+
+/**
+ * free shared key enumerator data and unlock list
+ */
+static void shared_data_destroy(shared_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ free(data);
+}
+
+/**
+ * filter function for certs enumerator
+ */
+static bool shared_filter(shared_data_t *data,
+ private_shared_key_t **in, private_shared_key_t **out,
+ void **unused1, id_match_t *me,
+ void **unused2, id_match_t *other)
+{
+ id_match_t my_match, other_match;
+
+ if (!(*in)->type == SHARED_ANY && !(*in)->type == data->type)
+ {
+ return FALSE;
+ }
+ my_match = has_owner(*in, data->me);
+ other_match = has_owner(*in, data->other);
+ if (!my_match && !other_match)
+ {
+ return FALSE;
+ }
+ *out = *in;
+ if (me)
+ {
+ *me = my_match;
+ }
+ if (other)
+ {
+ *other = other_match;
+ }
+ return TRUE;
+}
+
+/**
+ * Implements creds_t.set.create_shared_enumerator
+ */
+static enumerator_t* create_shared_enumerator(creds_t *this,
+ shared_key_type_t type, identification_t *me,
+ identification_t *other)
+{
+ shared_data_t *data = malloc_thing(shared_data_t);
+
+ data->this = this;
+ data->me = me;
+ data->other = other;
+ data->type = type;
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->shared->create_enumerator(this->shared),
+ (void*)shared_filter, data,
+ (void*)shared_data_destroy);
+}
+
+/**
+ * Helper function which corrects the string pointers
+ * in a stroke_msg_t. Strings in a stroke_msg sent over "wire"
+ * contains RELATIVE addresses (relative to the beginning of the
+ * stroke_msg). They must be corrected if they reach our address
+ * space...
+ */
+static void pop_string(stroke_msg_t *msg, char **string)
+{
+ if (*string == NULL)
+ {
+ return;
+ }
+
+ /* check for sanity of string pointer and string */
+ if (string < (char**)msg ||
+ string > (char**)msg + sizeof(stroke_msg_t) ||
+ (unsigned long)*string < (unsigned long)((char*)msg->buffer - (char*)msg) ||
+ (unsigned long)*string > msg->length)
+ {
+ *string = "(invalid pointer in stroke msg)";
+ }
+ else
+ {
+ *string = (char*)msg + (unsigned long)*string;
+ }
+}
+
+/**
+ * Load an X.509 certificate
+ */
+static x509_t* load_cert(char *path, x509_flag_t flag)
+{
+ bool pgp = FALSE;
+ chunk_t chunk;
+ x509_flag_t flags;
+ x509_t *x509;
+ certificate_t *cert;
+
+ if (!pem_asn1_load_file(path, NULL, &chunk, &pgp))
+ {
+ DBG1(DBG_CFG, " could not load certificate file '%s'", path);
+ return NULL;
+ }
+ x509 = (x509_t*)lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ if (x509 == NULL)
+ {
+ DBG1(DBG_CFG, " could not load certificate file '%s'", path);
+ return NULL;
+ }
+ DBG1(DBG_CFG, " loaded certificate file '%s'", path);
+
+ cert = &x509->interface;
+ flags = x509->get_flags(x509);
+
+ /* check basicConstraints */
+ if ((flag & X509_CA) && !(flags & X509_CA))
+ {
+ DBG1(DBG_CFG, " isCA basicConstraint is not set, certificate discarded");
+ cert->destroy(cert);
+ return NULL;
+ }
+
+ /* set cert flags to flag but keep X509_SELF_SIGNED property */
+ x509->set_flags(x509, flag | (flags & X509_SELF_SIGNED));
+
+ /* check validity */
+ {
+ time_t notBefore, notAfter, now = time(NULL);
+
+ cert->get_validity(cert, &now, &notBefore, &notAfter);
+ if (now > notAfter)
+ {
+ DBG1(DBG_CFG, " certificate expired at %T, discarded", &notAfter);
+ cert->destroy(cert);
+ return NULL;
+ }
+ if (now < notBefore)
+ {
+ DBG1(DBG_CFG, " certificate not valid before %T", &notBefore);
+ }
+ }
+ return x509;
+}
+
+/**
+ * Add X.509 certificate to chain
+ */
+static certificate_t* add_x509_cert(private_stroke_t *this, x509_t* x509)
+{
+ certificate_t *current, *cert = &x509->interface;
+ enumerator_t *enumerator;
+ bool new = TRUE;
+
+ this->creds.mutex->lock(this->creds.mutex);
+ enumerator = this->creds.certs->create_enumerator(this->creds.certs);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current->equals(current, cert))
+ {
+ x509_flag_t flags = x509->get_flags(x509);
+ x509_t *x509c = (x509_t*)current;
+
+ /* cert already in queue - add flags and discard */
+ x509c->set_flags(x509c, flags | x509c->get_flags(x509c));
+ cert->destroy(cert);
+ cert = current;
+ new = FALSE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (new)
+ {
+ this->creds.certs->insert_last(this->creds.certs, cert);
+ }
+ this->creds.mutex->unlock(this->creds.mutex);
+ return cert;
+}
+
+/**
+ * Verify the signature of an X.509 CRL
+ */
+static bool verify_crl(crl_t* crl)
+{
+ certificate_t *crl_cert = &crl->certificate;
+ identification_t *issuer = crl_cert->get_issuer(crl_cert);
+ identification_t *authKeyIdentifier = crl->get_authKeyIdentifier(crl);
+ certificate_t *issuer_cert;
+
+ DBG1(DBG_CFG, " issuer: %D", issuer);
+ if (authKeyIdentifier)
+ {
+ DBG1(DBG_CFG, " authkey: %D", authKeyIdentifier);
+ }
+
+ issuer_cert = charon->credentials->get_cert(charon->credentials, CERT_X509,
+ KEY_ANY, issuer, TRUE);
+
+ if (issuer_cert)
+ {
+
+ bool ok = crl_cert->issued_by(crl_cert, issuer_cert, TRUE);
+
+ DBG1(DBG_CFG, " crl is %strusted: %s signature",
+ ok? "":"un", ok? "good":"bad");
+ return ok;
+ }
+ else
+ {
+ DBG1(DBG_CFG, " crl is untrusted: issuer certificate not found");
+ return FALSE;
+ }
+}
+
+/**
+ * Add X.509 CRL to chain
+ */
+static void add_crl(private_stroke_t *this, crl_t* crl)
+{
+ certificate_t *current, *cert = &crl->certificate;
+ enumerator_t *enumerator;
+ bool new = TRUE, found = FALSE;
+
+ this->creds.mutex->lock(this->creds.mutex);
+ enumerator = this->creds.certs->create_enumerator(this->creds.certs);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current->get_type(current) == CERT_X509_CRL)
+ {
+ crl_t *crl_c = (crl_t*)current;
+ identification_t *authkey = crl->get_authKeyIdentifier(crl);
+ identification_t *authkey_c = crl_c->get_authKeyIdentifier(crl_c);
+
+ /* if compare authorityKeyIdentifiers if available */
+ if (authkey != NULL && authkey_c != NULL &&
+ authkey->equals(authkey, authkey_c))
+ {
+ found = TRUE;
+ }
+ else
+ {
+ identification_t *issuer = cert->get_issuer(cert);
+ identification_t *issuer_c = current->get_issuer(current);
+
+ /* otherwise compare issuer distinguished names */
+ if (issuer->equals(issuer, issuer_c))
+ {
+ found = TRUE;
+ }
+ }
+ if (found)
+ {
+ new = crl->is_newer(crl, crl_c);
+ if (new)
+ {
+ this->creds.certs->remove_at(this->creds.certs, enumerator);
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (new)
+ {
+ this->creds.certs->insert_last(this->creds.certs, cert);
+ }
+ this->creds.mutex->unlock(this->creds.mutex);
+}
+
+/**
+ * Load end entitity certificate
+ */
+static void load_peer_cert(private_stroke_t *this,
+ char *filename, identification_t **id)
+{
+ char path[PATH_MAX];
+ x509_t *x509;
+ identification_t *peerid = *id;
+
+ if (*filename == '/')
+ {
+ snprintf(path, sizeof(path), "%s", filename);
+ }
+ else
+ {
+ snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename);
+ }
+
+ x509 = load_cert(path, X509_PEER);
+
+ if (x509)
+ {
+ certificate_t *cert = &x509->interface;;
+ identification_t *subject = cert->get_subject(cert);
+
+ if (!cert->has_subject(cert, peerid))
+ {
+ DBG1(DBG_CFG, " peerid %D not confirmed by certificate, "
+ "defaulting to subject DN", peerid);
+ peerid->destroy(peerid);
+ *id = subject->clone(subject);
+ }
+ add_x509_cert(this, x509);
+ }
+}
+
+/**
+ * Load ca certificate
+ */
+static certificate_t* load_ca_cert(private_stroke_t *this, char *filename)
+{
+ char path[PATH_MAX];
+ x509_t *x509;
+
+ if (*filename == '/')
+ {
+ snprintf(path, sizeof(path), "%s", filename);
+ }
+ else
+ {
+ snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename);
+ }
+
+ x509 = load_cert(path, X509_CA);
+
+ if (x509)
+ {
+ return add_x509_cert(this, x509);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/**
+ * load trusted certificates from a directory
+ */
+static void load_certdir(private_stroke_t *this,
+ char *path, certificate_type_t type, x509_flag_t flag)
+{
+ struct stat st;
+ char *file;
+
+ enumerator_t *enumerator = enumerator_create_directory(path);
+
+ if (!enumerator)
+ {
+ DBG1(DBG_CFG, " reading directory failed");
+ return;
+ }
+
+ while (enumerator->enumerate(enumerator, NULL, &file, &st))
+ {
+ if (!S_ISREG(st.st_mode))
+ {
+ /* skip special file */
+ continue;
+ }
+ if (type == CERT_X509)
+ {
+ x509_t *x509 = load_cert(file, flag);
+
+ if (x509)
+ {
+ add_x509_cert(this, x509);
+ }
+ }
+ else
+ {
+ certificate_t *cert;
+ bool pgp = FALSE;
+ chunk_t chunk;
+
+ if (!pem_asn1_load_file(file, NULL, &chunk, &pgp))
+ {
+ continue;
+ }
+ cert = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, type,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ if (type == CERT_X509_CRL)
+ {
+ if (cert)
+ {
+ crl_t *crl = (crl_t*)cert;
+
+ DBG1(DBG_CFG, " loaded crl file '%s'", file);
+
+ /* only trusted crls are added to the store */
+ if (verify_crl(crl))
+ {
+ add_crl(this, crl);
+ }
+ else
+ {
+ DBG1(DBG_CFG, " crl discarded");
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, " could not load crl file '%s'", file);
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Convert a string of characters into a binary secret
+ * A string between single or double quotes is treated as ASCII characters
+ * A string prepended by 0x is treated as HEX and prepended by 0s as Base64
+ */
+static err_t extract_secret(chunk_t *secret, chunk_t *line)
+{
+ chunk_t raw_secret;
+ char delimiter = ' ';
+ bool quotes = FALSE;
+
+ if (!eat_whitespace(line))
+ {
+ return "missing secret";
+ }
+
+ if (*line->ptr == '\'' || *line->ptr == '"')
+ {
+ quotes = TRUE;
+ delimiter = *line->ptr;
+ line->ptr++; line->len--;
+ }
+
+ if (!extract_token(&raw_secret, delimiter, line))
+ {
+ if (delimiter == ' ')
+ {
+ raw_secret = *line;
+ }
+ else
+ {
+ return "missing second delimiter";
+ }
+ }
+
+ if (quotes)
+ {
+ /* treat as an ASCII string */
+ *secret = chunk_clone(raw_secret);
+ }
+ else
+ {
+ size_t len;
+ err_t ugh;
+
+ /* secret converted to binary form doesn't use more space than the raw_secret */
+ *secret = chunk_alloc(raw_secret.len);
+
+ /* convert from HEX or Base64 to binary */
+ ugh = ttodata(raw_secret.ptr, raw_secret.len, 0, secret->ptr, secret->len, &len);
+
+ if (ugh != NULL)
+ {
+ chunk_free_randomized(secret);
+ return ugh;
+ }
+ secret->len = len;
+ }
+ return NULL;
+}
+
+/**
+ * reload ipsec.secrets
+ */
+static void load_secrets(private_stroke_t *this)
+{
+ size_t bytes;
+ int line_nr = 0;
+ chunk_t chunk, src, line;
+ FILE *fd;
+ private_key_t *private;
+ shared_key_t *shared;
+
+ DBG1(DBG_CFG, "loading secrets from '%s'", SECRETS_FILE);
+
+ fd = fopen(SECRETS_FILE, "r");
+ if (fd == NULL)
+ {
+ DBG1(DBG_CFG, "opening secrets file '%s' failed");
+ return;
+ }
+
+ /* TODO: do error checks */
+ fseek(fd, 0, SEEK_END);
+ chunk.len = ftell(fd);
+ rewind(fd);
+ chunk.ptr = malloc(chunk.len);
+ bytes = fread(chunk.ptr, 1, chunk.len, fd);
+ fclose(fd);
+ src = chunk;
+
+ this->creds.mutex->lock(this->creds.mutex);
+ while (this->creds.shared->remove_last(this->creds.shared,
+ (void**)&shared) == SUCCESS)
+ {
+ shared->destroy(shared);
+ }
+ while (this->creds.private->remove_last(this->creds.private,
+ (void**)&private) == SUCCESS)
+ {
+ private->destroy(private);
+ }
+
+ while (fetchline(&src, &line))
+ {
+ chunk_t ids, token;
+ shared_key_type_t type;
+
+ line_nr++;
+
+ if (!eat_whitespace(&line))
+ {
+ continue;
+ }
+ if (!extract_token(&ids, ':', &line))
+ {
+ DBG1(DBG_CFG, "line %d: missing ':' separator", line_nr);
+ goto error;
+ }
+ /* NULL terminate the ids string by replacing the : separator */
+ *(ids.ptr + ids.len) = '\0';
+
+ if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line))
+ {
+ DBG1(DBG_CFG, "line %d: missing token", line_nr);
+ goto error;
+ }
+ if (match("RSA", &token))
+ {
+ char path[PATH_MAX];
+ chunk_t filename;
+ chunk_t secret = chunk_empty;
+ private_key_t *key;
+ bool pgp = FALSE;
+ chunk_t chunk = chunk_empty;
+
+ err_t ugh = extract_value(&filename, &line);
+
+ if (ugh != NULL)
+ {
+ DBG1(DBG_CFG, "line %d: %s", line_nr, ugh);
+ goto error;
+ }
+ if (filename.len == 0)
+ {
+ DBG1(DBG_CFG, "line %d: empty filename", line_nr);
+ goto error;
+ }
+ if (*filename.ptr == '/')
+ {
+ /* absolute path name */
+ snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr);
+ }
+ else
+ {
+ /* relative path name */
+ snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR,
+ filename.len, filename.ptr);
+ }
+
+ /* check for optional passphrase */
+ if (eat_whitespace(&line))
+ {
+ ugh = extract_secret(&secret, &line);
+ if (ugh != NULL)
+ {
+ DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh);
+ goto error;
+ }
+ }
+
+ if (pem_asn1_load_file(path, &secret, &chunk, &pgp))
+ {
+ key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ if (key)
+ {
+ DBG1(DBG_CFG, " loaded private key file '%s'", path);
+ this->creds.private->insert_last(this->creds.private, key);
+ }
+ }
+ chunk_free_randomized(&secret);
+ }
+ else if ((match("PSK", &token) && (type = SHARED_IKE)) ||
+ (match("EAP", &token) && (type = SHARED_EAP)) ||
+ (match("XAUTH", &token) && (type = SHARED_EAP)) ||
+ (match("PIN", &token) && (type = SHARED_PIN)))
+ {
+ private_shared_key_t *shared_key;
+ chunk_t secret = chunk_empty;
+ bool any = TRUE;
+
+ err_t ugh = extract_secret(&secret, &line);
+ if (ugh != NULL)
+ {
+ DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh);
+ goto error;
+ }
+ shared_key = shared_key_create(type, secret);
+ DBG1(DBG_CFG, " loaded %N secret for %s", shared_key_type_names, type,
+ ids.len > 0 ? (char*)ids.ptr : "%any");
+ DBG4(DBG_CFG, " secret:", secret);
+
+ this->creds.shared->insert_last(this->creds.shared, shared_key);
+ while (ids.len > 0)
+ {
+ chunk_t id;
+ identification_t *peer_id;
+
+ ugh = extract_value(&id, &ids);
+ if (ugh != NULL)
+ {
+ DBG1(DBG_CFG, "line %d: %s", line_nr, ugh);
+ goto error;
+ }
+ if (id.len == 0)
+ {
+ continue;
+ }
+
+ /* NULL terminate the ID string */
+ *(id.ptr + id.len) = '\0';
+
+ peer_id = identification_create_from_string(id.ptr);
+ if (peer_id == NULL)
+ {
+ DBG1(DBG_CFG, "line %d: malformed ID: %s", line_nr, id.ptr);
+ goto error;
+ }
+ if (peer_id->get_type(peer_id) == ID_ANY)
+ {
+ peer_id->destroy(peer_id);
+ continue;
+ }
+
+ shared_key->owners->insert_last(shared_key->owners, peer_id);
+ any = FALSE;
+ }
+ if (any)
+ {
+ shared_key->owners->insert_last(shared_key->owners,
+ identification_create_from_encoding(ID_ANY, chunk_empty));
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, "line %d: token must be either "
+ "RSA, PSK, EAP, or PIN", line_nr);
+ goto error;
+ }
+ }
+error:
+ this->creds.mutex->unlock(this->creds.mutex);
+ chunk_free_randomized(&chunk);
+}
+
+/**
+ * data to pass peer_filter
+ */
+typedef struct {
+ configs_t *this;
+ identification_t *me;
+ identification_t *other;
+} peer_data_t;
+
+/**
+ * destroy id enumerator data and unlock list
+ */
+static void peer_data_destroy(peer_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ free(data);
+}
+
+/**
+ * filter function for peer configs
+ */
+static bool peer_filter(peer_data_t *data, peer_cfg_t **in, peer_cfg_t **out)
+{
+
+ if ((!data->me || data->me->matches(data->me, (*in)->get_my_id(*in))) &&
+ (!data->other || data->other->matches(data->other, (*in)->get_other_id(*in))))
+ {
+ *out = *in;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of backend_t.create_peer_cfg_enumerator.
+ */
+static enumerator_t* create_peer_cfg_enumerator(configs_t *this,
+ identification_t *me,
+ identification_t *other)
+{
+ peer_data_t *data;
+
+ data = malloc_thing(peer_data_t);
+ data->this = this;
+ data->me = me;
+ data->other = other;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->list->create_enumerator(this->list),
+ (void*)peer_filter, data,
+ (void*)peer_data_destroy);
+}
+
+/**
+ * data to pass ike_filter
+ */
+typedef struct {
+ configs_t *this;
+ host_t *me;
+ host_t *other;
+} ike_data_t;
+
+/**
+ * destroy id enumerator data and unlock list
+ */
+static void ike_data_destroy(ike_data_t *data)
+{
+ data->this->mutex->unlock(data->this->mutex);
+ free(data);
+}
+
+/**
+ * filter function for ike configs
+ */
+static bool ike_filter(ike_data_t *data, peer_cfg_t **in, ike_cfg_t **out)
+{
+ ike_cfg_t *ike_cfg;
+ host_t *me, *other;
+
+ ike_cfg = (*in)->get_ike_cfg(*in);
+
+ me = ike_cfg->get_my_host(ike_cfg);
+ other = ike_cfg->get_other_host(ike_cfg);
+ if ((!data->me || me->is_anyaddr(me) || me->ip_equals(me, data->me)) &&
+ (!data->other || other->is_anyaddr(other) || other->ip_equals(other, data->me)))
+ {
+ *out = ike_cfg;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of backend_t.create_ike_cfg_enumerator.
+ */
+static enumerator_t* create_ike_cfg_enumerator(configs_t *this,
+ host_t *me, host_t *other)
+{
+ ike_data_t *data;
+
+ data = malloc_thing(ike_data_t);
+ data->this = this;
+ data->me = me;
+ data->other = other;
+
+ this->mutex->lock(this->mutex);
+ return enumerator_create_filter(this->list->create_enumerator(this->list),
+ (void*)ike_filter, data,
+ (void*)ike_data_destroy);
+}
+
+/**
+ * implements backend_t.get_peer_cfg_by_name.
+ */
+static peer_cfg_t *get_peer_cfg_by_name(configs_t *this, char *name)
+{
+ enumerator_t *e1, *e2;
+ peer_cfg_t *current, *found = NULL;
+ child_cfg_t *child;
+
+ this->mutex->lock(this->mutex);
+ e1 = this->list->create_enumerator(this->list);
+ while (e1->enumerate(e1, &current))
+ {
+ /* compare peer_cfgs name first */
+ if (streq(current->get_name(current), name))
+ {
+ found = current;
+ found->get_ref(found);
+ break;
+ }
+ /* compare all child_cfg names otherwise */
+ e2 = current->create_child_cfg_enumerator(current);
+ while (e2->enumerate(e2, &child))
+ {
+ if (streq(child->get_name(child), name))
+ {
+ found = current;
+ found->get_ref(found);
+ break;
+ }
+ }
+ e2->destroy(e2);
+ if (found)
+ {
+ break;
+ }
+ }
+ e1->destroy(e1);
+ this->mutex->unlock(this->mutex);
+ return found;
+}
+
+/**
+ * Pop the strings of a stroke_end_t struct and log them for debugging purposes
+ */
+static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end)
+{
+ pop_string(msg, &end->address);
+ pop_string(msg, &end->subnet);
+ pop_string(msg, &end->sourceip);
+ pop_string(msg, &end->id);
+ pop_string(msg, &end->cert);
+ pop_string(msg, &end->ca);
+ pop_string(msg, &end->groups);
+ pop_string(msg, &end->updown);
+
+ DBG2(DBG_CFG, " %s=%s", label, end->address);
+ DBG2(DBG_CFG, " %ssubnet=%s", label, end->subnet);
+ DBG2(DBG_CFG, " %ssourceip=%s", label, end->sourceip);
+ DBG2(DBG_CFG, " %sid=%s", label, end->id);
+ DBG2(DBG_CFG, " %scert=%s", label, end->cert);
+ DBG2(DBG_CFG, " %sca=%s", label, end->ca);
+ DBG2(DBG_CFG, " %sgroups=%s", label, end->groups);
+ DBG2(DBG_CFG, " %supdown=%s", label, end->updown);
+}
+
+/**
+ * Add a connection to the configuration list
+ */
+static void stroke_add_conn(private_stroke_t *this,
+ stroke_msg_t *msg, FILE *out)
+{
+ ike_cfg_t *ike_cfg;
+ peer_cfg_t *peer_cfg;
+ peer_cfg_t *mediated_by_cfg = NULL;
+ child_cfg_t *child_cfg;
+ auth_info_t *auth;
+ identification_t *my_id, *other_id;
+ identification_t *my_ca = NULL;
+ identification_t *other_ca = NULL;
+ identification_t *peer_id = NULL;
+ bool my_ca_same = FALSE;
+ bool other_ca_same =FALSE;
+ host_t *my_host = NULL, *other_host = NULL, *my_subnet, *other_subnet;
+ host_t *my_vip = NULL, *other_vip = NULL;
+ proposal_t *proposal;
+ traffic_selector_t *my_ts, *other_ts;
+ char *interface;
+ bool use_existing = FALSE;
+ enumerator_t *enumerator;
+ u_int32_t vendor;
+
+ pop_string(msg, &msg->add_conn.name);
+ DBG1(DBG_CFG, "received stroke: add connection '%s'", msg->add_conn.name);
+ DBG2(DBG_CFG, "conn %s", msg->add_conn.name);
+ pop_end(msg, "left", &msg->add_conn.me);
+ pop_end(msg, "right", &msg->add_conn.other);
+ pop_string(msg, &msg->add_conn.algorithms.ike);
+ pop_string(msg, &msg->add_conn.algorithms.esp);
+ DBG2(DBG_CFG, " ike=%s", msg->add_conn.algorithms.ike);
+ DBG2(DBG_CFG, " esp=%s", msg->add_conn.algorithms.esp);
+ pop_string(msg, &msg->add_conn.p2p.mediated_by);
+ pop_string(msg, &msg->add_conn.p2p.peerid);
+ DBG2(DBG_CFG, " p2p_mediation=%s", msg->add_conn.p2p.mediation ? "yes" : "no");
+ DBG2(DBG_CFG, " p2p_mediated_by=%s", msg->add_conn.p2p.mediated_by);
+ DBG2(DBG_CFG, " p2p_peerid=%s", msg->add_conn.p2p.peerid);
+
+ if (msg->add_conn.me.address)
+ {
+ my_host = host_create_from_string(msg->add_conn.me.address,
+ IKEV2_UDP_PORT);
+ }
+ if (my_host == NULL)
+ {
+ DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.me.address);
+ return;
+ }
+ if (msg->add_conn.other.address)
+ {
+ other_host = host_create_from_string(msg->add_conn.other.address,
+ IKEV2_UDP_PORT);
+ }
+ if (other_host == NULL)
+ {
+ DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.other.address);
+ my_host->destroy(my_host);
+ return;
+ }
+
+ interface = charon->kernel_interface->get_interface(charon->kernel_interface,
+ other_host);
+ if (interface)
+ {
+ stroke_end_t tmp_end;
+ host_t *tmp_host;
+
+ DBG2(DBG_CFG, "left is other host, swapping ends\n");
+
+ tmp_host = my_host;
+ my_host = other_host;
+ other_host = tmp_host;
+
+ tmp_end = msg->add_conn.me;
+ msg->add_conn.me = msg->add_conn.other;
+ msg->add_conn.other = tmp_end;
+ free(interface);
+ }
+ else
+ {
+ interface = charon->kernel_interface->get_interface(
+ charon->kernel_interface, my_host);
+ if (!interface)
+ {
+ DBG1(DBG_CFG, "left nor right host is our side, assuming left=local");
+ }
+ else
+ {
+ free(interface);
+ }
+ }
+
+ my_id = identification_create_from_string(msg->add_conn.me.id ?
+ msg->add_conn.me.id : msg->add_conn.me.address);
+ if (my_id == NULL)
+ {
+ DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.me.id);
+ goto destroy_hosts;
+ }
+
+ other_id = identification_create_from_string(msg->add_conn.other.id ?
+ msg->add_conn.other.id : msg->add_conn.other.address);
+ if (other_id == NULL)
+ {
+ DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.other.id);
+ my_id->destroy(my_id);
+ goto destroy_hosts;
+ }
+
+#ifdef P2P
+ if (msg->add_conn.p2p.mediation && msg->add_conn.p2p.mediated_by)
+ {
+ DBG1(DBG_CFG, "a mediation connection cannot be a"
+ " mediated connection at the same time, aborting");
+ goto destroy_ids;
+ }
+
+ if (msg->add_conn.p2p.mediated_by)
+ {
+ mediated_by_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
+ msg->add_conn.p2p.mediated_by);
+ if (!mediated_by_cfg)
+ {
+ DBG1(DBG_CFG, "mediation connection '%s' not found, aborting",
+ msg->add_conn.p2p.mediated_by);
+ goto destroy_ids;
+ }
+
+ if (!mediated_by_cfg->is_mediation(mediated_by_cfg))
+ {
+ DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is"
+ "no mediation connection, aborting",
+ msg->add_conn.p2p.mediated_by, msg->add_conn.name);
+ goto destroy_ids;
+ }
+ }
+
+ if (msg->add_conn.p2p.peerid)
+ {
+ peer_id = identification_create_from_string(msg->add_conn.p2p.peerid);
+ if (!peer_id)
+ {
+ DBG1(DBG_CFG, "invalid peer ID: %s\n", msg->add_conn.p2p.peerid);
+ goto destroy_ids;
+ }
+ }
+ else
+ {
+ /* no peer ID supplied, assume right ID */
+ peer_id = other_id->clone(other_id);
+ }
+#endif /* P2P */
+
+ my_subnet = host_create_from_string(
+ msg->add_conn.me.subnet ? msg->add_conn.me.subnet
+ : msg->add_conn.me.address,
+ IKEV2_UDP_PORT);
+ if (my_subnet == NULL)
+ {
+ DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet);
+ goto destroy_ids;
+ }
+
+ other_subnet = host_create_from_string(
+ msg->add_conn.other.subnet ? msg->add_conn.other.subnet
+ : msg->add_conn.other.address,
+ IKEV2_UDP_PORT);
+ if (other_subnet == NULL)
+ {
+ DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet);
+ my_subnet->destroy(my_subnet);
+ goto destroy_ids;
+ }
+
+ if (msg->add_conn.me.virtual_ip && msg->add_conn.me.sourceip)
+ {
+ my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
+ }
+ if (msg->add_conn.other.virtual_ip && msg->add_conn.other.sourceip)
+ {
+ other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0);
+ }
+
+ if (msg->add_conn.me.tohost)
+ {
+ my_ts = traffic_selector_create_dynamic(msg->add_conn.me.protocol,
+ my_host->get_family(my_host) == AF_INET ?
+ TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE,
+ msg->add_conn.me.port ? msg->add_conn.me.port : 0,
+ msg->add_conn.me.port ? msg->add_conn.me.port : 65535);
+ }
+ else
+ {
+ my_ts = traffic_selector_create_from_subnet(my_subnet,
+ msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 0,
+ msg->add_conn.me.protocol, msg->add_conn.me.port);
+ }
+ my_subnet->destroy(my_subnet);
+
+ if (msg->add_conn.other.tohost)
+ {
+ other_ts = traffic_selector_create_dynamic(msg->add_conn.other.protocol,
+ other_host->get_family(other_host) == AF_INET ?
+ TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE,
+ msg->add_conn.other.port ? msg->add_conn.other.port : 0,
+ msg->add_conn.other.port ? msg->add_conn.other.port : 65535);
+ }
+ else
+ {
+ other_ts = traffic_selector_create_from_subnet(other_subnet,
+ msg->add_conn.other.subnet ? msg->add_conn.other.subnet_mask : 0,
+ msg->add_conn.other.protocol, msg->add_conn.other.port);
+ }
+ other_subnet->destroy(other_subnet);
+
+ if (msg->add_conn.me.ca)
+ {
+ if (streq(msg->add_conn.me.ca, "%same"))
+ {
+ my_ca_same = TRUE;
+ }
+ else
+ {
+ my_ca = identification_create_from_string(msg->add_conn.me.ca);
+ }
+ }
+ if (msg->add_conn.other.ca)
+ {
+ if (streq(msg->add_conn.other.ca, "%same"))
+ {
+ other_ca_same = TRUE;
+ }
+ else
+ {
+ other_ca = identification_create_from_string(msg->add_conn.other.ca);
+ }
+ }
+ if (msg->add_conn.me.cert)
+ {
+ load_peer_cert(this, msg->add_conn.me.cert, &my_id);
+ }
+ if (msg->add_conn.other.cert)
+ {
+ load_peer_cert(this, msg->add_conn.other.cert, &other_id);
+ }
+ if (other_ca_same && my_ca)
+ {
+ other_ca = my_ca->clone(my_ca);
+ }
+ else if (my_ca_same && other_ca)
+ {
+ my_ca = other_ca->clone(other_ca);
+ }
+
+ if (my_ca)
+ {
+ DBG2(DBG_CFG, " my ca: %D", my_ca);
+ }
+ if (other_ca)
+ {
+ DBG2(DBG_CFG, " other ca: %D", other_ca);
+ }
+
+ if (msg->add_conn.other.groups)
+ {
+ /* TODO: AC groups */
+ }
+
+ /* TODO: update matching */
+ /* have a look for an (almost) identical peer config to reuse */
+ enumerator = create_peer_cfg_enumerator(&this->configs, NULL, NULL);
+ while (enumerator->enumerate(enumerator, &peer_cfg))
+ {
+ host_t *my_vip_conf, *other_vip_conf;
+ bool my_vip_equals = FALSE, other_vip_equals = FALSE;
+
+ my_vip_conf = peer_cfg->get_my_virtual_ip(peer_cfg);
+ if ((my_vip && my_vip_conf && my_vip->equals(my_vip, my_vip_conf)) ||
+ (!my_vip_conf && !my_vip))
+ {
+ my_vip_equals = TRUE;
+ }
+ DESTROY_IF(my_vip_conf);
+ other_vip_conf = peer_cfg->get_other_virtual_ip(peer_cfg, NULL);
+ if ((other_vip && other_vip_conf && other_vip->equals(other_vip, other_vip_conf)) ||
+ (!other_vip_conf && !other_vip))
+ {
+ other_vip_equals = TRUE;
+ }
+ DESTROY_IF(other_vip_conf);
+
+ ike_cfg = peer_cfg->get_ike_cfg(peer_cfg);
+ if (my_id->equals(my_id, peer_cfg->get_my_id(peer_cfg))
+ && other_id->equals(other_id, peer_cfg->get_other_id(peer_cfg))
+ && my_host->equals(my_host, ike_cfg->get_my_host(ike_cfg))
+ && other_host->equals(other_host, ike_cfg->get_other_host(ike_cfg))
+ && peer_cfg->get_ike_version(peer_cfg) == (msg->add_conn.ikev2 ? 2 : 1)
+ && peer_cfg->get_auth_method(peer_cfg) == msg->add_conn.auth_method
+ && peer_cfg->get_eap_type(peer_cfg, &vendor) == msg->add_conn.eap_type
+ && vendor == msg->add_conn.eap_vendor
+ && my_vip_equals && other_vip_equals)
+ {
+ DBG1(DBG_CFG, "reusing existing configuration '%s'",
+ peer_cfg->get_name(peer_cfg));
+ use_existing = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (use_existing)
+ {
+ DESTROY_IF(my_vip);
+ DESTROY_IF(other_vip);
+ my_host->destroy(my_host);
+ my_id->destroy(my_id);
+ DESTROY_IF(my_ca);
+ other_host->destroy(other_host);
+ other_id->destroy(other_id);
+ DESTROY_IF(other_ca);
+ DESTROY_IF(peer_id);
+ DESTROY_IF(mediated_by_cfg);
+ }
+ else
+ {
+ ike_cfg = ike_cfg_create(msg->add_conn.other.sendcert != CERT_NEVER_SEND,
+ msg->add_conn.force_encap, my_host, other_host);
+
+ if (msg->add_conn.algorithms.ike)
+ {
+ char *proposal_string;
+ char *strict = msg->add_conn.algorithms.ike + strlen(msg->add_conn.algorithms.ike) - 1;
+
+ if (*strict == '!')
+ *strict = '\0';
+ else
+ strict = NULL;
+
+ while ((proposal_string = strsep(&msg->add_conn.algorithms.ike, ",")))
+ {
+ proposal = proposal_create_from_string(PROTO_IKE, proposal_string);
+ if (proposal == NULL)
+ {
+ DBG1(DBG_CFG, "invalid IKE proposal string: %s", proposal_string);
+ my_id->destroy(my_id);
+ other_id->destroy(other_id);
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ DESTROY_IF(my_ca);
+ DESTROY_IF(other_ca);
+ ike_cfg->destroy(ike_cfg);
+ return;
+ }
+ ike_cfg->add_proposal(ike_cfg, proposal);
+ }
+ if (!strict)
+ {
+ proposal = proposal_create_default(PROTO_IKE);
+ ike_cfg->add_proposal(ike_cfg, proposal);
+ }
+ }
+ else
+ {
+ proposal = proposal_create_default(PROTO_IKE);
+ ike_cfg->add_proposal(ike_cfg, proposal);
+ }
+
+ u_int32_t rekey = 0, reauth = 0, over, jitter;
+ cert_validation_t valid;
+
+ jitter = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
+ over = msg->add_conn.rekey.margin;
+ if (msg->add_conn.rekey.reauth)
+ {
+ reauth = msg->add_conn.rekey.ike_lifetime - over;
+ }
+ else
+ {
+ rekey = msg->add_conn.rekey.ike_lifetime - over;
+ }
+
+ peer_cfg = peer_cfg_create(msg->add_conn.name,
+ msg->add_conn.ikev2 ? 2 : 1, ike_cfg, my_id, other_id,
+ msg->add_conn.me.sendcert, msg->add_conn.auth_method,
+ msg->add_conn.eap_type, msg->add_conn.eap_vendor,
+ msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
+ msg->add_conn.mobike,
+ msg->add_conn.dpd.delay, msg->add_conn.dpd.action, my_vip, other_vip,
+ msg->add_conn.p2p.mediation, mediated_by_cfg, peer_id);
+ auth = peer_cfg->get_auth(peer_cfg);
+ switch (msg->add_conn.crl_policy)
+ {
+ case CRL_STRICT_YES:
+ valid = VALIDATION_GOOD;
+ auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
+ break;
+ case CRL_STRICT_IFURI:
+ valid = VALIDATION_SKIPPED;
+ auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
+ break;
+ default:
+ break;
+ }
+
+ if (other_ca)
+ {
+ DBG1(DBG_CFG, " required other CA: %D", other_ca);
+ certificate_t *cert = charon->credentials->get_cert(charon->credentials,
+ CERT_X509, KEY_ANY, other_ca, TRUE);
+ if (!cert)
+ {
+ DBG1(DBG_CFG, "deleted connection '%s': "
+ "no trusted certificate found for required other CA",
+ msg->add_conn.name);
+ peer_cfg->destroy(peer_cfg);
+ other_ca->destroy(other_ca);
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ return;
+ }
+ /* require peer to authenticate against this cert */
+ auth->add_item(auth, AUTHZ_CA_CERT, cert);
+ cert->destroy(cert);
+ other_ca->destroy(other_ca);
+ }
+ if (my_ca)
+ {
+ certificate_t *cert = charon->credentials->get_cert(charon->credentials,
+ CERT_X509, KEY_ANY, my_ca, TRUE);
+ if (!cert)
+ {
+ DBG1(DBG_CFG, "deleted connection '%s': "
+ "no trusted certificate found for my CA",
+ msg->add_conn.name);
+ peer_cfg->destroy(peer_cfg);
+ my_ca->destroy(my_ca);
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ return;
+ }
+ /* we authenticate against this cert */
+ auth->add_item(auth, AUTHN_CA_CERT, cert);
+ cert->destroy(cert);
+ }
+ }
+ child_cfg = child_cfg_create(
+ msg->add_conn.name, msg->add_conn.rekey.ipsec_lifetime,
+ msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
+ msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100,
+ msg->add_conn.me.updown, msg->add_conn.me.hostaccess,
+ msg->add_conn.mode);
+
+ peer_cfg->add_child_cfg(peer_cfg, child_cfg);
+
+ child_cfg->add_traffic_selector(child_cfg, TRUE, my_ts);
+ child_cfg->add_traffic_selector(child_cfg, FALSE, other_ts);
+
+ if (msg->add_conn.algorithms.esp)
+ {
+ char *proposal_string;
+ char *strict = msg->add_conn.algorithms.esp + strlen(msg->add_conn.algorithms.esp) - 1;
+
+ if (*strict == '!')
+ *strict = '\0';
+ else
+ strict = NULL;
+
+ while ((proposal_string = strsep(&msg->add_conn.algorithms.esp, ",")))
+ {
+ proposal = proposal_create_from_string(PROTO_ESP, proposal_string);
+ if (proposal == NULL)
+ {
+ DBG1(DBG_CFG, "invalid ESP proposal string: %s", proposal_string);
+ peer_cfg->destroy(peer_cfg);
+ return;
+ }
+ child_cfg->add_proposal(child_cfg, proposal);
+ }
+ if (!strict)
+ {
+ proposal = proposal_create_default(PROTO_ESP);
+ child_cfg->add_proposal(child_cfg, proposal);
+ }
+ }
+ else
+ {
+ proposal = proposal_create_default(PROTO_ESP);
+ child_cfg->add_proposal(child_cfg, proposal);
+ }
+
+ if (!use_existing)
+ {
+ /* add config to backend */
+ this->configs.mutex->lock(this->configs.mutex);
+ this->configs.list->insert_last(this->configs.list, peer_cfg);
+ this->configs.mutex->unlock(this->configs.mutex);
+ DBG1(DBG_CFG, "added configuration '%s': %H[%D]...%H[%D]",
+ msg->add_conn.name, my_host, my_id, other_host, other_id);
+ }
+ return;
+
+ /* mopping up after parsing errors */
+
+destroy_ids:
+ my_id->destroy(my_id);
+ other_id->destroy(other_id);
+ DESTROY_IF(mediated_by_cfg);
+ DESTROY_IF(peer_id);
+
+destroy_hosts:
+ my_host->destroy(my_host);
+ other_host->destroy(other_host);
+}
+
+/**
+ * Delete a connection from the list
+ */
+static void stroke_del_conn(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ enumerator_t *enumerator, *children;
+ peer_cfg_t *peer;
+ child_cfg_t *child;
+
+ pop_string(msg, &(msg->del_conn.name));
+ DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name);
+
+ this->configs.mutex->lock(this->configs.mutex);
+ enumerator = this->configs.list->create_enumerator(this->configs.list);
+ while (enumerator->enumerate(enumerator, (void**)&peer))
+ {
+ /* remove peer config with such a name */
+ if (streq(peer->get_name(peer), msg->del_conn.name))
+ {
+ this->configs.list->remove_at(this->configs.list, enumerator);
+ peer->destroy(peer);
+ continue;
+ }
+ /* remove any child with such a name */
+ children = peer->create_child_cfg_enumerator(peer);
+ while (children->enumerate(children, &child))
+ {
+ if (streq(child->get_name(child), msg->del_conn.name))
+ {
+ peer->remove_child_cfg(peer, enumerator);
+ child->destroy(child);
+ }
+ }
+ children->destroy(children);
+ }
+ enumerator->destroy(enumerator);
+ this->configs.mutex->unlock(this->configs.mutex);
+
+ fprintf(out, "deleted connection '%s'\n", msg->del_conn.name);
+}
+
+/**
+ * get the child_cfg with the same name as the peer cfg
+ */
+static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
+{
+ child_cfg_t *current, *found = NULL;
+ enumerator_t *enumerator;
+
+ enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (streq(current->get_name(current), name))
+ {
+ found = current;
+ found->get_ref(found);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
+ * logging to the stroke interface
+ */
+static bool stroke_log(stroke_log_info_t *info, signal_t signal, level_t level,
+ ike_sa_t *ike_sa, char *format, va_list args)
+{
+ if (level <= info->level)
+ {
+ if (vfprintf(info->out, format, args) < 0 ||
+ fprintf(info->out, "\n") < 0 ||
+ fflush(info->out) != 0)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * initiate a connection by name
+ */
+static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ peer_cfg_t *peer_cfg;
+ child_cfg_t *child_cfg;
+ stroke_log_info_t info;
+
+ pop_string(msg, &(msg->initiate.name));
+ DBG1(DBG_CFG, "received stroke: initiate '%s'", msg->initiate.name);
+
+ peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
+ msg->initiate.name);
+ if (peer_cfg == NULL)
+ {
+ fprintf(out, "no config named '%s'\n", msg->initiate.name);
+ return;
+ }
+ if (peer_cfg->get_ike_version(peer_cfg) != 2)
+ {
+ DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
+ peer_cfg->get_ike_version(peer_cfg));
+ peer_cfg->destroy(peer_cfg);
+ return;
+ }
+
+ child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
+ if (child_cfg == NULL)
+ {
+ fprintf(out, "no child config named '%s'\n", msg->initiate.name);
+ peer_cfg->destroy(peer_cfg);
+ return;
+ }
+
+ if (msg->output_verbosity < 0)
+ {
+ charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+ NULL, NULL);
+ }
+ else
+ {
+ info.out = out;
+ info.level = msg->output_verbosity;
+ charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+ (controller_cb_t)stroke_log, &info);
+ }
+}
+
+/**
+ * route a policy (install SPD entries)
+ */
+static void stroke_route(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ peer_cfg_t *peer_cfg;
+ child_cfg_t *child_cfg;
+ stroke_log_info_t info;
+
+ pop_string(msg, &(msg->route.name));
+ DBG1(DBG_CFG, "received stroke: route '%s'", msg->route.name);
+
+ peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
+ msg->route.name);
+ if (peer_cfg == NULL)
+ {
+ fprintf(out, "no config named '%s'\n", msg->route.name);
+ return;
+ }
+ if (peer_cfg->get_ike_version(peer_cfg) != 2)
+ {
+ peer_cfg->destroy(peer_cfg);
+ return;
+ }
+
+ child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
+ if (child_cfg == NULL)
+ {
+ fprintf(out, "no child config named '%s'\n", msg->route.name);
+ peer_cfg->destroy(peer_cfg);
+ return;
+ }
+
+ info.out = out;
+ info.level = msg->output_verbosity;
+ charon->controller->route(charon->controller, peer_cfg, child_cfg,
+ (controller_cb_t)stroke_log, &info);
+ peer_cfg->destroy(peer_cfg);
+ child_cfg->destroy(child_cfg);
+}
+
+/**
+ * unroute a policy
+ */
+static void stroke_unroute(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ char *name;
+ ike_sa_t *ike_sa;
+ iterator_t *iterator;
+ stroke_log_info_t info;
+
+ pop_string(msg, &(msg->terminate.name));
+ name = msg->terminate.name;
+
+ info.out = out;
+ info.level = msg->output_verbosity;
+
+ iterator = charon->controller->create_ike_sa_iterator(charon->controller);
+ while (iterator->iterate(iterator, (void**)&ike_sa))
+ {
+ child_sa_t *child_sa;
+ iterator_t *children;
+ u_int32_t id;
+
+ children = ike_sa->create_child_sa_iterator(ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (child_sa->get_state(child_sa) == CHILD_ROUTED &&
+ streq(name, child_sa->get_name(child_sa)))
+ {
+ id = child_sa->get_reqid(child_sa);
+ children->destroy(children);
+ iterator->destroy(iterator);
+ charon->controller->unroute(charon->controller, id,
+ (controller_cb_t)stroke_log, &info);
+ return;
+ }
+ }
+ children->destroy(children);
+ }
+ iterator->destroy(iterator);
+ DBG1(DBG_CFG, "no such SA found");
+}
+
+/**
+ * terminate a connection by name
+ */
+static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ char *string, *pos = NULL, *name = NULL;
+ u_int32_t id = 0;
+ bool child;
+ int len;
+ ike_sa_t *ike_sa;
+ iterator_t *iterator;
+ stroke_log_info_t info;
+
+ pop_string(msg, &(msg->terminate.name));
+ string = msg->terminate.name;
+ DBG1(DBG_CFG, "received stroke: terminate '%s'", string);
+
+ len = strlen(string);
+ if (len < 1)
+ {
+ DBG1(DBG_CFG, "error parsing string");
+ return;
+ }
+ switch (string[len-1])
+ {
+ case '}':
+ child = TRUE;
+ pos = strchr(string, '{');
+ break;
+ case ']':
+ child = FALSE;
+ pos = strchr(string, '[');
+ break;
+ default:
+ name = string;
+ child = FALSE;
+ break;
+ }
+
+ if (name)
+ {
+ /* is a single name */
+ }
+ else if (pos == string + len - 2)
+ { /* is name[] or name{} */
+ string[len-2] = '\0';
+ name = string;
+ }
+ else
+ { /* is name[123] or name{23} */
+ string[len-1] = '\0';
+ id = atoi(pos + 1);
+ if (id == 0)
+ {
+ DBG1(DBG_CFG, "error parsing string");
+ return;
+ }
+ }
+
+ info.out = out;
+ info.level = msg->output_verbosity;
+
+ iterator = charon->controller->create_ike_sa_iterator(charon->controller);
+ while (iterator->iterate(iterator, (void**)&ike_sa))
+ {
+ child_sa_t *child_sa;
+ iterator_t *children;
+
+ if (child)
+ {
+ children = ike_sa->create_child_sa_iterator(ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if ((name && streq(name, child_sa->get_name(child_sa))) ||
+ (id && id == child_sa->get_reqid(child_sa)))
+ {
+ id = child_sa->get_reqid(child_sa);
+ children->destroy(children);
+ iterator->destroy(iterator);
+
+ charon->controller->terminate_child(charon->controller, id,
+ (controller_cb_t)stroke_log, &info);
+ return;
+ }
+ }
+ children->destroy(children);
+ }
+ else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
+ (id && id == ike_sa->get_unique_id(ike_sa)))
+ {
+ id = ike_sa->get_unique_id(ike_sa);
+ /* unlock manager first */
+ iterator->destroy(iterator);
+
+ charon->controller->terminate_ike(charon->controller, id,
+ (controller_cb_t)stroke_log, &info);
+ return;
+ }
+
+ }
+ iterator->destroy(iterator);
+ DBG1(DBG_CFG, "no such SA found");
+}
+
+/**
+ * Add a ca information record to the cainfo list
+ */
+static void stroke_add_ca(private_stroke_t *this,
+ stroke_msg_t *msg, FILE *out)
+{
+ certificate_t *cert;
+ ca_section_t *ca;
+
+ pop_string(msg, &msg->add_ca.name);
+ pop_string(msg, &msg->add_ca.cacert);
+ pop_string(msg, &msg->add_ca.crluri);
+ pop_string(msg, &msg->add_ca.crluri2);
+ pop_string(msg, &msg->add_ca.ocspuri);
+ pop_string(msg, &msg->add_ca.ocspuri2);
+
+ DBG1(DBG_CFG, "received stroke: add ca '%s'", msg->add_ca.name);
+
+ DBG2(DBG_CFG, "ca %s", msg->add_ca.name);
+ DBG2(DBG_CFG, " cacert=%s", msg->add_ca.cacert);
+ DBG2(DBG_CFG, " crluri=%s", msg->add_ca.crluri);
+ DBG2(DBG_CFG, " crluri2=%s", msg->add_ca.crluri2);
+ DBG2(DBG_CFG, " ocspuri=%s", msg->add_ca.ocspuri);
+ DBG2(DBG_CFG, " ocspuri2=%s", msg->add_ca.ocspuri2);
+
+ if (msg->add_ca.cacert == NULL)
+ {
+ DBG1(DBG_CFG, "missing cacert parameter");
+ return;
+ }
+
+ cert = load_ca_cert(this, msg->add_ca.cacert);
+ if (cert)
+ {
+ ca = ca_section_create(msg->add_ca.name, cert);
+ if (msg->add_ca.crluri)
+ {
+ ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri));
+ }
+ if (msg->add_ca.crluri2)
+ {
+ ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2));
+ }
+ if (msg->add_ca.ocspuri)
+ {
+ ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri));
+ }
+ if (msg->add_ca.ocspuri2)
+ {
+ ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
+ }
+ this->ca_creds.mutex->lock(this->ca_creds.mutex);
+ this->ca_creds.sections->insert_last(this->ca_creds.sections, ca);
+ this->ca_creds.mutex->unlock(this->ca_creds.mutex);
+ DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name);
+ }
+}
+
+/**
+ * Delete a ca information record from the cainfo list
+ */
+static void stroke_del_ca(private_stroke_t *this,
+ stroke_msg_t *msg, FILE *out)
+{
+ enumerator_t *enumerator;
+ ca_section_t *ca = NULL;
+
+ pop_string(msg, &(msg->del_ca.name));
+ DBG1(DBG_CFG, "received stroke: delete ca '%s'", msg->del_ca.name);
+
+ this->ca_creds.mutex->lock(this->ca_creds.mutex);
+ enumerator = this->ca_creds.sections->create_enumerator(this->ca_creds.sections);
+ while (enumerator->enumerate(enumerator, &ca))
+ {
+ if (streq(ca->name, msg->del_ca.name))
+ {
+ this->ca_creds.sections->remove_at(this->ca_creds.sections, enumerator);
+ break;
+ }
+ ca = NULL;
+ }
+ enumerator->destroy(enumerator);
+ this->ca_creds.mutex->unlock(this->ca_creds.mutex);
+ if (ca == NULL)
+ {
+ fprintf(out, "no ca named '%s' found\n", msg->del_ca.name);
+ return;
+ }
+ ca_section_destroy(ca);
+ /* TODO: flush cached certs */
+}
+
+/**
+ * log an IKE_SA to out
+ */
+static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all)
+{
+ ike_sa_id_t *id = ike_sa->get_id(ike_sa);
+ u_int32_t rekey, reauth;
+
+ fprintf(out, "%12s[%d]: %N, %H[%D]...%H[%D]\n",
+ ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa),
+ ike_sa_state_names, ike_sa->get_state(ike_sa),
+ ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa),
+ ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa));
+
+ if (all)
+ {
+ fprintf(out, "%12s[%d]: IKE SPIs: %.16llx_i%s %.16llx_r%s",
+ ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa),
+ id->get_initiator_spi(id), id->is_initiator(id) ? "*" : "",
+ id->get_responder_spi(id), id->is_initiator(id) ? "" : "*");
+
+ rekey = ike_sa->get_statistic(ike_sa, STAT_REKEY_TIME);
+ reauth = ike_sa->get_statistic(ike_sa, STAT_REAUTH_TIME);
+ if (rekey)
+ {
+ fprintf(out, ", rekeying in %V", &rekey);
+ }
+ if (reauth)
+ {
+ fprintf(out, ", reauthentication in %V", &reauth);
+ }
+ if (!rekey && !reauth)
+ {
+ fprintf(out, ", rekeying disabled");
+ }
+ fprintf(out, "\n");
+ }
+}
+
+/**
+ * log an CHILD_SA to out
+ */
+static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all)
+{
+ u_int32_t rekey, now = time(NULL);
+ u_int32_t use_in, use_out, use_fwd;
+ encryption_algorithm_t encr_alg;
+ integrity_algorithm_t int_alg;
+ size_t encr_len, int_len;
+ mode_t mode;
+
+ child_sa->get_stats(child_sa, &mode, &encr_alg, &encr_len,
+ &int_alg, &int_len, &rekey, &use_in, &use_out,
+ &use_fwd);
+
+ fprintf(out, "%12s{%d}: %N, %N",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ child_sa_state_names, child_sa->get_state(child_sa),
+ mode_names, mode);
+
+ if (child_sa->get_state(child_sa) == CHILD_INSTALLED)
+ {
+ fprintf(out, ", %N SPIs: %.8x_i %.8x_o",
+ protocol_id_names, child_sa->get_protocol(child_sa),
+ htonl(child_sa->get_spi(child_sa, TRUE)),
+ htonl(child_sa->get_spi(child_sa, FALSE)));
+
+ if (all)
+ {
+ fprintf(out, "\n%12s{%d}: ", child_sa->get_name(child_sa),
+ child_sa->get_reqid(child_sa));
+
+
+ if (child_sa->get_protocol(child_sa) == PROTO_ESP)
+ {
+ fprintf(out, "%N", encryption_algorithm_names, encr_alg);
+
+ if (encr_len)
+ {
+ fprintf(out, "-%d", encr_len);
+ }
+ fprintf(out, "/");
+ }
+
+ fprintf(out, "%N", integrity_algorithm_names, int_alg);
+ if (int_len)
+ {
+ fprintf(out, "-%d", int_len);
+ }
+ fprintf(out, ", rekeying ");
+
+ if (rekey)
+ {
+ fprintf(out, "in %#V", &now, &rekey);
+ }
+ else
+ {
+ fprintf(out, "disabled");
+ }
+
+ fprintf(out, ", last use: ");
+ use_in = max(use_in, use_fwd);
+ if (use_in)
+ {
+ fprintf(out, "%ds_i ", now - use_in);
+ }
+ else
+ {
+ fprintf(out, "no_i ");
+ }
+ if (use_out)
+ {
+ fprintf(out, "%ds_o ", now - use_out);
+ }
+ else
+ {
+ fprintf(out, "no_o ");
+ }
+ }
+ }
+
+ fprintf(out, "\n%12s{%d}: %#R=== %#R\n",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ child_sa->get_traffic_selectors(child_sa, TRUE),
+ child_sa->get_traffic_selectors(child_sa, FALSE));
+}
+
+/**
+ * show status of daemon
+ */
+static void stroke_status(private_stroke_t *this, stroke_msg_t *msg, FILE *out,
+ bool all)
+{
+ enumerator_t *enumerator, *children;
+ iterator_t *iterator;
+ host_t *host;
+ peer_cfg_t *peer_cfg;
+ ike_cfg_t *ike_cfg;
+ child_cfg_t *child_cfg;
+ ike_sa_t *ike_sa;
+ char *name = NULL;
+
+ if (msg->status.name)
+ {
+ pop_string(msg, &(msg->status.name));
+ name = msg->status.name;
+ }
+
+ if (all)
+ {
+ fprintf(out, "Performance:\n");
+ fprintf(out, " worker threads: %d idle of %d,",
+ charon->processor->get_idle_threads(charon->processor),
+ charon->processor->get_total_threads(charon->processor));
+ fprintf(out, " job queue load: %d,",
+ charon->processor->get_job_load(charon->processor));
+ fprintf(out, " scheduled events: %d\n",
+ charon->scheduler->get_job_load(charon->scheduler));
+ iterator = charon->kernel_interface->create_address_iterator(
+ charon->kernel_interface);
+ fprintf(out, "Listening IP addresses:\n");
+ while (iterator->iterate(iterator, (void**)&host))
+ {
+ fprintf(out, " %H\n", host);
+ }
+ iterator->destroy(iterator);
+
+ fprintf(out, "Connections:\n");
+ enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends);
+ while (enumerator->enumerate(enumerator, (void**)&peer_cfg))
+ {
+ if (peer_cfg->get_ike_version(peer_cfg) != 2 ||
+ (name && !streq(name, peer_cfg->get_name(peer_cfg))))
+ {
+ continue;
+ }
+
+ ike_cfg = peer_cfg->get_ike_cfg(peer_cfg);
+ fprintf(out, "%12s: %H[%D]...%H[%D]\n", peer_cfg->get_name(peer_cfg),
+ ike_cfg->get_my_host(ike_cfg), peer_cfg->get_my_id(peer_cfg),
+ ike_cfg->get_other_host(ike_cfg), peer_cfg->get_other_id(peer_cfg));
+ /* TODO: list CAs and groups */
+ children = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+ while (children->enumerate(children, &child_cfg))
+ {
+ linked_list_t *my_ts, *other_ts;
+ my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
+ other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL);
+ fprintf(out, "%12s: %#R=== %#R\n", child_cfg->get_name(child_cfg),
+ my_ts, other_ts);
+ my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+ }
+ children->destroy(children);
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager);
+ if (all && iterator->get_count(iterator) > 0)
+ {
+ fprintf(out, "Security Associations:\n");
+ }
+ while (iterator->iterate(iterator, (void**)&ike_sa))
+ {
+ bool ike_printed = FALSE;
+ child_sa_t *child_sa;
+ iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa);
+
+ if (name == NULL || streq(name, ike_sa->get_name(ike_sa)))
+ {
+ log_ike_sa(out, ike_sa, all);
+ ike_printed = TRUE;
+ }
+
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (name == NULL || streq(name, child_sa->get_name(child_sa)))
+ {
+ if (!ike_printed)
+ {
+ log_ike_sa(out, ike_sa, all);
+ ike_printed = TRUE;
+ }
+ log_child_sa(out, child_sa, all);
+ }
+ }
+ children->destroy(children);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * list all X.509 certificates matching the flags
+ */
+static void stroke_list_certs(char *label, x509_flag_t flags, bool utc, FILE *out)
+{
+ bool first = TRUE;
+ time_t now = time(NULL);
+ certificate_t *cert;
+ enumerator_t *enumerator;
+
+ enumerator = charon->credentials->create_cert_enumerator(
+ charon->credentials, CERT_X509, KEY_ANY, NULL, FALSE);
+ while (enumerator->enumerate(enumerator, (void**)&cert))
+ {
+ x509_t *x509 = (x509_t*)cert;
+ x509_flag_t x509_flags = x509->get_flags(x509);
+
+ if (x509_flags & flags)
+ {
+ enumerator_t *enumerator;
+ identification_t *altName;
+ bool first_altName = TRUE;
+ chunk_t serial = x509->get_serial(x509);
+ identification_t *authkey = x509->get_authKeyIdentifier(x509);
+ time_t notBefore, notAfter;
+ public_key_t *public = cert->get_public_key(cert);
+
+ if (first)
+ {
+ fprintf(out, "\n");
+ fprintf(out, "List of %s:\n", label);
+ first = FALSE;
+ }
+ fprintf(out, "\n");
+
+ /* list subjectAltNames */
+ enumerator = x509->create_subjectAltName_enumerator(x509);
+ while (enumerator->enumerate(enumerator, (void**)&altName))
+ {
+ if (first_altName)
+ {
+ fprintf(out, " altNames: ");
+ first_altName = FALSE;
+ }
+ else
+ {
+ fprintf(out, ", ");
+ }
+ fprintf(out, "%D", altName);
+ }
+ if (!first_altName)
+ {
+ fprintf(out, "\n");
+ }
+ enumerator->destroy(enumerator);
+
+ fprintf(out, " subject: %D\n", cert->get_subject(cert));
+ fprintf(out, " issuer: %D\n", cert->get_issuer(cert));
+ fprintf(out, " serial: %#B\n", &serial);
+
+ /* list validity */
+ cert->get_validity(cert, &now, &notBefore, &notAfter);
+ fprintf(out, " validity: not before %#T, ", &notBefore, utc);
+ if (now < notBefore)
+ {
+ fprintf(out, "not valid yet (valid in %#V)\n", &now, &notBefore);
+ }
+ else
+ {
+ fprintf(out, "ok\n");
+ }
+ fprintf(out, " not after %#T, ", &notAfter, utc);
+ if (now > notAfter)
+ {
+ fprintf(out, "expired (%#V ago)\n", &now, &notAfter);
+ }
+ else
+ {
+ fprintf(out, "ok");
+ if (now > notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24)
+ {
+ fprintf(out, " (expires in %#V)", &now, &notAfter);
+ }
+ fprintf(out, " \n");
+ }
+
+ /* list public key information */
+ if (public)
+ {
+ private_key_t *private = NULL;
+ identification_t *id, *keyid;
+
+ id = public->get_id(public, ID_PUBKEY_SHA1);
+ keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1);
+
+ if (flags & X509_PEER)
+ {
+ private = charon->credentials->get_private(
+ charon->credentials,
+ public->get_type(public), id, NULL);
+ }
+ fprintf(out, " pubkey: %N %d bits%s\n",
+ key_type_names, public->get_type(public),
+ public->get_keysize(public) * 8,
+ private ? ", has private key" : "");
+ fprintf(out, " keyid: %D\n", keyid);
+ fprintf(out, " subjkey: %D\n", id);
+ DESTROY_IF(private);
+ public->destroy(public);
+ }
+
+ /* list optional authorityKeyIdentifier */
+ if (authkey)
+ {
+ fprintf(out, " authkey: %D\n", authkey);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * list all X.509 CRLs
+ */
+static void stroke_list_crls(bool utc, FILE *out)
+{
+ bool first = TRUE;
+ time_t thisUpdate, nextUpdate, now = time(NULL);
+ certificate_t *cert;
+ enumerator_t *enumerator;
+
+ enumerator = charon->credentials->create_cert_enumerator(
+ charon->credentials, CERT_X509_CRL, KEY_ANY, NULL, FALSE);
+ while (enumerator->enumerate(enumerator, (void**)&cert))
+ {
+ crl_t *crl = (crl_t*)cert;
+ chunk_t serial = crl->get_serial(crl);
+ identification_t *authkey = crl->get_authKeyIdentifier(crl);
+
+ if (first)
+ {
+ fprintf(out, "\n");
+ fprintf(out, "List of X.509 CRLs:\n");
+ first = FALSE;
+ }
+ fprintf(out, "\n");
+
+ fprintf(out, " issuer: %D\n", cert->get_issuer(cert));
+
+ /* list optional crlNumber */
+ if (serial.ptr)
+ {
+ fprintf(out, " serial: %#B\n", &serial);
+ }
+
+ /* count the number of revoked certificates */
+ {
+ int count = 0;
+ enumerator_t *enumerator = crl->create_enumerator(crl);
+
+ while (enumerator->enumerate(enumerator, NULL, NULL, NULL))
+ {
+ count++;
+ }
+ fprintf(out, " revoked: %d certificate%s\n", count,
+ (count == 1)? "" : "s");
+ enumerator->destroy(enumerator);
+ }
+
+ /* list validity */
+ cert->get_validity(cert, &now, &thisUpdate, &nextUpdate);
+ fprintf(out, " updates: this %#T\n", &thisUpdate, utc);
+ fprintf(out, " next %#T, ", &nextUpdate, utc);
+ if (now > nextUpdate)
+ {
+ fprintf(out, "expired (%#V ago)\n", &now, &nextUpdate);
+ }
+ else
+ {
+ fprintf(out, "ok");
+ if (now > nextUpdate - CRL_WARNING_INTERVAL * 60 * 60 * 24)
+ {
+ fprintf(out, " (expires in %#V)", &now, &nextUpdate);
+ }
+ fprintf(out, " \n");
+ }
+
+ /* list optional authorityKeyIdentifier */
+ if (authkey)
+ {
+ fprintf(out, " authkey: %D\n", authkey);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * list all CA information sections
+ */
+static void stroke_list_cainfos(private_stroke_t *this, FILE *out)
+{
+ bool first = TRUE;
+ ca_section_t *section;
+ enumerator_t *enumerator;
+
+ this->ca_creds.mutex->lock(this->ca_creds.mutex);
+ enumerator = this->ca_creds.sections->create_enumerator(this->ca_creds.sections);
+ while (enumerator->enumerate(enumerator, (void**)&section))
+ {
+ certificate_t *cert = section->cert;
+ public_key_t *public = cert->get_public_key(cert);
+
+ if (first)
+ {
+ fprintf(out, "\n");
+ fprintf(out, "List of CA Information Sections:\n");
+ first = FALSE;
+ }
+ fprintf(out, "\n");
+ fprintf(out, " authname: %D\n", cert->get_subject(cert));
+
+ /* list authkey and keyid */
+ if (public)
+ {
+ fprintf(out, " authkey: %D\n",
+ public->get_id(public, ID_PUBKEY_SHA1));
+ fprintf(out, " keyid: %D\n",
+ public->get_id(public, ID_PUBKEY_INFO_SHA1));
+ public->destroy(public);
+ }
+
+ /* list CRL URIs */
+ {
+ bool first = TRUE;
+ char *crluri;
+ enumerator_t *enumerator;
+
+ enumerator = section->crl->create_enumerator(section->crl);
+ while (enumerator->enumerate(enumerator, (void**)&crluri))
+ {
+ if (first)
+ {
+ fprintf(out, " crluris: ");
+ first = FALSE;
+ }
+ else
+ {
+ fprintf(out, " ");
+ }
+ fprintf(out, "'%s'\n", crluri);
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* list OCSP URIs */
+ {
+ bool first = TRUE;
+ char *ocspuri;
+ enumerator_t *enumerator;
+
+ enumerator = section->ocsp->create_enumerator(section->ocsp);
+ while (enumerator->enumerate(enumerator, (void**)&ocspuri))
+ {
+ if (first)
+ {
+ fprintf(out, " ocspuris: ");
+ first = FALSE;
+ }
+ else
+ {
+ fprintf(out, " ");
+ }
+ fprintf(out, "'%s'\n", ocspuri);
+ }
+ enumerator->destroy(enumerator);
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->ca_creds.mutex->unlock(this->ca_creds.mutex);
+}
+
+/**
+ * list various information
+ */
+static void stroke_list(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ if (msg->list.flags & LIST_CERTS)
+ {
+ stroke_list_certs("X.509 End Entity Certificates",
+ X509_PEER, msg->list.utc, out);
+ }
+ if (msg->list.flags & LIST_CACERTS)
+ {
+ stroke_list_certs("X.509 CA Certificates",
+ X509_CA, msg->list.utc, out);
+ }
+ if (msg->list.flags & LIST_OCSPCERTS)
+ {
+ stroke_list_certs("X.509 OCSP Signer Certificates",
+ X509_OCSP_SIGNER, msg->list.utc, out);
+ }
+ if (msg->list.flags & LIST_AACERTS)
+ {
+ stroke_list_certs("X.509 AA Certificates",
+ X509_AA, msg->list.utc, out);
+ }
+ if (msg->list.flags & LIST_ACERTS)
+ {
+
+ }
+ if (msg->list.flags & LIST_CAINFOS)
+ {
+ stroke_list_cainfos(this, out);
+ }
+ if (msg->list.flags & LIST_CRLS)
+ {
+ stroke_list_crls(msg->list.utc, out);
+ }
+ if (msg->list.flags & LIST_OCSP)
+ {
+
+ }
+}
+
+/**
+ * reread various information
+ */
+static void stroke_reread(private_stroke_t *this,
+ stroke_msg_t *msg, FILE *out)
+{
+ if (msg->reread.flags & REREAD_SECRETS)
+ {
+ DBG1(DBG_CFG, "rereading secrets");
+ load_secrets(this);
+ }
+ if (msg->reread.flags & REREAD_CACERTS)
+ {
+ DBG1(DBG_CFG, "rereading CA certificates from '%s'",
+ CA_CERTIFICATE_DIR);
+ load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA);
+ }
+ if (msg->reread.flags & REREAD_OCSPCERTS)
+ {
+ DBG1(DBG_CFG, "rereading OCSP signer certificates from '%s'",
+ OCSP_CERTIFICATE_DIR);
+ load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509,
+ X509_OCSP_SIGNER);
+ }
+ if (msg->reread.flags & REREAD_AACERTS)
+ {
+ DBG1(DBG_CFG, "rereading AA certificates from '%s'",
+ AA_CERTIFICATE_DIR);
+ load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA);
+ }
+ if (msg->reread.flags & REREAD_ACERTS)
+ {
+ DBG1(DBG_CFG, "rereading attribute certificates from '%s'",
+ ATTR_CERTIFICATE_DIR);
+ load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0);
+ }
+ if (msg->reread.flags & REREAD_CRLS)
+ {
+ DBG1(DBG_CFG, "rereading CRLs from '%s'",
+ CRL_DIR);
+ load_certdir(this, CRL_DIR, CERT_X509_CRL, 0);
+ }
+}
+
+/**
+ * purge various information
+ */
+static void stroke_purge(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ /* TODO: flush cache */
+}
+
+signal_t get_signal_from_logtype(char *type)
+{
+ if (strcasecmp(type, "any") == 0) return SIG_ANY;
+ else if (strcasecmp(type, "mgr") == 0) return DBG_MGR;
+ else if (strcasecmp(type, "ike") == 0) return DBG_IKE;
+ else if (strcasecmp(type, "chd") == 0) return DBG_CHD;
+ else if (strcasecmp(type, "job") == 0) return DBG_JOB;
+ else if (strcasecmp(type, "cfg") == 0) return DBG_CFG;
+ else if (strcasecmp(type, "knl") == 0) return DBG_KNL;
+ else if (strcasecmp(type, "net") == 0) return DBG_NET;
+ else if (strcasecmp(type, "enc") == 0) return DBG_ENC;
+ else if (strcasecmp(type, "lib") == 0) return DBG_LIB;
+ else return -1;
+}
+
+/**
+ * set the verbosity debug output
+ */
+static void stroke_loglevel(private_stroke_t *this, stroke_msg_t *msg, FILE *out)
+{
+ signal_t signal;
+
+ pop_string(msg, &(msg->loglevel.type));
+ DBG1(DBG_CFG, "received stroke: loglevel %d for %s",
+ msg->loglevel.level, msg->loglevel.type);
+
+ signal = get_signal_from_logtype(msg->loglevel.type);
+ if (signal < 0)
+ {
+ fprintf(out, "invalid type (%s)!\n", msg->loglevel.type);
+ return;
+ }
+
+ charon->outlog->set_level(charon->outlog, signal, msg->loglevel.level);
+ charon->syslog->set_level(charon->syslog, signal, msg->loglevel.level);
+}
+
+typedef struct stroke_job_context_t stroke_job_context_t;
+
+/** job context to pass to processing thread */
+struct stroke_job_context_t {
+
+ /** file descriptor to read from */
+ int fd;
+
+ /** global stroke interface */
+ private_stroke_t *this;
+};
+
+/**
+ * destroy a job context
+ */
+static void stroke_job_context_destroy(stroke_job_context_t *this)
+{
+ close(this->fd);
+ free(this);
+}
+
+/**
+ * process a stroke request from the socket pointed by "fd"
+ */
+static job_requeue_t stroke_process(stroke_job_context_t *ctx)
+{
+ stroke_msg_t *msg;
+ u_int16_t msg_length;
+ ssize_t bytes_read;
+ FILE *out;
+ private_stroke_t *this = ctx->this;
+ int strokefd = ctx->fd;
+
+ /* peek the length */
+ bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK);
+ if (bytes_read != sizeof(msg_length))
+ {
+ DBG1(DBG_CFG, "reading length of stroke message failed: %s",
+ strerror(errno));
+ close(strokefd);
+ return JOB_REQUEUE_NONE;
+ }
+
+ /* read message */
+ msg = malloc(msg_length);
+ bytes_read = recv(strokefd, msg, msg_length, 0);
+ if (bytes_read != msg_length)
+ {
+ DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno));
+ close(strokefd);
+ return JOB_REQUEUE_NONE;
+ }
+
+ out = fdopen(strokefd, "w");
+ if (out == NULL)
+ {
+ DBG1(DBG_CFG, "opening stroke output channel failed: %s", strerror(errno));
+ close(strokefd);
+ free(msg);
+ return JOB_REQUEUE_NONE;
+ }
+
+ DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length);
+
+ /* the stroke_* functions are blocking, as they listen on the bus. Add
+ * cancellation handlers. */
+ pthread_cleanup_push((void*)fclose, out);
+ pthread_cleanup_push(free, msg);
+
+ switch (msg->type)
+ {
+ case STR_INITIATE:
+ stroke_initiate(this, msg, out);
+ break;
+ case STR_ROUTE:
+ stroke_route(this, msg, out);
+ break;
+ case STR_UNROUTE:
+ stroke_unroute(this, msg, out);
+ break;
+ case STR_TERMINATE:
+ stroke_terminate(this, msg, out);
+ break;
+ case STR_STATUS:
+ stroke_status(this, msg, out, FALSE);
+ break;
+ case STR_STATUS_ALL:
+ stroke_status(this, msg, out, TRUE);
+ break;
+ case STR_ADD_CONN:
+ stroke_add_conn(this, msg, out);
+ break;
+ case STR_DEL_CONN:
+ stroke_del_conn(this, msg, out);
+ break;
+ case STR_ADD_CA:
+ stroke_add_ca(this, msg, out);
+ break;
+ case STR_DEL_CA:
+ stroke_del_ca(this, msg, out);
+ break;
+ case STR_LOGLEVEL:
+ stroke_loglevel(this, msg, out);
+ break;
+ case STR_LIST:
+ stroke_list(this, msg, out);
+ break;
+ case STR_REREAD:
+ stroke_reread(this, msg, out);
+ break;
+ case STR_PURGE:
+ stroke_purge(this, msg, out);
+ break;
+ default:
+ DBG1(DBG_CFG, "received unknown stroke");
+ }
+ /* remove and execute cancellation handlers */
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Implementation of private_stroke_t.stroke_receive.
+ */
+static job_requeue_t stroke_receive(private_stroke_t *this)
+{
+ struct sockaddr_un strokeaddr;
+ int strokeaddrlen = sizeof(strokeaddr);
+ int strokefd;
+ int oldstate;
+ callback_job_t *job;
+ stroke_job_context_t *ctx;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+ strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
+ pthread_setcancelstate(oldstate, NULL);
+
+ if (strokefd < 0)
+ {
+ DBG1(DBG_CFG, "accepting stroke connection failed: %s", strerror(errno));
+ return JOB_REQUEUE_FAIR;
+ }
+
+ ctx = malloc_thing(stroke_job_context_t);
+ ctx->fd = strokefd;
+ ctx->this = this;
+ job = callback_job_create((callback_job_cb_t)stroke_process,
+ ctx, (void*)stroke_job_context_destroy, this->job);
+ charon->processor->queue_job(charon->processor, (job_t*)job);
+
+ return JOB_REQUEUE_FAIR;
+}
+
+/**
+ * Implementation of interface_t.destroy.
+ */
+static void destroy(private_stroke_t *this)
+{
+ this->job->cancel(this->job);
+ charon->credentials->remove_set(charon->credentials, &this->ca_creds.set);
+ charon->credentials->remove_set(charon->credentials, &this->creds.set);
+ charon->backends->remove_backend(charon->backends, &this->configs.backend);
+ this->ca_creds.sections->destroy_function(this->ca_creds.sections, (void*)ca_section_destroy);
+ this->ca_creds.mutex->destroy(this->ca_creds.mutex);
+ this->creds.certs->destroy_offset(this->creds.certs, offsetof(certificate_t, destroy));
+ this->creds.shared->destroy_offset(this->creds.shared, offsetof(shared_key_t, destroy));
+ this->creds.private->destroy_offset(this->creds.private, offsetof(private_key_t, destroy));
+ this->creds.mutex->destroy(this->creds.mutex);
+ this->configs.list->destroy_offset(this->configs.list, offsetof(peer_cfg_t, destroy));
+ this->configs.mutex->destroy(this->configs.mutex);
+ free(this);
+}
+
+/**
+ * initialize and open stroke socket
+ */
+static bool open_socket(private_stroke_t *this)
+{
+ struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET};
+ mode_t old;
+
+ /* set up unix socket */
+ this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (this->socket == -1)
+ {
+ DBG1(DBG_CFG, "could not create stroke socket");
+ return FALSE;
+ }
+
+ unlink(socket_addr.sun_path);
+ old = umask(~(S_IRWXU | S_IRWXG));
+ if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
+ {
+ DBG1(DBG_CFG, "could not bind stroke socket: %s", strerror(errno));
+ close(this->socket);
+ return FALSE;
+ }
+ umask(old);
+ if (chown(socket_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0)
+ {
+ DBG1(DBG_CFG, "changing stroke socket permissions failed: %s",
+ strerror(errno));
+ }
+
+ if (listen(this->socket, 0) < 0)
+ {
+ DBG1(DBG_CFG, "could not listen on stroke socket: %s", strerror(errno));
+ close(this->socket);
+ unlink(socket_addr.sun_path);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * load all certificates from ipsec.d
+ */
+static void load_certs(private_stroke_t *this)
+{
+ DBG1(DBG_CFG, "loading CA certificates from '%s'",
+ CA_CERTIFICATE_DIR);
+ load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA);
+
+ DBG1(DBG_CFG, "loading AA certificates from '%s'",
+ AA_CERTIFICATE_DIR);
+ load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA);
+
+ DBG1(DBG_CFG, "loading OCSP signer certificates from '%s'",
+ OCSP_CERTIFICATE_DIR);
+ load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, X509_OCSP_SIGNER);
+
+ DBG1(DBG_CFG, "loading attribute certificates from '%s'",
+ ATTR_CERTIFICATE_DIR);
+ load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0);
+
+ DBG1(DBG_CFG, "loading CRLs from '%s'",
+ CRL_DIR);
+ load_certdir(this, CRL_DIR, CERT_X509_CRL, 0);
+}
+
+/*
+ * Described in header-file
+ */
+plugin_t *plugin_create()
+{
+ private_stroke_t *this = malloc_thing(private_stroke_t);
+
+ /* public functions */
+ this->public.plugin.destroy = (void (*)(plugin_t*))destroy;
+
+ if (!open_socket(this))
+ {
+ free(this);
+ return NULL;
+ }
+
+ this->ca_creds.sections = linked_list_create();
+ this->ca_creds.mutex = mutex_create(MUTEX_RECURSIVE);
+ this->creds.certs = linked_list_create();
+ this->creds.shared = linked_list_create();
+ this->creds.private = linked_list_create();
+ this->creds.mutex = mutex_create(MUTEX_RECURSIVE);
+ this->configs.list = linked_list_create();
+ this->configs.mutex = mutex_create(MUTEX_RECURSIVE);
+
+ this->ca_creds.set.create_private_enumerator = (void*)return_null;
+ this->ca_creds.set.create_cert_enumerator = (void*)return_null;
+ this->ca_creds.set.create_shared_enumerator = (void*)return_null;
+ this->ca_creds.set.create_cdp_enumerator = (void*)create_cdp_enumerator;
+ charon->credentials->add_set(charon->credentials, &this->ca_creds.set);
+
+ this->creds.set.create_private_enumerator = (void*)create_private_enumerator;
+ this->creds.set.create_cert_enumerator = (void*)create_cert_enumerator;
+ this->creds.set.create_shared_enumerator = (void*)create_shared_enumerator;
+ this->creds.set.create_cdp_enumerator = (void*)return_null;
+ charon->credentials->add_set(charon->credentials, &this->creds.set);
+
+ load_certs(this);
+ load_secrets(this);
+
+ this->configs.backend.create_peer_cfg_enumerator = (enumerator_t*(*)(backend_t*, identification_t *me, identification_t *other))create_peer_cfg_enumerator;
+ this->configs.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator;
+ this->configs.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name;
+ charon->backends->add_backend(charon->backends, &this->configs.backend);
+
+ this->job = callback_job_create((callback_job_cb_t)stroke_receive,
+ this, NULL, NULL);
+ charon->processor->queue_job(charon->processor, (job_t*)this->job);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/stroke/stroke.h b/src/charon/plugins/stroke/stroke.h
new file mode 100644
index 000000000..b61f4109d
--- /dev/null
+++ b/src/charon/plugins/stroke/stroke.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup stroke stroke
+ * @ingroup cplugins
+ *
+ * @defgroup stroke_i stroke
+ * @{ @ingroup stroke
+ */
+
+#ifndef STROKE_H_
+#define STROKE_H_
+
+#include <plugins/plugin.h>
+
+typedef struct stroke_t stroke_t;
+
+/**
+ * strongSwan 2.x style configuration and control interface.
+ *
+ * Stroke is a home-brewed communication interface inspired by whack. It
+ * uses a unix socket (/var/run/charon.ctl).
+ */
+struct stroke_t {
+
+ /**
+ * implements plugin interface
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Instanciate stroke plugin.
+ */
+plugin_t *plugin_create();
+
+#endif /* STROKE_H_ @}*/
diff --git a/src/charon/plugins/unit_tester/Makefile.am b/src/charon/plugins/unit_tester/Makefile.am
new file mode 100644
index 000000000..fb5244314
--- /dev/null
+++ b/src/charon/plugins/unit_tester/Makefile.am
@@ -0,0 +1,17 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libcharon-unit-tester.la
+
+libcharon_unit_tester_la_SOURCES = unit_tester.c unit_tester.h \
+ tests/test_enumerator.c \
+ tests/test_auth_info.c \
+ tests/test_fips_prf.c \
+ tests/test_curl.c \
+ tests/test_mysql.c \
+ tests/test_sqlite.c \
+ tests/test_mutex.c
+libcharon_unit_tester_la_LDFLAGS = -module
+
diff --git a/src/charon/plugins/unit_tester/tests.h b/src/charon/plugins/unit_tester/tests.h
new file mode 100644
index 000000000..5def0b0a5
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup tests tests
+ * @{ @ingroup unit_tester
+ */
+
+DEFINE_TEST("linked_list_t->remove()", test_list_remove, FALSE)
+DEFINE_TEST("simple enumerator", test_enumerate, FALSE)
+DEFINE_TEST("nested enumerator", test_enumerate_nested, FALSE)
+DEFINE_TEST("filtered enumerator", test_enumerate_filtered, FALSE)
+DEFINE_TEST("auth info", test_auth_info, FALSE)
+DEFINE_TEST("FIPS PRF", fips_prf_test, FALSE)
+DEFINE_TEST("CURL get", test_curl_get, FALSE)
+DEFINE_TEST("MySQL operations", test_mysql, FALSE)
+DEFINE_TEST("SQLite operations", test_sqlite, FALSE)
+DEFINE_TEST("mutex primitive", test_mutex, TRUE)
+
diff --git a/src/charon/plugins/unit_tester/tests/test_auth_info.c b/src/charon/plugins/unit_tester/tests/test_auth_info.c
new file mode 100644
index 000000000..2640c951c
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_auth_info.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <daemon.h>
+#include <library.h>
+#include <credentials/auth_info.h>
+
+
+char buf[] = {0x01,0x02,0x03,0x04};
+chunk_t chunk = chunk_from_buf(buf);
+char certbuf[] = {
+ 0x30,0x82,0x02,0xfa,0x30,0x82,0x01,0xe2,0xa0,0x03,0x02,0x01,0x02,0x02,0x10,0x5a,
+ 0xf2,0x65,0xae,0x78,0xff,0x23,0xde,0xf7,0xa6,0xa3,0x94,0x8c,0x3f,0xa0,0xc1,0x30,
+ 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x30,0x39,
+ 0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x19,0x30,
+ 0x17,0x06,0x03,0x55,0x04,0x0a,0x13,0x10,0x4c,0x69,0x6e,0x75,0x78,0x20,0x73,0x74,
+ 0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x0f,0x30,0x0d,0x06,0x03,0x55,0x04,
+ 0x03,0x13,0x06,0x6d,0x61,0x72,0x74,0x69,0x6e,0x30,0x1e,0x17,0x0d,0x30,0x37,0x30,
+ 0x34,0x32,0x37,0x30,0x37,0x31,0x34,0x32,0x36,0x5a,0x17,0x0d,0x31,0x32,0x30,0x34,
+ 0x32,0x35,0x30,0x37,0x31,0x34,0x32,0x36,0x5a,0x30,0x39,0x31,0x0b,0x30,0x09,0x06,
+ 0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,
+ 0x0a,0x13,0x10,0x4c,0x69,0x6e,0x75,0x78,0x20,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,
+ 0x77,0x61,0x6e,0x31,0x0f,0x30,0x0d,0x06,0x03,0x55,0x04,0x03,0x13,0x06,0x6d,0x61,
+ 0x72,0x74,0x69,0x6e,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,
+ 0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,
+ 0x02,0x82,0x01,0x01,0x00,0xd7,0xb9,0xba,0x4d,0xe2,0x3b,0x3d,0x35,0x7a,0x3f,0x88,
+ 0x67,0x95,0xe7,0xfd,0x9f,0xe9,0x0a,0x0d,0x79,0x3a,0x9e,0x21,0x8f,0xcb,0xe4,0x67,
+ 0x24,0xae,0x0c,0xda,0xb3,0xcc,0xec,0x36,0xb4,0xa8,0x4d,0xf1,0x3d,0xad,0xe4,0x8c,
+ 0x63,0x92,0x54,0xb7,0xb2,0x02,0xa2,0x00,0x62,0x8b,0x04,0xac,0xa0,0x17,0xad,0x17,
+ 0x9a,0x05,0x0d,0xd7,0xb3,0x08,0x02,0xc5,0x26,0xcf,0xdd,0x05,0x42,0xfc,0x13,0x6d,
+ 0x9f,0xb1,0xf3,0x4f,0x82,0x1d,0xef,0x01,0xc9,0x91,0xea,0x37,0x1b,0x79,0x28,0xfa,
+ 0xbf,0x9f,0xb3,0xeb,0x82,0x4f,0x10,0xc6,0x4b,0xa4,0x08,0xf7,0x8e,0xf2,0x00,0xea,
+ 0x04,0x97,0x80,0x9f,0x65,0x86,0xde,0x6b,0xc7,0xda,0x83,0xfc,0xad,0x4a,0xaf,0x52,
+ 0x8b,0x4d,0x33,0xee,0x49,0x87,0x2f,0x3b,0x60,0x45,0x66,0x8f,0xe6,0x89,0xcc,0xb1,
+ 0x92,0x02,0x17,0x2b,0x7b,0x8e,0x90,0x47,0x84,0x84,0x59,0x95,0x81,0xd8,0xe0,0xf3,
+ 0x87,0xe0,0x04,0x09,0xfd,0xcc,0x3a,0x21,0x34,0xfa,0xec,0xbe,0xf5,0x9c,0xcf,0x55,
+ 0x80,0x7b,0xe3,0x75,0x9d,0x36,0x68,0xab,0x83,0xe3,0xad,0x01,0x53,0x0d,0x8a,0x9a,
+ 0xa6,0xb0,0x15,0xc9,0xc5,0xf8,0x9b,0x51,0x32,0xcf,0x97,0x6c,0xfe,0x4a,0x56,0x3c,
+ 0xc8,0x8f,0x4a,0x70,0x23,0x4f,0xf6,0xf7,0xe6,0x9f,0x09,0xcd,0x8f,0xea,0x20,0x7d,
+ 0x34,0xc0,0xc5,0xc0,0x34,0x06,0x6f,0x8b,0xeb,0x04,0x54,0x3f,0x0e,0xcd,0xe2,0x85,
+ 0xab,0x94,0x3e,0x91,0x6c,0x18,0x6f,0x96,0x5d,0xf2,0x8b,0x10,0xe9,0x90,0x43,0xb0,
+ 0x61,0x52,0xac,0xcf,0x75,0x02,0x03,0x01,0x00,0x01,0x30,0x0d,0x06,0x09,0x2a,0x86,
+ 0x48,0x86,0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x09,0x63,
+ 0x42,0xad,0xe5,0xa3,0xf6,0xc9,0x5d,0x08,0xf2,0x78,0x7b,0xeb,0x8a,0xef,0x50,0x00,
+ 0xc8,0xeb,0xe9,0x26,0x94,0xcb,0x84,0x10,0x7e,0x42,0x6b,0x86,0x38,0x57,0xa6,0x02,
+ 0x98,0x5a,0x2c,0x8f,0x44,0x32,0x1b,0x97,0x8c,0x7e,0x4b,0xd8,0xe8,0xe8,0x0f,0x4a,
+ 0xb9,0x31,0x9f,0xf6,0x9f,0x0e,0x67,0x26,0x05,0x2a,0x99,0x14,0x35,0x41,0x47,0x9a,
+ 0xfa,0x12,0x94,0x0b,0xe9,0x27,0x7c,0x71,0x20,0xd7,0x8d,0x3b,0x97,0x19,0x2d,0x15,
+ 0xff,0xa4,0xf3,0x89,0x8d,0x29,0x5f,0xf6,0x3f,0x93,0xaf,0x78,0x61,0xe4,0xe1,0x2e,
+ 0x75,0xc1,0x2c,0xc4,0x76,0x95,0x19,0xf8,0x37,0xdc,0xd8,0x00,0x7a,0x3c,0x0f,0x49,
+ 0x2e,0x88,0x09,0x16,0xb3,0x92,0x33,0xdf,0x77,0x83,0x4f,0xb5,0x9e,0x30,0x8c,0x48,
+ 0x1d,0xd8,0x84,0xfb,0xf1,0xb9,0xa0,0xbe,0x25,0xff,0x4c,0xeb,0xef,0x2b,0xcd,0xfa,
+ 0x0b,0x94,0x66,0x3b,0x28,0x08,0x3f,0x3a,0xda,0x41,0xd0,0x6b,0xab,0x5e,0xbb,0x8a,
+ 0x9f,0xdc,0x98,0x3e,0x59,0x37,0x48,0xbe,0x69,0xde,0x85,0x82,0xf2,0x53,0x8b,0xe4,
+ 0x44,0xe4,0x71,0x91,0x14,0x85,0x0e,0x1e,0x79,0xdd,0x62,0xf5,0xdc,0x25,0x89,0xab,
+ 0x50,0x5b,0xaa,0xae,0xe3,0x64,0x6a,0x23,0x34,0xd7,0x30,0xe2,0x2a,0xc8,0x81,0x0c,
+ 0xec,0xd2,0x31,0xc6,0x1e,0xb6,0xc0,0x57,0xd9,0xe1,0x14,0x06,0x9b,0xf8,0x51,0x69,
+ 0x47,0xf0,0x9c,0xcd,0x69,0xef,0x8e,0x5f,0x62,0xda,0x10,0xf7,0x3c,0x6d,0x0f,0x33,
+ 0xec,0x6f,0xfd,0x94,0x07,0x16,0x41,0x32,0x06,0xa4,0xe1,0x08,0x31,0x87,
+};
+chunk_t certchunk = chunk_from_buf(certbuf);
+
+/*******************************************************************************
+ * auth info test
+ ******************************************************************************/
+bool test_auth_info()
+{
+ auth_info_t *auth = auth_info_create(), *auth2;
+ certificate_t *c1, *c2;
+ enumerator_t *enumerator;
+ int round = 0;
+ void *value;
+ auth_item_t type;
+
+ c1 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB_ASN1_DER, chunk_clone(certchunk),
+ BUILD_END);
+ if (!c1)
+ {
+ return FALSE;
+ }
+
+ auth->add_item(auth, AUTHN_SUBJECT_CERT, c1);
+ if (!auth->get_item(auth, AUTHN_SUBJECT_CERT, (void**)&c2))
+ {
+ return FALSE;
+ }
+ if (!c1->equals(c1, c2))
+ {
+ return FALSE;
+ }
+
+ enumerator = auth->create_item_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ round++;
+ if (round == 1 && type == AUTHN_SUBJECT_CERT && value == c1)
+ {
+ continue;
+ }
+ return FALSE;
+ }
+ enumerator->destroy(enumerator);
+
+ auth2 = auth_info_create();
+ auth2->add_item(auth2, AUTHN_CA_CERT, c1);
+ auth2->merge(auth2, auth);
+
+ round = 0;
+ enumerator = auth2->create_item_enumerator(auth2);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ round++;
+ if (round == 1 && type == AUTHN_CA_CERT && value == c1)
+ {
+ continue;
+ }
+ if (round == 2 && type == AUTHN_SUBJECT_CERT && value == c1)
+ {
+ continue;
+ }
+ return FALSE;
+ }
+ enumerator->destroy(enumerator);
+ auth->destroy(auth);
+ auth2->destroy(auth2);
+ c1->destroy(c1);
+ return TRUE;
+}
+
diff --git a/src/charon/plugins/unit_tester/tests/test_curl.c b/src/charon/plugins/unit_tester/tests/test_curl.c
new file mode 100644
index 000000000..c011617a7
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_curl.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <daemon.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+/*******************************************************************************
+ * curl get test
+ ******************************************************************************/
+
+bool test_curl_get()
+{
+ chunk_t chunk;
+
+ if (lib->fetcher->fetch(lib->fetcher, "http://www.strongswan.org",
+ &chunk, FETCH_END) != SUCCESS)
+ {
+ return FALSE;
+ }
+ free(chunk.ptr);
+
+ if (lib->fetcher->fetch(lib->fetcher, "http://www.google.com",
+ &chunk, FETCH_END) != SUCCESS)
+ {
+ return FALSE;
+ }
+ free(chunk.ptr);
+ return TRUE;
+}
+
diff --git a/src/charon/plugins/unit_tester/tests/test_enumerator.c b/src/charon/plugins/unit_tester/tests/test_enumerator.c
new file mode 100644
index 000000000..d17d62bef
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_enumerator.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <utils/linked_list.h>
+
+
+/*******************************************************************************
+ * linked list remove test
+ ******************************************************************************/
+bool test_list_remove()
+{
+ void *a = (void*)1, *b = (void*)2;
+ linked_list_t *list;
+
+ list = linked_list_create();
+ list->insert_last(list, a);
+ if (list->remove(list, a, NULL) != 1)
+ {
+ return FALSE;
+ }
+ list->insert_last(list, a);
+ list->insert_first(list, a);
+ list->insert_last(list, a);
+ list->insert_last(list, b);
+ if (list->remove(list, a, NULL) != 3)
+ {
+ return FALSE;
+ }
+ if (list->remove(list, a, NULL) != 0)
+ {
+ return FALSE;
+ }
+ if (list->get_count(list) != 1)
+ {
+ return FALSE;
+ }
+ if (list->remove(list, b, NULL) != 1)
+ {
+ return FALSE;
+ }
+ if (list->remove(list, b, NULL) != 0)
+ {
+ return FALSE;
+ }
+ list->destroy(list);
+ return TRUE;
+}
+
+/*******************************************************************************
+ * Simple insert first/last and enumerate test
+ ******************************************************************************/
+bool test_enumerate()
+{
+ int round, x;
+ void *a = (void*)4, *b = (void*)3, *c = (void*)2, *d = (void*)5, *e = (void*)1;
+ linked_list_t *list;
+ enumerator_t *enumerator;
+
+ list = linked_list_create();
+
+ list->insert_last(list, a);
+ list->insert_first(list, b);
+ list->insert_first(list, c);
+ list->insert_last(list, d);
+ list->insert_first(list, e);
+
+ round = 1;
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &x))
+ {
+ if (round != x)
+ {
+ return FALSE;
+ }
+ round++;
+ }
+ enumerator->destroy(enumerator);
+
+ list->destroy(list);
+ return TRUE;
+}
+
+/*******************************************************************************
+ * nested enumerator test
+ ******************************************************************************/
+
+static bool bad_data;
+
+static enumerator_t* create_inner(linked_list_t *outer, void *data)
+{
+ if (data != (void*)101)
+ {
+ bad_data = TRUE;
+ }
+ return outer->create_enumerator(outer);
+}
+
+
+static void destroy_data(void *data)
+{
+ if (data != (void*)101)
+ {
+ bad_data = TRUE;
+ }
+}
+
+bool test_enumerate_nested()
+{
+ int round, x;
+ void *a = (void*)1, *b = (void*)2, *c = (void*)3, *d = (void*)4, *e = (void*)5;
+ linked_list_t *list, *l1, *l2, *l3;
+ enumerator_t *enumerator;
+
+ bad_data = FALSE;
+ list = linked_list_create();
+ l1 = linked_list_create();
+ l2 = linked_list_create();
+ l3 = linked_list_create();
+ list->insert_last(list, l1);
+ list->insert_last(list, l2);
+ list->insert_last(list, l3);
+
+ l1->insert_last(l1, a);
+ l1->insert_last(l1, b);
+ l3->insert_last(l3, c);
+ l3->insert_last(l3, d);
+ l3->insert_last(l3, e);
+
+ round = 1;
+ enumerator = enumerator_create_nested(list->create_enumerator(list),
+ (void*)create_inner, (void*)101, destroy_data);
+ while (enumerator->enumerate(enumerator, &x))
+ {
+ if (round != x)
+ {
+ return FALSE;
+ }
+ round++;
+ }
+ enumerator->destroy(enumerator);
+
+ list->destroy(list);
+ l1->destroy(l1);
+ l2->destroy(l2);
+ l3->destroy(l3);
+ return !bad_data;
+}
+
+
+/*******************************************************************************
+ * filtered enumerator test
+ ******************************************************************************/
+static bool filter(void *data, int *v, int *vo, int *w, int *wo,
+ int *x, int *xo, int *y, int *yo, int *z, int *zo)
+{
+ int val = *v;
+
+ *vo = val++;
+ *wo = val++;
+ *xo = val++;
+ *yo = val++;
+ *zo = val++;
+ if (data != (void*)101)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool test_enumerate_filtered()
+{
+ int round, v, w, x, y, z;
+ void *a = (void*)1, *b = (void*)2, *c = (void*)3, *d = (void*)4, *e = (void*)5;
+ linked_list_t *list;
+ enumerator_t *enumerator;
+
+ bad_data = FALSE;
+ list = linked_list_create();
+
+ list->insert_last(list, a);
+ list->insert_last(list, b);
+ list->insert_last(list, c);
+ list->insert_last(list, d);
+ list->insert_last(list, e);
+
+ round = 1;
+ enumerator = enumerator_create_filter(list->create_enumerator(list),
+ (void*)filter, (void*)101, destroy_data);
+ while (enumerator->enumerate(enumerator, &v, &w, &x, &y, &z))
+ {
+ if (v != round || w != round + 1 || x != round + 2 ||
+ y != round + 3 || z != round + 4)
+ {
+ return FALSE;
+ }
+ round++;
+ }
+ enumerator->destroy(enumerator);
+
+ list->destroy(list);
+ return !bad_data;
+}
diff --git a/src/charon/plugins/unit_tester/tests/test_fips_prf.c b/src/charon/plugins/unit_tester/tests/test_fips_prf.c
new file mode 100644
index 000000000..56ba556f5
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_fips_prf.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <utils/linked_list.h>
+#include <daemon.h>
+
+/*******************************************************************************
+ * fips prf known value test
+ ******************************************************************************/
+bool fips_prf_test()
+{
+ prf_t *prf;
+ u_int8_t key_buf[] = {
+ 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+ 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+ 0xeb, 0x5a, 0x38, 0xb6
+ };
+ u_int8_t seed_buf[] = {
+ 0x00
+ };
+ u_int8_t result_buf[] = {
+ 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+ 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+ 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+ 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+ 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+ };
+ chunk_t key = chunk_from_buf(key_buf);
+ chunk_t seed = chunk_from_buf(seed_buf);
+ chunk_t expected = chunk_from_buf(result_buf);
+ chunk_t result;
+
+ prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160);
+ if (prf == NULL)
+ {
+ return FALSE;
+ }
+ prf->set_key(prf, key);
+ prf->allocate_bytes(prf, seed, &result);
+ prf->destroy(prf);
+ if (!chunk_equals(result, expected))
+ {
+ chunk_free(&result);
+ return FALSE;
+ }
+ chunk_free(&result);
+ return TRUE;
+}
+
diff --git a/src/charon/plugins/unit_tester/tests/test_mutex.c b/src/charon/plugins/unit_tester/tests/test_mutex.c
new file mode 100644
index 000000000..a305d5082
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_mutex.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <utils/mutex.h>
+
+#include <unistd.h>
+#include <sched.h>
+#include <pthread.h>
+
+
+static mutex_t *mutex;
+
+static int locked = 0;
+
+static bool failed = FALSE;
+
+static pthread_barrier_t barrier;
+
+static void* run(void* null)
+{
+ int i;
+
+ /* wait for all threads before getting in action */
+ pthread_barrier_wait(&barrier);
+
+ for (i = 0; i < 100; i++)
+ {
+ mutex->lock(mutex);
+ mutex->lock(mutex);
+ mutex->lock(mutex);
+ locked++;
+ sched_yield();
+ if (locked > 1)
+ {
+ failed = TRUE;
+ }
+ locked--;
+ mutex->unlock(mutex);
+ mutex->unlock(mutex);
+ mutex->unlock(mutex);
+ }
+ return NULL;
+}
+
+#define THREADS 20
+
+/*******************************************************************************
+ * mutex test
+ ******************************************************************************/
+bool test_mutex()
+{
+ int i;
+ pthread_t threads[THREADS];
+
+ mutex = mutex_create(MUTEX_RECURSIVE);
+
+ for (i = 0; i < 10; i++)
+ {
+ mutex->lock(mutex);
+ mutex->unlock(mutex);
+ }
+ for (i = 0; i < 10; i++)
+ {
+ mutex->lock(mutex);
+ }
+ for (i = 0; i < 10; i++)
+ {
+ mutex->unlock(mutex);
+ }
+
+ pthread_barrier_init(&barrier, NULL, THREADS);
+
+ for (i = 0; i < THREADS; i++)
+ {
+ pthread_create(&threads[i], NULL, run, NULL);
+ }
+ for (i = 0; i < THREADS; i++)
+ {
+ pthread_join(threads[i], NULL);
+ }
+ pthread_barrier_destroy(&barrier);
+
+ mutex->destroy(mutex);
+
+ return !failed;
+}
+
diff --git a/src/charon/plugins/unit_tester/tests/test_mysql.c b/src/charon/plugins/unit_tester/tests/test_mysql.c
new file mode 100644
index 000000000..ff3d38ad8
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_mysql.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <daemon.h>
+#include <utils/enumerator.h>
+
+/*******************************************************************************
+ * mysql simple test
+ ******************************************************************************/
+bool test_mysql()
+{
+ database_t *db;
+ char *txt = "I'm a superduper test";
+ char buf[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
+ chunk_t data = chunk_from_buf(buf);
+ int row;
+ chunk_t qdata;
+ char *qtxt;
+ bool good = FALSE;
+ enumerator_t *enumerator;
+
+ db = lib->db->create(lib->db, "mysql://testuser:testpass@localhost/test");
+ if (!db)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "CREATE TABLE test ("
+ "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
+ "txt TEXT, data BLOB)") < 0)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, &row, "INSERT INTO test (txt, data) VALUES (?,?)",
+ DB_TEXT, txt, DB_BLOB, data) < 0)
+ {
+ return FALSE;
+ }
+ if (row != 1)
+ {
+ return FALSE;
+ }
+ enumerator = db->query(db, "SELECT txt, data FROM test WHERE id = ?",
+ DB_INT, row,
+ DB_TEXT, DB_BLOB);
+ if (!enumerator)
+ {
+ return FALSE;
+ }
+ while (enumerator->enumerate(enumerator, &qtxt, &qdata))
+ {
+ if (good)
+ { /* only one row */
+ good = FALSE;
+ break;
+ }
+ if (streq(qtxt, txt) && chunk_equals(data, qdata))
+ {
+ good = TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!good)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "DELETE FROM test WHERE id = ?", DB_INT, row) != 1)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "DROP TABLE test") < 0)
+ {
+ return FALSE;
+ }
+ db->destroy(db);
+ return TRUE;
+}
+
diff --git a/src/charon/plugins/unit_tester/tests/test_sqlite.c b/src/charon/plugins/unit_tester/tests/test_sqlite.c
new file mode 100644
index 000000000..d152fc594
--- /dev/null
+++ b/src/charon/plugins/unit_tester/tests/test_sqlite.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <daemon.h>
+#include <utils/enumerator.h>
+
+#include <unistd.h>
+
+
+#define DBFILE "/tmp/strongswan-test.db"
+
+/*******************************************************************************
+ * sqlite simple test
+ ******************************************************************************/
+bool test_sqlite()
+{
+ database_t *db;
+ char *txt = "I'm a superduper test";
+ char buf[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
+ chunk_t data = chunk_from_buf(buf);
+ int row;
+ chunk_t qdata;
+ char *qtxt;
+ bool good = FALSE;
+ enumerator_t *enumerator;
+
+ db = lib->db->create(lib->db, "sqlite://" DBFILE);
+ if (!db)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "CREATE TABLE test (txt TEXT, data BLOB)") < 0)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, &row, "INSERT INTO test (txt, data) VALUES (?,?)",
+ DB_TEXT, txt, DB_BLOB, data) < 0)
+ {
+ return FALSE;
+ }
+ if (row != 1)
+ {
+ return FALSE;
+ }
+ enumerator = db->query(db, "SELECT txt, data FROM test WHERE oid = ?",
+ DB_INT, row,
+ DB_TEXT, DB_BLOB);
+ if (!enumerator)
+ {
+ return FALSE;
+ }
+ while (enumerator->enumerate(enumerator, &qtxt, &qdata))
+ {
+ if (good)
+ { /* only one row */
+ good = FALSE;
+ break;
+ }
+ if (streq(qtxt, txt) && chunk_equals(data, qdata))
+ {
+ good = TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!good)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "DELETE FROM test WHERE oid = ?", DB_INT, row) != 1)
+ {
+ return FALSE;
+ }
+ if (db->execute(db, NULL, "DROP TABLE test") < 0)
+ {
+ return FALSE;
+ }
+ db->destroy(db);
+ unlink(DBFILE);
+ return TRUE;
+}
+
diff --git a/src/charon/plugins/unit_tester/unit_tester.c b/src/charon/plugins/unit_tester/unit_tester.c
new file mode 100644
index 000000000..f996a4988
--- /dev/null
+++ b/src/charon/plugins/unit_tester/unit_tester.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include "unit_tester.h"
+
+#include <daemon.h>
+
+typedef struct private_unit_tester_t private_unit_tester_t;
+typedef struct unit_test_t unit_test_t;
+typedef enum test_status_t test_status_t;
+
+/**
+ * private data of unit_tester
+ */
+struct private_unit_tester_t {
+
+ /**
+ * public functions
+ */
+ unit_tester_t public;
+};
+
+struct unit_test_t {
+
+ /**
+ * name of the test
+ */
+ char *name;
+
+ /**
+ * test function
+ */
+ bool (*test)(void);
+
+ /**
+ * run the test?
+ */
+ bool enabled;
+};
+
+#undef DEFINE_TEST
+#define DEFINE_TEST(name, function, enabled) bool function();
+#include <plugins/unit_tester/tests.h>
+#undef DEFINE_TEST
+#define DEFINE_TEST(name, function, enabled) {name, function, enabled},
+static unit_test_t tests[] = {
+#include <plugins/unit_tester/tests.h>
+};
+
+static void run_tests(private_unit_tester_t *this)
+{
+ int i, run = 0, failed = 0, success = 0, skipped = 0;
+
+ DBG1(DBG_CFG, "running unit tests, %d tests registered",
+ sizeof(tests)/sizeof(unit_test_t));
+
+ for (i = 0; i < sizeof(tests)/sizeof(unit_test_t); i++)
+ {
+ if (tests[i].enabled)
+ {
+ run++;
+ if (tests[i].test())
+ {
+ DBG1(DBG_CFG, "test '%s' successful", tests[i].name);
+ success++;
+ }
+ else
+ {
+ DBG1(DBG_CFG, "test '%s' failed", tests[i].name);
+ failed++;
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, "test '%s' disabled", tests[i].name);
+ skipped++;
+ }
+ }
+ DBG1(DBG_CFG, "%d/%d tests successful (%d failed, %d disabled)",
+ success, run, failed, skipped);
+}
+
+/**
+ * Implementation of 2007_t.destroy
+ */
+static void destroy(private_unit_tester_t *this)
+{
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+ private_unit_tester_t *this = malloc_thing(private_unit_tester_t);
+
+ this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+
+ run_tests(this);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/unit_tester/unit_tester.h b/src/charon/plugins/unit_tester/unit_tester.h
new file mode 100644
index 000000000..a87c86251
--- /dev/null
+++ b/src/charon/plugins/unit_tester/unit_tester.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup unit_tester unit_tester
+ * @{ @ingroup cplugins
+ */
+
+#ifndef UNIT_TESTER_H_
+#define UNIT_TESTER_H_
+
+#include <plugins/plugin.h>
+
+typedef struct unit_tester_t unit_tester_t;
+
+/**
+ * Unit testing plugin.
+ *
+ * The unit testing plugin runs tests on plugin initialization. Tests are
+ * defined in tests.h using the DEFINE_TEST macro. Implementation of the
+ * tests is done in the tests folder. Each test has uses a function which
+ * returns TRUE for success or FALSE for failure.
+ */
+struct unit_tester_t {
+
+ /**
+ * Implements the plugin interface.
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a unit_tester plugin.
+ */
+plugin_t *plugin_create();
+
+#endif /* UNIT_TESTER_H_ @}*/
diff --git a/src/charon/plugins/xml/Makefile.am b/src/charon/plugins/xml/Makefile.am
new file mode 100644
index 000000000..0e4735a41
--- /dev/null
+++ b/src/charon/plugins/xml/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon ${xml_CFLAGS}
+
+AM_CFLAGS = -rdynamic -DIPSEC_PIDDIR=\"${piddir}\"
+
+plugin_LTLIBRARIES = libcharon-xml.la
+libcharon_xml_la_SOURCES = xml.h xml.c
+libcharon_xml_la_LDFLAGS = -module
+libcharon_xml_la_LIBADD = ${xml_LIBS}
+
diff --git a/src/charon/plugins/xml/schema.xml b/src/charon/plugins/xml/schema.xml
new file mode 100644
index 000000000..66a51117e
--- /dev/null
+++ b/src/charon/plugins/xml/schema.xml
@@ -0,0 +1,400 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- strongSwan Managment Protocol (SMP) V1.0 -->
+
+<!--
+ Copyright (C) 2007 Martin Willi
+ Copyright (C) 2006 Andreas Eigenmann, Joël Stillhart
+ Hochschule fuer Technik Rapperswil
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+-->
+
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+ ns="http://www.strongswan.org/smp/1.0">
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Message -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <start>
+ <element name="message">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>request</value>
+ </attribute>
+ <optional>
+ <element name="query">
+ <optional>
+ <ref name="QueryRequestIkesa"/>
+ </optional>
+ <optional>
+ <ref name="QueryRequestConfig"/>
+ </optional>
+ <!-- others -->
+ </element>
+ </optional>
+ <optional>
+ <element name="control">
+ <optional>
+ <ref name="ControlRequestIkeTerminate"/>
+ </optional>
+ <optional>
+ <ref name="ControlRequestChildTerminate"/>
+ </optional>
+ <optional>
+ <ref name="ControlRequestIkeInitiate"/>
+ </optional>
+ <optional>
+ <ref name="ControlRequestChildInitiate"/>
+ </optional>
+ <!-- others -->
+ </element>
+ </optional>
+ <!-- others -->
+ </group>
+ <group>
+ <attribute name="type">
+ <value>response</value>
+ </attribute>
+ <choice>
+ <element name="error">
+ <attribute name="code">
+ <data type="nonNegativeInteger"/>
+ </attribute>
+ <data type="string"/>
+ </element>
+ <group>
+ <optional>
+ <element name="query">
+ <optional>
+ <ref name="QueryResponseIkesa"/>
+ </optional>
+ <optional>
+ <ref name="QueryResponseConfig"/>
+ </optional>
+ <!-- others -->
+ </element>
+ </optional>
+ <optional>
+ <element name="control">
+ <optional>
+ <ref name="ControlResponse"/>
+ </optional>
+ <!-- others -->
+ </element>
+ </optional>
+ <!-- others -->
+ </group>
+ </choice>
+ </group>
+ </choice>
+ </element>
+ </start>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Query -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <define name="QueryRequestIkesa">
+ <element name="ikesalist">
+ <empty/>
+ </element>
+ </define>
+ <define name="QueryResponseIkesa">
+ <element name="ikesalist">
+ <zeroOrMore>
+ <element name="ikesa">
+ <element name="id">
+ <data type="positiveInteger"/>
+ </element>
+ <element name="status">
+ <choice>
+ <value type="string">created</value>
+ <value type="string">connecting</value>
+ <value type="string">established</value>
+ <value type="string">rekeying</value>
+ <value type="string">deleting</value>
+ </choice>
+ </element>
+ <element name="role">
+ <choice>
+ <value type="string">initiator</value>
+ <value type="string">responder</value>
+ </choice>
+ </element>
+ <element name="peerconfig">
+ <data type="string"/>
+ </element>
+ <element name="lifetime">
+ <data type="integer"/>
+ </element>
+ <element name="rekeytime">
+ <data type="integer"/>
+ </element>
+ <element name="local">
+ <ref name="ikeEnd"/>
+ </element>
+ <element name="remote">
+ <ref name="ikeEnd"/>
+ </element>
+ <element name="childsalist">
+ <zeroOrMore>
+ <element name="childsa">
+ <ref name="childsa"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="ikeEnd">
+ <element name="spi">
+ <data type="hexBinary" />
+ </element>
+ <element name="identification">
+ <ref name="identification"/>
+ </element>
+ <element name="address">
+ <ref name="address"/>
+ </element>
+ <element name="port">
+ <data type="nonNegativeInteger">
+ <param name="maxInclusive">65535</param>
+ </data>
+ </element>
+ <optional>
+ <element name="nat">
+ <data type="boolean"/>
+ </element>
+ </optional>
+ </define>
+ <define name="childsa">
+ <element name="reqid">
+ <data type="nonNegativeInteger"/>
+ </element>
+ <element name="lifetime">
+ <data type="integer"/>
+ </element>
+ <element name="rekeytime">
+ <data type="integer"/>
+ </element>
+ <element name="local">
+ <ref name="childEnd"/>
+ </element>
+ <element name="remote">
+ <ref name="childEnd"/>
+ </element>
+ </define>
+ <define name="childEnd">
+ <element name="spi">
+ <element name="networks">
+ <ref name="networks">
+ </element>
+ </define>
+ <define name="QueryRequestConfig">
+ <element name="configlist">
+ <empty/>
+ </element>
+ </define>
+ <define name="QueryResponseConfig">
+ <element name="configlist">
+ <zeroOrMore>
+ <element name="peerconfig">
+ <element name="name">
+ <data type="string"/>
+ </element>
+ <element name="local">
+ <ref name="identification"/>
+ </element>
+ <element name="remote">
+ <ref name="identification"/>
+ </element>
+ <element name="ikeconfig">
+ <ref name="ikeconfig"/>
+ </element>
+ <element name="childconfiglist">
+ <zeroOrMore>
+ <element name="childconfig">
+ <ref name="childconfig"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="ikeconfig">
+ <element name="local">
+ <ref name="address"/>
+ </element>
+ <element name="remote">
+ <ref name="address"/>
+ </element>
+ </define>
+ <define name="childconfig">
+ <element name="name">
+ <data type="string"/>
+ </element>
+ <element name="local">
+ <ref name="networks">
+ </element>
+ <element name="remote">
+ <ref name="networks">
+ </element>
+ </define>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Control -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <define name="ControlRequestIkeTerminate">
+ <element name="ikesaterminate">
+ <data type="positiveInteger"/>
+ </element>
+ </define>
+ <define name="ControlRequestChildTerminate">
+ <element name="childsaterminate">
+ <data type="positiveInteger"/>
+ </element>
+ </define>
+ <define name="ControlRequestIkeInitiate">
+ <element name="ikesainitiate">
+ <data type="string"/>
+ </element>
+ </define>
+ <define name="ControlRequestChildInitiate">
+ <element name="childsainitiate">
+ <data type="string"/>
+ </element>
+ </define>
+ <define name="QueryResponse">
+ <element name="status">
+ <data type="nonNegativeInteger"/>
+ </element>
+ <element name="log">
+ <zeroOrMore>
+ <element name="item">
+ <attribute name="level">
+ <data type="nonNegativeInteger">
+ </attribute>
+ <attribute name="thread">
+ <data type="nonNegativeInteger">
+ </attribute>
+ <attribute name="source">
+ <data type="string">
+ </attribute>
+ <data type="string"/>
+ <element>
+ </zeroOrMore>
+ </element>
+ </define>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- identification and address -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <define name="identification">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>any</value>
+ </attribute>
+ <empty/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ipv4</value>
+ </attribute>
+ <ref name="ipv4"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ipv6</value>
+ </attribute>
+ <ref name="ipv6"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>fqdn</value>
+ </attribute>
+ <ref name="fqdn"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>email</value>
+ </attribute>
+ <ref name="email"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>asn1gn</value>
+ </attribute>
+ <data type="string"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>asn1dn</value>
+ </attribute>
+ <data type="string"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>keyid</value>
+ </attribute>
+ <data type="base64Binary"/>
+ </group>
+ </choice>
+ </define>
+ <define name="address">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>ipv4</value>
+ </attribute>
+ <ref name="ipv4"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>ipv6</value>
+ </attribute>
+ <ref name="ipv6"/>
+ </group>
+ </choice>
+ </define>
+ <define name="ipv4">
+ <data type="string">
+ <param name="pattern">(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))?</param>
+ </data>
+ </define>
+ <define name="ipv6">
+ <data type="string">
+ <param name="pattern">([0-9a-fA-F]{1,4}:|:){1,7}([0-9a-fA-F]{1,4}|:)(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?</param>
+ </data>
+ </define>
+ <define name="fqdn">
+ <data type="string">
+ <param name="pattern">[a-z0-9\-](\.[a-z0-9\-]+)*</param>
+ </data>
+ </define>
+ <define name="email">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\-\.]+@(([a-z0-9\-](\.[a-z0-9\-]+)*)|(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]))</param>
+ </data>
+ </define>
+ <define name="networks">
+ <zeroOrMore>
+ <element name="network">
+ <optional>
+ <attribute name="protocol"/>
+ </optional>
+ <optional>
+ <attribute name="port"/>
+ </optional>
+ </element>
+ </zeroOrMore>
+ </define>
+</grammar>
diff --git a/src/charon/plugins/xml/xml.c b/src/charon/plugins/xml/xml.c
new file mode 100644
index 000000000..85778f608
--- /dev/null
+++ b/src/charon/plugins/xml/xml.c
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+
+#include "xml.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <libxml/xmlreader.h>
+#include <libxml/xmlwriter.h>
+
+#include <library.h>
+#include <daemon.h>
+#include <processing/jobs/callback_job.h>
+
+
+typedef struct private_xml_t private_xml_t;
+
+/**
+ * Private data of an xml_t object.
+ */
+struct private_xml_t {
+
+ /**
+ * Public part of xml_t object.
+ */
+ xml_t public;
+
+ /**
+ * XML unix socket fd
+ */
+ int socket;
+
+ /**
+ * job accepting stroke messages
+ */
+ callback_job_t *job;
+};
+
+ENUM(ike_sa_state_lower_names, IKE_CREATED, IKE_DELETING,
+ "created",
+ "connecting",
+ "established",
+ "rekeying",
+ "deleting",
+);
+
+/**
+ * write a bool into element
+ */
+static void write_bool(xmlTextWriterPtr writer, char *element, bool val)
+{
+ xmlTextWriterWriteElement(writer, element, val ? "true" : "false");
+}
+
+/**
+ * write a identification_t into element
+ */
+static void write_id(xmlTextWriterPtr writer, char *element, identification_t *id)
+{
+ xmlTextWriterStartElement(writer, element);
+ switch (id->get_type(id))
+ {
+ {
+ char *type = "";
+ while (TRUE)
+ {
+ case ID_ANY:
+ type = "any";
+ break;
+ case ID_IPV4_ADDR:
+ type = "ipv4";
+ break;
+ case ID_IPV6_ADDR:
+ type = "ipv6";
+ break;
+ case ID_FQDN:
+ type = "fqdn";
+ break;
+ case ID_RFC822_ADDR:
+ type = "email";
+ break;
+ case ID_DER_ASN1_DN:
+ type = "asn1dn";
+ break;
+ case ID_DER_ASN1_GN:
+ type = "asn1gn";
+ break;
+ }
+ xmlTextWriterWriteAttribute(writer, "type", type);
+ xmlTextWriterWriteFormatString(writer, "%D", id);
+ break;
+ }
+ default:
+ /* TODO: base64 keyid */
+ xmlTextWriterWriteAttribute(writer, "type", "keyid");
+ break;
+ }
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * write a host_t address into an element
+ */
+static void write_address(xmlTextWriterPtr writer, char *element, host_t *host)
+{
+ xmlTextWriterStartElement(writer, element);
+ xmlTextWriterWriteAttribute(writer, "type",
+ host->get_family(host) == AF_INET ? "ipv4" : "ipv6");
+ if (host->is_anyaddr(host))
+ { /* do not use %any for XML */
+ xmlTextWriterWriteFormatString(writer, "%s",
+ host->get_family(host) == AF_INET ? "0.0.0.0" : "::");
+ }
+ else
+ {
+ xmlTextWriterWriteFormatString(writer, "%H", host);
+ }
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * write networks element
+ */
+static void write_networks(xmlTextWriterPtr writer, char *element,
+ linked_list_t *list)
+{
+ iterator_t *iterator;
+ traffic_selector_t *ts;
+
+ xmlTextWriterStartElement(writer, element);
+ iterator = list->create_iterator(list, TRUE);
+ while (iterator->iterate(iterator, (void**)&ts))
+ {
+ xmlTextWriterStartElement(writer, "network");
+ xmlTextWriterWriteAttribute(writer, "type",
+ ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? "ipv4" : "ipv6");
+ xmlTextWriterWriteFormatString(writer, "%R", ts);
+ xmlTextWriterEndElement(writer);
+ }
+ iterator->destroy(iterator);
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * write a childEnd
+ */
+static void write_childend(xmlTextWriterPtr writer, child_sa_t *child, bool local)
+{
+ linked_list_t *list;
+
+ xmlTextWriterWriteFormatElement(writer, "spi", "%lx",
+ htonl(child->get_spi(child, local)));
+ list = child->get_traffic_selectors(child, local);
+ write_networks(writer, "networks", list);
+}
+
+/**
+ * write a child_sa_t
+ */
+static void write_child(xmlTextWriterPtr writer, child_sa_t *child)
+{
+ mode_t mode;
+ encryption_algorithm_t encr;
+ integrity_algorithm_t int_algo;
+ size_t encr_len, int_len;
+ u_int32_t rekey, use_in, use_out, use_fwd;
+ child_cfg_t *config;
+
+ config = child->get_config(child);
+ child->get_stats(child, &mode, &encr, &encr_len, &int_algo, &int_len,
+ &rekey, &use_in, &use_out, &use_fwd);
+
+ xmlTextWriterStartElement(writer, "childsa");
+ xmlTextWriterWriteFormatElement(writer, "reqid", "%d", child->get_reqid(child));
+ xmlTextWriterWriteFormatElement(writer, "childconfig", "%s",
+ config->get_name(config));
+ xmlTextWriterStartElement(writer, "local");
+ write_childend(writer, child, TRUE);
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterStartElement(writer, "remote");
+ write_childend(writer, child, FALSE);
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * process a ikesalist query request message
+ */
+static void request_query_ikesa(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
+{
+ iterator_t *iterator;
+ ike_sa_t *ike_sa;
+
+ /* <ikesalist> */
+ xmlTextWriterStartElement(writer, "ikesalist");
+
+ iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager);
+ while (iterator->iterate(iterator, (void**)&ike_sa))
+ {
+ ike_sa_id_t *id;
+ host_t *local, *remote;
+ iterator_t *children;
+ child_sa_t *child_sa;
+
+ id = ike_sa->get_id(ike_sa);
+
+ xmlTextWriterStartElement(writer, "ikesa");
+ xmlTextWriterWriteFormatElement(writer, "id", "%d",
+ ike_sa->get_unique_id(ike_sa));
+ xmlTextWriterWriteFormatElement(writer, "status", "%N",
+ ike_sa_state_lower_names, ike_sa->get_state(ike_sa));
+ xmlTextWriterWriteElement(writer, "role",
+ id->is_initiator(id) ? "initiator" : "responder");
+ xmlTextWriterWriteElement(writer, "peerconfig", ike_sa->get_name(ike_sa));
+
+ /* <local> */
+ local = ike_sa->get_my_host(ike_sa);
+ xmlTextWriterStartElement(writer, "local");
+ xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx",
+ id->is_initiator(id) ? id->get_initiator_spi(id)
+ : id->get_responder_spi(id));
+ write_id(writer, "identification", ike_sa->get_my_id(ike_sa));
+ write_address(writer, "address", local);
+ xmlTextWriterWriteFormatElement(writer, "port", "%d",
+ local->get_port(local));
+ if (ike_sa->supports_extension(ike_sa, EXT_NATT))
+ {
+ write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_HERE));
+ }
+ xmlTextWriterEndElement(writer);
+ /* </local> */
+
+ /* <remote> */
+ remote = ike_sa->get_other_host(ike_sa);
+ xmlTextWriterStartElement(writer, "remote");
+ xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx",
+ id->is_initiator(id) ? id->get_responder_spi(id)
+ : id->get_initiator_spi(id));
+ write_id(writer, "identification", ike_sa->get_other_id(ike_sa));
+ write_address(writer, "address", remote);
+ xmlTextWriterWriteFormatElement(writer, "port", "%d",
+ remote->get_port(remote));
+ if (ike_sa->supports_extension(ike_sa, EXT_NATT))
+ {
+ write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_THERE));
+ }
+ xmlTextWriterEndElement(writer);
+ /* </remote> */
+
+ /* <childsalist> */
+ xmlTextWriterStartElement(writer, "childsalist");
+ children = ike_sa->create_child_sa_iterator(ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ write_child(writer, child_sa);
+ }
+ children->destroy(children);
+ /* </childsalist> */
+ xmlTextWriterEndElement(writer);
+
+ /* </ikesa> */
+ xmlTextWriterEndElement(writer);
+ }
+ iterator->destroy(iterator);
+
+ /* </ikesalist> */
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * process a configlist query request message
+ */
+static void request_query_config(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+
+ /* <configlist> */
+ xmlTextWriterStartElement(writer, "configlist");
+
+ enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends);
+ while (enumerator->enumerate(enumerator, (void**)&peer_cfg))
+ {
+ enumerator_t *children;
+ child_cfg_t *child_cfg;
+ ike_cfg_t *ike_cfg;
+ linked_list_t *list;
+
+ if (peer_cfg->get_ike_version(peer_cfg) != 2)
+ { /* only IKEv2 connections yet */
+ continue;
+ }
+
+ /* <peerconfig> */
+ xmlTextWriterStartElement(writer, "peerconfig");
+ xmlTextWriterWriteElement(writer, "name", peer_cfg->get_name(peer_cfg));
+ write_id(writer, "local", peer_cfg->get_my_id(peer_cfg));
+ write_id(writer, "remote", peer_cfg->get_other_id(peer_cfg));
+
+ /* <ikeconfig> */
+ ike_cfg = peer_cfg->get_ike_cfg(peer_cfg);
+ xmlTextWriterStartElement(writer, "ikeconfig");
+ write_address(writer, "local", ike_cfg->get_my_host(ike_cfg));
+ write_address(writer, "remote", ike_cfg->get_other_host(ike_cfg));
+ xmlTextWriterEndElement(writer);
+ /* </ikeconfig> */
+
+ /* <childconfiglist> */
+ xmlTextWriterStartElement(writer, "childconfiglist");
+ children = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+ while (children->enumerate(children, &child_cfg))
+ {
+ /* <childconfig> */
+ xmlTextWriterStartElement(writer, "childconfig");
+ xmlTextWriterWriteElement(writer, "name",
+ child_cfg->get_name(child_cfg));
+ list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
+ write_networks(writer, "local", list);
+ list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+ list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL);
+ write_networks(writer, "remote", list);
+ list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+ xmlTextWriterEndElement(writer);
+ /* </childconfig> */
+ }
+ children->destroy(children);
+ /* </childconfiglist> */
+ xmlTextWriterEndElement(writer);
+ /* </peerconfig> */
+ xmlTextWriterEndElement(writer);
+ }
+ enumerator->destroy(enumerator);
+ /* </configlist> */
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * callback which logs to a XML writer
+ */
+static bool xml_callback(xmlTextWriterPtr writer, signal_t signal, level_t level,
+ ike_sa_t* ike_sa, char* format, va_list args)
+{
+ if (level <= 1)
+ {
+ /* <item> */
+ xmlTextWriterStartElement(writer, "item");
+ xmlTextWriterWriteFormatAttribute(writer, "level", "%d", level);
+ xmlTextWriterWriteFormatAttribute(writer, "source", "%N", signal_names, signal);
+ xmlTextWriterWriteFormatAttribute(writer, "thread", "%u", pthread_self());
+ xmlTextWriterWriteVFormatString(writer, format, args);
+ xmlTextWriterEndElement(writer);
+ /* </item> */
+ }
+ return TRUE;
+}
+
+/**
+ * process a *terminate control request message
+ */
+static void request_control_terminate(xmlTextReaderPtr reader,
+ xmlTextWriterPtr writer, bool ike)
+{
+ if (xmlTextReaderRead(reader) &&
+ xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT)
+ {
+ const char *str;
+ u_int32_t id;
+ status_t status;
+
+ str = xmlTextReaderConstValue(reader);
+ if (str == NULL || !(id = atoi(str)))
+ {
+ DBG1(DBG_CFG, "error parsing XML id string");
+ return;
+ }
+ DBG1(DBG_CFG, "terminating %s_SA %d", ike ? "IKE" : "CHILD", id);
+
+ /* <log> */
+ xmlTextWriterStartElement(writer, "log");
+ if (ike)
+ {
+ status = charon->controller->terminate_ike(
+ charon->controller, id,
+ (controller_cb_t)xml_callback, writer);
+ }
+ else
+ {
+ status = charon->controller->terminate_child(
+ charon->controller, id,
+ (controller_cb_t)xml_callback, writer);
+ }
+ /* </log> */
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterWriteFormatElement(writer, "status", "%d", status);
+ }
+}
+
+/**
+ * process a *initiate control request message
+ */
+static void request_control_initiate(xmlTextReaderPtr reader,
+ xmlTextWriterPtr writer, bool ike)
+{
+ if (xmlTextReaderRead(reader) &&
+ xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT)
+ {
+ const char *str;
+ status_t status = FAILED;
+ peer_cfg_t *peer;
+ child_cfg_t *child = NULL;
+ enumerator_t *enumerator;
+
+ str = xmlTextReaderConstValue(reader);
+ if (str == NULL)
+ {
+ DBG1(DBG_CFG, "error parsing XML config name string");
+ return;
+ }
+ DBG1(DBG_CFG, "initiating %s_SA %s", ike ? "IKE" : "CHILD", str);
+
+ /* <log> */
+ xmlTextWriterStartElement(writer, "log");
+ peer = charon->backends->get_peer_cfg_by_name(charon->backends, (char*)str);
+ if (peer)
+ {
+ enumerator = peer->create_child_cfg_enumerator(peer);
+ if (ike)
+ {
+ if (!enumerator->enumerate(enumerator, &child))
+ {
+ child = NULL;
+ }
+ child->get_ref(child);
+ }
+ else
+ {
+ while (enumerator->enumerate(enumerator, &child))
+ {
+ if (streq(child->get_name(child), str))
+ {
+ child->get_ref(child);
+ break;
+ }
+ child = NULL;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (child)
+ {
+ status = charon->controller->initiate(charon->controller,
+ peer, child, (controller_cb_t)xml_callback,
+ writer);
+ }
+ else
+ {
+ peer->destroy(peer);
+ }
+ }
+ /* </log> */
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterWriteFormatElement(writer, "status", "%d", status);
+ }
+}
+
+/**
+ * process a query request
+ */
+static void request_query(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
+{
+ /* <query> */
+ xmlTextWriterStartElement(writer, "query");
+ while (xmlTextReaderRead(reader))
+ {
+ if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
+ {
+ if (streq(xmlTextReaderConstName(reader), "ikesalist"))
+ {
+ request_query_ikesa(reader, writer);
+ break;
+ }
+ if (streq(xmlTextReaderConstName(reader), "configlist"))
+ {
+ request_query_config(reader, writer);
+ break;
+ }
+ }
+ }
+ /* </query> */
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * process a control request
+ */
+static void request_control(xmlTextReaderPtr reader, xmlTextWriterPtr writer)
+{
+ /* <control> */
+ xmlTextWriterStartElement(writer, "control");
+ while (xmlTextReaderRead(reader))
+ {
+ if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
+ {
+ if (streq(xmlTextReaderConstName(reader), "ikesaterminate"))
+ {
+ request_control_terminate(reader, writer, TRUE);
+ break;
+ }
+ if (streq(xmlTextReaderConstName(reader), "childsaterminate"))
+ {
+ request_control_terminate(reader, writer, FALSE);
+ break;
+ }
+ if (streq(xmlTextReaderConstName(reader), "ikesainitiate"))
+ {
+ request_control_initiate(reader, writer, TRUE);
+ break;
+ }
+ if (streq(xmlTextReaderConstName(reader), "childsainitiate"))
+ {
+ request_control_initiate(reader, writer, FALSE);
+ break;
+ }
+ }
+ }
+ /* </control> */
+ xmlTextWriterEndElement(writer);
+}
+
+/**
+ * process a request message
+ */
+static void request(xmlTextReaderPtr reader, char *id, int fd)
+{
+ xmlTextWriterPtr writer;
+
+ writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL));
+ if (writer == NULL)
+ {
+ DBG1(DBG_CFG, "opening SMP XML writer failed");
+ return;
+ }
+
+ xmlTextWriterStartDocument(writer, NULL, NULL, NULL);
+ /* <message xmlns="http://www.strongswan.org/smp/1.0"
+ id="id" type="response"> */
+ xmlTextWriterStartElement(writer, "message");
+ xmlTextWriterWriteAttribute(writer, "xmlns",
+ "http://www.strongswan.org/smp/1.0");
+ xmlTextWriterWriteAttribute(writer, "id", id);
+ xmlTextWriterWriteAttribute(writer, "type", "response");
+
+ while (xmlTextReaderRead(reader))
+ {
+ if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT)
+ {
+ if (streq(xmlTextReaderConstName(reader), "query"))
+ {
+ request_query(reader, writer);
+ break;
+ }
+ if (streq(xmlTextReaderConstName(reader), "control"))
+ {
+ request_control(reader, writer);
+ break;
+ }
+ }
+ }
+ /* </message> and close document */
+ xmlTextWriterEndDocument(writer);
+ xmlFreeTextWriter(writer);
+}
+
+/**
+ * cleanup helper function for open file descriptors
+ */
+static void closefdp(int *fd)
+{
+ close(*fd);
+}
+
+/**
+ * read from a opened connection and process it
+ */
+static job_requeue_t process(int *fdp)
+{
+ int oldstate, fd = *fdp;
+ char buffer[4096];
+ size_t len;
+ xmlTextReaderPtr reader;
+ char *id = NULL, *type = NULL;
+
+ pthread_cleanup_push((void*)closefdp, (void*)&fd);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+ len = read(fd, buffer, sizeof(buffer));
+ pthread_setcancelstate(oldstate, NULL);
+ pthread_cleanup_pop(0);
+ if (len <= 0)
+ {
+ close(fd);
+ DBG2(DBG_CFG, "SMP XML connection closed");
+ return JOB_REQUEUE_NONE;
+ }
+ DBG3(DBG_CFG, "got XML request: %b", buffer, len);
+
+ reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0);
+ if (reader == NULL)
+ {
+ DBG1(DBG_CFG, "opening SMP XML reader failed");
+ return JOB_REQUEUE_FAIR;;
+ }
+
+ /* read message type and id */
+ while (xmlTextReaderRead(reader))
+ {
+ if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT &&
+ streq(xmlTextReaderConstName(reader), "message"))
+ {
+ id = xmlTextReaderGetAttribute(reader, "id");
+ type = xmlTextReaderGetAttribute(reader, "type");
+ break;
+ }
+ }
+
+ /* process message */
+ if (id && type)
+ {
+ if (streq(type, "request"))
+ {
+ request(reader, id, fd);
+ }
+ else
+ {
+ /* response(reader, id) */
+ }
+ }
+ xmlFreeTextReader(reader);
+ return JOB_REQUEUE_FAIR;;
+}
+
+/**
+ * accept from XML socket and create jobs to process connections
+ */
+static job_requeue_t dispatch(private_xml_t *this)
+{
+ struct sockaddr_un strokeaddr;
+ int oldstate, fd, *fdp, strokeaddrlen = sizeof(strokeaddr);
+ callback_job_t *job;
+
+ /* wait for connections, but allow thread to terminate */
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+ fd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
+ pthread_setcancelstate(oldstate, NULL);
+
+ if (fd < 0)
+ {
+ DBG1(DBG_CFG, "accepting SMP XML socket failed: %s", strerror(errno));
+ sleep(1);
+ return JOB_REQUEUE_FAIR;;
+ }
+
+ fdp = malloc_thing(int);
+ *fdp = fd;
+ job = callback_job_create((callback_job_cb_t)process, fdp, free, this->job);
+ charon->processor->queue_job(charon->processor, (job_t*)job);
+
+ return JOB_REQUEUE_DIRECT;
+}
+
+/**
+ * Implementation of itnerface_t.destroy.
+ */
+static void destroy(private_xml_t *this)
+{
+ this->job->cancel(this->job);
+ close(this->socket);
+ free(this);
+}
+
+/*
+ * Described in header file
+ */
+plugin_t *plugin_create()
+{
+ struct sockaddr_un unix_addr = { AF_UNIX, IPSEC_PIDDIR "/charon.xml"};
+ private_xml_t *this = malloc_thing(private_xml_t);
+ mode_t old;
+
+ this->public.plugin.destroy = (void (*)(plugin_t*))destroy;
+
+ /* set up unix socket */
+ this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (this->socket == -1)
+ {
+ DBG1(DBG_CFG, "could not create XML socket");
+ free(this);
+ return NULL;
+ }
+
+ unlink(unix_addr.sun_path);
+ old = umask(~(S_IRWXU | S_IRWXG));
+ if (bind(this->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0)
+ {
+ DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno));
+ close(this->socket);
+ free(this);
+ return NULL;
+ }
+ umask(old);
+ if (chown(unix_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0)
+ {
+ DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno));
+ }
+
+ if (listen(this->socket, 5) < 0)
+ {
+ DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno));
+ close(this->socket);
+ free(this);
+ return NULL;
+ }
+
+ this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
+ charon->processor->queue_job(charon->processor, (job_t*)this->job);
+
+ return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/xml/xml.h b/src/charon/plugins/xml/xml.h
new file mode 100644
index 000000000..289fca5f6
--- /dev/null
+++ b/src/charon/plugins/xml/xml.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007-2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup xml xml
+ * @ingroup cplugins
+ *
+ * @defgroup xml_i xml
+ * @{ @ingroup xml
+ */
+
+#ifndef XML_H_
+#define XML_H_
+
+#include <plugins/plugin.h>
+
+typedef struct xml_t xml_t;
+
+/**
+ * XML configuration and control interface.
+ *
+ * The XML interface uses a socket and a to communicate. The syntax is strict
+ * XML, defined in the schema.xml specification.
+ */
+struct xml_t {
+
+ /**
+ * implements the plugin interface.
+ */
+ plugin_t plugin;
+};
+
+/**
+ * Create a xml plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* XML_H_ @}*/