implement proper handling of most simultaneous IKE_SA rekeying cases

This commit is contained in:
Martin Willi
2006-08-23 07:30:43 +00:00
parent c3f97102f1
commit 3183006de2
8 changed files with 288 additions and 57 deletions
+12 -1
View File
@@ -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))
+2 -2
View File
@@ -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");
+150 -16
View File
@@ -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;
}
/**
+2 -2
View File
@@ -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;
}