diff options
author | Martin Willi <martin@strongswan.org> | 2006-07-05 10:53:20 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2006-07-05 10:53:20 +0000 |
commit | 3dd3c5f39e57ef2f402870136579f7478a4eba34 (patch) | |
tree | 7438497060efb7eafe2eb866930adc5026a171d4 | |
parent | b12af2ead628521c8c55638b4f1eedc311864435 (diff) | |
download | strongswan-3dd3c5f39e57ef2f402870136579f7478a4eba34.tar.bz2 strongswan-3dd3c5f39e57ef2f402870136579f7478a4eba34.tar.xz |
redesigned IKE_SA using a transaction mechanism:
removed old state machine
reimplemented IKE_SA setup and delete
implemented dead peer detection
implemented keep-alives
a lot of fixes
no rekeying yet
71 files changed, 4767 insertions, 8081 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 54028b0e1..09eab4649 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -1,4 +1,3 @@ -SUBDIRS = . testing ipsec_PROGRAMS = charon charon_SOURCES = \ @@ -9,13 +8,11 @@ config/policies/local_policy_store.c config/policies/policy_store.h config/polic config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ config/credentials/credential_store.h config/traffic_selector.c config/traffic_selector.h \ config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ -sa/states/state.c sa/states/state.h sa/states/ike_sa_init_requested.c sa/states/ike_sa_init_requested.h \ -sa/states/ike_sa_init_responded.c sa/states/ike_sa_established.c sa/states/ike_sa_established.h \ -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/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/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 \ sa/ike_sa_id.c sa/ike_sa_id.h sa/authenticator.c sa/authenticator.h encoding/payloads/encryption_payload.c \ encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c index cd205867e..2dcf0bb72 100755 --- a/src/charon/config/configuration.c +++ b/src/charon/config/configuration.c @@ -29,6 +29,7 @@ /** * Timeout in milliseconds after that a half open IKE_SA gets deleted. + * Set to zero to disable */ #define HALF_OPEN_IKE_SA_TIMEOUT 30000 @@ -51,14 +52,14 @@ #define MAX_RETRANSMIT_COUNT 6 /** - * Keepalive interval in milliseconds. + * Keepalive interval in seconds. */ -#define KEEPALIVE_INTERVAL 2000000 +#define KEEPALIVE_INTERVAL 20 /** - * DPD interval in milliseconds. + * DPD interval in seconds. */ -#define DPD_INTERVAL 6000000 +#define DPD_INTERVAL 60 typedef struct private_configuration_t private_configuration_t; @@ -78,16 +79,13 @@ struct private_configuration_t { /** * Implementation of configuration_t.get_retransmit_timeout. */ -static status_t get_retransmit_timeout (private_configuration_t *this, u_int32_t retransmit_count, u_int32_t *timeout) +static u_int32_t get_retransmit_timeout (private_configuration_t *this, u_int32_t retransmit_count) { if (retransmit_count > MAX_RETRANSMIT_COUNT && MAX_RETRANSMIT_COUNT != 0) { - return FAILED; + return 0; } - - *timeout = (u_int32_t)(RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); - - return SUCCESS; + return (u_int32_t)(RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); } /** @@ -131,7 +129,7 @@ configuration_t *configuration_create() /* public functions */ this->public.destroy = (void(*)(configuration_t*))destroy; - this->public.get_retransmit_timeout = (status_t (*) (configuration_t *, u_int32_t retransmit_count, u_int32_t *timeout))get_retransmit_timeout; + this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t *, u_int32_t retransmit_count))get_retransmit_timeout; this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t *)) get_half_open_ike_sa_timeout; this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t *)) get_keepalive_interval; this->public.get_dpd_interval = (u_int32_t (*) (configuration_t *)) get_dpd_interval; diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h index 813b95788..553a01edd 100755 --- a/src/charon/config/configuration.h +++ b/src/charon/config/configuration.h @@ -41,18 +41,15 @@ struct configuration_t { /** * @brief Returns the retransmit timeout. * + * A return value of zero means the request should not retransmitted again. * The timeout values are managed by the configuration, so * another backoff algorithm may be implemented here. * * @param this calling object * @param retransmit_count number of times a message was retransmitted so far - * @param[out] timeout the new retransmit timeout in milliseconds - * - * @return - * - FAILED, if the message should not be retransmitted - * - SUCCESS + * @return time in milliseconds, when to schedule next retransmit */ - status_t (*get_retransmit_timeout) (configuration_t *this, u_int32_t retransmit_count, u_int32_t *timeout); + u_int32_t (*get_retransmit_timeout) (configuration_t *this, u_int32_t retransmit_count); /** * @brief Returns the timeout for an half open IKE_SA in ms. @@ -76,7 +73,7 @@ struct configuration_t { * NAT keepalive packet should be sent. * * @param this calling object - * @return interval in milliseconds (ms) + * @return interval in seconds */ u_int32_t (*get_keepalive_interval) (configuration_t *this); @@ -87,7 +84,7 @@ struct configuration_t { * DPD request packet should be sent. * * @param this calling object - * @return interval in milliseconds (ms) + * @return interval in seconds */ u_int32_t (*get_dpd_interval) (configuration_t *this); diff --git a/src/charon/config/connections/connection.c b/src/charon/config/connections/connection.c index ce1f0f31a..e31466039 100644 --- a/src/charon/config/connections/connection.c +++ b/src/charon/config/connections/connection.c @@ -280,9 +280,9 @@ static bool check_dh_group(private_connection_t *this, diffie_hellman_group_t dh return TRUE; } } + alg_iter->destroy(alg_iter); } prop_iter->destroy(prop_iter); - alg_iter->destroy(alg_iter); return FALSE; } diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c index 9e163f9de..0e2d148a4 100644 --- a/src/charon/config/policies/policy.c +++ b/src/charon/config/policies/policy.c @@ -503,7 +503,7 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o this->public.add_other_traffic_selector = (void(*)(policy_t*,traffic_selector_t*))add_other_traffic_selector; this->public.add_proposal = (void(*)(policy_t*,proposal_t*))add_proposal; this->public.add_authorities = (void(*)(policy_t*,identification_t*, identification_t*))add_authorities; - this->public.add_updown = (void(*)(policy_t*,identification_t*,char*))add_updown; + this->public.add_updown = (void(*)(policy_t*,char*))add_updown; this->public.get_soft_lifetime = (u_int32_t (*) (policy_t *))get_soft_lifetime; this->public.get_hard_lifetime = (u_int32_t (*) (policy_t *))get_hard_lifetime; this->public.clone = (policy_t*(*)(policy_t*))clone; diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c index 3eb081544..503b4c1c6 100644 --- a/src/charon/config/proposal.c +++ b/src/charon/config/proposal.c @@ -572,6 +572,8 @@ proposal_t *proposal_create_default(protocol_id_t protocol) add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); break; + default: + break; } return &this->public; diff --git a/src/charon/daemon.c b/src/charon/daemon.c index 73d131ca1..d9ce83ee3 100644 --- a/src/charon/daemon.c +++ b/src/charon/daemon.c @@ -34,7 +34,7 @@ #include "daemon.h" #include <types.h> -#include <config/credentials/credential_store.h> +#include <config/credentials/local_credential_store.h> #include <config/connections/local_connection_store.h> #include <config/policies/local_policy_store.h> diff --git a/src/charon/doc/Todo-list.txt b/src/charon/doc/Todo-list.txt index 8508d2492..7fb33e5a2 100644 --- a/src/charon/doc/Todo-list.txt +++ b/src/charon/doc/Todo-list.txt @@ -35,9 +35,9 @@ / useable certificate support + more id types (use atodn from pluto) + rewrite certificate storage the clean way - - further subjectAltName support - - certificate validation/chaining - - certificate exchange + + further subjectAltName support + + certificate validation/chaining + + certificate exchange + Apply -W's from Makefile.program to charon + do ipsec status via starter @@ -45,7 +45,7 @@ + stroke status should show configured connections + stroke loglevel update -- stroke argument parsing via getopts/gperf? ++ stroke argument parsing via getopts/gperf? - implement 3DES to load encrypted pem files + ipsec.secrets parsing @@ -60,3 +60,4 @@ - add a crl fetch mechanism which synchronizes equal fetches - replace state machine with something more transaction oriented +- find existing IKE_SA on CHILD_SA initiation diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c index 4f5f3b899..4593d33e8 100644 --- a/src/charon/encoding/message.c +++ b/src/charon/encoding/message.c @@ -22,6 +22,7 @@ */ #include <stdlib.h> +#include <string.h> #include "message.h" @@ -413,14 +414,9 @@ static void set_ike_sa_id (private_message_t *this,ike_sa_id_t *ike_sa_id) /** * Implementation of message_t.get_ike_sa_id. */ -static status_t get_ike_sa_id (private_message_t *this,ike_sa_id_t **ike_sa_id) +static ike_sa_id_t* get_ike_sa_id (private_message_t *this) { - if (this->ike_sa_id == NULL) - { - return FAILED; - } - *ike_sa_id = this->ike_sa_id->clone(this->ike_sa_id); - return SUCCESS; + return this->ike_sa_id; } /** @@ -521,6 +517,20 @@ static exchange_type_t get_request (private_message_t *this) } /** + * Is this message in an encoded form? + */ +static bool is_encoded(private_message_t *this) +{ + chunk_t data = this->packet->get_data(this->packet); + + if (data.ptr == NULL) + { + return FALSE; + } + return TRUE; +} + +/** * Implementation of message_t.add_payload. */ static void add_payload(private_message_t *this, payload_t *payload) @@ -583,6 +593,48 @@ static iterator_t *get_payload_iterator(private_message_t *this) return this->payloads->create_iterator(this->payloads, TRUE); } +/** + * Build a string containing short names for all payload in this message + */ +static void build_payload_string(private_message_t *this, char* buffer, size_t size) +{ + iterator_t *iterator; + payload_t *payload; + bool first = TRUE; + + *buffer = '\0'; + size--; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)&payload)) + { + payload_type_t type = payload->get_type(payload); + char *name = mapping_find(payload_type_short_m, type); + size_t name_len = strlen(name); + if (!first) + { + strncat(buffer, " ", size); + if (size) + { + size--; + } + } + else + { + first = FALSE; + } + strncat(buffer, name, size); + if (name_len > size) + { + size = 0; + } + else + { + size -= name_len; + } + } + iterator->destroy(iterator); +} /** * Implementation of message_t.generate. @@ -595,11 +647,20 @@ static status_t generate(private_message_t *this, crypter_t *crypter, signer_t* iterator_t *iterator; status_t status; chunk_t packet_data; + char payload_names[128]; + + if (is_encoded(this)) + { + /* already generated, return a new packet clone */ + *packet = this->packet->clone(this->packet); + return SUCCESS; + } - this->logger->log(this->logger, CONTROL, "generating %s %s, contains %d payloads", + build_payload_string(this, payload_names, sizeof(payload_names)); + this->logger->log(this->logger, CONTROL, "generating %s %s [%s]", mapping_find(exchange_type_m,this->exchange_type), this->is_request ? "request" : "response", - this->payloads->get_count(this->payloads)); + payload_names); if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) { @@ -711,20 +772,6 @@ static chunk_t get_packet_data (private_message_t *this) } /** - * Implementation of message_t.is_encoded. - */ -static bool is_encoded(private_message_t *this) -{ - chunk_t data = this->packet->get_data(this->packet); - - if (data.ptr == NULL) - { - return FALSE; - } - return TRUE; -} - -/** * Implementation of message_t.parse_header. */ static status_t parse_header(private_message_t *this) @@ -794,6 +841,7 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t { status_t status = SUCCESS; payload_type_t current_payload_type; + char payload_names[128]; current_payload_type = this->first_payload; @@ -815,7 +863,7 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t { this->logger->log(this->logger, ERROR, "payload type %s could not be parsed", mapping_find(payload_type_m,current_payload_type)); - return status; + return PARSE_ERROR; } this->logger->log(this->logger, CONTROL|LEVEL2, "verify payload of type %s", @@ -828,8 +876,7 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t this->logger->log(this->logger, ERROR, "%s payload verification failed", mapping_find(payload_type_m,current_payload_type)); current_payload->destroy(current_payload); - status = VERIFY_ERROR; - return status; + return VERIFY_ERROR; } this->logger->log(this->logger, CONTROL|LEVEL2, "%s payload verified. Adding to payload list", @@ -862,14 +909,16 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t if (status != SUCCESS) { this->logger->log(this->logger, ERROR, "verification of message failed"); + return status; } - this->logger->log(this->logger, CONTROL, "parsed %s %s, contains %d payloads", + build_payload_string(this, payload_names, sizeof(payload_names)); + this->logger->log(this->logger, CONTROL, "parsed %s %s [%s]", mapping_find(exchange_type_m, this->exchange_type), this->is_request ? "request" : "response", - this->payloads->get_count(this->payloads)); + payload_names); - return status; + return SUCCESS; } /** @@ -926,7 +975,7 @@ static status_t verify(private_message_t *this) mapping_find(payload_type_m, current_payload_type), this->message_rule->payload_rules[i].max_occurence, found_payloads); iterator->destroy(iterator); - return FAILED; + return VERIFY_ERROR; } } } @@ -937,7 +986,7 @@ static status_t verify(private_message_t *this) mapping_find(payload_type_m, this->message_rule->payload_rules[i].payload_type), this->message_rule->payload_rules[i].min_occurence, found_payloads); iterator->destroy(iterator); - return FAILED; + return VERIFY_ERROR; } if ((this->message_rule->payload_rules[i].sufficient) && (this->payloads->get_count(this->payloads) == total_found_payloads)) { @@ -993,7 +1042,7 @@ static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, sig /* encrypted payload is not last one */ this->logger->log(this->logger, ERROR, "encrypted payload is not last payload"); iterator->destroy(iterator); - return FAILED; + return VERIFY_ERROR; } /* decrypt */ encryption_payload->set_transforms(encryption_payload, crypter, signer); @@ -1003,7 +1052,7 @@ static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, sig { this->logger->log(this->logger, ERROR, "encryption payload signature invalid"); iterator->destroy(iterator); - return status; + return FAILED; } this->logger->log(this->logger, CONTROL | LEVEL2, "decrypt content of encryption payload"); status = encryption_payload->decrypt(encryption_payload); @@ -1013,7 +1062,7 @@ static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, sig "encrypted payload could not be decrypted and parsed: %s", mapping_find(status_m, status)); iterator->destroy(iterator); - return status; + return PARSE_ERROR; } /* needed later to find out if a payload was encrypted */ @@ -1073,7 +1122,7 @@ static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, sig this->logger->log(this->logger, ERROR, "payload type %s not allowed", mapping_find(payload_type_m,current_payload_type)); iterator->destroy(iterator); - return status; + return VERIFY_ERROR; } /* check if the payload was encrypted, and if it should been have encrypted */ @@ -1084,7 +1133,7 @@ static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, sig mapping_find(payload_type_m,current_payload_type), (payload_rule->encrypted) ? "encrypted" : "not encrypted"); iterator->destroy(iterator); - return FAILED; + return VERIFY_ERROR; } } /* advance to the next payload */ @@ -1188,8 +1237,6 @@ static void destroy (private_message_t *this) { iterator_t *iterator; - this->logger->log(this->logger, CONTROL|LEVEL3, "going to destroy message_t object"); - this->packet->destroy(this->packet); if (this->ike_sa_id != NULL) @@ -1202,8 +1249,6 @@ static void destroy (private_message_t *this) { payload_t *payload; iterator->current(iterator, (void**)&payload); - this->logger->log(this->logger, CONTROL|LEVEL3, "destroying payload of type %s", - mapping_find(payload_type_m, payload->get_type(payload))); payload->destroy(payload); } iterator->destroy(iterator); @@ -1230,7 +1275,7 @@ message_t *message_create_from_packet(packet_t *packet) this->public.get_initiator_spi = (u_int64_t(*)(message_t*))get_initiator_spi; this->public.get_responder_spi = (u_int64_t(*)(message_t*))get_responder_spi; this->public.set_ike_sa_id = (void(*)(message_t*, ike_sa_id_t *))set_ike_sa_id; - this->public.get_ike_sa_id = (status_t(*)(message_t*, ike_sa_id_t **))get_ike_sa_id; + this->public.get_ike_sa_id = (ike_sa_id_t*(*)(message_t*))get_ike_sa_id; this->public.set_exchange_type = (void(*)(message_t*, exchange_type_t))set_exchange_type; this->public.get_exchange_type = (exchange_type_t(*)(message_t*))get_exchange_type; this->public.set_request = (void(*)(message_t*, bool))set_request; @@ -1246,7 +1291,6 @@ message_t *message_create_from_packet(packet_t *packet) this->public.parse_body = (status_t (*) (message_t *,crypter_t*,signer_t*)) parse_body; this->public.get_packet = (packet_t * (*) (message_t*)) get_packet; this->public.get_packet_data = (chunk_t (*) (message_t *this)) get_packet_data; - this->public.is_encoded = (bool (*) (message_t *this)) is_encoded; this->public.destroy = (void(*)(message_t*))destroy; /* private values */ diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h index 8c105e597..41edd4c75 100644 --- a/src/charon/encoding/message.h +++ b/src/charon/encoding/message.h @@ -120,27 +120,22 @@ struct message_t { /** * @brief Sets the IKE_SA ID of the message. * - * @warning ike_sa_id gets cloned internaly and - * so can be destroyed afterwards. + * ike_sa_id gets cloned. * * @param this message_t object * @param ike_sa_id ike_sa_id to set */ - void (*set_ike_sa_id) (message_t *this,ike_sa_id_t * ike_sa_id); + void (*set_ike_sa_id) (message_t *this, ike_sa_id_t * ike_sa_id); /** * @brief Gets the IKE_SA ID of the message. - * - * @warning The returned ike_sa_id is a clone of the internal one. - * So it has to be destroyed by the caller. + * + * The ike_sa_id points to the message internal id, do not modify. * * @param this message_t object - * @param ike_sa_id pointer to ike_sa_id pointer which will be set - * @return - * - SUCCESS - * - FAILED if no ike_sa_id is set + * @return ike_sa_id of message */ - status_t (*get_ike_sa_id) (message_t *this,ike_sa_id_t **ike_sa_id); + ike_sa_id_t *(*get_ike_sa_id) (message_t *this); /** * @brief Sets the exchange type of the message. @@ -219,11 +214,12 @@ struct message_t { * @param crypter crypter to decrypt encryption payloads * @param signer signer to verifiy a message with an encryption payload * @return - * - SUCCESS if header could be parsed + * - SUCCESS if parsing successful * - NOT_SUPPORTED if ciritcal unknown payloads found - * - FAILED if message type is not suppported! - * - PARSE_ERROR if corrupted/invalid data found - * - VERIFY_ERROR if verification of some payload failed + * - NOT_SUPPORTED if message type is not supported! + * - PARSE_ERROR if message parsing failed + * - VERIFY_ERROR if message verification failed (bad syntax) + * - FAILED if integrity check failed * - INVALID_STATE if crypter/signer not supplied, but needed */ status_t (*parse_body) (message_t *this, crypter_t *crypter, signer_t *signer); @@ -238,10 +234,12 @@ struct message_t { * message. * Crypter/signer can be omitted (by passing NULL) when no encryption * payload is expected. + * Generation is only done once, multiple calls will just return a packet copy. * * @param this message_t object * @param crypter crypter to use when a payload must be encrypted * @param signer signer to build a mac + * @param packet copy of generated packet * @return * - SUCCESS if packet could be generated * - INVALID_STATE if exchange type is currently not set @@ -322,19 +320,6 @@ struct message_t { chunk_t (*get_packet_data) (message_t *this); /** - * @brief Check if a message is encoded. - * - * Check if the packet is in a generated (and encrypted) form available - * and can be passed down to the socket. If not, it has to be generated - * first. - * - * @param this message_t object - * @return TRUE if encoded, FALSE if not - */ - bool (*is_encoded) (message_t *this); - - - /** * @brief Destroys a message and all including objects. * * @param this message_t object diff --git a/src/charon/encoding/payloads/cert_payload.c b/src/charon/encoding/payloads/cert_payload.c index 18bf24d47..1df30e1e0 100644 --- a/src/charon/encoding/payloads/cert_payload.c +++ b/src/charon/encoding/payloads/cert_payload.c @@ -291,4 +291,4 @@ cert_payload_t *cert_payload_create_from_x509(x509_t *cert) this->set_cert_encoding(this, CERT_X509_SIGNATURE); this->set_data(this, cert->get_certificate(cert)); return this; -}
\ No newline at end of file +} diff --git a/src/charon/encoding/payloads/ke_payload.c b/src/charon/encoding/payloads/ke_payload.c index 0c92e033d..02af60187 100644 --- a/src/charon/encoding/payloads/ke_payload.c +++ b/src/charon/encoding/payloads/ke_payload.c @@ -216,9 +216,7 @@ static void set_key_exchange_data(private_ke_payload_t *this, chunk_t key_exchan } - this->key_exchange_data.ptr = clalloc(key_exchange_data.ptr,key_exchange_data.len); - - this->key_exchange_data.len = key_exchange_data.len; + this->key_exchange_data = chunk_clone(key_exchange_data); this->compute_length(this); } @@ -268,9 +266,22 @@ ke_payload_t *ke_payload_create() this->critical = FALSE; this->next_payload = NO_PAYLOAD; this->payload_length = KE_PAYLOAD_HEADER_LENGTH; - this->key_exchange_data.ptr = NULL; - this->key_exchange_data.len = 0; - this->dh_group_number = 0; + this->key_exchange_data = CHUNK_INITIALIZER; + this->dh_group_number = MODP_NONE; + + return &this->public; +} - return (&(this->public)); +/* + * Described in header + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *dh) +{ + private_ke_payload_t *this = (private_ke_payload_t*)ke_payload_create(); + + dh->get_my_public_value(dh, &this->key_exchange_data); + this->dh_group_number = dh->get_dh_group(dh); + this->compute_length(this); + + return &this->public; } diff --git a/src/charon/encoding/payloads/ke_payload.h b/src/charon/encoding/payloads/ke_payload.h index 9490bef89..43a55fd9d 100644 --- a/src/charon/encoding/payloads/ke_payload.h +++ b/src/charon/encoding/payloads/ke_payload.h @@ -27,6 +27,8 @@ #include <encoding/payloads/payload.h> #include <encoding/payloads/transform_substructure.h> #include <utils/linked_list.h> +#include <crypto/diffie_hellman.h> + /** * KE payload length in bytes without any key exchange data. * @@ -106,5 +108,14 @@ struct ke_payload_t { */ ke_payload_t *ke_payload_create(void); +/** + * @brief Creates a ke_payload_t from a diffie_hellman_t + * + * @param diffie_hellman diffie hellman object containing group and key + * @return ke_payload_t object + * + * @ingroup payloads + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *diffie_hellman); -#endif /*KE_PAYLOAD_H_*/ +#endif /* KE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/nonce_payload.h b/src/charon/encoding/payloads/nonce_payload.h index e875fb263..9365bb52b 100644 --- a/src/charon/encoding/payloads/nonce_payload.h +++ b/src/charon/encoding/payloads/nonce_payload.h @@ -27,6 +27,15 @@ #include <encoding/payloads/payload.h> /** + * Nonce size in bytes for nonces sending to other peer. + * + * @warning Nonce size MUST be between 16 and 256 bytes. + * + * @ingroup payloads + */ +#define NONCE_SIZE 16 + +/** * Length of a nonce payload without a nonce in bytes. * * @ingroup payloads diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c index 4242b4c1e..7d032ec6e 100644 --- a/src/charon/encoding/payloads/notify_payload.c +++ b/src/charon/encoding/payloads/notify_payload.c @@ -28,10 +28,12 @@ #include <daemon.h> #include <encoding/payloads/encodings.h> +#define SHA1_HASH_SIZE 20 + /** - * String mappings for notify_message_type_t. + * String mappings for notify_type_t. */ -mapping_t notify_message_type_m[] = { +mapping_t notify_type_m[] = { {UNSUPPORTED_CRITICAL_PAYLOAD, "UNSUPPORTED_CRITICAL_PAYLOAD"}, {INVALID_IKE_SPI, "INVALID_IKE_SPI"}, {INVALID_MAJOR_VERSION, "INVALID_MAJOR_VERSION"}, @@ -94,7 +96,7 @@ struct private_notify_payload_t { /** * Notify message type. */ - u_int16_t notify_message_type; + u_int16_t notify_type; /** * Security parameter index (spi). @@ -146,7 +148,7 @@ encoding_rule_t notify_payload_encodings[] = { /* SPI Size as 8 bit field*/ { SPI_SIZE, offsetof(private_notify_payload_t, spi_size) }, /* Notify message type as 16 bit field*/ - { U_INT_16, offsetof(private_notify_payload_t, notify_message_type) }, + { U_INT_16, offsetof(private_notify_payload_t, notify_type) }, /* SPI as variable length field*/ { SPI, offsetof(private_notify_payload_t, spi) }, /* Key Exchange Data is from variable size */ @@ -195,31 +197,60 @@ static status_t verify(private_notify_payload_t *this) return FAILED; } - /* TODO: Check all kinds of notify */ - if (this->notify_message_type == INVALID_KE_PAYLOAD) + switch (this->notify_type) { - /* check notification data */ - diffie_hellman_group_t dh_group; - if (this->notification_data.len != 2) + case INVALID_KE_PAYLOAD: { - return FAILED; + /* check notification data */ + diffie_hellman_group_t dh_group; + if (this->notification_data.len != 2) + { + return FAILED; + } + dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr)); + switch (dh_group) + { + case MODP_768_BIT: + case MODP_1024_BIT: + case MODP_1536_BIT: + case MODP_2048_BIT: + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_6144_BIT: + case MODP_8192_BIT: + break; + default: + this->logger->log(this->logger, ERROR, "Bad DH group (%d)", dh_group); + return FAILED; + } + break; } - dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr)); - switch (dh_group) + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: { - case MODP_768_BIT: - case MODP_1024_BIT: - case MODP_1536_BIT: - case MODP_2048_BIT: - case MODP_3072_BIT: - case MODP_4096_BIT: - case MODP_6144_BIT: - case MODP_8192_BIT: - break; - default: - this->logger->log(this->logger, ERROR, "Bad DH group (%d)", dh_group); + if (this->notification_data.len != SHA1_HASH_SIZE) + { + this->logger->log(this->logger, ERROR, "invalid %s notify length", + mapping_find(notify_type_m, this->notify_type)); + return FAILED; + } + break; + } + case INVALID_SYNTAX: + case INVALID_MAJOR_VERSION: + case NO_PROPOSAL_CHOSEN: + { + if (this->notification_data.len != 0) + { + this->logger->log(this->logger, ERROR, "invalid %s notify", + mapping_find(notify_type_m, this->notify_type)); return FAILED; + } + break; } + default: + /* TODO: verify */ + break; } return SUCCESS; } @@ -300,19 +331,19 @@ static void set_protocol_id(private_notify_payload_t *this, u_int8_t protocol_id } /** - * Implementation of notify_payload_t.get_notify_message_type. + * Implementation of notify_payload_t.get_notify_type. */ -static notify_message_type_t get_notify_message_type(private_notify_payload_t *this) +static notify_type_t get_notify_type(private_notify_payload_t *this) { - return this->notify_message_type; + return this->notify_type; } /** - * Implementation of notify_payload_t.set_notify_message_type. + * Implementation of notify_payload_t.set_notify_type. */ -static void set_notify_message_type(private_notify_payload_t *this, u_int16_t notify_message_type) +static void set_notify_type(private_notify_payload_t *this, u_int16_t notify_type) { - this->notify_message_type = notify_message_type; + this->notify_type = notify_type; } /** @@ -363,20 +394,9 @@ static chunk_t get_notification_data(private_notify_payload_t *this) */ static status_t set_notification_data(private_notify_payload_t *this, chunk_t notification_data) { - /* destroy existing data first */ - if (this->notification_data.ptr != NULL) - { - /* free existing value */ - free(this->notification_data.ptr); - this->notification_data.ptr = NULL; - this->notification_data.len = 0; - - } - - this->notification_data.ptr = clalloc(notification_data.ptr,notification_data.len); - this->notification_data.len = notification_data.len; + chunk_free(&this->notification_data); + this->notification_data = chunk_clone(notification_data); this->compute_length(this); - return SUCCESS; } @@ -385,14 +405,8 @@ static status_t set_notification_data(private_notify_payload_t *this, chunk_t no */ static status_t destroy(private_notify_payload_t *this) { - if (this->notification_data.ptr != NULL) - { - free(this->notification_data.ptr); - } - if (this->spi.ptr != NULL) - { - free(this->spi.ptr); - } + chunk_free(&this->notification_data); + chunk_free(&this->spi); free(this); return SUCCESS; } @@ -416,8 +430,8 @@ notify_payload_t *notify_payload_create() /* public functions */ this->public.get_protocol_id = (u_int8_t (*) (notify_payload_t *)) get_protocol_id; this->public.set_protocol_id = (void (*) (notify_payload_t *,u_int8_t)) set_protocol_id; - this->public.get_notify_message_type = (notify_message_type_t (*) (notify_payload_t *)) get_notify_message_type; - this->public.set_notify_message_type = (void (*) (notify_payload_t *,notify_message_type_t)) set_notify_message_type; + this->public.get_notify_type = (notify_type_t (*) (notify_payload_t *)) get_notify_type; + this->public.set_notify_type = (void (*) (notify_payload_t *,notify_type_t)) set_notify_type; this->public.get_spi = (u_int32_t (*) (notify_payload_t *)) get_spi; this->public.set_spi = (void (*) (notify_payload_t *,u_int32_t)) set_spi; this->public.get_notification_data = (chunk_t (*) (notify_payload_t *)) get_notification_data; @@ -432,7 +446,7 @@ notify_payload_t *notify_payload_create() this->next_payload = NO_PAYLOAD; this->payload_length = NOTIFY_PAYLOAD_HEADER_LENGTH; this->protocol_id = 0; - this->notify_message_type = 0; + this->notify_type = 0; this->spi.ptr = NULL; this->spi.len = 0; this->spi_size = 0; @@ -440,17 +454,17 @@ notify_payload_t *notify_payload_create() this->notification_data.len = 0; this->logger = logger_manager->get_logger(logger_manager, PAYLOAD); - return (&(this->public)); + return &this->public; } /* * Described in header. */ -notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_message_type_t notify_message_type) +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t notify_type) { notify_payload_t *notify = notify_payload_create(); - notify->set_notify_message_type(notify,notify_message_type); + notify->set_notify_type(notify,notify_type); notify->set_protocol_id(notify,protocol_id); return notify; diff --git a/src/charon/encoding/payloads/notify_payload.h b/src/charon/encoding/payloads/notify_payload.h index 2d2e4ba45..660fdcb81 100644 --- a/src/charon/encoding/payloads/notify_payload.h +++ b/src/charon/encoding/payloads/notify_payload.h @@ -37,7 +37,7 @@ */ #define NOTIFY_PAYLOAD_HEADER_LENGTH 8 -typedef enum notify_message_type_t notify_message_type_t; +typedef enum notify_type_t notify_type_t; /** @@ -47,7 +47,7 @@ typedef enum notify_message_type_t notify_message_type_t; * * @ingroup payloads */ -enum notify_message_type_t { +enum notify_type_t { UNSUPPORTED_CRITICAL_PAYLOAD = 1, INVALID_IKE_SPI = 4, INVALID_MAJOR_VERSION = 5, @@ -72,11 +72,11 @@ enum notify_message_type_t { }; /** - * String mappings for notify_message_type_t. + * String mappings for notify_type_t. * * @ingroup payloads */ -extern mapping_t notify_message_type_m[]; +extern mapping_t notify_type_m[]; typedef struct notify_payload_t notify_payload_t; @@ -122,7 +122,7 @@ struct notify_payload_t { * @param this calling notify_payload_t object * @return notify message type of this payload */ - notify_message_type_t (*get_notify_message_type) (notify_payload_t *this); + notify_type_t (*get_notify_type) (notify_payload_t *this); /** * @brief Sets notify message type of this payload. @@ -130,7 +130,7 @@ struct notify_payload_t { * @param this calling notify_payload_t object * @param type notify message type to set */ - void (*set_notify_message_type) (notify_payload_t *this, notify_message_type_t type); + void (*set_notify_type) (notify_payload_t *this, notify_type_t type); /** * @brief Returns the currently set spi of this payload. @@ -193,12 +193,12 @@ notify_payload_t *notify_payload_create(void); * @brief Creates an notify_payload_t object of specific type for specific protocol id. * * @param protocol_id protocol id (IKE, AH or ESP) - * @param notify_message_type notify type (see notify_message_type_t) + * @param type notify type (see notify_type_t) * @return notify_payload_t object * * @ingroup payloads */ -notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_message_type_t notify_message_type); +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t type); #endif /*NOTIFY_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/payload.c b/src/charon/encoding/payloads/payload.c index be58adf8e..13e7515b4 100644 --- a/src/charon/encoding/payloads/payload.c +++ b/src/charon/encoding/payloads/payload.c @@ -74,6 +74,37 @@ mapping_t payload_type_m[] = { }; /* + * build the short mappings for payload_type_t + */ +mapping_t payload_type_short_m[] = { + {NO_PAYLOAD, "--"}, + {SECURITY_ASSOCIATION, "SA"}, + {KEY_EXCHANGE, "KE"}, + {ID_INITIATOR, "IDi"}, + {ID_RESPONDER, "IDr"}, + {CERTIFICATE, "CERT"}, + {CERTIFICATE_REQUEST, "CERTREQ"}, + {AUTHENTICATION, "AUTH"}, + {NONCE, "No"}, + {NOTIFY, "N"}, + {DELETE, "D"}, + {VENDOR_ID, "V"}, + {TRAFFIC_SELECTOR_INITIATOR, "TSi"}, + {TRAFFIC_SELECTOR_RESPONDER, "TSr"}, + {ENCRYPTED, "E"}, + {CONFIGURATION, "CP"}, + {EXTENSIBLE_AUTHENTICATION, "EAP"}, + {HEADER, "HDR"}, + {PROPOSAL_SUBSTRUCTURE, "PROP"}, + {TRANSFORM_SUBSTRUCTURE, "TRANS"}, + {TRANSFORM_ATTRIBUTE, "TRANSATTR"}, + {TRAFFIC_SELECTOR_SUBSTRUCTURE, "TSSUB"}, + {CONFIGURATION_ATTRIBUTE, "CPATTR"}, + {UNKNOWN_PAYLOAD, "??"}, + {MAPPING_END, NULL} +}; + +/* * see header */ payload_t *payload_create(payload_type_t type) diff --git a/src/charon/encoding/payloads/payload.h b/src/charon/encoding/payloads/payload.h index bc593f618..68e22b31d 100644 --- a/src/charon/encoding/payloads/payload.h +++ b/src/charon/encoding/payloads/payload.h @@ -188,6 +188,11 @@ enum payload_type_t{ */ extern mapping_t payload_type_m[]; +/** + * Special string mappings for payload_type_t in a short form. + */ +extern mapping_t payload_type_short_m[]; + typedef struct payload_t payload_t; diff --git a/src/charon/encoding/payloads/proposal_substructure.c b/src/charon/encoding/payloads/proposal_substructure.c index f72fbb1c2..e7a4058f2 100644 --- a/src/charon/encoding/payloads/proposal_substructure.c +++ b/src/charon/encoding/payloads/proposal_substructure.c @@ -632,15 +632,11 @@ proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t * iterator->destroy(iterator); /* take over general infos */ - this->spi_size = proposal->get_protocol(proposal) == PROTO_IKE ? 8 : 4; + this->spi_size = proposal->get_protocol(proposal) == PROTO_IKE ? 0 : 4; this->spi.len = this->spi_size; - this->spi.ptr = malloc(this->spi_size); - if (this->spi_size == 8) - { - *((u_int64_t*)this->spi.ptr) = proposal->get_spi(proposal); - } - else + if (this->spi_size == 4) { + this->spi.ptr = malloc(this->spi_size); *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal); } this->proposal_number = 0; diff --git a/src/charon/encoding/payloads/sa_payload.c b/src/charon/encoding/payloads/sa_payload.c index 0c752b81f..8e5022dd6 100644 --- a/src/charon/encoding/payloads/sa_payload.c +++ b/src/charon/encoding/payloads/sa_payload.c @@ -303,7 +303,7 @@ static linked_list_t *get_proposals(private_sa_payload_t *this) if (ignore_struct_number < struct_number) { /* remova an already added, if first of series */ - proposal_list->remove_last(proposal_list, (void**)proposal); + proposal_list->remove_last(proposal_list, (void**)&proposal); proposal->destroy(proposal); ignore_struct_number = struct_number; } diff --git a/src/charon/network/interfaces.c b/src/charon/network/interfaces.c index ce0141849..7578d579c 100644 --- a/src/charon/network/interfaces.c +++ b/src/charon/network/interfaces.c @@ -50,11 +50,11 @@ struct private_interfaces_t { }; /** - * Implements interfaces_t.get_addresses + * Implements interfaces_t.create_address_iterator */ -static linked_list_t* get_addresses(private_interfaces_t *this) +static iterator_t* create_address_iterator(private_interfaces_t *this) { - return this->addresses; + return this->addresses->create_iterator(this->addresses, TRUE); } /** @@ -138,7 +138,7 @@ interfaces_t *interfaces_create(u_int16_t port) this->port = port; - this->public.get_addresses = (linked_list_t* (*) (interfaces_t*)) get_addresses; + this->public.create_address_iterator = (iterator_t* (*) (interfaces_t*)) create_address_iterator; this->public.is_local_address = (bool (*) (interfaces_t*, host_t*)) is_local_address; this->public.destroy = (void (*) (interfaces_t*)) destroy; diff --git a/src/charon/network/interfaces.h b/src/charon/network/interfaces.h index 6500460cf..33143e07c 100644 --- a/src/charon/network/interfaces.h +++ b/src/charon/network/interfaces.h @@ -41,12 +41,12 @@ typedef struct interfaces_t interfaces_t; struct interfaces_t { /** - * @brief Get addresses of local interfaces + * @brief Get an iterator over addresses of local interfaces * * @param this calling object - * @return linked_list_t of host_t objects + * @return iterator over host_t objects */ - linked_list_t* (*get_addresses) (interfaces_t *ifaces); + iterator_t* (*create_address_iterator) (interfaces_t *this); /** * @brief Check if address is associated with a local interface @@ -55,7 +55,7 @@ struct interfaces_t { * @param host address to set as destination * @return TRUE if address is associated with a local interface, FALSE otherwise */ - bool (*is_local_address) (interfaces_t *ifaces, host_t *host); + bool (*is_local_address) (interfaces_t *this, host_t *host); /** * @brief Destroy the object, freeing contained data. @@ -77,4 +77,4 @@ struct interfaces_t { interfaces_t *interfaces_create(u_int16_t port); -#endif /*INTERFACES_H_*/ +#endif /* INTERFACES_H_ */ diff --git a/src/charon/network/socket.c b/src/charon/network/socket.c index 0d5470c2a..78c793e98 100644 --- a/src/charon/network/socket.c +++ b/src/charon/network/socket.c @@ -164,7 +164,7 @@ static status_t receiver(private_socket_t *this, packet_t **packet) pkt->set_source(pkt, source); pkt->set_destination(pkt, dest); - this->logger->log(this->logger, CONTROL, "received packet: from %s:%d to %s:%d", + this->logger->log(this->logger, CONTROL|LEVEL1, "received packet: from %s:%d to %s:%d", source->get_address(source), source->get_port(source), dest->get_address(dest), dest->get_port(dest)); @@ -202,7 +202,7 @@ status_t sender(private_socket_t *this, packet_t *packet) dst = packet->get_destination(packet); data = packet->get_data(packet); - this->logger->log(this->logger, CONTROL, "sending packet: from %s:%d to %s:%d", + this->logger->log(this->logger, CONTROL|LEVEL1, "sending packet: from %s:%d to %s:%d", src->get_address(src), src->get_port(src), dst->get_address(dst), dst->get_port(dst)); diff --git a/src/charon/queues/jobs/delete_half_open_ike_sa_job.c b/src/charon/queues/jobs/delete_half_open_ike_sa_job.c index c81783446..864711b4d 100644 --- a/src/charon/queues/jobs/delete_half_open_ike_sa_job.c +++ b/src/charon/queues/jobs/delete_half_open_ike_sa_job.c @@ -60,40 +60,27 @@ static job_type_t get_type(private_delete_half_open_ike_sa_job_t *this) static status_t execute(private_delete_half_open_ike_sa_job_t *this) { ike_sa_t *ike_sa; - status_t status; - status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id, &ike_sa); - if ((status != SUCCESS) && (status != CREATED)) + if (charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id, + &ike_sa) != SUCCESS) { - this->logger->log(this->logger, CONTROL | LEVEL3, "IKE SA seems to be already deleted"); return DESTROY_ME; } switch (ike_sa->get_state(ike_sa)) { - case INITIATOR_INIT: - case RESPONDER_INIT: - case IKE_SA_INIT_REQUESTED: - case IKE_SA_INIT_RESPONDED: - case IKE_AUTH_REQUESTED: - case DELETE_IKE_SA_REQUESTED: + case SA_ESTABLISHED: { - /* IKE_SA is half open and gets deleted! */ - status = charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Could not checkin and delete checked out IKE_SA!"); - } + /* IKE_SA is established and so is not getting destroyed */ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); return DESTROY_ME; } default: { - /* IKE_SA is established and so is not getting deleted! */ - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Could not checkin a checked out IKE_SA!"); - } + /* IKE_SA is half open and gets destroyed */ + this->logger->log(this->logger, AUDIT, + "deleting half open IKE_SA after timeout"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); return DESTROY_ME; } } diff --git a/src/charon/queues/jobs/delete_half_open_ike_sa_job.h b/src/charon/queues/jobs/delete_half_open_ike_sa_job.h index b26750cc1..ff4f5220d 100644 --- a/src/charon/queues/jobs/delete_half_open_ike_sa_job.h +++ b/src/charon/queues/jobs/delete_half_open_ike_sa_job.h @@ -34,7 +34,7 @@ typedef struct delete_half_open_ike_sa_job_t delete_half_open_ike_sa_job_t; * @brief Class representing an DELETE_HALF_OPEN_IKE_SA Job. * * This job is responsible for deleting of half open IKE_SAs. A half - * open IKE_SA is every IKE_SA which hasn't reache the ike_sa_established + * open IKE_SA is every IKE_SA which hasn't reache the SA_ESTABLISHED * state. * * @b Constructors: @@ -43,6 +43,7 @@ typedef struct delete_half_open_ike_sa_job_t delete_half_open_ike_sa_job_t; * @ingroup jobs */ struct delete_half_open_ike_sa_job_t { + /** * The job_t interface. */ @@ -59,4 +60,4 @@ struct delete_half_open_ike_sa_job_t { */ delete_half_open_ike_sa_job_t *delete_half_open_ike_sa_job_create(ike_sa_id_t *ike_sa_id); -#endif /*DELETE_HALF_OPEN_IKE_SA_JOB_H_*/ +#endif /* DELETE_HALF_OPEN_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/incoming_packet_job.c b/src/charon/queues/jobs/incoming_packet_job.c index fbd52b7a6..fa6eb331d 100644 --- a/src/charon/queues/jobs/incoming_packet_job.c +++ b/src/charon/queues/jobs/incoming_packet_job.c @@ -57,6 +57,47 @@ static job_type_t get_type(private_incoming_packet_job_t *this) } /** + * send a notify back to the sender + */ +static void send_notify_response(private_incoming_packet_job_t *this, + message_t *request, + notify_type_t type) +{ + notify_payload_t *notify; + message_t *response; + host_t *src, *dst; + packet_t *packet; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = request->get_ike_sa_id(request); + ike_sa_id = ike_sa_id->clone(ike_sa_id); + ike_sa_id->switch_initiator(ike_sa_id); + + response = message_create(); + dst = request->get_source(request); + src = request->get_destination(request); + response->set_source(response, src->clone(src)); + response->set_destination(response, dst->clone(dst)); + response->set_exchange_type(response, IKE_SA_INIT); + response->set_request(response, FALSE); + response->set_message_id(response, 0); + response->set_ike_sa_id(response, ike_sa_id); + notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); + response->add_payload(response, (payload_t *)notify); + if (response->generate(response, NULL, NULL, &packet) != SUCCESS) + { + response->destroy(response); + return; + } + this->logger->log(this->logger, CONTROL, "sending %s notify", + mapping_find(notify_type_m, type)); + charon->send_queue->add(charon->send_queue, packet); + response->destroy(response); + ike_sa_id->destroy(ike_sa_id); + return; +} + +/** * Implementation of job_t.execute. */ static status_t execute(private_incoming_packet_job_t *this) @@ -65,104 +106,55 @@ static status_t execute(private_incoming_packet_job_t *this) ike_sa_t *ike_sa; ike_sa_id_t *ike_sa_id; status_t status; - packet_t *packet; + host_t *src, *dst; message = message_create_from_packet(this->packet->clone(this->packet)); + src = message->get_source(message); + dst = message->get_destination(message); + this->logger->log(this->logger, CONTROL, "received packet: from %s:%d to %s:%d", + src->get_address(src), src->get_port(src), + dst->get_address(dst), dst->get_port(dst)); + status = message->parse_header(message); if (status != SUCCESS) { - this->logger->log(this->logger, ERROR, "Message header could not be verified!"); + this->logger->log(this->logger, ERROR, "received message with invalid IKE header, ignored"); message->destroy(message); return DESTROY_ME; } - this->logger->log(this->logger, CONTROL|LEVEL2, "Message is a %s %s", - mapping_find(exchange_type_m, message->get_exchange_type(message)), - message->get_request(message) ? "request" : "reply"); - if ((message->get_major_version(message) != IKE_MAJOR_VERSION) || (message->get_minor_version(message) != IKE_MINOR_VERSION)) { - this->logger->log(this->logger, ERROR | LEVEL2, - "IKE version %d.%d not supported", + this->logger->log(this->logger, ERROR, + "received a packet with IKE version %d.%d, not supported", message->get_major_version(message), message->get_minor_version(message)); if ((message->get_exchange_type(message) == IKE_SA_INIT) && (message->get_request(message))) { - notify_payload_t *notify; - message_t *response; - host_t *src, *dst; - - message->get_ike_sa_id(message, &ike_sa_id); - ike_sa_id->switch_initiator(ike_sa_id); - - response = message_create(); - src = message->get_source(message); - dst = message->get_destination(message); - response->set_source(response, src->clone(src)); - response->set_destination(response, dst->clone(dst)); - response->set_exchange_type(response, IKE_SA_INIT); - response->set_request(response, FALSE); - response->set_message_id(response, 0); - response->set_ike_sa_id(response, ike_sa_id); - - notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, INVALID_MAJOR_VERSION); - response->add_payload(response, (payload_t *)notify); - - status = response->generate(response, NULL, NULL, &packet); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Could not generate packet from message"); - response->destroy(response); - return DESTROY_ME; - } - this->logger->log(this->logger, ERROR, "Send notify reply of type INVALID_MAJOR_VERSION"); - charon->send_queue->add(charon->send_queue, packet); - response->destroy(response); - return DESTROY_ME; + send_notify_response(this, message, INVALID_MAJOR_VERSION); } message->destroy(message); return DESTROY_ME; } - message->get_ike_sa_id(message, &ike_sa_id); + ike_sa_id = message->get_ike_sa_id(message); + ike_sa_id = ike_sa_id->clone(ike_sa_id); ike_sa_id->switch_initiator(ike_sa_id); - this->logger->log(this->logger, CONTROL|LEVEL3, "Checking out IKE SA %lld:%lld, role %s", - ike_sa_id->get_initiator_spi(ike_sa_id), - ike_sa_id->get_responder_spi(ike_sa_id), - ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); - status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, ike_sa_id, &ike_sa); - if ((status != SUCCESS) && (status != CREATED)) + if (status != SUCCESS) { - this->logger->log(this->logger, ERROR, "IKE SA could not be checked out"); + this->logger->log(this->logger, ERROR, + "received packet with SPIs %llx:%llx, but no such IKE_SA", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id)); + send_notify_response(this, message, INVALID_IKE_SPI); ike_sa_id->destroy(ike_sa_id); message->destroy(message); - - /* TODO: send notify reply of type INVALID_IKE_SPI if SPI could not be found ? */ return DESTROY_ME; } - - if (status == CREATED) - { - job_t *delete_job; - this->logger->log(this->logger, CONTROL|LEVEL3, - "Create Job to delete half open IKE_SA."); - - delete_job = (job_t *) delete_half_open_ike_sa_job_create(ike_sa_id); - charon->event_queue->add_relative(charon->event_queue, delete_job, - charon->configuration->get_half_open_ike_sa_timeout(charon->configuration)); - } status = ike_sa->process_message(ike_sa, message); - - this->logger->log(this->logger, CONTROL|LEVEL3, "%s IKE SA %lld:%lld, role %s", - status == DESTROY_ME ? "Checkin and delete" : "Checkin", - ike_sa_id->get_initiator_spi(ike_sa_id), - ike_sa_id->get_responder_spi(ike_sa_id), - ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); - ike_sa_id->destroy(ike_sa_id); - if (status == DESTROY_ME) { status = charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); @@ -171,11 +163,7 @@ static status_t execute(private_incoming_packet_job_t *this) { status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } - - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); - } + ike_sa_id->destroy(ike_sa_id); message->destroy(message); return DESTROY_ME; } diff --git a/src/charon/queues/jobs/initiate_ike_sa_job.c b/src/charon/queues/jobs/initiate_ike_sa_job.c index 16e440768..6c1d7bf9a 100644 --- a/src/charon/queues/jobs/initiate_ike_sa_job.c +++ b/src/charon/queues/jobs/initiate_ike_sa_job.c @@ -63,41 +63,28 @@ static job_type_t get_type(private_initiate_ike_sa_job_t *this) */ static status_t execute(private_initiate_ike_sa_job_t *this) { - /* - * Initiatie an IKE_SA: - * - is defined by a name of a configuration - * - create an empty IKE_SA via manager - * - call initiate_connection on this sa - */ + /* Initiatie an IKE_SA: + * - is defined by a connection + * - create an empty IKE_SA via manager + * - call initiate() on this IKE_SA + */ ike_sa_t *ike_sa; status_t status; - job_t *delete_job; this->logger->log(this->logger, CONTROL|LEVEL2, "Creating and checking out IKE SA"); charon->ike_sa_manager->create_and_checkout(charon->ike_sa_manager, &ike_sa); - status = ike_sa->initiate_connection(ike_sa, this->connection->clone(this->connection)); + status = ike_sa->initiate(ike_sa, this->connection->clone(this->connection)); if (status != SUCCESS) { - this->logger->log(this->logger, ERROR, "Initiation returned %s, going to delete IKE_SA.", - mapping_find(status_m, status)); + this->logger->log(this->logger, ERROR, + "initiation returned %s, going to delete IKE_SA.", + mapping_find(status_m, status)); charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); return DESTROY_ME; } - this->logger->log(this->logger, CONTROL|LEVEL3, "Create Job to delete half open IKE_SA."); - - delete_job = (job_t *) delete_half_open_ike_sa_job_create(ike_sa->get_id(ike_sa)); - charon->event_queue->add_relative(charon->event_queue, delete_job, - charon->configuration->get_half_open_ike_sa_timeout(charon->configuration)); - - this->logger->log(this->logger, CONTROL|LEVEL2, "Checking in IKE SA"); - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Could not checkin IKE_SA (%s)", - mapping_find(status_m, status)); - } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); return DESTROY_ME; } diff --git a/src/charon/queues/jobs/retransmit_request_job.c b/src/charon/queues/jobs/retransmit_request_job.c index 40f2ffd94..7c2d742dc 100644 --- a/src/charon/queues/jobs/retransmit_request_job.c +++ b/src/charon/queues/jobs/retransmit_request_job.c @@ -46,11 +46,6 @@ struct private_retransmit_request_job_t { ike_sa_id_t *ike_sa_id; /** - * Number of times a request was retransmitted - */ - u_int32_t retransmit_count; - - /** * Logger reference */ logger_t *logger; @@ -69,79 +64,27 @@ static job_type_t get_type(private_retransmit_request_job_t *this) */ static status_t execute(private_retransmit_request_job_t *this) { - bool stop_retransmitting = FALSE, timed_out = FALSE; - u_int32_t timeout; ike_sa_t *ike_sa; status_t status; - - this->logger->log(this->logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id, &ike_sa); - if ((status != SUCCESS) && (status != CREATED)) + if (status != SUCCESS) { this->logger->log(this->logger, ERROR|LEVEL1, "IKE SA could not be checked out. Already deleted?"); return DESTROY_ME; } - this->retransmit_count++; - status = charon->configuration->get_retransmit_timeout(charon->configuration, - this->retransmit_count, &timeout); - timed_out = (status != SUCCESS); - - if (ike_sa->retransmit_possible(ike_sa, this->message_id)) + if (ike_sa->retransmit_request(ike_sa, this->message_id) == DESTROY_ME) { - if (!timed_out) - { - status = ike_sa->retransmit_request(ike_sa, this->message_id); - if (status != SUCCESS) - { - this->logger->log(this->logger, CONTROL|LEVEL3, - "Message doesn't have to be retransmitted"); - stop_retransmitting = TRUE; - } - } + /* retransmission hopeless, kill SA */ + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); } else { - stop_retransmitting = TRUE; - } - - this->logger->log(this->logger, CONTROL|LEVEL2, "Checkin IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); - - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); - } - - if (timed_out) - { - /* - * XXX: We should act depending on DPD policy here, or not act at all. - */ - this->logger->log(this->logger, CONTROL|LEVEL2, "Timeout: Deleting SA!"); - status = charon->ike_sa_manager->delete(charon->ike_sa_manager, this->ike_sa_id); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR|LEVEL1, "Cannot delete SA!"); - } - return DESTROY_ME; - } - - if (stop_retransmitting) - { - return DESTROY_ME; + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } - - charon->event_queue->add_relative(charon->event_queue, (job_t *)this, timeout); - return SUCCESS; + return DESTROY_ME; } /** @@ -167,7 +110,6 @@ retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike /* private variables */ this->message_id = message_id; - this->retransmit_count = 0; this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->logger = logger_manager->get_logger(logger_manager, WORKER); diff --git a/src/charon/queues/jobs/send_dpd_job.c b/src/charon/queues/jobs/send_dpd_job.c index 3c38110b0..56b1fac0d 100644 --- a/src/charon/queues/jobs/send_dpd_job.c +++ b/src/charon/queues/jobs/send_dpd_job.c @@ -66,58 +66,23 @@ static status_t execute(private_send_dpd_job_t *this) { ike_sa_t *ike_sa; status_t status; - u_int32_t dt; - u_int32_t interval = charon->configuration->get_dpd_interval(charon->configuration); - struct timeval last_msg_tv, current_tv; - - this->logger->log(this->logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, - this->ike_sa_id, &ike_sa); + this->ike_sa_id, &ike_sa); if (status != SUCCESS) { - this->logger->log(this->logger, ERROR|LEVEL1, - "IKE SA could not be checked out. Already deleted?"); return DESTROY_ME; } - - last_msg_tv = ike_sa->get_last_traffic_in_tv(ike_sa); - if (0 > gettimeofday(¤t_tv, NULL) ) - { - this->logger->log(this->logger, ERROR|LEVEL1, - "Warning: Failed to get time of day."); - } - dt = (current_tv.tv_sec - last_msg_tv.tv_sec) * 1000 - + (current_tv.tv_usec - last_msg_tv.tv_usec) / 1000; - - if (dt >= interval) + status = ike_sa->send_dpd(ike_sa); + if (status == DESTROY_ME) { - ike_sa->send_dpd_request(ike_sa); - this->logger->log(this->logger, CONTROL|LEVEL1, - "DPD request packet scheduled"); - + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); } else { - charon->event_queue->add_relative(charon->event_queue, (job_t*) this, interval - dt); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } - - this->logger->log(this->logger, CONTROL|LEVEL2, - "Checkin IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); - - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); - } - - return SUCCESS; + return DESTROY_ME; } /** diff --git a/src/charon/queues/jobs/send_keepalive_job.c b/src/charon/queues/jobs/send_keepalive_job.c index f17296af6..6c7dceb3e 100644 --- a/src/charon/queues/jobs/send_keepalive_job.c +++ b/src/charon/queues/jobs/send_keepalive_job.c @@ -60,74 +60,22 @@ static job_type_t get_type(private_send_keepalive_job_t *this) } /** - * Implementation of job_t.execute. + * Implementation of job_t.execute. */ static status_t execute(private_send_keepalive_job_t *this) { ike_sa_t *ike_sa; status_t status; - u_int32_t dt; - u_int32_t interval = charon->configuration->get_keepalive_interval(charon->configuration); - struct timeval last_msg_tv, current_tv; - packet_t *packet; - host_t *host; - connection_t *connection; - chunk_t data; - - this->logger->log(this->logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, - this->ike_sa_id, &ike_sa); + this->ike_sa_id, &ike_sa); if (status != SUCCESS) { - this->logger->log(this->logger, ERROR|LEVEL1, - "IKE SA could not be checked out. Already deleted?"); return DESTROY_ME; } - - last_msg_tv = ike_sa->get_last_traffic_out_tv(ike_sa); - if (0 > gettimeofday(¤t_tv, NULL) ) - { - this->logger->log(this->logger, ERROR|LEVEL1, - "Warning: Failed to get time of day."); - } - dt = (current_tv.tv_sec - last_msg_tv.tv_sec) * 1000 - + (current_tv.tv_usec - last_msg_tv.tv_usec) / 1000; - - if (dt >= interval) - { - packet = packet_create(); - connection = ike_sa->get_connection(ike_sa); - host = connection->get_my_host(connection); - packet->set_source(packet, host->clone(host)); - host = connection->get_other_host(connection); - packet->set_destination(packet, host->clone(host)); - data = chunk_alloc(1); - data.ptr[0] = 0xFF; - packet->set_data(packet, data); - charon->send_queue->add(charon->send_queue, packet); - dt = 0; - this->logger->log(this->logger, CONTROL|LEVEL1, - "NAT keepalive packet scheduled"); - } - charon->event_queue->add_relative(charon->event_queue, (job_t*) this, interval - dt); - - this->logger->log(this->logger, CONTROL|LEVEL2, - "Checkin IKE SA %lld:%lld, role %s", - this->ike_sa_id->get_initiator_spi(this->ike_sa_id), - this->ike_sa_id->get_responder_spi(this->ike_sa_id), - this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); - - status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); - } - - return SUCCESS; + ike_sa->send_keepalive(ike_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; } /** diff --git a/src/charon/queues/send_queue.c b/src/charon/queues/send_queue.c index 6a55d96ab..f3a6b0f12 100644 --- a/src/charon/queues/send_queue.c +++ b/src/charon/queues/send_queue.c @@ -25,6 +25,7 @@ #include "send_queue.h" #include <utils/linked_list.h> +#include <utils/logger_manager.h> typedef struct private_send_queue_t private_send_queue_t; @@ -54,8 +55,12 @@ struct private_send_queue_t { * This condvar is used to wake up such a thread */ pthread_cond_t condvar; -}; + /** + * Logger reference + */ + logger_t *logger; +}; /** * implements send_queue_t.get_count @@ -100,6 +105,14 @@ static packet_t *get(private_send_queue_t *this) */ static void add(private_send_queue_t *this, packet_t *packet) { + host_t *src, *dst; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + this->logger->log(this->logger, CONTROL, "sending packet: from %s:%d to %s:%d", + src->get_address(src), src->get_port(src), + dst->get_address(dst), dst->get_port(dst)); + pthread_mutex_lock(&(this->mutex)); this->list->insert_last(this->list,packet); pthread_cond_signal( &(this->condvar)); @@ -148,6 +161,7 @@ send_queue_t *send_queue_create(void) this->list = linked_list_create(); pthread_mutex_init(&(this->mutex), NULL); pthread_cond_init(&(this->condvar), NULL); + this->logger = logger_manager->get_logger(logger_manager, SOCKET); return (&this->public); } diff --git a/src/charon/sa/authenticator.c b/src/charon/sa/authenticator.c index aefd1e941..029dd8ea9 100644 --- a/src/charon/sa/authenticator.c +++ b/src/charon/sa/authenticator.c @@ -47,7 +47,7 @@ struct private_authenticator_t { /** * Assigned IKE_SA. Needed to get objects of type prf_t and logger_t. */ - protected_ike_sa_t *ike_sa; + ike_sa_t *ike_sa; /** * PRF taken from the IKE_SA. @@ -404,7 +404,7 @@ static void destroy (private_authenticator_t *this) /* * Described in header. */ -authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa) +authenticator_t *authenticator_create(ike_sa_t *ike_sa) { private_authenticator_t *this = malloc_thing(private_authenticator_t); diff --git a/src/charon/sa/authenticator.h b/src/charon/sa/authenticator.h index b6bc317ac..143cdb6fd 100644 --- a/src/charon/sa/authenticator.h +++ b/src/charon/sa/authenticator.h @@ -120,19 +120,12 @@ struct authenticator_t { /** * @brief Creates an authenticator object. * - * @warning: The following functions of the assigned protected_ike_sa_t object - * must return a valid value: - * - protected_ike_sa_t.get_policy - * - protected_ike_sa_t.get_prf - * - protected_ike_sa_t.get_logger - * This preconditions are not given in IKE_SA states INITIATOR_INIT or RESPONDER_INIT! - * - * @param ike_sa object of type protected_ike_sa_t + * @param ike_sa associated ike_sa * * @return authenticator_t object * * @ingroup sa */ -authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa); +authenticator_t *authenticator_create(ike_sa_t *ike_sa); #endif /* AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 895c9676e..5ff67be9a 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -858,6 +858,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; 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.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,int,int))update_hosts; this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; this->public.set_rekeyed = (void (*)(child_sa_t*))set_rekeyed; diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 4bff80846..863a32994 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -31,7 +31,6 @@ #include <definitions.h> #include <utils/linked_list.h> #include <utils/logger_manager.h> -#include <utils/randomizer.h> #include <crypto/diffie_hellman.h> #include <crypto/prf_plus.h> #include <crypto/crypters/crypter.h> @@ -43,16 +42,27 @@ #include <encoding/payloads/transform_substructure.h> #include <encoding/payloads/transform_attribute.h> #include <encoding/payloads/ts_payload.h> -#include <sa/states/initiator_init.h> -#include <sa/states/responder_init.h> -#include <sa/states/create_child_sa_requested.h> -#include <sa/states/delete_child_sa_requested.h> -#include <sa/states/delete_ike_sa_requested.h> +#include <sa/transactions/transaction.h> +#include <sa/transactions/ike_sa_init.h> +#include <sa/transactions/delete_ike_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> #include <queues/jobs/delete_half_open_ike_sa_job.h> +#include <queues/jobs/send_dpd_job.h> +#include <queues/jobs/send_keepalive_job.h> +/** + * String mappings for ike_sa_state_t. + */ +mapping_t ike_sa_state_m[] = { + {SA_CREATED, "CREATED"}, + {SA_CONNECTING, "CONNECTING"}, + {SA_ESTABLISHED, "ESTABLISHED"}, + {SA_DELETING, "DELETING"}, + {MAPPING_END, NULL} +}; typedef struct private_ike_sa_t private_ike_sa_t; @@ -63,19 +73,9 @@ typedef struct private_ike_sa_t private_ike_sa_t; struct private_ike_sa_t { /** - * Protected part of a ike_sa_t object. + * Public members */ - protected_ike_sa_t protected; - - /** - * Update a timestamp on ike traffic - */ - void (*update_timestamp)(private_ike_sa_t *this, bool in); - - /** - * Returns the time since last traffic on kernel policies - */ - struct timeval (*get_last_esp_traffic_tv)(private_ike_sa_t * this, bool inbound); + ike_sa_t public; /** * Identifier for the current IKE_SA. @@ -88,18 +88,9 @@ struct private_ike_sa_t { linked_list_t *child_sas; /** - * Current state of the IKE_SA represented as state_t object. - * - * A state object representates one of the following states and is processing - * messages in the specific state: - * - INITIATOR_INIT - * - RESPONDER_INIT - * - IKE_SA_INIT_REQUESTED - * - IKE_SA_INIT_RESPONDED - * - IKE_AUTH_REQUESTED - * -IKE_SA_ESTABLISHED + * Current state of the IKE_SA */ - state_t *current_state; + ike_sa_state_t state; /** * Connection definition used for this IKE_SA @@ -112,41 +103,24 @@ struct private_ike_sa_t { policy_t *policy; /** - * This SA's source for random data. - * - * Is available in every state. - */ - randomizer_t *randomizer; - - /** - * The last responded message. - */ - message_t *last_responded_message; - - /** - * The ast requested message. - */ - message_t *last_requested_message; - - /** - * Crypter object for initiator. + * crypter for inbound traffic */ - crypter_t *crypter_initiator; + crypter_t *crypter_in; /** - * Crypter object for responder. + * crypter for outbound traffic */ - crypter_t *crypter_responder; + crypter_t *crypter_out; /** - * Signer object for initiator. + * Signer for inbound traffic */ - signer_t *signer_initiator; + signer_t *signer_in; /** - * Signer object for responder. + * Signer for outbound traffic */ - signer_t *signer_responder; + signer_t *signer_out; /** * Multi purpose prf, set key, use it, forget it @@ -167,21 +141,6 @@ struct private_ike_sa_t { * PRF, with key set to pr_key, used for authentication */ prf_t *prf_auth_r; - - /** - * Next message id to receive. - */ - u_int32_t message_id_in; - - /** - * Next message id to send. - */ - u_int32_t message_id_out; - - /** - * Last reply id which was successfully received. - */ - int32_t last_replied_message_id; /** * A logger for this IKE_SA. @@ -202,236 +161,819 @@ struct private_ike_sa_t { * NAT status of remote host. */ bool nat_there; + + /** + * message ID for next outgoung request + */ + u_int32_t message_id_out; /** * Timestamp of last IKE message received on this SA */ - struct timeval last_msg_in_tv; + time_t time_inbound; /** * Timestamp of last IKE message sent on this SA */ - struct timeval last_msg_out_tv; + time_t time_outbound; + + /** + * List of queued transactions to process + */ + linked_list_t *transaction_queue; + + /** + * Transaction currently initiated + * (only one supported yet, window size = 1) + */ + transaction_t *transaction_out; + + /** + * last transaction initiated by peer processed. + * (only one supported yet, window size = 1) + * Stored for retransmission. + */ + transaction_t *transaction_in; + + /** + * Next incoming transaction expected. Used to + * do multi transaction operations. + */ + transaction_t *transaction_in_next; +}; + +/** + * get the time of the latest traffic processed by the kernel + */ +static time_t get_esp_time(private_ike_sa_t* this, bool inbound) +{ + iterator_t *iterator; + child_sa_t *child_sa; + time_t latest = 0, use_time; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS) + { + latest = max(latest, use_time); + } + } + iterator->destroy(iterator); + + return latest; +} + +/** + * get the time of the latest received traffice + */ +static time_t get_time_inbound(private_ike_sa_t *this) +{ + return max(this->time_inbound, get_esp_time(this, TRUE)); +} +/** + * get the time of the latest sent traffic + */ +static time_t get_time_outbound(private_ike_sa_t *this) +{ + return max(this->time_outbound, get_esp_time(this, FALSE)); +} + + +/** + * Update connection host, as addresses may change (NAT) + */ +static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) +{ /* - * Message ID of last DPD message + * Quoting RFC 4306: + * + * 2.11. Address and Port Agility + * + * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and + * AH associations for the same IP addresses it runs over. The IP + * addresses and ports in the outer header are, however, not themselves + * cryptographically protected, and IKE is designed to work even through + * Network Address Translation (NAT) boxes. An implementation MUST + * accept incoming requests even if the source port is not 500 or 4500, + * and MUST respond to the address and port from which the request was + * received. It MUST specify the address and port at which the request + * was received as the source address and port in the response. IKE + * functions identically over IPv4 or IPv6. + * + * [...] + * + * There are cases where a NAT box decides to remove mappings that + * are still alive (for example, the keepalive interval is too long, + * or the NAT box is rebooted). To recover in these cases, hosts + * that are not behind a NAT SHOULD send all packets (including + * retransmission packets) to the IP address and port from the last + * valid authenticated packet from the other end (i.e., dynamically + * update the address). A host behind a NAT SHOULD NOT do this + * because it opens a DoS attack possibility. Any authenticated IKE + * packet or any authenticated UDP-encapsulated ESP packet can be + * used to detect that the IP address or the port has changed. */ - u_int32_t last_dpd_message_id; -}; + host_t *old_other = NULL; + iterator_t *iterator = NULL; + child_sa_t *child_sa = NULL; + int my_changes, other_changes; + + my_changes = me->get_differences(me, this->connection->get_my_host(this->connection)); + + old_other = this->connection->get_other_host(this->connection); + other_changes = other->get_differences(other, old_other); + + if (!my_changes && !other_changes) + { + return; + } + + if (my_changes) + { + this->connection->update_my_host(this->connection, me->clone(me)); + } + + if (!this->nat_here) + { + /* update without restrictions if we are not NATted */ + if (other_changes) + { + this->connection->update_other_host(this->connection, other->clone(other)); + } + } + else + { + /* if we are natted, only port may change */ + if (other_changes & HOST_DIFF_ADDR) + { + return; + } + else if (other_changes & HOST_DIFF_PORT) + { + old_other->set_port(old_other, other->get_port(other)); + } + } + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_sa->update_hosts(child_sa, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + my_changes, other_changes); + /* TODO: what to do if update fails? Delete CHILD_SA? */ + } + iterator->destroy(iterator); +} /** - * Implementation of protected_ike_sa_t.build_message. + * send a request and schedule retransmission */ -static void build_message(private_ike_sa_t *this, exchange_type_t type, bool request, message_t **message) +static status_t transmit_request(private_ike_sa_t *this) { - message_t *new_message; - host_t *me, *other; + message_t *request; + packet_t *packet; + status_t status; + retransmit_request_job_t *job; + u_int32_t transmitted; + u_int32_t timeout; + transaction_t *transaction = this->transaction_out; + u_int32_t message_id = transaction->get_message_id(transaction); + + transmitted = transaction->requested(transaction); + timeout = charon->configuration->get_retransmit_timeout(charon->configuration, + transmitted); + if (timeout == 0) + { + this->logger->log(this->logger, ERROR, + "giving up after %d retransmits, deleting IKE_SA", + transmitted - 1); + return DESTROY_ME; + } - me = this->connection->get_my_host(this->connection); - other = this->connection->get_other_host(this->connection); + status = transaction->get_request(transaction, &request); + if (status != SUCCESS) + { + return status; + } + /* if we retransmit, the request is already generated */ + if (transmitted == 0) + { + status = request->generate(request, this->crypter_out, this->signer_out, &packet); + if (status != SUCCESS) + { + return FAILED; + } + } + else + { + this->logger->log(this->logger, CONTROL, + "sending retransmit %d for %s request with message ID %d", + transmitted, + mapping_find(exchange_type_m, request->get_exchange_type(request)), + message_id); + packet = request->get_packet(request); + } + /* finally send */ + charon->send_queue->add(charon->send_queue, packet); + this->time_outbound = time(NULL); + + /* schedule retransmission job */ + job = retransmit_request_job_create(message_id, this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout); + return SUCCESS; +} + +/** + * Implementation of ike_sa.retransmit_request. + */ +static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) +{ + if (this->transaction_out == NULL || + this->transaction_out->get_message_id(this->transaction_out) != message_id) + { + /* no retransmit necessary, transaction did already complete */ + return SUCCESS; + } + return transmit_request(this); +} - this->logger->log(this->logger, CONTROL|LEVEL2, "build empty message"); - new_message = message_create(); - new_message->set_source(new_message, me->clone(me)); - new_message->set_destination(new_message, other->clone(other)); - new_message->set_exchange_type(new_message, type); - new_message->set_request(new_message, request); - new_message->set_message_id(new_message, (request) ? this->message_id_out : this->message_id_in); - new_message->set_ike_sa_id(new_message, this->ike_sa_id); +/** + * Check for transactions in the queue and initiate the first transaction found. + */ +static status_t process_transaction_queue(private_ike_sa_t *this) +{ + if (this->transaction_out) + { + /* already a transaction in progress */ + return SUCCESS; + } + + while (TRUE) + { + if (this->transaction_queue->remove_first(this->transaction_queue, + (void**)&this->transaction_out) != SUCCESS) + { + /* transaction queue empty */ + return SUCCESS; + } + switch (transmit_request(this)) + { + case SUCCESS: + return SUCCESS; + case DESTROY_ME: + /* critical, IKE_SA unusable, destroy immediately */ + this->logger->log(this->logger, ERROR, + "transaction initiaton failed, deleting IKE_SA"); + return DESTROY_ME; + default: + /* discard transaction, process next one */ + this->logger->log(this->logger, ERROR, + "transaction initiation failed, discarded"); + this->transaction_out->destroy(this->transaction_out); + this->transaction_out = NULL; + /* handle next transaction */ + continue; + } + } +} - *message = new_message; +/** + * Queue a new transaction and execute the next outstanding transaction + */ +static status_t queue_transaction(private_ike_sa_t *this, transaction_t *transaction, bool prefer) +{ + /* inject next transaction */ + if (transaction) + { + if (prefer) + { + this->transaction_queue->insert_first(this->transaction_queue, transaction); + } + else + { + this->transaction_queue->insert_last(this->transaction_queue, transaction); + } + } + /* process a transaction */ + return process_transaction_queue(this); } /** - * Implementation of ike_sa_t.get_state. + * process an incoming request. */ -static ike_sa_state_t get_state(private_ike_sa_t *this) +static status_t process_request(private_ike_sa_t *this, message_t *request) { - return this->current_state->get_state(this->current_state); + transaction_t *last, *current = NULL; + message_t *response; + packet_t *packet; + u_int32_t request_mid; + status_t status; + + request_mid = request->get_message_id(request); + last = this->transaction_in; + + /* check if message ID is correct */ + if (last) + { + u_int32_t last_mid = last->get_message_id(last); + + if (last_mid == request_mid) + { + /* retransmit detected */ + this->logger->log(this->logger, ERROR, + "received retransmitted request for message ID %d, retransmitting response", + request_mid); + last->get_response(last, request, &response, &this->transaction_in_next); + packet = response->get_packet(response); + charon->send_queue->add(charon->send_queue, packet); + this->time_outbound = time(NULL); + return SUCCESS; + } + + if (last_mid > request_mid) + { + /* something seriously wrong here, message id may not decrease */ + this->logger->log(this->logger, ERROR, + "received request with message ID %d, excepted %d, ingored", + request_mid, last_mid + 1); + return FAILED; + } + /* we allow jumps in message IDs, as long as they are incremental */ + if (last_mid + 1 < request_mid) + { + this->logger->log(this->logger, ERROR, + "received request with message ID %d, excepted %d", + request_mid, last_mid + 1); + } + } + else + { + if (request_mid != 0) + { + /* warn, but allow it */ + this->logger->log(this->logger, CONTROL, + "first received request has message ID %d, excepted 0", + request_mid); + } + } + + /* check if we already have a pre-created transaction for this request */ + if (this->transaction_in_next) + { + u_int32_t trans_mid = this->transaction_in_next->get_message_id(this->transaction_in_next); + + /* check message id consistency */ + if (trans_mid == request_mid) + { + /* use it */ + current = this->transaction_in_next; + } + else + { + /* discard queued transaction */ + this->transaction_in_next->destroy(this->transaction_in_next); + } + this->transaction_in_next = NULL; + } + /* create new transaction if "next" unusable */ + if (current == NULL) + { + current = transaction_create(&this->public, request); + if (current == NULL) + { + this->logger->log(this->logger, ERROR, + "no idea how to handle received message (%d), ignored", + request->get_exchange_type(request)); + return FAILED; + } + } + + /* send message. get_request() always gives a valid response */ + status = current->get_response(current, request, &response, &this->transaction_in_next); + if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "response generation failed, discarding transaction"); + current->destroy(current); + return FAILED; + } + + charon->send_queue->add(charon->send_queue, packet); + this->time_outbound = time(NULL); + /* act depending on transaction result */ + switch (status) + { + case DESTROY_ME: + /* transactions says we should destroy the IKE_SA, so do it */ + current->destroy(current); + return DESTROY_ME; + default: + /* store for retransmission, destroy old transaction */ + this->transaction_in = current; + if (last) + { + last->destroy(last); + } + return SUCCESS; + } } + +/** + * process an incoming response + */ +static status_t process_response(private_ike_sa_t *this, message_t *response) +{ + transaction_t *current, *new = NULL; + current = this->transaction_out; + /* check if message ID is that of our currently active transaction */ + if (current == NULL || + current->get_message_id(current) != + response->get_message_id(response)) + { + this->logger->log(this->logger, ERROR, + "received response with message ID %d not requested, ignored"); + return FAILED; + } + + switch (current->conclude(current, response, &new)) + { + case DESTROY_ME: + /* state requested to destroy IKE_SA */ + return DESTROY_ME; + default: + /* discard transaction, process next one */ + break; + } + /* transaction comleted, remove */ + current->destroy(current); + this->transaction_out = NULL; + + /* queue new transaction */ + return queue_transaction(this, new, TRUE); +} + /** - * Implementation of protected_ike_sa_t.set_new_state. + * send a notify back to the sender */ -static void set_new_state(private_ike_sa_t *this, state_t *state) +static void send_notify_response(private_ike_sa_t *this, + message_t *request, + notify_type_t type) { - this->logger->log(this->logger, CONTROL, "state change: %s => %s", - mapping_find(ike_sa_state_m, get_state(this)), - mapping_find(ike_sa_state_m, state->get_state(state))); - this->current_state = state; + notify_payload_t *notify; + message_t *response; + host_t *src, *dst; + packet_t *packet; + + response = message_create(); + dst = request->get_source(request); + src = request->get_destination(request); + response->set_source(response, src->clone(src)); + response->set_destination(response, dst->clone(dst)); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, request->get_message_id(request)); + response->set_ike_sa_id(response, this->ike_sa_id); + notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); + response->add_payload(response, (payload_t *)notify); + if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS) + { + response->destroy(response); + return; + } + charon->send_queue->add(charon->send_queue, packet); + this->time_outbound = time(NULL); + response->destroy(response); + return; } + /** - * Implementation of protected_ike_sa_t.get_connection. + * Implementation of ike_sa_t.process_message. */ -static connection_t *get_connection(private_ike_sa_t *this) +static status_t process_message(private_ike_sa_t *this, message_t *message) { - return this->connection; + status_t status; + bool is_request; + + is_request = message->get_request(message); + + status = message->parse_body(message, this->crypter_in, this->signer_in); + if (status != SUCCESS) + { + switch (status) + { + case NOT_SUPPORTED: + this->logger->log(this->logger, ERROR, + "ciritcal unknown payloads found"); + if (is_request) + { + send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD); + } + break; + case PARSE_ERROR: + this->logger->log(this->logger, ERROR, + "message parsing failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case VERIFY_ERROR: + this->logger->log(this->logger, ERROR, + "message verification failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case FAILED: + this->logger->log(this->logger, ERROR, + "integrity check failed"); + /* ignored */ + break; + case INVALID_STATE: + this->logger->log(this->logger, ERROR, + "found encrypted message, but no keys available"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + default: + break; + } + this->logger->log(this->logger, ERROR, + "%s %s with message ID %d processing failed", + mapping_find(exchange_type_m, message->get_exchange_type(message)), + message->get_request(message) ? "request" : "response", + message->get_message_id(message)); + } + else + { + /* check if message is trustworthy, and update connection information */ + if ((this->state == SA_CREATED && this->connection) || + message->get_exchange_type(message) != IKE_SA_INIT) + { + update_hosts(this, message->get_destination(message), + message->get_source(message)); + this->time_inbound = time(NULL); + } + if (is_request) + { + status = process_request(this, message); + } + else + { + status = process_response(this, message); + } + } + return status; } /** - * Implementation of protected_ike_sa_t.set_connection. + * Implementation of ike_sa_t.initiate. */ -static void set_connection(private_ike_sa_t *this,connection_t * connection) +static status_t initiate(private_ike_sa_t *this, connection_t *connection) { + ike_sa_init_t *ike_sa_init; + + /* set connection and policy */ this->connection = connection; + this->policy = charon->policies->get_policy_by_name(charon->policies, + this->connection->get_name(this->connection)); + if (this->policy == NULL) + { + this->logger->log(this->logger, ERROR, + "no policy found for connection %s, aborting", + connection->get_name(connection)); + return DESTROY_ME; + } + ike_sa_init = ike_sa_init_create(&this->public, 0); + this->message_id_out = 2; + return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); } /** - * Implementation of protected_ike_sa_t.get_policy. + * Implementation of ike_sa_t.send_dpd */ -static policy_t *get_policy(private_ike_sa_t *this) +static status_t send_dpd(private_ike_sa_t *this) { - return this->policy; + send_dpd_job_t *job; + time_t diff, interval; + status_t status = SUCCESS; + + interval = charon->configuration->get_dpd_interval(charon->configuration); + + if (this->transaction_out) + { + /* there is a transaction in progress. Come back later */ + diff = 0; + } + else + { + /* check if there was any inbound traffic */ + time_t last_in, now; + last_in = get_time_inbound(this); + now = time(NULL); + diff = now - last_in; + if (diff >= interval) + { + /* to long ago, initiate dead peer detection */ + dead_peer_detection_t *dpd; + this->logger->log(this->logger, CONTROL, "sending DPD request"); + dpd = dead_peer_detection_create(&this->public, this->message_id_out++); + status = queue_transaction(this, (transaction_t*)dpd, FALSE); + diff = 0; + } + } + /* recheck in "interval" seconds */ + job = send_dpd_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (interval - diff) * 1000); + return SUCCESS; } /** - * Implementation of protected_ike_sa_t.set_policy. + * Implementation of ike_sa_t.send_keepalive */ -static void set_policy(private_ike_sa_t *this,policy_t * policy) +static void send_keepalive(private_ike_sa_t *this) { - this->policy = policy; + send_keepalive_job_t *job; + time_t last_out, now, diff, interval; + + last_out = get_time_outbound(this); + now = time(NULL); + + diff = now - last_out; + interval = charon->configuration->get_keepalive_interval(charon->configuration); + + if (diff >= interval) + { + host_t *me, *other; + packet_t *packet; + chunk_t data; + + packet = packet_create(); + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + packet->set_source(packet, me->clone(me)); + packet->set_destination(packet, other->clone(other)); + data.ptr = malloc(1); + data.ptr[0] = 0xFF; + data.len = 1; + packet->set_data(packet, data); + charon->send_queue->add(charon->send_queue, packet); + this->logger->log(this->logger, CONTROL, "sending keep alive"); + diff = 0; + } + job = send_keepalive_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (interval - diff) * 1000); } /** - * Implementation of protected_ike_sa_t.get_prf. + * Implementation of ike_sa_t.get_state. */ -static prf_t *get_prf(private_ike_sa_t *this) +static ike_sa_state_t get_state(private_ike_sa_t *this) { - return this->prf; + return this->state; } /** - * Implementation of protected_ike_sa_t.get_prf. + * Implementation of ike_sa_t.set_state. */ -static prf_t *get_child_prf(private_ike_sa_t *this) +static void set_state(private_ike_sa_t *this, ike_sa_state_t state) { - return this->child_prf; + this->logger->log(this->logger, CONTROL, "state change: %s => %s", + mapping_find(ike_sa_state_m, this->state), + mapping_find(ike_sa_state_m, state)); + if (state == SA_ESTABLISHED) + { + host_t *my_host, *other_host; + identification_t *my_id, *other_id; + my_host = this->connection->get_my_host(this->connection); + other_host = this->connection->get_other_host(this->connection); + my_id = this->policy->get_my_id(this->policy); + other_id = this->policy->get_other_id(this->policy); + this->logger->log(this->logger, AUDIT, "IKE_SA established: %s[%s]...%s[%s]", + my_host->get_address(my_host), + my_id->get_string(my_id), + other_host->get_address(other_host), + other_id->get_string(other_id)); + + send_dpd(this); + } + this->state = state; } /** - * Implementation of protected_ike_sa_t.get_prf_auth_i. + * Implementation of protected_ike_sa_t.get_connection. */ -static prf_t *get_prf_auth_i(private_ike_sa_t *this) +static connection_t *get_connection(private_ike_sa_t *this) { - return this->prf_auth_i; + return this->connection; } /** - * Implementation of protected_ike_sa_t.get_prf_auth_r. + * Implementation of protected_ike_sa_t.set_connection. */ -static prf_t *get_prf_auth_r(private_ike_sa_t *this) +static void set_connection(private_ike_sa_t *this,connection_t * connection) { - return this->prf_auth_r; + this->connection = connection; } + /** - * Implementation of ike_sa_t.get_id. + * Implementation of protected_ike_sa_t.get_policy. */ -static ike_sa_id_t* get_id(private_ike_sa_t *this) +static policy_t *get_policy(private_ike_sa_t *this) { - return this->ike_sa_id; + return this->policy; } /** - * Implementation of ike_sa_t.get_my_host. + * Implementation of protected_ike_sa_t.set_policy. */ -static host_t* get_my_host(private_ike_sa_t *this) +static void set_policy(private_ike_sa_t *this,policy_t * policy) { - return this->connection->get_my_host(this->connection);; + this->policy = policy; } /** - * Implementation of ike_sa_t.get_other_host. + * Implementation of protected_ike_sa_t.get_prf. */ -static host_t* get_other_host(private_ike_sa_t *this) +static prf_t *get_prf(private_ike_sa_t *this) { - return this->connection->get_other_host(this->connection); + return this->prf; } /** - * Implementation of ike_sa_t.get_my_id. + * Implementation of protected_ike_sa_t.get_prf. */ -static identification_t* get_my_id(private_ike_sa_t *this) +static prf_t *get_child_prf(private_ike_sa_t *this) { - return this->policy->get_my_id(this->policy); + return this->child_prf; } /** - * Implementation of ike_sa_t.get_other_id. + * Implementation of protected_ike_sa_t.get_prf_auth_i. */ -static identification_t* get_other_id(private_ike_sa_t *this) +static prf_t *get_prf_auth_i(private_ike_sa_t *this) { - return this->policy->get_other_id(this->policy); + return this->prf_auth_i; } /** - * Implementation of ike_sa_t.retransmit_possible. + * Implementation of protected_ike_sa_t.get_prf_auth_r. */ -static bool retransmit_possible(private_ike_sa_t *this, u_int32_t message_id) +static prf_t *get_prf_auth_r(private_ike_sa_t *this) { - return ((this->last_requested_message) - && (message_id != this->last_replied_message_id) - && (message_id == this->last_requested_message->get_message_id( - this->last_requested_message))); + return this->prf_auth_r; } - /** - * Implementation of ike_sa_t.retransmit_request. + * Implementation of ike_sa_t.get_id. */ -static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) +static ike_sa_id_t* get_id(private_ike_sa_t *this) { - packet_t *packet; - - if (!this->protected.public.retransmit_possible(&this->protected.public, message_id)) - { - return NOT_FOUND; - } - - this->logger->log(this->logger, CONTROL | LEVEL1, "going to retransmit message with id %d",message_id); - packet = this->last_requested_message->get_packet(this->last_requested_message); - charon->send_queue->add(charon->send_queue, packet); - this->update_timestamp(this, FALSE); - return SUCCESS; + return this->ike_sa_id; } /** * Implementation of protected_ike_sa_t.build_transforms. */ -static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r) +static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, + diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, + bool initiator) { chunk_t nonces, nonces_spis, skeyseed, key, secret; u_int64_t spi_i, spi_r; prf_plus_t *prf_plus; algorithm_t *algo; size_t key_size; + crypter_t *crypter_i, *crypter_r; + signer_t *signer_i, *signer_r; - /* - * Build the PRF+ instance for deriving keys - */ - if (this->prf != NULL) - { - this->prf->destroy(this->prf); - } + /* Build the PRF+ instance for deriving keys */ if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo)) { - this->logger->log(this->logger, ERROR|LEVEL2, "no PRF algoithm selected!?"); + this->logger->log(this->logger, ERROR, "no PSEUDO_RANDOM_FUNCTION selected!"); return FAILED; } this->prf = prf_create(algo->algorithm); if (this->prf == NULL) { - this->logger->log(this->logger, ERROR|LEVEL1, - "PSEUDO_RANDOM_FUNCTION %s not supported!", + this->logger->log(this->logger, ERROR, "PSEUDO_RANDOM_FUNCTION %s not supported!", mapping_find(pseudo_random_function_m, algo->algorithm)); return FAILED; } - /* concatenate nonces = nonce_i | nonce_r */ + /* nonces = nonce_i | nonce_r */ nonces = chunk_alloc(nonce_i.len + nonce_r.len); memcpy(nonces.ptr, nonce_i.ptr, nonce_i.len); memcpy(nonces.ptr + nonce_i.len, nonce_r.ptr, nonce_r.len); - /* concatenate prf_seed = nonce_i | nonce_r | spi_i | spi_r */ + /* prf_seed = nonce_i | nonce_r | spi_i | spi_r */ nonces_spis = chunk_alloc(nonces.len + 16); memcpy(nonces_spis.ptr, nonces.ptr, nonces.len); spi_i = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); @@ -441,17 +983,15 @@ static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, d /* SKEYSEED = prf(Ni | Nr, g^ir) */ dh->get_shared_secret(dh, &secret); - this->logger->log_chunk(this->logger, PRIVATE, "shared Diffie-Hellman secret", secret); + this->logger->log_chunk(this->logger, PRIVATE, "shared Diffie Hellman secret", secret); this->prf->set_key(this->prf, nonces); this->prf->allocate_bytes(this->prf, secret, &skeyseed); - this->logger->log_chunk(this->logger, PRIVATE | LEVEL1, "SKEYSEED", skeyseed); + this->logger->log_chunk(this->logger, PRIVATE|LEVEL1, "SKEYSEED", skeyseed); chunk_free(&secret); /* prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr ) - * = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr - * - * we use the prf directly for prf+ - */ + * = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr + */ this->prf->set_key(this->prf, skeyseed); prf_plus = prf_plus_create(this->prf, nonces_spis); @@ -460,13 +1000,6 @@ static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, d chunk_free(&nonces_spis); chunk_free(&skeyseed); - - /* - * We now can derive all of our key. We build the transforms - * directly. - */ - - /* SK_d used for prf+ to derive keys for child SAs */ this->child_prf = prf_create(algo->algorithm); key_size = this->child_prf->get_key_size(this->child_prf); @@ -475,62 +1008,53 @@ static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, d this->child_prf->set_key(this->child_prf, key); chunk_free(&key); - /* SK_ai/SK_ar used for integrity protection */ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo)) { - this->logger->log(this->logger, ERROR, "no integrity algoithm selected?!"); + this->logger->log(this->logger, ERROR, "no INTEGRITY_ALGORITHM selected?!"); return FAILED; } - if (this->signer_initiator != NULL) - { - this->signer_initiator->destroy(this->signer_initiator); - } - if (this->signer_responder != NULL) - { - this->signer_responder->destroy(this->signer_responder); - } - this->signer_initiator = signer_create(algo->algorithm); - this->signer_responder = signer_create(algo->algorithm); - if (this->signer_initiator == NULL || this->signer_responder == NULL) + signer_i = signer_create(algo->algorithm); + signer_r = signer_create(algo->algorithm); + if (signer_i == NULL || signer_r == NULL) { - this->logger->log(this->logger, ERROR, - "INTEGRITY_ALGORITHM %s not supported!", + this->logger->log(this->logger, ERROR, "INTEGRITY_ALGORITHM %s not supported!", mapping_find(integrity_algorithm_m,algo->algorithm)); return FAILED; } - key_size = this->signer_initiator->get_key_size(this->signer_initiator); + key_size = signer_i->get_key_size(signer_i); prf_plus->allocate_bytes(prf_plus, key_size, &key); this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ai secret", key); - this->signer_initiator->set_key(this->signer_initiator, key); + signer_i->set_key(signer_i, key); chunk_free(&key); prf_plus->allocate_bytes(prf_plus, key_size, &key); this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ar secret", key); - this->signer_responder->set_key(this->signer_responder, key); + signer_r->set_key(signer_r, key); chunk_free(&key); - - /* SK_ei/SK_er used for encryption */ - if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) + if (initiator) { - this->logger->log(this->logger, ERROR, "no encryption algoithm selected!?"); - return FAILED; + this->signer_in = signer_i; + this->signer_out = signer_r; } - if (this->crypter_initiator != NULL) - { - this->crypter_initiator->destroy(this->crypter_initiator); - } - if (this->crypter_responder != NULL) + else { - this->crypter_responder->destroy(this->crypter_responder); + this->signer_in = signer_r; + this->signer_out = signer_i; } - this->crypter_initiator = crypter_create(algo->algorithm, algo->key_size / 8); - this->crypter_responder = crypter_create(algo->algorithm, algo->key_size / 8); - if (this->crypter_initiator == NULL || this->crypter_responder == NULL) + /* SK_ei/SK_er used for encryption */ + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) + { + this->logger->log(this->logger, ERROR, "no ENCRYPTION_ALGORITHM selected!"); + return FAILED; + } + crypter_i = crypter_create(algo->algorithm, algo->key_size / 8); + crypter_r = crypter_create(algo->algorithm, algo->key_size / 8); + if (crypter_i == NULL || crypter_r == NULL) { this->logger->log(this->logger, ERROR, "ENCRYPTION_ALGORITHM %s (key size %d) not supported!", @@ -538,28 +1062,30 @@ static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, d algo->key_size); return FAILED; } - key_size = this->crypter_initiator->get_key_size(this->crypter_initiator); + key_size = crypter_i->get_key_size(crypter_i); prf_plus->allocate_bytes(prf_plus, key_size, &key); this->logger->log_chunk(this->logger, PRIVATE, "Sk_ei secret", key); - this->crypter_initiator->set_key(this->crypter_initiator, key); + crypter_i->set_key(crypter_i, key); chunk_free(&key); prf_plus->allocate_bytes(prf_plus, key_size, &key); this->logger->log_chunk(this->logger, PRIVATE, "Sk_er secret", key); - this->crypter_responder->set_key(this->crypter_responder, key); + crypter_r->set_key(crypter_r, key); chunk_free(&key); - /* SK_pi/SK_pr used for authentication */ - if (this->prf_auth_i != NULL) + if (initiator) { - this->prf_auth_i->destroy(this->prf_auth_i); + this->crypter_in = crypter_i; + this->crypter_out = crypter_r; } - if (this->prf_auth_r != NULL) + else { - this->prf_auth_r->destroy(this->prf_auth_r); + this->crypter_in = crypter_r; + this->crypter_out = crypter_i; } + /* SK_pi/SK_pr used for authentication */ proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); this->prf_auth_i = prf_create(algo->algorithm); this->prf_auth_r = prf_create(algo->algorithm); @@ -582,260 +1108,6 @@ static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, d } /** - * Implementation of protected_ike_sa_t.get_randomizer. - */ -static randomizer_t *get_randomizer(private_ike_sa_t *this) -{ - return this->randomizer; -} - -/** - * Implementation of protected_ike_sa_t.get_crypter_initiator. - */ -static crypter_t *get_crypter_initiator(private_ike_sa_t *this) -{ - return this->crypter_initiator; -} - -/** - * Implementation of protected_ike_sa_t.get_signer_initiator. - */ -static signer_t *get_signer_initiator(private_ike_sa_t *this) -{ - return this->signer_initiator; -} - -/** - * Implementation of protected_ike_sa_t.get_crypter_responder. - */ -static crypter_t *get_crypter_responder(private_ike_sa_t *this) -{ - return this->crypter_responder; -} - -/** - * Implementation of protected_ike_sa_t.get_signer_responder. - */ -static signer_t *get_signer_responder(private_ike_sa_t *this) -{ - return this->signer_responder; -} - -/** - * Implementation of protected_ike_sa_t.update_timestamp - */ -static void update_timestamp(private_ike_sa_t *this, bool in) -{ - /* bump last message sent timestamp */ - struct timeval *tv = in ? &this->last_msg_in_tv : &this->last_msg_out_tv; - if (0 > gettimeofday(tv, NULL)) - { - this->logger->log(this->logger, ERROR|LEVEL1, - "warning: failed to get time of day."); - } -} - -/** - * Implementation of protected_ike_sa_t.send_request. - */ -static status_t send_request(private_ike_sa_t *this, message_t *message) -{ - retransmit_request_job_t *retransmit_job; - u_int32_t timeout; - crypter_t *crypter; - signer_t *signer; - packet_t *packet; - status_t status; - - if (message->get_message_id(message) != this->message_id_out) - { - this->logger->log(this->logger, ERROR, "message could not be sent cause id (%d) was not as expected (%d)", - message->get_message_id(message),this->message_id_out); - return FAILED; - } - - /* generate packet */ - this->logger->log(this->logger, CONTROL|LEVEL2, "generate packet from message"); - - if (this->ike_sa_id->is_initiator(this->ike_sa_id)) - { - crypter = this->crypter_initiator; - signer = this->signer_initiator; - } - else - { - crypter = this->crypter_responder; - signer =this->signer_responder; - } - - status = message->generate(message, crypter,signer, &packet); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "could not generate packet from message"); - return FAILED; - } - - this->logger->log(this->logger, CONTROL|LEVEL3, - "add request packet with message id %d to global send queue", - this->message_id_out); - charon->send_queue->add(charon->send_queue, packet); - - /* replace last message for retransmit with current */ - if (this->last_requested_message != NULL) - { - this->last_requested_message->destroy(this->last_requested_message); - } - this->logger->log(this->logger, CONTROL|LEVEL3, "replace last requested message with new one"); - this->last_requested_message = message; - - /* schedule a job for retransmission */ - status = charon->configuration->get_retransmit_timeout(charon->configuration, 0, &timeout); - if (status != SUCCESS) - { - this->logger->log(this->logger, CONTROL|LEVEL2, "no retransmit job for message created!"); - } - else - { - this->logger->log(this->logger, CONTROL|LEVEL2, "request will be retransmitted in %d ms.", timeout); - retransmit_job = retransmit_request_job_create(this->message_id_out, this->ike_sa_id); - charon->event_queue->add_relative(charon->event_queue, (job_t *)retransmit_job, timeout); - } - - /* message counter can now be increased */ - this->logger->log(this->logger, CONTROL|LEVEL3, - "increase message counter for outgoing messages from %d", - this->message_id_out); - this->message_id_out++; - - this->update_timestamp(this, FALSE); - return SUCCESS; -} - -/** - * Implementation of protected_ike_sa_t.send_response. - */ -static status_t send_response(private_ike_sa_t *this, message_t *message) -{ - crypter_t *crypter; - signer_t *signer; - packet_t *packet; - status_t status; - - if (message->get_message_id(message) != this->message_id_in) - { - - this->logger->log(this->logger, ERROR, "message could not be sent cause id (%d) was not as expected (%d)", - message->get_message_id(message),this->message_id_in); - return FAILED; - } - - if (this->ike_sa_id->is_initiator(this->ike_sa_id)) - { - crypter = this->crypter_initiator; - signer = this->signer_initiator; - } - else - { - crypter = this->crypter_responder; - signer =this->signer_responder; - } - - status = message->generate(message, crypter,signer, &packet); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "could not generate packet from message"); - return FAILED; - } - - this->logger->log(this->logger, CONTROL|LEVEL3, - "add response packet with message id %d to global send queue", - this->message_id_in); - charon->send_queue->add(charon->send_queue, packet); - - if (this->last_responded_message != NULL) - { - /* destroy message */ - this->last_responded_message->destroy(this->last_responded_message); - } - - this->logger->log(this->logger, CONTROL|LEVEL3, "replace last responded message with new one"); - this->last_responded_message = message; - - /* message counter can now be increased */ - this->logger->log(this->logger, CONTROL|LEVEL3, "increase message counter for incoming messages"); - this->message_id_in++; - - this->update_timestamp(this, FALSE); - - return SUCCESS; -} - -/** - * Implementation of of private_responder_init_t.send_notify_reply. - */ -static void send_notify(private_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data) -{ - notify_payload_t *payload; - message_t *response; - packet_t *packet; - status_t status; - - this->logger->log(this->logger, CONTROL|LEVEL2, "going to build message with notify payload"); - /* set up the reply */ - build_message(this, exchange_type, FALSE, &response); - payload = notify_payload_create_from_protocol_and_type(PROTO_NONE, type); - if ((data.ptr != NULL) && (data.len > 0)) - { - this->logger->log(this->logger, CONTROL|LEVEL2, "add Data to notify payload"); - payload->set_notification_data(payload,data); - } - - this->logger->log(this->logger, CONTROL|LEVEL2, "add Notify payload to message"); - response->add_payload(response,(payload_t *) payload); - - /* generate packet */ - this->logger->log(this->logger, CONTROL|LEVEL2, "generate packet from message"); - status = response->generate(response, this->crypter_responder, this->signer_responder, &packet); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR|LEVEL1, "could not generate notify message"); - response->destroy(response); - return; - } - - this->logger->log(this->logger, CONTROL|LEVEL2, "add packet to global send queue"); - charon->send_queue->add(charon->send_queue, packet); - this->logger->log(this->logger, CONTROL|LEVEL2, "destroy message"); - response->destroy(response); - - this->update_timestamp(this, FALSE); -} - -/** - * Implementation of protected_ike_sa_t.set_last_replied_message_id. - */ -static void set_last_replied_message_id (private_ike_sa_t *this,u_int32_t message_id) -{ - this->last_replied_message_id = message_id; -} - -/** - * Implementation of protected_ike_sa_t.get_last_responded_message. - */ -static message_t *get_last_responded_message (private_ike_sa_t *this) -{ - return this->last_responded_message; -} - -/** - * Implementation of protected_ike_sa_t.get_last_requested_message. - */ -static message_t *get_last_requested_message(private_ike_sa_t *this) -{ - return this->last_requested_message; -} - -/** * Implementation of protected_ike_sa_t.add_child_sa. */ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) @@ -844,306 +1116,39 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) } /** - * Implementation of ike_sa_t.process_message. - */ -static status_t process_message(private_ike_sa_t *this, message_t *message) -{ - u_int32_t message_id; - exchange_type_t exchange_type; - bool is_request; - - /* Find out type of message (request or response) */ - is_request = message->get_request(message); - exchange_type = message->get_exchange_type(message); - - this->logger->log(this->logger, CONTROL|LEVEL1, "process %s of exchange type %s", - (is_request) ? "request" : "response", - mapping_find(exchange_type_m, exchange_type)); - - message_id = message->get_message_id(message); - - /* check if message already received, and retransmit its reply */ - if (is_request && (message_id == (this->message_id_in - 1))) - { - /* resend last message, if any */ - if (this->last_responded_message) - { - packet_t *packet = this->last_responded_message->get_packet(this->last_responded_message); - this->logger->log(this->logger, CONTROL|LEVEL1, "resent request detected. Send stored reply."); - charon->send_queue->add(charon->send_queue, packet); - this->update_timestamp(this, FALSE); - return SUCCESS; - } - else - { - /* somebody does something nasty here... */ - return FAILED; - } - } - - /* Now, the message id is checked for request AND reply */ - if (is_request) - { - /* In a request, the message has to be this->message_id_in (other case is already handled) */ - if (message_id != this->message_id_in) - { - this->logger->log(this->logger, ERROR | LEVEL1, - "message request with message id %d received, but %d expected", - message_id,this->message_id_in); - return FAILED; - } - } - else - { - /* In a reply, the message has to be this->message_id_out -1 cause it is the reply to the last sent message*/ - if (message_id != (this->message_id_out - 1)) - { - this->logger->log(this->logger, ERROR | LEVEL1, - "message reply with message id %d received, but %d expected", - message_id,this->message_id_in); - return FAILED; - } - } - - this->update_timestamp(this, TRUE); - - /* now the message is processed by the current state object. - * The specific state object is responsible to check if a message can be received in - * the state it represents. - * The current state is also responsible to change the state object to the next state - * by calling protected_ike_sa_t.set_new_state - */ - return this->current_state->process_message(this->current_state, message); -} - -/** - * Implementation of protected_ike_sa_t.initiate_connection. - */ -static status_t initiate_connection(private_ike_sa_t *this, connection_t *connection) -{ - initiator_init_t *current_state; - - /* Work is done in state object of type INITIATOR_INIT. All other states are not - * initial states and so don't have a initiate_connection function */ - - if (this->current_state->get_state(this->current_state) != INITIATOR_INIT) - { - return FAILED; - } - - current_state = (initiator_init_t *) this->current_state; - - return current_state->initiate_connection(current_state, connection); -} - -/** - * Implementation of protected_ike_sa_t.update_connection_hosts. - * - * Quoting RFC 4306: - * - * 2.11. Address and Port Agility - * - * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and - * AH associations for the same IP addresses it runs over. The IP - * addresses and ports in the outer header are, however, not themselves - * cryptographically protected, and IKE is designed to work even through - * Network Address Translation (NAT) boxes. An implementation MUST - * accept incoming requests even if the source port is not 500 or 4500, - * and MUST respond to the address and port from which the request was - * received. It MUST specify the address and port at which the request - * was received as the source address and port in the response. IKE - * functions identically over IPv4 or IPv6. - * - * [...] - * - * There are cases where a NAT box decides to remove mappings that - * are still alive (for example, the keepalive interval is too long, - * or the NAT box is rebooted). To recover in these cases, hosts - * that are not behind a NAT SHOULD send all packets (including - * retransmission packets) to the IP address and port from the last - * valid authenticated packet from the other end (i.e., dynamically - * update the address). A host behind a NAT SHOULD NOT do this - * because it opens a DoS attack possibility. Any authenticated IKE - * packet or any authenticated UDP-encapsulated ESP packet can be - * used to detect that the IP address or the port has changed. - */ -static status_t update_connection_hosts(private_ike_sa_t *this, host_t *me, host_t *other) -{ - host_t *old_other = NULL; - iterator_t *iterator = NULL; - child_sa_t *child_sa = NULL; - int my_changes, other_changes; - ike_sa_state_t s; - - my_changes = me->get_differences(me, this->connection->get_my_host(this->connection)); - - old_other = this->connection->get_other_host(this->connection); - other_changes = other->get_differences(other, old_other); - - if (!my_changes && !other_changes) { - return SUCCESS; - } - - if (my_changes) - { - this->connection->update_my_host(this->connection, me->clone(me)); - } - - s = this->protected.public.get_state(&this->protected.public); - - if (s == RESPONDER_INIT || s == IKE_SA_INIT_REQUESTED || !this->nat_here) - { - if (other_changes) - { - this->connection->update_other_host(this->connection, other->clone(other)); - } - } - else - { - if (other_changes & HOST_DIFF_ADDR) - { - this->logger->log(this->logger, ERROR|LEVEL1, - "destination ip changed from %s to %s. As we are NATed this is not allowed!", - old_other->get_address(old_other), other->get_address(other)); - return DESTROY_ME; - } - else if (other_changes & HOST_DIFF_PORT) - { - old_other->set_port(old_other, other->get_port(other)); - } - } - - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)&child_sa)) - { - child_sa->update_hosts(child_sa, - this->connection->get_my_host(this->connection), - this->connection->get_other_host(this->connection), - my_changes, other_changes); - /* XXX error handling */ - } - iterator->destroy(iterator); - - return SUCCESS; -} - -/** - * Implementation of protected_ike_sa_t.build_transforms. - * TODO: IPv6 support. - */ -static chunk_t generate_natd_hash(private_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host) -{ - chunk_t natd_string; - chunk_t natd_hash; - void *p; - struct sockaddr_in* sai; - char buf[512]; - - natd_hash = chunk_alloc(this->nat_hasher->get_hash_size(this->nat_hasher)); - natd_string = chunk_alloc(8 + 8 + 4 + 2); - - sai = (struct sockaddr_in*)host->get_sockaddr(host); - p = natd_string.ptr; - *(u_int64_t*)p = spi_i; p += sizeof(spi_i); - *(u_int64_t*)p = spi_r; p += sizeof(spi_r); - *(u_int32_t*)p = sai->sin_addr.s_addr; p += sizeof(sai->sin_addr.s_addr); - *(u_int16_t*)p = sai->sin_port; p += sizeof(sai->sin_port); - - this->nat_hasher->get_hash(this->nat_hasher, natd_string, natd_hash.ptr); - this->nat_hasher->reset(this->nat_hasher); - - sprintf(buf, "natd_hash(%016llx %016llx %s:%d)\n == SHA1(", spi_i, spi_r, - host->get_address(host), host->get_port(host)); - chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string); - strcat(buf, ") == "); - chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash); - this->logger->log(this->logger, CONTROL|LEVEL3, buf); - - chunk_free(&natd_string); - return natd_hash; -} - -/** - * Implementation of ike_sa_t.send_dpd_request. - */ -static status_t send_dpd_request(private_ike_sa_t *this) -{ - message_t *dpd_msg; - status_t status; - this->protected.build_message(&this->protected, INFORMATIONAL, TRUE, &dpd_msg); - status = this->protected.send_request(&this->protected, dpd_msg); - if (status != SUCCESS) - { - dpd_msg->destroy(dpd_msg); - } - this->last_dpd_message_id = dpd_msg->get_message_id(dpd_msg); - return status; -} - -/** - * Implementation of ike_sa_t.get_last_dpd_message_id - */ -static u_int32_t get_last_dpd_message_id(private_ike_sa_t *this) -{ - return this->last_dpd_message_id; -} - -/** - * Implementation of ike_sa_t.get_child_sa. - */ -static child_sa_t *get_child_sa(private_ike_sa_t *this, u_int32_t reqid) -{ - iterator_t *iterator; - child_sa_t *current, *found = NULL; - - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->has_next(iterator)) - { - iterator->current(iterator, (void**)¤t); - if (current->get_reqid(current) == reqid) - { - found = current; - break; - } - } - iterator->destroy(iterator); - return found; -} - -/** * Implementation of ike_sa_t.delete_child_sa. */ -static status_t delete_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 spi) { - 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, 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); + /* 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; } @@ -1185,7 +1190,7 @@ static u_int32_t destroy_child_sa(private_ike_sa_t *this, u_int32_t 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) +static child_sa_t* get_child_sa(private_ike_sa_t *this, u_int32_t spi) { iterator_t *iterator; child_sa_t *current, *found = NULL; @@ -1206,8 +1211,9 @@ static child_sa_t* get_child_sa_by_spi(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 reqid) +static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t spi) { +/* TODO reimplement message_t *request; child_sa_t *child_sa; notify_payload_t *notify; @@ -1217,7 +1223,6 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) linked_list_t *proposals; chunk_t nonce; linked_list_t *my_ts, *other_ts; - state_t *old_state; if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) { @@ -1272,58 +1277,11 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) 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); + old_state->destroy(old_state);*/ return SUCCESS; } -/** - * Implementation of protected_ike_sa_t.establish. - */ -static void establish(private_ike_sa_t *this) -{ - protected_ike_sa_t *ike_sa = (protected_ike_sa_t *)this; - - connection_t *connection = ike_sa->get_connection(ike_sa); - host_t *my_host = connection->get_my_host(connection); - host_t *other_host = connection->get_other_host(connection); - policy_t *policy = ike_sa->get_policy(ike_sa); - identification_t *my_id = policy->get_my_id(policy); - identification_t *other_id = policy->get_other_id(policy); - - ike_sa->set_new_state(ike_sa, (state_t*)ike_sa_established_create(ike_sa)); - - this->logger->log(this->logger, AUDIT, "IKE_SA established %s[%s]...%s[%s]", - my_host->get_address(my_host), - my_id->get_string(my_id), - other_host->get_address(other_host), - other_id->get_string(other_id)); -} - -/** - * Implementation of protected_ike_sa_t.reset_message_buffers. - */ -static void reset_message_buffers(private_ike_sa_t *this) -{ - this->logger->log(this->logger, CONTROL|LEVEL2, "reset message counters and destroy stored messages"); - /* destroy stored requested message */ - if (this->last_requested_message != NULL) - { - this->last_requested_message->destroy(this->last_requested_message); - this->last_requested_message = NULL; - } - - /* destroy stored responded messages */ - if (this->last_responded_message != NULL) - { - this->last_responded_message->destroy(this->last_responded_message); - this->last_responded_message = NULL; - } - - this->message_id_out = 0; - this->message_id_in = 0; - this->last_replied_message_id = -1; -} /** * Implementation of protected_ike_sa_t.log_status. @@ -1364,7 +1322,7 @@ static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) } logger->log(logger, CONTROL|LEVEL1, " \"%s\": IKE_SA in state %s, SPIs: 0x%.16llx 0x%.16llx", name, - mapping_find(ike_sa_state_m, this->current_state->get_state(this->current_state)), + mapping_find(ike_sa_state_m, this->state), this->ike_sa_id->get_initiator_spi(this->ike_sa_id), this->ike_sa_id->get_responder_spi(this->ike_sa_id)); logger->log(logger, CONTROL, " \"%s\": %s[%s]...%s[%s]", @@ -1388,129 +1346,38 @@ static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) */ static status_t delete_(private_ike_sa_t *this) { - message_t *informational_request; - delete_payload_t *delete_payload; - u_int32_t timeout; - delete_half_open_ike_sa_job_t *job; - state_t *old_state; - - if (get_state(this) != IKE_SA_ESTABLISHED) - { - return INVALID_STATE; - } - - 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(PROTO_IKE); - - informational_request->add_payload(informational_request, (payload_t*)delete_payload); + delete_ike_sa_t *delete_ike_sa; + delete_ike_sa = delete_ike_sa_create(&this->public, this->message_id_out++); - if (send_request(this, informational_request) != SUCCESS) - { - /* send failed, but we ignore this, SA will get deleted anyway later */ - informational_request->destroy(informational_request); - } - - /* transit to state delete_ike_sa_requested */ - old_state = this->current_state; - set_new_state(this, (state_t*)delete_ike_sa_requested_create(&this->protected)); - old_state->destroy(old_state); - - /* there is no guarantee that the other peer will acknowledge the delete, - * so we have to set a timeout where we destroy the SA... This is done with - * the delete_half_open_ike_sa_job as used in IKE SA setup. - */ - timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration); - job = delete_half_open_ike_sa_job_create(this->ike_sa_id); - charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout); - return SUCCESS; -} - -/** - * Implementation of ike_sa_t.is_my_host_behind_nat. - */ -static bool is_my_host_behind_nat (private_ike_sa_t *this) -{ - return this->nat_here; + return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE); } /** - * Implementation of ike_sa_t.is_other_host_behind_nat. + * Implementation of ike_sa_t.is_natt_enabled. */ -static bool is_other_host_behind_nat (private_ike_sa_t *this) -{ - return this->nat_there; -} - -/** - * Implementation of ike_sa_t.is_any_host_behind_nat. - */ -static bool is_any_host_behind_nat (private_ike_sa_t *this) +static bool is_natt_enabled (private_ike_sa_t *this) { return this->nat_here || this->nat_there; } /** - * Implementation of protected_ike_sa_t.set_my_host_behind_nat. - */ -static void set_my_host_behind_nat (private_ike_sa_t *this, bool nat) -{ - this->nat_here = nat; -} - -/** - * Implementation of protected_ike_sa_t.set_other_host_behind_nat. - */ -static void set_other_host_behind_nat (private_ike_sa_t *this, bool nat) -{ - this->nat_there = nat; -} - -/** - * Implementation of private_ike_sa_t.get_last_esp_traffic_tv + * Implementation of protected_ike_sa_t.enable_natt. */ -static struct timeval get_last_esp_traffic_tv(private_ike_sa_t * this, bool inbound) +static void enable_natt (private_ike_sa_t *this, bool local) { - iterator_t *iterator; - child_sa_t *child_sa; - bool ret = TRUE; - time_t use_time = 0; - struct timeval tv = {0, 0}; - - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)&child_sa)) + if (local) { - if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS - && use_time != 0) - { - tv.tv_sec = max(tv.tv_sec, use_time); - } + this->logger->log(this->logger, CONTROL, + "local host is behind NAT, using NAT-T, scheduled keep alives"); + this->nat_here = TRUE; + send_keepalive(this); + } + else + { + this->logger->log(this->logger, CONTROL, + "remote host is behind NAT, using NAT-T"); + this->nat_there = TRUE; } - iterator->destroy(iterator); - - return tv; -} - -/** - * Implementation of ike_sa_t.get_last_traffic_in_tv. - */ -static struct timeval get_last_traffic_in_tv (private_ike_sa_t *this) -{ - struct timeval esp_tv = this->get_last_esp_traffic_tv(this, TRUE); - return this->last_msg_in_tv.tv_sec > esp_tv.tv_sec ? this->last_msg_in_tv - : this->last_msg_in_tv.tv_sec < esp_tv.tv_sec ? esp_tv - : this->last_msg_in_tv.tv_usec > esp_tv.tv_usec ? this->last_msg_in_tv : esp_tv; -} - -/** - * Implementation of ike_sa_t.get_last_traffic_out_tv. - */ -static struct timeval get_last_traffic_out_tv (private_ike_sa_t *this) -{ - struct timeval esp_tv = this->get_last_esp_traffic_tv(this, FALSE); - return this->last_msg_out_tv.tv_sec > esp_tv.tv_sec ? this->last_msg_out_tv - : this->last_msg_out_tv.tv_sec < esp_tv.tv_sec ? esp_tv - : this->last_msg_out_tv.tv_usec > esp_tv.tv_usec ? this->last_msg_out_tv : esp_tv; } /** @@ -1519,15 +1386,17 @@ static struct timeval get_last_traffic_out_tv (private_ike_sa_t *this) static void destroy(private_ike_sa_t *this) { child_sa_t *child_sa; + transaction_t *transaction; this->logger->log(this->logger, CONTROL|LEVEL2, "going to destroy IKE SA %llu:%llu, role %s", this->ike_sa_id->get_initiator_spi(this->ike_sa_id), this->ike_sa_id->get_responder_spi(this->ike_sa_id), this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); - if (get_state(this) == IKE_SA_ESTABLISHED) + if (this->state == SA_ESTABLISHED) { - this->logger->log(this->logger, ERROR, "destroying an established IKE SA without knowledge from remote peer!"); + this->logger->log(this->logger, ERROR, + "destroying an established IKE SA without knowledge from remote peer!"); } while (this->child_sas->remove_last(this->child_sas, (void**)&child_sa) == SUCCESS) @@ -1536,21 +1405,38 @@ static void destroy(private_ike_sa_t *this) } this->child_sas->destroy(this->child_sas); - if (this->crypter_initiator) + while (this->transaction_queue->remove_last(this->transaction_queue, (void**)&transaction) == SUCCESS) { - this->crypter_initiator->destroy(this->crypter_initiator); + transaction->destroy(transaction); } - if (this->crypter_responder) + this->transaction_queue->destroy(this->transaction_queue); + if (this->transaction_in) { - this->crypter_responder->destroy(this->crypter_responder); + this->transaction_in->destroy(this->transaction_in); } - if (this->signer_initiator) + if (this->transaction_in_next) { - this->signer_initiator->destroy(this->signer_initiator); + this->transaction_in_next->destroy(this->transaction_in_next); } - if (this->signer_responder) + if (this->transaction_out) { - this->signer_responder->destroy(this->signer_responder); + this->transaction_out->destroy(this->transaction_out); + } + if (this->crypter_in) + { + this->crypter_in->destroy(this->crypter_in); + } + if (this->crypter_out) + { + this->crypter_out->destroy(this->crypter_out); + } + if (this->signer_in) + { + this->signer_in->destroy(this->signer_in); + } + if (this->signer_out) + { + this->signer_out->destroy(this->signer_out); } if (this->prf) { @@ -1591,18 +1477,7 @@ static void destroy(private_ike_sa_t *this) { this->policy->destroy(this->policy); } - if (this->last_requested_message) - { - this->last_requested_message->destroy(this->last_requested_message); - } - if (this->last_responded_message) - { - this->last_responded_message->destroy(this->last_responded_message); - } - this->nat_hasher->destroy(this->nat_hasher); this->ike_sa_id->destroy(this->ike_sa_id); - this->randomizer->destroy(this->randomizer); - this->current_state->destroy(this->current_state); free(this); } @@ -1614,109 +1489,59 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) private_ike_sa_t *this = malloc_thing(private_ike_sa_t); /* Public functions */ - this->protected.public.process_message = (status_t (*) (ike_sa_t*,message_t*)) process_message; - this->protected.public.initiate_connection = (status_t (*) (ike_sa_t*,connection_t*)) initiate_connection; - this->protected.public.delete_child_sa = (status_t (*) (ike_sa_t*,u_int32_t)) delete_child_sa; - this->protected.public.rekey_child_sa = (status_t (*) (ike_sa_t*,u_int32_t)) rekey_child_sa; - this->protected.public.get_child_sa = (child_sa_t* (*) (ike_sa_t*,u_int32_t))get_child_sa; - this->protected.public.get_id = (ike_sa_id_t* (*) (ike_sa_t*)) get_id; - this->protected.public.get_my_host = (host_t* (*) (ike_sa_t*)) get_my_host; - this->protected.public.get_other_host = (host_t* (*) (ike_sa_t*)) get_other_host; - this->protected.public.get_my_id = (identification_t* (*) (ike_sa_t*)) get_my_id; - this->protected.public.get_other_id = (identification_t* (*) (ike_sa_t*)) get_other_id; - this->protected.public.get_connection = (connection_t* (*) (ike_sa_t*)) get_connection; - this->protected.public.retransmit_possible = (bool (*) (ike_sa_t*,u_int32_t)) retransmit_possible; - this->protected.public.retransmit_request = (status_t (*) (ike_sa_t*,u_int32_t)) retransmit_request; - this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t*)) get_state; - this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; - this->protected.public.delete = (status_t (*) (ike_sa_t*))delete_; - this->protected.public.destroy = (void (*) (ike_sa_t*))destroy; - this->protected.public.is_my_host_behind_nat = (bool (*) (ike_sa_t*)) is_my_host_behind_nat; - this->protected.public.is_other_host_behind_nat = (bool (*) (ike_sa_t*)) is_other_host_behind_nat; - this->protected.public.is_any_host_behind_nat = (bool (*) (ike_sa_t*)) is_any_host_behind_nat; - this->protected.public.get_last_traffic_in_tv = (struct timeval (*) (ike_sa_t*)) get_last_traffic_in_tv; - this->protected.public.get_last_traffic_out_tv = (struct timeval (*) (ike_sa_t*)) get_last_traffic_out_tv; - this->protected.public.send_dpd_request = (status_t (*) (ike_sa_t*)) send_dpd_request; - - /* protected functions */ - this->protected.build_message = (void (*) (protected_ike_sa_t*,exchange_type_t,bool,message_t**)) build_message; - this->protected.get_prf = (prf_t *(*) (protected_ike_sa_t*)) get_prf; - this->protected.get_child_prf = (prf_t* (*) (protected_ike_sa_t*)) get_child_prf; - this->protected.get_prf_auth_i = (prf_t* (*) (protected_ike_sa_t*)) get_prf_auth_i; - this->protected.get_prf_auth_r = (prf_t* (*) (protected_ike_sa_t*)) get_prf_auth_r; - this->protected.add_child_sa = (void (*) (protected_ike_sa_t*,child_sa_t*)) add_child_sa; - this->protected.establish = (void (*) (protected_ike_sa_t*)) establish; - this->protected.set_connection = (void (*) (protected_ike_sa_t*,connection_t*)) set_connection; - this->protected.get_connection = (connection_t* (*) (protected_ike_sa_t*)) get_connection; - this->protected.set_policy = (void (*) (protected_ike_sa_t *,policy_t*)) set_policy; - this->protected.get_policy = (policy_t* (*) (protected_ike_sa_t*)) get_policy; - this->protected.get_randomizer = (randomizer_t* (*) (protected_ike_sa_t*)) get_randomizer; - this->protected.send_request = (status_t (*) (protected_ike_sa_t*,message_t*)) send_request; - this->protected.send_response = (status_t (*) (protected_ike_sa_t*,message_t*)) send_response; - this->protected.send_notify = (void (*) (protected_ike_sa_t*,exchange_type_t,notify_message_type_t,chunk_t)) send_notify; - this->protected.build_transforms = (status_t (*) (protected_ike_sa_t*,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t)) build_transforms; - this->protected.set_new_state = (void (*) (protected_ike_sa_t*,state_t*)) set_new_state; - this->protected.get_crypter_initiator = (crypter_t* (*) (protected_ike_sa_t*)) get_crypter_initiator; - this->protected.get_signer_initiator = (signer_t* (*) (protected_ike_sa_t*)) get_signer_initiator; - this->protected.get_crypter_responder = (crypter_t* (*) (protected_ike_sa_t*)) get_crypter_responder; - this->protected.get_signer_responder = (signer_t* (*) (protected_ike_sa_t*)) get_signer_responder; - 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; - this->protected.set_my_host_behind_nat = (void (*) (protected_ike_sa_t*,bool)) set_my_host_behind_nat; - this->protected.set_other_host_behind_nat = (void (*) (protected_ike_sa_t*,bool)) set_other_host_behind_nat; - this->protected.generate_natd_hash = (chunk_t (*) (protected_ike_sa_t*,u_int64_t, u_int64_t, host_t*)) generate_natd_hash; - this->protected.get_last_dpd_message_id = (u_int32_t (*) (protected_ike_sa_t*)) get_last_dpd_message_id; - this->protected.update_connection_hosts = (status_t (*) (protected_ike_sa_t*,host_t*,host_t*)) update_connection_hosts; - - /* private functions */ - this->update_timestamp = (void (*) (private_ike_sa_t*,bool))update_timestamp; - this->get_last_esp_traffic_tv = (struct timeval (*) (private_ike_sa_t*,bool))get_last_esp_traffic_tv; - + this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state; + this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state; + this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; + this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*)) initiate; + this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; + this->public.get_connection = (connection_t*(*)(ike_sa_t*)) get_connection; + this->public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; + this->public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; + this->public.delete = (status_t(*)(ike_sa_t*))delete_; + this->public.destroy = (void(*)(ike_sa_t*))destroy; + this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; + this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; + this->public.get_prf = (prf_t *(*) (ike_sa_t *)) get_prf; + 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.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; + this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; + /* initialize private fields */ this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->child_sas = linked_list_create(); - this->randomizer = randomizer_create(); - - this->last_requested_message = NULL; - this->last_responded_message = NULL; - this->message_id_out = 0; - this->message_id_in = 0; - this->last_replied_message_id = -1; - this->crypter_initiator = NULL; - this->crypter_responder = NULL; - this->signer_initiator = NULL; - this->signer_responder = NULL; + this->crypter_in = NULL; + this->crypter_out = NULL; + this->signer_in = NULL; + this->signer_out = NULL; this->prf = NULL; this->prf_auth_i = NULL; this->prf_auth_r = NULL; this->child_prf = NULL; this->connection = NULL; this->policy = NULL; - this->nat_hasher = hasher_create(HASH_SHA1); this->nat_here = FALSE; this->nat_there = FALSE; - this->last_msg_in_tv.tv_sec = 0; - this->last_msg_in_tv.tv_usec = 0; - this->last_msg_out_tv.tv_sec = 0; - this->last_msg_out_tv.tv_usec = 0; - this->last_dpd_message_id = 0; - - /* at creation time, IKE_SA is in a initiator state */ - if (ike_sa_id->is_initiator(ike_sa_id)) - { - this->logger->log(this->logger, CONTROL | LEVEL2, "create first state_t object of type INITIATOR_INIT"); - this->current_state = (state_t *) initiator_init_create(&(this->protected)); - } - else - { - this->logger->log(this->logger, CONTROL | LEVEL2, "create first state_t object of type RESPONDER_INIT"); - this->current_state = (state_t *) responder_init_create(&(this->protected)); - } - return &(this->protected.public); + this->transaction_queue = linked_list_create(); + this->transaction_in = NULL; + this->transaction_in_next = NULL; + this->transaction_out = NULL; + this->state = SA_CREATED; + this->message_id_out = 0; + this->time_inbound = 0; + this->time_outbound = 0; + + return &this->public; } diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 06a5930e4..863271950 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -29,7 +29,6 @@ #include <encoding/payloads/proposal_substructure.h> #include <sa/ike_sa_id.h> #include <sa/child_sa.h> -#include <sa/states/state.h> #include <config/configuration.h> #include <utils/logger.h> #include <utils/randomizer.h> @@ -40,25 +39,53 @@ #include <config/policies/policy.h> #include <utils/logger.h> + +typedef enum ike_sa_state_t ike_sa_state_t; + /** - * Nonce size in bytes for nonces sending to other peer. - * - * @warning Nonce size MUST be between 16 and 256 bytes. + * @brief State of an IKE_SA. * * @ingroup sa */ -#define NONCE_SIZE 16 +enum ike_sa_state_t { + + /** + * IKE_SA just got created, but is not initiating nor responding yet. + */ + SA_CREATED, + + /** + * IKE_SA gets initiated actively or passively + */ + SA_CONNECTING, + + /** + * IKE_SA is fully established + */ + SA_ESTABLISHED, + + /** + * IKE_SA is in progress of deletion + */ + SA_DELETING, +}; + +/** + * String mappings for ike_sa_state_t. + */ +extern mapping_t ike_sa_state_m[]; typedef struct ike_sa_t ike_sa_t; /** - * @brief Class ike_sa_t representing an IKE_SA. - * - * An object of this type is managed by an ike_sa_manager_t object - * and represents an IKE_SA. Message processing is split up in different states. - * They will handle all related things for the state they represent. - * + * @brief Class ike_sa_t representing an IKE_SA. + * + * An IKE_SA contains crypto information related to a connection + * with a peer. It contains multiple IPsec CHILD_SA, for which + * it is responsible. All traffic is handled by an IKE_SA, using + * transactions. + * * @b Constructors: * - ike_sa_create() * @@ -67,20 +94,34 @@ typedef struct ike_sa_t ike_sa_t; struct ike_sa_t { /** - * @brief Processes a incoming IKEv2-Message of type message_t. + * @brief Get the id of the SA. + * + * Returned ike_sa_id_t object is not getting cloned! * - * @param this ike_sa_t object object - * @param[in] message message_t object to process - * @return - * - SUCCESS - * - FAILED - * - DESTROY_ME if this IKE_SA MUST be deleted + * @param this calling object + * @return ike_sa's ike_sa_id_t */ - status_t (*process_message) (ike_sa_t *this,message_t *message); + ike_sa_id_t* (*get_id) (ike_sa_t *this); + + /** + * @brief Get the state of the IKE_SA. + * + * @param this calling object + * @return state of the IKE_SA + */ + ike_sa_state_t (*get_state) (ike_sa_t *this); + + /** + * @brief Set the state of the IKE_SA. + * + * @param this calling object + * @param state state to set for the IKE_SA + */ + void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa); /** - * @brief Initiate a new connection with given connection_t object. - * + * @brief Initiate a new connection. + * * The connection_t object is owned by the IKE_SA after the call, so * do not modify or destroy it. * @@ -91,19 +132,24 @@ struct ike_sa_t { * - FAILED if in wrong state * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted */ - status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection); + status_t (*initiate) (ike_sa_t *this, connection_t *connection); /** - * @brief Checks whether retransmission is possible. + * @brief Initiates the deletion of an IKE_SA. + * + * Sends a delete message to the remote peer and waits for + * its response. If the response comes in, or a timeout occurs, + * the IKE SA gets deleted. * * @param this calling object - * @param message_id ID of the request to retransmit * @return - * - TRUE if retransmit is possible - * - FALSE if not + * - SUCCESS if deletion is initialized + * - INVALID_STATE, if the IKE_SA is not in + * an established state and can not be + * delete (but destroyed). */ - bool (*retransmit_possible) (ike_sa_t *this, u_int32_t message_id); - + status_t (*delete) (ike_sa_t *this); + /** * @brief Retransmits a request. * @@ -114,157 +160,71 @@ struct ike_sa_t { * - NOT_FOUND if request doesn't have to be retransmited */ status_t (*retransmit_request) (ike_sa_t *this, u_int32_t message_id); - - /** - * @brief Get the id of the SA. - * - * Returned ike_sa_id_t object is not getting cloned! - * - * @param this calling object - * @return ike_sa's ike_sa_id_t - */ - ike_sa_id_t* (*get_id) (ike_sa_t *this); - - /** - * @brief Get the CHILD_SA with the specified reqid. - * - * The reqid is a unique ID for a child SA, which is - * generated on child SA creation. - * Returned child_sa_t object is not cloned! - * - * @param this calling object - * @param reqid reqid of the child SA, as used in the kernel - * @return child_sa, or NULL if not found - */ - child_sa_t* (*get_child_sa) (ike_sa_t *this, u_int32_t reqid); - - /** - * @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 - */ - status_t (*delete_child_sa) (ike_sa_t *this, u_int32_t reqid); - - /** - * @brief Rekey the CHILD SA with the specified reqid. - * - * 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 - * @return - * - NOT_FOUND, if IKE_SA has no such CHILD_SA - * - SUCCESS, if rekeying initiated - */ - status_t (*rekey_child_sa) (ike_sa_t *this, u_int32_t reqid); - - /** - * @brief Get local peer address of the IKE_SA. - * - * @param this calling object - * @return local host_t - */ - host_t* (*get_my_host) (ike_sa_t *this); - - /** - * @brief Get remote peer address of the IKE_SA. - * - * @param this calling object - * @return remote host_t - */ - host_t* (*get_other_host) (ike_sa_t *this); - - /** - * @brief Get own ID of the IKE_SA. - * - * @param this calling object - * @return local identification_t - */ - identification_t* (*get_my_id) (ike_sa_t *this); - + /** - * @brief Get remote ID the IKE_SA. + * @brief Processes a incoming IKEv2-Message. * - * @param this calling object - * @return remote identification_t - */ - identification_t* (*get_other_id) (ike_sa_t *this); - - /** - * @brief Get the connection of the IKE_SA. + * Message processing may fail. If a critical failure occurs, + * process_message() return DESTROY_ME. Then the caller must + * destroy the IKE_SA immediatly, as it is unusable. * - * The internal used connection specification - * can be queried to get some data of an IKE_SA. - * The connection is still owned to the IKE_SA - * and must not be manipulated. - * * @param this calling object - * @return connection_t + * @param[in] message message to process + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted */ - connection_t* (*get_connection) (ike_sa_t *this); + status_t (*process_message) (ike_sa_t *this,message_t *message); /** - * @brief Query NAT detection status for local host. + * @brief Check if NAT traversal is enabled for this IKE_SA. * * @param this calling object - * @return TRUE if this host is behind NAT + * @return TRUE if NAT traversal enabled */ - bool (*is_my_host_behind_nat) (ike_sa_t *this); + bool (*is_natt_enabled) (ike_sa_t *this); /** - * @brief Query NAT detection status for remote host. + * @brief Enable NAT detection for this IKE_SA. * - * @param this calling object - * @return TRUE if other host is behind NAT - */ - bool (*is_other_host_behind_nat) (ike_sa_t *this); - - /** - * @brief Query NAT detection status for any host. + * If a Network address translation is detected with + * NAT_DETECTION notifys, a SA must switch to ports + * 4500. To enable this behavior, call enable_natt(). + * It is relevant which peer is NATted, this is specified + * with the "local" parameter. Call it twice when both + * are NATted. * * @param this calling object - * @return TRUE if this or other host is behind NAT + * @param local TRUE, if we are NATted, FALSE if other */ - bool (*is_any_host_behind_nat) (ike_sa_t *this); + void (*enable_natt) (ike_sa_t *this, bool local); /** - * @brief Query timeval of last inbound IKE or ESP traffic. + * @brief Sends a DPD request to the peer. * + * To check if a peer is still alive, periodic + * empty INFORMATIONAL messages are sent if no + * other traffic was received. + * * @param this calling object - * @return time when the last traffic was seen + * @return + * - SUCCESS + * - DESTROY_ME, if peer did not respond */ - struct timeval (*get_last_traffic_in_tv) (ike_sa_t *this); - + status_t (*send_dpd) (ike_sa_t *this); + /** - * @brief Query timeval of last outbound IKE or ESP traffic. + * @brief Sends a keep alive packet. * - * @param this calling object - * @return time when the last traffic was seen - */ - struct timeval (*get_last_traffic_out_tv) (ike_sa_t *this); - - /** - * @brief Get the state of type of associated state object. + * To refresh NAT tables in a NAT router + * between the peers, periodic empty + * UDP packets are sent if no other traffic + * was sent. * - * @param this calling object - * @return state of IKE_SA - */ - ike_sa_state_t (*get_state) (ike_sa_t *this); - - /** - * @brief Sends a DPD request to the peer. - * * @param this calling object */ - status_t (*send_dpd_request) (ike_sa_t *this); + void (*send_keepalive) (ike_sa_t *this); /** * @brief Log the status of a the ike sa to a logger. @@ -279,71 +239,14 @@ struct ike_sa_t { * @param name name of the connection */ void (*log_status) (ike_sa_t *this, logger_t *logger, char *name); - - /** - * @brief Initiates the deletion of an IKE_SA. - * - * Sends a delete message to the remote peer and waits for - * its response. If the response comes in, or a timeout occurs, - * the IKE SA gets deleted. - * - * @param this calling object - * @return - * - SUCCESS if deletion is initialized - * - INVALID_STATE, if the IKE_SA is not in - * an established state and can not be - * delete (but destroyed). - */ - status_t (*delete) (ike_sa_t *this); - - /** - * @brief Destroys a ike_sa_t object. - * - * @param this calling object - */ - void (*destroy) (ike_sa_t *this); -}; - - -typedef struct protected_ike_sa_t protected_ike_sa_t; - -/** - * @brief Protected functions of an ike_sa_t object. - * - * This members are only accessed out from - * the various state_t implementations. - * - * @ingroup sa - */ -struct protected_ike_sa_t { - - /** - * Public interface of an ike_sa_t object. - */ - ike_sa_t public; - - /** - * @brief Build an empty IKEv2-Message and fills in default informations. - * - * Depending on the type of message (request or response), the message id is - * either message_id_out or message_id_in. - * - * Used in state_t Implementation to build an empty IKEv2-Message. - * - * @param this calling object - * @param type exchange type of new message - * @param request TRUE, if message has to be a request - * @param message new message is stored at this location - */ - void (*build_message) (protected_ike_sa_t *this, exchange_type_t type, bool request, message_t **message); - + /** * @brief Get the internal stored connection_t object. * * @param this calling object * @return pointer to the internal stored connection_t object */ - connection_t *(*get_connection) (protected_ike_sa_t *this); + connection_t *(*get_connection) (ike_sa_t *this); /** * @brief Set the internal connection object. @@ -351,7 +254,7 @@ struct protected_ike_sa_t { * @param this calling object * @param connection object of type connection_t */ - void (*set_connection) (protected_ike_sa_t *this, connection_t *connection); + void (*set_connection) (ike_sa_t *this, connection_t *connection); /** * @brief Get the internal stored policy object. @@ -359,7 +262,7 @@ struct protected_ike_sa_t { * @param this calling object * @return pointer to the internal stored policy_t object */ - policy_t *(*get_policy) (protected_ike_sa_t *this); + policy_t *(*get_policy) (ike_sa_t *this); /** * @brief Set the internal policy_t object. @@ -367,274 +270,135 @@ struct protected_ike_sa_t { * @param this calling object * @param policy object of type policy_t */ - void (*set_policy) (protected_ike_sa_t *this,policy_t *policy); - + void (*set_policy) (ike_sa_t *this, policy_t *policy); + /** * @brief Derive all keys and create the transforms for IKE communication. * * Keys are derived using the diffie hellman secret, nonces and internal - * stored SPIs. + * stored SPIs. * Already existing objects get destroyed. * - * @param this calling object - * @param proposal proposal which contains algorithms to use - * @param dh diffie hellman object with shared secret - * @param nonce_i initiators nonce - * @param nonce_r responders nonce - */ - status_t (*build_transforms) (protected_ike_sa_t *this, proposal_t* proposal, - diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r); - - /** - * @brief Send the next request message. - * - * Also the first retransmit job is created. - * - * Last stored requested message gets destroyed. Object gets not cloned! - * - * @param this calling object - * @param message pointer to the message which should be sent - * @return - * - SUCCESS - * - FAILED if message id is not next expected one - */ - status_t (*send_request) (protected_ike_sa_t *this,message_t * message); - - /** - * @brief Send the next response message. - * - * Last stored responded message gets destroyed. Object gets not cloned! - * - * @param this calling object - * @param message pointer to the message which should be sent - * return - * - SUCCESS - * - FAILED if message id is not next expected one - */ - status_t (*send_response) (protected_ike_sa_t *this,message_t * message); - - /** - * @brief Send a notify reply message. - * - * @param this calling object - * @param exchange_type type of exchange in which the notify should be wrapped - * @param type type of the notify message to send - * @param data notification data - */ - void (*send_notify) (protected_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data); - - /** - * @brief Get the internal stored randomizer_t object. - * - * @param this calling object - * @return pointer to the internal randomizer_t object - */ - randomizer_t *(*get_randomizer) (protected_ike_sa_t *this); - - /** - * @brief Set the new state_t object of the IKE_SA object. - * - * The old state_t object gets not destroyed. It's the callers duty to - * make sure old state is destroyed (Normally the old state is the caller). - * - * @param this calling object - * @param state pointer to the new state_t object - */ - void (*set_new_state) (protected_ike_sa_t *this,state_t *state); - - /** - * @brief Set the last replied message id. - * - * @param this calling object - * @param message_id message id - */ - void (*set_last_replied_message_id) (protected_ike_sa_t *this,u_int32_t message_id); - - /** - * @brief Get the internal stored initiator crypter_t object. - * - * @param this calling object - * @return pointer to crypter_t object - */ - crypter_t *(*get_crypter_initiator) (protected_ike_sa_t *this); - - /** - * @brief Get the internal stored initiator signer_t object. - * - * @param this calling object - * @return pointer to signer_t object - */ - signer_t *(*get_signer_initiator) (protected_ike_sa_t *this); - - /** - * @brief Get the internal stored responder crypter_t object. - * - * @param this calling object - * @return pointer to crypter_t object - */ - crypter_t *(*get_crypter_responder) (protected_ike_sa_t *this); - - /** - * @brief Get the internal stored responder signer object. - * - * @param this calling object - * @return pointer to signer_t object - */ - signer_t *(*get_signer_responder) (protected_ike_sa_t *this); + * @param this calling object + * @param proposal proposal which contains algorithms to use + * @param dh diffie hellman object with shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @param initiator role of this IKE SA (TRUE = originial initiator) + */ + status_t (*build_transforms) (ike_sa_t *this, proposal_t* proposal, + diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r, + bool initiator); /** * @brief Get the multi purpose prf. * - * @param this calling object - * @return pointer to prf_t object + * @param this calling object + * @return pointer to prf_t object */ - prf_t *(*get_prf) (protected_ike_sa_t *this); + prf_t *(*get_prf) (ike_sa_t *this); /** * @brief Get the prf-object, which is used to derive keys for child SAs. * - * @param this calling object - * @return pointer to prf_t object + * @param this calling object + * @return pointer to prf_t object */ - prf_t *(*get_child_prf) (protected_ike_sa_t *this); + prf_t *(*get_child_prf) (ike_sa_t *this); /** * @brief Get the prf used for authentication of initiator. * - * @param this calling object - * @return pointer to prf_t object + * @param this calling object + * @return pointer to prf_t object */ - prf_t *(*get_prf_auth_i) (protected_ike_sa_t *this); + prf_t *(*get_prf_auth_i) (ike_sa_t *this); /** * @brief Get the prf used for authentication of responder. * - * @param this calling object - * @return pointer to prf_t object - */ - prf_t *(*get_prf_auth_r) (protected_ike_sa_t *this); - - /** - * @brief Associates a child SA to this IKE SA - * - * @param this calling object - * @param child_sa child_sa to add - */ - 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 + * @param this calling object + * @return pointer to prf_t object */ - u_int32_t (*destroy_child_sa) (protected_ike_sa_t *this, u_int32_t spi); + prf_t *(*get_prf_auth_r) (ike_sa_t *this); /** * @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 + * @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); + child_sa_t* (*get_child_sa) (ike_sa_t *this, u_int32_t spi); /** - * @brief establish the IKE 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 - */ - void (*establish) (protected_ike_sa_t *this); - - /** - * @brief Get the last responded message. - * - * @param this calling object - * @return - * - last received as message_t object - * - NULL if no last request available - */ - message_t *(*get_last_responded_message) (protected_ike_sa_t *this); - - /** - * @brief Get the last requested message. - * - * @param this calling object - * @return - * - last sent as message_t object - * - NULL if no last request available - */ - message_t *(*get_last_requested_message) (protected_ike_sa_t *this); - - /** - * @brief Resets message counters and does destroy stored received and sent messages. + * @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 * - * @param this calling object - */ - void (*reset_message_buffers) (protected_ike_sa_t *this); + * @TODO use spi, not reqid + */ + status_t (*delete_child_sa) (ike_sa_t *this, u_int32_t reqid); /** - * @brief Set NAT detection status for local host. + * @brief Rekey the CHILD SA with the specified reqid. * - * @param this calling object - * @param nat if TRUE, local host is behing NAT - */ - void (*set_my_host_behind_nat) (protected_ike_sa_t *this, bool nat); - - /** - * @brief Set NAT detection status for remote host. + * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing. * - * @param this calling object - * @param nat if TRUE, remote host is behing NAT + * @param this calling object + * @param spi security parameter index identifying the SA to rekey + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if rekeying initiated + * + * @TODO use spi, not reqid */ - void (*set_other_host_behind_nat) (protected_ike_sa_t *this, bool nat); - + status_t (*rekey_child_sa) (ike_sa_t *this, u_int32_t reqid); + /** - * @brief Generate NAT-D payload hash. - * - * @param this calling object - * @param spi_i IKE SPI of initiator - * @param spi_r IKE SPI of responder - * @param host address and port of the host/interface - * @return chunk containing calculated NAT-D hash + * @brief Associates a child SA to this IKE SA + * + * @param this calling object + * @param child_sa child_sa to add */ - chunk_t (*generate_natd_hash) (protected_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host); - + void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); + /** - * @brief Dynamically update hosts on the associated connection. - * - * Warning: me and other host are cloned. - * - * @param this calling object - * @param me local address and port - * @param other remote address and port + * @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 */ - status_t (*update_connection_hosts) (protected_ike_sa_t *this, host_t *me, host_t *other); - + u_int32_t (*destroy_child_sa) (ike_sa_t *this, u_int32_t spi); + /** - * @brief Return the message id of the last DPD message + * @brief Destroys a ike_sa_t object. * - * @param this calling object - * @return the messages id + * @param this calling object */ - u_int32_t (*get_last_dpd_message_id) (protected_ike_sa_t *this); + void (*destroy) (ike_sa_t *this); }; - /** * @brief Creates an ike_sa_t object with a specific ID. - * - * @warning the Content of internal ike_sa_id_t object can change over time - * e.g. when a IKE_SA_INIT has been finished. * - * @param[in] ike_sa_id ike_sa_id_t object to associate with new IKE_SA. - * The object is internal getting cloned - * and so has to be destroyed by the caller. + * 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 * * @ingroup sa */ -ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id); +ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id); #endif /*IKE_SA_H_*/ diff --git a/src/charon/sa/ike_sa_id.c b/src/charon/sa/ike_sa_id.c index bf3a05d11..4f1f33bdc 100644 --- a/src/charon/sa/ike_sa_id.c +++ b/src/charon/sa/ike_sa_id.c @@ -181,5 +181,5 @@ ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, this->responder_spi = responder_spi; this->is_initiator_flag = is_initiator_flag; - return (&this->public); + return &this->public; } diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 82f5d22af..1aad1b393 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -175,7 +175,7 @@ struct private_ike_sa_manager_t { status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry); /** - * @brief Felete an entry from the linked list. + * @brief Delete an entry from the linked list. * * @param this calling object * @param entry entry to delete @@ -229,10 +229,13 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike if (current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0) { /* seems to be a half ready ike_sa */ - if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == ike_sa_id->get_initiator_spi(ike_sa_id)) - && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id))) + if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == + ike_sa_id->get_initiator_spi(ike_sa_id)) && + (ike_sa_id->is_initiator(ike_sa_id) == + current->ike_sa_id->is_initiator(current->ike_sa_id))) { - this->logger->log(this->logger, CONTROL|LEVEL2, "found entry by initiator spi %d", + this->logger->log(this->logger, CONTROL|LEVEL2, + "found entry by initiator spi %d", ike_sa_id->get_initiator_spi(ike_sa_id)); *entry = current; status = SUCCESS; @@ -241,8 +244,10 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike } else if (ike_sa_id->get_responder_spi(ike_sa_id) == 0) { - if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == ike_sa_id->get_initiator_spi(ike_sa_id)) - && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id))) + if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == + ike_sa_id->get_initiator_spi(ike_sa_id)) && + (ike_sa_id->is_initiator(ike_sa_id) == + current->ike_sa_id->is_initiator(current->ike_sa_id))) { this->logger->log(this->logger, CONTROL|LEVEL2, "found entry by initiator spi %d", ike_sa_id->get_initiator_spi(ike_sa_id)); @@ -315,7 +320,8 @@ static status_t delete_entry(private_ike_sa_manager_t *this, ike_sa_entry_t *ent iterator->current(iterator, (void**)¤t); if (current == entry) { - this->logger->log(this->logger, CONTROL|LEVEL2, "found entry by pointer. Going to delete it."); + this->logger->log(this->logger, CONTROL|LEVEL2, + "found entry by pointer. Going to delete it"); iterator->remove(iterator); entry->destroy(entry); status = SUCCESS; @@ -355,6 +361,11 @@ static void create_and_checkout(private_ike_sa_manager_t *this,ike_sa_t **ike_sa /* create entry */ new_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id); new_ike_sa_id->destroy(new_ike_sa_id); + this->logger->log(this->logger, CONTROL|LEVEL2, + "created IKE_SA %llx:%llx, role %s", + new_ike_sa_id->get_initiator_spi(new_ike_sa_id), + new_ike_sa_id->get_responder_spi(new_ike_sa_id), + new_ike_sa_id->is_initiator(new_ike_sa_id) ? "initiator" : "responder"); /* each access is locked */ pthread_mutex_lock(&(this->mutex)); @@ -362,7 +373,8 @@ static void create_and_checkout(private_ike_sa_manager_t *this,ike_sa_t **ike_sa this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry); /* check ike_sa out */ - this->logger->log(this->logger, CONTROL|LEVEL1, "new IKE_SA created and added to list of known IKE_SA's"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "new IKE_SA created and added to list of known IKE_SA's"); new_ike_sa_entry->checked_out = TRUE; *ike_sa = new_ike_sa_entry->ike_sa; @@ -379,6 +391,15 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, bool original_initiator; status_t retval; + this->logger->log(this->logger, CONTROL|LEVEL2, + "checkout IKE_SA %llx:%llx, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + + this->logger->log(this->logger, CONTROL|LEVEL2, "%d IKE_SAs in manager", + this->ike_sa_list->get_count(this->ike_sa_list)); + /* each access is locked */ pthread_mutex_lock(&(this->mutex)); @@ -399,7 +420,8 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, /* can we give this ike_sa out to new requesters?*/ if (entry->driveout_new_threads) { - this->logger->log(this->logger, CONTROL|LEVEL1, "drive out new thread for existing IKE_SA"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "drive out new thread for existing IKE_SA"); /* no we can't */ retval = NOT_FOUND; } @@ -422,12 +444,14 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, { /* we must signal here, others are interested that we leave */ pthread_cond_signal(&(entry->condvar)); - this->logger->log(this->logger, CONTROL|LEVEL1, "drive out waiting thread for existing IKE_SA"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "drive out waiting thread for existing IKE_SA"); retval = NOT_FOUND; } else { - this->logger->log(this->logger, CONTROL|LEVEL2, "IKE SA successfully checked out"); + this->logger->log(this->logger, CONTROL|LEVEL2, + "IKE SA successfully checked out"); /* ok, this IKE_SA is finally ours */ entry->checked_out = TRUE; *ike_sa = entry->ike_sa; @@ -438,7 +462,8 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, } else { - this->logger->log(this->logger, ERROR|LEVEL1, "IKE SA not stored in known IKE_SA list"); + this->logger->log(this->logger, ERROR|LEVEL1, + "IKE SA not stored in known IKE_SA list"); /* looks like there is no such IKE_SA, better luck next time... */ /* DON'T use return, we must unlock the mutex! */ retval = NOT_FOUND; @@ -469,11 +494,12 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry); /* check ike_sa out */ - this->logger->log(this->logger, CONTROL|LEVEL1 ,"IKE_SA added to list of known IKE_SA's"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "IKE_SA added to list of known IKE_SA's"); new_ike_sa_entry->checked_out = TRUE; *ike_sa = new_ike_sa_entry->ike_sa; - retval = CREATED; + retval = SUCCESS; } else { @@ -482,7 +508,7 @@ static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, /* DON'T use return, we must unlock the mutex! */ retval = INVALID_ARG; } - + pthread_mutex_unlock(&(this->mutex)); /* OK, unlocked... */ return retval; @@ -491,7 +517,8 @@ 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. */ -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_reqid(private_ike_sa_manager_t *this, + u_int32_t reqid, ike_sa_t **ike_sa) { iterator_t *iterator; status_t status = NOT_FOUND; @@ -600,15 +627,16 @@ linked_list_t *get_ike_sa_list_by_name(private_ike_sa_manager_t* this, const cha static void log_status(private_ike_sa_manager_t* this, logger_t* logger, char* name) { iterator_t *iterator; + u_int instances; pthread_mutex_lock(&(this->mutex)); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - - if (iterator->get_count(iterator)) + instances = this->ike_sa_list->get_count(this->ike_sa_list); + if (instances) { - logger->log(logger, CONTROL, "Instances:"); + logger->log(logger, CONTROL, "Instances (%d):", instances); } + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); while (iterator->has_next(iterator)) { ike_sa_entry_t *entry; @@ -633,6 +661,15 @@ static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) */ status_t retval; ike_sa_entry_t *entry; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + + this->logger->log(this->logger, CONTROL|LEVEL2, + "checkin IKE_SA %llx:%llx, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); pthread_mutex_lock(&(this->mutex)); @@ -649,10 +686,14 @@ static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) } else { - this->logger->log(this->logger, ERROR, "tried to check in nonexisting IKE_SA"); + this->logger->log(this->logger, ERROR, + "tried to check in nonexisting IKE_SA"); /* this SA is no more, this REALLY should not happen */ retval = NOT_FOUND; } + + this->logger->log(this->logger, CONTROL|LEVEL2, "%d IKE_SAs in manager now", + this->ike_sa_list->get_count(this->ike_sa_list)); pthread_mutex_unlock(&(this->mutex)); return retval; } @@ -670,6 +711,14 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik */ ike_sa_entry_t *entry; status_t retval; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + this->logger->log(this->logger, CONTROL|LEVEL2, + "checkin and destroy IKE_SA %llx:%llx, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); pthread_mutex_lock(&(this->mutex)); @@ -690,12 +739,14 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik } /* ok, we are alone now, no threads waiting in the entry's condvar */ this->delete_entry(this, entry); - this->logger->log(this->logger, CONTROL|LEVEL1, "check-in and destroy of IKE_SA successful"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "check-in and destroy of IKE_SA successful"); retval = SUCCESS; } else { - this->logger->log(this->logger,ERROR, "tried to check-in and delete nonexisting IKE_SA"); + this->logger->log(this->logger,ERROR, + "tried to check-in and delete nonexisting IKE_SA"); retval = NOT_FOUND; } @@ -715,9 +766,15 @@ static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) */ ike_sa_entry_t *entry; status_t retval; - + + this->logger->log(this->logger, CONTROL|LEVEL2, + "delete IKE_SA %llx:%llx, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + pthread_mutex_lock(&(this->mutex)); - + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) { /* we try a delete. If it succeeds, our job is done here. The @@ -725,11 +782,13 @@ static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) */ if (entry->ike_sa->delete(entry->ike_sa) == SUCCESS) { - this->logger->log(this->logger, CONTROL|LEVEL1, "initiated delete for IKE_SA"); + this->logger->log(this->logger, CONTROL|LEVEL1, + "initiated delete for IKE_SA"); } - /* but if the IKE SA is not in a state where the deletion is negotiated with - * the other peer, we can destroy the IKE SA on our own. For this, we must - * be sure that really NO other threads are waiting for this SA... + /* but if the IKE SA is not in a state where the deletion is + * negotiated with the other peer, we can destroy the IKE SA on our own. + * For this, we must be sure that really NO other threads are + * waiting for this SA... */ else { @@ -740,7 +799,8 @@ static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) { /* wake up all */ pthread_cond_broadcast(&(entry->condvar)); - /* and the nice thing, they will wake us again when their work is done */ + /* and the nice thing, they will wake us again when their work + * is done */ pthread_cond_wait(&(entry->condvar), &(this->mutex)); } /* ok, we are alone now, no threads waiting in the entry's condvar */ @@ -751,7 +811,8 @@ static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) } else { - this->logger->log(this->logger,ERROR, "tried to delete nonexisting IKE_SA"); + this->logger->log(this->logger,ERROR, + "tried to delete nonexisting IKE_SA"); retval = NOT_FOUND; } @@ -770,13 +831,12 @@ static void destroy(private_ike_sa_manager_t *this) ike_sa_entry_t *entry; pthread_mutex_lock(&(this->mutex)); - - this->logger->log(this->logger, CONTROL|LEVEL1, "going to destroy IKE_SA manager and all managed IKE_SA's"); - + this->logger->log(this->logger, CONTROL|LEVEL1, + "going to destroy IKE_SA manager and all managed IKE_SA's"); /* Step 1: drive out all waiting threads */ + this->logger->log(this->logger, CONTROL|LEVEL2, + "set driveout flags for all stored IKE_SA's"); iterator = list->create_iterator(list, TRUE); - - this->logger->log(this->logger, CONTROL|LEVEL2, "set driveout flags for all stored IKE_SA's"); while (iterator->has_next(iterator)) { iterator->current(iterator, (void**)&entry); @@ -784,8 +844,8 @@ static void destroy(private_ike_sa_manager_t *this) entry->driveout_new_threads = TRUE; entry->driveout_waiting_threads = TRUE; } - - this->logger->log(this->logger, CONTROL|LEVEL2, "wait for all threads to leave IKE_SA's"); + this->logger->log(this->logger, CONTROL|LEVEL2, + "wait for all threads to leave IKE_SA's"); /* Step 2: wait until all are gone */ iterator->reset(iterator); while (iterator->has_next(iterator)) diff --git a/src/charon/sa/states/create_child_sa_requested.c b/src/charon/sa/states/create_child_sa_requested.c deleted file mode 100644 index 3e7abe1b8..000000000 --- a/src/charon/sa/states/create_child_sa_requested.c +++ /dev/null @@ -1,435 +0,0 @@ -/** - * @file create_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 <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 <string.h> - -#include "create_child_sa_requested.h" - -#include <sa/child_sa.h> -#include <sa/states/delete_ike_sa_requested.h> -#include <sa/states/ike_sa_established.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <utils/logger_manager.h> - - -typedef struct private_create_child_sa_requested_t private_create_child_sa_requested_t; - -/** - * Private data of a create_child_sa_requested_t object. - */ -struct private_create_child_sa_requested_t { - /** - * Public interface of create_child_sa_requested_t. - */ - create_child_sa_requested_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * nonce chosen by initiator - */ - chunk_t nonce_i; - - /** - * nonce chosen by the responder - */ - chunk_t nonce_r; - - /** - * Policy to use for new child_sa - */ - policy_t *policy; - - /** - * Proposal negotiated - */ - proposal_t *proposal; - - /** - * Negotiated list of traffic selectors for local site - */ - linked_list_t *my_ts; - - /** - * Negotiated list of traffic selectors for remote site - */ - linked_list_t *other_ts; - - /** - * Child SA to create - */ - child_sa_t *child_sa; - - /** - * Reqid of the old CHILD_SA, when rekeying - */ - u_int32_t reqid; - - /** - * Assigned logger. - * - * Is logger of ike_sa! - */ - logger_t *logger; -}; - -/** - * Implementation of private_create_child_sa_requested_t.process_sa_payload. - */ -static status_t process_sa_payload(private_create_child_sa_requested_t *this, sa_payload_t *sa_payload) -{ - proposal_t *proposal, *proposal_tmp; - linked_list_t *proposal_list; - - /* get his selected proposal */ - proposal_list = sa_payload->get_proposals(sa_payload); - /* check count of proposals */ - if (proposal_list->get_count(proposal_list) == 0) - { - /* no proposal? we accept this, but no child sa is built */ - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained no proposals. CHILD_SA not created"); - proposal_list->destroy(proposal_list); - return FAILED; - } - if (proposal_list->get_count(proposal_list) > 1) - { - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained %d proposals. Aborting", - proposal_list->get_count(proposal_list)); - while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) - { - proposal->destroy(proposal); - } - proposal_list->destroy(proposal_list); - return FAILED; - } - - /* we have to re-check here if other's selection is valid */ - proposal = this->policy->select_proposal(this->policy, proposal_list); - /* list not needed anymore */ - while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) - { - proposal_tmp->destroy(proposal_tmp); - } - proposal_list->destroy(proposal_list); - /* got a match? */ - if (proposal == NULL) - { - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA reply contained a not offered proposal. Aborting"); - return FAILED; - } - - /* apply proposal */ - this->proposal = proposal; - - return SUCCESS; -} - -/** - * Implementation of private_create_child_sa_requested_t.process_ts_payload. - */ -static status_t process_ts_payload(private_create_child_sa_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload) -{ - linked_list_t *ts_received, *ts_selected; - traffic_selector_t *ts; - - /* get ts form payload */ - ts_received = ts_payload->get_traffic_selectors(ts_payload); - /* select ts depending on payload type */ - if (ts_initiator) - { - ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received); - this->my_ts = ts_selected; - } - else - { - ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received); - this->other_ts = ts_selected; - } - /* check if the responder selected valid proposals */ - if (ts_selected->get_count(ts_selected) != ts_received->get_count(ts_received)) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained not offered traffic selectors."); - } - - /* cleanup */ - while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - ts_received->destroy(ts_received); - - return SUCCESS; -} - -/** - * Implementation of private_create_child_sa_requested_t.process_nonce_payload. - */ -static status_t process_nonce_payload(private_create_child_sa_requested_t *this, nonce_payload_t *nonce_request) -{ - this->nonce_r = nonce_request->get_nonce(nonce_request); - return SUCCESS; -} - -/** - * Process a CREATE_CHILD_SA response - */ -static status_t process_message(private_create_child_sa_requested_t *this, message_t *response) -{ - ts_payload_t *tsi_request = NULL, *tsr_request = NULL; - sa_payload_t *sa_request = NULL; - nonce_payload_t *nonce_request = NULL; - ike_sa_id_t *ike_sa_id; - iterator_t *payloads; - crypter_t *crypter; - signer_t *signer; - 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) - { - this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state create_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, "CREATE_CHILD_SA requests not allowed state create_child_sa_requested"); - /* TODO: our state implementation currently can not handle incoming requests cleanly here. - * If a request comes in before an outstanding reply, we can not handle it the correct way. - * Currently, we create a ESTABLISHED state and let it process the message... But we - * need changes in the whole state mechanism. - */ - state_t *state = (state_t*)ike_sa_established_create(this->ike_sa); - state->process_message(state, response); - state->destroy(state); - return SUCCESS; - } - - /* 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, "CREATE_CHILD_SA r decryption failed. Ignoring message"); - return status; - } - - /* iterate over incoming payloads. Message is verified, we can be sure there are the required 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 SECURITY_ASSOCIATION: - { - sa_request = (sa_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 NONCE: - { - nonce_request = (nonce_payload_t*)payload; - break; - } - case NOTIFY: - { - /* TODO: handle notifys */ - 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; - } - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(sa_request && nonce_request && tsi_request && tsr_request)) - { - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA request did not contain all required payloads. Ignored"); - return FAILED; - } - - /* add payloads to it */ - status = process_nonce_payload(this, nonce_request); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = process_sa_payload(this, sa_request); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = process_ts_payload(this, TRUE, tsi_request); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = process_ts_payload(this, FALSE, tsr_request); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - - /* install child SAs for AH and esp */ - if (!this->proposal) - { - this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) - { - this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else - { - 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); - - 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); - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA"); - return DESTROY_ME; - } - status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA"); - return DESTROY_ME; - } - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - } - - 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)); - - /* 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->ike_sa->public.delete_child_sa(&this->ike_sa->public, this->reqid); - } - this->public.state_interface.destroy(&this->public.state_interface); - return SUCCESS; -} - -/** - * Implements state_t.get_state - */ -static ike_sa_state_t get_state(private_create_child_sa_requested_t *this) -{ - return CREATE_CHILD_SA_REQUESTED; -} - -/** - * Implementation of state_t.destroy. - */ -static void destroy(private_create_child_sa_requested_t *this) -{ - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - free(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, u_int32_t reqid) -{ - private_create_child_sa_requested_t *this = malloc_thing(private_create_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->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 deleted file mode 100644 index 20c17b6a1..000000000 --- a/src/charon/sa/states/create_child_sa_requested.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file create_child_sa_requested.h - * - * @brief Interface of create_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 <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_REQEUSTED_H_ -#define CREATE_CHILD_SA_REQEUSTED_H_ - -#include <sa/states/state.h> -#include <sa/ike_sa.h> -#include <sa/child_sa.h> - -typedef struct create_child_sa_requested_t create_child_sa_requested_t; - -/** - * @brief State after a CREATE_CHILD_SA request was sent. - * - * @b Constructors: - * - create_child_sa_requested_create() - * - * @ingroup states - */ -struct create_child_sa_requested_t { - /** - * methods of the state_t interface - */ - state_t state_interface; -}; - -/** - * @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, 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 deleted file mode 100644 index 8cad93d8d..000000000 --- a/src/charon/sa/states/delete_child_sa_requested.c +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @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 <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 <string.h> - -#include "delete_child_sa_requested.h" - -#include <sa/child_sa.h> -#include <sa/states/delete_ike_sa_requested.h> -#include <sa/states/ike_sa_established.h> -#include <encoding/payloads/notify_payload.h> -#include <encoding/payloads/delete_payload.h> -#include <utils/logger_manager.h> - - -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"); - /* TODO: our state implementation currently can not handle incoming requests cleanly here. - * If a request comes in before an outstanding reply, we can not handle it cleanly. - * Currently, we create a ESTABLISHED state and let it process the message... But we - * need changes in the whole state mechanism. - */ - state_t *state = (state_t*)ike_sa_established_create(this->ike_sa); - state->process_message(state, response); - state->destroy(state); - return SUCCESS; - } - - /* 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 deleted file mode 100644 index e70ff182d..000000000 --- a/src/charon/sa/states/delete_child_sa_requested.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @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 <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_REQEUSTED_H_ -#define DELETE_CHILD_SA_REQEUSTED_H_ - -#include <sa/states/state.h> -#include <sa/ike_sa.h> -#include <sa/child_sa.h> - -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/delete_ike_sa_requested.c b/src/charon/sa/states/delete_ike_sa_requested.c deleted file mode 100644 index 36e9c2fb2..000000000 --- a/src/charon/sa/states/delete_ike_sa_requested.c +++ /dev/null @@ -1,163 +0,0 @@ -/** - * @file delete_ike_sa_requested.c - * - * @brief Implementation of delete_ike_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 <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_ike_sa_requested.h" - -#include <daemon.h> - - -typedef struct private_delete_ike_sa_requested_t private_delete_ike_sa_requested_t; - -/** - * Private data of a delete_ike_sa_requested_t object. - */ -struct private_delete_ike_sa_requested_t { - - /** - * methods of the state_t interface - */ - delete_ike_sa_requested_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * Assigned logger. Use logger of IKE_SA. - */ - logger_t *logger; -}; - -/** - * Implements state_t.get_state - */ -static status_t process_message(private_delete_ike_sa_requested_t *this, message_t *message) -{ - ike_sa_id_t *ike_sa_id; - crypter_t *crypter; - signer_t *signer; - status_t status; - - /* Notation as follows: - * Mx{D} means: Message, with message ID "x", containing a Delete payload - * - * The clarifcation Document says in 5.8, that a IKE_SA delete should not - * be acknowledged with the same delete. This only makes sense for CHILD_SAs, - * as they are paired. IKE_SAs are not, there is only one for both ends. - * - * Normal case: - * ---------------- - * Mx{D} --> - * <-- Mx{} - * Delete request is sent, and we wait for the acknowledge. - * - * Special case 1: - * --------------- - * Mx{D} --> - * <-- My{D} - * My{} --> - * <-- Mx{} - * Both initate a delete at the same time. We ack the delete, but wait for - * our delete to be acknowledged. - */ - - if (message->get_exchange_type(message) != INFORMATIONAL) - { - /* anything other than information is ignored. We can an will not handle - * messages such as CREATE_CHILD_SA */ - this->logger->log(this->logger, ERROR | LEVEL1, - "%s messages not supported in state delete_ike_sa_requested. Ignored", - mapping_find(exchange_type_m, message->get_exchange_type(message))); - return FAILED; - } - - if (message->get_request(message)) - { - /* if it is a request, not a reply to our delete request, we - * just acknowledge this. We stay in our state, as the other peer - * has to ACK our request. - */ - message_t *acknowledge; - this->ike_sa->build_message(this->ike_sa, INFORMATIONAL, FALSE, &acknowledge); - return this->ike_sa->send_response(this->ike_sa, acknowledge); - } - - /* 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, check if it's proper signed */ - status = message->parse_body(message, crypter, signer); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "INFORMATIONAL message decryption failed. Ignoring message"); - return status; - } - - /* ok, he knows about the deletion, destroy this IKE SA */ - return DESTROY_ME; -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_delete_ike_sa_requested_t *this) -{ - return DELETE_IKE_SA_REQUESTED; -} - -/** - * Implementation of state_t.get_state - */ -static void destroy(private_delete_ike_sa_requested_t *this) -{ - free(this); -} - -/* - * Described in header. - */ -delete_ike_sa_requested_t *delete_ike_sa_requested_create(protected_ike_sa_t *ike_sa) -{ - private_delete_ike_sa_requested_t *this = malloc_thing(private_delete_ike_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_ike_sa_requested.h b/src/charon/sa/states/delete_ike_sa_requested.h deleted file mode 100644 index a010a0f5b..000000000 --- a/src/charon/sa/states/delete_ike_sa_requested.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file delete_ike_sa_requested.h - * - * @brief Interface of delete_ike_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 <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_IKE_SA_REQUESTED_H_ -#define DELETE_IKE_SA_REQUESTED_H_ - -#include <sa/states/state.h> -#include <sa/ike_sa.h> - -typedef struct delete_ike_sa_requested_t delete_ike_sa_requested_t; - -/** - * @brief This class represents an the state of a half closed IKE_SA. - * - * @b Constructors: - * - delete_ike_sa_requested_create() - * - * @ingroup states - */ -struct delete_ike_sa_requested_t { - /** - * methods of the state_t interface - */ - state_t state_interface; - -}; - -/** - * @brief Constructor of class delete_ike_sa_requested_t - * - * @param ike_sa assigned ike_sa - * @return created delete_ike_sa_requested_t object - * - * @ingroup states - */ -delete_ike_sa_requested_t *delete_ike_sa_requested_create(protected_ike_sa_t *ike_sa); - -#endif /*DELETE_IKE_SA_REQUESTED_H_*/ diff --git a/src/charon/sa/states/ike_auth_requested.c b/src/charon/sa/states/ike_auth_requested.c deleted file mode 100644 index b2d42fd60..000000000 --- a/src/charon/sa/states/ike_auth_requested.c +++ /dev/null @@ -1,719 +0,0 @@ -/** - * @file ike_auth_requested.c - * - * @brief Implementation of ike_auth_requested_t. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 <string.h> - -#include "ike_auth_requested.h" - -#include <daemon.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/id_payload.h> -#include <encoding/payloads/cert_payload.h> -#include <encoding/payloads/auth_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <crypto/signers/signer.h> -#include <crypto/crypters/crypter.h> -#include <sa/states/ike_sa_established.h> -#include <sa/authenticator.h> -#include <sa/child_sa.h> - -typedef struct private_ike_auth_requested_t private_ike_auth_requested_t; - -/** - * Private data of a ike_auth_requested_t object. - * - */ -struct private_ike_auth_requested_t { - /** - * Public interface of ike_auth_requested_t. - */ - ike_auth_requested_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * SA config, just a copy of the one stored in the ike_sa. - */ - policy_t *policy; - - /** - * Received nonce from responder. - */ - chunk_t received_nonce; - - /** - * Sent nonce in IKE_SA_INIT request. - */ - chunk_t sent_nonce; - - /** - * IKE_SA_INIT-Request in binary form. - */ - chunk_t ike_sa_init_reply_data; - - /** - * Proposal to setup CHILD_SA - */ - proposal_t *proposal; - - /** - * Traffic selectors applicable at our site - */ - linked_list_t *my_ts; - - /** - * Traffic selectors applicable at remote site - */ - linked_list_t *other_ts; - - /** - * Child sa created in ike_sa_init_requested - */ - child_sa_t *child_sa; - - /** - * Assigned Logger. - * - * Is logger of ike_sa! - */ - logger_t *logger; - - /** - * Process the IDr payload (check if other id is valid) - * - * @param this calling object - * @param idr_payload ID payload of responder - * @return - * - SUCCESS - * - DESTROY_ME - */ - status_t (*process_idr_payload) (private_ike_auth_requested_t *this, id_payload_t *idr_payload); - - /** - * Process received CERT payload - * - * @param this calling object - * @param cert_payload payload to process - * @return - * - DESTROY_ME if IKE_SA should be deleted - * - SUCCSS if processed successful - */ - status_t (*process_cert_payload) (private_ike_auth_requested_t *this, cert_payload_t *cert_payload); - - /** - * Process the SA payload (check if selected proposals are valid, setup child sa) - * - * @param this calling object - * @param sa_payload SA payload of responder - * - * - SUCCESS - * - DESTROY_ME - */ - status_t (*process_sa_payload) (private_ike_auth_requested_t *this, sa_payload_t *sa_payload); - - /** - * Process the AUTH payload (check authenticity of message) - * - * @param this calling object - * @param auth_payload AUTH payload of responder - * @param other_id_payload ID payload of responder - * - * - SUCCESS - * - DESTROY_ME - */ - status_t (*process_auth_payload) (private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload); - - /** - * Process the TS payload (check if selected traffic selectors are valid) - * - * @param this calling object - * @param ts_initiator TRUE if TS payload is TSi, FALSE for TSr - * @param ts_payload TS payload of responder - * - * - SUCCESS - * - DESTROY_ME - */ - status_t (*process_ts_payload) (private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload); - - /** - * Process a notify payload - * - * @param this calling object - * @param notify_payload notify payload - * - * - SUCCESS - * - FAILED - * - DESTROY_ME - */ - status_t (*process_notify_payload) (private_ike_auth_requested_t *this, notify_payload_t *notify_payload); - - /** - * Destroy function called internally of this class after state change to - * state IKE_SA_ESTABLISHED succeeded. - * - * This destroy function does not destroy objects which were passed to the new state. - * - * @param this calling object - */ - void (*destroy_after_state_change) (private_ike_auth_requested_t *this); -}; - - -/** - * Implements state_t.process_message - */ -static status_t process_message(private_ike_auth_requested_t *this, message_t *ike_auth_reply) -{ - ts_payload_t *tsi_payload = NULL; - ts_payload_t *tsr_payload = NULL; - id_payload_t *idr_payload = NULL; - cert_payload_t *cert_payload = NULL; - auth_payload_t *auth_payload = NULL; - sa_payload_t *sa_payload = NULL; - iterator_t *payloads = NULL; - crypter_t *crypter = NULL; - signer_t *signer = NULL; - status_t status; - host_t *my_host, *other_host; - identification_t *my_id, *other_id; - chunk_t seed; - prf_plus_t *prf_plus; - connection_t *connection; - policy_t *policy; - - if (ike_auth_reply->get_exchange_type(ike_auth_reply) != IKE_AUTH) - { - this->logger->log(this->logger, ERROR | LEVEL1, "message of type %s not supported in state ike_auth_requested", - mapping_find(exchange_type_m,ike_auth_reply->get_exchange_type(ike_auth_reply))); - return FAILED; - } - - if (ike_auth_reply->get_request(ike_auth_reply)) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH requests not allowed state ike_sa_init_responded"); - return FAILED; - } - - /* get signer for verification and crypter for decryption */ - signer = this->ike_sa->get_signer_responder(this->ike_sa); - crypter = this->ike_sa->get_crypter_responder(this->ike_sa); - - /* parse incoming message */ - status = ike_auth_reply->parse_body(ike_auth_reply, crypter, signer); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply decryption failed. Ignoring message"); - return status; - } - - this->policy = this->ike_sa->get_policy(this->ike_sa); - - /* we collect all payloads, which are processed later. Notify's are processed - * in place, since we don't know how may are there. - */ - payloads = ike_auth_reply->get_payload_iterator(ike_auth_reply); - while (payloads->has_next(payloads)) - { - payload_t *payload; - payloads->current(payloads, (void**)&payload); - - switch (payload->get_type(payload)) - { - case AUTHENTICATION: - auth_payload = (auth_payload_t*)payload; - break; - case CERTIFICATE: - cert_payload = (cert_payload_t*)payload; - status = this->process_cert_payload(this, cert_payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - - } - break; - case ID_RESPONDER: - idr_payload = (id_payload_t*)payload; - break; - case SECURITY_ASSOCIATION: - sa_payload = (sa_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: - { - notify_payload_t *notify_payload = (notify_payload_t *) payload; - - /* handle the notify directly, abort if no further processing required */ - status = this->process_notify_payload(this, notify_payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - } - 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; - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(idr_payload && sa_payload && auth_payload && tsi_payload && tsr_payload)) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - - status = this->ike_sa->update_connection_hosts(this->ike_sa, - ike_auth_reply->get_destination(ike_auth_reply), - ike_auth_reply->get_source(ike_auth_reply)); - if (status != SUCCESS) - return status; - - /* process all payloads */ - status = this->process_idr_payload(this, idr_payload); - if (status != SUCCESS) - return status; - - status = this->process_auth_payload(this, auth_payload,idr_payload); - if (status != SUCCESS) - return status; - - status = this->process_sa_payload(this, sa_payload); - if (status != SUCCESS) - return status; - - status = this->process_ts_payload(this, TRUE, tsi_payload); - if (status != SUCCESS) - return status; - - status = this->process_ts_payload(this, FALSE, tsr_payload); - if (status != SUCCESS) - return status; - - /* install child SAs for AH and esp */ - if (!this->child_sa) - { - this->logger->log(this->logger, CONTROL, "no CHILD_SA requested, no CHILD_SA built"); - } - else if (!this->proposal) - { - this->logger->log(this->logger, CONTROL, "proposal negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) - { - this->logger->log(this->logger, CONTROL, "traffic selector negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else - { - seed = chunk_alloc(this->sent_nonce.len + this->received_nonce.len); - memcpy(seed.ptr, this->sent_nonce.ptr, this->sent_nonce.len); - memcpy(seed.ptr + this->sent_nonce.len, this->received_nonce.ptr, this->received_nonce.len); - prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); - chunk_free(&seed); - - status = this->child_sa->update(this->child_sa, this->proposal, prf_plus); - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA! Deleting IKE_SA"); - return DESTROY_ME; - } - status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA policy! Deleting IKE_SA"); - return DESTROY_ME; - } - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - } - - this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_auth_reply->get_message_id(ike_auth_reply)); - - /* create new state */ - this->ike_sa->establish(this->ike_sa); - this->destroy_after_state_change(this); - return SUCCESS; -} - -/** - * Implements private_ike_auth_requested_t.process_idr_payload - */ -static status_t process_idr_payload(private_ike_auth_requested_t *this, id_payload_t *idr_payload) -{ - identification_t *other_id, *configured_other_id; - - other_id = idr_payload->get_identification(idr_payload); - configured_other_id = this->policy->get_other_id(this->policy); - - this->logger->log(this->logger, CONTROL|LEVEL1, "configured ID: %s, ID of responder: %s", - configured_other_id->get_string(configured_other_id), - other_id->get_string(other_id)); - - if (!other_id->belongs_to(other_id, configured_other_id)) - { - other_id->destroy(other_id); - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not acceptable ID. Deleting IKE_SA"); - return DESTROY_ME; - } - - this->policy->update_other_id(this->policy, other_id); - return SUCCESS; -} - -/** - * Implements private_ike_auth_requested_t.process_cert_payload - */ -static status_t process_cert_payload(private_ike_auth_requested_t *this, cert_payload_t * cert_payload) -{ - bool found; - x509_t *cert; - - if (cert_payload->get_cert_encoding(cert_payload) != CERT_X509_SIGNATURE) - { - this->logger->log(this->logger, CONTROL, "certificate encoding is %s, ignored", - enum_name(&cert_encoding_names, cert_payload->get_cert_encoding(cert_payload))); - return SUCCESS; - } - cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload)); - - if (charon->credentials->verify(charon->credentials, cert, &found)) - { - this->logger->log(this->logger, CONTROL, "end entity certificate is trusted"); - if (!found) - { - cert = charon->credentials->add_end_certificate(charon->credentials, cert); - } - } - else - { - this->logger->log(this->logger, ERROR, "end entity certificate is not trusted"); - } - return SUCCESS; -} - - -/** - * Implements private_ike_auth_requested_t.process_sa_payload - */ -static status_t process_sa_payload(private_ike_auth_requested_t *this, sa_payload_t *sa_payload) -{ - proposal_t *proposal, *proposal_tmp; - linked_list_t *proposal_list; - - /* get his selected proposal */ - proposal_list = sa_payload->get_proposals(sa_payload); - /* check count of proposals */ - if (proposal_list->get_count(proposal_list) == 0) - { - /* no proposal? we accept this, but no child sa is built */ - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD didn't contain any proposals. No CHILD_SA created", - proposal_list->get_count(proposal_list)); - proposal_list->destroy(proposal_list); - return SUCCESS; - } - if (proposal_list->get_count(proposal_list) > 1) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD contained %d proposal. Deleting IKE_SA", - proposal_list->get_count(proposal_list)); - while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) - { - proposal->destroy(proposal); - } - proposal_list->destroy(proposal_list); - return DESTROY_ME; - } - - /* we have to re-check here if other's selection is valid */ - proposal = this->policy->select_proposal(this->policy, proposal_list); - /* list not needed anymore */ - while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) - { - proposal_tmp->destroy(proposal_tmp); - } - proposal_list->destroy(proposal_list); - /* got a match? */ - if (proposal == NULL) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not offered proposal. Deleting IKE_SA"); - return DESTROY_ME; - } - - /* apply proposal */ - this->proposal = proposal; - - return SUCCESS; -} - -/** - * Implements private_ike_auth_requested_t.process_auth_payload - */ -static status_t process_auth_payload(private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload) -{ - authenticator_t *authenticator; - status_t status; - - authenticator = authenticator_create(this->ike_sa); - status = authenticator->verify_auth_data(authenticator,auth_payload,this->ike_sa_init_reply_data,this->sent_nonce,other_id_payload,FALSE); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "verification of IKE_AUTH reply failed. Deleting IKE_SA"); - return DESTROY_ME; - } - - this->logger->log(this->logger, CONTROL|LEVEL1, "AUTH data verified successfully"); - return SUCCESS; -} - -/** - * Implements private_ike_auth_requested_t.process_ts_payload - */ -static status_t process_ts_payload(private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload) -{ - linked_list_t *ts_received, *ts_selected; - traffic_selector_t *ts; - - /* get ts form payload */ - ts_received = ts_payload->get_traffic_selectors(ts_payload); - /* select ts depending on payload type */ - if (ts_initiator) - { - ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received); - this->my_ts = ts_selected; - } - else - { - ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received); - this->other_ts = ts_selected; - } - /* check if the responder selected valid proposals */ - if (ts_selected->get_count(ts_selected) != ts_received->get_count(ts_received)) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained not offered traffic selectors."); - } - - /* cleanup */ - while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - ts_received->destroy(ts_received); - - return SUCCESS; -} - -/** - * Implements private_ike_auth_requested_t.process_notify_payload - */ -static status_t process_notify_payload(private_ike_auth_requested_t *this, notify_payload_t *notify_payload) -{ - notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); - - this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", - mapping_find(notify_message_type_m, notify_message_type)); - - switch (notify_message_type) - { - case INVALID_SYNTAX: - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an INVALID_SYNTAX notify. Deleting IKE_SA"); - return DESTROY_ME; - - } - case AUTHENTICATION_FAILED: - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an AUTHENTICATION_FAILED notify. Deleting IKE_SA"); - return DESTROY_ME; - - } - case SINGLE_PAIR_REQUIRED: - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a SINGLE_PAIR_REQUIRED notify. Deleting IKE_SA"); - return DESTROY_ME; - } - case TS_UNACCEPTABLE: - { - /* TODO: We currently check only the replied TS payloads, which should be empty. Should - * we interpret the notify additionaly? */ - this->logger->log(this->logger, CONTROL, "IKE_AUTH reply contained a TS_UNACCEPTABLE notify. Ignored"); - return SUCCESS; - } - case NO_PROPOSAL_CHOSEN: - { - /* TODO: We currently check only the replied SA payload, which should be empty. Should - * we interpret the notify additionaly? */ - this->logger->log(this->logger, CONTROL, "IKE_AUTH reply contained a NO_PROPOSAL_CHOSEN notify. Ignored"); - return SUCCESS; - } - default: - { - /* - * - In case of unknown error: IKE_SA gets destroyed. - * - In case of unknown status: logging - */ - - if (notify_message_type < 16383) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an unknown notify error (%d). Deleting IKE_SA", - notify_message_type); - return DESTROY_ME; - - } - else - { - this->logger->log(this->logger, CONTROL, "IKE_AUTH reply contained an unknown notify (%d), ignored.", - notify_message_type); - return SUCCESS; - } - } - } -} - -/** - * Implements state_t.get_state - */ -static ike_sa_state_t get_state(private_ike_auth_requested_t *this) -{ - return IKE_AUTH_REQUESTED; -} - -/** - * Implements state_t.get_state - */ -static void destroy(private_ike_auth_requested_t *this) -{ - chunk_free(&(this->received_nonce)); - chunk_free(&(this->sent_nonce)); - chunk_free(&(this->ike_sa_init_reply_data)); - if (this->child_sa) - { - this->child_sa->destroy(this->child_sa); - } - if (this->my_ts) - { - traffic_selector_t *ts; - while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->my_ts->destroy(this->my_ts); - } - if (this->other_ts) - { - traffic_selector_t *ts; - while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->other_ts->destroy(this->other_ts); - } - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - free(this); -} -/** - * Implements protected_ike_sa_t.destroy_after_state_change - */ -static void destroy_after_state_change(private_ike_auth_requested_t *this) -{ - chunk_free(&(this->received_nonce)); - chunk_free(&(this->sent_nonce)); - chunk_free(&(this->ike_sa_init_reply_data)); - if (this->my_ts) - { - traffic_selector_t *ts; - while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->my_ts->destroy(this->my_ts); - } - if (this->other_ts) - { - traffic_selector_t *ts; - while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->other_ts->destroy(this->other_ts); - } - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - free(this); -} - -/* - * Described in header. - */ -ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa,chunk_t sent_nonce,chunk_t received_nonce,chunk_t ike_sa_init_reply_data, child_sa_t *child_sa) -{ - private_ike_auth_requested_t *this = malloc_thing(private_ike_auth_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 functions */ - this->process_idr_payload = process_idr_payload; - this->process_cert_payload = process_cert_payload; - this->process_sa_payload = process_sa_payload; - this->process_auth_payload = process_auth_payload; - this->process_ts_payload = process_ts_payload; - this->process_notify_payload = process_notify_payload; - this->destroy_after_state_change = destroy_after_state_change; - - /* private data */ - this->ike_sa = ike_sa; - this->received_nonce = received_nonce; - this->sent_nonce = sent_nonce; - this->ike_sa_init_reply_data = ike_sa_init_reply_data; - this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - this->my_ts = NULL; - this->other_ts = NULL; - this->proposal = NULL; - this->child_sa = child_sa; - - return &(this->public); -} diff --git a/src/charon/sa/states/ike_auth_requested.h b/src/charon/sa/states/ike_auth_requested.h deleted file mode 100644 index a8eef014c..000000000 --- a/src/charon/sa/states/ike_auth_requested.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file ike_auth_requested.h - * - * @brief Interface of ike_auth_requested_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 IKE_AUTH_REQUESTED_H_ -#define IKE_AUTH_REQUESTED_H_ - -#include <sa/states/state.h> -#include <sa/ike_sa.h> - - -typedef struct ike_auth_requested_t ike_auth_requested_t; - -/** - * @brief This class represents an IKE_SA, which has requested an IKE_AUTH. - * - * The state accpets IKE_AUTH responses. It proves the authenticity - * and sets up the first child sa. After that, it changes IKE_SA state to - * IKE_SA_ESTABLISHED. - * - * @ Constructors: - * - ike_auth_requested_create() - * - * @todo handle certificate payloads - * - * @ingroup states - */ -struct ike_auth_requested_t { - /** - * The state_t interface. - */ - state_t state_interface; - -}; - -/** - * Constructor of class ike_auth_requested_t - * - * @param ike_sa assigned ike_sa object - * @param sent_nonce Sent nonce value in IKE_SA_INIT request - * @param received_nonce Received nonce value in IKE_SA_INIT response - * @param ike_sa_init_reply_data binary representation of IKE_SA_INIT reply - * @param child_sa opened but not completed child_sa - * @return created ike_auth_requested_t object - * - * @ingroup states - */ -ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa, - chunk_t sent_nonce, - chunk_t received_nonce, - chunk_t ike_sa_init_reply_data, - child_sa_t *child_sa); - -#endif /*IKE_AUTH_REQUESTED_H_*/ diff --git a/src/charon/sa/states/ike_sa_established.c b/src/charon/sa/states/ike_sa_established.c deleted file mode 100644 index 1a29fbba3..000000000 --- a/src/charon/sa/states/ike_sa_established.c +++ /dev/null @@ -1,671 +0,0 @@ -/** - * @file ike_sa_established.c - * - * @brief Implementation of ike_sa_established_t. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 <string.h> - -#include "ike_sa_established.h" - -#include <daemon.h> -#include <encoding/payloads/delete_payload.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <sa/child_sa.h> -#include <sa/states/delete_ike_sa_requested.h> -#include <queues/jobs/send_dpd_job.h> - -typedef struct private_ike_sa_established_t private_ike_sa_established_t; - -/** - * Private data of a ike_sa_established_t object. - */ -struct private_ike_sa_established_t { - /** - * methods of the state_t interface - */ - ike_sa_established_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * Nonce for a new child SA, chosen by initiator - */ - chunk_t nonce_i; - - /** - * Nonce for a new child SA, chosen by responder - */ - chunk_t nonce_r; - - /** - * Traffic selectors for a new child SA, responder side - */ - linked_list_t *my_ts; - - /** - * Traffic selectors for a new child SA, initiator side - */ - linked_list_t *other_ts; - - /** - * Newly set up child sa - */ - child_sa_t *child_sa; - - /** - * Old child sa, if we are rekeying - */ - child_sa_t *old_child_sa; - - /** - * Assigned logger. Use logger of IKE_SA. - */ - logger_t *logger; -}; - -/** - * Schedule send dpd job - */ -static void schedule_dpd_job(private_ike_sa_established_t *this) -{ - u_int32_t interval = charon->configuration->get_dpd_interval(charon->configuration); - - if (interval) - { - charon->event_queue->add_relative(charon->event_queue, - (job_t*)send_dpd_job_create(this->ike_sa->public.get_id(&this->ike_sa->public)), - interval); - } -} - -/** - * Implementation of private_ike_sa_established_t.build_sa_payload. - */ -static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_t *request, message_t *response) -{ - proposal_t *proposal, *proposal_tmp; - linked_list_t *proposal_list; - sa_payload_t *sa_response; - chunk_t seed; - prf_plus_t *prf_plus; - status_t status; - connection_t *connection; - policy_t *policy; - u_int32_t reqid = 0; - bool use_natt; - - /* prepare reply */ - sa_response = sa_payload_create(); - - /* get proposals from request, and select one with ours */ - policy = this->ike_sa->get_policy(this->ike_sa); - proposal_list = request->get_proposals(request); - this->logger->log(this->logger, CONTROL|LEVEL1, "selecting proposals:"); - proposal = policy->select_proposal(policy, proposal_list); - /* list is not needed anymore */ - while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) - { - proposal_tmp->destroy(proposal_tmp); - } - proposal_list->destroy(proposal_list); - /* do we have a proposal? */ - if (proposal == NULL) - { - notify_payload_t *notify; - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA request did not contain any proposals we accept. " - "Adding NO_PROPOSAL_CHOSEN notify"); - /* add NO_PROPOSAL_CHOSEN and an empty SA payload */ - notify = notify_payload_create_from_protocol_and_type(PROTO_IKE, NO_PROPOSAL_CHOSEN); - response->add_payload(response, (payload_t*)notify); - } - else - { - /* set up child sa */ - 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); - this->logger->log_chunk(this->logger, RAW|LEVEL2, "sekey seed", seed); - chunk_free(&seed); - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - - policy = this->ike_sa->get_policy(this->ike_sa); - connection = this->ike_sa->get_connection(this->ike_sa); - if (this->old_child_sa) - { /* reuse old reqid if we are rekeying */ - reqid = this->old_child_sa->get_reqid(this->old_child_sa); - } - use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public); - this->child_sa = child_sa_create(reqid, - connection->get_my_host(connection), - connection->get_other_host(connection), - policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy), - use_natt); - - status = this->child_sa->add(this->child_sa, proposal, prf_plus); - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "sould not install CHILD_SA!"); - sa_response->destroy(sa_response); - proposal->destroy(proposal); - return DESTROY_ME; - } - - /* add proposal to sa payload */ - sa_response->add_proposal(sa_response, proposal); - proposal->destroy(proposal); - } - response->add_payload(response, (payload_t*)sa_response); - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_established_t.build_ts_payload. - */ -static status_t build_ts_payload(private_ike_sa_established_t *this, bool ts_initiator, ts_payload_t *request, message_t* response) -{ - linked_list_t *ts_received, *ts_selected; - traffic_selector_t *ts; - status_t status = SUCCESS; - ts_payload_t *ts_response; - policy_t *policy; - - policy = this->ike_sa->get_policy(this->ike_sa); - - /* build a reply payload with selected traffic selectors */ - ts_received = request->get_traffic_selectors(request); - /* select ts depending on payload type */ - if (ts_initiator) - { - ts_selected = policy->select_other_traffic_selectors(policy, ts_received); - this->other_ts = ts_selected; - } - else - { - ts_selected = policy->select_my_traffic_selectors(policy, ts_received); - this->my_ts = ts_selected; - } - - ts_response = ts_payload_create_from_traffic_selectors(ts_initiator, ts_selected); - response->add_payload(response, (payload_t*)ts_response); - - /* add notify if traffic selectors do not match */ - if (!ts_initiator && - (ts_selected->get_count(ts_selected) == 0 || this->other_ts->get_count(this->other_ts) == 0)) - { - notify_payload_t *notify; - - this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any traffic selectors we accept. " - "Adding TS_UNACCEPTABLE notify"); - - notify = notify_payload_create_from_protocol_and_type(0, TS_UNACCEPTABLE); - response->add_payload(response, (payload_t*)notify); - } - - /* cleanup */ - while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - ts_received->destroy(ts_received); - - return status; -} - -/** - * Implementation of private_ike_sa_established_t.build_nonce_payload. - */ -static status_t build_nonce_payload(private_ike_sa_established_t *this, nonce_payload_t *nonce_request, message_t *response) -{ - nonce_payload_t *nonce_payload; - randomizer_t *randomizer; - status_t status; - - randomizer = this->ike_sa->get_randomizer(this->ike_sa); - status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &this->nonce_r); - if (status != SUCCESS) - { - return status; - } - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->nonce_r); - - response->add_payload(response,(payload_t *) nonce_payload); - - this->nonce_i = nonce_request->get_nonce(nonce_request); - - return SUCCESS; -} - -/** - * Process a CREATE_CHILD_SA request - */ -static status_t process_create_child_sa(private_ike_sa_established_t *this, message_t *request, message_t *response) -{ - 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; - - /* iterate over incoming payloads. Message is verified, we can be sure there are the required 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 TRAFFIC_SELECTOR_INITIATOR: - { - tsi_request = (ts_payload_t*)payload; - break; - } - case TRAFFIC_SELECTOR_RESPONDER: - { - tsr_request = (ts_payload_t*)payload; - break; - } - case NONCE: - { - nonce_request = (nonce_payload_t*)payload; - break; - } - case KEY_EXCHANGE: - { - /* we currently do not support a diffie hellman exchange - * for CHILD_SAs. */ - u_int16_t no_group[1]; - no_group[0] = htons(MODP_NONE); - chunk_t no_group_chunk = chunk_from_buf((u_int8_t*)no_group); - this->ike_sa->send_notify(this->ike_sa, CREATE_CHILD_SA, INVALID_KE_PAYLOAD, no_group_chunk); - payloads->destroy(payloads); - return FAILED; - } - case NOTIFY: - { - notify = (notify_payload_t*)payload; - break; - } - default: - { - this->logger->log(this->logger, ERROR|LEVEL1, "sgnoring payload %s (%d)", - mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); - break; - } - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(sa_request && nonce_request && tsi_request && tsr_request)) - { - this->logger->log(this->logger, AUDIT, "CREATE_CHILD_SA request did not contain all required payloads. Ignored"); - return FAILED; - } - - if (notify && notify->get_notify_message_type(notify) == REKEY_SA) - { - u_int32_t spi = notify->get_spi(notify); - this->old_child_sa = this->ike_sa->get_child_sa(this->ike_sa, spi); - this->logger->log(this->logger, CONTROL, "sekeying CHILD_SA with SPI 0x%x", spi); - } - else - { - this->logger->log(this->logger, CONTROL, "create new CHILD_SA"); - } - - /* build response */ - this->ike_sa->build_message(this->ike_sa, CREATE_CHILD_SA, FALSE, &response); - - /* add payloads to it */ - status = build_nonce_payload(this, nonce_request, response); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = build_sa_payload(this, sa_request, response); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = build_ts_payload(this, TRUE, tsi_request, response); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - status = build_ts_payload(this, FALSE, tsr_request, response); - if (status != SUCCESS) - { - response->destroy(response); - return status; - } - - status = this->ike_sa->send_response(this->ike_sa, response); - /* message can now be sent (must not be destroyed) */ - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to send CREATE_CHILD_SA reply. Ignored"); - response->destroy(response); - return FAILED; - } - - /* install child SA policies */ - if (!this->child_sa) - { - this->logger->log(this->logger, ERROR, "proposal negotiation failed, no CHILD_SA built"); - } - else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) - { - this->logger->log(this->logger, ERROR, "traffic selector negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else - { - status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA policy!"); - } - if (this->old_child_sa) - { /* mark old child sa as rekeyed */ - this->old_child_sa->set_rekeyed(this->old_child_sa); - } - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - } - - return SUCCESS; -} - - -/** - * Process an informational request - */ -static status_t process_informational(private_ike_sa_established_t *this, message_t *request, message_t *response) -{ - delete_payload_t *delete_request = NULL; - iterator_t *payloads = request->get_payload_iterator(request); - - if (!payloads->get_count(payloads)) - { - this->logger->log(this->logger, CONTROL, "DPD request received."); - } - - while (payloads->has_next(payloads)) - { - payload_t *payload; - payloads->current(payloads, (void**)&payload); - - switch (payload->get_type(payload)) - { - case DELETE: - { - delete_request = (delete_payload_t *) payload; - 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; - } - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - if (delete_request) - { - if (delete_request->get_protocol_id(delete_request) == PROTO_IKE) - { - this->logger->log(this->logger, CONTROL, "DELETE request for IKE_SA received"); - /* switch to delete_ike_sa_requested. This is not absolutely correct, but we - * allow the clean destruction of an SA only in this state. */ - this->ike_sa->set_new_state(this->ike_sa, (state_t*)delete_ike_sa_requested_create(this->ike_sa)); - this->public.state_interface.destroy(&(this->public.state_interface)); - this->ike_sa->send_response(this->ike_sa, response); - return DESTROY_ME; - } - else - { - 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); - } - } - - if (this->ike_sa->send_response(this->ike_sa, response) != SUCCESS) - { - /* something is seriously wrong, kill connection */ - this->logger->log(this->logger, AUDIT, "unable to send reply. Deleting IKE_SA"); - response->destroy(response); - return DESTROY_ME; - } - return SUCCESS; -} - -/** - * Process an informational response - */ -static status_t process_informational_response(private_ike_sa_established_t *this, message_t *message) -{ - iterator_t *payloads = message->get_payload_iterator(message); - - if (!payloads->get_count(payloads)) - { - if (message->get_message_id(message) - != this->ike_sa->get_last_dpd_message_id(this->ike_sa)) - { - this->logger->log(this->logger, ERROR|LEVEL1, "DPD response received that does not match our last sent dpd message."); - payloads->destroy(payloads); - return FAILED; - } - - this->logger->log(this->logger, CONTROL, "DPD response received. Schedule job."); - schedule_dpd_job(this); - - payloads->destroy(payloads); - return SUCCESS; - } - - while (payloads->has_next(payloads)) - { - payload_t *payload; - payloads->current(payloads, (void**)&payload); - - switch (payload->get_type(payload)) - { - 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; - } - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - return SUCCESS; -} - -/** - * Implements state_t.get_state - * Implements state_t.process_message - */ -static status_t process_message(private_ike_sa_established_t *this, message_t *message) -{ - ike_sa_id_t *ike_sa_id; - message_t *response; - crypter_t *crypter; - signer_t *signer; - status_t status; - - /* 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 = message->parse_body(message, crypter, signer); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "%s request decryption failed. Ignoring message", - mapping_find(exchange_type_m, message->get_exchange_type(message))); - return status; - } - - status = this->ike_sa->update_connection_hosts(this->ike_sa, - message->get_destination(message), message->get_source(message)); - if (status != SUCCESS) - { - return status; - } - - /* process responses */ - if (!message->get_request(message)) - { - switch (message->get_exchange_type(message)) - { - case INFORMATIONAL: - status = process_informational_response(this, message); - break; - default: - this->logger->log(this->logger, ERROR | LEVEL1, - "Only INFORMATIONAL responses are handled in state ike_sa_established"); - status = FAILED; - break; - } - - /* we don't really reply to this message but the retransmit mechanism relies on this */ - this->ike_sa->set_last_replied_message_id(this->ike_sa, message->get_message_id(message)); - - /* return here */ - return status; - } - - /* prepare a reply of the same type */ - this->ike_sa->build_message(this->ike_sa, message->get_exchange_type(message), FALSE, &response); - - /* handle the different message types in their functions */ - switch (message->get_exchange_type(message)) - { - case INFORMATIONAL: - status = process_informational(this, message, response); - break; - case CREATE_CHILD_SA: - status = process_create_child_sa(this, message, response); - break; - default: - this->logger->log(this->logger, ERROR | LEVEL1, - "message of type %s not supported in state ike_sa_established", - mapping_find(exchange_type_m, message->get_exchange_type(message))); - status = NOT_SUPPORTED; - } - - return status; -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_ike_sa_established_t *this) -{ - return IKE_SA_ESTABLISHED; -} - -/** - * Implementation of state_t.get_state - */ -static void destroy(private_ike_sa_established_t *this) -{ - chunk_free(&this->nonce_i); - chunk_free(&this->nonce_r); - free(this); -} - -/* - * Described in header. - */ -ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa) -{ - private_ike_sa_established_t *this = malloc_thing(private_ike_sa_established_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); - this->nonce_i = CHUNK_INITIALIZER; - this->nonce_r = CHUNK_INITIALIZER; - this->old_child_sa = NULL; - - /* schedule initial dpd job */ - schedule_dpd_job(this); - - return &(this->public); -} diff --git a/src/charon/sa/states/ike_sa_established.h b/src/charon/sa/states/ike_sa_established.h deleted file mode 100644 index 8477ad5bc..000000000 --- a/src/charon/sa/states/ike_sa_established.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file ike_sa_established.h - * - * @brief Interface of ike_sa_established_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 IKE_SA_ESTABLISHED_H_ -#define IKE_SA_ESTABLISHED_H_ - -#include <sa/states/state.h> -#include <sa/ike_sa.h> - -typedef struct ike_sa_established_t ike_sa_established_t; - -/** - * @brief This class represents an the state of an established - * IKE_SA. - * - * @b Constructors: - * - ike_sa_established_create() - * - * @todo Implement handling of CREATE_CHILD_SA requests - * - * @todo Implement initialization of CREATE_CHILD_SA requests - * - * @todo Implement handling of any other message - * - * @ingroup states - */ -struct ike_sa_established_t { - /** - * methods of the state_t interface - */ - state_t state_interface; - -}; - -/** - * @brief Constructor of class ike_sa_established_t - * - * @param ike_sa assigned ike_sa - * @return created ike_sa_established_t object - * - * @ingroup states - */ -ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa); - -#endif /*IKE_SA_ESTABLISHED_H_*/ diff --git a/src/charon/sa/states/ike_sa_init_requested.c b/src/charon/sa/states/ike_sa_init_requested.c deleted file mode 100644 index 6f121ac18..000000000 --- a/src/charon/sa/states/ike_sa_init_requested.c +++ /dev/null @@ -1,990 +0,0 @@ -/** - * @file ike_sa_init_requested.c - * - * @brief Implementation of ike_sa_init_requested_t. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_init_requested.h" - -#include <daemon.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <encoding/payloads/id_payload.h> -#include <encoding/payloads/cert_payload.h> -#include <encoding/payloads/certreq_payload.h> -#include <encoding/payloads/auth_payload.h> -#include <encoding/payloads/ts_payload.h> -#include <crypto/diffie_hellman.h> -#include <sa/states/ike_auth_requested.h> -#include <sa/states/initiator_init.h> -#include <sa/authenticator.h> - - -typedef struct private_ike_sa_init_requested_t private_ike_sa_init_requested_t; - -/** - * Private data of a ike_sa_init_requested_t object. - * - */ -struct private_ike_sa_init_requested_t { - /** - * Public interface of an ike_sa_init_requested_t object. - */ - ike_sa_init_requested_t public; - - /** - * Assigned IKE_SA - */ - protected_ike_sa_t *ike_sa; - - /** - * Diffie Hellman object used to compute shared secret. - */ - diffie_hellman_t *diffie_hellman; - - /** - * Sent nonce value. - */ - chunk_t sent_nonce; - - /** - * Received nonce - */ - chunk_t received_nonce; - - /** - * Selected proposal - */ - proposal_t *proposal; - - /** - * Packet data of ike_sa_init request - */ - chunk_t ike_sa_init_request_data; - - /** - * Created child sa, if any - */ - child_sa_t *child_sa; - - /** - * Assigned logger - * - * Is logger of ike_sa! - */ - logger_t *logger; - - /** - * Precomputed NAT-D hash for initiator. - */ - chunk_t natd_hash_i; - - /** - * Flag indicating that an initiator NAT-D hash matched. - */ - bool natd_hash_i_matched; - - /** - * NAT-D payload count for NAT_DETECTION_SOURCE_IP. - */ - int natd_seen_i; - - /** - * Precomputed NAT-D hash of responder. - */ - chunk_t natd_hash_r; - - /** - * Flag indicating that a responder NAT-D hash matched. - */ - bool natd_hash_r_matched; - - /** - * NAT-D payload count for NAT_DETECTION_DESTINATION_IP. - */ - int natd_seen_r; - - - /** - * Process NONCE payload of IKE_SA_INIT response. - * - * @param this calling object - * @param nonce_payload NONCE payload to process - * @return SUCCESS in any case - */ - status_t (*process_nonce_payload) (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload); - - /** - * Process SA payload of IKE_SA_INIT response. - * - * @param this calling object - * @param sa_payload SA payload to process - * @return - * - SUCCESS - * - FAILED - */ - status_t (*process_sa_payload) (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload); - - /** - * Process KE payload of IKE_SA_INIT response. - * - * @param this calling object - * @param sa_payload KE payload to process - * @return - * - SUCCESS - * - FAILED - */ - status_t (*process_ke_payload) (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload); - - /** - * Build ID payload for IKE_AUTH request. - * - * @param this calling object - * @param[out] id_payload buildet ID payload - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_id_payload) (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *msg); - - /** - * Build CERT payload for IKE_AUTH request. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_cert_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Build CERTREQ payload for IKE_AUTH request. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_certreq_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Build IDr payload for IKE_AUTH request. - * - * Only built when the ID of the responder contains no wildcards. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_idr_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Build AUTH payload for IKE_AUTH request. - * - * @param this calling object - * @param my_id_payload buildet ID payload - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_auth_payload) (private_ike_sa_init_requested_t *this,id_payload_t *my_id_payload, message_t *msg); - - /** - * Build SA payload for IKE_AUTH request. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_sa_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Build TSi payload for IKE_AUTH request. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_tsi_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Build TSr payload for IKE_AUTH request. - * - * @param this calling object - * @param msg created payload will be added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_tsr_payload) (private_ike_sa_init_requested_t *this, message_t *msg); - - /** - * Process a notify payload and react. - * - * @param this calling object - * @param notify_payload notify_payload to handle - */ - status_t (*process_notify_payload) (private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload); - - /** - * Destroy function called internally of this class after state change to - * state IKE_AUTH_REQUESTED succeeded. - * - * This destroy function does not destroy objects which were passed to the new state. - * - * @param this calling object - */ - void (*destroy_after_state_change) (private_ike_sa_init_requested_t *this); -}; - -/** - * Implementation of state_t.process_message. - */ -static status_t process_message(private_ike_sa_init_requested_t *this, message_t *ike_sa_init_reply) -{ - ike_auth_requested_t *next_state; - chunk_t ike_sa_init_reply_data; - sa_payload_t *sa_payload = NULL; - ke_payload_t *ke_payload = NULL; - id_payload_t *id_payload = NULL; - nonce_payload_t *nonce_payload = NULL; - u_int64_t responder_spi; - ike_sa_id_t *ike_sa_id; - iterator_t *payloads; - host_t *me, *other; - connection_t *connection; - policy_t *policy; - - message_t *request; - status_t status; - - /* - * In this state a reply message of type IKE_SA_INIT is expected: - * - * <-- HDR, SAr1, KEr, Nr, [CERTREQ] - * or - * <-- HDR, N - */ - - if (ike_sa_init_reply->get_exchange_type(ike_sa_init_reply) != IKE_SA_INIT) - { - this->logger->log(this->logger, ERROR | LEVEL1, "message of type %s not supported in state ike_sa_init_requested", - mapping_find(exchange_type_m,ike_sa_init_reply->get_exchange_type(ike_sa_init_reply))); - return FAILED; - } - - if (ike_sa_init_reply->get_request(ike_sa_init_reply)) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT requests not allowed state ike_sa_init_responded"); - return FAILED; - } - - /* parse incoming message */ - status = ike_sa_init_reply->parse_body(ike_sa_init_reply, NULL, NULL); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply parsing faild. Ignoring message"); - return status; - } - - /* because we are original initiator we have to update the responder SPI to the new one */ - responder_spi = ike_sa_init_reply->get_responder_spi(ike_sa_init_reply); - if (responder_spi == 0) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply contained a SPI of zero"); - return FAILED; - } - ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); - ike_sa_id->set_responder_spi(ike_sa_id,responder_spi); - - /* - * Precompute NAT-D hashes. - * Even though there SHOULD only be a single payload of each - * Notify type, we precompute both hashes. - */ - this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa, - ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply), - ike_sa_init_reply->get_responder_spi(ike_sa_init_reply), - ike_sa_init_reply->get_source(ike_sa_init_reply)); - this->natd_hash_i_matched = FALSE; - this->natd_seen_i = 0; - this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa, - ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply), - ike_sa_init_reply->get_responder_spi(ike_sa_init_reply), - ike_sa_init_reply->get_destination(ike_sa_init_reply)); - this->natd_hash_r_matched = FALSE; - this->natd_seen_r = 0; - this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE); - this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE); - - /* Iterate over all payloads. - * - * The message is already checked for the right payload types. - */ - payloads = ike_sa_init_reply->get_payload_iterator(ike_sa_init_reply); - 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 KEY_EXCHANGE: - ke_payload = (ke_payload_t*)payload; - break; - case NONCE: - nonce_payload = (nonce_payload_t*)payload; - break; - case NOTIFY: - { - notify_payload_t *notify_payload = (notify_payload_t *) payload; - - status = this->process_notify_payload(this, notify_payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - 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); - - if (!(nonce_payload && sa_payload && ke_payload)) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply did not contain all required payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - - status = this->process_nonce_payload (this,nonce_payload); - if (status != SUCCESS) - return status; - - status = this->process_sa_payload (this,sa_payload); - if (status != SUCCESS) - return status; - - status = this->process_ke_payload (this,ke_payload); - if (status != SUCCESS) - return status; - - /* derive all the keys used in the IKE_SA */ - status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->sent_nonce, this->received_nonce); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "transform objects could not be created from selected proposal. Deleting IKE_SA"); - return DESTROY_ME; - } - - /* NAT-D */ - if ((!this->natd_seen_i && this->natd_seen_r > 0) - || (this->natd_seen_i > 0 && !this->natd_seen_r)) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - if (this->natd_seen_r > 1) - { - this->logger->log(this->logger, AUDIT, "warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads."); - } - if (this->natd_seen_i > 0 && !this->natd_hash_i_matched) - { - this->logger->log(this->logger, AUDIT, "remote host is behind NAT, using NAT-Traversal"); - this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE); - } - if (this->natd_seen_r > 0 && !this->natd_hash_r_matched) - { - this->logger->log(this->logger, AUDIT, "local host is behind NAT, using NAT-Traversal"); - this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE); - } - - /* apply the address on wich we really received the packet, - * and switch to port 4500 when using NAT-T and NAT was detected. - */ - connection = this->ike_sa->get_connection(this->ike_sa); - me = ike_sa_init_reply->get_destination(ike_sa_init_reply); - other = ike_sa_init_reply->get_source(ike_sa_init_reply); - - if (this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa)) - { - me->set_port(me, IKEV2_NATT_PORT); - other->set_port(other, IKEV2_NATT_PORT); - this->logger->log(this->logger, AUDIT, "switching to port %d.", IKEV2_NATT_PORT); - } - else - { - this->logger->log(this->logger, AUDIT, "no NAT detected, not using NAT-Traversal"); - } - - if (this->ike_sa->public.is_my_host_behind_nat(&this->ike_sa->public)) - { - charon->event_queue->add_relative(charon->event_queue, - (job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)), - charon->configuration->get_keepalive_interval(charon->configuration)); - } - - status = this->ike_sa->update_connection_hosts(this->ike_sa, me, other); - if (status != SUCCESS) - return status; - - policy = this->ike_sa->get_policy(this->ike_sa); - policy->update_my_ts(policy, me); - policy->update_other_ts(policy, other); - - /* build empty message */ - this->ike_sa->build_message(this->ike_sa, IKE_AUTH, TRUE, &request); - - status = this->build_id_payload(this, &id_payload, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_cert_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_certreq_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_idr_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_auth_payload(this, (id_payload_t*)id_payload, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_sa_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_tsi_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - status = this->build_tsr_payload(this, request); - if (status != SUCCESS) - goto destroy_request; - - /* message can now be sent (must not be destroyed) */ - status = this->ike_sa->send_request(this->ike_sa, request); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to send IKE_AUTH request. Deleting IKE_SA"); - request->destroy(request); - return DESTROY_ME; - } - - this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_sa_init_reply->get_message_id(ike_sa_init_reply)); - - ike_sa_init_reply_data = ike_sa_init_reply->get_packet_data(ike_sa_init_reply); - - /* state can now be changed */ - next_state = ike_auth_requested_create(this->ike_sa, this->sent_nonce, this->received_nonce, - ike_sa_init_reply_data, this->child_sa); - this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state); - - this->destroy_after_state_change(this); - return SUCCESS; - -destroy_request: - request->destroy(request); - return status; - -} - - -/** - * Implementation of private_ike_sa_init_requested_t.process_nonce_payload. - */ -status_t process_nonce_payload (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload) -{ - free(this->received_nonce.ptr); - this->received_nonce = nonce_payload->get_nonce(nonce_payload); - return SUCCESS; -} - - -/** - * Implementation of private_ike_sa_init_requested_t.process_sa_payload. - */ -status_t process_sa_payload (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload) -{ - proposal_t *proposal; - linked_list_t *proposal_list; - connection_t *connection; - - connection = this->ike_sa->get_connection(this->ike_sa); - - /* get the list of selected proposals, the peer has to select only one proposal */ - proposal_list = sa_payload->get_proposals (sa_payload); - if (proposal_list->get_count(proposal_list) != 1) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response did not contain a single proposal. Deleting IKE_SA"); - while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) - { - proposal->destroy(proposal); - } - proposal_list->destroy(proposal_list); - return DESTROY_ME; - } - - /* we have to re-check if the others selection is valid */ - this->proposal = connection->select_proposal(connection, proposal_list); - while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) - { - proposal->destroy(proposal); - } - proposal_list->destroy(proposal_list); - - if (this->proposal == NULL) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained selected proposal we did not offer. Deleting IKE_SA"); - return DESTROY_ME; - } - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.process_ke_payload. - */ -status_t process_ke_payload (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload) -{ - this->diffie_hellman->set_other_public_value(this->diffie_hellman, ke_payload->get_key_exchange_data(ke_payload)); - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_id_payload. - */ -static status_t build_id_payload (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *msg) -{ - policy_t *policy; - identification_t *my_id; - id_payload_t *new_id_payload; - - policy = this->ike_sa->get_policy(this->ike_sa); - my_id = policy->get_my_id(policy); - new_id_payload = id_payload_create_from_identification(TRUE, my_id); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add ID payload to message"); - msg->add_payload(msg, (payload_t *) new_id_payload); - - *id_payload = new_id_payload; - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_cert_payload. - */ -static status_t build_cert_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - connection_t *connection = this->ike_sa->get_connection(this->ike_sa); - - if (connection->get_cert_policy(connection) != CERT_NEVER_SEND) - { - policy_t *policy; - identification_t *my_id; - x509_t *cert; - cert_payload_t *cert_payload; - - policy = this->ike_sa->get_policy(this->ike_sa); - my_id = policy->get_my_id(policy); - - cert = charon->credentials->get_certificate(charon->credentials, my_id); - if (cert == NULL) - { - this->logger->log(this->logger, ERROR, "could not find my certificate"); - return NOT_FOUND; - } - cert_payload = cert_payload_create_from_x509(cert); - this->logger->log(this->logger, CONTROL|LEVEL2, "add CERT payload to message"); - msg->add_payload(msg, (payload_t *) cert_payload); - } - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_certreq_payload. - */ -static status_t build_certreq_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - if (FALSE) - { - certreq_payload_t *certreq_payload; - - this->logger->log(this->logger, CONTROL|LEVEL2, "add CERTREQ payload to message"); - msg->add_payload(msg, (payload_t *) certreq_payload); - } - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_idr_payload. - */ -static status_t build_idr_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - policy_t *policy = this->ike_sa->get_policy(this->ike_sa); - identification_t *identification = policy->get_other_id(policy); - - if (!identification->contains_wildcards(identification)) - { - id_payload_t *idr_payload = id_payload_create_from_identification(FALSE, identification); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add IDr payload to message"); - msg->add_payload(msg, (payload_t *) idr_payload); - } - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_auth_payload. - */ -static status_t build_auth_payload (private_ike_sa_init_requested_t *this, id_payload_t *my_id_payload, message_t *msg) -{ - authenticator_t *authenticator; - auth_payload_t *auth_payload; - status_t status; - - authenticator = authenticator_create(this->ike_sa); - status = authenticator->compute_auth_data(authenticator,&auth_payload,this->ike_sa_init_request_data,this->received_nonce,my_id_payload,TRUE); - authenticator->destroy(authenticator); - - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not generate AUTH data for IKE_AUTH request. Deleting IKE_SA"); - return DESTROY_ME; - } - - this->logger->log(this->logger, CONTROL|LEVEL2, "add AUTH payload to message"); - msg->add_payload(msg, (payload_t *) auth_payload); - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_sa_payload. - */ -static status_t build_sa_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - linked_list_t *proposal_list; - sa_payload_t *sa_payload; - policy_t *policy; - connection_t *connection; - - /* get proposals form config, add to payload */ - policy = this->ike_sa->get_policy(this->ike_sa); - proposal_list = policy->get_proposals(policy); - /* build child sa */ - connection = this->ike_sa->get_connection(this->ike_sa); - this->child_sa = child_sa_create(0, - connection->get_my_host(connection), - connection->get_other_host(connection), - policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy), - this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public)); - if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA! Deleting IKE_SA"); - return DESTROY_ME; - } - - sa_payload = sa_payload_create_from_proposal_list(proposal_list); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message"); - msg->add_payload(msg, (payload_t *) sa_payload); - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_tsi_payload. - */ -static status_t build_tsi_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - policy_t *policy = this->ike_sa->get_policy(this->ike_sa); - linked_list_t *ts_list = policy->get_my_traffic_selectors(policy); - ts_payload_t *ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add TSi payload to message"); - msg->add_payload(msg, (payload_t *) ts_payload); - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.build_tsr_payload. - */ -static status_t build_tsr_payload (private_ike_sa_init_requested_t *this, message_t *msg) -{ - policy_t *policy = this->ike_sa->get_policy(this->ike_sa); - linked_list_t *ts_list = policy->get_other_traffic_selectors(policy); - ts_payload_t *ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add TSr payload to message"); - msg->add_payload(msg, (payload_t *) ts_payload); - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_requested_t.process_notify_payload. - */ -static status_t process_notify_payload(private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload) -{ - chunk_t notification_data; - notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); - - this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", - mapping_find(notify_message_type_m, notify_message_type)); - - switch (notify_message_type) - { - case NO_PROPOSAL_CHOSEN: - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a NO_PROPOSAL_CHOSEN notify. Deleting IKE_SA"); - return DESTROY_ME; - } - case INVALID_MAJOR_VERSION: - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a INVALID_MAJOR_VERSION notify. Deleting IKE_SA"); - return DESTROY_ME; - } - case INVALID_KE_PAYLOAD: - { - initiator_init_t *initiator_init_state; - chunk_t notify_data; - diffie_hellman_group_t dh_group, old_dh_group; - connection_t *connection; - - connection = this->ike_sa->get_connection(this->ike_sa); - old_dh_group = connection->get_dh_group(connection); - notify_data = notify_payload->get_notification_data(notify_payload); - dh_group = ntohs(*((u_int16_t*)notify_data.ptr)); - - /* TODO: - * We are very restrictive here: If the other didn't accept - * our DH group, and we do not accept his offer, continuation - * is cancelled... - */ - - this->logger->log(this->logger, AUDIT, "peer didn't accept %s, it requested %s!", - mapping_find(diffie_hellman_group_m, old_dh_group), - mapping_find(diffie_hellman_group_m, dh_group)); - /* check if we can accept this dh group */ - if (!connection->check_dh_group(connection, dh_group)) - { - this->logger->log(this->logger, AUDIT, - "peer does only accept DH group %s, which we do not accept! Aborting", - mapping_find(diffie_hellman_group_m, dh_group)); - return DESTROY_ME; - } - - /* Going to change state back to initiator_init_t */ - this->logger->log(this->logger, CONTROL|LEVEL2, "create next state object"); - initiator_init_state = initiator_init_create(this->ike_sa); - - /* buffer of sent and received messages has to get reseted */ - this->ike_sa->reset_message_buffers(this->ike_sa); - - /* state can now be changed */ - this->ike_sa->set_new_state(this->ike_sa,(state_t *) initiator_init_state); - - /* state has NOW changed :-) */ - this->logger->log(this->logger, CONTROL|LEVEL2, "destroy old sate object"); - this->logger->log(this->logger, CONTROL|LEVEL2, "going to retry initialization of connection"); - - this->public.state_interface.destroy(&(this->public.state_interface)); - if (initiator_init_state->retry_initiate_connection (initiator_init_state, dh_group) != SUCCESS) - { - return DESTROY_ME; - } - return FAILED; - } - case NAT_DETECTION_DESTINATION_IP: - { - this->natd_seen_r++; - if (this->natd_hash_r_matched) - return SUCCESS; - - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_hash_r)) - { - this->natd_hash_r_matched = TRUE; - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); - } - else - { - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); - } - - return SUCCESS; - } - case NAT_DETECTION_SOURCE_IP: - { - this->natd_seen_i++; - if (this->natd_hash_i_matched) - return SUCCESS; - - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_hash_i)) - { - this->natd_hash_i_matched = TRUE; - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); - } - else - { - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); - } - - return SUCCESS; - } - default: - { - /* - * - In case of unknown error: IKE_SA gets destroyed. - * - In case of unknown status: logging - */ - if (notify_message_type < 16383) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply contained an unknown notify error (%d). Deleting IKE_SA", - notify_message_type); - return DESTROY_ME; - } - else - { - this->logger->log(this->logger, CONTROL, "IKE_SA_INIT reply contained an unknown notify (%d), ignored.", - notify_message_type); - return SUCCESS; - } - } - } -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_ike_sa_init_requested_t *this) -{ - return IKE_SA_INIT_REQUESTED; -} - -/** - * Implementation of private_ike_sa_init_requested_t.destroy_after_state_change. - */ -static void destroy_after_state_change (private_ike_sa_init_requested_t *this) -{ - this->diffie_hellman->destroy(this->diffie_hellman); - free(this->natd_hash_i.ptr); - free(this->natd_hash_r.ptr); - free(this->ike_sa_init_request_data.ptr); - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - free(this); -} - -/** - * Implementation state_t.destroy. - */ -static void destroy(private_ike_sa_init_requested_t *this) -{ - this->diffie_hellman->destroy(this->diffie_hellman); - free(this->sent_nonce.ptr); - free(this->received_nonce.ptr); - free(this->natd_hash_i.ptr); - free(this->natd_hash_r.ptr); - free(this->ike_sa_init_request_data.ptr); - - if (this->child_sa) - { - this->child_sa->destroy(this->child_sa); - } - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - free(this); -} - -/* - * Described in header. - */ -ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa, diffie_hellman_t *diffie_hellman, chunk_t sent_nonce,chunk_t ike_sa_init_request_data) -{ - private_ike_sa_init_requested_t *this = malloc_thing(private_ike_sa_init_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 functions */ - this->destroy_after_state_change = destroy_after_state_change; - this->process_nonce_payload = process_nonce_payload; - this->process_sa_payload = process_sa_payload; - this->process_ke_payload = process_ke_payload; - this->build_auth_payload = build_auth_payload; - this->build_tsi_payload = build_tsi_payload; - this->build_tsr_payload = build_tsr_payload; - this->build_id_payload = build_id_payload; - this->build_idr_payload = build_idr_payload; - this->build_cert_payload = build_cert_payload; - this->build_certreq_payload = build_certreq_payload; - this->build_sa_payload = build_sa_payload; - this->process_notify_payload = process_notify_payload; - - /* private data */ - this->ike_sa = ike_sa; - this->received_nonce = CHUNK_INITIALIZER; - this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - this->diffie_hellman = diffie_hellman; - this->proposal = NULL; - this->sent_nonce = sent_nonce; - this->child_sa = NULL; - this->ike_sa_init_request_data = ike_sa_init_request_data; - - return &(this->public); -} diff --git a/src/charon/sa/states/ike_sa_init_requested.h b/src/charon/sa/states/ike_sa_init_requested.h deleted file mode 100644 index 0a43afad1..000000000 --- a/src/charon/sa/states/ike_sa_init_requested.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file ike_sa_init_requested.h - * - * @brief Interface of ike_sa_init_requestet_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 IKE_SA_INIT_REQUESTED_H_ -#define IKE_SA_INIT_REQUESTED_H_ - -#include <types.h> -#include <sa/ike_sa.h> -#include <sa/states/state.h> -#include <crypto/diffie_hellman.h> - -typedef struct ike_sa_init_requested_t ike_sa_init_requested_t; - -/** - * @brief This class represents an IKE_SA state when - * requested an IKE_SA_INIT as initiator. - * - * @b Constructors: - * - ike_sa_init_requested_create() - * - * @todo Include valid child sa SPIs in proposal - * - * @ingroup states - */ -struct ike_sa_init_requested_t { - /** - * The state_t interface. - */ - state_t state_interface; -}; - -/** - * Constructor of class ike_sa_init_requested_t. - * - * @param ike_sa assigned ike_sa - * @param diffie_hellman diffie_hellman object use to retrieve shared secret - * @param sent_nonce Sent nonce value - * @param ike_sa_init_request_data the binary representation of the IKE_SA_INIT request message - * @return created ike_sa_init_request_t object - * - * @ingroup states - */ -ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa, - diffie_hellman_t *diffie_hellman, - chunk_t sent_nonce, - chunk_t ike_sa_init_request_data); - -#endif /*IKE_SA_INIT_REQUESTED_H_*/ diff --git a/src/charon/sa/states/ike_sa_init_responded.c b/src/charon/sa/states/ike_sa_init_responded.c deleted file mode 100644 index 860a53f7b..000000000 --- a/src/charon/sa/states/ike_sa_init_responded.c +++ /dev/null @@ -1,796 +0,0 @@ -/** - * @file ike_sa_init_responded.c - * - * @brief State of a IKE_SA after responding to an IKE_SA_INIT request - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 <string.h> - -#include "ike_sa_init_responded.h" - -#include <daemon.h> -#include <sa/authenticator.h> -#include <sa/child_sa.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/id_payload.h> -#include <encoding/payloads/cert_payload.h> -#include <encoding/payloads/auth_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <crypto/signers/signer.h> -#include <crypto/crypters/crypter.h> -#include <sa/states/ike_sa_established.h> - - -typedef struct private_ike_sa_init_responded_t private_ike_sa_init_responded_t; - -/** - * Private data of a ike_sa_init_responded_t object. - * - */ -struct private_ike_sa_init_responded_t { - /** - * Public interface of ike_sa_init_responded_t. - */ - ike_sa_init_responded_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * Received nonce. - */ - chunk_t received_nonce; - - /** - * Sent nonce. - */ - chunk_t sent_nonce; - - /** - * Binary representation of the IKE_SA_INIT response. - */ - chunk_t ike_sa_init_response_data; - - /** - * Binary representation of the IKE_SA_INIT request. - */ - chunk_t ike_sa_init_request_data; - - /** - * SA config to use. - */ - policy_t *policy; - - /** - * CHILD_SA, if set up - */ - child_sa_t *child_sa; - - /** - * Traffic selectors applicable at our site - */ - linked_list_t *my_ts; - - /** - * Traffic selectors applicable at remote site - */ - linked_list_t *other_ts; - - /** - * Assigned logger. - * - * Is logger of ike_sa! - */ - logger_t *logger; - - /** - * Process received IDi and IDr payload and build IDr payload for IKE_AUTH response. - * - * @param this calling object - * @param request_idi ID payload representing initiator - * @param request_idr ID payload representing responder (May be zero) - * @param msg The created IDr payload is added to this message_t object - * @param response_idr The created IDr payload is also written to this location - */ - status_t (*build_idr_payload) (private_ike_sa_init_responded_t *this, - id_payload_t *request_idi, - id_payload_t *request_idr, - message_t *msg, - id_payload_t **response_idr); - - /** - * Build CERT payload for IKE_AUTH response. - * - * @param this calling object - * @param msg The created CERT payload is added to this message_t object - */ - status_t (*build_cert_payload) (private_ike_sa_init_responded_t *this, message_t *msg); - - /** - * Process received AUTH payload and build AUTH payload for IKE_AUTH response. - * - * @param this calling object - * @param request AUTH payload received in IKE_AUTH request - * @param other_id_payload other ID payload needed to verify AUTH data - * @param my_id_payload my ID payload needed to compute AUTH data - * @param msg The created AUTH payload is added to this message_t object - */ - status_t (*build_auth_payload) (private_ike_sa_init_responded_t *this, auth_payload_t *request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* msg); - - /** - * Process received SA payload and build SA payload for IKE_AUTH response. - * - * @param this calling object - * @param request SA payload received in IKE_AUTH request - * @param msg The created SA payload is added to this message_t object - */ - status_t (*build_sa_payload) (private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *msg); - - /** - * Process received TS payload and build TS payload for IKE_AUTH response. - * - * @param this calling object - * @param is_initiator type of TS payload. TRUE for TSi, FALSE for TSr - * @param request TS payload received in IKE_AUTH request - * @param msg the created TS payload is added to this message_t object - */ - status_t (*build_ts_payload) (private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t *msg); - - /** - * Process received CERT payload - * - * @param this calling object - * @param cert_payload payload to process - * @return - * - DESTROY_ME if IKE_SA should be deleted - * - SUCCSS if processed successful - */ - status_t (*process_cert_payload) (private_ike_sa_init_responded_t *this, cert_payload_t *cert_payload); - - /** - * Sends a IKE_AUTH reply containing a notify payload. - * - * @param this calling object - * @param notify_payload payload to process - * @return - * - DESTROY_ME if IKE_SA should be deleted - * - SUCCSS if processed successful - */ - status_t (*process_notify_payload) (private_ike_sa_init_responded_t *this, notify_payload_t* notify_payload); - - /** - * Destroy function called internally of this class after state change to - * state IKE_SA_ESTABLISHED succeeded. - * - * This destroy function does not destroy objects which were passed to the new state. - * - * @param this calling object - */ - void (*destroy_after_state_change) (private_ike_sa_init_responded_t *this); -}; - -/** - * Implements state_t.process_message - */ -static status_t process_message(private_ike_sa_init_responded_t *this, message_t *request) -{ - id_payload_t *idi_request = NULL; - id_payload_t *idr_request = NULL; - id_payload_t *idr_response; - ts_payload_t *tsi_request = NULL; - ts_payload_t *tsr_request = NULL; - auth_payload_t *auth_request = NULL; - sa_payload_t *sa_request = NULL; - cert_payload_t *cert_request = NULL; - iterator_t *payloads; - message_t *response; - crypter_t *crypter; - signer_t *signer; - status_t status; - host_t *my_host, *other_host; - identification_t *my_id, *other_id; - connection_t *connection; - policy_t *policy; - - if (request->get_exchange_type(request) != IKE_AUTH) - { - this->logger->log(this->logger, ERROR | LEVEL1, "message of type %s not supported in state ike_sa_init_respondd", - mapping_find(exchange_type_m,request->get_exchange_type(request))); - return FAILED; - } - - if (!request->get_request(request)) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH responses not allowed state ike_sa_init_responded"); - return FAILED; - } - - /* get signer for verification and crypter for decryption */ - signer = this->ike_sa->get_signer_initiator(this->ike_sa); - crypter = this->ike_sa->get_crypter_initiator(this->ike_sa); - - status = request->parse_body(request, crypter, signer); - if (status != SUCCESS) - { - if (status == NOT_SUPPORTED) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH request contains unsupported payload with critical flag set. " - "Deleting IKE_SA"); - this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER); - return DESTROY_ME; - } - else - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH request decryption failed. Ignoring message"); - } - return status; - } - - /* iterate over incoming payloads. Message is verified, we can be sure there are the required 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 ID_INITIATOR: - idi_request = (id_payload_t*)payload; - break; - case CERTIFICATE: - cert_request = (cert_payload_t*)payload; - status = this->process_cert_payload(this, cert_request); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - break; - case AUTHENTICATION: - auth_request = (auth_payload_t*)payload; - break; - case ID_RESPONDER: - idr_request = (id_payload_t*)payload; - break; - case SECURITY_ASSOCIATION: - sa_request = (sa_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: - { - notify_payload_t *notify_payload = (notify_payload_t *) payload; - status = this->process_notify_payload(this, notify_payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - } - case CERTIFICATE_REQUEST: - { - /* TODO handle certrequest payloads */ - } - 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; - } - } - /* iterator can be destroyed */ - payloads->destroy(payloads); - - /* check if we have all payloads */ - if (!(idi_request && sa_request && auth_request && tsi_request && tsr_request)) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - - status = this->ike_sa->update_connection_hosts(this->ike_sa, - request->get_destination(request), request->get_source(request)); - if (status != SUCCESS) - return status; - - /* build response */ - this->ike_sa->build_message(this->ike_sa, IKE_AUTH, FALSE, &response); - - /* add payloads to it */ - status = this->build_idr_payload(this, idi_request, idr_request, response, &idr_response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_cert_payload(this, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_auth_payload(this, auth_request,idi_request, idr_response,response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_sa_payload(this, sa_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_ts_payload(this, TRUE, tsi_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_ts_payload(this, FALSE, tsr_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->ike_sa->send_response(this->ike_sa, response); - /* message can now be sent (must not be destroyed) */ - - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to send IKE_AUTH reply. Deleting IKE_SA"); - response->destroy(response); - return DESTROY_ME; - } - - /* install child SA policies */ - if (!this->child_sa) - { - this->logger->log(this->logger, CONTROL, "proposal negotiation failed, no CHILD_SA built"); - } - else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) - { - this->logger->log(this->logger, CONTROL, "traffic selector negotiation failed, no CHILD_SA built"); - this->child_sa->destroy(this->child_sa); - this->child_sa = NULL; - } - else - { - status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA policy! Deleting IKE_SA"); - return DESTROY_ME; - } - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - } - - /* create new state */ - this->ike_sa->establish(this->ike_sa); - this->destroy_after_state_change(this); - return SUCCESS; - -destroy_response: - response->destroy(response); - return status; - -} - -/** - * Implementation of private_ike_sa_init_responded_t.build_idr_payload. - */ -static status_t build_idr_payload(private_ike_sa_init_responded_t *this, id_payload_t *request_idi, id_payload_t *request_idr, message_t *msg,id_payload_t **response_idr) -{ - identification_t *other_id, *my_id; - id_payload_t *idr_response; - - /* use others ID, an ours if peer requested one */ - other_id = request_idi->get_identification(request_idi); - if (request_idr) - { - my_id = request_idr->get_identification(request_idr); - } - else - { - my_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); - } - - /* build new sa config */ - this->policy = charon->policies->get_policy_by_ids(charon->policies, my_id, other_id); - if (this->policy == NULL) - { - this->logger->log(this->logger, AUDIT, "we don't have a policy for IDs %s - %s. Deleting IKE_SA", - my_id->get_string(my_id), other_id->get_string(other_id)); - my_id->destroy(my_id); - other_id->destroy(other_id); - return DESTROY_ME; - } - my_id->destroy(my_id); - other_id->destroy(other_id); - - /* get my id from policy, which must contain a fully qualified valid id */ - my_id = this->policy->get_my_id(this->policy); - - /* update others traffic selectors with actually used address */ - this->policy->update_my_ts(this->policy, msg->get_source(msg)); - this->policy->update_other_ts(this->policy, msg->get_destination(msg)); - - /* set policy in ike_sa for other states */ - this->ike_sa->set_policy(this->ike_sa, this->policy); - - /* build response */ - idr_response = id_payload_create_from_identification(FALSE, my_id); - msg->add_payload(msg, (payload_t*)idr_response); - *response_idr = idr_response; - - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_responded_t.build_cert_payload. - */ -static status_t build_cert_payload (private_ike_sa_init_responded_t *this, message_t *msg) -{ - connection_t *connection = this->ike_sa->get_connection(this->ike_sa); - - if (connection->get_cert_policy(connection) != CERT_NEVER_SEND) - { - policy_t *policy; - identification_t *my_id; - x509_t *cert; - cert_payload_t *cert_payload; - - policy = this->ike_sa->get_policy(this->ike_sa); - my_id = policy->get_my_id(policy); - - cert = charon->credentials->get_certificate(charon->credentials, my_id); - if (cert == NULL) - { - this->logger->log(this->logger, ERROR, "could not find my certificate"); - return NOT_FOUND; - } - cert_payload = cert_payload_create_from_x509(cert); - this->logger->log(this->logger, CONTROL|LEVEL2, "add CERT payload to message"); - msg->add_payload(msg, (payload_t *) cert_payload); - } - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_responded_t.build_auth_payload. - */ -static status_t build_auth_payload(private_ike_sa_init_responded_t *this, auth_payload_t *auth_request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* msg) -{ - authenticator_t *authenticator; - auth_payload_t *auth_reply; - status_t status; - - authenticator = authenticator_create(this->ike_sa); - status = authenticator->verify_auth_data(authenticator,auth_request, this->ike_sa_init_request_data,this->sent_nonce,other_id_payload,TRUE); - - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH request verification failed. Deleting IKE_SA"); - this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, AUTHENTICATION_FAILED, CHUNK_INITIALIZER); - authenticator->destroy(authenticator); - return DESTROY_ME; - } - - status = authenticator->compute_auth_data(authenticator,&auth_reply, this->ike_sa_init_response_data,this->received_nonce,my_id_payload,FALSE); - authenticator->destroy(authenticator); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to build authentication data for IKE_AUTH reply. Deleting IKE_S"); - return DESTROY_ME; - } - - msg->add_payload(msg, (payload_t *)auth_reply); - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_responded_t.build_sa_payload. - */ -static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *msg) -{ - proposal_t *proposal, *proposal_tmp; - linked_list_t *proposal_list; - sa_payload_t *sa_response; - chunk_t seed; - prf_plus_t *prf_plus; - status_t status; - connection_t *connection; - policy_t *policy; - bool use_natt; - - /* prepare reply */ - sa_response = sa_payload_create(); - - /* get proposals from request, and select one with ours */ - proposal_list = request->get_proposals(request); - this->logger->log(this->logger, CONTROL|LEVEL1, "selecting proposals:"); - proposal = this->policy->select_proposal(this->policy, proposal_list); - /* list is not needed anymore */ - while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) - { - proposal_tmp->destroy(proposal_tmp); - } - proposal_list->destroy(proposal_list); - /* do we have a proposal? */ - if (proposal == NULL) - { - notify_payload_t *notify; - this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any proposals we accept. " - "Adding NO_PROPOSAL_CHOSEN notify"); - /* add NO_PROPOSAL_CHOSEN and an empty SA payload */ - notify = notify_payload_create_from_protocol_and_type(PROTO_IKE, NO_PROPOSAL_CHOSEN); - msg->add_payload(msg, (payload_t*) notify); - } - else - { - /* set up child sa */ - seed = chunk_alloc(this->received_nonce.len + this->sent_nonce.len); - memcpy(seed.ptr, this->received_nonce.ptr, this->received_nonce.len); - memcpy(seed.ptr + this->received_nonce.len, this->sent_nonce.ptr, this->sent_nonce.len); - prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); - chunk_free(&seed); - - policy = this->ike_sa->get_policy(this->ike_sa); - connection = this->ike_sa->get_connection(this->ike_sa); - use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public); - this->child_sa = child_sa_create(0, - connection->get_my_host(connection), - connection->get_other_host(connection), - policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy), - use_natt); - - status = this->child_sa->add(this->child_sa, proposal, prf_plus); - prf_plus->destroy(prf_plus); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "could not install CHILD_SA! Deleting IKE_SA"); - /* TODO: how do we handle this cleanly? */ - sa_response->destroy(sa_response); - proposal->destroy(proposal); - return DESTROY_ME; - } - - /* add proposal to sa payload */ - sa_response->add_proposal(sa_response, proposal); - proposal->destroy(proposal); - } - msg->add_payload(msg, (payload_t*)sa_response); - return SUCCESS; -} - -/** - * Implementation of private_ike_sa_init_responded_t.build_ts_payload. - */ -static status_t build_ts_payload(private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t* msg) -{ - linked_list_t *ts_received, *ts_selected; - traffic_selector_t *ts; - status_t status = SUCCESS; - ts_payload_t *ts_response; - - /* build a reply payload with selected traffic selectors */ - ts_received = request->get_traffic_selectors(request); - /* select ts depending on payload type */ - if (ts_initiator) - { - ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received); - this->other_ts = ts_selected; - } - else - { - ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received); - this->my_ts = ts_selected; - } - - ts_response = ts_payload_create_from_traffic_selectors(ts_initiator, ts_selected); - msg->add_payload(msg, (payload_t*) ts_response); - - /* add notify if traffic selectors do not match */ - if (!ts_initiator && - (ts_selected->get_count(ts_selected) == 0 || this->other_ts->get_count(this->other_ts) == 0)) - { - notify_payload_t *notify; - - this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any traffic selectors we accept. " - "Adding TS_UNACCEPTABLE notify"); - - notify = notify_payload_create_from_protocol_and_type(0, TS_UNACCEPTABLE); - msg->add_payload(msg, (payload_t*)notify); - } - - /* cleanup */ - while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - ts_received->destroy(ts_received); - - return status; -} - -/** - * Implements private_ike_sa_init_responded_t.process_cert_payload - */ -static status_t process_cert_payload(private_ike_sa_init_responded_t *this, cert_payload_t * cert_payload) -{ - bool found; - x509_t *cert; - - if (cert_payload->get_cert_encoding(cert_payload) != CERT_X509_SIGNATURE) - { - this->logger->log(this->logger, CONTROL, "certificate encoding is %s, ignored", - enum_name(&cert_encoding_names, cert_payload->get_cert_encoding(cert_payload))); - return SUCCESS; - } - cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload)); - - if (charon->credentials->verify(charon->credentials, cert, &found)) - { - this->logger->log(this->logger, CONTROL, "end entity certificate is trusted"); - if (!found) - { - cert = charon->credentials->add_end_certificate(charon->credentials, cert); - } - } - else - { - this->logger->log(this->logger, ERROR, "end entity certificate is not trusted"); - } - return SUCCESS; -} - -/** - * Implements private_ike_sa_init_responded_t.process_notify_payload - */ -static status_t process_notify_payload(private_ike_sa_init_responded_t *this, notify_payload_t *notify_payload) -{ - notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); - - this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", - mapping_find(notify_message_type_m, notify_message_type)); - - switch (notify_message_type) - { - case SET_WINDOW_SIZE: - /* - * TODO Increase window size. - */ - case INITIAL_CONTACT: - /* - * TODO Delete existing IKE_SA's with other Identity. - */ - default: - { - this->logger->log(this->logger, AUDIT, "IKE_AUTH request contained an unknown notify (%d), ignored.", notify_message_type); - } - } - - return SUCCESS; -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_ike_sa_init_responded_t *this) -{ - return IKE_SA_INIT_RESPONDED; -} - -/** - * Implementation of state_t.destroy. - */ -static void destroy(private_ike_sa_init_responded_t *this) -{ - chunk_free(&(this->received_nonce)); - chunk_free(&(this->sent_nonce)); - chunk_free(&(this->ike_sa_init_response_data)); - chunk_free(&(this->ike_sa_init_request_data)); - if (this->my_ts) - { - traffic_selector_t *ts; - while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->my_ts->destroy(this->my_ts); - } - if (this->other_ts) - { - traffic_selector_t *ts; - while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->other_ts->destroy(this->other_ts); - } - if (this->child_sa) - { - this->child_sa->destroy(this->child_sa); - } - - free(this); -} -/** - * Implementation of private_ike_sa_init_responded.destroy_after_state_change. - */ -static void destroy_after_state_change(private_ike_sa_init_responded_t *this) -{ - chunk_free(&(this->received_nonce)); - chunk_free(&(this->sent_nonce)); - chunk_free(&(this->ike_sa_init_response_data)); - chunk_free(&(this->ike_sa_init_request_data)); - if (this->my_ts) - { - traffic_selector_t *ts; - while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->my_ts->destroy(this->my_ts); - } - if (this->other_ts) - { - traffic_selector_t *ts; - while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) - { - ts->destroy(ts); - } - this->other_ts->destroy(this->other_ts); - } - - free(this); -} - -/* - * Described in header. - */ -ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_nonce,chunk_t ike_sa_init_request_data, chunk_t ike_sa_init_response_data) -{ - private_ike_sa_init_responded_t *this = malloc_thing(private_ike_sa_init_responded_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 functions */ - this->build_idr_payload = build_idr_payload; - this->build_cert_payload = build_cert_payload; - this->build_auth_payload = build_auth_payload; - this->build_sa_payload = build_sa_payload; - this->build_ts_payload = build_ts_payload; - this->process_cert_payload = process_cert_payload; - this->process_notify_payload = process_notify_payload; - this->destroy_after_state_change = destroy_after_state_change; - - /* private data */ - this->ike_sa = ike_sa; - this->received_nonce = received_nonce; - this->sent_nonce = sent_nonce; - this->ike_sa_init_response_data = ike_sa_init_response_data; - this->ike_sa_init_request_data = ike_sa_init_request_data; - this->my_ts = NULL; - this->other_ts = NULL; - this->child_sa = NULL; - this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - - return &(this->public); -} diff --git a/src/charon/sa/states/ike_sa_init_responded.h b/src/charon/sa/states/ike_sa_init_responded.h deleted file mode 100644 index 43aecf26f..000000000 --- a/src/charon/sa/states/ike_sa_init_responded.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file ike_sa_init_responded.h - * - * @brief Interface of ike_sa_init_responded_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 IKE_SA_INIT_RESPONDED_H_ -#define IKE_SA_INIT_RESPONDED_H_ - -#include <sa/ike_sa.h> -#include <sa/states/state.h> - -typedef struct ike_sa_init_responded_t ike_sa_init_responded_t; - -/** - * @brief This class represents an IKE_SA state when - * responded to an IKE_SA_INIT request. - * - * The state accpets IKE_AUTH requests. It proves the authenticity - * and sets up the first child sa. Then it sends back an IKE_AUTH - * reply and changes to the IKE_SA_ESTABLISHED state. - * - * @b Constructors: - * - ike_sa_init_response_data() - * - * @todo Implement handling of SET_WINDOW_SIZE notify - * - * @todo Implement handling of INITIAL_CONTACT notify - * - * @ingroup states - */ -struct ike_sa_init_responded_t { - /** - * The state_t interface. - */ - state_t state_interface; - -}; - -/** - * @brief Constructor of class ike_sa_init_responded_t - * - * @param ike_sa assigned IKE_SA - * @param received_nonce received nonce data in IKE_SA_INIT request - * @param sent_nonce sent nonce data in IKE_SA_INIT response - * @param ike_sa_init_request_data binary representation of received IKE_SA_INIT request - * @param ike_sa_init_response_data binary representation of sent IKE_SA_INIT response - * - * @ingroup states - */ -ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa, - chunk_t received_nonce, - chunk_t sent_nonce, - chunk_t ike_sa_init_request_data, - chunk_t ike_sa_init_response_data); - -#endif /*IKE_SA_INIT_RESPONDED_H_*/ diff --git a/src/charon/sa/states/initiator_init.c b/src/charon/sa/states/initiator_init.c deleted file mode 100644 index aa86b514b..000000000 --- a/src/charon/sa/states/initiator_init.c +++ /dev/null @@ -1,440 +0,0 @@ -/** - * @file initiator_init.c - * - * @brief Implementation of initiator_init_t. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 "initiator_init.h" - - -#include <daemon.h> -#include <sa/states/state.h> -#include <sa/states/ike_sa_init_requested.h> -#include <queues/jobs/retransmit_request_job.h> -#include <crypto/diffie_hellman.h> -#include <crypto/hashers/hasher.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/nonce_payload.h> - - -typedef struct private_initiator_init_t private_initiator_init_t; - -/** - * Private data of a initiator_init_t object.. - * - */ -struct private_initiator_init_t { - /** - * Methods of the state_t interface. - */ - initiator_init_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * Diffie hellman object used to generate public DH value. - * This objet is passed to the next state of type IKE_SA_INIT_REQUESTED. - */ - diffie_hellman_t *diffie_hellman; - - /** - * Sent nonce. - * This nonce is passed to the next state of type IKE_SA_INIT_REQUESTED. - */ - chunk_t sent_nonce; - - /** - * Assigned logger. - * - * Is logger of ike_sa! - */ - logger_t *logger; - - /** - * Builds the SA payload for this state. - * - * @param this calling object - * @param msg message_t object to add the SA payload - */ - void (*build_sa_payload) (private_initiator_init_t *this, message_t *msg); - - /** - * Builds the KE payload for this state. - * - * @param this calling object - * @param msg message_t object to add the KE payload - */ - void (*build_ke_payload) (private_initiator_init_t *this, message_t *msg); - - /** - * Builds the NONCE payload for this state. - * - * @param this calling object - * @param msg message_t object to add the NONCE payload - */ - status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *msg); - - /** - * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and - * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. - * - * @param this calling object - * @param msg message_t object to add the Notify payloads - */ - void (*build_natd_payload) (private_initiator_init_t *this, message_t *msg, notify_message_type_t type, host_t *host); - - /** - * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and - * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. - * - * @param this calling object - * @param msg message_t object to add the Notify payloads - */ - void (*build_natd_payloads) (private_initiator_init_t *this, message_t *msg); - - /** - * Destroy function called internally of this class after state change to state - * IKE_SA_INIT_REQUESTED succeeded. - * - * This destroy function does not destroy objects which were passed to the new state. - * - * @param this calling object - */ - void (*destroy_after_state_change) (private_initiator_init_t *this); -}; - -/** - * Implementation of initiator_init_t.initiate_connection. - */ -static status_t initiate_connection (private_initiator_init_t *this, connection_t *connection) -{ - policy_t *policy; - diffie_hellman_group_t dh_group; - host_t *my_host, *other_host; - identification_t *my_id, *other_id; - char *name; - - name = connection->get_name(connection); - this->ike_sa->set_connection(this->ike_sa, connection); - - /* get policy */ - policy = charon->policies->get_policy_by_name(charon->policies, name); - if (policy == NULL) - { - this->logger->log(this->logger, ERROR | LEVEL1, - "could not get a policy named '%s', aborting", name); - return DESTROY_ME; - } - this->ike_sa->set_policy(this->ike_sa, policy); - - my_host = connection->get_my_host(connection); - other_host = connection->get_other_host(connection); - my_id = policy->get_my_id(policy); - other_id = policy->get_other_id(policy); - - this->logger->log(this->logger, CONTROL, "initiating connection \"%s\": %s[%s]...%s[%s]", - name, - my_host->get_address(my_host), - my_id->get_string(my_id), - other_host->get_address(other_host), - other_id->get_string(other_id)); - - /* we must guess now a DH group. For that we choose our most preferred group */ - dh_group = connection->get_dh_group(connection); - - /* next step is done in retry_initiate_connection */ - return this->public.retry_initiate_connection(&this->public, dh_group); -} - -/** - * Implementation of initiator_init_t.retry_initiate_connection. - */ -status_t retry_initiate_connection (private_initiator_init_t *this, diffie_hellman_group_t dh_group) -{ - ike_sa_init_requested_t *next_state; - chunk_t ike_sa_init_request_data; - connection_t *connection; - ike_sa_id_t *ike_sa_id; - message_t *message; - status_t status; - - this->diffie_hellman = diffie_hellman_create(dh_group); - if (this->diffie_hellman == NULL) - { - this->logger->log(this->logger, AUDIT, "DH group %s (%d) not supported, aborting", - mapping_find(diffie_hellman_group_m, dh_group), dh_group); - return DESTROY_ME; - } - - connection = this->ike_sa->get_connection(this->ike_sa); - ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); - ike_sa_id->set_responder_spi(ike_sa_id,0); - - /* going to build message */ - this->logger->log(this->logger, CONTROL|LEVEL2, "going to build message"); - this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, TRUE, &message); - - /* build SA payload */ - this->build_sa_payload(this, message); - /* build KE payload */ - this->build_ke_payload(this, message); - /* build Nonce payload */ - status = this->build_nonce_payload(this, message); - if (status != SUCCESS) - { - this->logger->log(this->logger, ERROR, "building nonce payload failed, aborting"); - message->destroy(message); - return DESTROY_ME; - } - - /* build Notify(NAT-D) payloads */ - this->build_natd_payloads(this, message); - - /* message can now be sent (must not be destroyed) */ - status = this->ike_sa->send_request(this->ike_sa, message); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to initiate connection, could not send message, aborting"); - message->destroy(message); - return DESTROY_ME; - } - - message = this->ike_sa->get_last_requested_message(this->ike_sa); - - ike_sa_init_request_data = message->get_packet_data(message); - - /* state can now be changed */ - this->logger->log(this->logger, CONTROL|LEVEL2, "create next state object"); - next_state = ike_sa_init_requested_create(this->ike_sa, this->diffie_hellman, this->sent_nonce,ike_sa_init_request_data); - this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state); - - this->logger->log(this->logger, CONTROL|LEVEL2, "destroy old state object"); - this->destroy_after_state_change(this); - return SUCCESS; -} - -/** - * Implementation of private_initiator_init_t.build_sa_payload. - */ -static void build_sa_payload(private_initiator_init_t *this, message_t *msg) -{ - sa_payload_t* sa_payload; - linked_list_t *proposal_list; - connection_t *connection; - - this->logger->log(this->logger, CONTROL|LEVEL1, "building SA payload"); - - connection = this->ike_sa->get_connection(this->ike_sa); - - proposal_list = connection->get_proposals(connection); - - sa_payload = sa_payload_create_from_proposal_list(proposal_list); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message"); - msg->add_payload(msg, (payload_t *) sa_payload); -} - -/** - * Implementation of private_initiator_init_t.build_ke_payload. - */ -static void build_ke_payload(private_initiator_init_t *this, message_t *msg) -{ - ke_payload_t *ke_payload; - chunk_t key_data; - diffie_hellman_group_t dh_group; - - this->logger->log(this->logger, CONTROL|LEVEL1, "building KE payload"); - - this->diffie_hellman->get_my_public_value(this->diffie_hellman, &key_data); - dh_group = this->diffie_hellman->get_dh_group(this->diffie_hellman); - - ke_payload = ke_payload_create(); - ke_payload->set_dh_group_number(ke_payload, dh_group); - ke_payload->set_key_exchange_data(ke_payload, key_data); - - chunk_free(&key_data); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add KE payload to message"); - msg->add_payload(msg, (payload_t *) ke_payload); -} - -/** - * Implementation of private_initiator_init_t.build_nonce_payload. - */ -static status_t build_nonce_payload(private_initiator_init_t *this, message_t *msg) -{ - nonce_payload_t *nonce_payload; - randomizer_t *randomizer; - status_t status; - - this->logger->log(this->logger, CONTROL|LEVEL1, "building NONCE payload"); - - this->logger->log(this->logger, CONTROL|LEVEL2, "get pseudo random bytes for NONCE"); - randomizer = this->ike_sa->get_randomizer(this->ike_sa); - - status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce)); - if (status != SUCCESS) - { - return status; - } - - this->logger->log(this->logger, RAW|LEVEL2, "initiator NONCE",&(this->sent_nonce)); - - nonce_payload = nonce_payload_create(); - - nonce_payload->set_nonce(nonce_payload, this->sent_nonce); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add NONCE payload to message"); - msg->add_payload(msg, (payload_t *) nonce_payload); - return SUCCESS; -} - -/** - * Implementation of private_initiator_init_t.build_natd_payload. - */ -static void build_natd_payload(private_initiator_init_t *this, message_t *msg, notify_message_type_t type, host_t *host) -{ - chunk_t hash; - this->logger->log(this->logger, CONTROL|LEVEL1, "building Notify(NAT-D) payload"); - notify_payload_t *notify_payload; - notify_payload = notify_payload_create(); - /*notify_payload->set_protocol_id(notify_payload, NULL);*/ - /*notify_payload->set_spi(notify_payload, NULL);*/ - notify_payload->set_notify_message_type(notify_payload, type); - hash = this->ike_sa->generate_natd_hash(this->ike_sa, - msg->get_initiator_spi(msg), - msg->get_responder_spi(msg), - host); - notify_payload->set_notification_data(notify_payload, hash); - chunk_free(&hash); - this->logger->log(this->logger, CONTROL|LEVEL2, "add Notify(NAT-D) payload to message"); - msg->add_payload(msg, (payload_t *) notify_payload); -} - -/** - * Implementation of private_initiator_init_t.build_natd_payloads. - */ -static void build_natd_payloads(private_initiator_init_t *this, message_t *msg) -{ - connection_t *connection; - linked_list_t *hostlist; - iterator_t *hostiter; - host_t *host; - - /* - * N(NAT_DETECTION_SOURCE_IP)+ - */ - hostlist = charon->interfaces->get_addresses(charon->interfaces); - hostiter = hostlist->create_iterator(hostlist, TRUE); - while(hostiter->iterate(hostiter, (void**)&host)) { - this->build_natd_payload(this, msg, NAT_DETECTION_SOURCE_IP, - host); - } - hostiter->destroy(hostiter); - - /* - * N(NAT_DETECTION_DESTINATION_IP) - */ - connection = this->ike_sa->get_connection(this->ike_sa); - this->build_natd_payload(this, msg, NAT_DETECTION_DESTINATION_IP, - connection->get_other_host(connection)); -} - -/** - * Implementation of state_t.process_message. - */ -static status_t process_message(private_initiator_init_t *this, message_t *message) -{ - this->logger->log(this->logger, ERROR, "in state INITIATOR_INIT, no message is processed"); - return FAILED; -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_initiator_init_t *this) -{ - return INITIATOR_INIT; -} - -/** - * Implementation of state_t.destroy. - */ -static void destroy(private_initiator_init_t *this) -{ - this->logger->log(this->logger, CONTROL | LEVEL3, "going to destroy initiator_init_t state object"); - - /* destroy diffie hellman object */ - if (this->diffie_hellman != NULL) - { - this->diffie_hellman->destroy(this->diffie_hellman); - } - if (this->sent_nonce.ptr != NULL) - { - free(this->sent_nonce.ptr); - } - free(this); -} - -/** - * Implementation of private_initiator_init_t.destroy_after_state_change - */ -static void destroy_after_state_change (private_initiator_init_t *this) -{ - this->logger->log(this->logger, CONTROL | LEVEL3, "going to destroy initiator_init_t state object"); - free(this); -} - -/* - * Described in header. - */ -initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa) -{ - private_initiator_init_t *this = malloc_thing(private_initiator_init_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; - - /* public functions */ - this->public.initiate_connection = (status_t (*)(initiator_init_t *, connection_t*)) initiate_connection; - this->public.retry_initiate_connection = (status_t (*)(initiator_init_t *, int )) retry_initiate_connection; - - /* private functions */ - this->destroy_after_state_change = destroy_after_state_change; - this->build_nonce_payload = build_nonce_payload; - this->build_sa_payload = build_sa_payload; - this->build_ke_payload = build_ke_payload; - this->build_natd_payload = build_natd_payload; - this->build_natd_payloads = build_natd_payloads; - - /* private data */ - this->ike_sa = ike_sa; - this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - this->sent_nonce = CHUNK_INITIALIZER; - this->diffie_hellman = NULL; - - return &(this->public); -} diff --git a/src/charon/sa/states/initiator_init.h b/src/charon/sa/states/initiator_init.h deleted file mode 100644 index 903c58038..000000000 --- a/src/charon/sa/states/initiator_init.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file initiator_init.h - * - * @brief Interface of initiator_init_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 INITIATOR_INIT_H_ -#define INITIATOR_INIT_H_ - -#include <sa/ike_sa.h> -#include <sa/states/state.h> - - -typedef struct initiator_init_t initiator_init_t; - -/** - * @brief This class represents an IKE_SA state when - * initializing a connection as initiator. - * - * @b Constructors: - * - initiator_init_create() - * - * @ingroup states - */ -struct initiator_init_t { - /** - * The state_t interface. - */ - state_t state_interface; - - /** - * Initiate a new connection with given connection_t object. - * - * @param this calling object - * @param connection connection to initiate - * @return - * - SUCCESS - * - DESTROY_ME if something failed - */ - status_t (*initiate_connection) (initiator_init_t *this, connection_t *connection); - - /** - * Retry to initiate a new connection with a specific dh_group_priority. - * - * The dh_group_priority is starting at 1. - * - * @param this calling object - * @param dh_group_priority dh group priority to try with - * @return - * - SUCCESS - * - DESTROY_ME if something failed (see log for error) - */ - status_t (*retry_initiate_connection) (initiator_init_t *this, int dh_group_priority); -}; - -/** - * @brief Constructor of class initiator_init_t. - * - * @param ike_sa assigned IKE_SA - * @return created initiator_init_t object - * - * @ingroup states - */ -initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa); - - -#endif /*INITIATOR_INIT_H_*/ diff --git a/src/charon/sa/states/responder_init.c b/src/charon/sa/states/responder_init.c deleted file mode 100644 index f9d61f90d..000000000 --- a/src/charon/sa/states/responder_init.c +++ /dev/null @@ -1,798 +0,0 @@ -/** - * @file responder_init.c - * - * @brief Implementation of responder_init_t. - * - */ - -/* - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger - * Copyright (C) 2005 Jan Hutter, 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 "responder_init.h" - -#include <daemon.h> -#include <sa/states/state.h> -#include <sa/states/ike_sa_init_responded.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/certreq_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <crypto/diffie_hellman.h> -#include <queues/jobs/send_keepalive_job.h> - - -typedef struct private_responder_init_t private_responder_init_t; - -/** - * Private data of a responder_init_t object. - * - */ -struct private_responder_init_t { - /** - * Methods of the state_t interface. - */ - responder_init_t public; - - /** - * Assigned IKE_SA. - */ - protected_ike_sa_t *ike_sa; - - /** - * Diffie Hellman object used to compute shared secret. - */ - diffie_hellman_t *diffie_hellman; - - /** - * Diffie Hellman group number from selected IKE proposal. - */ - u_int16_t dh_group_number; - - /** - * Priority used to get matching dh_group number. - */ - u_int16_t dh_group_priority; - - /** - * Sent nonce value. - * - * This value is passed to the next state of type IKE_SA_INIT_RESPONDED. - */ - chunk_t sent_nonce; - - /** - * Received nonce value - * - * This value is passed to the next state of type IKE_SA_INIT_RESPONDED. - */ - chunk_t received_nonce; - - /** - * Selected proposal - */ - proposal_t *proposal; - - /** - * Logger used to log data . - * - * Is logger of ike_sa! - */ - logger_t *logger; - - /** - * Precomputed NAT-D hash for initiator. - */ - chunk_t natd_hash_i; - - /** - * Flag indicating that an initiator NAT-D hash matched. - */ - bool natd_hash_i_matched; - - /** - * NAT-D payload count for NAT_DETECTION_SOURCE_IP. - */ - int natd_seen_i; - - /** - * Precomputed NAT-D hash of responder. - */ - chunk_t natd_hash_r; - - /** - * Flag indicating that a responder NAT-D hash matched. - */ - bool natd_hash_r_matched; - - /** - * NAT-D payload count for NAT_DETECTION_DESTINATION_IP. - */ - int natd_seen_r; - - - /** - * Handles received SA payload and builds the SA payload for the response. - * - * @param this calling object - * @param sa_request The received SA payload - * @param msg the SA payload is added to this message_t object. - * @return - * - DESTROY_ME - * - SUCCESS - */ - status_t (*build_sa_payload) (private_responder_init_t *this,sa_payload_t *sa_request, message_t *msg); - - /** - * Handles received KE payload and builds the KE payload for the response. - * - * @param this calling object - * @param ke_request The received KE payload - * @param msg the KE payload is added to this message_t object. - * @return - * - DESTROY_ME - * - SUCCESS - */ - status_t (*build_ke_payload) (private_responder_init_t *this,ke_payload_t *ke_request, message_t *msg); - - /** - * Handles received NONCE payload and builds the NONCE payload for the response. - * - * @param this calling object - * @param nonce_request The received NONCE payload - * @param msg the NONCE payload is added to this message_t object. - * @return - * - DESTROY_ME - * - SUCCESS - */ - status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *msg); - - /** - * Build CERTREQ payload for the response. - * - * @param this calling object - * @param msg the CERTREQ payload is added to this message_t object - * @return - * - SUCCESS - * - FAILED - */ - status_t (*build_certreq_payload) (private_responder_init_t *this, message_t *msg); - - - /** - * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and - * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. - * - * @param this calling object - * @param msg message_t object to add the Notify payloads - */ - void (*build_natd_payload) (private_responder_init_t *this, message_t *msg, notify_message_type_t type, host_t *host); - - /** - * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and - * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. - * - * @param this calling object - * @param msg message_t object to add the Notify payloads - */ - void (*build_natd_payloads) (private_responder_init_t *this, message_t *msg); - - /** - * Sends a IKE_SA_INIT reply containing a notify payload. - * - * @param this calling object - * @param notify_payload notify_payload to process - */ - status_t (*process_notify_payload) (private_responder_init_t *this, notify_payload_t *notify_payload); - - /** - * Destroy function called internally of this class after change - * to state IKE_SA_INIT_RESPONDED succeeded. - * - * This destroy function does not destroy objects which were passed to the new state. - * - * @param this calling object - */ - void (*destroy_after_state_change) (private_responder_init_t *this); - -}; - -/** - * Implementation of state_t.process_message. - */ -static status_t process_message(private_responder_init_t *this, message_t *message) -{ - ike_sa_init_responded_t *next_state; - chunk_t ike_sa_init_response_data; - chunk_t ike_sa_init_request_data; - sa_payload_t *sa_request = NULL; - ke_payload_t *ke_request = NULL; - nonce_payload_t *nonce_request = NULL; - host_t *source, *destination; - connection_t *connection; - iterator_t *payloads; - message_t *response; - status_t status; - - if (message->get_exchange_type(message) != IKE_SA_INIT) - { - this->logger->log(this->logger, ERROR | LEVEL1, "message of type %s not supported in state responder_init", - mapping_find(exchange_type_m,message->get_exchange_type(message))); - return DESTROY_ME; - } - if (!message->get_request(message)) - { - this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT responses not allowed in state ike_sa_init_responded"); - return DESTROY_ME; - } - - /* this is the first message to process, so get host infos */ - source = message->get_source(message); - destination = message->get_destination(message); - - connection = charon->connections->get_connection_by_hosts(charon->connections, destination, source); - if (connection == NULL) - { - /* no configuration matches given hosts */ - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request does not match any available connection, deleting IKE_SA"); - /* TODO: inform requestor */ - return DESTROY_ME; - } - this->ike_sa->set_connection(this->ike_sa, connection); - status = this->ike_sa->update_connection_hosts(this->ike_sa, - destination, source); - if (status != SUCCESS) - { - return status; - } - - /* parse incoming message */ - status = message->parse_body(message, NULL, NULL); - if (status != SUCCESS) - { - if (status == NOT_SUPPORTED) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contains unsupported payload with critical flag set, " - "deleting IKE_SA"); - this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER); - } - else - { - this->logger->log(this->logger, AUDIT, "unable to parse IKE_SA_INIT request, deleting IKE_SA"); - } - return DESTROY_ME; - } - - /* - * Precompute NAT-D hashes. - * Even though there SHOULD only be a single payload of Notify type - * NAT_DETECTION_DESTINATION_IP we precompute both hashes. - */ - this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa, - message->get_initiator_spi(message), - message->get_responder_spi(message), - message->get_source(message)); - this->natd_hash_i_matched = FALSE; - this->natd_seen_i = 0; - this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa, - message->get_initiator_spi(message), - message->get_responder_spi(message), - message->get_destination(message)); - this->natd_hash_r_matched = FALSE; - this->natd_seen_r = 0; - this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE); - this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE); - - /* Iterate over all payloads. - * - * The message is already checked for the right payload types. - */ - payloads = message->get_payload_iterator(message); - 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 KEY_EXCHANGE: - { - ke_request = (ke_payload_t*)payload; - break; - } - case NONCE: - { - nonce_request = (nonce_payload_t*)payload; - break; - } - case NOTIFY: - { - notify_payload_t *notify_payload = (notify_payload_t *) payload; - status = this->process_notify_payload(this, notify_payload); - if (status != SUCCESS) - { - payloads->destroy(payloads); - return status; - } - 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); - - /* check if we have all payloads */ - if (!(sa_request && ke_request && nonce_request)) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - - /* NAT-D */ - if ((!this->natd_seen_i && this->natd_seen_r > 0) - || (this->natd_seen_i > 0 && !this->natd_seen_r)) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA"); - return DESTROY_ME; - } - if (this->natd_seen_r > 1) - { - this->logger->log(this->logger, AUDIT, "warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads."); - } - if (this->natd_seen_i > 0 && !this->natd_hash_i_matched) - { - this->logger->log(this->logger, AUDIT, "remote host is behind NAT, using NAT-Traversal"); - this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE); - } - if (this->natd_seen_r > 0 && !this->natd_hash_r_matched) - { - this->logger->log(this->logger, AUDIT, "local host is behind NAT, using NAT-Traversal"); - this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE); - charon->event_queue->add_relative(charon->event_queue, - (job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)), - charon->configuration->get_keepalive_interval(charon->configuration)); - } - if (!this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa)) - { - this->logger->log(this->logger, AUDIT, "no NAT detected, not using NAT-Traversal"); - } - - this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response); - - status = this->build_sa_payload(this, sa_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_ke_payload(this, ke_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_nonce_payload(this, nonce_request, response); - if (status != SUCCESS) - goto destroy_response; - - status = this->build_certreq_payload(this, response); - if (status != SUCCESS) - goto destroy_response; - - /* build Notify(NAT-D) payloads */ - this->build_natd_payloads(this, response); - - /* derive all the keys used in the IKE_SA */ - status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "transform objects could not be created from selected proposal, deleting IKE_SA"); - return DESTROY_ME; - } - - /* message can now be sent (must not be destroyed) */ - status = this->ike_sa->send_response(this->ike_sa, response); - if (status != SUCCESS) - { - this->logger->log(this->logger, AUDIT, "unable to send IKE_SA_INIT response, deleting IKE_SA"); - response->destroy(response); - return DESTROY_ME; - } - - /* state can now be changed */ - this->logger->log(this->logger, CONTROL|LEVEL2, "create next state object of type IKE_SA_INIT_RESPONDED"); - - response = this->ike_sa->get_last_responded_message(this->ike_sa); - ike_sa_init_response_data = response->get_packet_data(response); - ike_sa_init_request_data = message->get_packet_data(message); - - next_state = ike_sa_init_responded_create(this->ike_sa, this->received_nonce, this->sent_nonce,ike_sa_init_request_data, - ike_sa_init_response_data); - - /* state can now be changed */ - this->ike_sa->set_new_state(this->ike_sa, (state_t *) next_state); - this->destroy_after_state_change(this); - return SUCCESS; - -destroy_response: - response->destroy(response); - return status; - -} - -/** - * Implementation of private_initiator_init_t.build_sa_payload. - */ -static status_t build_sa_payload(private_responder_init_t *this,sa_payload_t *sa_request, message_t *msg) -{ - proposal_t *proposal; - linked_list_t *proposal_list; - connection_t *connection; - sa_payload_t* sa_payload; - algorithm_t *algo; - - connection = this->ike_sa->get_connection(this->ike_sa); - - this->logger->log(this->logger, CONTROL | LEVEL2, "process received SA payload"); - - /* get the list of suggested proposals */ - proposal_list = sa_request->get_proposals (sa_request); - - /* select proposal */ - this->proposal = connection->select_proposal(connection, proposal_list); - while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) - { - proposal->destroy(proposal); - } - proposal_list->destroy(proposal_list); - if (this->proposal == NULL) - { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain any acceptable proposals, deleting IKE_SA"); - this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER); - return DESTROY_ME; - } - /* get selected DH group to force policy, this is very restrictive!? */ - if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &algo)) - { - this->dh_group_number = algo->algorithm; - } - - this->logger->log(this->logger, CONTROL | LEVEL2, "SA Payload processed"); - - this->logger->log(this->logger, CONTROL|LEVEL2, "building SA payload"); - sa_payload = sa_payload_create_from_proposal(this->proposal); - this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message"); - msg->add_payload(msg, (payload_t *) sa_payload); - - return SUCCESS; -} - -/** - * Implementation of private_initiator_init_t.build_ke_payload. - */ -static status_t build_ke_payload(private_responder_init_t *this,ke_payload_t *ke_request, message_t *msg) -{ - diffie_hellman_group_t group; - ke_payload_t *ke_payload; - diffie_hellman_t *dh; - chunk_t key_data; - - this->logger->log(this->logger, CONTROL | LEVEL2, "process received KE payload"); - group = ke_request->get_dh_group_number(ke_request); - - if (group == MODP_NONE) - { - this->logger->log(this->logger, AUDIT, "no Diffie-Hellman group to select, deleting IKE_SA"); - return DESTROY_ME; - } - - if (this->dh_group_number != group) - { - u_int16_t accepted_group; - chunk_t accepted_group_chunk; - /* group not same as selected one - * Maybe key exchange payload is before SA payload */ - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain an acceptable Diffie-Hellman group, deleting IKE_SA"); - - accepted_group = htons(this->dh_group_number); - accepted_group_chunk.ptr = (u_int8_t*) &(accepted_group); - accepted_group_chunk.len = 2; - this->ike_sa->send_notify(this->ike_sa,IKE_SA_INIT,INVALID_KE_PAYLOAD,accepted_group_chunk); - return DESTROY_ME; - } - - /* create diffie hellman object to handle DH exchange */ - dh = diffie_hellman_create(group); - if (dh == NULL) - { - this->logger->log(this->logger, AUDIT, "could not generate DH object with group %d, deleting IKE_SA", - mapping_find(diffie_hellman_group_m,group) ); - return DESTROY_ME; - } - this->logger->log(this->logger, CONTROL | LEVEL2, "set other DH public value"); - - dh->set_other_public_value(dh, ke_request->get_key_exchange_data(ke_request)); - - this->diffie_hellman = dh; - - this->logger->log(this->logger, CONTROL | LEVEL2, "KE payload processed."); - - this->logger->log(this->logger, CONTROL|LEVEL2, "building KE payload"); - this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data); - - ke_payload = ke_payload_create(); - ke_payload->set_key_exchange_data(ke_payload,key_data); - ke_payload->set_dh_group_number(ke_payload, this->dh_group_number); - chunk_free(&key_data); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add KE payload to message"); - msg->add_payload(msg, (payload_t *) ke_payload); - - return SUCCESS; -} - -/** - * Implementation of private_responder_init_t.build_nonce_payload. - */ -static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *msg) -{ - nonce_payload_t *nonce_payload; - randomizer_t *randomizer; - status_t status; - - this->logger->log(this->logger, CONTROL | LEVEL2, "process received NONCE payload"); - free(this->received_nonce.ptr); - this->received_nonce = CHUNK_INITIALIZER; - - this->logger->log(this->logger, CONTROL | LEVEL2, "get NONCE value and store it"); - this->received_nonce = nonce_request->get_nonce(nonce_request); - - this->logger->log(this->logger, CONTROL | LEVEL2, "create new NONCE value."); - - randomizer = this->ike_sa->get_randomizer(this->ike_sa); - status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce)); - if (status != SUCCESS) - { - return status; - } - - this->logger->log(this->logger, CONTROL|LEVEL2, "building NONCE payload"); - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->sent_nonce); - - this->logger->log(this->logger, CONTROL|LEVEL2, "add NONCE payload to message"); - msg->add_payload(msg, (payload_t *) nonce_payload); - - return SUCCESS; -} - -/** - * Implementation of private_responder_init_t.build_certreq_payload. - */ -static status_t build_certreq_payload (private_responder_init_t *this, message_t *msg) -{ - if (FALSE) - { - certreq_payload_t *certreq_payload; - - this->logger->log(this->logger, CONTROL|LEVEL2, "add CERTREQ payload to message"); - msg->add_payload(msg, (payload_t *) certreq_payload); - } - return SUCCESS; -} - -/** - * Implementation of private_initiator_init_t.build_natd_payload. - */ -static void build_natd_payload(private_responder_init_t *this, message_t *msg, notify_message_type_t type, host_t *host) -{ - chunk_t hash; - this->logger->log(this->logger, CONTROL|LEVEL1, "building Notify(NAT-D) payload"); - notify_payload_t *notify_payload; - notify_payload = notify_payload_create(); - /*notify_payload->set_protocol_id(notify_payload, NULL);*/ - /*notify_payload->set_spi(notify_payload, NULL);*/ - notify_payload->set_notify_message_type(notify_payload, type); - hash = this->ike_sa->generate_natd_hash(this->ike_sa, - msg->get_initiator_spi(msg), - msg->get_responder_spi(msg), - host); - notify_payload->set_notification_data(notify_payload, hash); - chunk_free(&hash); - this->logger->log(this->logger, CONTROL|LEVEL2, "add Notify(NAT-D) payload to message"); - msg->add_payload(msg, (payload_t *) notify_payload); -} - -/** - * Implementation of private_initiator_init_t.build_natd_payloads. - */ -static void build_natd_payloads(private_responder_init_t *this, message_t *msg) -{ - connection_t *connection; - connection = this->ike_sa->get_connection(this->ike_sa); - this->build_natd_payload(this, msg, NAT_DETECTION_SOURCE_IP, - connection->get_my_host(connection)); - this->build_natd_payload(this, msg, NAT_DETECTION_DESTINATION_IP, - connection->get_other_host(connection)); -} - -/** - * Implementation of private_responder_init_t.process_notify_payload. - */ -static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload) -{ - chunk_t notification_data; - notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); - - this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", - mapping_find(notify_message_type_m, notify_message_type)); - - switch (notify_message_type) - { - case NAT_DETECTION_DESTINATION_IP: - { - this->natd_seen_r++; - if (this->natd_hash_r_matched) - return SUCCESS; - - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_hash_r)) - { - this->natd_hash_r_matched = TRUE; - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); - } - else - { - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); - } - - return SUCCESS; - } - case NAT_DETECTION_SOURCE_IP: - { - this->natd_seen_i++; - if (this->natd_hash_i_matched) - return SUCCESS; - - notification_data = notify_payload->get_notification_data(notify_payload); - if (chunk_equals(notification_data, this->natd_hash_i)) - { - this->natd_hash_i_matched = TRUE; - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); - } - else - { - this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); - } - - return SUCCESS; - } - default: - { - this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.", - notify_message_type); - return SUCCESS; - } - } -} - -/** - * Implementation of state_t.get_state. - */ -static ike_sa_state_t get_state(private_responder_init_t *this) -{ - return RESPONDER_INIT; -} - -/** - * Implementation of state_t.destroy. - */ -static void destroy(private_responder_init_t *this) -{ - this->logger->log(this->logger, CONTROL | LEVEL1, "going to destroy responder init state object"); - - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy nonces"); - chunk_free(&(this->sent_nonce)); - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy received nonce"); - chunk_free(&(this->received_nonce)); - - chunk_free(&(this->natd_hash_i)); - chunk_free(&(this->natd_hash_r)); - - if (this->diffie_hellman != NULL) - { - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t hellman object"); - this->diffie_hellman->destroy(this->diffie_hellman); - } - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy object"); - free(this); -} - -/** - * Implementation of private_responder_init_t.destroy_after_state_change - */ -static void destroy_after_state_change (private_responder_init_t *this) -{ - this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder_init_t state object"); - - chunk_free(&(this->natd_hash_i)); - chunk_free(&(this->natd_hash_r)); - - /* destroy diffie hellman object */ - if (this->diffie_hellman != NULL) - { - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t object"); - this->diffie_hellman->destroy(this->diffie_hellman); - } - if (this->proposal) - { - this->proposal->destroy(this->proposal); - } - - this->logger->log(this->logger, CONTROL | LEVEL2, "destroy object"); - free(this); -} - -/* - * Described in header. - */ -responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa) -{ - private_responder_init_t *this = malloc_thing(private_responder_init_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 functions */ - this->build_sa_payload = build_sa_payload; - this->build_ke_payload = build_ke_payload; - this->build_nonce_payload = build_nonce_payload; - this->build_certreq_payload = build_certreq_payload; - this->destroy_after_state_change = destroy_after_state_change; - this->process_notify_payload = process_notify_payload; - this->build_natd_payload = build_natd_payload; - this->build_natd_payloads = build_natd_payloads; - - /* private data */ - this->ike_sa = ike_sa; - this->logger = logger_manager->get_logger(logger_manager, IKE_SA); - this->sent_nonce = CHUNK_INITIALIZER; - this->received_nonce = CHUNK_INITIALIZER; - this->dh_group_number = MODP_NONE; - this->diffie_hellman = NULL; - this->proposal = NULL; - this->natd_hash_i = CHUNK_INITIALIZER; - this->natd_hash_i_matched = FALSE; - this->natd_seen_i = 0; - this->natd_hash_r = CHUNK_INITIALIZER; - this->natd_hash_r_matched = FALSE; - this->natd_seen_r = 0; - - return &(this->public); -} diff --git a/src/charon/sa/states/responder_init.h b/src/charon/sa/states/responder_init.h deleted file mode 100644 index c8ba73ea3..000000000 --- a/src/charon/sa/states/responder_init.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file responder_init.h - * - * @brief Interface of responder_init_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 RESPONDER_INIT_H_ -#define RESPONDER_INIT_H_ - -#include <sa/ike_sa.h> -#include <sa/states/state.h> - - -typedef struct responder_init_t responder_init_t; - -/** - * @brief This class represents an IKE_SA state when - * initializing a connection as responder. - * - * @b Constructors: - * - responder_init_create() - * - * @ingroup states - */ -struct responder_init_t { - /** - * The state_t interface. - */ - state_t state_interface; -}; - -/** - * Constructor of class responder_init_t. - * - * The following functions of the assigned protected_ike_sa_t object are being called with - * valid values after successfully processing a received message and before changing - * to next state IKE_SA_INIT_RESPONDED: - * - protected_ike_sa_t.set_connection() - * - protected_ike_sa_t.set_my_host() - * - protected_ike_sa_t.set_other_host() - * - protected_ike_sa_t.compute_secrets() - * - protected_ike_sa_t.create_transforms_from_proposal() - * - * @param ike_sa assigned IKE_SA - * - * @return responder_init_t object - * - * @ingroup states - */ -responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa); - -#endif /*RESPONDER_INIT_H_*/ diff --git a/src/charon/sa/states/state.c b/src/charon/sa/states/state.c deleted file mode 100644 index 5436e7d44..000000000 --- a/src/charon/sa/states/state.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file state.c - * - * @brief Interface state_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 "state.h" - - -/** - * String mappings for ike_sa_state_t. - */ -mapping_t ike_sa_state_m[] = { - {INITIATOR_INIT, "INITIATOR_INIT"}, - {RESPONDER_INIT, "RESPONDER_INIT"}, - {IKE_SA_INIT_REQUESTED, "IKE_SA_INIT_REQUESTED"}, - {IKE_SA_INIT_RESPONDED, "IKE_SA_INIT_RESPONDED"}, - {IKE_AUTH_REQUESTED, "IKE_AUTH_REQUESTED"}, - {IKE_SA_ESTABLISHED, "IKE_SA_ESTABLISHED"}, - {DELETE_IKE_SA_REQUESTED, "DELETE_IKE_SA_REQUESTED"}, - {CREATE_CHILD_SA_REQUESTED, "CREATE_CHILD_SA_REQUESTED"}, - {DELETE_CHILD_SA_REQUESTED, "DELETE_CHILD_SA_REQUESTED"}, - {MAPPING_END, NULL} -}; - diff --git a/src/charon/sa/states/state.h b/src/charon/sa/states/state.h deleted file mode 100644 index e1cd490fa..000000000 --- a/src/charon/sa/states/state.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @file state.h - * - * @brief Interface state_t. - * - */ - -/* - * Copyright (C) 2005 Jan Hutter, 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 STATE_H_ -#define STATE_H_ - -#include <definitions.h> -#include <types.h> -#include <encoding/message.h> - -typedef enum ike_sa_state_t ike_sa_state_t; - -/** - * States in which a IKE_SA can be. - * - * @todo Support of more states (CHILD_SA_REQUESTED, etc...) - * - * @see state_t for state diagram - * - * @ingroup states - */ -enum ike_sa_state_t { - - /** - * @brief IKE_SA is in initial state as initiator and is going to initiate a new connection. - * - * Next state following this state is IKE_SA_INIT_REQUESTED. - * - * Implemented in class initiator_init_t. - */ - INITIATOR_INIT = 1, - - /** - * @brief IKE_SA is in initial state as responder and is going to respond to a initiated connection. - * - * Next state following this state is IKE_SA_INIT_RESPONDED. - * - * Implemented in class responder_init_t. - */ - RESPONDER_INIT, - - /** - * @brief A IKE_SA_INIT request was sent. In this state a reply of type IKE_SA_INIT is expected. - * - * Two states are possible as next states: - * - IKE_AUTH_REQUESTED if IKE_SA_INIT reply could successfully processed and IKE_AUTH request could be sent. - * - INITIATOR_INIT if selected DH group was not the one selected by other peer. - * - * Implemented in class ike_sa_init_requested_t. - */ - IKE_SA_INIT_REQUESTED, - - /** - * @brief A IKE_SA_INIT response was sent. In this state a request of type IKE_AUTH is expected. - * - * Next state following this state is IKE_SA_ESTABLISHED. - * - * Implemented in class ike_sa_init_responded_t. - */ - IKE_SA_INIT_RESPONDED, - - /** - * @brief An IKE_AUTH request was sent after a successful IKE_SA_INIT-exchange. - * - * Next state following this state is IKE_SA_ESTABLISHED. - * - * Implemented in class ike_auth_requested_t. - */ - IKE_AUTH_REQUESTED, - - /** - * @brief An IKE_AUTH exchange was successfuly handled either as initiator or responder. - * - * In this state, all the informations for an IKE_SA and one CHILD_SA are known. - * - * Implemented in class ike_sa_established_t. - */ - IKE_SA_ESTABLISHED, - - /** - * @brief A rekeying/create CHILD_SA request was sent. - * - * Implemented in class create_child_sa_requested. - */ - CREATE_CHILD_SA_REQUESTED, - - /** - * @brief A delete CHILD_SA request was sent. - * - * Implemented in class delete_child_sa_requested. - */ - DELETE_CHILD_SA_REQUESTED, - - /** - * @brief An IKE SA has sent a DELETE IKE_SA to the other peer. - * - * After a call to ike_sa.close(), the IKE_SA sends a delete message - * to the remote peer and switches to this state. It waits until the - * message is aknowledged, or a certain timout occurs. - * - * Implemented in class delete_requested. - */ - DELETE_IKE_SA_REQUESTED, -}; - - -/** - * String mappings for ike_sa_state_t. - */ -extern mapping_t ike_sa_state_m[]; - - -typedef struct state_t state_t; - -/** - * @brief This interface represents an IKE_SA state. - * - * A state_t object is responsible to handle incoming messages. States - * are exclusive, an IKE_SA is exactly in one state. They are used on IKE_SA - * setup, as there is a strict scheme message exchange follow. This can be - * mapped in a state machine. Every state is represented in a single class, - * and the IKE_SA may switch these states by replacing the owned state. - @verbatim - initiator responder - --------- --------- - - ¦ ¦ - V ¦ - +-----------------------+ ¦ - ¦ initiator_init ¦ msg1 V - +-----------------------+ -----> +-----------------------+ - ¦ msg2 ¦ responder_init ¦ - V <----- +-----------------------+ - +-----------------------+ ¦ - ¦ ike_sa_init_requested ¦ msg3 V - +-----------------------+ -----> +-----------------------+ - ¦ msg4 ¦ ike_sa_init_requested ¦ - V <----- +-----------------------+ - +-----------------------+ ¦ - ¦ ike_auth_requested ¦ ¦ - +-----------------------+ ¦ - ¦ ¦ - V V - +---------------------------+ - ¦ ike_sa_established ¦ - +---------------------------+ - ¦ - V - +---------------------------+ - ¦ delete_requested ¦ - +---------------------------+ - - msg1 = IKE_SA_INIT request - msg2 = IKE_SA_INIT response - msg3 = IKE_AUTH request - msg4 = IKE_AUTH response - @endverbatim - * Every state can be left by deleting the IKE_SA, except the state - * ike_sa_established: it must switch to the delete_requested state first, - * as the peer must be informed about the delete. - * - * For the handling of message in a established IKE_SA, another concept is used. - * The state-concept is good if a single state is possible. But in a established - * IKE_SA, there is no strict message order, and if a window size > 1 is used, - * multiple "states" would be possible. We call this transactions, better - * descripted in the transaction_t interface. - * - * @b Constructors: - * - initiator_init_create() - * - responder_init_create() - * - ike_sa_init_requested_create() - * - ike_sa_init_responded_create() - * - ike_auth_requested_create() - * - ike_sa_established_create() - * - delete_requested_create() - * - * @ingroup states - */ -struct state_t { - - /** - * @brief Processes a incoming IKEv2-Message of type message_t. - * - * @param this calling object - * @param[in] message message_t object to process - * @return - * - SUCCESSFUL - * - FAILED - * - DESTROY_ME if belonging IKE_SA should be deleted - */ - status_t (*process_message) (state_t *this,message_t *message); - - /** - * @brief Get the current state representing by this state_t object. - * - * @param this calling object - * @return state - */ - ike_sa_state_t (*get_state) (state_t *this); - - /** - * @brief Destroys a state_t object. - * - * @param this calling object - */ - void (*destroy) (state_t *this); -}; - -#endif /* STATE_H_ */ diff --git a/src/charon/sa/transactions/dead_peer_detection.c b/src/charon/sa/transactions/dead_peer_detection.c new file mode 100644 index 000000000..de15c49ab --- /dev/null +++ b/src/charon/sa/transactions/dead_peer_detection.c @@ -0,0 +1,198 @@ +/** + * @file dead_peer_detection.c + * + * @brief Implementation of the dead_peer_detection 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 "dead_peer_detection.h" + +#include <daemon.h> + + +typedef struct private_dead_peer_detection_t private_dead_peer_detection_t; + +/** + * Private members of a dead_peer_detection_t object.. + */ +struct private_dead_peer_detection_t { + + /** + * Public methods and transaction_t interface. + */ + dead_peer_detection_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; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_dead_peer_detection_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_dead_peer_detection_t *this) +{ + return this->requested++; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_dead_peer_detection_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)); + /* apply for caller */ + *result = request; + /* store for retransmission */ + this->message = request; + + return SUCCESS; +} + +/** + * Implementation of transaction_t.get_response. + */ +static status_t get_response(private_dead_peer_detection_t *this, message_t *request, + message_t **result, transaction_t **next) +{ + host_t *me, *other; + message_t *response; + 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; + + return SUCCESS; +} + + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_dead_peer_detection_t *this, message_t *response, + transaction_t **transaction) +{ + return SUCCESS; +} + +/** + * implements transaction_t.destroy + */ +static void destroy(private_dead_peer_detection_t *this) +{ + if (this->message) + { + this->message->destroy(this->message); + } + free(this); +} + +/* + * Described in header. + */ +dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_dead_peer_detection_t *this = malloc_thing(private_dead_peer_detection_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; + + /* 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/dead_peer_detection.h b/src/charon/sa/transactions/dead_peer_detection.h new file mode 100644 index 000000000..7df4410d1 --- /dev/null +++ b/src/charon/sa/transactions/dead_peer_detection.h @@ -0,0 +1,60 @@ +/** + * @file dead_peer_detection.h + * + * @brief Interface of transaction dead_peer_detection. + * + */ + +/* + * 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 DEAD_PEER_DETECTION_H_ +#define DEAD_PEER_DETECTION_H_ + +#include <sa/ike_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct dead_peer_detection_t dead_peer_detection_t; + +/** + * @brief A transaction used to detect dead peers. + * + * In IKEv2, dead peer detection is done using empty + * informational messages. These must be acknowledged. + * + * @ingroup transactions + */ +struct dead_peer_detection_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; +}; + +/** + * @brief Create a new transaction which detects dead peers. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created dead_peer_detection transaction + * + * @ingroup transactions + */ +dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* DEAD_PEER_DETECTION_H_ */ diff --git a/src/charon/sa/transactions/delete_ike_sa.c b/src/charon/sa/transactions/delete_ike_sa.c new file mode 100644 index 000000000..a8ec334a7 --- /dev/null +++ b/src/charon/sa/transactions/delete_ike_sa.c @@ -0,0 +1,271 @@ +/** + * @file delete_ike_sa.c + * + * @brief Implementation of the delete_ike_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_ike_sa.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.h> + + +typedef struct private_delete_ike_sa_t private_delete_ike_sa_t; + +/** + * Private members of a delete_ike_sa_t object.. + */ +struct private_delete_ike_sa_t { + + /** + * Public methods and transaction_t interface. + */ + delete_ike_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; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_delete_ike_sa_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_delete_ike_sa_t *this) +{ + return this->requested++; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_delete_ike_sa_t *this, message_t **result) +{ + message_t *request; + connection_t *connection; + host_t *me, *other; + delete_payload_t *delete_payload; + + /* 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)); + /* apply for caller */ + *result = request; + /* store for retransmission */ + this->message = request; + + delete_payload = delete_payload_create(PROTO_IKE); + request->add_payload(request, (payload_t*)delete_payload); + + /* transit to state SA_DELETING */ + this->ike_sa->set_state(this->ike_sa, SA_DELETING); + + return SUCCESS; +} + +/** + * Implementation of transaction_t.get_response. + */ +static status_t get_response(private_delete_ike_sa_t *this, message_t *request, + message_t **result, transaction_t **next) +{ + host_t *me, *other; + message_t *response; + iterator_t *payloads; + 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. */ + 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; + + /* 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: + { + delete_request = (delete_payload_t *)payload; + 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); + + if (delete_request && + delete_request->get_protocol_id(delete_request) == PROTO_IKE) + { + this->logger->log(this->logger, CONTROL, + "DELETE request for IKE_SA received, deleting IKE_SA"); + } + else + { + /* should not happen, as we preparsed this at transaction construction */ + this->logger->log(this->logger, CONTROL, + "received a weird DELETE request for IKE_SA, deleting anyway"); + } + if (this->ike_sa->get_state(this->ike_sa) == SA_DELETING) + { + /* if we are already deleting an IKE_SA, we do not destroy. We wait + * until we get the response for our initiated delete. */ + return SUCCESS; + } + this->ike_sa->set_state(this->ike_sa, SA_DELETING); + return DESTROY_ME; +} + + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_delete_ike_sa_t *this, message_t *response, + transaction_t **transaction) +{ + /* check message type */ + if (response->get_exchange_type(response) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR, + "INFORMATIONAL response of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + /* this is only an acknowledge. We can't do anything here, but delete + * the IKE_SA. */ + return DESTROY_ME; +} + +/** + * implements transaction_t.destroy + */ +static void destroy(private_delete_ike_sa_t *this) +{ + if (this->message) + { + this->message->destroy(this->message); + } + free(this); +} + +/* + * Described in header. + */ +delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_delete_ike_sa_t *this = malloc_thing(private_delete_ike_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; + + /* 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_ike_sa.h b/src/charon/sa/transactions/delete_ike_sa.h new file mode 100644 index 000000000..bb556acc7 --- /dev/null +++ b/src/charon/sa/transactions/delete_ike_sa.h @@ -0,0 +1,83 @@ +/** + * @file delete_ike_sa.h + * + * @brief Interface of transaction delete_ike_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_IKE_SA_H_ +#define DELETE_IKE_SA_H_ + +#include <sa/ike_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct delete_ike_sa_t delete_ike_sa_t; + +/** + * @brief A transaction used to delete the IKE_SA. + * + * Notation as follows: + * Mx{D} means: Message, with message ID "x", containing a Delete payload + * + * The clarifcation Document says in 5.8, that a IKE_SA delete should not + * be acknowledged with the same delete. This only makes sense for CHILD_SAs, + * as they are paired. IKE_SAs are not, there is only one for both ends. + * + * Normal case: + * ---------------- + * Mx{D} --> + * <-- Mx{} + * Delete request is sent, and we wait for the acknowledge. + * + * Special case 1: + * --------------- + * Mx{D} --> + * <-- My{D} + * My{} --> + * <-- Mx{} + * Both initate a delete at the same time. We ack the delete, but wait for + * our delete to be acknowledged. + * + * @b Constructors: + * - delete_ike_sa_create() + * - transaction_create() with the appropriate message + * + * @ingroup transactions + */ +struct delete_ike_sa_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; +}; + +/** + * @brief Create a new transaction which deletes the IKE_SA. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created delete_ike_sa transaction + * + * @ingroup transactions + */ +delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* DELETE_IKE_SA_H_ */ diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c new file mode 100644 index 000000000..faf0ef6e5 --- /dev/null +++ b/src/charon/sa/transactions/ike_auth.c @@ -0,0 +1,1007 @@ +/** + * @file ike_auth.c + * + * @brief Implementation of ike_auth_t transaction. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter, 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 "ike_auth.h" + +#include <string.h> + +#include <daemon.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/id_payload.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/ts_payload.h> +#include <sa/authenticator.h> +#include <sa/child_sa.h> + + +typedef struct private_ike_auth_t private_ike_auth_t; + +/** + * Private members of a ike_auth_t object.. + */ +struct private_ike_auth_t { + + /** + * Public methods and transaction_t interface. + */ + ike_auth_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; + + /** + * initiator chosen nonce + */ + chunk_t nonce_i; + + /** + * responder chosen nonce + */ + chunk_t nonce_r; + + /** + * encoded request message of ike_sa_init transaction + */ + chunk_t init_request; + + /** + * encoded response message of ike_sa_init transaction + */ + chunk_t init_response; + + /** + * connection definition used + */ + connection_t *connection; + + /** + * policy definition used + */ + policy_t *policy; + + /** + * Negotiated proposal used for CHILD_SA + */ + proposal_t *proposal; + + /** + * Negotiated traffic selectors for initiator + */ + linked_list_t *tsi; + + /** + * Negotiated traffic selectors for responder + */ + linked_list_t *tsr; + + /** + * CHILD_SA created along with IKE_AUTH + */ + child_sa_t *child_sa; + + /** + * did other peer create a CHILD_SA? + */ + bool build_child; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_ike_auth_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_ike_auth_t *this) +{ + return this->requested++; +} + +/** + * Implementation of transaction_t.set_nonces. + */ +static void set_nonces(private_ike_auth_t *this, chunk_t nonce_i, chunk_t nonce_r) +{ + this->nonce_i = nonce_i; + this->nonce_r = nonce_r; +} + +/** + * Implementation of transaction_t.set_init_messages. + */ +static void set_init_messages(private_ike_auth_t *this, chunk_t init_request, chunk_t init_response) +{ + this->init_request = init_request; + this->init_response = init_response; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_ike_auth_t *this, message_t **result) +{ + message_t *request; + host_t *me, *other; + identification_t *my_id, *other_id; + id_payload_t *my_id_payload; + + /* 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); + my_id = this->policy->get_my_id(this->policy); + other_id = this->policy->get_other_id(this->policy); + + /* 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, IKE_AUTH); + 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)); + /* apply for caller */ + *result = request; + /* store for retransmission */ + this->message = request; + + { /* build ID payload */ + my_id_payload = id_payload_create_from_identification(TRUE, my_id); + request->add_payload(request, (payload_t*)my_id_payload); + } + + { /* TODO: build certreq payload */ + + } + + if (this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) + { /* build certificate payload. TODO: Handle certreq from init_ike_sa. */ + x509_t *cert; + cert_payload_t *cert_payload; + + cert = charon->credentials->get_certificate(charon->credentials, my_id); + if (cert) + { + cert_payload = cert_payload_create_from_x509(cert); + request->add_payload(request, (payload_t*)cert_payload); + } + else + { + this->logger->log(this->logger, ERROR, + "could not find my certificate, certificate payload ommited"); + } + } + + { /* build IDr payload, if other_id defined */ + id_payload_t *id_payload; + if (!other_id->contains_wildcards(other_id)) + { + id_payload = id_payload_create_from_identification(FALSE, other_id); + request->add_payload(request, (payload_t*)id_payload); + } + } + + { /* build auth payload */ + authenticator_t *authenticator; + auth_payload_t *auth_payload; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->compute_auth_data(authenticator, &auth_payload, + this->init_request, this->nonce_r, my_id_payload, TRUE); + authenticator->destroy(authenticator); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, + "could not generate AUTH data, deleting IKE_SA"); + return DESTROY_ME; + } + request->add_payload(request, (payload_t*)auth_payload); + } + + { /* build SA payload for CHILD_SA */ + linked_list_t *proposal_list; + sa_payload_t *sa_payload; + u_int32_t soft_lifetime, hard_lifetime; + + proposal_list = this->policy->get_proposals(this->policy); + soft_lifetime = this->policy->get_soft_lifetime(this->policy); + hard_lifetime = this->policy->get_hard_lifetime(this->policy); + this->child_sa = child_sa_create(0, me, other, soft_lifetime, hard_lifetime, + this->ike_sa->is_natt_enabled(this->ike_sa)); + if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "could not install CHILD_SA, deleting IKE_SA"); + return DESTROY_ME; + } + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + request->add_payload(request, (payload_t*)sa_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); + } + + return SUCCESS; +} + +/** + * Handle all kind of notifys + */ +static status_t process_notifys(private_ike_auth_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) + { + /* these notifys are not critical. no child_sa is built, but IKE stays alive */ + case SINGLE_PAIR_REQUIRED: + { + this->logger->log(this->logger, AUDIT, + "received a SINGLE_PAIR_REQUIRED notify"); + this->build_child = FALSE; + return SUCCESS; + } + case TS_UNACCEPTABLE: + { + this->logger->log(this->logger, CONTROL, + "received TS_UNACCEPTABLE notify"); + this->build_child = FALSE; + return SUCCESS; + } + case NO_PROPOSAL_CHOSEN: + { + this->logger->log(this->logger, CONTROL, + "received NO_PROPOSAL_CHOSEN notify"); + this->build_child = FALSE; + 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 DESTROY_ME; + } + 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); +} + +/** + * Import a certificate from a cert payload + */ +static void import_certificate(private_ike_auth_t *this, cert_payload_t *cert_payload) +{ + bool found; + x509_t *cert; + cert_encoding_t encoding; + + encoding = cert_payload->get_cert_encoding(cert_payload); + if (encoding != CERT_X509_SIGNATURE) + { + this->logger->log(this->logger, CONTROL, + "certificate payload %s not supported, ignored", + enum_name(&cert_encoding_names, encoding)); + return; + } + cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload)); + + if (charon->credentials->verify(charon->credentials, cert, &found)) + { + this->logger->log(this->logger, CONTROL|LEVEL1, + "end entity certificate is trusted"); + if (found) + { + cert->destroy(cert); + } + else + { + cert = charon->credentials->add_end_certificate(charon->credentials, cert); + } + } + else + { + this->logger->log(this->logger, ERROR, + "end entity certificate is not trusted"); + } +} + +/** + * Install a CHILD_SA for usage + */ +static status_t install_child_sa(private_ike_auth_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_ike_auth_t *this, message_t *request, + message_t **result, transaction_t **next) +{ + host_t *me, *other; + identification_t *my_id, *other_id; + message_t *response; + status_t status; + iterator_t *payloads; + id_payload_t *idi_request = NULL; + id_payload_t *idr_request = NULL; + auth_payload_t *auth_request = NULL; + cert_payload_t *cert_request = NULL; + sa_payload_t *sa_request = NULL; + ts_payload_t *tsi_request = NULL; + 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) + { + *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); + + /* 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, IKE_AUTH); + 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; + + /* 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 ID_INITIATOR: + idi_request = (id_payload_t*)payload; + break; + case ID_RESPONDER: + idr_request = (id_payload_t*)payload; + break; + case AUTHENTICATION: + auth_request = (auth_payload_t*)payload; + break; + case CERTIFICATE: + cert_request = (cert_payload_t*)payload; + break; + case SECURITY_ASSOCIATION: + sa_request = (sa_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 == FAILED) + { + payloads->destroy(payloads); + /* we return SUCCESS, returned FAILED means do next transaction */ + return SUCCESS; + } + if (status == DESTROY_ME) + { + payloads->destroy(payloads); + return DESTROY_ME; + } + 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 (!(idi_request && auth_request && sa_request && tsi_request && tsr_request)) + { + build_notify(INVALID_SYNTAX, response, TRUE); + this->logger->log(this->logger, AUDIT, + "request message incomplete, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* process ID payload */ + other_id = idi_request->get_identification(idi_request); + if (idr_request) + { + my_id = idr_request->get_identification(idr_request); + } + else + { + my_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); + } + + /* get policy from store */ + this->policy = charon->policies->get_policy_by_ids(charon->policies, my_id, other_id); + if (this->policy == NULL) + { + this->logger->log(this->logger, AUDIT, + "we don't have a policy for IDs %s - %s, deleting IKE_SA", + my_id->get_string(my_id), other_id->get_string(other_id)); + my_id->destroy(my_id); + other_id->destroy(other_id); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + return DESTROY_ME; + } + my_id->destroy(my_id); + other_id->destroy(other_id); + + /* get my id from policy, which must contain a fully qualified valid id */ + my_id = this->policy->get_my_id(this->policy); + + /* update others traffic selectors with actually used address */ + this->policy->update_my_ts(this->policy, me); + this->policy->update_other_ts(this->policy, other); + + this->ike_sa->set_policy(this->ike_sa, this->policy); + idr_response = id_payload_create_from_identification(FALSE, my_id); + response->add_payload(response, (payload_t*)idr_response); + } + + if (this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) + { /* build certificate payload */ + x509_t *cert; + cert_payload_t *cert_payload; + + cert = charon->credentials->get_certificate(charon->credentials, my_id); + if (cert == NULL) + { + this->logger->log(this->logger, ERROR, + "could not find my certificate, cert payload ommited"); + } + cert_payload = cert_payload_create_from_x509(cert); + response->add_payload(response, (payload_t *)cert_payload); + } + + if (cert_request) + { /* process certificate payload */ + import_certificate(this, cert_request); + } + + { /* process auth payload */ + authenticator_t *authenticator; + auth_payload_t *auth_response; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->verify_auth_data(authenticator, auth_request, + this->init_request, + this->nonce_r, idi_request, + TRUE); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, + "authentication failed, deleting IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + authenticator->destroy(authenticator); + return DESTROY_ME; + } + status = authenticator->compute_auth_data(authenticator, &auth_response, + this->init_response, + this->nonce_i, idr_response, + FALSE); + authenticator->destroy(authenticator); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, + "authentication data generation failed, deleting IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + return DESTROY_ME; + } + response->add_payload(response, (payload_t*)auth_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; + + /* prepare reply */ + 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, FALSE); + } + /* 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, FALSE); + } + else + { + /* create child 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(0, 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, FALSE); + } + /* 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); + } + /* set established state */ + this->ike_sa->set_state(this->ike_sa, SA_ESTABLISHED); + return SUCCESS; +} + + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_ike_auth_t *this, message_t *response, + transaction_t **transaction) +{ + iterator_t *payloads; + host_t *me, *other; + identification_t *other_id; + ts_payload_t *tsi_payload = NULL; + ts_payload_t *tsr_payload = NULL; + id_payload_t *idr_payload = NULL; + cert_payload_t *cert_payload = NULL; + auth_payload_t *auth_payload = NULL; + sa_payload_t *sa_payload = NULL; + status_t status; + + /* check message type */ + if (response->get_exchange_type(response) != IKE_AUTH) + { + this->logger->log(this->logger, ERROR, + "IKE_AUTH response of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + + 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 ID_RESPONDER: + idr_payload = (id_payload_t*)payload; + break; + case AUTHENTICATION: + auth_payload = (auth_payload_t*)payload; + break; + case CERTIFICATE: + cert_payload = (cert_payload_t*)payload; + break; + case SECURITY_ASSOCIATION: + sa_payload = (sa_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 == FAILED) + { + payloads->destroy(payloads); + /* we return SUCCESS, returned FAILED means do next transaction */ + return SUCCESS; + } + if (status == DESTROY_ME) + { + payloads->destroy(payloads); + return status; + } + break; + } + default: + { + this->logger->log(this->logger, CONTROL, "ignoring payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), + payload->get_type(payload)); + break; + } + } + } + payloads->destroy(payloads); + + if (!(idr_payload && auth_payload && sa_payload && tsi_payload && tsr_payload)) + { + this->logger->log(this->logger, AUDIT, "response message incomplete, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* process idr payload */ + identification_t *configured_other_id; + + other_id = idr_payload->get_identification(idr_payload); + configured_other_id = this->policy->get_other_id(this->policy); + + if (!other_id->belongs_to(other_id, configured_other_id)) + { + other_id->destroy(other_id); + this->logger->log(this->logger, AUDIT, + "other peer uses unacceptable ID (%s, excepted %s), deleting IKE_SA", + other_id->get_string(other_id), + configured_other_id->get_string(configured_other_id)); + return DESTROY_ME; + } + + this->policy->update_other_id(this->policy, other_id); + } + + if (cert_payload) + { /* process cert payload */ + import_certificate(this, cert_payload); + } + + { /* authenticate peer */ + authenticator_t *authenticator; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->verify_auth_data(authenticator, auth_payload, + this->init_response, + this->nonce_i, idr_payload, + FALSE); + authenticator->destroy(authenticator); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "authentication failed, deleting IKE_SA"); + return DESTROY_ME; + } + } + + { /* 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->build_child) + { + this->logger->log(this->logger, AUDIT, + "CHILD_SA creation failed"); + } + else + { + if (install_child_sa(this, TRUE) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "installing CHILD_SA failed, no CHILD_SA built"); + } + } + } + /* set new state */ + this->ike_sa->set_state(this->ike_sa, SA_ESTABLISHED); + return SUCCESS; +} + +/** + * implements transaction_t.destroy + */ +static void destroy(private_ike_auth_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); + chunk_free(&this->init_request); + chunk_free(&this->init_response); + free(this); +} + +/* + * Described in header. + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_ike_auth_t *this = malloc_thing(private_ike_auth_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.set_nonces = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_nonces; + this->public.set_init_messages = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_init_messages; + + /* private data */ + this->ike_sa = ike_sa; + this->message_id = message_id; + this->message = NULL; + this->requested = 0; + this->nonce_i = CHUNK_INITIALIZER; + this->nonce_r = CHUNK_INITIALIZER; + this->init_request = CHUNK_INITIALIZER; + this->init_response = CHUNK_INITIALIZER; + this->child_sa = NULL; + this->proposal = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->build_child = TRUE; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &this->public; +} diff --git a/src/charon/sa/transactions/ike_auth.h b/src/charon/sa/transactions/ike_auth.h new file mode 100644 index 000000000..1404b785e --- /dev/null +++ b/src/charon/sa/transactions/ike_auth.h @@ -0,0 +1,86 @@ +/** + * @file ike_auth.h + * + * @brief Interface of transaction ike_auth. + * + */ + +/* + * 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 IKE_AUTH_H_ +#define IKE_AUTH_H_ + +#include <sa/ike_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct ike_auth_t ike_auth_t; + +/** + * @brief A transaction for the second message exchange to authenticate an IKE_SA. + * + * The second transaction is encrypted and authenticates the peers. It also + * sets up a first CHILD_SA. + * + * @b Constructors: + * - ike_auth_create() + * - transaction_create() with the appropriate message + * + * @ingroup transactions + */ +struct ike_auth_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; + + /** + * @brief Set the nonces used in the previous ike_sa_init transaction. + * + * The nonces are used to create the authentication data. + * + * @param this calling object + * @param nonce_i initiator chosen nonce + * @param nonce_r responder chosen nonce + */ + void (*set_nonces) (ike_auth_t* this, chunk_t nonce_i, chunk_t nonce_r); + + /** + * @brief Set the messages used in the previous ike_sa_init transaction. + * + * The messages are used to create the authentication data. + * + * @param this calling object + * @param request encoded request message as a chunk + * @param response encoded response message as a chunk + */ + void (*set_init_messages) (ike_auth_t* this, chunk_t request, chunk_t response); +}; + +/** + * @brief Create a new transaction which processes IKE_AUTH exchanges. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created ike_auth transaction + * + * @ingroup transactions + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* IKE_AUTH_H_ */ diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c new file mode 100644 index 000000000..ce6d045ac --- /dev/null +++ b/src/charon/sa/transactions/ike_sa_init.c @@ -0,0 +1,1079 @@ +/** + * @file ike_sa_init.c + * + * @brief Implementation of ike_sa_init_t transaction. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_init.h" + +#include <string.h> + +#include <daemon.h> +#include <crypto/diffie_hellman.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <sa/transactions/ike_auth.h> +#include <queues/jobs/delete_half_open_ike_sa_job.h> + + +typedef struct private_ike_sa_init_t private_ike_sa_init_t; + +/** + * Private members of a ike_sa_init_t object.. + */ +struct private_ike_sa_init_t { + + /** + * Public methods and transaction_t interface. + */ + ike_sa_init_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; + + /** + * Next transaction followed to this one. May be IKE_AUTH, + * or a IKE_SA_INIT retry + */ + transaction_t **next; + + /** + * Diffie hellman object used to generate public DH value. + */ + diffie_hellman_t *diffie_hellman; + + /** + * initiator chosen nonce + */ + chunk_t nonce_i; + + /** + * responder chosen nonce + */ + chunk_t nonce_r; + + /** + * connection definition used + */ + connection_t *connection; + + /** + * Negotiated proposal used for IKE_SA + */ + proposal_t *proposal; + + /** + * Randomizer to generate nonces + */ + randomizer_t *randomizer; + + /** + * Hasher used to build NAT detection hashes + */ + hasher_t *nat_hasher; + + /** + * Precomputed NAT hash for source address + */ + chunk_t natd_src_hash; + + /** + * Precomputed NAT hash for destination address + */ + chunk_t natd_dst_hash; + + /** + * Did we process any NAT detection notifys for a source address? + */ + bool natd_src_seen; + + /** + * Did we process any NAT detection notifys for a destination address? + */ + bool natd_dst_seen; + + /** + * Have we found a matching source address NAT hash? + */ + bool natd_src_matched; + + /** + * Have we found a matching destination address NAT hash? + */ + bool natd_dst_matched; + + /** + * Assigned logger. + */ + logger_t *logger; +}; + +/** + * Implementation of ike_sa_init_t.use_dh_group. + */ +static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group) +{ + this->connection = this->ike_sa->get_connection(this->ike_sa); + + if (this->connection->check_dh_group(this->connection, dh_group)) + { + this->diffie_hellman = diffie_hellman_create(dh_group); + if (this->diffie_hellman) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Implementation of transaction_t.get_message_id. + */ +static u_int32_t get_message_id(private_ike_sa_init_t *this) +{ + return this->message_id; +} + +/** + * Implementation of transaction_t.requested. + */ +static u_int32_t requested(private_ike_sa_init_t *this) +{ + return this->requested++; +} + +/** + * Build NAT detection hash for a host + */ +static chunk_t generate_natd_hash(private_ike_sa_init_t *this, + ike_sa_id_t * ike_sa_id, host_t *host) +{ + chunk_t natd_string; + chunk_t natd_hash; + u_int8_t *p; + u_int64_t spi_i, spi_r; + char buf[512]; + + spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); + spi_r = ike_sa_id->get_responder_spi(ike_sa_id); + + switch (host->get_family(host)) + { + case AF_INET: + { + struct sockaddr_in* sai; + natd_string = chunk_alloc(sizeof(spi_i) + sizeof(spi_r) + + sizeof(sai->sin_addr.s_addr) + + sizeof(sai->sin_port)); + sai = (struct sockaddr_in*)host->get_sockaddr(host); + p = natd_string.ptr; + *(u_int64_t*)p = spi_i; p += sizeof(spi_i); + *(u_int64_t*)p = spi_r; p += sizeof(spi_r); + *(u_int32_t*)p = sai->sin_addr.s_addr; p += sizeof(sai->sin_addr.s_addr); + *(u_int16_t*)p = sai->sin_port; p += sizeof(sai->sin_port); + break; + } + case AF_INET6: + default: + /* TODO: Add IPv6 support */ + natd_string = CHUNK_INITIALIZER; + } + + this->nat_hasher->allocate_hash(this->nat_hasher, natd_string, &natd_hash); + + sprintf(buf, "natd_hash(%016llx %016llx %s:%d) == SHA1(", spi_i, spi_r, + host->get_address(host), host->get_port(host)); + chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string); + strcat(buf, ") == "); + chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash); + this->logger->log(this->logger, CONTROL|LEVEL3, buf); + + chunk_free(&natd_string); + return natd_hash; +} + +/** + * Build a NAT detection notify payload. + */ +static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this, + notify_type_t type, host_t *host) +{ + chunk_t hash; + notify_payload_t *notify; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + hash = generate_natd_hash(this, ike_sa_id, host); + notify->set_notification_data(notify, hash); + chunk_free(&hash); + + return notify; +} + +/** + * Implementation of transaction_t.get_request. + */ +static status_t get_request(private_ike_sa_init_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); + + /* 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, IKE_SA_INIT); + 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)); + /* apply for caller */ + *result = request; + /* store for retransmission */ + this->message = request; + + /* if the DH group is set via use_dh_group(), we already have a DH object */ + if (!this->diffie_hellman) + { + diffie_hellman_group_t dh_group; + + dh_group = this->connection->get_dh_group(this->connection); + this->diffie_hellman = diffie_hellman_create(dh_group); + if (this->diffie_hellman == NULL) + { + this->logger->log(this->logger, AUDIT, + "DH group %s (%d) not supported, aborting", + mapping_find(diffie_hellman_group_m, dh_group), dh_group); + return DESTROY_ME; + } + } + + { /* build the SA payload from proposals */ + sa_payload_t *sa_payload; + linked_list_t *proposal_list; + + proposal_list = this->connection->get_proposals(this->connection); + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + + request->add_payload(request, (payload_t*)sa_payload); + } + + { /* build the KE payload from the DH object */ + ke_payload_t *ke_payload; + + ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + + request->add_payload(request, (payload_t*)ke_payload); + } + + { /* 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, + 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); + + request->add_payload(request, (payload_t*)nonce_payload); + } + + { /* build NAT_DETECTION notifys */ + notify_payload_t *notify; + iterator_t *iterator; + host_t *host; + + /* N(NAT_DETECTION_SOURCE_IP)+ */ + iterator = charon->interfaces->create_address_iterator(charon->interfaces); + while (iterator->iterate(iterator, (void**)&host)) + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + request->add_payload(request, (payload_t*)notify); + } + iterator->destroy(iterator); + + /* N(NAT_DETECTION_DESTINATION_IP) */ + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); + request->add_payload(request, (payload_t*)notify); + } + + /* set new state */ + this->ike_sa->set_state(this->ike_sa, SA_CONNECTING); + return SUCCESS; +} + +/** + * Handle all kind of notifys + */ +static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *notify_payload) +{ + chunk_t notification_data; + 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 NO_PROPOSAL_CHOSEN: + { + this->logger->log(this->logger, AUDIT, + "received a NO_PROPOSAL_CHOSEN notify, deleting IKE_SA"); + return DESTROY_ME; + } + case INVALID_MAJOR_VERSION: + { + this->logger->log(this->logger, AUDIT, + "received a INVALID_MAJOR_VERSION notify, deleting IKE_SA"); + return DESTROY_ME; + } + case INVALID_KE_PAYLOAD: + { + chunk_t notify_data; + diffie_hellman_group_t dh_group, old_dh_group; + ike_sa_init_t *retry; + + old_dh_group = this->connection->get_dh_group(this->connection); + notify_data = notify_payload->get_notification_data(notify_payload); + dh_group = ntohs(*((u_int16_t*)notify_data.ptr)); + + this->logger->log(this->logger, AUDIT, + "peer didn't accept DH group %s, it requested %s", + mapping_find(diffie_hellman_group_m, old_dh_group), + mapping_find(diffie_hellman_group_m, dh_group)); + if (!this->connection->check_dh_group(this->connection, dh_group)) + { + this->logger->log(this->logger, AUDIT, + "requested DH group not acceptable, aborting"); + return DESTROY_ME; + } + retry = ike_sa_init_create(this->ike_sa, 0); + retry->use_dh_group(retry, dh_group); + *this->next = (transaction_t*)retry; + return FAILED; + } + case NAT_DETECTION_DESTINATION_IP: + { + this->natd_dst_seen = TRUE; + if (this->natd_dst_matched) + { + return SUCCESS; + } + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_dst_hash)) + { + this->natd_dst_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D dst hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D dst hash mismatch"); + } + return SUCCESS; + } + case NAT_DETECTION_SOURCE_IP: + { + this->natd_src_seen = TRUE;; + if (this->natd_src_matched) + { + return SUCCESS; + } + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_src_hash)) + { + this->natd_src_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D src hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D src hash mismatch"); + } + 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 DESTROY_ME; + } + else + { + this->logger->log(this->logger, CONTROL, + "received %s notify (%d), ignored", + mapping_find(notify_type_m, notify_type), + notify_type); + return SUCCESS; + } + } + } +} + +/** + * Implementation of transaction_t.get_response. + */ +static status_t get_response(private_ike_sa_init_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; + ke_payload_t *ke_request = NULL; + nonce_payload_t *nonce_request = NULL; + 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) + { + *result = this->message; + return SUCCESS; + } + + me = request->get_destination(request); + other = request->get_source(request); + + /* 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, IKE_SA_INIT); + 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; + + /* 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); + if (this->connection == NULL) + { + notify_payload_t *notify = notify_payload_create(); + notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); + response->add_payload(response, (payload_t*)notify); + + this->logger->log(this->logger, AUDIT, + "no connection for hosts %s...%s found, deleting IKE_SA", + me->get_address(me), other->get_address(other)); + return DESTROY_ME; + } + this->ike_sa->set_connection(this->ike_sa, this->connection); + + /* Precompute NAT-D hashes for incoming NAT notify comparison */ + ike_sa_id = request->get_ike_sa_id(request); + this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me); + this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other); + + /* 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 KEY_EXCHANGE: + ke_request = (ke_payload_t*)payload; + break; + case NONCE: + nonce_request = (nonce_payload_t*)payload; + break; + case NOTIFY: + { + status = process_notifys(this, (notify_payload_t*)payload); + if (status == FAILED) + { + payloads->destroy(payloads); + /* we return SUCCESS, returned FAILED means do next transaction */ + return SUCCESS; + } + if (status == DESTROY_ME) + { + payloads->destroy(payloads); + return DESTROY_ME; + } + break; + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, + "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 && ke_request && nonce_request)) + { + notify_payload_t *notify = notify_payload_create(); + notify->set_notify_type(notify, INVALID_SYNTAX); + response->add_payload(response, (payload_t*)notify); + this->logger->log(this->logger, AUDIT, + "request message incomplete, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* process SA payload: + * ------------------- + * - extract proposals + * - select our most preferred proposal found in extracted + * - if no matches, return NO_PROPOSAL_CHOSEN + * - add sa payload with selected proposal + */ + sa_payload_t* sa_response; + linked_list_t *proposal_list; + proposal_t *proposal; + + proposal_list = sa_request->get_proposals(sa_request); + this->proposal = this->connection->select_proposal(this->connection, proposal_list); + while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + if (this->proposal == NULL) + { + notify_payload_t *notify = notify_payload_create(); + notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); + response->add_payload(response, (payload_t*)notify); + this->logger->log(this->logger, AUDIT, + "request did not contain any acceptable proposals, deleting IKE_SA"); + return DESTROY_ME; + } + sa_response = sa_payload_create_from_proposal(this->proposal); + response->add_payload(response, (payload_t *)sa_response); + } + + { /* process KE payload: + * -------------------- + * - check if used group match the selected proposal + * - if not, stop with INVALID_KE_PAYLOAD + * - apply others public value to complete diffie hellman exchange + * - add our public value to response + */ + diffie_hellman_group_t used_group; + ke_payload_t *ke_response; + + used_group = ke_request->get_dh_group_number(ke_request); + + if (!this->connection->check_dh_group(this->connection, used_group) || + (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL) + { + u_int16_t notify_group; + chunk_t notify_chunk; + notify_payload_t *notify; + iterator_t *iterator; + payload_t *payload; + + notify_group = this->connection->get_dh_group(this->connection); + this->logger->log(this->logger, AUDIT, + "request used inacceptable DH group %s, sending INVALID_KE_PAYLOAD with %s, deleting IKE_SA", + mapping_find(diffie_hellman_group_m, used_group), + mapping_find(diffie_hellman_group_m, notify_group)); + + /* remove already added payloads */ + iterator = response->get_payload_iterator(response); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&payload); + iterator->remove(iterator); + payload->destroy(payload); + } + iterator->destroy(iterator); + + notify_group = htons(notify_group); + notify_chunk.ptr = (u_int8_t*)¬ify_group; + notify_chunk.len = sizeof(notify_group); + notify = notify_payload_create(); + notify->set_notify_type(notify, INVALID_KE_PAYLOAD); + notify->set_notification_data(notify, notify_chunk); + response->add_payload(response, (payload_t*)notify); + return DESTROY_ME; + } + this->diffie_hellman->set_other_public_value(this->diffie_hellman, + ke_request->get_key_exchange_data(ke_request)); + + /* build response */ + ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + response->add_payload(response, (payload_t*)ke_response); + } + + { /* process nonce payload: + * ---------------------- + * - get nonce from payload + * - generate own nonce and add to reply + */ + nonce_payload_t *nonce_response; + + this->nonce_i = nonce_request->get_nonce(nonce_request); + + /* build response nonce */ + if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer, + NONCE_SIZE, &this->nonce_r) != SUCCESS) + { + notify_payload_t *notify = notify_payload_create(); + notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); + response->add_payload(response, (payload_t*)notify); + this->logger->log(this->logger, AUDIT, + "could not get random bytes for nonce, deleting IKE_SA"); + return DESTROY_ME; + } + nonce_response = nonce_payload_create(); + nonce_response->set_nonce(nonce_response, this->nonce_r); + response->add_payload(response, (payload_t *)nonce_response); + } + + { /* processs NATT stuff: + * -------------------- + * - check if we or other is behind NAT + * - enable NATT if so + * - build NAT detection notifys for reply + */ + notify_payload_t *notify; + + if ((!this->natd_src_seen && this->natd_dst_seen) || + (this->natd_src_seen && !this->natd_dst_seen)) + { + notify = notify_payload_create(); + notify->set_notify_type(notify, INVALID_SYNTAX); + response->add_payload(response, (payload_t*)notify); + this->logger->log(this->logger, AUDIT, + "request contained wrong number of NAT-D payloads, deleting IKE_SA"); + return DESTROY_ME; + } + if (this->natd_dst_seen && !this->natd_dst_matched) + { + this->ike_sa->enable_natt(this->ike_sa, TRUE); + } + if (this->natd_src_seen && !this->natd_src_matched) + { + this->ike_sa->enable_natt(this->ike_sa, FALSE); + } + /* build response NAT DETECTION notifys, if remote supports it */ + if (this->natd_src_seen || this->natd_dst_seen) + { + /* N(NAT_DETECTION_SOURCE_IP) */ + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); + response->add_payload(response, (payload_t*)notify); + + /* N(NAT_DETECTION_DESTINATION_IP) */ + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); + response->add_payload(response, (payload_t*)notify); + } + } + + /* derive all the keys used in the IKE_SA */ + if (this->ike_sa->build_transforms(this->ike_sa, this->proposal, + this->diffie_hellman, + this->nonce_i, this->nonce_r, + FALSE) != SUCCESS) + { + notify_payload_t *notify = notify_payload_create(); + notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN); + response->add_payload(response, (payload_t*)notify); + this->logger->log(this->logger, AUDIT, + "transform objects could not be created from selected proposal, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* create ike_auth transaction, which will store informations for us */ + packet_t *response_packet; + chunk_t request_chunk, response_chunk; + ike_auth_t *ike_auth; + + /* we normally do not generate the message. But we need the generated message + * for authentication in the next state, so we do it here. This is not problematic, + * as we don't use a crypter/signer in ike_sa_init... */ + if (response->generate(response, NULL, NULL, &response_packet) != SUCCESS) + { + this->logger->log(this->logger, AUDIT, + "error in response generation, deleting IKE_SA"); + return DESTROY_ME; + } + response_packet->destroy(response_packet); + request_chunk = request->get_packet_data(request); + 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->set_nonces(ike_auth, + chunk_clone(this->nonce_i), + chunk_clone(this->nonce_r)); + ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk); + *next = (transaction_t*)ike_auth; + } + + /* everything went fine. Now we set a timeout to destroy half initiated IKE_SAs */ + timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration); + if (timeout) + { + job_t *job = (job_t*)delete_half_open_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa)); + charon->event_queue->add_relative(charon->event_queue, job, timeout); + } + /* set new state */ + this->ike_sa->set_state(this->ike_sa, SA_CONNECTING); + return SUCCESS; +} + + +/** + * Implementation of transaction_t.conclude + */ +static status_t conclude(private_ike_sa_init_t *this, message_t *response, + transaction_t **next) +{ + u_int64_t responder_spi; + ike_sa_id_t *ike_sa_id; + iterator_t *payloads; + host_t *me, *other; + sa_payload_t *sa_payload = NULL; + ke_payload_t *ke_payload = NULL; + nonce_payload_t *nonce_payload = NULL; + policy_t *policy; + status_t status; + + /* check message type */ + if (response->get_exchange_type(response) != IKE_SA_INIT) + { + this->logger->log(this->logger, ERROR, + "IKE_SA_INIT response of invalid type, deleting IKE_SA"); + return DESTROY_ME; + } + + /* allow setting of next transaction in other functions */ + this->next = next; + + 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); + + /* check if SPI has been updated, but apply only if all goes ok later */ + responder_spi = response->get_responder_spi(response); + if (responder_spi == 0) + { + this->logger->log(this->logger, ERROR, + "response contained a SPI of zero, deleting IKE_SA"); + return DESTROY_ME; + } + + /* Precompute NAT-D hashes for later comparison */ + ike_sa_id = response->get_ike_sa_id(response); + this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other); + this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me); + + /* 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 KEY_EXCHANGE: + { + ke_payload = (ke_payload_t*)payload; + break; + } + case NONCE: + { + nonce_payload = (nonce_payload_t*)payload; + break; + } + case NOTIFY: + { + status = process_notifys(this, (notify_payload_t*)payload); + if (status == FAILED) + { + payloads->destroy(payloads); + /* we return SUCCESS, returned FAILED means do next transaction */ + return SUCCESS; + } + if (status == DESTROY_ME) + { + payloads->destroy(payloads); + return status; + } + break; + } + default: + { + this->logger->log(this->logger, ERROR, "ignoring payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), + payload->get_type(payload)); + break; + } + } + } + payloads->destroy(payloads); + + if (!(nonce_payload && sa_payload && ke_payload)) + { + this->logger->log(this->logger, AUDIT, "response message incomplete, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* process SA payload: + * ------------------- + * - get proposals from it + * - check if peer selected a proposal + * - verify it's selection againts our set + */ + proposal_t *proposal; + linked_list_t *proposal_list; + + /* get the list of selected proposals, the peer has to select only one proposal */ + proposal_list = sa_payload->get_proposals (sa_payload); + if (proposal_list->get_count(proposal_list) != 1) + { + this->logger->log(this->logger, AUDIT, + "response did not contain a single proposal, deleting IKE_SA"); + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + return DESTROY_ME; + } + + /* we have to re-check if the others selection is valid */ + this->proposal = this->connection->select_proposal(this->connection, proposal_list); + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + + if (this->proposal == NULL) + { + this->logger->log(this->logger, AUDIT, + "peer selected a proposal we did not offer, deleting IKE_SA"); + return DESTROY_ME; + } + } + + { /* process KE payload: + * ------------------- + * - extract others public value + * - complete diffie-hellman exchange + */ + this->diffie_hellman->set_other_public_value(this->diffie_hellman, + ke_payload->get_key_exchange_data(ke_payload)); + } + + { /* process NONCE payload: + * ---------------------- + * - extract nonce used for key derivation */ + this->nonce_r = nonce_payload->get_nonce(nonce_payload); + } + + { /* process NATT stuff: + * ------------------- + * - check if we or other is NATted + * - switch to port 4500 if so + */ + if ((!this->natd_dst_seen && this->natd_src_seen) || + (this->natd_dst_seen && !this->natd_src_seen)) + { + this->logger->log(this->logger, AUDIT, + "request contained wrong number of NAT-D payloads, deleting IKE_SA"); + return DESTROY_ME; + } + if (this->natd_src_seen && !this->natd_src_matched) + { + this->ike_sa->enable_natt(this->ike_sa, FALSE); + } + if (this->natd_dst_seen && !this->natd_dst_matched) + { + this->ike_sa->enable_natt(this->ike_sa, TRUE); + } + if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + me->set_port(me, IKEV2_NATT_PORT); + other->set_port(other, IKEV2_NATT_PORT); + this->logger->log(this->logger, CONTROL|LEVEL1, "switching to port %d", IKEV2_NATT_PORT); + } + policy = this->ike_sa->get_policy(this->ike_sa); + policy->update_my_ts(policy, me); + policy->update_other_ts(policy, other); + } + + /* because we are original initiator we have to update the responder SPI to the new one */ + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* derive all the keys used in the IKE_SA */ + if (this->ike_sa->build_transforms(this->ike_sa, this->proposal, + this->diffie_hellman, + this->nonce_i, this->nonce_r, + TRUE) != SUCCESS) + { + this->logger->log(this->logger, AUDIT, + "transform objects could not be created from selected proposal, deleting IKE_SA"); + return DESTROY_ME; + } + + { /* create ike_auth transaction, which will continue IKE_SA setup */ + chunk_t request_chunk, response_chunk; + ike_auth_t *ike_auth; + + request_chunk = this->message->get_packet_data(this->message); + 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->set_nonces(ike_auth, + chunk_clone(this->nonce_i), + chunk_clone(this->nonce_r)); + ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk); + *next = (transaction_t*)ike_auth; + } + + return SUCCESS; +} + +static void destroy(private_ike_sa_init_t *this) +{ + if (this->message) + { + this->message->destroy(this->message); + } + if (this->diffie_hellman) + { + this->diffie_hellman->destroy(this->diffie_hellman); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + chunk_free(&this->nonce_i); + chunk_free(&this->nonce_r); + this->randomizer->destroy(this->randomizer); + this->nat_hasher->destroy(this->nat_hasher); + chunk_free(&this->natd_src_hash); + chunk_free(&this->natd_dst_hash); + free(this); +} + +/* + * Described in header. + */ +ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id) +{ + private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_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.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group; + + /* private data */ + this->ike_sa = ike_sa; + this->message_id = message_id; + this->message = NULL; + this->requested = 0; + this->diffie_hellman = NULL; + this->nonce_i = CHUNK_INITIALIZER; + this->nonce_r = CHUNK_INITIALIZER; + this->connection = NULL; + this->proposal = NULL; + this->randomizer = randomizer_create(); + this->nat_hasher = hasher_create(HASH_SHA1); + this->natd_src_hash = CHUNK_INITIALIZER; + this->natd_dst_hash = CHUNK_INITIALIZER; + this->natd_src_seen = FALSE; + this->natd_dst_seen = FALSE; + this->natd_src_matched = FALSE; + this->natd_dst_matched = FALSE; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &this->public; +} diff --git a/src/charon/sa/transactions/ike_sa_init.h b/src/charon/sa/transactions/ike_sa_init.h new file mode 100644 index 000000000..a7701d625 --- /dev/null +++ b/src/charon/sa/transactions/ike_sa_init.h @@ -0,0 +1,73 @@ +/** + * @file ike_sa_init.h + * + * @brief Interface of transaction ike_sa_init. + * + */ + +/* + * 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 IKE_SA_INIT_H_ +#define IKE_SA_INIT_H_ + +#include <sa/ike_sa.h> +#include <sa/transactions/transaction.h> + + +typedef struct ike_sa_init_t ike_sa_init_t; + +/** + * @brief A transaction for the first message exchange to set up an IKE_SA. + * + * @b Constructors: + * - ike_sa_init_create() + * - transaction_create() with the appropriate message + * + * @ingroup transactions + */ +struct ike_sa_init_t { + + /** + * The transaction_t interface. + */ + transaction_t transaction; + + /** + * @brief Set the Diffie Hellman group to use for initiating. + * + * If a first exchange fails with a INVALID_KE_PAYLOAD, the second + * try uses the DH group proposed by the responder. + * + * @param this calling object + * @param dh_group diffie hellman group to use + * @return FALSE, if DH group not allowed/supported + */ + bool (*use_dh_group) (ike_sa_init_t* this, diffie_hellman_group_t dh_group); +}; + +/** + * @brief Create a new transaction which processes IKE_SA_INIT exchanges. + * + * @param ike_sa assigned IKE_SA + * @param message_id message ids used in this transaction + * @return created ike_sa_init transaction + * + * @ingroup transactions + */ +ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id); + +#endif /* IKE_SA_INIT_H_ */ diff --git a/src/charon/sa/transactions/transaction.c b/src/charon/sa/transactions/transaction.c new file mode 100644 index 000000000..ecbfd0abc --- /dev/null +++ b/src/charon/sa/transactions/transaction.c @@ -0,0 +1,147 @@ +/** + * @file transaction.c + * + * @brief Generic contstructor for the different transaction types. + * + */ + +/* + * 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 "transaction.h" + +#include <sa/child_sa.h> +#include <sa/transactions/ike_sa_init.h> +#include <sa/transactions/ike_auth.h> +#include <sa/transactions/delete_ike_sa.h> +#include <sa/transactions/dead_peer_detection.h> +#include <encoding/payloads/ts_payload.h> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <utils/logger_manager.h> + + +/* + * see header file + */ +transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request) +{ + iterator_t *iterator; + payload_t *current; + notify_payload_t *notify; + transaction_t *transaction = NULL; + u_int32_t message_id; + + if (!request->get_request(request)) + { + return NULL; + } + message_id = request->get_message_id(request); + + switch (request->get_exchange_type(request)) + { + case IKE_SA_INIT: + { + transaction = (transaction_t*)ike_sa_init_create(ike_sa, message_id); + break; + } + case IKE_AUTH: + { + /* IKE_AUTH is always created in IKE_SA_INIT, it never should + * appear alone */ + break; + } + case CREATE_CHILD_SA: + { + /* look for a REKEY_SA notify */ + iterator = request->get_payload_iterator(request); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t); + if (current->get_type(current) != NOTIFY) + { + continue; + } + notify = (notify_payload_t*)current; + if (notify->get_notify_type(notify) != REKEY_SA) + { + continue; + } + switch (notify->get_protocol_id(notify)) + { + case PROTO_IKE: + /* TODO: transaction = rekey_ike_sa_create(ike_sa, message_id); */ + break; + case PROTO_AH: + case PROTO_ESP: + { + /* TODO: transaction = rekey_child_sa_create(ike_sa, message_id); */ + break; + } + default: + break; + } + if (transaction) + { + break; + } + } + iterator->destroy(iterator); + break; + } + case INFORMATIONAL: + { + u_int payload_count = 0; + iterator = request->get_payload_iterator(request); + while (iterator->has_next(iterator)) + { + payload_count++; + iterator->current(iterator, (void**)¤t); + switch (current->get_type(current)) + { + case DELETE: + { + delete_payload_t *delete_payload; + delete_payload = (delete_payload_t*)current; + if (delete_payload->get_protocol_id(delete_payload) == PROTO_IKE) + { + transaction = (transaction_t*) + delete_ike_sa_create(ike_sa, message_id); + break; + } + } + default: + break; + } + if (transaction) + { + break; + } + } + iterator->destroy(iterator); + if (payload_count == 0) + { + transaction = (transaction_t*) + dead_peer_detection_create(ike_sa, message_id); + } + break; + } + default: + break; + } + return transaction; +} diff --git a/src/charon/sa/transactions/transaction.h b/src/charon/sa/transactions/transaction.h new file mode 100644 index 000000000..ea84b0394 --- /dev/null +++ b/src/charon/sa/transactions/transaction.h @@ -0,0 +1,181 @@ +/** + * @file transaction.h + * + * @brief Interface transaction_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 <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 TRANSACTION_H_ +#define TRANSACTION_H_ + +#include <types.h> +#include <encoding/message.h> +#include <sa/ike_sa.h> + + +typedef struct transaction_t transaction_t; + +/** + * @brief This interface represents a transaction an established IKE_SA can do. + * + * To every transaction, a message ID is associated. IKEv2 uses strict message + * IDs, which are equal for a request/response pair in a transaction. + * An initiator of a transaction does the following: + * - create the transaction using a specific constructor + * - call request() to get the message for initiaton + * - call conclude() to process received reply + * The other peer does the following: + * - create a transanction using the generic transaction constructor + * - call respond() to get a reply to send + * + * The responder must not destroy the transaction, until the + * initiator initiates another transaction (or a number of transactions + * > window size). This allows us to redo a transaction in case of a + * message loss. The initiator can destroy the the transaction once + * the conclude() function is called. + * + * @b Constructors: + * - transaction_create() + * - ike_sa_init_create() + * - ike_auth_create() + * + * @ingroup transactions + */ +struct transaction_t { + + /** + * @brief Get the request to use for initiating the transaction. + * + * A transaction creates a request only once. The request is stored + * internally and may be queried multiple times for retransmission. + * The transaction is not responsible for generating/encrypting the + * message, this is the job of the caller. But it MAY be already + * generated when calling get_request() the second time. + * + * @param this calling object + * @param[out] request resultin request + * @return + * - FAILED if transaction failed + * - DESTROY_ME if transaction failed and IKE SA + * must be deleted + * - SUCCESS + */ + status_t (*get_request) (transaction_t *this, message_t **request); + + /** + * @brief Build the response for a received request. + * + * A transaction creates a response only once for a unique request. + * This allows the use of get_response multiple times for retransmission + * purposes. + * The transaction is not responsible for generating/encrypting the + * response, nor is it responsible for decrypting/parsing the request. + * This is the job of the caller. But the response MAY be already + * generated when calling get_request() the second time. + * The initiator waits for a response, so we send one in every case. This + * means response points always to a valid message. This message + * may not be modified or destroyed, it gets destroyed along with the + * transaction. + * The get_response() function may return a next transaction. This allows + * passing of informations from one transaction to a next one. + * + * @param this calling object + * @param request received request + * @param[out] response resulting response + * @param[out] next transaction expected as next, or NULL + * @return + * - FAILED if transaction failed + * - DESTROY_ME if transaction failed and IKE SA + * must be deleted + * - SUCCESS + */ + status_t (*get_response) (transaction_t *this, message_t *request, + message_t **response, transaction_t **next); + + /** + * @brief Conclude an initiated transaction with a received response. + * + * The response must be decrypted and parsed. The conclude function + * may return a new transaction. This transaction has to be executed + * next to complete a multi-exchange scenario. It allows a clean + * transaction mechanism, as the transaction knows best whats to do + * after it completes. It must only be executed if conclude returns + * SUCCESS. + * + * @param this calling object + * @param response received response + * @param[out] next transaction to execute as next, or NULL + * @return + * - FAILED if transaction failed + * - DESTROY_ME if transaction failed and IKE SA + * must be deleted + * - SUCCESS + */ + status_t (*conclude) (transaction_t *this, message_t *response, + transaction_t **next); + + /** + * @brief Get the message ID associated with this transaction. + * + * Every transaction consists of a message pair with the same + * message ID. This ID can be queried with get_message_id(). + * + * @param this calling object + * @return message id + */ + u_int32_t (*get_message_id) (transaction_t *this); + + /** + * @brief Times we already sent the request (retransmitted). + * + * The transaction stores an internal counter to see how + * many times we sent the request. This counter is incremented + * each time after a call to requested(). + * + * @param this calling object + * @return message id + */ + u_int32_t (*requested) (transaction_t *this); + + /** + * @brief Destroys a transaction_t object. + * + * @param this calling object + */ + void (*destroy) (transaction_t *this); +}; + +/** + * @brief Create a transaction instance based on a received request. + * + * Incoming requests are handled by a transaction. But as we don't + * know what kind of transaction we use for a specific request, we use + * a generic constructor. This constructor decides which instance will + * handle the transaction, and creates it. + * + * @param ike_sa ike_sa associated with this transaction + * @param request received request + * @return + * - created transaction, or + * - NULL no transaction needed + * + * @ingroup transactions + */ +transaction_t *transaction_create(ike_sa_t *ike_sa, message_t* request); + +#endif /* TRANSACTION_H_ */ diff --git a/src/charon/testing/generator_test.c b/src/charon/testing/generator_test.c index b557b5880..d68cdb593 100644 --- a/src/charon/testing/generator_test.c +++ b/src/charon/testing/generator_test.c @@ -709,7 +709,7 @@ void test_generator_with_notify_payload(protected_tester_t *tester) notification_data.len = strlen(notification_data.ptr); notify_payload->set_protocol_id(notify_payload,255); - notify_payload->set_notify_message_type(notify_payload,63333); /* Hex F765 */ + notify_payload->set_notify_type(notify_payload,63333); /* Hex F765 */ notify_payload->set_spi(notify_payload, 0x31323334); notify_payload->set_notification_data(notify_payload,notification_data); diff --git a/src/charon/testing/parser_test.c b/src/charon/testing/parser_test.c index 66e65479b..ed9f7272f 100644 --- a/src/charon/testing/parser_test.c +++ b/src/charon/testing/parser_test.c @@ -558,7 +558,7 @@ void test_parser_with_notify_payload(protected_tester_t *tester) return; } tester->assert_true(tester,(notify_payload->get_protocol_id(notify_payload) == 3), "Protocol id"); - tester->assert_true(tester,(notify_payload->get_notify_message_type(notify_payload) == 1), "notify message type"); + tester->assert_true(tester,(notify_payload->get_notify_type(notify_payload) == 1), "notify message type"); spi = notify_payload->get_spi(notify_payload); tester->assert_false(tester, spi == 0x01020303, "parsed spi"); diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c index f6736a86f..4c0f80836 100755 --- a/src/charon/threads/stroke_interface.c +++ b/src/charon/threads/stroke_interface.c @@ -42,7 +42,6 @@ #define IKE_PORT 500 #define PATH_BUF 256 -static bool strict = FALSE; struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; @@ -134,7 +133,6 @@ static x509_t* load_end_certificate(const char *filename, identification_t **idp if (cert) { - bool found; identification_t *id = *idp; identification_t *subject = cert->get_subject(cert); |