implement proper handling of most simultaneous IKE_SA rekeying cases
This commit is contained in:
@@ -79,12 +79,14 @@ static void send_notify_response(private_incoming_packet_job_t *this,
|
||||
src = request->get_destination(request);
|
||||
response->set_source(response, src->clone(src));
|
||||
response->set_destination(response, dst->clone(dst));
|
||||
response->set_exchange_type(response, IKE_SA_INIT);
|
||||
response->set_exchange_type(response, request->get_exchange_type(request));
|
||||
response->set_request(response, FALSE);
|
||||
response->set_message_id(response, 0);
|
||||
response->set_ike_sa_id(response, ike_sa_id);
|
||||
notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type);
|
||||
response->add_payload(response, (payload_t *)notify);
|
||||
/* generation may fail, as most messages need a crypter/signer.
|
||||
* TODO: Use transforms implementing the "NULL" algorithm */
|
||||
if (response->generate(response, NULL, NULL, &packet) != SUCCESS)
|
||||
{
|
||||
response->destroy(response);
|
||||
|
||||
+52
-13
@@ -64,6 +64,7 @@ mapping_t ike_sa_state_m[] = {
|
||||
{IKE_CREATED, "CREATED"},
|
||||
{IKE_CONNECTING, "CONNECTING"},
|
||||
{IKE_ESTABLISHED, "ESTABLISHED"},
|
||||
{IKE_REKEYING, "REKEYING"},
|
||||
{IKE_DELETING, "DELETING"},
|
||||
{MAPPING_END, NULL}
|
||||
};
|
||||
@@ -225,6 +226,11 @@ struct private_ike_sa_t {
|
||||
* do multi transaction operations.
|
||||
*/
|
||||
transaction_t *transaction_in_next;
|
||||
|
||||
/**
|
||||
* Transaction which rekeys this IKE_SA, used do detect simultaneus rekeying
|
||||
*/
|
||||
rekey_ike_sa_t *rekeying_transaction;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -840,10 +846,12 @@ static status_t initiate(private_ike_sa_t *this,
|
||||
return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
|
||||
}
|
||||
case IKE_DELETING:
|
||||
case IKE_REKEYING:
|
||||
{
|
||||
/* if we are in DELETING, we deny set up of a policy. */
|
||||
/* if we are in DELETING/REKEYING, we deny set up of a policy. */
|
||||
this->logger->log(this->logger, CONTROL,
|
||||
"creating CHILD_SA discarded, as IKE_SA is deleting");
|
||||
"creating CHILD_SA discarded, as IKE_SA is in state %s",
|
||||
mapping_find(ike_sa_state_m, this->state));
|
||||
policy->destroy(policy);
|
||||
connection->destroy(connection);
|
||||
return FAILED;
|
||||
@@ -1084,7 +1092,9 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t
|
||||
set_name(this, connection->get_name(connection));
|
||||
break;
|
||||
case IKE_ESTABLISHED:
|
||||
/* nothing to do */
|
||||
case IKE_REKEYING:
|
||||
/* nothing to do. We allow it for rekeying, as it will be
|
||||
* adopted by the new IKE_SA */
|
||||
break;
|
||||
case IKE_DELETING:
|
||||
/* deny */
|
||||
@@ -1331,7 +1341,7 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other)
|
||||
static status_t derive_keys(private_ike_sa_t *this,
|
||||
proposal_t *proposal, diffie_hellman_t *dh,
|
||||
chunk_t nonce_i, chunk_t nonce_r,
|
||||
bool initiator, prf_t *rekey_prf)
|
||||
bool initiator, prf_t *child_prf, prf_t *old_prf)
|
||||
{
|
||||
prf_plus_t *prf_plus;
|
||||
chunk_t skeyseed, secret, key, nonces, prf_plus_seed;
|
||||
@@ -1368,7 +1378,7 @@ static status_t derive_keys(private_ike_sa_t *this,
|
||||
*
|
||||
* if we are rekeying, SKEYSEED built on another way
|
||||
*/
|
||||
if (rekey_prf == NULL) /* not rekeying */
|
||||
if (child_prf == NULL) /* not rekeying */
|
||||
{
|
||||
/* SKEYSEED = prf(Ni | Nr, g^ir) */
|
||||
this->prf->set_key(this->prf, nonces);
|
||||
@@ -1381,14 +1391,15 @@ static status_t derive_keys(private_ike_sa_t *this,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) */
|
||||
/* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
|
||||
* use OLD SAs PRF functions for both prf_plus and prf */
|
||||
secret = chunk_cat("mc", secret, nonces);
|
||||
rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
|
||||
child_prf->allocate_bytes(child_prf, secret, &skeyseed);
|
||||
this->logger->log_chunk(this->logger, PRIVATE|LEVEL1, "SKEYSEED", skeyseed);
|
||||
rekey_prf->set_key(rekey_prf, skeyseed); /* abuse of rekeyed SAs child_prf */
|
||||
old_prf->set_key(old_prf, skeyseed);
|
||||
chunk_free(&skeyseed);
|
||||
chunk_free(&secret);
|
||||
prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
|
||||
prf_plus = prf_plus_create(old_prf, prf_plus_seed);
|
||||
}
|
||||
chunk_free(&nonces);
|
||||
chunk_free(&prf_plus_seed);
|
||||
@@ -1421,12 +1432,12 @@ static status_t derive_keys(private_ike_sa_t *this,
|
||||
key_size = signer_i->get_key_size(signer_i);
|
||||
|
||||
prf_plus->allocate_bytes(prf_plus, key_size, &key);
|
||||
this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ai secret", key);
|
||||
this->logger->log_chunk(this->logger, PRIVATE, "Sk_ai secret", key);
|
||||
signer_i->set_key(signer_i, key);
|
||||
chunk_free(&key);
|
||||
|
||||
prf_plus->allocate_bytes(prf_plus, key_size, &key);
|
||||
this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ar secret", key);
|
||||
this->logger->log_chunk(this->logger, PRIVATE, "Sk_ar secret", key);
|
||||
signer_r->set_key(signer_r, key);
|
||||
chunk_free(&key);
|
||||
|
||||
@@ -1547,7 +1558,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
|
||||
{
|
||||
iterator->current(iterator, (void**)¤t);
|
||||
if (current->get_spi(current, inbound) == spi &&
|
||||
current->get_protocol(current) == protocol)
|
||||
current->get_protocol(current) == protocol)
|
||||
{
|
||||
found = current;
|
||||
}
|
||||
@@ -1556,6 +1567,14 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.create_child_sa_iterator.
|
||||
*/
|
||||
static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
|
||||
{
|
||||
return this->child_sas->create_iterator(this->child_sas, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.rekey_child_sa.
|
||||
*/
|
||||
@@ -1670,6 +1689,22 @@ static status_t rekey(private_ike_sa_t *this)
|
||||
return queue_transaction(this, (transaction_t*)rekey_ike_sa, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.get_rekeying_transaction.
|
||||
*/
|
||||
static rekey_ike_sa_t* get_rekeying_transaction(private_ike_sa_t *this)
|
||||
{
|
||||
return this->rekeying_transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.set_rekeying_transaction.
|
||||
*/
|
||||
static void set_rekeying_transaction(private_ike_sa_t *this, rekey_ike_sa_t *rekey)
|
||||
{
|
||||
this->rekeying_transaction = rekey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.adopt_children.
|
||||
*/
|
||||
@@ -1883,10 +1918,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf;
|
||||
this->public.get_prf_auth_i = (prf_t *(*) (ike_sa_t *)) get_prf_auth_i;
|
||||
this->public.get_prf_auth_r = (prf_t *(*) (ike_sa_t *)) get_prf_auth_r;
|
||||
this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*)) derive_keys;
|
||||
this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
|
||||
this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
|
||||
this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa;
|
||||
this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
|
||||
this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator;
|
||||
this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
|
||||
this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa;
|
||||
this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
|
||||
@@ -1894,6 +1930,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
|
||||
this->public.set_lifetimes = (void(*)(ike_sa_t*,u_int32_t,u_int32_t))set_lifetimes;
|
||||
this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
|
||||
this->public.get_rekeying_transaction = (void*(*)(ike_sa_t*))get_rekeying_transaction;
|
||||
this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,void*))set_rekeying_transaction;
|
||||
this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children;
|
||||
|
||||
/* initialize private fields */
|
||||
@@ -1919,6 +1957,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->transaction_in = NULL;
|
||||
this->transaction_in_next = NULL;
|
||||
this->transaction_out = NULL;
|
||||
this->rekeying_transaction = NULL;
|
||||
this->state = IKE_CREATED;
|
||||
this->message_id_out = 0;
|
||||
/* set to NOW, as when we rekey an existing IKE_SA no message is exchanged */
|
||||
|
||||
+56
-22
@@ -51,30 +51,32 @@ typedef enum ike_sa_state_t ike_sa_state_t;
|
||||
* SA is in the state CREATED.
|
||||
* @verbatim
|
||||
+----------------+
|
||||
¦ SA_CREATED ¦
|
||||
¦ SA_CREATED ¦
|
||||
+----------------+
|
||||
¦
|
||||
on initiate()---> ¦ <----- on IKE_SA_INIT received
|
||||
¦
|
||||
¦
|
||||
on initiate()---> ¦ <----- on IKE_SA_INIT received
|
||||
V
|
||||
+----------------+
|
||||
¦ SA_CONNECTING ¦
|
||||
¦ SA_CONNECTING ¦
|
||||
+----------------+
|
||||
¦
|
||||
¦ <----- on IKE_AUTH successfully completed
|
||||
¦
|
||||
¦
|
||||
¦ <----- on IKE_AUTH successfully completed
|
||||
V
|
||||
+----------------+
|
||||
¦ SA_ESTABLISHED ¦
|
||||
¦ SA_ESTABLISHED ¦-------------------------+ <-- on rekeying
|
||||
+----------------+ ¦
|
||||
¦ V
|
||||
on delete()---> ¦ <----- on IKE_SA +-------------+
|
||||
¦ delete request ¦ SA_REKEYING ¦
|
||||
¦ received +-------------+
|
||||
V ¦
|
||||
+----------------+ ¦
|
||||
¦ SA_DELETING ¦<------------------------+ <-- after rekeying
|
||||
+----------------+
|
||||
¦
|
||||
on delete()---> ¦ <----- on IKE_SA delete request received
|
||||
¦
|
||||
+----------------+
|
||||
¦ SA_DELETING ¦
|
||||
+----------------+
|
||||
¦
|
||||
¦ <----- after delete() acknowledged
|
||||
¦
|
||||
\¦/
|
||||
¦
|
||||
¦ <----- after delete() acknowledged
|
||||
¦
|
||||
\V/
|
||||
X
|
||||
/ \
|
||||
@endverbatim
|
||||
@@ -98,6 +100,11 @@ enum ike_sa_state_t {
|
||||
*/
|
||||
IKE_ESTABLISHED,
|
||||
|
||||
/**
|
||||
* IKE_SA rekeying in progress
|
||||
*/
|
||||
IKE_REKEYING,
|
||||
|
||||
/**
|
||||
* IKE_SA is in progress of deletion
|
||||
*/
|
||||
@@ -279,7 +286,7 @@ struct ike_sa_t {
|
||||
* @brief Acquire connection setup for a policy.
|
||||
*
|
||||
* If an installed policy raises an acquire, the kernel calls
|
||||
* this function to establsh the CHILD_SA (and maybe the IKE_SA).
|
||||
* this function to establish the CHILD_SA (and maybe the IKE_SA).
|
||||
*
|
||||
* @param this calling object
|
||||
* @param reqid reqid of the CHILD_SA the policy belongs to.
|
||||
@@ -418,12 +425,13 @@ struct ike_sa_t {
|
||||
* @param nonce_i initiators nonce
|
||||
* @param nonce_r responders nonce
|
||||
* @param initiator TRUE if initiator, FALSE otherwise
|
||||
* @param rekey_prf PRF with SK_d key when rekeying, NULL otherwise
|
||||
* @param child_prf PRF with SK_d key when rekeying, NULL otherwise
|
||||
* @param old_prf general purpose PRF of old SA when rekeying
|
||||
*/
|
||||
status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal,
|
||||
diffie_hellman_t *dh,
|
||||
chunk_t nonce_i, chunk_t nonce_r,
|
||||
bool initiator, prf_t *rekey_prf);
|
||||
bool initiator, prf_t *child_prf, prf_t *old_prf);
|
||||
|
||||
/**
|
||||
* @brief Get the multi purpose prf.
|
||||
@@ -486,6 +494,14 @@ struct ike_sa_t {
|
||||
child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol,
|
||||
u_int32_t spi, bool inbound);
|
||||
|
||||
/**
|
||||
* @brief Create an iterator over all CHILD_SAs.
|
||||
*
|
||||
* @param this calling object
|
||||
* @return iterator
|
||||
*/
|
||||
iterator_t* (*create_child_sa_iterator) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Rekey the CHILD SA with the specified reqid.
|
||||
*
|
||||
@@ -555,6 +571,24 @@ struct ike_sa_t {
|
||||
*/
|
||||
status_t (*rekey) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Get the transaction which rekeys this IKE_SA.
|
||||
*
|
||||
* @todo Fix include for rekey_ike_sa.h
|
||||
*
|
||||
* @param this calling object
|
||||
* @return rekey_ike_sa_t transaction or NULL
|
||||
*/
|
||||
void* (*get_rekeying_transaction) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Set the transaction which rekeys this IKE_SA.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param rekey rekey_ike_sa_t transaction or NULL
|
||||
*/
|
||||
void (*set_rekeying_transaction) (ike_sa_t *this, void *rekey);
|
||||
|
||||
/**
|
||||
* @brief Move all children from other IKE_SA to this IKE_SA.
|
||||
*
|
||||
|
||||
@@ -406,7 +406,7 @@ static status_t process_notifys(private_create_child_sa_t *this, notify_payload_
|
||||
if (notify_type < 16383)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT,
|
||||
"received %s notify error (%d), deleting IKE_SA",
|
||||
"received %s notify error (%d), CHILD_SA creation failed",
|
||||
mapping_find(notify_type_m, notify_type),
|
||||
notify_type);
|
||||
return FAILED;
|
||||
@@ -540,6 +540,17 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* we do not allow the creation of new CHILDren/rekeying when IKE_SA is
|
||||
* rekeying */
|
||||
if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
|
||||
this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
|
||||
{
|
||||
build_notify(NO_ADDITIONAL_SAS, CHUNK_INITIALIZER, response, TRUE);
|
||||
this->logger->log(this->logger, AUDIT,
|
||||
"unable to create new CHILD_SAs, as rekeying in progress");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* Iterate over all payloads. */
|
||||
payloads = request->get_payload_iterator(request);
|
||||
while (payloads->has_next(payloads))
|
||||
|
||||
@@ -256,6 +256,17 @@ static status_t get_response(private_delete_child_sa_t *this, message_t *request
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* we can't handle a delete for a CHILD when we are rekeying. There
|
||||
* is no proper solution for this. We send a empty informational response,
|
||||
* as described in ikev2-clarifications draft */
|
||||
if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
|
||||
this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT,
|
||||
"unable to delete CHILD_SA, as rekeying in progress");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* iterate over all payloads */
|
||||
payloads = request->get_payload_iterator(request);
|
||||
while (payloads->has_next(payloads))
|
||||
|
||||
@@ -775,7 +775,7 @@ static status_t get_response(private_ike_sa_init_t *this,
|
||||
if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
|
||||
this->diffie_hellman,
|
||||
this->nonce_i, this->nonce_r,
|
||||
FALSE, NULL) != SUCCESS)
|
||||
FALSE, NULL, NULL) != SUCCESS)
|
||||
{
|
||||
notify_payload_t *notify = notify_payload_create();
|
||||
notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
|
||||
@@ -1019,7 +1019,7 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
|
||||
if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
|
||||
this->diffie_hellman,
|
||||
this->nonce_i, this->nonce_r,
|
||||
TRUE, NULL) != SUCCESS)
|
||||
TRUE, NULL, NULL) != SUCCESS)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT,
|
||||
"transform objects could not be created from selected proposal, deleting IKE_SA");
|
||||
|
||||
@@ -190,6 +190,15 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
|
||||
{
|
||||
this->logger->log(this->logger, ERROR,
|
||||
"tried to rekey in state %s, aborted",
|
||||
mapping_find(ike_sa_state_m,
|
||||
this->ike_sa->get_state(this->ike_sa)));
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
me = this->ike_sa->get_my_host(this->ike_sa);
|
||||
other = this->ike_sa->get_other_host(this->ike_sa);
|
||||
|
||||
@@ -274,7 +283,7 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
|
||||
this->logger->log(this->logger, AUDIT,
|
||||
"DH group %s (%d) not supported, aborting",
|
||||
mapping_find(diffie_hellman_group_m, dh_group), dh_group);
|
||||
return DESTROY_ME;
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +297,10 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
|
||||
this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
|
||||
request->set_message_id(request, this->message_id);
|
||||
|
||||
/* register us as rekeying to detect multiple rekeying */
|
||||
this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
|
||||
this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -380,7 +393,8 @@ static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
|
||||
if (this->new_sa->derive_keys(this->new_sa, this->proposal,
|
||||
this->diffie_hellman,
|
||||
this->nonce_i, this->nonce_r, initiator,
|
||||
this->ike_sa->get_child_prf(this->ike_sa)
|
||||
this->ike_sa->get_child_prf(this->ike_sa),
|
||||
this->ike_sa->get_prf(this->ike_sa)
|
||||
) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
@@ -388,14 +402,9 @@ static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
|
||||
|
||||
this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
|
||||
|
||||
this->new_sa->adopt_children(this->new_sa, this->ike_sa);
|
||||
|
||||
this->new_sa->set_lifetimes(this->new_sa,
|
||||
this->connection->get_soft_lifetime(this->connection),
|
||||
this->connection->get_hard_lifetime(this->connection));
|
||||
|
||||
charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
|
||||
this->new_sa = NULL;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -433,7 +442,8 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
|
||||
host_t *me, *other;
|
||||
message_t *response;
|
||||
status_t status;
|
||||
iterator_t *payloads;
|
||||
iterator_t *payloads, *iterator;
|
||||
child_sa_t *child_sa;
|
||||
sa_payload_t *sa_request = NULL;
|
||||
nonce_payload_t *nonce_request = NULL;
|
||||
ke_payload_t *ke_request = NULL;
|
||||
@@ -469,6 +479,33 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* if we already initiate a delete, we do not allow rekeying */
|
||||
if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
|
||||
{
|
||||
build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
|
||||
this->logger->log(this->logger, CONTROL,
|
||||
"unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* if we have a CHILD which is "half-open", we do not allow rekeying */
|
||||
iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
|
||||
while (iterator->iterate(iterator, (void**)&child_sa))
|
||||
{
|
||||
child_sa_state_t state = child_sa->get_state(child_sa);
|
||||
if (state == CHILD_CREATED ||
|
||||
state == CHILD_REKEYING ||
|
||||
state == CHILD_DELETING)
|
||||
{
|
||||
build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
|
||||
this->logger->log(this->logger, CONTROL,
|
||||
"unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
|
||||
iterator->destroy(iterator);
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
/* apply for notify processing */
|
||||
this->next = next;
|
||||
|
||||
@@ -622,7 +659,49 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
|
||||
response->add_payload(response, (payload_t*)ke_response);
|
||||
}
|
||||
|
||||
return switchto_new_sa(this, FALSE);
|
||||
status = switchto_new_sa(this, FALSE);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/* IKE_SA successfully created. If another transaction is already rekeying
|
||||
* this SA, our lower nonce must be registered for a later nonce compare. */
|
||||
{
|
||||
private_rekey_ike_sa_t *other;
|
||||
|
||||
other = this->ike_sa->get_rekeying_transaction(this->ike_sa);
|
||||
if (other)
|
||||
{
|
||||
/* store our lower nonce in the simultaneus transaction, we
|
||||
* will later compare it against his nonces when we calls conclude().
|
||||
* We do not adopt childrens yet, as we don't know if we'll win
|
||||
* the race...
|
||||
*/
|
||||
if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
|
||||
min(this->nonce_i.len, this->nonce_r.len)) < 0)
|
||||
{
|
||||
other->nonce_s = chunk_clone(this->nonce_i);
|
||||
}
|
||||
else
|
||||
{
|
||||
other->nonce_s = chunk_clone(this->nonce_r);
|
||||
}
|
||||
/* overwrite "other" in IKE_SA, allows "other" to access "this" */
|
||||
this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if we have no simultaneus transaction, we can safely adopt
|
||||
* all children and complete. */
|
||||
this->new_sa->adopt_children(this->new_sa, this->ike_sa);
|
||||
charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
|
||||
this->new_sa = NULL;
|
||||
}
|
||||
this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,6 +715,7 @@ static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
|
||||
sa_payload_t *sa_payload = NULL;
|
||||
nonce_payload_t *nonce_payload = NULL;
|
||||
ke_payload_t *ke_payload = NULL;
|
||||
private_rekey_ike_sa_t *other_trans;
|
||||
status_t status;
|
||||
|
||||
/* check message type */
|
||||
@@ -726,18 +806,72 @@ static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
|
||||
ke_payload->get_key_exchange_data(ke_payload));
|
||||
}
|
||||
|
||||
if (switchto_new_sa(this, TRUE) == SUCCESS)
|
||||
{
|
||||
/* new IKE_SA is in use now, delete old */
|
||||
*next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
|
||||
return SUCCESS;
|
||||
}
|
||||
else
|
||||
if (switchto_new_sa(this, TRUE) != SUCCESS)
|
||||
{
|
||||
/* this should not happen. But if, we destroy both SAs */
|
||||
*next = (transaction_t*)delete_ike_sa_create(this->new_sa);
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
/* IKE_SA successfully created. If the other peer initiated rekeying
|
||||
* in the meantime, we detect this by comparing the rekeying_transaction
|
||||
* of the SA. If it changed, we are not alone. Then we must compare the nonces.
|
||||
* If no simultaneous rekeying is going on, we just initiate the delete of
|
||||
* the superseded SA. */
|
||||
other_trans = this->ike_sa->get_rekeying_transaction(this->ike_sa);
|
||||
this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
|
||||
|
||||
if (this->nonce_s.ptr)
|
||||
{ /* simlutaneous rekeying is going on, not so good */
|
||||
chunk_t this_lowest;
|
||||
|
||||
/* first get our lowest nonce */
|
||||
if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
|
||||
min(this->nonce_i.len, this->nonce_r.len)) < 0)
|
||||
{
|
||||
this_lowest = this->nonce_i;
|
||||
}
|
||||
else
|
||||
{
|
||||
this_lowest = this->nonce_r;
|
||||
}
|
||||
/* then compare against other lowest nonce */
|
||||
if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
|
||||
min(this_lowest.len, this->nonce_s.len)) < 0)
|
||||
{
|
||||
this->logger->log(this->logger, ERROR,
|
||||
"detected simultaneous IKE_SA rekeying, deleting ours");
|
||||
this->lost = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, ERROR,
|
||||
"detected simultaneous IKE_SA rekeying, but ours is preferred");
|
||||
}
|
||||
if (this->lost)
|
||||
{
|
||||
/* the other has won, he gets our children */
|
||||
other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
|
||||
/* we have lost simlutaneous rekeying, delete the SA we just have created */
|
||||
this->new_sa->delete(this->new_sa);
|
||||
}
|
||||
/* other trans' SA is still not checked in, so do it now. It's SA will get
|
||||
* deleted by remote peer. */
|
||||
charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
|
||||
other_trans->new_sa = NULL;
|
||||
}
|
||||
|
||||
if (!this->lost)
|
||||
{
|
||||
/* we have won. delete old IKE_SA, and migrate all children */
|
||||
*next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
|
||||
this->new_sa->adopt_children(this->new_sa, this->ike_sa);
|
||||
}
|
||||
|
||||
charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
|
||||
this->new_sa = NULL;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
|
||||
}
|
||||
case CREATE_CHILD_SA:
|
||||
{
|
||||
if (ike_sa->get_state(ike_sa) != IKE_ESTABLISHED)
|
||||
if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
|
||||
}
|
||||
case INFORMATIONAL:
|
||||
{
|
||||
if (ike_sa->get_state(ike_sa) == IKE_CREATED)
|
||||
if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user