diff options
Diffstat (limited to 'src')
21 files changed, 1582 insertions, 294 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 09eab4649..c2f35839f 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -11,6 +11,8 @@ config/proposal.c config/proposal.h config/configuration.c config/configuration. sa/transactions/transaction.h sa/transactions/transaction.c \ sa/transactions/ike_sa_init.h sa/transactions/ike_sa_init.c \ sa/transactions/ike_auth.h sa/transactions/ike_auth.c \ +sa/transactions/create_child_sa.h sa/transactions/create_child_sa.c \ +sa/transactions/delete_child_sa.h sa/transactions/delete_child_sa.c \ sa/transactions/dead_peer_detection.h sa/transactions/dead_peer_detection.c \ sa/transactions/delete_ike_sa.h sa/transactions/delete_ike_sa.c \ 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 \ diff --git a/src/charon/doc/Todo-list.txt b/src/charon/doc/Todo-list.txt index 7fb33e5a2..8ae08fb35 100644 --- a/src/charon/doc/Todo-list.txt +++ b/src/charon/doc/Todo-list.txt @@ -61,3 +61,5 @@ - replace state machine with something more transaction oriented - find existing IKE_SA on CHILD_SA initiation + +- configure flag which allows to ommit vendor id in pluto diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c index 7d032ec6e..6af546cbc 100644 --- a/src/charon/encoding/payloads/notify_payload.c +++ b/src/charon/encoding/payloads/notify_payload.c @@ -53,6 +53,7 @@ mapping_t notify_type_m[] = { {SET_WINDOW_SIZE, "SET_WINDOW_SIZE"}, {NAT_DETECTION_SOURCE_IP, "NAT_DETECTION_SOURCE_IP"}, {NAT_DETECTION_DESTINATION_IP, "NAT_DETECTION_DESTINATION_IP"}, + {REKEY_SA, "REKEY_SA"}, {MAPPING_END, NULL} }; diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c index 2e5f024f9..3cf7060c9 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.c +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -37,9 +37,14 @@ struct private_delete_child_sa_job_t { delete_child_sa_job_t public; /** - * reqid of the sa to delete. + * protocol of the CHILD_SA (ESP/AH) */ - u_int32_t reqid; + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; /** * Logger ref @@ -63,16 +68,18 @@ static status_t execute(private_delete_child_sa_job_t *this) ike_sa_t *ike_sa; status_t status; - status = charon->ike_sa_manager->checkout_by_reqid(charon->ike_sa_manager, this->reqid, &ike_sa); + status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, + this->protocol, this->spi, + &ike_sa); if (status != SUCCESS) { - this->logger->log(this->logger, CONTROL, "CHILD SA didn't exist anymore"); + this->logger->log(this->logger, ERROR|LEVEL1, + "CHILD_SA not found for delete"); return DESTROY_ME; } + ike_sa->delete_child_sa(ike_sa, this->protocol, this->spi); - ike_sa->delete_child_sa(ike_sa, this->reqid); - - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); return DESTROY_ME; } @@ -87,7 +94,7 @@ static void destroy(private_delete_child_sa_job_t *this) /* * Described in header */ -delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid) +delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi) { private_delete_child_sa_job_t *this = malloc_thing(private_delete_child_sa_job_t); @@ -95,9 +102,10 @@ delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid) this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; this->public.job_interface.execute = (status_t (*) (job_t *)) execute; this->public.job_interface.destroy = (void (*)(job_t*)) destroy; - + /* private variables */ - this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; this->logger = logger_manager->get_logger(logger_manager, WORKER); return &(this->public); diff --git a/src/charon/queues/jobs/delete_child_sa_job.h b/src/charon/queues/jobs/delete_child_sa_job.h index 29802403e..2e16ef832 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.h +++ b/src/charon/queues/jobs/delete_child_sa_job.h @@ -26,6 +26,7 @@ #include <types.h> #include <sa/ike_sa_id.h> #include <queues/jobs/job.h> +#include <config/proposal.h> typedef struct delete_child_sa_job_t delete_child_sa_job_t; @@ -33,8 +34,7 @@ typedef struct delete_child_sa_job_t delete_child_sa_job_t; /** * @brief Class representing an DELETE_CHILD_SA Job. * - * This job initiates the deletion of an CHILD_SA. The SA - * to delete is specified via the unique reqid used in kernel. + * This job initiates the delete of a CHILD SA. * * @b Constructors: * - delete_child_sa_job_create() @@ -51,13 +51,15 @@ struct delete_child_sa_job_t { /** * @brief Creates a job of type DELETE_CHILD_SA. * - * To find the targeted CHILD_SA, the uniqe reqid used in - * the kernel is used. + * The CHILD_SA is identified by its protocol (AH/ESP) and its + * inbound SPI. * - * @param reqid reqid CHILD_SA to rekey + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA + * @return delete_child_sa_job_t object * * @ingroup jobs */ -delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid); +delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi); #endif /* DELETE_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c index 17f88b12f..a2b5b095f 100644 --- a/src/charon/queues/jobs/rekey_child_sa_job.c +++ b/src/charon/queues/jobs/rekey_child_sa_job.c @@ -37,9 +37,14 @@ struct private_rekey_child_sa_job_t { rekey_child_sa_job_t public; /** - * reqid of the child sa, as used in the kernel + * protocol of the CHILD_SA (ESP/AH) */ - u_int32_t reqid; + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; /** * Logger ref @@ -55,7 +60,6 @@ static job_type_t get_type(private_rekey_child_sa_job_t *this) return REKEY_CHILD_SA; } - /** * Implementation of job_t.execute. */ @@ -64,14 +68,16 @@ static status_t execute(private_rekey_child_sa_job_t *this) ike_sa_t *ike_sa; status_t status; - status = charon->ike_sa_manager->checkout_by_reqid(charon->ike_sa_manager, this->reqid, &ike_sa); + status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, + this->protocol, this->spi, + &ike_sa); if (status != SUCCESS) { - this->logger->log(this->logger, CONTROL, "CHILD SA didn't exist anymore"); + this->logger->log(this->logger, ERROR|LEVEL1, + "CHILD_SA not found for rekeying"); return DESTROY_ME; } - - ike_sa->rekey_child_sa(ike_sa, this->reqid); + ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); return DESTROY_ME; @@ -88,7 +94,7 @@ static void destroy(private_rekey_child_sa_job_t *this) /* * Described in header */ -rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid) +rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi) { private_rekey_child_sa_job_t *this = malloc_thing(private_rekey_child_sa_job_t); @@ -98,7 +104,8 @@ rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid) this->public.job_interface.destroy = (void (*)(job_t*)) destroy; /* private variables */ - this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; this->logger = logger_manager->get_logger(logger_manager, WORKER); return &(this->public); diff --git a/src/charon/queues/jobs/rekey_child_sa_job.h b/src/charon/queues/jobs/rekey_child_sa_job.h index 54f701137..87be00c0f 100644 --- a/src/charon/queues/jobs/rekey_child_sa_job.h +++ b/src/charon/queues/jobs/rekey_child_sa_job.h @@ -26,6 +26,7 @@ #include <types.h> #include <sa/ike_sa_id.h> #include <queues/jobs/job.h> +#include <config/proposal.h> typedef struct rekey_child_sa_job_t rekey_child_sa_job_t; @@ -50,17 +51,15 @@ struct rekey_child_sa_job_t { /** * @brief Creates a job of type REKEY_CHILD_SA. * - * To find the targeted CHILD_SA, the uniqe reqid used in - * the kernel is used. As a CHILD_SA may contain multiple SAs - * (AH and/or ESP), we must provide an additional spi to - * know which IPsec SA to rekey. + * The CHILD_SA is identified by its protocol (AH/ESP) and its + * inbound SPI. * - * @param reqid reqid CHILD_SA to rekey - * @param spi security parameter index of the SA to rekey + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA * @return rekey_child_sa_job_t object * * @ingroup jobs */ -rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid); +rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi); #endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index fd144ca70..89741bda9 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -315,7 +315,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus src, dst, spi, this->protocol, this->reqid, - mine ? 0 : this->soft_lifetime, + mine ? this->soft_lifetime : 0, this->hard_lifetime, enc_algo, int_algo, prf_plus, natt, mine); @@ -773,7 +773,7 @@ static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, ho } iterator->destroy(iterator); - return SUCCESS; + return SUCCESS; } /** diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 863a32994..270a19f04 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -45,6 +45,8 @@ #include <sa/transactions/transaction.h> #include <sa/transactions/ike_sa_init.h> #include <sa/transactions/delete_ike_sa.h> +#include <sa/transactions/create_child_sa.h> +#include <sa/transactions/delete_child_sa.h> #include <sa/transactions/dead_peer_detection.h> #include <queues/jobs/retransmit_request_job.h> #include <queues/jobs/delete_established_ike_sa_job.h> @@ -611,6 +613,13 @@ static status_t process_response(private_ike_sa_t *this, message_t *response) current->destroy(current); this->transaction_out = NULL; + /* if conclude() created a new transaction, we increment the message_id + * counter, as the new transaction used the next one */ + if (new) + { + this->message_id_out++; + } + /* queue new transaction */ return queue_transaction(this, new, TRUE); } @@ -750,8 +759,8 @@ static status_t initiate(private_ike_sa_t *this, connection_t *connection) connection->get_name(connection)); return DESTROY_ME; } - ike_sa_init = ike_sa_init_create(&this->public, 0); - this->message_id_out = 2; + this->message_id_out = 0; + ike_sa_init = ike_sa_init_create(&this->public, this->message_id_out++); return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); } @@ -1116,81 +1125,10 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) } /** - * Implementation of ike_sa_t.delete_child_sa. - */ -static status_t delete_child_sa(private_ike_sa_t *this, u_int32_t spi) -{ - /* TODO: Reimplement */ -// message_t *request; -// child_sa_t *child_sa; -// delete_payload_t *delete_payload; -// -// 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, TRUE)); -// 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, FALSE) == 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, TRUE); - child_sa->destroy(child_sa); - return spi; -} - -/** * Implementation of protected_ike_sa_t.get_child_sa. */ -static child_sa_t* get_child_sa(private_ike_sa_t *this, u_int32_t spi) +static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound) { iterator_t *iterator; child_sa_t *current, *found = NULL; @@ -1199,7 +1137,8 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, u_int32_t spi) while (iterator->has_next(iterator)) { iterator->current(iterator, (void**)¤t); - if (current->get_spi(current, FALSE) == spi) + if (current->get_spi(current, inbound) == spi && + current->get_protocol(current) == protocol) { found = current; } @@ -1211,75 +1150,64 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, u_int32_t spi) /** * Implementation of ike_sa_t.rekey_child_sa. */ -static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t spi) +static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) { -/* TODO reimplement - message_t *request; + create_child_sa_t *rekey; child_sa_t *child_sa; - notify_payload_t *notify; - sa_payload_t *sa_payload; - ts_payload_t *tsi_payload, *tsr_payload; - nonce_payload_t *nonce_payload; - linked_list_t *proposals; - chunk_t nonce; - linked_list_t *my_ts, *other_ts; - if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) + child_sa = get_child_sa(this, protocol, spi, TRUE); + if (child_sa == NULL) { - this->logger->log(this->logger, ERROR|LEVEL1, - "rekeying of an CHILD_SA whose IKE_SA not in state IKE_SA_ESTABLISHED, aborting"); - return FAILED; + return NOT_FOUND; } - child_sa = get_child_sa(this, reqid); + rekey = create_child_sa_create(&this->public, this->message_id_out++); + rekey->rekeys_child(rekey, child_sa); + return queue_transaction(this, (transaction_t*)rekey, FALSE); +} + +/** + * Implementation of ike_sa_t.delete_child_sa. + */ +static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + delete_child_sa_t *del; + child_sa_t *child_sa; + + child_sa = get_child_sa(this, protocol, spi, TRUE); 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; + return NOT_FOUND; } - 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)); - request->add_payload(request, (payload_t*)notify); + del = delete_child_sa_create(&this->public, this->message_id_out++); + del->set_child_sa(del, child_sa); + return queue_transaction(this, (transaction_t*)del, FALSE); +} + +/** + * Implementation of protected_ike_sa_t.destroy_child_sa. + */ +static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + iterator_t *iterator; + child_sa_t *child_sa; + status_t status = NOT_FOUND; - proposals = this->policy->get_proposals(this->policy); - child_sa = child_sa_create(reqid, - this->connection->get_my_host(this->connection), - this->connection->get_other_host(this->connection), - this->policy->get_soft_lifetime(this->policy), - this->policy->get_hard_lifetime(this->policy), - this->nat_here || this->nat_there); - child_sa->alloc(child_sa, proposals); - sa_payload = sa_payload_create_from_proposal_list(proposals); - request->add_payload(request, (payload_t*)sa_payload); - - nonce_payload = nonce_payload_create(); - if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, - NONCE_SIZE, &nonce)) - { - request->destroy(request); - return FAILED; + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_protocol(child_sa) == protocol && + child_sa->get_spi(child_sa, TRUE) == spi) + { + child_sa->destroy(child_sa); + iterator->remove(iterator); + status = SUCCESS; + break; + } } - nonce_payload->set_nonce(nonce_payload, nonce); - request->add_payload(request, (payload_t*)nonce_payload); - - my_ts = this->policy->get_my_traffic_selectors(this->policy); - other_ts = this->policy->get_other_traffic_selectors(this->policy); - tsi_payload = ts_payload_create_from_traffic_selectors(TRUE, my_ts); - tsr_payload = ts_payload_create_from_traffic_selectors(FALSE, other_ts); - request->add_payload(request, (payload_t*)tsi_payload); - request->add_payload(request, (payload_t*)tsr_payload); - - 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, reqid)); - old_state->destroy(old_state);*/ - - return SUCCESS; + iterator->destroy(iterator); + return status; } @@ -1505,16 +1433,16 @@ 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.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; this->public.set_connection = (void (*) (ike_sa_t *,connection_t *)) set_connection; this->public.get_connection = (connection_t *(*) (ike_sa_t *)) get_connection; this->public.set_policy = (void (*) (ike_sa_t *,policy_t *)) set_policy; this->public.get_policy = (policy_t *(*) (ike_sa_t *)) get_policy; this->public.build_transforms = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool)) build_transforms; - this->public.destroy_child_sa = (u_int32_t (*)(ike_sa_t*,u_int32_t))destroy_child_sa; - this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,u_int32_t)) get_child_sa; - this->public.delete_child_sa = (status_t(*)(ike_sa_t*,u_int32_t)) delete_child_sa; - this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,u_int32_t)) rekey_child_sa; + this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_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.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; this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 863271950..57afbf9f7 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -37,6 +37,7 @@ #include <crypto/signers/signer.h> #include <config/connections/connection.h> #include <config/policies/policy.h> +#include <config/proposal.h> #include <utils/logger.h> @@ -129,7 +130,6 @@ struct ike_sa_t { * @param connection connection to initiate * @return * - SUCCESS if initialization started - * - FAILED if in wrong state * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted */ status_t (*initiate) (ike_sa_t *this, connection_t *connection); @@ -175,7 +175,7 @@ struct ike_sa_t { * - FAILED * - DESTROY_ME if this IKE_SA MUST be deleted */ - status_t (*process_message) (ike_sa_t *this,message_t *message); + status_t (*process_message) (ike_sa_t *this, message_t *message); /** * @brief Check if NAT traversal is enabled for this IKE_SA. @@ -222,7 +222,7 @@ struct ike_sa_t { * UDP packets are sent if no other traffic * was sent. * - * @param this calling object + * @param this calling object */ void (*send_keepalive) (ike_sa_t *this); @@ -234,41 +234,41 @@ struct ike_sa_t { * to do the logging. The log is only done if the supplied * connection name is NULL or matches the connections name. * - * @param this calling object - * @param logger logger to use for logging - * @param name name of the connection + * @param this calling object + * @param logger logger to use for logging + * @param name name of the connection */ void (*log_status) (ike_sa_t *this, logger_t *logger, char *name); /** * @brief Get the internal stored connection_t object. * - * @param this calling object - * @return pointer to the internal stored connection_t object + * @param this calling object + * @return pointer to the internal stored connection_t object */ connection_t *(*get_connection) (ike_sa_t *this); /** * @brief Set the internal connection object. * - * @param this calling object - * @param connection object of type connection_t + * @param this calling object + * @param connection object of type connection_t */ void (*set_connection) (ike_sa_t *this, connection_t *connection); /** * @brief Get the internal stored policy object. * - * @param this calling object - * @return pointer to the internal stored policy_t object + * @param this calling object + * @return pointer to the internal stored policy_t object */ policy_t *(*get_policy) (ike_sa_t *this); /** * @brief Set the internal policy_t object. * - * @param this calling object - * @param policy object of type policy_t + * @param this calling object + * @param policy object of type policy_t */ void (*set_policy) (ike_sa_t *this, policy_t *policy); @@ -324,30 +324,24 @@ struct ike_sa_t { prf_t *(*get_prf_auth_r) (ike_sa_t *this); /** - * @brief Get a CHILD_SA upon request from the other peer. + * @brief Associates a child SA to this IKE SA * * @param this calling object - * @param spi spi of the CHILD_SA - * @return child_sa, or NULL if none found + * @param child_sa child_sa to add */ - child_sa_t* (*get_child_sa) (ike_sa_t *this, u_int32_t spi); - + void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); + /** - * @brief Close the CHILD SA with the specified reqid. - * - * Looks for a CHILD SA owned by this IKE_SA, deletes it and - * notify's the remote peer about the delete. The associated - * states and policies in the kernel get deleted, if they exist. - * - * @param this calling object - * @param reqid reqid of the child SA, as used in the kernel - * @return - * - NOT_FOUND, if IKE_SA has no such CHILD_SA - * - SUCCESS, if deleted and delete message sent + * @brief Get a CHILD_SA identified by protocol and SPI. * - * @TODO use spi, not reqid + * @param this calling object + * @param protocol protocol of the SA + * @param spi SPI of the CHILD_SA + * @param inbound TRUE if SPI is inbound, FALSE if outbound + * @return child_sa, or NULL if none found */ - status_t (*delete_child_sa) (ike_sa_t *this, u_int32_t reqid); + child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound); /** * @brief Rekey the CHILD SA with the specified reqid. @@ -355,31 +349,43 @@ struct ike_sa_t { * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing. * * @param this calling object - * @param spi security parameter index identifying the SA to rekey + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA * @return * - NOT_FOUND, if IKE_SA has no such CHILD_SA * - SUCCESS, if rekeying initiated - * - * @TODO use spi, not reqid */ - status_t (*rekey_child_sa) (ike_sa_t *this, u_int32_t reqid); - + status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + /** - * @brief Associates a child SA to this IKE SA - * + * @brief Close the CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA, deletes it and + * notify's the remote peer about the delete. The associated + * states and policies in the kernel get deleted, if they exist. + * * @param this calling object - * @param child_sa child_sa to add + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if delete message sent */ - void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); - + status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + /** - * @brief Destroys a CHILD_SA upon request from the other peer. - * + * @brief Destroy a CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA and destroys it. + * * @param this calling object - * @param spi inbound spi of the CHILD_SA to destroy - * @return outbound spi of the destroyed CHILD_SA + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS */ - u_int32_t (*destroy_child_sa) (ike_sa_t *this, u_int32_t spi); + status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); /** * @brief Destroys a ike_sa_t object. @@ -394,11 +400,11 @@ struct ike_sa_t { * * The ID gets cloned internally. * - * @param[in] ike_sa_id ike_sa_id_t object to associate with new IKE_SA - * @return ike_sa_t object + * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA + * @return ike_sa_t object * * @ingroup sa */ ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id); -#endif /*IKE_SA_H_*/ +#endif /* IKE_SA_H_ */ diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 1aad1b393..f9e47c679 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -515,10 +515,11 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, } /** - * Implementation of of ike_sa_manager.checkout_by_reqid. + * Implementation of of ike_sa_manager.checkout_by_child. */ -static status_t checkout_by_reqid(private_ike_sa_manager_t *this, - u_int32_t reqid, ike_sa_t **ike_sa) +static status_t checkout_by_child(private_ike_sa_manager_t *this, + protocol_id_t protocol, u_int32_t spi, + ike_sa_t **ike_sa) { iterator_t *iterator; status_t status = NOT_FOUND; @@ -551,8 +552,8 @@ static status_t checkout_by_reqid(private_ike_sa_manager_t *this, pthread_cond_signal(&(entry->condvar)); continue; } - /* ok, access is exclusive for us, check reqid */ - if (entry->ike_sa->get_child_sa(entry->ike_sa, reqid) != NULL) + /* ok, access is exclusive for us, check for child */ + if (entry->ike_sa->get_child_sa(entry->ike_sa, protocol, spi, TRUE) != NULL) { /* match */ entry->checked_out = TRUE; @@ -895,7 +896,7 @@ ike_sa_manager_t *ike_sa_manager_create() this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; this->public.create_and_checkout = (void(*)(ike_sa_manager_t*,ike_sa_t**))create_and_checkout; this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t*,ike_sa_t**))checkout; - this->public.checkout_by_reqid = (status_t(*)(ike_sa_manager_t*,u_int32_t,ike_sa_t**))checkout_by_reqid; + this->public.checkout_by_child = (status_t(*)(ike_sa_manager_t*,protocol_id_t,u_int32_t,ike_sa_t**))checkout_by_child; this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list; this->public.get_ike_sa_list_by_name = (linked_list_t*(*)(ike_sa_manager_t*,const char*))get_ike_sa_list_by_name; this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status; diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index 6982e0852..076dfd814 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -60,8 +60,8 @@ struct ike_sa_manager_t { * result in a deadlock! * * @param this the manager object - * @param ike_sa_id[in/out] the SA identifier, will be updated - * @param ike_sa[out] checked out SA + * @param[in/out] ike_sa_id the SA identifier, will be updated + * @param[out] ike_sa checked out SA * @returns * - SUCCESS if checkout successful * - NOT_FOUND when no such SA is available @@ -76,26 +76,27 @@ struct ike_sa_manager_t { * Management of SPIs is the managers job, he will set it. * * @param this the manager object - * @param ike_sa[out] checked out SA + * @param[out] ike_sa checked out SA */ void (*create_and_checkout) (ike_sa_manager_t* this,ike_sa_t **ike_sa); /** - * @brief Check out an IKE_SA by the reqid of one of its CHILD_SAs. + * @brief Check out an IKE_SA by protocol and SPI of one of its CHILD_SA. * * The kernel sends us expire messages for IPsec SAs. To fullfill * this request, we must check out the IKE SA which contains the - * CHILD_SA the kernel wants to modify. We do this by the reqid, which - * is unique to every CHILD_SA. + * CHILD_SA the kernel wants to modify. * * @param this the manager object - * @param reqid reqid of the IPsec SA - * @param ike_sa[out] checked out SA + * @param protocol protocol of the CHILD_SA + * @param spi SPI of the CHILD_SA + * @param[out] ike_sa checked out SA * @return * - NOT_FOUND, if no IKE SA with such a child found * - SUCCESS, if ike_sa set */ - status_t (*checkout_by_reqid) (ike_sa_manager_t* this, u_int32_t reqid, ike_sa_t **ike_sa); + status_t (*checkout_by_child) (ike_sa_manager_t* this, protocol_id_t protocol, + u_int32_t spi, ike_sa_t **ike_sa); /** * @brief Get a list of all IKE_SA SAs currently set up. @@ -139,8 +140,8 @@ struct ike_sa_manager_t { * The SA must be checked out again! * * @param this the manager object - * @param ike_sa_id[in/out] the SA identifier, will be updated - * @param ike_sa[out] checked out SA + * @param[in/out] ike_sa_id the SA identifier, will be updated + * @param[out] ike_sa checked out SA * @returns * - SUCCESS if checked in * - NOT_FOUND when not found (shouldn't happen!) @@ -158,7 +159,7 @@ struct ike_sa_manager_t { * deadlock! * * @param this the manager object - * @param ike_sa_id[in/out] the SA identifier + * @param[in/out] ike_sa_id the SA identifier * @returns * - SUCCESS if found * - NOT_FOUND when no such SA is available diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c new file mode 100644 index 000000000..6fce5e07b --- /dev/null +++ b/src/charon/sa/transactions/create_child_sa.c @@ -0,0 +1,809 @@ +/** + * @file create_child_sa.c + * + * @brief Implementation of create_child_sa_t transaction. + * + */ + +/* + * 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 "create_child_sa.h" + +#include <string.h> + +#include <daemon.h> +#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> + + +typedef struct private_create_child_sa_t private_create_child_sa_t; + +/** + * Private members of a create_child_sa_t object.. + */ +struct private_create_child_sa_t { + + /** + * Public methods and transaction_t interface. + */ + create_child_sa_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Message sent by our peer, if already generated + */ + message_t *message; + + /** + * Message ID this transaction uses + */ + u_int32_t message_id; + + /** + * Times we did send the request + */ + u_int32_t requested; + + /** + * initiators inbound SPI of the CHILD_SA which gets rekeyed + */ + u_int32_t rekey_spi; + + /** + * connection of IKE_SA + */ + connection_t *connection; + + /** + * policy definition used + */ + policy_t *policy; + + /** + * Negotiated proposal used for CHILD_SA + */ + proposal_t *proposal; + + /** + * initiator chosen nonce + */ + chunk_t nonce_i; + + /** + * responder chosen nonce + */ + chunk_t nonce_r; + + /** + * Negotiated traffic selectors for initiator + */ + linked_list_t *tsi; + + /** + * Negotiated traffic selectors for responder + */ + linked_list_t *tsr; + + /** + * CHILD_SA created by this transaction + */ + child_sa_t *child_sa; + + /** + * CHILD_SA rekeyed if we are rekeying + */ + child_sa_t *rekeyed_sa; + + /** + * source of randomness + */ + randomizer_t *randomizer; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_create_child_sa_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_create_child_sa_t *this) +{ + return this->requested++; +} + +/** + * Implementation of transaction_t.rekeys_child. + */ +static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa) +{ + this->rekeyed_sa = child_sa; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_create_child_sa_t *this, message_t **result) +{ + message_t *request; + host_t *me, *other; + + /* check if we already have built a message (retransmission) */ + if (this->message) + { + *result = this->message; + return SUCCESS; + } + + this->connection = this->ike_sa->get_connection(this->ike_sa); + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + this->policy = this->ike_sa->get_policy(this->ike_sa); + + /* build the request */ + request = message_create(); + request->set_source(request, me->clone(me)); + request->set_destination(request, other->clone(other)); + request->set_exchange_type(request, CREATE_CHILD_SA); + request->set_request(request, TRUE); + request->set_message_id(request, this->message_id); + request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); + *result = request; + this->message = request; + + { /* build SA payload */ + sa_payload_t *sa_payload; + linked_list_t *proposals; + bool use_natt; + u_int32_t reqid = 0; + + if (this->rekeyed_sa) + { + reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); + } + + proposals = this->policy->get_proposals(this->policy); + use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); + this->child_sa = child_sa_create(reqid, me, other, + this->policy->get_soft_lifetime(this->policy), + this->policy->get_hard_lifetime(this->policy), + use_natt); + if (this->child_sa->alloc(this->child_sa, proposals) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "could not install CHILD_SA, CHILD_SA creation aborted"); + return FAILED; + } + sa_payload = sa_payload_create_from_proposal_list(proposals); + request->add_payload(request, (payload_t*)sa_payload); + } + + { /* build the NONCE payload for us (initiator) */ + nonce_payload_t *nonce_payload; + + if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, + NONCE_SIZE, &this->nonce_i) != SUCCESS) + { + return FAILED; + } + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->nonce_i); + request->add_payload(request, (payload_t*)nonce_payload); + } + + { /* build TSi payload */ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + + ts_list = this->policy->get_my_traffic_selectors(this->policy); + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); + request->add_payload(request, (payload_t*)ts_payload); + } + + { /* build TSr payload */ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + + ts_list = this->policy->get_other_traffic_selectors(this->policy); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); + request->add_payload(request, (payload_t*)ts_payload); + } + + if (this->rekeyed_sa) + { /* add REKEY_SA notify if we are rekeying */ + notify_payload_t *notify; + protocol_id_t protocol; + + protocol = this->rekeyed_sa->get_protocol(this->rekeyed_sa); + notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); + 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); + } + + return SUCCESS; +} + +/** + * Handle all kind of notifys + */ +static status_t process_notifys(private_create_child_sa_t *this, notify_payload_t *notify_payload) +{ + notify_type_t notify_type = notify_payload->get_notify_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", + mapping_find(notify_type_m, notify_type)); + + switch (notify_type) + { + case SINGLE_PAIR_REQUIRED: + { + this->logger->log(this->logger, AUDIT, + "received a SINGLE_PAIR_REQUIRED notify"); + return FAILED; + } + case TS_UNACCEPTABLE: + { + this->logger->log(this->logger, CONTROL, + "received TS_UNACCEPTABLE notify"); + return FAILED; + } + case NO_PROPOSAL_CHOSEN: + { + this->logger->log(this->logger, CONTROL, + "received NO_PROPOSAL_CHOSEN notify"); + return FAILED; + } + case REKEY_SA: + { + u_int32_t spi; + protocol_id_t protocol; + + protocol = notify_payload->get_protocol_id(notify_payload); + switch (protocol) + { + case PROTO_AH: + case PROTO_ESP: + spi = notify_payload->get_spi(notify_payload); + this->rekeyed_sa = this->ike_sa->get_child_sa(this->ike_sa, + protocol, spi, + FALSE); + break; + default: + break; + } + return SUCCESS; + } + default: + { + if (notify_type < 16383) + { + this->logger->log(this->logger, AUDIT, + "received %s notify error (%d), deleting IKE_SA", + mapping_find(notify_type_m, notify_type), + notify_type); + return FAILED; + } + else + { + this->logger->log(this->logger, CONTROL, + "received %s notify (%d), ignored", + mapping_find(notify_type_m, notify_type), + notify_type); + return SUCCESS; + } + } + } +} + +/** + * Build a notify message. + */ +static void build_notify(notify_type_t type, message_t *message, bool flush_message) +{ + notify_payload_t *notify; + + if (flush_message) + { + payload_t *payload; + iterator_t *iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + payload->destroy(payload); + iterator->remove(iterator); + } + iterator->destroy(iterator); + } + + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + message->add_payload(message, (payload_t*)notify); +} + +/** + * Install a CHILD_SA for usage + */ +static status_t install_child_sa(private_create_child_sa_t *this, bool initiator) +{ + prf_plus_t *prf_plus; + chunk_t seed; + status_t status; + + 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); + chunk_free(&seed); + + if (initiator) + { + status = this->child_sa->update(this->child_sa, this->proposal, prf_plus); + } + else + { + status = this->child_sa->add(this->child_sa, this->proposal, prf_plus); + } + prf_plus->destroy(prf_plus); + if (status != SUCCESS) + { + return DESTROY_ME; + } + if (initiator) + { + status = this->child_sa->add_policies(this->child_sa, this->tsi, this->tsr); + } + else + { + status = this->child_sa->add_policies(this->child_sa, this->tsr, this->tsi); + } + if (status != SUCCESS) + { + return DESTROY_ME; + } + /* add to IKE_SA, and remove from transaction */ + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + this->child_sa = NULL; + return SUCCESS; +} + +/** + * destroy a list of traffic selectors + */ +static void destroy_ts_list(linked_list_t *list) +{ + if (list) + { + traffic_selector_t *ts; + while (list->remove_last(list, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + list->destroy(list); + } +} + +/** + * Implementation of transaction_t.get_response. + */ +static status_t get_response(private_create_child_sa_t *this, message_t *request, + message_t **result, transaction_t **next) +{ + host_t *me, *other; + message_t *response; + status_t status; + iterator_t *payloads; + sa_payload_t *sa_request = NULL; + nonce_payload_t *nonce_request = NULL; + ts_payload_t *tsi_request = NULL; + ts_payload_t *tsr_request = NULL; + + /* check if we already have built a response (retransmission) */ + if (this->message) + { + *result = this->message; + return SUCCESS; + } + + this->connection = this->ike_sa->get_connection(this->ike_sa); + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + this->policy = this->ike_sa->get_policy(this->ike_sa); + + /* set up response */ + response = message_create(); + response->set_source(response, me->clone(me)); + response->set_destination(response, other->clone(other)); + response->set_exchange_type(response, CREATE_CHILD_SA); + response->set_request(response, FALSE); + response->set_message_id(response, this->message_id); + response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); + this->message = response; + *result = response; + + /* check message type */ + if (request->get_exchange_type(request) != CREATE_CHILD_SA) + { + this->logger->log(this->logger, ERROR, + "CREATE_CHILD_SA response of invalid type, aborted"); + return FAILED; + } + + /* Iterate over all payloads. */ + payloads = request->get_payload_iterator(request); + 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 NONCE: + nonce_request = (nonce_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 NOTIFY: + { + status = process_notifys(this, (notify_payload_t*)payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + break; + } + default: + { + this->logger->log(this->logger, ERROR, "ignoring %s payload (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), + payload->get_type(payload)); + break; + } + } + } + payloads->destroy(payloads); + + /* check if we have all payloads */ + if (!(sa_request && nonce_request && tsi_request && tsr_request)) + { + build_notify(INVALID_SYNTAX, response, TRUE); + this->logger->log(this->logger, AUDIT, + "request message incomplete, no CHILD_SA created"); + return FAILED; + } + + { /* process nonce payload */ + nonce_payload_t *nonce_response; + + this->nonce_i = nonce_request->get_nonce(nonce_request); + if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, + NONCE_SIZE, &this->nonce_r) != SUCCESS) + { + build_notify(NO_PROPOSAL_CHOSEN, response, TRUE); + return FAILED; + } + nonce_response = nonce_payload_create(); + nonce_response->set_nonce(nonce_response, this->nonce_r); + response->add_payload(response, (payload_t *)nonce_response); + } + + { /* process traffic selectors for other */ + linked_list_t *ts_received = tsi_request->get_traffic_selectors(tsi_request); + this->tsi = this->policy->select_other_traffic_selectors(this->policy, ts_received); + destroy_ts_list(ts_received); + } + + { /* process traffic selectors for us */ + linked_list_t *ts_received = ts_received = tsr_request->get_traffic_selectors(tsr_request); + this->tsr = this->policy->select_my_traffic_selectors(this->policy, ts_received); + destroy_ts_list(ts_received); + } + + { /* process SA payload */ + proposal_t *proposal; + linked_list_t *proposal_list; + sa_payload_t *sa_response; + ts_payload_t *ts_response; + bool use_natt; + u_int32_t soft_lifetime, hard_lifetime; + + sa_response = sa_payload_create(); + /* get proposals from request, and select one with ours */ + proposal_list = sa_request->get_proposals(sa_request); + this->logger->log(this->logger, CONTROL|LEVEL1, "selecting proposals:"); + this->proposal = this->policy->select_proposal(this->policy, proposal_list); + /* list is not needed anymore */ + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + + /* do we have a proposal? */ + if (this->proposal == NULL) + { + this->logger->log(this->logger, AUDIT, + "CHILD_SA proposals unacceptable, adding NO_PROPOSAL_CHOSEN notify"); + build_notify(NO_PROPOSAL_CHOSEN, response, TRUE); + return FAILED; + } + /* do we have traffic selectors? */ + else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0) + { + this->logger->log(this->logger, AUDIT, + "CHILD_SA traffic selectors unacceptable, adding TS_UNACCEPTABLE notify"); + build_notify(TS_UNACCEPTABLE, response, TRUE); + return FAILED; + } + else + { /* create child sa */ + u_int32_t reqid = 0; + + if (this->rekeyed_sa) + { + reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); + } + soft_lifetime = this->policy->get_soft_lifetime(this->policy); + hard_lifetime = this->policy->get_hard_lifetime(this->policy); + use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); + this->child_sa = child_sa_create(reqid, me, other, + soft_lifetime, hard_lifetime, + use_natt); + if (install_child_sa(this, FALSE) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "installing CHILD_SA failed, adding NO_PROPOSAL_CHOSEN notify"); + build_notify(NO_PROPOSAL_CHOSEN, response, TRUE); + return FAILED; + } + /* add proposal to sa payload */ + sa_response->add_proposal(sa_response, this->proposal); + } + response->add_payload(response, (payload_t*)sa_response); + + /* add ts payload after sa payload */ + ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); + response->add_payload(response, (payload_t*)ts_response); + 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. */ + if (this->rekeyed_sa) + { + this->rekeyed_sa->set_rekeyed(this->rekeyed_sa); + } + return SUCCESS; +} + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_create_child_sa_t *this, message_t *response, + transaction_t **next) +{ + iterator_t *payloads; + host_t *me, *other; + sa_payload_t *sa_payload = NULL; + nonce_payload_t *nonce_payload = NULL; + ts_payload_t *tsi_payload = NULL; + ts_payload_t *tsr_payload = NULL; + status_t status; + + /* check message type */ + if (response->get_exchange_type(response) != CREATE_CHILD_SA) + { + this->logger->log(this->logger, ERROR, + "CREATE_CHILD_SA response of invalid type, aborting"); + return FAILED; + } + + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + + /* Iterate over all payloads to collect them */ + 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_payload = (sa_payload_t*)payload; + break; + case NONCE: + nonce_payload = (nonce_payload_t*)payload; + break; + case TRAFFIC_SELECTOR_INITIATOR: + tsi_payload = (ts_payload_t*)payload; + break; + case TRAFFIC_SELECTOR_RESPONDER: + tsr_payload = (ts_payload_t*)payload; + break; + case NOTIFY: + { + status = process_notifys(this, (notify_payload_t*)payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + break; + } + default: + { + this->logger->log(this->logger, ERROR, "ignoring %s payload (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), + payload->get_type(payload)); + break; + } + } + } + payloads->destroy(payloads); + + if (!(sa_payload && nonce_payload && tsi_payload && tsr_payload)) + { + this->logger->log(this->logger, AUDIT, "response message incomplete, no CHILD_SA built"); + return FAILED; + } + + { /* process NONCE payload */ + this->nonce_r = nonce_payload->get_nonce(nonce_payload); + } + + { /* process traffic selectors for us */ + linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload); + this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received); + destroy_ts_list(ts_received); + } + + { /* process traffic selectors for other */ + linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload); + this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received); + destroy_ts_list(ts_received); + } + + { /* process sa payload */ + proposal_t *proposal; + linked_list_t *proposal_list; + + proposal_list = sa_payload->get_proposals(sa_payload); + /* we have to re-check here if other's selection is valid */ + this->proposal = this->policy->select_proposal(this->policy, proposal_list); + /* list not needed anymore */ + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + + /* everything fine to create CHILD? */ + if (this->proposal == NULL || + this->tsi->get_count(this->tsi) == 0 || + this->tsr->get_count(this->tsr) == 0) + { + this->logger->log(this->logger, AUDIT, + "CHILD_SA creation failed"); + return FAILED; + } + if (install_child_sa(this, TRUE) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "installing CHILD_SA failed, no CHILD_SA built"); + return FAILED; + } + } + /* CHILD_SA successfully created. If we have rekeyed an old one, delete it */ + if (this->rekeyed_sa) + { + delete_child_sa_t *delete_child_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); + *next = (transaction_t*)delete_child_sa; + } + return SUCCESS; +} + +/** + * implements transaction_t.destroy + */ +static void destroy(private_create_child_sa_t *this) +{ + if (this->message) + { + this->message->destroy(this->message); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + if (this->child_sa) + { + this->child_sa->destroy(this->child_sa); + } + destroy_ts_list(this->tsi); + destroy_ts_list(this->tsr); + chunk_free(&this->nonce_i); + chunk_free(&this->nonce_r); + this->randomizer->destroy(this->randomizer); + free(this); +} + +/* + * Described in header. + */ +create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_create_child_sa_t *this = malloc_thing(private_create_child_sa_t); + + /* transaction interface functions */ + this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; + this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; + this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; + this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; + this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; + this->public.transaction.destroy = (void(*)(transaction_t*))destroy; + + /* public functions */ + this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child; + + /* private data */ + this->ike_sa = ike_sa; + this->message_id = message_id; + this->message = NULL; + this->requested = 0; + this->rekey_spi = 0; + this->nonce_i = CHUNK_INITIALIZER; + this->nonce_r = CHUNK_INITIALIZER; + this->child_sa = NULL; + this->rekeyed_sa = NULL; + this->proposal = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->randomizer = randomizer_create(); + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &this->public; +} diff --git a/src/charon/sa/transactions/create_child_sa.h b/src/charon/sa/transactions/create_child_sa.h new file mode 100644 index 000000000..b2bae7728 --- /dev/null +++ b/src/charon/sa/transactions/create_child_sa.h @@ -0,0 +1,77 @@ +/** + * @file create_child_sa.h + * + * @brief Interface of transaction create_child_sa. + * + */ + +/* + * 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. + */ + + +#ifndef CREATE_CHILD_SA_ +#define CREATE_CHILD_SA_ + +#include <sa/ike_sa.h> +#include <sa/child_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct create_child_sa_t create_child_sa_t; + +/** + * @brief A transaction to create a new or rekey an existing CHILD_SA. + * + * Rekeying of an CHILD_SA works the same way as creating a new one, + * but includes an additional REKEY_SA notify and deletes the old + * one (in a separate transaction). + * + * @b Constructors: + * - create_child_sa_create() + * - transaction_create() with the appropriate message + * + * @ingroup transactions + */ +struct create_child_sa_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; + + /** + * @brief Set the CHILD_SA which gets rekeyed by the new one. + * + * If this transaction is used for rekeying, set the inbound + * SPI of the CHILD_SA which the new CHILD_SA rekeys. + * + * @param this calling object + * @param child_sa CHILD_SA to rekey + */ + void (*rekeys_child) (create_child_sa_t* this, child_sa_t *child_sa); +}; + +/** + * @brief Create a new transaction which creates/rekeys CHILD_SAs. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created create_child_sa transaction + * + * @ingroup transactions + */ +create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* CREATE_CHILD_SA_ */ diff --git a/src/charon/sa/transactions/delete_child_sa.c b/src/charon/sa/transactions/delete_child_sa.c new file mode 100644 index 000000000..760105204 --- /dev/null +++ b/src/charon/sa/transactions/delete_child_sa.c @@ -0,0 +1,352 @@ +/** + * @file delete_child_sa.c + * + * @brief Implementation of the delete_child_sa transaction. + * + */ + +/* + * 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 "delete_child_sa.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_delete_child_sa_t private_delete_child_sa_t; + +/** + * Private members of a delete_child_sa_t object.. + */ +struct private_delete_child_sa_t { + + /** + * Public methods and transaction_t interface. + */ + delete_child_sa_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Message sent by our peer, if already generated + */ + message_t *message; + + /** + * Message ID this transaction uses + */ + u_int32_t message_id; + + /** + * Times we did send the request + */ + u_int32_t requested; + + /** + * CHILD SA to delete + */ + child_sa_t *child_sa; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_delete_child_sa_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_delete_child_sa_t *this) +{ + return this->requested++; +} + +/** + * Implementation of delete_child_sa_t.set_child_sa. + */ +static void set_child_sa(private_delete_child_sa_t *this, child_sa_t *child_sa) +{ + this->child_sa = child_sa; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_delete_child_sa_t *this, message_t **result) +{ + message_t *request; + connection_t *connection; + host_t *me, *other; + + /* check if we already have built a message (retransmission) */ + if (this->message) + { + *result = this->message; + return SUCCESS; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + me = connection->get_my_host(connection); + other = connection->get_other_host(connection); + + /* build the request */ + request = message_create(); + request->set_source(request, me->clone(me)); + request->set_destination(request, other->clone(other)); + request->set_exchange_type(request, INFORMATIONAL); + request->set_request(request, TRUE); + request->set_message_id(request, this->message_id); + request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa)); + *result = request; + this->message = request; + + { /* add delete payload */ + delete_payload_t *delete_payload; + protocol_id_t protocol; + u_int32_t spi; + + protocol = this->child_sa->get_protocol(this->child_sa); + spi = this->child_sa->get_spi(this->child_sa, TRUE); + delete_payload = delete_payload_create(protocol); + + this->logger->log(this->logger, CONTROL, + "created DELETE payload for %s CHILD_SA with SPI 0x%x", + mapping_find(protocol_id_m, protocol), htonl(spi)); + delete_payload->add_spi(delete_payload, spi); + request->add_payload(request, (payload_t*)delete_payload); + } + + return SUCCESS; +} + +/** + * process a delete payload + */ +static status_t process_delete(private_delete_child_sa_t *this, delete_payload_t *delete_request, message_t *response) +{ + protocol_id_t protocol; + u_int32_t spi; + iterator_t *iterator; + delete_payload_t *delete_response; + + /* get requested CHILD */ + protocol = delete_request->get_protocol_id(delete_request); + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + this->logger->log(this->logger, CONTROL, + "CHILD_SA delete response contained unexpected protocol"); + return FAILED; + } + + /* prepare response payload */ + if (response) + { + delete_response = delete_payload_create(protocol); + response->add_payload(response, (payload_t*)delete_response); + } + + iterator = delete_request->create_spi_iterator(delete_request); + while (iterator->iterate(iterator, (void**)&spi)) + { + child_sa_t *child_sa; + + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE); + + if (child_sa != NULL) + { + this->logger->log(this->logger, CONTROL, + "received DELETE for %s CHILD_SA with SPI 0x%x, deleting", + mapping_find(protocol_id_m, protocol), ntohl(spi)); + /* delete it, with inbound spi */ + spi = child_sa->get_spi(child_sa, TRUE); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + /* add delete response to message, if we are responding */ + if (response) + { + delete_response->add_spi(delete_response, spi); + } + } + else + { + this->logger->log(this->logger, ERROR, + "received DELETE for %s CHILD_SA with SPI 0x%x, but no such SA", + mapping_find(protocol_id_m, protocol), ntohl(spi)); + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of transaction_t.get_response. + */ +static status_t get_response(private_delete_child_sa_t *this, message_t *request, + message_t **result, transaction_t **next) +{ + host_t *me, *other; + message_t *response; + iterator_t *payloads; + connection_t *connection; + + /* check if we already have built a response (retransmission) */ + if (this->message) + { + *result = this->message; + return SUCCESS; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + me = connection->get_my_host(connection); + other = connection->get_other_host(connection); + + /* set up response */ + response = message_create(); + response->set_source(response, me->clone(me)); + response->set_destination(response, other->clone(other)); + response->set_exchange_type(response, INFORMATIONAL); + response->set_request(response, FALSE); + response->set_message_id(response, this->message_id); + response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa)); + this->message = response; + *result = response; + + if (request->get_exchange_type(request) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR, + "INFORMATIONAL response of invalid type, aborting"); + return FAILED; + } + + /* iterate over all payloads */ + payloads = request->get_payload_iterator(request); + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case DELETE: + { + process_delete(this, (delete_payload_t*)payload, response); + 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; + } + } + } + payloads->destroy(payloads); + return SUCCESS; +} + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_delete_child_sa_t *this, message_t *response, + transaction_t **transaction) +{ + iterator_t *payloads; + + /* check message type */ + if (response->get_exchange_type(response) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR, + "INFORMATIONAL response of invalid type, aborting"); + return FAILED; + } + + /* iterate over all 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 DELETE: + { + process_delete(this, (delete_payload_t*)payload, NULL); + 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; + } + } + } + payloads->destroy(payloads); + return SUCCESS; +} + +/** + * implements transaction_t.destroy + */ +static void destroy(private_delete_child_sa_t *this) +{ + if (this->message) + { + this->message->destroy(this->message); + } + free(this); +} + +/* + * Described in header. + */ +delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_delete_child_sa_t *this = malloc_thing(private_delete_child_sa_t); + + /* transaction interface functions */ + this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request; + this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response; + this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude; + this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id; + this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested; + this->public.transaction.destroy = (void(*)(transaction_t*))destroy; + + /* publics */ + this->public.set_child_sa = (void(*)(delete_child_sa_t*,child_sa_t*))set_child_sa; + + /* private data */ + this->ike_sa = ike_sa; + this->message_id = message_id; + this->message = NULL; + this->requested = 0; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &this->public; +} diff --git a/src/charon/sa/transactions/delete_child_sa.h b/src/charon/sa/transactions/delete_child_sa.h new file mode 100644 index 000000000..9d2800979 --- /dev/null +++ b/src/charon/sa/transactions/delete_child_sa.h @@ -0,0 +1,69 @@ +/** + * @file delete_child_sa.h + * + * @brief Interface of transaction delete_child_sa. + * + */ + +/* + * 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. + */ + + +#ifndef DELETE_CHILD_SA_H_ +#define DELETE_CHILD_SA_H_ + +#include <sa/ike_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct delete_child_sa_t delete_child_sa_t; + +/** + * @brief A transaction used to delete a CHILD_SA. + * + * @b Constructors: + * - delete_child_sa_create() + * - transaction_create() with the appropriate message + * + * @ingroup transactions + */ +struct delete_child_sa_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; + + /** + * @brief Set the CHILD_SA to delete. + * + * @param this calling object + * @param child_sa CHILD_SA to rekey + */ + void (*set_child_sa) (delete_child_sa_t* this, child_sa_t *child_sa); +}; + +/** + * @brief Create a new transaction which deletes a CHILD_SA. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created delete_child_sa transaction + * + * @ingroup transactions + */ +delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* DELETE_CHILD_SA_H_ */ diff --git a/src/charon/sa/transactions/delete_ike_sa.c b/src/charon/sa/transactions/delete_ike_sa.c index a8ec334a7..737763239 100644 --- a/src/charon/sa/transactions/delete_ike_sa.c +++ b/src/charon/sa/transactions/delete_ike_sa.c @@ -135,14 +135,6 @@ static status_t get_response(private_delete_ike_sa_t *this, message_t *request, delete_payload_t *delete_request = NULL; connection_t *connection; - /* check message type */ - if (request->get_exchange_type(request) != INFORMATIONAL) - { - this->logger->log(this->logger, ERROR, - "INFORMATIONAL response of invalid type, deleting IKE_SA"); - return DESTROY_ME; - } - /* check if we already have built a response (retransmission) * this only happens in special simultanous transaction cases, * as we delete the IKE_SA after the response is sent. */ @@ -167,6 +159,14 @@ static status_t get_response(private_delete_ike_sa_t *this, message_t *request, this->message = response; *result = response; + /* check message type */ + if (request->get_exchange_type(request) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR, + "INFORMATIONAL response of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + /* iterate over all payloads */ payloads = request->get_payload_iterator(request); while (payloads->has_next(payloads)) diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c index faf0ef6e5..06eec335c 100644 --- a/src/charon/sa/transactions/ike_auth.c +++ b/src/charon/sa/transactions/ike_auth.c @@ -498,14 +498,6 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, ts_payload_t *tsr_request = NULL; id_payload_t *idr_response; - /* check message type */ - if (request->get_exchange_type(request) != IKE_AUTH) - { - this->logger->log(this->logger, ERROR, - "IKE_AUTH response of invalid type, deleting IKE_SA"); - return DESTROY_ME; - } - /* check if we already have built a response (retransmission) */ if (this->message) { @@ -528,6 +520,14 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, this->message = response; *result = response; + /* check message type */ + if (request->get_exchange_type(request) != IKE_AUTH) + { + this->logger->log(this->logger, ERROR, + "IKE_AUTH response of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + /* Iterate over all payloads. */ payloads = request->get_payload_iterator(request); while (payloads->has_next(payloads)) diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c index ce6d045ac..00ddbf444 100644 --- a/src/charon/sa/transactions/ike_sa_init.c +++ b/src/charon/sa/transactions/ike_sa_init.c @@ -316,17 +316,12 @@ static status_t get_request(private_ike_sa_init_t *this, message_t **result) { /* build the NONCE payload for us (initiator) */ nonce_payload_t *nonce_payload; - randomizer_t *randomizer; - randomizer = randomizer_create(); - if (randomizer->allocate_pseudo_random_bytes(randomizer, + if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, NONCE_SIZE, &this->nonce_i) != SUCCESS) { - randomizer->destroy(randomizer); - request->destroy(request); return DESTROY_ME; } - randomizer->destroy(randomizer); nonce_payload = nonce_payload_create(); nonce_payload->set_nonce(nonce_payload, this->nonce_i); @@ -484,14 +479,6 @@ static status_t get_response(private_ike_sa_init_t *this, ike_sa_id_t *ike_sa_id; u_int32_t timeout; - /* check message type */ - if (request->get_exchange_type(request) != IKE_SA_INIT) - { - this->logger->log(this->logger, ERROR, - "IKE_SA_INIT request of invalid type, deleting IKE_SA"); - return DESTROY_ME; - } - /* check if we already have built a response (retransmission) */ if (this->message) { @@ -513,6 +500,14 @@ static status_t get_response(private_ike_sa_init_t *this, this->message = response; *result = response; + /* check message type */ + if (request->get_exchange_type(request) != IKE_SA_INIT) + { + this->logger->log(this->logger, ERROR, + "IKE_SA_INIT request of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + /* this is the first message to process, find a connection for IKE_SA */ this->connection = charon->connections->get_connection_by_hosts( charon->connections, me, other); @@ -1003,7 +998,7 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response, response_chunk = response->get_packet_data(response); /* create next transaction, for which we except a message */ - ike_auth = ike_auth_create(this->ike_sa, 1); + ike_auth = ike_auth_create(this->ike_sa, this->message_id + 1); ike_auth->set_nonces(ike_auth, chunk_clone(this->nonce_i), chunk_clone(this->nonce_r)); diff --git a/src/charon/sa/transactions/transaction.c b/src/charon/sa/transactions/transaction.c index ecbfd0abc..e03b0cc0f 100644 --- a/src/charon/sa/transactions/transaction.c +++ b/src/charon/sa/transactions/transaction.c @@ -26,6 +26,8 @@ #include <sa/transactions/ike_sa_init.h> #include <sa/transactions/ike_auth.h> #include <sa/transactions/delete_ike_sa.h> +#include <sa/transactions/create_child_sa.h> +#include <sa/transactions/delete_child_sa.h> #include <sa/transactions/dead_peer_detection.h> #include <encoding/payloads/ts_payload.h> #include <encoding/payloads/sa_payload.h> @@ -56,7 +58,10 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) { case IKE_SA_INIT: { - transaction = (transaction_t*)ike_sa_init_create(ike_sa, message_id); + if (ike_sa->get_state(ike_sa) == SA_CREATED) + { + transaction = (transaction_t*)ike_sa_init_create(ike_sa, message_id); + } break; } case IKE_AUTH: @@ -67,6 +72,10 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) } case CREATE_CHILD_SA: { + if (ike_sa->get_state(ike_sa) != SA_ESTABLISHED) + { + break; + } /* look for a REKEY_SA notify */ iterator = request->get_payload_iterator(request); while (iterator->has_next(iterator)) @@ -88,10 +97,9 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) break; case PROTO_AH: case PROTO_ESP: - { - /* TODO: transaction = rekey_child_sa_create(ike_sa, message_id); */ - break; - } + /* we do not handle rekeying of CHILD_SAs in a special + * transaction, as the procedure is nearly equal + * to create a new CHILD_SA. */ default: break; } @@ -101,10 +109,21 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) } } iterator->destroy(iterator); + if (!transaction) + { + /* we have not found a REKEY_SA notify for IKE. This means + * we create a new CHILD_SA, or rekey an existing one. + * Both cases are handled with the create_child_sa transaction. */ + transaction = (transaction_t*)create_child_sa_create(ike_sa, message_id); + } break; } case INFORMATIONAL: { + if (ike_sa->get_state(ike_sa) == SA_CREATED) + { + break; + } u_int payload_count = 0; iterator = request->get_payload_iterator(request); while (iterator->has_next(iterator)) @@ -117,12 +136,21 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) { delete_payload_t *delete_payload; delete_payload = (delete_payload_t*)current; - if (delete_payload->get_protocol_id(delete_payload) == PROTO_IKE) + switch (delete_payload->get_protocol_id(delete_payload)) { - transaction = (transaction_t*) - delete_ike_sa_create(ike_sa, message_id); - break; + case PROTO_IKE: + transaction = (transaction_t*) + delete_ike_sa_create(ike_sa, message_id); + break; + case PROTO_AH: + case PROTO_ESP: + transaction = (transaction_t*) + delete_child_sa_create(ike_sa, message_id); + break; + default: + break; } + break; } default: break; @@ -133,6 +161,8 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) } } iterator->destroy(iterator); + /* empty informationals are used for dead peer detection in + * IKEv2. We use a special transaction for it. */ if (payload_count == 0) { transaction = (transaction_t*) diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 16111582e..d272a43bf 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -921,29 +921,28 @@ static void receive_messages(private_kernel_interface_t *this) else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) { job_t *job; + protocol_id_t protocol; + u_int32_t spi; struct xfrm_user_expire *expire; + + expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); + protocol = expire->state.id.proto == KERNEL_ESP ? + PROTO_ESP : PROTO_AH; + spi = expire->state.id.spi; + this->logger->log(this->logger, CONTROL|LEVEL1, "Received a XFRM_MSG_EXPIRE"); - expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); - this->logger->log(this->logger, CONTROL|LEVEL0, - "creating %s job for CHILD_SA with reqid %d", + this->logger->log(this->logger, CONTROL, + "creating %s job for %s CHILD_SA 0x%x", expire->hard ? "delete" : "rekey", - expire->state.reqid); + mapping_find(protocol_id_m, protocol), ntohl(spi)); if (expire->hard) { - this->logger->log(this->logger, CONTROL|LEVEL0, - "creating delete job for CHILD_SA with reqid %d", - expire->state.reqid); - job = (job_t*)delete_child_sa_job_create( - expire->state.reqid); + job = (job_t*)delete_child_sa_job_create(protocol, spi); } else { - this->logger->log(this->logger, CONTROL|LEVEL0, - "creating rekey job for CHILD_SA with reqid %d", - expire->state.reqid); - job = (job_t*)rekey_child_sa_job_create( - expire->state.reqid); + job = (job_t*)rekey_child_sa_job_create(protocol, spi); } charon->job_queue->add(charon->job_queue, job); } |