ok
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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_*/
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_*/
|
||||
@@ -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**)¤t);
|
||||
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**)¤t);
|
||||
/* 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**)¤t);
|
||||
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;
|
||||
}
|
||||
@@ -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_*/
|
||||
Reference in New Issue
Block a user