diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 93546c987..e5779fe47 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -13,6 +13,7 @@ sa/states/ike_sa_init_responded.c sa/states/ike_sa_established.c sa/states/ike_s sa/states/responder_init.c sa/states/responder_init.h sa/states/initiator_init.c sa/states/initiator_init.h \ sa/states/ike_sa_init_responded.h sa/states/ike_auth_requested.c sa/states/ike_auth_requested.h \ sa/states/delete_ike_sa_requested.h sa/states/delete_ike_sa_requested.c \ +sa/states/delete_child_sa_requested.h sa/states/delete_child_sa_requested.c \ sa/states/create_child_sa_requested.c sa/states/create_child_sa_requested.h \ sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ sa/ike_sa_id.c sa/ike_sa_id.h sa/authenticator.c sa/authenticator.h encoding/payloads/encryption_payload.c \ diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c index 4e238a32e..5f6706bc1 100644 --- a/src/charon/config/policies/policy.c +++ b/src/charon/config/policies/policy.c @@ -315,8 +315,8 @@ static void add_proposal(private_policy_t *this, proposal_t *proposal) */ static u_int32_t get_soft_lifetime(policy_t *this) { - srandom(time(NULL)); - return 0; //5 + random() % 3; + srandom(time(NULL)+getpid()); + return 5 + (random() % 10); } /** @@ -324,7 +324,7 @@ static u_int32_t get_soft_lifetime(policy_t *this) */ static u_int32_t get_hard_lifetime(policy_t *this) { - return 0; //9; + return 20; } /** diff --git a/src/charon/encoding/payloads/delete_payload.c b/src/charon/encoding/payloads/delete_payload.c index 28e78800f..eb8dee191 100644 --- a/src/charon/encoding/payloads/delete_payload.c +++ b/src/charon/encoding/payloads/delete_payload.c @@ -71,6 +71,11 @@ struct private_delete_payload_t { * The contained SPI's. */ chunk_t spis; + + /** + * List containing u_int32_t spis + */ + linked_list_t *spi_list; }; /** @@ -121,23 +126,30 @@ encoding_rule_t delete_payload_encodings[] = { */ static status_t verify(private_delete_payload_t *this) { - if ((this->protocol_id == 0) || - (this->protocol_id > 3)) + switch (this->protocol_id) { - /* reserved IDs */ - return FAILED; + case PROTO_AH: + case PROTO_ESP: + if (this->spi_size != 4) + { + return FAILED; + } + break; + case PROTO_IKE: + case 0: + /* IKE deletion has no spi assigned! */ + if (this->spi_size != 0) + { + return FAILED; + } + break; + default: + return FAILED; } if (this->spis.len != (this->spi_count * this->spi_size)) { return FAILED; } - if ((this->protocol_id == PROTO_IKE) && (this->spis.len != 0)) - { - /* IKE deletion has no spi assigned! */ - return FAILED; - } - - return SUCCESS; } @@ -225,16 +237,15 @@ static void set_spi_count (private_delete_payload_t *this, u_int16_t spi_count) /** * Implementation of delete_payload_t.get_spi_count. */ -static u_int16_t get_spi_count (private_delete_payload_t *this) +static u_int16_t get_spi_count(private_delete_payload_t *this) { return (this->spi_count); } - /** * Implementation of delete_payload_t.set_spis. */ -static void set_spis (private_delete_payload_t *this, chunk_t spis) +static void set_spis(private_delete_payload_t *this, chunk_t spis) { if (this->spis.ptr != NULL) { @@ -254,18 +265,41 @@ static chunk_t get_spis (private_delete_payload_t *this) } /** - * Implementation of delete_payload_t.get_spis_clone. + * Implementation of delete_payload_t.add_spi. */ -static chunk_t get_spis_clone (private_delete_payload_t *this) +static void add_spi(private_delete_payload_t *this, u_int32_t spi) { - chunk_t cloned_spis; - if (this->spis.ptr == NULL) + /* only add SPIs if AH|ESP, ignore others */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) { - return (this->spis); + this->spi_count += 1; + this->spis.len += this->spi_size; + this->spis.ptr = realloc(this->spis.ptr, this->spis.len); + *(u_int32_t*)(this->spis.ptr + (this->spis.len / this->spi_size - 1)) = spi; } - cloned_spis.ptr = clalloc(this->spis.ptr,this->spis.len); - cloned_spis.len = this->spis.len; - return cloned_spis; +} + +/** + * Implementation of delete_payload_t.create_spi_iterator. + */ +static iterator_t* create_spi_iterator(private_delete_payload_t *this) +{ + int i; + + if (this->spi_list == NULL) + { + this->spi_list = linked_list_create(); + /* only parse SPIs if AH|ESP */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) + { + for (i = 0; i < this->spi_count; i++) + { + u_int32_t spi = *(u_int32_t*)(this->spis.ptr + i * this->spi_size); + this->spi_list->insert_last(this->spi_list, (void*)spi); + } + } + } + return this->spi_list->create_iterator(this->spi_list, TRUE); } /** @@ -275,16 +309,19 @@ static void destroy(private_delete_payload_t *this) { if (this->spis.ptr != NULL) { - chunk_free(&(this->spis)); + chunk_free(&this->spis); + } + if (this->spi_list) + { + this->spi_list->destroy(this->spi_list); } - free(this); } /* * Described in header */ -delete_payload_t *delete_payload_create() +delete_payload_t *delete_payload_create(protocol_id_t protocol_id) { private_delete_payload_t *this = malloc_thing(private_delete_payload_t); @@ -306,17 +343,19 @@ delete_payload_t *delete_payload_create() this->public.set_spi_count = (void (*) (delete_payload_t *,u_int16_t)) set_spi_count; this->public.get_spi_count = (u_int16_t (*) (delete_payload_t *)) get_spi_count; this->public.set_spis = (void (*) (delete_payload_t *,chunk_t)) set_spis; - this->public.get_spis_clone = (chunk_t (*) (delete_payload_t *)) get_spis_clone; this->public.get_spis = (chunk_t (*) (delete_payload_t *)) get_spis; + this->public.add_spi = (void (*) (delete_payload_t *,u_int32_t))add_spi; + this->public.create_spi_iterator = (iterator_t* (*) (delete_payload_t *)) create_spi_iterator; /* private variables */ this->critical = FALSE; this->next_payload = NO_PAYLOAD; - this->payload_length =DELETE_PAYLOAD_HEADER_LENGTH; - this->protocol_id = PROTO_NONE; - this->spi_size = 0; + this->payload_length = DELETE_PAYLOAD_HEADER_LENGTH; + this->protocol_id = protocol_id; + this->spi_size = protocol_id == PROTO_AH || protocol_id == PROTO_ESP ? 4 : 0; this->spi_count = 0; this->spis = CHUNK_INITIALIZER; + this->spi_list = NULL; - return (&(this->public)); + return (&this->public); } diff --git a/src/charon/encoding/payloads/delete_payload.h b/src/charon/encoding/payloads/delete_payload.h index a25d0f116..231461005 100644 --- a/src/charon/encoding/payloads/delete_payload.h +++ b/src/charon/encoding/payloads/delete_payload.h @@ -61,6 +61,8 @@ struct delete_payload_t { * * @param this calling delete_payload_t object * @param protocol_id protocol ID + * + * @deprecated is set by constructor */ void (*set_protocol_id) (delete_payload_t *this, protocol_id_t protocol_id); @@ -74,10 +76,11 @@ struct delete_payload_t { /** * @brief Set the SPI size. - * * * @param this calling delete_payload_t object * @param spi_size SPI size + * + * @deprecated is set by constructor */ void (*set_spi_size) (delete_payload_t *this, u_int8_t spi_size); @@ -94,6 +97,8 @@ struct delete_payload_t { * * @param this calling delete_payload_t object * @param spi_count SPI count + * + * @deprecated is incremented via add_spi */ void (*set_spi_count) (delete_payload_t *this, u_int16_t spi_count); @@ -112,19 +117,11 @@ struct delete_payload_t { * * @param this calling delete_payload_t object * @param data SPI's as chunk_t + * + * @deprecated use add_spi */ void (*set_spis) (delete_payload_t *this, chunk_t spis); - /** - * @brief Get the SPI's. - * - * Returned data are a copy of the internal one. - * - * @param this calling delete_payload_t object - * @return SPI's chunk_t - */ - chunk_t (*get_spis_clone) (delete_payload_t *this); - /** * @brief Get the SPI's. * @@ -132,9 +129,30 @@ struct delete_payload_t { * * @param this calling delete_payload_t object * @return SPI's as chunk_t + * + * @deprecated use create_spi_iterator */ chunk_t (*get_spis) (delete_payload_t *this); + /** + * @brief Add an SPI to the list of deleted SAs. + * + * @param this calling delete_payload_t object + * @param spi spi to add + */ + void (*add_spi) (delete_payload_t *this, u_int32_t spi); + + /** + * @brief Get an iterator over the SPIs. + * + * The resulting interators current() function returns + * u_int32_t SPIs directly. + * + * @param this calling delete_payload_t object + * @return iterator over SPIs + */ + iterator_t *(*create_spi_iterator) (delete_payload_t *this); + /** * @brief Destroys an delete_payload_t object. * @@ -146,11 +164,11 @@ struct delete_payload_t { /** * @brief Creates an empty delete_payload_t object. * - * @return delete_payload_t object + * @param protocol_id protocol, such as AH|ESP + * @return delete_payload_t object * * @ingroup payloads */ -delete_payload_t *delete_payload_create(void); - +delete_payload_t *delete_payload_create(protocol_id_t protocol_id); #endif /* DELETE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/payload.c b/src/charon/encoding/payloads/payload.c index b89e80a53..be58adf8e 100644 --- a/src/charon/encoding/payloads/payload.c +++ b/src/charon/encoding/payloads/payload.c @@ -113,7 +113,7 @@ payload_t *payload_create(payload_type_t type) case NOTIFY: return (payload_t*)notify_payload_create(); case DELETE: - return (payload_t*)delete_payload_create(); + return (payload_t*)delete_payload_create(0); case VENDOR_ID: return (payload_t*)vendor_id_payload_create(); case CONFIGURATION: diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c index ccad9103a..2e5f024f9 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.c +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -70,7 +70,7 @@ static status_t execute(private_delete_child_sa_job_t *this) return DESTROY_ME; } - /* TODO */ + ike_sa->delete_child_sa(ike_sa, this->reqid); status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); return DESTROY_ME; diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 5c09b731e..e1dff27da 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -92,6 +92,11 @@ struct private_child_sa_t { */ u_int32_t hard_lifetime; + /** + * reqid of a CHILD_SA which rekeyed this one + */ + u_int32_t rekeyed; + /** * CHILD_SAs own logger */ @@ -378,6 +383,14 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list return SUCCESS; } +/** + * Implementation of child_sa_t.set_rekeyed. + */ +static void set_rekeyed(private_child_sa_t *this, u_int32_t reqid) +{ + this->rekeyed = reqid; +} + /** * Implementation of child_sa_t.log_status. */ @@ -428,42 +441,47 @@ static void log_status(private_child_sa_t *this, logger_t *logger, char* name) */ static void destroy(private_child_sa_t *this) { - /* delete all policies in the kernel */ sa_policy_t *policy; - while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) - { - charon->kernel_interface->del_policy(charon->kernel_interface, - this->me.addr, this->other.addr, - policy->me.net, policy->other.net, - policy->me.net_mask, policy->other.net_mask, - XFRM_POLICY_OUT, policy->upper_proto); - - charon->kernel_interface->del_policy(charon->kernel_interface, - this->other.addr, this->me.addr, - policy->other.net, policy->me.net, - policy->other.net_mask, policy->me.net_mask, - XFRM_POLICY_IN, policy->upper_proto); - - charon->kernel_interface->del_policy(charon->kernel_interface, - this->other.addr, this->me.addr, - policy->other.net, policy->me.net, - policy->other.net_mask, policy->me.net_mask, - XFRM_POLICY_FWD, policy->upper_proto); - - policy->me.net->destroy(policy->me.net); - policy->other.net->destroy(policy->other.net); - free(policy); - } - this->policies->destroy(this->policies); /* delete SAs in the kernel, if they are set up */ if (this->protocol != PROTO_NONE) { charon->kernel_interface->del_sa(charon->kernel_interface, - this->other.addr, this->me.spi, this->protocol); + this->me.addr, this->me.spi, this->protocol); charon->kernel_interface->del_sa(charon->kernel_interface, - this->me.addr, this->other.spi, this->protocol); + this->other.addr, this->other.spi, this->protocol); } + + /* delete all policies in the kernel */ + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + if (!this->rekeyed) + { + /* let rekeyed policies, as they are used by another child_sa */ + charon->kernel_interface->del_policy(charon->kernel_interface, + this->me.addr, this->other.addr, + policy->me.net, policy->other.net, + policy->me.net_mask, policy->other.net_mask, + XFRM_POLICY_OUT, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other.addr, this->me.addr, + policy->other.net, policy->me.net, + policy->other.net_mask, policy->me.net_mask, + XFRM_POLICY_IN, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other.addr, this->me.addr, + policy->other.net, policy->me.net, + policy->other.net_mask, policy->me.net_mask, + XFRM_POLICY_FWD, policy->upper_proto); + } + policy->me.net->destroy(policy->me.net); + policy->other.net->destroy(policy->other.net); + free(policy); + } + this->policies->destroy(this->policies); + free(this); } @@ -483,6 +501,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, u_int32_t soft_lifetime, this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add; this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update; this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; + this->public.set_rekeyed = (void (*)(child_sa_t*,u_int32_t))set_rekeyed; this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status; this->public.destroy = (void(*)(child_sa_t*))destroy; @@ -497,6 +516,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, u_int32_t soft_lifetime, this->reqid = ++reqid; this->policies = linked_list_create(); this->protocol = PROTO_NONE; + this->rekeyed = 0; return (&this->public); } diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 921b570af..2bbe95fe0 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -145,6 +145,18 @@ struct child_sa_t { */ status_t (*add_policies) (child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list); + /** + * @brief Mark this child_sa as rekeyed. + * + * Since an SA which rekeys a old SA shares the same policy, + * we must mark a child_sa as rekeyed. A so marked SA does + * not remove its policy, as the new SA uses it. + * + * @param this calling object + * @param reqid reqid of the SA which replaces this one. + */ + void (*set_rekeyed) (child_sa_t *this, u_int32_t reqid); + /** * @brief Log the status of a child_sa to a logger. * diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 8dbca1982..7ddf0d806 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -898,7 +899,92 @@ static child_sa_t *get_child_sa(private_ike_sa_t *this, u_int32_t reqid) */ static status_t delete_child_sa(private_ike_sa_t *this, u_int32_t reqid) { - return NOT_FOUND; + message_t *request; + child_sa_t *child_sa; + delete_payload_t *delete_payload; + state_t *old_state; + + if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "Delete of a CHILD_SA whose IKE_SA not in state IKE_SA_ESTABLISHED, aborting"); + return FAILED; + } + + child_sa = get_child_sa(this, reqid); + if (child_sa == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "IKE_SA does not contain a CHILD_SA with reqid %d", reqid); + return FAILED; + } + build_message(this, INFORMATIONAL, TRUE, &request); + delete_payload = delete_payload_create(child_sa->get_protocol(child_sa)); + delete_payload->add_spi(delete_payload, child_sa->get_spi(child_sa, FALSE)); + request->add_payload(request, (payload_t*)delete_payload); + + send_request(this, request); + + old_state = this->current_state; + set_new_state(this, (state_t*)delete_child_sa_requested_create(&this->protected)); + old_state->destroy(old_state); + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.destroy_child_sa. + */ +static u_int32_t destroy_child_sa(private_ike_sa_t *this, u_int32_t spi) +{ + iterator_t *iterator; + child_sa_t *child_sa; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&child_sa); + if (child_sa->get_spi(child_sa, TRUE) == spi) + { + iterator->remove(iterator); + break; + } + else + { + child_sa = NULL; + } + } + iterator->destroy(iterator); + if (child_sa == NULL) + { + this->logger->log(this->logger, ERROR, + "IKE_SA does not contain a CHILD_SA with spi 0x%x", spi); + return 0; + } + + spi = child_sa->get_spi(child_sa, FALSE); + child_sa->destroy(child_sa); + return spi; +} + +/** + * Implementation of protected_ike_sa_t.get_child_sa. + */ +static child_sa_t* get_child_sa_by_spi(private_ike_sa_t *this, u_int32_t spi) +{ + iterator_t *iterator; + child_sa_t *current, *found = NULL; + + iterator = this->child_sas->create_iterator(this->child_sas, FALSE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t); + if (current->get_spi(current, TRUE) == spi) + { + found = current; + } + } + iterator->destroy(iterator); + return found; } /** @@ -920,7 +1006,7 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) { this->logger->log(this->logger, ERROR|LEVEL1, - "Rekeying of an IKE_SA not in state IKE_SA_ESTABLISHED, aborting", reqid); + "Rekeying of an CHILD_SA whose IKE_SA not in state IKE_SA_ESTABLISHED, aborting"); return FAILED; } @@ -935,7 +1021,7 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) build_message(this, CREATE_CHILD_SA, TRUE, &request); notify = notify_payload_create_from_protocol_and_type( child_sa->get_protocol(child_sa), REKEY_SA); - notify->set_spi(notify, child_sa->get_spi(child_sa, TRUE)); + notify->set_spi(notify, child_sa->get_spi(child_sa, FALSE)); request->add_payload(request, (payload_t*)notify); proposals = this->policy->get_proposals(this->policy); @@ -967,7 +1053,7 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) send_request(this, request); old_state = this->current_state; - set_new_state(this, (state_t*)create_child_sa_requested_create(&this->protected, child_sa, nonce)); + set_new_state(this, (state_t*)create_child_sa_requested_create(&this->protected, child_sa, nonce, reqid)); old_state->destroy(old_state); return SUCCESS; @@ -1074,8 +1160,7 @@ static status_t delete_(private_ike_sa_t *this) build_message(this, INFORMATIONAL, TRUE, &informational_request); /* delete for the full IKE_SA, this deletes all child_sa's implicit */ - delete_payload = delete_payload_create(); - delete_payload->set_protocol_id(delete_payload, PROTO_IKE); + delete_payload = delete_payload_create(PROTO_IKE); informational_request->add_payload(informational_request, (payload_t*)delete_payload); @@ -1241,8 +1326,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->protected.reset_message_buffers = (void (*) (protected_ike_sa_t *)) reset_message_buffers; this->protected.get_last_responded_message = (message_t * (*) (protected_ike_sa_t *)) get_last_responded_message; this->protected.get_last_requested_message = (message_t * (*) (protected_ike_sa_t *)) get_last_requested_message; - this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id; + this->protected.destroy_child_sa = (u_int32_t (*)(protected_ike_sa_t*,u_int32_t))destroy_child_sa; + this->protected.get_child_sa = (child_sa_t* (*)(protected_ike_sa_t*,u_int32_t))get_child_sa_by_spi; /* initialize private fields */ this->logger = logger_manager->get_logger(logger_manager, IKE_SA); diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index d73dc7165..6b9d9b662 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -463,6 +463,24 @@ struct protected_ike_sa_t { */ void (*add_child_sa) (protected_ike_sa_t *this, child_sa_t *child_sa); + /** + * @brief Destroys a CHILD_SA upon request from the other peer. + * + * @param this calling object + * @param spi inbound spi of the CHILD_SA to destroy + * @return outbound spi of the destroyed CHILD_SA + */ + u_int32_t (*destroy_child_sa) (protected_ike_sa_t *this, u_int32_t spi); + + /** + * @brief Get a CHILD_SA upon request from the other peer. + * + * @param this calling object + * @param spi spi of the CHILD_SA + * @return child_sa, or NULL if none found + */ + child_sa_t* (*get_child_sa) (protected_ike_sa_t *this, u_int32_t spi); + /** * @brief Get the last responded message. * diff --git a/src/charon/sa/states/create_child_sa_requested.c b/src/charon/sa/states/create_child_sa_requested.c index cb5313966..b9bf2800d 100644 --- a/src/charon/sa/states/create_child_sa_requested.c +++ b/src/charon/sa/states/create_child_sa_requested.c @@ -85,6 +85,11 @@ struct private_create_child_sa_requested_t { */ child_sa_t *child_sa; + /** + * Reqid of the old CHILD_SA, when rekeying + */ + u_int32_t reqid; + /** * Assigned logger. * @@ -205,6 +210,7 @@ static status_t process_message(private_create_child_sa_requested_t *this, messa status_t status; chunk_t seed; prf_plus_t *prf_plus; + child_sa_t *old_child_sa; this->policy = this->ike_sa->get_policy(this->ike_sa); if (response->get_exchange_type(response) != CREATE_CHILD_SA) @@ -339,7 +345,7 @@ static status_t process_message(private_create_child_sa_requested_t *this, messa 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); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "Rekey seed", seed); chunk_free(&seed); status = this->child_sa->update(this->child_sa, this->proposal, prf_plus); @@ -364,6 +370,17 @@ static status_t process_message(private_create_child_sa_requested_t *this, messa 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); + /* if we are rekeying, inform the old child SA that it has been superseeded and + * start its delete */ + if (this->reqid) + { + old_child_sa = this->ike_sa->public.get_child_sa(&this->ike_sa->public, this->reqid); + if (old_child_sa) + { + old_child_sa->set_rekeyed(old_child_sa, this->child_sa->get_reqid(this->child_sa)); + } + this->ike_sa->public.delete_child_sa(&this->ike_sa->public, this->reqid); + } return SUCCESS; } @@ -388,7 +405,7 @@ static void destroy(private_create_child_sa_requested_t *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) +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, u_int32_t reqid) { private_create_child_sa_requested_t *this = malloc_thing(private_create_child_sa_requested_t); @@ -402,6 +419,7 @@ create_child_sa_requested_t *create_child_sa_requested_create(protected_ike_sa_t this->child_sa = child_sa; this->nonce_i = nonce_i; this->nonce_r = CHUNK_INITIALIZER; + this->reqid = reqid; this->logger = logger_manager->get_logger(logger_manager, IKE_SA); return &(this->public); diff --git a/src/charon/sa/states/create_child_sa_requested.h b/src/charon/sa/states/create_child_sa_requested.h index ced6273b0..20c17b6a1 100644 --- a/src/charon/sa/states/create_child_sa_requested.h +++ b/src/charon/sa/states/create_child_sa_requested.h @@ -47,13 +47,18 @@ struct create_child_sa_requested_t { /** * @brief Constructor of class create_child_sa_requested_t * + * If this CREATE_CHILD_SA message is to rekey a CHILD_SA, + * the child_sa with the specified reqid gets deleted after a new + * one is set up. + * * @param ike_sa assigned ike_sa * @param child_sa newly created child sa to complete * @param nonce nonce sent at initialization + * @param reqid reqid, when rekeying a child SA. * @return created create_child_sa_requested_t object * * @ingroup states */ -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); +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, u_int32_t reqid); #endif /*CREATE_CHILD_SA_REQEUSTED_H_*/ diff --git a/src/charon/sa/states/delete_child_sa_requested.c b/src/charon/sa/states/delete_child_sa_requested.c new file mode 100644 index 000000000..4d4ea5f11 --- /dev/null +++ b/src/charon/sa/states/delete_child_sa_requested.c @@ -0,0 +1,177 @@ +/** + * @file delete_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 . + * + * 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 + +#include "delete_child_sa_requested.h" + +#include +#include +#include +#include +#include +#include + + +typedef struct private_delete_child_sa_requested_t private_delete_child_sa_requested_t; + +/** + * Private data of a delete_child_sa_requested_t object. + */ +struct private_delete_child_sa_requested_t { + /** + * Public interface of delete_child_sa_requested_t. + */ + delete_child_sa_requested_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * Assigned logger. + * + * Is logger of ike_sa! + */ + logger_t *logger; +}; + + +/** + * Process the response + */ +static status_t process_message(private_delete_child_sa_requested_t *this, message_t *response) +{ + ike_sa_id_t *ike_sa_id; + crypter_t *crypter; + signer_t *signer; + status_t status; + iterator_t *iterator; + payload_t *payload; + delete_payload_t *delete_response; + + if (response->get_exchange_type(response) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state delete_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, "INFORMATIONAL requests not allowed state delete_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, "INFORMATIONAL response decryption failed. Ignoring message"); + return status; + } + + iterator = response->get_payload_iterator(response); + while (iterator->has_next(iterator)) { + iterator->current(iterator, (void**)&payload); + switch (payload->get_type(payload)) + { + case DELETE: + delete_response = (delete_payload_t*)payload; + break; + default: + break; + } + + } + iterator->destroy(iterator); + + if (delete_response) + { + iterator = delete_response->create_spi_iterator(delete_response); + while (iterator->has_next(iterator)) + { + u_int32_t spi; + iterator->current(iterator, (void**)&spi); + this->logger->log(this->logger, CONTROL, "DELETE request for CHILD_SA with SPI 0x%x received", spi); + this->ike_sa->destroy_child_sa(this->ike_sa, spi); + } + iterator->destroy(iterator); + } + + 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_delete_child_sa_requested_t *this) +{ + return DELETE_CHILD_SA_REQUESTED; +} + +/** + * Implementation of state_t.destroy. + */ +static void destroy(private_delete_child_sa_requested_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +delete_child_sa_requested_t *delete_child_sa_requested_create(protected_ike_sa_t *ike_sa) +{ + private_delete_child_sa_requested_t *this = malloc_thing(private_delete_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->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &(this->public); +} diff --git a/src/charon/sa/states/delete_child_sa_requested.h b/src/charon/sa/states/delete_child_sa_requested.h new file mode 100644 index 000000000..e70ff182d --- /dev/null +++ b/src/charon/sa/states/delete_child_sa_requested.h @@ -0,0 +1,57 @@ +/** + * @file delete_child_sa_requested.h + * + * @brief Interface of delete_child_sa_requested_t. + * + */ + +/* + * 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 . + * + * 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 DELETE_CHILD_SA_REQEUSTED_H_ +#define DELETE_CHILD_SA_REQEUSTED_H_ + +#include +#include +#include + +typedef struct delete_child_sa_requested_t delete_child_sa_requested_t; + +/** + * @brief State after a CREATE_CHILD_SA request was sent. + * + * @b Constructors: + * - delete_child_sa_requested_create() + * + * @ingroup states + */ +struct delete_child_sa_requested_t { + /** + * methods of the state_t interface + */ + state_t state_interface; +}; + +/** + * @brief Constructor of class delete_child_sa_requested_t + * + * @param ike_sa assigned ike_sa + * @return created delete_child_sa_requested_t object + * + * @ingroup states + */ +delete_child_sa_requested_t *delete_child_sa_requested_create(protected_ike_sa_t *ike_sa); + +#endif /*DELETE_CHILD_SA_REQEUSTED_H_*/ diff --git a/src/charon/sa/states/ike_sa_established.c b/src/charon/sa/states/ike_sa_established.c index d408988d0..5841f74f4 100644 --- a/src/charon/sa/states/ike_sa_established.c +++ b/src/charon/sa/states/ike_sa_established.c @@ -125,7 +125,7 @@ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_ 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); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "Rekey seed", seed); chunk_free(&seed); policy = this->ike_sa->get_policy(this->ike_sa); @@ -240,8 +240,10 @@ static status_t process_create_child_sa(private_ike_sa_established_t *this, mess ts_payload_t *tsi_request = NULL, *tsr_request = NULL; sa_payload_t *sa_request = NULL; nonce_payload_t *nonce_request = NULL; + notify_payload_t *notify = NULL; iterator_t *payloads; status_t status; + child_sa_t *old_child_sa; /* iterate over incoming payloads. Message is verified, we can be sure there are the required payloads */ payloads = request->get_payload_iterator(request); @@ -260,7 +262,7 @@ static status_t process_create_child_sa(private_ike_sa_established_t *this, mess case TRAFFIC_SELECTOR_INITIATOR: { tsi_request = (ts_payload_t*)payload; - break; + break; } case TRAFFIC_SELECTOR_RESPONDER: { @@ -274,7 +276,7 @@ static status_t process_create_child_sa(private_ike_sa_established_t *this, mess } case NOTIFY: { - /* TODO: handle notifys */ + notify = (notify_payload_t*)payload; break; } default: @@ -351,6 +353,16 @@ static status_t process_create_child_sa(private_ike_sa_established_t *this, mess { this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy!"); } + + if (notify && notify->get_notify_message_type(notify) == REKEY_SA) + { + /* mark old child sa as rekeyed */ + old_child_sa = this->ike_sa->get_child_sa(this->ike_sa, notify->get_spi(notify)); + if (old_child_sa) + { + old_child_sa->set_rekeyed(old_child_sa, this->child_sa->get_reqid(this->child_sa)); + } + } this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); } @@ -403,8 +415,22 @@ static status_t process_informational(private_ike_sa_established_t *this, messag } else { - this->logger->log(this->logger, CONTROL, "DELETE request for CHILD_SA received. Ignored"); - return SUCCESS; + iterator_t *iterator; + delete_payload_t *delete_response = delete_payload_create(delete_request->get_protocol_id(delete_request)); + iterator = delete_request->create_spi_iterator(delete_request); + while (iterator->has_next(iterator)) + { + u_int32_t spi; + iterator->current(iterator, (void**)&spi); + this->logger->log(this->logger, CONTROL, "DELETE request for CHILD_SA with SPI 0x%x received", spi); + spi = this->ike_sa->destroy_child_sa(this->ike_sa, spi); + if (spi) + { + delete_response->add_spi(delete_response, spi); + } + } + iterator->destroy(iterator); + response->add_payload(response, (payload_t*)delete_response); } } diff --git a/src/charon/testing/Makefile.am b/src/charon/testing/Makefile.am index 6648fbb92..32f80d2de 100644 --- a/src/charon/testing/Makefile.am +++ b/src/charon/testing/Makefile.am @@ -20,6 +20,7 @@ $(top_srcdir)/src/charon/local_policy_store.o $(top_srcdir)/src/charon/local_cre $(top_srcdir)/src/charon/proposal.o $(top_srcdir)/src/charon/configuration.o $(top_srcdir)/src/charon/state.o $(top_srcdir)/src/charon/ike_sa_init_requested.o \ $(top_srcdir)/src/charon/ike_sa_init_responded.o $(top_srcdir)/src/charon/ike_sa_established.o $(top_srcdir)/src/charon/responder_init.o \ $(top_srcdir)/src/charon/initiator_init.o $(top_srcdir)/src/charon/ike_auth_requested.o $(top_srcdir)/src/charon/delete_ike_sa_requested.o \ +$(top_srcdir)/src/charon/delete_child_sa_requested.o \ $(top_srcdir)/src/charon/child_sa.o $(top_srcdir)/src/charon/ike_sa.o $(top_srcdir)/src/charon/ike_sa_manager.o $(top_srcdir)/src/charon/ike_sa_id.o \ $(top_srcdir)/src/charon/authenticator.o $(top_srcdir)/src/charon/encryption_payload.o $(top_srcdir)/src/charon/cert_payload.o \ $(top_srcdir)/src/charon/traffic_selector_substructure.o $(top_srcdir)/src/charon/transform_attribute.o $(top_srcdir)/src/charon/configuration_attribute.o \ diff --git a/src/charon/testing/generator_test.c b/src/charon/testing/generator_test.c index 38f4c364b..1b3798597 100644 --- a/src/charon/testing/generator_test.c +++ b/src/charon/testing/generator_test.c @@ -1091,13 +1091,12 @@ void test_generator_with_delete_payload(protected_tester_t *tester) generator = generator_create(); tester->assert_true(tester,(generator != NULL), "generator create check"); - delete_payload = delete_payload_create(); + delete_payload = delete_payload_create(PROTO_AH); spis.ptr = "123456789012"; spis.len = strlen(spis.ptr); - delete_payload->set_protocol_id(delete_payload, PROTO_AH); delete_payload->set_spi_count(delete_payload,3); delete_payload->set_spi_size(delete_payload,4); delete_payload->set_spis(delete_payload,spis); diff --git a/src/charon/testing/kernel_interface_test.c b/src/charon/testing/kernel_interface_test.c index 1df26e8dd..96b3aa6d2 100644 --- a/src/charon/testing/kernel_interface_test.c +++ b/src/charon/testing/kernel_interface_test.c @@ -57,28 +57,35 @@ void test_kernel_interface(protected_tester_t *tester) me = host_create(AF_INET, "192.168.0.2", 0); other = host_create(AF_INET, "192.168.0.3", 0); - status = kernel_interface->get_spi(kernel_interface, me, other, 50, 1234, &spi); + status = kernel_interface->get_spi(kernel_interface, me, other, PROTO_ESP, 1234, &spi); tester->assert_true(tester, status == SUCCESS, "spi get"); - status = kernel_interface->add_sa(kernel_interface, me, other, spi, 50, 1234, 5, 10, &enc_alg, &int_alg, prf_plus, TRUE); + status = kernel_interface->add_sa(kernel_interface, me, other, spi, PROTO_ESP, 1234, 0, 0, &enc_alg, &int_alg, prf_plus, TRUE); tester->assert_true(tester, status == SUCCESS, "add sa"); left = host_create(AF_INET, "10.1.0.0", 0); right = host_create(AF_INET, "10.2.0.0", 0); - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0, PROTO_ESP, 1234); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_OUT, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy OUT"); - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_IN, 0, PROTO_ESP, 1234); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_OUT, 0, PROTO_ESP, 2345); + tester->assert_true(tester, status == SUCCESS, "add policy OUT"); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_IN, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy IN"); - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_FWD, 0, PROTO_ESP, 1234); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_FWD, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy FWD"); + + kernel_interface->del_sa(kernel_interface, other, spi, PROTO_ESP); + + sleep(10); + me->destroy(me); other->destroy(other); left->destroy(left); right->destroy(right); - sleep(15); + kernel_interface->destroy(kernel_interface); diff --git a/src/charon/testing/testcases.c b/src/charon/testing/testcases.c index ebcc01b1a..2c8c04a68 100644 --- a/src/charon/testing/testcases.c +++ b/src/charon/testing/testcases.c @@ -159,12 +159,12 @@ daemon_t *daemon_create() /* assign methods */ charon->kill = daemon_kill; - charon->socket = socket_create(500); + //charon->socket = socket_create(500); charon->ike_sa_manager = ike_sa_manager_create(); charon->job_queue = job_queue_create(); charon->event_queue = event_queue_create(); charon->send_queue = send_queue_create(); - charon->kernel_interface = kernel_interface_create(); + //charon->kernel_interface = kernel_interface_create(); //charon->configuration = configuration_create(RETRANSMIT_TIMEOUT,MAX_RETRANSMIT_COUNT,HALF_OPEN_IKE_SA_TIMEOUT); charon->sender = NULL; charon->receiver = NULL;