further CHILD_SA rekeying work done: creation of a new CHILD_SA on a expire from a kernel works delete of old CHILD_SA still missing some issues when both initiate rekeing
409 lines
11 KiB
C
409 lines
11 KiB
C
/**
|
|
* @file create_child_sa_requested.c
|
|
*
|
|
* @brief State after a CREATE_CHILD_SA request was sent.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006 Martin Willi
|
|
* Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "create_child_sa_requested.h"
|
|
|
|
#include <sa/child_sa.h>
|
|
#include <sa/states/delete_ike_sa_requested.h>
|
|
#include <sa/states/ike_sa_established.h>
|
|
#include <encoding/payloads/ts_payload.h>
|
|
#include <encoding/payloads/sa_payload.h>
|
|
#include <encoding/payloads/nonce_payload.h>
|
|
#include <encoding/payloads/notify_payload.h>
|
|
#include <utils/logger_manager.h>
|
|
|
|
|
|
typedef struct private_create_child_sa_requested_t private_create_child_sa_requested_t;
|
|
|
|
/**
|
|
* Private data of a create_child_sa_requested_t object.
|
|
*/
|
|
struct private_create_child_sa_requested_t {
|
|
/**
|
|
* Public interface of create_child_sa_requested_t.
|
|
*/
|
|
create_child_sa_requested_t public;
|
|
|
|
/**
|
|
* Assigned IKE_SA.
|
|
*/
|
|
protected_ike_sa_t *ike_sa;
|
|
|
|
/**
|
|
* nonce chosen by initiator
|
|
*/
|
|
chunk_t nonce_i;
|
|
|
|
/**
|
|
* nonce chosen by the responder
|
|
*/
|
|
chunk_t nonce_r;
|
|
|
|
/**
|
|
* Policy to use for new child_sa
|
|
*/
|
|
policy_t *policy;
|
|
|
|
/**
|
|
* Proposal negotiated
|
|
*/
|
|
proposal_t *proposal;
|
|
|
|
/**
|
|
* Negotiated list of traffic selectors for local site
|
|
*/
|
|
linked_list_t *my_ts;
|
|
|
|
/**
|
|
* Negotiated list of traffic selectors for remote site
|
|
*/
|
|
linked_list_t *other_ts;
|
|
|
|
/**
|
|
* Child SA to create
|
|
*/
|
|
child_sa_t *child_sa;
|
|
|
|
/**
|
|
* Assigned logger.
|
|
*
|
|
* Is logger of ike_sa!
|
|
*/
|
|
logger_t *logger;
|
|
};
|
|
|
|
/**
|
|
* Implementation of private_create_child_sa_requested_t.process_sa_payload.
|
|
*/
|
|
static status_t process_sa_payload(private_create_child_sa_requested_t *this, sa_payload_t *sa_payload)
|
|
{
|
|
proposal_t *proposal, *proposal_tmp;
|
|
linked_list_t *proposal_list;
|
|
|
|
/* get his selected proposal */
|
|
proposal_list = sa_payload->get_proposals(sa_payload);
|
|
/* check count of proposals */
|
|
if (proposal_list->get_count(proposal_list) == 0)
|
|
{
|
|
/* no proposal? we accept this, but no child sa is built */
|
|
this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained no proposals. CHILD_SA not created");
|
|
proposal_list->destroy(proposal_list);
|
|
return FAILED;
|
|
}
|
|
if (proposal_list->get_count(proposal_list) > 1)
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained %d proposals. Aborting",
|
|
proposal_list->get_count(proposal_list));
|
|
while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
|
|
{
|
|
proposal->destroy(proposal);
|
|
}
|
|
proposal_list->destroy(proposal_list);
|
|
return FAILED;
|
|
}
|
|
|
|
/* we have to re-check here if other's selection is valid */
|
|
proposal = this->policy->select_proposal(this->policy, proposal_list);
|
|
/* list not needed anymore */
|
|
while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS)
|
|
{
|
|
proposal_tmp->destroy(proposal_tmp);
|
|
}
|
|
proposal_list->destroy(proposal_list);
|
|
/* got a match? */
|
|
if (proposal == NULL)
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained a not offered proposal. Aborting");
|
|
return FAILED;
|
|
}
|
|
|
|
/* apply proposal */
|
|
this->proposal = proposal;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Implementation of private_create_child_sa_requested_t.process_ts_payload.
|
|
*/
|
|
static status_t process_ts_payload(private_create_child_sa_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload)
|
|
{
|
|
linked_list_t *ts_received, *ts_selected;
|
|
traffic_selector_t *ts;
|
|
|
|
/* get ts form payload */
|
|
ts_received = ts_payload->get_traffic_selectors(ts_payload);
|
|
/* select ts depending on payload type */
|
|
if (ts_initiator)
|
|
{
|
|
ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received);
|
|
this->my_ts = ts_selected;
|
|
}
|
|
else
|
|
{
|
|
ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received);
|
|
this->other_ts = ts_selected;
|
|
}
|
|
/* check if the responder selected valid proposals */
|
|
if (ts_selected->get_count(ts_selected) != ts_received->get_count(ts_received))
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained not offered traffic selectors.");
|
|
}
|
|
|
|
/* cleanup */
|
|
while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS)
|
|
{
|
|
ts->destroy(ts);
|
|
}
|
|
ts_received->destroy(ts_received);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Implementation of private_create_child_sa_requested_t.process_nonce_payload.
|
|
*/
|
|
static status_t process_nonce_payload(private_create_child_sa_requested_t *this, nonce_payload_t *nonce_request)
|
|
{
|
|
this->nonce_r = nonce_request->get_nonce(nonce_request);
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Process a CREATE_CHILD_SA response
|
|
*/
|
|
static status_t process_message(private_create_child_sa_requested_t *this, message_t *response)
|
|
{
|
|
ts_payload_t *tsi_request = NULL, *tsr_request = NULL;
|
|
sa_payload_t *sa_request = NULL;
|
|
nonce_payload_t *nonce_request = NULL;
|
|
ike_sa_id_t *ike_sa_id;
|
|
iterator_t *payloads;
|
|
crypter_t *crypter;
|
|
signer_t *signer;
|
|
status_t status;
|
|
chunk_t seed;
|
|
prf_plus_t *prf_plus;
|
|
|
|
this->policy = this->ike_sa->get_policy(this->ike_sa);
|
|
if (response->get_exchange_type(response) != CREATE_CHILD_SA)
|
|
{
|
|
this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state create_child_sa_requested",
|
|
mapping_find(exchange_type_m, response->get_exchange_type(response)));
|
|
return FAILED;
|
|
}
|
|
|
|
if (response->get_request(response))
|
|
{
|
|
this->logger->log(this->logger, ERROR | LEVEL1, "CREATE_CHILD_SA requests not allowed state create_child_sa_requested");
|
|
return FAILED;
|
|
}
|
|
|
|
/* get signer for verification and crypter for decryption */
|
|
ike_sa_id = this->ike_sa->public.get_id(&this->ike_sa->public);
|
|
if (!ike_sa_id->is_initiator(ike_sa_id))
|
|
{
|
|
crypter = this->ike_sa->get_crypter_initiator(this->ike_sa);
|
|
signer = this->ike_sa->get_signer_initiator(this->ike_sa);
|
|
}
|
|
else
|
|
{
|
|
crypter = this->ike_sa->get_crypter_responder(this->ike_sa);
|
|
signer = this->ike_sa->get_signer_responder(this->ike_sa);
|
|
}
|
|
|
|
/* parse incoming message */
|
|
status = response->parse_body(response, crypter, signer);
|
|
if (status != SUCCESS)
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA r decryption failed. Ignoring message");
|
|
return status;
|
|
}
|
|
|
|
/* iterate over incoming payloads. Message is verified, we can be sure there are the required payloads */
|
|
payloads = response->get_payload_iterator(response);
|
|
while (payloads->has_next(payloads))
|
|
{
|
|
payload_t *payload;
|
|
payloads->current(payloads, (void**)&payload);
|
|
|
|
switch (payload->get_type(payload))
|
|
{
|
|
case SECURITY_ASSOCIATION:
|
|
{
|
|
sa_request = (sa_payload_t*)payload;
|
|
break;
|
|
}
|
|
case TRAFFIC_SELECTOR_INITIATOR:
|
|
{
|
|
tsi_request = (ts_payload_t*)payload;
|
|
break;
|
|
}
|
|
case TRAFFIC_SELECTOR_RESPONDER:
|
|
{
|
|
tsr_request = (ts_payload_t*)payload;
|
|
break;
|
|
}
|
|
case NONCE:
|
|
{
|
|
nonce_request = (nonce_payload_t*)payload;
|
|
break;
|
|
}
|
|
case NOTIFY:
|
|
{
|
|
/* TODO: handle notifys */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)",
|
|
mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* iterator can be destroyed */
|
|
payloads->destroy(payloads);
|
|
|
|
/* check if we have all payloads */
|
|
if (!(sa_request && nonce_request && tsi_request && tsr_request))
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA request did not contain all required payloads. Ignored");
|
|
return FAILED;
|
|
}
|
|
|
|
/* add payloads to it */
|
|
status = process_nonce_payload(this, nonce_request);
|
|
if (status != SUCCESS)
|
|
{
|
|
response->destroy(response);
|
|
return status;
|
|
}
|
|
status = process_sa_payload(this, sa_request);
|
|
if (status != SUCCESS)
|
|
{
|
|
response->destroy(response);
|
|
return status;
|
|
}
|
|
status = process_ts_payload(this, TRUE, tsi_request);
|
|
if (status != SUCCESS)
|
|
{
|
|
response->destroy(response);
|
|
return status;
|
|
}
|
|
status = process_ts_payload(this, FALSE, tsr_request);
|
|
if (status != SUCCESS)
|
|
{
|
|
response->destroy(response);
|
|
return status;
|
|
}
|
|
|
|
/* install child SAs for AH and esp */
|
|
if (!this->proposal)
|
|
{
|
|
this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built");
|
|
this->child_sa->destroy(this->child_sa);
|
|
this->child_sa = NULL;
|
|
}
|
|
else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0)
|
|
{
|
|
this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built");
|
|
this->child_sa->destroy(this->child_sa);
|
|
this->child_sa = NULL;
|
|
}
|
|
else
|
|
{
|
|
seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len);
|
|
memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len);
|
|
memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len);
|
|
prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
|
|
|
|
this->logger->log_chunk(this->logger, CONTROL, "Seed", seed);
|
|
chunk_free(&seed);
|
|
|
|
status = this->child_sa->update(this->child_sa, this->proposal, prf_plus);
|
|
prf_plus->destroy(prf_plus);
|
|
if (status != SUCCESS)
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA");
|
|
return DESTROY_ME;
|
|
}
|
|
status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts);
|
|
if (status != SUCCESS)
|
|
{
|
|
this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA");
|
|
return DESTROY_ME;
|
|
}
|
|
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
|
|
}
|
|
|
|
this->ike_sa->set_last_replied_message_id(this->ike_sa, response->get_message_id(response));
|
|
|
|
/* create new state */
|
|
this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa));
|
|
this->public.state_interface.destroy(&this->public.state_interface);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Implements state_t.get_state
|
|
*/
|
|
static ike_sa_state_t get_state(private_create_child_sa_requested_t *this)
|
|
{
|
|
return CREATE_CHILD_SA_REQUESTED;
|
|
}
|
|
|
|
/**
|
|
* Implementation of state_t.destroy.
|
|
*/
|
|
static void destroy(private_create_child_sa_requested_t *this)
|
|
{
|
|
chunk_free(&this->nonce_i);
|
|
chunk_free(&this->nonce_r);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
create_child_sa_requested_t *create_child_sa_requested_create(protected_ike_sa_t *ike_sa, child_sa_t *child_sa, chunk_t nonce_i)
|
|
{
|
|
private_create_child_sa_requested_t *this = malloc_thing(private_create_child_sa_requested_t);
|
|
|
|
/* interface functions */
|
|
this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
|
|
this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
|
|
this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
|
|
|
|
/* private data */
|
|
this->ike_sa = ike_sa;
|
|
this->child_sa = child_sa;
|
|
this->nonce_i = nonce_i;
|
|
this->nonce_r = CHUNK_INITIALIZER;
|
|
this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
|
|
|
|
return &(this->public);
|
|
}
|