summaryrefslogtreecommitdiff
path: root/src/charon/sa
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2007-04-12 20:41:31 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2007-04-12 20:41:31 +0000
commit774a362e87feab25f1be16fbca08269ddc7121a4 (patch)
treecf71f4e7466468ac3edc2127125f333224a9acfb /src/charon/sa
parentc54a140a445bfe7aa66721f68bb0781f26add91c (diff)
downloadvyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.tar.gz
vyos-strongswan-774a362e87feab25f1be16fbca08269ddc7121a4.zip
Major new upstream release, just ran svn-upgrade for now (and wrote some
debian/changelong entries).
Diffstat (limited to 'src/charon/sa')
-rw-r--r--src/charon/sa/authenticators/authenticator.c56
-rw-r--r--src/charon/sa/authenticators/authenticator.h139
-rw-r--r--src/charon/sa/authenticators/eap/eap_identity.c135
-rw-r--r--src/charon/sa/authenticators/eap/eap_identity.h59
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.c245
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.h242
-rw-r--r--src/charon/sa/authenticators/eap/eap_sim.c703
-rw-r--r--src/charon/sa/authenticators/eap/eap_sim.h141
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.c360
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.h156
-rw-r--r--src/charon/sa/authenticators/psk_authenticator.c204
-rw-r--r--src/charon/sa/authenticators/psk_authenticator.h57
-rw-r--r--src/charon/sa/authenticators/rsa_authenticator.c180
-rw-r--r--src/charon/sa/authenticators/rsa_authenticator.h57
-rw-r--r--src/charon/sa/child_sa.c1130
-rw-r--r--src/charon/sa/child_sa.h298
-rw-r--r--src/charon/sa/ike_sa.c2032
-rw-r--r--src/charon/sa/ike_sa.h649
-rw-r--r--src/charon/sa/ike_sa_id.c215
-rw-r--r--src/charon/sa/ike_sa_id.h147
-rw-r--r--src/charon/sa/ike_sa_manager.c914
-rw-r--r--src/charon/sa/ike_sa_manager.h231
-rw-r--r--src/charon/sa/task_manager.c854
-rw-r--r--src/charon/sa/task_manager.h144
-rw-r--r--src/charon/sa/tasks/child_create.c804
-rw-r--r--src/charon/sa/tasks/child_create.h88
-rw-r--r--src/charon/sa/tasks/child_delete.c292
-rw-r--r--src/charon/sa/tasks/child_delete.h66
-rw-r--r--src/charon/sa/tasks/child_rekey.c346
-rw-r--r--src/charon/sa/tasks/child_rekey.h70
-rw-r--r--src/charon/sa/tasks/ike_auth.c750
-rw-r--r--src/charon/sa/tasks/ike_auth.h64
-rw-r--r--src/charon/sa/tasks/ike_cert.c370
-rw-r--r--src/charon/sa/tasks/ike_cert.h61
-rw-r--r--src/charon/sa/tasks/ike_config.c428
-rw-r--r--src/charon/sa/tasks/ike_config.h59
-rw-r--r--src/charon/sa/tasks/ike_delete.c172
-rw-r--r--src/charon/sa/tasks/ike_delete.h57
-rw-r--r--src/charon/sa/tasks/ike_dpd.c106
-rw-r--r--src/charon/sa/tasks/ike_dpd.h58
-rw-r--r--src/charon/sa/tasks/ike_init.c598
-rw-r--r--src/charon/sa/tasks/ike_init.h68
-rw-r--r--src/charon/sa/tasks/ike_natd.c371
-rw-r--r--src/charon/sa/tasks/ike_natd.h57
-rw-r--r--src/charon/sa/tasks/ike_rekey.c329
-rw-r--r--src/charon/sa/tasks/ike_rekey.h69
-rw-r--r--src/charon/sa/tasks/task.c38
-rw-r--r--src/charon/sa/tasks/task.h151
48 files changed, 14820 insertions, 0 deletions
diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c
new file mode 100644
index 000000000..707aae9ad
--- /dev/null
+++ b/src/charon/sa/authenticators/authenticator.c
@@ -0,0 +1,56 @@
+/**
+ * @file authenticator.c
+ *
+ * @brief Generic constructor for authenticators.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <string.h>
+
+#include "authenticator.h"
+
+#include <sa/authenticators/rsa_authenticator.h>
+#include <sa/authenticators/psk_authenticator.h>
+#include <sa/authenticators/eap_authenticator.h>
+
+
+ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS,
+ "RSA signature",
+ "pre-shared key",
+ "DSS signature");
+ENUM_NEXT(auth_method_names, AUTH_EAP, AUTH_EAP, AUTH_DSS,
+ "EAP");
+ENUM_END(auth_method_names, AUTH_EAP);
+
+/*
+ * Described in header.
+ */
+authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method)
+{
+ switch (auth_method)
+ {
+ case AUTH_RSA:
+ return (authenticator_t*)rsa_authenticator_create(ike_sa);
+ case AUTH_PSK:
+ return (authenticator_t*)psk_authenticator_create(ike_sa);
+ case AUTH_EAP:
+ return (authenticator_t*)eap_authenticator_create(ike_sa);
+ default:
+ return NULL;
+ }
+}
diff --git a/src/charon/sa/authenticators/authenticator.h b/src/charon/sa/authenticators/authenticator.h
new file mode 100644
index 000000000..c7b0fc81a
--- /dev/null
+++ b/src/charon/sa/authenticators/authenticator.h
@@ -0,0 +1,139 @@
+/**
+ * @file authenticator.h
+ *
+ * @brief Interface of authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef AUTHENTICATOR_H_
+#define AUTHENTICATOR_H_
+
+typedef enum auth_method_t auth_method_t;
+typedef struct authenticator_t authenticator_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <encoding/payloads/auth_payload.h>
+
+/**
+ * Method to use for authentication.
+ *
+ * @ingroup authenticators
+ */
+enum auth_method_t {
+ /**
+ * Computed as specified in section 2.15 of RFC using
+ * an RSA private key over a PKCS#1 padded hash.
+ */
+ AUTH_RSA = 1,
+
+ /**
+ * Computed as specified in section 2.15 of RFC using the
+ * shared key associated with the identity in the ID payload
+ * and the negotiated prf function
+ */
+ AUTH_PSK = 2,
+
+ /**
+ * Computed as specified in section 2.15 of RFC using a
+ * DSS private key over a SHA-1 hash.
+ */
+ AUTH_DSS = 3,
+
+ /**
+ * EAP authentication. This value is never negotiated and therefore
+ * a value from private use.
+ */
+ AUTH_EAP = 201,
+};
+
+/**
+ * enum names for auth_method_t.
+ *
+ * @ingroup authenticators
+ */
+extern enum_name_t *auth_method_names;
+
+/**
+ * @brief Authenticator interface implemented by the various authenticators.
+ *
+ * Currently the following two AUTH methods are supported:
+ * - shared key message integrity code (AUTH_PSK)
+ * - RSA digital signature (AUTH_RSA)
+ *
+ * @b Constructors:
+ * - authenticator_create()
+ *
+ * @ingroup authenticators
+ */
+struct authenticator_t {
+
+ /**
+ * @brief Verify a received authentication payload.
+ *
+ * @param this calling object
+ * @param ike_sa_init binary representation of received ike_sa_init
+ * @param my_nonce the sent nonce
+ * @param auth_payload authentication payload to verify
+ *
+ * @return
+ * - SUCCESS,
+ * - FAILED if verification failed
+ * - INVALID_ARG if auth_method does not match
+ * - NOT_FOUND if credentials not found
+ */
+ status_t (*verify) (authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t my_nonce, auth_payload_t *auth_payload);
+
+ /**
+ * @brief Build an authentication payload to send to the other peer.
+ *
+ * @param this calling object
+ * @param ike_sa_init binary representation of sent ike_sa_init
+ * @param other_nonce the received nonce
+ * @param[out] auth_payload the resulting authentication payload
+ *
+ * @return
+ * - SUCCESS,
+ * - NOT_FOUND if the data for AUTH method could not be found
+ */
+ status_t (*build) (authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t other_nonce, auth_payload_t **auth_payload);
+
+ /**
+ * @brief Destroys a authenticator_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (authenticator_t *this);
+};
+
+/**
+ * @brief Creates an authenticator for the specified auth method.
+ *
+ * @param ike_sa associated ike_sa
+ * @param auth_method authentication method to use for build()/verify()
+ *
+ * @return authenticator_t object
+ *
+ * @ingroup authenticators
+ */
+authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method);
+
+#endif /* AUTHENTICATOR_H_ */
diff --git a/src/charon/sa/authenticators/eap/eap_identity.c b/src/charon/sa/authenticators/eap/eap_identity.c
new file mode 100644
index 000000000..12a8bf7cc
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_identity.c
@@ -0,0 +1,135 @@
+/**
+ * @file eap_identity.c
+ *
+ * @brief Implementation of eap_identity_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "eap_identity.h"
+
+#include <daemon.h>
+#include <library.h>
+
+typedef struct private_eap_identity_t private_eap_identity_t;
+
+/**
+ * Private data of an eap_identity_t object.
+ */
+struct private_eap_identity_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_identity_t public;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+};
+
+/**
+ * Implementation of eap_method_t.process for the peer
+ */
+static status_t process(private_eap_identity_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ chunk_t id, hdr;
+
+ hdr = chunk_alloca(5);
+ id = this->peer->get_encoding(this->peer);
+
+ *(hdr.ptr + 0) = EAP_RESPONSE;
+ *(hdr.ptr + 1) = in->get_identifier(in);
+ *(u_int16_t*)(hdr.ptr + 2) = htons(hdr.len + id.len);
+ *(hdr.ptr + 4) = EAP_IDENTITY;
+
+ *out = eap_payload_create_data(chunk_cata("cc", hdr, id));
+ return SUCCESS;
+
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the peer
+ */
+static status_t initiate(private_eap_identity_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_identity_t *this)
+{
+ return EAP_IDENTITY;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_identity_t *this, chunk_t *msk)
+{
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_identity_t *this)
+{
+ return FALSE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_identity_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_identity_t *eap_create(eap_role_t role,
+ identification_t *server, identification_t *peer)
+{
+ private_eap_identity_t *this;
+
+ if (role != EAP_PEER)
+ {
+ return NULL;
+ }
+
+ this = malloc_thing(private_eap_identity_t);
+
+ /* public functions */
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ /* private data */
+ this->peer = peer;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/authenticators/eap/eap_identity.h b/src/charon/sa/authenticators/eap/eap_identity.h
new file mode 100644
index 000000000..20f0f0b67
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_identity.h
@@ -0,0 +1,59 @@
+/**
+ * @file eap_identity.h
+ *
+ * @brief Interface of eap_identity_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef EAP_IDENTITY_H_
+#define EAP_IDENTITY_H_
+
+typedef struct eap_identity_t eap_identity_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * @brief Implementation of the eap_method_t interface using EAP Identity.
+ *
+ * @b Constructors:
+ * - eap_identity_create()
+ * - eap_client_create() using eap_method EAP_IDENTITY
+ *
+ * @ingroup eap
+ */
+struct eap_identity_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * @brief Creates the EAP method EAP Identity.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_identity_t object
+ *
+ * @ingroup eap
+ */
+eap_identity_t *eap_create(eap_role_t role,
+ identification_t *server, identification_t *peer);
+
+#endif /* EAP_IDENTITY_H_ */
diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c
new file mode 100644
index 000000000..a4d8abb58
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_method.c
@@ -0,0 +1,245 @@
+/**
+ * @file eap_method.c
+ *
+ * @brief Generic constructor for eap_methods.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <error.h>
+#include <dlfcn.h>
+
+#include "eap_method.h"
+
+#include <daemon.h>
+#include <library.h>
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+
+
+ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD,
+ "EAP_IDENTITY",
+ "EAP_NOTIFICATION",
+ "EAP_NAK",
+ "EAP_MD5",
+ "EAP_ONE_TIME_PASSWORD",
+ "EAP_TOKEN_CARD");
+ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_TOKEN_CARD,
+ "EAP_SIM");
+ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM,
+ "EAP_AKA");
+ENUM_END(eap_type_names, EAP_AKA);
+
+ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE,
+ "EAP_REQUEST",
+ "EAP_RESPONSE",
+ "EAP_SUCCESS",
+ "EAP_FAILURE",
+);
+
+ENUM(eap_role_names, EAP_SERVER, EAP_PEER,
+ "EAP_SERVER",
+ "EAP_PEER",
+);
+
+
+typedef struct module_entry_t module_entry_t;
+
+/**
+ * Representation of a loaded module: EAP type, library handle, constructor
+ */
+struct module_entry_t {
+ eap_type_t type;
+ void *handle;
+ eap_constructor_t constructor;
+};
+
+/** List of module_entry_t's */
+static linked_list_t *modules = NULL;
+
+/**
+ * unload modules at daemon shutdown
+ */
+void eap_method_unload()
+{
+ if (modules)
+ {
+ module_entry_t *entry;
+
+ while (modules->remove_last(modules, (void**)&entry) == SUCCESS)
+ {
+ DBG2(DBG_CFG, "unloaded module for %s", eap_type_names, entry->type);
+ dlclose(entry->handle);
+ free(entry);
+ }
+ modules->destroy(modules);
+ modules = NULL;
+ }
+}
+
+/**
+ * Load EAP modules at daemon startup
+ */
+void eap_method_load(char *directory)
+{
+ struct dirent* entry;
+ struct stat stb;
+ DIR* dir;
+
+ eap_method_unload();
+ modules = linked_list_create();
+
+ if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR))
+ {
+ DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
+ return;
+ }
+ if (stb.st_uid != 0)
+ {
+ DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory);
+ return;
+ }
+ if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
+ {
+ DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory);
+ return;
+ }
+
+ dir = opendir(directory);
+ if (dir == NULL)
+ {
+ DBG1(DBG_CFG, "error opening EAP modules directory %s", directory);
+ return;
+ }
+
+ DBG1(DBG_CFG, "loading EAP modules from '%s'", directory);
+
+ while ((entry = readdir(dir)) != NULL)
+ {
+ char file[256];
+ module_entry_t module, *loaded_module;
+ eap_method_t *method;
+ identification_t *id;
+ char *ending;
+
+ snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name);
+
+ if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG))
+ {
+ DBG2(DBG_CFG, " skipping %s, doesn't look like a file",
+ entry->d_name);
+ continue;
+ }
+ ending = entry->d_name + strlen(entry->d_name) - 3;
+ if (ending <= entry->d_name || !streq(ending, ".so"))
+ {
+ /* skip anything which does not look like a library */
+ DBG2(DBG_CFG, " skipping %s, doesn't look like a library",
+ entry->d_name);
+ continue;
+ }
+ if (stb.st_uid != 0)
+ {
+ DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name);
+ return;
+ }
+ if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP)
+ {
+ DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name);
+ continue;
+ }
+
+ /* try to load the library */
+ module.handle = dlopen(file, RTLD_LAZY);
+ if (module.handle == NULL)
+ {
+ DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name,
+ dlerror());
+ continue;
+ }
+ module.constructor = dlsym(module.handle, "eap_create");
+ if (module.constructor == NULL)
+ {
+ DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped",
+ entry->d_name);
+ dlclose(module.handle);
+ continue;
+ }
+
+ /* get the type implemented in the method, create an instance for it */
+ id = identification_create_from_string("john@doe.xyz");
+ method = module.constructor(EAP_SERVER, id, id);
+ if (method == NULL)
+ {
+ method = module.constructor(EAP_PEER, id, id);
+ }
+ id->destroy(id);
+ if (method == NULL)
+ {
+ DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped",
+ entry->d_name);
+ dlclose(module.handle);
+ continue;
+ }
+ module.type = method->get_type(method);
+ method->destroy(method);
+
+ DBG1(DBG_CFG, " loaded EAP method %N successfully from %s",
+ eap_type_names, module.type, entry->d_name);
+
+ loaded_module = malloc_thing(module_entry_t);
+ memcpy(loaded_module, &module, sizeof(module));
+ modules->insert_last(modules, loaded_module);
+ }
+ closedir(dir);
+}
+
+/*
+ * Described in header.
+ */
+eap_method_t *eap_method_create(eap_type_t type, eap_role_t role,
+ identification_t *server,
+ identification_t *peer)
+{
+ eap_method_t *method = NULL;
+ iterator_t *iterator;
+ module_entry_t *entry;
+
+ iterator = modules->create_iterator(modules, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ if (entry->type == type)
+ {
+ method = entry->constructor(role, server, peer);
+ if (method)
+ {
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (method == NULL)
+ {
+ DBG1(DBG_CFG, "no EAP module found for %N %N",
+ eap_type_names, type, eap_role_names, role);
+ }
+ return method;
+}
diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h
new file mode 100644
index 000000000..d43dc001f
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_method.h
@@ -0,0 +1,242 @@
+/**
+ * @file eap_method.h
+ *
+ * @brief Interface eap_method_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef EAP_METHOD_H_
+#define EAP_METHOD_H_
+
+typedef struct eap_method_t eap_method_t;
+typedef enum eap_role_t eap_role_t;
+typedef enum eap_type_t eap_type_t;
+typedef enum eap_code_t eap_code_t;
+
+#include <library.h>
+#include <utils/identification.h>
+#include <encoding/payloads/eap_payload.h>
+
+/**
+ * Role of an eap_method, SERVER or PEER (client)
+ *
+ * @ingroup eap
+ */
+enum eap_role_t {
+ EAP_SERVER,
+ EAP_PEER,
+};
+/**
+ * enum names for eap_role_t.
+ *
+ * @ingroup eap
+ */
+extern enum_name_t *eap_role_names;
+
+/**
+ * EAP types, defines the EAP method implementation
+ *
+ * @ingroup eap
+ */
+enum eap_type_t {
+ EAP_IDENTITY = 1,
+ EAP_NOTIFICATION = 2,
+ EAP_NAK = 3,
+ EAP_MD5 = 4,
+ EAP_ONE_TIME_PASSWORD = 5,
+ EAP_TOKEN_CARD = 6,
+ EAP_SIM = 18,
+ EAP_AKA = 23,
+};
+
+/**
+ * enum names for eap_type_t.
+ *
+ * @ingroup eap
+ */
+extern enum_name_t *eap_type_names;
+
+/**
+ * EAP code, type of an EAP message
+ *
+ * @ingroup eap
+ */
+enum eap_code_t {
+ EAP_REQUEST = 1,
+ EAP_RESPONSE = 2,
+ EAP_SUCCESS = 3,
+ EAP_FAILURE = 4,
+};
+
+/**
+ * enum names for eap_code_t.
+ *
+ * @ingroup eap
+ */
+extern enum_name_t *eap_code_names;
+
+
+/**
+ * @brief Interface of an EAP method for server and client side.
+ *
+ * An EAP method initiates an EAP exchange and processes requests and
+ * responses. An EAP method may need multiple exchanges before succeeding, and
+ * the eap_authentication may use multiple EAP methods to authenticate a peer.
+ * To accomplish these requirements, all EAP methods have their own
+ * implementation while the eap_authenticatior uses one or more of these
+ * EAP methods. Sending of EAP(SUCCESS/FAILURE) message is not the job
+ * of the method, the eap_authenticator does this.
+ * An EAP method may establish a MSK, this is used the complete the
+ * authentication. Even if a mutual EAP method is used, the traditional
+ * AUTH payloads are required. Only these include the nonces and messages from
+ * ike_sa_init and therefore prevent man in the middle attacks.
+ *
+ * @b Constructors:
+ * - eap_method_create()
+ *
+ * @ingroup eap
+ */
+struct eap_method_t {
+
+ /**
+ * @brief Initiate the EAP exchange.
+ *
+ * initiate() is only useable for server implementations, as clients only
+ * reply to server requests.
+ * A eap_payload is created in "out" if result is NEED_MORE.
+ *
+ * @param this calling object
+ * @param out eap_payload to send to the client
+ * @return
+ * - NEED_MORE, if an other exchange is required
+ * - FAILED, if unable to create eap request payload
+ */
+ status_t (*initiate) (eap_method_t *this, eap_payload_t **out);
+
+ /**
+ * @brief Process a received EAP message.
+ *
+ * A eap_payload is created in "out" if result is NEED_MORE.
+ *
+ * @param this calling object
+ * @param in eap_payload response received
+ * @param out created eap_payload to send
+ * @return
+ * - NEED_MORE, if an other exchange is required
+ * - FAILED, if EAP method failed
+ * - SUCCESS, if EAP method succeeded
+ */
+ status_t (*process) (eap_method_t *this, eap_payload_t *in,
+ eap_payload_t **out);
+
+ /**
+ * @brief Get the EAP type implemented in this method.
+ *
+ * @param this calling object
+ * @return type of the EAP method
+ */
+ eap_type_t (*get_type) (eap_method_t *this);
+
+ /**
+ * @brief Check if this EAP method authenticates the server.
+ *
+ * Some EAP methods provide mutual authentication and
+ * allow authentication using only EAP, if the peer supports it.
+ *
+ * @param this calling object
+ * @return TRUE if methods provides mutual authentication
+ */
+ bool (*is_mutual) (eap_method_t *this);
+
+ /**
+ * @brief Get the MSK established by this EAP method.
+ *
+ * Not all EAP methods establish a shared secret.
+ *
+ * @param this calling object
+ * @param msk chunk receiving internal stored MSK
+ * @return
+ * - SUCCESS, or
+ * - FAILED, if MSK not established (yet)
+ */
+ status_t (*get_msk) (eap_method_t *this, chunk_t *msk);
+
+ /**
+ * @brief Destroys a eap_method_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (eap_method_t *this);
+};
+
+/**
+ * @brief Creates an EAP method for a specific type and role.
+ *
+ * @param eap_type EAP type to use
+ * @param role role of the eap_method, server or peer
+ * @param server ID of acting server
+ * @param peer ID of involved peer (client)
+ * @return eap_method_t object
+ *
+ * @ingroup eap
+ */
+eap_method_t *eap_method_create(eap_type_t eap_type, eap_role_t role,
+ identification_t *server, identification_t *peer);
+
+/**
+ * @brief (Re-)Load all EAP modules in the EAP modules directory.
+ *
+ * For security reasons, the directory and all it's modules must be owned
+ * by root and must not be writeable by someone else.
+ *
+ * @param dir directory of the EAP modules
+ *
+ * @ingroup eap
+ */
+void eap_method_load(char *directory);
+
+/**
+ * @brief Unload all loaded EAP modules
+ *
+ * @ingroup eap
+ */
+void eap_method_unload();
+
+/**
+ * @brief Constructor definition for a pluggable EAP module.
+ *
+ * Each EAP module must define a constructor function which will return
+ * an initialized object with the methods defined in eap_method_t. The
+ * constructor must be named eap_create() and it's signature must be equal
+ * to that of eap_constructor_t.
+ * A module may implement only a single role. If it does not support the role
+ * requested, NULL should be returned. Multiple modules are allowed of the
+ * same EAP type to support seperate implementations of peer/server.
+ *
+ * @param role role the module will play, peer or server
+ * @param server ID of the server to use for credential lookup
+ * @param peer ID of the peer to use for credential lookup
+ * @return implementation of the eap_method_t interface
+ *
+ * @ingroup eap
+ */
+typedef eap_method_t *(*eap_constructor_t)(eap_role_t role,
+ identification_t *server,
+ identification_t *peer);
+
+#endif /* EAP_METHOD_H_ */
diff --git a/src/charon/sa/authenticators/eap/eap_sim.c b/src/charon/sa/authenticators/eap/eap_sim.c
new file mode 100644
index 000000000..3dc59fb6b
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_sim.c
@@ -0,0 +1,703 @@
+/**
+ * @file eap_sim.c
+ *
+ * @brief Implementation of eap_sim_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "eap_sim.h"
+
+#include <dlfcn.h>
+
+#include <daemon.h>
+#include <library.h>
+
+#define MAX_TRIES 3
+
+ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
+ "SIM_START",
+ "SIM_CHALLENGE",
+ "SIM_NOTIFICATION",
+ "SIM_13",
+ "SIM_CLIENT_ERROR",
+);
+
+ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
+ "AT_END",
+ "AT_0",
+ "AT_RAND",
+ "AT_AUTN",
+ "AT_RES",
+ "AT_AUTS",
+ "AT_5",
+ "AT_PADDING",
+ "AT_NONCE_MT",
+ "AT_8",
+ "AT_9",
+ "AT_PERMANENT_ID_REQ",
+ "AT_MAC",
+ "AT_NOTIFICATION",
+ "AT_ANY_ID_REQ",
+ "AT_IDENTITY",
+ "AT_VERSION_LIST",
+ "AT_SELECTED_VERSION",
+ "AT_FULLAUTH_ID_REQ",
+ "AT_18",
+ "AT_COUNTER",
+ "AT_COUNTER_TOO_SMALL",
+ "AT_NONCE_S",
+ "AT_CLIENT_ERROR_CODE");
+ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
+ "AT_IV",
+ "AT_ENCR_DATA",
+ "AT_131",
+ "AT_NEXT_PSEUDONYM",
+ "AT_NEXT_REAUTH_ID",
+ "AT_CHECKCODE",
+ "AT_RESULT_IND");
+ENUM_END(sim_attribute_names, AT_RESULT_IND);
+
+
+typedef struct private_eap_sim_t private_eap_sim_t;
+
+/**
+ * Private data of an eap_sim_t object.
+ */
+struct private_eap_sim_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_sim_t public;
+
+ /**
+ * ID of ourself
+ */
+ identification_t *peer;
+
+ /**
+ * SIM cardreader function loaded from library
+ */
+ sim_algo_t alg;
+
+ /**
+ * handle of the loaded library
+ */
+ void *handle;
+
+ /**
+ * how many times we try to authenticate
+ */
+ int tries;
+
+ /**
+ * version this implementation uses
+ */
+ chunk_t version;
+
+ /**
+ * version list received from server
+ */
+ chunk_t version_list;
+
+ /**
+ * Nonce value used in AT_NONCE_MT
+ */
+ chunk_t nonce;
+
+ /**
+ * k_encr key derived from MK
+ */
+ chunk_t k_encr;
+
+ /**
+ * k_auth key derived from MK, used for AT_MAC verification
+ */
+ chunk_t k_auth;
+
+ /**
+ * MSK, used for EAP-SIM based IKEv2 authentication
+ */
+ chunk_t msk;
+
+ /**
+ * EMSK, extendes MSK for further uses
+ */
+ chunk_t emsk;
+};
+
+/** length of the AT_NONCE_MT nonce value */
+#define NONCE_LEN 16
+/** length of the AT_MAC value */
+#define MAC_LEN 16
+/** length of the AT_RAND value */
+#define RAND_LEN 16
+/** length of the k_encr key */
+#define KENCR_LEN 16
+/** length of the k_auth key */
+#define KAUTH_LEN 16
+/** length of the MSK */
+#define MSK_LEN 64
+/** length of the EMSK */
+#define EMSK_LEN 64
+
+/* client error codes used in AT_CLIENT_ERROR_CODE */
+char client_error_general_buf[] = {0x00, 0x01};
+char client_error_unsupported_buf[] = {0x00, 0x02};
+char client_error_insufficient_buf[] = {0x00, 0x03};
+char client_error_notfresh_buf[] = {0x00, 0x04};
+chunk_t client_error_general = chunk_from_buf(client_error_general_buf);
+chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf);
+chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf);
+chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf);
+
+/**
+ * Read EAP and EAP-SIM header, return SIM type
+ */
+static sim_subtype_t read_header(chunk_t *message)
+{
+ sim_subtype_t type;
+
+ if (message->len < 8)
+ {
+ *message = chunk_empty;
+ return 0;
+ }
+ type = *(message->ptr + 5);
+ *message = chunk_skip(*message, 8);
+ return type;
+}
+
+/**
+ * read the next attribute from the chunk data
+ */
+static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
+{
+ sim_attribute_t attribute;
+ size_t length;
+
+ DBG3(DBG_IKE, "reading attribute from %B", message);
+
+ if (message->len < 2)
+ {
+ return AT_END;
+ }
+ attribute = *message->ptr++;
+ length = *message->ptr++ * 4 - 2;
+ message->len -= 2;
+ DBG3(DBG_IKE, "found attribute %N with length %d",
+ sim_attribute_names, attribute, length);
+
+ if (length > message->len)
+ {
+ return AT_END;
+ }
+ data->len = length;
+ data->ptr = message->ptr;
+ *message = chunk_skip(*message, length);
+ return attribute;
+}
+
+/**
+ * Build an EAP-SIM payload using a variable length attribute list.
+ * The variable argument takes a sim_attribute_t followed by its data in a chunk.
+ */
+static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier,
+ sim_subtype_t type, ...)
+{
+ chunk_t message = chunk_alloca(512);
+ chunk_t pos = message;
+ eap_payload_t *payload;
+ va_list args;
+ sim_attribute_t attr;
+ u_int8_t *mac_pos = NULL;
+ chunk_t mac_data = chunk_empty;
+
+ /* write EAP header, skip length bytes */
+ *pos.ptr++ = EAP_RESPONSE;
+ *pos.ptr++ = identifier;
+ pos.ptr += 2;
+ pos.len -= 4;
+ /* write SIM header with type and subtype, zero reserved bytes */
+ *pos.ptr++ = EAP_SIM;
+ *pos.ptr++ = type;
+ *pos.ptr++ = 0;
+ *pos.ptr++ = 0;
+ pos.len -= 4;
+
+ va_start(args, type);
+ while ((attr = va_arg(args, sim_attribute_t)) != AT_END)
+ {
+ chunk_t data = va_arg(args, chunk_t);
+
+ DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data);
+
+ /* write attribute header */
+ *pos.ptr++ = attr;
+ pos.len--;
+
+ switch (attr)
+ {
+ case AT_CLIENT_ERROR_CODE:
+ case AT_SELECTED_VERSION:
+ {
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_IDENTITY:
+ {
+ /* align up to four byte */
+ if (data.len % 4)
+ {
+ chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
+ memset(tmp.ptr, 0, tmp.len);
+ memcpy(tmp.ptr, data.ptr, data.len);
+ data = tmp;
+ }
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ /* actual length in bytes */
+ *(u_int16_t*)pos.ptr = htons(data.len);
+ pos = chunk_skip(pos, sizeof(u_int16_t));
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_NONCE_MT:
+ {
+ *pos.ptr = data.len/4 + 1;
+ pos = chunk_skip(pos, 1);
+ memset(pos.ptr, 0, 2);
+ pos = chunk_skip(pos, 2);
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ case AT_MAC:
+ {
+ *pos.ptr++ = 5; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ mac_pos = pos.ptr;
+ memset(mac_pos, 0, MAC_LEN);
+ pos = chunk_skip(pos, MAC_LEN);
+ mac_data = data;
+ break;
+ }
+ case AT_RAND:
+ {
+ *pos.ptr++ = data.len/4 + 1; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ *pos.ptr++ = 0; pos.len--;
+ memcpy(pos.ptr, data.ptr, data.len);
+ pos = chunk_skip(pos, data.len);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
+ sim_attribute_names, attr);
+ break;
+ }
+ }
+ va_end(args);
+
+ /* calculate message length, write into header */
+ message.len = pos.ptr - message.ptr;
+ *(u_int16_t*)(message.ptr + 2) = htons(message.len);
+
+ /* create MAC if AT_MAC attribte was included. Append supplied va_arg
+ * chunk mac_data to "to-sign" chunk */
+ if (mac_pos)
+ {
+ signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
+ signer->set_key(signer, this->k_auth);
+ mac_data = chunk_cata("cc", message, mac_data);
+ signer->get_signature(signer, mac_data, mac_pos);
+ DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b",
+ &mac_data, mac_pos, MAC_LEN);
+ signer->destroy(signer);
+ }
+
+ payload = eap_payload_create_data(message);
+
+ DBG3(DBG_IKE, "created EAP message %B", &message);
+ return payload;
+}
+
+/**
+ * process an EAP-SIM/Request/Start message
+ */
+static status_t process_start(private_eap_sim_t *this, eap_payload_t *in,
+ eap_payload_t **out)
+{
+ chunk_t message, data;
+ sim_attribute_t attribute, include_id = AT_END;
+ u_int8_t identifier;
+
+ identifier = in->get_identifier(in);
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_VERSION_LIST:
+ {
+ /* check if server supports our implementation */
+ bool found = FALSE;
+ if (data.len > 2)
+ {
+ /* read actual length first */
+ data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2);
+ data = chunk_skip(data, 2);
+ chunk_free(&this->version_list);
+ this->version_list = chunk_clone(data);
+ while (data.len >= this->version.len)
+ {
+ if (memeq(data.ptr, this->version.ptr, this->version.len))
+ {
+ found = TRUE;
+ break;
+ }
+ data = chunk_skip(data, this->version.len);
+ }
+ }
+ if (!found)
+ {
+ DBG1(DBG_IKE, "server does not support EAP_SIM "
+ "version number %#B", &this->version);
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_unsupported,
+ AT_END);
+ return NEED_MORE;
+ }
+ break;
+ }
+ case AT_PERMANENT_ID_REQ:
+ case AT_FULLAUTH_ID_REQ:
+ case AT_ANY_ID_REQ:
+ /* only include AT_IDENTITY if requested */
+ include_id = AT_IDENTITY;
+ break;
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+
+ /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
+ *out = build_payload(this, identifier, SIM_START,
+ AT_SELECTED_VERSION, this->version,
+ AT_NONCE_MT, this->nonce,
+ include_id, this->peer->get_encoding(this->peer),
+ AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * process an EAP-SIM/Request/Challenge message
+ */
+static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
+ eap_payload_t **out)
+{
+ chunk_t message, data, tmp, kcs, kc, sreses, sres, mk;
+ sim_attribute_t attribute;
+ u_int8_t identifier, i;
+ chunk_t mac = chunk_empty, rands = chunk_empty;
+ signer_t *signer;
+ hasher_t *hasher;
+ prf_t *prf;
+
+ if (this->tries-- <= 0)
+ {
+ /* give up without notification. This hack is required as some buggy
+ * server implementations won't respect our client-error. */
+ return FAILED;
+ }
+
+ identifier = in->get_identifier(in);
+ message = in->get_data(in);
+ read_header(&message);
+
+ while ((attribute = read_attribute(&message, &data)) != AT_END)
+ {
+ switch (attribute)
+ {
+ case AT_RAND:
+ {
+ rands = chunk_skip(data, 2);
+ break;
+ }
+ case AT_MAC:
+ {
+ /* backup MAC, zero it inline for later verification */
+ data = chunk_skip(data, 2);
+ mac = chunk_clonea(data);
+ memset(data.ptr, 0, data.len);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
+ sim_attribute_names, attribute);
+ break;
+ }
+ }
+
+ /* excepting two or three RAND, each 16 bytes. We require two valid
+ * and different RANDs */
+ if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) ||
+ memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN))
+ {
+ DBG1(DBG_IKE, "no valid AT_RAND received");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_insufficient,
+ AT_END);
+ return FAILED;
+ }
+ if (mac.len != MAC_LEN)
+ {
+ DBG1(DBG_IKE, "no valid AT_MAC received");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+
+ /* get two or three KCs/SRESes from SIM using RANDs */
+ kcs = kc = chunk_alloca(rands.len / 2);
+ sreses = sres = chunk_alloca(rands.len / 4);
+ while (rands.len > 0)
+ {
+ int kc_len = kc.len, sres_len = sres.len;
+
+ if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len))
+ {
+ DBG1(DBG_IKE, "unable to get triplets from SIM");
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
+ rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len);
+ kc = chunk_skip(kc, kc_len);
+ sres = chunk_skip(sres, sres_len);
+ rands = chunk_skip(rands, RAND_LEN);
+ }
+
+ /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+ tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
+ this->nonce, this->version_list, this->version);
+ hasher = hasher_create(HASH_SHA1);
+ mk = chunk_alloca(hasher->get_hash_size(hasher));
+ hasher->get_hash(hasher, tmp, mk.ptr);
+ hasher->destroy(hasher);
+ DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
+
+ /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
+ * FIPS PRF has 320 bit block size, we need 160 byte for keys
+ * => run prf four times */
+ prf = prf_create(PRF_FIPS_SHA1_160);
+ prf->set_key(prf, mk);
+ tmp = chunk_alloca(prf->get_block_size(prf) * 4);
+ for (i = 0; i < 4; i++)
+ {
+ prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
+ }
+ prf->destroy(prf);
+ chunk_free(&this->k_encr);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
+ MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
+ DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
+ &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
+
+ /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
+ signer = signer_create(AUTH_HMAC_SHA1_128);
+ signer->set_key(signer, this->k_auth);
+ tmp = chunk_cata("cc", in->get_data(in), this->nonce);
+ if (!signer->verify_signature(signer, tmp, mac))
+ {
+ DBG1(DBG_IKE, "AT_MAC verification failed");
+ signer->destroy(signer);
+ *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general,
+ AT_END);
+ return NEED_MORE;
+ }
+ signer->destroy(signer);
+
+ /* build response, AT_MAC is built over "EAP packet | n*SRES" */
+ *out = build_payload(this, identifier, SIM_CHALLENGE,
+ AT_MAC, sreses,
+ AT_END);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of eap_method_t.process for the peer
+ */
+static status_t process(private_eap_sim_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ sim_subtype_t type;
+ chunk_t message;
+
+ message = in->get_data(in);
+ type = read_header(&message);
+
+ switch (type)
+ {
+ case SIM_START:
+ return process_start(this, in, out);
+ case SIM_CHALLENGE:
+ return process_challenge(this, in, out);
+ default:
+ DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
+ sim_subtype_names, type);
+ *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR,
+ AT_CLIENT_ERROR_CODE, client_error_general, AT_END);
+ return NEED_MORE;
+ }
+}
+
+/**
+ * Implementation of eap_method_t.initiate for the peer
+ */
+static status_t initiate(private_eap_sim_t *this, eap_payload_t **out)
+{
+ /* peer never initiates */
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_sim_t *this)
+{
+ return EAP_SIM;
+}
+
+/**
+ * Implementation of eap_method_t.get_msk.
+ */
+static status_t get_msk(private_eap_sim_t *this, chunk_t *msk)
+{
+ if (this->msk.ptr)
+ {
+ *msk = this->msk;
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.is_mutual.
+ */
+static bool is_mutual(private_eap_sim_t *this)
+{
+ return TRUE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_sim_t *this)
+{
+ dlclose(this->handle);
+ chunk_free(&this->nonce);
+ chunk_free(&this->version_list);
+ chunk_free(&this->k_auth);
+ chunk_free(&this->k_encr);
+ chunk_free(&this->msk);
+ chunk_free(&this->emsk);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_sim_t *eap_create(eap_role_t role,
+ identification_t *server, identification_t *peer)
+{
+ private_eap_sim_t *this;
+ randomizer_t *randomizer;
+ static char version[] = {0x00,0x01};
+
+ if (role != EAP_PEER)
+ {
+ return NULL;
+ }
+ this = malloc_thing(private_eap_sim_t);
+
+ this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
+ if (this->handle == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
+ free(this);
+ return NULL;
+ }
+ this->alg = dlsym(this->handle, SIM_READER_ALG);
+ if (this->alg == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'",
+ SIM_READER_ALG, SIM_READER_LIB);
+ dlclose(this->handle);
+ free(this);
+ return NULL;
+ }
+
+ randomizer = randomizer_create();
+ if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
+ &this->nonce))
+ {
+ DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");
+ randomizer->destroy(randomizer);
+ free(this);
+ return NULL;
+ }
+ randomizer->destroy(randomizer);
+
+ /* public functions */
+ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
+ this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ /* private data */
+ this->peer = peer;
+ this->tries = MAX_TRIES;
+ this->version.ptr = version;
+ this->version.len = sizeof(version);
+ this->version_list = chunk_empty;
+ this->k_auth = chunk_empty;
+ this->k_encr = chunk_empty;
+ this->msk = chunk_empty;
+ this->emsk = chunk_empty;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/authenticators/eap/eap_sim.h b/src/charon/sa/authenticators/eap/eap_sim.h
new file mode 100644
index 000000000..10640babe
--- /dev/null
+++ b/src/charon/sa/authenticators/eap/eap_sim.h
@@ -0,0 +1,141 @@
+/**
+ * @file eap_sim.h
+ *
+ * @brief Interface of eap_sim_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef EAP_SIM_H_
+#define EAP_SIM_H_
+
+typedef struct eap_sim_t eap_sim_t;
+typedef enum sim_subtype_t sim_subtype_t;
+typedef enum sim_attribute_t sim_attribute_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Subtypes of SIM messages
+ */
+enum sim_subtype_t {
+ SIM_START = 10,
+ SIM_CHALLENGE = 11,
+ SIM_NOTIFICATION = 12,
+ SIM_CLIENT_ERROR = 14,
+};
+
+/**
+ * enum names for sim_subtype_t
+ */
+extern enum_name_t *sim_subtype_names;
+
+enum sim_attribute_t {
+ /** defines the end of attribute list */
+ AT_END = -1,
+ AT_RAND = 1,
+ AT_AUTN = 2,
+ AT_RES = 3,
+ AT_AUTS = 4,
+ AT_PADDING = 6,
+ AT_NONCE_MT = 7,
+ AT_PERMANENT_ID_REQ = 10,
+ AT_MAC = 11,
+ AT_NOTIFICATION = 12,
+ AT_ANY_ID_REQ = 13,
+ AT_IDENTITY = 14,
+ AT_VERSION_LIST = 15,
+ AT_SELECTED_VERSION = 16,
+ AT_FULLAUTH_ID_REQ = 17,
+ AT_COUNTER = 19,
+ AT_COUNTER_TOO_SMALL = 20,
+ AT_NONCE_S = 21,
+ AT_CLIENT_ERROR_CODE = 22,
+ AT_IV = 129,
+ AT_ENCR_DATA = 130,
+ AT_NEXT_PSEUDONYM = 132,
+ AT_NEXT_REAUTH_ID = 133,
+ AT_CHECKCODE = 134,
+ AT_RESULT_IND = 135,
+};
+
+/**
+ * enum names for sim_subtype_t
+ */
+extern enum_name_t *sim_attribute_names;
+
+/**
+ * @brief Cardreaders SIM function.
+ *
+ * @param rand RAND to run algo with
+ * @param rand_length length of value in rand
+ * @param sres buffer to get SRES
+ * @param sres_length size of buffer in sres, returns bytes written to SRES
+ * @param kc buffer to get Kc
+ * @param kc_length size of buffer in Kc, returns bytes written to Kc
+ * @return zero on success
+ */
+typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length,
+ unsigned char *sres, int *sres_length,
+ unsigned char *kc, int *kc_length);
+
+#ifndef SIM_READER_LIB
+/** the library containing the cardreader with the SIM function */
+#error SIM_READER_LIB not specified, use --with-sim-reader option
+#endif /* SIM_READER_LIB */
+
+#ifndef SIM_READER_ALG
+/** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */
+#define SIM_READER_ALG "sim_run_alg"
+#endif /* SIM_READER_ALG */
+
+
+/**
+ * @brief Implementation of the eap_method_t interface using EAP-SIM.
+ *
+ * This EAP-SIM client implementation uses another pluggable library to
+ * access the SIM card. This module is specified using the SIM_READER_LIB
+ * definition. The function to run the algorithm has the sim_algo_t type and
+ * is named as SIM_READER_ALG is defined.
+ *
+ * @b Constructors:
+ * - eap_create() of this module
+ * - eap_client_create() using eap_method EAP_SIM
+ *
+ * @ingroup eap
+ */
+struct eap_sim_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t eap_method_interface;
+};
+
+/**
+ * @brief Creates the EAP method EAP-SIM.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_sim_t object
+ *
+ * @ingroup eap
+ */
+eap_sim_t *eap_create(eap_role_t role,
+ identification_t *server, identification_t *peer);
+
+#endif /* EAP_SIM_H_ */
diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c
new file mode 100644
index 000000000..6c8ca8d8f
--- /dev/null
+++ b/src/charon/sa/authenticators/eap_authenticator.c
@@ -0,0 +1,360 @@
+/**
+ * @file eap_authenticator.c
+ *
+ * @brief Implementation of eap_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <string.h>
+
+#include "eap_authenticator.h"
+
+#include <daemon.h>
+#include <config/policies/policy.h>
+#include <sa/authenticators/eap/eap_method.h>
+
+typedef struct private_eap_authenticator_t private_eap_authenticator_t;
+
+/**
+ * Private data of an eap_authenticator_t object.
+ */
+struct private_eap_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_authenticator_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Role of this authenticator, PEER or SERVER
+ */
+ eap_role_t role;
+
+ /**
+ * Current EAP method processing
+ */
+ eap_method_t *method;
+
+ /**
+ * MSK used to build and verify auth payload
+ */
+ chunk_t msk;
+};
+
+extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce,
+ chunk_t secret, identification_t *id,
+ prf_t *prf_skp, prf_t *prf);
+
+/**
+ * Implementation of authenticator_t.verify.
+ */
+static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t my_nonce, auth_payload_t *auth_payload)
+{
+ chunk_t auth_data, recv_auth_data;
+ identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa);
+
+ auth_data = build_shared_key_signature(ike_sa_init, my_nonce, this->msk,
+ other_id, this->ike_sa->get_auth_verify(this->ike_sa),
+ this->ike_sa->get_prf(this->ike_sa));
+
+ recv_auth_data = auth_payload->get_data(auth_payload);
+ if (!chunk_equals(auth_data, recv_auth_data))
+ {
+ DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed");
+ chunk_free(&auth_data);
+ return FAILED;
+ }
+ chunk_free(&auth_data);
+
+ DBG1(DBG_IKE, "authentication of '%D' with %N successful",
+ other_id, auth_method_names, AUTH_EAP);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of authenticator_t.build.
+ */
+static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t other_nonce, auth_payload_t **auth_payload)
+{
+ chunk_t auth_data;
+ identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa);
+
+ DBG1(DBG_IKE, "authentication of '%D' (myself) with %N",
+ my_id, auth_method_names, AUTH_EAP);
+
+ auth_data = build_shared_key_signature(ike_sa_init, other_nonce, this->msk,
+ my_id, this->ike_sa->get_auth_build(this->ike_sa),
+ this->ike_sa->get_prf(this->ike_sa));
+
+ *auth_payload = auth_payload_create();
+ (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK);
+ (*auth_payload)->set_data(*auth_payload, auth_data);
+ chunk_free(&auth_data);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of eap_authenticator_t.initiate
+ */
+static status_t initiate(private_eap_authenticator_t *this, eap_type_t type,
+ eap_payload_t **out)
+{
+ /* if initiate() is called, role is always server */
+ this->role = EAP_SERVER;
+
+ if (type == 0)
+ {
+ DBG1(DBG_IKE,
+ "client requested EAP authentication, but configuration forbids it");
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ }
+
+ DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type);
+ this->method = eap_method_create(type, this->role,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+
+ if (this->method == NULL)
+ {
+ DBG1(DBG_IKE, "configured EAP server method %N not supported, sending %N",
+ eap_type_names, type, eap_code_names, EAP_FAILURE);
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ }
+ if (this->method->initiate(this->method, out) != NEED_MORE)
+ {
+ DBG1(DBG_IKE, "failed to initiate %N, sending %N",
+ eap_type_names, type, eap_code_names, EAP_FAILURE);
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Processing method for a peer
+ */
+static status_t process_peer(private_eap_authenticator_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ eap_type_t type = in->get_type(in);
+
+ if (type == EAP_IDENTITY)
+ {
+ eap_method_t *method = eap_method_create(type, EAP_PEER,
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa));
+
+ if (method == NULL || method->process(method, in, out) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "EAP server requested %N, but unable to process",
+ eap_type_names, type);
+ DESTROY_IF(method);
+ return FAILED;
+ }
+
+ DBG1(DBG_IKE, "EAP server requested %N, sending IKE identity",
+ eap_type_names, type);
+
+ method->destroy(method);
+ return NEED_MORE;
+ }
+
+ /* create an eap_method for the first call */
+ if (this->method == NULL)
+ {
+ DBG1(DBG_IKE, "EAP server requested %N authentication",
+ eap_type_names, type);
+ this->method = eap_method_create(type, EAP_PEER,
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa));
+ if (this->method == NULL)
+ {
+ DBG1(DBG_IKE, "EAP server requested unsupported "
+ "EAP method %N, sending EAP_NAK", eap_type_names, type);
+ *out = eap_payload_create_nak();
+ return NEED_MORE;
+ }
+ }
+
+ switch (this->method->process(this->method, in, out))
+ {
+ case NEED_MORE:
+ return NEED_MORE;
+ case SUCCESS:
+ DBG1(DBG_IKE, "EAP method %N succeded",
+ eap_type_names, this->method->get_type(this->method));
+ return SUCCESS;
+ case FAILED:
+ default:
+ DBG1(DBG_IKE, "EAP method %N failed",
+ eap_type_names, this->method->get_type(this->method));
+ return FAILED;
+ }
+}
+
+/**
+ * Processing method for a server
+ */
+static status_t process_server(private_eap_authenticator_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ switch (this->method->process(this->method, in, out))
+ {
+ case NEED_MORE:
+ return NEED_MORE;
+ case SUCCESS:
+ if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
+ {
+ DBG1(DBG_IKE, "EAP method %N succeded, MSK established",
+ eap_type_names, this->method->get_type(this->method));
+ this->msk = chunk_clone(this->msk);
+ *out = eap_payload_create_code(EAP_SUCCESS);
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established",
+ eap_type_names, this->method->get_type(this->method));
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ case FAILED:
+ default:
+ DBG1(DBG_IKE, "EAP method %N failed for peer %D",
+ eap_type_names, this->method->get_type(this->method),
+ this->ike_sa->get_other_id(this->ike_sa));
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ }
+}
+
+/**
+ * Implementation of eap_authenticator_t.process
+ */
+static status_t process(private_eap_authenticator_t *this, eap_payload_t *in,
+ eap_payload_t **out)
+{
+ eap_code_t code = in->get_code(in);
+
+ switch (this->role)
+ {
+ case EAP_SERVER:
+ {
+ switch (code)
+ {
+ case EAP_RESPONSE:
+ {
+ return process_server(this, in, out);
+ }
+ default:
+ {
+ DBG1(DBG_IKE, "received %N, sending %N",
+ eap_code_names, code, eap_code_names, EAP_FAILURE);
+ *out = eap_payload_create_code(EAP_FAILURE);
+ return FAILED;
+ }
+ }
+ }
+ case EAP_PEER:
+ {
+ switch (code)
+ {
+ case EAP_REQUEST:
+ {
+ return process_peer(this, in, out);
+ }
+ case EAP_SUCCESS:
+ {
+ if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
+ {
+ this->msk = chunk_clone(this->msk);
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "EAP method %N has no MSK established",
+ eap_type_names, this->method->get_type(this->method));
+ return FAILED;
+ }
+ case EAP_FAILURE:
+ default:
+ {
+ DBG1(DBG_IKE, "received %N, EAP authentication failed",
+ eap_code_names, code);
+ return FAILED;
+ }
+ }
+ }
+ default:
+ {
+ return FAILED;
+ }
+ }
+}
+
+/**
+ * Implementation of authenticator_t.is_mutual.
+ */
+static bool is_mutual(private_eap_authenticator_t *this)
+{
+ if (this->method)
+ {
+ return this->method->is_mutual(this->method);
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of authenticator_t.destroy.
+ */
+static void destroy(private_eap_authenticator_t *this)
+{
+ DESTROY_IF(this->method);
+ chunk_free(&this->msk);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa)
+{
+ private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t);
+
+ /* public functions */
+ this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify;
+ this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build;
+ this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy;
+
+ this->public.is_mutual = (bool(*)(eap_authenticator_t*))is_mutual;
+ this->public.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,eap_payload_t**))initiate;
+ this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->role = EAP_PEER;
+ this->method = NULL;
+ this->msk = chunk_empty;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h
new file mode 100644
index 000000000..ffa162343
--- /dev/null
+++ b/src/charon/sa/authenticators/eap_authenticator.h
@@ -0,0 +1,156 @@
+/**
+ * @file eap_authenticator.h
+ *
+ * @brief Interface of eap_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef EAP_AUTHENTICATOR_H_
+#define EAP_AUTHENTICATOR_H_
+
+typedef struct eap_authenticator_t eap_authenticator_t;
+
+#include <sa/authenticators/authenticator.h>
+#include <encoding/payloads/eap_payload.h>
+
+/**
+ * @brief Implementation of the authenticator_t interface using AUTH_EAP.
+ *
+ * Authentication using EAP involves the most complex authenticator. It stays
+ * alive over multiple ike_auth transactions and handles multiple EAP
+ * messages.
+ * EAP authentication must be clearly distinguished between using
+ * mutual EAP methods and using methods not providing server authentication.
+ * If no mutual authentication is used, the server must prove it's identity
+ * by traditional AUTH methods (RSA, psk). Only when the EAP method is mutual,
+ * the client should accept an EAP-only authentication.
+ * RFC4306 does always use traditional authentiction, EAP only authentication
+ * is described in the internet draft draft-eronen-ipsec-ikev2-eap-auth-05.txt.
+ *
+ * @verbatim
+ ike_sa_init
+ ------------------------->
+ <-------------------------
+ followed by multiple ike_auth:
+
+ +--------+ +--------+
+ | EAP | ID, SA, TS, N(EAP_ONLY) | EAP |
+ | client | ---------------------------> | server |
+ | | ID, [AUTH,] EAP | | AUTH payload is
+ | | <--------------------------- | | only included if
+ | | EAP | | authentication
+ | | ---------------------------> | | is not mutual.
+ | | EAP | |
+ | | <--------------------------- | |
+ | | EAP | |
+ | | ---------------------------> | |
+ | | EAP(SUCCESS) | |
+ | | <--------------------------- | |
+ | | AUTH | | If EAP establishes
+ | | ---------------------------> | | a session key, AUTH
+ | | AUTH, SA, TS | | payloads use this
+ | | <--------------------------- | | key, not SK_pi/pr
+ +--------+ +--------+
+
+ @endverbatim
+ * @b Constructors:
+ * - eap_authenticator_create()
+ * - authenticator_create() using auth_method AUTH_EAP
+ *
+ * @ingroup authenticators
+ */
+struct eap_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator_interface;
+
+ /**
+ * @brief Check if the EAP method was/is mutual and secure.
+ *
+ * RFC4306 proposes to authenticate the EAP responder (server) by standard
+ * IKEv2 methods (RSA, psk). Not all, but some EAP methods
+ * provide mutual authentication, which would result in a redundant
+ * authentication. If the client supports EAP_ONLY_AUTHENTICATION, and
+ * the the server provides mutual authentication, authentication using
+ * RSA/PSK may be omitted. If the server did not include a traditional
+ * AUTH payload, the client must verify that the server initiated mutual
+ * EAP authentication before it can trust the server.
+ *
+ * @param this calling object
+ * @return TRUE, if no AUTH payload required, FALSE otherwise
+ */
+ bool (*is_mutual) (eap_authenticator_t* this);
+
+ /**
+ * @brief Initiate the EAP exchange.
+ *
+ * The server initiates EAP exchanges, so the client never calls
+ * this method. If initiate() returns NEED_MORE, the EAP authentication
+ * process started. In any case, a payload is created in "out".
+ *
+ * @param this calling object
+ * @param type EAP method to use to authenticate client
+ * @param out created initiaal EAP message to send
+ * @return
+ * - FAILED, if initiation failed
+ * - NEED_MORE, if more EAP exchanges reqired
+ */
+ status_t (*initiate) (eap_authenticator_t* this, eap_type_t type,
+ eap_payload_t **out);
+
+ /**
+ * @brief Process an EAP message.
+ *
+ * After receiving an EAP message "in", the peer/server processes
+ * the payload and creates a reply/subsequent request.
+ * The server side always returns NEED_MORE if another EAP message
+ * is excepted from the client, SUCCESS if EAP exchange completed and
+ * "out" is EAP_SUCCES, or FAILED if the EAP exchange failed with
+ * a EAP_FAILURE payload in "out". Anyway, a payload in "out" is always
+ * created.
+ * The peer (client) side only creates a "out" payload if result is
+ * NEED_MORE, a SUCCESS/FAILED is returned whenever a
+ * EAP_SUCCESS/EAP_FAILURE message is received in "in".
+ * If a SUCCESS is returned (on any side), the EAP authentication was
+ * successful and the AUTH payload can be exchanged.
+ *
+ * @param this calling object
+ * @param in received EAP message
+ * @param out created EAP message to send
+ * @return
+ * - FAILED, if authentication/EAP exchange failed
+ * - SUCCESS, if authentication completed
+ * - NEED_MORE, if more EAP exchanges reqired
+ */
+ status_t (*process) (eap_authenticator_t* this,
+ eap_payload_t *in, eap_payload_t **out);
+};
+
+/**
+ * @brief Creates an authenticator for AUTH_EAP.
+ *
+ * @param ike_sa associated ike_sa
+ * @return eap_authenticator_t object
+ *
+ * @ingroup authenticators
+ */
+eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa);
+
+#endif /* EAP_AUTHENTICATOR_H_ */
diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c
new file mode 100644
index 000000000..43aec0971
--- /dev/null
+++ b/src/charon/sa/authenticators/psk_authenticator.c
@@ -0,0 +1,204 @@
+/**
+ * @file psk_authenticator.c
+ *
+ * @brief Implementation of psk_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <string.h>
+
+#include "psk_authenticator.h"
+
+#include <config/policies/policy.h>
+#include <daemon.h>
+
+/**
+ * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ */
+#define IKEV2_KEY_PAD "Key Pad for IKEv2"
+#define IKEV2_KEY_PAD_LENGTH 17
+
+
+typedef struct private_psk_authenticator_t private_psk_authenticator_t;
+
+/**
+ * Private data of an psk_authenticator_t object.
+ */
+struct private_psk_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ psk_authenticator_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+};
+
+/**
+ * Builds the octets to be signed as described in section 2.15 of RFC 4306
+ */
+chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce,
+ identification_t *id, prf_t *prf)
+{
+ u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00};
+ chunk_t id_header = chunk_from_buf(id_header_buf);
+ chunk_t id_with_header, id_prfd, id_encoding;
+
+ id_header_buf[0] = id->get_type(id);
+ id_encoding = id->get_encoding(id);
+
+ id_with_header = chunk_cat("cc", id_header, id_encoding);
+ prf->allocate_bytes(prf, id_with_header, &id_prfd);
+ chunk_free(&id_with_header);
+
+ return chunk_cat("ccm", ike_sa_init, nonce, id_prfd);
+}
+
+/**
+ * Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ */
+chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce,
+ chunk_t secret, identification_t *id,
+ prf_t *prf_skp, prf_t *prf)
+{
+ chunk_t key_pad, key, auth_data, octets;
+
+ octets = build_tbs_octets(ike_sa_init, nonce, id, prf_skp);
+ /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
+ key_pad.ptr = IKEV2_KEY_PAD;
+ key_pad.len = IKEV2_KEY_PAD_LENGTH;
+ prf->set_key(prf, secret);
+ prf->allocate_bytes(prf, key_pad, &key);
+ prf->set_key(prf, key);
+ prf->allocate_bytes(prf, octets, &auth_data);
+ DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets);
+ DBG3(DBG_IKE, "secret %B", &secret);
+ DBG3(DBG_IKE, "keypad %B", &key_pad);
+ DBG3(DBG_IKE, "prf(secret, keypad) %B", &key);
+ DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &auth_data);
+ chunk_free(&octets);
+ chunk_free(&key);
+
+ return auth_data;
+}
+
+/**
+ * Implementation of authenticator_t.verify.
+ */
+static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t my_nonce, auth_payload_t *auth_payload)
+{
+ status_t status;
+ chunk_t auth_data, recv_auth_data, shared_key;
+ identification_t *my_id, *other_id;
+
+ my_id = this->ike_sa->get_my_id(this->ike_sa);
+ other_id = this->ike_sa->get_other_id(this->ike_sa);
+ status = charon->credentials->get_shared_key(charon->credentials, my_id,
+ other_id, &shared_key);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id);
+ return status;
+ }
+
+ auth_data = build_shared_key_signature(ike_sa_init, my_nonce, shared_key,
+ other_id, this->ike_sa->get_auth_verify(this->ike_sa),
+ this->ike_sa->get_prf(this->ike_sa));
+ chunk_free(&shared_key);
+
+ recv_auth_data = auth_payload->get_data(auth_payload);
+ if (auth_data.len != recv_auth_data.len ||
+ !memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len))
+ {
+ DBG1(DBG_IKE, "PSK MAC verification failed");
+ chunk_free(&auth_data);
+ return FAILED;
+ }
+ chunk_free(&auth_data);
+
+ DBG1(DBG_IKE, "authentication of '%D' with %N successful",
+ other_id, auth_method_names, AUTH_PSK);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of authenticator_t.build.
+ */
+static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t other_nonce, auth_payload_t **auth_payload)
+{
+ chunk_t shared_key;
+ chunk_t auth_data;
+ status_t status;
+ identification_t *my_id, *other_id;
+
+ my_id = this->ike_sa->get_my_id(this->ike_sa);
+ other_id = this->ike_sa->get_other_id(this->ike_sa);
+ DBG1(DBG_IKE, "authentication of '%D' (myself) with %N",
+ my_id, auth_method_names, AUTH_PSK);
+ status = charon->credentials->get_shared_key(charon->credentials, my_id,
+ other_id, &shared_key);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id);
+ return status;
+ }
+
+ auth_data = build_shared_key_signature(ike_sa_init, other_nonce, shared_key,
+ my_id, this->ike_sa->get_auth_build(this->ike_sa),
+ this->ike_sa->get_prf(this->ike_sa));
+ DBG2(DBG_IKE, "successfully created shared key MAC");
+ chunk_free(&shared_key);
+ *auth_payload = auth_payload_create();
+ (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK);
+ (*auth_payload)->set_data(*auth_payload, auth_data);
+
+ chunk_free(&auth_data);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of authenticator_t.destroy.
+ */
+static void destroy(private_psk_authenticator_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa)
+{
+ private_psk_authenticator_t *this = malloc_thing(private_psk_authenticator_t);
+
+ /* public functions */
+ this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify;
+ this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build;
+ this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/authenticators/psk_authenticator.h b/src/charon/sa/authenticators/psk_authenticator.h
new file mode 100644
index 000000000..c1c5bcaac
--- /dev/null
+++ b/src/charon/sa/authenticators/psk_authenticator.h
@@ -0,0 +1,57 @@
+/**
+ * @file psk_authenticator.h
+ *
+ * @brief Interface of psk_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef PSK_AUTHENTICATOR_H_
+#define PSK_AUTHENTICATOR_H_
+
+typedef struct psk_authenticator_t psk_authenticator_t;
+
+#include <sa/authenticators/authenticator.h>
+
+/**
+ * @brief Implementation of the authenticator_t interface using AUTH_PSK.
+ *
+ * @b Constructors:
+ * - psk_authenticator_create()
+ * - authenticator_create() using auth_method AUTH_PSK
+ *
+ * @ingroup authenticators
+ */
+struct psk_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator_interface;
+};
+
+/**
+ * @brief Creates an authenticator for AUTH_PSK.
+ *
+ * @param ike_sa associated ike_sa
+ * @return psk_authenticator_t object
+ *
+ * @ingroup authenticators
+ */
+psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa);
+
+#endif /* PSK_AUTHENTICATOR_H_ */
diff --git a/src/charon/sa/authenticators/rsa_authenticator.c b/src/charon/sa/authenticators/rsa_authenticator.c
new file mode 100644
index 000000000..dfa01e332
--- /dev/null
+++ b/src/charon/sa/authenticators/rsa_authenticator.c
@@ -0,0 +1,180 @@
+/**
+ * @file rsa_authenticator.c
+ *
+ * @brief Implementation of rsa_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <string.h>
+
+#include "rsa_authenticator.h"
+
+#include <config/policies/policy.h>
+#include <daemon.h>
+
+
+typedef struct private_rsa_authenticator_t private_rsa_authenticator_t;
+
+/**
+ * Private data of an rsa_authenticator_t object.
+ */
+struct private_rsa_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ rsa_authenticator_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+};
+
+/**
+ * Function implemented in psk_authenticator.c
+ */
+extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce,
+ identification_t *id, prf_t *prf);
+
+/**
+ * Implementation of authenticator_t.verify.
+ */
+static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t my_nonce, auth_payload_t *auth_payload)
+{
+ status_t status;
+ chunk_t auth_data, octets;
+ rsa_public_key_t *public_key;
+ identification_t *other_id;
+
+ other_id = this->ike_sa->get_other_id(this->ike_sa);
+
+ if (auth_payload->get_auth_method(auth_payload) != AUTH_RSA)
+ {
+ return INVALID_ARG;
+ }
+ auth_data = auth_payload->get_data(auth_payload);
+ public_key = charon->credentials->get_trusted_public_key(charon->credentials,
+ other_id);
+ if (public_key == NULL)
+ {
+ DBG1(DBG_IKE, "no RSA public key found for '%D'", other_id);
+ return NOT_FOUND;
+ }
+ octets = build_tbs_octets(ike_sa_init, my_nonce, other_id,
+ this->ike_sa->get_auth_verify(this->ike_sa));
+ status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data);
+ chunk_free(&octets);
+
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "RSA signature verification failed");
+ return status;
+ }
+
+ DBG1(DBG_IKE, "authentication of '%D' with %N successful",
+ other_id, auth_method_names, AUTH_RSA);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of authenticator_t.build.
+ */
+static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init,
+ chunk_t other_nonce, auth_payload_t **auth_payload)
+{
+ chunk_t chunk;
+ chunk_t octets;
+ chunk_t auth_data;
+ status_t status;
+ rsa_public_key_t *my_pubkey;
+ rsa_private_key_t *my_key;
+ identification_t *my_id;
+
+ my_id = this->ike_sa->get_my_id(this->ike_sa);
+ DBG1(DBG_IKE, "authentication of '%D' (myself) with %N",
+ my_id, auth_method_names, AUTH_RSA);
+ DBG2(DBG_IKE, "looking for RSA public key belonging to '%D'", my_id);
+
+ my_pubkey = charon->credentials->get_rsa_public_key(charon->credentials, my_id);
+ if (my_pubkey == NULL)
+ {
+ DBG1(DBG_IKE, "no RSA public key found for '%D'", my_id);
+ return NOT_FOUND;
+ }
+ DBG2(DBG_IKE, "matching RSA public key found");
+ chunk = my_pubkey->get_keyid(my_pubkey);
+ DBG2(DBG_IKE, "looking for RSA private key with keyid %#B", &chunk);
+ my_key = charon->credentials->get_rsa_private_key(charon->credentials, my_pubkey);
+ if (my_key == NULL)
+ {
+ DBG1(DBG_IKE, "no RSA private key found with for %D with keyid %#B",
+ my_id, &chunk);
+ return NOT_FOUND;
+ }
+ DBG2(DBG_IKE, "matching RSA private key found");
+
+ octets = build_tbs_octets(ike_sa_init, other_nonce, my_id,
+ this->ike_sa->get_auth_build(this->ike_sa));
+ status = my_key->build_emsa_pkcs1_signature(my_key, HASH_SHA1, octets, &auth_data);
+ chunk_free(&octets);
+
+ if (status != SUCCESS)
+ {
+ my_key->destroy(my_key);
+ DBG1(DBG_IKE, "build signature of SHA1 hash failed");
+ return status;
+ }
+ DBG2(DBG_IKE, "successfully signed with RSA private key");
+
+ *auth_payload = auth_payload_create();
+ (*auth_payload)->set_auth_method(*auth_payload, AUTH_RSA);
+ (*auth_payload)->set_data(*auth_payload, auth_data);
+
+ my_key->destroy(my_key);
+ chunk_free(&auth_data);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of authenticator_t.destroy.
+ */
+static void destroy(private_rsa_authenticator_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa)
+{
+ private_rsa_authenticator_t *this = malloc_thing(private_rsa_authenticator_t);
+
+ /* public functions */
+ this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify;
+ this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build;
+ this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/authenticators/rsa_authenticator.h b/src/charon/sa/authenticators/rsa_authenticator.h
new file mode 100644
index 000000000..cc5cc0150
--- /dev/null
+++ b/src/charon/sa/authenticators/rsa_authenticator.h
@@ -0,0 +1,57 @@
+/**
+ * @file rsa_authenticator.h
+ *
+ * @brief Interface of rsa_authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef RSA_AUTHENTICATOR_H_
+#define RSA_AUTHENTICATOR_H_
+
+typedef struct rsa_authenticator_t rsa_authenticator_t;
+
+#include <sa/authenticators/authenticator.h>
+
+/**
+ * @brief Implementation of the authenticator_t interface using AUTH_RSA.
+ *
+ * @b Constructors:
+ * - rsa_authenticator_create()
+ * - authenticator_create() using auth_method AUTH_RSA
+ *
+ * @ingroup authenticators
+ */
+struct rsa_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator_interface;
+};
+
+/**
+ * @brief Creates an authenticator for AUTH_RSA.
+ *
+ * @param ike_sa associated ike_sa
+ * @return rsa_authenticator_t object
+ *
+ * @ingroup authenticators
+ */
+rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa);
+
+#endif /* RSA_AUTHENTICATOR_H_ */
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
new file mode 100644
index 000000000..19131389d
--- /dev/null
+++ b/src/charon/sa/child_sa.c
@@ -0,0 +1,1130 @@
+/**
+ * @file child_sa.c
+ *
+ * @brief Implementation of child_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include "child_sa.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <printf.h>
+
+#include <daemon.h>
+
+ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DELETING,
+ "CREATED",
+ "ROUTED",
+ "INSTALLED",
+ "REKEYING",
+ "DELETING",
+);
+
+typedef struct sa_policy_t sa_policy_t;
+
+/**
+ * Struct used to store information for a policy. This
+ * is needed since we must provide all this information
+ * for deleting a policy...
+ */
+struct sa_policy_t {
+ /**
+ * Traffic selector for us
+ */
+ traffic_selector_t *my_ts;
+
+ /**
+ * Traffic selector for other
+ */
+ traffic_selector_t *other_ts;
+};
+
+typedef struct private_child_sa_t private_child_sa_t;
+
+/**
+ * Private data of a child_sa_t bject.
+ */
+struct private_child_sa_t {
+ /**
+ * Public interface of child_sa_t.
+ */
+ child_sa_t public;
+
+ struct {
+ /** address of peer */
+ host_t *addr;
+ /** id of peer */
+ identification_t *id;
+ /** actual used SPI, 0 if unused */
+ u_int32_t spi;
+ } me, other;
+
+ /**
+ * Allocated SPI for a ESP proposal candidates
+ */
+ u_int32_t alloc_esp_spi;
+
+ /**
+ * Allocated SPI for a AH proposal candidates
+ */
+ u_int32_t alloc_ah_spi;
+
+ /**
+ * Protocol used to protect this SA, ESP|AH
+ */
+ protocol_id_t protocol;
+
+ /**
+ * List containing sa_policy_t objects
+ */
+ linked_list_t *policies;
+
+ /**
+ * Seperate list for local traffic selectors
+ */
+ linked_list_t *my_ts;
+
+ /**
+ * Seperate list for remote traffic selectors
+ */
+ linked_list_t *other_ts;
+
+ /**
+ * reqid used for this child_sa
+ */
+ u_int32_t reqid;
+
+ /**
+ * encryption algorithm used for this SA
+ */
+ algorithm_t encryption;
+
+ /**
+ * integrity protection algorithm used for this SA
+ */
+ algorithm_t integrity;
+
+ /**
+ * time, on which SA was installed
+ */
+ time_t install_time;
+
+ /**
+ * absolute time when rekeying is sceduled
+ */
+ time_t rekey_time;
+
+ /**
+ * state of the CHILD_SA
+ */
+ child_sa_state_t state;
+
+ /**
+ * Specifies if NAT traversal is used
+ */
+ bool use_natt;
+
+ /**
+ * mode this SA uses, tunnel/transport
+ */
+ mode_t mode;
+
+ /**
+ * virtual IP assinged to local host
+ */
+ host_t *virtual_ip;
+
+ /**
+ * policy used to create this child
+ */
+ policy_t *policy;
+};
+
+/**
+ * Implementation of child_sa_t.get_name.
+ */
+static char *get_name(private_child_sa_t *this)
+{
+ return this->policy->get_name(this->policy);;
+}
+
+/**
+ * Implements child_sa_t.get_reqid
+ */
+static u_int32_t get_reqid(private_child_sa_t *this)
+{
+ return this->reqid;
+}
+
+/**
+ * Implements child_sa_t.get_spi
+ */
+u_int32_t get_spi(private_child_sa_t *this, bool inbound)
+{
+ if (inbound)
+ {
+ return this->me.spi;
+ }
+ return this->other.spi;
+}
+
+/**
+ * Implements child_sa_t.get_protocol
+ */
+protocol_id_t get_protocol(private_child_sa_t *this)
+{
+ return this->protocol;
+}
+
+/**
+ * Implements child_sa_t.get_state
+ */
+static child_sa_state_t get_state(private_child_sa_t *this)
+{
+ return this->state;
+}
+
+/**
+ * Implements child_sa_t.get_policy
+ */
+static policy_t* get_policy(private_child_sa_t *this)
+{
+ return this->policy;
+}
+
+/**
+ * Run the up/down script
+ */
+static void updown(private_child_sa_t *this, bool up)
+{
+ sa_policy_t *policy;
+ iterator_t *iterator;
+ char *script;
+
+ script = this->policy->get_updown(this->policy);
+
+ if (script == NULL)
+ {
+ return;
+ }
+
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&policy))
+ {
+ char command[1024];
+ char *ifname = NULL;
+ char *my_client, *other_client, *my_client_mask, *other_client_mask;
+ char *pos, *virtual_ip;
+ FILE *shell;
+
+ /* get subnet/bits from string */
+ asprintf(&my_client, "%R", policy->my_ts);
+ pos = strchr(my_client, '/');
+ *pos = '\0';
+ my_client_mask = pos + 1;
+ pos = strchr(my_client_mask, '[');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ asprintf(&other_client, "%R", policy->other_ts);
+ pos = strchr(other_client, '/');
+ *pos = '\0';
+ other_client_mask = pos + 1;
+ pos = strchr(other_client_mask, '[');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+
+ if (this->virtual_ip)
+ {
+ asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ",
+ this->virtual_ip);
+ }
+ else
+ {
+ asprintf(&virtual_ip, "");
+ }
+
+ ifname = charon->kernel_interface->get_interface(charon->kernel_interface,
+ this->me.addr);
+
+ /* build the command with all env variables.
+ * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing
+ */
+ snprintf(command, sizeof(command),
+ "2>&1 "
+ "PLUTO_VERSION='1.1' "
+ "PLUTO_VERB='%s%s%s' "
+ "PLUTO_CONNECTION='%s' "
+ "PLUTO_INTERFACE='%s' "
+ "PLUTO_REQID='%u' "
+ "PLUTO_ME='%H' "
+ "PLUTO_MY_ID='%D' "
+ "PLUTO_MY_CLIENT='%s/%s' "
+ "PLUTO_MY_CLIENT_NET='%s' "
+ "PLUTO_MY_CLIENT_MASK='%s' "
+ "PLUTO_MY_PORT='%u' "
+ "PLUTO_MY_PROTOCOL='%u' "
+ "PLUTO_PEER='%H' "
+ "PLUTO_PEER_ID='%D' "
+ "PLUTO_PEER_CLIENT='%s/%s' "
+ "PLUTO_PEER_CLIENT_NET='%s' "
+ "PLUTO_PEER_CLIENT_MASK='%s' "
+ "PLUTO_PEER_PORT='%u' "
+ "PLUTO_PEER_PROTOCOL='%u' "
+ "%s"
+ "%s"
+ "%s",
+ up ? "up" : "down",
+ policy->my_ts->is_host(policy->my_ts,
+ this->me.addr) ? "-host" : "-client",
+ this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6",
+ this->policy->get_name(this->policy),
+ ifname ? ifname : "(unknown)",
+ this->reqid,
+ this->me.addr,
+ this->me.id,
+ my_client, my_client_mask,
+ my_client, my_client_mask,
+ policy->my_ts->get_from_port(policy->my_ts),
+ policy->my_ts->get_protocol(policy->my_ts),
+ this->other.addr,
+ this->other.id,
+ other_client, other_client_mask,
+ other_client, other_client_mask,
+ policy->other_ts->get_from_port(policy->other_ts),
+ policy->other_ts->get_protocol(policy->other_ts),
+ virtual_ip,
+ this->policy->get_hostaccess(this->policy) ?
+ "PLUTO_HOST_ACCESS='1' " : "",
+ script);
+ free(ifname);
+ free(my_client);
+ free(other_client);
+ free(virtual_ip);
+
+ shell = popen(command, "r");
+
+ if (shell == NULL)
+ {
+ DBG1(DBG_CHD, "could not execute updown script '%s'", script);
+ return;
+ }
+
+ while (TRUE)
+ {
+ char resp[128];
+
+ if (fgets(resp, sizeof(resp), shell) == NULL)
+ {
+ if (ferror(shell))
+ {
+ DBG1(DBG_CHD, "error reading output from updown script");
+ return;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ char *e = resp + strlen(resp);
+ if (e > resp && e[-1] == '\n')
+ { /* trim trailing '\n' */
+ e[-1] = '\0';
+ }
+ DBG1(DBG_CHD, "updown: %s", resp);
+ }
+ }
+ pclose(shell);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implements child_sa_t.set_state
+ */
+static void set_state(private_child_sa_t *this, child_sa_state_t state)
+{
+ this->state = state;
+ if (state == CHILD_INSTALLED)
+ {
+ updown(this, TRUE);
+ }
+}
+
+/**
+ * Allocate SPI for a single proposal
+ */
+static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal)
+{
+ protocol_id_t protocol = proposal->get_protocol(proposal);
+
+ if (protocol == PROTO_AH)
+ {
+ /* get a new spi for AH, if not already done */
+ if (this->alloc_ah_spi == 0)
+ {
+ if (charon->kernel_interface->get_spi(
+ charon->kernel_interface,
+ this->other.addr, this->me.addr,
+ PROTO_AH, this->reqid,
+ &this->alloc_ah_spi) != SUCCESS)
+ {
+ return FAILED;
+ }
+ }
+ proposal->set_spi(proposal, this->alloc_ah_spi);
+ }
+ if (protocol == PROTO_ESP)
+ {
+ /* get a new spi for ESP, if not already done */
+ if (this->alloc_esp_spi == 0)
+ {
+ if (charon->kernel_interface->get_spi(
+ charon->kernel_interface,
+ this->other.addr, this->me.addr,
+ PROTO_ESP, this->reqid,
+ &this->alloc_esp_spi) != SUCCESS)
+ {
+ return FAILED;
+ }
+ }
+ proposal->set_spi(proposal, this->alloc_esp_spi);
+ }
+ return SUCCESS;
+}
+
+
+/**
+ * Implements child_sa_t.alloc
+ */
+static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
+{
+ iterator_t *iterator;
+ proposal_t *proposal;
+
+ /* iterator through proposals to update spis */
+ iterator = proposals->create_iterator(proposals, TRUE);
+ while(iterator->iterate(iterator, (void**)&proposal))
+ {
+ if (alloc_proposal(this, proposal) != SUCCESS)
+ {
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ iterator->destroy(iterator);
+ return SUCCESS;
+}
+
+static status_t install(private_child_sa_t *this, proposal_t *proposal,
+ mode_t mode, prf_plus_t *prf_plus, bool mine)
+{
+ u_int32_t spi, soft, hard;;
+ algorithm_t *enc_algo, *int_algo;
+ algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0};
+ algorithm_t int_algo_none = {AUTH_UNDEFINED, 0};
+ host_t *src;
+ host_t *dst;
+ natt_conf_t *natt;
+ status_t status;
+
+ this->protocol = proposal->get_protocol(proposal);
+
+ /* now we have to decide which spi to use. Use self allocated, if "mine",
+ * or the one in the proposal, if not "mine" (others). Additionally,
+ * source and dest host switch depending on the role */
+ if (mine)
+ {
+ /* if we have allocated SPIs for AH and ESP, we must delete the unused
+ * one. */
+ if (this->protocol == PROTO_ESP)
+ {
+ this->me.spi = this->alloc_esp_spi;
+ if (this->alloc_ah_spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr,
+ this->alloc_ah_spi, PROTO_AH);
+ }
+ }
+ else
+ {
+ this->me.spi = this->alloc_ah_spi;
+ if (this->alloc_esp_spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr,
+ this->alloc_esp_spi, PROTO_ESP);
+ }
+ }
+ spi = this->me.spi;
+ dst = this->me.addr;
+ src = this->other.addr;
+ }
+ else
+ {
+ this->other.spi = proposal->get_spi(proposal);
+ spi = this->other.spi;
+ src = this->me.addr;
+ dst = this->other.addr;
+ }
+
+ DBG2(DBG_CHD, "adding %s %N SA", mine ? "inbound" : "outbound",
+ protocol_id_names, this->protocol);
+
+ /* select encryption algo */
+ if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo))
+ {
+ DBG2(DBG_CHD, " using %N for encryption",
+ encryption_algorithm_names, enc_algo->algorithm);
+ }
+ else
+ {
+ enc_algo = &enc_algo_none;
+ }
+
+ /* select integrity algo */
+ if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo))
+ {
+ DBG2(DBG_CHD, " using %N for integrity",
+ integrity_algorithm_names, int_algo->algorithm);
+ }
+ else
+ {
+ int_algo = &int_algo_none;
+ }
+
+ /* setup nat-t */
+ if (this->use_natt)
+ {
+ natt = alloca(sizeof(natt_conf_t));
+ natt->sport = src->get_port(src);
+ natt->dport = dst->get_port(dst);
+ }
+ else
+ {
+ natt = NULL;
+ }
+
+ soft = this->policy->get_soft_lifetime(this->policy);
+ hard = this->policy->get_hard_lifetime(this->policy);
+
+ /* send SA down to the kernel */
+ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
+ status = charon->kernel_interface->add_sa(charon->kernel_interface,
+ src, dst, spi, this->protocol,
+ this->reqid, mine ? soft : 0,
+ hard, enc_algo, int_algo,
+ prf_plus, natt, mode, mine);
+
+ this->encryption = *enc_algo;
+ this->integrity = *int_algo;
+ this->install_time = time(NULL);
+ this->rekey_time = soft;
+
+ return status;
+}
+
+static status_t add(private_child_sa_t *this, proposal_t *proposal,
+ mode_t mode, prf_plus_t *prf_plus)
+{
+ u_int32_t outbound_spi, inbound_spi;
+
+ /* backup outbound spi, as alloc overwrites it */
+ outbound_spi = proposal->get_spi(proposal);
+
+ /* get SPIs inbound SAs */
+ if (alloc_proposal(this, proposal) != SUCCESS)
+ {
+ return FAILED;
+ }
+ inbound_spi = proposal->get_spi(proposal);
+
+ /* install inbound SAs */
+ if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ /* install outbound SAs, restore spi*/
+ proposal->set_spi(proposal, outbound_spi);
+ if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS)
+ {
+ return FAILED;
+ }
+ proposal->set_spi(proposal, inbound_spi);
+
+ return SUCCESS;
+}
+
+static status_t update(private_child_sa_t *this, proposal_t *proposal,
+ mode_t mode, prf_plus_t *prf_plus)
+{
+ u_int32_t inbound_spi;
+
+ /* backup received spi, as install() overwrites it */
+ inbound_spi = proposal->get_spi(proposal);
+
+ /* install outbound SAs */
+ if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ /* restore spi */
+ proposal->set_spi(proposal, inbound_spi);
+ /* install inbound SAs */
+ if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+static status_t add_policies(private_child_sa_t *this,
+ linked_list_t *my_ts_list,
+ linked_list_t *other_ts_list, mode_t mode)
+{
+ iterator_t *my_iter, *other_iter;
+ traffic_selector_t *my_ts, *other_ts;
+ /* use low prio for ROUTED policies */
+ bool high_prio = (this->state != CHILD_CREATED);
+
+ /* iterate over both lists */
+ my_iter = my_ts_list->create_iterator(my_ts_list, TRUE);
+ other_iter = other_ts_list->create_iterator(other_ts_list, TRUE);
+ while (my_iter->iterate(my_iter, (void**)&my_ts))
+ {
+ other_iter->reset(other_iter);
+ while (other_iter->iterate(other_iter, (void**)&other_ts))
+ {
+ /* set up policies for every entry in my_ts_list to every entry in other_ts_list */
+ status_t status;
+ sa_policy_t *policy;
+
+ if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
+ {
+ DBG2(DBG_CHD,
+ "CHILD_SA policy uses two different IP families, ignored");
+ continue;
+ }
+
+ /* only set up policies if protocol matches, or if one is zero (any) */
+ if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts) &&
+ my_ts->get_protocol(my_ts) && other_ts->get_protocol(other_ts))
+ {
+ DBG2(DBG_CHD,
+ "CHILD_SA policy uses two different protocols, ignored");
+ continue;
+ }
+
+ /* install 3 policies: out, in and forward */
+ status = charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT,
+ this->protocol, this->reqid, high_prio, mode, FALSE);
+
+ status |= charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN,
+ this->protocol, this->reqid, high_prio, mode, FALSE);
+
+ status |= charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD,
+ this->protocol, this->reqid, high_prio, mode, FALSE);
+
+ if (status != SUCCESS)
+ {
+ my_iter->destroy(my_iter);
+ other_iter->destroy(other_iter);
+ return status;
+ }
+
+ /* store policy to delete/update them later */
+ policy = malloc_thing(sa_policy_t);
+ policy->my_ts = my_ts->clone(my_ts);
+ policy->other_ts = other_ts->clone(other_ts);
+ this->policies->insert_last(this->policies, (void*)policy);
+ /* add to separate list to query them via get_*_traffic_selectors() */
+ this->my_ts->insert_last(this->my_ts, (void*)policy->my_ts);
+ this->other_ts->insert_last(this->other_ts, (void*)policy->other_ts);
+ }
+ }
+ my_iter->destroy(my_iter);
+ other_iter->destroy(other_iter);
+
+ /* switch to routed state if no SAD entry set up */
+ if (this->state == CHILD_CREATED)
+ {
+ this->state = CHILD_ROUTED;
+ }
+ /* needed to update hosts */
+ this->mode = mode;
+ return SUCCESS;
+}
+
+/**
+ * Implementation of child_sa_t.get_my_traffic_selectors.
+ */
+static linked_list_t *get_my_traffic_selectors(private_child_sa_t *this)
+{
+ return this->my_ts;
+}
+
+/**
+ * Implementation of child_sa_t.get_my_traffic_selectors.
+ */
+static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this)
+{
+ return this->other_ts;
+}
+
+/**
+ * Implementation of child_sa_t.get_use_time
+ */
+static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time)
+{
+ iterator_t *iterator;
+ sa_policy_t *policy;
+ status_t status = FAILED;
+
+ *use_time = UNDEFINED_TIME;
+
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&policy))
+ {
+ if (inbound)
+ {
+ time_t in = UNDEFINED_TIME, fwd = UNDEFINED_TIME;
+
+ status = charon->kernel_interface->query_policy(
+ charon->kernel_interface,
+ policy->other_ts, policy->my_ts,
+ POLICY_IN, (u_int32_t*)&in);
+ status |= charon->kernel_interface->query_policy(
+ charon->kernel_interface,
+ policy->other_ts, policy->my_ts,
+ POLICY_FWD, (u_int32_t*)&fwd);
+ *use_time = max(in, fwd);
+ }
+ else
+ {
+ status = charon->kernel_interface->query_policy(
+ charon->kernel_interface,
+ policy->my_ts, policy->other_ts,
+ POLICY_OUT, (u_int32_t*)use_time);
+ }
+ }
+ iterator->destroy(iterator);
+ return status;
+}
+
+/**
+ * output handler in printf()
+ */
+static int print(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ private_child_sa_t *this = *((private_child_sa_t**)(args[0]));
+ iterator_t *iterator;
+ sa_policy_t *policy;
+ u_int32_t now, rekeying;
+ u_int32_t use, use_in, use_fwd;
+ status_t status;
+ size_t written = 0;
+
+ if (this == NULL)
+ {
+ return fprintf(stream, "(null)");
+ }
+
+ now = time(NULL);
+
+ written += fprintf(stream, "%12s{%d}: %N, %N",
+ this->policy->get_name(this->policy), this->reqid,
+ child_sa_state_names, this->state,
+ mode_names, this->mode);
+
+ if (this->state == CHILD_INSTALLED)
+ {
+ written += fprintf(stream, ", %N SPIs: 0x%0x_i 0x%0x_o",
+ protocol_id_names, this->protocol,
+ htonl(this->me.spi), htonl(this->other.spi));
+
+ if (info->alt)
+ {
+ written += fprintf(stream, "\n%12s{%d}: ",
+ this->policy->get_name(this->policy),
+ this->reqid);
+
+ if (this->protocol == PROTO_ESP)
+ {
+ written += fprintf(stream, "%N", encryption_algorithm_names,
+ this->encryption.algorithm);
+
+ if (this->encryption.key_size)
+ {
+ written += fprintf(stream, "-%d", this->encryption.key_size);
+ }
+ written += fprintf(stream, "/");
+ }
+
+ written += fprintf(stream, "%N", integrity_algorithm_names,
+ this->integrity.algorithm);
+ if (this->integrity.key_size)
+ {
+ written += fprintf(stream, "-%d", this->integrity.key_size);
+ }
+ written += fprintf(stream, ", rekeying ");
+
+ /* calculate rekey times */
+ if (this->rekey_time)
+ {
+ rekeying = this->install_time + this->rekey_time - now;
+ written += fprintf(stream, "in %ds", rekeying);
+ }
+ else
+ {
+ written += fprintf(stream, "disabled");
+ }
+ }
+ }
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&policy))
+ {
+ written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ",
+ this->policy->get_name(this->policy), this->reqid,
+ policy->my_ts, policy->other_ts);
+
+ /* query time of last policy use */
+
+ /* inbound: POLICY_IN or POLICY_FWD */
+ status = charon->kernel_interface->query_policy(charon->kernel_interface,
+ policy->other_ts, policy->my_ts, POLICY_IN, &use_in);
+ use_in = (status == SUCCESS)? use_in : 0;
+ status = charon->kernel_interface->query_policy(charon->kernel_interface,
+ policy->other_ts, policy->my_ts, POLICY_FWD, &use_fwd);
+ use_fwd = (status == SUCCESS)? use_fwd : 0;
+ use = max(use_in, use_fwd);
+ if (use)
+ {
+ written += fprintf(stream, "%ds_i ", now - use);
+ }
+ else
+ {
+ written += fprintf(stream, "no_i ");
+ }
+
+ /* outbound: POLICY_OUT */
+ status = charon->kernel_interface->query_policy(charon->kernel_interface,
+ policy->my_ts, policy->other_ts, POLICY_OUT, &use);
+ if (status == SUCCESS && use)
+ {
+ written += fprintf(stream, "%ds_o ", now - use);
+ }
+ else
+ {
+ written += fprintf(stream, "no_o ");
+ }
+ }
+ iterator->destroy(iterator);
+ return written;
+}
+
+/**
+ * register printf() handlers
+ */
+static void __attribute__ ((constructor))print_register()
+{
+ register_printf_function(PRINTF_CHILD_SA, print, arginfo_ptr);
+}
+
+/**
+ * Update the host adress/port of a SA
+ */
+static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other,
+ int my_changes, int other_changes, bool mine)
+{
+ host_t *src, *dst, *new_src, *new_dst;
+ int src_changes, dst_changes;
+ status_t status;
+ u_int32_t spi;
+
+ if (mine)
+ {
+ src = this->other.addr;
+ dst = this->me.addr;
+ new_src = new_other;
+ new_dst = new_me;
+ src_changes = other_changes;
+ dst_changes = my_changes;
+ spi = this->other.spi;
+ }
+ else
+ {
+ src = this->me.addr;
+ dst = this->other.addr;
+ new_src = new_me;
+ new_dst = new_other;
+ src_changes = my_changes;
+ dst_changes = other_changes;
+ spi = this->me.spi;
+ }
+
+ DBG2(DBG_CHD, "updating %N SA 0x%x, from %#H..#H to %#H..%#H",
+ protocol_id_names, this->protocol, ntohl(spi), src, dst, new_src, new_dst);
+
+ status = charon->kernel_interface->update_sa(charon->kernel_interface,
+ dst, spi, this->protocol,
+ new_src, new_dst,
+ src_changes, dst_changes);
+
+ if (status != SUCCESS)
+ {
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Update the host adress/port of a policy
+ */
+static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other)
+{
+ iterator_t *iterator;
+ sa_policy_t *policy;
+ status_t status;
+ /* we always use high priorities, as hosts getting updated are INSTALLED */
+
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&policy))
+ {
+ status = charon->kernel_interface->add_policy(
+ charon->kernel_interface,
+ new_me, new_other,
+ policy->my_ts, policy->other_ts,
+ POLICY_OUT, this->protocol, this->reqid, TRUE, this->mode, TRUE);
+
+ status |= charon->kernel_interface->add_policy(
+ charon->kernel_interface,
+ new_other, new_me,
+ policy->other_ts, policy->my_ts,
+ POLICY_IN, this->protocol, this->reqid, TRUE, this->mode, TRUE);
+
+ status |= charon->kernel_interface->add_policy(
+ charon->kernel_interface,
+ new_other, new_me,
+ policy->other_ts, policy->my_ts,
+ POLICY_FWD, this->protocol, this->reqid, TRUE, this->mode, TRUE);
+
+ if (status != SUCCESS)
+ {
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of child_sa_t.update_hosts.
+ */
+static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other,
+ host_diff_t my_changes, host_diff_t other_changes)
+{
+ if (!my_changes && !other_changes)
+ {
+ return SUCCESS;
+ }
+
+ /* update our (initator) SAs */
+ if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ /* update his (responder) SAs */
+ if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ /* update policies */
+ if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR)
+ {
+ if (update_policy_hosts(this, new_me, new_other) != SUCCESS)
+ {
+ return FAILED;
+ }
+ }
+
+ /* update hosts */
+ if (my_changes)
+ {
+ this->me.addr->destroy(this->me.addr);
+ this->me.addr = new_me->clone(new_me);
+ }
+
+ if (other_changes)
+ {
+ this->other.addr->destroy(this->other.addr);
+ this->other.addr = new_other->clone(new_other);
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of child_sa_t.set_virtual_ip.
+ */
+static void set_virtual_ip(private_child_sa_t *this, host_t *ip)
+{
+ this->virtual_ip = ip->clone(ip);
+}
+
+/**
+ * Implementation of child_sa_t.destroy.
+ */
+static void destroy(private_child_sa_t *this)
+{
+ sa_policy_t *policy;
+
+ if (this->state == CHILD_DELETING || this->state == CHILD_INSTALLED)
+ {
+ updown(this, FALSE);
+ }
+
+ /* delete SAs in the kernel, if they are set up */
+ if (this->me.spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->me.addr, this->me.spi, this->protocol);
+ }
+ if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->me.addr, this->alloc_esp_spi, PROTO_ESP);
+ }
+ if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->me.addr, this->alloc_ah_spi, PROTO_AH);
+ }
+ if (this->other.spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->other.addr, this->other.spi, this->protocol);
+ }
+
+ /* delete all policies in the kernel */
+ while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS)
+ {
+ /* let rekeyed policies, as they are used by another child_sa */
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->my_ts, policy->other_ts,
+ POLICY_OUT);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->other_ts, policy->my_ts,
+ POLICY_IN);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->other_ts, policy->my_ts,
+ POLICY_FWD);
+ policy->my_ts->destroy(policy->my_ts);
+ policy->other_ts->destroy(policy->other_ts);
+ free(policy);
+ }
+ this->policies->destroy(this->policies);
+
+ this->my_ts->destroy(this->my_ts);
+ this->other_ts->destroy(this->other_ts);
+ this->me.addr->destroy(this->me.addr);
+ this->other.addr->destroy(this->other.addr);
+ this->me.id->destroy(this->me.id);
+ this->other.id->destroy(this->other.id);
+ this->policy->destroy(this->policy);
+ DESTROY_IF(this->virtual_ip);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_sa_t * child_sa_create(host_t *me, host_t* other,
+ identification_t *my_id, identification_t *other_id,
+ policy_t *policy, u_int32_t rekey, bool use_natt)
+{
+ static u_int32_t reqid = 0;
+ private_child_sa_t *this = malloc_thing(private_child_sa_t);
+
+ /* public functions */
+ this->public.get_name = (char*(*)(child_sa_t*))get_name;
+ this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
+ this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
+ this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
+ this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
+ this->public.add = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))add;
+ this->public.update = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))update;
+ this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_diff_t,host_diff_t))update_hosts;
+ this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,mode_t))add_policies;
+ this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors;
+ this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors;
+ this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time;
+ this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
+ this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
+ this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy;
+ this->public.set_virtual_ip = (void(*)(child_sa_t*,host_t*))set_virtual_ip;
+ this->public.destroy = (void(*)(child_sa_t*))destroy;
+
+ /* private data */
+ this->me.addr = me->clone(me);
+ this->other.addr = other->clone(other);
+ this->me.id = my_id->clone(my_id);
+ this->other.id = other_id->clone(other_id);
+ this->me.spi = 0;
+ this->other.spi = 0;
+ this->alloc_ah_spi = 0;
+ this->alloc_esp_spi = 0;
+ this->use_natt = use_natt;
+ this->state = CHILD_CREATED;
+ /* reuse old reqid if we are rekeying an existing CHILD_SA */
+ this->reqid = rekey ? rekey : ++reqid;
+ this->encryption.algorithm = ENCR_UNDEFINED;
+ this->encryption.key_size = 0;
+ this->integrity.algorithm = AUTH_UNDEFINED;
+ this->encryption.key_size = 0;
+ this->policies = linked_list_create();
+ this->my_ts = linked_list_create();
+ this->other_ts = linked_list_create();
+ this->protocol = PROTO_NONE;
+ this->mode = MODE_TUNNEL;
+ this->virtual_ip = NULL;
+ this->policy = policy;
+ policy->get_ref(policy);
+
+ return &this->public;
+}
diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h
new file mode 100644
index 000000000..216e56659
--- /dev/null
+++ b/src/charon/sa/child_sa.h
@@ -0,0 +1,298 @@
+/**
+ * @file child_sa.h
+ *
+ * @brief Interface of child_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+
+#ifndef CHILD_SA_H_
+#define CHILD_SA_H_
+
+typedef enum child_sa_state_t child_sa_state_t;
+typedef struct child_sa_t child_sa_t;
+
+#include <library.h>
+#include <crypto/prf_plus.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <config/proposal.h>
+#include <config/policies/policy.h>
+
+/**
+ * Where we should start with reqid enumeration
+ */
+#define REQID_START 2000000000
+
+/**
+ * @brief States of a CHILD_SA
+ */
+enum child_sa_state_t {
+
+ /**
+ * Just created, uninstalled CHILD_SA
+ */
+ CHILD_CREATED,
+
+ /**
+ * Installed SPD, but no SAD entries
+ */
+ CHILD_ROUTED,
+
+ /**
+ * Installed an in-use CHILD_SA
+ */
+ CHILD_INSTALLED,
+
+ /**
+ * CHILD_SA which is rekeying
+ */
+ CHILD_REKEYING,
+
+ /**
+ * CHILD_SA in progress of delete
+ */
+ CHILD_DELETING,
+};
+
+/**
+ * enum strings for child_sa_state_t.
+ */
+extern enum_name_t *child_sa_state_names;
+
+/**
+ * @brief Represents an IPsec SAs between two hosts.
+ *
+ * A child_sa_t contains two SAs. SAs for both
+ * directions are managed in one child_sa_t object. Both
+ * SAs and the policies have the same reqid.
+ *
+ * The procedure for child sa setup is as follows:
+ * - A gets SPIs for a proposal via child_sa_t.alloc
+ * - A send the updated proposal to B
+ * - B selects a suitable proposal
+ * - B calls child_sa_t.add to add and update the selected proposal
+ * - B sends the updated proposal to A
+ * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal
+ *
+ * Once SAs are set up, policies can be added using add_policies.
+ *
+ *
+ * @b Constructors:
+ * - child_sa_create()
+ *
+ * @ingroup sa
+ */
+struct child_sa_t {
+
+ /**
+ * @brief Get the name of the policy this CHILD_SA uses.
+ *
+ * @param this calling object
+ * @return name
+ */
+ char* (*get_name) (child_sa_t *this);
+
+ /**
+ * @brief Get the reqid of the CHILD SA.
+ *
+ * Every CHILD_SA has a reqid. The kernel uses this ID to
+ * identify it.
+ *
+ * @param this calling object
+ * @return reqid of the CHILD SA
+ */
+ u_int32_t (*get_reqid)(child_sa_t *this);
+
+ /**
+ * @brief Get the SPI of this CHILD_SA.
+ *
+ * Set the boolean parameter inbound to TRUE to
+ * get the SPI for which we receive packets, use
+ * FALSE to get those we use for sending packets.
+ *
+ * @param this calling object
+ * @param inbound TRUE to get inbound SPI, FALSE for outbound.
+ * @return spi of the CHILD SA
+ */
+ u_int32_t (*get_spi) (child_sa_t *this, bool inbound);
+
+ /**
+ * @brief Get the protocol which this CHILD_SA uses to protect traffic.
+ *
+ * @param this calling object
+ * @return AH | ESP
+ */
+ protocol_id_t (*get_protocol) (child_sa_t *this);
+
+ /**
+ * @brief Allocate SPIs for given proposals.
+ *
+ * Since the kernel manages SPIs for us, we need
+ * to allocate them. If a proposal contains more
+ * than one protocol, for each protocol an SPI is
+ * allocated. SPIs are stored internally and written
+ * back to the proposal.
+ *
+ * @param this calling object
+ * @param proposals list of proposals for which SPIs are allocated
+ */
+ status_t (*alloc)(child_sa_t *this, linked_list_t* proposals);
+
+ /**
+ * @brief Install the kernel SAs for a proposal, without previous SPI allocation.
+ *
+ * @param this calling object
+ * @param proposal proposal for which SPIs are allocated
+ * @param mode mode for the CHILD_SA
+ * @param prf_plus key material to use for key derivation
+ * @return SUCCESS or FAILED
+ */
+ status_t (*add)(child_sa_t *this, proposal_t *proposal, mode_t mode,
+ prf_plus_t *prf_plus);
+
+ /**
+ * @brief Install the kernel SAs for a proposal, after SPIs have been allocated.
+ *
+ * Updates an SA, for which SPIs are already allocated via alloc().
+ *
+ * @param this calling object
+ * @param proposal proposal for which SPIs are allocated
+ * @param mode mode for the CHILD_SA
+ * @param prf_plus key material to use for key derivation
+ * @return SUCCESS or FAILED
+ */
+ status_t (*update)(child_sa_t *this, proposal_t *proposal, mode_t mode,
+ prf_plus_t *prf_plus);
+
+ /**
+ * @brief Update the hosts in the kernel SAs and policies
+ *
+ * @warning only call this after update() has been called.
+ *
+ * @param this calling object
+ * @param new_me the new local host
+ * @param new_other the new remote host
+ * @param my_diff differences to apply for me
+ * @param other_diff differences to apply for other
+ * @return SUCCESS or FAILED
+ */
+ status_t (*update_hosts)(child_sa_t *this, host_t *new_me, host_t *new_other,
+ host_diff_t my_diff, host_diff_t other_diff);
+
+ /**
+ * @brief Install the policies using some traffic selectors.
+ *
+ * Supplied lists of traffic_selector_t's specify the policies
+ * to use for this child sa.
+ *
+ * @param this calling object
+ * @param my_ts traffic selectors for local site
+ * @param other_ts traffic selectors for remote site
+ * @param mode mode for the SA: tunnel/transport
+ * @return SUCCESS or FAILED
+ */
+ status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list,
+ linked_list_t *other_ts_list, mode_t mode);
+
+ /**
+ * @brief Get the traffic selectors of added policies of local host.
+ *
+ * @param this calling object
+ * @return list of traffic selectors
+ */
+ linked_list_t* (*get_my_traffic_selectors) (child_sa_t *this);
+
+ /**
+ * @brief Get the traffic selectors of added policies of remote host.
+ *
+ * @param this calling object
+ * @return list of traffic selectors
+ */
+ linked_list_t* (*get_other_traffic_selectors) (child_sa_t *this);
+
+ /**
+ * @brief Get the time of this child_sa_t's last use (i.e. last use of any of its policies)
+ *
+ * @param this calling object
+ * @param inbound query for in- or outbound usage
+ * @param use_time the time
+ * @return SUCCESS or FAILED
+ */
+ status_t (*get_use_time) (child_sa_t *this, bool inbound, time_t *use_time);
+
+ /**
+ * @brief Get the state of the CHILD_SA.
+ *
+ * @param this calling object
+ */
+ child_sa_state_t (*get_state) (child_sa_t *this);
+
+ /**
+ * @brief Set the state of the CHILD_SA.
+ *
+ * @param this calling object
+ */
+ void (*set_state) (child_sa_t *this, child_sa_state_t state);
+
+ /**
+ * @brief Get the policy used to set up this child sa.
+ *
+ * @param this calling object
+ * @return policy
+ */
+ policy_t* (*get_policy) (child_sa_t *this);
+
+ /**
+ * @brief Set the virtual IP used received from IRAS.
+ *
+ * To allow proper setup of firewall rules, the virtual IP is required
+ * for filtering.
+ *
+ * @param this calling object
+ * @param ip own virtual IP
+ */
+ void (*set_virtual_ip) (child_sa_t *this, host_t *ip);
+
+ /**
+ * @brief Destroys a child_sa.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (child_sa_t *this);
+};
+
+/**
+ * @brief Constructor to create a new child_sa_t.
+ *
+ * @param me own address
+ * @param other remote address
+ * @param my_id id of own peer
+ * @param other_id id of remote peer
+ * @param policy policy this CHILD_SA instantiates
+ * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise
+ * @param use_natt TRUE if NAT traversal is used
+ * @return child_sa_t object
+ *
+ * @ingroup sa
+ */
+child_sa_t * child_sa_create(host_t *me, host_t *other,
+ identification_t *my_id, identification_t* other_id,
+ policy_t *policy, u_int32_t reqid, bool use_natt);
+
+#endif /*CHILD_SA_H_*/
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
new file mode 100644
index 000000000..68aba3064
--- /dev/null
+++ b/src/charon/sa/ike_sa.c
@@ -0,0 +1,2032 @@
+/**
+ * @file ike_sa.c
+ *
+ * @brief Implementation of ike_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <sys/time.h>
+#include <string.h>
+#include <printf.h>
+#include <sys/stat.h>
+
+#include "ike_sa.h"
+
+#include <library.h>
+#include <daemon.h>
+#include <utils/linked_list.h>
+#include <utils/lexparser.h>
+#include <crypto/diffie_hellman.h>
+#include <crypto/prf_plus.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <encoding/payloads/transform_attribute.h>
+#include <encoding/payloads/ts_payload.h>
+#include <sa/task_manager.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_delete.h>
+#include <sa/tasks/child_rekey.h>
+#include <queues/jobs/retransmit_job.h>
+#include <queues/jobs/delete_ike_sa_job.h>
+#include <queues/jobs/send_dpd_job.h>
+#include <queues/jobs/send_keepalive_job.h>
+#include <queues/jobs/rekey_ike_sa_job.h>
+#include <queues/jobs/route_job.h>
+#include <queues/jobs/initiate_job.h>
+
+
+#ifndef RESOLV_CONF
+#define RESOLV_CONF "/etc/resolv.conf"
+#endif
+
+ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING,
+ "CREATED",
+ "CONNECTING",
+ "ESTABLISHED",
+ "REKEYING",
+ "DELETING",
+);
+
+typedef struct private_ike_sa_t private_ike_sa_t;
+
+/**
+ * Private data of an ike_sa_t object.
+ */
+struct private_ike_sa_t {
+
+ /**
+ * Public members
+ */
+ ike_sa_t public;
+
+ /**
+ * Identifier for the current IKE_SA.
+ */
+ ike_sa_id_t *ike_sa_id;
+
+ /**
+ * unique numerical ID for this IKE_SA.
+ */
+ u_int32_t unique_id;
+
+ /**
+ * Current state of the IKE_SA
+ */
+ ike_sa_state_t state;
+
+ /**
+ * connection used to establish this IKE_SA.
+ */
+ connection_t *connection;
+
+ /**
+ * Peer and authentication information to establish IKE_SA.
+ */
+ policy_t *policy;
+
+ /**
+ * Juggles tasks to process messages
+ */
+ task_manager_t *task_manager;
+
+ /**
+ * Address of local host
+ */
+ host_t *my_host;
+
+ /**
+ * Address of remote host
+ */
+ host_t *other_host;
+
+ /**
+ * Identification used for us
+ */
+ identification_t *my_id;
+
+ /**
+ * Identification used for other
+ */
+ identification_t *other_id;
+
+ /**
+ * Linked List containing the child sa's of the current IKE_SA.
+ */
+ linked_list_t *child_sas;
+
+ /**
+ * crypter for inbound traffic
+ */
+ crypter_t *crypter_in;
+
+ /**
+ * crypter for outbound traffic
+ */
+ crypter_t *crypter_out;
+
+ /**
+ * Signer for inbound traffic
+ */
+ signer_t *signer_in;
+
+ /**
+ * Signer for outbound traffic
+ */
+ signer_t *signer_out;
+
+ /**
+ * Multi purpose prf, set key, use it, forget it
+ */
+ prf_t *prf;
+
+ /**
+ * Prf function for derivating keymat child SAs
+ */
+ prf_t *child_prf;
+
+ /**
+ * PRF to build outging authentication data
+ */
+ prf_t *auth_build;
+
+ /**
+ * PRF to verify incoming authentication data
+ */
+ prf_t *auth_verify;
+
+ /**
+ * NAT status of local host.
+ */
+ bool nat_here;
+
+ /**
+ * NAT status of remote host.
+ */
+ bool nat_there;
+
+ /**
+ * Virtual IP on local host, if any
+ */
+ host_t *my_virtual_ip;
+
+ /**
+ * Virtual IP on remote host, if any
+ */
+ host_t *other_virtual_ip;
+
+ /**
+ * List of DNS servers installed by us
+ */
+ linked_list_t *dns_servers;
+
+ /**
+ * Timestamps for this IKE_SA
+ */
+ struct {
+ /** last IKE message received */
+ u_int32_t inbound;
+ /** last IKE message sent */
+ u_int32_t outbound;
+ /** when IKE_SA became established */
+ u_int32_t established;
+ /** when IKE_SA gets rekeyed */
+ u_int32_t rekey;
+ /** when IKE_SA gets deleted */
+ u_int32_t delete;
+ } time;
+
+ /**
+ * how many times we have retried so far (keyingtries)
+ */
+ u_int32_t keyingtry;
+};
+
+/**
+ * get the time of the latest traffic processed by the kernel
+ */
+static time_t get_use_time(private_ike_sa_t* this, bool inbound)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ time_t latest = 0, use_time;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS)
+ {
+ latest = max(latest, use_time);
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (inbound)
+ {
+ return max(this->time.inbound, latest);
+ }
+ else
+ {
+ return max(this->time.outbound, latest);
+ }
+}
+
+/**
+ * Implementation of ike_sa_t.get_unique_id
+ */
+static u_int32_t get_unique_id(private_ike_sa_t *this)
+{
+ return this->unique_id;
+}
+
+/**
+ * Implementation of ike_sa_t.get_name.
+ */
+static char *get_name(private_ike_sa_t *this)
+{
+ if (this->connection)
+ {
+ return this->connection->get_name(this->connection);
+ }
+ return "(unnamed)";
+}
+
+/**
+ * Implementation of ike_sa_t.get_connection
+ */
+static connection_t* get_connection(private_ike_sa_t *this)
+{
+ return this->connection;
+}
+
+/**
+ * Implementation of ike_sa_t.set_connection
+ */
+static void set_connection(private_ike_sa_t *this, connection_t *connection)
+{
+ this->connection = connection;
+ connection->get_ref(connection);
+}
+
+/**
+ * Implementation of ike_sa_t.get_policy
+ */
+static policy_t *get_policy(private_ike_sa_t *this)
+{
+ return this->policy;
+}
+
+/**
+ * Implementation of ike_sa_t.set_policy
+ */
+static void set_policy(private_ike_sa_t *this, policy_t *policy)
+{
+ policy->get_ref(policy);
+ this->policy = policy;
+}
+
+/**
+ * Implementation of ike_sa_t.get_my_host.
+ */
+static host_t *get_my_host(private_ike_sa_t *this)
+{
+ return this->my_host;
+}
+
+/**
+ * Implementation of ike_sa_t.set_my_host.
+ */
+static void set_my_host(private_ike_sa_t *this, host_t *me)
+{
+ DESTROY_IF(this->my_host);
+ this->my_host = me;
+}
+
+/**
+ * Implementation of ike_sa_t.get_other_host.
+ */
+static host_t *get_other_host(private_ike_sa_t *this)
+{
+ return this->other_host;
+}
+
+/**
+ * Implementation of ike_sa_t.set_other_host.
+ */
+static void set_other_host(private_ike_sa_t *this, host_t *other)
+{
+ DESTROY_IF(this->other_host);
+ this->other_host = other;
+}
+
+/**
+ * Implementation of ike_sa_t.send_dpd
+ */
+static status_t send_dpd(private_ike_sa_t *this)
+{
+ send_dpd_job_t *job;
+ time_t diff, delay;
+
+ delay = this->connection->get_dpd_delay(this->connection);
+
+ if (delay == 0)
+ {
+ /* DPD disabled */
+ return SUCCESS;
+ }
+
+ if (this->task_manager->busy(this->task_manager))
+ {
+ /* an exchange is in the air, no need to start a DPD check */
+ diff = 0;
+ }
+ else
+ {
+ /* check if there was any inbound traffic */
+ time_t last_in, now;
+ last_in = get_use_time(this, TRUE);
+ now = time(NULL);
+ diff = now - last_in;
+ if (diff >= delay)
+ {
+ /* to long ago, initiate dead peer detection */
+ task_t *task;
+
+ task = (task_t*)ike_dpd_create(TRUE);
+ diff = 0;
+ DBG1(DBG_IKE, "sending DPD request");
+
+ this->task_manager->queue_task(this->task_manager, task);
+ this->task_manager->initiate(this->task_manager);
+ }
+ }
+ /* recheck in "interval" seconds */
+ job = send_dpd_job_create(this->ike_sa_id);
+ charon->event_queue->add_relative(charon->event_queue, (job_t*)job,
+ (delay - diff) * 1000);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of ike_sa_t.send_keepalive
+ */
+static void send_keepalive(private_ike_sa_t *this)
+{
+ send_keepalive_job_t *job;
+ time_t last_out, now, diff, interval;
+
+ last_out = get_use_time(this, FALSE);
+ now = time(NULL);
+
+ diff = now - last_out;
+ interval = charon->configuration->get_keepalive_interval(charon->configuration);
+
+ if (diff >= interval)
+ {
+ packet_t *packet;
+ chunk_t data;
+
+ packet = packet_create();
+ packet->set_source(packet, this->my_host->clone(this->my_host));
+ packet->set_destination(packet, this->other_host->clone(this->other_host));
+ data.ptr = malloc(1);
+ data.ptr[0] = 0xFF;
+ data.len = 1;
+ packet->set_data(packet, data);
+ charon->sender->send(charon->sender, packet);
+ DBG1(DBG_IKE, "sending keep alive");
+ diff = 0;
+ }
+ job = send_keepalive_job_create(this->ike_sa_id);
+ charon->event_queue->add_relative(charon->event_queue, (job_t*)job,
+ (interval - diff) * 1000);
+}
+
+/**
+ * Implementation of ike_sa_t.get_state.
+ */
+static ike_sa_state_t get_state(private_ike_sa_t *this)
+{
+ return this->state;
+}
+
+/**
+ * Implementation of ike_sa_t.set_state.
+ */
+static void set_state(private_ike_sa_t *this, ike_sa_state_t state)
+{
+ DBG1(DBG_IKE, "IKE_SA state change: %N => %N",
+ ike_sa_state_names, this->state,
+ ike_sa_state_names, state);
+
+ switch (state)
+ {
+ case IKE_ESTABLISHED:
+ {
+ if (this->state == IKE_CONNECTING)
+ {
+ job_t *job;
+ u_int32_t now = time(NULL);
+ u_int32_t soft, hard;
+ bool reauth;
+
+ this->time.established = now;
+ /* start DPD checks */
+ send_dpd(this);
+
+ /* schedule rekeying/reauthentication */
+ soft = this->connection->get_soft_lifetime(this->connection);
+ hard = this->connection->get_hard_lifetime(this->connection);
+ reauth = this->connection->get_reauth(this->connection);
+ DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds",
+ reauth ? "reauthentication": "rekeying", soft, hard);
+
+ if (soft)
+ {
+ this->time.rekey = now + soft;
+ job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ soft * 1000);
+ }
+
+ if (hard)
+ {
+ this->time.delete = now + hard;
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ hard * 1000);
+ }
+ }
+ break;
+ }
+ case IKE_DELETING:
+ {
+ /* delete may fail if a packet gets lost, so set a timeout */
+ job_t *job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ charon->configuration->get_half_open_ike_sa_timeout(
+ charon->configuration));
+ break;
+ }
+ default:
+ break;
+ }
+
+ this->state = state;
+}
+
+/**
+ * Implementation of ike_sa_t.reset
+ */
+static void reset(private_ike_sa_t *this)
+{
+ /* the responder ID is reset, as peer may choose another one */
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0);
+ }
+
+ set_state(this, IKE_CREATED);
+
+ this->task_manager->reset(this->task_manager);
+}
+
+/**
+ * Update connection host, as addresses may change (NAT)
+ */
+static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
+{
+ iterator_t *iterator = NULL;
+ child_sa_t *child_sa = NULL;
+ host_diff_t my_diff, other_diff;
+
+ if (this->my_host->is_anyaddr(this->my_host) ||
+ this->other_host->is_anyaddr(this->other_host))
+ {
+ /* on first received message */
+ this->my_host->destroy(this->my_host);
+ this->my_host = me->clone(me);
+ this->other_host->destroy(this->other_host);
+ this->other_host = other->clone(other);
+ return;
+ }
+
+ my_diff = me->get_differences(me, this->my_host);
+ other_diff = other->get_differences(other, this->other_host);
+
+ if (!my_diff && !other_diff)
+ {
+ return;
+ }
+
+ if (my_diff)
+ {
+ this->my_host->destroy(this->my_host);
+ this->my_host = me->clone(me);
+ }
+
+ if (!this->nat_here)
+ {
+ /* update without restrictions if we are not NATted */
+ if (other_diff)
+ {
+ this->other_host->destroy(this->other_host);
+ this->other_host = other->clone(other);
+ }
+ }
+ else
+ {
+ /* if we are natted, only port may change */
+ if (other_diff & HOST_DIFF_ADDR)
+ {
+ return;
+ }
+ else if (other_diff & HOST_DIFF_PORT)
+ {
+ this->other_host->set_port(this->other_host, other->get_port(other));
+ }
+ }
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ child_sa->update_hosts(child_sa, this->my_host, this->other_host,
+ my_diff, other_diff);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of ike_sa_t.generate
+ */
+static status_t generate_message(private_ike_sa_t *this, message_t *message,
+ packet_t **packet)
+{
+ this->time.outbound = time(NULL);
+ message->set_ike_sa_id(message, this->ike_sa_id);
+ message->set_destination(message, this->other_host->clone(this->other_host));
+ message->set_source(message, this->my_host->clone(this->my_host));
+ return message->generate(message, this->crypter_out, this->signer_out, packet);
+}
+
+/**
+ * send a notify back to the sender
+ */
+static void send_notify_response(private_ike_sa_t *this, message_t *request,
+ notify_type_t type)
+{
+ message_t *response;
+ packet_t *packet;
+
+ response = message_create();
+ response->set_exchange_type(response, request->get_exchange_type(request));
+ response->set_request(response, FALSE);
+ response->set_message_id(response, request->get_message_id(request));
+ response->add_notify(response, FALSE, type, chunk_empty);
+ if (this->my_host->is_anyaddr(this->my_host))
+ {
+ this->my_host->destroy(this->my_host);
+ this->my_host = request->get_destination(request);
+ this->my_host = this->my_host->clone(this->my_host);
+ }
+ if (this->other_host->is_anyaddr(this->other_host))
+ {
+ this->other_host->destroy(this->other_host);
+ this->other_host = request->get_source(request);
+ this->other_host = this->other_host->clone(this->other_host);
+ }
+ if (generate_message(this, response, &packet) == SUCCESS)
+ {
+ charon->sender->send(charon->sender, packet);
+ }
+ response->destroy(response);
+}
+
+/**
+ * Implementation of ike_sa_t.process_message.
+ */
+static status_t process_message(private_ike_sa_t *this, message_t *message)
+{
+ status_t status;
+ bool is_request;
+
+ is_request = message->get_request(message);
+
+ status = message->parse_body(message, this->crypter_in, this->signer_in);
+ if (status != SUCCESS)
+ {
+
+ if (is_request)
+ {
+ switch (status)
+ {
+ case NOT_SUPPORTED:
+ DBG1(DBG_IKE, "ciritcal unknown payloads found");
+ if (is_request)
+ {
+ send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD);
+ }
+ break;
+ case PARSE_ERROR:
+ DBG1(DBG_IKE, "message parsing failed");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ break;
+ case VERIFY_ERROR:
+ DBG1(DBG_IKE, "message verification failed");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ break;
+ case FAILED:
+ DBG1(DBG_IKE, "integrity check failed");
+ /* ignored */
+ break;
+ case INVALID_STATE:
+ DBG1(DBG_IKE, "found encrypted message, but no keys available");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ default:
+ break;
+ }
+ }
+ DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
+ exchange_type_names, message->get_exchange_type(message),
+ message->get_request(message) ? "request" : "response",
+ message->get_message_id(message));
+ return status;
+ }
+ else
+ {
+ host_t *me, *other;
+
+ me = message->get_destination(message);
+ other = message->get_source(message);
+
+ /* if this IKE_SA is virgin, we check for a connection */
+ if (this->connection == NULL)
+ {
+ job_t *job;
+ this->connection = charon->connections->get_connection_by_hosts(
+ charon->connections, me, other);
+ if (this->connection == NULL)
+ {
+ /* no connection found for these hosts, destroy */
+ DBG1(DBG_IKE, "no connection found for %H...%H, sending %N",
+ me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
+ send_notify_response(this, message, NO_PROPOSAL_CHOSEN);
+ return DESTROY_ME;
+ }
+ /* add a timeout if peer does not establish it completely */
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, FALSE);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ charon->configuration->get_half_open_ike_sa_timeout(
+ charon->configuration));
+ }
+
+ /* check if message is trustworthy, and update connection information */
+ if (this->state == IKE_CREATED ||
+ message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ update_hosts(this, me, other);
+ this->time.inbound = time(NULL);
+ }
+ return this->task_manager->process_message(this->task_manager, message);
+ }
+}
+
+/**
+ * apply the connection/policy information to this IKE_SA
+ */
+static void apply_config(private_ike_sa_t *this,
+ connection_t *connection, policy_t *policy)
+{
+ host_t *me, *other;
+ identification_t *my_id, *other_id;
+
+ if (this->connection == NULL && this->policy == NULL)
+ {
+ this->connection = connection;
+ connection->get_ref(connection);
+ this->policy = policy;
+ policy->get_ref(policy);
+
+ me = connection->get_my_host(connection);
+ other = connection->get_other_host(connection);
+ my_id = policy->get_my_id(policy);
+ other_id = policy->get_other_id(policy);
+ set_my_host(this, me->clone(me));
+ set_other_host(this, other->clone(other));
+ DESTROY_IF(this->my_id);
+ DESTROY_IF(this->other_id);
+ this->my_id = my_id->clone(my_id);
+ this->other_id = other_id->clone(other_id);
+ }
+}
+
+/**
+ * Implementation of ike_sa_t.initiate.
+ */
+static status_t initiate(private_ike_sa_t *this,
+ connection_t *connection, policy_t *policy)
+{
+ task_t *task;
+
+ if (this->state == IKE_CREATED)
+ {
+ /* if we aren't established/establishing, do so */
+ apply_config(this, connection, policy);
+
+ if (this->other_host->is_anyaddr(this->other_host))
+ {
+ SIG(IKE_UP_START, "initiating IKE_SA");
+ SIG(IKE_UP_FAILED, "unable to initiate to %%any");
+ return DESTROY_ME;
+ }
+
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
+
+ task = (task_t*)child_create_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.acquire.
+ */
+static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
+{
+ policy_t *policy;
+ iterator_t *iterator;
+ child_sa_t *current, *child_sa = NULL;
+ task_t *task;
+ child_create_t *child_create;
+
+ if (this->state == IKE_DELETING)
+ {
+ SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request");
+ SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
+ "IKE_SA is deleting", reqid);
+ return FAILED;
+ }
+
+ /* find CHILD_SA */
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (current->get_reqid(current) == reqid)
+ {
+ child_sa = current;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ if (!child_sa)
+ {
+ SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request");
+ SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
+ "CHILD_SA not found", reqid);
+ return FAILED;
+ }
+
+ policy = child_sa->get_policy(child_sa);
+
+ if (this->state == IKE_CREATED)
+ {
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
+
+ child_create = child_create_create(&this->public, policy);
+ child_create->use_reqid(child_create, reqid);
+ this->task_manager->queue_task(this->task_manager, (task_t*)child_create);
+
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * compare two lists of traffic selectors for equality
+ */
+static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2)
+{
+ bool equals = TRUE;
+ iterator_t *i1, *i2;
+ traffic_selector_t *t1, *t2;
+
+ if (l1->get_count(l1) != l2->get_count(l2))
+ {
+ return FALSE;
+ }
+
+ i1 = l1->create_iterator(l1, TRUE);
+ i2 = l2->create_iterator(l2, TRUE);
+ while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2))
+ {
+ if (!t1->equals(t1, t2))
+ {
+ equals = FALSE;
+ break;
+ }
+ }
+ i1->destroy(i1);
+ i2->destroy(i2);
+ return equals;
+}
+
+/**
+ * Implementation of ike_sa_t.route.
+ */
+static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy)
+{
+ child_sa_t *child_sa = NULL;
+ iterator_t *iterator;
+ linked_list_t *my_ts, *other_ts;
+ status_t status;
+
+ SIG(CHILD_ROUTE_START, "routing CHILD_SA");
+
+ /* check if not already routed*/
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ if (child_sa->get_state(child_sa) == CHILD_ROUTED)
+ {
+ linked_list_t *my_ts_conf, *other_ts_conf;
+
+ my_ts = child_sa->get_my_traffic_selectors(child_sa);
+ other_ts = child_sa->get_other_traffic_selectors(child_sa);
+
+ my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host);
+ other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host);
+
+ if (ts_list_equals(my_ts, my_ts_conf) &&
+ ts_list_equals(other_ts, other_ts_conf))
+ {
+ iterator->destroy(iterator);
+ my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy));
+ other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy));
+ SIG(CHILD_ROUTE_FAILED, "CHILD_SA with such a policy already routed");
+ return FAILED;
+ }
+ my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy));
+ other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy));
+ }
+ }
+ iterator->destroy(iterator);
+
+ switch (this->state)
+ {
+ case IKE_DELETING:
+ case IKE_REKEYING:
+ SIG(CHILD_ROUTE_FAILED,
+ "unable to route CHILD_SA, as its IKE_SA gets deleted");
+ return FAILED;
+ case IKE_CREATED:
+ /* apply connection information, we need it to acquire */
+ apply_config(this, connection, policy);
+ break;
+ case IKE_CONNECTING:
+ case IKE_ESTABLISHED:
+ default:
+ break;
+ }
+
+ /* install kernel policies */
+ child_sa = child_sa_create(this->my_host, this->other_host,
+ this->my_id, this->other_id, policy, FALSE, 0);
+
+ my_ts = policy->get_my_traffic_selectors(policy, this->my_host);
+ other_ts = policy->get_other_traffic_selectors(policy, this->other_host);
+ status = child_sa->add_policies(child_sa, my_ts, other_ts,
+ policy->get_mode(policy));
+ my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ SIG(CHILD_ROUTE_SUCCESS, "CHILD_SA routed");
+ return status;
+}
+
+/**
+ * Implementation of ike_sa_t.unroute.
+ */
+static status_t unroute(private_ike_sa_t *this, policy_t *policy)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa = NULL;
+ bool found = FALSE;
+ linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf;
+
+ SIG(CHILD_UNROUTE_START, "unrouting CHILD_SA");
+
+ /* find CHILD_SA in ROUTED state */
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ if (child_sa->get_state(child_sa) == CHILD_ROUTED)
+ {
+ my_ts = child_sa->get_my_traffic_selectors(child_sa);
+ other_ts = child_sa->get_other_traffic_selectors(child_sa);
+
+ my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host);
+ other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host);
+
+ if (ts_list_equals(my_ts, my_ts_conf) &&
+ ts_list_equals(other_ts, other_ts_conf))
+ {
+ iterator->remove(iterator);
+ SIG(CHILD_UNROUTE_SUCCESS, "CHILD_SA unrouted");
+ child_sa->destroy(child_sa);
+ my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy));
+ other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy));
+ found = TRUE;
+ break;
+ }
+ my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy));
+ other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy));
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (!found)
+ {
+ SIG(CHILD_UNROUTE_FAILED, "CHILD_SA to unroute not found");
+ return FAILED;
+ }
+ /* if we are not established, and we have no more routed childs, remove whole SA */
+ if (this->state == IKE_CREATED &&
+ this->child_sas->get_count(this->child_sas) == 0)
+ {
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of ike_sa_t.retransmit.
+ */
+static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id)
+{
+ this->time.outbound = time(NULL);
+ if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
+ {
+ policy_t *policy;
+ child_sa_t* child_sa;
+ linked_list_t *to_route, *to_restart;
+ iterator_t *iterator;
+
+ /* send a proper signal to brief interested bus listeners */
+ switch (this->state)
+ {
+ case IKE_CONNECTING:
+ {
+ /* retry IKE_SA_INIT if we have multiple keyingtries */
+ u_int32_t tries = this->connection->get_keyingtries(this->connection);
+ this->keyingtry++;
+ if (tries == 0 || tries > this->keyingtry)
+ {
+ SIG(IKE_UP_FAILED, "peer not responding, trying again "
+ "(%d/%d) in background ", this->keyingtry + 1, tries);
+ reset(this);
+ return this->task_manager->initiate(this->task_manager);
+ }
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding");
+ break;
+ }
+ case IKE_REKEYING:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding");
+ break;
+ case IKE_DELETING:
+ SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding");
+ break;
+ default:
+ break;
+ }
+
+ /* summarize how we have to handle each child */
+ to_route = linked_list_create();
+ to_restart = linked_list_create();
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ policy = child_sa->get_policy(child_sa);
+
+ if (child_sa->get_state(child_sa) == CHILD_ROUTED)
+ {
+ /* reroute routed CHILD_SAs */
+ to_route->insert_last(to_route, policy);
+ }
+ else
+ {
+ /* use DPD action for established CHILD_SAs */
+ switch (policy->get_dpd_action(policy))
+ {
+ case DPD_ROUTE:
+ to_route->insert_last(to_route, policy);
+ break;
+ case DPD_RESTART:
+ to_restart->insert_last(to_restart, policy);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* create a new IKE_SA if we have to route or to restart */
+ if (to_route->get_count(to_route) || to_restart->get_count(to_restart))
+ {
+ private_ike_sa_t *new;
+ task_t *task;
+
+ new = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new(
+ charon->ike_sa_manager, TRUE);
+
+ apply_config(new, this->connection, this->policy);
+ /* use actual used host, not the wildcarded one in connection */
+ new->other_host->destroy(new->other_host);
+ new->other_host = this->other_host->clone(this->other_host);
+
+ /* install routes */
+ while (to_route->remove_last(to_route, (void**)&policy) == SUCCESS)
+ {
+ route(new, new->connection, policy);
+ }
+
+ /* restart children */
+ if (to_restart->get_count(to_restart))
+ {
+ task = (task_t*)ike_init_create(&new->public, TRUE, NULL);
+ new->task_manager->queue_task(new->task_manager, task);
+ task = (task_t*)ike_natd_create(&new->public, TRUE);
+ new->task_manager->queue_task(new->task_manager, task);
+ task = (task_t*)ike_cert_create(&new->public, TRUE);
+ new->task_manager->queue_task(new->task_manager, task);
+ task = (task_t*)ike_config_create(&new->public, new->policy);
+ new->task_manager->queue_task(new->task_manager, task);
+ task = (task_t*)ike_auth_create(&new->public, TRUE);
+ new->task_manager->queue_task(new->task_manager, task);
+
+ while (to_restart->remove_last(to_restart, (void**)&policy) == SUCCESS)
+ {
+ task = (task_t*)child_create_create(&new->public, policy);
+ new->task_manager->queue_task(new->task_manager, task);
+ }
+ new->task_manager->initiate(new->task_manager);
+ }
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public);
+ }
+ to_route->destroy(to_route);
+ to_restart->destroy(to_restart);
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of ike_sa_t.get_prf.
+ */
+static prf_t *get_prf(private_ike_sa_t *this)
+{
+ return this->prf;
+}
+
+/**
+ * Implementation of ike_sa_t.get_prf.
+ */
+static prf_t *get_child_prf(private_ike_sa_t *this)
+{
+ return this->child_prf;
+}
+
+/**
+ * Implementation of ike_sa_t.get_auth_bild
+ */
+static prf_t *get_auth_build(private_ike_sa_t *this)
+{
+ return this->auth_build;
+}
+
+/**
+ * Implementation of ike_sa_t.get_auth_verify
+ */
+static prf_t *get_auth_verify(private_ike_sa_t *this)
+{
+ return this->auth_verify;
+}
+
+/**
+ * Implementation of ike_sa_t.get_id.
+ */
+static ike_sa_id_t* get_id(private_ike_sa_t *this)
+{
+ return this->ike_sa_id;
+}
+
+/**
+ * Implementation of ike_sa_t.get_my_id.
+ */
+static identification_t* get_my_id(private_ike_sa_t *this)
+{
+ return this->my_id;
+}
+
+/**
+ * Implementation of ike_sa_t.set_my_id.
+ */
+static void set_my_id(private_ike_sa_t *this, identification_t *me)
+{
+ DESTROY_IF(this->my_id);
+ this->my_id = me;
+}
+
+/**
+ * Implementation of ike_sa_t.get_other_id.
+ */
+static identification_t* get_other_id(private_ike_sa_t *this)
+{
+ return this->other_id;
+}
+
+/**
+ * Implementation of ike_sa_t.set_other_id.
+ */
+static void set_other_id(private_ike_sa_t *this, identification_t *other)
+{
+ DESTROY_IF(this->other_id);
+ this->other_id = other;
+}
+
+/**
+ * Implementation of ike_sa_t.derive_keys.
+ */
+static status_t derive_keys(private_ike_sa_t *this,
+ proposal_t *proposal, chunk_t secret,
+ chunk_t nonce_i, chunk_t nonce_r,
+ bool initiator, prf_t *child_prf, prf_t *old_prf)
+{
+ prf_plus_t *prf_plus;
+ chunk_t skeyseed, key, nonces, prf_plus_seed;
+ algorithm_t *algo;
+ size_t key_size;
+ crypter_t *crypter_i, *crypter_r;
+ signer_t *signer_i, *signer_r;
+ prf_t *prf_i, *prf_r;
+ u_int8_t spi_i_buf[sizeof(u_int64_t)], spi_r_buf[sizeof(u_int64_t)];
+ chunk_t spi_i = chunk_from_buf(spi_i_buf);
+ chunk_t spi_r = chunk_from_buf(spi_r_buf);
+
+ /* Create SAs general purpose PRF first, we may use it here */
+ if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo))
+ {
+ DBG1(DBG_IKE, "key derivation failed: no PSEUDO_RANDOM_FUNCTION");;
+ return FAILED;
+ }
+ this->prf = prf_create(algo->algorithm);
+ if (this->prf == NULL)
+ {
+ DBG1(DBG_IKE, "key derivation failed: PSEUDO_RANDOM_FUNCTION "
+ "%N not supported!", pseudo_random_function_names, algo->algorithm);
+ return FAILED;
+ }
+
+ DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
+ nonces = chunk_cat("cc", nonce_i, nonce_r);
+ *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id);
+ *((u_int64_t*)spi_r.ptr) = this->ike_sa_id->get_responder_spi(this->ike_sa_id);
+ prf_plus_seed = chunk_cat("ccc", nonces, spi_i, spi_r);
+
+ /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
+ *
+ * if we are rekeying, SKEYSEED is built on another way
+ */
+ if (child_prf == NULL) /* not rekeying */
+ {
+ /* SKEYSEED = prf(Ni | Nr, g^ir) */
+ this->prf->set_key(this->prf, nonces);
+ this->prf->allocate_bytes(this->prf, secret, &skeyseed);
+ DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
+ this->prf->set_key(this->prf, skeyseed);
+ chunk_free(&skeyseed);
+ chunk_free(&secret);
+ prf_plus = prf_plus_create(this->prf, prf_plus_seed);
+ }
+ else
+ {
+ /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
+ * use OLD SAs PRF functions for both prf_plus and prf */
+ secret = chunk_cat("mc", secret, nonces);
+ child_prf->allocate_bytes(child_prf, secret, &skeyseed);
+ DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
+ old_prf->set_key(old_prf, skeyseed);
+ chunk_free(&skeyseed);
+ chunk_free(&secret);
+ prf_plus = prf_plus_create(old_prf, prf_plus_seed);
+ }
+ chunk_free(&nonces);
+ chunk_free(&prf_plus_seed);
+
+ /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
+
+ /* SK_d is used for generating CHILD_SA key mat => child_prf */
+ proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo);
+ this->child_prf = prf_create(algo->algorithm);
+ key_size = this->child_prf->get_key_size(this->child_prf);
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_d secret %B", &key);
+ this->child_prf->set_key(this->child_prf, key);
+ chunk_free(&key);
+
+ /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */
+ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo))
+ {
+ DBG1(DBG_IKE, "key derivation failed: no INTEGRITY_ALGORITHM");
+ return FAILED;
+ }
+ signer_i = signer_create(algo->algorithm);
+ signer_r = signer_create(algo->algorithm);
+ if (signer_i == NULL || signer_r == NULL)
+ {
+ DBG1(DBG_IKE, "key derivation failed: INTEGRITY_ALGORITHM "
+ "%N not supported!", integrity_algorithm_names ,algo->algorithm);
+ return FAILED;
+ }
+ key_size = signer_i->get_key_size(signer_i);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_ai secret %B", &key);
+ signer_i->set_key(signer_i, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_ar secret %B", &key);
+ signer_r->set_key(signer_r, key);
+ chunk_free(&key);
+
+ if (initiator)
+ {
+ this->signer_in = signer_r;
+ this->signer_out = signer_i;
+ }
+ else
+ {
+ this->signer_in = signer_i;
+ this->signer_out = signer_r;
+ }
+
+ /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */
+ if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo))
+ {
+ DBG1(DBG_IKE, "key derivation failed: no ENCRYPTION_ALGORITHM");
+ return FAILED;
+ }
+ crypter_i = crypter_create(algo->algorithm, algo->key_size / 8);
+ crypter_r = crypter_create(algo->algorithm, algo->key_size / 8);
+ if (crypter_i == NULL || crypter_r == NULL)
+ {
+ DBG1(DBG_IKE, "key derivation failed: ENCRYPTION_ALGORITHM "
+ "%N (key size %d) not supported!",
+ encryption_algorithm_names, algo->algorithm, algo->key_size);
+ return FAILED;
+ }
+ key_size = crypter_i->get_key_size(crypter_i);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_ei secret %B", &key);
+ crypter_i->set_key(crypter_i, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_er secret %B", &key);
+ crypter_r->set_key(crypter_r, key);
+ chunk_free(&key);
+
+ if (initiator)
+ {
+ this->crypter_in = crypter_r;
+ this->crypter_out = crypter_i;
+ }
+ else
+ {
+ this->crypter_in = crypter_i;
+ this->crypter_out = crypter_r;
+ }
+
+ /* SK_pi/SK_pr used for authentication => prf_auth_i, prf_auth_r */
+ proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo);
+ prf_i = prf_create(algo->algorithm);
+ prf_r = prf_create(algo->algorithm);
+
+ key_size = prf_i->get_key_size(prf_i);
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_pi secret %B", &key);
+ prf_i->set_key(prf_i, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ DBG4(DBG_IKE, "Sk_pr secret %B", &key);
+ prf_r->set_key(prf_r, key);
+ chunk_free(&key);
+
+ if (initiator)
+ {
+ this->auth_verify = prf_r;
+ this->auth_build = prf_i;
+ }
+ else
+ {
+ this->auth_verify = prf_i;
+ this->auth_build = prf_r;
+ }
+
+ /* all done, prf_plus not needed anymore */
+ prf_plus->destroy(prf_plus);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of ike_sa_t.add_child_sa.
+ */
+static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa)
+{
+ this->child_sas->insert_last(this->child_sas, child_sa);
+}
+
+/**
+ * Implementation of ike_sa_t.get_child_sa.
+ */
+static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi, bool inbound)
+{
+ iterator_t *iterator;
+ child_sa_t *current, *found = NULL;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (current->get_spi(current, inbound) == spi &&
+ current->get_protocol(current) == protocol)
+ {
+ found = current;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+/**
+ * Implementation of ike_sa_t.create_child_sa_iterator.
+ */
+static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
+{
+ return this->child_sas->create_iterator(this->child_sas, TRUE);
+}
+
+/**
+ * Implementation of ike_sa_t.rekey_child_sa.
+ */
+static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+{
+ child_sa_t *child_sa;
+ child_rekey_t *child_rekey;
+
+ child_sa = get_child_sa(this, protocol, spi, TRUE);
+ if (child_sa)
+ {
+ child_rekey = child_rekey_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of ike_sa_t.delete_child_sa.
+ */
+static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+{
+ child_sa_t *child_sa;
+ child_delete_t *child_delete;
+
+ child_sa = get_child_sa(this, protocol, spi, TRUE);
+ if (child_sa)
+ {
+ child_delete = child_delete_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_delete->task);
+ return this->task_manager->initiate(this->task_manager);
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of ike_sa_t.destroy_child_sa.
+ */
+static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ status_t status = NOT_FOUND;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ if (child_sa->get_protocol(child_sa) == protocol &&
+ child_sa->get_spi(child_sa, TRUE) == spi)
+ {
+ child_sa->destroy(child_sa);
+ iterator->remove(iterator);
+ status = SUCCESS;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return status;
+}
+
+/**
+ * Implementation of public_ike_sa_t.delete.
+ */
+static status_t delete_(private_ike_sa_t *this)
+{
+ ike_delete_t *ike_delete;
+
+ switch (this->state)
+ {
+ case IKE_ESTABLISHED:
+ DBG1(DBG_IKE, "deleting IKE_SA");
+ /* do not log when rekeyed */
+ case IKE_REKEYING:
+ ike_delete = ike_delete_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, &ike_delete->task);
+ return this->task_manager->initiate(this->task_manager);
+ default:
+ DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification",
+ ike_sa_state_names, this->state);
+ break;
+ }
+ return DESTROY_ME;
+}
+
+/**
+ * Implementation of ike_sa_t.rekey.
+ */
+static status_t rekey(private_ike_sa_t *this)
+{
+ ike_rekey_t *ike_rekey;
+
+ ike_rekey = ike_rekey_create(&this->public, TRUE);
+
+ this->task_manager->queue_task(this->task_manager, &ike_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.reestablish
+ */
+static void reestablish(private_ike_sa_t *this)
+{
+ private_ike_sa_t *other;
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ policy_t *policy;
+ task_t *task;
+ job_t *job;
+
+ other = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new(
+ charon->ike_sa_manager, TRUE);
+
+ apply_config(other, this->connection, this->policy);
+ other->other_host->destroy(other->other_host);
+ other->other_host = this->other_host->clone(this->other_host);
+
+ if (this->state == IKE_ESTABLISHED)
+ {
+ task = (task_t*)ike_init_create(&other->public, TRUE, NULL);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_natd_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_cert_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_config_create(&other->public, other->policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_auth_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ }
+
+ other->task_manager->adopt_tasks(other->task_manager, this->task_manager);
+
+ /* Create task for established children, adopt routed children directly */
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while(iterator->iterate(iterator, (void**)&child_sa))
+ {
+ switch (child_sa->get_state(child_sa))
+ {
+ case CHILD_ROUTED:
+ {
+ iterator->remove(iterator);
+ other->child_sas->insert_first(other->child_sas, child_sa);
+ break;
+ }
+ default:
+ {
+ policy = child_sa->get_policy(child_sa);
+ task = (task_t*)child_create_create(&other->public, policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ other->task_manager->initiate(other->task_manager);
+
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public);
+
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
+}
+
+/**
+ * Implementation of ike_sa_t.inherit.
+ */
+static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other)
+{
+ child_sa_t *child_sa;
+ host_t *ip;
+
+ /* apply hosts and ids */
+ this->my_host->destroy(this->my_host);
+ this->other_host->destroy(this->other_host);
+ this->my_id->destroy(this->my_id);
+ this->other_id->destroy(this->other_id);
+ this->my_host = other->my_host->clone(other->my_host);
+ this->other_host = other->other_host->clone(other->other_host);
+ this->my_id = other->my_id->clone(other->my_id);
+ this->other_id = other->other_id->clone(other->other_id);
+
+ /* apply virtual assigned IPs... */
+ if (other->my_virtual_ip)
+ {
+ this->my_virtual_ip = other->my_virtual_ip;
+ other->my_virtual_ip = NULL;
+ }
+ if (other->other_virtual_ip)
+ {
+ this->other_virtual_ip = other->other_virtual_ip;
+ other->other_virtual_ip = NULL;
+ }
+
+ /* ... and DNS servers */
+ while (other->dns_servers->remove_last(other->dns_servers,
+ (void**)&ip) == SUCCESS)
+ {
+ this->dns_servers->insert_first(this->dns_servers, ip);
+ }
+
+ /* adopt all children */
+ while (other->child_sas->remove_last(other->child_sas,
+ (void**)&child_sa) == SUCCESS)
+ {
+ this->child_sas->insert_first(this->child_sas, (void*)child_sa);
+ }
+
+ /* move pending tasks to the new IKE_SA */
+ this->task_manager->adopt_tasks(this->task_manager, other->task_manager);
+
+ /* we have to initate here, there may be new tasks to handle */
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.is_natt_enabled.
+ */
+static bool is_natt_enabled(private_ike_sa_t *this)
+{
+ return this->nat_here || this->nat_there;
+}
+
+/**
+ * Implementation of ike_sa_t.enable_natt.
+ */
+static void enable_natt(private_ike_sa_t *this, bool local)
+{
+ if (local)
+ {
+ DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives");
+ this->nat_here = TRUE;
+ send_keepalive(this);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "remote host is behind NAT");
+ this->nat_there = TRUE;
+ }
+}
+
+/**
+ * Implementation of ike_sa_t.set_virtual_ip
+ */
+static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip)
+{
+ if (local)
+ {
+ DBG1(DBG_IKE, "installing new virtual IP %H", ip);
+ if (this->my_virtual_ip)
+ {
+ DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip,
+ this->my_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ if (charon->kernel_interface->add_ip(charon->kernel_interface, ip,
+ this->my_host) == SUCCESS)
+ {
+ this->my_virtual_ip = ip->clone(ip);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
+ this->my_virtual_ip = NULL;
+ }
+ }
+ else
+ {
+ DESTROY_IF(this->other_virtual_ip);
+ this->other_virtual_ip = ip->clone(ip);
+ }
+}
+
+/**
+ * Implementation of ike_sa_t.get_virtual_ip
+ */
+static host_t* get_virtual_ip(private_ike_sa_t *this, bool local)
+{
+ if (local)
+ {
+ return this->my_virtual_ip;
+ }
+ else
+ {
+ return this->other_virtual_ip;
+ }
+}
+
+/**
+ * Implementation of ike_sa_t.remove_dns_server
+ */
+static void remove_dns_servers(private_ike_sa_t *this)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents, line, orig_line, token;
+ char string[INET6_ADDRSTRLEN];
+ host_t *ip;
+ iterator_t *iterator;
+
+ if (this->dns_servers->get_count(this->dns_servers) == 0)
+ {
+ /* don't touch anything if we have no nameservers installed */
+ return;
+ }
+
+ file = fopen(RESOLV_CONF, "r");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca((size_t)stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE);
+ while (fetchline(&contents, &line))
+ {
+ bool found = FALSE;
+ orig_line = line;
+ if (extract_token(&token, ' ', &line) &&
+ strncasecmp(token.ptr, "nameserver", token.len) == 0)
+ {
+ if (!extract_token(&token, ' ', &line))
+ {
+ token = line;
+ }
+ iterator->reset(iterator);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ snprintf(string, sizeof(string), "%H", ip);
+ if (strlen(string) == token.len &&
+ strncmp(token.ptr, string, token.len) == 0)
+ {
+ iterator->remove(iterator);
+ ip->destroy(ip);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ /* write line untouched back to file */
+ fwrite(orig_line.ptr, orig_line.len, 1, file);
+ fprintf(file, "\n");
+ }
+ }
+ iterator->destroy(iterator);
+ fclose(file);
+}
+
+/**
+ * Implementation of ike_sa_t.add_dns_server
+ */
+static void add_dns_server(private_ike_sa_t *this, host_t *dns)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents;
+
+ DBG1(DBG_IKE, "installing DNS server %H", dns);
+
+ file = fopen(RESOLV_CONF, "a+");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca(stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n",
+ dns, this->other_id) < 0)
+ {
+ DBG1(DBG_IKE, "unable to write DNS configuration: %m");
+ }
+ else
+ {
+ this->dns_servers->insert_last(this->dns_servers, dns->clone(dns));
+ }
+ fwrite(contents.ptr, contents.len, 1, file);
+
+ fclose(file);
+}
+
+/**
+ * output handler in printf()
+ */
+static int print(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ int written = 0;
+ bool reauth = FALSE;
+ private_ike_sa_t *this = *((private_ike_sa_t**)(args[0]));
+
+ if (this->connection)
+ {
+ reauth = this->connection->get_reauth(this->connection);
+ }
+
+ if (this == NULL)
+ {
+ return fprintf(stream, "(null)");
+ }
+
+ written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this),
+ this->unique_id, ike_sa_state_names, this->state,
+ this->my_host, this->my_id, this->other_host,
+ this->other_id);
+ written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds",
+ get_name(this), this->unique_id, this->ike_sa_id,
+ this->connection && reauth? "reauthentication":"rekeying",
+ this->time.rekey - time(NULL));
+
+ if (info->alt)
+ {
+
+ }
+ return written;
+}
+
+/**
+ * register printf() handlers
+ */
+static void __attribute__ ((constructor))print_register()
+{
+ register_printf_function(PRINTF_IKE_SA, print, arginfo_ptr);
+}
+
+/**
+ * Implementation of ike_sa_t.destroy.
+ */
+static void destroy(private_ike_sa_t *this)
+{
+ this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy));
+
+ DESTROY_IF(this->crypter_in);
+ DESTROY_IF(this->crypter_out);
+ DESTROY_IF(this->signer_in);
+ DESTROY_IF(this->signer_out);
+ DESTROY_IF(this->prf);
+ DESTROY_IF(this->child_prf);
+ DESTROY_IF(this->auth_verify);
+ DESTROY_IF(this->auth_build);
+
+ if (this->my_virtual_ip)
+ {
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip, this->my_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ DESTROY_IF(this->other_virtual_ip);
+
+ remove_dns_servers(this);
+ this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy));
+
+ DESTROY_IF(this->my_host);
+ DESTROY_IF(this->other_host);
+ DESTROY_IF(this->my_id);
+ DESTROY_IF(this->other_id);
+
+ DESTROY_IF(this->connection);
+ DESTROY_IF(this->policy);
+
+ this->ike_sa_id->destroy(this->ike_sa_id);
+ this->task_manager->destroy(this->task_manager);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
+{
+ private_ike_sa_t *this = malloc_thing(private_ike_sa_t);
+ static u_int32_t unique_id = 0;
+
+ /* Public functions */
+ this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state;
+ this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state;
+ this->public.get_name = (char*(*)(ike_sa_t*))get_name;
+ this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
+ this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate;
+ this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route;
+ this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute;
+ this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire;
+ this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection;
+ this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection;
+ this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy;
+ this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy;
+ this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
+ this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host;
+ this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host;
+ this->public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host;
+ this->public.set_other_host = (void(*)(ike_sa_t*,host_t*)) set_other_host;
+ this->public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id;
+ this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id;
+ this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id;
+ this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id;
+ this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit;
+ this->public.delete = (status_t(*)(ike_sa_t*))delete_;
+ this->public.destroy = (void(*)(ike_sa_t*))destroy;
+ this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd;
+ this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive;
+ this->public.get_prf = (prf_t *(*) (ike_sa_t *)) get_prf;
+ this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf;
+ this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify;
+ this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build;
+ this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
+ this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
+ this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
+ this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator;
+ this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
+ this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa;
+ this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
+ this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt;
+ this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
+ this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
+ this->public.reestablish = (void(*)(ike_sa_t*))reestablish;
+ this->public.inherit = (status_t(*)(ike_sa_t*,ike_sa_t*))inherit;
+ this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message;
+ this->public.reset = (void(*)(ike_sa_t*))reset;
+ this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id;
+ this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip;
+ this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip;
+ this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server;
+
+ /* initialize private fields */
+ this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+ this->child_sas = linked_list_create();
+ this->my_host = host_create_any(AF_INET);
+ this->other_host = host_create_any(AF_INET);
+ this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
+ this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty);
+ this->crypter_in = NULL;
+ this->crypter_out = NULL;
+ this->signer_in = NULL;
+ this->signer_out = NULL;
+ this->prf = NULL;
+ this->auth_verify = NULL;
+ this->auth_build = NULL;
+ this->child_prf = NULL;
+ this->nat_here = FALSE;
+ this->nat_there = FALSE;
+ this->state = IKE_CREATED;
+ this->time.inbound = this->time.outbound = time(NULL);
+ this->time.established = 0;
+ this->time.rekey = 0;
+ this->time.delete = 0;
+ this->connection = NULL;
+ this->policy = NULL;
+ this->task_manager = task_manager_create(&this->public);
+ this->unique_id = ++unique_id;
+ this->my_virtual_ip = NULL;
+ this->other_virtual_ip = NULL;
+ this->dns_servers = linked_list_create();
+ this->keyingtry = 0;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h
new file mode 100644
index 000000000..604ec94a9
--- /dev/null
+++ b/src/charon/sa/ike_sa.h
@@ -0,0 +1,649 @@
+/**
+ * @file ike_sa.h
+ *
+ * @brief Interface of ike_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_SA_H_
+#define IKE_SA_H_
+
+typedef enum ike_sa_state_t ike_sa_state_t;
+typedef struct ike_sa_t ike_sa_t;
+
+#include <library.h>
+#include <encoding/message.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <sa/ike_sa_id.h>
+#include <sa/child_sa.h>
+#include <sa/tasks/task.h>
+#include <config/configuration.h>
+#include <utils/randomizer.h>
+#include <crypto/prfs/prf.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <config/connections/connection.h>
+#include <config/policies/policy.h>
+#include <config/proposal.h>
+
+/**
+ * @brief State of an IKE_SA.
+ *
+ * An IKE_SA passes various states in its lifetime. A newly created
+ * SA is in the state CREATED.
+ * @verbatim
+ +----------------+
+ ¦ SA_CREATED ¦
+ +----------------+
+ ¦
+ on initiate()---> ¦ <----- on IKE_SA_INIT received
+ V
+ +----------------+
+ ¦ SA_CONNECTING ¦
+ +----------------+
+ ¦
+ ¦ <----- on IKE_AUTH successfully completed
+ V
+ +----------------+
+ ¦ SA_ESTABLISHED ¦-------------------------+ <-- on rekeying
+ +----------------+ ¦
+ ¦ V
+ on delete()---> ¦ <----- on IKE_SA +-------------+
+ ¦ delete request ¦ SA_REKEYING ¦
+ ¦ received +-------------+
+ V ¦
+ +----------------+ ¦
+ ¦ SA_DELETING ¦<------------------------+ <-- after rekeying
+ +----------------+
+ ¦
+ ¦ <----- after delete() acknowledged
+ ¦
+ \V/
+ X
+ / \
+ @endverbatim
+ *
+ * @ingroup sa
+ */
+enum ike_sa_state_t {
+
+ /**
+ * IKE_SA just got created, but is not initiating nor responding yet.
+ */
+ IKE_CREATED,
+
+ /**
+ * IKE_SA gets initiated actively or passively
+ */
+ IKE_CONNECTING,
+
+ /**
+ * IKE_SA is fully established
+ */
+ IKE_ESTABLISHED,
+
+ /**
+ * IKE_SA rekeying in progress
+ */
+ IKE_REKEYING,
+
+ /**
+ * IKE_SA is in progress of deletion
+ */
+ IKE_DELETING,
+};
+
+/**
+ * enum names for ike_sa_state_t.
+ */
+extern enum_name_t *ike_sa_state_names;
+
+/**
+ * @brief Class ike_sa_t representing an IKE_SA.
+ *
+ * An IKE_SA contains crypto information related to a connection
+ * with a peer. It contains multiple IPsec CHILD_SA, for which
+ * it is responsible. All traffic is handled by an IKE_SA, using
+ * the task manager and its tasks.
+ *
+ * @b Constructors:
+ * - ike_sa_create()
+ *
+ * @ingroup sa
+ */
+struct ike_sa_t {
+
+ /**
+ * @brief Get the id of the SA.
+ *
+ * Returned ike_sa_id_t object is not getting cloned!
+ *
+ * @param this calling object
+ * @return ike_sa's ike_sa_id_t
+ */
+ ike_sa_id_t* (*get_id) (ike_sa_t *this);
+
+ /**
+ * @brief Get the numerical ID uniquely defining this IKE_SA.
+ *
+ * @param this calling object
+ * @return unique ID
+ */
+ u_int32_t (*get_unique_id) (ike_sa_t *this);
+
+ /**
+ * @brief Get the state of the IKE_SA.
+ *
+ * @param this calling object
+ * @return state of the IKE_SA
+ */
+ ike_sa_state_t (*get_state) (ike_sa_t *this);
+
+ /**
+ * @brief Set the state of the IKE_SA.
+ *
+ * @param this calling object
+ * @param state state to set for the IKE_SA
+ */
+ void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa);
+
+ /**
+ * @brief Get the name of the connection this IKE_SA uses.
+ *
+ * @param this calling object
+ * @return name
+ */
+ char* (*get_name) (ike_sa_t *this);
+
+ /**
+ * @brief Get the own host address.
+ *
+ * @param this calling object
+ * @return host address
+ */
+ host_t* (*get_my_host) (ike_sa_t *this);
+
+ /**
+ * @brief Set the own host address.
+ *
+ * @param this calling object
+ * @param me host address
+ */
+ void (*set_my_host) (ike_sa_t *this, host_t *me);
+
+ /**
+ * @brief Get the other peers host address.
+ *
+ * @param this calling object
+ * @return host address
+ */
+ host_t* (*get_other_host) (ike_sa_t *this);
+
+ /**
+ * @brief Set the others host address.
+ *
+ * @param this calling object
+ * @param other host address
+ */
+ void (*set_other_host) (ike_sa_t *this, host_t *other);
+
+ /**
+ * @brief Get the own identification.
+ *
+ * @param this calling object
+ * @return identification
+ */
+ identification_t* (*get_my_id) (ike_sa_t *this);
+
+ /**
+ * @brief Set the own identification.
+ *
+ * @param this calling object
+ * @param me identification
+ */
+ void (*set_my_id) (ike_sa_t *this, identification_t *me);
+
+ /**
+ * @brief Get the other peers identification.
+ *
+ * @param this calling object
+ * @return identification
+ */
+ identification_t* (*get_other_id) (ike_sa_t *this);
+
+ /**
+ * @brief Set the other peers identification.
+ *
+ * @param this calling object
+ * @param other identification
+ */
+ void (*set_other_id) (ike_sa_t *this, identification_t *other);
+
+ /**
+ * @brief Get the connection used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return connection
+ */
+ connection_t* (*get_connection) (ike_sa_t *this);
+
+ /**
+ * @brief Set the connection to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param connection connection to use
+ */
+ void (*set_connection) (ike_sa_t *this, connection_t* connection);
+
+ /**
+ * @brief Get the policy used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return policy
+ */
+ policy_t* (*get_policy) (ike_sa_t *this);
+
+ /**
+ * @brief Set the policy to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param policy policy to use
+ */
+ void (*set_policy) (ike_sa_t *this, policy_t *policy);
+
+ /**
+ * @brief Initiate a new connection.
+ *
+ * The policy/connection is owned by the IKE_SA after the call, so
+ * do not modify or destroy it.
+ *
+ * @param this calling object
+ * @param connection connection to initiate
+ * @param policy policy to set up
+ * @return
+ * - SUCCESS if initialization started
+ * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted
+ */
+ status_t (*initiate) (ike_sa_t *this, connection_t *connection, policy_t *policy);
+
+ /**
+ * @brief Route a policy in the kernel.
+ *
+ * Installs the policies in the kernel. If traffic matches,
+ * the kernel requests connection setup from the IKE_SA via acquire().
+ *
+ * @param this calling object
+ * @param connection connection definition used for routing
+ * @param policy policy to route
+ * @return
+ * - SUCCESS if routed successfully
+ * - FAILED if routing failed
+ */
+ status_t (*route) (ike_sa_t *this, connection_t *connection, policy_t *policy);
+
+ /**
+ * @brief Unroute a policy in the kernel previously routed.
+ *
+ * @param this calling object
+ * @param policy policy to route
+ * @return
+ * - SUCCESS if route removed
+ * - DESTROY_ME if last route was removed from
+ * an IKE_SA which was not established
+ */
+ status_t (*unroute) (ike_sa_t *this, policy_t *policy);
+
+ /**
+ * @brief Acquire connection setup for a policy.
+ *
+ * If an installed policy raises an acquire, the kernel calls
+ * this function to establish the CHILD_SA (and maybe the IKE_SA).
+ *
+ * @param this calling object
+ * @param reqid reqid of the CHILD_SA the policy belongs to.
+ * @return
+ * - SUCCESS if initialization started
+ * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted
+ */
+ status_t (*acquire) (ike_sa_t *this, u_int32_t reqid);
+
+ /**
+ * @brief Initiates the deletion of an IKE_SA.
+ *
+ * Sends a delete message to the remote peer and waits for
+ * its response. If the response comes in, or a timeout occurs,
+ * the IKE SA gets deleted.
+ *
+ * @param this calling object
+ * @return
+ * - SUCCESS if deletion is initialized
+ * - INVALID_STATE, if the IKE_SA is not in
+ * an established state and can not be
+ * delete (but destroyed).
+ */
+ status_t (*delete) (ike_sa_t *this);
+
+ /**
+ * @brief Processes a incoming IKEv2-Message.
+ *
+ * Message processing may fail. If a critical failure occurs,
+ * process_message() return DESTROY_ME. Then the caller must
+ * destroy the IKE_SA immediatly, as it is unusable.
+ *
+ * @param this calling object
+ * @param message message to process
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DESTROY_ME if this IKE_SA MUST be deleted
+ */
+ status_t (*process_message) (ike_sa_t *this, message_t *message);
+
+ /**
+ * @brief Generate a IKE message to send it to the peer.
+ *
+ * This method generates all payloads in the message and encrypts/signs
+ * the packet.
+ *
+ * @param this calling object
+ * @param message message to generate
+ * @param packet generated output packet
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DESTROY_ME if this IKE_SA MUST be deleted
+ */
+ status_t (*generate_message) (ike_sa_t *this, message_t *message,
+ packet_t **packet);
+
+ /**
+ * @brief Retransmits a request.
+ *
+ * @param this calling object
+ * @param message_id ID of the request to retransmit
+ * @return
+ * - SUCCESS
+ * - NOT_FOUND if request doesn't have to be retransmited
+ */
+ status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id);
+
+ /**
+ * @brief Sends a DPD request to the peer.
+ *
+ * To check if a peer is still alive, periodic
+ * empty INFORMATIONAL messages are sent if no
+ * other traffic was received.
+ *
+ * @param this calling object
+ * @return
+ * - SUCCESS
+ * - DESTROY_ME, if peer did not respond
+ */
+ status_t (*send_dpd) (ike_sa_t *this);
+
+ /**
+ * @brief Sends a keep alive packet.
+ *
+ * To refresh NAT tables in a NAT router
+ * between the peers, periodic empty
+ * UDP packets are sent if no other traffic
+ * was sent.
+ *
+ * @param this calling object
+ */
+ void (*send_keepalive) (ike_sa_t *this);
+
+ /**
+ * @brief Check if NAT traversal is enabled for this IKE_SA.
+ *
+ * @param this calling object
+ * @return TRUE if NAT traversal enabled
+ */
+ bool (*is_natt_enabled) (ike_sa_t *this);
+
+ /**
+ * @brief Enable NAT detection for this IKE_SA.
+ *
+ * If a Network address translation is detected with
+ * NAT_DETECTION notifys, a SA must switch to ports
+ * 4500. To enable this behavior, call enable_natt().
+ * It is relevant which peer is NATted, this is specified
+ * with the "local" parameter. Call it twice when both
+ * are NATted.
+ *
+ * @param this calling object
+ * @param local TRUE, if we are NATted, FALSE if other
+ */
+ void (*enable_natt) (ike_sa_t *this, bool local);
+
+ /**
+ * @brief Derive all keys and create the transforms for IKE communication.
+ *
+ * Keys are derived using the diffie hellman secret, nonces and internal
+ * stored SPIs.
+ * Key derivation differs when an IKE_SA is set up to replace an
+ * existing IKE_SA (rekeying). The SK_d key from the old IKE_SA
+ * is included in the derivation process.
+ *
+ * @param this calling object
+ * @param proposal proposal which contains algorithms to use
+ * @param secret secret derived from DH exchange, gets freed
+ * @param nonce_i initiators nonce
+ * @param nonce_r responders nonce
+ * @param initiator TRUE if initiator, FALSE otherwise
+ * @param child_prf PRF with SK_d key when rekeying, NULL otherwise
+ * @param old_prf general purpose PRF of old SA when rekeying
+ */
+ status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret,
+ chunk_t nonce_i, chunk_t nonce_r,
+ bool initiator, prf_t *child_prf, prf_t *old_prf);
+
+ /**
+ * @brief Get the multi purpose prf.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_prf) (ike_sa_t *this);
+
+ /**
+ * @brief Get the prf-object, which is used to derive keys for child SAs.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_child_prf) (ike_sa_t *this);
+
+ /**
+ * @brief Get the prf to build outgoing authentication data.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_auth_build) (ike_sa_t *this);
+
+ /**
+ * @brief Get the prf to verify incoming authentication data.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_auth_verify) (ike_sa_t *this);
+
+ /**
+ * @brief Associates a child SA to this IKE SA
+ *
+ * @param this calling object
+ * @param child_sa child_sa to add
+ */
+ void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa);
+
+ /**
+ * @brief Get a CHILD_SA identified by protocol and SPI.
+ *
+ * @param this calling object
+ * @param protocol protocol of the SA
+ * @param spi SPI of the CHILD_SA
+ * @param inbound TRUE if SPI is inbound, FALSE if outbound
+ * @return child_sa, or NULL if none found
+ */
+ child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi, bool inbound);
+
+ /**
+ * @brief Create an iterator over all CHILD_SAs.
+ *
+ * @param this calling object
+ * @return iterator
+ */
+ iterator_t* (*create_child_sa_iterator) (ike_sa_t *this);
+
+ /**
+ * @brief Rekey the CHILD SA with the specified reqid.
+ *
+ * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing.
+ *
+ * @param this calling object
+ * @param protocol protocol of the SA
+ * @param spi inbound SPI of the CHILD_SA
+ * @return
+ * - NOT_FOUND, if IKE_SA has no such CHILD_SA
+ * - SUCCESS, if rekeying initiated
+ */
+ status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
+
+ /**
+ * @brief Close the CHILD SA with the specified protocol/SPI.
+ *
+ * Looks for a CHILD SA owned by this IKE_SA, deletes it and
+ * notify's the remote peer about the delete. The associated
+ * states and policies in the kernel get deleted, if they exist.
+ *
+ * @param this calling object
+ * @param protocol protocol of the SA
+ * @param spi inbound SPI of the CHILD_SA
+ * @return
+ * - NOT_FOUND, if IKE_SA has no such CHILD_SA
+ * - SUCCESS, if delete message sent
+ */
+ status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
+
+ /**
+ * @brief Destroy a CHILD SA with the specified protocol/SPI.
+ *
+ * Looks for a CHILD SA owned by this IKE_SA and destroys it.
+ *
+ * @param this calling object
+ * @param protocol protocol of the SA
+ * @param spi inbound SPI of the CHILD_SA
+ * @return
+ * - NOT_FOUND, if IKE_SA has no such CHILD_SA
+ * - SUCCESS
+ */
+ status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
+
+ /**
+ * @brief Rekey the IKE_SA.
+ *
+ * Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA.
+ *
+ * @param this calling object
+ * @return - SUCCESS, if IKE_SA rekeying initiated
+ */
+ status_t (*rekey) (ike_sa_t *this);
+
+ /**
+ * @brief Restablish the IKE_SA.
+ *
+ * Create a completely new IKE_SA with authentication, recreates all children
+ * within the IKE_SA, but lets the old IKE_SA untouched.
+ *
+ * @param this calling object
+ */
+ void (*reestablish) (ike_sa_t *this);
+
+ /**
+ * @brief Set the virtual IP to use for this IKE_SA and its children.
+ *
+ * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same
+ * lifetime as the IKE_SA.
+ *
+ * @param this calling object
+ */
+ void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip);
+
+ /**
+ * @brief Get the virtual IP configured.
+ *
+ * @param this calling object
+ * @param local TRUE to get local virtual IP, FALSE for remote
+ */
+ host_t* (*get_virtual_ip) (ike_sa_t *this, bool local);
+
+ /**
+ * @brief Add a DNS server to the system.
+ *
+ * An IRAS may send a DNS server. To use it, it is installed on the
+ * system. The DNS entry has a lifetime until the IKE_SA gets closed.
+ *
+ * @param this calling object
+ * @param dns DNS server to install on the system
+ */
+ void (*add_dns_server) (ike_sa_t *this, host_t *dns);
+
+ /**
+ * @brief Inherit all attributes of other to this after rekeying.
+ *
+ * When rekeying is completed, all CHILD_SAs, the virtual IP and all
+ * outstanding tasks are moved from other to this.
+ * As this call may initiate inherited tasks, a status is returned.
+ *
+ * @param this calling object
+ * @param other other task to inherit from
+ * @return DESTROY_ME if initiation of inherited task failed
+ */
+ status_t (*inherit) (ike_sa_t *this, ike_sa_t *other);
+
+ /**
+ * @brief Reset the IKE_SA, useable when initiating fails
+ *
+ * @param this calling object
+ */
+ void (*reset) (ike_sa_t *this);
+
+ /**
+ * @brief Destroys a ike_sa_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (ike_sa_t *this);
+};
+
+/**
+ * @brief Creates an ike_sa_t object with a specific ID.
+ *
+ * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA
+ * @return ike_sa_t object
+ *
+ * @ingroup sa
+ */
+ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id);
+
+#endif /* IKE_SA_H_ */
diff --git a/src/charon/sa/ike_sa_id.c b/src/charon/sa/ike_sa_id.c
new file mode 100644
index 000000000..c143fc0ba
--- /dev/null
+++ b/src/charon/sa/ike_sa_id.c
@@ -0,0 +1,215 @@
+/**
+ * @file ike_sa_id.c
+ *
+ * @brief Implementation of ike_sa_id_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+
+#include "ike_sa_id.h"
+
+#include <printf.h>
+#include <stdio.h>
+
+
+typedef struct private_ike_sa_id_t private_ike_sa_id_t;
+
+/**
+ * Private data of an ike_sa_id_t object.
+ */
+struct private_ike_sa_id_t {
+ /**
+ * Public interface of ike_sa_id_t.
+ */
+ ike_sa_id_t public;
+
+ /**
+ * SPI of Initiator.
+ */
+ u_int64_t initiator_spi;
+
+ /**
+ * SPI of Responder.
+ */
+ u_int64_t responder_spi;
+
+ /**
+ * Role for specific IKE_SA.
+ */
+ bool is_initiator_flag;
+};
+
+/**
+ * Implementation of ike_sa_id_t.set_responder_spi.
+ */
+static void set_responder_spi (private_ike_sa_id_t *this, u_int64_t responder_spi)
+{
+ this->responder_spi = responder_spi;
+}
+
+/**
+ * Implementation of ike_sa_id_t.set_initiator_spi.
+ */
+static void set_initiator_spi(private_ike_sa_id_t *this, u_int64_t initiator_spi)
+{
+ this->initiator_spi = initiator_spi;
+}
+
+/**
+ * Implementation of ike_sa_id_t.get_initiator_spi.
+ */
+static u_int64_t get_initiator_spi (private_ike_sa_id_t *this)
+{
+ return this->initiator_spi;
+}
+
+/**
+ * Implementation of ike_sa_id_t.get_responder_spi.
+ */
+static u_int64_t get_responder_spi (private_ike_sa_id_t *this)
+{
+ return this->responder_spi;
+}
+
+/**
+ * Implementation of ike_sa_id_t.equals.
+ */
+static bool equals (private_ike_sa_id_t *this, private_ike_sa_id_t *other)
+{
+ if (other == NULL)
+ {
+ return FALSE;
+ }
+ if ((this->is_initiator_flag == other->is_initiator_flag) &&
+ (this->initiator_spi == other->initiator_spi) &&
+ (this->responder_spi == other->responder_spi))
+ {
+ /* private_ike_sa_id's are equal */
+ return TRUE;
+ }
+ else
+ {
+ /* private_ike_sa_id's are not equal */
+ return FALSE;
+ }
+}
+
+/**
+ * Implementation of ike_sa_id_t.replace_values.
+ */
+static void replace_values(private_ike_sa_id_t *this, private_ike_sa_id_t *other)
+{
+ this->initiator_spi = other->initiator_spi;
+ this->responder_spi = other->responder_spi;
+ this->is_initiator_flag = other->is_initiator_flag;
+}
+
+/**
+ * Implementation of ike_sa_id_t.is_initiator.
+ */
+static bool is_initiator(private_ike_sa_id_t *this)
+{
+ return this->is_initiator_flag;
+}
+
+/**
+ * Implementation of ike_sa_id_t.switch_initiator.
+ */
+static bool switch_initiator(private_ike_sa_id_t *this)
+{
+ if (this->is_initiator_flag)
+ {
+ this->is_initiator_flag = FALSE;
+ }
+ else
+ {
+ this->is_initiator_flag = TRUE;
+ }
+ return this->is_initiator_flag;
+}
+
+/**
+ * Implementation of ike_sa_id_t.clone.
+ */
+static ike_sa_id_t* clone_(private_ike_sa_id_t *this)
+{
+ return ike_sa_id_create(this->initiator_spi, this->responder_spi, this->is_initiator_flag);
+}
+
+/**
+ * output handler in printf()
+ */
+static int print(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ private_ike_sa_id_t *this = *((private_ike_sa_id_t**)(args[0]));
+
+ if (this == NULL)
+ {
+ return fprintf(stream, "(null)");
+ }
+ return fprintf(stream, "0x%0llx_i%s 0x%0llx_r%s",
+ this->initiator_spi,
+ this->is_initiator_flag ? "*" : "",
+ this->responder_spi,
+ this->is_initiator_flag ? "" : "*");
+}
+
+/**
+ * register printf() handlers
+ */
+static void __attribute__ ((constructor))print_register()
+{
+ register_printf_function(PRINTF_IKE_SA_ID, print, arginfo_ptr);
+}
+
+/**
+ * Implementation of ike_sa_id_t.destroy.
+ */
+static void destroy(private_ike_sa_id_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiator_flag)
+{
+ private_ike_sa_id_t *this = malloc_thing(private_ike_sa_id_t);
+
+ /* public functions */
+ this->public.set_responder_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_responder_spi;
+ this->public.set_initiator_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_initiator_spi;
+ this->public.get_responder_spi = (u_int64_t(*)(ike_sa_id_t*)) get_responder_spi;
+ this->public.get_initiator_spi = (u_int64_t(*)(ike_sa_id_t*)) get_initiator_spi;
+ this->public.equals = (bool(*)(ike_sa_id_t*,ike_sa_id_t*)) equals;
+ this->public.replace_values = (void(*)(ike_sa_id_t*,ike_sa_id_t*)) replace_values;
+ this->public.is_initiator = (bool(*)(ike_sa_id_t*)) is_initiator;
+ this->public.switch_initiator = (bool(*)(ike_sa_id_t*)) switch_initiator;
+ this->public.clone = (ike_sa_id_t*(*)(ike_sa_id_t*)) clone_;
+ this->public.destroy = (void(*)(ike_sa_id_t*))destroy;
+
+ /* private data */
+ this->initiator_spi = initiator_spi;
+ this->responder_spi = responder_spi;
+ this->is_initiator_flag = is_initiator_flag;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h
new file mode 100644
index 000000000..0606b7222
--- /dev/null
+++ b/src/charon/sa/ike_sa_id.h
@@ -0,0 +1,147 @@
+/**
+ * @file ike_sa_id.h
+ *
+ * @brief Interface of ike_sa_id_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+
+#ifndef IKE_SA_ID_H_
+#define IKE_SA_ID_H_
+
+typedef struct ike_sa_id_t ike_sa_id_t;
+
+#include <library.h>
+
+
+/**
+ * @brief An object of type ike_sa_id_t is used to identify an IKE_SA.
+ *
+ * An IKE_SA is identified by its initiator and responder spi's.
+ * Additionaly it contains the role of the actual running IKEv2-Daemon
+ * for the specific IKE_SA (original initiator or responder).
+ *
+ * @b Constructors:
+ * - ike_sa_id_create()
+ *
+ * @ingroup sa
+ */
+struct ike_sa_id_t {
+
+ /**
+ * @brief Set the SPI of the responder.
+ *
+ * This function is called when a request or reply of a IKE_SA_INIT is received.
+ *
+ * @param this calling object
+ * @param responder_spi SPI of responder to set
+ */
+ void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi);
+
+ /**
+ * @brief Set the SPI of the initiator.
+ *
+ * @param this calling object
+ * @param initiator_spi SPI to set
+ */
+ void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi);
+
+ /**
+ * @brief Get the initiator SPI.
+ *
+ * @param this calling object
+ * @return SPI of the initiator
+ */
+ u_int64_t (*get_initiator_spi) (ike_sa_id_t *this);
+
+ /**
+ * @brief Get the responder SPI.
+ *
+ * @param this calling object
+ * @return SPI of the responder
+ */
+ u_int64_t (*get_responder_spi) (ike_sa_id_t *this);
+
+ /**
+ * @brief Check if two ike_sa_id_t objects are equal.
+ *
+ * Two ike_sa_id_t objects are equal if both SPI values and the role matches.
+ *
+ * @param this calling object
+ * @param other ike_sa_id_t object to check if equal
+ * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise
+ */
+ bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other);
+
+ /**
+ * @brief Replace all values of a given ike_sa_id_t object with values.
+ * from another ike_sa_id_t object.
+ *
+ * After calling this function, both objects are equal.
+ *
+ * @param this calling object
+ * @param other ike_sa_id_t object from which values will be taken
+ */
+ void (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other);
+
+ /**
+ * @brief Get the initiator flag.
+ *
+ * @param this calling object
+ * @return TRUE if we are the original initator
+ */
+ bool (*is_initiator) (ike_sa_id_t *this);
+
+ /**
+ * @brief Switche the original initiator flag.
+ *
+ * @param this calling object
+ * @return TRUE if we are the original initator after switch, FALSE otherwise
+ */
+ bool (*switch_initiator) (ike_sa_id_t *this);
+
+ /**
+ * @brief Clones a given ike_sa_id_t object.
+ *
+ * @param this calling object
+ * @return cloned ike_sa_id_t object
+ */
+ ike_sa_id_t *(*clone) (ike_sa_id_t *this);
+
+ /**
+ * @brief Destroys an ike_sa_id_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (ike_sa_id_t *this);
+};
+
+/**
+ * @brief Creates an ike_sa_id_t object with specific SPI's and defined role.
+ *
+ * @param initiator_spi initiators SPI
+ * @param responder_spi responders SPI
+ * @param is_initiaor TRUE if we are the original initiator
+ * @return ike_sa_id_t object
+ *
+ * @ingroup sa
+ */
+ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiaor);
+
+#endif /*IKE_SA_ID_H_*/
diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c
new file mode 100644
index 000000000..791ef805e
--- /dev/null
+++ b/src/charon/sa/ike_sa_manager.c
@@ -0,0 +1,914 @@
+/**
+ * @file ike_sa_manager.c
+ *
+ * @brief Implementation of ike_sa_mananger_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <pthread.h>
+#include <string.h>
+
+#include "ike_sa_manager.h"
+
+#include <daemon.h>
+#include <sa/ike_sa_id.h>
+#include <bus/bus.h>
+#include <utils/linked_list.h>
+
+typedef struct entry_t entry_t;
+
+/**
+ * An entry in the linked list, contains IKE_SA, locking and lookup data.
+ */
+struct entry_t {
+
+ /**
+ * Number of threads waiting for this ike_sa_t object.
+ */
+ int waiting_threads;
+
+ /**
+ * Condvar where threads can wait until ike_sa_t object is free for use again.
+ */
+ pthread_cond_t condvar;
+
+ /**
+ * Is this ike_sa currently checked out?
+ */
+ bool checked_out;
+
+ /**
+ * Does this SA drives out new threads?
+ */
+ bool driveout_new_threads;
+
+ /**
+ * Does this SA drives out waiting threads?
+ */
+ bool driveout_waiting_threads;
+
+ /**
+ * Identifiaction of an IKE_SA (SPIs).
+ */
+ ike_sa_id_t *ike_sa_id;
+
+ /**
+ * The contained ike_sa_t object.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * hash of the IKE_SA_INIT message, used to detect retransmissions
+ */
+ chunk_t init_hash;
+
+ /**
+ * message ID currently processing, if any
+ */
+ u_int32_t message_id;
+};
+
+/**
+ * Implementation of entry_t.destroy.
+ */
+static status_t entry_destroy(entry_t *this)
+{
+ /* also destroy IKE SA */
+ this->ike_sa->destroy(this->ike_sa);
+ this->ike_sa_id->destroy(this->ike_sa_id);
+ chunk_free(&this->init_hash);
+ free(this);
+ return SUCCESS;
+}
+
+/**
+ * Creates a new entry for the ike_sa_t list.
+ */
+static entry_t *entry_create(ike_sa_id_t *ike_sa_id)
+{
+ entry_t *this = malloc_thing(entry_t);
+
+ this->waiting_threads = 0;
+ pthread_cond_init(&this->condvar, NULL);
+
+ /* we set checkout flag when we really give it out */
+ this->checked_out = FALSE;
+ this->driveout_new_threads = FALSE;
+ this->driveout_waiting_threads = FALSE;
+ this->message_id = -1;
+ this->init_hash = chunk_empty;
+
+ /* ike_sa_id is always cloned */
+ this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+
+ /* create new ike_sa */
+ this->ike_sa = ike_sa_create(ike_sa_id);
+
+ return this;
+}
+
+
+typedef struct private_ike_sa_manager_t private_ike_sa_manager_t;
+
+/**
+ * Additional private members of ike_sa_manager_t.
+ */
+struct private_ike_sa_manager_t {
+ /**
+ * Public interface of ike_sa_manager_t.
+ */
+ ike_sa_manager_t public;
+
+ /**
+ * Lock for exclusivly accessing the manager.
+ */
+ pthread_mutex_t mutex;
+
+ /**
+ * Linked list with entries for the ike_sa_t objects.
+ */
+ linked_list_t *ike_sa_list;
+
+ /**
+ * A randomizer, to get random SPIs for our side
+ */
+ randomizer_t *randomizer;
+
+ /**
+ * SHA1 hasher for IKE_SA_INIT retransmit detection
+ */
+ hasher_t *hasher;
+};
+
+/**
+ * Implementation of private_ike_sa_manager_t.get_entry_by_id.
+ */
+static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ entry_t *current;
+ status_t status;
+
+ /* create iterator over list of ike_sa's */
+ iterator = list->create_iterator(list, TRUE);
+
+ /* default status */
+ status = NOT_FOUND;
+
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id))
+ {
+ DBG2(DBG_MGR, "found entry by both SPIs");
+ *entry = current;
+ status = SUCCESS;
+ break;
+ }
+ if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 ||
+ current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0)
+ {
+ /* seems to be a half ready ike_sa */
+ if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) ==
+ ike_sa_id->get_initiator_spi(ike_sa_id)) &&
+ (current->ike_sa_id->is_initiator(ike_sa_id) ==
+ ike_sa_id->is_initiator(current->ike_sa_id)))
+ {
+ DBG2(DBG_MGR, "found entry by initiator SPI");
+ *entry = current;
+ status = SUCCESS;
+ break;
+ }
+ }
+ }
+
+ iterator->destroy(iterator);
+ return status;
+}
+
+/**
+ * Implementation of private_ike_sa_manager_t.get_entry_by_sa.
+ */
+static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, entry_t **entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ entry_t *current;
+ status_t status;
+
+ iterator = list->create_iterator(list, TRUE);
+
+ /* default status */
+ status = NOT_FOUND;
+
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ /* only pointers are compared */
+ if (current->ike_sa == ike_sa)
+ {
+ DBG2(DBG_MGR, "found entry by pointer");
+ *entry = current;
+ status = SUCCESS;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return status;
+}
+
+/**
+ * Implementation of private_ike_sa_manager_s.delete_entry.
+ */
+static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ entry_t *current;
+ status_t status;
+
+ iterator = list->create_iterator(list, TRUE);
+
+ status = NOT_FOUND;
+
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (current == entry)
+ {
+ /* mark it, so now new threads can get this entry */
+ entry->driveout_new_threads = TRUE;
+ /* wait until all workers have done their work */
+ while (entry->waiting_threads)
+ {
+ /* wake up all */
+ pthread_cond_broadcast(&(entry->condvar));
+ /* they will wake us again when their work is done */
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ }
+
+ DBG2(DBG_MGR, "found entry by pointer, deleting it");
+ iterator->remove(iterator);
+ entry_destroy(entry);
+ status = SUCCESS;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return status;
+}
+
+/**
+ * Wait until no other thread is using an IKE_SA, return FALSE if entry not
+ * acquireable
+ */
+static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ if (entry->driveout_new_threads)
+ {
+ /* we are not allowed to get this */
+ return FALSE;
+ }
+ while (entry->checked_out && !entry->driveout_waiting_threads)
+ {
+ /* so wait until we can get it for us.
+ * we register us as waiting. */
+ entry->waiting_threads++;
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ entry->waiting_threads--;
+ }
+ /* hm, a deletion request forbids us to get this SA, get next one */
+ if (entry->driveout_waiting_threads)
+ {
+ /* we must signal here, others may be waiting on it, too */
+ pthread_cond_signal(&(entry->condvar));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Implementation of private_ike_sa_manager_t.get_next_spi.
+ */
+static u_int64_t get_next_spi(private_ike_sa_manager_t *this)
+{
+ u_int64_t spi;
+
+ this->randomizer->get_pseudo_random_bytes(this->randomizer, sizeof(spi),
+ (u_int8_t*)&spi);
+ return spi;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout.
+ */
+static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+{
+ ike_sa_t *ike_sa = NULL;
+ entry_t *entry;
+
+ DBG2(DBG_MGR, "checkout IKE_SA: %J, %d IKE_SAs in manager",
+ ike_sa_id, this->ike_sa_list->get_count(this->ike_sa_list));
+
+ pthread_mutex_lock(&(this->mutex));
+ if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ {
+ if (wait_for_entry(this, entry))
+ {
+ DBG2(DBG_MGR, "IKE_SA successfully checked out");
+ entry->checked_out = TRUE;
+ ike_sa = entry->ike_sa;
+ }
+ }
+ pthread_mutex_unlock(&this->mutex);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_new.
+ */
+static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator)
+{
+ entry_t *entry;
+ ike_sa_id_t *id;
+
+ if (initiator)
+ {
+ id = ike_sa_id_create(get_next_spi(this), 0, TRUE);
+ }
+ else
+ {
+ id = ike_sa_id_create(0, get_next_spi(this), FALSE);
+ }
+ entry = entry_create(id);
+ pthread_mutex_lock(&this->mutex);
+ this->ike_sa_list->insert_last(this->ike_sa_list, entry);
+ entry->checked_out = TRUE;
+ pthread_mutex_unlock(&this->mutex);
+ DBG2(DBG_MGR, "created IKE_SA: %J, %d IKE_SAs in manager",
+ id, this->ike_sa_list->get_count(this->ike_sa_list));
+ return entry->ike_sa;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_by_id.
+ */
+static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
+ message_t *message)
+{
+ entry_t *entry;
+ ike_sa_t *ike_sa = NULL;
+ ike_sa_id_t *id = message->get_ike_sa_id(message);
+ id = id->clone(id);
+ id->switch_initiator(id);
+
+ DBG2(DBG_MGR, "checkout IKE_SA: %J by message, %d IKE_SAs in manager",
+ id, this->ike_sa_list->get_count(this->ike_sa_list));
+
+ if (message->get_request(message) &&
+ message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */
+ iterator_t *iterator;
+ chunk_t data, hash;
+
+ data = message->get_packet_data(message);
+ this->hasher->allocate_hash(this->hasher, data, &hash);
+ chunk_free(&data);
+
+ pthread_mutex_lock(&this->mutex);
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ if (chunk_equals(hash, entry->init_hash))
+ {
+ if (entry->message_id == 0)
+ {
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&this->mutex);
+ chunk_free(&hash);
+ id->destroy(id);
+ DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
+ return NULL;
+ }
+ else if (wait_for_entry(this, entry))
+ {
+ DBG2(DBG_MGR, "IKE_SA checked out by hash");
+ entry->checked_out = TRUE;
+ entry->message_id = message->get_message_id(message);
+ ike_sa = entry->ike_sa;
+ }
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&this->mutex);
+
+ if (ike_sa == NULL)
+ {
+ if (id->get_responder_spi(id) == 0 &&
+ message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ /* no IKE_SA found, create a new one */
+ id->set_responder_spi(id, get_next_spi(this));
+ entry = entry_create(id);
+
+ pthread_mutex_lock(&this->mutex);
+ this->ike_sa_list->insert_last(this->ike_sa_list, entry);
+ entry->checked_out = TRUE;
+ entry->message_id = message->get_message_id(message);
+ pthread_mutex_unlock(&this->mutex);
+ entry->init_hash = hash;
+ ike_sa = entry->ike_sa;
+ }
+ else
+ {
+ DBG1(DBG_MGR, "ignoring message for %J, no such IKE_SA", id);
+ }
+ }
+ else
+ {
+ chunk_free(&hash);
+ }
+ id->destroy(id);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+ }
+
+ pthread_mutex_lock(&(this->mutex));
+ if (get_entry_by_id(this, id, &entry) == SUCCESS)
+ {
+ /* only check out if we are not processing this request */
+ if (message->get_request(message) &&
+ message->get_message_id(message) == entry->message_id)
+ {
+ DBG1(DBG_MGR, "ignoring request with ID %d, already processing",
+ entry->message_id);
+ }
+ else if (wait_for_entry(this, entry))
+ {
+ ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa);
+ DBG2(DBG_MGR, "IKE_SA successfully checked out");
+ entry->checked_out = TRUE;
+ entry->message_id = message->get_message_id(message);
+ if (ike_id->get_responder_spi(ike_id) == 0)
+ {
+ ike_id->set_responder_spi(ike_id, id->get_responder_spi(id));
+ }
+ ike_sa = entry->ike_sa;
+ }
+ }
+ pthread_mutex_unlock(&this->mutex);
+ id->destroy(id);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_by_id.
+ */
+static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this,
+ host_t *my_host, host_t *other_host,
+ identification_t *my_id,
+ identification_t *other_id)
+{
+ iterator_t *iterator;
+ entry_t *entry;
+ ike_sa_t *ike_sa = NULL;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ identification_t *found_my_id, *found_other_id;
+ host_t *found_my_host, *found_other_host;
+ int wc;
+
+ if (!wait_for_entry(this, entry))
+ {
+ continue;
+ }
+
+ if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING)
+ {
+ /* skip IKE_SA which are not useable */
+ continue;
+ }
+
+ found_my_id = entry->ike_sa->get_my_id(entry->ike_sa);
+ found_other_id = entry->ike_sa->get_other_id(entry->ike_sa);
+ found_my_host = entry->ike_sa->get_my_host(entry->ike_sa);
+ found_other_host = entry->ike_sa->get_other_host(entry->ike_sa);
+
+ if (found_my_id->get_type(found_my_id) == ID_ANY &&
+ found_other_id->get_type(found_other_id) == ID_ANY)
+ {
+ /* IKE_SA has no IDs yet, so we can't use it */
+ continue;
+ }
+
+ /* compare ID and hosts. Supplied ID may contain wildcards, and IP
+ * may be %any. */
+ if ((found_my_host->is_anyaddr(found_my_host) ||
+ my_host->ip_equals(my_host, found_my_host)) &&
+ (found_other_host->is_anyaddr(found_other_host) ||
+ other_host->ip_equals(other_host, found_other_host)) &&
+ found_my_id->matches(found_my_id, my_id, &wc) &&
+ found_other_id->matches(found_other_id, other_id, &wc))
+ {
+ /* looks good, we take this one */
+ DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]",
+ my_host, other_host, my_id, other_id);
+ entry->checked_out = TRUE;
+ ike_sa = entry->ike_sa;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (!ike_sa)
+ {
+ u_int64_t initiator_spi;
+ entry_t *new_entry;
+ ike_sa_id_t *new_ike_sa_id;
+
+ initiator_spi = get_next_spi(this);
+ new_ike_sa_id = ike_sa_id_create(0, 0, TRUE);
+ new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi);
+
+ /* create entry */
+ new_entry = entry_create(new_ike_sa_id);
+ DBG2(DBG_MGR, "created IKE_SA: %J", new_ike_sa_id);
+ new_ike_sa_id->destroy(new_ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
+
+ /* check ike_sa out */
+ DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id);
+ new_entry->checked_out = TRUE;
+ ike_sa = new_entry->ike_sa;
+ }
+ pthread_mutex_unlock(&(this->mutex));
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_by_id.
+ */
+static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id,
+ bool child)
+{
+ iterator_t *iterator, *children;
+ entry_t *entry;
+ ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ if (wait_for_entry(this, entry))
+ {
+ /* look for a child with such a reqid ... */
+ if (child)
+ {
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (child_sa->get_reqid(child_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
+ }
+ else /* ... or for a IKE_SA with such a unique id */
+ {
+ if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
+ {
+ entry->checked_out = TRUE;
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&(this->mutex));
+
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_by_name.
+ */
+static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
+ bool child)
+{
+ iterator_t *iterator, *children;
+ entry_t *entry;
+ ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ if (wait_for_entry(this, entry))
+ {
+ /* look for a child with such a policy name ... */
+ if (child)
+ {
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (streq(child_sa->get_name(child_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
+ }
+ else /* ... or for a IKE_SA with such a connection name */
+ {
+ if (streq(entry->ike_sa->get_name(entry->ike_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
+ {
+ entry->checked_out = TRUE;
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&(this->mutex));
+
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
+ * Iterator hook for iterate, gets ike_sas instead of entries
+ */
+static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in,
+ ike_sa_t **out)
+{
+ /* check out entry */
+ if (wait_for_entry(this, in))
+ {
+ *out = in->ike_sa;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.create_iterator.
+ */
+static iterator_t *create_iterator(private_ike_sa_manager_t* this)
+{
+ iterator_t *iterator = this->ike_sa_list->create_iterator_locked(
+ this->ike_sa_list, &this->mutex);
+ /* register hook to iterator over ike_sas, not entries */
+ iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this);
+ return iterator;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.checkin.
+ */
+static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+{
+ /* to check the SA back in, we look for the pointer of the ike_sa
+ * in all entries.
+ * We can't search by SPI's since the MAY have changed (e.g. on reception
+ * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary...
+ */
+ status_t retval;
+ entry_t *entry;
+ ike_sa_id_t *ike_sa_id;
+
+ ike_sa_id = ike_sa->get_id(ike_sa);
+
+ DBG2(DBG_MGR, "checkin IKE_SA: %J", ike_sa_id);
+
+ pthread_mutex_lock(&(this->mutex));
+
+ /* look for the entry */
+ if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
+ {
+ /* ike_sa_id must be updated */
+ entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
+ /* signal waiting threads */
+ entry->checked_out = FALSE;
+ entry->message_id = -1;
+ DBG2(DBG_MGR, "check-in of IKE_SA successful.");
+ pthread_cond_signal(&(entry->condvar));
+ retval = SUCCESS;
+ }
+ else
+ {
+ DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA");
+ /* this SA is no more, this REALLY should not happen */
+ retval = NOT_FOUND;
+ }
+
+ DBG2(DBG_MGR, "%d IKE_SAs in manager now",
+ this->ike_sa_list->get_count(this->ike_sa_list));
+ pthread_mutex_unlock(&(this->mutex));
+
+ charon->bus->set_sa(charon->bus, NULL);
+ return retval;
+}
+
+
+/**
+ * Implementation of ike_sa_manager_t.checkin_and_destroy.
+ */
+static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+{
+ /* deletion is a bit complex, we must garant that no thread is waiting for
+ * this SA.
+ * We take this SA from the list, and start signaling while threads
+ * are in the condvar.
+ */
+ entry_t *entry;
+ status_t retval;
+ ike_sa_id_t *ike_sa_id;
+
+ ike_sa_id = ike_sa->get_id(ike_sa);
+ DBG2(DBG_MGR, "checkin and destroy IKE_SA: %J", ike_sa_id);
+
+ pthread_mutex_lock(&(this->mutex));
+
+ if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
+ {
+ /* drive out waiting threads, as we are in hurry */
+ entry->driveout_waiting_threads = TRUE;
+
+ delete_entry(this, entry);
+
+ DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful");
+ retval = SUCCESS;
+ }
+ else
+ {
+ DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA");
+ retval = NOT_FOUND;
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return retval;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.get_half_open_count.
+ */
+static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip)
+{
+ iterator_t *iterator;
+ entry_t *entry;
+ int count = 0;
+
+ pthread_mutex_lock(&(this->mutex));
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ /* we check if we have a responder CONNECTING IKE_SA without checkout */
+ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING)
+ {
+ /* if we have a host, we have wait until no other uses the IKE_SA */
+ if (ip)
+ {
+ if (wait_for_entry(this, entry) && ip->ip_equals(ip,
+ entry->ike_sa->get_other_host(entry->ike_sa)))
+ {
+ count++;
+ }
+ }
+ else
+ {
+ count++;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ pthread_mutex_unlock(&(this->mutex));
+ return count;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.destroy.
+ */
+static void destroy(private_ike_sa_manager_t *this)
+{
+ /* destroy all list entries */
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ entry_t *entry;
+
+ pthread_mutex_lock(&(this->mutex));
+ DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's");
+ /* Step 1: drive out all waiting threads */
+ DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's");
+ iterator = list->create_iterator(list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ /* do not accept new threads, drive out waiting threads */
+ entry->driveout_new_threads = TRUE;
+ entry->driveout_waiting_threads = TRUE;
+ }
+ DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's");
+ /* Step 2: wait until all are gone */
+ iterator->reset(iterator);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ while (entry->waiting_threads)
+ {
+ /* wake up all */
+ pthread_cond_broadcast(&(entry->condvar));
+ /* go sleeping until they are gone */
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ }
+ }
+ DBG2(DBG_MGR, "delete all IKE_SA's");
+ /* Step 3: initiate deletion of all IKE_SAs */
+ iterator->reset(iterator);
+ while (iterator->iterate(iterator, (void**)&entry))
+ {
+ entry->ike_sa->delete(entry->ike_sa);
+ }
+ iterator->destroy(iterator);
+
+ DBG2(DBG_MGR, "destroy all entries");
+ /* Step 4: destroy all entries */
+ list->destroy_function(list, (void*)entry_destroy);
+ pthread_mutex_unlock(&(this->mutex));
+
+ this->randomizer->destroy(this->randomizer);
+ this->hasher->destroy(this->hasher);
+
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_manager_t *ike_sa_manager_create()
+{
+ private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t);
+
+ /* assign public functions */
+ this->public.destroy = (void(*)(ike_sa_manager_t*))destroy;
+ this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout;
+ this->public.checkout_new = (ike_sa_t*(*)(ike_sa_manager_t*,bool))checkout_new;
+ this->public.checkout_by_message = (ike_sa_t*(*)(ike_sa_manager_t*,message_t*))checkout_by_message;
+ this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer;
+ this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id;
+ this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name;
+ this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator;
+ this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
+ this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
+ this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count;
+
+ /* initialize private variables */
+ this->ike_sa_list = linked_list_create();
+ pthread_mutex_init(&this->mutex, NULL);
+ this->randomizer = randomizer_create();
+ this->hasher = hasher_create(HASH_SHA1);
+
+ return &this->public;
+}
diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h
new file mode 100644
index 000000000..1125e5d16
--- /dev/null
+++ b/src/charon/sa/ike_sa_manager.h
@@ -0,0 +1,231 @@
+/**
+ * @file ike_sa_manager.h
+ *
+ * @brief Interface of ike_sa_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_SA_MANAGER_H_
+#define IKE_SA_MANAGER_H_
+
+typedef struct ike_sa_manager_t ike_sa_manager_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <encoding/message.h>
+
+/**
+ * @brief The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's.
+ *
+ * To avoid access from multiple threads, IKE_SAs must be checked out from
+ * the manager, and checked in after usage.
+ * The manager also handles deletion of SAs.
+ *
+ * @todo checking of double-checkouts from the same threads would be nice.
+ * This could be done by comparing thread-ids via pthread_self()...
+ *
+ * @todo Managing of ike_sa_t objects in a hash table instead of linked list.
+ *
+ * @b Constructors:
+ * - ike_sa_manager_create()
+ *
+ * @ingroup sa
+ */
+struct ike_sa_manager_t {
+
+ /**
+ * @brief Checkout an existing IKE_SA.
+ *
+ * @param this the manager object
+ * @param ike_sa_id the SA identifier, will be updated
+ * @returns
+ * - checked out IKE_SA if found
+ * - NULL, if specified IKE_SA is not found.
+ */
+ ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id);
+
+ /**
+ * @brief Create and check out a new IKE_SA.
+ *
+ * @param this the manager object
+ * @param initiator TRUE for initiator, FALSE otherwise
+ * @returns created andchecked out IKE_SA
+ */
+ ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator);
+
+ /**
+ * @brief Checkout an IKE_SA by a message.
+ *
+ * In some situations, it is necessary that the manager knows the
+ * message to use for the checkout. This has the folloing reasons:
+ *
+ * 1. If the targeted IKE_SA is already processing a message, we do not
+ * check it out if the message ID is the same.
+ * 2. If it is an IKE_SA_INIT request, we have to check if it is a
+ * retransmission. If so, we have to drop the message, we would
+ * create another unneded IKE_SA for each retransmitted packet.
+ *
+ * A call to checkout_by_message() returns a (maybe new created) IKE_SA.
+ * If processing the message does not make sense (for the reasons above),
+ * NULL is returned.
+ *
+ * @param this the manager object
+ * @param ike_sa_id the SA identifier, will be updated
+ * @returns
+ * - checked out/created IKE_SA
+ * - NULL to not process message further
+ */
+ ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message);
+
+ /**
+ * @brief Checkout an existing IKE_SA by hosts and identifications.
+ *
+ * Allows the lookup of an IKE_SA by user IDs and hosts. It returns the
+ * first found occurence, if there are multiple candidates. Supplied IDs
+ * may contain wildcards, hosts may be %any.
+ * If no IKE_SA is found, a new one is created. This is also the case when
+ * the found IKE_SA is in the DELETING state.
+ *
+ * @param this the manager object
+ * @param my_host address of our host
+ * @param other_id address of remote host
+ * @param my_id ID used by us
+ * @param other_id ID used by remote
+ * @return checked out/created IKE_SA
+ */
+ ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this,
+ host_t *my_host, host_t* other_host,
+ identification_t *my_id,
+ identification_t *other_id);
+
+ /**
+ * @brief Check out an IKE_SA a unique ID.
+ *
+ * Every IKE_SA and every CHILD_SA is uniquely identified by an ID.
+ * These checkout function uses, depending
+ * on the child parameter, the unique ID of the IKE_SA or the reqid
+ * of one of a IKE_SAs CHILD_SA.
+ *
+ * @param this the manager object
+ * @param id unique ID of the object
+ * @param child TRUE to use CHILD, FALSE to use IKE_SA
+ * @return
+ * - checked out IKE_SA, if found
+ * - NULL, if not found
+ */
+ ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id,
+ bool child);
+
+ /**
+ * @brief Check out an IKE_SA by the policy/connection name.
+ *
+ * Check out the IKE_SA by the connections name or by a CHILD_SAs policy
+ * name.
+ *
+ * @param this the manager object
+ * @param name name of the connection/policy
+ * @param child TRUE to use policy name, FALSE to use conn name
+ * @return
+ * - checked out IKE_SA, if found
+ * - NULL, if not found
+ */
+ ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name,
+ bool child);
+
+ /**
+ * @brief Create an iterator over all stored IKE_SAs.
+ *
+ * The avoid synchronization issues, the iterator locks access
+ * to the manager exclusively, until it gets destroyed.
+ * This iterator is for reading only! Writing will corrupt the manager.
+ *
+ * @param this the manager object
+ * @return iterator over all IKE_SAs.
+ */
+ iterator_t *(*create_iterator) (ike_sa_manager_t* this);
+
+ /**
+ * @brief Checkin the SA after usage.
+ *
+ * @warning the SA pointer MUST NOT be used after checkin!
+ * The SA must be checked out again!
+ *
+ * @param this the manager object
+ * @param ike_sa_id the SA identifier, will be updated
+ * @param ike_sa checked out SA
+ * @returns
+ * - SUCCESS if checked in
+ * - NOT_FOUND when not found (shouldn't happen!)
+ */
+ status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
+
+ /**
+ * @brief Destroy a checked out SA.
+ *
+ * The IKE SA is destroyed without notification of the remote peer.
+ * Use this only if the other peer doesn't respond or behaves not
+ * as predicted.
+ * Checking in and destruction is an atomic operation (for the IKE_SA),
+ * so this can be called if the SA is in a "unclean" state, without the
+ * risk that another thread can get the SA.
+ *
+ * @param this the manager object
+ * @param ike_sa SA to delete
+ * @returns
+ * - SUCCESS if found
+ * - NOT_FOUND when no such SA is available
+ */
+ status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
+
+ /**
+ * @brief Get the number of IKE_SAs which are in the connecting state.
+ *
+ * To prevent the server from resource exhaustion, cookies and other
+ * mechanisms are used. The number of half open IKE_SAs is a good
+ * indicator to see if a peer is flooding the server.
+ * If a host is supplied, only the number of half open IKE_SAs initiated
+ * from this IP are counted.
+ * Only SAs for which we are the responder are counted.
+ *
+ * @param this the manager object
+ * @param ip NULL for all, IP for half open IKE_SAs with IP
+ * @return number of half open IKE_SAs
+ */
+ int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip);
+
+ /**
+ * @brief Destroys the manager with all associated SAs.
+ *
+ * Threads will be driven out, so all SAs can be deleted cleanly.
+ *
+ * @param this the manager object
+ */
+ void (*destroy) (ike_sa_manager_t *this);
+};
+
+/**
+ * @brief Create a manager.
+ *
+ * @returns ike_sa_manager_t object
+ *
+ * @ingroup sa
+ */
+ike_sa_manager_t *ike_sa_manager_create(void);
+
+#endif /*IKE_SA_MANAGER_H_*/
diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c
new file mode 100644
index 000000000..844300735
--- /dev/null
+++ b/src/charon/sa/task_manager.c
@@ -0,0 +1,854 @@
+/**
+ * @file task_manager.c
+ *
+ * @brief Implementation of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "task_manager.h"
+
+#include <daemon.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_rekey.h>
+#include <sa/tasks/child_delete.h>
+#include <encoding/payloads/delete_payload.h>
+#include <queues/jobs/retransmit_job.h>
+
+typedef struct exchange_t exchange_t;
+
+/**
+ * An exchange in the air, used do detect and handle retransmission
+ */
+struct exchange_t {
+
+ /**
+ * Message ID used for this transaction
+ */
+ u_int32_t mid;
+
+ /**
+ * generated packet for retransmission
+ */
+ packet_t *packet;
+};
+
+typedef struct private_task_manager_t private_task_manager_t;
+
+/**
+ * private data of the task manager
+ */
+struct private_task_manager_t {
+
+ /**
+ * public functions
+ */
+ task_manager_t public;
+
+ /**
+ * associated IKE_SA we are serving
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Exchange we are currently handling as responder
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ } responding;
+
+ /**
+ * Exchange we are currently handling as initiator
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * how many times we have retransmitted so far
+ */
+ u_int retransmitted;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ /**
+ * type of the initated exchange
+ */
+ exchange_type_t type;
+
+ } initiating;
+
+ /**
+ * List of queued tasks not yet in action
+ */
+ linked_list_t *queued_tasks;
+
+ /**
+ * List of active tasks, initiated by ourselve
+ */
+ linked_list_t *active_tasks;
+
+ /**
+ * List of tasks initiated by peer
+ */
+ linked_list_t *passive_tasks;
+};
+
+/**
+ * flush all tasks in the task manager
+ */
+static void flush(private_task_manager_t *this)
+{
+ task_t *task;
+
+ this->queued_tasks->destroy_offset(this->queued_tasks,
+ offsetof(task_t, destroy));
+ this->passive_tasks->destroy_offset(this->passive_tasks,
+ offsetof(task_t, destroy));
+
+ /* emmit outstanding signals for tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ switch (task->get_type(task))
+ {
+ case IKE_AUTH:
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed");
+ break;
+ case IKE_DELETE:
+ SIG(IKE_DOWN_FAILED, "IKE_SA deleted");
+ break;
+ case IKE_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed");
+ break;
+ case CHILD_CREATE:
+ SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed");
+ break;
+ case CHILD_DELETE:
+ SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed");
+ break;
+ case CHILD_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed");
+ break;
+ default:
+ break;
+ }
+ task->destroy(task);
+ }
+ this->queued_tasks = linked_list_create();
+ this->passive_tasks = linked_list_create();
+}
+
+/**
+ * move a task of a specific type from the queue to the active list
+ */
+static bool activate_task(private_task_manager_t *this, task_type_t type)
+{
+ iterator_t *iterator;
+ task_t *task;
+ bool found = FALSE;
+
+ iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ if (task->get_type(task) == type)
+ {
+ DBG2(DBG_IKE, " activating %N task", task_type_names, type);
+ iterator->remove(iterator);
+ this->active_tasks->insert_last(this->active_tasks, task);
+ found = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+/**
+ * Implementation of task_manager_t.retransmit
+ */
+static status_t retransmit(private_task_manager_t *this, u_int32_t message_id)
+{
+ if (message_id == this->initiating.mid)
+ {
+ u_int32_t timeout;
+ job_t *job;
+
+ timeout = charon->configuration->get_retransmit_timeout(
+ charon->configuration, this->initiating.retransmitted);
+ if (timeout == 0)
+ {
+ DBG1(DBG_IKE, "giving up after %d retransmits",
+ this->initiating.retransmitted - 1);
+ return DESTROY_ME;
+ }
+
+ if (this->initiating.retransmitted)
+ {
+ DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+ this->initiating.retransmitted, message_id);
+ }
+ this->initiating.retransmitted++;
+
+ charon->sender->send(charon->sender,
+ this->initiating.packet->clone(this->initiating.packet));
+ job = (job_t*)retransmit_job_create(this->initiating.mid,
+ this->ike_sa->get_id(this->ike_sa));
+ charon->event_queue->add_relative(charon->event_queue, job, timeout);
+ }
+ return SUCCESS;
+}
+
+/**
+ * build a request using the active task list
+ * Implementation of task_manager_t.initiate
+ */
+static status_t build_request(private_task_manager_t *this)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ status_t status;
+ exchange_type_t exchange = 0;
+
+ if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
+ {
+ DBG2(DBG_IKE, "delaying task initiation, exchange in progress");
+ /* do not initiate if we already have a message in the air */
+ return SUCCESS;
+ }
+
+ if (this->active_tasks->get_count(this->active_tasks) == 0)
+ {
+ DBG2(DBG_IKE, "activating new tasks");
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_CREATED:
+ if (activate_task(this, IKE_INIT))
+ {
+ exchange = IKE_SA_INIT;
+ activate_task(this, IKE_NATD);
+ activate_task(this, IKE_CERT);
+ activate_task(this, IKE_AUTHENTICATE);
+ activate_task(this, IKE_CONFIG);
+ activate_task(this, CHILD_CREATE);
+ }
+ break;
+ case IKE_ESTABLISHED:
+ if (activate_task(this, CHILD_CREATE))
+ {
+ exchange = CREATE_CHILD_SA;
+ activate_task(this, IKE_CONFIG);
+ break;
+ }
+ if (activate_task(this, CHILD_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, CHILD_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, IKE_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DEADPEER))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_REKEYING:
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_DELETING:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ DBG2(DBG_IKE, "reinitiating already active tasks");
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task));
+ switch (task->get_type(task))
+ {
+ case IKE_INIT:
+ exchange = IKE_SA_INIT;
+ break;
+ case IKE_AUTHENTICATE:
+ exchange = IKE_AUTH;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ iterator->destroy(iterator);
+ }
+
+ if (exchange == 0)
+ {
+ DBG2(DBG_IKE, "nothing to initiate");
+ /* nothing to do yet... */
+ return SUCCESS;
+ }
+
+ message = message_create();
+ message->set_message_id(message, this->initiating.mid);
+ message->set_exchange_type(message, exchange);
+ this->initiating.type = exchange;
+ this->initiating.retransmitted = 0;
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ message->destroy(message);
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ DESTROY_IF(this->initiating.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->initiating.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ /* message generation failed. There is nothing more to do than to
+ * close the SA */
+ flush(this);
+ return DESTROY_ME;
+ }
+
+ return retransmit(this, this->initiating.mid);
+}
+
+/**
+ * handle an incoming response message
+ */
+static status_t process_response(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task;
+
+ if (message->get_exchange_type(message) != this->initiating.type)
+ {
+ DBG1(DBG_IKE, "received %N response, but expected %N",
+ exchange_type_names, message->get_exchange_type(message),
+ exchange_type_names, this->initiating.type);
+ return DESTROY_ME;
+ }
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ this->initiating.mid++;
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+
+ return build_request(this);
+}
+
+/**
+ * handle exchange collisions
+ */
+static void handle_collisions(private_task_manager_t *this, task_t *task)
+{
+ iterator_t *iterator;
+ task_t *active;
+ task_type_t type;
+
+ type = task->get_type(task);
+
+ /* do we have to check */
+ if (type == IKE_REKEY || type == CHILD_REKEY ||
+ type == CHILD_DELETE || type == IKE_DELETE)
+ {
+ /* find an exchange collision, and notify these tasks */
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&active))
+ {
+ switch (active->get_type(active))
+ {
+ case IKE_REKEY:
+ if (type == IKE_REKEY || type == IKE_DELETE)
+ {
+ ike_rekey_t *rekey = (ike_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ case CHILD_REKEY:
+ if (type == CHILD_REKEY || type == CHILD_DELETE)
+ {
+ child_rekey_t *rekey = (child_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ iterator->destroy(iterator);
+ return;
+ }
+ iterator->destroy(iterator);
+ }
+ /* destroy task if not registered in any active task */
+ task->destroy(task);
+}
+
+/**
+ * build a response depending on the "passive" task list
+ */
+static status_t build_response(private_task_manager_t *this,
+ exchange_type_t exchange)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ bool delete = FALSE;
+ status_t status;
+
+ message = message_create();
+ message->set_exchange_type(message, exchange);
+ message->set_message_id(message, this->responding.mid);
+ message->set_request(message, FALSE);
+
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ handle_collisions(this, task);
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ if (delete)
+ {
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* remove resonder SPI if IKE_SA_INIT failed */
+ if (delete && exchange == IKE_SA_INIT)
+ {
+ ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
+ id->set_responder_spi(id, 0);
+ }
+
+ /* message complete, send it */
+ DESTROY_IF(this->responding.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->responding.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+
+ charon->sender->send(charon->sender,
+ this->responding.packet->clone(this->responding.packet));
+ if (delete)
+ {
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * handle an incoming request message
+ */
+static status_t process_request(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task = NULL;
+ exchange_type_t exchange;
+ payload_t *payload;
+ notify_payload_t *notify;
+
+ exchange = message->get_exchange_type(message);
+
+ /* create tasks depending on request type */
+ switch (exchange)
+ {
+ case IKE_SA_INIT:
+ {
+ task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_cert_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_config_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case CREATE_CHILD_SA:
+ {
+ bool notify_found = FALSE, ts_found = FALSE;
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ {
+ /* if we find a rekey notify, its CHILD_SA rekeying */
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == REKEY_SA &&
+ (notify->get_protocol_id(notify) == PROTO_AH ||
+ notify->get_protocol_id(notify) == PROTO_ESP))
+ {
+ notify_found = TRUE;
+ }
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ case TRAFFIC_SELECTOR_RESPONDER:
+ {
+ /* if we don't find a TS, its IKE rekeying */
+ ts_found = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (ts_found)
+ {
+ if (notify_found)
+ {
+ task = (task_t*)child_rekey_create(this->ike_sa, NULL);
+ }
+ else
+ {
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case INFORMATIONAL:
+ {
+ delete_payload_t *delete;
+
+ delete = (delete_payload_t*)message->get_payload(message, DELETE);
+ if (delete)
+ {
+ if (delete->get_protocol_id(delete) == PROTO_IKE)
+ {
+ task = (task_t*)ike_delete_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ else
+ {
+ task = (task_t*)child_delete_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_dpd_create(FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* let the tasks process the message */
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return build_response(this, exchange);
+}
+
+/**
+ * Implementation of task_manager_t.process_message
+ */
+static status_t process_message(private_task_manager_t *this, message_t *msg)
+{
+ u_int32_t mid = msg->get_message_id(msg);
+
+ if (msg->get_request(msg))
+ {
+ if (mid == this->responding.mid)
+ {
+ if (process_request(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ this->responding.mid++;
+ }
+ else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ {
+ DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+ "retransmitting response", mid);
+ charon->sender->send(charon->sender,
+ this->responding.packet->clone(this->responding.packet));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->responding.mid);
+ }
+ }
+ else
+ {
+ if (mid == this->initiating.mid)
+ {
+ if (process_response(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->initiating.mid);
+ return SUCCESS;
+ }
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_manager_t.queue_task
+ */
+static void queue_task(private_task_manager_t *this, task_t *task)
+{
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+}
+
+/**
+ * Implementation of task_manager_t.adopt_tasks
+ */
+static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other)
+{
+ task_t *task;
+
+ /* move queued tasks from other to this */
+ while (other->queued_tasks->remove_last(other->queued_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+
+ /* reset active tasks and move them to others queued tasks */
+ while (other->active_tasks->remove_last(other->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.busy
+ */
+static bool busy(private_task_manager_t *this)
+{
+ return (this->active_tasks->get_count(this->active_tasks) > 0);
+}
+
+/**
+ * Implementation of task_manager_t.reset
+ */
+static void reset(private_task_manager_t *this)
+{
+ task_t *task;
+
+ /* reset message counters and retransmit packets */
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = -1;
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+
+ /* reset active tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.destroy
+ */
+static void destroy(private_task_manager_t *this)
+{
+ flush(this);
+
+ this->active_tasks->destroy(this->active_tasks);
+ this->queued_tasks->destroy(this->queued_tasks);
+ this->passive_tasks->destroy(this->passive_tasks);
+
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa)
+{
+ private_task_manager_t *this = malloc_thing(private_task_manager_t);
+
+ this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message;
+ this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task;
+ this->public.initiate = (status_t(*)(task_manager_t*))build_request;
+ this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit;
+ this->public.reset = (void(*)(task_manager_t*))reset;
+ this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks;
+ this->public.busy = (bool(*)(task_manager_t*))busy;
+ this->public.destroy = (void(*)(task_manager_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = 0;
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ this->queued_tasks = linked_list_create();
+ this->active_tasks = linked_list_create();
+ this->passive_tasks = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h
new file mode 100644
index 000000000..c766d4a65
--- /dev/null
+++ b/src/charon/sa/task_manager.h
@@ -0,0 +1,144 @@
+/**
+ * @file task_manager.h
+ *
+ * @brief Interface of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TASK_MANAGER_H_
+#define TASK_MANAGER_H_
+
+typedef struct task_manager_t task_manager_t;
+
+#include <library.h>
+#include <encoding/message.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief The task manager, juggles task and handles message exchanges.
+ *
+ * On incoming requests, the task manager creates new tasks on demand and
+ * juggles the request through all available tasks. Each task inspects the
+ * request and adds payloads as necessary to the response.
+ * On outgoing requests, the task manager delivers the request through the tasks
+ * to build it, the response gets processed by each task to complete.
+ * The task manager has an internal Queue to store task which should get
+ * completed.
+ * For the initial IKE_SA setup, several tasks are queued: One for the
+ * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup
+ * and maybe one for virtual IP assignement.
+ *
+ * @b Constructors:
+ * - task_manager_create()
+ *
+ * @ingroup sa
+ */
+struct task_manager_t {
+
+ /**
+ * @brief Process an incoming message.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - DESTROY_ME if IKE_SA must be closed
+ * - SUCCESS otherwise
+ */
+ status_t (*process_message) (task_manager_t *this, message_t *message);
+
+ /**
+ * @brief Initiate an exchange with the currently queued tasks.
+ *
+ * @param this calling object
+ */
+ status_t (*initiate) (task_manager_t *this);
+
+ /**
+ * @brief Queue a task in the manager.
+ *
+ * @param this calling object
+ * @param task task to queue
+ */
+ void (*queue_task) (task_manager_t *this, task_t *task);
+
+ /**
+ * @brief Retransmit a request if it hasn't been acknowledged yet.
+ *
+ * A return value of INVALID_STATE means that the message was already
+ * acknowledged and has not to be retransmitted. A return value of SUCCESS
+ * means retransmission was required and the message has been resent.
+ *
+ * @param this calling object
+ * @param message_id ID of the message to retransmit
+ * @return
+ * - INVALID_STATE if retransmission not required
+ * - SUCCESS if retransmission sent
+ */
+ status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
+
+ /**
+ * @brief Migrate all tasks from other to this.
+ *
+ * To rekey or reestablish an IKE_SA completely, all queued or active
+ * tasks should get migrated to the new IKE_SA.
+ *
+ * @param this manager which gets all tasks
+ * @param other manager which gives away its tasks
+ */
+ void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
+
+ /**
+ * @brief Reset message ID counters of the task manager.
+ *
+ * The IKEv2 protocol requires to restart exchanges with message IDs
+ * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method
+ * resets the message IDs and resets all active tasks using the migrate()
+ * method.
+ *
+ * @param this calling object
+ * @param other manager which gives away its tasks
+ */
+ void (*reset) (task_manager_t *this);
+
+ /**
+ * @brief Check if we are currently waiting for a reply.
+ *
+ * @param this calling object
+ * @return TRUE if we are waiting, FALSE otherwise
+ */
+ bool (*busy) (task_manager_t *this);
+
+ /**
+ * @brief Destroy the task_manager_t.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_manager_t *this);
+};
+
+/**
+ * @brief Create an instance of the task manager.
+ *
+ * @param ike_sa IKE_SA to manage.
+ *
+ * @ingroup sa
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa);
+
+#endif /* TASK_MANAGER_H_ */
diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c
new file mode 100644
index 000000000..781d679f2
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.c
@@ -0,0 +1,804 @@
+/**
+ * @file child_create.c
+ *
+ * @brief Implementation of the child_create task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_create.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_child_create_t private_child_create_t;
+
+/**
+ * Private members of a child_create_t task.
+ */
+struct private_child_create_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_create_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * policy to create the CHILD_SA from
+ */
+ policy_t *policy;
+
+ /**
+ * list of proposal candidates
+ */
+ linked_list_t *proposals;
+
+ /**
+ * selected proposal to use for CHILD_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * traffic selectors for initiators side
+ */
+ linked_list_t *tsi;
+
+ /**
+ * traffic selectors for responders side
+ */
+ linked_list_t *tsr;
+
+ /**
+ * mode the new CHILD_SA uses (transport/tunnel/beet)
+ */
+ mode_t mode;
+
+ /**
+ * reqid to use if we are rekeying
+ */
+ u_int32_t reqid;
+
+ /**
+ * CHILD_SA which gets established
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * successfully established the CHILD?
+ */
+ bool established;
+};
+
+/**
+ * get the nonce from a message
+ */
+static status_t get_nonce(message_t *message, chunk_t *nonce)
+{
+ nonce_payload_t *payload;
+
+ payload = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (payload == NULL)
+ {
+ return FAILED;
+ }
+ *nonce = payload->get_nonce(payload);
+ return NEED_MORE;
+}
+
+/**
+ * generate a new nonce to include in a CREATE_CHILD_SA message
+ */
+static status_t generate_nonce(chunk_t *nonce)
+{
+ status_t status;
+ randomizer_t *randomizer = randomizer_create();
+
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Check a list of traffic selectors if any selector belongs to host
+ */
+static bool ts_list_is_host(linked_list_t *list, host_t *host)
+{
+ traffic_selector_t *ts;
+ bool is_host = TRUE;
+ iterator_t *iterator = list->create_iterator(list, TRUE);
+
+ while (is_host && iterator->iterate(iterator, (void**)&ts))
+ {
+ is_host = is_host && ts->is_host(ts, host);
+ }
+ iterator->destroy(iterator);
+ return is_host;
+}
+
+/**
+ * Install a CHILD_SA for usage
+ */
+static status_t select_and_install(private_child_create_t *this)
+{
+ prf_plus_t *prf_plus;
+ status_t status;
+ chunk_t nonce_i, nonce_r, seed;
+ linked_list_t *my_ts, *other_ts;
+ host_t *me, *other, *other_vip, *my_vip;
+
+ if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message");
+ return FAILED;
+ }
+
+ if (this->initiator)
+ {
+ nonce_i = this->my_nonce;
+ nonce_r = this->other_nonce;
+ my_ts = this->tsi;
+ other_ts = this->tsr;
+ }
+ else
+ {
+ nonce_r = this->my_nonce;
+ nonce_i = this->other_nonce;
+ my_ts = this->tsr;
+ other_ts = this->tsi;
+ }
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
+ other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
+
+ this->proposal = this->policy->select_proposal(this->policy, this->proposals);
+
+ if (this->proposal == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "no acceptable proposal found");
+ return FAILED;
+ }
+
+ if (this->initiator && my_vip)
+ { /* if we have a virtual IP, shorten our TS to the minimum */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts,
+ my_vip);
+ /* to setup firewall rules correctly, CHILD_SA needs the virtual IP */
+ this->child_sa->set_virtual_ip(this->child_sa, my_vip);
+ }
+ else
+ { /* shorten in the host2host case only */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy,
+ my_ts, me);
+ }
+ if (other_vip)
+ { /* if other has a virtual IP, shorten it's traffic selectors to it */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other_vip);
+ }
+ else
+ { /* use his host for the host2host case */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other);
+ }
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ if (this->initiator)
+ {
+ this->tsi = my_ts;
+ this->tsr = other_ts;
+ }
+ else
+ {
+ this->tsr = my_ts;
+ this->tsi = other_ts;
+ }
+
+ if (this->tsi->get_count(this->tsi) == 0 ||
+ this->tsr->get_count(this->tsr) == 0)
+ {
+ SIG(CHILD_UP_FAILED, "no acceptable traffic selectors found");
+ return FAILED;
+ }
+
+ if (!this->initiator)
+ {
+ /* check if requested mode is acceptable, downgrade if required */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ if (!ts_list_is_host(this->tsi, other) ||
+ !ts_list_is_host(this->tsr, me))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
+ }
+ else if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, connection NATed");
+ }
+ break;
+ case MODE_BEET:
+ if (!ts_list_is_host(this->tsi, NULL) ||
+ !ts_list_is_host(this->tsr, NULL))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ seed = chunk_cata("cc", nonce_i, nonce_r);
+ prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
+
+ if (this->initiator)
+ {
+ status = this->child_sa->update(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ else
+ {
+ status = this->child_sa->add(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ prf_plus->destroy(prf_plus);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel");
+ return status;
+ }
+
+ status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
+ this->mode);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel");
+ return status;
+ }
+ /* add to IKE_SA, and remove from task */
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+ this->established = TRUE;
+ return SUCCESS;
+}
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_child_create_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ nonce_payload_t *nonce_payload;
+
+ /* add SA payload */
+ if (this->initiator)
+ {
+ sa_payload = sa_payload_create_from_proposal_list(this->proposals);
+ }
+ else
+ {
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ /* add nonce payload if not in IKE_AUTH */
+ if (message->get_exchange_type(message) == CREATE_CHILD_SA)
+ {
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+ message->add_payload(message, (payload_t*)nonce_payload);
+ }
+
+ /* add TSi/TSr payloads */
+ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
+ message->add_payload(message, (payload_t*)ts_payload);
+ ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
+ message->add_payload(message, (payload_t*)ts_payload);
+
+ /* add a notify if we are not in tunnel mode */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty);
+ break;
+ case MODE_BEET:
+ message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ notify_payload_t *notify_payload;
+
+ /* defaults to TUNNEL mode */
+ this->mode = MODE_TUNNEL;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ sa_payload = (sa_payload_t*)payload;
+ this->proposals = sa_payload->get_proposals(sa_payload);
+ break;
+ case TRAFFIC_SELECTOR_INITIATOR:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsi = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case TRAFFIC_SELECTOR_RESPONDER:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsr = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case NOTIFY:
+ notify_payload = (notify_payload_t*)payload;
+ switch (notify_payload ->get_notify_type(notify_payload ))
+ {
+ case USE_TRANSPORT_MODE:
+ this->mode = MODE_TRANSPORT;
+ break;
+ case USE_BEET_MODE:
+ this->mode = MODE_BEET;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_create_t *this, message_t *message)
+{
+ host_t *me, *other, *vip;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ case IKE_AUTH:
+ if (!message->get_payload(message, ID_INITIATOR))
+ {
+ /* send only in the first request, not in subsequent EAP */
+ return NEED_MORE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ SIG(CHILD_UP_START, "establishing CHILD_SA");
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ vip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ if (vip)
+ { /* propose a 0.0.0.0/0 subnet when we use virtual ip */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL);
+ vip->destroy(vip);
+ }
+ else
+ { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, me);
+ }
+ this->tsr = this->policy->get_other_traffic_selectors(this->policy, other);
+ this->proposals = this->policy->get_proposals(this->policy);
+ this->mode = this->policy->get_mode(this->policy);
+
+ this->child_sa = child_sa_create(me, other,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel");
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->proposals = NULL;
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ case IKE_AUTH:
+ if (message->get_payload(message, ID_INITIATOR) == NULL)
+ {
+ /* wait until extensible authentication completed, if used */
+ return NEED_MORE;
+ }
+ default:
+ break;
+ }
+
+ process_payloads(this, message);
+
+ if (this->tsi == NULL || this->tsr == NULL)
+ {
+ DBG1(DBG_IKE, "TS payload missing in message");
+ return NEED_MORE;
+ }
+
+ this->policy = charon->policies->get_policy(charon->policies,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->tsr, this->tsi,
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa));
+
+ if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL)
+ {
+ this->ike_sa->set_policy(this->ike_sa, this->policy);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ case IKE_AUTH:
+ if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+ {
+ /* wait until extensible authentication completed, if used */
+ return NEED_MORE;
+ }
+ default:
+ break;
+ }
+
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING)
+ {
+ SIG(CHILD_UP_FAILED, "unable to create CHILD_SA while rekeying IKE_SA");
+ message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty);
+ return SUCCESS;
+ }
+
+ if (this->policy == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "no acceptable policy found");
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (select_and_install(this) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
+ return SUCCESS;
+ }
+
+ build_payloads(this, message);
+
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ case IKE_AUTH:
+ if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+ {
+ /* wait until extensible authentication completed, if used */
+ return NEED_MORE;
+ }
+ default:
+ break;
+ }
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ /* handle notify errors related to CHILD_SA only */
+ case NO_PROPOSAL_CHOSEN:
+ case SINGLE_PAIR_REQUIRED:
+ case NO_ADDITIONAL_SAS:
+ case INTERNAL_ADDRESS_FAILURE:
+ case FAILED_CP_REQUIRED:
+ case TS_UNACCEPTABLE:
+ case INVALID_SELECTORS:
+ {
+ SIG(CHILD_UP_FAILED, "received %N notify, no CHILD_SA built",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ /* an error in CHILD_SA creation is not critical */
+ return SUCCESS;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ if (select_and_install(this) == SUCCESS)
+ {
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_create_t *this)
+{
+ return CHILD_CREATE;
+}
+
+/**
+ * Implementation of child_create_t.use_reqid
+ */
+static void use_reqid(private_child_create_t *this, u_int32_t reqid)
+{
+ this->reqid = reqid;
+}
+
+/**
+ * Implementation of child_create_t.get_child
+ */
+static child_sa_t* get_child(private_child_create_t *this)
+{
+ return this->child_sa;
+}
+
+/**
+ * Implementation of child_create_t.get_lower_nonce
+ */
+static chunk_t get_lower_nonce(private_child_create_t *this)
+{
+ if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr,
+ min(this->my_nonce.len, this->other_nonce.len)) < 0)
+ {
+ return this->my_nonce;
+ }
+ else
+ {
+ return this->other_nonce;
+ }
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ this->ike_sa = ike_sa;
+ this->proposals = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+ this->established = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_create_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ if (!this->established)
+ {
+ DESTROY_IF(this->child_sa);
+ }
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ DESTROY_IF(this->policy);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_child_create_t *this = malloc_thing(private_child_create_t);
+
+ this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child;
+ this->public.get_lower_nonce = (chunk_t(*)(child_create_t*))get_lower_nonce;
+ this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy->get_ref(policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->proposals = NULL;
+ this->proposal = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+ this->established = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h
new file mode 100644
index 000000000..200d37457
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.h
@@ -0,0 +1,88 @@
+/**
+ * @file child_create.h
+ *
+ * @brief Interface child_create_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_CREATE_H_
+#define CHILD_CREATE_H_
+
+typedef struct child_create_t child_create_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type CHILD_CREATE, established a new CHILD_SA.
+ *
+ * This task may be included in the IKE_AUTH message or in a separate
+ * CREATE_CHILD_SA exchange.
+ *
+ * @b Constructors:
+ * - child_create_create()
+ *
+ * @ingroup tasks
+ */
+struct child_create_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Use a specific reqid for the CHILD_SA.
+ *
+ * When this task is used for rekeying, the same reqid is used
+ * for the new CHILD_SA.
+ *
+ * @param this calling object
+ * @param reqid reqid to use
+ */
+ void (*use_reqid) (child_create_t *this, u_int32_t reqid);
+
+ /**
+ * @brief Get the lower of the two nonces, used for rekey collisions.
+ *
+ * @param this calling object
+ * @return lower nonce
+ */
+ chunk_t (*get_lower_nonce) (child_create_t *this);
+
+ /**
+ * @brief Get the CHILD_SA established/establishing by this task.
+ *
+ * @param this calling object
+ * @return child_sa
+ */
+ child_sa_t* (*get_child) (child_create_t *this);
+};
+
+/**
+ * @brief Create a new child_create task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy if task initiator, NULL if responder
+ * @return child_create task to handle by the task_manager
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* CHILD_CREATE_H_ */
diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c
new file mode 100644
index 000000000..23d509de5
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.c
@@ -0,0 +1,292 @@
+/**
+ * @file child_delete.c
+ *
+ * @brief Implementation of the child_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_child_delete_t private_child_delete_t;
+
+/**
+ * Private members of a child_delete_t task.
+ */
+struct private_child_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * CHILD_SAs which get deleted
+ */
+ linked_list_t *child_sas;
+};
+
+/**
+ * build the delete payloads from the listed child_sas
+ */
+static void build_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ delete_payload_t *ah = NULL, *esp = NULL;
+ u_int32_t spi;
+ child_sa_t *child_sa;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ spi = child_sa->get_spi(child_sa, TRUE);
+ switch (child_sa->get_protocol(child_sa))
+ {
+ case PROTO_ESP:
+ if (esp == NULL)
+ {
+ esp = delete_payload_create(PROTO_ESP);
+ message->add_payload(message, (payload_t*)esp);
+ }
+ esp->add_spi(esp, spi);
+ break;
+ case PROTO_AH:
+ if (ah == NULL)
+ {
+ ah = delete_payload_create(PROTO_AH);
+ message->add_payload(message, (payload_t*)ah);
+ }
+ ah->add_spi(ah, spi);
+ break;
+ default:
+ break;
+ }
+ child_sa->set_state(child_sa, CHILD_DELETING);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * read in payloads and find the children to delete
+ */
+static void process_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *payloads, *spis;
+ payload_t *payload;
+ delete_payload_t *delete_payload;
+ u_int32_t *spi;
+ protocol_id_t protocol;
+ child_sa_t *child_sa;
+
+ payloads = message->get_payload_iterator(message);
+ while (payloads->iterate(payloads, (void**)&payload))
+ {
+ if (payload->get_type(payload) == DELETE)
+ {
+ delete_payload = (delete_payload_t*)payload;
+ protocol = delete_payload->get_protocol_id(delete_payload);
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ spis = delete_payload->create_spi_iterator(delete_payload);
+ while (spis->iterate(spis, (void**)&spi))
+ {
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ *spi, FALSE);
+ if (child_sa == NULL)
+ {
+ DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, "
+ "but no such SA", protocol_id_names, protocol, ntohl(*spi));
+ continue;
+ }
+ DBG2(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x",
+ protocol_id_names, protocol, ntohl(*spi));
+
+ switch (child_sa->get_state(child_sa))
+ {
+ case CHILD_REKEYING:
+ /* we reply as usual, rekeying will fail */
+ break;
+ case CHILD_DELETING:
+ /* we don't send back a delete if we initiated ourself */
+ if (!this->initiator)
+ {
+ this->ike_sa->destroy_child_sa(this->ike_sa,
+ protocol, *spi);
+ continue;
+ }
+ default:
+ break;
+ }
+
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ spis->destroy(spis);
+ }
+ }
+ payloads->destroy(payloads);
+}
+
+/**
+ * destroy the children listed in this->child_sas
+ */
+static void destroy_children(private_child_delete_t *this)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ spi = child_sa->get_spi(child_sa, TRUE);
+ protocol = child_sa->get_protocol(child_sa);
+ this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_delete_t *this, message_t *message)
+{
+ build_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_delete_t *this, message_t *message)
+{
+ /* flush the list before adding new SAs */
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+
+ process_payloads(this, message);
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_delete_t *this, message_t *message)
+{
+ process_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_delete_t *this, message_t *message)
+{
+ /* if we are rekeying, we send an empty informational */
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
+ {
+ build_payloads(this, message);
+ }
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_delete_t *this)
+{
+ return CHILD_DELETE;
+}
+
+/**
+ * Implementation of child_delete_t.get_child
+ */
+static child_sa_t* get_child(private_child_delete_t *this)
+{
+ child_sa_t *child_sa = NULL;
+ this->child_sas->get_first(this->child_sas, (void**)&child_sa);
+ return child_sa;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_delete_t *this)
+{
+ this->child_sas->destroy(this->child_sas);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_delete_t *this = malloc_thing(private_child_delete_t);
+
+ this->public.get_child = (child_sa_t*(*)(child_delete_t*))get_child;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->child_sas = linked_list_create();
+
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h
new file mode 100644
index 000000000..a7e676a50
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.h
@@ -0,0 +1,66 @@
+/**
+ * @file child_delete.h
+ *
+ * @brief Interface child_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_DELETE_H_
+#define CHILD_DELETE_H_
+
+typedef struct child_delete_t child_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <sa/child_sa.h>
+
+/**
+ * @brief Task of type child_delete, delete a CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct child_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Get the CHILD_SA to delete by this task.
+ *
+ * @param this calling object
+ * @return child_sa
+ */
+ child_sa_t* (*get_child) (child_delete_t *this);
+};
+
+/**
+ * @brief Create a new child_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa CHILD_SA to delete, or NULL as responder
+ * @return child_delete task to handle by the task_manager
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_DELETE_H_ */
diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c
new file mode 100644
index 000000000..745895dbb
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.c
@@ -0,0 +1,346 @@
+/**
+ * @file child_rekey.c
+ *
+ * @brief Implementation of the child_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_rekey.h"
+
+#include <daemon.h>
+#include <encoding/payloads/notify_payload.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_delete.h>
+#include <queues/jobs/rekey_child_sa_job.h>
+
+
+typedef struct private_child_rekey_t private_child_rekey_t;
+
+/**
+ * Private members of a child_rekey_t task.
+ */
+struct private_child_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the CHILD_CREATE task which is reused to simplify rekeying
+ */
+ child_create_t *child_create;
+
+ /**
+ * CHILD_SA which gets rekeyed
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * colliding task, may be delete or rekey
+ */
+ task_t *collision;
+};
+
+/**
+ * find a child using the REKEY_SA notify
+ */
+static void find_child(private_child_rekey_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ notify_payload_t *notify;
+ u_int32_t spi;
+ protocol_id_t protocol;
+
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+
+ notify = (notify_payload_t*)payload;
+ protocol = notify->get_protocol_id(notify);
+ spi = notify->get_spi(notify);
+
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ break;
+
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_rekey_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ protocol_id_t protocol;
+ u_int32_t spi, reqid;
+
+ /* we just need the rekey notify ... */
+ protocol = this->child_sa->get_protocol(this->child_sa);
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
+ notify->set_spi(notify, spi);
+ message->add_payload(message, (payload_t*)notify);
+
+ /* ... our CHILD_CREATE task does the hard work for us. */
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->task.build(&this->child_create->task, message);
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_rekey_t *this, message_t *message)
+{
+ /* let the CHILD_CREATE task process the message */
+ this->child_create->task.process(&this->child_create->task, message);
+
+ find_child(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_rekey_t *this, message_t *message)
+{
+ u_int32_t reqid;
+
+ if (this->child_sa == NULL ||
+ this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
+ {
+ DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ /* let the CHILD_CREATE task build the response */
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->task.build(&this->child_create->task, message);
+
+ if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
+ {
+ /* rekeying failed, reuse old child */
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ return SUCCESS;
+ }
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_rekey_t *this, message_t *message)
+{
+ protocol_id_t protocol;
+ u_int32_t spi;
+ child_sa_t *to_delete;
+
+ this->child_create->task.process(&this->child_create->task, message);
+ if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
+ {
+ /* establishing new child failed, reuse old. but not when we
+ * recieved a delete in the meantime */
+ if (!(this->collision &&
+ this->collision->get_type(this->collision) == CHILD_DELETE))
+ {
+ job_t *job;
+ u_int32_t retry = charon->configuration->get_retry_interval(
+ charon->configuration);
+ job = (job_t*)rekey_child_sa_job_create(
+ this->child_sa->get_reqid(this->child_sa),
+ this->child_sa->get_protocol(this->child_sa),
+ this->child_sa->get_spi(this->child_sa, TRUE));
+ DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
+ "trying again in %d seconds", retry);
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ charon->event_queue->add_relative(charon->event_queue, job, retry * 1000);
+ }
+ return SUCCESS;
+ }
+
+ to_delete = this->child_sa;
+
+ /* check for rekey collisions */
+ if (this->collision &&
+ this->collision->get_type(this->collision) == CHILD_REKEY)
+ {
+ chunk_t this_nonce, other_nonce;
+ private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
+
+ this_nonce = this->child_create->get_lower_nonce(this->child_create);
+ other_nonce = other->child_create->get_lower_nonce(other->child_create);
+
+ /* if we have the lower nonce, delete rekeyed SA. If not, delete
+ * the redundant. */
+ if (memcmp(this_nonce.ptr, other_nonce.ptr,
+ min(this_nonce.len, other_nonce.len)) < 0)
+ {
+ DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
+ }
+ else
+ {
+ DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
+ to_delete = this->child_create->get_child(this->child_create);
+ if (to_delete == NULL)
+ {
+ /* ooops, should not happen, fallback */
+ to_delete = this->child_sa;
+ }
+ }
+ }
+
+ spi = to_delete->get_spi(to_delete, TRUE);
+ protocol = to_delete->get_protocol(to_delete);
+ if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_rekey_t *this)
+{
+ return CHILD_REKEY;
+}
+
+/**
+ * Implementation of child_rekey_t.collide
+ */
+static void collide(private_child_rekey_t *this, task_t *other)
+{
+ /* the task manager only detects exchange collision, but not if
+ * the collision is for the same child. we check it here. */
+ if (other->get_type(other) == CHILD_REKEY)
+ {
+ private_child_rekey_t *rekey = (private_child_rekey_t*)other;
+ if (rekey == NULL || rekey->child_sa != this->child_sa)
+ {
+ /* not the same child => no collision */
+ return;
+ }
+ }
+ else if (other->get_type(other) == CHILD_DELETE)
+ {
+ child_delete_t *del = (child_delete_t*)other;
+ if (del == NULL || del->get_child(del) != this->child_sa)
+ {
+ /* not the same child => no collision */
+ return;
+ }
+ }
+ else
+ {
+ /* any other task is not critical for collisisions, ignore */
+ return;
+ }
+ DESTROY_IF(this->collision);
+ this->collision = other;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
+{
+ this->child_create->task.migrate(&this->child_create->task, ike_sa);
+ DESTROY_IF(this->collision);
+
+ this->ike_sa = ike_sa;
+ this->collision = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_rekey_t *this)
+{
+ this->child_create->task.destroy(&this->child_create->task);
+ DESTROY_IF(this->collision);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
+ policy_t *policy;
+
+ this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy = child_sa->get_policy(child_sa);
+ this->child_create = child_create_create(ike_sa, policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ this->child_create = child_create_create(ike_sa, NULL);
+ }
+
+ this->ike_sa = ike_sa;
+ this->child_sa = child_sa;
+ this->collision = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h
new file mode 100644
index 000000000..3515f0c3f
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.h
@@ -0,0 +1,70 @@
+/**
+ * @file child_rekey.h
+ *
+ * @brief Interface child_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_REKEY_H_
+#define CHILD_REKEY_H_
+
+typedef struct child_rekey_t child_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/child_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct child_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Register a rekeying task which collides with this one
+ *
+ * If two peers initiate rekeying at the same time, the collision must
+ * be handled gracefully. The task manager is aware of what exchanges
+ * are going on and notifies the outgoing task by passing the incoming.
+ *
+ * @param this task initated by us
+ * @param other incoming task
+ */
+ void (*collide)(child_rekey_t* this, task_t *other);
+};
+
+/**
+ * @brief Create a new CHILD_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa child_sa to rekey, NULL if responder
+ * @return child_rekey task to handle by the task_manager
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_REKEY_H_ */
diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c
new file mode 100644
index 000000000..541e1bb37
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.c
@@ -0,0 +1,750 @@
+/**
+ * @file ike_auth.c
+ *
+ * @brief Implementation of the ike_auth task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_auth.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/eap_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <sa/authenticators/eap_authenticator.h>
+
+
+
+typedef struct private_ike_auth_t private_ike_auth_t;
+
+/**
+ * Private members of a ike_auth_t task.
+ */
+struct private_ike_auth_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_auth_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Nonce chosen by us in ike_init
+ */
+ chunk_t my_nonce;
+
+ /**
+ * Nonce chosen by peer in ike_init
+ */
+ chunk_t other_nonce;
+
+ /**
+ * IKE_SA_INIT message sent by us
+ */
+ packet_t *my_packet;
+
+ /**
+ * IKE_SA_INIT message sent by peer
+ */
+ packet_t *other_packet;
+
+ /**
+ * EAP authenticator when using EAP
+ */
+ eap_authenticator_t *eap_auth;
+
+ /**
+ * EAP payload received and ready to process
+ */
+ eap_payload_t *eap_payload;
+
+ /**
+ * has the peer been authenticated successfully?
+ */
+ bool peer_authenticated;
+};
+
+/**
+ * build the AUTH payload
+ */
+static status_t build_auth(private_ike_auth_t *this, message_t *message)
+{
+ authenticator_t *auth;
+ auth_payload_t *auth_payload;
+ policy_t *policy;
+ auth_method_t method;
+ status_t status;
+
+ /* create own authenticator and add auth payload */
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ if (!policy)
+ {
+ SIG(IKE_UP_FAILED, "unable to authenticate, no policy found");
+ return FAILED;
+ }
+ method = policy->get_auth_method(policy);
+
+ auth = authenticator_create(this->ike_sa, method);
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured authentication method %N not supported",
+ auth_method_names, method);
+ return FAILED;
+ }
+
+ status = auth->build(auth, this->my_packet->get_data(this->my_packet),
+ this->other_nonce, &auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "generating authentication data failed");
+ return FAILED;
+ }
+ message->add_payload(message, (payload_t*)auth_payload);
+ return SUCCESS;
+}
+
+/**
+ * build ID payload(s)
+ */
+static status_t build_id(private_ike_auth_t *this, message_t *message)
+{
+ identification_t *me, *other;
+ id_payload_t *id;
+ policy_t *policy;
+
+ me = this->ike_sa->get_my_id(this->ike_sa);
+ other = this->ike_sa->get_other_id(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (me->contains_wildcards(me))
+ {
+ me = policy->get_my_id(policy);
+ if (me->contains_wildcards(me))
+ {
+ SIG(IKE_UP_FAILED, "negotiation of own ID failed");
+ return FAILED;
+ }
+ this->ike_sa->set_my_id(this->ike_sa, me->clone(me));
+ }
+
+ id = id_payload_create_from_identification(this->initiator, me);
+ message->add_payload(message, (payload_t*)id);
+
+ /* as initiator, include other ID if it does not contain wildcards */
+ if (this->initiator && !other->contains_wildcards(other))
+ {
+ id = id_payload_create_from_identification(FALSE, other);
+ message->add_payload(message, (payload_t*)id);
+ }
+ return SUCCESS;
+}
+
+/**
+ * process AUTH payload
+ */
+static status_t process_auth(private_ike_auth_t *this, message_t *message)
+{
+ auth_payload_t *auth_payload;
+ authenticator_t *auth;
+ auth_method_t auth_method;
+ status_t status;
+
+ auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
+
+ if (auth_payload == NULL)
+ {
+ /* AUTH payload is missing, client wants to use EAP authentication */
+ return NOT_FOUND;
+ }
+
+ auth_method = auth_payload->get_auth_method(auth_payload);
+ auth = authenticator_create(this->ike_sa, auth_method);
+
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "authentication method %N used by %D not "
+ "supported", auth_method_names, auth_method,
+ this->ike_sa->get_other_id(this->ike_sa));
+ return NOT_SUPPORTED;
+ }
+ status = auth->verify(auth, this->other_packet->get_data(this->other_packet),
+ this->my_nonce, auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "authentication of %D using %N failed",
+ this->ike_sa->get_other_id(this->ike_sa),
+ auth_method_names, auth_method);
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * process ID payload(s)
+ */
+static status_t process_id(private_ike_auth_t *this, message_t *message)
+{
+ identification_t *id;
+ id_payload_t *idr, *idi;
+
+ idi = (id_payload_t*)message->get_payload(message, ID_INITIATOR);
+ idr = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
+
+ if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL))
+ {
+ SIG(IKE_UP_FAILED, "ID payload missing in message");
+ return FAILED;
+ }
+
+ if (this->initiator)
+ {
+ id = idr->get_identification(idr);
+ this->ike_sa->set_other_id(this->ike_sa, id);
+ }
+ else
+ {
+ id = idi->get_identification(idi);
+ this->ike_sa->set_other_id(this->ike_sa, id);
+ if (idr)
+ {
+ id = idr->get_identification(idr);
+ this->ike_sa->set_my_id(this->ike_sa, id);
+ }
+ }
+ return SUCCESS;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from our message
+ */
+static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message)
+{
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->my_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ if (this->ike_sa->generate_message(this->ike_sa, message,
+ &this->my_packet) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from others message
+ */
+static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message)
+{
+ /* we collect the needed information in the IKE_SA_INIT exchange */
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->other_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ this->other_packet = message->get_packet(message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build to create AUTH payload from EAP data
+ */
+static status_t build_auth_eap(private_ike_auth_t *this, message_t *message)
+{
+ authenticator_t *auth;
+ auth_payload_t *auth_payload;
+
+ auth = (authenticator_t*)this->eap_auth;
+ if (auth->build(auth, this->my_packet->get_data(this->my_packet),
+ this->other_nonce, &auth_payload) != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "generating authentication data failed");
+ if (!this->initiator)
+ {
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ }
+ return FAILED;
+ }
+ message->add_payload(message, (payload_t*)auth_payload);
+ if (!this->initiator)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process to verify AUTH payload after EAP
+ */
+static status_t process_auth_eap(private_ike_auth_t *this, message_t *message)
+{
+ auth_payload_t *auth_payload;
+ authenticator_t *auth;
+
+ auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
+ this->peer_authenticated = FALSE;
+
+ if (auth_payload)
+ {
+ auth = (authenticator_t*)this->eap_auth;
+ if (auth->verify(auth, this->other_packet->get_data(this->other_packet),
+ this->my_nonce, auth_payload) == SUCCESS)
+ {
+ this->peer_authenticated = TRUE;
+ }
+ }
+
+ if (!this->peer_authenticated)
+ {
+ SIG(IKE_UP_FAILED, "authentication of %D using %N failed",
+ this->ike_sa->get_other_id(this->ike_sa),
+ auth_method_names, AUTH_EAP);
+ if (this->initiator)
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+ }
+ if (this->initiator)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for EAP exchanges
+ */
+static status_t process_eap_i(private_ike_auth_t *this, message_t *message)
+{
+ eap_payload_t *eap;
+
+ eap = (eap_payload_t*)message->get_payload(message, EXTENSIBLE_AUTHENTICATION);
+ if (eap == NULL)
+ {
+ SIG(IKE_UP_FAILED, "EAP payload missing");
+ return FAILED;
+ }
+ switch (this->eap_auth->process(this->eap_auth, eap, &eap))
+ {
+ case NEED_MORE:
+ this->eap_payload = eap;
+ return NEED_MORE;
+ case SUCCESS:
+ /* EAP exchange completed, now create and process AUTH */
+ this->eap_payload = NULL;
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap;
+ return NEED_MORE;
+ default:
+ this->eap_payload = NULL;
+ SIG(IKE_UP_FAILED, "failed to authenticate against %D using EAP",
+ this->ike_sa->get_other_id(this->ike_sa));
+ return FAILED;
+ }
+}
+
+/**
+ * Implementation of task_t.process for EAP exchanges
+ */
+static status_t process_eap_r(private_ike_auth_t *this, message_t *message)
+{
+ this->eap_payload = (eap_payload_t*)message->get_payload(message,
+ EXTENSIBLE_AUTHENTICATION);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for EAP exchanges
+ */
+static status_t build_eap_i(private_ike_auth_t *this, message_t *message)
+{
+ message->add_payload(message, (payload_t*)this->eap_payload);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for EAP exchanges
+ */
+static status_t build_eap_r(private_ike_auth_t *this, message_t *message)
+{
+ status_t status = NEED_MORE;
+ eap_payload_t *eap;
+
+ if (this->eap_payload == NULL)
+ {
+ SIG(IKE_UP_FAILED, "EAP payload missing");
+ return FAILED;
+ }
+
+ switch (this->eap_auth->process(this->eap_auth, this->eap_payload, &eap))
+ {
+ case NEED_MORE:
+
+ break;
+ case SUCCESS:
+ /* EAP exchange completed, now create and process AUTH */
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap;
+ break;
+ default:
+ SIG(IKE_UP_FAILED, "authentication of %D using %N failed",
+ this->ike_sa->get_other_id(this->ike_sa),
+ auth_method_names, AUTH_EAP);
+ status = FAILED;
+ break;
+ }
+ message->add_payload(message, (payload_t*)eap);
+ return status;
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_auth_t *this, message_t *message)
+{
+ policy_t *policy;
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ if (build_id(this, message) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ if (policy->get_auth_method(policy) == AUTH_EAP)
+ {
+ this->eap_auth = eap_authenticator_create(this->ike_sa);
+ }
+ else
+ {
+ if (build_auth(this, message) != SUCCESS)
+ {
+ return FAILED;
+ }
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ if (process_id(this, message) != SUCCESS)
+ {
+ return NEED_MORE;
+ }
+
+ switch (process_auth(this, message))
+ {
+ case SUCCESS:
+ this->peer_authenticated = TRUE;
+ break;
+ case NOT_FOUND:
+ /* use EAP if no AUTH payload found */
+ this->eap_auth = eap_authenticator_create(this->ike_sa);
+ break;
+ default:
+ break;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_auth_t *this, message_t *message)
+{
+ policy_t *policy;
+ eap_type_t eap_type;
+ eap_payload_t *eap_payload;
+ status_t status;
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ if (policy == NULL)
+ {
+ SIG(IKE_UP_FAILED, "no acceptable policy found");
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
+ if (build_id(this, message) != SUCCESS ||
+ build_auth(this, message) != SUCCESS)
+ {
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
+ /* use "traditional" authentication if we could authenticate peer */
+ if (this->peer_authenticated)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+
+ if (this->eap_auth == NULL)
+ {
+ /* peer not authenticated, nor does it want to use EAP */
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
+ /* initiate EAP authenitcation */
+ eap_type = policy->get_eap_type(policy);
+ status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_payload);
+ message->add_payload(message, (payload_t*)eap_payload);
+ if (status != NEED_MORE)
+ {
+ SIG(IKE_UP_FAILED, "unable to initiate EAP authentication");
+ return FAILED;
+ }
+
+ /* switch to EAP methods */
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_r;
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_auth_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case NO_PROPOSAL_CHOSEN:
+ case SINGLE_PAIR_REQUIRED:
+ case NO_ADDITIONAL_SAS:
+ case INTERNAL_ADDRESS_FAILURE:
+ case FAILED_CP_REQUIRED:
+ case TS_UNACCEPTABLE:
+ case INVALID_SELECTORS:
+ /* these are errors, but are not critical as only the
+ * CHILD_SA won't get build, but IKE_SA establishes anyway */
+ break;
+ default:
+ {
+ if (type < 16383)
+ {
+ SIG(IKE_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "received %N notify",
+ notify_type_names, type);
+ break;
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (process_id(this, message) != SUCCESS ||
+ process_auth(this, message) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ if (this->eap_auth)
+ {
+ /* switch to EAP authentication methods */
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_i;
+ return process_eap_i(this, message);
+ }
+
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_auth_t *this)
+{
+ return IKE_AUTHENTICATE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ if (this->eap_auth)
+ {
+ this->eap_auth->authenticator_interface.destroy(
+ &this->eap_auth->authenticator_interface);
+ }
+
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->peer_authenticated = FALSE;
+ this->eap_auth = NULL;
+ this->eap_payload = NULL;
+ this->ike_sa = ike_sa;
+ if (this->initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_auth_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ if (this->eap_auth)
+ {
+ this->eap_auth->authenticator_interface.destroy(
+ &this->eap_auth->authenticator_interface);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_auth_t *this = malloc_thing(private_ike_auth_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->peer_authenticated = FALSE;
+ this->eap_auth = NULL;
+ this->eap_payload = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h
new file mode 100644
index 000000000..d7326c988
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.h
@@ -0,0 +1,64 @@
+/**
+ * @file ike_auth.h
+ *
+ * @brief Interface ike_auth_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_AUTH_H_
+#define IKE_AUTH_H_
+
+typedef struct ike_auth_t ike_auth_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_auth, authenticates an IKE_SA using authenticators.
+ *
+ * The ike_auth task authenticates the IKE_SA using the IKE_AUTH
+ * exchange. It processes and build IDi and IDr payloads and also
+ * handles AUTH payloads. The AUTH payloads are passed to authenticator_t's,
+ * which do the actual authentication process. If the ike_auth task is used
+ * with EAP authentication, it stays alive over multiple exchanges until
+ * EAP has completed.
+ *
+ * @b Constructors:
+ * - ike_auth_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_auth_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new task of type IKE_AUTHENTICATE.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the initator of an exchange
+ * @return ike_auth task to handle by the task_manager
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_AUTH_H_ */
diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c
new file mode 100644
index 000000000..160600742
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.c
@@ -0,0 +1,370 @@
+/**
+ * @file ike_cert.c
+ *
+ * @brief Implementation of the ike_cert task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_cert.h"
+
+#include <daemon.h>
+#include <sa/ike_sa.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+
+
+typedef struct private_ike_cert_t private_ike_cert_t;
+
+/**
+ * Private members of a ike_cert_t task.
+ */
+struct private_ike_cert_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_cert_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * list of CA cert hashes requested, items point to 20 byte chunk
+ */
+ linked_list_t *cas;
+
+ /**
+ * have we seen a certificate request?
+ */
+ bool certreq_seen;
+};
+
+/**
+ * read certificate requests
+ */
+static void process_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE_REQUEST)
+ {
+ certreq_payload_t *certreq = (certreq_payload_t*)payload;
+ cert_encoding_t encoding;
+ chunk_t keyids, keyid;
+
+ this->certreq_seen = TRUE;
+
+ encoding = certreq->get_cert_encoding(certreq);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certreq payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ keyids = certreq->get_data(certreq);
+
+ while (keyids.len >= HASH_SIZE_SHA1)
+ {
+ keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1);
+ keyid = chunk_clone(keyid);
+ this->cas->insert_last(this->cas, keyid.ptr);
+ keyids = chunk_skip(keyids, HASH_SIZE_SHA1);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * import certificates
+ */
+static void process_certs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE)
+ {
+ cert_encoding_t encoding;
+ x509_t *cert;
+ chunk_t cert_data;
+ bool found;
+ cert_payload_t *cert_payload = (cert_payload_t*)payload;
+
+ encoding = cert_payload->get_cert_encoding(cert_payload);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certificate payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ cert_data = cert_payload->get_data_clone(cert_payload);
+ cert = x509_create_from_chunk(cert_data, 0);
+ if (cert)
+ {
+ if (charon->credentials->verify(charon->credentials,
+ cert, &found))
+ {
+ DBG2(DBG_IKE, "received end entity certificate is trusted, "
+ "added to store");
+ if (!found)
+ {
+ charon->credentials->add_end_certificate(
+ charon->credentials, cert);
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received end entity certificate is not "
+ "trusted, discarded");
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "parsing of received certificate failed, discarded");
+ chunk_free(&cert_data);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * build certificate requests
+ */
+static void build_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ identification_t *ca;
+ certreq_payload_t *certreq;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND)
+ {
+ policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (policy)
+ {
+ ca = policy->get_other_ca(policy);
+
+ if (ca && ca->get_type(ca) != ID_ANY)
+ {
+ certreq = certreq_payload_create_from_cacert(ca);
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+
+ if (certreq)
+ {
+ message->add_payload(message, (payload_t*)certreq);
+ }
+ }
+}
+
+/**
+ * add certificates to message
+ */
+static void build_certs(private_ike_cert_t *this, message_t *message)
+{
+ policy_t *policy;
+ connection_t *connection;
+ x509_t *cert;
+ cert_payload_t *payload;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (policy && policy->get_auth_method(policy) == AUTH_RSA)
+ {
+ switch (connection->get_cert_policy(connection))
+ {
+ case CERT_NEVER_SEND:
+ break;
+ case CERT_SEND_IF_ASKED:
+ if (!this->certreq_seen)
+ {
+ break;
+ }
+ /* FALL */
+ case CERT_ALWAYS_SEND:
+ {
+ /* TODO: respect CA cert request */
+ cert = charon->credentials->get_certificate(charon->credentials,
+ policy->get_my_id(policy));
+ if (cert)
+ {
+ payload = cert_payload_create_from_x509(cert);
+ message->add_payload(message, (payload_t*)payload);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ build_certreqs(this, message);
+ build_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ process_certreqs(this, message);
+ process_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ build_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ build_certs(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ process_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ process_certs(this, message);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_cert_t *this)
+{
+ return IKE_CERT;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->cas->destroy_function(this->cas, free);
+ this->cas = linked_list_create();
+ this->certreq_seen = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_cert_t *this)
+{
+ this->cas->destroy_function(this->cas, free);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_cert_t *this = malloc_thing(private_ike_cert_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->cas = linked_list_create();
+ this->certreq_seen = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h
new file mode 100644
index 000000000..ba0283953
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.h
@@ -0,0 +1,61 @@
+/**
+ * @file ike_cert.h
+ *
+ * @brief Interface ike_cert_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_CERT_H_
+#define IKE_CERT_H_
+
+typedef struct ike_cert_t ike_cert_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_cert, exchanges certificates and
+ * certificate requests.
+ *
+ * @b Constructors:
+ * - ike_cert_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_cert_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_cert task.
+ *
+ * The initiator parameter means the original initiator, not the initiator
+ * of the certificate request.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_cert task to handle by the task_manager
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_CERT_H_ */
diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c
new file mode 100644
index 000000000..ce29b9220
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.c
@@ -0,0 +1,428 @@
+/**
+ * @file ike_config.c
+ *
+ * @brief Implementation of the ike_config task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_config.h"
+
+#include <daemon.h>
+#include <encoding/payloads/cp_payload.h>
+
+typedef struct private_ike_config_t private_ike_config_t;
+
+/**
+ * Private members of a ike_config_t task.
+ */
+struct private_ike_config_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_config_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * associated policy with virtual IP configuration
+ */
+ policy_t *policy;
+
+ /**
+ * virtual ip
+ */
+ host_t *virtual_ip;
+
+ /**
+ * list of DNS servers
+ */
+ linked_list_t *dns;
+};
+
+/**
+ * build configuration payloads and attributes
+ */
+static void build_payloads(private_ike_config_t *this, message_t *message,
+ config_type_t type)
+{
+ cp_payload_t *cp;
+ configuration_attribute_t *ca;
+ chunk_t chunk, prefix;
+
+ if (!this->virtual_ip)
+ {
+ return;
+ }
+
+ cp = cp_payload_create();
+ cp->set_config_type(cp, type);
+
+ ca = configuration_attribute_create();
+
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ }
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ prefix = chunk_alloca(1);
+ *prefix.ptr = 64;
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ chunk = chunk_cata("cc", chunk, prefix);
+ }
+ }
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+
+ /* we currently always add a DNS request if we request an IP */
+ if (this->initiator)
+ {
+ ca = configuration_attribute_create();
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ cp->add_configuration_attribute(cp, ca);
+ }
+ else
+ {
+ host_t *ip;
+ iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ ca = configuration_attribute_create();
+ if (ip->get_family(ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ chunk = ip->get_address(ip);
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+ }
+ iterator->destroy(iterator);
+ }
+ message->add_payload(message, (payload_t*)cp);
+}
+
+/**
+ * process a single configuration attribute
+ */
+static void process_attribute(private_ike_config_t *this,
+ configuration_attribute_t *ca)
+{
+ host_t *ip;
+ chunk_t addr;
+ int family = AF_INET6;
+
+ switch (ca->get_type(ca))
+ {
+ case INTERNAL_IP4_ADDRESS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_ADDRESS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ /* skip prefix byte in IPv6 payload*/
+ if (family == AF_INET6)
+ {
+ addr.len--;
+ }
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip && !this->virtual_ip)
+ {
+ this->virtual_ip = ip;
+ }
+ break;
+ }
+ case INTERNAL_IP4_DNS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_DNS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip)
+ {
+ this->dns->insert_last(this->dns, ip);
+ }
+ break;
+ }
+ case INTERNAL_IP4_NBNS:
+ case INTERNAL_IP6_NBNS:
+ /* TODO */
+ default:
+ DBG1(DBG_IKE, "ignoring %N config attribute",
+ configuration_attribute_type_names,
+ ca->get_type(ca));
+ break;
+ }
+}
+
+/**
+ * Scan for configuration payloads and attributes
+ */
+static void process_payloads(private_ike_config_t *this, message_t *message)
+{
+ iterator_t *iterator, *attributes;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CONFIGURATION)
+ {
+ cp_payload_t *cp = (cp_payload_t*)payload;
+ configuration_attribute_t *ca;
+ switch (cp->get_config_type(cp))
+ {
+ case CFG_REQUEST:
+ case CFG_REPLY:
+ {
+ attributes = cp->create_attribute_iterator(cp);
+ while (attributes->iterate(attributes, (void**)&ca))
+ {
+ process_attribute(this, ca);
+ }
+ attributes->destroy(attributes);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring %N config payload",
+ config_type_names, cp->get_config_type(cp));
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_AUTH &&
+ message->get_payload(message, ID_INITIATOR))
+ {
+ this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ build_payloads(this, message, CFG_REQUEST);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_AUTH &&
+ message->get_payload(message, ID_INITIATOR))
+ {
+ process_payloads(this, message);
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_AUTH &&
+ message->get_payload(message, EXTENSIBLE_AUTHENTICATION) == NULL)
+ {
+ this->policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (this->policy && this->virtual_ip)
+ {
+ host_t *ip;
+
+ DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
+ ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip);
+ if (ip == NULL || ip->is_anyaddr(ip))
+ {
+ DBG1(DBG_IKE, "not assigning a virtual IP to peer");
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip);
+ this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip);
+
+ this->virtual_ip->destroy(this->virtual_ip);
+ this->virtual_ip = ip;
+
+ /* DNS testing values
+ if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ ip->destroy(ip);
+ ip = host_create_from_string("10.3.0.1", 0);
+ this->dns->insert_last(this->dns, ip);
+ ip = host_create_from_string("10.3.0.2", 0);
+ this->dns->insert_last(this->dns, ip);
+ } */
+
+ build_payloads(this, message, CFG_REPLY);
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_AUTH &&
+ !message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+ {
+ host_t *ip;
+
+ DESTROY_IF(this->virtual_ip);
+ this->virtual_ip = NULL;
+
+ process_payloads(this, message);
+
+ if (this->virtual_ip)
+ {
+ this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
+
+ while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ if (!ip->is_anyaddr(ip))
+ {
+ this->ike_sa->add_dns_server(this->ike_sa, ip);
+ }
+ ip->destroy(ip);
+ }
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_config_t *this)
+{
+ return IKE_CONFIG;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+
+ this->ike_sa = ike_sa;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_config_t *this)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_ike_config_t *this = malloc_thing(private_ike_config_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h
new file mode 100644
index 000000000..0c9b961b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.h
@@ -0,0 +1,59 @@
+/**
+ * @file ike_config.h
+ *
+ * @brief Interface ike_config_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_CONFIG_H_
+#define IKE_CONFIG_H_
+
+typedef struct ike_config_t ike_config_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type IKE_CONFIG, sets up a virtual IP and other
+ * configurations for an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_config_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_config_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_config task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy for the initiator, NULL for the responder
+ * @return ike_config task to handle by the task_manager
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* IKE_CONFIG_H_ */
diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c
new file mode 100644
index 000000000..9c4fdac0e
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.c
@@ -0,0 +1,172 @@
+/**
+ * @file ike_delete.c
+ *
+ * @brief Implementation of the ike_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_ike_delete_t private_ike_delete_t;
+
+/**
+ * Private members of a ike_delete_t task.
+ */
+struct private_ike_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * are we responding to a delete, but have initated our own?
+ */
+ bool simultaneous;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_delete_t *this, message_t *message)
+{
+ delete_payload_t *delete_payload;
+
+ delete_payload = delete_payload_create(PROTO_IKE);
+ message->add_payload(message, (payload_t*)delete_payload);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_delete_t *this, message_t *message)
+{
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_delete_t *this, message_t *message)
+{
+ /* we don't even scan the payloads, as the message wouldn't have
+ * come so far without being correct */
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_DELETING:
+ this->simultaneous = TRUE;
+ break;
+ case IKE_ESTABLISHED:
+ DBG1(DBG_IKE, "deleting IKE_SA on request");
+ break;
+ case IKE_REKEYING:
+ DBG1(DBG_IKE, "initiated rekeying, but received delete for IKE_SA");
+ break;
+ default:
+ break;
+ }
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_delete_t *this, message_t *message)
+{
+ if (this->simultaneous)
+ {
+ /* wait for peers response for our delete request, but set a timeout */
+ return SUCCESS;
+ }
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_delete_t *this)
+{
+ return IKE_DELETE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->simultaneous = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_delete_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_delete_t *this = malloc_thing(private_ike_delete_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->simultaneous = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h
new file mode 100644
index 000000000..e8ec5ebbe
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_delete.h
+ *
+ * @brief Interface ike_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_DELETE_H_
+#define IKE_DELETE_H_
+
+typedef struct ike_delete_t ike_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_delete, delete an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if we initiate the delete
+ * @return ike_delete task to handle by the task_manager
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_DELETE_H_ */
diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c
new file mode 100644
index 000000000..1cb05c45c
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.c
@@ -0,0 +1,106 @@
+/**
+ * @file ike_dpd.c
+ *
+ * @brief Implementation of the ike_dpd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_dpd.h"
+
+#include <daemon.h>
+
+
+typedef struct private_ike_dpd_t private_ike_dpd_t;
+
+/**
+ * Private members of a ike_dpd_t task.
+ */
+struct private_ike_dpd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_dpd_t public;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ * Implementation of task_t.process for responder
+ */
+static status_t return_need_more(private_ike_dpd_t *this, message_t *message)
+{
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ * Implementation of task_t.build for responder
+ */
+static status_t return_success(private_ike_dpd_t *this, message_t *message)
+{
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_dpd_t *this)
+{
+ return IKE_DEADPEER;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa)
+{
+
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_dpd_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_dpd_t *ike_dpd_create(bool initiator)
+{
+ private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_success;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_success;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more;
+ }
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h
new file mode 100644
index 000000000..531b0502d
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.h
@@ -0,0 +1,58 @@
+/**
+ * @file ike_dpd.h
+ *
+ * @brief Interface ike_dpd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_DPD_H_
+#define IKE_DPD_H_
+
+typedef struct ike_dpd_t ike_dpd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_dpd, detects dead peers.
+ *
+ * The DPD task actually does nothing, as a DPD has no associated payloads.
+ *
+ * @b Constructors:
+ * - ike_dpd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_dpd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_dpd task.
+ *
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_dpd task to handle by the task_manager
+ */
+ike_dpd_t *ike_dpd_create(bool initiator);
+
+#endif /* IKE_DPD_H_ */
diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c
new file mode 100644
index 000000000..0b493666a
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.c
@@ -0,0 +1,598 @@
+/**
+ * @file ike_init.c
+ *
+ * @brief Implementation of the ike_init task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_init.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+/** maximum retries to do with cookies/other dh groups */
+#define MAX_RETRIES 5
+
+typedef struct private_ike_init_t private_ike_init_t;
+
+/**
+ * Private members of a ike_init_t task.
+ */
+struct private_ike_init_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_init_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Connection established by this IKE_SA
+ */
+ connection_t *connection;
+
+ /**
+ * diffie hellman group to use
+ */
+ diffie_hellman_group_t dh_group;
+
+ /**
+ * Diffie hellman object used to generate public DH value.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * Negotiated proposal used for IKE_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * Old IKE_SA which gets rekeyed
+ */
+ ike_sa_t *old_sa;
+
+ /**
+ * cookie received from responder
+ */
+ chunk_t cookie;
+
+ /**
+ * retries done so far after failure (cookie or bad dh group)
+ */
+ u_int retry;
+};
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_ike_init_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ke_payload_t *ke_payload;
+ nonce_payload_t *nonce_payload;
+ linked_list_t *proposal_list;
+ ike_sa_id_t *id;
+ proposal_t *proposal;
+ iterator_t *iterator;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (this->initiator)
+ {
+ proposal_list = this->connection->get_proposals(this->connection);
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ iterator = proposal_list->create_iterator(proposal_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&proposal))
+ {
+ proposal->set_spi(proposal, id->get_initiator_spi(id));
+ }
+ iterator->destroy(iterator);
+ }
+
+ sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+ proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
+ }
+ else
+ {
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
+ }
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+ message->add_payload(message, (payload_t*)nonce_payload);
+
+ ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
+ message->add_payload(message, (payload_t*)ke_payload);
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_ike_init_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ {
+ sa_payload_t *sa_payload = (sa_payload_t*)payload;
+ linked_list_t *proposal_list;
+
+ proposal_list = sa_payload->get_proposals(sa_payload);
+ this->proposal = this->connection->select_proposal(
+ this->connection, proposal_list);
+ proposal_list->destroy_offset(proposal_list,
+ offsetof(proposal_t, destroy));
+ break;
+ }
+ case KEY_EXCHANGE:
+ {
+ ke_payload_t *ke_payload = (ke_payload_t*)payload;
+ diffie_hellman_group_t dh_group;
+ chunk_t key_data;
+
+ dh_group = ke_payload->get_dh_group_number(ke_payload);
+
+ if (this->initiator)
+ {
+ if (dh_group != this->dh_group)
+ {
+ DBG1(DBG_IKE, "received a DH group not requested (%N)",
+ diffie_hellman_group_names, dh_group);
+ break;
+ }
+ }
+ else
+ {
+ this->dh_group = dh_group;
+ if (!this->connection->check_dh_group(this->connection,
+ dh_group))
+ {
+ break;
+ }
+ this->diffie_hellman = diffie_hellman_create(dh_group);
+ }
+ if (this->diffie_hellman)
+ {
+ key_data = ke_payload->get_key_exchange_data(ke_payload);
+ this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data);
+ }
+ break;
+ }
+ case NONCE:
+ {
+ nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
+ this->other_nonce = nonce_payload->get_nonce(nonce_payload);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+ status_t status;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_START, "initiating IKE_SA to %H",
+ this->connection->get_other_host(this->connection));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ if (this->retry++ >= MAX_RETRIES)
+ {
+ SIG(IKE_UP_FAILED, "giving up after %d retries", MAX_RETRIES);
+ return FAILED;
+ }
+
+ /* if the DH group is set via use_dh_group(), we already have a DH object */
+ if (!this->diffie_hellman)
+ {
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+ if (this->diffie_hellman == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured DH group %N not supported",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+ }
+
+ /* generate nonce only when we are trying the first time */
+ if (this->my_nonce.ptr == NULL)
+ {
+ randomizer = randomizer_create();
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "error generating random nonce value");
+ return FAILED;
+ }
+ }
+
+ if (this->cookie.ptr)
+ {
+ message->add_notify(message, FALSE, COOKIE, this->cookie);
+ }
+
+ build_payloads(this, message);
+
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA",
+ message->get_source(message));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ randomizer = randomizer_create();
+ if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ }
+ randomizer->destroy(randomizer);
+
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+
+ /* check if we have everything we need */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "received proposals inacceptable");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ chunk_t chunk;
+ u_int16_t dh_enc;
+
+ SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)",
+ diffie_hellman_group_names, this->dh_group);
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ dh_enc = htons(this->dh_group);
+ chunk.ptr = (u_int8_t*)&dh_enc;
+ chunk.len = sizeof(dh_enc);
+ message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk);
+ DBG1(DBG_IKE, "requesting DH group %N",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+
+
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ /* Apply SPI if we are rekeying */
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+ iterator_t *iterator;
+ payload_t *payload;
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case INVALID_KE_PAYLOAD:
+ {
+ chunk_t data;
+ diffie_hellman_group_t old_dh_group;
+
+ old_dh_group = this->dh_group;
+ data = notify->get_notification_data(notify);
+ this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+
+ DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested"
+ " %N", diffie_hellman_group_names, old_dh_group,
+ diffie_hellman_group_names, this->dh_group);
+ if (!this->connection->check_dh_group(this->connection,
+ this->dh_group))
+ {
+ DBG1(DBG_IKE, "requested DH group %N not acceptable, "
+ "giving up", diffie_hellman_group_names,
+ this->dh_group);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+
+ this->ike_sa->reset(this->ike_sa);
+
+ iterator->destroy(iterator);
+ return NEED_MORE;
+ }
+ case NAT_DETECTION_SOURCE_IP:
+ case NAT_DETECTION_DESTINATION_IP:
+ /* skip, handled in ike_natd_t */
+ break;
+ case COOKIE:
+ {
+ chunk_free(&this->cookie);
+ this->cookie = chunk_clone(notify->get_notification_data(notify));
+ this->ike_sa->reset(this->ike_sa);
+ iterator->destroy(iterator);
+ DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
+ return NEED_MORE;
+ }
+ default:
+ {
+ if (type < 16383)
+ {
+ SIG(IKE_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "received %N notify",
+ notify_type_names, type);
+ break;
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ /* check if we have everything */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "peers proposal selection invalid");
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "peers DH group selection invalid");
+ return FAILED;
+ }
+
+ /* Apply SPI if we are rekeying */
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys for a new SA */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_init_t *this)
+{
+ return IKE_INIT;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static chunk_t get_lower_nonce(private_ike_init_t *this)
+{
+ if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr,
+ min(this->my_nonce.len, this->other_nonce.len)) < 0)
+ {
+ return this->my_nonce;
+ }
+ else
+ {
+ return this->other_nonce;
+ }
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->other_nonce);
+
+ this->ike_sa = ike_sa;
+ this->proposal = NULL;
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_init_t *this)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ chunk_free(&this->cookie);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
+{
+ private_ike_init_t *this = malloc_thing(private_ike_init_t);
+
+ this->public.get_lower_nonce = (chunk_t(*)(ike_init_t*))get_lower_nonce;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->dh_group = MODP_NONE;
+ this->diffie_hellman = NULL;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->cookie = chunk_empty;
+ this->proposal = NULL;
+ this->connection = NULL;
+ this->old_sa = old_sa;
+ this->retry = 0;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h
new file mode 100644
index 000000000..f60c096e8
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.h
@@ -0,0 +1,68 @@
+/**
+ * @file ike_init.h
+ *
+ * @brief Interface ike_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_INIT_H_
+#define IKE_INIT_H_
+
+typedef struct ike_init_t ike_init_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_INIT, creates an IKE_SA without authentication.
+ *
+ * The authentication of is handle in the ike_auth task.
+ *
+ * @b Constructors:
+ * - ike_init_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_init_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Get the lower of the two nonces, used for rekey collisions.
+ *
+ * @param this calling object
+ * @return lower nonce
+ */
+ chunk_t (*get_lower_nonce) (ike_init_t *this);
+};
+
+/**
+ * @brief Create a new IKE_INIT task.
+ *
+ * @param ike_sa IKE_SA this task works for (new one when rekeying)
+ * @param initiator TRUE if thask is the original initator
+ * @param old_sa old IKE_SA when we are rekeying
+ * @return ike_init task to handle by the task_manager
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa);
+
+#endif /* IKE_INIT_H_ */
diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c
new file mode 100644
index 000000000..50b5d652b
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.c
@@ -0,0 +1,371 @@
+/**
+ * @file ike_natd.c
+ *
+ * @brief Implementation of the ike_natd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_natd.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_ike_natd_t private_ike_natd_t;
+
+/**
+ * Private members of a ike_natd_t task.
+ */
+struct private_ike_natd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_natd_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Hasher used to build NAT detection hashes
+ */
+ hasher_t *hasher;
+
+ /**
+ * Did we process any NAT detection notifys for a source address?
+ */
+ bool src_seen;
+
+ /**
+ * Did we process any NAT detection notifys for a destination address?
+ */
+ bool dst_seen;
+
+ /**
+ * Have we found a matching source address NAT hash?
+ */
+ bool src_matched;
+
+ /**
+ * Have we found a matching destination address NAT hash?
+ */
+ bool dst_matched;
+};
+
+
+/**
+ * Build NAT detection hash for a host
+ */
+static chunk_t generate_natd_hash(private_ike_natd_t *this,
+ ike_sa_id_t *ike_sa_id, host_t *host)
+{
+ chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
+ chunk_t natd_hash;
+ u_int64_t spi_i, spi_r;
+ u_int16_t port;
+
+ /* prepare all requred chunks */
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi_i_chunk.ptr = (void*)&spi_i;
+ spi_i_chunk.len = sizeof(spi_i);
+ spi_r_chunk.ptr = (void*)&spi_r;
+ spi_r_chunk.len = sizeof(spi_r);
+ port = htons(host->get_port(host));
+ port_chunk.ptr = (void*)&port;
+ port_chunk.len = sizeof(port);
+ addr_chunk = host->get_address(host);
+
+ /* natd_hash = SHA1( spi_i | spi_r | address | port ) */
+ natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
+ this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash);
+ DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
+ DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
+
+ chunk_free(&natd_chunk);
+ return natd_hash;
+}
+
+/**
+ * Build a NAT detection notify payload.
+ */
+static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
+ notify_type_t type, host_t *host)
+{
+ chunk_t hash;
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ notify = notify_payload_create();
+ notify->set_notify_type(notify, type);
+ hash = generate_natd_hash(this, ike_sa_id, host);
+ notify->set_notification_data(notify, hash);
+ chunk_free(&hash);
+
+ return notify;
+}
+
+/**
+ * read notifys from message and evaluate them
+ */
+static void process_payloads(private_ike_natd_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ notify_payload_t *notify;
+ chunk_t hash, src_hash, dst_hash;
+ ike_sa_id_t *ike_sa_id;
+ host_t *me, *other;
+
+ /* Precompute NAT-D hashes for incoming NAT notify comparison */
+ ike_sa_id = message->get_ike_sa_id(message);
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ dst_hash = generate_natd_hash(this, ike_sa_id, me);
+ src_hash = generate_natd_hash(this, ike_sa_id, other);
+
+ DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash);
+ DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash);
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+ notify = (notify_payload_t*)payload;
+ switch (notify->get_notify_type(notify))
+ {
+ case NAT_DETECTION_DESTINATION_IP:
+ {
+ this->dst_seen = TRUE;
+ if (!this->dst_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG3(DBG_IKE, "received dst_hash %B", &hash);
+ if (chunk_equals(hash, dst_hash))
+ {
+ this->dst_matched = TRUE;
+ }
+ }
+ break;
+ }
+ case NAT_DETECTION_SOURCE_IP:
+ {
+ this->src_seen = TRUE;
+ if (!this->src_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG3(DBG_IKE, "received src_hash %B", &hash);
+ if (chunk_equals(hash, src_hash))
+ {
+ this->src_matched = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ chunk_free(&src_hash);
+ chunk_free(&dst_hash);
+
+ if (this->src_seen && this->dst_seen)
+ {
+ if (!this->dst_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, TRUE);
+ }
+ if (!this->src_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, FALSE);
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ host_t *me, *other;
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ me->set_port(me, IKEV2_NATT_PORT);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ other->set_port(other, IKEV2_NATT_PORT);
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ linked_list_t *list;
+ host_t *host;
+
+ /* include one notify if our address is defined, all addresses otherwise */
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ if (host->is_anyaddr(host))
+ {
+ /* TODO: we could get the src address from netlink!? */
+ list = charon->kernel_interface->create_address_list(charon->kernel_interface);
+ while (list->remove_first(list, (void**)&host) == SUCCESS)
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ host->destroy(host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ list->destroy(list);
+ }
+ else
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ host_t *me, *other;
+
+ /* only add notifies on successfull responses. */
+ if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
+ {
+ return SUCCESS;
+ }
+
+ if (this->src_seen && this->dst_seen)
+ {
+ /* initiator seems to support NAT detection, add response */
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
+ message->add_payload(message, (payload_t*)notify);
+
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_natd_t *this)
+{
+ return IKE_NATD;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_natd_t *this)
+{
+ this->hasher->destroy(this->hasher);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_natd_t *this = malloc_thing(private_ike_natd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->hasher = hasher_create(HASH_SHA1);
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h
new file mode 100644
index 000000000..8d0cb58b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_natd.h
+ *
+ * @brief Interface ike_natd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_NATD_H_
+#define IKE_NATD_H_
+
+typedef struct ike_natd_t ike_natd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange.
+ *
+ * @b Constructors:
+ * - ike_natd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_natd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_natd task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_natd task to handle by the task_manager
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_NATD_H_ */
diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c
new file mode 100644
index 000000000..a33e7ee34
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.c
@@ -0,0 +1,329 @@
+/**
+ * @file ike_rekey.c
+ *
+ * @brief Implementation of the ike_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_rekey.h"
+
+#include <daemon.h>
+#include <encoding/payloads/notify_payload.h>
+#include <sa/tasks/ike_init.h>
+#include <queues/jobs/delete_ike_sa_job.h>
+#include <queues/jobs/rekey_ike_sa_job.h>
+
+
+typedef struct private_ike_rekey_t private_ike_rekey_t;
+
+/**
+ * Private members of a ike_rekey_t task.
+ */
+struct private_ike_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * New IKE_SA which replaces the current one
+ */
+ ike_sa_t *new_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the IKE_INIT task which is reused to simplify rekeying
+ */
+ ike_init_t *ike_init;
+
+ /**
+ * colliding task detected by the task manager
+ */
+ task_t *collision;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+
+ this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ TRUE);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
+ this->ike_init->task.build(&this->ike_init->task, message);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+ {
+ DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
+ return NEED_MORE;
+ }
+
+ iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ switch (child_sa->get_state(child_sa))
+ {
+ case CHILD_CREATED:
+ case CHILD_REKEYING:
+ case CHILD_DELETING:
+ /* we do not allow rekeying while we have children in-progress */
+ DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
+ iterator->destroy(iterator);
+ return NEED_MORE;
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ FALSE);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa);
+ this->ike_init->task.process(&this->ike_init->task, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_rekey_t *this, message_t *message)
+{
+ if (this->new_sa == NULL)
+ {
+ /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
+ {
+ return SUCCESS;
+ }
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_rekey_t *this, message_t *message)
+{
+ job_t *job;
+ ike_sa_id_t *to_delete;
+
+ if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED)
+ {
+ /* rekeying failed, fallback to old SA */
+ if (!(this->collision &&
+ this->collision->get_type(this->collision) == IKE_DELETE))
+ {
+ job_t *job;
+ u_int32_t retry = charon->configuration->get_retry_interval(
+ charon->configuration);
+ job = (job_t*)rekey_ike_sa_job_create(
+ this->ike_sa->get_id(this->ike_sa), FALSE);
+ DBG1(DBG_IKE, "IKE_SA rekeying failed, "
+ "trying again in %d seconds", retry);
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ charon->event_queue->add_relative(charon->event_queue, job, retry * 1000);
+ }
+ return SUCCESS;
+ }
+
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+ to_delete = this->ike_sa->get_id(this->ike_sa);
+
+ /* check for collisions */
+ if (this->collision &&
+ this->collision->get_type(this->collision) == IKE_REKEY)
+ {
+ chunk_t this_nonce, other_nonce;
+ host_t *host;
+ private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision;
+
+ this_nonce = this->ike_init->get_lower_nonce(this->ike_init);
+ other_nonce = other->ike_init->get_lower_nonce(other->ike_init);
+
+ /* if we have the lower nonce, delete rekeyed SA. If not, delete
+ * the redundant. */
+ if (memcmp(this_nonce.ptr, other_nonce.ptr,
+ min(this_nonce.len, other_nonce.len)) < 0)
+ {
+ DBG1(DBG_IKE, "IKE_SA rekey collision won, deleting rekeyed IKE_SA");
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant IKE_SA");
+ /* apply host for a proper delete */
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ this->new_sa->set_my_host(this->new_sa, host->clone(host));
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ this->new_sa->set_other_host(this->new_sa, host->clone(host));
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ to_delete = this->new_sa->get_id(this->new_sa);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ /* inherit to other->new_sa in destroy() */
+ this->new_sa = other->new_sa;
+ other->new_sa = NULL;
+ }
+ }
+
+ job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_rekey_t *this)
+{
+ return IKE_REKEY;
+}
+
+static void collide(private_ike_rekey_t* this, task_t *other)
+{
+ DESTROY_IF(this->collision);
+ this->collision = other;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
+{
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ if (this->new_sa)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+ DESTROY_IF(this->collision);
+
+ this->collision = NULL;
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_rekey_t *this)
+{
+ if (this->new_sa)
+ {
+ if (this->new_sa->get_state(this->new_sa) == IKE_ESTABLISHED &&
+ this->new_sa->inherit(this->new_sa, this->ike_sa) != DESTROY_ME)
+ {
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+ }
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ DESTROY_IF(this->collision);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t);
+
+ this->public.collide = (void(*)(ike_rekey_t*,task_t*))collide;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+ this->initiator = initiator;
+ this->collision = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h
new file mode 100644
index 000000000..125422efd
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.h
@@ -0,0 +1,69 @@
+/**
+ * @file ike_rekey.h
+ *
+ * @brief Interface ike_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_REKEY_H_
+#define IKE_REKEY_H_
+
+typedef struct ike_rekey_t ike_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_REKEY, rekey an established IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Register a rekeying task which collides with this one.
+ *
+ * If two peers initiate rekeying at the same time, the collision must
+ * be handled gracefully. The task manager is aware of what exchanges
+ * are going on and notifies the outgoing task by passing the incoming.
+ *
+ * @param this task initated by us
+ * @param other incoming task
+ */
+ void (*collide)(ike_rekey_t* this, task_t *other);
+};
+
+/**
+ * @brief Create a new IKE_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE for initiator, FALSE for responder
+ * @return IKE_REKEY task to handle by the task_manager
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_REKEY_H_ */
diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c
new file mode 100644
index 000000000..68d8ebf0c
--- /dev/null
+++ b/src/charon/sa/tasks/task.c
@@ -0,0 +1,38 @@
+/**
+ * @file task.c
+ *
+ * @brief Enum values for task types
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "task.h"
+
+ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
+ "IKE_INIT",
+ "IKE_NATD",
+ "IKE_AUTHENTICATE",
+ "IKE_CERT",
+ "IKE_CONFIG",
+ "IKE_DPD",
+ "IKE_REKEY",
+ "IKE_DELETE",
+ "IKE_DEADPEER",
+ "CHILD_CREATE",
+ "CHILD_DELETE",
+ "CHILD_REKEY",
+);
diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h
new file mode 100644
index 000000000..128d7db4a
--- /dev/null
+++ b/src/charon/sa/tasks/task.h
@@ -0,0 +1,151 @@
+/**
+ * @file task.h
+ *
+ * @brief Interface task_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TASK_H_
+#define TASK_H_
+
+typedef enum task_type_t task_type_t;
+typedef struct task_t task_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <encoding/message.h>
+
+/**
+ * @brief Different kinds of tasks.
+ *
+ * @ingroup tasks
+ */
+enum task_type_t {
+ /** establish an unauthenticated IKE_SA */
+ IKE_INIT,
+ /** detect NAT situation */
+ IKE_NATD,
+ /** authenticate the initiated IKE_SA */
+ IKE_AUTHENTICATE,
+ /** exchange certificates and requests */
+ IKE_CERT,
+ /** Configuration payloads, virtual IP and such */
+ IKE_CONFIG,
+ /** DPD detection */
+ IKE_DEADPEER,
+ /** rekey an IKE_SA */
+ IKE_REKEY,
+ /** delete an IKE_SA */
+ IKE_DELETE,
+ /** liveness check */
+ IKE_DPD,
+ /** establish a CHILD_SA within an IKE_SA */
+ CHILD_CREATE,
+ /** delete an established CHILD_SA */
+ CHILD_DELETE,
+ /** rekey an CHILD_SA */
+ CHILD_REKEY,
+};
+
+/**
+ * enum names for task_type_t.
+ */
+extern enum_name_t *task_type_names;
+
+/**
+ * @brief Interface for a task, an operation handled within exchanges.
+ *
+ * A task is an elemantary operation. It may be handled by a single or by
+ * multiple exchanges. An exchange may even complete multiple tasks.
+ * A task has a build() and an process() operation. The build() operation
+ * creates payloads and adds it to the message. The process() operation
+ * inspects a message and handles its payloads. An initiator of an exchange
+ * first calls build() to build the request, and processes the response message
+ * with the process() method.
+ * A responder does the opposite; it calls process() first to handle an incoming
+ * request and secondly calls build() to build an appropriate response.
+ * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates
+ * that the task completed, even when the task completed unsuccesfully. The
+ * manager then removes the task from the list. A NEED_MORE is returned when
+ * the task needs further build()/process() calls to complete, the manager
+ * leaves the taks in the queue. A returned FAILED indicates a critical failure.
+ * The manager closes the IKE_SA whenever a task returns FAILED.
+ *
+ * @b Constructors:
+ * - None, use implementations specific constructors
+ *
+ * @ingroup tasks
+ */
+struct task_t {
+
+ /**
+ * @brief Build a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*build) (task_t *this, message_t *message);
+
+ /**
+ * @brief Process a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to read payloads from
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*process) (task_t *this, message_t *message);
+
+ /**
+ * @brief Get the type of the task implementation.
+ *
+ * @param this calling object
+ */
+ task_type_t (*get_type) (task_t *this);
+
+ /**
+ * @brief Migrate a task to a new IKE_SA.
+ *
+ * After migrating a task, it goes back to a state where it can be
+ * used again to initate an exchange. This is useful when a task
+ * has to get migrated to a new IKE_SA.
+ * A special usage is when a INVALID_KE_PAYLOAD is received. A call
+ * to reset resets the task, but uses another DH group for the next
+ * try.
+ * The ike_sa is the new IKE_SA this task belongs to and operates on.
+ *
+ * @param this calling object
+ * @param ike_sa new IKE_SA this task works for
+ */
+ void (*migrate) (task_t *this, ike_sa_t *ike_sa);
+
+ /**
+ * @brief Destroys a task_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_t *this);
+};
+
+#endif /* TASK_H_ */