diff options
Diffstat (limited to 'src/charon/sa')
26 files changed, 8354 insertions, 0 deletions
diff --git a/src/charon/sa/Makefile.sa b/src/charon/sa/Makefile.sa new file mode 100644 index 000000000..825c19959 --- /dev/null +++ b/src/charon/sa/Makefile.sa @@ -0,0 +1,37 @@ +# 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. +# + +SA_DIR= $(CHARON_DIR)sa/ + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_id.o +$(BUILD_DIR)ike_sa_id.o : $(SA_DIR)ike_sa_id.c $(SA_DIR)ike_sa_id.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_manager.o +$(BUILD_DIR)ike_sa_manager.o : $(SA_DIR)ike_sa_manager.c $(SA_DIR)ike_sa_manager.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa.o +$(BUILD_DIR)ike_sa.o : $(SA_DIR)ike_sa.c $(SA_DIR)ike_sa.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)authenticator.o +$(BUILD_DIR)authenticator.o : $(SA_DIR)authenticator.c $(SA_DIR)authenticator.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)child_sa.o +$(BUILD_DIR)child_sa.o : $(SA_DIR)child_sa.c $(SA_DIR)child_sa.h + $(CC) $(CFLAGS) -c -o $@ $< + +include $(SA_DIR)states/Makefile.states
\ No newline at end of file diff --git a/src/charon/sa/authenticator.c b/src/charon/sa/authenticator.c new file mode 100644 index 000000000..3aeb8795f --- /dev/null +++ b/src/charon/sa/authenticator.c @@ -0,0 +1,405 @@ +/** + * @file authenticator.c + * + * @brief Implementation of authenticator_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 <string.h> + +#include "authenticator.h" + +#include <daemon.h> + +/** + * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +#define IKEV2_KEY_PAD "Key Pad for IKEv2" + + +typedef struct private_authenticator_t private_authenticator_t; + +/** + * Private data of an authenticator_t object. + */ +struct private_authenticator_t { + + /** + * Public authenticator_t interface. + */ + authenticator_t public; + + /** + * Assigned IKE_SA. Needed to get objects of type prf_t and logger_t. + */ + protected_ike_sa_t *ike_sa; + + /** + * PRF taken from the IKE_SA. + */ + prf_t *prf; + + /** + * A logger for. + * + * Using logger of IKE_SA. + */ + logger_t *logger; + + /** + * @brief Creates the octets which are signed (RSA) or MACed (shared secret) as described in section + * 2.15 of RFC. + * + * @param this calling object + * @param last_message the last message to include in created octets + * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response) + * @param other_nonce Nonce data received from other peer + * @param my_id id_payload_t object representing an ID payload + * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise + * @return octets as described in section 2.15. Memory gets allocated and has to get + * destroyed by caller. + */ + chunk_t (*allocate_octets) (private_authenticator_t *this, + chunk_t last_message, + chunk_t other_nonce, + id_payload_t *my_id, + bool initiator); + + /** + * @brief Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + * + * @param this calling object + * @param last_message the last message + * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response) + * @param nonce Nonce data to include in auth data compution + * @param id_payload id_payload_t object representing an ID payload + * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise + * @param shared_secret shared secret as chunk_t. If shared secret is a string, + * the NULL termination is not included. + * @return AUTH data as dscribed in section 2.15 for + * AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + * Memory gets allocated and has to get destroyed by caller. + */ + chunk_t (*build_preshared_secret_signature) (private_authenticator_t *this, + chunk_t last_message, + chunk_t nonce, + id_payload_t *id_payload, + bool initiator, + chunk_t preshared_secret); +}; + +/** + * Implementation of private_authenticator_t.allocate_octets. + */ +static chunk_t allocate_octets(private_authenticator_t *this, + chunk_t last_message, + chunk_t other_nonce, + id_payload_t *my_id, + bool initiator) +{ + prf_t *prf; + chunk_t id_chunk = my_id->get_data(my_id); + u_int8_t id_with_header[4 + id_chunk.len]; + /* + * IKEv2 for linux (http://sf.net/projects/ikev2/) + * is not compatible with IKEv2 Draft and so not compatible with this + * implementation, cause AUTH data are computed without + * ID type and the three reserved bytes. + */ + chunk_t id_with_header_chunk = {ptr:id_with_header, len: sizeof(id_with_header)}; + u_int8_t *current_pos; + chunk_t octets; + + id_with_header[0] = my_id->get_id_type(my_id); + id_with_header[1] = 0x00; + id_with_header[2] = 0x00; + id_with_header[3] = 0x00; + memcpy(id_with_header + 4,id_chunk.ptr,id_chunk.len); + + if (initiator) + { + prf = this->ike_sa->get_prf_auth_i(this->ike_sa); + } + else + { + prf = this->ike_sa->get_prf_auth_r(this->ike_sa); + } + + /* 4 bytes are id type and reserved fields of id payload */ + octets.len = last_message.len + other_nonce.len + prf->get_block_size(prf); + octets.ptr = malloc(octets.len); + current_pos = octets.ptr; + memcpy(current_pos,last_message.ptr,last_message.len); + current_pos += last_message.len; + memcpy(current_pos,other_nonce.ptr,other_nonce.len); + current_pos += other_nonce.len; + prf->get_bytes(prf, id_with_header_chunk, current_pos); + + this->logger->log_chunk(this->logger,RAW | LEVEL2, "Octets (Mesage + Nonce + prf(Sk_px,Idx)",octets); + return octets; +} + +/** + * Implementation of private_authenticator_t.build_preshared_secret_signature. + */ +static chunk_t build_preshared_secret_signature(private_authenticator_t *this, + chunk_t last_message, + chunk_t nonce, + id_payload_t *id_payload, + bool initiator, + chunk_t preshared_secret) +{ + chunk_t key_pad = {ptr: IKEV2_KEY_PAD, len:strlen(IKEV2_KEY_PAD)}; + u_int8_t key_buffer[this->prf->get_block_size(this->prf)]; + chunk_t key = {ptr: key_buffer, len: sizeof(key_buffer)}; + chunk_t auth_data; + + chunk_t octets = this->allocate_octets(this,last_message,nonce,id_payload,initiator); + + /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */ + this->prf->set_key(this->prf, preshared_secret); + this->prf->get_bytes(this->prf, key_pad, key_buffer); + this->prf->set_key(this->prf, key); + this->prf->allocate_bytes(this->prf, octets, &auth_data); + chunk_free(&octets); + this->logger->log_chunk(this->logger,RAW | LEVEL2, "Authenticated data",auth_data); + + return auth_data; +} + +/** + * Implementation of authenticator_t.verify_auth_data. + */ +static status_t verify_auth_data (private_authenticator_t *this, + auth_payload_t *auth_payload, + chunk_t last_received_packet, + chunk_t my_nonce, + id_payload_t *other_id_payload, + bool initiator) +{ + switch(auth_payload->get_auth_method(auth_payload)) + { + case SHARED_KEY_MESSAGE_INTEGRITY_CODE: + { + identification_t *other_id = other_id_payload->get_identification(other_id_payload); + chunk_t auth_data = auth_payload->get_data(auth_payload); + chunk_t preshared_secret; + status_t status; + + status = charon->credentials->get_shared_secret(charon->credentials, + other_id, + &preshared_secret); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s", + other_id->get_string(other_id)); + other_id->destroy(other_id); + return status; + } + + chunk_t my_auth_data = this->build_preshared_secret_signature(this, + last_received_packet, + my_nonce, + other_id_payload, + initiator, + preshared_secret); + chunk_free(&preshared_secret); + + if (auth_data.len != my_auth_data.len) + { + chunk_free(&my_auth_data); + status = FAILED; + } + else if (memcmp(auth_data.ptr,my_auth_data.ptr, my_auth_data.len) == 0) + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret successful", + other_id->get_string(other_id)); + status = SUCCESS; + } + else + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret failed", + other_id->get_string(other_id)); + status = FAILED; + } + other_id->destroy(other_id); + chunk_free(&my_auth_data); + return status; + } + case RSA_DIGITAL_SIGNATURE: + { + identification_t *other_id = other_id_payload->get_identification(other_id_payload); + rsa_public_key_t *public_key; + status_t status; + chunk_t octets, auth_data; + + auth_data = auth_payload->get_data(auth_payload); + + public_key = charon->credentials->get_rsa_public_key(charon->credentials, + other_id); + if (public_key == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No RSA public key found for %s", + other_id->get_string(other_id)); + other_id->destroy(other_id); + return NOT_FOUND; + } + + octets = this->allocate_octets(this,last_received_packet, my_nonce,other_id_payload, initiator); + + status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data); + if (status == SUCCESS) + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA successful", + other_id->get_string(other_id)); + } + else + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA failed", + other_id->get_string(other_id)); + } + + public_key->destroy(public_key); + other_id->destroy(other_id); + chunk_free(&octets); + return status; + } + default: + { + return NOT_SUPPORTED; + } + } +} + +/** + * Implementation of authenticator_t.compute_auth_data. + */ +static status_t compute_auth_data (private_authenticator_t *this, + auth_payload_t **auth_payload, + chunk_t last_sent_packet, + chunk_t other_nonce, + id_payload_t *my_id_payload, + bool initiator) +{ + connection_t *connection = this->ike_sa->get_connection(this->ike_sa); + + switch(connection->get_auth_method(connection)) + { + case SHARED_KEY_MESSAGE_INTEGRITY_CODE: + { + identification_t *my_id = my_id_payload->get_identification(my_id_payload); + chunk_t preshared_secret; + status_t status; + chunk_t auth_data; + + status = charon->credentials->get_shared_secret(charon->credentials, + my_id, + &preshared_secret); + + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s", + my_id->get_string(my_id)); + my_id->destroy(my_id); + return status; + } + my_id->destroy(my_id); + + auth_data = this->build_preshared_secret_signature(this, last_sent_packet, other_nonce, + my_id_payload, initiator, preshared_secret); + chunk_free(&preshared_secret); + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, SHARED_KEY_MESSAGE_INTEGRITY_CODE); + (*auth_payload)->set_data(*auth_payload, auth_data); + + chunk_free(&auth_data); + return SUCCESS; + } + case RSA_DIGITAL_SIGNATURE: + { + identification_t *my_id = my_id_payload->get_identification(my_id_payload); + rsa_private_key_t *private_key; + status_t status; + chunk_t octets, auth_data; + + private_key = charon->credentials->get_rsa_private_key(charon->credentials, my_id); + if (private_key == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No RSA private key found for %s", + my_id->get_string(my_id)); + my_id->destroy(my_id); + return NOT_FOUND; + } + my_id->destroy(my_id); + + octets = this->allocate_octets(this,last_sent_packet,other_nonce,my_id_payload,initiator); + + status = private_key->build_emsa_pkcs1_signature(private_key, HASH_SHA1, octets, &auth_data); + chunk_free(&octets); + if (status != SUCCESS) + { + private_key->destroy(private_key); + return status; + } + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, RSA_DIGITAL_SIGNATURE); + (*auth_payload)->set_data(*auth_payload, auth_data); + + private_key->destroy(private_key); + chunk_free(&auth_data); + return SUCCESS; + } + default: + { + return NOT_SUPPORTED; + } + } +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy (private_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa) +{ + private_authenticator_t *this = malloc_thing(private_authenticator_t); + + /* Public functions */ + this->public.destroy = (void(*)(authenticator_t*))destroy; + this->public.verify_auth_data = (status_t (*) (authenticator_t *,auth_payload_t *, chunk_t ,chunk_t ,id_payload_t *,bool)) verify_auth_data; + this->public.compute_auth_data = (status_t (*) (authenticator_t *,auth_payload_t **, chunk_t ,chunk_t ,id_payload_t *,bool)) compute_auth_data; + + /* private functions */ + this->allocate_octets = allocate_octets; + this->build_preshared_secret_signature = build_preshared_secret_signature; + + /* private data */ + this->ike_sa = ike_sa; + this->prf = this->ike_sa->get_prf(this->ike_sa); + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &(this->public); +} diff --git a/src/charon/sa/authenticator.h b/src/charon/sa/authenticator.h new file mode 100644 index 000000000..b6bc317ac --- /dev/null +++ b/src/charon/sa/authenticator.h @@ -0,0 +1,138 @@ +/** + * @file authenticator.h + * + * @brief Interface of authenticator_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 AUTHENTICATOR_H_ +#define AUTHENTICATOR_H_ + +#include <types.h> +#include <sa/ike_sa.h> +#include <network/packet.h> +#include <encoding/payloads/auth_payload.h> +#include <encoding/payloads/id_payload.h> + + +typedef struct authenticator_t authenticator_t; + +/** + * @brief Class used to authenticate a peer. + * + * Currently the following two AUTH methods are supported: + * - SHARED_KEY_MESSAGE_INTEGRITY_CODE + * - RSA_DIGITAL_SIGNATURE + * + * This class retrieves needed data for specific AUTH methods (RSA keys, shared secrets, etc.) + * over an internal stored protected_ike_sa_t object or directly from the configuration_t over + * the daemon_t object "charon". + * + * @b Constructors: + * - authenticator_create() + * + * @ingroup sa + */ +struct authenticator_t { + + /** + * @brief Verify's given authentication data. + * + * To verify a received AUTH payload the following data must be provided: + * - the last received IKEv2 Message from the other peer in binary form + * - the nonce value sent to the other peer + * - the ID payload of the other peer + * + * @param this calling object + * @param last_received_packet binary representation of the last received IKEv2-Message + * @param my_nonce the sent nonce (without payload header) + * @param other_id_payload the ID payload received from other peer + * @param initiator type of other peer. TRUE, if it is original initiator, FALSE otherwise + * + * @todo Document RSA error status types + * + * @return + * - SUCCESS if verification successful + * - FAILED if verification failed + * - NOT_SUPPORTED if AUTH method not supported + * - NOT_FOUND if the data for specific AUTH method could not be found + * (e.g. shared secret, rsa key) + */ + status_t (*verify_auth_data) (authenticator_t *this, + auth_payload_t *auth_payload, + chunk_t last_received_packet, + chunk_t my_nonce, + id_payload_t *other_id_payload, + bool initiator); + + /** + * @brief Computes authentication data and creates specific AUTH payload. + * + * To create an AUTH payload, the following data must be provided: + * - the last sent IKEv2 Message in binary form + * - the nonce value received from the other peer + * - the ID payload of myself + * + * @param this calling object + * @param[out] auth_payload The object of typee auth_payload_t will be created at pointing location + * @param last_sent_packet binary representation of the last sent IKEv2-Message + * @param other_nonce the received nonce (without payload header) + * @param my_id_payload the ID payload going to send to other peer + * @param initiator type of myself. TRUE, if I'm original initiator, FALSE otherwise + * + * @todo Document RSA error status types + * + * @return + * - SUCCESS if authentication data could be computed + * - NOT_SUPPORTED if AUTH method not supported + * - NOT_FOUND if the data for AUTH method could not be found + */ + status_t (*compute_auth_data) (authenticator_t *this, + auth_payload_t **auth_payload, + chunk_t last_sent_packet, + chunk_t other_nonce, + id_payload_t *my_id_payload, + bool initiator); + + /** + * @brief Destroys a authenticator_t object. + * + * @param this calling object + */ + void (*destroy) (authenticator_t *this); +}; + +/** + * @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 + * + * @return authenticator_t object + * + * @ingroup sa + */ +authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa); + +#endif /* AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c new file mode 100644 index 000000000..a678ea9b8 --- /dev/null +++ b/src/charon/sa/child_sa.c @@ -0,0 +1,590 @@ +/** + * @file child_sa.c + * + * @brief Implementation of child_sa_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 <netdb.h> + +#include "child_sa.h" + +#include <daemon.h> + + +typedef struct sa_policy_t sa_policy_t; + +/** + * Struct used to store information for a policy. This + * is needed since we must provide all this information + * for deleting a policy... + */ +struct sa_policy_t { + + /** + * Network on local side + */ + host_t *my_net; + + /** + * Network on remote side + */ + host_t *other_net; + + /** + * Number of bits for local network (subnet size) + */ + u_int8_t my_net_mask; + + /** + * Number of bits for remote network (subnet size) + */ + u_int8_t other_net_mask; + + /** + * Protocol for this policy, such as TCP/UDP/ICMP... + */ + int upper_proto; +}; + +typedef struct private_child_sa_t private_child_sa_t; + +/** + * Private data of a child_sa_t object. + */ +struct private_child_sa_t { + /** + * Public interface of child_sa_t. + */ + child_sa_t public; + + /** + * IP of this peer + */ + host_t *me; + + /** + * IP of other peer + */ + host_t *other; + + /** + * Local security parameter index for AH protocol, 0 if not used + */ + u_int32_t my_ah_spi; + + /** + * Local security parameter index for ESP protocol, 0 if not used + */ + u_int32_t my_esp_spi; + + /** + * Remote security parameter index for AH protocol, 0 if not used + */ + u_int32_t other_ah_spi; + + /** + * Remote security parameter index for ESP protocol, 0 if not used + */ + u_int32_t other_esp_spi; + + /** + * List containing policy_id_t objects + */ + linked_list_t *policies; + + /** + * reqid used for this child_sa + */ + u_int32_t reqid; + + /** + * CHILD_SAs own logger + */ + logger_t *logger; +}; + +/** + * Implements child_sa_t.alloc + */ +static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) +{ + protocol_id_t protocols[2]; + iterator_t *iterator; + proposal_t *proposal; + status_t status; + u_int i; + + /* iterator through proposals */ + iterator = proposals->create_iterator(proposals, TRUE); + while(iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&proposal); + proposal->get_protocols(proposal, protocols); + + /* check all protocols */ + for (i = 0; i<2; i++) + { + switch (protocols[i]) + { + case PROTO_AH: + /* do we already have an spi for AH?*/ + if (this->my_ah_spi == 0) + { + /* nope, get one */ + status = charon->kernel_interface->get_spi( + charon->kernel_interface, + this->me, this->other, + PROTO_AH, FALSE, + &(this->my_ah_spi)); + } + /* update proposal */ + proposal->set_spi(proposal, PROTO_AH, (u_int64_t)this->my_ah_spi); + break; + case PROTO_ESP: + /* do we already have an spi for ESP?*/ + if (this->my_esp_spi == 0) + { + /* nope, get one */ + status = charon->kernel_interface->get_spi( + charon->kernel_interface, + this->me, this->other, + PROTO_ESP, FALSE, + &(this->my_esp_spi)); + } + /* update proposal */ + proposal->set_spi(proposal, PROTO_ESP, (u_int64_t)this->my_esp_spi); + break; + default: + break; + } + if (status != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine) +{ + protocol_id_t protocols[2]; + u_int32_t spi; + encryption_algorithm_t enc_algo; + integrity_algorithm_t int_algo; + chunk_t enc_key, int_key; + algorithm_t *algo; + crypter_t *crypter; + signer_t *signer; + size_t key_size; + host_t *src; + host_t *dst; + status_t status; + u_int i; + + /* we must assign the roles to correctly set up the SAs */ + if (mine) + { + src = this->me; + dst = this->other; + } + else + { + dst = this->me; + src = this->other; + } + + proposal->get_protocols(proposal, protocols); + /* derive keys in order as protocols appear */ + for (i = 0; i<2; i++) + { + if (protocols[i] != PROTO_NONE) + { + + /* now we have to decide which spi to use. Use self allocated, if "mine", + * or the one in the proposal, if not "mine" (others). */ + if (mine) + { + if (protocols[i] == PROTO_AH) + { + spi = this->my_ah_spi; + } + else + { + spi = this->my_esp_spi; + } + } + else /* use proposals spi */ + { + spi = proposal->get_spi(proposal, protocols[i]); + if (protocols[i] == PROTO_AH) + { + this->other_ah_spi = spi; + } + else + { + this->other_esp_spi = spi; + } + } + + /* derive encryption key first */ + if (proposal->get_algorithm(proposal, protocols[i], ENCRYPTION_ALGORITHM, &algo)) + { + enc_algo = algo->algorithm; + this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s, ", + mapping_find(protocol_id_m, protocols[i]), + mine ? "me" : "other", + mapping_find(transform_type_m, ENCRYPTION_ALGORITHM), + mapping_find(encryption_algorithm_m, enc_algo)); + + /* we must create a (unused) crypter, since its the only way to get the size + * of the key. This is not so nice, since charon must support all algorithms + * the kernel supports... + * TODO: build something of a encryption algorithm lookup function + */ + crypter = crypter_create(enc_algo, algo->key_size); + key_size = crypter->get_key_size(crypter); + crypter->destroy(crypter); + prf_plus->allocate_bytes(prf_plus, key_size, &enc_key); + this->logger->log_chunk(this->logger, PRIVATE, "key:", enc_key); + } + else + { + enc_algo = ENCR_UNDEFINED; + } + + /* derive integrity key */ + if (proposal->get_algorithm(proposal, protocols[i], INTEGRITY_ALGORITHM, &algo)) + { + int_algo = algo->algorithm; + this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s,", + mapping_find(protocol_id_m, protocols[i]), + mine ? "me" : "other", + mapping_find(transform_type_m, INTEGRITY_ALGORITHM), + mapping_find(integrity_algorithm_m, algo->algorithm)); + + signer = signer_create(int_algo); + key_size = signer->get_key_size(signer); + signer->destroy(signer); + prf_plus->allocate_bytes(prf_plus, key_size, &int_key); + this->logger->log_chunk(this->logger, PRIVATE, "key:", int_key); + } + else + { + int_algo = AUTH_UNDEFINED; + } + /* send keys down to kernel */ + this->logger->log(this->logger, CONTROL|LEVEL1, + "installing 0x%.8x for %s, src %s dst %s", + ntohl(spi), mapping_find(protocol_id_m, protocols[i]), + src->get_address(src), dst->get_address(dst)); + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, + spi, protocols[i], + this->reqid, + enc_algo, enc_key, + int_algo, int_key, mine); + /* clean up for next round */ + if (enc_algo != ENCR_UNDEFINED) + { + chunk_free(&enc_key); + } + if (int_algo != AUTH_UNDEFINED) + { + chunk_free(&int_key); + } + + if (status != SUCCESS) + { + return FAILED; + } + + + } + } + return SUCCESS; +} + +static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) +{ + linked_list_t *list; + + /* install others (initiators) SAs*/ + if (install(this, proposal, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + + /* get SPIs for our SAs */ + list = linked_list_create(); + list->insert_last(list, proposal); + if (alloc(this, list) != SUCCESS) + { + list->destroy(list); + return FAILED; + } + list->destroy(list); + + /* install our (responders) SAs */ + if (install(this, proposal, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + return SUCCESS; +} + +static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) +{ + /* install our (initator) SAs */ + if (install(this, proposal, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + /* install his (responder) SAs */ + if (install(this, proposal, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + + return SUCCESS; +} + +static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list) +{ + iterator_t *my_iter, *other_iter; + traffic_selector_t *my_ts, *other_ts; + + /* iterate over both lists */ + my_iter = my_ts_list->create_iterator(my_ts_list, TRUE); + other_iter = other_ts_list->create_iterator(other_ts_list, TRUE); + while (my_iter->has_next(my_iter)) + { + my_iter->current(my_iter, (void**)&my_ts); + other_iter->reset(other_iter); + while (other_iter->has_next(other_iter)) + { + /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ + int family; + chunk_t from_addr; + u_int16_t from_port, to_port; + sa_policy_t *policy; + status_t status; + + other_iter->current(other_iter, (void**)&other_ts); + + /* only set up policies if protocol matches */ + if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts)) + { + continue; + } + policy = malloc_thing(sa_policy_t); + policy->upper_proto = my_ts->get_protocol(my_ts); + + /* calculate net and ports for local side */ + family = my_ts->get_type(my_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + from_addr = my_ts->get_from_address(my_ts); + from_port = my_ts->get_from_port(my_ts); + to_port = my_ts->get_to_port(my_ts); + from_port = (from_port != to_port) ? 0 : from_port; + policy->my_net = host_create_from_chunk(family, from_addr, from_port); + policy->my_net_mask = my_ts->get_netmask(my_ts); + chunk_free(&from_addr); + + /* calculate net and ports for remote side */ + family = other_ts->get_type(other_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + from_addr = other_ts->get_from_address(other_ts); + from_port = other_ts->get_from_port(other_ts); + to_port = other_ts->get_to_port(other_ts); + from_port = (from_port != to_port) ? 0 : from_port; + policy->other_net = host_create_from_chunk(family, from_addr, from_port); + policy->other_net_mask = other_ts->get_netmask(other_ts); + chunk_free(&from_addr); + + /* install 3 policies: out, in and forward */ + status = charon->kernel_interface->add_policy(charon->kernel_interface, + this->me, this->other, + policy->my_net, policy->other_net, + policy->my_net_mask, policy->other_net_mask, + XFRM_POLICY_OUT, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_IN, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_FWD, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + if (status != SUCCESS) + { + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + policy->my_net->destroy(policy->my_net); + policy->other_net->destroy(policy->other_net); + free(policy); + return status; + } + + /* add it to the policy list, since we want to know which policies we own */ + this->policies->insert_last(this->policies, policy); + } + } + + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + return SUCCESS; +} + +/** + * Implementation of child_sa_t.log_status. + */ +static void log_status(private_child_sa_t *this, logger_t *logger, char* name) +{ + iterator_t *iterator; + sa_policy_t *policy; + struct protoent *proto; + char proto_buf[8] = ""; + char *proto_name = proto_buf; + + if (logger == NULL) + { + logger = this->logger; + } + logger->log(logger, CONTROL|LEVEL1, "\"%s\": protected with ESP (0x%x/0x%x), AH (0x%x,0x%x):", + name, + htonl(this->my_esp_spi), htonl(this->other_esp_spi), + htonl(this->my_ah_spi), htonl(this->other_ah_spi)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&policy); + if (policy->upper_proto) + { + proto = getprotobynumber(policy->upper_proto); + if (proto) + { + proto_name = proto->p_name; + } + else + { + snprintf(proto_buf, sizeof(proto_buf), "<%d>", policy->upper_proto); + } + } + logger->log(logger, CONTROL, "\"%s\": %s/%d==%s==%s/%d", + name, + policy->my_net->get_address(policy->my_net), policy->my_net_mask, + proto_name, + policy->other_net->get_address(policy->other_net), policy->other_net_mask); + } + iterator->destroy(iterator); +} + +/** + * Implementation of child_sa_t.destroy. + */ +static void destroy(private_child_sa_t *this) +{ + /* delete all policys in the kernel */ + sa_policy_t *policy; + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + charon->kernel_interface->del_policy(charon->kernel_interface, + this->me, this->other, + policy->my_net, policy->other_net, + policy->my_net_mask, policy->other_net_mask, + XFRM_POLICY_OUT, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_IN, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_FWD, policy->upper_proto); + + policy->my_net->destroy(policy->my_net); + policy->other_net->destroy(policy->other_net); + free(policy); + } + this->policies->destroy(this->policies); + + /* delete SAs in the kernel, if they are set up */ + if (this->my_ah_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other, this->my_ah_spi, PROTO_AH); + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me, this->other_ah_spi, PROTO_AH); + } + if (this->my_esp_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other, this->my_esp_spi, PROTO_ESP); + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me, this->other_esp_spi, PROTO_ESP); + } + free(this); +} + +/* + * Described in header. + */ +child_sa_t * child_sa_create(host_t *me, host_t* other) +{ + static u_int32_t reqid = 0xc0000000; + private_child_sa_t *this = malloc_thing(private_child_sa_t); + + /* public functions */ + 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.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; + this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status; + this->public.destroy = (void(*)(child_sa_t*))destroy; + + /* private data */ + this->logger = logger_manager->get_logger(logger_manager, CHILD_SA); + this->me = me; + this->other = other; + this->my_ah_spi = 0; + this->my_esp_spi = 0; + this->other_ah_spi = 0; + this->other_esp_spi = 0; + this->reqid = reqid++; + this->policies = linked_list_create(); + + return (&this->public); +} diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h new file mode 100644 index 000000000..6ccbff13f --- /dev/null +++ b/src/charon/sa/child_sa.h @@ -0,0 +1,149 @@ +/** + * @file child_sa.h + * + * @brief Interface of child_sa_t. + * + */ + +/* + * Copyright (C) 2005 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 CHILD_SA_H_ +#define CHILD_SA_H_ + +#include <types.h> +#include <crypto/prf_plus.h> +#include <encoding/payloads/proposal_substructure.h> +#include <utils/logger.h> + +typedef struct child_sa_t child_sa_t; + +/** + * @brief Represents multiple IPsec SAs between two hosts. + * + * A child_sa_t contains multiple SAs. SAs for both + * directions are managed in one child_sa_t object, and + * if both AH and ESP is set up, both protocols are managed + * by one child_sa_t. This means we can have two or + * in the AH+ESP case four IPsec-SAs in one child_sa_t. + * + * The procedure for child sa setup is as follows: + * - A gets SPIs for a proposal via child_sa_t.alloc + * - A send the updated proposal to B + * - B selects a suitable proposal + * - B calls child_sa_t.add to add and update the selected proposal + * - B sends the updated proposal to A + * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal + * + * Once SAs are set up, policies can be added using add_policies. + * + * + * @b Constructors: + * - child_sa_create() + * + * @ingroup sa + */ +struct child_sa_t { + + /** + * @brief Allocate SPIs for a given proposals. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If the proposal contains more + * than one protocol, for each protocol an SPI is + * allocated. SPIs are stored internally and written + * back to the proposal. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + */ + status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); + + /** + * @brief Install the kernel SAs for a proposal. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If the proposal contains more + * than one protocol, for each protocol an SPI is + * allocated. SPIs are stored internally and written + * back to the proposal. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param prf_plus key material to use for key derivation + */ + status_t (*add)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); + + /** + * @brief Install the kernel SAs for a proposal, if SPIs already allocated. + * + * This one updates the SAs in the kernel, which are + * allocated via alloc, with a selected proposals. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param prf_plus key material to use for key derivation + */ + status_t (*update)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); + + /** + * @brief Install the policies using some traffic selectors. + * + * Spplied lists of traffic_selector_t's specify the policies + * to use for this child sa. + * + * @param this calling object + * @param my_ts traffic selectors for local site + * @param other_ts traffic selectors for remote site + * @return SUCCESS or FAILED + */ + status_t (*add_policies) (child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list); + + /** + * @brief Log the status of a child_sa to a logger. + * + * The status of ESP/AH SAs is logged with the supplied logger in + * a human readable form. + * Supplying NULL as logger uses the internal child_sa logger + * to do the logging. The name is only a log-prefix without further + * meaning. + * + * @param this calling object + * @param logger logger to use for logging + * @param name connection name + */ + void (*log_status) (child_sa_t *this, logger_t *logger, char *name); + + /** + * @brief Destroys a child_sa. + * + * @param this calling object + */ + void (*destroy) (child_sa_t *this); +}; + +/** + * @brief Constructor to create a new child_sa_t. + * + * @param me own address + * @param other remote address + * @return child_sa_t object + * + * @ingroup sa + */ +child_sa_t * child_sa_create(host_t *me, host_t *other); + +#endif /*CHILD_SA_H_*/ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c new file mode 100644 index 000000000..6322eb8e9 --- /dev/null +++ b/src/charon/sa/ike_sa.c @@ -0,0 +1,1199 @@ +/** + * @file ike_sa.c + * + * @brief Implementation of ike_sa_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 <string.h> + +#include "ike_sa.h" + +#include <types.h> +#include <daemon.h> +#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> +#include <encoding/payloads/sa_payload.h> +#include <encoding/payloads/nonce_payload.h> +#include <encoding/payloads/ke_payload.h> +#include <encoding/payloads/delete_payload.h> +#include <encoding/payloads/transform_substructure.h> +#include <encoding/payloads/transform_attribute.h> +#include <sa/states/initiator_init.h> +#include <sa/states/responder_init.h> +#include <queues/jobs/retransmit_request_job.h> +#include <queues/jobs/delete_established_ike_sa_job.h> + + + + +typedef struct private_ike_sa_t private_ike_sa_t; + +/** + * Private data of an ike_sa_t object. + */ +struct private_ike_sa_t { + + /** + * Protected part of a ike_sa_t object. + */ + protected_ike_sa_t protected; + + /** + * Identifier for the current IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Linked List containing the child sa's of the current IKE_SA. + */ + 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 + */ + state_t *current_state; + + /** + * INIT configuration, needed for the IKE_SA_INIT exchange. + * + * Gets set in states: + * - INITATOR_INIT + * - RESPONDER_INIT + * + * Available in states: + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * - IKE_AUTH_REQUESTED + * -IKE_SA_ESTABLISHED + */ + connection_t *connection; + + /** + * SA configuration, needed for all other exchanges after IKE_SA_INIT exchange. + * + * Gets set in states: + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * + * Available in states: + * - IKE_AUTH_REQUESTED + * -IKE_SA_ESTABLISHED + */ + 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_t *crypter_initiator; + + /** + * Crypter object for responder. + */ + crypter_t *crypter_responder; + + /** + * Signer object for initiator. + */ + signer_t *signer_initiator; + + /** + * Signer object for responder. + */ + signer_t *signer_responder; + + /** + * Multi purpose prf, set key, use it, forget it + */ + prf_t *prf; + + /** + * Prf function for derivating keymat child SAs + */ + prf_t *child_prf; + + /** + * PRF, with key set to pi_key, used for authentication + */ + prf_t *prf_auth_i; + + /** + * 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. + */ + logger_t *logger; + + /** + * Resends the last sent reply. + * + * @param this calling object + */ + status_t (*resend_last_reply) (private_ike_sa_t *this); +}; + +/** + * 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; + + /* We must process each request or response from remote host */ + + /* 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); + + /* + * It has to be checked, if the message has to be resent cause of lost packets! + */ + if (is_request && (message_id == (this->message_id_in - 1))) + { + /* Message can be resent ! */ + this->logger->log(this->logger, CONTROL|LEVEL1, "Resent request detected. Send stored reply."); + return (this->resend_last_reply(this)); + } + + /* 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; + } + } + + /* 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.build_message. + */ +static void build_message(private_ike_sa_t *this, exchange_type_t type, bool request, message_t **message) +{ + message_t *new_message; + host_t *me, *other; + + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + + 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); + + *message = new_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 ike_sa_t.send_delete_ike_sa_request. + */ +static void send_delete_ike_sa_request (private_ike_sa_t *this) +{ + message_t *informational_request; + delete_payload_t *delete_payload; + crypter_t *crypter; + signer_t *signer; + packet_t *packet; + status_t status; + + if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) + { + return; + } + + /* build empty INFORMATIONAL message */ + this->protected.build_message(&(this->protected), INFORMATIONAL, TRUE, &informational_request); + + delete_payload = delete_payload_create(); + delete_payload->set_protocol_id(delete_payload, PROTO_IKE); + + informational_request->add_payload(informational_request,(payload_t *)delete_payload); + + 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 = informational_request->generate(informational_request, + crypter, + signer, &packet); + informational_request->destroy(informational_request); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not generate packet from message"); + return ; + } + + charon->send_queue->add(charon->send_queue,packet); +} + +/** + * Implementation of ike_sa_t.get_id. + */ +static ike_sa_id_t* get_id(private_ike_sa_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of ike_sa_t.get_my_host. + */ +static host_t* get_my_host(private_ike_sa_t *this) +{ + return this->connection->get_my_host(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_other_host. + */ +static host_t* get_other_host(private_ike_sa_t *this) +{ + return this->connection->get_other_host(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_my_id. + */ +static identification_t* get_my_id(private_ike_sa_t *this) +{ + return this->connection->get_my_id(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_other_id. + */ +static identification_t* get_other_id(private_ike_sa_t *this) +{ + return this->connection->get_other_id(this->connection);; +} + +/** + * Implementation of private_ike_sa_t.resend_last_reply. + */ +static status_t resend_last_reply(private_ike_sa_t *this) +{ + packet_t *packet; + + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to retransmit last reply"); + packet = this->last_responded_message->get_packet(this->last_responded_message); + charon->send_queue->add(charon->send_queue, packet); + + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.retransmit_request. + */ +status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id) +{ + packet_t *packet; + + if (this->last_requested_message == NULL) + { + return NOT_FOUND; + } + + if (message_id == this->last_replied_message_id) + { + return NOT_FOUND; + } + + if ((this->last_requested_message->get_message_id(this->last_requested_message)) != 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); + + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.set_new_state. + */ +static void set_new_state (private_ike_sa_t *this, state_t *state) +{ + this->logger->log(this->logger, CONTROL, "statechange: %s => %s", + mapping_find(ike_sa_state_m,this->current_state->get_state(this->current_state)), + mapping_find(ike_sa_state_m,state->get_state(state))); + this->current_state = state; +} + +/** + * Implementation of protected_ike_sa_t.get_connection. + */ +static connection_t *get_connection (private_ike_sa_t *this) +{ + return this->connection; +} + +/** + * Implementation of protected_ike_sa_t.set_connection. + */ +static void set_connection (private_ike_sa_t *this,connection_t * connection) +{ + this->connection = connection; +} + +/** + * Implementation of protected_ike_sa_t.get_policy. + */ +static policy_t *get_policy (private_ike_sa_t *this) +{ + return this->policy; +} + +/** + * Implementation of protected_ike_sa_t.set_policy. + */ +static void set_policy (private_ike_sa_t *this,policy_t * policy) +{ + this->policy = policy; +} + +/** + * Implementation of protected_ike_sa_t.get_prf. + */ +static prf_t *get_prf (private_ike_sa_t *this) +{ + return this->prf; +} + +/** + * Implementation of protected_ike_sa_t.get_prf. + */ +static prf_t *get_child_prf (private_ike_sa_t *this) +{ + return this->child_prf; +} + +/** + * Implementation of protected_ike_sa_t.get_prf_auth_i. + */ +static prf_t *get_prf_auth_i (private_ike_sa_t *this) +{ + return this->prf_auth_i; +} + +/** + * Implementation of protected_ike_sa_t.get_prf_auth_r. + */ +static prf_t *get_prf_auth_r (private_ike_sa_t *this) +{ + return this->prf_auth_r; +} + + +/** + * 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) +{ + 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; + + /* + * Build the PRF+ instance for deriving keys + */ + if (this->prf != NULL) + { + this->prf->destroy(this->prf); + } + proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No PRF algoithm 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!", + mapping_find(pseudo_random_function_m, algo->algorithm)); + return FAILED; + } + + /* concatenate 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 */ + 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); + spi_r = this->ike_sa_id->get_responder_spi(this->ike_sa_id); + memcpy(nonces_spis.ptr + nonces.len, &spi_i, 8); + memcpy(nonces_spis.ptr + nonces.len + 8, &spi_r, 8); + + /* 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->prf->set_key(this->prf, nonces); + this->prf->allocate_bytes(this->prf, secret, &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+ + */ + this->prf->set_key(this->prf, skeyseed); + prf_plus = prf_plus_create(this->prf, nonces_spis); + + /* clean up unused stuff */ + chunk_free(&nonces); + 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); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_d secret", key); + this->child_prf->set_key(this->child_prf, key); + chunk_free(&key); + + + /* SK_ai/SK_ar used for integrity protection */ + proposal->get_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No integrity algoithm 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "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); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_ai secret", key); + this->signer_initiator->set_key(this->signer_initiator, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_ar secret", key); + this->signer_responder->set_key(this->signer_responder, key); + chunk_free(&key); + + + /* SK_ei/SK_er used for encryption */ + proposal->get_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No encryption algoithm selected!?"); + return FAILED; + } + if (this->crypter_initiator != NULL) + { + this->crypter_initiator->destroy(this->crypter_initiator); + } + if (this->crypter_responder != NULL) + { + this->crypter_responder->destroy(this->crypter_responder); + } + + this->crypter_initiator = crypter_create(algo->algorithm, algo->key_size); + this->crypter_responder = crypter_create(algo->algorithm, algo->key_size); + if (this->crypter_initiator == NULL || this->crypter_responder == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "ENCRYPTION_ALGORITHM %s (key size %d) not supported!", + mapping_find(encryption_algorithm_m, algo->algorithm), + algo->key_size); + return FAILED; + } + key_size = this->crypter_initiator->get_key_size(this->crypter_initiator); + + 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); + 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); + chunk_free(&key); + + /* SK_pi/SK_pr used for authentication */ + proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo); + if (this->prf_auth_i != NULL) + { + this->prf_auth_i->destroy(this->prf_auth_i); + } + if (this->prf_auth_r != NULL) + { + this->prf_auth_r->destroy(this->prf_auth_r); + } + + this->prf_auth_i = prf_create(algo->algorithm); + this->prf_auth_r = prf_create(algo->algorithm); + + key_size = this->prf_auth_i->get_key_size(this->prf_auth_i); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_pi secret", key); + this->prf_auth_i->set_key(this->prf_auth_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_pr secret", key); + this->prf_auth_r->set_key(this->prf_auth_r, key); + chunk_free(&key); + + /* all done, prf_plus not needed anymore */ + prf_plus->destroy(prf_plus); + + return SUCCESS; +} + +/** + * 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.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); + + if (this->last_requested_message != NULL) + { + /* destroy message */ + 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; + + retransmit_job = retransmit_request_job_create(this->message_id_out,this->ike_sa_id); + + status = charon->configuration->get_retransmit_timeout (charon->configuration, + retransmit_job->get_retransmit_count(retransmit_job),&timeout); + + if (status != SUCCESS) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "No retransmit job for message created!"); + retransmit_job->destroy(retransmit_job); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL2, "Request will be retransmitted in %d ms.",timeout); + 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++; + 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 was not as expected"); + 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++; + + 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 */ + this->protected.build_message(&(this->protected), exchange_type, FALSE, &response); + payload = notify_payload_create_from_protocol_and_type(PROTO_IKE, 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); +} + +/** + * 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.get_state. + */ +static ike_sa_state_t get_state (private_ike_sa_t *this) +{ + return this->current_state->get_state(this->current_state); +} + +/** + * Implementation of protected_ike_sa_t.get_state. + */ +static void add_child_sa (private_ike_sa_t *this, child_sa_t *child_sa) +{ + this->child_sas->insert_last(this->child_sas, child_sa); +} + +/** + * 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. + */ +static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) +{ + iterator_t *iterator; + child_sa_t *child_sa; + + /* only log if name == NULL or name == connection_name */ + if (name) + { + if (strcmp(this->connection->get_name(this->connection), name) != 0) + { + return; + } + } + else + { + name = this->connection->get_name(this->connection); + } + + host_t *my_host = this->connection->get_my_host(this->connection); + host_t *other_host = this->connection->get_other_host(this->connection); + + identification_t *my_id = this->connection->get_my_id(this->connection); + identification_t *other_id = this->connection->get_other_id(this->connection); + + if (logger == NULL) + { + logger = this->logger; + } + 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)), + 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]", + 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)); + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&child_sa); + child_sa->log_status(child_sa, logger, name); + } + iterator->destroy(iterator); +} + +/** + * Implementation of protected_ike_sa_t.destroy. + */ +static void destroy (private_ike_sa_t *this) +{ + child_sa_t *child_sa; + + 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"); + + /* inform other peer of delete */ + send_delete_ike_sa_request(this); + while (this->child_sas->remove_last(this->child_sas, (void**)&child_sa) == SUCCESS) + { + child_sa->destroy(child_sa); + } + this->child_sas->destroy(this->child_sas); + + if (this->crypter_initiator) + { + this->crypter_initiator->destroy(this->crypter_initiator); + } + if (this->crypter_responder) + { + this->crypter_responder->destroy(this->crypter_responder); + } + if (this->signer_initiator) + { + this->signer_initiator->destroy(this->signer_initiator); + } + if (this->signer_responder) + { + this->signer_responder->destroy(this->signer_responder); + } + if (this->prf) + { + this->prf->destroy(this->prf); + } + if (this->child_prf) + { + this->child_prf->destroy(this->child_prf); + } + if (this->prf_auth_i) + { + this->prf_auth_i->destroy(this->prf_auth_i); + } + if (this->prf_auth_r) + { + this->prf_auth_r->destroy(this->prf_auth_r); + } + if (this->connection) + { + host_t *me, *other; + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + + this->logger->log(this->logger, AUDIT, "IKE_SA deleted between %s - %s", + me->get_address(me), other->get_address(other)); + this->connection->destroy(this->connection); + } + if (this->policy) + { + 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->ike_sa_id->destroy(this->ike_sa_id); + this->randomizer->destroy(this->randomizer); + this->current_state->destroy(this->current_state); + + free(this); +} + +/* + * Described in header. + */ +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.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_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; + this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t *this)) get_state; + this->protected.public.send_delete_ike_sa_request = (void (*)(ike_sa_t*)) send_delete_ike_sa_request; + this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; + this->protected.public.destroy = (void(*)(ike_sa_t*))destroy; + + /* 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.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 *this)) get_last_responded_message; + this->protected.get_last_requested_message = (message_t * (*) (protected_ike_sa_t *this)) get_last_requested_message; + + this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id; + + /* private functions */ + this->resend_last_reply = resend_last_reply; + + /* 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->prf = NULL; + this->prf_auth_i = NULL; + this->prf_auth_r = NULL; + this->child_prf = NULL; + this->connection = NULL; + this->policy = NULL; + + /* 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); +} diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h new file mode 100644 index 000000000..c526c6347 --- /dev/null +++ b/src/charon/sa/ike_sa.h @@ -0,0 +1,462 @@ +/** + * @file ike_sa.h + * + * @brief Interface of ike_sa_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_H_ +#define IKE_SA_H_ + +#include <types.h> +#include <encoding/message.h> +#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> +#include <crypto/prfs/prf.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <config/connections/connection.h> +#include <config/policies/policy.h> +#include <utils/logger.h> + +/** + * Nonce size in bytes for nonces sending to other peer. + * + * @warning Nonce size MUST be between 16 and 256 bytes. + * + * @ingroup sa + */ +#define NONCE_SIZE 16 + + +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. + * + * @b Constructors: + * - ike_sa_create() + * + * @ingroup sa + */ +struct ike_sa_t { + + /** + * @brief Processes a incoming IKEv2-Message of type message_t. + * + * @param this ike_sa_t object object + * @param[in] message message_t object to process + * @return + * - SUCCESS + * - FAILED + * - DELETE_ME if this IKE_SA MUST be deleted + */ + status_t (*process_message) (ike_sa_t *this,message_t *message); + + /** + * @brief Initiate a new connection with given connection_t object. + * + * The connection_t object is owned by the IKE_SA after the call, so + * do not modify or destroy it. + * + * @param this calling object + * @param connection connection to initiate + * @return + * - SUCCESS if initialization started + * - FAILED if in wrong state + * - DELETE_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection); + + /** + * @brief Retransmits a request. + * + * @param this calling object + * @param message_id ID of the request to retransmit + * @return + * - SUCCESS + * - NOT_FOUND if request doesn't have to be retransmited + */ + status_t (*retransmit_request) (ike_sa_t *this, u_int32_t message_id); + + /** + * @brief Sends a request to delete IKE_SA. + * + * Only supported in state IKE_SA_ESTABLISHED + * + * @param this calling object + */ + void (*send_delete_ike_sa_request) (ike_sa_t *this); + + /** + * @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 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. + * + * @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. + * + * 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 + */ + connection_t* (*get_connection) (ike_sa_t *this); + + /** + * @brief Get the state of type of associated state object. + * + * @param this calling object + * @return state of IKE_SA + */ + ike_sa_state_t (*get_state) (ike_sa_t *this); + + /** + * @brief Log the status of a the ike sa to a logger. + * + * The status of the IKE SA and all child SAs is logged. + * Supplying NULL as logger uses the internal child_sa logger + * to do the logging. The log is only done if the supplied + * connection name is NULL or matches the connections name. + * + * @param this calling object + * @param logger logger to use for logging + * @param name name of the connection + */ + void (*log_status) (ike_sa_t *this, logger_t *logger, char *name); + + /** + * @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); + + /** + * @brief Set the internal connection object. + * + * @param this calling object + * @param connection object of type connection_t + */ + void (*set_connection) (protected_ike_sa_t *this, connection_t *connection); + + /** + * @brief Get the internal stored policy object. + * + * @param this calling object + * @return pointer to the internal stored policy_t object + */ + policy_t *(*get_policy) (protected_ike_sa_t *this); + + /** + * @brief Set the internal policy_t object. + * + * @param this calling object + * @param policy object of type policy_t + */ + void (*set_policy) (protected_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. + * Allready 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); + + /** + * @brief Get the multi purpose prf. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf) (protected_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 + */ + prf_t *(*get_child_prf) (protected_ike_sa_t *this); + + /** + * @brief Get the prf used for authentication of initiator. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf_auth_i) (protected_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 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 this calling object + */ + void (*reset_message_buffers) (protected_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. + * @return ike_sa_t object + * + * @ingroup sa + */ +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 new file mode 100644 index 000000000..bf3a05d11 --- /dev/null +++ b/src/charon/sa/ike_sa_id.c @@ -0,0 +1,185 @@ +/** + * @file ike_sa_id.c + * + * @brief Implementation of ike_sa_id_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 "ike_sa_id.h" + + + +typedef struct private_ike_sa_id_t private_ike_sa_id_t; + +/** + * Private data of an ike_sa_id_t object. + */ +struct private_ike_sa_id_t { + /** + * Public interface of ike_sa_id_t. + */ + ike_sa_id_t public; + + /** + * SPI of Initiator. + */ + u_int64_t initiator_spi; + + /** + * SPI of Responder. + */ + u_int64_t responder_spi; + + /** + * Role for specific IKE_SA. + */ + bool is_initiator_flag; +}; + +/** + * Implementation of ike_sa_id_t.set_responder_spi. + */ +static void set_responder_spi (private_ike_sa_id_t *this, u_int64_t responder_spi) +{ + this->responder_spi = responder_spi; +} + +/** + * Implementation of ike_sa_id_t.set_initiator_spi. + */ +static void set_initiator_spi(private_ike_sa_id_t *this, u_int64_t initiator_spi) +{ + this->initiator_spi = initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_ike_sa_id_t *this) +{ + return this->initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_responder_spi. + */ +static u_int64_t get_responder_spi (private_ike_sa_id_t *this) +{ + return this->responder_spi; +} + +/** + * Implementation of ike_sa_id_t.equals. + */ +static bool equals (private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + if (other == NULL) + { + return FALSE; + } + if ((this->is_initiator_flag == other->is_initiator_flag) && + (this->initiator_spi == other->initiator_spi) && + (this->responder_spi == other->responder_spi)) + { + /* private_ike_sa_id's are equal */ + return TRUE; + } + else + { + /* private_ike_sa_id's are not equal */ + return FALSE; + } +} + +/** + * Implementation of ike_sa_id_t.replace_values. + */ +static void replace_values(private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + this->initiator_spi = other->initiator_spi; + this->responder_spi = other->responder_spi; + this->is_initiator_flag = other->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.is_initiator. + */ +static bool is_initiator(private_ike_sa_id_t *this) +{ + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.switch_initiator. + */ +static bool switch_initiator(private_ike_sa_id_t *this) +{ + if (this->is_initiator_flag) + { + this->is_initiator_flag = FALSE; + } + else + { + this->is_initiator_flag = TRUE; + } + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.clone. + */ +static ike_sa_id_t* clone(private_ike_sa_id_t *this) +{ + return ike_sa_id_create(this->initiator_spi, this->responder_spi, this->is_initiator_flag); +} + +/** + * Implementation of ike_sa_id_t.destroy. + */ +static void destroy(private_ike_sa_id_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiator_flag) +{ + private_ike_sa_id_t *this = malloc_thing(private_ike_sa_id_t); + + /* public functions */ + this->public.set_responder_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_responder_spi; + this->public.set_initiator_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(ike_sa_id_t*)) get_responder_spi; + this->public.get_initiator_spi = (u_int64_t(*)(ike_sa_id_t*)) get_initiator_spi; + this->public.equals = (bool(*)(ike_sa_id_t*,ike_sa_id_t*)) equals; + this->public.replace_values = (void(*)(ike_sa_id_t*,ike_sa_id_t*)) replace_values; + this->public.is_initiator = (bool(*)(ike_sa_id_t*)) is_initiator; + this->public.switch_initiator = (bool(*)(ike_sa_id_t*)) switch_initiator; + this->public.clone = (ike_sa_id_t*(*)(ike_sa_id_t*)) clone; + this->public.destroy = (void(*)(ike_sa_id_t*))destroy; + + /* private data */ + this->initiator_spi = initiator_spi; + this->responder_spi = responder_spi; + this->is_initiator_flag = is_initiator_flag; + + return (&this->public); +} diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h new file mode 100644 index 000000000..0f16f7637 --- /dev/null +++ b/src/charon/sa/ike_sa_id.h @@ -0,0 +1,146 @@ +/** + * @file ike_sa_id.h + * + * @brief Interface of ike_sa_id_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_ID_H_ +#define IKE_SA_ID_H_ + +#include <types.h> + + +typedef struct ike_sa_id_t ike_sa_id_t; + +/** + * @brief An object of type ike_sa_id_t is used to identify an IKE_SA. + * + * An IKE_SA is identified by its initiator and responder spi's. + * Additionaly it contains the role of the actual running IKEv2-Daemon + * for the specific IKE_SA (original initiator or responder). + * + * @b Constructors: + * - ike_sa_id_create() + * + * @ingroup sa + */ +struct ike_sa_id_t { + + /** + * @brief Set the SPI of the responder. + * + * This function is called when a request or reply of a IKE_SA_INIT is received. + * + * @param this calling object + * @param responder_spi SPI of responder to set + */ + void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi); + + /** + * @brief Set the SPI of the initiator. + * + * @param this calling object + * @param initiator_spi SPI to set + */ + void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi); + + /** + * @brief Get the initiator SPI. + * + * @param this calling object + * @return SPI of the initiator + */ + u_int64_t (*get_initiator_spi) (ike_sa_id_t *this); + + /** + * @brief Get the responder SPI. + * + * @param this calling object + * @return SPI of the responder + */ + u_int64_t (*get_responder_spi) (ike_sa_id_t *this); + + /** + * @brief Check if two ike_sa_id_t objects are equal. + * + * Two ike_sa_id_t objects are equal if both SPI values and the role matches. + * + * @param this calling object + * @param other ike_sa_id_t object to check if equal + * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise + */ + bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Replace all values of a given ike_sa_id_t object with values. + * from another ike_sa_id_t object. + * + * After calling this function, both objects are equal. + * + * @param this calling object + * @param other ike_sa_id_t object from which values will be taken + */ + void (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Get the initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator + */ + bool (*is_initiator) (ike_sa_id_t *this); + + /** + * @brief Switche the original initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator after switch, FALSE otherwise + */ + bool (*switch_initiator) (ike_sa_id_t *this); + + /** + * @brief Clones a given ike_sa_id_t object. + * + * @param this calling object + * @return cloned ike_sa_id_t object + */ + ike_sa_id_t *(*clone) (ike_sa_id_t *this); + + /** + * @brief Destroys an ike_sa_id_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_id_t *this); +}; + +/** + * @brief Creates an ike_sa_id_t object with specific SPI's and defined role. + * + * @param initiator_spi initiators SPI + * @param responder_spi responders SPI + * @param is_initiaor TRUE if we are the original initiator + * @return ike_sa_id_t object + * + * @ingroup sa + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiaor); + +#endif /*IKE_SA_ID_H_*/ diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c new file mode 100644 index 000000000..a65f41042 --- /dev/null +++ b/src/charon/sa/ike_sa_manager.c @@ -0,0 +1,843 @@ +/** + * @file ike_sa_manager.c + * + * @brief Implementation of ike_sa_mananger_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 <pthread.h> +#include <string.h> + +#include "ike_sa_manager.h" + +#include <daemon.h> +#include <sa/ike_sa_id.h> +#include <utils/logger.h> +#include <utils/logger_manager.h> +#include <utils/linked_list.h> + +typedef struct ike_sa_entry_t ike_sa_entry_t; + +/** + * An entry in the linked list, contains IKE_SA, locking and lookup data. + */ +struct ike_sa_entry_t { + /** + * Destructor, also destroys associated ike_sa_t object. + */ + status_t (*destroy) (ike_sa_entry_t *this); + + /** + * Number of threads waiting for this ike_sa_t object. + */ + int waiting_threads; + + /** + * Condvar where threads can wait until ike_sa_t object is free for use again. + */ + pthread_cond_t condvar; + + /** + * Is this ike_sa currently checked out? + */ + bool checked_out; + + /** + * Does this SA drives out new threads? + */ + bool driveout_new_threads; + + /** + * Does this SA drives out waiting threads? + */ + bool driveout_waiting_threads; + + /** + * Identifiaction of an IKE_SA (SPIs). + */ + ike_sa_id_t *ike_sa_id; + + /** + * The contained ike_sa_t object. + */ + ike_sa_t *ike_sa; +}; + +/** + * Implementation of ike_sa_entry_t.destroy. + */ +static status_t ike_sa_entry_destroy(ike_sa_entry_t *this) +{ + /* also destroy IKE SA */ + this->ike_sa->destroy(this->ike_sa); + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); + return SUCCESS; +} + +/** + * @brief Creates a new entry for the ike_sa_t list. + * + * This constructor additionaly creates a new and empty SA. + * + * @param ike_sa_id The associated ike_sa_id_t, will be cloned + * @return ike_sa_entry_t object + */ +static ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id) +{ + ike_sa_entry_t *this = malloc_thing(ike_sa_entry_t); + + /* destroy function */ + this->destroy = ike_sa_entry_destroy; + + this->waiting_threads = 0; + pthread_cond_init(&(this->condvar), NULL); + + /* we set checkout flag when we really give it out */ + this->checked_out = FALSE; + this->driveout_new_threads = FALSE; + this->driveout_waiting_threads = FALSE; + + /* ike_sa_id is always cloned */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + /* create new ike_sa */ + this->ike_sa = ike_sa_create(ike_sa_id); + + return this; +} + + +typedef struct private_ike_sa_manager_t private_ike_sa_manager_t; + +/** + * Additional private members of ike_sa_manager_t. + */ +struct private_ike_sa_manager_t { + /** + * Public interface of ike_sa_manager_t. + */ + ike_sa_manager_t public; + + /** + * @brief Get next spi. + * + * We give out SPIs incremental starting at 1. + * + * @param this the ike_sa_manager + * @return the next spi + */ + u_int64_t (*get_next_spi) (private_ike_sa_manager_t *this); + + /** + * @brief Find the ike_sa_entry_t object in the list by SPIs. + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this calling object + * @param ike_sa_id id of the ike_sa, containing SPIs + * @param[out] entry pointer to set to the found entry + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + status_t (*get_entry_by_id) (private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry); + + /** + * @brief Find the ike_sa_entry_t in the list by pointer to SA. + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this calling object + * @param ike_sa pointer to the ike_sa + * @param[out] entry pointer to set to the found entry + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + 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. + * + * @param this calling object + * @param entry entry to delete + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + status_t (*delete_entry) (private_ike_sa_manager_t *this, ike_sa_entry_t *entry); + + /** + * Lock for exclusivly accessing the manager. + */ + pthread_mutex_t mutex; + + /** + * Logger used for this IKE SA Manager. + */ + logger_t *logger; + + /** + * Linked list with entries for the ike_sa_t objects. + */ + linked_list_t *ike_sa_list; + + /** + * A randomizer, to get random SPIs for our side + */ + randomizer_t *randomizer; +}; + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_id. + */ +static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + /* create iterator over list of ike_sa's */ + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + + iterator->current(iterator, (void**)¤t); + 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))) + { + 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; + break; + } + } + 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))) + { + 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; + break; + } + } + if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by full ID"); + *entry = current; + status = SUCCESS; + break; + } + } + + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_sa. + */ +static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + iterator->current(iterator, (void**)¤t); + /* only pointers are compared */ + if (current->ike_sa == ike_sa) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by pointer"); + *entry = current; + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implementation of private_ike_sa_manager_s.delete_entry. + */ +static status_t delete_entry(private_ike_sa_manager_t *this, ike_sa_entry_t *entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + iterator->current(iterator, (void**)¤t); + if (current == entry) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by pointer. Going to delete it."); + iterator->remove(iterator); + entry->destroy(entry); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + + +/** + * Implementation of private_ike_sa_manager_t.get_next_spi. + */ +static u_int64_t get_next_spi(private_ike_sa_manager_t *this) +{ + u_int64_t spi; + + this->randomizer->get_pseudo_random_bytes(this->randomizer, 8, (u_int8_t*)&spi); + + return spi; +} + +/** + * Implementation of of ike_sa_manager.create_and_checkout. + */ +static void create_and_checkout(private_ike_sa_manager_t *this,ike_sa_t **ike_sa) +{ + u_int64_t initiator_spi; + ike_sa_entry_t *new_ike_sa_entry; + ike_sa_id_t *new_ike_sa_id; + + initiator_spi = this->get_next_spi(this); + new_ike_sa_id = ike_sa_id_create(0, 0, TRUE); + new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi); + + /* create entry */ + new_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id); + new_ike_sa_id->destroy(new_ike_sa_id); + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + 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"); + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implementation of of ike_sa_manager.checkout. + */ +static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t **ike_sa) +{ + bool responder_spi_set; + bool initiator_spi_set; + bool original_initiator; + status_t retval; + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + responder_spi_set = (FALSE != ike_sa_id->get_responder_spi(ike_sa_id)); + initiator_spi_set = (FALSE != ike_sa_id->get_initiator_spi(ike_sa_id)); + original_initiator = ike_sa_id->is_initiator(ike_sa_id); + + if ((initiator_spi_set && responder_spi_set) || + ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + { + /* we SHOULD have an IKE_SA for these SPIs in the list, + * if not, we can't handle the request... + */ + ike_sa_entry_t *entry; + /* look for the entry */ + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + /* 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"); + /* no we can't */ + retval = NOT_FOUND; + } + else + { + /* is this IKE_SA already checked out ?? + * are we welcome to get this SA ? */ + while (entry->checked_out && !entry->driveout_waiting_threads) + { + /* so wait until we can get it for us. + * we register us as waiting. + */ + entry->waiting_threads++; + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + entry->waiting_threads--; + } + + /* hm, a deletion request forbids us to get this SA, go home */ + if (entry->driveout_waiting_threads) + { + /* 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"); + retval = NOT_FOUND; + } + else + { + 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; + /* DON'T use return, we must unlock the mutex! */ + retval = SUCCESS; + } + } + } + else + { + 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; + } + } + else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) + { + /* an IKE_SA_INIT from an another endpoint, + * he is the initiator. + * For simplicity, we do NOT check for retransmitted + * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- + * Request (even a retransmitted one) will result in a + * IKE_SA. This could be improved... + */ + u_int64_t responder_spi; + ike_sa_entry_t *new_ike_sa_entry; + + + /* set SPIs, we are the responder */ + responder_spi = this->get_next_spi(this); + + /* we also set arguments spi, so its still valid */ + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* create entry */ + new_ike_sa_entry = ike_sa_entry_create(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"); + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + retval = CREATED; + } + else + { + /* responder set, initiator not: here is something seriously wrong! */ + this->logger->log(this->logger,ERROR | LEVEL1, "Invalid IKE_SA SPI's"); + /* DON'T use return, we must unlock the mutex! */ + retval = INVALID_ARG; + } + + pthread_mutex_unlock(&(this->mutex)); + /* OK, unlocked... */ + return retval; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_hosts. + */ +static status_t checkout_by_hosts(private_ike_sa_manager_t *this, host_t *me, host_t *other, ike_sa_t **ike_sa) +{ + iterator_t *iterator; + ike_sa_id_t *ike_sa_id = NULL; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + host_t *sa_me, *sa_other; + + iterator->current(iterator, (void**)¤t); + sa_me = current->ike_sa->get_my_host(current->ike_sa); + sa_other = current->ike_sa->get_other_host(current->ike_sa); + + /* one end may be default/any, but not both */ + if (me->is_default_route(me)) + { + if (other->is_default_route(other)) + { + break; + } + if (other->equals(other, sa_other)) + { + /* other matches */ + ike_sa_id = current->ike_sa_id; + } + } + else if (other->is_default_route(other)) + { + if (me->equals(me, sa_me)) + { + /* ME matches */ + ike_sa_id = current->ike_sa_id; + } + } + else + { + if (me->equals(me, sa_me) && other->equals(other, sa_other)) + { + /* both matches */ + ike_sa_id = current->ike_sa_id; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + if (ike_sa_id) + { + /* checkout is done in the checkout function, since its rather complex */ + return checkout(this, ike_sa_id, ike_sa); + } + return NOT_FOUND; +} + +/** + * Implementation of ike_sa_manager_t.get_ike_sa_list. + */ +linked_list_t *get_ike_sa_list(private_ike_sa_manager_t* this) +{ + linked_list_t *list; + iterator_t *iterator; + + pthread_mutex_lock(&(this->mutex)); + + list = linked_list_create(); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + iterator->current(iterator, (void**)&entry); + list->insert_last(list, (void*)entry->ike_sa_id->clone(entry->ike_sa_id)); + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); + return list; +} + +/** + * Implementation of ike_sa_manager_t.get_ike_sa_list_by_name. + */ +linked_list_t *get_ike_sa_list_by_name(private_ike_sa_manager_t* this, const char *name) +{ + linked_list_t *list; + iterator_t *iterator; + + pthread_mutex_lock(&(this->mutex)); + + list = linked_list_create(); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + connection_t *connection; + + iterator->current(iterator, (void**)&entry); + connection = entry->ike_sa->get_connection(entry->ike_sa); + if (strcmp(name, connection->get_name(connection)) == 0) + { + list->insert_last(list, (void*)entry->ike_sa_id->clone(entry->ike_sa_id)); + } + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); + return list; +} + +/** + * Implementation of ike_sa_manager_t.log_status. + */ +static void log_status(private_ike_sa_manager_t* this, logger_t* logger, char* name) +{ + iterator_t *iterator; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + iterator->current(iterator, (void**)&entry); + entry->ike_sa->log_status(entry->ike_sa, logger, name); + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implementation of ike_sa_manager_t.checkin. + */ +static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* to check the SA back in, we look for the pointer of the ike_sa + * in all entries. + * We can't search by SPI's since the MAY have changed (e.g. on reception + * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... + */ + status_t retval; + ike_sa_entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + + /* look for the entry */ + if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* ike_sa_id must be updated */ + entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); + /* signal waiting threads */ + entry->checked_out = FALSE; + this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin of IKE_SA successful."); + pthread_cond_signal(&(entry->condvar)); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin nonexisting IKE_SA"); + /* this SA is no more, this REALLY should not happen */ + retval = NOT_FOUND; + } + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + + +/** + * Implementation of ike_sa_manager_t.checkin_and_delete. + */ +static status_t checkin_and_delete(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + ike_sa_entry_t *entry; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* mark it, so now new threads can acquire this SA */ + entry->driveout_new_threads = TRUE; + /* additionaly, drive out waiting threads */ + entry->driveout_waiting_threads = TRUE; + + /* wait until all workers have done their work */ + while (entry->waiting_threads > 0) + { + /* let the other threads do some work*/ + pthread_cond_signal(&(entry->condvar)); + /* 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 */ + this->delete_entry(this, entry); + this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin and delete of IKE_SA successful"); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin and delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +/** + * Implementation of ike_sa_manager_t.delete. + */ +static status_t delete(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + ike_sa_entry_t *entry; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + /* mark it, so now new threads can acquire this SA */ + entry->driveout_new_threads = TRUE; + + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* 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 */ + this->delete_entry(this, entry); + this->logger->log(this->logger,CONTROL | LEVEL1,"Delete of IKE_SA successful"); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +/** + * Implementation of ike_sa_manager_t.destroy. + */ +static void destroy(private_ike_sa_manager_t *this) +{ + /* destroy all list entries */ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + 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"); + + /* Step 1: drive out all waiting threads */ + 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); + /* do not accept new threads, drive out waiting threads */ + 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"); + /* Step 2: wait until all are gone */ + iterator->reset(iterator); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&entry); + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* go sleeping until they are gone */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + } + this->logger->log(this->logger,CONTROL | LEVEL2,"Delete all IKE_SA's"); + /* Step 3: delete all entries */ + iterator->destroy(iterator); + + while (list->get_count(list) > 0) + { + list->get_first(list, (void**)&entry); + this->delete_entry(this, entry); + } + list->destroy(list); + this->logger->log(this->logger,CONTROL | LEVEL2,"IKE_SA's deleted"); + pthread_mutex_unlock(&(this->mutex)); + + this->randomizer->destroy(this->randomizer); + + free(this); +} + +/* + * Described in header. + */ +ike_sa_manager_t *ike_sa_manager_create() +{ + private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); + + /* assign public functions */ + this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; + this->public.create_and_checkout = (void(*)(ike_sa_manager_t*,ike_sa_t**))create_and_checkout; + this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t*,ike_sa_t**))checkout; + this->public.checkout_by_hosts = (status_t(*)(ike_sa_manager_t*,host_t*,host_t*,ike_sa_t**))checkout_by_hosts; + this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list; + this->public.get_ike_sa_list_by_name = (linked_list_t*(*)(ike_sa_manager_t*,const char*))get_ike_sa_list_by_name; + this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status; + this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; + this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete; + this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_delete; + + /* initialize private functions */ + this->get_next_spi = get_next_spi; + this->get_entry_by_sa = get_entry_by_sa; + this->get_entry_by_id = get_entry_by_id; + this->delete_entry = delete_entry; + + /* initialize private variables */ + this->logger = logger_manager->get_logger(logger_manager, IKE_SA_MANAGER); + + this->ike_sa_list = linked_list_create(); + + pthread_mutex_init(&(this->mutex), NULL); + + this->randomizer = randomizer_create(); + + return (ike_sa_manager_t*)this; +} diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h new file mode 100644 index 000000000..d4cd749dc --- /dev/null +++ b/src/charon/sa/ike_sa_manager.h @@ -0,0 +1,194 @@ +/** + * @file ike_sa_manager.h + * + * @brief Interface of ike_sa_manager_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_MANAGER_H_ +#define IKE_SA_MANAGER_H_ + +#include <types.h> +#include <sa/ike_sa.h> +#include <utils/logger.h> + + +typedef struct ike_sa_manager_t ike_sa_manager_t; + +/** + * @brief The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. + * + * To avoid access from multiple threads, IKE_SAs must be checked out from + * the manager, and checked in after usage. + * The manager also handles deletion of SAs. + * + * @todo checking of double-checkouts from the same threads would be nice. + * This could be done by comparing thread-ids via pthread_self()... + * + * @todo Managing of ike_sa_t objects in a hash table instead of linked list. + * + * @b Constructors: + * - ike_sa_manager_create() + * + * @ingroup sa + */ +struct ike_sa_manager_t { + /** + * @brief Checkout an IKE_SA, create it when necesarry. + * + * Checks out a SA by its ID. An SA will be created, when: + * - Responder SPI is not set (when received an IKE_SA_INIT from initiator) + * Management of SPIs is the managers job, he will set it. + * This function blocks until SA is available for checkout. + * + * @warning checking out two times without checking in will + * result in a deadlock! + * + * @param this the manager object + * @param ike_sa_id[in/out] the SA identifier, will be updated + * @param ike_sa[out] checked out SA + * @returns + * - SUCCESS if checkout successful + * - NOT_FOUND when no such SA is available + * - CREATED if a new IKE_SA got created + */ + status_t (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id, ike_sa_t **ike_sa); + + /** + * @brief Create and checkout an IKE_SA as original initator. + * + * Creates and checks out a SA as initiator. + * Management of SPIs is the managers job, he will set it. + * + * @param this the manager object + * @param ike_sa[out] checked out SA + */ + void (*create_and_checkout) (ike_sa_manager_t* this,ike_sa_t **ike_sa); + + /** + * @brief Check out an IKE_SA, defined be the two peers. + * + * Checking out an IKE_SA by their peer addresses may be necessary + * for kernel traps, status querying and so on... one of the hosts + * may be 0.0.0.0 (defaultroute/any), but not both. + * + * @param this the manager object + * @param me host on local side + * @param other host on remote side + * @param ike_sa[out] checked out SA + * @return + * - NOT_FOUND, if no such SA found + * - SUCCESS, if SA found and ike_sa set appropriatly + */ + status_t (*checkout_by_hosts) (ike_sa_manager_t* this, host_t *me, host_t *other, ike_sa_t **ike_sa); + + /** + * @brief Get a list of all IKE_SA SAs currently set up. + * + * The resulting list with all IDs must be destroyd by + * the caller. There is no guarantee an ike_sa with the + * corrensponding ID really exists, since it may be deleted + * in the meantime by another thread. + * + * @param this the manager object + * @return a list with ike_sa_id_t s + */ + linked_list_t *(*get_ike_sa_list) (ike_sa_manager_t* this); + + /** + * @brief Get a list of all IKE_SA SAs currently set up specified + * by the connections name. + * + * @param this the manager object + * @return a list with ike_sa_id_t s + */ + linked_list_t *(*get_ike_sa_list_by_name) (ike_sa_manager_t* this, const char *name); + + /** + * @brief Log the status of the IKE_SA's in the manager. + * + * A informational log is done to the supplied logger. If logger is + * NULL, an internal logger is used. If a name is supplied, + * only connections with the matching name will be logged. + * + * @param this the manager object + * @param logger logger to do the log, or NULL + * @param name name of a connection, or NULL + */ + void (*log_status) (ike_sa_manager_t* this, logger_t* logger, char* name); + + /** + * @brief Checkin the SA after usage. + * + * @warning the SA pointer MUST NOT be used after checkin! + * The SA must be checked out again! + * + * @param this the manager object + * @param ike_sa_id[in/out] the SA identifier, will be updated + * @param ike_sa[out] checked out SA + * @returns + * - SUCCESS if checked in + * - NOT_FOUND when not found (shouldn't happen!) + */ + status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Delete a SA, which was not checked out. + * + * @warning do not use this when the SA is already checked out, this will + * deadlock! + * + * @param this the manager object + * @param ike_sa_id[in/out] the SA identifier + * @returns + * - SUCCESS if found + * - NOT_FOUND when no such SA is available + */ + status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id); + + /** + * @brief Delete a checked out SA. + * + * @param this the manager object + * @param ike_sa SA to delete + * @returns + * - SUCCESS if found + * - NOT_FOUND when no such SA is available + */ + status_t (*checkin_and_delete) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Destroys the manager with all associated SAs. + * + * Threads will be driven out, so all SAs can be deleted cleanly. + * + * @param this the manager object + */ + void (*destroy) (ike_sa_manager_t *this); +}; + +/** + * @brief Create a manager. + * + * @returns ike_sa_manager_t object + * + * @ingroup sa + */ +ike_sa_manager_t *ike_sa_manager_create(void); + +#endif /*IKE_SA_MANAGER_H_*/ diff --git a/src/charon/sa/states/Makefile.states b/src/charon/sa/states/Makefile.states new file mode 100644 index 000000000..a258ebef0 --- /dev/null +++ b/src/charon/sa/states/Makefile.states @@ -0,0 +1,43 @@ +# 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. +# + +STATES_DIR= $(SA_DIR)states/ + +CHARON_OBJS+= $(BUILD_DIR)ike_auth_requested.o +$(BUILD_DIR)ike_auth_requested.o : $(STATES_DIR)ike_auth_requested.c $(STATES_DIR)ike_auth_requested.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_established.o +$(BUILD_DIR)ike_sa_established.o : $(STATES_DIR)ike_sa_established.c $(STATES_DIR)ike_sa_established.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_requested.o +$(BUILD_DIR)ike_sa_init_requested.o : $(STATES_DIR)ike_sa_init_requested.c $(STATES_DIR)ike_sa_init_requested.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_responded.o +$(BUILD_DIR)ike_sa_init_responded.o : $(STATES_DIR)ike_sa_init_responded.c $(STATES_DIR)ike_sa_init_responded.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)initiator_init.o +$(BUILD_DIR)initiator_init.o : $(STATES_DIR)initiator_init.c $(STATES_DIR)initiator_init.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)responder_init.o +$(BUILD_DIR)responder_init.o : $(STATES_DIR)responder_init.c $(STATES_DIR)responder_init.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)state.o +$(BUILD_DIR)state.o : $(STATES_DIR)state.c $(STATES_DIR)state.h + $(CC) $(CFLAGS) -c -o $@ $<
\ No newline at end of file diff --git a/src/charon/sa/states/ike_auth_requested.c b/src/charon/sa/states/ike_auth_requested.c new file mode 100644 index 000000000..3d49f440f --- /dev/null +++ b/src/charon/sa/states/ike_auth_requested.c @@ -0,0 +1,671 @@ +/** + * @file ike_auth_requested.c + * + * @brief Implementation 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. + */ + +#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/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 + * - DELETE_ME + */ + status_t (*process_idr_payload) (private_ike_auth_requested_t *this, id_payload_t *idr_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 + * - DELETE_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 + * - DELETE_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 + * - DELETE_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 + * - DELETE_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, *tsr_payload = NULL; + id_payload_t *idr_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; + chunk_t seed; + prf_plus_t *prf_plus; + connection_t *connection; + + 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 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; + } + } + case CERTIFICATE: + { + /* TODO handle cert 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 (!(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 DELETE_ME; + } + + /* 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"); + } + 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 DELETE_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 DELETE_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->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa)); + this->destroy_after_state_change(this); + + connection = this->ike_sa->get_connection(this->ike_sa); + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + 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; + connection_t *connection; + + 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 DELETE_ME; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + connection->update_other_id(connection, other_id->clone(other_id)); + + this->policy->update_other_id(this->policy, other_id); + 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 DELETE_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 DELETE_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 DELETE_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 DELETE_ME; + + } + case AUTHENTICATION_FAILED: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an AUTHENTICATION_FAILED notify. Deleting IKE_SA"); + return DELETE_ME; + + } + case SINGLE_PAIR_REQUIRED: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a SINGLE_PAIR_REQUIRED notify. Deleting IKE_SA"); + return DELETE_ME; + } + 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 DELETE_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_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 new file mode 100644 index 000000000..a8eef014c --- /dev/null +++ b/src/charon/sa/states/ike_auth_requested.h @@ -0,0 +1,72 @@ +/** + * @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 new file mode 100644 index 000000000..e91409f6a --- /dev/null +++ b/src/charon/sa/states/ike_sa_established.c @@ -0,0 +1,239 @@ +/** + * @file ike_sa_established.c + * + * @brief Implementation 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. + */ + +#include "ike_sa_established.h" + +#include <daemon.h> +#include <encoding/payloads/delete_payload.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; + + /** + * Assigned logger. Use logger of IKE_SA. + */ + logger_t *logger; + + /** + * Process a notify payload + * + * @param this calling object + * @param notify_payload notify payload + * @param response response message of type INFORMATIONAL + * + * - SUCCESS + * - FAILED + * - DELETE_ME + */ + status_t (*process_notify_payload) (private_ike_sa_established_t *this, notify_payload_t *notify_payload,message_t *response); +}; + +/** + * Implements state_t.get_state + */ +static status_t process_message(private_ike_sa_established_t *this, message_t *message) +{ + delete_payload_t *delete_request = NULL; + ike_sa_id_t *ike_sa_id; + iterator_t *payloads; + message_t *response; + crypter_t *crypter; + signer_t *signer; + status_t status; + + if (message->get_exchange_type(message) != INFORMATIONAL) + { + 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))); + return FAILED; + } + + if (!message->get_request(message)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "INFORMATIONAL responses not handled in state ike_sa_established"); + return FAILED; + } + + ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); + + /* get signer for verification and crypter for decryption */ + 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, "INFORMATIONAL request decryption failed. Ignoring message"); + return status; + } + + /* build empty INFORMATIONAL message */ + this->ike_sa->build_message(this->ike_sa, INFORMATIONAL, FALSE, &response); + + 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 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,response); + if (status != SUCCESS) + { + payloads->destroy(payloads); + response->destroy(response); + return status; + } + } + 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, AUDIT, "DELETE request for IKE_SA received"); + response->destroy(response); + return DELETE_ME; + } + else + { + this->logger->log(this->logger, AUDIT, "DELETE request for CHILD_SA received. Ignored"); + response->destroy(response); + return SUCCESS; + } + } + + 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 INFORMATIONAL reply"); + response->destroy(response); + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_established_t.process_notify_payload; + */ +static status_t process_notify_payload (private_ike_sa_established_t *this, notify_payload_t *notify_payload, message_t *response) +{ + 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 for protocol %s", + mapping_find(notify_message_type_m, notify_message_type), + mapping_find(protocol_id_m, notify_payload->get_protocol_id(notify_payload))); + + switch (notify_message_type) + { + default: + { + this->logger->log(this->logger, AUDIT, "INFORMATIONAL 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_established_t *this) +{ + return IKE_SA_ESTABLISHED; +} + +/** + * Implementation of state_t.get_state + */ +static void destroy(private_ike_sa_established_t *this) +{ + 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 functions */ + this->process_notify_payload = process_notify_payload; + + /* 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/ike_sa_established.h b/src/charon/sa/states/ike_sa_established.h new file mode 100644 index 000000000..8477ad5bc --- /dev/null +++ b/src/charon/sa/states/ike_sa_established.h @@ -0,0 +1,64 @@ +/** + * @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 new file mode 100644 index 000000000..311cdf0a0 --- /dev/null +++ b/src/charon/sa/states/ike_sa_init_requested.c @@ -0,0 +1,798 @@ +/** + * @file ike_sa_init_requested.c + * + * @brief Implementation of ike_sa_init_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. + */ + +#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/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; + + + /** + * 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 response 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 *response); + + /** + * Build IDr payload for IKE_AUTH request. + * + * Only built when the ID of the responder contains no wildcards. + * + * @param this calling object + * @param response 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 *response); + + /** + * Build AUTH payload for IKE_AUTH request. + * + * @param this calling object + * @param my_id_payload buildet ID payload + * @param response 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 *response); + + /** + * Build SA payload for IKE_AUTH request. + * + * @param this calling object + * @param response 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 *response); + + /** + * Build TSi payload for IKE_AUTH request. + * + * @param this calling object + * @param response 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 *response); + + /** + * Build TSr payload for IKE_AUTH request. + * + * @param this calling object + * @param response 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 *response); + + /** + * 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; + 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); + + /* Iterate over all payloads. + * + * The message is allready 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 DELETE_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 DELETE_ME; + } + + /* apply the address on wich we really received the packet */ + connection = this->ike_sa->get_connection(this->ike_sa); + me = ike_sa_init_reply->get_destination(ike_sa_init_reply); + connection->update_my_host(connection, me->clone(me)); + policy = this->ike_sa->get_policy(this->ike_sa); + policy->update_my_ts(policy, me); + + /* 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) + { + request->destroy(request); + return status; + } + status = this->build_idr_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_auth_payload(this, (id_payload_t*)id_payload, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_sa_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_tsi_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_tsr_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + + /* 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 DELETE_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; +} + + +/** + * 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 DELETE_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 DELETE_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 *request) +{ + policy_t *policy; + id_payload_t *new_id_payload; + identification_t *identification; + + policy = this->ike_sa->get_policy(this->ike_sa); + identification = policy->get_my_id(policy); + new_id_payload = id_payload_create_from_identification(TRUE, identification); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add ID payload to message"); + request->add_payload(request,(payload_t *) new_id_payload); + + *id_payload = new_id_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 *request) +{ + policy_t *policy; + id_payload_t *idr_payload; + identification_t *identification; + + policy = this->ike_sa->get_policy(this->ike_sa); + identification = policy->get_other_id(policy); + if (!identification->contains_wildcards(identification)) + { + idr_payload = id_payload_create_from_identification(FALSE, identification); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add IDr payload to message"); + request->add_payload(request,(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 *request) +{ + 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 DELETE_ME; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add AUTH payload to message"); + request->add_payload(request,(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 *request) +{ + 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(connection->get_my_host(connection), + connection->get_other_host(connection)); + 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 DELETE_ME; + } + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add SA payload to message"); + request->add_payload(request,(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 *request) +{ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + policy_t *policy; + + policy = this->ike_sa->get_policy(this->ike_sa); + ts_list = policy->get_my_traffic_selectors(policy); + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSi payload to message"); + request->add_payload(request,(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 *request) +{ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + policy_t *policy; + + policy = this->ike_sa->get_policy(this->ike_sa); + ts_list = policy->get_other_traffic_selectors(policy); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSr payload to message"); + request->add_payload(request,(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) +{ + 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 DELETE_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 DELETE_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 DELETE_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 DELETE_ME; + } + return FAILED; + } + 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 DELETE_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); + chunk_free(&(this->ike_sa_init_request_data)); + 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); + chunk_free(&(this->ike_sa_init_request_data)); + 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_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 new file mode 100644 index 000000000..0a43afad1 --- /dev/null +++ b/src/charon/sa/states/ike_sa_init_requested.h @@ -0,0 +1,68 @@ +/** + * @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 new file mode 100644 index 000000000..e40b0cf22 --- /dev/null +++ b/src/charon/sa/states/ike_sa_init_responded.c @@ -0,0 +1,695 @@ +/** + * @file ike_sa_init_responded.c + * + * @brief State of a IKE_SA after responding to an IKE_SA_INIT request + * + */ + +/* + * 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/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 response 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 *response, + id_payload_t **response_idr); + + /** + * 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 response 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 *response); + + /** + * 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 response 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* response); + + /** + * 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 response 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 *response); + + /** + * Sends a IKE_AUTH reply containing a notify payload. + * + * @param this calling object + * @param notify_payload payload to process + * @return + * - DELETE_ME if IKE_SA should be deleted + * - SUCCSS if processed successfull + */ + 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.get_state + */ +static status_t process_message(private_ike_sa_init_responded_t *this, message_t *request) +{ + id_payload_t *idi_request = NULL, *idr_request = NULL,*idr_response; + ts_payload_t *tsi_request = NULL, *tsr_request = NULL; + auth_payload_t *auth_request = NULL; + sa_payload_t *sa_request = NULL; + iterator_t *payloads; + message_t *response; + crypter_t *crypter; + signer_t *signer; + status_t status; + host_t *my_host, *other_host; + connection_t *connection; + + 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_responded", + 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 DELETE_ME; + } + else + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request decryption faild. 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 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: + { + /* TODO handle cert payloads */ + } + 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 DELETE_ME; + } + + /* 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) + { + response->destroy(response); + return status; + } + status = this->build_auth_payload(this, auth_request,idi_request, idr_response,response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_sa_payload(this, sa_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_ts_payload(this, TRUE, tsi_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->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 IKE_AUTH reply. Deleting IKE_SA"); + response->destroy(response); + return DELETE_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 DELETE_ME; + } + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + } + + /* create new state */ + this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa)); + this->destroy_after_state_change(this); + + connection = this->ike_sa->get_connection(this->ike_sa); + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + return SUCCESS; +} + +/** + * 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 *response,id_payload_t **response_idr) +{ + identification_t *other_id, *my_id = NULL; + connection_t *connection; + id_payload_t *idr_response; + + connection = this->ike_sa->get_connection(this->ike_sa); + + /* update adresses, as connection may contain wildcards, or wrong IDs */ + other_id = request_idi->get_identification(request_idi); + if (request_idr) + { + my_id = request_idr->get_identification(request_idr); + connection->update_my_id(connection, my_id); + } + else + { + my_id = connection->get_my_id(connection); + } + connection->update_other_id(connection, other_id); + + /* build new sa config */ + this->policy = charon->policies->get_policy(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)); + return DELETE_ME; + } + + /* 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_other_ts(this->policy, response->get_destination(response)); + + /* 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); + response->add_payload(response, (payload_t*)idr_response); + *response_idr = idr_response; + + 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 *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; + + /* get proposals from request */ + proposal_list = request->get_proposals(request); + if (proposal_list->get_count(proposal_list) == 0) + { + /* if the other side did not offer any proposals, we do not create child sa's */ + this->logger->log(this->logger, AUDIT, "IKE_AUH request did not contain any proposals. No CHILD_SA created"); + sa_response = sa_payload_create(); + response->add_payload(response, (payload_t*)sa_response); + proposal_list->destroy(proposal_list); + return SUCCESS; + } + + /* now select a proposal */ + 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) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any proposals we accept. Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER); + return DELETE_ME; + } + + /* 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); + + connection = this->ike_sa->get_connection(this->ike_sa); + this->child_sa = child_sa_create(connection->get_my_host(connection), + connection->get_other_host(connection)); + + 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"); + return DELETE_ME; + } + + /* create payload with selected propsal */ + sa_response = sa_payload_create_from_proposal(proposal); + response->add_payload(response, (payload_t*)sa_response); + proposal->destroy(proposal); + 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* response) +{ + 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 DELETE_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_SA"); + return DELETE_ME; + + } + + response->add_payload(response, (payload_t *)auth_reply); + 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* response) +{ + 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); + response->add_payload(response, (payload_t*)ts_response); + + /* cleanup */ + while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + ts_received->destroy(ts_received); + + return status; +} + +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_sa_payload = build_sa_payload; + this->build_auth_payload = build_auth_payload; + this->build_ts_payload = build_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_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 new file mode 100644 index 000000000..43aecf26f --- /dev/null +++ b/src/charon/sa/states/ike_sa_init_responded.h @@ -0,0 +1,73 @@ +/** + * @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 new file mode 100644 index 000000000..35d15235d --- /dev/null +++ b/src/charon/sa/states/initiator_init.c @@ -0,0 +1,360 @@ +/** + * @file initiator_init.c + * + * @brief Implementation 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. + */ + +#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 <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 request message_t object to add the SA payload + */ + void (*build_sa_payload) (private_initiator_init_t *this, message_t *request); + + /** + * Builds the KE payload for this state. + * + * @param this calling object + * @param request message_t object to add the KE payload + */ + void (*build_ke_payload) (private_initiator_init_t *this, message_t *request); + + /** + * Builds the NONCE payload for this state. + * + * @param this calling object + * @param request message_t object to add the NONCE payload + */ + status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *request); + + /** + * 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; + + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + my_id = connection->get_my_id(connection); + other_id = connection->get_other_id(connection); + + this->logger->log(this->logger, CONTROL, "Initiating connection between %s (%s) - %s (%s)", + my_id->get_string(my_id), my_host->get_address(my_host), + other_id->get_string(other_id), other_host->get_address(other_host)); + + this->ike_sa->set_connection(this->ike_sa, connection); + + /* get policy */ + policy = charon->policies->get_policy(charon->policies, my_id, other_id); + if (policy == NULL) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Could not get a policy for '%s - %s', aborting", + my_id->get_string(my_id), other_id->get_string(other_id)); + return DELETE_ME; + } + this->ike_sa->set_policy(this->ike_sa,policy); + + /* 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; + + if (dh_group == MODP_UNDEFINED) + { + this->logger->log(this->logger, AUDIT, "No DH group acceptable for initialization, Aborting"); + return DELETE_ME; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + this->diffie_hellman = diffie_hellman_create(dh_group); + 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 DELETE_ME; + } + + /* 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 DELETE_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 sate 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 *request) +{ + 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"); + request->add_payload(request, (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 *request) +{ + 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"); + request->add_payload(request, (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 *request) +{ + 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"); + request->add_payload(request, (payload_t *) nonce_payload); + return SUCCESS; +} + +/** + * 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; + + /* 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 new file mode 100644 index 000000000..6b4940a73 --- /dev/null +++ b/src/charon/sa/states/initiator_init.h @@ -0,0 +1,84 @@ +/** + * @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 + * - DELETE_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 + * - DELETE_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 new file mode 100644 index 000000000..10acf645c --- /dev/null +++ b/src/charon/sa/states/responder_init.c @@ -0,0 +1,568 @@ +/** + * @file responder_init.c + * + * @brief Implementation 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. + */ + +#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/notify_payload.h> +#include <crypto/diffie_hellman.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; + + /** + * Handles received SA payload and builds the SA payload for the response. + * + * @param this calling object + * @param sa_request The received SA payload + * @param response the SA payload is added to this response message_t object. + * @return + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_sa_payload) (private_responder_init_t *this,sa_payload_t *sa_request, message_t *response); + + /** + * Handles received KE payload and builds the KE payload for the response. + * + * @param this calling object + * @param ke_request The received KE payload + * @param response the KE payload is added to this response message_t object. + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_ke_payload) (private_responder_init_t *this,ke_payload_t *ke_request, message_t *response); + + /** + * Handles received NONCE payload and builds the NONCE payload for the response. + * + * @param this calling object + * @param nonce_request The received NONCE payload + * @param response the NONCE payload is added to this response message_t object. + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response); + + /** + * 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 DELETE_ME; + } + if (!message->get_request(message)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT responses not allowed state ike_sa_init_responded"); + return DELETE_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 DELETE_ME; + } + this->ike_sa->set_connection(this->ike_sa,connection); + + /* 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 DELETE_ME; + } + + 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; + } + } + 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 DELETE_ME; + } + + 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) + { + response->destroy(response); + return status; + } + + status = this->build_ke_payload(this, ke_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + + status = this->build_nonce_payload(this, nonce_request, response); + if (status != SUCCESS) + { + response->destroy(response); + 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->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 DELETE_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 DELETE_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; +} + +/** + * 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 *response) +{ + 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 DELETE_ME; + } + /* get selected DH group to force policy, this is very restrictive!? */ + this->proposal->get_algorithm(this->proposal, PROTO_IKE, 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"); + response->add_payload(response,(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 *response) +{ + 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_UNDEFINED) + { + this->logger->log(this->logger, AUDIT, "No diffie hellman group to select. Deleting IKE_SA"); + return DELETE_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 a 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 DELETE_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 DELETE_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"); + response->add_payload(response,(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 *response) +{ + 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"); + response->add_payload(response,(payload_t *) nonce_payload); + + return SUCCESS; +} + +/** + * 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) +{ + 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)); + + if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Notify reply not for IKE protocol."); + return FAILED; + } + switch (notify_message_type) + { + 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 sent nonce"); + chunk_free(&(this->sent_nonce)); + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy received nonce"); + chunk_free(&(this->received_nonce)); + + 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"); + + /* 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->destroy_after_state_change = destroy_after_state_change; + this->process_notify_payload = process_notify_payload; + + /* 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_UNDEFINED; + this->diffie_hellman = NULL; + this->proposal = NULL; + + return &(this->public); +} diff --git a/src/charon/sa/states/responder_init.h b/src/charon/sa/states/responder_init.h new file mode 100644 index 000000000..c8ba73ea3 --- /dev/null +++ b/src/charon/sa/states/responder_init.h @@ -0,0 +1,68 @@ +/** + * @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 new file mode 100644 index 000000000..595f5abbb --- /dev/null +++ b/src/charon/sa/states/state.c @@ -0,0 +1,37 @@ +/** + * @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"}, + {MAPPING_END, NULL} +}; diff --git a/src/charon/sa/states/state.h b/src/charon/sa/states/state.h new file mode 100644 index 000000000..c93068d35 --- /dev/null +++ b/src/charon/sa/states/state.h @@ -0,0 +1,166 @@ +/** + * @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...) + * + * @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 = 2, + + /** + * @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 = 3, + + /** + * @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 = 4, + + /** + * @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 = 5, + + /** + * @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 = 6 +}; + + +/** + * 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. + * + * It's the responsibility of the state_t object to parse the body of the message and to process each + * payload. + * + * Needed Configurations and transform objects can be retrieved over an internal stored protected_ike_sa_t object + * which is passed to a state_t object when creating it (see different constructors). + * + * The following states are supported and implemented: + * - INITIATOR_INIT: implemented in initiator_init_t + * - RESPONDER_INIT: implemented in responder_init_t + * - IKE_SA_INIT_REQUESTED: implemented in ike_sa_init_requested_t + * - IKE_SA_INIT_RESPONDED: implemented in ike_sa_init_responded_t + * - IKE_AUTH_REQUESTED: implemented in ike_auth_requested_t + * - IKE_SA_ESTABLISHED: implemented in ike_sa_established_t + * + * @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() + * + * @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 + * - DELETE_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_*/ |