diff options
Diffstat (limited to 'src/charon/sa/transactions/create_child_sa.c')
-rw-r--r-- | src/charon/sa/transactions/create_child_sa.c | 121 |
1 files changed, 108 insertions, 13 deletions
diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c index 6fce5e07b..30ccdef06 100644 --- a/src/charon/sa/transactions/create_child_sa.c +++ b/src/charon/sa/transactions/create_child_sa.c @@ -28,7 +28,6 @@ #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/ts_payload.h> -#include <sa/child_sa.h> #include <sa/transactions/delete_child_sa.h> #include <utils/randomizer.h> @@ -116,6 +115,11 @@ struct private_create_child_sa_t { child_sa_t *rekeyed_sa; /** + * Have we lost the simultaneous rekeying nonce compare? + */ + bool lost; + + /** * source of randomness */ randomizer_t *randomizer; @@ -143,7 +147,7 @@ static u_int32_t requested(private_create_child_sa_t *this) } /** - * Implementation of transaction_t.rekeys_child. + * Implementation of create_child_sa_t.rekeys_child. */ static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa) { @@ -151,6 +155,15 @@ static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa) } /** + * Implementation of create_child_sa_t.cancel. + */ +static void cancel(private_create_child_sa_t *this) +{ + this->rekeyed_sa = NULL; + this->lost = TRUE; +} + +/** * Implementation of transaction_t.get_request. */ static status_t get_request(private_create_child_sa_t *this, message_t **result) @@ -158,6 +171,15 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result) message_t *request; host_t *me, *other; + /* check if we are not already rekeying */ + if (this->rekeyed_sa && + this->rekeyed_sa->is_rekeying(this->rekeyed_sa)) + { + this->logger->log(this->logger, ERROR, + "rekeying a CHILD_SA which is already rekeying, aborted"); + return FAILED; + } + /* check if we already have built a message (retransmission) */ if (this->message) { @@ -249,8 +271,8 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result) notify->set_spi(notify, this->rekeyed_sa->get_spi(this->rekeyed_sa, TRUE)); request->add_payload(request, (payload_t*)notify); - /* and mark sa with rekeying-in-progress */ - this->rekeyed_sa->set_rekeyed(this->rekeyed_sa); + /* register us as rekeying to detect multiple rekeying */ + this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, &this->public); } return SUCCESS; @@ -605,12 +627,21 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); response->add_payload(response, (payload_t*)ts_response); } - /* CHILD_SA successfully created. Now we must check if it rekeys an old one, - * and if so, mark the old as rekeyed. It will get deleted from the other - * peer. */ + /* CHILD_SA successfully created. We set us as the rekeying transaction of + * this SA. If we already initiated rekeying of the same SA, we will detect + * this later in the conclude() call. */ if (this->rekeyed_sa) { - this->rekeyed_sa->set_rekeyed(this->rekeyed_sa); + if (this->rekeyed_sa->is_rekeying(this->rekeyed_sa)) + { + /* rekeying already in progress, register us, too */ + this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, &this->public); + } + else + { + /* no rekeying in progress. mark SA as rekeyed, but not conflicted */ + this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL); + } } return SUCCESS; } @@ -628,6 +659,8 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response, ts_payload_t *tsi_payload = NULL; ts_payload_t *tsr_payload = NULL; status_t status; + child_sa_t *new_child = NULL; + delete_child_sa_t *delete_child_sa; /* check message type */ if (response->get_exchange_type(response) != CREATE_CHILD_SA) @@ -726,6 +759,7 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response, "CHILD_SA creation failed"); return FAILED; } + new_child = this->child_sa; if (install_child_sa(this, TRUE) != SUCCESS) { this->logger->log(this->logger, ERROR, @@ -733,14 +767,73 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response, return FAILED; } } - /* CHILD_SA successfully created. If we have rekeyed an old one, delete it */ + /* CHILD_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. */ if (this->rekeyed_sa) { - delete_child_sa_t *delete_child_sa; + private_create_child_sa_t *other; + + other = (private_create_child_sa_t*) + this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa); - delete_child_sa = delete_child_sa_create(this->ike_sa, - this->message_id + 1); - delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa); + /* we are not rekeying anymore, unregister us */ + this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL); + + if (other != this) + { /* simlutaneous rekeying is going on, not so good */ + chunk_t this_lowest, other_lowest; + + /* check if this has a lower nonce the other */ + 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; + } + if (memcmp(other->nonce_i.ptr, other->nonce_r.ptr, + min(other->nonce_i.len, other->nonce_r.len)) < 0) + { + other_lowest = other->nonce_i; + } + else + { + other_lowest = other->nonce_r; + } + if (memcmp(this_lowest.ptr, other_lowest.ptr, + min(this_lowest.len, other_lowest.len)) < 0) + { + this->logger->log(this->logger, ERROR, + "detected simultaneous CHILD_SA rekeying, but ours is preferred"); + } + else + { + + this->logger->log(this->logger, ERROR, + "detected simultaneous CHILD_SA rekeying, deleting ours"); + this->lost = TRUE; + } + } + /* delete the old SA if we have won the rekeying nonce compare*/ + if (!this->lost) + { + other->rekeyed_sa->set_rekeying_transaction(other->rekeyed_sa, NULL); + delete_child_sa = delete_child_sa_create(this->ike_sa, this->message_id + 1); + delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa); + *next = (transaction_t*)delete_child_sa; + } + } + if (this->lost) + { + /* we have lost simlutaneous rekeying, delete the CHILD_SA we just have created */ + delete_child_sa = delete_child_sa_create(this->ike_sa, this->message_id + 1); + new_child->set_rekeying_transaction(new_child, NULL); + delete_child_sa->set_child_sa(delete_child_sa, new_child); *next = (transaction_t*)delete_child_sa; } return SUCCESS; @@ -788,6 +881,7 @@ create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id /* public functions */ this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child; + this->public.cancel = (void(*)(create_child_sa_t*))cancel; /* private data */ this->ike_sa = ike_sa; @@ -799,6 +893,7 @@ create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id this->nonce_r = CHUNK_INITIALIZER; this->child_sa = NULL; this->rekeyed_sa = NULL; + this->lost = FALSE; this->proposal = NULL; this->tsi = NULL; this->tsr = NULL; |