aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/sa')
-rw-r--r--src/charon/sa/Makefile.sa37
-rw-r--r--src/charon/sa/authenticator.c405
-rw-r--r--src/charon/sa/authenticator.h138
-rw-r--r--src/charon/sa/child_sa.c590
-rw-r--r--src/charon/sa/child_sa.h149
-rw-r--r--src/charon/sa/ike_sa.c1199
-rw-r--r--src/charon/sa/ike_sa.h462
-rw-r--r--src/charon/sa/ike_sa_id.c185
-rw-r--r--src/charon/sa/ike_sa_id.h146
-rw-r--r--src/charon/sa/ike_sa_manager.c843
-rw-r--r--src/charon/sa/ike_sa_manager.h194
-rw-r--r--src/charon/sa/states/Makefile.states43
-rw-r--r--src/charon/sa/states/ike_auth_requested.c671
-rw-r--r--src/charon/sa/states/ike_auth_requested.h72
-rw-r--r--src/charon/sa/states/ike_sa_established.c239
-rw-r--r--src/charon/sa/states/ike_sa_established.h64
-rw-r--r--src/charon/sa/states/ike_sa_init_requested.c798
-rw-r--r--src/charon/sa/states/ike_sa_init_requested.h68
-rw-r--r--src/charon/sa/states/ike_sa_init_responded.c695
-rw-r--r--src/charon/sa/states/ike_sa_init_responded.h73
-rw-r--r--src/charon/sa/states/initiator_init.c360
-rw-r--r--src/charon/sa/states/initiator_init.h84
-rw-r--r--src/charon/sa/states/responder_init.c568
-rw-r--r--src/charon/sa/states/responder_init.h68
-rw-r--r--src/charon/sa/states/state.c37
-rw-r--r--src/charon/sa/states/state.h166
26 files changed, 8354 insertions, 0 deletions
diff --git a/src/charon/sa/Makefile.sa b/src/charon/sa/Makefile.sa
new file mode 100644
index 000000000..825c19959
--- /dev/null
+++ b/src/charon/sa/Makefile.sa
@@ -0,0 +1,37 @@
+# Copyright (C) 2005 Jan Hutter, 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.
+#
+
+SA_DIR= $(CHARON_DIR)sa/
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa_id.o
+$(BUILD_DIR)ike_sa_id.o : $(SA_DIR)ike_sa_id.c $(SA_DIR)ike_sa_id.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa_manager.o
+$(BUILD_DIR)ike_sa_manager.o : $(SA_DIR)ike_sa_manager.c $(SA_DIR)ike_sa_manager.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa.o
+$(BUILD_DIR)ike_sa.o : $(SA_DIR)ike_sa.c $(SA_DIR)ike_sa.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)authenticator.o
+$(BUILD_DIR)authenticator.o : $(SA_DIR)authenticator.c $(SA_DIR)authenticator.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)child_sa.o
+$(BUILD_DIR)child_sa.o : $(SA_DIR)child_sa.c $(SA_DIR)child_sa.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+include $(SA_DIR)states/Makefile.states \ No newline at end of file
diff --git a/src/charon/sa/authenticator.c b/src/charon/sa/authenticator.c
new file mode 100644
index 000000000..3aeb8795f
--- /dev/null
+++ b/src/charon/sa/authenticator.c
@@ -0,0 +1,405 @@
+/**
+ * @file authenticator.c
+ *
+ * @brief Implementation of authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 <daemon.h>
+
+/**
+ * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ */
+#define IKEV2_KEY_PAD "Key Pad for IKEv2"
+
+
+typedef struct private_authenticator_t private_authenticator_t;
+
+/**
+ * Private data of an authenticator_t object.
+ */
+struct private_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ authenticator_t public;
+
+ /**
+ * Assigned IKE_SA. Needed to get objects of type prf_t and logger_t.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * PRF taken from the IKE_SA.
+ */
+ prf_t *prf;
+
+ /**
+ * A logger for.
+ *
+ * Using logger of IKE_SA.
+ */
+ logger_t *logger;
+
+ /**
+ * @brief Creates the octets which are signed (RSA) or MACed (shared secret) as described in section
+ * 2.15 of RFC.
+ *
+ * @param this calling object
+ * @param last_message the last message to include in created octets
+ * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response)
+ * @param other_nonce Nonce data received from other peer
+ * @param my_id id_payload_t object representing an ID payload
+ * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise
+ * @return octets as described in section 2.15. Memory gets allocated and has to get
+ * destroyed by caller.
+ */
+ chunk_t (*allocate_octets) (private_authenticator_t *this,
+ chunk_t last_message,
+ chunk_t other_nonce,
+ id_payload_t *my_id,
+ bool initiator);
+
+ /**
+ * @brief Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ *
+ * @param this calling object
+ * @param last_message the last message
+ * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response)
+ * @param nonce Nonce data to include in auth data compution
+ * @param id_payload id_payload_t object representing an ID payload
+ * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise
+ * @param shared_secret shared secret as chunk_t. If shared secret is a string,
+ * the NULL termination is not included.
+ * @return AUTH data as dscribed in section 2.15 for
+ * AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ * Memory gets allocated and has to get destroyed by caller.
+ */
+ chunk_t (*build_preshared_secret_signature) (private_authenticator_t *this,
+ chunk_t last_message,
+ chunk_t nonce,
+ id_payload_t *id_payload,
+ bool initiator,
+ chunk_t preshared_secret);
+};
+
+/**
+ * Implementation of private_authenticator_t.allocate_octets.
+ */
+static chunk_t allocate_octets(private_authenticator_t *this,
+ chunk_t last_message,
+ chunk_t other_nonce,
+ id_payload_t *my_id,
+ bool initiator)
+{
+ prf_t *prf;
+ chunk_t id_chunk = my_id->get_data(my_id);
+ u_int8_t id_with_header[4 + id_chunk.len];
+ /*
+ * IKEv2 for linux (http://sf.net/projects/ikev2/)
+ * is not compatible with IKEv2 Draft and so not compatible with this
+ * implementation, cause AUTH data are computed without
+ * ID type and the three reserved bytes.
+ */
+ chunk_t id_with_header_chunk = {ptr:id_with_header, len: sizeof(id_with_header)};
+ u_int8_t *current_pos;
+ chunk_t octets;
+
+ id_with_header[0] = my_id->get_id_type(my_id);
+ id_with_header[1] = 0x00;
+ id_with_header[2] = 0x00;
+ id_with_header[3] = 0x00;
+ memcpy(id_with_header + 4,id_chunk.ptr,id_chunk.len);
+
+ if (initiator)
+ {
+ prf = this->ike_sa->get_prf_auth_i(this->ike_sa);
+ }
+ else
+ {
+ prf = this->ike_sa->get_prf_auth_r(this->ike_sa);
+ }
+
+ /* 4 bytes are id type and reserved fields of id payload */
+ octets.len = last_message.len + other_nonce.len + prf->get_block_size(prf);
+ octets.ptr = malloc(octets.len);
+ current_pos = octets.ptr;
+ memcpy(current_pos,last_message.ptr,last_message.len);
+ current_pos += last_message.len;
+ memcpy(current_pos,other_nonce.ptr,other_nonce.len);
+ current_pos += other_nonce.len;
+ prf->get_bytes(prf, id_with_header_chunk, current_pos);
+
+ this->logger->log_chunk(this->logger,RAW | LEVEL2, "Octets (Mesage + Nonce + prf(Sk_px,Idx)",octets);
+ return octets;
+}
+
+/**
+ * Implementation of private_authenticator_t.build_preshared_secret_signature.
+ */
+static chunk_t build_preshared_secret_signature(private_authenticator_t *this,
+ chunk_t last_message,
+ chunk_t nonce,
+ id_payload_t *id_payload,
+ bool initiator,
+ chunk_t preshared_secret)
+{
+ chunk_t key_pad = {ptr: IKEV2_KEY_PAD, len:strlen(IKEV2_KEY_PAD)};
+ u_int8_t key_buffer[this->prf->get_block_size(this->prf)];
+ chunk_t key = {ptr: key_buffer, len: sizeof(key_buffer)};
+ chunk_t auth_data;
+
+ chunk_t octets = this->allocate_octets(this,last_message,nonce,id_payload,initiator);
+
+ /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
+ this->prf->set_key(this->prf, preshared_secret);
+ this->prf->get_bytes(this->prf, key_pad, key_buffer);
+ this->prf->set_key(this->prf, key);
+ this->prf->allocate_bytes(this->prf, octets, &auth_data);
+ chunk_free(&octets);
+ this->logger->log_chunk(this->logger,RAW | LEVEL2, "Authenticated data",auth_data);
+
+ return auth_data;
+}
+
+/**
+ * Implementation of authenticator_t.verify_auth_data.
+ */
+static status_t verify_auth_data (private_authenticator_t *this,
+ auth_payload_t *auth_payload,
+ chunk_t last_received_packet,
+ chunk_t my_nonce,
+ id_payload_t *other_id_payload,
+ bool initiator)
+{
+ switch(auth_payload->get_auth_method(auth_payload))
+ {
+ case SHARED_KEY_MESSAGE_INTEGRITY_CODE:
+ {
+ identification_t *other_id = other_id_payload->get_identification(other_id_payload);
+ chunk_t auth_data = auth_payload->get_data(auth_payload);
+ chunk_t preshared_secret;
+ status_t status;
+
+ status = charon->credentials->get_shared_secret(charon->credentials,
+ other_id,
+ &preshared_secret);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s",
+ other_id->get_string(other_id));
+ other_id->destroy(other_id);
+ return status;
+ }
+
+ chunk_t my_auth_data = this->build_preshared_secret_signature(this,
+ last_received_packet,
+ my_nonce,
+ other_id_payload,
+ initiator,
+ preshared_secret);
+ chunk_free(&preshared_secret);
+
+ if (auth_data.len != my_auth_data.len)
+ {
+ chunk_free(&my_auth_data);
+ status = FAILED;
+ }
+ else if (memcmp(auth_data.ptr,my_auth_data.ptr, my_auth_data.len) == 0)
+ {
+ this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret successful",
+ other_id->get_string(other_id));
+ status = SUCCESS;
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret failed",
+ other_id->get_string(other_id));
+ status = FAILED;
+ }
+ other_id->destroy(other_id);
+ chunk_free(&my_auth_data);
+ return status;
+ }
+ case RSA_DIGITAL_SIGNATURE:
+ {
+ identification_t *other_id = other_id_payload->get_identification(other_id_payload);
+ rsa_public_key_t *public_key;
+ status_t status;
+ chunk_t octets, auth_data;
+
+ auth_data = auth_payload->get_data(auth_payload);
+
+ public_key = charon->credentials->get_rsa_public_key(charon->credentials,
+ other_id);
+ if (public_key == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "No RSA public key found for %s",
+ other_id->get_string(other_id));
+ other_id->destroy(other_id);
+ return NOT_FOUND;
+ }
+
+ octets = this->allocate_octets(this,last_received_packet, my_nonce,other_id_payload, initiator);
+
+ status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data);
+ if (status == SUCCESS)
+ {
+ this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA successful",
+ other_id->get_string(other_id));
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA failed",
+ other_id->get_string(other_id));
+ }
+
+ public_key->destroy(public_key);
+ other_id->destroy(other_id);
+ chunk_free(&octets);
+ return status;
+ }
+ default:
+ {
+ return NOT_SUPPORTED;
+ }
+ }
+}
+
+/**
+ * Implementation of authenticator_t.compute_auth_data.
+ */
+static status_t compute_auth_data (private_authenticator_t *this,
+ auth_payload_t **auth_payload,
+ chunk_t last_sent_packet,
+ chunk_t other_nonce,
+ id_payload_t *my_id_payload,
+ bool initiator)
+{
+ connection_t *connection = this->ike_sa->get_connection(this->ike_sa);
+
+ switch(connection->get_auth_method(connection))
+ {
+ case SHARED_KEY_MESSAGE_INTEGRITY_CODE:
+ {
+ identification_t *my_id = my_id_payload->get_identification(my_id_payload);
+ chunk_t preshared_secret;
+ status_t status;
+ chunk_t auth_data;
+
+ status = charon->credentials->get_shared_secret(charon->credentials,
+ my_id,
+ &preshared_secret);
+
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s",
+ my_id->get_string(my_id));
+ my_id->destroy(my_id);
+ return status;
+ }
+ my_id->destroy(my_id);
+
+ auth_data = this->build_preshared_secret_signature(this, last_sent_packet, other_nonce,
+ my_id_payload, initiator, preshared_secret);
+ chunk_free(&preshared_secret);
+ *auth_payload = auth_payload_create();
+ (*auth_payload)->set_auth_method(*auth_payload, SHARED_KEY_MESSAGE_INTEGRITY_CODE);
+ (*auth_payload)->set_data(*auth_payload, auth_data);
+
+ chunk_free(&auth_data);
+ return SUCCESS;
+ }
+ case RSA_DIGITAL_SIGNATURE:
+ {
+ identification_t *my_id = my_id_payload->get_identification(my_id_payload);
+ rsa_private_key_t *private_key;
+ status_t status;
+ chunk_t octets, auth_data;
+
+ private_key = charon->credentials->get_rsa_private_key(charon->credentials, my_id);
+ if (private_key == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "No RSA private key found for %s",
+ my_id->get_string(my_id));
+ my_id->destroy(my_id);
+ return NOT_FOUND;
+ }
+ my_id->destroy(my_id);
+
+ octets = this->allocate_octets(this,last_sent_packet,other_nonce,my_id_payload,initiator);
+
+ status = private_key->build_emsa_pkcs1_signature(private_key, HASH_SHA1, octets, &auth_data);
+ chunk_free(&octets);
+ if (status != SUCCESS)
+ {
+ private_key->destroy(private_key);
+ return status;
+ }
+
+ *auth_payload = auth_payload_create();
+ (*auth_payload)->set_auth_method(*auth_payload, RSA_DIGITAL_SIGNATURE);
+ (*auth_payload)->set_data(*auth_payload, auth_data);
+
+ private_key->destroy(private_key);
+ chunk_free(&auth_data);
+ return SUCCESS;
+ }
+ default:
+ {
+ return NOT_SUPPORTED;
+ }
+ }
+}
+
+/**
+ * Implementation of authenticator_t.destroy.
+ */
+static void destroy (private_authenticator_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa)
+{
+ private_authenticator_t *this = malloc_thing(private_authenticator_t);
+
+ /* Public functions */
+ this->public.destroy = (void(*)(authenticator_t*))destroy;
+ this->public.verify_auth_data = (status_t (*) (authenticator_t *,auth_payload_t *, chunk_t ,chunk_t ,id_payload_t *,bool)) verify_auth_data;
+ this->public.compute_auth_data = (status_t (*) (authenticator_t *,auth_payload_t **, chunk_t ,chunk_t ,id_payload_t *,bool)) compute_auth_data;
+
+ /* private functions */
+ this->allocate_octets = allocate_octets;
+ this->build_preshared_secret_signature = build_preshared_secret_signature;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->prf = this->ike_sa->get_prf(this->ike_sa);
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/authenticator.h b/src/charon/sa/authenticator.h
new file mode 100644
index 000000000..b6bc317ac
--- /dev/null
+++ b/src/charon/sa/authenticator.h
@@ -0,0 +1,138 @@
+/**
+ * @file authenticator.h
+ *
+ * @brief Interface of authenticator_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 AUTHENTICATOR_H_
+#define AUTHENTICATOR_H_
+
+#include <types.h>
+#include <sa/ike_sa.h>
+#include <network/packet.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/id_payload.h>
+
+
+typedef struct authenticator_t authenticator_t;
+
+/**
+ * @brief Class used to authenticate a peer.
+ *
+ * Currently the following two AUTH methods are supported:
+ * - SHARED_KEY_MESSAGE_INTEGRITY_CODE
+ * - RSA_DIGITAL_SIGNATURE
+ *
+ * This class retrieves needed data for specific AUTH methods (RSA keys, shared secrets, etc.)
+ * over an internal stored protected_ike_sa_t object or directly from the configuration_t over
+ * the daemon_t object "charon".
+ *
+ * @b Constructors:
+ * - authenticator_create()
+ *
+ * @ingroup sa
+ */
+struct authenticator_t {
+
+ /**
+ * @brief Verify's given authentication data.
+ *
+ * To verify a received AUTH payload the following data must be provided:
+ * - the last received IKEv2 Message from the other peer in binary form
+ * - the nonce value sent to the other peer
+ * - the ID payload of the other peer
+ *
+ * @param this calling object
+ * @param last_received_packet binary representation of the last received IKEv2-Message
+ * @param my_nonce the sent nonce (without payload header)
+ * @param other_id_payload the ID payload received from other peer
+ * @param initiator type of other peer. TRUE, if it is original initiator, FALSE otherwise
+ *
+ * @todo Document RSA error status types
+ *
+ * @return
+ * - SUCCESS if verification successful
+ * - FAILED if verification failed
+ * - NOT_SUPPORTED if AUTH method not supported
+ * - NOT_FOUND if the data for specific AUTH method could not be found
+ * (e.g. shared secret, rsa key)
+ */
+ status_t (*verify_auth_data) (authenticator_t *this,
+ auth_payload_t *auth_payload,
+ chunk_t last_received_packet,
+ chunk_t my_nonce,
+ id_payload_t *other_id_payload,
+ bool initiator);
+
+ /**
+ * @brief Computes authentication data and creates specific AUTH payload.
+ *
+ * To create an AUTH payload, the following data must be provided:
+ * - the last sent IKEv2 Message in binary form
+ * - the nonce value received from the other peer
+ * - the ID payload of myself
+ *
+ * @param this calling object
+ * @param[out] auth_payload The object of typee auth_payload_t will be created at pointing location
+ * @param last_sent_packet binary representation of the last sent IKEv2-Message
+ * @param other_nonce the received nonce (without payload header)
+ * @param my_id_payload the ID payload going to send to other peer
+ * @param initiator type of myself. TRUE, if I'm original initiator, FALSE otherwise
+ *
+ * @todo Document RSA error status types
+ *
+ * @return
+ * - SUCCESS if authentication data could be computed
+ * - NOT_SUPPORTED if AUTH method not supported
+ * - NOT_FOUND if the data for AUTH method could not be found
+ */
+ status_t (*compute_auth_data) (authenticator_t *this,
+ auth_payload_t **auth_payload,
+ chunk_t last_sent_packet,
+ chunk_t other_nonce,
+ id_payload_t *my_id_payload,
+ bool initiator);
+
+ /**
+ * @brief Destroys a authenticator_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (authenticator_t *this);
+};
+
+/**
+ * @brief Creates an authenticator object.
+ *
+ * @warning: The following functions of the assigned protected_ike_sa_t object
+ * must return a valid value:
+ * - protected_ike_sa_t.get_policy
+ * - protected_ike_sa_t.get_prf
+ * - protected_ike_sa_t.get_logger
+ * This preconditions are not given in IKE_SA states INITIATOR_INIT or RESPONDER_INIT!
+ *
+ * @param ike_sa object of type protected_ike_sa_t
+ *
+ * @return authenticator_t object
+ *
+ * @ingroup sa
+ */
+authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa);
+
+#endif /* AUTHENTICATOR_H_ */
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
new file mode 100644
index 000000000..a678ea9b8
--- /dev/null
+++ b/src/charon/sa/child_sa.c
@@ -0,0 +1,590 @@
+/**
+ * @file child_sa.c
+ *
+ * @brief Implementation of child_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 <netdb.h>
+
+#include "child_sa.h"
+
+#include <daemon.h>
+
+
+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 {
+
+ /**
+ * Network on local side
+ */
+ host_t *my_net;
+
+ /**
+ * Network on remote side
+ */
+ host_t *other_net;
+
+ /**
+ * Number of bits for local network (subnet size)
+ */
+ u_int8_t my_net_mask;
+
+ /**
+ * Number of bits for remote network (subnet size)
+ */
+ u_int8_t other_net_mask;
+
+ /**
+ * Protocol for this policy, such as TCP/UDP/ICMP...
+ */
+ int upper_proto;
+};
+
+typedef struct private_child_sa_t private_child_sa_t;
+
+/**
+ * Private data of a child_sa_t object.
+ */
+struct private_child_sa_t {
+ /**
+ * Public interface of child_sa_t.
+ */
+ child_sa_t public;
+
+ /**
+ * IP of this peer
+ */
+ host_t *me;
+
+ /**
+ * IP of other peer
+ */
+ host_t *other;
+
+ /**
+ * Local security parameter index for AH protocol, 0 if not used
+ */
+ u_int32_t my_ah_spi;
+
+ /**
+ * Local security parameter index for ESP protocol, 0 if not used
+ */
+ u_int32_t my_esp_spi;
+
+ /**
+ * Remote security parameter index for AH protocol, 0 if not used
+ */
+ u_int32_t other_ah_spi;
+
+ /**
+ * Remote security parameter index for ESP protocol, 0 if not used
+ */
+ u_int32_t other_esp_spi;
+
+ /**
+ * List containing policy_id_t objects
+ */
+ linked_list_t *policies;
+
+ /**
+ * reqid used for this child_sa
+ */
+ u_int32_t reqid;
+
+ /**
+ * CHILD_SAs own logger
+ */
+ logger_t *logger;
+};
+
+/**
+ * Implements child_sa_t.alloc
+ */
+static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
+{
+ protocol_id_t protocols[2];
+ iterator_t *iterator;
+ proposal_t *proposal;
+ status_t status;
+ u_int i;
+
+ /* iterator through proposals */
+ iterator = proposals->create_iterator(proposals, TRUE);
+ while(iterator->has_next(iterator))
+ {
+ iterator->current(iterator, (void**)&proposal);
+ proposal->get_protocols(proposal, protocols);
+
+ /* check all protocols */
+ for (i = 0; i<2; i++)
+ {
+ switch (protocols[i])
+ {
+ case PROTO_AH:
+ /* do we already have an spi for AH?*/
+ if (this->my_ah_spi == 0)
+ {
+ /* nope, get one */
+ status = charon->kernel_interface->get_spi(
+ charon->kernel_interface,
+ this->me, this->other,
+ PROTO_AH, FALSE,
+ &(this->my_ah_spi));
+ }
+ /* update proposal */
+ proposal->set_spi(proposal, PROTO_AH, (u_int64_t)this->my_ah_spi);
+ break;
+ case PROTO_ESP:
+ /* do we already have an spi for ESP?*/
+ if (this->my_esp_spi == 0)
+ {
+ /* nope, get one */
+ status = charon->kernel_interface->get_spi(
+ charon->kernel_interface,
+ this->me, this->other,
+ PROTO_ESP, FALSE,
+ &(this->my_esp_spi));
+ }
+ /* update proposal */
+ proposal->set_spi(proposal, PROTO_ESP, (u_int64_t)this->my_esp_spi);
+ break;
+ default:
+ break;
+ }
+ if (status != SUCCESS)
+ {
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+ return SUCCESS;
+}
+
+static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine)
+{
+ protocol_id_t protocols[2];
+ u_int32_t spi;
+ encryption_algorithm_t enc_algo;
+ integrity_algorithm_t int_algo;
+ chunk_t enc_key, int_key;
+ algorithm_t *algo;
+ crypter_t *crypter;
+ signer_t *signer;
+ size_t key_size;
+ host_t *src;
+ host_t *dst;
+ status_t status;
+ u_int i;
+
+ /* we must assign the roles to correctly set up the SAs */
+ if (mine)
+ {
+ src = this->me;
+ dst = this->other;
+ }
+ else
+ {
+ dst = this->me;
+ src = this->other;
+ }
+
+ proposal->get_protocols(proposal, protocols);
+ /* derive keys in order as protocols appear */
+ for (i = 0; i<2; i++)
+ {
+ if (protocols[i] != PROTO_NONE)
+ {
+
+ /* now we have to decide which spi to use. Use self allocated, if "mine",
+ * or the one in the proposal, if not "mine" (others). */
+ if (mine)
+ {
+ if (protocols[i] == PROTO_AH)
+ {
+ spi = this->my_ah_spi;
+ }
+ else
+ {
+ spi = this->my_esp_spi;
+ }
+ }
+ else /* use proposals spi */
+ {
+ spi = proposal->get_spi(proposal, protocols[i]);
+ if (protocols[i] == PROTO_AH)
+ {
+ this->other_ah_spi = spi;
+ }
+ else
+ {
+ this->other_esp_spi = spi;
+ }
+ }
+
+ /* derive encryption key first */
+ if (proposal->get_algorithm(proposal, protocols[i], ENCRYPTION_ALGORITHM, &algo))
+ {
+ enc_algo = algo->algorithm;
+ this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s, ",
+ mapping_find(protocol_id_m, protocols[i]),
+ mine ? "me" : "other",
+ mapping_find(transform_type_m, ENCRYPTION_ALGORITHM),
+ mapping_find(encryption_algorithm_m, enc_algo));
+
+ /* we must create a (unused) crypter, since its the only way to get the size
+ * of the key. This is not so nice, since charon must support all algorithms
+ * the kernel supports...
+ * TODO: build something of a encryption algorithm lookup function
+ */
+ crypter = crypter_create(enc_algo, algo->key_size);
+ key_size = crypter->get_key_size(crypter);
+ crypter->destroy(crypter);
+ prf_plus->allocate_bytes(prf_plus, key_size, &enc_key);
+ this->logger->log_chunk(this->logger, PRIVATE, "key:", enc_key);
+ }
+ else
+ {
+ enc_algo = ENCR_UNDEFINED;
+ }
+
+ /* derive integrity key */
+ if (proposal->get_algorithm(proposal, protocols[i], INTEGRITY_ALGORITHM, &algo))
+ {
+ int_algo = algo->algorithm;
+ this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s,",
+ mapping_find(protocol_id_m, protocols[i]),
+ mine ? "me" : "other",
+ mapping_find(transform_type_m, INTEGRITY_ALGORITHM),
+ mapping_find(integrity_algorithm_m, algo->algorithm));
+
+ signer = signer_create(int_algo);
+ key_size = signer->get_key_size(signer);
+ signer->destroy(signer);
+ prf_plus->allocate_bytes(prf_plus, key_size, &int_key);
+ this->logger->log_chunk(this->logger, PRIVATE, "key:", int_key);
+ }
+ else
+ {
+ int_algo = AUTH_UNDEFINED;
+ }
+ /* send keys down to kernel */
+ this->logger->log(this->logger, CONTROL|LEVEL1,
+ "installing 0x%.8x for %s, src %s dst %s",
+ ntohl(spi), mapping_find(protocol_id_m, protocols[i]),
+ src->get_address(src), dst->get_address(dst));
+ status = charon->kernel_interface->add_sa(charon->kernel_interface,
+ src, dst,
+ spi, protocols[i],
+ this->reqid,
+ enc_algo, enc_key,
+ int_algo, int_key, mine);
+ /* clean up for next round */
+ if (enc_algo != ENCR_UNDEFINED)
+ {
+ chunk_free(&enc_key);
+ }
+ if (int_algo != AUTH_UNDEFINED)
+ {
+ chunk_free(&int_key);
+ }
+
+ if (status != SUCCESS)
+ {
+ return FAILED;
+ }
+
+
+ }
+ }
+ return SUCCESS;
+}
+
+static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
+{
+ linked_list_t *list;
+
+ /* install others (initiators) SAs*/
+ if (install(this, proposal, prf_plus, FALSE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ /* get SPIs for our SAs */
+ list = linked_list_create();
+ list->insert_last(list, proposal);
+ if (alloc(this, list) != SUCCESS)
+ {
+ list->destroy(list);
+ return FAILED;
+ }
+ list->destroy(list);
+
+ /* install our (responders) SAs */
+ if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus)
+{
+ /* install our (initator) SAs */
+ if (install(this, proposal, prf_plus, TRUE) != SUCCESS)
+ {
+ return FAILED;
+ }
+ /* install his (responder) SAs */
+ if (install(this, proposal, prf_plus, FALSE) != 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)
+{
+ iterator_t *my_iter, *other_iter;
+ traffic_selector_t *my_ts, *other_ts;
+
+ /* 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->has_next(my_iter))
+ {
+ my_iter->current(my_iter, (void**)&my_ts);
+ other_iter->reset(other_iter);
+ while (other_iter->has_next(other_iter))
+ {
+ /* set up policies for every entry in my_ts_list to every entry in other_ts_list */
+ int family;
+ chunk_t from_addr;
+ u_int16_t from_port, to_port;
+ sa_policy_t *policy;
+ status_t status;
+
+ other_iter->current(other_iter, (void**)&other_ts);
+
+ /* only set up policies if protocol matches */
+ if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
+ {
+ continue;
+ }
+ policy = malloc_thing(sa_policy_t);
+ policy->upper_proto = my_ts->get_protocol(my_ts);
+
+ /* calculate net and ports for local side */
+ family = my_ts->get_type(my_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
+ from_addr = my_ts->get_from_address(my_ts);
+ from_port = my_ts->get_from_port(my_ts);
+ to_port = my_ts->get_to_port(my_ts);
+ from_port = (from_port != to_port) ? 0 : from_port;
+ policy->my_net = host_create_from_chunk(family, from_addr, from_port);
+ policy->my_net_mask = my_ts->get_netmask(my_ts);
+ chunk_free(&from_addr);
+
+ /* calculate net and ports for remote side */
+ family = other_ts->get_type(other_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
+ from_addr = other_ts->get_from_address(other_ts);
+ from_port = other_ts->get_from_port(other_ts);
+ to_port = other_ts->get_to_port(other_ts);
+ from_port = (from_port != to_port) ? 0 : from_port;
+ policy->other_net = host_create_from_chunk(family, from_addr, from_port);
+ policy->other_net_mask = other_ts->get_netmask(other_ts);
+ chunk_free(&from_addr);
+
+ /* install 3 policies: out, in and forward */
+ status = charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->me, this->other,
+ policy->my_net, policy->other_net,
+ policy->my_net_mask, policy->other_net_mask,
+ XFRM_POLICY_OUT, policy->upper_proto,
+ this->my_ah_spi, this->my_esp_spi,
+ this->reqid);
+
+ status |= charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->other, this->me,
+ policy->other_net, policy->my_net,
+ policy->other_net_mask, policy->my_net_mask,
+ XFRM_POLICY_IN, policy->upper_proto,
+ this->my_ah_spi, this->my_esp_spi,
+ this->reqid);
+
+ status |= charon->kernel_interface->add_policy(charon->kernel_interface,
+ this->other, this->me,
+ policy->other_net, policy->my_net,
+ policy->other_net_mask, policy->my_net_mask,
+ XFRM_POLICY_FWD, policy->upper_proto,
+ this->my_ah_spi, this->my_esp_spi,
+ this->reqid);
+
+ if (status != SUCCESS)
+ {
+ my_iter->destroy(my_iter);
+ other_iter->destroy(other_iter);
+ policy->my_net->destroy(policy->my_net);
+ policy->other_net->destroy(policy->other_net);
+ free(policy);
+ return status;
+ }
+
+ /* add it to the policy list, since we want to know which policies we own */
+ this->policies->insert_last(this->policies, policy);
+ }
+ }
+
+ my_iter->destroy(my_iter);
+ other_iter->destroy(other_iter);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of child_sa_t.log_status.
+ */
+static void log_status(private_child_sa_t *this, logger_t *logger, char* name)
+{
+ iterator_t *iterator;
+ sa_policy_t *policy;
+ struct protoent *proto;
+ char proto_buf[8] = "";
+ char *proto_name = proto_buf;
+
+ if (logger == NULL)
+ {
+ logger = this->logger;
+ }
+ logger->log(logger, CONTROL|LEVEL1, "\"%s\": protected with ESP (0x%x/0x%x), AH (0x%x,0x%x):",
+ name,
+ htonl(this->my_esp_spi), htonl(this->other_esp_spi),
+ htonl(this->my_ah_spi), htonl(this->other_ah_spi));
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ iterator->current(iterator, (void**)&policy);
+ if (policy->upper_proto)
+ {
+ proto = getprotobynumber(policy->upper_proto);
+ if (proto)
+ {
+ proto_name = proto->p_name;
+ }
+ else
+ {
+ snprintf(proto_buf, sizeof(proto_buf), "<%d>", policy->upper_proto);
+ }
+ }
+ logger->log(logger, CONTROL, "\"%s\": %s/%d==%s==%s/%d",
+ name,
+ policy->my_net->get_address(policy->my_net), policy->my_net_mask,
+ proto_name,
+ policy->other_net->get_address(policy->other_net), policy->other_net_mask);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of child_sa_t.destroy.
+ */
+static void destroy(private_child_sa_t *this)
+{
+ /* delete all policys in the kernel */
+ sa_policy_t *policy;
+ while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS)
+ {
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ this->me, this->other,
+ policy->my_net, policy->other_net,
+ policy->my_net_mask, policy->other_net_mask,
+ XFRM_POLICY_OUT, policy->upper_proto);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ this->other, this->me,
+ policy->other_net, policy->my_net,
+ policy->other_net_mask, policy->my_net_mask,
+ XFRM_POLICY_IN, policy->upper_proto);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ this->other, this->me,
+ policy->other_net, policy->my_net,
+ policy->other_net_mask, policy->my_net_mask,
+ XFRM_POLICY_FWD, policy->upper_proto);
+
+ policy->my_net->destroy(policy->my_net);
+ policy->other_net->destroy(policy->other_net);
+ free(policy);
+ }
+ this->policies->destroy(this->policies);
+
+ /* delete SAs in the kernel, if they are set up */
+ if (this->my_ah_spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->other, this->my_ah_spi, PROTO_AH);
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->me, this->other_ah_spi, PROTO_AH);
+ }
+ if (this->my_esp_spi)
+ {
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->other, this->my_esp_spi, PROTO_ESP);
+ charon->kernel_interface->del_sa(charon->kernel_interface,
+ this->me, this->other_esp_spi, PROTO_ESP);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_sa_t * child_sa_create(host_t *me, host_t* other)
+{
+ static u_int32_t reqid = 0xc0000000;
+ private_child_sa_t *this = malloc_thing(private_child_sa_t);
+
+ /* public functions */
+ this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
+ this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add;
+ this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update;
+ this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
+ this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status;
+ this->public.destroy = (void(*)(child_sa_t*))destroy;
+
+ /* private data */
+ this->logger = logger_manager->get_logger(logger_manager, CHILD_SA);
+ this->me = me;
+ this->other = other;
+ this->my_ah_spi = 0;
+ this->my_esp_spi = 0;
+ this->other_ah_spi = 0;
+ this->other_esp_spi = 0;
+ this->reqid = reqid++;
+ this->policies = linked_list_create();
+
+ 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..6ccbff13f
--- /dev/null
+++ b/src/charon/sa/child_sa.h
@@ -0,0 +1,149 @@
+/**
+ * @file child_sa.h
+ *
+ * @brief Interface of child_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 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_SA_H_
+#define CHILD_SA_H_
+
+#include <types.h>
+#include <crypto/prf_plus.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <utils/logger.h>
+
+typedef struct child_sa_t child_sa_t;
+
+/**
+ * @brief Represents multiple IPsec SAs between two hosts.
+ *
+ * A child_sa_t contains multiple SAs. SAs for both
+ * directions are managed in one child_sa_t object, and
+ * if both AH and ESP is set up, both protocols are managed
+ * by one child_sa_t. This means we can have two or
+ * in the AH+ESP case four IPsec-SAs in one child_sa_t.
+ *
+ * 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 Allocate SPIs for a given proposals.
+ *
+ * Since the kernel manages SPIs for us, we need
+ * to allocate them. If the 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 proposal proposal for which SPIs are allocated
+ */
+ status_t (*alloc)(child_sa_t *this, linked_list_t* proposals);
+
+ /**
+ * @brief Install the kernel SAs for a proposal.
+ *
+ * Since the kernel manages SPIs for us, we need
+ * to allocate them. If the 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 proposal proposal for which SPIs are allocated
+ * @param prf_plus key material to use for key derivation
+ */
+ status_t (*add)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus);
+
+ /**
+ * @brief Install the kernel SAs for a proposal, if SPIs already allocated.
+ *
+ * This one updates the SAs in the kernel, which are
+ * allocated via alloc, with a selected proposals.
+ *
+ * @param this calling object
+ * @param proposal proposal for which SPIs are allocated
+ * @param prf_plus key material to use for key derivation
+ */
+ status_t (*update)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus);
+
+ /**
+ * @brief Install the policies using some traffic selectors.
+ *
+ * Spplied 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
+ * @return SUCCESS or FAILED
+ */
+ status_t (*add_policies) (child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list);
+
+ /**
+ * @brief Log the status of a child_sa to a logger.
+ *
+ * The status of ESP/AH SAs is logged with the supplied logger in
+ * a human readable form.
+ * Supplying NULL as logger uses the internal child_sa logger
+ * to do the logging. The name is only a log-prefix without further
+ * meaning.
+ *
+ * @param this calling object
+ * @param logger logger to use for logging
+ * @param name connection name
+ */
+ void (*log_status) (child_sa_t *this, logger_t *logger, char *name);
+
+ /**
+ * @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
+ * @return child_sa_t object
+ *
+ * @ingroup sa
+ */
+child_sa_t * child_sa_create(host_t *me, host_t *other);
+
+#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..6322eb8e9
--- /dev/null
+++ b/src/charon/sa/ike_sa.c
@@ -0,0 +1,1199 @@
+/**
+ * @file ike_sa.c
+ *
+ * @brief Implementation of ike_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "ike_sa.h"
+
+#include <types.h>
+#include <daemon.h>
+#include <definitions.h>
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+#include <utils/randomizer.h>
+#include <crypto/diffie_hellman.h>
+#include <crypto/prf_plus.h>
+#include <crypto/crypters/crypter.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 <sa/states/initiator_init.h>
+#include <sa/states/responder_init.h>
+#include <queues/jobs/retransmit_request_job.h>
+#include <queues/jobs/delete_established_ike_sa_job.h>
+
+
+
+
+typedef struct private_ike_sa_t private_ike_sa_t;
+
+/**
+ * Private data of an ike_sa_t object.
+ */
+struct private_ike_sa_t {
+
+ /**
+ * Protected part of a ike_sa_t object.
+ */
+ protected_ike_sa_t protected;
+
+ /**
+ * Identifier for the current IKE_SA.
+ */
+ ike_sa_id_t *ike_sa_id;
+
+ /**
+ * Linked List containing the child sa's of the current IKE_SA.
+ */
+ linked_list_t *child_sas;
+
+ /**
+ * Current state of the IKE_SA represented as state_t object.
+ *
+ * A state object representates one of the following states and is processing
+ * messages in the specific state:
+ * - INITIATOR_INIT
+ * - RESPONDER_INIT
+ * - IKE_SA_INIT_REQUESTED
+ * - IKE_SA_INIT_RESPONDED
+ * - IKE_AUTH_REQUESTED
+ * -IKE_SA_ESTABLISHED
+ */
+ state_t *current_state;
+
+ /**
+ * INIT configuration, needed for the IKE_SA_INIT exchange.
+ *
+ * Gets set in states:
+ * - INITATOR_INIT
+ * - RESPONDER_INIT
+ *
+ * Available in states:
+ * - IKE_SA_INIT_REQUESTED
+ * - IKE_SA_INIT_RESPONDED
+ * - IKE_AUTH_REQUESTED
+ * -IKE_SA_ESTABLISHED
+ */
+ connection_t *connection;
+
+ /**
+ * SA configuration, needed for all other exchanges after IKE_SA_INIT exchange.
+ *
+ * Gets set in states:
+ * - IKE_SA_INIT_REQUESTED
+ * - IKE_SA_INIT_RESPONDED
+ *
+ * Available in states:
+ * - IKE_AUTH_REQUESTED
+ * -IKE_SA_ESTABLISHED
+ */
+ policy_t *policy;
+
+ /**
+ * This SA's source for random data.
+ *
+ * Is available in every state.
+ */
+ randomizer_t *randomizer;
+
+ /**
+ * The last responded message.
+ */
+ message_t *last_responded_message;
+
+ /**
+ * The ast requested message.
+ */
+ message_t *last_requested_message;
+
+ /**
+ * Crypter object for initiator.
+ */
+ crypter_t *crypter_initiator;
+
+ /**
+ * Crypter object for responder.
+ */
+ crypter_t *crypter_responder;
+
+ /**
+ * Signer object for initiator.
+ */
+ signer_t *signer_initiator;
+
+ /**
+ * Signer object for responder.
+ */
+ signer_t *signer_responder;
+
+ /**
+ * Multi purpose prf, set key, use it, forget it
+ */
+ prf_t *prf;
+
+ /**
+ * Prf function for derivating keymat child SAs
+ */
+ prf_t *child_prf;
+
+ /**
+ * PRF, with key set to pi_key, used for authentication
+ */
+ prf_t *prf_auth_i;
+
+ /**
+ * PRF, with key set to pr_key, used for authentication
+ */
+ prf_t *prf_auth_r;
+
+ /**
+ * Next message id to receive.
+ */
+ u_int32_t message_id_in;
+
+ /**
+ * Next message id to send.
+ */
+ u_int32_t message_id_out;
+
+ /**
+ * Last reply id which was successfully received.
+ */
+ int32_t last_replied_message_id;
+
+ /**
+ * A logger for this IKE_SA.
+ */
+ logger_t *logger;
+
+ /**
+ * Resends the last sent reply.
+ *
+ * @param this calling object
+ */
+ status_t (*resend_last_reply) (private_ike_sa_t *this);
+};
+
+/**
+ * Implementation of ike_sa_t.process_message.
+ */
+static status_t process_message (private_ike_sa_t *this, message_t *message)
+{
+ u_int32_t message_id;
+ exchange_type_t exchange_type;
+ bool is_request;
+
+ /* We must process each request or response from remote host */
+
+ /* Find out type of message (request or response) */
+ is_request = message->get_request(message);
+ exchange_type = message->get_exchange_type(message);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process %s of exchange type %s",
+ (is_request) ? "request" : "response",mapping_find(exchange_type_m,exchange_type));
+
+ message_id = message->get_message_id(message);
+
+ /*
+ * It has to be checked, if the message has to be resent cause of lost packets!
+ */
+ if (is_request && (message_id == (this->message_id_in - 1)))
+ {
+ /* Message can be resent ! */
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Resent request detected. Send stored reply.");
+ return (this->resend_last_reply(this));
+ }
+
+ /* Now, the message id is checked for request AND reply */
+ if (is_request)
+ {
+ /* In a request, the message has to be this->message_id_in (other case is already handled) */
+ if (message_id != this->message_id_in)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1,
+ "Message request with message id %d received, but %d expected",
+ message_id,this->message_id_in);
+ return FAILED;
+ }
+ }
+ else
+ {
+ /* In a reply, the message has to be this->message_id_out -1 cause it is the reply to the last sent message*/
+ if (message_id != (this->message_id_out - 1))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1,
+ "Message reply with message id %d received, but %d expected",
+ message_id,this->message_id_in);
+ return FAILED;
+ }
+ }
+
+ /* now the message is processed by the current state object.
+ * The specific state object is responsible to check if a message can be received in
+ * the state it represents.
+ * The current state is also responsible to change the state object to the next state
+ * by calling protected_ike_sa_t.set_new_state*/
+ return this->current_state->process_message(this->current_state,message);
+}
+
+/**
+ * Implementation of protected_ike_sa_t.build_message.
+ */
+static void build_message(private_ike_sa_t *this, exchange_type_t type, bool request, message_t **message)
+{
+ message_t *new_message;
+ host_t *me, *other;
+
+ me = this->connection->get_my_host(this->connection);
+ other = this->connection->get_other_host(this->connection);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Build empty message");
+ new_message = message_create();
+ new_message->set_source(new_message, me->clone(me));
+ new_message->set_destination(new_message, other->clone(other));
+ new_message->set_exchange_type(new_message, type);
+ new_message->set_request(new_message, request);
+ new_message->set_message_id(new_message, (request) ? this->message_id_out : this->message_id_in);
+ new_message->set_ike_sa_id(new_message, this->ike_sa_id);
+
+ *message = new_message;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.initiate_connection.
+ */
+static status_t initiate_connection(private_ike_sa_t *this, connection_t *connection)
+{
+ initiator_init_t *current_state;
+
+ /* Work is done in state object of type INITIATOR_INIT. All other states are not
+ * initial states and so don't have a initiate_connection function */
+
+ if (this->current_state->get_state(this->current_state) != INITIATOR_INIT)
+ {
+ return FAILED;
+ }
+
+ current_state = (initiator_init_t *) this->current_state;
+
+ return current_state->initiate_connection(current_state, connection);
+}
+
+/**
+ * Implementation of ike_sa_t.send_delete_ike_sa_request.
+ */
+static void send_delete_ike_sa_request (private_ike_sa_t *this)
+{
+ message_t *informational_request;
+ delete_payload_t *delete_payload;
+ crypter_t *crypter;
+ signer_t *signer;
+ packet_t *packet;
+ status_t status;
+
+ if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED)
+ {
+ return;
+ }
+
+ /* build empty INFORMATIONAL message */
+ this->protected.build_message(&(this->protected), INFORMATIONAL, TRUE, &informational_request);
+
+ delete_payload = delete_payload_create();
+ delete_payload->set_protocol_id(delete_payload, PROTO_IKE);
+
+ informational_request->add_payload(informational_request,(payload_t *)delete_payload);
+
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ crypter = this->crypter_initiator;
+ signer = this->signer_initiator;
+ }
+ else
+ {
+ crypter = this->crypter_responder;
+ signer = this->signer_responder;
+ }
+
+ status = informational_request->generate(informational_request,
+ crypter,
+ signer, &packet);
+ informational_request->destroy(informational_request);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR, "Could not generate packet from message");
+ return ;
+ }
+
+ charon->send_queue->add(charon->send_queue,packet);
+}
+
+/**
+ * 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_host.
+ */
+static host_t* get_my_host(private_ike_sa_t *this)
+{
+ return this->connection->get_my_host(this->connection);;
+}
+
+/**
+ * Implementation of ike_sa_t.get_other_host.
+ */
+static host_t* get_other_host(private_ike_sa_t *this)
+{
+ return this->connection->get_other_host(this->connection);;
+}
+
+/**
+ * Implementation of ike_sa_t.get_my_id.
+ */
+static identification_t* get_my_id(private_ike_sa_t *this)
+{
+ return this->connection->get_my_id(this->connection);;
+}
+
+/**
+ * Implementation of ike_sa_t.get_other_id.
+ */
+static identification_t* get_other_id(private_ike_sa_t *this)
+{
+ return this->connection->get_other_id(this->connection);;
+}
+
+/**
+ * Implementation of private_ike_sa_t.resend_last_reply.
+ */
+static status_t resend_last_reply(private_ike_sa_t *this)
+{
+ packet_t *packet;
+
+ this->logger->log(this->logger, CONTROL | LEVEL1, "Going to retransmit last reply");
+ packet = this->last_responded_message->get_packet(this->last_responded_message);
+ charon->send_queue->add(charon->send_queue, packet);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of ike_sa_t.retransmit_request.
+ */
+status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id)
+{
+ packet_t *packet;
+
+ if (this->last_requested_message == NULL)
+ {
+ return NOT_FOUND;
+ }
+
+ if (message_id == this->last_replied_message_id)
+ {
+ return NOT_FOUND;
+ }
+
+ if ((this->last_requested_message->get_message_id(this->last_requested_message)) != message_id)
+ {
+ return NOT_FOUND;
+ }
+
+ this->logger->log(this->logger, CONTROL | LEVEL1, "Going to retransmit message with id %d",message_id);
+ packet = this->last_requested_message->get_packet(this->last_requested_message);
+ charon->send_queue->add(charon->send_queue, packet);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.set_new_state.
+ */
+static void set_new_state (private_ike_sa_t *this, state_t *state)
+{
+ this->logger->log(this->logger, CONTROL, "statechange: %s => %s",
+ mapping_find(ike_sa_state_m,this->current_state->get_state(this->current_state)),
+ mapping_find(ike_sa_state_m,state->get_state(state)));
+ this->current_state = state;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_connection.
+ */
+static connection_t *get_connection (private_ike_sa_t *this)
+{
+ return this->connection;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.set_connection.
+ */
+static void set_connection (private_ike_sa_t *this,connection_t * connection)
+{
+ this->connection = connection;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_policy.
+ */
+static policy_t *get_policy (private_ike_sa_t *this)
+{
+ return this->policy;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.set_policy.
+ */
+static void set_policy (private_ike_sa_t *this,policy_t * policy)
+{
+ this->policy = policy;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_prf.
+ */
+static prf_t *get_prf (private_ike_sa_t *this)
+{
+ return this->prf;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_prf.
+ */
+static prf_t *get_child_prf (private_ike_sa_t *this)
+{
+ return this->child_prf;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_prf_auth_i.
+ */
+static prf_t *get_prf_auth_i (private_ike_sa_t *this)
+{
+ return this->prf_auth_i;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_prf_auth_r.
+ */
+static prf_t *get_prf_auth_r (private_ike_sa_t *this)
+{
+ return this->prf_auth_r;
+}
+
+
+/**
+ * Implementation of protected_ike_sa_t.build_transforms.
+ */
+static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
+{
+ chunk_t nonces, nonces_spis, skeyseed, key, secret;
+ u_int64_t spi_i, spi_r;
+ prf_plus_t *prf_plus;
+ algorithm_t *algo;
+ size_t key_size;
+
+ /*
+ * Build the PRF+ instance for deriving keys
+ */
+ if (this->prf != NULL)
+ {
+ this->prf->destroy(this->prf);
+ }
+ proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo);
+ if (algo == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL2, "No PRF algoithm selected!?");
+ return FAILED;
+ }
+ this->prf = prf_create(algo->algorithm);
+ if (this->prf == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1,
+ "PSEUDO_RANDOM_FUNCTION %s not supported!",
+ mapping_find(pseudo_random_function_m, algo->algorithm));
+ return FAILED;
+ }
+
+ /* concatenate nonces = nonce_i | nonce_r */
+ nonces = chunk_alloc(nonce_i.len + nonce_r.len);
+ memcpy(nonces.ptr, nonce_i.ptr, nonce_i.len);
+ memcpy(nonces.ptr + nonce_i.len, nonce_r.ptr, nonce_r.len);
+
+ /* concatenate prf_seed = nonce_i | nonce_r | spi_i | spi_r */
+ nonces_spis = chunk_alloc(nonces.len + 16);
+ memcpy(nonces_spis.ptr, nonces.ptr, nonces.len);
+ spi_i = this->ike_sa_id->get_initiator_spi(this->ike_sa_id);
+ spi_r = this->ike_sa_id->get_responder_spi(this->ike_sa_id);
+ memcpy(nonces_spis.ptr + nonces.len, &spi_i, 8);
+ memcpy(nonces_spis.ptr + nonces.len + 8, &spi_r, 8);
+
+ /* SKEYSEED = prf(Ni | Nr, g^ir) */
+ dh->get_shared_secret(dh, &secret);
+ this->logger->log_chunk(this->logger, PRIVATE, "Shared Diffie Hellman secret", secret);
+ this->prf->set_key(this->prf, nonces);
+ this->prf->allocate_bytes(this->prf, secret, &skeyseed);
+ this->logger->log_chunk(this->logger, PRIVATE | LEVEL1, "SKEYSEED", skeyseed);
+ chunk_free(&secret);
+
+ /* prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr )
+ * = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr
+ *
+ * we use the prf directly for prf+
+ */
+ this->prf->set_key(this->prf, skeyseed);
+ prf_plus = prf_plus_create(this->prf, nonces_spis);
+
+ /* clean up unused stuff */
+ chunk_free(&nonces);
+ chunk_free(&nonces_spis);
+ chunk_free(&skeyseed);
+
+
+ /*
+ * We now can derive all of our key. We build the transforms
+ * directly.
+ */
+
+
+ /* SK_d used for prf+ to derive keys for child SAs */
+ 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);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_d secret", key);
+ this->child_prf->set_key(this->child_prf, key);
+ chunk_free(&key);
+
+
+ /* SK_ai/SK_ar used for integrity protection */
+ proposal->get_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, &algo);
+ if (algo == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL2, "No integrity algoithm selected?!");
+ return FAILED;
+ }
+ if (this->signer_initiator != NULL)
+ {
+ this->signer_initiator->destroy(this->signer_initiator);
+ }
+ if (this->signer_responder != NULL)
+ {
+ this->signer_responder->destroy(this->signer_responder);
+ }
+
+ this->signer_initiator = signer_create(algo->algorithm);
+ this->signer_responder = signer_create(algo->algorithm);
+ if (this->signer_initiator == NULL || this->signer_responder == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1,
+ "INTEGRITY_ALGORITHM %s not supported!",
+ mapping_find(integrity_algorithm_m,algo->algorithm));
+ return FAILED;
+ }
+ key_size = this->signer_initiator->get_key_size(this->signer_initiator);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_ai secret", key);
+ this->signer_initiator->set_key(this->signer_initiator, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_ar secret", key);
+ this->signer_responder->set_key(this->signer_responder, key);
+ chunk_free(&key);
+
+
+ /* SK_ei/SK_er used for encryption */
+ proposal->get_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, &algo);
+ if (algo == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL2, "No encryption algoithm selected!?");
+ return FAILED;
+ }
+ if (this->crypter_initiator != NULL)
+ {
+ this->crypter_initiator->destroy(this->crypter_initiator);
+ }
+ if (this->crypter_responder != NULL)
+ {
+ this->crypter_responder->destroy(this->crypter_responder);
+ }
+
+ this->crypter_initiator = crypter_create(algo->algorithm, algo->key_size);
+ this->crypter_responder = crypter_create(algo->algorithm, algo->key_size);
+ if (this->crypter_initiator == NULL || this->crypter_responder == NULL)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1,
+ "ENCRYPTION_ALGORITHM %s (key size %d) not supported!",
+ mapping_find(encryption_algorithm_m, algo->algorithm),
+ algo->key_size);
+ return FAILED;
+ }
+ key_size = this->crypter_initiator->get_key_size(this->crypter_initiator);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_ei secret", key);
+ this->crypter_initiator->set_key(this->crypter_initiator, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_er secret", key);
+ this->crypter_responder->set_key(this->crypter_responder, key);
+ chunk_free(&key);
+
+ /* SK_pi/SK_pr used for authentication */
+ proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo);
+ if (this->prf_auth_i != NULL)
+ {
+ this->prf_auth_i->destroy(this->prf_auth_i);
+ }
+ if (this->prf_auth_r != NULL)
+ {
+ this->prf_auth_r->destroy(this->prf_auth_r);
+ }
+
+ this->prf_auth_i = prf_create(algo->algorithm);
+ this->prf_auth_r = prf_create(algo->algorithm);
+
+ key_size = this->prf_auth_i->get_key_size(this->prf_auth_i);
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_pi secret", key);
+ this->prf_auth_i->set_key(this->prf_auth_i, key);
+ chunk_free(&key);
+
+ prf_plus->allocate_bytes(prf_plus, key_size, &key);
+ this->logger->log_chunk(this->logger, PRIVATE, "Sk_pr secret", key);
+ this->prf_auth_r->set_key(this->prf_auth_r, key);
+ chunk_free(&key);
+
+ /* all done, prf_plus not needed anymore */
+ prf_plus->destroy(prf_plus);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_randomizer.
+ */
+static randomizer_t *get_randomizer (private_ike_sa_t *this)
+{
+ return this->randomizer;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_crypter_initiator.
+ */
+static crypter_t *get_crypter_initiator (private_ike_sa_t *this)
+{
+ return this->crypter_initiator;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_signer_initiator.
+ */
+static signer_t *get_signer_initiator (private_ike_sa_t *this)
+{
+ return this->signer_initiator;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_crypter_responder.
+ */
+static crypter_t *get_crypter_responder(private_ike_sa_t *this)
+{
+ return this->crypter_responder;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_signer_responder.
+ */
+static signer_t *get_signer_responder (private_ike_sa_t *this)
+{
+ return this->signer_responder;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.send_request.
+ */
+static status_t send_request (private_ike_sa_t *this,message_t * message)
+{
+ retransmit_request_job_t *retransmit_job;
+ u_int32_t timeout;
+ crypter_t *crypter;
+ signer_t *signer;
+ packet_t *packet;
+ status_t status;
+
+ if (message->get_message_id(message) != this->message_id_out)
+ {
+ this->logger->log(this->logger, ERROR, "Message could not be sent cause id (%d) was not as expected (%d)",
+ message->get_message_id(message),this->message_id_out);
+ return FAILED;
+ }
+
+ /* generate packet */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Generate packet from message");
+
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ crypter = this->crypter_initiator;
+ signer = this->signer_initiator;
+ }
+ else
+ {
+ crypter = this->crypter_responder;
+ signer =this->signer_responder;
+ }
+
+ status = message->generate(message, crypter,signer, &packet);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR, "Could not generate packet from message");
+ return FAILED;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL3,
+ "Add request packet with message id %d to global send queue",
+ this->message_id_out);
+ charon->send_queue->add(charon->send_queue, packet);
+
+ if (this->last_requested_message != NULL)
+ {
+ /* destroy message */
+ this->last_requested_message->destroy(this->last_requested_message);
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL3, "Replace last requested message with new one");
+ this->last_requested_message = message;
+
+ retransmit_job = retransmit_request_job_create(this->message_id_out,this->ike_sa_id);
+
+ status = charon->configuration->get_retransmit_timeout (charon->configuration,
+ retransmit_job->get_retransmit_count(retransmit_job),&timeout);
+
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, CONTROL|LEVEL2, "No retransmit job for message created!");
+ retransmit_job->destroy(retransmit_job);
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Request will be retransmitted in %d ms.",timeout);
+ charon->event_queue->add_relative(charon->event_queue,(job_t *) retransmit_job,timeout);
+ }
+
+ /* message counter can now be increased */
+ this->logger->log(this->logger, CONTROL|LEVEL3,
+ "Increase message counter for outgoing messages from %d",
+ this->message_id_out);
+ this->message_id_out++;
+ return SUCCESS;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.send_response.
+ */
+static status_t send_response (private_ike_sa_t *this,message_t * message)
+{
+ crypter_t *crypter;
+ signer_t *signer;
+ packet_t *packet;
+ status_t status;
+
+ if (message->get_message_id(message) != this->message_id_in)
+ {
+ this->logger->log(this->logger, ERROR, "Message could not be sent cause id was not as expected");
+ return FAILED;
+ }
+
+
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ crypter = this->crypter_initiator;
+ signer = this->signer_initiator;
+ }
+ else
+ {
+ crypter = this->crypter_responder;
+ signer =this->signer_responder;
+ }
+
+ status = message->generate(message, crypter,signer, &packet);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR, "Could not generate packet from message");
+ return FAILED;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL3,
+ "Add response packet with message id %d to global send queue",
+ this->message_id_in);
+ charon->send_queue->add(charon->send_queue, packet);
+
+ if (this->last_responded_message != NULL)
+ {
+ /* destroy message */
+ this->last_responded_message->destroy(this->last_responded_message);
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL3, "Replace last responded message with new one");
+ this->last_responded_message = message;
+
+ /* message counter can now be increased */
+ this->logger->log(this->logger, CONTROL|LEVEL3, "Increase message counter for incoming messages");
+ this->message_id_in++;
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of of private_responder_init_t.send_notify_reply.
+ */
+static void send_notify(private_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data)
+{
+ notify_payload_t *payload;
+ message_t *response;
+ packet_t *packet;
+ status_t status;
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Going to build message with notify payload");
+ /* set up the reply */
+ this->protected.build_message(&(this->protected), exchange_type, FALSE, &response);
+ payload = notify_payload_create_from_protocol_and_type(PROTO_IKE, type);
+ if ((data.ptr != NULL) && (data.len > 0))
+ {
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add Data to notify payload");
+ payload->set_notification_data(payload,data);
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify payload to message");
+ response->add_payload(response,(payload_t *) payload);
+
+ /* generate packet */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Generate packet from message");
+ status = response->generate(response, this->crypter_responder, this->signer_responder, &packet);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Could not generate notify message");
+ response->destroy(response);
+ return;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add packet to global send queue");
+ charon->send_queue->add(charon->send_queue, packet);
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy message");
+ response->destroy(response);
+}
+
+/**
+ * Implementation of protected_ike_sa_t.set_last_replied_message_id.
+ */
+static void set_last_replied_message_id (private_ike_sa_t *this,u_int32_t message_id)
+{
+ this->last_replied_message_id = message_id;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_last_responded_message.
+ */
+static message_t * get_last_responded_message (private_ike_sa_t *this)
+{
+ return this->last_responded_message;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_last_requested_message.
+ */
+static message_t * get_last_requested_message (private_ike_sa_t *this)
+{
+ return this->last_requested_message;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_state.
+ */
+static ike_sa_state_t get_state (private_ike_sa_t *this)
+{
+ return this->current_state->get_state(this->current_state);
+}
+
+/**
+ * Implementation of protected_ike_sa_t.get_state.
+ */
+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 protected_ike_sa_t.reset_message_buffers.
+ */
+static void reset_message_buffers (private_ike_sa_t *this)
+{
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Reset message counters and destroy stored messages");
+ /* destroy stored requested message */
+ if (this->last_requested_message != NULL)
+ {
+ this->last_requested_message->destroy(this->last_requested_message);
+ this->last_requested_message = NULL;
+ }
+
+ /* destroy stored responded messages */
+ if (this->last_responded_message != NULL)
+ {
+ this->last_responded_message->destroy(this->last_responded_message);
+ this->last_responded_message = NULL;
+ }
+
+ this->message_id_out = 0;
+ this->message_id_in = 0;
+ this->last_replied_message_id = -1;
+}
+
+/**
+ * Implementation of protected_ike_sa_t.log_status.
+ */
+static void log_status(private_ike_sa_t *this, logger_t *logger, char *name)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+
+ /* only log if name == NULL or name == connection_name */
+ if (name)
+ {
+ if (strcmp(this->connection->get_name(this->connection), name) != 0)
+ {
+ return;
+ }
+ }
+ else
+ {
+ name = this->connection->get_name(this->connection);
+ }
+
+ host_t *my_host = this->connection->get_my_host(this->connection);
+ host_t *other_host = this->connection->get_other_host(this->connection);
+
+ identification_t *my_id = this->connection->get_my_id(this->connection);
+ identification_t *other_id = this->connection->get_other_id(this->connection);
+
+ if (logger == NULL)
+ {
+ logger = this->logger;
+ }
+ logger->log(logger, CONTROL|LEVEL1, "\"%s\": IKE_SA in state %s, SPIs: 0x%.16llx 0x%.16llx",
+ name,
+ mapping_find(ike_sa_state_m, this->current_state->get_state(this->current_state)),
+ this->ike_sa_id->get_initiator_spi(this->ike_sa_id),
+ this->ike_sa_id->get_responder_spi(this->ike_sa_id));
+ logger->log(logger, CONTROL, "\"%s\": %s[%s]...%s[%s]",
+ name,
+ my_host->get_address(my_host),
+ my_id->get_string(my_id),
+ other_host->get_address(other_host),
+ other_id->get_string(other_id));
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ iterator->current(iterator, (void**)&child_sa);
+ child_sa->log_status(child_sa, logger, name);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of protected_ike_sa_t.destroy.
+ */
+static void destroy (private_ike_sa_t *this)
+{
+ child_sa_t *child_sa;
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Going to destroy IKE SA %llu:%llu, role %s",
+ this->ike_sa_id->get_initiator_spi(this->ike_sa_id),
+ this->ike_sa_id->get_responder_spi(this->ike_sa_id),
+ this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder");
+
+ /* inform other peer of delete */
+ send_delete_ike_sa_request(this);
+ while (this->child_sas->remove_last(this->child_sas, (void**)&child_sa) == SUCCESS)
+ {
+ child_sa->destroy(child_sa);
+ }
+ this->child_sas->destroy(this->child_sas);
+
+ if (this->crypter_initiator)
+ {
+ this->crypter_initiator->destroy(this->crypter_initiator);
+ }
+ if (this->crypter_responder)
+ {
+ this->crypter_responder->destroy(this->crypter_responder);
+ }
+ if (this->signer_initiator)
+ {
+ this->signer_initiator->destroy(this->signer_initiator);
+ }
+ if (this->signer_responder)
+ {
+ this->signer_responder->destroy(this->signer_responder);
+ }
+ if (this->prf)
+ {
+ this->prf->destroy(this->prf);
+ }
+ if (this->child_prf)
+ {
+ this->child_prf->destroy(this->child_prf);
+ }
+ if (this->prf_auth_i)
+ {
+ this->prf_auth_i->destroy(this->prf_auth_i);
+ }
+ if (this->prf_auth_r)
+ {
+ this->prf_auth_r->destroy(this->prf_auth_r);
+ }
+ if (this->connection)
+ {
+ host_t *me, *other;
+ me = this->connection->get_my_host(this->connection);
+ other = this->connection->get_other_host(this->connection);
+
+ this->logger->log(this->logger, AUDIT, "IKE_SA deleted between %s - %s",
+ me->get_address(me), other->get_address(other));
+ this->connection->destroy(this->connection);
+ }
+ if (this->policy)
+ {
+ this->policy->destroy(this->policy);
+ }
+ if (this->last_requested_message)
+ {
+ this->last_requested_message->destroy(this->last_requested_message);
+ }
+ if (this->last_responded_message)
+ {
+ this->last_responded_message->destroy(this->last_responded_message);
+ }
+ this->ike_sa_id->destroy(this->ike_sa_id);
+ this->randomizer->destroy(this->randomizer);
+ this->current_state->destroy(this->current_state);
+
+ 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);
+
+ /* Public functions */
+ this->protected.public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
+ this->protected.public.initiate_connection = (status_t(*)(ike_sa_t*,connection_t*)) initiate_connection;
+ this->protected.public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
+ this->protected.public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host;
+ this->protected.public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host;
+ this->protected.public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id;
+ this->protected.public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id;
+ this->protected.public.get_connection = (connection_t*(*)(ike_sa_t*)) get_connection;
+ this->protected.public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request;
+ this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t *this)) get_state;
+ this->protected.public.send_delete_ike_sa_request = (void (*)(ike_sa_t*)) send_delete_ike_sa_request;
+ this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status;
+ this->protected.public.destroy = (void(*)(ike_sa_t*))destroy;
+
+ /* protected functions */
+ this->protected.build_message = (void (*) (protected_ike_sa_t *, exchange_type_t , bool , message_t **)) build_message;
+ this->protected.get_prf = (prf_t *(*) (protected_ike_sa_t *)) get_prf;
+ this->protected.get_child_prf = (prf_t *(*) (protected_ike_sa_t *)) get_child_prf;
+ this->protected.get_prf_auth_i = (prf_t *(*) (protected_ike_sa_t *)) get_prf_auth_i;
+ this->protected.get_prf_auth_r = (prf_t *(*) (protected_ike_sa_t *)) get_prf_auth_r;
+ this->protected.add_child_sa = (void (*) (protected_ike_sa_t*,child_sa_t*)) add_child_sa;
+ this->protected.set_connection = (void (*) (protected_ike_sa_t *,connection_t *)) set_connection;
+ this->protected.get_connection = (connection_t *(*) (protected_ike_sa_t *)) get_connection;
+ this->protected.set_policy = (void (*) (protected_ike_sa_t *,policy_t *)) set_policy;
+ this->protected.get_policy = (policy_t *(*) (protected_ike_sa_t *)) get_policy;
+ this->protected.get_randomizer = (randomizer_t *(*) (protected_ike_sa_t *)) get_randomizer;
+ this->protected.send_request = (status_t (*) (protected_ike_sa_t *,message_t *)) send_request;
+ this->protected.send_response = (status_t (*) (protected_ike_sa_t *,message_t *)) send_response;
+ this->protected.send_notify = (void (*)(protected_ike_sa_t*,exchange_type_t,notify_message_type_t,chunk_t)) send_notify;
+ this->protected.build_transforms = (status_t (*) (protected_ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t)) build_transforms;
+ this->protected.set_new_state = (void (*) (protected_ike_sa_t *,state_t *)) set_new_state;
+ this->protected.get_crypter_initiator = (crypter_t *(*) (protected_ike_sa_t *)) get_crypter_initiator;
+ this->protected.get_signer_initiator = (signer_t *(*) (protected_ike_sa_t *)) get_signer_initiator;
+ this->protected.get_crypter_responder = (crypter_t *(*) (protected_ike_sa_t *)) get_crypter_responder;
+ this->protected.get_signer_responder = (signer_t *(*) (protected_ike_sa_t *)) get_signer_responder;
+ this->protected.reset_message_buffers = (void (*) (protected_ike_sa_t *)) reset_message_buffers;
+ this->protected.get_last_responded_message = (message_t * (*) (protected_ike_sa_t *this)) get_last_responded_message;
+ this->protected.get_last_requested_message = (message_t * (*) (protected_ike_sa_t *this)) get_last_requested_message;
+
+ this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id;
+
+ /* private functions */
+ this->resend_last_reply = resend_last_reply;
+
+ /* initialize private fields */
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+
+ this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+ this->child_sas = linked_list_create();
+ this->randomizer = randomizer_create();
+
+ this->last_requested_message = NULL;
+ this->last_responded_message = NULL;
+ this->message_id_out = 0;
+ this->message_id_in = 0;
+ this->last_replied_message_id = -1;
+ this->crypter_initiator = NULL;
+ this->crypter_responder = NULL;
+ this->signer_initiator = NULL;
+ this->signer_responder = NULL;
+ this->prf = NULL;
+ this->prf_auth_i = NULL;
+ this->prf_auth_r = NULL;
+ this->child_prf = NULL;
+ this->connection = NULL;
+ this->policy = NULL;
+
+ /* at creation time, IKE_SA is in a initiator state */
+ if (ike_sa_id->is_initiator(ike_sa_id))
+ {
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Create first state_t object of type INITIATOR_INIT");
+ this->current_state = (state_t *) initiator_init_create(&(this->protected));
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Create first state_t object of type RESPONDER_INIT");
+ this->current_state = (state_t *) responder_init_create(&(this->protected));
+ }
+ return &(this->protected.public);
+}
diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h
new file mode 100644
index 000000000..c526c6347
--- /dev/null
+++ b/src/charon/sa/ike_sa.h
@@ -0,0 +1,462 @@
+/**
+ * @file ike_sa.h
+ *
+ * @brief Interface of ike_sa_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_H_
+#define IKE_SA_H_
+
+#include <types.h>
+#include <encoding/message.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <sa/ike_sa_id.h>
+#include <sa/child_sa.h>
+#include <sa/states/state.h>
+#include <config/configuration.h>
+#include <utils/logger.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 <utils/logger.h>
+
+/**
+ * Nonce size in bytes for nonces sending to other peer.
+ *
+ * @warning Nonce size MUST be between 16 and 256 bytes.
+ *
+ * @ingroup sa
+ */
+#define NONCE_SIZE 16
+
+
+typedef struct ike_sa_t ike_sa_t;
+
+/**
+ * @brief Class ike_sa_t representing an IKE_SA.
+ *
+ * An object of this type is managed by an ike_sa_manager_t object
+ * and represents an IKE_SA. Message processing is split up in different states.
+ * They will handle all related things for the state they represent.
+ *
+ * @b Constructors:
+ * - ike_sa_create()
+ *
+ * @ingroup sa
+ */
+struct ike_sa_t {
+
+ /**
+ * @brief Processes a incoming IKEv2-Message of type message_t.
+ *
+ * @param this ike_sa_t object object
+ * @param[in] message message_t object to process
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DELETE_ME if this IKE_SA MUST be deleted
+ */
+ status_t (*process_message) (ike_sa_t *this,message_t *message);
+
+ /**
+ * @brief Initiate a new connection with given connection_t object.
+ *
+ * The connection_t object 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
+ * @return
+ * - SUCCESS if initialization started
+ * - FAILED if in wrong state
+ * - DELETE_ME if initialization failed and IKE_SA MUST be deleted
+ */
+ status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection);
+
+ /**
+ * @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_request) (ike_sa_t *this, u_int32_t message_id);
+
+ /**
+ * @brief Sends a request to delete IKE_SA.
+ *
+ * Only supported in state IKE_SA_ESTABLISHED
+ *
+ * @param this calling object
+ */
+ void (*send_delete_ike_sa_request) (ike_sa_t *this);
+
+ /**
+ * @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 local peer address of the IKE_SA.
+ *
+ * @param this calling object
+ * @return local host_t
+ */
+ host_t* (*get_my_host) (ike_sa_t *this);
+
+ /**
+ * @brief Get remote peer address of the IKE_SA.
+ *
+ * @param this calling object
+ * @return remote host_t
+ */
+ host_t* (*get_other_host) (ike_sa_t *this);
+
+ /**
+ * @brief Get own ID of the IKE_SA.
+ *
+ * @param this calling object
+ * @return local identification_t
+ */
+ identification_t* (*get_my_id) (ike_sa_t *this);
+
+ /**
+ * @brief Get remote ID the IKE_SA.
+ *
+ * @param this calling object
+ * @return remote identification_t
+ */
+ identification_t* (*get_other_id) (ike_sa_t *this);
+
+ /**
+ * @brief Get the connection of the IKE_SA.
+ *
+ * The internal used connection specification
+ * can be queried to get some data of an IKE_SA.
+ * The connection is still owned to the IKE_SA
+ * and must not be manipulated.
+ *
+ * @param this calling object
+ * @return connection_t
+ */
+ connection_t* (*get_connection) (ike_sa_t *this);
+
+ /**
+ * @brief Get the state of type of associated state object.
+ *
+ * @param this calling object
+ * @return state of IKE_SA
+ */
+ ike_sa_state_t (*get_state) (ike_sa_t *this);
+
+ /**
+ * @brief Log the status of a the ike sa to a logger.
+ *
+ * The status of the IKE SA and all child SAs is logged.
+ * Supplying NULL as logger uses the internal child_sa logger
+ * to do the logging. The log is only done if the supplied
+ * connection name is NULL or matches the connections name.
+ *
+ * @param this calling object
+ * @param logger logger to use for logging
+ * @param name name of the connection
+ */
+ void (*log_status) (ike_sa_t *this, logger_t *logger, char *name);
+
+ /**
+ * @brief Destroys a ike_sa_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (ike_sa_t *this);
+};
+
+
+typedef struct protected_ike_sa_t protected_ike_sa_t;
+
+/**
+ * @brief Protected functions of an ike_sa_t object.
+ *
+ * This members are only accessed out from
+ * the various state_t implementations.
+ *
+ * @ingroup sa
+ */
+struct protected_ike_sa_t {
+
+ /**
+ * Public interface of an ike_sa_t object.
+ */
+ ike_sa_t public;
+
+ /**
+ * @brief Build an empty IKEv2-Message and fills in default informations.
+ *
+ * Depending on the type of message (request or response), the message id is
+ * either message_id_out or message_id_in.
+ *
+ * Used in state_t Implementation to build an empty IKEv2-Message.
+ *
+ * @param this calling object
+ * @param type exchange type of new message
+ * @param request TRUE, if message has to be a request
+ * @param message new message is stored at this location
+ */
+ void (*build_message) (protected_ike_sa_t *this, exchange_type_t type, bool request, message_t **message);
+
+ /**
+ * @brief Get the internal stored connection_t object.
+ *
+ * @param this calling object
+ * @return pointer to the internal stored connection_t object
+ */
+ connection_t *(*get_connection) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Set the internal connection object.
+ *
+ * @param this calling object
+ * @param connection object of type connection_t
+ */
+ void (*set_connection) (protected_ike_sa_t *this, connection_t *connection);
+
+ /**
+ * @brief Get the internal stored policy object.
+ *
+ * @param this calling object
+ * @return pointer to the internal stored policy_t object
+ */
+ policy_t *(*get_policy) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Set the internal policy_t object.
+ *
+ * @param this calling object
+ * @param policy object of type policy_t
+ */
+ void (*set_policy) (protected_ike_sa_t *this,policy_t *policy);
+
+ /**
+ * @brief Derive all keys and create the transforms for IKE communication.
+ *
+ * Keys are derived using the diffie hellman secret, nonces and internal
+ * stored SPIs.
+ * Allready existing objects get destroyed.
+ *
+ * @param this calling object
+ * @param proposal proposal which contains algorithms to use
+ * @param dh diffie hellman object with shared secret
+ * @param nonce_i initiators nonce
+ * @param nonce_r responders nonce
+ */
+ status_t (*build_transforms) (protected_ike_sa_t *this, proposal_t* proposal,
+ diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r);
+
+ /**
+ * @brief Send the next request message.
+ *
+ * Also the first retransmit job is created.
+ *
+ * Last stored requested message gets destroyed. Object gets not cloned!
+ *
+ * @param this calling object
+ * @param message pointer to the message which should be sent
+ * @return
+ * - SUCCESS
+ * - FAILED if message id is not next expected one
+ */
+ status_t (*send_request) (protected_ike_sa_t *this,message_t * message);
+
+ /**
+ * @brief Send the next response message.
+ *
+ * Last stored responded message gets destroyed. Object gets not cloned!
+ *
+ * @param this calling object
+ * @param message pointer to the message which should be sent
+ * return
+ * - SUCCESS
+ * - FAILED if message id is not next expected one
+ */
+ status_t (*send_response) (protected_ike_sa_t *this,message_t * message);
+
+ /**
+ * @brief Send a notify reply message.
+ *
+ * @param this calling object
+ * @param exchange_type type of exchange in which the notify should be wrapped
+ * @param type type of the notify message to send
+ * @param data notification data
+ */
+ void (*send_notify) (protected_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data);
+
+ /**
+ * @brief Get the internal stored randomizer_t object.
+ *
+ * @param this calling object
+ * @return pointer to the internal randomizer_t object
+ */
+ randomizer_t *(*get_randomizer) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Set the new state_t object of the IKE_SA object.
+ *
+ * The old state_t object gets not destroyed. It's the callers duty to
+ * make sure old state is destroyed (Normally the old state is the caller).
+ *
+ * @param this calling object
+ * @param state pointer to the new state_t object
+ */
+ void (*set_new_state) (protected_ike_sa_t *this,state_t *state);
+
+ /**
+ * @brief Set the last replied message id.
+ *
+ * @param this calling object
+ * @param message_id message id
+ */
+ void (*set_last_replied_message_id) (protected_ike_sa_t *this,u_int32_t message_id);
+
+ /**
+ * @brief Get the internal stored initiator crypter_t object.
+ *
+ * @param this calling object
+ * @return pointer to crypter_t object
+ */
+ crypter_t *(*get_crypter_initiator) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the internal stored initiator signer_t object.
+ *
+ * @param this calling object
+ * @return pointer to signer_t object
+ */
+ signer_t *(*get_signer_initiator) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the internal stored responder crypter_t object.
+ *
+ * @param this calling object
+ * @return pointer to crypter_t object
+ */
+ crypter_t *(*get_crypter_responder) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the internal stored responder signer object.
+ *
+ * @param this calling object
+ * @return pointer to signer_t object
+ */
+ signer_t *(*get_signer_responder) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the multi purpose prf.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_prf) (protected_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) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the prf used for authentication of initiator.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_prf_auth_i) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the prf used for authentication of responder.
+ *
+ * @param this calling object
+ * @return pointer to prf_t object
+ */
+ prf_t *(*get_prf_auth_r) (protected_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) (protected_ike_sa_t *this, child_sa_t *child_sa);
+
+ /**
+ * @brief Get the last responded message.
+ *
+ * @param this calling object
+ * @return
+ * - last received as message_t object
+ * - NULL if no last request available
+ */
+ message_t *(*get_last_responded_message) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Get the last requested message.
+ *
+ * @param this calling object
+ * @return
+ * - last sent as message_t object
+ * - NULL if no last request available
+ */
+ message_t *(*get_last_requested_message) (protected_ike_sa_t *this);
+
+ /**
+ * @brief Resets message counters and does destroy stored received and sent messages.
+ *
+ * @param this calling object
+ */
+ void (*reset_message_buffers) (protected_ike_sa_t *this);
+};
+
+
+/**
+ * @brief Creates an ike_sa_t object with a specific ID.
+ *
+ * @warning the Content of internal ike_sa_id_t object can change over time
+ * e.g. when a IKE_SA_INIT has been finished.
+ *
+ * @param[in] ike_sa_id ike_sa_id_t object to associate with new IKE_SA.
+ * The object is internal getting cloned
+ * and so has to be destroyed by the caller.
+ * @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..bf3a05d11
--- /dev/null
+++ b/src/charon/sa/ike_sa_id.c
@@ -0,0 +1,185 @@
+/**
+ * @file ike_sa_id.c
+ *
+ * @brief Implementation of ike_sa_id_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_sa_id.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);
+}
+
+/**
+ * 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..0f16f7637
--- /dev/null
+++ b/src/charon/sa/ike_sa_id.h
@@ -0,0 +1,146 @@
+/**
+ * @file ike_sa_id.h
+ *
+ * @brief Interface of ike_sa_id_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_ID_H_
+#define IKE_SA_ID_H_
+
+#include <types.h>
+
+
+typedef struct ike_sa_id_t ike_sa_id_t;
+
+/**
+ * @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..a65f41042
--- /dev/null
+++ b/src/charon/sa/ike_sa_manager.c
@@ -0,0 +1,843 @@
+/**
+ * @file ike_sa_manager.c
+ *
+ * @brief Implementation of ike_sa_mananger_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 <pthread.h>
+#include <string.h>
+
+#include "ike_sa_manager.h"
+
+#include <daemon.h>
+#include <sa/ike_sa_id.h>
+#include <utils/logger.h>
+#include <utils/logger_manager.h>
+#include <utils/linked_list.h>
+
+typedef struct ike_sa_entry_t ike_sa_entry_t;
+
+/**
+ * An entry in the linked list, contains IKE_SA, locking and lookup data.
+ */
+struct ike_sa_entry_t {
+ /**
+ * Destructor, also destroys associated ike_sa_t object.
+ */
+ status_t (*destroy) (ike_sa_entry_t *this);
+
+ /**
+ * 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;
+};
+
+/**
+ * Implementation of ike_sa_entry_t.destroy.
+ */
+static status_t ike_sa_entry_destroy(ike_sa_entry_t *this)
+{
+ /* also destroy IKE SA */
+ this->ike_sa->destroy(this->ike_sa);
+ this->ike_sa_id->destroy(this->ike_sa_id);
+ free(this);
+ return SUCCESS;
+}
+
+/**
+ * @brief Creates a new entry for the ike_sa_t list.
+ *
+ * This constructor additionaly creates a new and empty SA.
+ *
+ * @param ike_sa_id The associated ike_sa_id_t, will be cloned
+ * @return ike_sa_entry_t object
+ */
+static ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id)
+{
+ ike_sa_entry_t *this = malloc_thing(ike_sa_entry_t);
+
+ /* destroy function */
+ this->destroy = ike_sa_entry_destroy;
+
+ 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;
+
+ /* 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;
+
+ /**
+ * @brief Get next spi.
+ *
+ * We give out SPIs incremental starting at 1.
+ *
+ * @param this the ike_sa_manager
+ * @return the next spi
+ */
+ u_int64_t (*get_next_spi) (private_ike_sa_manager_t *this);
+
+ /**
+ * @brief Find the ike_sa_entry_t object in the list by SPIs.
+ *
+ * This function simply iterates over the linked list. A hash-table
+ * would be more efficient when storing a lot of IKE_SAs...
+ *
+ * @param this calling object
+ * @param ike_sa_id id of the ike_sa, containing SPIs
+ * @param[out] entry pointer to set to the found entry
+ * @return
+ * - SUCCESS when found,
+ * - NOT_FOUND when no such ike_sa_id in list
+ */
+ status_t (*get_entry_by_id) (private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry);
+
+ /**
+ * @brief Find the ike_sa_entry_t in the list by pointer to SA.
+ *
+ * This function simply iterates over the linked list. A hash-table
+ * would be more efficient when storing a lot of IKE_SAs...
+ *
+ * @param this calling object
+ * @param ike_sa pointer to the ike_sa
+ * @param[out] entry pointer to set to the found entry
+ * @return
+ * - SUCCESS when found,
+ * - NOT_FOUND when no such ike_sa_id in list
+ */
+ status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry);
+
+ /**
+ * @brief Felete an entry from the linked list.
+ *
+ * @param this calling object
+ * @param entry entry to delete
+ * @return
+ * - SUCCESS when found,
+ * - NOT_FOUND when no such ike_sa_id in list
+ */
+ status_t (*delete_entry) (private_ike_sa_manager_t *this, ike_sa_entry_t *entry);
+
+ /**
+ * Lock for exclusivly accessing the manager.
+ */
+ pthread_mutex_t mutex;
+
+ /**
+ * Logger used for this IKE SA Manager.
+ */
+ logger_t *logger;
+
+ /**
+ * 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;
+};
+
+/**
+ * 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, ike_sa_entry_t **entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ status_t status;
+
+ /* create iterator over list of ike_sa's */
+ iterator = list->create_iterator(list, TRUE);
+
+ /* default status */
+ status = NOT_FOUND;
+
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *current;
+
+ iterator->current(iterator, (void**)&current);
+ if (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))
+ && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id)))
+ {
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by initiator spi %d",ike_sa_id->get_initiator_spi(ike_sa_id));
+ *entry = current;
+ status = SUCCESS;
+ break;
+ }
+ }
+ else if (ike_sa_id->get_responder_spi(ike_sa_id) == 0)
+ {
+ if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == ike_sa_id->get_initiator_spi(ike_sa_id))
+ && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id)))
+ {
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by initiator spi %d",ike_sa_id->get_initiator_spi(ike_sa_id));
+ *entry = current;
+ status = SUCCESS;
+ break;
+ }
+ }
+ if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id))
+ {
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by full ID");
+ *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, ike_sa_entry_t **entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ status_t status;
+
+ iterator = list->create_iterator(list, TRUE);
+
+ /* default status */
+ status = NOT_FOUND;
+
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *current;
+ iterator->current(iterator, (void**)&current);
+ /* only pointers are compared */
+ if (current->ike_sa == ike_sa)
+ {
+ this->logger->log(this->logger,CONTROL | LEVEL2,"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, ike_sa_entry_t *entry)
+{
+ linked_list_t *list = this->ike_sa_list;
+ iterator_t *iterator;
+ status_t status;
+
+ iterator = list->create_iterator(list, TRUE);
+
+ status = NOT_FOUND;
+
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *current;
+ iterator->current(iterator, (void**)&current);
+ if (current == entry)
+ {
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by pointer. Going to delete it.");
+ iterator->remove(iterator);
+ entry->destroy(entry);
+ status = SUCCESS;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return status;
+}
+
+
+/**
+ * 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, 8, (u_int8_t*)&spi);
+
+ return spi;
+}
+
+/**
+ * Implementation of of ike_sa_manager.create_and_checkout.
+ */
+static void create_and_checkout(private_ike_sa_manager_t *this,ike_sa_t **ike_sa)
+{
+ u_int64_t initiator_spi;
+ ike_sa_entry_t *new_ike_sa_entry;
+ ike_sa_id_t *new_ike_sa_id;
+
+ initiator_spi = this->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_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id);
+ new_ike_sa_id->destroy(new_ike_sa_id);
+
+ /* each access is locked */
+ pthread_mutex_lock(&(this->mutex));
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry);
+
+ /* check ike_sa out */
+ this->logger->log(this->logger,CONTROL | LEVEL1 ,"New IKE_SA created and added to list of known IKE_SA's");
+ new_ike_sa_entry->checked_out = TRUE;
+ *ike_sa = new_ike_sa_entry->ike_sa;
+
+ pthread_mutex_unlock(&(this->mutex));
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout.
+ */
+static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t **ike_sa)
+{
+ bool responder_spi_set;
+ bool initiator_spi_set;
+ bool original_initiator;
+ status_t retval;
+
+ /* each access is locked */
+ pthread_mutex_lock(&(this->mutex));
+
+ responder_spi_set = (FALSE != ike_sa_id->get_responder_spi(ike_sa_id));
+ initiator_spi_set = (FALSE != ike_sa_id->get_initiator_spi(ike_sa_id));
+ original_initiator = ike_sa_id->is_initiator(ike_sa_id);
+
+ if ((initiator_spi_set && responder_spi_set) ||
+ ((initiator_spi_set && !responder_spi_set) && (original_initiator)))
+ {
+ /* we SHOULD have an IKE_SA for these SPIs in the list,
+ * if not, we can't handle the request...
+ */
+ ike_sa_entry_t *entry;
+ /* look for the entry */
+ if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ {
+ /* can we give this ike_sa out to new requesters?*/
+ if (entry->driveout_new_threads)
+ {
+ this->logger->log(this->logger,CONTROL|LEVEL1,"Drive out new thread for existing IKE_SA");
+ /* no we can't */
+ retval = NOT_FOUND;
+ }
+ else
+ {
+ /* is this IKE_SA already checked out ??
+ * are we welcome to get this SA ? */
+ 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, go home */
+ if (entry->driveout_waiting_threads)
+ {
+ /* we must signal here, others are interested that we leave */
+ pthread_cond_signal(&(entry->condvar));
+ this->logger->log(this->logger,CONTROL|LEVEL1,"Drive out waiting thread for existing IKE_SA");
+ retval = NOT_FOUND;
+ }
+ else
+ {
+ this->logger->log(this->logger,CONTROL|LEVEL2,"IKE SA successfully checked out");
+ /* ok, this IKE_SA is finally ours */
+ entry->checked_out = TRUE;
+ *ike_sa = entry->ike_sa;
+ /* DON'T use return, we must unlock the mutex! */
+ retval = SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ this->logger->log(this->logger,ERROR | LEVEL1,"IKE SA not stored in known IKE_SA list");
+ /* looks like there is no such IKE_SA, better luck next time... */
+ /* DON'T use return, we must unlock the mutex! */
+ retval = NOT_FOUND;
+ }
+ }
+ else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator))
+ {
+ /* an IKE_SA_INIT from an another endpoint,
+ * he is the initiator.
+ * For simplicity, we do NOT check for retransmitted
+ * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
+ * Request (even a retransmitted one) will result in a
+ * IKE_SA. This could be improved...
+ */
+ u_int64_t responder_spi;
+ ike_sa_entry_t *new_ike_sa_entry;
+
+
+ /* set SPIs, we are the responder */
+ responder_spi = this->get_next_spi(this);
+
+ /* we also set arguments spi, so its still valid */
+ ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
+
+ /* create entry */
+ new_ike_sa_entry = ike_sa_entry_create(ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry);
+
+ /* check ike_sa out */
+ this->logger->log(this->logger,CONTROL | LEVEL1 ,"IKE_SA added to list of known IKE_SA's");
+ new_ike_sa_entry->checked_out = TRUE;
+ *ike_sa = new_ike_sa_entry->ike_sa;
+
+ retval = CREATED;
+ }
+ else
+ {
+ /* responder set, initiator not: here is something seriously wrong! */
+ this->logger->log(this->logger,ERROR | LEVEL1, "Invalid IKE_SA SPI's");
+ /* DON'T use return, we must unlock the mutex! */
+ retval = INVALID_ARG;
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+ /* OK, unlocked... */
+ return retval;
+}
+
+/**
+ * Implementation of of ike_sa_manager.checkout_by_hosts.
+ */
+static status_t checkout_by_hosts(private_ike_sa_manager_t *this, host_t *me, host_t *other, ike_sa_t **ike_sa)
+{
+ iterator_t *iterator;
+ ike_sa_id_t *ike_sa_id = NULL;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *current;
+ host_t *sa_me, *sa_other;
+
+ iterator->current(iterator, (void**)&current);
+ sa_me = current->ike_sa->get_my_host(current->ike_sa);
+ sa_other = current->ike_sa->get_other_host(current->ike_sa);
+
+ /* one end may be default/any, but not both */
+ if (me->is_default_route(me))
+ {
+ if (other->is_default_route(other))
+ {
+ break;
+ }
+ if (other->equals(other, sa_other))
+ {
+ /* other matches */
+ ike_sa_id = current->ike_sa_id;
+ }
+ }
+ else if (other->is_default_route(other))
+ {
+ if (me->equals(me, sa_me))
+ {
+ /* ME matches */
+ ike_sa_id = current->ike_sa_id;
+ }
+ }
+ else
+ {
+ if (me->equals(me, sa_me) && other->equals(other, sa_other))
+ {
+ /* both matches */
+ ike_sa_id = current->ike_sa_id;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&(this->mutex));
+
+ if (ike_sa_id)
+ {
+ /* checkout is done in the checkout function, since its rather complex */
+ return checkout(this, ike_sa_id, ike_sa);
+ }
+ return NOT_FOUND;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.get_ike_sa_list.
+ */
+linked_list_t *get_ike_sa_list(private_ike_sa_manager_t* this)
+{
+ linked_list_t *list;
+ iterator_t *iterator;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ list = linked_list_create();
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *entry;
+ iterator->current(iterator, (void**)&entry);
+ list->insert_last(list, (void*)entry->ike_sa_id->clone(entry->ike_sa_id));
+ }
+ iterator->destroy(iterator);
+
+ pthread_mutex_unlock(&(this->mutex));
+ return list;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.get_ike_sa_list_by_name.
+ */
+linked_list_t *get_ike_sa_list_by_name(private_ike_sa_manager_t* this, const char *name)
+{
+ linked_list_t *list;
+ iterator_t *iterator;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ list = linked_list_create();
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *entry;
+ connection_t *connection;
+
+ iterator->current(iterator, (void**)&entry);
+ connection = entry->ike_sa->get_connection(entry->ike_sa);
+ if (strcmp(name, connection->get_name(connection)) == 0)
+ {
+ list->insert_last(list, (void*)entry->ike_sa_id->clone(entry->ike_sa_id));
+ }
+ }
+ iterator->destroy(iterator);
+
+ pthread_mutex_unlock(&(this->mutex));
+ return list;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.log_status.
+ */
+static void log_status(private_ike_sa_manager_t* this, logger_t* logger, char* name)
+{
+ iterator_t *iterator;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ ike_sa_entry_t *entry;
+ iterator->current(iterator, (void**)&entry);
+ entry->ike_sa->log_status(entry->ike_sa, logger, name);
+ }
+ iterator->destroy(iterator);
+
+ pthread_mutex_unlock(&(this->mutex));
+}
+
+/**
+ * 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;
+ ike_sa_entry_t *entry;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ /* look for the entry */
+ if (this->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;
+ this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin of IKE_SA successful.");
+ pthread_cond_signal(&(entry->condvar));
+ retval = SUCCESS;
+ }
+ else
+ {
+ this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin nonexisting IKE_SA");
+ /* this SA is no more, this REALLY should not happen */
+ retval = NOT_FOUND;
+ }
+ pthread_mutex_unlock(&(this->mutex));
+ return retval;
+}
+
+
+/**
+ * Implementation of ike_sa_manager_t.checkin_and_delete.
+ */
+static status_t checkin_and_delete(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.
+ */
+ ike_sa_entry_t *entry;
+ status_t retval;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
+ {
+ /* mark it, so now new threads can acquire this SA */
+ entry->driveout_new_threads = TRUE;
+ /* additionaly, drive out waiting threads */
+ entry->driveout_waiting_threads = TRUE;
+
+ /* wait until all workers have done their work */
+ while (entry->waiting_threads > 0)
+ {
+ /* let the other threads do some work*/
+ pthread_cond_signal(&(entry->condvar));
+ /* and the nice thing, they will wake us again when their work is done */
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ }
+ /* ok, we are alone now, no threads waiting in the entry's condvar */
+ this->delete_entry(this, entry);
+ this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin and delete of IKE_SA successful");
+ retval = SUCCESS;
+ }
+ else
+ {
+ this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin and delete nonexisting IKE_SA");
+ retval = NOT_FOUND;
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+ return retval;
+}
+
+/**
+ * Implementation of ike_sa_manager_t.delete.
+ */
+static status_t delete(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+{
+ /* 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.
+ */
+ ike_sa_entry_t *entry;
+ status_t retval;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ {
+ /* mark it, so now new threads can acquire this SA */
+ entry->driveout_new_threads = TRUE;
+
+ /* wait until all workers have done their work */
+ while (entry->waiting_threads)
+ {
+ /* wake up all */
+ pthread_cond_signal(&(entry->condvar));
+ /* and the nice thing, they will wake us again when their work is done */
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ }
+ /* ok, we are alone now, no threads waiting in the entry's condvar */
+ this->delete_entry(this, entry);
+ this->logger->log(this->logger,CONTROL | LEVEL1,"Delete of IKE_SA successful");
+ retval = SUCCESS;
+ }
+ else
+ {
+ this->logger->log(this->logger,ERROR,"Fatal Error: Tried to delete nonexisting IKE_SA");
+ retval = NOT_FOUND;
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+ return retval;
+}
+
+/**
+ * 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;
+ ike_sa_entry_t *entry;
+
+ pthread_mutex_lock(&(this->mutex));
+
+ this->logger->log(this->logger,CONTROL | LEVEL1,"Going to destroy IKE_SA manager and all managed IKE_SA's");
+
+ /* Step 1: drive out all waiting threads */
+ iterator = list->create_iterator(list, TRUE);
+
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Set driveout flags for all stored IKE_SA's");
+ while (iterator->has_next(iterator))
+ {
+ iterator->current(iterator, (void**)&entry);
+ /* do not accept new threads, drive out waiting threads */
+ entry->driveout_new_threads = TRUE;
+ entry->driveout_waiting_threads = TRUE;
+ }
+
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Wait for all threads to leave IKE_SA's");
+ /* Step 2: wait until all are gone */
+ iterator->reset(iterator);
+ while (iterator->has_next(iterator))
+ {
+ iterator->current(iterator, (void**)&entry);
+ while (entry->waiting_threads)
+ {
+ /* wake up all */
+ pthread_cond_signal(&(entry->condvar));
+ /* go sleeping until they are gone */
+ pthread_cond_wait(&(entry->condvar), &(this->mutex));
+ }
+ }
+ this->logger->log(this->logger,CONTROL | LEVEL2,"Delete all IKE_SA's");
+ /* Step 3: delete all entries */
+ iterator->destroy(iterator);
+
+ while (list->get_count(list) > 0)
+ {
+ list->get_first(list, (void**)&entry);
+ this->delete_entry(this, entry);
+ }
+ list->destroy(list);
+ this->logger->log(this->logger,CONTROL | LEVEL2,"IKE_SA's deleted");
+ pthread_mutex_unlock(&(this->mutex));
+
+ this->randomizer->destroy(this->randomizer);
+
+ 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.create_and_checkout = (void(*)(ike_sa_manager_t*,ike_sa_t**))create_and_checkout;
+ this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t*,ike_sa_t**))checkout;
+ this->public.checkout_by_hosts = (status_t(*)(ike_sa_manager_t*,host_t*,host_t*,ike_sa_t**))checkout_by_hosts;
+ this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list;
+ this->public.get_ike_sa_list_by_name = (linked_list_t*(*)(ike_sa_manager_t*,const char*))get_ike_sa_list_by_name;
+ this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status;
+ this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
+ this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete;
+ this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_delete;
+
+ /* initialize private functions */
+ this->get_next_spi = get_next_spi;
+ this->get_entry_by_sa = get_entry_by_sa;
+ this->get_entry_by_id = get_entry_by_id;
+ this->delete_entry = delete_entry;
+
+ /* initialize private variables */
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA_MANAGER);
+
+ this->ike_sa_list = linked_list_create();
+
+ pthread_mutex_init(&(this->mutex), NULL);
+
+ this->randomizer = randomizer_create();
+
+ return (ike_sa_manager_t*)this;
+}
diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h
new file mode 100644
index 000000000..d4cd749dc
--- /dev/null
+++ b/src/charon/sa/ike_sa_manager.h
@@ -0,0 +1,194 @@
+/**
+ * @file ike_sa_manager.h
+ *
+ * @brief Interface of ike_sa_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_MANAGER_H_
+#define IKE_SA_MANAGER_H_
+
+#include <types.h>
+#include <sa/ike_sa.h>
+#include <utils/logger.h>
+
+
+typedef struct ike_sa_manager_t ike_sa_manager_t;
+
+/**
+ * @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 IKE_SA, create it when necesarry.
+ *
+ * Checks out a SA by its ID. An SA will be created, when:
+ * - Responder SPI is not set (when received an IKE_SA_INIT from initiator)
+ * Management of SPIs is the managers job, he will set it.
+ * This function blocks until SA is available for checkout.
+ *
+ * @warning checking out two times without checking in will
+ * result in a deadlock!
+ *
+ * @param this the manager object
+ * @param ike_sa_id[in/out] the SA identifier, will be updated
+ * @param ike_sa[out] checked out SA
+ * @returns
+ * - SUCCESS if checkout successful
+ * - NOT_FOUND when no such SA is available
+ * - CREATED if a new IKE_SA got created
+ */
+ status_t (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id, ike_sa_t **ike_sa);
+
+ /**
+ * @brief Create and checkout an IKE_SA as original initator.
+ *
+ * Creates and checks out a SA as initiator.
+ * Management of SPIs is the managers job, he will set it.
+ *
+ * @param this the manager object
+ * @param ike_sa[out] checked out SA
+ */
+ void (*create_and_checkout) (ike_sa_manager_t* this,ike_sa_t **ike_sa);
+
+ /**
+ * @brief Check out an IKE_SA, defined be the two peers.
+ *
+ * Checking out an IKE_SA by their peer addresses may be necessary
+ * for kernel traps, status querying and so on... one of the hosts
+ * may be 0.0.0.0 (defaultroute/any), but not both.
+ *
+ * @param this the manager object
+ * @param me host on local side
+ * @param other host on remote side
+ * @param ike_sa[out] checked out SA
+ * @return
+ * - NOT_FOUND, if no such SA found
+ * - SUCCESS, if SA found and ike_sa set appropriatly
+ */
+ status_t (*checkout_by_hosts) (ike_sa_manager_t* this, host_t *me, host_t *other, ike_sa_t **ike_sa);
+
+ /**
+ * @brief Get a list of all IKE_SA SAs currently set up.
+ *
+ * The resulting list with all IDs must be destroyd by
+ * the caller. There is no guarantee an ike_sa with the
+ * corrensponding ID really exists, since it may be deleted
+ * in the meantime by another thread.
+ *
+ * @param this the manager object
+ * @return a list with ike_sa_id_t s
+ */
+ linked_list_t *(*get_ike_sa_list) (ike_sa_manager_t* this);
+
+ /**
+ * @brief Get a list of all IKE_SA SAs currently set up specified
+ * by the connections name.
+ *
+ * @param this the manager object
+ * @return a list with ike_sa_id_t s
+ */
+ linked_list_t *(*get_ike_sa_list_by_name) (ike_sa_manager_t* this, const char *name);
+
+ /**
+ * @brief Log the status of the IKE_SA's in the manager.
+ *
+ * A informational log is done to the supplied logger. If logger is
+ * NULL, an internal logger is used. If a name is supplied,
+ * only connections with the matching name will be logged.
+ *
+ * @param this the manager object
+ * @param logger logger to do the log, or NULL
+ * @param name name of a connection, or NULL
+ */
+ void (*log_status) (ike_sa_manager_t* this, logger_t* logger, char* name);
+
+ /**
+ * @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[in/out] the SA identifier, will be updated
+ * @param ike_sa[out] 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 Delete a SA, which was not checked out.
+ *
+ * @warning do not use this when the SA is already checked out, this will
+ * deadlock!
+ *
+ * @param this the manager object
+ * @param ike_sa_id[in/out] the SA identifier
+ * @returns
+ * - SUCCESS if found
+ * - NOT_FOUND when no such SA is available
+ */
+ status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id);
+
+ /**
+ * @brief Delete a checked out 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_delete) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
+
+ /**
+ * @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/states/Makefile.states b/src/charon/sa/states/Makefile.states
new file mode 100644
index 000000000..a258ebef0
--- /dev/null
+++ b/src/charon/sa/states/Makefile.states
@@ -0,0 +1,43 @@
+# Copyright (C) 2005 Jan Hutter, 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.
+#
+
+STATES_DIR= $(SA_DIR)states/
+
+CHARON_OBJS+= $(BUILD_DIR)ike_auth_requested.o
+$(BUILD_DIR)ike_auth_requested.o : $(STATES_DIR)ike_auth_requested.c $(STATES_DIR)ike_auth_requested.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa_established.o
+$(BUILD_DIR)ike_sa_established.o : $(STATES_DIR)ike_sa_established.c $(STATES_DIR)ike_sa_established.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_requested.o
+$(BUILD_DIR)ike_sa_init_requested.o : $(STATES_DIR)ike_sa_init_requested.c $(STATES_DIR)ike_sa_init_requested.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_responded.o
+$(BUILD_DIR)ike_sa_init_responded.o : $(STATES_DIR)ike_sa_init_responded.c $(STATES_DIR)ike_sa_init_responded.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)initiator_init.o
+$(BUILD_DIR)initiator_init.o : $(STATES_DIR)initiator_init.c $(STATES_DIR)initiator_init.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)responder_init.o
+$(BUILD_DIR)responder_init.o : $(STATES_DIR)responder_init.c $(STATES_DIR)responder_init.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+CHARON_OBJS+= $(BUILD_DIR)state.o
+$(BUILD_DIR)state.o : $(STATES_DIR)state.c $(STATES_DIR)state.h
+ $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file
diff --git a/src/charon/sa/states/ike_auth_requested.c b/src/charon/sa/states/ike_auth_requested.c
new file mode 100644
index 000000000..3d49f440f
--- /dev/null
+++ b/src/charon/sa/states/ike_auth_requested.c
@@ -0,0 +1,671 @@
+/**
+ * @file ike_auth_requested.c
+ *
+ * @brief Implementation of ike_auth_requested_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "ike_auth_requested.h"
+
+#include <daemon.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/signers/signer.h>
+#include <crypto/crypters/crypter.h>
+#include <sa/states/ike_sa_established.h>
+#include <sa/authenticator.h>
+#include <sa/child_sa.h>
+
+typedef struct private_ike_auth_requested_t private_ike_auth_requested_t;
+
+/**
+ * Private data of a ike_auth_requested_t object.
+ *
+ */
+struct private_ike_auth_requested_t {
+ /**
+ * Public interface of ike_auth_requested_t.
+ */
+ ike_auth_requested_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * SA config, just a copy of the one stored in the ike_sa.
+ */
+ policy_t *policy;
+
+ /**
+ * Received nonce from responder.
+ */
+ chunk_t received_nonce;
+
+ /**
+ * Sent nonce in IKE_SA_INIT request.
+ */
+ chunk_t sent_nonce;
+
+ /**
+ * IKE_SA_INIT-Request in binary form.
+ */
+ chunk_t ike_sa_init_reply_data;
+
+ /**
+ * Proposal to setup CHILD_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * Traffic selectors applicable at our site
+ */
+ linked_list_t *my_ts;
+
+ /**
+ * Traffic selectors applicable at remote site
+ */
+ linked_list_t *other_ts;
+
+ /**
+ * Child sa created in ike_sa_init_requested
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * Assigned Logger.
+ *
+ * Is logger of ike_sa!
+ */
+ logger_t *logger;
+
+ /**
+ * Process the IDr payload (check if other id is valid)
+ *
+ * @param this calling object
+ * @param idr_payload ID payload of responder
+ * @return
+ * - SUCCESS
+ * - DELETE_ME
+ */
+ status_t (*process_idr_payload) (private_ike_auth_requested_t *this, id_payload_t *idr_payload);
+
+ /**
+ * Process the SA payload (check if selected proposals are valid, setup child sa)
+ *
+ * @param this calling object
+ * @param sa_payload SA payload of responder
+ *
+ * - SUCCESS
+ * - DELETE_ME
+ */
+ status_t (*process_sa_payload) (private_ike_auth_requested_t *this, sa_payload_t *sa_payload);
+
+ /**
+ * Process the AUTH payload (check authenticity of message)
+ *
+ * @param this calling object
+ * @param auth_payload AUTH payload of responder
+ * @param other_id_payload ID payload of responder
+ *
+ * - SUCCESS
+ * - DELETE_ME
+ */
+ status_t (*process_auth_payload) (private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload);
+
+ /**
+ * Process the TS payload (check if selected traffic selectors are valid)
+ *
+ * @param this calling object
+ * @param ts_initiator TRUE if TS payload is TSi, FALSE for TSr
+ * @param ts_payload TS payload of responder
+ *
+ * - SUCCESS
+ * - DELETE_ME
+ */
+ status_t (*process_ts_payload) (private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload);
+
+ /**
+ * Process a notify payload
+ *
+ * @param this calling object
+ * @param notify_payload notify payload
+ *
+ * - SUCCESS
+ * - FAILED
+ * - DELETE_ME
+ */
+ status_t (*process_notify_payload) (private_ike_auth_requested_t *this, notify_payload_t *notify_payload);
+
+ /**
+ * Destroy function called internally of this class after state change to
+ * state IKE_SA_ESTABLISHED succeeded.
+ *
+ * This destroy function does not destroy objects which were passed to the new state.
+ *
+ * @param this calling object
+ */
+ void (*destroy_after_state_change) (private_ike_auth_requested_t *this);
+};
+
+
+/**
+ * Implements state_t.process_message
+ */
+static status_t process_message(private_ike_auth_requested_t *this, message_t *ike_auth_reply)
+{
+ ts_payload_t *tsi_payload = NULL, *tsr_payload = NULL;
+ id_payload_t *idr_payload = NULL;
+ auth_payload_t *auth_payload = NULL;
+ sa_payload_t *sa_payload = NULL;
+ iterator_t *payloads = NULL;
+ crypter_t *crypter = NULL;
+ signer_t *signer = NULL;
+ status_t status;
+ host_t *my_host, *other_host;
+ chunk_t seed;
+ prf_plus_t *prf_plus;
+ connection_t *connection;
+
+ if (ike_auth_reply->get_exchange_type(ike_auth_reply) != IKE_AUTH)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_auth_requested",
+ mapping_find(exchange_type_m,ike_auth_reply->get_exchange_type(ike_auth_reply)));
+ return FAILED;
+ }
+
+ if (ike_auth_reply->get_request(ike_auth_reply))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH requests not allowed state ike_sa_init_responded");
+ return FAILED;
+ }
+
+ /* get signer for verification and crypter for decryption */
+ signer = this->ike_sa->get_signer_responder(this->ike_sa);
+ crypter = this->ike_sa->get_crypter_responder(this->ike_sa);
+
+ /* parse incoming message */
+ status = ike_auth_reply->parse_body(ike_auth_reply, crypter, signer);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply decryption failed. Ignoring message");
+ return status;
+ }
+
+ this->policy = this->ike_sa->get_policy(this->ike_sa);
+
+ /* we collect all payloads, which are processed later. Notify's are processed
+ * in place, since we don't know how may are there.
+ */
+ payloads = ike_auth_reply->get_payload_iterator(ike_auth_reply);
+ while (payloads->has_next(payloads))
+ {
+ payload_t *payload;
+ payloads->current(payloads, (void**)&payload);
+
+ switch (payload->get_type(payload))
+ {
+ case AUTHENTICATION:
+ {
+ auth_payload = (auth_payload_t*)payload;
+ break;
+ }
+ case ID_RESPONDER:
+ {
+ idr_payload = (id_payload_t*)payload;
+ break;
+ }
+ case SECURITY_ASSOCIATION:
+ {
+ sa_payload = (sa_payload_t*)payload;
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ {
+ tsi_payload = (ts_payload_t*)payload;
+ break;
+ }
+ case TRAFFIC_SELECTOR_RESPONDER:
+ {
+ tsr_payload = (ts_payload_t*)payload;
+ break;
+ }
+ case NOTIFY:
+ {
+ notify_payload_t *notify_payload = (notify_payload_t *) payload;
+ /* handle the notify directly, abort if no further processing required */
+ status = this->process_notify_payload(this, notify_payload);
+ if (status != SUCCESS)
+ {
+ payloads->destroy(payloads);
+ return status;
+ }
+ }
+ case CERTIFICATE:
+ {
+ /* TODO handle cert payloads */
+ }
+ default:
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)",
+ mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
+ break;
+ }
+ }
+ }
+ /* iterator can be destroyed */
+ payloads->destroy(payloads);
+
+ /* check if we have all payloads */
+ if (!(idr_payload && sa_payload && auth_payload && tsi_payload && tsr_payload))
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* process all payloads */
+ status = this->process_idr_payload(this, idr_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ status = this->process_auth_payload(this, auth_payload,idr_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ status = this->process_sa_payload(this, sa_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ status = this->process_ts_payload(this, TRUE, tsi_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ status = this->process_ts_payload(this, FALSE, tsr_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ /* install child SAs for AH and esp */
+ if (!this->child_sa)
+ {
+ this->logger->log(this->logger, CONTROL, "No CHILD_SA requested, no CHILD_SA built");
+ }
+ if (!this->proposal)
+ {
+ this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built");
+ this->child_sa->destroy(this->child_sa);
+ this->child_sa = NULL;
+ }
+ else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0)
+ {
+ this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built");
+ this->child_sa->destroy(this->child_sa);
+ this->child_sa = NULL;
+ }
+ else
+ {
+ seed = chunk_alloc(this->sent_nonce.len + this->received_nonce.len);
+ memcpy(seed.ptr, this->sent_nonce.ptr, this->sent_nonce.len);
+ memcpy(seed.ptr + this->sent_nonce.len, this->received_nonce.ptr, this->received_nonce.len);
+ prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
+ chunk_free(&seed);
+
+ status = this->child_sa->update(this->child_sa, this->proposal, prf_plus);
+ prf_plus->destroy(prf_plus);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+ }
+
+ this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_auth_reply->get_message_id(ike_auth_reply));
+
+ /* create new state */
+ this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa));
+ this->destroy_after_state_change(this);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ my_host = connection->get_my_host(connection);
+ other_host = connection->get_other_host(connection);
+ this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s",
+ my_host->get_address(my_host), other_host->get_address(other_host));
+
+ return SUCCESS;
+}
+
+/**
+ * Implements private_ike_auth_requested_t.process_idr_payload
+ */
+static status_t process_idr_payload(private_ike_auth_requested_t *this, id_payload_t *idr_payload)
+{
+ identification_t *other_id, *configured_other_id;
+ connection_t *connection;
+
+ other_id = idr_payload->get_identification(idr_payload);
+ configured_other_id = this->policy->get_other_id(this->policy);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "configured ID: %s, ID of responder: %s",
+ configured_other_id->get_string(configured_other_id),
+ other_id->get_string(other_id));
+
+ if (!other_id->belongs_to(other_id, configured_other_id))
+ {
+ other_id->destroy(other_id);
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not acceptable ID. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ connection->update_other_id(connection, other_id->clone(other_id));
+
+ this->policy->update_other_id(this->policy, other_id);
+ return SUCCESS;
+}
+
+/**
+ * Implements private_ike_auth_requested_t.process_sa_payload
+ */
+static status_t process_sa_payload(private_ike_auth_requested_t *this, sa_payload_t *sa_payload)
+{
+ proposal_t *proposal, *proposal_tmp;
+ linked_list_t *proposal_list;
+
+ /* get his selected proposal */
+ proposal_list = sa_payload->get_proposals(sa_payload);
+ /* check count of proposals */
+ if (proposal_list->get_count(proposal_list) == 0)
+ {
+ /* no proposal? we accept this, but no child sa is built */
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD didn't contain any proposals. No CHILD_SA created",
+ proposal_list->get_count(proposal_list));
+ proposal_list->destroy(proposal_list);
+ return SUCCESS;
+ }
+ if (proposal_list->get_count(proposal_list) > 1)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD contained %d proposal. Deleting IKE_SA",
+ proposal_list->get_count(proposal_list));
+ while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
+ {
+ proposal->destroy(proposal);
+ }
+ proposal_list->destroy(proposal_list);
+ return DELETE_ME;
+ }
+
+ /* we have to re-check here if other's selection is valid */
+ proposal = this->policy->select_proposal(this->policy, proposal_list);
+ /* list not needed anymore */
+ while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS)
+ {
+ proposal_tmp->destroy(proposal_tmp);
+ }
+ proposal_list->destroy(proposal_list);
+ /* got a match? */
+ if (proposal == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not offered proposal. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* apply proposal */
+ this->proposal = proposal;
+
+ return SUCCESS;
+}
+
+/**
+ * Implements private_ike_auth_requested_t.process_auth_payload
+ */
+static status_t process_auth_payload(private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload)
+{
+ authenticator_t *authenticator;
+ status_t status;
+
+ authenticator = authenticator_create(this->ike_sa);
+ status = authenticator->verify_auth_data(authenticator,auth_payload,this->ike_sa_init_reply_data,this->sent_nonce,other_id_payload,FALSE);
+ authenticator->destroy(authenticator);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Verification of IKE_AUTH reply failed. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "AUTH data verified successfully");
+ return SUCCESS;
+}
+
+/**
+ * Implements private_ike_auth_requested_t.process_ts_payload
+ */
+static status_t process_ts_payload(private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload)
+{
+ linked_list_t *ts_received, *ts_selected;
+ traffic_selector_t *ts;
+
+ /* get ts form payload */
+ ts_received = ts_payload->get_traffic_selectors(ts_payload);
+ /* select ts depending on payload type */
+ if (ts_initiator)
+ {
+ ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received);
+ this->my_ts = ts_selected;
+ }
+ else
+ {
+ ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received);
+ this->other_ts = ts_selected;
+ }
+ /* check if the responder selected valid proposals */
+ if (ts_selected->get_count(ts_selected) != ts_received->get_count(ts_received))
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained not offered traffic selectors.");
+ }
+
+ /* cleanup */
+ while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ ts_received->destroy(ts_received);
+
+ return SUCCESS;
+}
+
+/**
+ * Implements private_ike_auth_requested_t.process_notify_payload
+ */
+static status_t process_notify_payload(private_ike_auth_requested_t *this, notify_payload_t *notify_payload)
+{
+ notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s",
+ mapping_find(notify_message_type_m, notify_message_type));
+
+ switch (notify_message_type)
+ {
+ case INVALID_SYNTAX:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an INVALID_SYNTAX notify. Deleting IKE_SA");
+ return DELETE_ME;
+
+ }
+ case AUTHENTICATION_FAILED:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an AUTHENTICATION_FAILED notify. Deleting IKE_SA");
+ return DELETE_ME;
+
+ }
+ case SINGLE_PAIR_REQUIRED:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a SINGLE_PAIR_REQUIRED notify. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ default:
+ {
+ /*
+ * - In case of unknown error: IKE_SA gets destroyed.
+ * - In case of unknown status: logging
+ */
+
+ if (notify_message_type < 16383)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an unknown notify error (%d). Deleting IKE_SA",
+ notify_message_type);
+ return DELETE_ME;
+
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL, "IKE_AUTH reply contained an unknown notify (%d), ignored.",
+ notify_message_type);
+ return SUCCESS;
+ }
+ }
+ }
+}
+
+/**
+ * Implements state_t.get_state
+ */
+static ike_sa_state_t get_state(private_ike_auth_requested_t *this)
+{
+ return IKE_AUTH_REQUESTED;
+}
+
+/**
+ * Implements state_t.get_state
+ */
+static void destroy(private_ike_auth_requested_t *this)
+{
+ chunk_free(&(this->received_nonce));
+ chunk_free(&(this->sent_nonce));
+ chunk_free(&(this->ike_sa_init_reply_data));
+ if (this->child_sa)
+ {
+ this->child_sa->destroy(this->child_sa);
+ }
+ if (this->my_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->my_ts->destroy(this->my_ts);
+ }
+ if (this->other_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->other_ts->destroy(this->other_ts);
+ }
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+ free(this);
+}
+/**
+ * Implements protected_ike_sa_t.destroy_after_state_change
+ */
+static void destroy_after_state_change(private_ike_auth_requested_t *this)
+{
+ chunk_free(&(this->received_nonce));
+ chunk_free(&(this->sent_nonce));
+ chunk_free(&(this->ike_sa_init_reply_data));
+ if (this->my_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->my_ts->destroy(this->my_ts);
+ }
+ if (this->other_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->other_ts->destroy(this->other_ts);
+ }
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa,chunk_t sent_nonce,chunk_t received_nonce,chunk_t ike_sa_init_reply_data, child_sa_t *child_sa)
+{
+ private_ike_auth_requested_t *this = malloc_thing(private_ike_auth_requested_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* private functions */
+ this->process_idr_payload = process_idr_payload;
+ this->process_sa_payload = process_sa_payload;
+ this->process_auth_payload = process_auth_payload;
+ this->process_ts_payload = process_ts_payload;
+ this->process_notify_payload = process_notify_payload;
+ this->destroy_after_state_change = destroy_after_state_change;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->received_nonce = received_nonce;
+ this->sent_nonce = sent_nonce;
+ this->ike_sa_init_reply_data = ike_sa_init_reply_data;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+ this->my_ts = NULL;
+ this->other_ts = NULL;
+ this->proposal = NULL;
+ this->child_sa = child_sa;
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/ike_auth_requested.h b/src/charon/sa/states/ike_auth_requested.h
new file mode 100644
index 000000000..a8eef014c
--- /dev/null
+++ b/src/charon/sa/states/ike_auth_requested.h
@@ -0,0 +1,72 @@
+/**
+ * @file ike_auth_requested.h
+ *
+ * @brief Interface of ike_auth_requested_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_REQUESTED_H_
+#define IKE_AUTH_REQUESTED_H_
+
+#include <sa/states/state.h>
+#include <sa/ike_sa.h>
+
+
+typedef struct ike_auth_requested_t ike_auth_requested_t;
+
+/**
+ * @brief This class represents an IKE_SA, which has requested an IKE_AUTH.
+ *
+ * The state accpets IKE_AUTH responses. It proves the authenticity
+ * and sets up the first child sa. After that, it changes IKE_SA state to
+ * IKE_SA_ESTABLISHED.
+ *
+ * @ Constructors:
+ * - ike_auth_requested_create()
+ *
+ * @todo handle certificate payloads
+ *
+ * @ingroup states
+ */
+struct ike_auth_requested_t {
+ /**
+ * The state_t interface.
+ */
+ state_t state_interface;
+
+};
+
+/**
+ * Constructor of class ike_auth_requested_t
+ *
+ * @param ike_sa assigned ike_sa object
+ * @param sent_nonce Sent nonce value in IKE_SA_INIT request
+ * @param received_nonce Received nonce value in IKE_SA_INIT response
+ * @param ike_sa_init_reply_data binary representation of IKE_SA_INIT reply
+ * @param child_sa opened but not completed child_sa
+ * @return created ike_auth_requested_t object
+ *
+ * @ingroup states
+ */
+ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa,
+ chunk_t sent_nonce,
+ chunk_t received_nonce,
+ chunk_t ike_sa_init_reply_data,
+ child_sa_t *child_sa);
+
+#endif /*IKE_AUTH_REQUESTED_H_*/
diff --git a/src/charon/sa/states/ike_sa_established.c b/src/charon/sa/states/ike_sa_established.c
new file mode 100644
index 000000000..e91409f6a
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_established.c
@@ -0,0 +1,239 @@
+/**
+ * @file ike_sa_established.c
+ *
+ * @brief Implementation of ike_sa_established_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_sa_established.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_ike_sa_established_t private_ike_sa_established_t;
+
+/**
+ * Private data of a ike_sa_established_t object.
+ */
+struct private_ike_sa_established_t {
+ /**
+ * methods of the state_t interface
+ */
+ ike_sa_established_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * Assigned logger. Use logger of IKE_SA.
+ */
+ logger_t *logger;
+
+ /**
+ * Process a notify payload
+ *
+ * @param this calling object
+ * @param notify_payload notify payload
+ * @param response response message of type INFORMATIONAL
+ *
+ * - SUCCESS
+ * - FAILED
+ * - DELETE_ME
+ */
+ status_t (*process_notify_payload) (private_ike_sa_established_t *this, notify_payload_t *notify_payload,message_t *response);
+};
+
+/**
+ * Implements state_t.get_state
+ */
+static status_t process_message(private_ike_sa_established_t *this, message_t *message)
+{
+ delete_payload_t *delete_request = NULL;
+ ike_sa_id_t *ike_sa_id;
+ iterator_t *payloads;
+ message_t *response;
+ crypter_t *crypter;
+ signer_t *signer;
+ status_t status;
+
+ if (message->get_exchange_type(message) != INFORMATIONAL)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_established",
+ mapping_find(exchange_type_m,message->get_exchange_type(message)));
+ return FAILED;
+ }
+
+ if (!message->get_request(message))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "INFORMATIONAL responses not handled in state ike_sa_established");
+ return FAILED;
+ }
+
+ ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public));
+
+ /* get signer for verification and crypter for decryption */
+ if (!ike_sa_id->is_initiator(ike_sa_id))
+ {
+ crypter = this->ike_sa->get_crypter_initiator(this->ike_sa);
+ signer = this->ike_sa->get_signer_initiator(this->ike_sa);
+ }
+ else
+ {
+ crypter = this->ike_sa->get_crypter_responder(this->ike_sa);
+ signer = this->ike_sa->get_signer_responder(this->ike_sa);
+ }
+
+ /* parse incoming message */
+ status = message->parse_body(message, crypter, signer);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "INFORMATIONAL request decryption failed. Ignoring message");
+ return status;
+ }
+
+ /* build empty INFORMATIONAL message */
+ this->ike_sa->build_message(this->ike_sa, INFORMATIONAL, FALSE, &response);
+
+ payloads = message->get_payload_iterator(message);
+
+ while (payloads->has_next(payloads))
+ {
+ payload_t *payload;
+ payloads->current(payloads, (void**)&payload);
+
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ {
+ notify_payload_t *notify_payload = (notify_payload_t *) payload;
+ /* handle the notify directly, abort if no further processing required */
+ status = this->process_notify_payload(this, notify_payload,response);
+ if (status != SUCCESS)
+ {
+ payloads->destroy(payloads);
+ response->destroy(response);
+ return status;
+ }
+ }
+ case DELETE:
+ {
+ delete_request = (delete_payload_t *) payload;
+ break;
+ }
+ default:
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)",
+ mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
+ break;
+ }
+ }
+ }
+ /* iterator can be destroyed */
+ payloads->destroy(payloads);
+
+ if (delete_request)
+ {
+ if (delete_request->get_protocol_id(delete_request) == PROTO_IKE)
+ {
+ this->logger->log(this->logger, AUDIT, "DELETE request for IKE_SA received");
+ response->destroy(response);
+ return DELETE_ME;
+ }
+ else
+ {
+ this->logger->log(this->logger, AUDIT, "DELETE request for CHILD_SA received. Ignored");
+ response->destroy(response);
+ return SUCCESS;
+ }
+ }
+
+ status = this->ike_sa->send_response(this->ike_sa, response);
+ /* message can now be sent (must not be destroyed) */
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to send INFORMATIONAL reply");
+ response->destroy(response);
+ return FAILED;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_established_t.process_notify_payload;
+ */
+static status_t process_notify_payload (private_ike_sa_established_t *this, notify_payload_t *notify_payload, message_t *response)
+{
+ notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s for protocol %s",
+ mapping_find(notify_message_type_m, notify_message_type),
+ mapping_find(protocol_id_m, notify_payload->get_protocol_id(notify_payload)));
+
+ switch (notify_message_type)
+ {
+ default:
+ {
+ this->logger->log(this->logger, AUDIT, "INFORMATIONAL request contained an unknown notify (%d), ignored.", notify_message_type);
+ }
+ }
+
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of state_t.get_state.
+ */
+static ike_sa_state_t get_state(private_ike_sa_established_t *this)
+{
+ return IKE_SA_ESTABLISHED;
+}
+
+/**
+ * Implementation of state_t.get_state
+ */
+static void destroy(private_ike_sa_established_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa)
+{
+ private_ike_sa_established_t *this = malloc_thing(private_ike_sa_established_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* private functions */
+ this->process_notify_payload = process_notify_payload;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/ike_sa_established.h b/src/charon/sa/states/ike_sa_established.h
new file mode 100644
index 000000000..8477ad5bc
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_established.h
@@ -0,0 +1,64 @@
+/**
+ * @file ike_sa_established.h
+ *
+ * @brief Interface of ike_sa_established_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_ESTABLISHED_H_
+#define IKE_SA_ESTABLISHED_H_
+
+#include <sa/states/state.h>
+#include <sa/ike_sa.h>
+
+typedef struct ike_sa_established_t ike_sa_established_t;
+
+/**
+ * @brief This class represents an the state of an established
+ * IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_sa_established_create()
+ *
+ * @todo Implement handling of CREATE_CHILD_SA requests
+ *
+ * @todo Implement initialization of CREATE_CHILD_SA requests
+ *
+ * @todo Implement handling of any other message
+ *
+ * @ingroup states
+ */
+struct ike_sa_established_t {
+ /**
+ * methods of the state_t interface
+ */
+ state_t state_interface;
+
+};
+
+/**
+ * @brief Constructor of class ike_sa_established_t
+ *
+ * @param ike_sa assigned ike_sa
+ * @return created ike_sa_established_t object
+ *
+ * @ingroup states
+ */
+ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa);
+
+#endif /*IKE_SA_ESTABLISHED_H_*/
diff --git a/src/charon/sa/states/ike_sa_init_requested.c b/src/charon/sa/states/ike_sa_init_requested.c
new file mode 100644
index 000000000..311cdf0a0
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_init_requested.c
@@ -0,0 +1,798 @@
+/**
+ * @file ike_sa_init_requested.c
+ *
+ * @brief Implementation of ike_sa_init_requested_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_sa_init_requested.h"
+
+#include <daemon.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <crypto/diffie_hellman.h>
+#include <sa/states/ike_auth_requested.h>
+#include <sa/states/initiator_init.h>
+#include <sa/authenticator.h>
+
+
+typedef struct private_ike_sa_init_requested_t private_ike_sa_init_requested_t;
+
+/**
+ * Private data of a ike_sa_init_requested_t object.
+ *
+ */
+struct private_ike_sa_init_requested_t {
+ /**
+ * Public interface of an ike_sa_init_requested_t object.
+ */
+ ike_sa_init_requested_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * Diffie Hellman object used to compute shared secret.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * Sent nonce value.
+ */
+ chunk_t sent_nonce;
+
+ /**
+ * Received nonce
+ */
+ chunk_t received_nonce;
+
+ /**
+ * Selected proposal
+ */
+ proposal_t *proposal;
+
+ /**
+ * Packet data of ike_sa_init request
+ */
+ chunk_t ike_sa_init_request_data;
+
+ /**
+ * Created child sa, if any
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * Assigned logger
+ *
+ * Is logger of ike_sa!
+ */
+ logger_t *logger;
+
+
+ /**
+ * Process NONCE payload of IKE_SA_INIT response.
+ *
+ * @param this calling object
+ * @param nonce_payload NONCE payload to process
+ * @return SUCCESS in any case
+ */
+ status_t (*process_nonce_payload) (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload);
+
+ /**
+ * Process SA payload of IKE_SA_INIT response.
+ *
+ * @param this calling object
+ * @param sa_payload SA payload to process
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*process_sa_payload) (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload);
+
+ /**
+ * Process KE payload of IKE_SA_INIT response.
+ *
+ * @param this calling object
+ * @param sa_payload KE payload to process
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*process_ke_payload) (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload);
+
+ /**
+ * Build ID payload for IKE_AUTH request.
+ *
+ * @param this calling object
+ * @param[out] id_payload buildet ID payload
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_id_payload) (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *response);
+
+ /**
+ * Build IDr payload for IKE_AUTH request.
+ *
+ * Only built when the ID of the responder contains no wildcards.
+ *
+ * @param this calling object
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_idr_payload) (private_ike_sa_init_requested_t *this, message_t *response);
+
+ /**
+ * Build AUTH payload for IKE_AUTH request.
+ *
+ * @param this calling object
+ * @param my_id_payload buildet ID payload
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_auth_payload) (private_ike_sa_init_requested_t *this,id_payload_t *my_id_payload, message_t *response);
+
+ /**
+ * Build SA payload for IKE_AUTH request.
+ *
+ * @param this calling object
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_sa_payload) (private_ike_sa_init_requested_t *this, message_t *response);
+
+ /**
+ * Build TSi payload for IKE_AUTH request.
+ *
+ * @param this calling object
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_tsi_payload) (private_ike_sa_init_requested_t *this, message_t *response);
+
+ /**
+ * Build TSr payload for IKE_AUTH request.
+ *
+ * @param this calling object
+ * @param response created payload will be added to this message_t object
+ * @return
+ * - SUCCESS
+ * - FAILED
+ */
+ status_t (*build_tsr_payload) (private_ike_sa_init_requested_t *this, message_t *response);
+
+ /**
+ * Process a notify payload and react.
+ *
+ * @param this calling object
+ * @param notify_payload notify_payload to handle
+ */
+ status_t (*process_notify_payload) (private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload);
+
+ /**
+ * Destroy function called internally of this class after state change to
+ * state IKE_AUTH_REQUESTED succeeded.
+ *
+ * This destroy function does not destroy objects which were passed to the new state.
+ *
+ * @param this calling object
+ */
+ void (*destroy_after_state_change) (private_ike_sa_init_requested_t *this);
+};
+
+/**
+ * Implementation of state_t.process_message.
+ */
+static status_t process_message(private_ike_sa_init_requested_t *this, message_t *ike_sa_init_reply)
+{
+ ike_auth_requested_t *next_state;
+ chunk_t ike_sa_init_reply_data;
+ sa_payload_t *sa_payload = NULL;
+ ke_payload_t *ke_payload = NULL;
+ id_payload_t *id_payload = NULL;
+ nonce_payload_t *nonce_payload = NULL;
+ u_int64_t responder_spi;
+ ike_sa_id_t *ike_sa_id;
+ iterator_t *payloads;
+ host_t *me;
+ connection_t *connection;
+ policy_t *policy;
+
+ message_t *request;
+ status_t status;
+
+ /*
+ * In this state a reply message of type IKE_SA_INIT is expected:
+ *
+ * <-- HDR, SAr1, KEr, Nr, [CERTREQ]
+ * or
+ * <-- HDR, N
+ */
+
+ if (ike_sa_init_reply->get_exchange_type(ike_sa_init_reply) != IKE_SA_INIT)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_init_requested",
+ mapping_find(exchange_type_m,ike_sa_init_reply->get_exchange_type(ike_sa_init_reply)));
+ return FAILED;
+ }
+
+ if (ike_sa_init_reply->get_request(ike_sa_init_reply))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT requests not allowed state ike_sa_init_responded");
+ return FAILED;
+ }
+
+ /* parse incoming message */
+ status = ike_sa_init_reply->parse_body(ike_sa_init_reply, NULL, NULL);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply parsing faild. Ignoring message");
+ return status;
+ }
+
+ /* because we are original initiator we have to update the responder SPI to the new one */
+ responder_spi = ike_sa_init_reply->get_responder_spi(ike_sa_init_reply);
+ if (responder_spi == 0)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply contained a SPI of zero");
+ return FAILED;
+ }
+ ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public));
+ ike_sa_id->set_responder_spi(ike_sa_id,responder_spi);
+
+ /* Iterate over all payloads.
+ *
+ * The message is allready checked for the right payload types.
+ */
+ payloads = ike_sa_init_reply->get_payload_iterator(ike_sa_init_reply);
+ while (payloads->has_next(payloads))
+ {
+ payload_t *payload;
+ payloads->current(payloads, (void**)&payload);
+
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ {
+ sa_payload = (sa_payload_t*)payload;
+ break;
+ }
+ case KEY_EXCHANGE:
+ {
+ ke_payload = (ke_payload_t*)payload;
+ break;
+ }
+ case NONCE:
+ {
+ nonce_payload = (nonce_payload_t*)payload;
+ break;
+ }
+ case NOTIFY:
+ {
+ notify_payload_t *notify_payload = (notify_payload_t *) payload;
+
+ status = this->process_notify_payload(this, notify_payload);
+ if (status != SUCCESS)
+ {
+ payloads->destroy(payloads);
+ return status;
+ }
+ break;
+ }
+ default:
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)",
+ mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
+ break;
+ }
+
+ }
+
+ }
+ payloads->destroy(payloads);
+
+ if (!(nonce_payload && sa_payload && ke_payload))
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply did not contain all required payloads. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ status = this->process_nonce_payload (this,nonce_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ status = this->process_sa_payload (this,sa_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ status = this->process_ke_payload (this,ke_payload);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ /* derive all the keys used in the IKE_SA */
+ status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->sent_nonce, this->received_nonce);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Transform objects could not be created from selected proposal. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* apply the address on wich we really received the packet */
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ me = ike_sa_init_reply->get_destination(ike_sa_init_reply);
+ connection->update_my_host(connection, me->clone(me));
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ policy->update_my_ts(policy, me);
+
+ /* build empty message */
+ this->ike_sa->build_message(this->ike_sa, IKE_AUTH, TRUE, &request);
+
+ status = this->build_id_payload(this, &id_payload, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+ status = this->build_idr_payload(this, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+ status = this->build_auth_payload(this, (id_payload_t*)id_payload, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+ status = this->build_sa_payload(this, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+ status = this->build_tsi_payload(this, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+ status = this->build_tsr_payload(this, request);
+ if (status != SUCCESS)
+ {
+ request->destroy(request);
+ return status;
+ }
+
+ /* message can now be sent (must not be destroyed) */
+ status = this->ike_sa->send_request(this->ike_sa, request);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to send IKE_AUTH request. Deleting IKE_SA");
+ request->destroy(request);
+ return DELETE_ME;
+ }
+
+ this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_sa_init_reply->get_message_id(ike_sa_init_reply));
+
+ ike_sa_init_reply_data = ike_sa_init_reply->get_packet_data(ike_sa_init_reply);
+
+ /* state can now be changed */
+ next_state = ike_auth_requested_create(this->ike_sa, this->sent_nonce, this->received_nonce,
+ ike_sa_init_reply_data, this->child_sa);
+ this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state);
+
+ this->destroy_after_state_change(this);
+ return SUCCESS;
+}
+
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.process_nonce_payload.
+ */
+status_t process_nonce_payload (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload)
+{
+ free(this->received_nonce.ptr);
+ this->received_nonce = nonce_payload->get_nonce(nonce_payload);
+ return SUCCESS;
+}
+
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.process_sa_payload.
+ */
+status_t process_sa_payload (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload)
+{
+ proposal_t *proposal;
+ linked_list_t *proposal_list;
+ connection_t *connection;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ /* get the list of selected proposals, the peer has to select only one proposal */
+ proposal_list = sa_payload->get_proposals (sa_payload);
+ if (proposal_list->get_count(proposal_list) != 1)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response did not contain a single proposal. Deleting IKE_SA");
+ while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
+ {
+ proposal->destroy(proposal);
+ }
+ proposal_list->destroy(proposal_list);
+ return DELETE_ME;
+ }
+
+ /* we have to re-check if the others selection is valid */
+ this->proposal = connection->select_proposal(connection, proposal_list);
+ while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
+ {
+ proposal->destroy(proposal);
+ }
+ proposal_list->destroy(proposal_list);
+
+ if (this->proposal == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained selected proposal we did not offer. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.process_ke_payload.
+ */
+status_t process_ke_payload (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload)
+{
+ this->diffie_hellman->set_other_public_value(this->diffie_hellman, ke_payload->get_key_exchange_data(ke_payload));
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_id_payload.
+ */
+static status_t build_id_payload (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *request)
+{
+ policy_t *policy;
+ id_payload_t *new_id_payload;
+ identification_t *identification;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ identification = policy->get_my_id(policy);
+ new_id_payload = id_payload_create_from_identification(TRUE, identification);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add ID payload to message");
+ request->add_payload(request,(payload_t *) new_id_payload);
+
+ *id_payload = new_id_payload;
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_idr_payload.
+ */
+static status_t build_idr_payload (private_ike_sa_init_requested_t *this, message_t *request)
+{
+ policy_t *policy;
+ id_payload_t *idr_payload;
+ identification_t *identification;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ identification = policy->get_other_id(policy);
+ if (!identification->contains_wildcards(identification))
+ {
+ idr_payload = id_payload_create_from_identification(FALSE, identification);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add IDr payload to message");
+ request->add_payload(request,(payload_t *) idr_payload);
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_auth_payload.
+ */
+static status_t build_auth_payload (private_ike_sa_init_requested_t *this, id_payload_t *my_id_payload, message_t *request)
+{
+ authenticator_t *authenticator;
+ auth_payload_t *auth_payload;
+ status_t status;
+
+ authenticator = authenticator_create(this->ike_sa);
+ status = authenticator->compute_auth_data(authenticator,&auth_payload,this->ike_sa_init_request_data,this->received_nonce,my_id_payload,TRUE);
+ authenticator->destroy(authenticator);
+
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not generate AUTH data for IKE_AUTH request. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add AUTH payload to message");
+ request->add_payload(request,(payload_t *) auth_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_sa_payload.
+ */
+static status_t build_sa_payload (private_ike_sa_init_requested_t *this, message_t *request)
+{
+ linked_list_t *proposal_list;
+ sa_payload_t *sa_payload;
+ policy_t *policy;
+ connection_t *connection;
+
+ /* get proposals form config, add to payload */
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ proposal_list = policy->get_proposals(policy);
+ /* build child sa */
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ this->child_sa = child_sa_create(connection->get_my_host(connection),
+ connection->get_other_host(connection));
+ if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add SA payload to message");
+ request->add_payload(request,(payload_t *) sa_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_tsi_payload.
+ */
+static status_t build_tsi_payload (private_ike_sa_init_requested_t *this, message_t *request)
+{
+ linked_list_t *ts_list;
+ ts_payload_t *ts_payload;
+ policy_t *policy;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ ts_list = policy->get_my_traffic_selectors(policy);
+ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSi payload to message");
+ request->add_payload(request,(payload_t *) ts_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.build_tsr_payload.
+ */
+static status_t build_tsr_payload (private_ike_sa_init_requested_t *this, message_t *request)
+{
+ linked_list_t *ts_list;
+ ts_payload_t *ts_payload;
+ policy_t *policy;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ ts_list = policy->get_other_traffic_selectors(policy);
+ ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSr payload to message");
+ request->add_payload(request,(payload_t *) ts_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.process_notify_payload.
+ */
+static status_t process_notify_payload(private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload)
+{
+ notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s",
+ mapping_find(notify_message_type_m, notify_message_type));
+
+ switch (notify_message_type)
+ {
+ case NO_PROPOSAL_CHOSEN:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a NO_PROPOSAL_CHOSEN notify. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ case INVALID_MAJOR_VERSION:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a INVALID_MAJOR_VERSION notify. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ case INVALID_KE_PAYLOAD:
+ {
+ initiator_init_t *initiator_init_state;
+ chunk_t notify_data;
+ diffie_hellman_group_t dh_group, old_dh_group;
+ connection_t *connection;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ old_dh_group = connection->get_dh_group(connection);
+ notify_data = notify_payload->get_notification_data(notify_payload);
+ dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
+
+ /* TODO:
+ * We are very restrictive here: If the other didn't accept
+ * our DH group, and we do not accept his offer, continuation
+ * is cancelled...
+ */
+
+ this->logger->log(this->logger, AUDIT, "Peer didn't accept %s, it requested %s!",
+ mapping_find(diffie_hellman_group_m, old_dh_group),
+ mapping_find(diffie_hellman_group_m, dh_group));
+ /* check if we can accept this dh group */
+ if (!connection->check_dh_group(connection, dh_group))
+ {
+ this->logger->log(this->logger, AUDIT,
+ "Peer does only accept DH group %s, which we do not accept! Aborting",
+ mapping_find(diffie_hellman_group_m, dh_group));
+ return DELETE_ME;
+ }
+
+ /* Going to change state back to initiator_init_t */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object");
+ initiator_init_state = initiator_init_create(this->ike_sa);
+
+ /* buffer of sent and received messages has to get reseted */
+ this->ike_sa->reset_message_buffers(this->ike_sa);
+
+ /* state can now be changed */
+ this->ike_sa->set_new_state(this->ike_sa,(state_t *) initiator_init_state);
+
+ /* state has NOW changed :-) */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy old sate object");
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Going to retry initialization of connection");
+
+ this->public.state_interface.destroy(&(this->public.state_interface));
+ if (initiator_init_state->retry_initiate_connection (initiator_init_state, dh_group) != SUCCESS)
+ {
+ return DELETE_ME;
+ }
+ return FAILED;
+ }
+ default:
+ {
+ /*
+ * - In case of unknown error: IKE_SA gets destroyed.
+ * - In case of unknown status: logging
+ */
+ if (notify_message_type < 16383)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply contained an unknown notify error (%d). Deleting IKE_SA",
+ notify_message_type);
+ return DELETE_ME;
+ }
+ else
+ {
+ this->logger->log(this->logger, CONTROL, "IKE_SA_INIT reply contained an unknown notify (%d), ignored.",
+ notify_message_type);
+ return SUCCESS;
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of state_t.get_state.
+ */
+static ike_sa_state_t get_state(private_ike_sa_init_requested_t *this)
+{
+ return IKE_SA_INIT_REQUESTED;
+}
+
+/**
+ * Implementation of private_ike_sa_init_requested_t.destroy_after_state_change.
+ */
+static void destroy_after_state_change (private_ike_sa_init_requested_t *this)
+{
+ this->diffie_hellman->destroy(this->diffie_hellman);
+ chunk_free(&(this->ike_sa_init_request_data));
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+ free(this);
+}
+
+/**
+ * Implementation state_t.destroy.
+ */
+static void destroy(private_ike_sa_init_requested_t *this)
+{
+ this->diffie_hellman->destroy(this->diffie_hellman);
+ free(this->sent_nonce.ptr);
+ free(this->received_nonce.ptr);
+ chunk_free(&(this->ike_sa_init_request_data));
+ if (this->child_sa)
+ {
+ this->child_sa->destroy(this->child_sa);
+ }
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa, diffie_hellman_t *diffie_hellman, chunk_t sent_nonce,chunk_t ike_sa_init_request_data)
+{
+ private_ike_sa_init_requested_t *this = malloc_thing(private_ike_sa_init_requested_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* private functions */
+ this->destroy_after_state_change = destroy_after_state_change;
+ this->process_nonce_payload = process_nonce_payload;
+ this->process_sa_payload = process_sa_payload;
+ this->process_ke_payload = process_ke_payload;
+ this->build_auth_payload = build_auth_payload;
+ this->build_tsi_payload = build_tsi_payload;
+ this->build_tsr_payload = build_tsr_payload;
+ this->build_id_payload = build_id_payload;
+ this->build_idr_payload = build_idr_payload;
+ this->build_sa_payload = build_sa_payload;
+ this->process_notify_payload = process_notify_payload;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->received_nonce = CHUNK_INITIALIZER;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+ this->diffie_hellman = diffie_hellman;
+ this->proposal = NULL;
+ this->sent_nonce = sent_nonce;
+ this->child_sa = NULL;
+ this->ike_sa_init_request_data = ike_sa_init_request_data;
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/ike_sa_init_requested.h b/src/charon/sa/states/ike_sa_init_requested.h
new file mode 100644
index 000000000..0a43afad1
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_init_requested.h
@@ -0,0 +1,68 @@
+/**
+ * @file ike_sa_init_requested.h
+ *
+ * @brief Interface of ike_sa_init_requestet_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_INIT_REQUESTED_H_
+#define IKE_SA_INIT_REQUESTED_H_
+
+#include <types.h>
+#include <sa/ike_sa.h>
+#include <sa/states/state.h>
+#include <crypto/diffie_hellman.h>
+
+typedef struct ike_sa_init_requested_t ike_sa_init_requested_t;
+
+/**
+ * @brief This class represents an IKE_SA state when
+ * requested an IKE_SA_INIT as initiator.
+ *
+ * @b Constructors:
+ * - ike_sa_init_requested_create()
+ *
+ * @todo Include valid child sa SPIs in proposal
+ *
+ * @ingroup states
+ */
+struct ike_sa_init_requested_t {
+ /**
+ * The state_t interface.
+ */
+ state_t state_interface;
+};
+
+/**
+ * Constructor of class ike_sa_init_requested_t.
+ *
+ * @param ike_sa assigned ike_sa
+ * @param diffie_hellman diffie_hellman object use to retrieve shared secret
+ * @param sent_nonce Sent nonce value
+ * @param ike_sa_init_request_data the binary representation of the IKE_SA_INIT request message
+ * @return created ike_sa_init_request_t object
+ *
+ * @ingroup states
+ */
+ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa,
+ diffie_hellman_t *diffie_hellman,
+ chunk_t sent_nonce,
+ chunk_t ike_sa_init_request_data);
+
+#endif /*IKE_SA_INIT_REQUESTED_H_*/
diff --git a/src/charon/sa/states/ike_sa_init_responded.c b/src/charon/sa/states/ike_sa_init_responded.c
new file mode 100644
index 000000000..e40b0cf22
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_init_responded.c
@@ -0,0 +1,695 @@
+/**
+ * @file ike_sa_init_responded.c
+ *
+ * @brief State of a IKE_SA after responding to an IKE_SA_INIT request
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_init_responded.h"
+
+#include <daemon.h>
+#include <sa/authenticator.h>
+#include <sa/child_sa.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/signers/signer.h>
+#include <crypto/crypters/crypter.h>
+#include <sa/states/ike_sa_established.h>
+
+
+typedef struct private_ike_sa_init_responded_t private_ike_sa_init_responded_t;
+
+/**
+ * Private data of a ike_sa_init_responded_t object.
+ *
+ */
+struct private_ike_sa_init_responded_t {
+ /**
+ * Public interface of ike_sa_init_responded_t.
+ */
+ ike_sa_init_responded_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * Received nonce.
+ */
+ chunk_t received_nonce;
+
+ /**
+ * Sent nonce.
+ */
+ chunk_t sent_nonce;
+
+ /**
+ * Binary representation of the IKE_SA_INIT response.
+ */
+ chunk_t ike_sa_init_response_data;
+
+ /**
+ * Binary representation of the IKE_SA_INIT request.
+ */
+ chunk_t ike_sa_init_request_data;
+
+ /**
+ * SA config to use.
+ */
+ policy_t *policy;
+
+ /**
+ * CHILD_SA, if set up
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * Traffic selectors applicable at our site
+ */
+ linked_list_t *my_ts;
+
+ /**
+ * Traffic selectors applicable at remote site
+ */
+ linked_list_t *other_ts;
+
+ /**
+ * Assigned logger.
+ *
+ * Is logger of ike_sa!
+ */
+ logger_t *logger;
+
+ /**
+ * Process received IDi and IDr payload and build IDr payload for IKE_AUTH response.
+ *
+ * @param this calling object
+ * @param request_idi ID payload representing initiator
+ * @param request_idr ID payload representing responder (May be zero)
+ * @param response The created IDr payload is added to this message_t object
+ * @param response_idr The created IDr payload is also written to this location
+ */
+ status_t (*build_idr_payload) (private_ike_sa_init_responded_t *this,
+ id_payload_t *request_idi,
+ id_payload_t *request_idr,
+ message_t *response,
+ id_payload_t **response_idr);
+
+ /**
+ * Process received SA payload and build SA payload for IKE_AUTH response.
+ *
+ * @param this calling object
+ * @param request SA payload received in IKE_AUTH request
+ * @param response The created SA payload is added to this message_t object
+ */
+ status_t (*build_sa_payload) (private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *response);
+
+ /**
+ * Process received AUTH payload and build AUTH payload for IKE_AUTH response.
+ *
+ * @param this calling object
+ * @param request AUTH payload received in IKE_AUTH request
+ * @param other_id_payload other ID payload needed to verify AUTH data
+ * @param my_id_payload my ID payload needed to compute AUTH data
+ * @param response The created AUTH payload is added to this message_t object
+ */
+ status_t (*build_auth_payload) (private_ike_sa_init_responded_t *this, auth_payload_t *request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* response);
+
+ /**
+ * Process received TS payload and build TS payload for IKE_AUTH response.
+ *
+ * @param this calling object
+ * @param is_initiator type of TS payload. TRUE for TSi, FALSE for TSr
+ * @param request TS payload received in IKE_AUTH request
+ * @param response the created TS payload is added to this message_t object
+ */
+ status_t (*build_ts_payload) (private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t *response);
+
+ /**
+ * Sends a IKE_AUTH reply containing a notify payload.
+ *
+ * @param this calling object
+ * @param notify_payload payload to process
+ * @return
+ * - DELETE_ME if IKE_SA should be deleted
+ * - SUCCSS if processed successfull
+ */
+ status_t (*process_notify_payload) (private_ike_sa_init_responded_t *this, notify_payload_t* notify_payload);
+
+ /**
+ * Destroy function called internally of this class after state change to
+ * state IKE_SA_ESTABLISHED succeeded.
+ *
+ * This destroy function does not destroy objects which were passed to the new state.
+ *
+ * @param this calling object
+ */
+ void (*destroy_after_state_change) (private_ike_sa_init_responded_t *this);
+};
+
+/**
+ * Implements state_t.get_state
+ */
+static status_t process_message(private_ike_sa_init_responded_t *this, message_t *request)
+{
+ id_payload_t *idi_request = NULL, *idr_request = NULL,*idr_response;
+ ts_payload_t *tsi_request = NULL, *tsr_request = NULL;
+ auth_payload_t *auth_request = NULL;
+ sa_payload_t *sa_request = NULL;
+ iterator_t *payloads;
+ message_t *response;
+ crypter_t *crypter;
+ signer_t *signer;
+ status_t status;
+ host_t *my_host, *other_host;
+ connection_t *connection;
+
+ if (request->get_exchange_type(request) != IKE_AUTH)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_init_responded",
+ mapping_find(exchange_type_m,request->get_exchange_type(request)));
+ return FAILED;
+ }
+
+ if (!request->get_request(request))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH responses not allowed state ike_sa_init_responded");
+ return FAILED;
+ }
+
+ /* get signer for verification and crypter for decryption */
+ signer = this->ike_sa->get_signer_initiator(this->ike_sa);
+ crypter = this->ike_sa->get_crypter_initiator(this->ike_sa);
+
+ status = request->parse_body(request, crypter, signer);
+ if (status != SUCCESS)
+ {
+ if (status == NOT_SUPPORTED)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH request contains unsupported payload with critical flag set."
+ "Deleting IKE_SA");
+ this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER);
+ return DELETE_ME;
+ }
+ else
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH request decryption faild. Ignoring message");
+ }
+ return status;
+ }
+
+ /* iterate over incoming payloads. Message is verified, we can be sure there are the required payloads */
+ payloads = request->get_payload_iterator(request);
+ while (payloads->has_next(payloads))
+ {
+ payload_t *payload;
+ payloads->current(payloads, (void**)&payload);
+
+ switch (payload->get_type(payload))
+ {
+ case ID_INITIATOR:
+ {
+ idi_request = (id_payload_t*)payload;
+ break;
+ }
+ case AUTHENTICATION:
+ {
+ auth_request = (auth_payload_t*)payload;
+ break;
+ }
+ case ID_RESPONDER:
+ {
+ idr_request = (id_payload_t*)payload;
+ break;
+ }
+ case SECURITY_ASSOCIATION:
+ {
+ sa_request = (sa_payload_t*)payload;
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ {
+ tsi_request = (ts_payload_t*)payload;
+ break;
+ }
+ case TRAFFIC_SELECTOR_RESPONDER:
+ {
+ tsr_request = (ts_payload_t*)payload;
+ break;
+ }
+ case NOTIFY:
+ {
+ notify_payload_t *notify_payload = (notify_payload_t *) payload;
+ status = this->process_notify_payload(this, notify_payload);
+ if (status != SUCCESS)
+ {
+ payloads->destroy(payloads);
+ return status;
+ }
+ }
+ case CERTIFICATE:
+ {
+ /* TODO handle cert payloads */
+ }
+ case CERTIFICATE_REQUEST:
+ {
+ /* TODO handle certrequest payloads */
+ }
+ default:
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)",
+ mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
+ break;
+ }
+ }
+ }
+ /* iterator can be destroyed */
+ payloads->destroy(payloads);
+
+ /* check if we have all payloads */
+ if (!(idi_request && sa_request && auth_request && tsi_request && tsr_request))
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* build response */
+ this->ike_sa->build_message(this->ike_sa, IKE_AUTH, FALSE, &response);
+
+ /* add payloads to it */
+ status = this->build_idr_payload(this, idi_request, idr_request, response,&idr_response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+ status = this->build_auth_payload(this, auth_request,idi_request, idr_response,response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+ status = this->build_sa_payload(this, sa_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+ status = this->build_ts_payload(this, TRUE, tsi_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+ status = this->build_ts_payload(this, FALSE, tsr_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+
+ status = this->ike_sa->send_response(this->ike_sa, response);
+ /* message can now be sent (must not be destroyed) */
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to send IKE_AUTH reply. Deleting IKE_SA");
+ response->destroy(response);
+ return DELETE_ME;
+ }
+
+ /* install child SA policies */
+ if (!this->child_sa)
+ {
+ this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built");
+ }
+ else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0)
+ {
+ this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built");
+ this->child_sa->destroy(this->child_sa);
+ this->child_sa = NULL;
+ }
+ else
+ {
+ status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA");
+ return DELETE_ME;
+ }
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+ }
+
+ /* create new state */
+ this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa));
+ this->destroy_after_state_change(this);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ my_host = connection->get_my_host(connection);
+ other_host = connection->get_other_host(connection);
+ this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s",
+ my_host->get_address(my_host), other_host->get_address(other_host));
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_responded_t.build_idr_payload.
+ */
+static status_t build_idr_payload(private_ike_sa_init_responded_t *this, id_payload_t *request_idi, id_payload_t *request_idr, message_t *response,id_payload_t **response_idr)
+{
+ identification_t *other_id, *my_id = NULL;
+ connection_t *connection;
+ id_payload_t *idr_response;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ /* update adresses, as connection may contain wildcards, or wrong IDs */
+ other_id = request_idi->get_identification(request_idi);
+ if (request_idr)
+ {
+ my_id = request_idr->get_identification(request_idr);
+ connection->update_my_id(connection, my_id);
+ }
+ else
+ {
+ my_id = connection->get_my_id(connection);
+ }
+ connection->update_other_id(connection, other_id);
+
+ /* build new sa config */
+ this->policy = charon->policies->get_policy(charon->policies, my_id, other_id);
+ if (this->policy == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "We don't have a policy for IDs %s - %s. Deleting IKE_SA",
+ my_id->get_string(my_id), other_id->get_string(other_id));
+ return DELETE_ME;
+ }
+
+ /* get my id from policy, which must contain a fully qualified valid id */
+ my_id = this->policy->get_my_id(this->policy);
+
+ /* update others traffic selectors with actually used address */
+ this->policy->update_other_ts(this->policy, response->get_destination(response));
+
+ /* set policy in ike_sa for other states */
+ this->ike_sa->set_policy(this->ike_sa, this->policy);
+
+ /* build response */
+ idr_response = id_payload_create_from_identification(FALSE, my_id);
+ response->add_payload(response, (payload_t*)idr_response);
+ *response_idr = idr_response;
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_responded_t.build_sa_payload.
+ */
+static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *response)
+{
+ proposal_t *proposal, *proposal_tmp;
+ linked_list_t *proposal_list;
+ sa_payload_t *sa_response;
+ chunk_t seed;
+ prf_plus_t *prf_plus;
+ status_t status;
+ connection_t *connection;
+
+ /* get proposals from request */
+ proposal_list = request->get_proposals(request);
+ if (proposal_list->get_count(proposal_list) == 0)
+ {
+ /* if the other side did not offer any proposals, we do not create child sa's */
+ this->logger->log(this->logger, AUDIT, "IKE_AUH request did not contain any proposals. No CHILD_SA created");
+ sa_response = sa_payload_create();
+ response->add_payload(response, (payload_t*)sa_response);
+ proposal_list->destroy(proposal_list);
+ return SUCCESS;
+ }
+
+ /* now select a proposal */
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Selecting proposals:");
+ proposal = this->policy->select_proposal(this->policy, proposal_list);
+ /* list is not needed anymore */
+ while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS)
+ {
+ proposal_tmp->destroy(proposal_tmp);
+ }
+ proposal_list->destroy(proposal_list);
+ /* do we have a proposal */
+ if (proposal == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any proposals we accept. Deleting IKE_SA");
+ this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER);
+ return DELETE_ME;
+ }
+
+ /* set up child sa */
+ seed = chunk_alloc(this->received_nonce.len + this->sent_nonce.len);
+ memcpy(seed.ptr, this->received_nonce.ptr, this->received_nonce.len);
+ memcpy(seed.ptr + this->received_nonce.len, this->sent_nonce.ptr, this->sent_nonce.len);
+ prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
+ chunk_free(&seed);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ this->child_sa = child_sa_create(connection->get_my_host(connection),
+ connection->get_other_host(connection));
+
+ status = this->child_sa->add(this->child_sa, proposal, prf_plus);
+ prf_plus->destroy(prf_plus);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* create payload with selected propsal */
+ sa_response = sa_payload_create_from_proposal(proposal);
+ response->add_payload(response, (payload_t*)sa_response);
+ proposal->destroy(proposal);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_responded_t.build_auth_payload.
+ */
+static status_t build_auth_payload(private_ike_sa_init_responded_t *this, auth_payload_t *auth_request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* response)
+{
+ authenticator_t *authenticator;
+ auth_payload_t *auth_reply;
+ status_t status;
+
+ authenticator = authenticator_create(this->ike_sa);
+ status = authenticator->verify_auth_data(authenticator,auth_request, this->ike_sa_init_request_data,this->sent_nonce,other_id_payload,TRUE);
+
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH request verification failed. Deleting IKE_SA");
+ this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, AUTHENTICATION_FAILED, CHUNK_INITIALIZER);
+ authenticator->destroy(authenticator);
+ return DELETE_ME;
+ }
+
+ status = authenticator->compute_auth_data(authenticator,&auth_reply, this->ike_sa_init_response_data,this->received_nonce,my_id_payload,FALSE);
+ authenticator->destroy(authenticator);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to build authentication data for IKE_AUTH reply. Deleting IKE_SA");
+ return DELETE_ME;
+
+ }
+
+ response->add_payload(response, (payload_t *)auth_reply);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_ike_sa_init_responded_t.build_ts_payload.
+ */
+static status_t build_ts_payload(private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t* response)
+{
+ linked_list_t *ts_received, *ts_selected;
+ traffic_selector_t *ts;
+ status_t status = SUCCESS;
+ ts_payload_t *ts_response;
+
+ /* build a reply payload with selected traffic selectors */
+ ts_received = request->get_traffic_selectors(request);
+ /* select ts depending on payload type */
+ if (ts_initiator)
+ {
+ ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received);
+ this->other_ts = ts_selected;
+ }
+ else
+ {
+ ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received);
+ this->my_ts = ts_selected;
+ }
+
+ ts_response = ts_payload_create_from_traffic_selectors(ts_initiator, ts_selected);
+ response->add_payload(response, (payload_t*)ts_response);
+
+ /* cleanup */
+ while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ ts_received->destroy(ts_received);
+
+ return status;
+}
+
+static status_t process_notify_payload(private_ike_sa_init_responded_t *this, notify_payload_t *notify_payload)
+{
+ notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s",
+ mapping_find(notify_message_type_m, notify_message_type));
+
+ switch (notify_message_type)
+ {
+ case SET_WINDOW_SIZE:
+ /*
+ * TODO Increase window size.
+ */
+ case INITIAL_CONTACT:
+ /*
+ * TODO Delete existing IKE_SA's with other Identity.
+ */
+ default:
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_AUTH request contained an unknown notify (%d), ignored.", notify_message_type);
+ }
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of state_t.get_state.
+ */
+static ike_sa_state_t get_state(private_ike_sa_init_responded_t *this)
+{
+ return IKE_SA_INIT_RESPONDED;
+}
+
+/**
+ * Implementation of state_t.destroy.
+ */
+static void destroy(private_ike_sa_init_responded_t *this)
+{
+ chunk_free(&(this->received_nonce));
+ chunk_free(&(this->sent_nonce));
+ chunk_free(&(this->ike_sa_init_response_data));
+ chunk_free(&(this->ike_sa_init_request_data));
+ if (this->my_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->my_ts->destroy(this->my_ts);
+ }
+ if (this->other_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->other_ts->destroy(this->other_ts);
+ }
+ if (this->child_sa)
+ {
+ this->child_sa->destroy(this->child_sa);
+ }
+
+ free(this);
+}
+/**
+ * Implementation of private_ike_sa_init_responded.destroy_after_state_change.
+ */
+static void destroy_after_state_change(private_ike_sa_init_responded_t *this)
+{
+ chunk_free(&(this->received_nonce));
+ chunk_free(&(this->sent_nonce));
+ chunk_free(&(this->ike_sa_init_response_data));
+ chunk_free(&(this->ike_sa_init_request_data));
+ if (this->my_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->my_ts->destroy(this->my_ts);
+ }
+ if (this->other_ts)
+ {
+ traffic_selector_t *ts;
+ while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ this->other_ts->destroy(this->other_ts);
+ }
+
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_nonce,chunk_t ike_sa_init_request_data, chunk_t ike_sa_init_response_data)
+{
+ private_ike_sa_init_responded_t *this = malloc_thing(private_ike_sa_init_responded_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* private functions */
+ this->build_idr_payload = build_idr_payload;
+ this->build_sa_payload = build_sa_payload;
+ this->build_auth_payload = build_auth_payload;
+ this->build_ts_payload = build_ts_payload;
+ this->process_notify_payload = process_notify_payload;
+ this->destroy_after_state_change = destroy_after_state_change;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->received_nonce = received_nonce;
+ this->sent_nonce = sent_nonce;
+ this->ike_sa_init_response_data = ike_sa_init_response_data;
+ this->ike_sa_init_request_data = ike_sa_init_request_data;
+ this->my_ts = NULL;
+ this->other_ts = NULL;
+ this->child_sa = NULL;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/ike_sa_init_responded.h b/src/charon/sa/states/ike_sa_init_responded.h
new file mode 100644
index 000000000..43aecf26f
--- /dev/null
+++ b/src/charon/sa/states/ike_sa_init_responded.h
@@ -0,0 +1,73 @@
+/**
+ * @file ike_sa_init_responded.h
+ *
+ * @brief Interface of ike_sa_init_responded_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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_SA_INIT_RESPONDED_H_
+#define IKE_SA_INIT_RESPONDED_H_
+
+#include <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+typedef struct ike_sa_init_responded_t ike_sa_init_responded_t;
+
+/**
+ * @brief This class represents an IKE_SA state when
+ * responded to an IKE_SA_INIT request.
+ *
+ * The state accpets IKE_AUTH requests. It proves the authenticity
+ * and sets up the first child sa. Then it sends back an IKE_AUTH
+ * reply and changes to the IKE_SA_ESTABLISHED state.
+ *
+ * @b Constructors:
+ * - ike_sa_init_response_data()
+ *
+ * @todo Implement handling of SET_WINDOW_SIZE notify
+ *
+ * @todo Implement handling of INITIAL_CONTACT notify
+ *
+ * @ingroup states
+ */
+struct ike_sa_init_responded_t {
+ /**
+ * The state_t interface.
+ */
+ state_t state_interface;
+
+};
+
+/**
+ * @brief Constructor of class ike_sa_init_responded_t
+ *
+ * @param ike_sa assigned IKE_SA
+ * @param received_nonce received nonce data in IKE_SA_INIT request
+ * @param sent_nonce sent nonce data in IKE_SA_INIT response
+ * @param ike_sa_init_request_data binary representation of received IKE_SA_INIT request
+ * @param ike_sa_init_response_data binary representation of sent IKE_SA_INIT response
+ *
+ * @ingroup states
+ */
+ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa,
+ chunk_t received_nonce,
+ chunk_t sent_nonce,
+ chunk_t ike_sa_init_request_data,
+ chunk_t ike_sa_init_response_data);
+
+#endif /*IKE_SA_INIT_RESPONDED_H_*/
diff --git a/src/charon/sa/states/initiator_init.c b/src/charon/sa/states/initiator_init.c
new file mode 100644
index 000000000..35d15235d
--- /dev/null
+++ b/src/charon/sa/states/initiator_init.c
@@ -0,0 +1,360 @@
+/**
+ * @file initiator_init.c
+ *
+ * @brief Implementation of initiator_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "initiator_init.h"
+
+
+#include <daemon.h>
+#include <sa/states/state.h>
+#include <sa/states/ike_sa_init_requested.h>
+#include <queues/jobs/retransmit_request_job.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+typedef struct private_initiator_init_t private_initiator_init_t;
+
+/**
+ * Private data of a initiator_init_t object..
+ *
+ */
+struct private_initiator_init_t {
+ /**
+ * Methods of the state_t interface.
+ */
+ initiator_init_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * Diffie hellman object used to generate public DH value.
+ * This objet is passed to the next state of type IKE_SA_INIT_REQUESTED.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * Sent nonce.
+ * This nonce is passed to the next state of type IKE_SA_INIT_REQUESTED.
+ */
+ chunk_t sent_nonce;
+
+ /**
+ * Assigned logger.
+ *
+ * Is logger of ike_sa!
+ */
+ logger_t *logger;
+
+ /**
+ * Builds the SA payload for this state.
+ *
+ * @param this calling object
+ * @param request message_t object to add the SA payload
+ */
+ void (*build_sa_payload) (private_initiator_init_t *this, message_t *request);
+
+ /**
+ * Builds the KE payload for this state.
+ *
+ * @param this calling object
+ * @param request message_t object to add the KE payload
+ */
+ void (*build_ke_payload) (private_initiator_init_t *this, message_t *request);
+
+ /**
+ * Builds the NONCE payload for this state.
+ *
+ * @param this calling object
+ * @param request message_t object to add the NONCE payload
+ */
+ status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *request);
+
+ /**
+ * Destroy function called internally of this class after state change to state
+ * IKE_SA_INIT_REQUESTED succeeded.
+ *
+ * This destroy function does not destroy objects which were passed to the new state.
+ *
+ * @param this calling object
+ */
+ void (*destroy_after_state_change) (private_initiator_init_t *this);
+};
+
+/**
+ * Implementation of initiator_init_t.initiate_connection.
+ */
+static status_t initiate_connection (private_initiator_init_t *this, connection_t *connection)
+{
+ policy_t *policy;
+ diffie_hellman_group_t dh_group;
+ host_t *my_host, *other_host;
+ identification_t *my_id, *other_id;
+
+ my_host = connection->get_my_host(connection);
+ other_host = connection->get_other_host(connection);
+ my_id = connection->get_my_id(connection);
+ other_id = connection->get_other_id(connection);
+
+ this->logger->log(this->logger, CONTROL, "Initiating connection between %s (%s) - %s (%s)",
+ my_id->get_string(my_id), my_host->get_address(my_host),
+ other_id->get_string(other_id), other_host->get_address(other_host));
+
+ this->ike_sa->set_connection(this->ike_sa, connection);
+
+ /* get policy */
+ policy = charon->policies->get_policy(charon->policies, my_id, other_id);
+ if (policy == NULL)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Could not get a policy for '%s - %s', aborting",
+ my_id->get_string(my_id), other_id->get_string(other_id));
+ return DELETE_ME;
+ }
+ this->ike_sa->set_policy(this->ike_sa,policy);
+
+ /* we must guess now a DH group. For that we choose our most preferred group */
+ dh_group = connection->get_dh_group(connection);
+
+ /* next step is done in retry_initiate_connection */
+ return this->public.retry_initiate_connection(&this->public, dh_group);
+}
+
+/**
+ * Implementation of initiator_init_t.retry_initiate_connection.
+ */
+status_t retry_initiate_connection (private_initiator_init_t *this, diffie_hellman_group_t dh_group)
+{
+ ike_sa_init_requested_t *next_state;
+ chunk_t ike_sa_init_request_data;
+ connection_t *connection;
+ ike_sa_id_t *ike_sa_id;
+ message_t *message;
+ status_t status;
+
+ if (dh_group == MODP_UNDEFINED)
+ {
+ this->logger->log(this->logger, AUDIT, "No DH group acceptable for initialization, Aborting");
+ return DELETE_ME;
+ }
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ this->diffie_hellman = diffie_hellman_create(dh_group);
+ ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public));
+ ike_sa_id->set_responder_spi(ike_sa_id,0);
+
+ /* going to build message */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Going to build message");
+ this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, TRUE, &message);
+
+ /* build SA payload */
+ this->build_sa_payload(this, message);
+
+ /* build KE payload */
+ this->build_ke_payload(this, message);
+
+ /* build Nonce payload */
+ status = this->build_nonce_payload(this, message);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR, "Building nonce payload failed. Aborting");
+ message->destroy(message);
+ return DELETE_ME;
+ }
+
+ /* message can now be sent (must not be destroyed) */
+ status = this->ike_sa->send_request(this->ike_sa, message);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to initiate connection, could not send message. Aborting");
+ message->destroy(message);
+ return DELETE_ME;
+ }
+
+ message = this->ike_sa->get_last_requested_message(this->ike_sa);
+
+ ike_sa_init_request_data = message->get_packet_data(message);
+
+ /* state can now be changed */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object");
+ next_state = ike_sa_init_requested_create(this->ike_sa, this->diffie_hellman, this->sent_nonce,ike_sa_init_request_data);
+ this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy old sate object");
+ this->destroy_after_state_change(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_initiator_init_t.build_sa_payload.
+ */
+static void build_sa_payload(private_initiator_init_t *this, message_t *request)
+{
+ sa_payload_t* sa_payload;
+ linked_list_t *proposal_list;
+ connection_t *connection;
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Building SA payload");
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ proposal_list = connection->get_proposals(connection);
+
+ sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add SA payload to message");
+ request->add_payload(request, (payload_t *) sa_payload);
+}
+
+/**
+ * Implementation of private_initiator_init_t.build_ke_payload.
+ */
+static void build_ke_payload(private_initiator_init_t *this, message_t *request)
+{
+ ke_payload_t *ke_payload;
+ chunk_t key_data;
+ diffie_hellman_group_t dh_group;
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Building KE payload");
+
+ this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data);
+ dh_group = this->diffie_hellman->get_dh_group(this->diffie_hellman);
+
+ ke_payload = ke_payload_create();
+ ke_payload->set_dh_group_number(ke_payload, dh_group);
+ ke_payload->set_key_exchange_data(ke_payload, key_data);
+
+ chunk_free(&key_data);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add KE payload to message");
+ request->add_payload(request, (payload_t *) ke_payload);
+}
+
+/**
+ * Implementation of private_initiator_init_t.build_nonce_payload.
+ */
+static status_t build_nonce_payload(private_initiator_init_t *this, message_t *request)
+{
+ nonce_payload_t *nonce_payload;
+ randomizer_t *randomizer;
+ status_t status;
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Building NONCE payload");
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Get pseudo random bytes for NONCE");
+ randomizer = this->ike_sa->get_randomizer(this->ike_sa);
+
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce));
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ this->logger->log(this->logger, RAW|LEVEL2, "Initiator NONCE",&(this->sent_nonce));
+
+ nonce_payload = nonce_payload_create();
+
+ nonce_payload->set_nonce(nonce_payload, this->sent_nonce);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add NONCE payload to message");
+ request->add_payload(request, (payload_t *) nonce_payload);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of state_t.process_message.
+ */
+static status_t process_message(private_initiator_init_t *this, message_t *message)
+{
+ this->logger->log(this->logger, ERROR, "In state INITIATOR_INIT, no message is processed");
+ return FAILED;
+}
+
+/**
+ * Implementation of state_t.get_state.
+ */
+static ike_sa_state_t get_state(private_initiator_init_t *this)
+{
+ return INITIATOR_INIT;
+}
+
+/**
+ * Implementation of state_t.destroy.
+ */
+static void destroy(private_initiator_init_t *this)
+{
+ this->logger->log(this->logger, CONTROL | LEVEL3, "Going to destroy initiator_init_t state object");
+
+ /* destroy diffie hellman object */
+ if (this->diffie_hellman != NULL)
+ {
+ this->diffie_hellman->destroy(this->diffie_hellman);
+ }
+ if (this->sent_nonce.ptr != NULL)
+ {
+ free(this->sent_nonce.ptr);
+ }
+ free(this);
+}
+
+/**
+ * Implementation of private_initiator_init_t.destroy_after_state_change
+ */
+static void destroy_after_state_change (private_initiator_init_t *this)
+{
+ this->logger->log(this->logger, CONTROL | LEVEL3, "Going to destroy initiator_init_t state object");
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa)
+{
+ private_initiator_init_t *this = malloc_thing(private_initiator_init_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* public functions */
+ this->public.initiate_connection = (status_t (*)(initiator_init_t *, connection_t*)) initiate_connection;
+ this->public.retry_initiate_connection = (status_t (*)(initiator_init_t *, int )) retry_initiate_connection;
+
+ /* private functions */
+ this->destroy_after_state_change = destroy_after_state_change;
+ this->build_nonce_payload = build_nonce_payload;
+ this->build_sa_payload = build_sa_payload;
+ this->build_ke_payload = build_ke_payload;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+ this->sent_nonce = CHUNK_INITIALIZER;
+ this->diffie_hellman = NULL;
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/initiator_init.h b/src/charon/sa/states/initiator_init.h
new file mode 100644
index 000000000..6b4940a73
--- /dev/null
+++ b/src/charon/sa/states/initiator_init.h
@@ -0,0 +1,84 @@
+/**
+ * @file initiator_init.h
+ *
+ * @brief Interface of initiator_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 INITIATOR_INIT_H_
+#define INITIATOR_INIT_H_
+
+#include <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+
+typedef struct initiator_init_t initiator_init_t;
+
+/**
+ * @brief This class represents an IKE_SA state when
+ * initializing a connection as initiator.
+ *
+ * @b Constructors:
+ * - initiator_init_create()
+ *
+ * @ingroup states
+ */
+struct initiator_init_t {
+ /**
+ * The state_t interface.
+ */
+ state_t state_interface;
+
+ /**
+ * Initiate a new connection with given connection_t object.
+ *
+ * @param this calling object
+ * @param connection connection to initiate
+ * @return
+ * - SUCCESS
+ * - DELETE_ME if something failed
+ */
+ status_t (*initiate_connection) (initiator_init_t *this, connection_t *connection);
+
+ /**
+ * Retry to initiate a new connection with a specific dh_group_priority.
+ *
+ * The dh_group_priority is starting at 1.
+ *
+ * @param this calling object
+ * @param dh_group_priority dh group priority to try with
+ * @return
+ * - SUCCESS
+ * - DELETE_ME if something failed (see log for error)
+ */
+ status_t (*retry_initiate_connection) (initiator_init_t *this, int dh_group_priority);
+};
+
+/**
+ * @brief Constructor of class initiator_init_t.
+ *
+ * @param ike_sa assigned IKE_SA
+ * @return created initiator_init_t object
+ *
+ * @ingroup states
+ */
+initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa);
+
+
+#endif /*INITIATOR_INIT_H_*/
diff --git a/src/charon/sa/states/responder_init.c b/src/charon/sa/states/responder_init.c
new file mode 100644
index 000000000..10acf645c
--- /dev/null
+++ b/src/charon/sa/states/responder_init.c
@@ -0,0 +1,568 @@
+/**
+ * @file responder_init.c
+ *
+ * @brief Implementation of responder_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "responder_init.h"
+
+#include <daemon.h>
+#include <sa/states/state.h>
+#include <sa/states/ike_sa_init_responded.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/diffie_hellman.h>
+
+
+typedef struct private_responder_init_t private_responder_init_t;
+
+/**
+ * Private data of a responder_init_t object.
+ *
+ */
+struct private_responder_init_t {
+ /**
+ * Methods of the state_t interface.
+ */
+ responder_init_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ protected_ike_sa_t *ike_sa;
+
+ /**
+ * Diffie Hellman object used to compute shared secret.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * Diffie Hellman group number from selected IKE proposal.
+ */
+ u_int16_t dh_group_number;
+
+ /**
+ * Priority used to get matching dh_group number.
+ */
+ u_int16_t dh_group_priority;
+
+ /**
+ * Sent nonce value.
+ *
+ * This value is passed to the next state of type IKE_SA_INIT_RESPONDED.
+ */
+ chunk_t sent_nonce;
+
+ /**
+ * Received nonce value
+ *
+ * This value is passed to the next state of type IKE_SA_INIT_RESPONDED.
+ */
+ chunk_t received_nonce;
+
+ /**
+ * Selected proposal
+ */
+ proposal_t *proposal;
+
+ /**
+ * Logger used to log data .
+ *
+ * Is logger of ike_sa!
+ */
+ logger_t *logger;
+
+ /**
+ * Handles received SA payload and builds the SA payload for the response.
+ *
+ * @param this calling object
+ * @param sa_request The received SA payload
+ * @param response the SA payload is added to this response message_t object.
+ * @return
+ * - DELETE_ME
+ * - SUCCESS
+ */
+ status_t (*build_sa_payload) (private_responder_init_t *this,sa_payload_t *sa_request, message_t *response);
+
+ /**
+ * Handles received KE payload and builds the KE payload for the response.
+ *
+ * @param this calling object
+ * @param ke_request The received KE payload
+ * @param response the KE payload is added to this response message_t object.
+ * - DELETE_ME
+ * - SUCCESS
+ */
+ status_t (*build_ke_payload) (private_responder_init_t *this,ke_payload_t *ke_request, message_t *response);
+
+ /**
+ * Handles received NONCE payload and builds the NONCE payload for the response.
+ *
+ * @param this calling object
+ * @param nonce_request The received NONCE payload
+ * @param response the NONCE payload is added to this response message_t object.
+ * - DELETE_ME
+ * - SUCCESS
+ */
+ status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response);
+
+ /**
+ * Sends a IKE_SA_INIT reply containing a notify payload.
+ *
+ * @param this calling object
+ * @param notify_payload notify_payload to process
+ */
+ status_t (*process_notify_payload) (private_responder_init_t *this, notify_payload_t *notify_payload);
+
+ /**
+ * Destroy function called internally of this class after change
+ * to state IKE_SA_INIT_RESPONDED succeeded.
+ *
+ * This destroy function does not destroy objects which were passed to the new state.
+ *
+ * @param this calling object
+ */
+ void (*destroy_after_state_change) (private_responder_init_t *this);
+
+};
+
+/**
+ * Implementation of state_t.process_message.
+ */
+static status_t process_message(private_responder_init_t *this, message_t *message)
+{
+ ike_sa_init_responded_t *next_state;
+ chunk_t ike_sa_init_response_data;
+ chunk_t ike_sa_init_request_data;
+ sa_payload_t *sa_request = NULL;
+ ke_payload_t *ke_request = NULL;
+ nonce_payload_t *nonce_request = NULL;
+ host_t *source, *destination;
+ connection_t *connection;
+ iterator_t *payloads;
+ message_t *response;
+ status_t status;
+
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state responder_init",mapping_find(exchange_type_m,message->get_exchange_type(message)));
+ return DELETE_ME;
+ }
+ if (!message->get_request(message))
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT responses not allowed state ike_sa_init_responded");
+ return DELETE_ME;
+ }
+
+ /* this is the first message to process, so get host infos */
+ source = message->get_source(message);
+ destination = message->get_destination(message);
+
+ connection = charon->connections->get_connection_by_hosts(charon->connections, destination, source);
+ if (connection == NULL)
+ {
+ /* no configuration matches given hosts */
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request does not match any available connection. Deleting IKE_SA");
+ /* TODO: inform requestor */
+ return DELETE_ME;
+ }
+ this->ike_sa->set_connection(this->ike_sa,connection);
+
+ /* parse incoming message */
+ status = message->parse_body(message, NULL, NULL);
+ if (status != SUCCESS)
+ {
+ if (status == NOT_SUPPORTED)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contains unsupported payload with critical flag set. "
+ "Deleting IKE_SA");
+ this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER);
+ }
+ else
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to parse IKE_SA_INIT request. Deleting IKE_SA");
+ }
+ return DELETE_ME;
+ }
+
+ payloads = message->get_payload_iterator(message);
+ while (payloads->has_next(payloads))
+ {
+ payload_t *payload;
+
+ payloads->current(payloads, (void**)&payload);
+
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ {
+ sa_request = (sa_payload_t*)payload;
+ break;
+ }
+ case KEY_EXCHANGE:
+ {
+ ke_request = (ke_payload_t*)payload;
+ break;
+ }
+ case NONCE:
+ {
+ nonce_request = (nonce_payload_t*)payload;
+ break;
+ }
+ case NOTIFY:
+ {
+ notify_payload_t *notify_payload = (notify_payload_t *) payload;
+ status = this->process_notify_payload(this, notify_payload);
+ if (status != SUCCESS)
+ {
+ payloads->destroy(payloads);
+ return status;
+ }
+ }
+ default:
+ {
+ this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)",
+ mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
+ break;
+ }
+ }
+ }
+ payloads->destroy(payloads);
+
+ /* check if we have all payloads */
+ if (!(sa_request && ke_request && nonce_request))
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response);
+
+ status = this->build_sa_payload(this, sa_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+
+ status = this->build_ke_payload(this, ke_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+
+ status = this->build_nonce_payload(this, nonce_request, response);
+ if (status != SUCCESS)
+ {
+ response->destroy(response);
+ return status;
+ }
+
+ /* derive all the keys used in the IKE_SA */
+ status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Transform objects could not be created from selected proposal. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ /* message can now be sent (must not be destroyed) */
+ status = this->ike_sa->send_response(this->ike_sa, response);
+ if (status != SUCCESS)
+ {
+ this->logger->log(this->logger, AUDIT, "Unable to send IKE_SA_INIT response. Deleting IKE_SA");
+ response->destroy(response);
+ return DELETE_ME;
+ }
+
+ /* state can now be changed */
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object of type IKE_SA_INIT_RESPONDED");
+
+ response = this->ike_sa->get_last_responded_message(this->ike_sa);
+ ike_sa_init_response_data = response->get_packet_data(response);
+ ike_sa_init_request_data = message->get_packet_data(message);
+
+ next_state = ike_sa_init_responded_create(this->ike_sa, this->received_nonce, this->sent_nonce,ike_sa_init_request_data,
+ ike_sa_init_response_data);
+
+ /* state can now be changed */
+ this->ike_sa->set_new_state(this->ike_sa, (state_t *) next_state);
+ this->destroy_after_state_change(this);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_initiator_init_t.build_sa_payload.
+ */
+static status_t build_sa_payload(private_responder_init_t *this,sa_payload_t *sa_request, message_t *response)
+{
+ proposal_t *proposal;
+ linked_list_t *proposal_list;
+ connection_t *connection;
+ sa_payload_t* sa_payload;
+ algorithm_t *algo;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Process received SA payload");
+
+ /* get the list of suggested proposals */
+ proposal_list = sa_request->get_proposals (sa_request);
+
+ /* select proposal */
+ this->proposal = connection->select_proposal(connection, proposal_list);
+ while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
+ {
+ proposal->destroy(proposal);
+ }
+ proposal_list->destroy(proposal_list);
+ if (this->proposal == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain any acceptable proposals. Deleting IKE_SA");
+ this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER);
+ return DELETE_ME;
+ }
+ /* get selected DH group to force policy, this is very restrictive!? */
+ this->proposal->get_algorithm(this->proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, &algo);
+ this->dh_group_number = algo->algorithm;
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "SA Payload processed");
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Building SA payload");
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message");
+ response->add_payload(response,(payload_t *) sa_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_initiator_init_t.build_ke_payload.
+ */
+static status_t build_ke_payload(private_responder_init_t *this,ke_payload_t *ke_request, message_t *response)
+{
+ diffie_hellman_group_t group;
+ ke_payload_t *ke_payload;
+ diffie_hellman_t *dh;
+ chunk_t key_data;
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Process received KE payload");
+ group = ke_request->get_dh_group_number(ke_request);
+
+ if (group == MODP_UNDEFINED)
+ {
+ this->logger->log(this->logger, AUDIT, "No diffie hellman group to select. Deleting IKE_SA");
+ return DELETE_ME;
+ }
+
+ if (this->dh_group_number != group)
+ {
+ u_int16_t accepted_group;
+ chunk_t accepted_group_chunk;
+ /* group not same as selected one
+ * Maybe key exchange payload is before SA payload */
+ this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain a acceptable diffie hellman group. Deleting IKE_SA");
+
+ accepted_group = htons(this->dh_group_number);
+ accepted_group_chunk.ptr = (u_int8_t*) &(accepted_group);
+ accepted_group_chunk.len = 2;
+ this->ike_sa->send_notify(this->ike_sa,IKE_SA_INIT,INVALID_KE_PAYLOAD,accepted_group_chunk);
+ return DELETE_ME;
+ }
+
+ /* create diffie hellman object to handle DH exchange */
+ dh = diffie_hellman_create(group);
+ if (dh == NULL)
+ {
+ this->logger->log(this->logger, AUDIT, "Could not generate DH object with group %d. Deleting IKE_SA",
+ mapping_find(diffie_hellman_group_m,group) );
+ return DELETE_ME;
+ }
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Set other DH public value");
+
+ dh->set_other_public_value(dh, ke_request->get_key_exchange_data(ke_request));
+
+ this->diffie_hellman = dh;
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "KE Payload processed.");
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Building KE payload");
+ this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data);
+
+ ke_payload = ke_payload_create();
+ ke_payload->set_key_exchange_data(ke_payload,key_data);
+ ke_payload->set_dh_group_number(ke_payload, this->dh_group_number);
+ chunk_free(&key_data);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add KE payload to message");
+ response->add_payload(response,(payload_t *) ke_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_responder_init_t.build_nonce_payload.
+ */
+static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response)
+{
+ nonce_payload_t *nonce_payload;
+ randomizer_t *randomizer;
+ status_t status;
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Process received NONCE payload");
+ free(this->received_nonce.ptr);
+ this->received_nonce = CHUNK_INITIALIZER;
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Get NONCE value and store it");
+ this->received_nonce = nonce_request->get_nonce(nonce_request);
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Create new NONCE value.");
+
+ randomizer = this->ike_sa->get_randomizer(this->ike_sa);
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce));
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Building NONCE payload");
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->sent_nonce);
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "Add NONCE payload to message");
+ response->add_payload(response,(payload_t *) nonce_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_responder_init_t.process_notify_payload.
+ */
+static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload)
+{
+ notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
+
+ this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s",
+ mapping_find(notify_message_type_m, notify_message_type));
+
+ if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE)
+ {
+ this->logger->log(this->logger, ERROR | LEVEL1, "Notify reply not for IKE protocol.");
+ return FAILED;
+ }
+ switch (notify_message_type)
+ {
+ default:
+ {
+ this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.",
+ notify_message_type);
+ return SUCCESS;
+ }
+ }
+}
+
+/**
+ * Implementation of state_t.get_state.
+ */
+static ike_sa_state_t get_state(private_responder_init_t *this)
+{
+ return RESPONDER_INIT;
+}
+
+/**
+ * Implementation of state_t.destroy.
+ */
+static void destroy(private_responder_init_t *this)
+{
+ this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder init state object");
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy sent nonce");
+ chunk_free(&(this->sent_nonce));
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy received nonce");
+ chunk_free(&(this->received_nonce));
+
+ if (this->diffie_hellman != NULL)
+ {
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy diffie_hellman_t hellman object");
+ this->diffie_hellman->destroy(this->diffie_hellman);
+ }
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy object");
+ free(this);
+}
+
+/**
+ * Implementation of private_responder_init_t.destroy_after_state_change
+ */
+static void destroy_after_state_change (private_responder_init_t *this)
+{
+ this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder_init_t state object");
+
+ /* destroy diffie hellman object */
+ if (this->diffie_hellman != NULL)
+ {
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy diffie_hellman_t object");
+ this->diffie_hellman->destroy(this->diffie_hellman);
+ }
+ if (this->proposal)
+ {
+ this->proposal->destroy(this->proposal);
+ }
+
+ this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy object");
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa)
+{
+ private_responder_init_t *this = malloc_thing(private_responder_init_t);
+
+ /* interface functions */
+ this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
+ this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
+ this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
+
+ /* private functions */
+ this->build_sa_payload = build_sa_payload;
+ this->build_ke_payload = build_ke_payload;
+ this->build_nonce_payload = build_nonce_payload;
+ this->destroy_after_state_change = destroy_after_state_change;
+ this->process_notify_payload = process_notify_payload;
+
+ /* private data */
+ this->ike_sa = ike_sa;
+ this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
+ this->sent_nonce = CHUNK_INITIALIZER;
+ this->received_nonce = CHUNK_INITIALIZER;
+ this->dh_group_number = MODP_UNDEFINED;
+ this->diffie_hellman = NULL;
+ this->proposal = NULL;
+
+ return &(this->public);
+}
diff --git a/src/charon/sa/states/responder_init.h b/src/charon/sa/states/responder_init.h
new file mode 100644
index 000000000..c8ba73ea3
--- /dev/null
+++ b/src/charon/sa/states/responder_init.h
@@ -0,0 +1,68 @@
+/**
+ * @file responder_init.h
+ *
+ * @brief Interface of responder_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 RESPONDER_INIT_H_
+#define RESPONDER_INIT_H_
+
+#include <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+
+typedef struct responder_init_t responder_init_t;
+
+/**
+ * @brief This class represents an IKE_SA state when
+ * initializing a connection as responder.
+ *
+ * @b Constructors:
+ * - responder_init_create()
+ *
+ * @ingroup states
+ */
+struct responder_init_t {
+ /**
+ * The state_t interface.
+ */
+ state_t state_interface;
+};
+
+/**
+ * Constructor of class responder_init_t.
+ *
+ * The following functions of the assigned protected_ike_sa_t object are being called with
+ * valid values after successfully processing a received message and before changing
+ * to next state IKE_SA_INIT_RESPONDED:
+ * - protected_ike_sa_t.set_connection()
+ * - protected_ike_sa_t.set_my_host()
+ * - protected_ike_sa_t.set_other_host()
+ * - protected_ike_sa_t.compute_secrets()
+ * - protected_ike_sa_t.create_transforms_from_proposal()
+ *
+ * @param ike_sa assigned IKE_SA
+ *
+ * @return responder_init_t object
+ *
+ * @ingroup states
+ */
+responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa);
+
+#endif /*RESPONDER_INIT_H_*/
diff --git a/src/charon/sa/states/state.c b/src/charon/sa/states/state.c
new file mode 100644
index 000000000..595f5abbb
--- /dev/null
+++ b/src/charon/sa/states/state.c
@@ -0,0 +1,37 @@
+/**
+ * @file state.c
+ *
+ * @brief Interface state_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 "state.h"
+
+
+/**
+ * String mappings for ike_sa_state_t.
+ */
+mapping_t ike_sa_state_m[] = {
+ {INITIATOR_INIT, "INITIATOR_INIT"},
+ {RESPONDER_INIT, "RESPONDER_INIT"},
+ {IKE_SA_INIT_REQUESTED, "IKE_SA_INIT_REQUESTED"},
+ {IKE_SA_INIT_RESPONDED, "IKE_SA_INIT_RESPONDED"},
+ {IKE_AUTH_REQUESTED, "IKE_AUTH_REQUESTED"},
+ {IKE_SA_ESTABLISHED, "IKE_SA_ESTABLISHED"},
+ {MAPPING_END, NULL}
+};
diff --git a/src/charon/sa/states/state.h b/src/charon/sa/states/state.h
new file mode 100644
index 000000000..c93068d35
--- /dev/null
+++ b/src/charon/sa/states/state.h
@@ -0,0 +1,166 @@
+/**
+ * @file state.h
+ *
+ * @brief Interface state_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2005 Jan Hutter, 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 STATE_H_
+#define STATE_H_
+
+#include <definitions.h>
+#include <types.h>
+#include <encoding/message.h>
+
+typedef enum ike_sa_state_t ike_sa_state_t;
+
+/**
+ * States in which a IKE_SA can be.
+ *
+ * @todo Support of more states (CHILD_SA_REQUESTED, etc...)
+ *
+ * @ingroup states
+ */
+enum ike_sa_state_t {
+
+ /**
+ * @brief IKE_SA is in initial state as initiator and is going to initiate a new connection.
+ *
+ * Next state following this state is IKE_SA_INIT_REQUESTED.
+ *
+ * Implemented in class initiator_init_t.
+ */
+ INITIATOR_INIT = 1,
+
+ /**
+ * @brief IKE_SA is in initial state as responder and is going to respond to a initiated connection.
+ *
+ * Next state following this state is IKE_SA_INIT_RESPONDED.
+ *
+ * Implemented in class responder_init_t.
+ */
+ RESPONDER_INIT = 2,
+
+ /**
+ * @brief A IKE_SA_INIT request was sent. In this state a reply of type IKE_SA_INIT is expected.
+ *
+ * Two states are possible as next states:
+ * - IKE_AUTH_REQUESTED if IKE_SA_INIT reply could successfully processed and IKE_AUTH request could be sent.
+ * - INITIATOR_INIT if selected DH group was not the one selected by other peer.
+ *
+ * Implemented in class ike_sa_init_requested_t.
+ */
+ IKE_SA_INIT_REQUESTED = 3,
+
+ /**
+ * @brief A IKE_SA_INIT response was sent. In this state a request of type IKE_AUTH is expected.
+ *
+ * Next state following this state is IKE_SA_ESTABLISHED.
+ *
+ * Implemented in class ike_sa_init_responded_t.
+ */
+ IKE_SA_INIT_RESPONDED = 4,
+
+ /**
+ * @brief An IKE_AUTH request was sent after a successful IKE_SA_INIT-exchange.
+ *
+ * Next state following this state is IKE_SA_ESTABLISHED.
+ *
+ * Implemented in class ike_auth_requested_t.
+ */
+ IKE_AUTH_REQUESTED = 5,
+
+ /**
+ * @brief An IKE_AUTH exchange was successfuly handled either as initiator or responder.
+ *
+ * In this state, all the informations for an IKE_SA and one CHILD_SA are known.
+ *
+ * Implemented in class ike_sa_established_t.
+ */
+ IKE_SA_ESTABLISHED = 6
+};
+
+
+/**
+ * String mappings for ike_sa_state_t.
+ */
+extern mapping_t ike_sa_state_m[];
+
+
+typedef struct state_t state_t;
+
+/**
+ * @brief This interface represents an IKE_SA state.
+ *
+ * A state_t object is responsible to handle incoming messages.
+ *
+ * It's the responsibility of the state_t object to parse the body of the message and to process each
+ * payload.
+ *
+ * Needed Configurations and transform objects can be retrieved over an internal stored protected_ike_sa_t object
+ * which is passed to a state_t object when creating it (see different constructors).
+ *
+ * The following states are supported and implemented:
+ * - INITIATOR_INIT: implemented in initiator_init_t
+ * - RESPONDER_INIT: implemented in responder_init_t
+ * - IKE_SA_INIT_REQUESTED: implemented in ike_sa_init_requested_t
+ * - IKE_SA_INIT_RESPONDED: implemented in ike_sa_init_responded_t
+ * - IKE_AUTH_REQUESTED: implemented in ike_auth_requested_t
+ * - IKE_SA_ESTABLISHED: implemented in ike_sa_established_t
+ *
+ * @b Constructors:
+ * - initiator_init_create()
+ * - responder_init_create()
+ * - ike_sa_init_requested_create()
+ * - ike_sa_init_responded_create()
+ * - ike_auth_requested_create()
+ * - ike_sa_established_create()
+ *
+ * @ingroup states
+ */
+struct state_t {
+
+ /**
+ * @brief Processes a incoming IKEv2-Message of type message_t.
+ *
+ * @param this calling object
+ * @param[in] message message_t object to process
+ * @return
+ * - SUCCESSFUL
+ * - FAILED
+ * - DELETE_ME if belonging IKE_SA should be deleted
+ */
+ status_t (*process_message) (state_t *this,message_t *message);
+
+ /**
+ * @brief Get the current state representing by this state_t object.
+ *
+ * @param this calling object
+ * @return state
+ */
+ ike_sa_state_t (*get_state) (state_t *this);
+
+ /**
+ * @brief Destroys a state_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (state_t *this);
+};
+
+#endif /*STATE_H_*/