This commit is contained in:
Martin Willi
2005-11-23 10:11:50 +00:00
parent 6725b525ff
commit 96f79ff1dc
16 changed files with 15 additions and 692 deletions
+574
View File
@@ -0,0 +1,574 @@
/**
* @file ike_sa.c
*
* @brief Class ike_sa_t. An object of this type is managed by an
* ike_sa_manager_t object and represents an IKE_SA
*
*/
/*
* 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.h"
#include <types.h>
#include <globals.h>
#include <definitions.h>
#include <utils/allocator.h>
#include <utils/linked_list.h>
#include <utils/logger_manager.h>
#include <utils/randomizer.h>
#include <transforms/diffie_hellman.h>
#include <transforms/prf_plus.h>
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/nonce_payload.h>
#include <encoding/payloads/ke_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>
/**
* @brief implements function process_message of protected_ike_sa_t
*/
static status_t process_message (protected_ike_sa_t *this, message_t *message)
{
u_int32_t message_id;
exchange_type_t exchange_type;
bool is_request;
status_t status;
state_t *new_state;
/* 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|MORE, "Process %s message 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|MORE, "Resent message 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 | MORE, "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 | MORE, "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 */
status = this->current_state->process_message(this->current_state,message,&new_state);
if (status == SUCCESS)
{
this->current_state = new_state;
}
return status;
}
/**
* @brief Implements function build_message of protected_ike_sa_t.
*/
static status_t build_message(protected_ike_sa_t *this, exchange_type_t type, bool request, message_t **message)
{
status_t status;
message_t *new_message;
host_t *source, *destination;
this ->logger->log(this->logger, CONTROL|MORE, "build empty message");
new_message = message_create();
if (new_message == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal error: could not create empty message object");
return OUT_OF_RES;
}
status = this->me.host->clone(this->me.host, &source);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Fatal error: could not clone my host information");
new_message->destroy(new_message);
return status;
}
status = this->other.host->clone(this->other.host, &destination);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Fatal error: could not clone other host information");
source->destroy(source);
new_message->destroy(new_message);
return status;
}
new_message->set_source(new_message, source);
new_message->set_destination(new_message, destination);
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);
status = new_message->set_ike_sa_id(new_message, this->ike_sa_id);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Fatal error: could not set ike_sa_id of message");
new_message->destroy(new_message);
return status;
}
*message = new_message;
return SUCCESS;
}
/**
* @brief implements function process_configuration of protected_ike_sa_t
*/
static status_t initialize_connection(protected_ike_sa_t *this, char *name)
{
/* work is done in state object of type INITIATOR_INIT */
initiator_init_t *current_state;
status_t status;
state_t *new_state;
if (this->current_state->get_state(this->current_state) != INITIATOR_INIT)
{
return FAILED;
}
current_state = (initiator_init_t *) this->current_state;
status = current_state->initiate_connection(current_state,name,&new_state);
if (status == SUCCESS)
{
this->current_state = new_state;
}
else
{
this->create_delete_job(this);
}
return status;
}
/**
* @brief implements function protected_ike_sa_t.get_id
*/
static ike_sa_id_t* get_id(protected_ike_sa_t *this)
{
return this->ike_sa_id;
}
static status_t compute_secrets (protected_ike_sa_t *this,chunk_t dh_shared_secret,chunk_t initiator_nonce, chunk_t responder_nonce)
{
chunk_t concatenated_nonces;
chunk_t skeyseed;
chunk_t prf_plus_seed;
status_t status;
u_int64_t initiator_spi;
u_int64_t responder_spi;
prf_plus_t *prf_plus;
chunk_t secrets_raw;
/*
* TODO check length for specific prf's
*/
concatenated_nonces.len = (initiator_nonce.len + responder_nonce.len);
concatenated_nonces.ptr = allocator_alloc(concatenated_nonces.len);
if (concatenated_nonces.ptr == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal errror: Could not allocate memory for concatenated nonces");
return FAILED;
}
/* first is initiator */
memcpy(concatenated_nonces.ptr,initiator_nonce.ptr,initiator_nonce.len);
/* second is responder */
memcpy(concatenated_nonces.ptr + initiator_nonce.len,responder_nonce.ptr,responder_nonce.len);
this->logger->log_chunk(this->logger, RAW, "Nonce data", &concatenated_nonces);
/* status of set_key is not checked */
status = this->prf->set_key(this->prf,concatenated_nonces);
status = this->prf->allocate_bytes(this->prf,dh_shared_secret,&skeyseed);
if (status != SUCCESS)
{
allocator_free_chunk(concatenated_nonces);
this->logger->log(this->logger, ERROR, "Fatal errror: Could not allocate bytes for skeyseed");
return status;
}
allocator_free_chunk(concatenated_nonces);
prf_plus_seed.len = (initiator_nonce.len + responder_nonce.len + 16);
prf_plus_seed.ptr = allocator_alloc(prf_plus_seed.len);
if (prf_plus_seed.ptr == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal errror: Could not allocate memory for prf+ seed");
allocator_free_chunk(skeyseed);
return FAILED;
}
/* first is initiator */
memcpy(prf_plus_seed.ptr,initiator_nonce.ptr,initiator_nonce.len);
/* second is responder */
memcpy(prf_plus_seed.ptr + initiator_nonce.len,responder_nonce.ptr,responder_nonce.len);
/* third is initiator spi */
initiator_spi = this->ike_sa_id->get_initiator_spi(this->ike_sa_id);
memcpy(prf_plus_seed.ptr + initiator_nonce.len + responder_nonce.len,&initiator_spi,8);
/* fourth is responder spi */
responder_spi = this->ike_sa_id->get_responder_spi(this->ike_sa_id);
memcpy(prf_plus_seed.ptr + initiator_nonce.len + responder_nonce.len + 8,&responder_spi,8);
this->logger->log_chunk(this->logger, PRIVATE, "Keyseed", &skeyseed);
this->logger->log_chunk(this->logger, PRIVATE, "PRF+ Seed", &prf_plus_seed);
this->logger->log(this->logger, CONTROL | MOST, "Set new key of prf object");
status = this->prf->set_key(this->prf,skeyseed);
allocator_free_chunk(skeyseed);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Fatal errror: Could not allocate memory for prf+ seed");
allocator_free_chunk(prf_plus_seed);
return FAILED;
}
this->logger->log(this->logger, CONTROL | MOST, "Create new prf+ object");
prf_plus = prf_plus_create(this->prf, prf_plus_seed);
allocator_free_chunk(prf_plus_seed);
if (prf_plus == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal errror: prf+ object could not be created");
return FAILED;
}
prf_plus->allocate_bytes(prf_plus,100,&secrets_raw);
this->logger->log_chunk(this->logger, PRIVATE, "Secrets", &secrets_raw);
allocator_free_chunk(secrets_raw);
prf_plus->destroy(prf_plus);
return SUCCESS;
}
/**
* @brief implements function resend_last_reply of protected_ike_sa_t
*/
status_t resend_last_reply (protected_ike_sa_t *this)
{
packet_t *packet;
status_t status;
status = this->last_responded_message->generate(this->last_responded_message, &packet);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Could not generate message to resent");
return status;
}
status = global_send_queue->add(global_send_queue, packet);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Could not add packet to send queue");
packet->destroy(packet);
return status;
}
return SUCCESS;
}
status_t create_delete_job (protected_ike_sa_t *this)
{
job_t *delete_job;
status_t status;
this->logger->log(this->logger, CONTROL | MORE, "Going to create job to delete this IKE_SA");
delete_job = (job_t *) delete_ike_sa_job_create(this->ike_sa_id);
if (delete_job == NULL)
{
this->logger->log(this->logger, ERROR, "Job to delete IKE SA could not be created");
return FAILED;
}
status = global_job_queue->add(global_job_queue,delete_job);
if (status != SUCCESS)
{
this->logger->log(this->logger, ERROR, "%s Job to delete IKE SA could not be added to job queue",mapping_find(status_m,status));
delete_job->destroy_all(delete_job);
return status;
}
return SUCCESS;
}
/**
* @brief implements function destroy of protected_ike_sa_t
*/
static status_t destroy (protected_ike_sa_t *this)
{
this->logger->log(this->logger, CONTROL | MORE, "Going to destroy IKE_SA");
/* destroy child sa's */
this->logger->log(this->logger, CONTROL | MOST, "Destroy all child_sa's");
while (this->child_sas->get_count(this->child_sas) > 0)
{
void *child_sa;
if (this->child_sas->remove_first(this->child_sas,&child_sa) != SUCCESS)
{
break;
}
/* destroy child sa */
}
this->child_sas->destroy(this->child_sas);
this->logger->log(this->logger, CONTROL | MOST, "Destroy secrets");
if (this->secrets.d_key.ptr != NULL)
{
allocator_free(this->secrets.d_key.ptr);
}
if (this->secrets.ai_key.ptr != NULL)
{
allocator_free(this->secrets.ai_key.ptr);
}
if (this->secrets.ar_key.ptr != NULL)
{
allocator_free(this->secrets.ar_key.ptr);
}
if (this->secrets.ei_key.ptr != NULL)
{
allocator_free(this->secrets.ei_key.ptr);
}
if (this->secrets.er_key.ptr != NULL)
{
allocator_free(this->secrets.er_key.ptr);
}
if (this->secrets.pi_key.ptr != NULL)
{
allocator_free(this->secrets.pi_key.ptr);
}
if (this->secrets.pr_key.ptr != NULL)
{
allocator_free(this->secrets.pr_key.ptr);
}
if ( this->crypter_initiator != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy initiator crypter");
this->crypter_initiator->destroy(this->crypter_initiator);
}
if ( this->crypter_responder != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy responder crypter");
this->crypter_responder->destroy(this->crypter_responder);
}
if ( this->signer_initiator != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy initiator signer");
this->signer_initiator->destroy(this->signer_initiator);
}
if (this->signer_responder != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy responder signer");
this->signer_responder->destroy(this->signer_responder);
}
if (this->prf != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy prf");
this->prf->destroy(this->prf);
}
/* destroy ike_sa_id */
this->logger->log(this->logger, CONTROL | MOST, "Destroy assigned ike_sa_id");
this->ike_sa_id->destroy(this->ike_sa_id);
/* destroy stored requested message */
if (this->last_requested_message != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy last requested message");
this->last_requested_message->destroy(this->last_requested_message);
}
/* destroy stored responded messages */
if (this->last_responded_message != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy last responded message");
this->last_responded_message->destroy(this->last_responded_message);
}
this->logger->log(this->logger, CONTROL | MOST, "Destroy randomizer");
this->randomizer->destroy(this->randomizer);
if (this->me.host != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy host informations of me");
this->me.host->destroy(this->me.host);
}
if (this->other.host != NULL)
{
this->logger->log(this->logger, CONTROL | MOST, "Destroy host informations of other");
this->other.host->destroy(this->other.host);
}
this->logger->log(this->logger, CONTROL | MOST, "Destroy current state object");
this->current_state->destroy(this->current_state);
this->logger->log(this->logger, CONTROL | MOST, "Destroy logger of IKE_SA");
global_logger_manager->destroy_logger(global_logger_manager, this->logger);
allocator_free(this);
return SUCCESS;
}
/*
* Described in Header
*/
ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
{
protected_ike_sa_t *this = allocator_alloc_thing(protected_ike_sa_t);
if (this == NULL)
{
return NULL;
}
/* Public functions */
this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
this->public.initialize_connection = (status_t(*)(ike_sa_t*, char*)) initialize_connection;
this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
this->public.destroy = (status_t(*)(ike_sa_t*))destroy;
/* private functions */
this->build_message = build_message;
this->resend_last_reply = resend_last_reply;
this->create_delete_job = create_delete_job;
this->compute_secrets = compute_secrets;
/* initialize private fields */
this->logger = global_logger_manager->create_logger(global_logger_manager, IKE_SA, NULL);
if (this->logger == NULL)
{
allocator_free(this);
}
if (ike_sa_id->clone(ike_sa_id,&(this->ike_sa_id)) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "Fatal error: Could not clone ike_sa_id");
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
allocator_free(this);
return NULL;
}
this->child_sas = linked_list_create();
if (this->child_sas == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal error: Could not create list for child_sa's");
this->ike_sa_id->destroy(this->ike_sa_id);
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
allocator_free(this);
return NULL;
}
this->randomizer = randomizer_create();
if (this->randomizer == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal error: Could not create list for child_sa's");
this->child_sas->destroy(this->child_sas);
this->ike_sa_id->destroy(this->ike_sa_id);
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
allocator_free(this);
}
this->me.host = NULL;
this->other.host = NULL;
this->last_requested_message = NULL;
this->last_responded_message = NULL;
this->message_id_out = 0;
this->message_id_in = 0;
this->secrets.d_key.ptr = NULL;
this->secrets.d_key.len = 0;
this->secrets.ai_key.ptr = NULL;
this->secrets.ai_key.len = 0;
this->secrets.ar_key.ptr = NULL;
this->secrets.ar_key.len = 0;
this->secrets.ei_key.ptr = NULL;
this->secrets.ei_key.len = 0;
this->secrets.er_key.ptr = NULL;
this->secrets.er_key.len = 0;
this->secrets.pi_key.ptr = NULL;
this->secrets.pi_key.len = 0;
this->secrets.pr_key.ptr = NULL;
this->secrets.pr_key.len = 0;
this->crypter_initiator = NULL;
this->crypter_responder = NULL;
this->signer_initiator = NULL;
this->signer_responder = NULL;
this->prf = NULL;
/* at creation time, IKE_SA is in a initiator state */
if (ike_sa_id->is_initiator(ike_sa_id))
{
this->current_state = (state_t *) initiator_init_create(this);
}
else
{
this->current_state = (state_t *) responder_init_create(this);
}
if (this->current_state == NULL)
{
this->logger->log(this->logger, ERROR, "Fatal error: Could not create state object");
this->child_sas->destroy(this->child_sas);
this->ike_sa_id->destroy(this->ike_sa_id);
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
this->randomizer->destroy(this->randomizer);
allocator_free(this);
}
return &(this->public);
}
+287
View File
@@ -0,0 +1,287 @@
/**
* @file ike_sa.h
*
* @brief Class ike_sa_t. An object of this type is managed by an
* ike_sa_manager_t object and represents an IKE_SA
*
*/
/*
* 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 <sa/ike_sa_id.h>
#include <utils/logger.h>
#include <utils/randomizer.h>
#include <sa/states/state.h>
#include <transforms/prfs/prf.h>
#include <transforms/crypters/crypter.h>
#include <transforms/signers/signer.h>
/**
* Nonce size in bytes of all sent nonces
*/
#define NONCE_SIZE 16
/**
* @brief This class is used to represent an IKE_SA
*
*/
typedef struct ike_sa_s ike_sa_t;
struct ike_sa_s {
/**
* @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 SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*process_message) (ike_sa_t *this,message_t *message);
/**
* Initiate a new connection with given configuration name
*
* @param this calling object
* @param name name of the configuration
* @return TODO
*/
status_t (*initialize_connection) (ike_sa_t *this, char *name);
/**
* @brief Get the id of the SA
*
* @param this ike_sa_t-message_t object object
* @return ike_sa's ike_sa_id_t
*/
ike_sa_id_t* (*get_id) (ike_sa_t *this);
/**
* @brief Destroys a ike_sa_t object
*
* @param this ike_sa_t object
* @return SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*destroy) (ike_sa_t *this);
};
/**
* Protected data of an ike_sa_t object
*/
typedef struct protected_ike_sa_s protected_ike_sa_t;
struct protected_ike_sa_s {
/**
* Public part of a ike_sa_t object
*/
ike_sa_t public;
/**
* Builds 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 every state.
*
* @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
* @return
* - SUCCESS
* - OUT_OF_RES
*/
status_t (*build_message) (protected_ike_sa_t *this, exchange_type_t type, bool request, message_t **message);
/**
* Initiate a new connection with given configuration name
*
* @param this calling object
* @param dh_shared_secret shared secret of diffie hellman exchange
* @param initiator_nonce nonce of initiator
* @param responder_nonce nonce of responder
* @return TODO
*/
status_t (*compute_secrets) (protected_ike_sa_t *this,chunk_t dh_shared_secret,chunk_t initiator_nonce, chunk_t responder_nonce);
/**
* Creates a job to delete the given IKE_SA
*/
status_t (*create_delete_job) (protected_ike_sa_t *this);
/**
* Resends the last sent reply
*/
status_t (*resend_last_reply) (protected_ike_sa_t *this);
/* protected values */
/**
* 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
*/
state_t *current_state;
/**
* this SA's source for random data
*/
randomizer_t *randomizer;
/**
* contains the last responded message
*
*/
message_t *last_responded_message;
/**
* contains the last requested message
*
*/
message_t *last_requested_message;
/**
* Informations of this host
*/
struct {
host_t *host;
} me;
/**
* Informations of the other host
*/
struct {
host_t *host;
} other;
/**
* 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;
/**
* prf function
*/
prf_t *prf;
/**
* Shared secrets
*/
struct {
/**
* Key used for deriving other keys
*/
chunk_t d_key;
/**
* Key for authenticate (initiator)
*/
chunk_t ai_key;
/**
* Key for authenticate (responder)
*/
chunk_t ar_key;
/**
* Key for encryption (initiator)
*/
chunk_t ei_key;
/**
* Key for encryption (responder)
*/
chunk_t er_key;
/**
* Key for generating auth payload (initiator)
*/
chunk_t pi_key;
/**
* Key for generating auth payload (responder)
*/
chunk_t pr_key;
} secrets;
/**
* next message id to receive
*/
u_int32_t message_id_in;
/**
* next message id to send
*/
u_int32_t message_id_out;
/**
* a logger for this IKE_SA
*/
logger_t *logger;
};
/**
* Creates an ike_sa_t object with a specific ike_sa_id_t object
*
* @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.
*
* @warning the Content of internal ike_sa_id_t object can change over time
* e.g. when a IKE_SA_INIT has been finished
*
* @return created ike_sa_t object
*/
ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id);
#endif /*IKE_SA_H_*/
+213
View File
@@ -0,0 +1,213 @@
/**
* @file ike_sa_id.c
*
* @brief Class for identification of an IKE_SA
*
*/
/*
* 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 <stdlib.h>
#include <string.h>
#include "ike_sa_id.h"
#include <types.h>
#include <utils/allocator.h>
/**
* Private data of an ike_sa_id object
*/
typedef struct private_ike_sa_id_s private_ike_sa_id_t;
struct private_ike_sa_id_s {
/**
* Public part of a ike_sa_id object
*/
ike_sa_id_t public;
/* Private values */
/**
* 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;
};
/**
* @brief implements function set_responder_spi of ike_sa_id_t
*/
static status_t set_responder_spi (private_ike_sa_id_t *this, u_int64_t responder_spi)
{
this->responder_spi = responder_spi;
return SUCCESS;
}
static status_t set_initiator_spi(private_ike_sa_id_t *this, u_int64_t initiator_spi)
{
this->initiator_spi = initiator_spi;
return SUCCESS;
}
/**
* @brief implements ike_sa_id_t.get_initiator_spi
*/
static u_int64_t get_initiator_spi (private_ike_sa_id_t *this)
{
return this->initiator_spi;
}
/**
* @brief implements ike_sa_id_t.get_responder_spi
*/
static u_int64_t get_responder_spi (private_ike_sa_id_t *this)
{
return this->responder_spi;
}
/**
* @brief implements function equals of ike_sa_id_t
*/
static status_t equals (private_ike_sa_id_t *this,private_ike_sa_id_t *other, bool *are_equal)
{
if ((this == NULL)||(other == NULL))
{
return FAILED;
}
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 */
*are_equal = TRUE;
}
else
{
/* private_ike_sa_id's are not equal */
*are_equal = FALSE;
}
return SUCCESS;
}
/**
* @brief implements function replace_values of ike_sa_id_t
*/
status_t replace_values (private_ike_sa_id_t *this, private_ike_sa_id_t *other)
{
if ((this == NULL) || (other == NULL))
{
return FAILED;
}
this->initiator_spi = other->initiator_spi;
this->responder_spi = other->responder_spi;
this->is_initiator_flag = other->is_initiator_flag;
return SUCCESS;
}
/**
* @brief implements ike_sa_id_t.is_initiator
*/
static bool is_initiator(private_ike_sa_id_t *this)
{
return this->is_initiator_flag;
}
/**
* @brief implements 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;
}
/**
* @brief implements function clone of ike_sa_id_t
*/
static status_t clone (private_ike_sa_id_t *this, ike_sa_id_t **clone_of_this)
{
*clone_of_this = ike_sa_id_create(this->initiator_spi, this->responder_spi, this->is_initiator_flag);
return (*clone_of_this == NULL) ? OUT_OF_RES : SUCCESS;
}
/**
* @brief implements function destroy of ike_sa_id_t
*/
static status_t destroy (private_ike_sa_id_t *this)
{
allocator_free(this);
return SUCCESS;
}
/*
* Described in Header-File
*/
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 = allocator_alloc_thing(private_ike_sa_id_t);
if (this == NULL)
{
return NULL;
}
/* Public functions */
this->public.set_responder_spi = (status_t(*)(ike_sa_id_t*,u_int64_t)) set_responder_spi;
this->public.set_initiator_spi = (status_t(*)(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 = (status_t(*)(ike_sa_id_t*,ike_sa_id_t*,bool*)) equals;
this->public.replace_values = (status_t(*)(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 = (status_t(*)(ike_sa_id_t*,ike_sa_id_t**)) clone;
this->public.destroy = (status_t(*)(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);
}
+139
View File
@@ -0,0 +1,139 @@
/**
* @file ike_sa_id.h
*
* @brief Class for identification of an IKE_SA
*
*/
/*
* 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"
/**
* @brief This class 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.
*/
typedef struct ike_sa_id_s ike_sa_id_t;
struct ike_sa_id_s {
/**
* @brief Sets the SPI of the responder.
*
* This function is called when a request or reply of a IKE_SA_INIT is received.
*
* @param this ike_sa_id_t object
* @param responder_spi SPI of responder to set
* @return SUCCESSFUL in any case
*/
status_t (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi);
/**
* @brief Sets the SPI of the initiator.
*
*
* @param this ike_sa_id_t object
* @param initiator_spi SPI to set
* @return SUCCESSFUL in any case
*/
status_t (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi);
/**
* @brief Returns the initiator spi
*
* @param this ike_sa_id_t object
* @return spi of the initiator
*/
u_int64_t (*get_initiator_spi) (ike_sa_id_t *this);
/**
* @brief Returns the responder spi
*
* @param this ike_sa_id_t object
* @return spi of the responder
*/
u_int64_t (*get_responder_spi) (ike_sa_id_t *this);
/**
* @brief Check if two ike_sa_ids are equal
*
* @param this ike_sa_id_t object
* @param other ike_sa_id object to check if equal
* @param are_equal is set to TRUE, if given ike_sa_ids are equal, FALSE otherwise
* @return SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*equals) (ike_sa_id_t *this,ike_sa_id_t *other, bool *are_equal);
/**
* @brief Replace the values of a given ike_sa_id_t object with values
* from another ike_sa_id_t object
*
* @param this ike_sa_id_t object
* @param other ike_sa_id_t object which values will be taken
* @return SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other);
/**
* @brief gets the initiator flag
*
* @param this ike_sa_id_t object
* @return TRUE if we are the original initator
*/
bool (*is_initiator) (ike_sa_id_t *this);
/**
* @brief switches the initiator flag
*
* @param this ike_sa_id_t object
* @return TRUE if we are the original initator after switch
*/
bool (*switch_initiator) (ike_sa_id_t *this);
/**
* @brief Clones a given ike_sa_id_t object
*
* @param this ike_sa_id_t object
* @param clone_of_this ike_sa_id_t object which will be created
* @return SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*clone) (ike_sa_id_t *this,ike_sa_id_t **clone_of_this);
/**
* @brief Destroys a ike_sa_id_tobject
*
* @param this ike_sa_id_t object
* @return SUCCESSFUL if succeeded, FAILED otherwise
*/
status_t (*destroy) (ike_sa_id_t *this);
};
/**
* Creates an ike_sa_id_t object with specific spi's and defined role
*
* @warning The initiator SPI and role is not changeable after initiating a ike_sa_id object
*/
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_*/
+790
View File
@@ -0,0 +1,790 @@
/**
* @file ike_sa_manager.c
*
* @brief Central point for managing IKE-SAs (creation, locking, deleting...)
*
*/
/*
* 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 <globals.h>
#include <sa/ike_sa_id.h>
#include <utils/allocator.h>
#include <utils/logger.h>
#include <utils/logger_manager.h>
#include <utils/linked_list.h>
/**
* @brief An entry in the linked list, contains IKE_SA, locking and lookup data.
*/
typedef struct ike_sa_entry_s ike_sa_entry_t;
struct ike_sa_entry_s {
/**
* destructor, also destroys ike_sa
*/
status_t (*destroy) (ike_sa_entry_t *this);
/**
* Number of threads waiting for this ike_sa
*/
int waiting_threads;
/**
* condvar where threads can wait until it's free 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 ike_sa (SPIs)
*/
ike_sa_id_t *ike_sa_id;
/**
* the contained ike_sa
*/
ike_sa_t *ike_sa;
};
/**
* @see 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);
allocator_free(this);
return SUCCESS;
}
/**
* @brief creates a new entry for the ike_sa list
*
* This constructor additionaly creates a new and empty SA
*
* @param ike_sa_id the associated ike_sa_id_t, will be cloned
* @return created entry, with ike_sa and ike_sa_id
*/
static ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id)
{
ike_sa_entry_t *this = allocator_alloc_thing(ike_sa_entry_t);
if (this == NULL)
{
return NULL;
}
/* 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 */
ike_sa_id->clone(ike_sa_id, &(this->ike_sa_id));
if (this->ike_sa_id == NULL)
{
allocator_free(this);
return NULL;
}
/* create new ike_sa */
this->ike_sa = ike_sa_create(ike_sa_id);
if (this->ike_sa == NULL)
{
this->ike_sa_id->destroy(this->ike_sa_id);
allocator_free(this);
return NULL;
}
return this;
}
/**
* Additional private members to ike_sa_manager_t
*/
typedef struct private_ike_sa_manager_s private_ike_sa_manager_t;
struct private_ike_sa_manager_s {
/**
* Public members
*/
ike_sa_manager_t public;
/**
* @brief get next spi
*
* we give out SPIs incremental
*
* @param this the ike_sa_manager
* @param spi[out] spi will be written here
* @return SUCCESS or,
* OUT_OF_RES when we already served 2^64 SPIs ;-)
*/
status_t (*get_next_spi) (private_ike_sa_manager_t *this, u_int64_t *spi);
/**
* @brief find the ike_sa_entry 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 the ike_sa_manager containing the list
* @param ike_sa_id id of the ike_sa, containing SPIs
* @param entry[out] pointer to set to the found entry
* @return
* - SUCCESS when found,
* - NOT_FOUND when no such ike_sa_id in list
* - OUT_OF_RES
*/
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 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 the ike_sa_manager containing the list
* @param ike_sa pointer to the ike_sa
* @param entry[out] pointer to set to the found entry
* @return
* - SUCCESS when found,
* - NOT_FOUND when no such ike_sa_id in list
* - OUT_OF_RES
*/
status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry);
/**
* @brief delete an entry from the linked list
*
* @param this the ike_sa_manager containing the list
* @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
*/
linked_list_t *ike_sa_list;
/**
* Next SPI, needed for incremental creation of SPIs
*/
u_int64_t next_spi;
};
/**
* @see 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;
linked_list_iterator_t *iterator;
status_t status;
/* create iterator over list of ike_sa's */
status = list->create_iterator(list, &iterator, TRUE);
if (status != SUCCESS)
{
this->logger->log(this->logger,ERROR,"Fatal Error: could not create iterator: %s",mapping_find(status_m,status));
/* out of res */
return status;
}
/* default status */
status = NOT_FOUND;
while (iterator->has_next(iterator))
{
ike_sa_entry_t *current;
bool are_equal = FALSE;
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 | MOST,"Found entry by initiator spi %d",ike_sa_id->get_initiator_spi(ike_sa_id));
*entry = current;
status = SUCCESS;
break;
}
}
current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id, &are_equal);
if (are_equal)
{
this->logger->log(this->logger,CONTROL | MOST,"Found entry by full ID");
*entry = current;
status = SUCCESS;
break;
}
}
iterator->destroy(iterator);
return status;
}
/**
* @see 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;
linked_list_iterator_t *iterator;
status_t status;
status = list->create_iterator(list, &iterator, TRUE);
if (status != SUCCESS)
{
this->logger->log(this->logger,ERROR,"Fatal Error: could not create iterator: %s",mapping_find(status_m,status));
return status;
}
/* 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 | MOST,"Found entry by pointer");
*entry = current;
status = SUCCESS;
break;
}
}
iterator->destroy(iterator);
return status;
}
/**
* @see 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;
linked_list_iterator_t *iterator;
status_t status;
status = list->create_iterator(list, &iterator, TRUE);
if (status != SUCCESS)
{
this->logger->log(this->logger,ERROR,"Fatal Error: could not create iterator: %s",mapping_find(status_m,status));
return status;
}
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 | MOST,"Found entry by pointer. Going to delete it.");
list->remove(list, iterator);
entry->destroy(entry);
status = SUCCESS;
break;
}
}
iterator->destroy(iterator);
return status;
}
/**
* @see private_ike_sa_manager_t.get_next_spi
*/
static status_t get_next_spi(private_ike_sa_manager_t *this, u_int64_t *spi)
{
this->next_spi++;
if (this->next_spi == 0) {
/* our software ran so incredible stable, we have no more
* SPIs to give away :-/. */
this->logger->log(this->logger,CONTROL | MOST,"No more SPI values available. WOW!");
return OUT_OF_RES;
}
*spi = this->next_spi;
return SUCCESS;
}
/**
* @see ike_sa_manager_s.checkout_ike_sa
*/
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;
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));
if (initiator_spi_set && responder_spi_set)
{
/* 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|MORE,"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|MORE,"Drive out waiting thread for existing IKE_SA");
retval = NOT_FOUND;
}
else
{
this->logger->log(this->logger,CONTROL|MOST,"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 | MORE,"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)
{
/* 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 */
retval = this->get_next_spi(this, &responder_spi);
if (retval == SUCCESS)
{ /* next SPI could be created */
/* 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);
if (new_ike_sa_entry != NULL)
{
retval = this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry);
if (retval == SUCCESS)
{
/* check ike_sa out */
this->logger->log(this->logger,CONTROL | MORE ,"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;
/* DON'T use return, we must unlock the mutex! */
}
else
{
/* ike_sa_entry could not be added to list*/
this->logger->log(this->logger,ERROR,"Fatal error: ike_sa_entry could not be added to list");
}
}
else
{
/* new ike_sa_entry could not be created */
this->logger->log(this->logger,ERROR,"Fatal error: ike_sa_entry could not be created");
}
}
else
{ /* next SPI could not be created */
this->logger->log(this->logger,ERROR,"Fatal error: Next SPI could not be created");
}
}
else if (!initiator_spi_set && !responder_spi_set)
{
/* creation of an IKE_SA from local site,
* we are the initiator!
*/
u_int64_t initiator_spi;
ike_sa_entry_t *new_ike_sa_entry;
retval = this->get_next_spi(this, &initiator_spi);
if (retval == SUCCESS)
{
/* we also set arguments SPI, so its still valid */
ike_sa_id->set_initiator_spi(ike_sa_id, initiator_spi);
/* create entry */
new_ike_sa_entry = ike_sa_entry_create(ike_sa_id);
if (new_ike_sa_entry != NULL)
{
retval = this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry);
if (retval == SUCCESS)
{
/* check ike_sa out */
this->logger->log(this->logger,CONTROL | MORE ,"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;
/* DON'T use return, we must unlock the mutex! */
}
else
{
/* ike_sa_entry could not be added to list*/
this->logger->log(this->logger,ERROR,"Fatal error: ike_sa_entry could not be added to list");
}
}
else
{
/* new ike_sa_entry could not be created */
this->logger->log(this->logger,ERROR,"Fatal error: ike_sa_entry could not be created");
}
}
else
{
/* next SPI could not be created */
this->logger->log(this->logger,ERROR,"Fatal error: Next SPI could not be created");
}
}
else
{
/* responder set, initiator not: here is something seriously wrong! */
this->logger->log(this->logger,ERROR | MORE,"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;
}
/**
* Implements ike_sa_manager_t-function checkin.
* @see 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 | MORE,"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;
}
/**
* Implements ike_sa_manager_t-function checkin_and_delete.
* @see 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 | MORE,"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;
}
/**
* Implements ike_sa_manager_t-function delete.
* @see 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 | MORE,"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;
}
/**
* Implements ike_sa_manager_t-function destroy.
* @see ike_sa_manager_t.destroy.
*/
static status_t destroy(private_ike_sa_manager_t *this)
{
/* destroy all list entries */
linked_list_t *list = this->ike_sa_list;
linked_list_iterator_t *iterator;
status_t status;
ike_sa_entry_t *entry;
pthread_mutex_lock(&(this->mutex));
this->logger->log(this->logger,CONTROL | MORE,"Going to destroy IKE_SA manager and all managed IKE_SA's");
/* Step 1: drive out all waiting threads */
status = list->create_iterator(list, &iterator, TRUE);
if (status != SUCCESS)
{
this->logger->log(this->logger,ERROR,"Fatal Error: Create of iterator while destroying IKE_SA-Manager failed");
return FAILED;
}
this->logger->log(this->logger,CONTROL | MOST,"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 | MOST,"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 | MOST,"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 | MOST,"IKE_SA's deleted");
pthread_mutex_unlock(&(this->mutex));
/* destroy logger at end */
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
allocator_free(this);
return SUCCESS;
}
/*
* Described in header
*/
ike_sa_manager_t *ike_sa_manager_create()
{
private_ike_sa_manager_t *this = allocator_alloc_thing(private_ike_sa_manager_t);
/* assign public functions */
this->public.destroy = (status_t(*)(ike_sa_manager_t*))destroy;
this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id, ike_sa_t **sa))checkout;
this->public.checkin = (status_t(*)(ike_sa_manager_t*, ike_sa_t *sa))checkin;
this->public.delete = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id))delete;
this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))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 = global_logger_manager->create_logger(global_logger_manager,IKE_SA_MANAGER,NULL);
if (this->logger == NULL)
{
allocator_free(this);
return NULL;
}
this->ike_sa_list = linked_list_create();
if (this->ike_sa_list == NULL)
{
this->logger->log(this->logger,ERROR,"Fatal error: Failed to create list for managed IKE_SA");
global_logger_manager->destroy_logger(global_logger_manager,this->logger);
allocator_free(this);
return NULL;
}
pthread_mutex_init(&(this->mutex), NULL);
this->next_spi = 0;
return (ike_sa_manager_t*)this;
}
+117
View File
@@ -0,0 +1,117 @@
/**
* @file ike_sa_manager.h
*
* @brief Central point for managing IKE-SAs (creation, locking, deleting...)
*
*/
/*
* 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>
/**
* @brief The IKE_SA-Manager manages the IKE_SAs ;-).
*
* To avoid access from multiple threads, IKE_SAs must be checked out from
* the manager, and checked back 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 by comparing thread-ids via pthread_self()...
*
*/
typedef struct ike_sa_manager_s ike_sa_manager_t;
struct ike_sa_manager_s {
/**
* @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)
* - Both SPIs are not set (for initiating IKE_SA_INIT)
* 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 ike_sa_manager 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
* - OUT_OF_RES
*/
status_t (*checkout) (ike_sa_manager_t* ike_sa_manager, ike_sa_id_t *sa_id, ike_sa_t **ike_sa);
/**
* @brief Checkin the SA after usage
*
* @warning the SA pointer MUST NOT be used after checkin! The SA must be checked
* out again!
*
* @param ike_sa_manager 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* ike_sa_manager, ike_sa_t *ike_sa);
/**
* @brief delete a SA, wich was not checked out
*
* @warning do not use this when the SA is already checked out, this will
* deadlock!
*
* @param ike_sa_manager 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* ike_sa_manager, ike_sa_id_t *ike_sa_id);
/**
* @brief delete a checked out SA
*
* @param ike_sa_manager 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* ike_sa_manager, 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 ike_sa_manager the manager object
* @returns SUCCESS if succeeded, FAILED otherwise
*/
status_t (*destroy) (ike_sa_manager_t *ike_sa_manager);
};
/**
* @brief Create a manager
*
* @returns the manager
*/
ike_sa_manager_t *ike_sa_manager_create();
#endif /*IKE_SA_MANAGER_H_*/