diff options
Diffstat (limited to 'src/charon/sa')
63 files changed, 3998 insertions, 2528 deletions
diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c index ea8a16279..13586a23e 100644 --- a/src/charon/sa/authenticators/authenticator.c +++ b/src/charon/sa/authenticators/authenticator.c @@ -75,7 +75,7 @@ authenticator_t *authenticator_create_verifier( chunk_t received_init, chunk_t sent_init) { auth_payload_t *auth_payload; - + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); if (auth_payload == NULL) { diff --git a/src/charon/sa/authenticators/authenticator.h b/src/charon/sa/authenticators/authenticator.h index c60881629..fff91ed34 100644 --- a/src/charon/sa/authenticators/authenticator.h +++ b/src/charon/sa/authenticators/authenticator.h @@ -36,34 +36,34 @@ typedef struct authenticator_t authenticator_t; */ enum auth_method_t { /** - * Computed as specified in section 2.15 of RFC using + * Computed as specified in section 2.15 of RFC using * an RSA private key over a PKCS#1 padded hash. */ AUTH_RSA = 1, - + /** - * Computed as specified in section 2.15 of RFC using the - * shared key associated with the identity in the ID payload + * Computed as specified in section 2.15 of RFC using the + * shared key associated with the identity in the ID payload * and the negotiated prf function */ AUTH_PSK = 2, - + /** - * Computed as specified in section 2.15 of RFC using a + * Computed as specified in section 2.15 of RFC using a * DSS private key over a SHA-1 hash. */ AUTH_DSS = 3, - + /** * ECDSA with SHA-256 on the P-256 curve as specified in RFC 4754 */ AUTH_ECDSA_256 = 9, - + /** * ECDSA with SHA-384 on the P-384 curve as specified in RFC 4754 */ AUTH_ECDSA_384 = 10, - + /** * ECDSA with SHA-512 on the P-521 curve as specified in RFC 4754 */ @@ -115,7 +115,7 @@ struct authenticator_t { * - NEED_MORE if another exchange required */ status_t (*process)(authenticator_t *this, message_t *message); - + /** * Attach authentication data to an outgoing message. * @@ -126,7 +126,17 @@ struct authenticator_t { * - NEED_MORE if another exchange required */ status_t (*build)(authenticator_t *this, message_t *message); - + + /** + * Check if the authenticator is capable of mutual authentication. + * + * Some authenticator authenticate both peers, e.g. EAP. To support + * mutual authentication with only a single authenticator (EAP-only + * authentication), it must be mutual. This method is invoked in ike_auth + * to check if the given authenticator is capable of doing so. + */ + bool (*is_mutual)(authenticator_t *this); + /** * Destroy authenticator instance. */ @@ -151,7 +161,7 @@ authenticator_t *authenticator_create_builder( /** * Create an authenticator to verify signatures. - * + * * @param ike_sa associated ike_sa * @param message message containing authentication data * @param received_nonce nonce received in IKE_SA_INIT diff --git a/src/charon/sa/authenticators/eap/eap_manager.c b/src/charon/sa/authenticators/eap/eap_manager.c index 24a4fd6ed..f795183f0 100644 --- a/src/charon/sa/authenticators/eap/eap_manager.c +++ b/src/charon/sa/authenticators/eap/eap_manager.c @@ -16,7 +16,7 @@ #include "eap_manager.h" #include <utils/linked_list.h> -#include <utils/mutex.h> +#include <threading/rwlock.h> typedef struct private_eap_manager_t private_eap_manager_t; typedef struct eap_entry_t eap_entry_t; @@ -25,22 +25,22 @@ typedef struct eap_entry_t eap_entry_t; * EAP constructor entry */ struct eap_entry_t { - + /** * EAP method type, vendor specific if vendor is set */ eap_type_t type; - + /** * vendor ID, 0 for default EAP methods */ u_int32_t vendor; - + /** * Role of the method returned by the constructor, EAP_SERVER or EAP_PEER */ eap_role_t role; - + /** * constructor function to create instance */ @@ -56,12 +56,12 @@ struct private_eap_manager_t { * public functions */ eap_manager_t public; - + /** * list of eap_entry_t's */ linked_list_t *methods; - + /** * rwlock to lock methods */ @@ -76,7 +76,7 @@ static void add_method(private_eap_manager_t *this, eap_type_t type, eap_constructor_t constructor) { eap_entry_t *entry = malloc_thing(eap_entry_t); - + entry->type = type; entry->vendor = vendor; entry->role = role; @@ -94,7 +94,7 @@ static void remove_method(private_eap_manager_t *this, eap_constructor_t constru { enumerator_t *enumerator; eap_entry_t *entry; - + this->lock->write_lock(this->lock); enumerator = this->methods->create_enumerator(this->methods); while (enumerator->enumerate(enumerator, &entry)) @@ -120,7 +120,7 @@ static eap_method_t* create_instance(private_eap_manager_t *this, enumerator_t *enumerator; eap_entry_t *entry; eap_method_t *method = NULL; - + this->lock->read_lock(this->lock); enumerator = this->methods->create_enumerator(this->methods); while (enumerator->enumerate(enumerator, &entry)) @@ -156,15 +156,15 @@ static void destroy(private_eap_manager_t *this) eap_manager_t *eap_manager_create() { private_eap_manager_t *this = malloc_thing(private_eap_manager_t); - + this->public.add_method = (void(*)(eap_manager_t*, eap_type_t type, u_int32_t vendor, eap_role_t role, eap_constructor_t constructor))add_method; this->public.remove_method = (void(*)(eap_manager_t*, eap_constructor_t constructor))remove_method; this->public.create_instance = (eap_method_t*(*)(eap_manager_t*, eap_type_t type, u_int32_t vendor, eap_role_t role, identification_t*,identification_t*))create_instance; this->public.destroy = (void(*)(eap_manager_t*))destroy; - + this->methods = linked_list_create(); this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - + return &this->public; } diff --git a/src/charon/sa/authenticators/eap/eap_manager.h b/src/charon/sa/authenticators/eap/eap_manager.h index 667c54a8e..0333fb6da 100644 --- a/src/charon/sa/authenticators/eap/eap_manager.h +++ b/src/charon/sa/authenticators/eap/eap_manager.h @@ -45,14 +45,14 @@ struct eap_manager_t { */ void (*add_method)(eap_manager_t *this, eap_type_t type, u_int32_t vendor, eap_role_t role, eap_constructor_t constructor); - + /** * Unregister a EAP method implementation using it's constructor. * * @param constructor constructor function to remove, as added in add_method */ void (*remove_method)(eap_manager_t *this, eap_constructor_t constructor); - + /** * Create a new EAP method instance. * @@ -67,11 +67,11 @@ struct eap_manager_t { u_int32_t vendor, eap_role_t role, identification_t *server, identification_t *peer); - + /** - * Destroy a eap_manager instance. - */ - void (*destroy)(eap_manager_t *this); + * Destroy a eap_manager instance. + */ + void (*destroy)(eap_manager_t *this); }; /** diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c index 1d1900301..91fa5305f 100644 --- a/src/charon/sa/authenticators/eap/eap_method.c +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -34,6 +34,25 @@ ENUM_NEXT(eap_type_names, EAP_RADIUS, EAP_EXPERIMENTAL, EAP_MSCHAPV2, "EAP_EXPERIMENTAL"); ENUM_END(eap_type_names, EAP_EXPERIMENTAL); +ENUM_BEGIN(eap_type_short_names, EAP_IDENTITY, EAP_GTC, + "ID", + "NTF", + "NAK", + "MD5", + "OTP", + "GTC"); +ENUM_NEXT(eap_type_short_names, EAP_SIM, EAP_SIM, EAP_GTC, + "SIM"); +ENUM_NEXT(eap_type_short_names, EAP_AKA, EAP_AKA, EAP_SIM, + "AKA"); +ENUM_NEXT(eap_type_short_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA, + "MSCHAPV2"); +ENUM_NEXT(eap_type_short_names, EAP_RADIUS, EAP_EXPERIMENTAL, EAP_MSCHAPV2, + "RAD", + "EXP", + "XP"); +ENUM_END(eap_type_short_names, EAP_EXPERIMENTAL); + /* * See header */ @@ -53,7 +72,7 @@ eap_type_t eap_type_from_string(char *name) {"mschapv2", EAP_MSCHAPV2}, {"radius", EAP_RADIUS}, }; - + for (i = 0; i < countof(types); i++) { if (strcaseeq(name, types[i].name)) @@ -71,6 +90,13 @@ ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, "EAP_FAILURE", ); +ENUM(eap_code_short_names, EAP_REQUEST, EAP_FAILURE, + "REQ", + "RES", + "SUCC", + "FAIL", +); + ENUM(eap_role_names, EAP_SERVER, EAP_PEER, "EAP_SERVER", "EAP_PEER", diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h index 578b89e96..4cab84535 100644 --- a/src/charon/sa/authenticators/eap/eap_method.h +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -67,6 +67,11 @@ enum eap_type_t { extern enum_name_t *eap_type_names; /** + * short string enum names for eap_type_t. + */ +extern enum_name_t *eap_type_short_names; + +/** * Lookup the EAP method type from a string. * * @param name EAP method name (such as "md5", "aka") @@ -90,6 +95,11 @@ enum eap_code_t { extern enum_name_t *eap_code_names; /** + * short string enum names for eap_code_t. + */ +extern enum_name_t *eap_code_short_names; + +/** * Interface of an EAP method for server and client side. * * An EAP method initiates an EAP exchange and processes requests and @@ -107,7 +117,7 @@ extern enum_name_t *eap_code_names; * EAP-Identity exchange always uses identifier 0. */ struct eap_method_t { - + /** * Initiate the EAP exchange. * @@ -121,7 +131,7 @@ struct eap_method_t { * - FAILED, if unable to create eap request payload */ status_t (*initiate) (eap_method_t *this, eap_payload_t **out); - + /** * Process a received EAP message. * @@ -136,7 +146,7 @@ struct eap_method_t { */ status_t (*process) (eap_method_t *this, eap_payload_t *in, eap_payload_t **out); - + /** * Get the EAP type implemented in this method. * @@ -144,17 +154,17 @@ struct eap_method_t { * @return type of the EAP method */ eap_type_t (*get_type) (eap_method_t *this, u_int32_t *vendor); - + /** * Check if this EAP method authenticates the server. * - * Some EAP methods provide mutual authentication and + * Some EAP methods provide mutual authentication and * allow authentication using only EAP, if the peer supports it. * * @return TRUE if methods provides mutual authentication */ bool (*is_mutual) (eap_method_t *this); - + /** * Get the MSK established by this EAP method. * @@ -167,7 +177,7 @@ struct eap_method_t { * - FAILED, if MSK not established (yet) */ status_t (*get_msk) (eap_method_t *this, chunk_t *msk); - + /** * Destroys a eap_method_t object. */ diff --git a/src/charon/sa/authenticators/eap/sim_manager.c b/src/charon/sa/authenticators/eap/sim_manager.c index 51cd4fb3f..5060a3147 100644 --- a/src/charon/sa/authenticators/eap/sim_manager.c +++ b/src/charon/sa/authenticators/eap/sim_manager.c @@ -15,6 +15,7 @@ #include "sim_manager.h" +#include <daemon.h> #include <utils/linked_list.h> typedef struct private_sim_manager_t private_sim_manager_t; @@ -23,21 +24,26 @@ typedef struct private_sim_manager_t private_sim_manager_t; * Private data of an sim_manager_t object. */ struct private_sim_manager_t { - + /** * Public sim_manager_t interface. */ sim_manager_t public; - + /** * list of added cards */ linked_list_t *cards; - + /** * list of added provider */ - linked_list_t *provider; + linked_list_t *providers; + + /** + * list of added hooks + */ + linked_list_t *hooks; }; /** @@ -57,37 +63,431 @@ static void remove_card(private_sim_manager_t *this, sim_card_t *card) } /** - * Implementation of sim_manager_t.create_card_enumerator + * Implementation of sim_manager_t.card_get_triplet + */ +static bool card_get_triplet(private_sim_manager_t *this, identification_t *id, + char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], + char kc[SIM_KC_LEN]) +{ + enumerator_t *enumerator; + sim_card_t *card; + int tried = 0; + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + if (card->get_triplet(card, id, rand, sres, kc)) + { + enumerator->destroy(enumerator); + return TRUE; + } + tried++; + } + enumerator->destroy(enumerator); + DBG1(DBG_IKE, "tried %d SIM cards, but none has triplets for '%Y'", + tried, id); + return FALSE; +} + +/** + * Implementation of sim_manager_t.card_get_quintuplet + */ +static status_t card_get_quintuplet(private_sim_manager_t *this, + identification_t *id, char rand[AKA_RAND_LEN], + char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN], + char ik[AKA_IK_LEN], char res[AKA_RES_MAX], + int *res_len) +{ + enumerator_t *enumerator; + sim_card_t *card; + status_t status = NOT_FOUND; + int tried = 0; + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + status = card->get_quintuplet(card, id, rand, autn, ck, ik, res, res_len); + switch (status) + { /* try next on error, but not on INVALID_STATE */ + case SUCCESS: + case INVALID_STATE: + enumerator->destroy(enumerator); + return status; + case NOT_SUPPORTED: + case FAILED: + default: + tried++; + continue; + } + } + enumerator->destroy(enumerator); + DBG1(DBG_IKE, "tried %d SIM cards, but none has quintuplets for '%Y'", + tried, id); + return status; +} + +/** + * Implementation of sim_manager_t.card_resync + */ +static bool card_resync(private_sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]) +{ + enumerator_t *enumerator; + sim_card_t *card; + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + if (card->resync(card, id, rand, auts)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + return FALSE; +} + +/** + * Implementation of sim_manager_t.card_set_pseudonym + */ +static void card_set_pseudonym(private_sim_manager_t *this, + identification_t *id, identification_t *pseudonym) +{ + enumerator_t *enumerator; + sim_card_t *card; + + DBG1(DBG_IKE, "storing pseudonym '%Y' for '%Y'", pseudonym, id); + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + card->set_pseudonym(card, id, pseudonym); + } + enumerator->destroy(enumerator); +} + +/** + * Implementation of sim_manager_t.card_get_pseudonym + */ +static identification_t* card_get_pseudonym(private_sim_manager_t *this, + identification_t *id) +{ + enumerator_t *enumerator; + sim_card_t *card; + identification_t *pseudonym = NULL; + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + pseudonym = card->get_pseudonym(card, id); + if (pseudonym) + { + DBG1(DBG_IKE, "using stored pseudonym identity '%Y' " + "instead of '%Y'", pseudonym, id); + break; + } + } + enumerator->destroy(enumerator); + return pseudonym; +} + +/** + * Implementation of sim_manager_t.card_set_reauth */ -static enumerator_t* create_card_enumerator(private_sim_manager_t *this) +static void card_set_reauth(private_sim_manager_t *this, identification_t *id, + identification_t *next, char mk[HASH_SIZE_SHA1], + u_int16_t counter) { - return this->cards->create_enumerator(this->cards); + enumerator_t *enumerator; + sim_card_t *card; + + DBG1(DBG_IKE, "storing next reauthentication identity '%Y' for '%Y'", + next, id); + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + card->set_reauth(card, id, next, mk, counter); + } + enumerator->destroy(enumerator); +} + +/** + * Implementation of sim_manager_t.card_get_reauth + */ +static identification_t* card_get_reauth(private_sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1], + u_int16_t *counter) +{ + enumerator_t *enumerator; + sim_card_t *card; + identification_t *reauth = NULL; + + enumerator = this->cards->create_enumerator(this->cards); + while (enumerator->enumerate(enumerator, &card)) + { + reauth = card->get_reauth(card, id, mk, counter); + if (reauth) + { + DBG1(DBG_IKE, "using stored reauthentication identity '%Y' " + "instead of '%Y'", reauth, id); + break; + } + } + enumerator->destroy(enumerator); + return reauth; } /** * Implementation of sim_manager_t.add_provider */ -static void add_provider(private_sim_manager_t *this, - sim_provider_t *provider) +static void add_provider(private_sim_manager_t *this, sim_provider_t *provider) { - this->provider->insert_last(this->provider, provider); + this->providers->insert_last(this->providers, provider); } /** * Implementation of sim_manager_t.remove_provider */ static void remove_provider(private_sim_manager_t *this, - sim_provider_t *provider) + sim_provider_t *provider) { - this->provider->remove(this->provider, provider, NULL); + this->providers->remove(this->providers, provider, NULL); } /** - * Implementation of sim_manager_t.create_provider_enumerator + * Implementation of sim_manager_t.provider_get_triplet */ -static enumerator_t* create_provider_enumerator(private_sim_manager_t *this) +static bool provider_get_triplet(private_sim_manager_t *this, + identification_t *id, char rand[SIM_RAND_LEN], + char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]) { - return this->provider->create_enumerator(this->provider); + enumerator_t *enumerator; + sim_provider_t *provider; + int tried = 0; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + if (provider->get_triplet(provider, id, rand, sres, kc)) + { + enumerator->destroy(enumerator); + return TRUE; + } + tried++; + } + enumerator->destroy(enumerator); + DBG1(DBG_IKE, "tried %d SIM providers, but none had a triplet for '%Y'", + tried, id); + return FALSE; +} + +/** + * Implementation of sim_manager_t.provider_get_quintuplet + */ +static bool provider_get_quintuplet(private_sim_manager_t *this, + identification_t *id, char rand[AKA_RAND_LEN], + char xres[AKA_RES_MAX], int *xres_len, + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char autn[AKA_AUTN_LEN]) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + int tried = 0; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + if (provider->get_quintuplet(provider, id, rand, xres, xres_len, + ck, ik, autn)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + DBG1(DBG_IKE, "tried %d SIM providers, but none had a quintuplet for '%Y'", + tried, id); + return FALSE; +} + +/** + * Implementation of sim_manager_t.provider_resync + */ +static bool provider_resync(private_sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + if (provider->resync(provider, id, rand, auts)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + return FALSE; +} + +/** + * Implementation of sim_manager_t.provider_is_pseudonym + */ +static identification_t* provider_is_pseudonym(private_sim_manager_t *this, + identification_t *id) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + identification_t *permanent = NULL; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + permanent = provider->is_pseudonym(provider, id); + if (permanent) + { + DBG1(DBG_IKE, "received pseudonym identity '%Y' " + "mapping to '%Y'", id, permanent); + break; + } + } + enumerator->destroy(enumerator); + return permanent; +} + +/** + * Implementation of sim_manager_t.provider_gen_pseudonym + */ +static identification_t* provider_gen_pseudonym(private_sim_manager_t *this, + identification_t *id) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + identification_t *pseudonym = NULL; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + pseudonym = provider->gen_pseudonym(provider, id); + if (pseudonym) + { + DBG1(DBG_IKE, "proposing new pseudonym '%Y'", pseudonym); + break; + } + } + enumerator->destroy(enumerator); + return pseudonym; +} + +/** + * Implementation of sim_manager_t.provider_is_reauth + */ +static identification_t* provider_is_reauth(private_sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1], + u_int16_t *counter) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + identification_t *permanent = NULL; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + permanent = provider->is_reauth(provider, id, mk, counter); + if (permanent) + { + DBG1(DBG_IKE, "received reauthentication identity '%Y' " + "mapping to '%Y'", id, permanent); + break; + } + } + enumerator->destroy(enumerator); + return permanent; +} + +/** + * Implementation of sim_manager_t.provider_gen_reauth + */ +static identification_t* provider_gen_reauth(private_sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1]) +{ + enumerator_t *enumerator; + sim_provider_t *provider; + identification_t *reauth = NULL; + + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + reauth = provider->gen_reauth(provider, id, mk); + if (reauth) + { + DBG1(DBG_IKE, "proposing new reauthentication identity '%Y'", reauth); + break; + } + } + enumerator->destroy(enumerator); + return reauth; +} + +/** + * Implementation of sim_manager_t.add_hooks + */ +static void add_hooks(private_sim_manager_t *this, sim_hooks_t *hooks) +{ + this->hooks->insert_last(this->hooks, hooks); +} + +/** + * Implementation of sim_manager_t.remove_hooks + */ +static void remove_hooks(private_sim_manager_t *this, sim_hooks_t *hooks) +{ + this->hooks->remove(this->hooks, hooks, NULL); +} + +/** + * Implementation of sim_manager_t.attribute_hook + */ +static bool attribute_hook(private_sim_manager_t *this, eap_code_t code, + eap_type_t type, u_int8_t subtype, + u_int8_t attribute, chunk_t data) +{ + enumerator_t *enumerator; + sim_hooks_t *hooks; + bool filter = FALSE; + + enumerator = this->hooks->create_enumerator(this->hooks); + while (enumerator->enumerate(enumerator, &hooks)) + { + if (hooks->attribute(hooks, code, type, subtype, attribute, data)) + { + filter = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return filter; +} + +/** + * Implementation of sim_manager_t.key_hook + */ +static void key_hook(private_sim_manager_t *this, + chunk_t k_encr, chunk_t k_auth) +{ + enumerator_t *enumerator; + sim_hooks_t *hooks; + + enumerator = this->hooks->create_enumerator(this->hooks); + while (enumerator->enumerate(enumerator, &hooks)) + { + hooks->keys(hooks, k_encr, k_auth); + } + enumerator->destroy(enumerator); } /** @@ -96,7 +496,8 @@ static enumerator_t* create_provider_enumerator(private_sim_manager_t *this) static void destroy(private_sim_manager_t *this) { this->cards->destroy(this->cards); - this->provider->destroy(this->provider); + this->providers->destroy(this->providers); + this->hooks->destroy(this->hooks); free(this); } @@ -106,18 +507,35 @@ static void destroy(private_sim_manager_t *this) sim_manager_t *sim_manager_create() { private_sim_manager_t *this = malloc_thing(private_sim_manager_t); - + this->public.add_card = (void(*)(sim_manager_t*, sim_card_t *card))add_card; this->public.remove_card = (void(*)(sim_manager_t*, sim_card_t *card))remove_card; - this->public.create_card_enumerator = (enumerator_t*(*)(sim_manager_t*))create_card_enumerator; + this->public.card_get_triplet = (bool(*)(sim_manager_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))card_get_triplet; + this->public.card_get_quintuplet = (status_t(*)(sim_manager_t*, identification_t *id, char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len))card_get_quintuplet; + this->public.card_resync = (bool(*)(sim_manager_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))card_resync; + this->public.card_set_pseudonym = (void(*)(sim_manager_t*, identification_t *id, identification_t *pseudonym))card_set_pseudonym; + this->public.card_get_pseudonym = (identification_t*(*)(sim_manager_t*, identification_t *id))card_get_pseudonym; + this->public.card_set_reauth = (void(*)(sim_manager_t*, identification_t *id, identification_t *next, char mk[HASH_SIZE_SHA1], u_int16_t counter))card_set_reauth; + this->public.card_get_reauth = (identification_t*(*)(sim_manager_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))card_get_reauth; this->public.add_provider = (void(*)(sim_manager_t*, sim_provider_t *provider))add_provider; this->public.remove_provider = (void(*)(sim_manager_t*, sim_provider_t *provider))remove_provider; - this->public.create_provider_enumerator = (enumerator_t*(*)(sim_manager_t*))create_provider_enumerator; + this->public.provider_get_triplet = (bool(*)(sim_manager_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))provider_get_triplet; + this->public.provider_get_quintuplet = (bool(*)(sim_manager_t*, identification_t *id, char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len, char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN]))provider_get_quintuplet; + this->public.provider_resync = (bool(*)(sim_manager_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))provider_resync; + this->public.provider_is_pseudonym = (identification_t*(*)(sim_manager_t*, identification_t *id))provider_is_pseudonym; + this->public.provider_gen_pseudonym = (identification_t*(*)(sim_manager_t*, identification_t *id))provider_gen_pseudonym; + this->public.provider_is_reauth = (identification_t*(*)(sim_manager_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))provider_is_reauth; + this->public.provider_gen_reauth = (identification_t*(*)(sim_manager_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))provider_gen_reauth; + this->public.add_hooks = (void(*)(sim_manager_t*, sim_hooks_t *hooks))add_hooks; + this->public.remove_hooks = (void(*)(sim_manager_t*, sim_hooks_t *hooks))remove_hooks; + this->public.attribute_hook = (bool(*)(sim_manager_t*, eap_code_t code, eap_type_t type, u_int8_t subtype, u_int8_t attribute, chunk_t data))attribute_hook; + this->public.key_hook = (void(*)(sim_manager_t*, chunk_t k_encr, chunk_t k_auth))key_hook; this->public.destroy = (void(*)(sim_manager_t*))destroy; - + this->cards = linked_list_create(); - this->provider = linked_list_create(); - + this->providers = linked_list_create(); + this->hooks = linked_list_create(); + return &this->public; } diff --git a/src/charon/sa/authenticators/eap/sim_manager.h b/src/charon/sa/authenticators/eap/sim_manager.h index 3c6d66dfe..49d27cbaa 100644 --- a/src/charon/sa/authenticators/eap/sim_manager.h +++ b/src/charon/sa/authenticators/eap/sim_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -21,105 +21,484 @@ #ifndef SIM_MANAGER_H_ #define SIM_MANAGER_H_ +#include <crypto/hashers/hasher.h> #include <utils/identification.h> #include <utils/enumerator.h> +#include <sa/authenticators/eap/eap_method.h> typedef struct sim_manager_t sim_manager_t; typedef struct sim_card_t sim_card_t; typedef struct sim_provider_t sim_provider_t; +typedef struct sim_hooks_t sim_hooks_t; + +#define SIM_RAND_LEN 16 +#define SIM_SRES_LEN 4 +#define SIM_KC_LEN 8 + +#define AKA_RAND_LEN 16 +#define AKA_RES_MAX 16 +#define AKA_CK_LEN 16 +#define AKA_IK_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 /** - * Interface for a SIM card (used as EAP client). + * Interface for a (U)SIM card (used as EAP client). + * + * The SIM card completes triplets/quintuplets requested in a challenge + * received from the server. + * An implementation supporting only one of SIM/AKA authentication may + * implement the other methods with return_false()/return NOT_SUPPORTED/NULL. */ struct sim_card_t { /** - * Get the identity of a SIM card. + * Calculate SRES/KC from a RAND for SIM authentication. + * + * @param id permanent identity to get a triplet for + * @param rand RAND input buffer, fixed size 16 bytes + * @param sres SRES output buffer, fixed size 4 byte + * @param kc KC output buffer, fixed size 8 bytes + * @return TRUE if SRES/KC calculated, FALSE on error/wrong identity + */ + bool (*get_triplet)(sim_card_t *this, identification_t *id, + char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], + char kc[SIM_KC_LEN]); + + /** + * Calculate CK/IK/RES from RAND/AUTN for AKA authentication. + * + * If the received sequence number (in autn) is out of sync, INVALID_STATE + * is returned. + * The RES value is the only one with variable length. Pass a buffer + * of at least AKA_RES_MAX, the actual number of bytes is written to the + * res_len value. While the standard would allow any bit length between + * 32 and 128 bits, we support only full bytes for now. + * + * @param id permanent identity to request quintuplet for + * @param rand random value rand + * @param autn authentication token autn + * @param ck buffer receiving encryption key ck + * @param ik buffer receiving integrity key ik + * @param res buffer receiving authentication result res + * @param res_len nubmer of bytes written to res buffer + * @return SUCCESS, FAILED, or INVALID_STATE if out of sync + */ + status_t (*get_quintuplet)(sim_card_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char res[AKA_RES_MAX], int *res_len); + + /** + * Calculate AUTS from RAND for AKA resynchronization. + * + * @param id permanent identity to request quintuplet for + * @param rand random value rand + * @param auts resynchronization parameter auts + * @return TRUE if parameter generated successfully + */ + bool (*resync)(sim_card_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); + + /** + * Set the pseudonym to use for next authentication. + * + * @param id permanent identity of the peer + * @param pseudonym pseudonym identity received from the server + */ + void (*set_pseudonym)(sim_card_t *this, identification_t *id, + identification_t *pseudonym); + + /** + * Get the pseudonym previously stored via set_pseudonym(). * - * The returned identity owned by the sim_card and not destroyed outside. - * The SIM card may return ID_ANY if it does not support/use an IMSI. + * @param id permanent identity of the peer + * @return associated pseudonym identity, NULL if none stored + */ + identification_t* (*get_pseudonym)(sim_card_t *this, identification_t *id); + + /** + * Store parameters to use for the next fast reauthentication. * - * @return identity + * @param id permanent identity of the peer + * @param next next fast reauthentication identity to use + * @param mk master key MK to store for reauthentication + * @param counter counter value to store, host order */ - identification_t* (*get_imsi)(sim_card_t *this); - + void (*set_reauth)(sim_card_t *this, identification_t *id, + identification_t *next, char mk[HASH_SIZE_SHA1], + u_int16_t counter); + /** - * Calculate SRES/KC from a RAND. + * Retrieve parameters for fast reauthentication stored via set_reauth(). * - * @param rand RAND input buffer, fixed size 16 bytes - * @param sres SRES output buffer, fixed size 4 byte - * @param kc KC output buffer, fixed size 8 bytes - * @return TRUE if SRES/KC calculated, FALSE on error + * @param id permanent identity of the peer + * @param mk buffer receiving master key MK + * @param counter pointer receiving counter value, in host order + * @return fast reauthentication identity, NULL if not found */ - bool (*get_triplet)(sim_card_t *this, - char rand[16], char sres[4], char kc[8]); + identification_t* (*get_reauth)(sim_card_t *this, identification_t *id, + char mk[HASH_SIZE_SHA1], u_int16_t *counter); }; /** - * Interface for a triplet provider (used as EAP server). + * Interface for a triplet/quintuplet provider (used as EAP server). + * + * A SIM provider hands out triplets for SIM authentication and quintuplets + * for AKA authentication. Multiple SIM provider instances can serve as + * authentication backend to authenticate clients using SIM/AKA. + * An implementation supporting only one of SIM/AKA authentication may + * implement the other methods with return_false(). */ struct sim_provider_t { - + /** - * Get a single triplet to authenticate a EAP client. + * Create a challenge for SIM authentication. * - * @param imsi client identity - * @param rand RAND output buffer, fixed size 16 bytes - * @param sres SRES output buffer, fixed size 4 byte - * @param kc KC output buffer, fixed size 8 bytes - * @return TRUE if triplet received, FALSE otherwise + * @param id permanent identity of peer to gen triplet for + * @param rand RAND output buffer, fixed size 16 bytes + * @param sres SRES output buffer, fixed size 4 byte + * @param kc KC output buffer, fixed size 8 bytes + * @return TRUE if triplet received, FALSE otherwise */ - bool (*get_triplet)(sim_provider_t *this, identification_t *imsi, - char rand[16], char sres[4], char kc[8]); + bool (*get_triplet)(sim_provider_t *this, identification_t *id, + char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], + char kc[SIM_KC_LEN]); + + /** + * Create a challenge for AKA authentication. + * + * The XRES value is the only one with variable length. Pass a buffer + * of at least AKA_RES_MAX, the actual number of bytes is written to the + * xres_len value. While the standard would allow any bit length between + * 32 and 128 bits, we support only full bytes for now. + * + * @param id permanent identity of peer to create challenge for + * @param rand buffer receiving random value rand + * @param xres buffer receiving expected authentication result xres + * @param xres_len nubmer of bytes written to xres buffer + * @param ck buffer receiving encryption key ck + * @param ik buffer receiving integrity key ik + * @param autn authentication token autn + * @return TRUE if quintuplet generated successfully + */ + bool (*get_quintuplet)(sim_provider_t *this, identification_t *id, + char rand[AKA_RAND_LEN], + char xres[AKA_RES_MAX], int *xres_len, + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char autn[AKA_AUTN_LEN]); + + /** + * Process AKA resynchroniusation request of a peer. + * + * @param id permanent identity of peer requesting resynchronisation + * @param rand random value rand + * @param auts synchronization parameter auts + * @return TRUE if resynchronized successfully + */ + bool (*resync)(sim_provider_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); + + /** + * Check if peer uses a pseudonym, get permanent identity. + * + * @param id pseudonym identity candidate + * @return permanent identity, NULL if id not a pseudonym + */ + identification_t* (*is_pseudonym)(sim_provider_t *this, + identification_t *id); + + /** + * Generate a pseudonym identitiy for a given peer identity. + * + * @param id permanent identity to generate a pseudonym for + * @return generated pseudonym, NULL to not use a pseudonym identity + */ + identification_t* (*gen_pseudonym)(sim_provider_t *this, + identification_t *id); + + /** + * Check if peer uses reauthentication, retrieve reauth parameters. + * + * @param id reauthentication identity (candidate) + * @param mk buffer receiving master key MK + * @param counter pointer receiving current counter value, host order + * @return permanent identity, NULL if id not a reauth identity + */ + identification_t* (*is_reauth)(sim_provider_t *this, identification_t *id, + char mk[HASH_SIZE_SHA1], u_int16_t *counter); + + /** + * Generate a fast reauthentication identity, associated to a master key. + * + * @param id permanent peer identity + * @param mk master key to store along with generated identity + * @return fast reauthentication identity, NULL to not use reauth + */ + identification_t* (*gen_reauth)(sim_provider_t *this, identification_t *id, + char mk[HASH_SIZE_SHA1]); }; /** - * The EAP-SIM manager handles multiple SIM cards and providers. + * Additional hooks invoked during EAP-SIM/AKA message processing. + */ +struct sim_hooks_t { + + /** + * SIM/AKA attribute parsing hook. + * + * @param code code of EAP message the attribute was parsed from + * @param type EAP method, SIM or AKA + * @param subtye method specific subtype + * @param attribute parsed SIM/AKA attribute type + * @param data attribute data + * @return TRUE to filter out attribute from further processing + */ + bool (*attribute)(sim_hooks_t *this, eap_code_t code, eap_type_t type, + u_int8_t subtype, u_int8_t attribute, chunk_t data); + + /** + * SIM/AKA encryption/authentication key hooks. + * + * @param k_encr derived SIM/AKA encryption key k_encr + * @param k_auth derived SIM/AKA authentication key k_auth + */ + void (*keys)(sim_hooks_t *this, chunk_t k_encr, chunk_t k_auth); +}; + +/** + * The SIM manager handles multiple (U)SIM cards/providers and hooks. */ struct sim_manager_t { - + /** * Register a SIM card (client) at the manager. * * @param card sim card to register */ void (*add_card)(sim_manager_t *this, sim_card_t *card); - + /** * Unregister a previously registered card from the manager. * * @param card sim card to unregister */ void (*remove_card)(sim_manager_t *this, sim_card_t *card); - + + /** + * Calculate SIM triplets on one of the registered SIM cards. + * + * @param id permanent identity to get a triplet for + * @param rand RAND input buffer, fixed size 16 bytes + * @param sres SRES output buffer, fixed size 4 byte + * @param kc KC output buffer, fixed size 8 bytes + * @return TRUE if calculated, FALSE if no matching card found + */ + bool (*card_get_triplet)(sim_manager_t *this, identification_t *id, + char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], + char kc[SIM_KC_LEN]); + + /** + * Calculate AKA quitpulets on one of the registered SIM cards. + * + * @param id permanent identity to request quintuplet for + * @param rand random value rand + * @param autn authentication token autn + * @param ck buffer receiving encryption key ck + * @param ik buffer receiving integrity key ik + * @param res buffer receiving authentication result res + * @param res_len nubmer of bytes written to res buffer + * @return SUCCESS, FAILED, or INVALID_STATE if out of sync + */ + status_t (*card_get_quintuplet)(sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char res[AKA_RES_MAX], int *res_len); + + /** + * Calculate resynchronization data on one of the registered SIM cards. + * + * @param id permanent identity to request quintuplet for + * @param rand random value rand + * @param auts resynchronization parameter auts + * @return TRUE if calculated, FALSE if no matcing card found + */ + bool (*card_resync)(sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); + + /** + * Store a received pseudonym on one of the registered SIM cards. + * + * @param id permanent identity of the peer + * @param pseudonym pseudonym identity received from the server + */ + void (*card_set_pseudonym)(sim_manager_t *this, identification_t *id, + identification_t *pseudonym); + + /** + * Get a stored pseudonym from one of the registerd SIM cards. + * + * @param id permanent identity of the peer + * @return associated pseudonym identity, NULL if none found + */ + identification_t* (*card_get_pseudonym)(sim_manager_t *this, + identification_t *id); + /** - * Create an enumerator over all registered cards. + * Store fast reauthentication parameters on one of the registered cards. * - * @return enumerator over sim_card_t's + * @param id permanent identity of the peer + * @param next next fast reauthentication identity to use + * @param mk master key MK to store for reauthentication + * @param counter counter value to store, host order */ - enumerator_t* (*create_card_enumerator)(sim_manager_t *this); - + void (*card_set_reauth)(sim_manager_t *this, identification_t *id, + identification_t *next, char mk[HASH_SIZE_SHA1], + u_int16_t counter); + + /** + * Retrieve fast reauthentication parameters from one of the registerd cards. + * + * @param id permanent identity of the peer + * @param mk buffer receiving master key MK + * @param counter pointer receiving counter value, in host order + * @return fast reauthentication identity, NULL if none found + */ + identification_t* (*card_get_reauth)(sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1], + u_int16_t *counter); + /** * Register a triplet provider (server) at the manager. * * @param card sim card to register */ void (*add_provider)(sim_manager_t *this, sim_provider_t *provider); - + /** * Unregister a previously registered provider from the manager. * * @param card sim card to unregister */ void (*remove_provider)(sim_manager_t *this, sim_provider_t *provider); - + + /** + * Get a SIM triplet from one of the registered providers. + * + * @param id permanent identity of peer to gen triplet for + * @param rand RAND output buffer, fixed size 16 bytes + * @param sres SRES output buffer, fixed size 4 byte + * @param kc KC output buffer, fixed size 8 bytes + * @return TRUE if triplet received, FALSE if no match found + */ + bool (*provider_get_triplet)(sim_manager_t *this, identification_t *id, + char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], + char kc[SIM_KC_LEN]); + + /** + * Get a AKA quintuplet from one of the registered providers. + * + * @param id permanent identity of peer to create challenge for + * @param rand buffer receiving random value rand + * @param xres buffer receiving expected authentication result xres + * @param ck buffer receiving encryption key ck + * @param ik buffer receiving integrity key ik + * @param autn authentication token autn + * @return TRUE if quintuplet received, FALSE if no match found + */ + bool (*provider_get_quintuplet)(sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], + char xres[AKA_RES_MAX], int *xres_len, + char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], + char autn[AKA_AUTN_LEN]); + /** - * Create an enumerator over all registered provider. + * Pass AKA resynchronization data to one of the registered providers. * - * @return enumerator over sim_provider_t's + * @param id permanent identity of peer requesting resynchronisation + * @param rand random value rand + * @param auts synchronization parameter auts + * @return TRUE if resynchronized, FALSE if not handled */ - enumerator_t* (*create_provider_enumerator)(sim_manager_t *this); - + bool (*provider_resync)(sim_manager_t *this, identification_t *id, + char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]); + + /** + * Check if a peer uses a pseudonym using one of the registered providers. + * + * @param id pseudonym identity candidate + * @return permanent identity, NULL if id not a pseudonym + */ + identification_t* (*provider_is_pseudonym)(sim_manager_t *this, + identification_t *id); + + /** + * Generate a new pseudonym using one of the registered providers. + * + * @param id permanent identity to generate a pseudonym for + * @return generated pseudonym, NULL to not use a pseudonym identity + */ + identification_t* (*provider_gen_pseudonym)(sim_manager_t *this, + identification_t *id); + + /** + * Check if a peer uses a reauth id using one of the registered providers. + * + * @param id reauthentication identity (candidate) + * @param mk buffer receiving master key MK + * @param counter pointer receiving current counter value, host order + * @return permanent identity, NULL if not a known reauth identity + */ + identification_t* (*provider_is_reauth)(sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1], + u_int16_t *counter); + + /** + * Generate a fast reauth id using one of the registered providers. + * + * @param id permanent peer identity + * @param mk master key to store along with generated identity + * @return fast reauthentication identity, NULL to not use reauth + */ + identification_t* (*provider_gen_reauth)(sim_manager_t *this, + identification_t *id, char mk[HASH_SIZE_SHA1]); + + /** + * Register a set of hooks to the manager. + * + * @param hooks hook interface implementation to register + */ + void (*add_hooks)(sim_manager_t *this, sim_hooks_t *hooks); + + /** + * Unregister a set of hooks from the manager. + * + * @param hooks hook interface implementation to unregister + */ + void (*remove_hooks)(sim_manager_t *this, sim_hooks_t *hooks); + + /** + * Invoke SIM/AKA attribute hook. + * + * @param code EAP message code (Request/response/success/failed) + * @param type EAP method type, EAP-SIM or AKA + * @param subtype method specific message subtype + * @param attribute SIM/AKA attribute type + * @param data attribute data + * @return TRUE to filter out attribute from further processing + */ + bool (*attribute_hook)(sim_manager_t *this, eap_code_t code, + eap_type_t type, u_int8_t subtype, + u_int8_t attribute, chunk_t data); + + /** + * Invoke SIM/AKA key hook. + * + * @param k_encr SIM/AKA encryption key k_encr + * @param k_auth SIM/AKA authentication key k_auth + */ + void (*key_hook)(sim_manager_t *this, chunk_t k_encr, chunk_t k_auth); + /** * Destroy a manager instance. */ @@ -127,7 +506,7 @@ struct sim_manager_t { }; /** - * Create an SIM manager to handle multiple SIM cards/providers. + * Create an SIM manager to handle multiple (U)SIM cards/providers. * * @return sim_t object */ diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c index 2abdf7a02..16911050a 100644 --- a/src/charon/sa/authenticators/eap_authenticator.c +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -26,62 +26,67 @@ typedef struct private_eap_authenticator_t private_eap_authenticator_t; * Private data of an eap_authenticator_t object. */ struct private_eap_authenticator_t { - + /** * Public authenticator_t interface. */ eap_authenticator_t public; - + /** * Assigned IKE_SA */ ike_sa_t *ike_sa; - + /** * others nonce to include in AUTH calculation */ chunk_t received_nonce; - + /** * our nonce to include in AUTH calculation */ chunk_t sent_nonce; - + /** * others IKE_SA_INIT message data to include in AUTH calculation */ chunk_t received_init; - + /** * our IKE_SA_INIT message data to include in AUTH calculation */ chunk_t sent_init; - + /** * Current EAP method processing */ eap_method_t *method; - + /** * MSK used to build and verify auth payload */ chunk_t msk; - + /** * EAP authentication method completed successfully */ bool eap_complete; - + + /** + * Set if we require mutual EAP due EAP-only authentication + */ + bool require_mutual; + /** * authentication payload verified successfully */ bool auth_complete; - + /** * generated EAP payload */ eap_payload_t *eap_payload; - + /** * EAP identity of peer */ @@ -95,7 +100,7 @@ static eap_method_t *load_method(private_eap_authenticator_t *this, eap_type_t type, u_int32_t vendor, eap_role_t role) { identification_t *server, *peer; - + if (role == EAP_SERVER) { server = this->ike_sa->get_my_id(this->ike_sa); @@ -125,9 +130,10 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this, identification_t *id; u_int32_t vendor; eap_payload_t *out; - + char *action; + auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); - + /* initiate EAP-Identity exchange if required */ if (!this->eap_identity && do_identity) { @@ -150,33 +156,62 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this, /* invoke real EAP method */ type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE); vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR); + action = "loading"; this->method = load_method(this, type, vendor, EAP_SERVER); - if (this->method && - this->method->initiate(this->method, &out) == NEED_MORE) + if (this->method) { - if (vendor) + action = "initiating"; + if (this->method->initiate(this->method, &out) == NEED_MORE) { - DBG1(DBG_IKE, "initiating EAP vendor type %d-%d", type, vendor); - - } - else - { - DBG1(DBG_IKE, "initiating %N", eap_type_names, type); + if (vendor) + { + DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method", + type, vendor); + } + else + { + DBG1(DBG_IKE, "initiating %N method", eap_type_names, type); + } + return out; } - return out; } if (vendor) { - DBG1(DBG_IKE, "initiating EAP vendor type %d-%d failed", type, vendor); + DBG1(DBG_IKE, "%s EAP vendor type %d-%d method failed", + action, type, vendor); } else { - DBG1(DBG_IKE, "initiating %N failed", eap_type_names, type); + DBG1(DBG_IKE, "%s %N method failed", action, eap_type_names, type); } return eap_payload_create_code(EAP_FAILURE, 0); } /** + * Replace the existing EAP-Identity in other auth config + */ +static void replace_eap_identity(private_eap_authenticator_t *this) +{ + enumerator_t *enumerator; + auth_rule_t rule; + auth_cfg_t *cfg; + void *ptr; + + cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); + enumerator = cfg->create_enumerator(cfg); + while (enumerator->enumerate(enumerator, &rule, &ptr)) + { + if (rule == AUTH_RULE_EAP_IDENTITY) + { + cfg->replace(cfg, enumerator, AUTH_RULE_EAP_IDENTITY, + this->eap_identity->clone(this->eap_identity)); + break; + } + } + enumerator->destroy(enumerator); +} + +/** * Handle EAP exchange as server */ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, @@ -186,14 +221,14 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, u_int32_t vendor, received_vendor; eap_payload_t *out; auth_cfg_t *cfg; - + if (in->get_code(in) != EAP_RESPONSE) { DBG1(DBG_IKE, "received %N, sending %N", eap_code_names, in->get_code(in), eap_code_names, EAP_FAILURE); return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in)); } - + type = this->method->get_type(this->method, &vendor); received_type = in->get_type(in, &received_vendor); if (type != received_type || vendor != received_vendor) @@ -210,7 +245,7 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, } return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in)); } - + switch (this->method->process(this->method, in, &out)) { case NEED_MORE: @@ -219,14 +254,13 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, if (type == EAP_IDENTITY) { chunk_t data; - char buf[256]; - + if (this->method->get_msk(this->method, &data) == SUCCESS) { - snprintf(buf, sizeof(buf), "%.*s", data.len, data.ptr); - this->eap_identity = identification_create_from_string(buf); + this->eap_identity = identification_create_from_data(data); DBG1(DBG_IKE, "received EAP identity '%Y'", this->eap_identity); + replace_eap_identity(this); } /* restart EAP exchange, but with real method */ this->method->destroy(this->method); @@ -262,7 +296,7 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, if (vendor) { DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for " - "peer %Y", type, vendor, + "peer %Y", type, vendor, this->ike_sa->get_other_id(this->ike_sa)); } else @@ -286,9 +320,9 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, auth_cfg_t *auth; eap_payload_t *out; identification_t *id; - + type = in->get_type(in, &vendor); - + if (!vendor && type == EAP_IDENTITY) { DESTROY_IF(this->eap_identity); @@ -301,7 +335,7 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, DBG1(DBG_IKE, "server requested %N, sending '%Y'", eap_type_names, type, id); this->eap_identity = id->clone(id); - + this->method = load_method(this, type, vendor, EAP_PEER); if (this->method) { @@ -337,14 +371,14 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, return eap_payload_create_nak(in->get_identifier(in)); } } - + type = this->method->get_type(this->method, &vendor); - + if (this->method->process(this->method, in, &out) == NEED_MORE) { /* client methods should never return SUCCESS */ return out; } - + if (vendor) { DBG1(DBG_IKE, "vendor specific EAP method %d-%d failed", type, vendor); @@ -367,7 +401,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, identification_t *other_id; auth_cfg_t *auth; keymat_t *keymat; - + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); if (!auth_payload) @@ -388,7 +422,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, return FALSE; } chunk_free(&auth_data); - + DBG1(DBG_IKE, "authentication of '%Y' with %N successful", other_id, auth_class_names, AUTH_CLASS_EAP); this->auth_complete = TRUE; @@ -407,13 +441,13 @@ static void build_auth(private_eap_authenticator_t *this, message_t *message, identification_t *my_id; chunk_t auth_data; keymat_t *keymat; - + my_id = this->ike_sa->get_my_id(this->ike_sa); keymat = this->ike_sa->get_keymat(this->ike_sa); - + DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP); - + auth_data = keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, my_id); auth_payload = auth_payload_create(); auth_payload->set_auth_method(auth_payload, AUTH_PSK); @@ -429,7 +463,7 @@ static status_t process_server(private_eap_authenticator_t *this, message_t *message) { eap_payload_t *eap_payload; - + if (this->eap_complete) { if (!verify_auth(this, message, this->sent_nonce, this->received_init)) @@ -438,7 +472,7 @@ static status_t process_server(private_eap_authenticator_t *this, } return NEED_MORE; } - + if (!this->method) { this->eap_payload = server_initiate_eap(this, TRUE); @@ -465,7 +499,7 @@ static status_t build_server(private_eap_authenticator_t *this, if (this->eap_payload) { eap_code_t code; - + code = this->eap_payload->get_code(this->eap_payload); message->add_payload(message, (payload_t*)this->eap_payload); this->eap_payload = NULL; @@ -490,16 +524,25 @@ static status_t process_client(private_eap_authenticator_t *this, message_t *message) { eap_payload_t *eap_payload; - + if (this->eap_complete) { if (!verify_auth(this, message, this->sent_nonce, this->received_init)) { return FAILED; } + if (this->require_mutual && !this->method->is_mutual(this->method)) + { /* we require mutual authentication due to EAP-only */ + u_int32_t vendor; + + DBG1(DBG_IKE, "EAP-only authentication requires a mutual and " + "MSK deriving EAP method, but %N is not", + eap_type_names, this->method->get_type(this->method, &vendor)); + return FAILED; + } return SUCCESS; } - + eap_payload = (eap_payload_t*)message->get_payload(message, EXTENSIBLE_AUTHENTICATION); if (eap_payload) @@ -520,7 +563,7 @@ static status_t process_client(private_eap_authenticator_t *this, eap_type_t type; u_int32_t vendor; auth_cfg_t *cfg; - + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) { this->msk = chunk_clone(this->msk); @@ -561,7 +604,7 @@ static status_t process_client(private_eap_authenticator_t *this, /** * Implementation of authenticator_t.build for a client */ -static status_t build_client(private_eap_authenticator_t *this, +static status_t build_client(private_eap_authenticator_t *this, message_t *message) { if (this->eap_payload) @@ -579,6 +622,16 @@ static status_t build_client(private_eap_authenticator_t *this, } /** + * Implementation of authenticator_t.is_mutual. + */ +static bool is_mutual(private_eap_authenticator_t *this) +{ + /* we don't know yet, but insist on it after EAP is complete */ + this->require_mutual = TRUE; + return TRUE; +} + +/** * Implementation of authenticator_t.destroy. */ static void destroy(private_eap_authenticator_t *this) @@ -598,11 +651,12 @@ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa, chunk_t received_init, chunk_t sent_init) { private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *message))build_client; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))process_client; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))is_mutual; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->received_init = received_init; this->received_nonce = received_nonce; @@ -614,7 +668,8 @@ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa, this->eap_complete = FALSE; this->auth_complete = FALSE; this->eap_identity = NULL; - + this->require_mutual = FALSE; + return &this->public; } @@ -626,11 +681,12 @@ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa, chunk_t received_init, chunk_t sent_init) { private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *messageh))build_server; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))process_server; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))is_mutual; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->received_init = received_init; this->received_nonce = received_nonce; @@ -642,7 +698,8 @@ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa, this->eap_complete = FALSE; this->auth_complete = FALSE; this->eap_identity = NULL; - + this->require_mutual = FALSE; + return &this->public; } diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h index b90a6f4df..41eb6a8c9 100644 --- a/src/charon/sa/authenticators/eap_authenticator.h +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -83,7 +83,7 @@ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa, /** * Create an authenticator to authenticate EAP clients. - * + * * @param ike_sa associated ike_sa * @param received_nonce nonce received in IKE_SA_INIT * @param sent_nonce nonce sent in IKE_SA_INIT diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c index 742b67789..67197d690 100644 --- a/src/charon/sa/authenticators/psk_authenticator.c +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -35,12 +35,12 @@ struct private_psk_authenticator_t { * Assigned IKE_SA */ ike_sa_t *ike_sa; - + /** * nonce to include in AUTH calculation */ chunk_t nonce; - + /** * IKE_SA_INIT message data to include in AUTH calculation */ @@ -57,7 +57,7 @@ static status_t build(private_psk_authenticator_t *this, message_t *message) shared_key_t *key; chunk_t auth_data; keymat_t *keymat; - + keymat = this->ike_sa->get_keymat(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); @@ -79,7 +79,7 @@ static status_t build(private_psk_authenticator_t *this, message_t *message) auth_payload->set_data(auth_payload, auth_data); chunk_free(&auth_data); message->add_payload(message, (payload_t*)auth_payload); - + return SUCCESS; } @@ -97,7 +97,7 @@ static status_t process(private_psk_authenticator_t *this, message_t *message) bool authenticated = FALSE; int keys_found = 0; keymat_t *keymat; - + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); if (!auth_payload) { @@ -112,7 +112,7 @@ static status_t process(private_psk_authenticator_t *this, message_t *message) while (!authenticated && enumerator->enumerate(enumerator, &key, NULL, NULL)) { keys_found++; - + auth_data = keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init, this->nonce, key->get_key(key), other_id); if (auth_data.len && chunk_equals(auth_data, recv_auth_data)) @@ -124,7 +124,7 @@ static status_t process(private_psk_authenticator_t *this, message_t *message) chunk_free(&auth_data); } enumerator->destroy(enumerator); - + if (!authenticated) { if (keys_found == 0) @@ -136,7 +136,7 @@ static status_t process(private_psk_authenticator_t *this, message_t *message) keys_found, keys_found == 1 ? "" : "s", my_id, other_id); return FAILED; } - + auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); return SUCCESS; @@ -166,15 +166,16 @@ psk_authenticator_t *psk_authenticator_create_builder(ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_init) { private_psk_authenticator_t *this = malloc_thing(private_psk_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *message))build; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))return_failed; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))return_false; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->ike_sa_init = sent_init; this->nonce = received_nonce; - + return &this->public; } @@ -185,15 +186,16 @@ psk_authenticator_t *psk_authenticator_create_verifier(ike_sa_t *ike_sa, chunk_t sent_nonce, chunk_t received_init) { private_psk_authenticator_t *this = malloc_thing(private_psk_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *messageh))return_failed; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))process; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))return_false; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->ike_sa_init = received_init; this->nonce = sent_nonce; - + return &this->public; } diff --git a/src/charon/sa/authenticators/psk_authenticator.h b/src/charon/sa/authenticators/psk_authenticator.h index 5bb743d93..0fab11095 100644 --- a/src/charon/sa/authenticators/psk_authenticator.h +++ b/src/charon/sa/authenticators/psk_authenticator.h @@ -49,7 +49,7 @@ psk_authenticator_t *psk_authenticator_create_builder(ike_sa_t *ike_sa, /** * Create an authenticator to verify PSK signatures. - * + * * @param ike_sa associated ike_sa * @param sent_nonce nonce sent in IKE_SA_INIT * @param received_init received IKE_SA_INIT message data diff --git a/src/charon/sa/authenticators/pubkey_authenticator.c b/src/charon/sa/authenticators/pubkey_authenticator.c index 44cabfb94..f1dca2702 100644 --- a/src/charon/sa/authenticators/pubkey_authenticator.c +++ b/src/charon/sa/authenticators/pubkey_authenticator.c @@ -26,22 +26,22 @@ typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t; * Private data of an pubkey_authenticator_t object. */ struct private_pubkey_authenticator_t { - + /** * Public authenticator_t interface. */ pubkey_authenticator_t public; - + /** * Assigned IKE_SA */ ike_sa_t *ike_sa; - + /** * nonce to include in AUTH calculation */ chunk_t nonce; - + /** * IKE_SA_INIT message data to include in AUTH calculation */ @@ -72,11 +72,11 @@ static status_t build(private_pubkey_authenticator_t *this, message_t *message) DBG1(DBG_IKE, "no private key found for '%Y'", id); return NOT_FOUND; } - + switch (private->get_type(private)) { case KEY_RSA: - /* we currently use always SHA1 for signatures, + /* we currently use always SHA1 for signatures, * TODO: support other hashes depending on configuration/auth */ scheme = SIGN_RSA_EMSA_PKCS1_SHA1; auth_method = AUTH_RSA; @@ -86,7 +86,7 @@ static status_t build(private_pubkey_authenticator_t *this, message_t *message) switch (private->get_keysize(private)) { case 32: - scheme = SIGN_ECDSA_256; + scheme = SIGN_ECDSA_256; auth_method = AUTH_ECDSA_256; break; case 48: @@ -121,11 +121,11 @@ static status_t build(private_pubkey_authenticator_t *this, message_t *message) status = SUCCESS; } DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, - auth_method_names, auth_method, + auth_method_names, auth_method, (status == SUCCESS)? "successful":"failed"); chunk_free(&octets); private->destroy(private); - + return status; } @@ -145,7 +145,7 @@ static status_t process(private_pubkey_authenticator_t *this, message_t *message signature_scheme_t scheme; status_t status = NOT_FOUND; keymat_t *keymat; - + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); if (!auth_payload) { @@ -231,15 +231,16 @@ pubkey_authenticator_t *pubkey_authenticator_create_builder(ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_init) { private_pubkey_authenticator_t *this = malloc_thing(private_pubkey_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *message))build; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))return_failed; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))return_false; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->ike_sa_init = sent_init; this->nonce = received_nonce; - + return &this->public; } @@ -250,14 +251,15 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, chunk_t sent_nonce, chunk_t received_init) { private_pubkey_authenticator_t *this = malloc_thing(private_pubkey_authenticator_t); - + this->public.authenticator.build = (status_t(*)(authenticator_t*, message_t *message))return_failed; this->public.authenticator.process = (status_t(*)(authenticator_t*, message_t *message))process; + this->public.authenticator.is_mutual = (bool(*)(authenticator_t*))return_false; this->public.authenticator.destroy = (void(*)(authenticator_t*))destroy; - + this->ike_sa = ike_sa; this->ike_sa_init = received_init; this->nonce = sent_nonce; - + return &this->public; } diff --git a/src/charon/sa/authenticators/pubkey_authenticator.h b/src/charon/sa/authenticators/pubkey_authenticator.h index e67f020ff..be369cb89 100644 --- a/src/charon/sa/authenticators/pubkey_authenticator.h +++ b/src/charon/sa/authenticators/pubkey_authenticator.h @@ -50,7 +50,7 @@ pubkey_authenticator_t *pubkey_authenticator_create_builder(ike_sa_t *ike_sa, /** * Create an authenticator to verify public key signatures. - * + * * @param ike_sa associated ike_sa * @param sent_nonce nonce sent in IKE_SA_INIT * @param received_init received IKE_SA_INIT message data diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 14d174ab5..3fdfb51ad 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Tobias Brunner + * Copyright (C) 2006-2009 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter @@ -46,67 +46,67 @@ struct private_child_sa_t { * Public interface of child_sa_t. */ child_sa_t public; - + /** * address of us */ host_t *my_addr; - + /** * address of remote */ host_t *other_addr; - + /** * our actually used SPI, 0 if unused */ u_int32_t my_spi; - + /** * others used SPI, 0 if unused */ u_int32_t other_spi; - + /** * our Compression Parameter Index (CPI) used, 0 if unused */ u_int16_t my_cpi; - + /** * others Compression Parameter Index (CPI) used, 0 if unused */ u_int16_t other_cpi; - + /** * List for local traffic selectors */ linked_list_t *my_ts; - + /** * List for remote traffic selectors */ linked_list_t *other_ts; - + /** * Protocol used to protect this SA, ESP|AH */ protocol_id_t protocol; - + /** * reqid used for this child_sa */ u_int32_t reqid; - + /** * absolute time when rekeying is scheduled */ time_t rekey_time; - + /** * absolute time when the SA expires */ time_t expire_time; - + /** * state of the CHILD_SA */ @@ -116,22 +116,22 @@ struct private_child_sa_t { * Specifies if UDP encapsulation is enabled (NAT traversal) */ bool encap; - + /** * Specifies the IPComp transform used (IPCOMP_NONE if disabled) */ ipcomp_transform_t ipcomp; - + /** * mode this SA uses, tunnel/transport */ ipsec_mode_t mode; - + /** - * selected proposal - */ - proposal_t *proposal; - + * selected proposal + */ + proposal_t *proposal; + /** * config used to create this child */ @@ -320,7 +320,7 @@ static bool policy_enumerate(policy_enumerator_t *this, traffic_selector_t **my_out, traffic_selector_t **other_out) { traffic_selector_t *other_ts; - + while (this->ts || this->mine->enumerate(this->mine, &this->ts)) { if (!this->other->enumerate(this->other, &other_ts)) @@ -363,14 +363,14 @@ static void policy_destroy(policy_enumerator_t *this) static enumerator_t* create_policy_enumerator(private_child_sa_t *this) { policy_enumerator_t *e = malloc_thing(policy_enumerator_t); - + e->public.enumerate = (void*)policy_enumerate; e->public.destroy = (void*)policy_destroy; e->mine = this->my_ts->create_enumerator(this->my_ts); e->other = this->other_ts->create_enumerator(this->other_ts); e->list = this->other_ts; e->ts = NULL; - + return &e->public; } @@ -384,7 +384,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { status_t status = FAILED; u_int64_t bytes; - + if (inbound) { if (this->my_spi) @@ -434,12 +434,12 @@ static void update_usetime(private_child_sa_t *this, bool inbound) enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; u_int32_t last_use = 0; - + enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { u_int32_t in, out, fwd; - + if (inbound) { if (charon->kernel_interface->query_policy(charon->kernel_interface, @@ -507,7 +507,7 @@ static void get_usestats(private_child_sa_t *this, bool inbound, /** * Implementation of child_sa_t.get_lifetime */ -static u_int32_t get_lifetime(private_child_sa_t *this, bool hard) +static time_t get_lifetime(private_child_sa_t *this, bool hard) { return hard ? this->expire_time : this->rekey_time; } @@ -544,14 +544,17 @@ static u_int16_t alloc_cpi(private_child_sa_t *this) * Implementation of child_sa_t.install */ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ, - u_int32_t spi, u_int16_t cpi, bool inbound) + u_int32_t spi, u_int16_t cpi, bool inbound, + linked_list_t *my_ts, linked_list_t *other_ts) { u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; - u_int32_t soft, hard, now; + traffic_selector_t *src_ts = NULL, *dst_ts = NULL; + time_t now; + lifetime_cfg_t *lifetime; host_t *src, *dst; status_t status; bool update = FALSE; - + /* now we have to decide which spi to use. Use self allocated, if "in", * or the one in the proposal, if not "in" (others). Additionally, * source and dest host switch depending on the role */ @@ -573,35 +576,59 @@ static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ, this->other_spi = spi; this->other_cpi = cpi; } - + DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", protocol_id_names, this->protocol); - + /* send SA down to the kernel */ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); - + this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM, &enc_alg, &size); this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM, &int_alg, &size); - - soft = this->config->get_lifetime(this->config, TRUE); - hard = this->config->get_lifetime(this->config, FALSE); - - status = charon->kernel_interface->add_sa(charon->kernel_interface, - src, dst, spi, this->protocol, this->reqid, - inbound ? soft : 0, hard, enc_alg, encr, int_alg, integ, - this->mode, this->ipcomp, cpi, this->encap, update); - - now = time(NULL); - if (soft) + + lifetime = this->config->get_lifetime(this->config); + + now = time_monotonic(NULL); + if (lifetime->time.rekey) { - this->rekey_time = now + soft; + this->rekey_time = now + lifetime->time.rekey; } - if (hard) + if (lifetime->time.life) + { + this->expire_time = now + lifetime->time.life; + } + + if (!lifetime->time.jitter && !inbound) + { /* avoid triggering multiple rekey events */ + lifetime->time.rekey = 0; + } + + if (this->mode == MODE_BEET) { - this->expire_time = now + hard; + /* BEET requires the bound address from the traffic selectors. + * TODO: We add just the first traffic selector for now, as the + * kernel accepts a single TS per SA only */ + if (inbound) + { + my_ts->get_first(my_ts, (void**)&dst_ts); + other_ts->get_first(other_ts, (void**)&src_ts); + } + else + { + my_ts->get_first(my_ts, (void**)&src_ts); + other_ts->get_first(other_ts, (void**)&dst_ts); + } } + + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, spi, this->protocol, this->reqid, lifetime, + enc_alg, encr, int_alg, integ, this->mode, this->ipcomp, cpi, + this->encap, update, src_ts, dst_ts); + + free(lifetime); + return status; } @@ -615,7 +642,7 @@ static status_t add_policies(private_child_sa_t *this, traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; bool routed = (this->state == CHILD_CREATED); - + /* apply traffic selectors */ enumerator = my_ts_list->create_enumerator(my_ts_list); while (enumerator->enumerate(enumerator, &my_ts)) @@ -629,7 +656,7 @@ static status_t add_policies(private_child_sa_t *this, this->other_ts->insert_last(this->other_ts, other_ts->clone(other_ts)); } enumerator->destroy(enumerator); - + if (this->config->install_policy(this->config)) { /* enumerate pairs of traffic selectors */ @@ -641,7 +668,7 @@ static status_t add_policies(private_child_sa_t *this, this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT, this->other_spi, this->protocol, this->reqid, this->mode, this->ipcomp, this->other_cpi, routed); - + status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN, this->my_spi, this->protocol, this->reqid, this->mode, @@ -653,7 +680,7 @@ static status_t add_policies(private_child_sa_t *this, this->my_spi, this->protocol, this->reqid, this->mode, this->ipcomp, this->my_cpi, routed); } - + if (status != SUCCESS) { break; @@ -661,7 +688,7 @@ static status_t add_policies(private_child_sa_t *this, } enumerator->destroy(enumerator); } - + if (status == SUCCESS && this->state == CHILD_CREATED) { /* switch to routed state if no SAD entry set up */ set_state(this, CHILD_ROUTED); @@ -677,19 +704,19 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, { child_sa_state_t old; bool transport_proxy_mode; - + /* anything changed at all? */ if (me->equals(me, this->my_addr) && other->equals(other, this->other_addr) && this->encap == encap) { return SUCCESS; } - + old = this->state; set_state(this, CHILD_UPDATING); transport_proxy_mode = this->config->use_proxy_mode(this->config) && this->mode == MODE_TRANSPORT; - + if (!transport_proxy_mode) { /* update our (initator) SA */ @@ -704,13 +731,13 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, return NOT_SUPPORTED; } } - + /* update his (responder) SA */ if (this->other_spi) { if (charon->kernel_interface->update_sa(charon->kernel_interface, this->other_spi, this->protocol, - this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, + this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, this->my_addr, this->other_addr, me, other, this->encap, encap) == NOT_SUPPORTED) { @@ -718,7 +745,7 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, } } } - + if (this->config->install_policy(this->config)) { /* update policies */ @@ -727,7 +754,7 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - + /* always use high priorities, as hosts getting updated are INSTALLED */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) @@ -742,7 +769,7 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, charon->kernel_interface->del_policy(charon->kernel_interface, other_ts, my_ts, POLICY_FWD, FALSE); } - + /* check whether we have to update a "dynamic" traffic selector */ if (!me->ip_equals(me, this->my_addr) && my_ts->is_host(my_ts, this->my_addr)) @@ -754,7 +781,7 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, { other_ts->set_address(other_ts, other); } - + /* we reinstall the virtual IP to handle interface roaming * correctly */ if (vip) @@ -762,7 +789,7 @@ static status_t update(private_child_sa_t *this, host_t *me, host_t *other, charon->kernel_interface->del_ip(charon->kernel_interface, vip); charon->kernel_interface->add_ip(charon->kernel_interface, vip, me); } - + /* reinstall updated policies */ charon->kernel_interface->add_policy(charon->kernel_interface, me, other, my_ts, other_ts, POLICY_OUT, this->other_spi, @@ -813,12 +840,18 @@ static void destroy(private_child_sa_t *this) enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; bool unrouted = (this->state == CHILD_ROUTED); - + set_state(this, CHILD_DESTROYING); - + /* delete SAs in the kernel, if they are set up */ if (this->my_spi) { + /* if CHILD was not established, use PROTO_ESP used during alloc_spi(). + * TODO: For AH support, we have to store protocol specific SPI.s */ + if (this->protocol == PROTO_NONE) + { + this->protocol = PROTO_ESP; + } charon->kernel_interface->del_sa(charon->kernel_interface, this->other_addr, this->my_addr, this->my_spi, this->protocol, this->my_cpi); @@ -829,7 +862,7 @@ static void destroy(private_child_sa_t *this) this->my_addr, this->other_addr, this->other_spi, this->protocol, this->other_cpi); } - + if (this->config->install_policy(this->config)) { /* delete all policies in the kernel */ @@ -848,7 +881,7 @@ static void destroy(private_child_sa_t *this) } enumerator->destroy(enumerator); } - + this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); this->my_addr->destroy(this->my_addr); @@ -881,20 +914,20 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->public.set_mode = (void(*)(child_sa_t*, ipsec_mode_t mode))set_mode; this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal; this->public.set_proposal = (void(*)(child_sa_t*, proposal_t *proposal))set_proposal; - this->public.get_lifetime = (u_int32_t(*)(child_sa_t*, bool))get_lifetime; + this->public.get_lifetime = (time_t(*)(child_sa_t*, bool))get_lifetime; this->public.get_usestats = (void(*)(child_sa_t*,bool,time_t*,u_int64_t*))get_usestats; this->public.has_encap = (bool(*)(child_sa_t*))has_encap; this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp; this->public.set_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t))set_ipcomp; this->public.alloc_spi = (u_int32_t(*)(child_sa_t*, protocol_id_t protocol))alloc_spi; this->public.alloc_cpi = (u_int16_t(*)(child_sa_t*))alloc_cpi; - this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound))install; + this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound, linked_list_t *my_ts_list, linked_list_t *other_ts_list))install; this->public.update = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update; this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors; this->public.create_policy_enumerator = (enumerator_t*(*)(child_sa_t*))create_policy_enumerator; this->public.destroy = (void(*)(child_sa_t*))destroy; - + /* private data */ this->my_addr = me->clone(me); this->other_addr = other->clone(other); @@ -920,10 +953,10 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->expire_time = 0; this->config = config; config->get_ref(config); - + /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && - config->use_proxy_mode(config)) + config->use_proxy_mode(config)) { ts_type_t type; int family; @@ -932,9 +965,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, enumerator_t *enumerator; linked_list_t *my_ts_list, *other_ts_list; traffic_selector_t *my_ts, *other_ts; - + this->mode = MODE_TRANSPORT; - + my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me); enumerator = my_ts_list->create_enumerator(my_ts_list); if (enumerator->enumerate(enumerator, &my_ts)) @@ -955,7 +988,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, } enumerator->destroy(enumerator); my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy)); - + other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other); enumerator = other_ts_list->create_enumerator(other_ts_list); if (enumerator->enumerate(enumerator, &other_ts)) @@ -977,6 +1010,6 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, enumerator->destroy(enumerator); other_ts_list->destroy_offset(other_ts_list, offsetof(traffic_selector_t, destroy)); } - + return &this->public; } diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 698da8bc7..d70bed664 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -36,42 +36,42 @@ typedef struct child_sa_t child_sa_t; * States of a CHILD_SA */ enum child_sa_state_t { - + /** * Just created, uninstalled CHILD_SA */ CHILD_CREATED, - + /** * Installed SPD, but no SAD entries */ CHILD_ROUTED, - + /** * Installing an in-use CHILD_SA */ CHILD_INSTALLING, - + /** * Installed an in-use CHILD_SA */ CHILD_INSTALLED, - + /** * While updating hosts, in update_hosts() */ CHILD_UPDATING, - + /** * CHILD_SA which is rekeying */ CHILD_REKEYING, - + /** * CHILD_SA in progress of delete */ CHILD_DELETING, - + /** * CHILD_SA object gets destroyed */ @@ -102,14 +102,14 @@ extern enum_name_t *child_sa_state_names; * Once SAs are set up, policies can be added using add_policies. */ struct child_sa_t { - + /** * Get the name of the config this CHILD_SA uses. * * @return name */ char* (*get_name) (child_sa_t *this); - + /** * Get the reqid of the CHILD SA. * @@ -119,28 +119,28 @@ struct child_sa_t { * @return reqid of the CHILD SA */ u_int32_t (*get_reqid)(child_sa_t *this); - + /** * Get the config used to set up this child sa. * * @return child_cfg */ child_cfg_t* (*get_config) (child_sa_t *this); - + /** * Get the state of the CHILD_SA. * * @return CHILD_SA state */ child_sa_state_t (*get_state) (child_sa_t *this); - + /** * Set the state of the CHILD_SA. * * @param state state to set on CHILD_SA */ void (*set_state) (child_sa_t *this, child_sa_state_t state); - + /** * Get the SPI of this CHILD_SA. * @@ -152,7 +152,7 @@ struct child_sa_t { * @return SPI of the CHILD SA */ u_int32_t (*get_spi) (child_sa_t *this, bool inbound); - + /** * Get the CPI of this CHILD_SA. * @@ -171,71 +171,71 @@ struct child_sa_t { * @return AH | ESP */ protocol_id_t (*get_protocol) (child_sa_t *this); - + /** * Set the negotiated protocol to use for this CHILD_SA. * * @param protocol AH | ESP */ void (*set_protocol)(child_sa_t *this, protocol_id_t protocol); - + /** * Get the IPsec mode of this CHILD_SA. * * @return TUNNEL | TRANSPORT | BEET */ ipsec_mode_t (*get_mode)(child_sa_t *this); - + /** * Set the negotiated IPsec mode to use. * * @param mode TUNNEL | TRANPORT | BEET */ void (*set_mode)(child_sa_t *this, ipsec_mode_t mode); - + /** * Get the used IPComp algorithm. * * @return IPComp compression algorithm. */ ipcomp_transform_t (*get_ipcomp)(child_sa_t *this); - + /** * Set the IPComp algorithm to use. * * @param ipcomp the IPComp transform to use */ void (*set_ipcomp)(child_sa_t *this, ipcomp_transform_t ipcomp); - + /** * Get the selected proposal. * * @return selected proposal */ proposal_t* (*get_proposal)(child_sa_t *this); - + /** * Set the negotiated proposal. * * @param proposal selected proposal */ void (*set_proposal)(child_sa_t *this, proposal_t *proposal); - + /** * Check if this CHILD_SA uses UDP encapsulation. * * @return TRUE if SA encapsulates ESP packets */ bool (*has_encap)(child_sa_t *this); - + /** - * Get the lifetime of the CHILD_SA. + * Get the absolute time when the CHILD_SA expires or gets rekeyed. * * @param hard TRUE for hard lifetime, FALSE for soft (rekey) lifetime - * @return lifetime in seconds + * @return absolute time */ - u_int32_t (*get_lifetime)(child_sa_t *this, bool hard); - + time_t (*get_lifetime)(child_sa_t *this, bool hard); + /** * Get last use time and the number of bytes processed. * @@ -245,7 +245,7 @@ struct child_sa_t { */ void (*get_usestats)(child_sa_t *this, bool inbound, time_t *time, u_int64_t *bytes); - + /** * Get the traffic selectors list added for one side. * @@ -253,14 +253,14 @@ struct child_sa_t { * @return list of traffic selectors */ linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local); - + /** * Create an enumerator over installed policies. * * @return enumerator over pairs of traffic selectors. */ enumerator_t* (*create_policy_enumerator)(child_sa_t *this); - + /** * Allocate an SPI to include in a proposal. * @@ -269,14 +269,14 @@ struct child_sa_t { * @return SPI, 0 on failure */ u_int32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol); - + /** * Allocate a CPI to use for IPComp. * * @return CPI, 0 on failure */ u_int16_t (*alloc_cpi)(child_sa_t *this); - + /** * Install an IPsec SA for one direction. * @@ -285,10 +285,13 @@ struct child_sa_t { * @param spi SPI to use, allocated for inbound * @param cpi CPI to use, allocated for outbound * @param inbound TRUE to install an inbound SA, FALSE for outbound + * @param my_ts negotiated local traffic selector list + * @param other_ts negotiated remote traffic selector list * @return SUCCESS or FAILED */ status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ, - u_int32_t spi, u_int16_t cpi, bool inbound); + u_int32_t spi, u_int16_t cpi, bool inbound, + linked_list_t *my_ts, linked_list_t *other_ts); /** * Install the policies using some traffic selectors. * diff --git a/src/charon/sa/connect_manager.c b/src/charon/sa/connect_manager.c index f26cf9405..b78ba070d 100644 --- a/src/charon/sa/connect_manager.c +++ b/src/charon/sa/connect_manager.c @@ -18,7 +18,7 @@ #include <math.h> #include <daemon.h> -#include <utils/mutex.h> +#include <threading/mutex.h> #include <utils/linked_list.h> #include <crypto/hashers/hasher.h> @@ -42,7 +42,6 @@ * the first check has succeeded */ #define ME_WAIT_TO_FINISH 1000 /* ms */ - typedef struct private_connect_manager_t private_connect_manager_t; /** @@ -53,24 +52,25 @@ struct private_connect_manager_t { * Public interface of connect_manager_t. */ connect_manager_t public; - + /** * Lock for exclusivly accessing the manager. */ mutex_t *mutex; - + /** * Hasher to generate signatures */ hasher_t *hasher; - + /** * Linked list with initiated mediated connections */ linked_list_t *initiated; - + /** - * Linked list with checklists (hash table with connect ID as key would be better). + * Linked list with checklists (hash table with connect ID as key would + * be better). */ linked_list_t *checklists; }; @@ -93,24 +93,24 @@ typedef struct endpoint_pair_t endpoint_pair_t; struct endpoint_pair_t { /** pair id */ u_int32_t id; - + /** priority */ u_int64_t priority; - + /** local endpoint */ - host_t *local; - - /** remote endpoint */ - host_t *remote; - - /** state */ - check_state_t state; - - /** number of retransmissions */ - u_int32_t retransmitted; - - /** the generated packet */ - packet_t *packet; + host_t *local; + + /** remote endpoint */ + host_t *remote; + + /** state */ + check_state_t state; + + /** number of retransmissions */ + u_int32_t retransmitted; + + /** the generated packet */ + packet_t *packet; }; /** @@ -119,8 +119,8 @@ struct endpoint_pair_t { static void endpoint_pair_destroy(endpoint_pair_t *this) { DESTROY_IF(this->local); - DESTROY_IF(this->remote); - DESTROY_IF(this->packet); + DESTROY_IF(this->remote); + DESTROY_IF(this->packet); free(this); } @@ -131,22 +131,24 @@ static endpoint_pair_t *endpoint_pair_create(endpoint_notify_t *initiator, endpoint_notify_t *responder, bool initiator_is_local) { endpoint_pair_t *this = malloc_thing(endpoint_pair_t); - + this->id = 0; - + u_int32_t pi = initiator->get_priority(initiator); u_int32_t pr = responder->get_priority(responder); this->priority = pow(2, 32) * min(pi, pr) + 2 * max(pi, pr) + (pi > pr ? 1 : 0); - - this->local = initiator_is_local ? initiator->get_base(initiator) : responder->get_base(responder); + + this->local = initiator_is_local ? initiator->get_base(initiator) + : responder->get_base(responder); this->local = this->local->clone(this->local); - this->remote = initiator_is_local ? responder->get_host(responder) : initiator->get_host(initiator); + this->remote = initiator_is_local ? responder->get_host(responder) + : initiator->get_host(initiator); this->remote = this->remote->clone(this->remote); - + this->state = CHECK_WAITING; this->retransmitted = 0; this->packet = NULL; - + return this; } @@ -157,50 +159,50 @@ typedef struct check_list_t check_list_t; * An entry in the linked list. */ struct check_list_t { - + struct { /** initiator's id */ identification_t *id; - + /** initiator's key */ chunk_t key; - + /** initiator's endpoints */ linked_list_t *endpoints; } initiator; - + struct { /** responder's id */ identification_t *id; - + /** responder's key */ chunk_t key; - + /** responder's endpoints */ linked_list_t *endpoints; } responder; - + /** connect id */ chunk_t connect_id; - - /** list of endpoint pairs */ - linked_list_t *pairs; - - /** pairs queued for triggered checks */ - linked_list_t *triggered; - - /** state */ - check_state_t state; - - /** TRUE if this is the initiator */ + + /** list of endpoint pairs */ + linked_list_t *pairs; + + /** pairs queued for triggered checks */ + linked_list_t *triggered; + + /** state */ + check_state_t state; + + /** TRUE if this is the initiator */ bool is_initiator; - + /** TRUE if the initiator is finishing the checks */ bool is_finishing; - + /** the current sender job */ job_t *sender; - + }; /** @@ -210,46 +212,51 @@ static void check_list_destroy(check_list_t *this) { DESTROY_IF(this->initiator.id); DESTROY_IF(this->responder.id); - + chunk_free(&this->connect_id); chunk_free(&this->initiator.key); chunk_free(&this->responder.key); - - DESTROY_OFFSET_IF(this->initiator.endpoints, offsetof(endpoint_notify_t, destroy)); - DESTROY_OFFSET_IF(this->responder.endpoints, offsetof(endpoint_notify_t, destroy)); - + + DESTROY_OFFSET_IF(this->initiator.endpoints, + offsetof(endpoint_notify_t, destroy)); + DESTROY_OFFSET_IF(this->responder.endpoints, + offsetof(endpoint_notify_t, destroy)); + DESTROY_FUNCTION_IF(this->pairs, (void*)endpoint_pair_destroy); - /* this list contains some of the same elements as contained in this->pairs */ - DESTROY_IF(this->triggered); - + /* this list contains some of the elements contained in this->pairs */ + DESTROY_IF(this->triggered); + free(this); } /** * Creates a new checklist */ -static check_list_t *check_list_create(identification_t *initiator, identification_t *responder, - chunk_t connect_id, chunk_t initiator_key, linked_list_t *initiator_endpoints, - bool is_initiator) +static check_list_t *check_list_create(identification_t *initiator, + identification_t *responder, + chunk_t connect_id, + chunk_t initiator_key, + linked_list_t *initiator_endpoints, + bool is_initiator) { check_list_t *this = malloc_thing(check_list_t); - + this->connect_id = chunk_clone(connect_id); - + this->initiator.id = initiator->clone(initiator); this->initiator.key = chunk_clone(initiator_key); this->initiator.endpoints = initiator_endpoints->clone_offset(initiator_endpoints, offsetof(endpoint_notify_t, clone)); - + this->responder.id = responder->clone(responder); this->responder.key = chunk_empty; - this->responder.endpoints = NULL; - - this->pairs = linked_list_create(); - this->triggered = linked_list_create(); - this->state = CHECK_NONE; - this->is_initiator = is_initiator; - this->is_finishing = FALSE; - + this->responder.endpoints = NULL; + + this->pairs = linked_list_create(); + this->triggered = linked_list_create(); + this->state = CHECK_NONE; + this->is_initiator = is_initiator; + this->is_finishing = FALSE; + return this; } @@ -261,10 +268,10 @@ typedef struct initiated_t initiated_t; struct initiated_t { /** my id */ identification_t *id; - + /** peer id */ identification_t *peer_id; - + /** list of mediated sas */ linked_list_t *mediated; }; @@ -276,21 +283,23 @@ static void initiated_destroy(initiated_t *this) { DESTROY_IF(this->id); DESTROY_IF(this->peer_id); - this->mediated->destroy_offset(this->mediated, offsetof(ike_sa_id_t, destroy)); + this->mediated->destroy_offset(this->mediated, + offsetof(ike_sa_id_t, destroy)); free(this); } /** * Creates a queued initiation */ -static initiated_t *initiated_create(identification_t *id, identification_t *peer_id) +static initiated_t *initiated_create(identification_t *id, + identification_t *peer_id) { initiated_t *this = malloc_thing(initiated_t); - + this->id = id->clone(id); this->peer_id = peer_id->clone(peer_id); this->mediated = linked_list_create(); - + return this; } @@ -303,24 +312,24 @@ typedef struct check_t check_t; struct check_t { /** message id */ u_int32_t mid; - + /** source of the connectivity check */ host_t *src; - + /** destination of the connectivity check */ host_t *dst; - + /** connect id */ chunk_t connect_id; - + /** endpoint */ endpoint_notify_t *endpoint; - + /** raw endpoint payload (to verify the signature) */ chunk_t endpoint_raw; - - /** connect auth */ - chunk_t auth; + + /** connect auth */ + chunk_t auth; }; /** @@ -343,16 +352,16 @@ static void check_destroy(check_t *this) static check_t *check_create() { check_t *this = malloc_thing(check_t); - + this->connect_id = chunk_empty; this->auth = chunk_empty; this->endpoint_raw = chunk_empty; this->src = NULL; this->dst = NULL; this->endpoint = NULL; - + this->mid = 0; - + return this; } @@ -364,10 +373,10 @@ typedef struct callback_data_t callback_data_t; struct callback_data_t { /** connect manager */ private_connect_manager_t *connect_manager; - + /** connect id */ chunk_t connect_id; - + /** message (pair) id */ u_int32_t mid; }; @@ -385,9 +394,9 @@ static void callback_data_destroy(callback_data_t *this) * Creates a new callback data object */ static callback_data_t *callback_data_create(private_connect_manager_t *connect_manager, - chunk_t connect_id) + chunk_t connect_id) { - callback_data_t *this = malloc_thing(callback_data_t); + callback_data_t *this = malloc_thing(callback_data_t); this->connect_manager = connect_manager; this->connect_id = chunk_clone(connect_id); this->mid = 0; @@ -398,7 +407,7 @@ static callback_data_t *callback_data_create(private_connect_manager_t *connect_ * Creates a new retransmission data object */ static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, - chunk_t connect_id, u_int32_t mid) + chunk_t connect_id, u_int32_t mid) { callback_data_t *this = callback_data_create(connect_manager, connect_id); this->mid = mid; @@ -413,7 +422,7 @@ typedef struct initiate_data_t initiate_data_t; struct initiate_data_t { /** checklist */ check_list_t *checklist; - + /** waiting mediated connections */ initiated_t *initiated; }; @@ -431,10 +440,11 @@ static void initiate_data_destroy(initiate_data_t *this) /** * Creates a new initiate data object */ -static initiate_data_t *initiate_data_create(check_list_t *checklist, initiated_t *initiated) +static initiate_data_t *initiate_data_create(check_list_t *checklist, + initiated_t *initiated) { initiate_data_t *this = malloc_thing(initiate_data_t); - + this->checklist = checklist; this->initiated = initiated; @@ -445,27 +455,30 @@ static initiate_data_t *initiate_data_create(check_list_t *checklist, initiated_ * Find an initiated connection by the peers' ids */ static bool match_initiated_by_ids(initiated_t *current, identification_t *id, - identification_t *peer_id) + identification_t *peer_id) { return id->equals(id, current->id) && peer_id->equals(peer_id, current->peer_id); } static status_t get_initiated_by_ids(private_connect_manager_t *this, - identification_t *id, identification_t *peer_id, initiated_t **initiated) + identification_t *id, + identification_t *peer_id, + initiated_t **initiated) { return this->initiated->find_first(this->initiated, - (linked_list_match_t)match_initiated_by_ids, - (void**)initiated, id, peer_id); + (linked_list_match_t)match_initiated_by_ids, + (void**)initiated, id, peer_id); } /** * Removes data about initiated connections */ -static void remove_initiated(private_connect_manager_t *this, initiated_t *initiated) +static void remove_initiated(private_connect_manager_t *this, + initiated_t *initiated) { iterator_t *iterator; initiated_t *current; - + iterator = this->initiated->create_iterator(this->initiated, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -487,21 +500,23 @@ static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id) } static status_t get_checklist_by_id(private_connect_manager_t *this, - chunk_t connect_id, check_list_t **check_list) + chunk_t connect_id, + check_list_t **check_list) { return this->checklists->find_first(this->checklists, - (linked_list_match_t)match_checklist_by_id, - (void**)check_list, &connect_id); + (linked_list_match_t)match_checklist_by_id, + (void**)check_list, &connect_id); } /** * Removes a checklist */ -static void remove_checklist(private_connect_manager_t *this, check_list_t *checklist) +static void remove_checklist(private_connect_manager_t *this, + check_list_t *checklist) { iterator_t *iterator; check_list_t *current; - + iterator = this->checklists->create_iterator(this->checklists, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -522,22 +537,23 @@ static bool match_endpoint_by_host(endpoint_notify_t *current, host_t *host) return host->equals(host, current->get_host(current)); } -static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoint_notify_t **endpoint) +static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, + endpoint_notify_t **endpoint) { return endpoints->find_first(endpoints, - (linked_list_match_t)match_endpoint_by_host, - (void**)endpoint, host); + (linked_list_match_t)match_endpoint_by_host, + (void**)endpoint, host); } /** - * Inserts an endpoint pair into the list of pairs ordered by priority (high to low) + * Inserts an endpoint pair into a list of pairs ordered by priority (high to low) */ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) { iterator_t *iterator; endpoint_pair_t *current; bool inserted = FALSE; - + iterator = pairs->create_iterator(pairs, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -549,7 +565,7 @@ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) } } iterator->destroy(iterator); - + if (!inserted) { pairs->insert_last(pairs, pair); @@ -559,16 +575,17 @@ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) /** * Searches a list of endpoint_pair_t for a pair with specific host_ts */ -static bool match_pair_by_hosts(endpoint_pair_t *current, host_t *local, host_t *remote) +static bool match_pair_by_hosts(endpoint_pair_t *current, host_t *local, + host_t *remote) { return local->equals(local, current->local) && remote->equals(remote, current->remote); } -static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, host_t *remote, endpoint_pair_t **pair) +static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, + host_t *remote, endpoint_pair_t **pair) { - return pairs->find_first(pairs, - (linked_list_match_t)match_pair_by_hosts, - (void**)pair, local, remote); + return pairs->find_first(pairs, (linked_list_match_t)match_pair_by_hosts, + (void**)pair, local, remote); } static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) @@ -579,11 +596,12 @@ static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) /** * Searches for a pair with a specific id */ -static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_pair_t **pair) +static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, + endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_pair_by_id, - (void**)pair, &id); + (linked_list_match_t)match_pair_by_id, + (void**)pair, &id); } static bool match_succeeded_pair(endpoint_pair_t *current) @@ -592,13 +610,14 @@ static bool match_succeeded_pair(endpoint_pair_t *current) } /** - * Returns the best pair of state CHECK_SUCCEEDED from a checklist. + * Returns the best pair of state CHECK_SUCCEEDED from a checklist. */ -static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair) +static status_t get_best_valid_pair(check_list_t *checklist, + endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_succeeded_pair, - (void**)pair); + (linked_list_match_t)match_succeeded_pair, + (void**)pair); } static bool match_waiting_pair(endpoint_pair_t *current) @@ -607,19 +626,20 @@ static bool match_waiting_pair(endpoint_pair_t *current) } /** - * Returns and *removes* the first triggered pair in state CHECK_WAITING. + * Returns and *removes* the first triggered pair in state CHECK_WAITING. */ -static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pair) +static status_t get_triggered_pair(check_list_t *checklist, + endpoint_pair_t **pair) { iterator_t *iterator; endpoint_pair_t *current; status_t status = NOT_FOUND; - + iterator = checklist->triggered->create_iterator(checklist->triggered, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { iterator->remove(iterator); - + if (current->state == CHECK_WAITING) { if (pair) @@ -631,7 +651,7 @@ static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pa } } iterator->destroy(iterator); - + return status; } @@ -642,13 +662,13 @@ static void print_checklist(check_list_t *checklist) { iterator_t *iterator; endpoint_pair_t *current; - + DBG1(DBG_IKE, "pairs on checklist %#B:", &checklist->connect_id); iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { DBG1(DBG_IKE, " * %#H - %#H (%d)", current->local, current->remote, - current->priority); + current->priority); } iterator->destroy(iterator); } @@ -662,29 +682,29 @@ static void prune_pairs(linked_list_t *pairs) iterator_t *iterator, *search; endpoint_pair_t *current, *other; u_int32_t id = 0; - + iterator = pairs->create_iterator(pairs, TRUE); search = pairs->create_iterator(pairs, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { current->id = ++id; - + while (search->iterate(search, (void**)&other)) { if (current == other) { continue; } - + if (current->local->equals(current->local, other->local) && - current->remote->equals(current->remote, other->remote)) + current->remote->equals(current->remote, other->remote)) { /* since the list of pairs is sorted by priority in descending * order, and we iterate the list from the beginning, we are * sure that the priority of 'other' is lower than that of * 'current', remove it */ DBG1(DBG_IKE, "pruning endpoint pair %#H - %#H with priority %d", - other->local, other->remote, other->priority); + other->local, other->remote, other->priority); search->remove(search); endpoint_pair_destroy(other); } @@ -703,25 +723,27 @@ static void build_pairs(check_list_t *checklist) /* FIXME: limit endpoints and pairs */ iterator_t *iterator_i, *iterator_r; endpoint_notify_t *initiator, *responder; - - iterator_i = checklist->initiator.endpoints->create_iterator(checklist->initiator.endpoints, TRUE); + + iterator_i = checklist->initiator.endpoints->create_iterator( + checklist->initiator.endpoints, TRUE); while (iterator_i->iterate(iterator_i, (void**)&initiator)) { - iterator_r = checklist->responder.endpoints->create_iterator(checklist->responder.endpoints, TRUE); + iterator_r = checklist->responder.endpoints->create_iterator( + checklist->responder.endpoints, TRUE); while (iterator_r->iterate(iterator_r, (void**)&responder)) { if (initiator->get_family(initiator) != responder->get_family(responder)) { continue; } - - insert_pair_by_priority(checklist->pairs, - endpoint_pair_create(initiator, responder, checklist->is_initiator)); + + insert_pair_by_priority(checklist->pairs, endpoint_pair_create( + initiator, responder, checklist->is_initiator)); } iterator_r->destroy(iterator_r); } iterator_i->destroy(iterator_i); - + print_checklist(checklist); prune_pairs(checklist->pairs); @@ -741,22 +763,24 @@ static status_t process_payloads(message_t *message, check_t *check) if (payload->get_type(payload) != NOTIFY) { DBG1(DBG_IKE, "ignoring payload of type '%N' while processing " - "connectivity check", payload_type_names, payload->get_type(payload)); + "connectivity check", payload_type_names, + payload->get_type(payload)); continue; } - + notify_payload_t *notify = (notify_payload_t*)payload; - + switch (notify->get_notify_type(notify)) { case ME_ENDPOINT: { if (check->endpoint) { - DBG1(DBG_IKE, "connectivity check contains multiple ME_ENDPOINT notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple " + "ME_ENDPOINT notifies"); break; } - + endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify); if (!endpoint) { @@ -772,7 +796,8 @@ static status_t process_payloads(message_t *message, check_t *check) { if (check->connect_id.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTID notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple " + "ME_CONNECTID notifies"); break; } check->connect_id = chunk_clone(notify->get_notification_data(notify)); @@ -783,7 +808,8 @@ static status_t process_payloads(message_t *message, check_t *check) { if (check->auth.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTAUTH notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple " + "ME_CONNECTAUTH notifies"); break; } check->auth = chunk_clone(notify->get_notification_data(notify)); @@ -795,38 +821,40 @@ static status_t process_payloads(message_t *message, check_t *check) } } enumerator->destroy(enumerator); - + if (!check->connect_id.ptr || !check->endpoint || !check->auth.ptr) { - DBG1(DBG_IKE, "at least one payload was missing from the connectivity check"); + DBG1(DBG_IKE, "at least one required payload was missing from the " + "connectivity check"); return FAILED; } - + return SUCCESS; } /** * Builds the signature for a connectivity check */ -static chunk_t build_signature(private_connect_manager_t *this, +static chunk_t build_signature(private_connect_manager_t *this, check_list_t *checklist, check_t *check, bool outbound) { u_int32_t mid; chunk_t mid_chunk, key_chunk, sig_chunk; chunk_t sig_hash; - + mid = htonl(check->mid); mid_chunk = chunk_from_thing(mid); - + key_chunk = (checklist->is_initiator && outbound) || (!checklist->is_initiator && !outbound) ? checklist->initiator.key : checklist->responder.key; - + /* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */ - sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id, check->endpoint_raw, key_chunk); + sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id, + check->endpoint_raw, key_chunk); this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash); DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk); DBG3(DBG_IKE, "sig_hash %#B", &sig_hash); - + chunk_free(&sig_chunk); return sig_hash; } @@ -837,7 +865,7 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli /** * After one of the initiator's pairs has succeeded we finish the checks without - * waiting for all the timeouts + * waiting for all the timeouts */ static job_requeue_t initiator_finish(callback_data_t *data) { @@ -848,23 +876,24 @@ static job_requeue_t initiator_finish(callback_data_t *data) check_list_t *checklist; if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish connectivity checks", - &data->connect_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish " + "connectivity checks", &data->connect_id); this->mutex->unlock(this->mutex); return JOB_REQUEUE_NONE; } - + finish_checks(this, checklist); - + this->mutex->unlock(this->mutex); - + return JOB_REQUEUE_NONE; } /** * Updates the state of the whole checklist */ -static void update_checklist_state(private_connect_manager_t *this, check_list_t *checklist) +static void update_checklist_state(private_connect_manager_t *this, + check_list_t *checklist) { iterator_t *iterator; endpoint_pair_t *current; @@ -891,21 +920,22 @@ static void update_checklist_state(private_connect_manager_t *this, check_list_t } } iterator->destroy(iterator); - + if (checklist->is_initiator && succeeded && !checklist->is_finishing) { /* instead of waiting until all checks have finished (i.e. all * retransmissions have failed) the initiator finishes the checks * right after the first check has succeeded. to allow a probably * better pair to succeed, we still wait a certain time */ - DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'", &checklist->connect_id); - + DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'", + &checklist->connect_id); + callback_data_t *data = callback_data_create(this, checklist->connect_id); job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiator_finish, data, (callback_job_cleanup_t)callback_data_destroy, NULL); charon->scheduler->schedule_job_ms(charon->scheduler, job, ME_WAIT_TO_FINISH); checklist->is_finishing = TRUE; } - + if (in_progress) { checklist->state = CHECK_IN_PROGRESS; @@ -926,48 +956,48 @@ static void update_checklist_state(private_connect_manager_t *this, check_list_t static job_requeue_t retransmit(callback_data_t *data) { private_connect_manager_t *this = data->connect_manager; - + this->mutex->lock(this->mutex); check_list_t *checklist; if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit connectivity check", - &data->connect_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit " + "connectivity check", &data->connect_id); this->mutex->unlock(this->mutex); return JOB_REQUEUE_NONE; } - + endpoint_pair_t *pair; if (get_pair_by_id(checklist, data->mid, &pair) != SUCCESS) { - DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit connectivity check", - data->mid); + DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit " + "connectivity check", data->mid); goto retransmit_end; } - + if (pair->state != CHECK_IN_PROGRESS) { - DBG2(DBG_IKE, "pair with id '%d' is in wrong state [%d], don't retransmit the connectivity check", - data->mid, pair->state); + DBG2(DBG_IKE, "pair with id '%d' is in wrong state [%d], don't " + "retransmit the connectivity check", data->mid, pair->state); goto retransmit_end; } - + if (++pair->retransmitted > ME_MAX_RETRANS) { DBG2(DBG_IKE, "pair with id '%d' failed after %d retransmissions", - data->mid, ME_MAX_RETRANS); + data->mid, ME_MAX_RETRANS); pair->state = CHECK_FAILED; goto retransmit_end; } - + charon->sender->send(charon->sender, pair->packet->clone(pair->packet)); - + queue_retransmission(this, checklist, pair); retransmit_end: update_checklist_state(this, checklist); - + switch(checklist->state) { case CHECK_SUCCEEDED: @@ -977,9 +1007,9 @@ retransmit_end: default: break; } - + this->mutex->unlock(this->mutex); - + /* we reschedule it manually */ return JOB_REQUEUE_NONE; } @@ -991,15 +1021,16 @@ static void queue_retransmission(private_connect_manager_t *this, check_list_t * { callback_data_t *data = retransmit_data_create(this, checklist->connect_id, pair->id); job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL); - + u_int32_t retransmission = pair->retransmitted + 1; u_int32_t rto = ME_INTERVAL; if (retransmission > ME_BOOST) { rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST)); } - DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", retransmission, pair->id, rto); - + DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", + retransmission, pair->id, rto); + charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*)job, rto); } @@ -1015,28 +1046,28 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, message->set_request(message, request); message->set_destination(message, check->dst->clone(check->dst)); message->set_source(message, check->src->clone(check->src)); - + ike_sa_id_t *ike_sa_id = ike_sa_id_create(0, 0, request); message->set_ike_sa_id(message, ike_sa_id); ike_sa_id->destroy(ike_sa_id); message->add_notify(message, FALSE, ME_CONNECTID, check->connect_id); DBG2(DBG_IKE, "send ME_CONNECTID %#B", &check->connect_id); - + notify_payload_t *endpoint = check->endpoint->build_notify(check->endpoint); check->endpoint_raw = chunk_clone(endpoint->get_notification_data(endpoint)); message->add_payload(message, (payload_t*)endpoint); DBG2(DBG_IKE, "send ME_ENDPOINT notify"); - + check->auth = build_signature(this, checklist, check, TRUE); message->add_notify(message, FALSE, ME_CONNECTAUTH, check->auth); DBG2(DBG_IKE, "send ME_CONNECTAUTH %#B", &check->auth); - + packet_t *packet; if (message->generate(message, NULL, NULL, &packet) == SUCCESS) { charon->sender->send(charon->sender, packet->clone(packet)); - + if (request) { DESTROY_IF(pair->packet); @@ -1055,18 +1086,18 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, /** * Queues a triggered check */ -static void queue_triggered_check(private_connect_manager_t *this, +static void queue_triggered_check(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair) { DBG2(DBG_IKE, "queueing triggered check for pair '%d'", pair->id); - pair->state = CHECK_WAITING; - checklist->triggered->insert_last(checklist->triggered, pair); - - if (!checklist->sender) - { - /* if the sender is not running we restart it */ - schedule_checks(this, checklist, ME_INTERVAL); - } + pair->state = CHECK_WAITING; + checklist->triggered->insert_last(checklist->triggered, pair); + + if (!checklist->sender) + { + /* if the sender is not running we restart it */ + schedule_checks(this, checklist, ME_INTERVAL); + } } /** @@ -1077,26 +1108,27 @@ static job_requeue_t sender(callback_data_t *data) private_connect_manager_t *this = data->connect_manager; this->mutex->lock(this->mutex); - + check_list_t *checklist; if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send connectivity check", - &data->connect_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send " + "connectivity check", &data->connect_id); this->mutex->unlock(this->mutex); return JOB_REQUEUE_NONE; } - + /* reset the sender */ checklist->sender = NULL; - + endpoint_pair_t *pair; if (get_triggered_pair(checklist, &pair) != SUCCESS) { DBG1(DBG_IKE, "no triggered check queued, sending an ordinary check"); - + if (checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_waiting_pair, (void**)&pair) != SUCCESS) + (linked_list_match_t)match_waiting_pair, + (void**)&pair) != SUCCESS) { this->mutex->unlock(this->mutex); DBG1(DBG_IKE, "no pairs in waiting state, aborting"); @@ -1113,19 +1145,20 @@ static job_requeue_t sender(callback_data_t *data) check->src = pair->local->clone(pair->local); check->dst = pair->remote->clone(pair->remote); check->connect_id = chunk_clone(checklist->connect_id); - check->endpoint = endpoint_notify_create(); - + check->endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE, NULL, + NULL); + pair->state = CHECK_IN_PROGRESS; - + send_check(this, checklist, check, pair, TRUE); - + check_destroy(check); - + /* schedule this job again */ schedule_checks(this, checklist, ME_INTERVAL); - + this->mutex->unlock(this->mutex); - + /* we reschedule it manually */ return JOB_REQUEUE_NONE; } @@ -1147,7 +1180,7 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) { check_list_t *checklist = data->checklist; initiated_t *initiated = data->initiated; - + endpoint_pair_t *pair; if (get_best_valid_pair(checklist, &pair) == SUCCESS) { @@ -1169,7 +1202,7 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) { /* this should (can?) not happen */ } - + return JOB_REQUEUE_NONE; } @@ -1186,7 +1219,7 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli { remove_checklist(this, checklist); remove_initiated(this, initiated); - + initiate_data_t *data = initiate_data_create(checklist, initiated); job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiate_mediated, data, (callback_job_cleanup_t)initiate_data_destroy, NULL); charon->processor->queue_job(charon->processor, job); @@ -1194,8 +1227,8 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli } else { - DBG1(DBG_IKE, "there is no mediated connection waiting between '%Y' " - "and '%Y'", checklist->initiator.id, checklist->responder.id); + DBG1(DBG_IKE, "there is no mediated connection waiting between '%Y'" + " and '%Y'", checklist->initiator.id, checklist->responder.id); } } } @@ -1210,28 +1243,30 @@ static void process_response(private_connect_manager_t *this, check_t *check, if (get_pair_by_id(checklist, check->mid, &pair) == SUCCESS) { if (pair->local->equals(pair->local, check->dst) && - pair->remote->equals(pair->remote, check->src)) + pair->remote->equals(pair->remote, check->src)) { - DBG1(DBG_IKE, "endpoint pair '%d' is valid: '%#H' - '%#H'", pair->id, - pair->local, pair->remote); + DBG1(DBG_IKE, "endpoint pair '%d' is valid: '%#H' - '%#H'", + pair->id, pair->local, pair->remote); pair->state = CHECK_SUCCEEDED; } - + linked_list_t *local_endpoints = checklist->is_initiator ? checklist->initiator.endpoints : checklist->responder.endpoints; - + endpoint_notify_t *local_endpoint; if (endpoints_contain(local_endpoints, - check->endpoint->get_host(check->endpoint), &local_endpoint) != SUCCESS) + check->endpoint->get_host(check->endpoint), + &local_endpoint) != SUCCESS) { local_endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE, check->endpoint->get_host(check->endpoint), pair->local); - local_endpoint->set_priority(local_endpoint, check->endpoint->get_priority(check->endpoint)); + local_endpoint->set_priority(local_endpoint, + check->endpoint->get_priority(check->endpoint)); local_endpoints->insert_last(local_endpoints, local_endpoint); } - + update_checklist_state(this, checklist); - + switch(checklist->state) { case CHECK_SUCCEEDED: @@ -1249,31 +1284,35 @@ static void process_response(private_connect_manager_t *this, check_t *check, } static void process_request(private_connect_manager_t *this, check_t *check, - check_list_t *checklist) + check_list_t *checklist) { linked_list_t *remote_endpoints = checklist->is_initiator ? checklist->responder.endpoints : checklist->initiator.endpoints; - + endpoint_notify_t *peer_reflexive, *remote_endpoint; - peer_reflexive = endpoint_notify_create_from_host(PEER_REFLEXIVE, check->src, NULL); - peer_reflexive->set_priority(peer_reflexive, check->endpoint->get_priority(check->endpoint)); - + peer_reflexive = endpoint_notify_create_from_host(PEER_REFLEXIVE, + check->src, NULL); + peer_reflexive->set_priority(peer_reflexive, + check->endpoint->get_priority(check->endpoint)); + if (endpoints_contain(remote_endpoints, check->src, &remote_endpoint) != SUCCESS) { remote_endpoint = peer_reflexive->clone(peer_reflexive); remote_endpoints->insert_last(remote_endpoints, remote_endpoint); } - + endpoint_pair_t *pair; - if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, &pair) == SUCCESS) + if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, + &pair) == SUCCESS) { switch(pair->state) { case CHECK_IN_PROGRESS: /* prevent retransmissions */ pair->retransmitted = ME_MAX_RETRANS; - /* FIXME: we should wait to the next rto to send the triggered check - * fall-through */ + /* FIXME: we should wait to the next rto to send the triggered + * check */ + /* fall-through */ case CHECK_WAITING: case CHECK_FAILED: queue_triggered_check(this, checklist, pair); @@ -1286,31 +1325,30 @@ static void process_request(private_connect_manager_t *this, check_t *check, else { endpoint_notify_t *local_endpoint = endpoint_notify_create_from_host(HOST, check->dst, NULL); - + endpoint_notify_t *initiator = checklist->is_initiator ? local_endpoint : remote_endpoint; endpoint_notify_t *responder = checklist->is_initiator ? remote_endpoint : local_endpoint; - + pair = endpoint_pair_create(initiator, responder, checklist->is_initiator); pair->id = checklist->pairs->get_count(checklist->pairs) + 1; - + insert_pair_by_priority(checklist->pairs, pair); - + queue_triggered_check(this, checklist, pair); - + local_endpoint->destroy(local_endpoint); } - - + check_t *response = check_create(); - + response->mid = check->mid; response->src = check->dst->clone(check->dst); response->dst = check->src->clone(check->src); response->connect_id = chunk_clone(check->connect_id); response->endpoint = peer_reflexive; - + send_check(this, checklist, response, pair, FALSE); - + check_destroy(response); } @@ -1327,35 +1365,35 @@ static void process_check(private_connect_manager_t *this, message_t *message) message->get_message_id(message)); return; } - + check_t *check = check_create(); check->mid = message->get_message_id(message); check->src = message->get_source(message); check->src = check->src->clone(check->src); check->dst = message->get_destination(message); check->dst = check->dst->clone(check->dst); - + if (process_payloads(message, check) != SUCCESS) { DBG1(DBG_IKE, "invalid connectivity check %s received", - message->get_request(message) ? "request" : "response"); + message->get_request(message) ? "request" : "response"); check_destroy(check); return; } - + this->mutex->lock(this->mutex); - + check_list_t *checklist; if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS) { DBG1(DBG_IKE, "checklist with id '%#B' not found", - &check->connect_id); + &check->connect_id); check_destroy(check); this->mutex->unlock(this->mutex); return; } - - chunk_t sig = build_signature(this, checklist, check, FALSE); + + chunk_t sig = build_signature(this, checklist, check, FALSE); if (!chunk_equals(sig, check->auth)) { DBG1(DBG_IKE, "connectivity check verification failed"); @@ -1365,7 +1403,7 @@ static void process_check(private_connect_manager_t *this, message_t *message) return; } chunk_free(&sig); - + if (message->get_request(message)) { process_request(this, check, checklist); @@ -1374,9 +1412,9 @@ static void process_check(private_connect_manager_t *this, message_t *message) { process_response(this, check, checklist); } - + this->mutex->unlock(this->mutex); - + check_destroy(check); } @@ -1394,16 +1432,19 @@ static bool check_and_register(private_connect_manager_t *this, if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS) { - DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'", peer_id); + DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'", + peer_id); initiated = initiated_create(id, peer_id); this->initiated->insert_last(this->initiated, initiated); already_there = FALSE; } - - if (initiated->mediated->find_first(initiated->mediated, - (linked_list_match_t)mediated_sa->equals, NULL, mediated_sa) != SUCCESS) + + if (initiated->mediated->find_first(initiated->mediated, + (linked_list_match_t)mediated_sa->equals, + NULL, mediated_sa) != SUCCESS) { - initiated->mediated->insert_last(initiated->mediated, mediated_sa->clone(mediated_sa)); + initiated->mediated->insert_last(initiated->mediated, + mediated_sa->clone(mediated_sa)); } this->mutex->unlock(this->mutex); @@ -1414,8 +1455,9 @@ static bool check_and_register(private_connect_manager_t *this, /** * Implementation of connect_manager_t.check_and_initiate. */ -static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *mediation_sa, - identification_t *id, identification_t *peer_id) +static void check_and_initiate(private_connect_manager_t *this, + ike_sa_id_t *mediation_sa, identification_t *id, + identification_t *peer_id) { initiated_t *initiated; @@ -1427,12 +1469,14 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med this->mutex->unlock(this->mutex); return; } - + ike_sa_id_t *waiting_sa; - iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE); + iterator_t *iterator = initiated->mediated->create_iterator( + initiated->mediated, TRUE); while (iterator->iterate(iterator, (void**)&waiting_sa)) { - job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, waiting_sa); + job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, + waiting_sa); charon->processor->queue_job(charon->processor, job); } iterator->destroy(iterator); @@ -1444,26 +1488,29 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med * Implementation of connect_manager_t.set_initiator_data. */ static status_t set_initiator_data(private_connect_manager_t *this, - identification_t *initiator, identification_t *responder, - chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator) + identification_t *initiator, + identification_t *responder, + chunk_t connect_id, chunk_t key, + linked_list_t *endpoints, bool is_initiator) { check_list_t *checklist; - - this->mutex->lock(this->mutex); - + + this->mutex->lock(this->mutex); + if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS) { DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting", - &connect_id); + &connect_id); this->mutex->unlock(this->mutex); return FAILED; } - - checklist = check_list_create(initiator, responder, connect_id, key, endpoints, is_initiator); + + checklist = check_list_create(initiator, responder, connect_id, key, + endpoints, is_initiator); this->checklists->insert_last(this->checklists, checklist); - + this->mutex->unlock(this->mutex); - + return SUCCESS; } @@ -1471,31 +1518,33 @@ static status_t set_initiator_data(private_connect_manager_t *this, * Implementation of connect_manager_t.set_responder_data. */ static status_t set_responder_data(private_connect_manager_t *this, - chunk_t connect_id, chunk_t key, linked_list_t *endpoints) + chunk_t connect_id, chunk_t key, + linked_list_t *endpoints) { check_list_t *checklist; this->mutex->lock(this->mutex); - + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) { DBG1(DBG_IKE, "checklist with id '%#B' not found", - &connect_id); + &connect_id); this->mutex->unlock(this->mutex); return NOT_FOUND; } - + checklist->responder.key = chunk_clone(key); - checklist->responder.endpoints = endpoints->clone_offset(endpoints, offsetof(endpoint_notify_t, clone)); + checklist->responder.endpoints = endpoints->clone_offset(endpoints, + offsetof(endpoint_notify_t, clone)); checklist->state = CHECK_WAITING; - + build_pairs(checklist); - + /* send the first check immediately */ schedule_checks(this, checklist, 0); - + this->mutex->unlock(this->mutex); - + return SUCCESS; } @@ -1507,22 +1556,22 @@ static status_t stop_checks(private_connect_manager_t *this, chunk_t connect_id) check_list_t *checklist; this->mutex->lock(this->mutex); - + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) { DBG1(DBG_IKE, "checklist with id '%#B' not found", - &connect_id); + &connect_id); this->mutex->unlock(this->mutex); return NOT_FOUND; } - + DBG1(DBG_IKE, "removing checklist with id '%#B'", &connect_id); - + remove_checklist(this, checklist); check_list_destroy(checklist); - + this->mutex->unlock(this->mutex); - + return SUCCESS; } @@ -1532,12 +1581,12 @@ static status_t stop_checks(private_connect_manager_t *this, chunk_t connect_id) static void destroy(private_connect_manager_t *this) { this->mutex->lock(this->mutex); - + this->hasher->destroy(this->hasher); this->checklists->destroy_function(this->checklists, (void*)check_list_destroy); this->initiated->destroy_function(this->initiated, (void*)initiated_destroy); - - this->mutex->unlock(this->mutex); + + this->mutex->unlock(this->mutex); this->mutex->destroy(this->mutex); free(this); } @@ -1556,7 +1605,7 @@ connect_manager_t *connect_manager_create() this->public.set_responder_data = (status_t(*)(connect_manager_t*,chunk_t,chunk_t,linked_list_t*))set_responder_data; this->public.process_check = (void(*)(connect_manager_t*,message_t*))process_check; this->public.stop_checks = (status_t(*)(connect_manager_t*,chunk_t))stop_checks; - + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (this->hasher == NULL) { @@ -1564,11 +1613,11 @@ connect_manager_t *connect_manager_create() free(this); return NULL; } - + this->checklists = linked_list_create(); this->initiated = linked_list_create(); - + this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); - + return (connect_manager_t*)this; } diff --git a/src/charon/sa/connect_manager.h b/src/charon/sa/connect_manager.h index b5abc853c..8fa8ff697 100644 --- a/src/charon/sa/connect_manager.h +++ b/src/charon/sa/connect_manager.h @@ -32,79 +32,84 @@ typedef struct connect_manager_t connect_manager_t; * connection with another peer. */ struct connect_manager_t { - + /** * Checks if a there is already a mediated connection registered * between two peers. - * - * @param id my id - * @param peer_id the other peer's id - * @param mediated_sa the IKE_SA ID of the mediated connection - * @returns - * - TRUE, if there was already a mediated connection registered - * - FALSE, otherwise + * + * @param id my id + * @param peer_id the other peer's id + * @param mediated_sa the IKE_SA ID of the mediated connection + * @returns + * - TRUE, if a mediated connection is registered + * - FALSE, otherwise */ - bool (*check_and_register) (connect_manager_t *this, - identification_t *id, identification_t *peer_id, ike_sa_id_t *mediated_sa); - + bool (*check_and_register) (connect_manager_t *this, identification_t *id, + identification_t *peer_id, + ike_sa_id_t *mediated_sa); + /** * Checks if there are waiting connections with a specific peer. * If so, reinitiate them. - * - * @param id my id - * @param peer_id the other peer's id + * + * @param id my id + * @param peer_id the other peer's id */ - void (*check_and_initiate) (connect_manager_t *this, ike_sa_id_t *mediation_sa, - identification_t *id, identification_t *peer_id); - + void (*check_and_initiate) (connect_manager_t *this, + ike_sa_id_t *mediation_sa, identification_t *id, + identification_t *peer_id); + /** * Creates a checklist and sets the initiator's data. - * - * @param initiator ID of the initiator - * @param responder ID of the responder - * @param connect_id the connect ID provided by the initiator - * @param key the initiator's key - * @param endpoints the initiator's endpoints - * @param is_initiator TRUE, if the caller of this method is the initiator - * FALSE, otherwise - * @returns SUCCESS + * + * @param initiator ID of the initiator + * @param responder ID of the responder + * @param connect_id the connect ID provided by the initiator + * @param key the initiator's key + * @param endpoints the initiator's endpoints + * @param is_initiator TRUE, if the caller of this method is the initiator + * @returns SUCCESS */ status_t (*set_initiator_data) (connect_manager_t *this, - identification_t *initiator, identification_t *responder, - chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator); - + identification_t *initiator, + identification_t *responder, + chunk_t connect_id, chunk_t key, + linked_list_t *endpoints, + bool is_initiator); + /** * Updates a checklist and sets the responder's data. The checklist's * state is advanced to WAITING which means that checks will be sent. - * - * @param connect_id the connect ID - * @param chunk_t the responder's key - * @param endpoints the responder's endpoints - * @returns - * - NOT_FOUND, if the checklist has not been found - * - SUCCESS, otherwise + * + * @param connect_id the connect ID + * @param chunk_t the responder's key + * @param endpoints the responder's endpoints + * @returns + * - NOT_FOUND, if the checklist has not been found + * - SUCCESS, otherwise */ status_t (*set_responder_data) (connect_manager_t *this, - chunk_t connect_id, chunk_t key, linked_list_t *endpoints); - + chunk_t connect_id, chunk_t key, + linked_list_t *endpoints); + /** - * Stops checks for a checklist. Used after the responder received an IKE_SA_INIT - * request which contains a ME_CONNECTID payload. - * - * @param connect_id the connect ID + * Stops checks for a checklist. Called after the responder received an + * IKE_SA_INIT request which contains a ME_CONNECTID payload. + * + * @param connect_id the connect ID * @returns - * - NOT_FOUND, if the checklist has not been found - * - SUCCESS, otherwise + * - NOT_FOUND, if the checklist has not been found + * - SUCCESS, otherwise */ status_t (*stop_checks) (connect_manager_t *this, chunk_t connect_id); - + /** * Processes a connectivity check - * - * @param message the received message + * + * @param message the received message */ void (*process_check) (connect_manager_t *this, message_t *message); - + /** * Destroys the manager with all data. */ @@ -113,8 +118,8 @@ struct connect_manager_t { /** * Create a manager. - * - * @returns connect_manager_t object + * + * @returns connect_manager_t object */ connect_manager_t *connect_manager_create(void); diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index be973a2ce..975a0904a 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -16,7 +16,6 @@ * for more details. */ -#include <sys/time.h> #include <string.h> #include <sys/stat.h> #include <errno.h> @@ -41,6 +40,7 @@ #include <sa/tasks/ike_reauth.h> #include <sa/tasks/ike_delete.h> #include <sa/tasks/ike_dpd.h> +#include <sa/tasks/ike_vendor.h> #include <sa/tasks/child_create.h> #include <sa/tasks/child_delete.h> #include <sa/tasks/child_rekey.h> @@ -72,169 +72,174 @@ typedef struct attribute_entry_t attribute_entry_t; * Private data of an ike_sa_t object. */ struct private_ike_sa_t { - + /** * Public members */ ike_sa_t public; - + /** * Identifier for the current IKE_SA. */ ike_sa_id_t *ike_sa_id; - + /** * unique numerical ID for this IKE_SA. */ u_int32_t unique_id; - + /** * Current state of the IKE_SA */ ike_sa_state_t state; - + /** * IKE configuration used to set up this IKE_SA */ ike_cfg_t *ike_cfg; - + /** * Peer and authentication information to establish IKE_SA. */ peer_cfg_t *peer_cfg; - + /** * currently used authentication ruleset, local (as auth_cfg_t) */ auth_cfg_t *my_auth; - + + /** + * list of completed local authentication rounds + */ + linked_list_t *my_auths; + + /** + * list of completed remote authentication rounds + */ + linked_list_t *other_auths; + /** * currently used authentication constraints, remote (as auth_cfg_t) */ auth_cfg_t *other_auth; - + /** * Selected IKE proposal */ proposal_t *proposal; - + /** * Juggles tasks to process messages */ task_manager_t *task_manager; - + /** * Address of local host */ host_t *my_host; - + /** * Address of remote host */ host_t *other_host; - + #ifdef ME /** * Are we mediation server */ bool is_mediation_server; - + /** * Server reflexive host */ host_t *server_reflexive_host; - + /** * Connect ID */ chunk_t connect_id; #endif /* ME */ - + /** * Identification used for us */ identification_t *my_id; - + /** * Identification used for other */ identification_t *other_id; - - /** - * EAP Identity exchange in EAP-Identity method - */ - identification_t *eap_identity;; - + /** * set of extensions the peer supports */ ike_extension_t extensions; - + /** * set of condition flags currently enabled for this IKE_SA */ ike_condition_t conditions; - + /** * Linked List containing the child sa's of the current IKE_SA. */ linked_list_t *child_sas; - + /** * keymat of this IKE_SA */ keymat_t *keymat; - + /** * Virtual IP on local host, if any */ host_t *my_virtual_ip; - + /** * Virtual IP on remote host, if any */ host_t *other_virtual_ip; - + /** * List of configuration attributes (attribute_entry_t) */ linked_list_t *attributes; - + /** * list of peers additional addresses, transmitted via MOBIKE */ linked_list_t *additional_addresses; - + /** * previously value of received DESTINATION_IP hash */ chunk_t nat_detection_dest; - + /** * number pending UPDATE_SA_ADDRESS (MOBIKE) */ u_int32_t pending_updates; - + /** * NAT keep alive interval */ u_int32_t keepalive_interval; - + /** * Timestamps for this IKE_SA */ u_int32_t stats[STAT_MAX]; - + /** * how many times we have retried so far (keyingtries) */ u_int32_t keyingtry; - + /** * local host address to be used for IKE, set via MIGRATE kernel message */ host_t *local_host; - + /** * remote host address to be used for IKE, set via MIGRATE kernel message */ @@ -261,7 +266,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound) enumerator_t *enumerator; child_sa_t *child_sa; time_t use_time, current; - + if (inbound) { use_time = this->stats[STAT_INBOUND]; @@ -277,7 +282,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound) use_time = max(use_time, current); } enumerator->destroy(enumerator); - + return use_time; } @@ -363,7 +368,7 @@ static void set_peer_cfg(private_ike_sa_t *this, peer_cfg_t *peer_cfg) DESTROY_IF(this->peer_cfg); peer_cfg->get_ref(peer_cfg); this->peer_cfg = peer_cfg; - + if (this->ike_cfg == NULL) { this->ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); @@ -384,6 +389,56 @@ static auth_cfg_t* get_auth_cfg(private_ike_sa_t *this, bool local) } /** + * Implementation of ike_sa_t.add_auth_cfg + */ +static void add_auth_cfg(private_ike_sa_t *this, bool local, auth_cfg_t *cfg) +{ + if (local) + { + this->my_auths->insert_last(this->my_auths, cfg); + } + else + { + this->other_auths->insert_last(this->other_auths, cfg); + } +} + +/** + * Implementation of ike_sa_t.create_auth_cfg_enumerator + */ +static enumerator_t* create_auth_cfg_enumerator(private_ike_sa_t *this, + bool local) +{ + if (local) + { + return this->my_auths->create_enumerator(this->my_auths); + } + return this->other_auths->create_enumerator(this->other_auths); +} + +/** + * Flush the stored authentication round information + */ +static void flush_auth_cfgs(private_ike_sa_t *this) +{ + auth_cfg_t *cfg; + + if (lib->settings->get_bool(lib->settings, "charon.flush_auth_cfg", TRUE)) + { + while (this->my_auths->remove_last(this->my_auths, + (void**)&cfg) == SUCCESS) + { + cfg->destroy(cfg); + } + while (this->other_auths->remove_last(this->other_auths, + (void**)&cfg) == SUCCESS) + { + cfg->destroy(cfg); + } + } +} + +/** * Implementation of ike_sa_t.get_proposal */ static proposal_t* get_proposal(private_ike_sa_t *this) @@ -422,22 +477,22 @@ static void send_keepalive(private_ike_sa_t *this) { send_keepalive_job_t *job; time_t last_out, now, diff; - + if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0) { /* disable keep alives if we are not NATed anymore */ return; } - + last_out = get_use_time(this, FALSE); - now = time(NULL); - + now = time_monotonic(NULL); + diff = now - last_out; - + if (diff >= this->keepalive_interval) { packet_t *packet; chunk_t data; - + packet = packet_create(); packet->set_source(packet, this->my_host->clone(this->my_host)); packet->set_destination(packet, this->other_host->clone(this->other_host)); @@ -552,15 +607,15 @@ static status_t send_dpd(private_ike_sa_t *this) { job_t *job; time_t diff, delay; - + delay = this->peer_cfg->get_dpd(this->peer_cfg); - + if (delay == 0) { /* DPD disabled */ return SUCCESS; } - + if (this->task_manager->busy(this->task_manager)) { /* an exchange is in the air, no need to start a DPD check */ @@ -571,14 +626,14 @@ static status_t send_dpd(private_ike_sa_t *this) /* check if there was any inbound traffic */ time_t last_in, now; last_in = get_use_time(this, TRUE); - now = time(NULL); + now = time_monotonic(NULL); diff = now - last_in; if (diff >= delay) { /* to long ago, initiate dead peer detection */ task_t *task; ike_mobike_t *mobike; - + if (supports_extension(this, EXT_MOBIKE) && has_condition(this, COND_NAT_HERE)) { @@ -593,7 +648,7 @@ static status_t send_dpd(private_ike_sa_t *this) } diff = 0; DBG1(DBG_IKE, "sending DPD request"); - + this->task_manager->queue_task(this->task_manager, task); this->task_manager->initiate(this->task_manager); } @@ -621,7 +676,7 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) get_name(this), this->unique_id, ike_sa_state_names, this->state, ike_sa_state_names, state); - + switch (state) { case IKE_ESTABLISHED: @@ -631,14 +686,14 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) { job_t *job; u_int32_t t; - + /* calculate rekey, reauth and lifetime */ - this->stats[STAT_ESTABLISHED] = time(NULL); - + this->stats[STAT_ESTABLISHED] = time_monotonic(NULL); + /* schedule rekeying if we have a time which is smaller than * an already scheduled rekeying */ t = this->peer_cfg->get_rekey_time(this->peer_cfg); - if (t && (this->stats[STAT_REKEY] == 0 || + if (t && (this->stats[STAT_REKEY] == 0 || (this->stats[STAT_REKEY] > t + this->stats[STAT_ESTABLISHED]))) { this->stats[STAT_REKEY] = t + this->stats[STAT_ESTABLISHED]; @@ -647,7 +702,7 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) DBG1(DBG_IKE, "scheduling rekeying in %ds", t); } t = this->peer_cfg->get_reauth_time(this->peer_cfg); - if (t && (this->stats[STAT_REAUTH] == 0 || + if (t && (this->stats[STAT_REAUTH] == 0 || (this->stats[STAT_REAUTH] > t + this->stats[STAT_ESTABLISHED]))) { this->stats[STAT_REAUTH] = t + this->stats[STAT_ESTABLISHED]; @@ -677,7 +732,7 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) charon->scheduler->schedule_job(charon->scheduler, job, t); DBG1(DBG_IKE, "maximum IKE_SA lifetime %ds", t); } - + /* start DPD checks */ send_dpd(this); } @@ -687,7 +742,7 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) { /* delete may fail if a packet gets lost, so set a timeout */ job_t *job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); - charon->scheduler->schedule_job(charon->scheduler, job, + charon->scheduler->schedule_job(charon->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); break; } @@ -708,9 +763,9 @@ static void reset(private_ike_sa_t *this) { this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0); } - + set_state(this, IKE_CREATED); - + this->task_manager->reset(this->task_manager, 0, 0); } @@ -777,7 +832,7 @@ static void add_additional_address(private_ike_sa_t *this, host_t *host) { this->additional_addresses->insert_last(this->additional_addresses, host); } - + /** * Implementation of ike_sa_t.create_additional_address_iterator. */ @@ -828,7 +883,7 @@ static u_int32_t get_pending_updates(private_ike_sa_t *this) static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) { bool update = FALSE; - + if (me == NULL) { me = this->my_host; @@ -837,7 +892,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) { other = this->other_host; } - + /* apply hosts on first received message */ if (this->my_host->is_anyaddr(this->my_host) || this->other_host->is_anyaddr(this->other_host)) @@ -854,7 +909,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) set_my_host(this, me->clone(me)); update = TRUE; } - + if (!other->equals(other, this->other_host)) { /* update others adress if we are NOT NATed, @@ -867,13 +922,13 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) } } } - + /* update all associated CHILD_SAs, if required */ if (update) { iterator_t *iterator; child_sa_t *child_sa; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { @@ -896,7 +951,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) static status_t generate_message(private_ike_sa_t *this, message_t *message, packet_t **packet) { - this->stats[STAT_OUTBOUND] = time(NULL); + this->stats[STAT_OUTBOUND] = time_monotonic(NULL); message->set_ike_sa_id(message, this->ike_sa_id); return message->generate(message, this->keymat->get_crypter(this->keymat, FALSE), @@ -911,7 +966,7 @@ static void send_notify_response(private_ike_sa_t *this, message_t *request, { message_t *response; packet_t *packet; - + response = message_create(); response->set_exchange_type(response, request->get_exchange_type(request)); response->set_request(response, FALSE); @@ -989,7 +1044,7 @@ static chunk_t get_connect_id(private_ike_sa_t *this) * Implementation of ike_sa_t.respond */ static status_t respond(private_ike_sa_t *this, identification_t *peer_id, - chunk_t connect_id) + chunk_t connect_id) { ike_me_t *task = ike_me_create(&this->public, TRUE); task->respond(task, peer_id, connect_id); @@ -1012,7 +1067,8 @@ static status_t callback(private_ike_sa_t *this, identification_t *peer_id) * Implementation of ike_sa_t.relay */ static status_t relay(private_ike_sa_t *this, identification_t *requester, - chunk_t connect_id, chunk_t connect_key, linked_list_t *endpoints, bool response) + chunk_t connect_id, chunk_t connect_key, + linked_list_t *endpoints, bool response) { ike_me_t *task = ike_me_create(&this->public, TRUE); task->relay(task, requester, connect_id, connect_key, endpoints, response); @@ -1023,7 +1079,8 @@ static status_t relay(private_ike_sa_t *this, identification_t *requester, /** * Implementation of ike_sa_t.initiate_mediation */ -static status_t initiate_mediation(private_ike_sa_t *this, peer_cfg_t *mediated_cfg) +static status_t initiate_mediation(private_ike_sa_t *this, + peer_cfg_t *mediated_cfg) { ike_me_t *task = ike_me_create(&this->public, TRUE); task->connect(task, mediated_cfg->get_peer_id(mediated_cfg)); @@ -1034,14 +1091,13 @@ static status_t initiate_mediation(private_ike_sa_t *this, peer_cfg_t *mediated_ /** * Implementation of ike_sa_t.initiate_mediated */ -static status_t initiate_mediated(private_ike_sa_t *this, host_t *me, host_t *other, - chunk_t connect_id) +static status_t initiate_mediated(private_ike_sa_t *this, host_t *me, + host_t *other, chunk_t connect_id) { set_my_host(this, me->clone(me)); set_other_host(this, other->clone(other)); chunk_free(&this->connect_id); this->connect_id = chunk_clone(connect_id); - return this->task_manager->initiate(this->task_manager); } #endif /* ME */ @@ -1052,7 +1108,7 @@ static status_t initiate_mediated(private_ike_sa_t *this, host_t *me, host_t *ot static void resolve_hosts(private_ike_sa_t *this) { host_t *host; - + if (this->remote_host) { host = this->remote_host->clone(this->remote_host); @@ -1067,7 +1123,7 @@ static void resolve_hosts(private_ike_sa_t *this) { set_other_host(this, host); } - + if (this->local_host) { host = this->local_host->clone(this->local_host); @@ -1075,10 +1131,16 @@ static void resolve_hosts(private_ike_sa_t *this) } else { + int family = 0; + + /* use same address family as for other */ + if (!this->other_host->is_anyaddr(this->other_host)) + { + family = this->other_host->get_family(this->other_host); + } host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), - this->my_host->get_family(this->my_host), - IKEV2_UDP_PORT); - + family, IKEV2_UDP_PORT); + if (host && host->is_anyaddr(host) && !this->other_host->is_anyaddr(this->other_host)) { @@ -1111,11 +1173,11 @@ static status_t initiate(private_ike_sa_t *this, traffic_selector_t *tsi, traffic_selector_t *tsr) { task_t *task; - + if (this->state == IKE_CREATED) { resolve_hosts(this); - + if (this->other_host->is_anyaddr(this->other_host) #ifdef ME && !this->peer_cfg->get_mediated_by(this->peer_cfg) @@ -1126,11 +1188,13 @@ static status_t initiate(private_ike_sa_t *this, DBG1(DBG_IKE, "unable to initiate to %%any"); return DESTROY_ME; } - + set_condition(this, COND_ORIGINAL_INITIATOR, TRUE); - + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_vendor_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_natd_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_cert_pre_create(&this->public, TRUE); @@ -1159,8 +1223,8 @@ static status_t initiate(private_ike_sa_t *this, { if (this->state == IKE_ESTABLISHED) { - /* mediation connection is already established, retrigger state change - * to notify bus listeners */ + /* mediation connection is already established, retrigger state + * change to notify bus listeners */ DBG1(DBG_IKE, "mediation connection is already up"); set_state(this, IKE_ESTABLISHED); } @@ -1190,7 +1254,7 @@ static status_t initiate(private_ike_sa_t *this, } #endif /* ME */ } - + return this->task_manager->initiate(this->task_manager); } @@ -1201,20 +1265,20 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) { status_t status; bool is_request; - + if (this->state == IKE_PASSIVE) { /* do not handle messages in passive state */ return FAILED; } - + is_request = message->get_request(message); - + status = message->parse_body(message, this->keymat->get_crypter(this->keymat, TRUE), this->keymat->get_signer(this->keymat, TRUE)); if (status != SUCCESS) { - + if (is_request) { switch (status) @@ -1258,20 +1322,19 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) exchange_type_names, message->get_exchange_type(message), message->get_request(message) ? "request" : "response", message->get_message_id(message)); - + if (this->state == IKE_CREATED) { /* invalid initiation attempt, close SA */ return DESTROY_ME; } - return status; } else { host_t *me, *other; - + me = message->get_destination(message); other = message->get_source(message); - + /* if this IKE_SA is virgin, we check for a config */ if (this->ike_cfg == NULL) { @@ -1291,7 +1354,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) charon->scheduler->schedule_job(charon->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); } - this->stats[STAT_INBOUND] = time(NULL); + this->stats[STAT_INBOUND] = time_monotonic(NULL); /* check if message is trustworthy, and update host information */ if (this->state == IKE_CREATED || this->state == IKE_CONNECTING || message->get_exchange_type(message) != IKE_SA_INIT) @@ -1301,8 +1364,14 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) update_hosts(this, me, other); } } - return this->task_manager->process_message(this->task_manager, message); + status = this->task_manager->process_message(this->task_manager, message); + if (message->get_exchange_type(message) == IKE_AUTH && + this->state == IKE_ESTABLISHED) + { /* authentication completed */ + flush_auth_cfgs(this); + } } + return status; } /** @@ -1348,23 +1417,6 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other) } /** - * Implementation of ike_sa_t.get_eap_identity. - */ -static identification_t* get_eap_identity(private_ike_sa_t *this) -{ - return this->eap_identity; -} - -/** - * Implementation of ike_sa_t.set_eap_identity. - */ -static void set_eap_identity(private_ike_sa_t *this, identification_t *id) -{ - DESTROY_IF(this->eap_identity); - this->eap_identity = id; -} - -/** * Implementation of ike_sa_t.add_child_sa. */ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) @@ -1380,7 +1432,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, { iterator_t *iterator; child_sa_t *current, *found = NULL; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -1409,7 +1461,7 @@ static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) { child_rekey_t *child_rekey; - + child_rekey = child_rekey_create(&this->public, protocol, spi); this->task_manager->queue_task(this->task_manager, &child_rekey->task); return this->task_manager->initiate(this->task_manager); @@ -1422,7 +1474,7 @@ static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) { child_delete_t *child_delete; - + child_delete = child_delete_create(&this->public, protocol, spi); this->task_manager->queue_task(this->task_manager, &child_delete->task); return this->task_manager->initiate(this->task_manager); @@ -1437,7 +1489,7 @@ static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, iterator_t *iterator; child_sa_t *child_sa; status_t status = NOT_FOUND; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { @@ -1487,9 +1539,9 @@ static status_t delete_(private_ike_sa_t *this) static status_t rekey(private_ike_sa_t *this) { ike_rekey_t *ike_rekey; - + ike_rekey = ike_rekey_create(&this->public, TRUE); - + this->task_manager->queue_task(this->task_manager, &ike_rekey->task); return this->task_manager->initiate(this->task_manager); } @@ -1510,13 +1562,13 @@ static status_t reauth(private_ike_sa_t *this) if (this->other_virtual_ip != NULL || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME - /* if we are mediation server we too cannot reauth the IKE_SA */ + /* as mediation server we too cannot reauth the IKE_SA */ || this->is_mediation_server #endif /* ME */ ) { - time_t now = time(NULL); - + time_t now = time_monotonic(NULL); + DBG1(DBG_IKE, "IKE_SA will timeout in %V", &now, &this->stats[STAT_DELETE]); return FAILED; @@ -1543,10 +1595,10 @@ static status_t reestablish(private_ike_sa_t *this) iterator_t *iterator; child_sa_t *child_sa; child_cfg_t *child_cfg; - bool required = FALSE; + bool restart = FALSE; status_t status = FAILED; - - /* check if we have children to keep up at all*/ + + /* check if we have children to keep up at all */ iterator = create_child_sa_iterator(this); while (iterator->iterate(iterator, (void**)&child_sa)) { @@ -1562,25 +1614,28 @@ static status_t reestablish(private_ike_sa_t *this) switch (action) { case ACTION_RESTART: + restart = TRUE; + break; case ACTION_ROUTE: - required = TRUE; + charon->traps->install(charon->traps, this->peer_cfg, child_cfg); + break; default: break; } } iterator->destroy(iterator); #ifdef ME - /* we initiate the new IKE_SA of the mediation connection without CHILD_SA */ + /* mediation connections have no children, keep them up anyway */ if (this->peer_cfg->is_mediation(this->peer_cfg)) { - required = TRUE; + restart = TRUE; } #endif /* ME */ - if (!required) + if (!restart) { return FAILED; } - + /* check if we are able to reestablish this IKE_SA */ if (!has_condition(this, COND_ORIGINAL_INITIATOR) && (this->other_virtual_ip != NULL || @@ -1593,7 +1648,7 @@ static status_t reestablish(private_ike_sa_t *this) DBG1(DBG_IKE, "unable to reestablish IKE_SA due asymetric setup"); return FAILED; } - + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE); new->set_peer_cfg(new, this->peer_cfg); host = this->other_host; @@ -1606,7 +1661,7 @@ static status_t reestablish(private_ike_sa_t *this) { new->set_virtual_ip(new, TRUE, host); } - + #ifdef ME if (this->peer_cfg->is_mediation(this->peer_cfg)) { @@ -1635,10 +1690,6 @@ static status_t reestablish(private_ike_sa_t *this) child_cfg->get_ref(child_cfg); status = new->initiate(new, child_cfg, 0, NULL, NULL); break; - case ACTION_ROUTE: - charon->traps->install(charon->traps, - this->peer_cfg, child_cfg); - break; default: continue; } @@ -1649,7 +1700,7 @@ static status_t reestablish(private_ike_sa_t *this) } iterator->destroy(iterator); } - + if (status == DESTROY_ME) { charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new); @@ -1669,7 +1720,7 @@ static status_t reestablish(private_ike_sa_t *this) */ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) { - this->stats[STAT_OUTBOUND] = time(NULL); + this->stats[STAT_OUTBOUND] = time_monotonic(NULL); if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) { /* send a proper signal to brief interested bus listeners */ @@ -1711,17 +1762,17 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) { u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg); - u_int32_t reauth_time = time(NULL) + lifetime - reduction; + u_int32_t reauth_time = time_monotonic(NULL) + lifetime - reduction; if (lifetime < reduction) { DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication", lifetime); charon->processor->queue_job(charon->processor, - (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE)); + (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE)); } else if (this->stats[STAT_REAUTH] == 0 || - this->stats[STAT_REAUTH] > reauth_time) + this->stats[STAT_REAUTH] > reauth_time) { this->stats[STAT_REAUTH] = reauth_time; DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication" @@ -1732,8 +1783,9 @@ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) } else { - DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, reauthentication already " - "scheduled in %ds", lifetime, this->stats[STAT_REAUTH] - time(NULL)); + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, " + "reauthentication already scheduled in %ds", lifetime, + this->stats[STAT_REAUTH] - time_monotonic(NULL)); } } @@ -1744,7 +1796,7 @@ static status_t roam(private_ike_sa_t *this, bool address) { host_t *src; ike_mobike_t *mobike; - + switch (this->state) { case IKE_CREATED: @@ -1767,7 +1819,7 @@ static status_t roam(private_ike_sa_t *this, bool address) } return SUCCESS; } - + /* keep existing path if possible */ src = charon->kernel_interface->get_source_addr(charon->kernel_interface, this->other_host, this->my_host); @@ -1782,14 +1834,14 @@ static status_t roam(private_ike_sa_t *this, bool address) return SUCCESS; } src->destroy(src); - + } else { /* check if we find a route at all */ enumerator_t *enumerator; host_t *addr; - + src = charon->kernel_interface->get_source_addr(charon->kernel_interface, this->other_host, NULL); if (!src) @@ -1818,7 +1870,7 @@ static status_t roam(private_ike_sa_t *this, bool address) src->destroy(src); } set_condition(this, COND_STALE, FALSE); - + /* update addresses with mobike, if supported ... */ if (supports_extension(this, EXT_MOBIKE)) { @@ -1837,22 +1889,16 @@ static status_t roam(private_ike_sa_t *this, bool address) * Implementation of ike_sa_t.add_configuration_attribute */ static void add_configuration_attribute(private_ike_sa_t *this, + attribute_handler_t *handler, configuration_attribute_type_t type, chunk_t data) { - attribute_entry_t *entry; - attribute_handler_t *handler; - - handler = charon->attributes->handle(charon->attributes, - &this->public, type, data); - if (handler) - { - entry = malloc_thing(attribute_entry_t); - entry->handler = handler; - entry->type = type; - entry->data = chunk_clone(data); - - this->attributes->insert_last(this->attributes, entry); - } + attribute_entry_t *entry = malloc_thing(attribute_entry_t); + + entry->handler = handler; + entry->type = type; + entry->data = chunk_clone(data); + + this->attributes->insert_last(this->attributes, entry); } /** @@ -1862,7 +1908,7 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) { child_sa_t *child_sa; attribute_entry_t *entry; - + /* apply hosts and ids */ this->my_host->destroy(this->my_host); this->other_host->destroy(this->other_host); @@ -1872,7 +1918,7 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->other_host = other->other_host->clone(other->other_host); this->my_id = other->my_id->clone(other->my_id); this->other_id = other->other_id->clone(other->other_id); - + /* apply virtual assigned IPs... */ if (other->my_virtual_ip) { @@ -1884,9 +1930,9 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->other_virtual_ip = other->other_virtual_ip; other->other_virtual_ip = NULL; } - + /* ... and configuration attributes */ - while (other->attributes->remove_last(other->attributes, + while (other->attributes->remove_last(other->attributes, (void**)&entry) == SUCCESS) { this->attributes->insert_first(this->attributes, entry); @@ -1898,7 +1944,7 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) { send_keepalive(this); } - + #ifdef ME if (other->is_mediation_server) { @@ -1913,28 +1959,28 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) /* adopt all children */ while (other->child_sas->remove_last(other->child_sas, - (void**)&child_sa) == SUCCESS) + (void**)&child_sa) == SUCCESS) { this->child_sas->insert_first(this->child_sas, (void*)child_sa); } - + /* move pending tasks to the new IKE_SA */ this->task_manager->adopt_tasks(this->task_manager, other->task_manager); - + /* reauthentication timeout survives a rekeying */ if (other->stats[STAT_REAUTH]) { - time_t reauth, delete, now = time(NULL); - + time_t reauth, delete, now = time_monotonic(NULL); + this->stats[STAT_REAUTH] = other->stats[STAT_REAUTH]; reauth = this->stats[STAT_REAUTH] - now; delete = reauth + this->peer_cfg->get_over_time(this->peer_cfg); this->stats[STAT_DELETE] = this->stats[STAT_REAUTH] + delete; DBG1(DBG_IKE, "rescheduling reauthentication in %ds after rekeying, " "lifetime reduced to %ds", reauth, delete); - charon->scheduler->schedule_job(charon->scheduler, + charon->scheduler->schedule_job(charon->scheduler, (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE), reauth); - charon->scheduler->schedule_job(charon->scheduler, + charon->scheduler->schedule_job(charon->scheduler, (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE), delete); } /* we have to initate here, there may be new tasks to handle */ @@ -1947,30 +1993,30 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) static void destroy(private_ike_sa_t *this) { attribute_entry_t *entry; - + charon->bus->set_sa(charon->bus, &this->public); - + set_state(this, IKE_DESTROYING); - + /* remove attributes first, as we pass the IKE_SA to the handler */ - while (this->attributes->remove_last(this->attributes, + while (this->attributes->remove_last(this->attributes, (void**)&entry) == SUCCESS) { - charon->attributes->release(charon->attributes, entry->handler, - &this->public, entry->type, entry->data); + lib->attributes->release(lib->attributes, entry->handler, + this->other_id, entry->type, entry->data); free(entry->data.ptr); free(entry); } this->attributes->destroy(this->attributes); - + this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); - + /* unset SA after here to avoid usage by the listeners */ charon->bus->set_sa(charon->bus, NULL); - + this->task_manager->destroy(this->task_manager); this->keymat->destroy(this->keymat); - + if (this->my_virtual_ip) { charon->kernel_interface->del_ip(charon->kernel_interface, @@ -1981,7 +2027,7 @@ static void destroy(private_ike_sa_t *this) { if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg)) { - charon->attributes->release_address(charon->attributes, + lib->attributes->release_address(lib->attributes, this->peer_cfg->get_pool(this->peer_cfg), this->other_virtual_ip, this->other_id); } @@ -1992,27 +2038,31 @@ static void destroy(private_ike_sa_t *this) #ifdef ME if (this->is_mediation_server) { - charon->mediation_manager->remove(charon->mediation_manager, this->ike_sa_id); + charon->mediation_manager->remove(charon->mediation_manager, + this->ike_sa_id); } DESTROY_IF(this->server_reflexive_host); chunk_free(&this->connect_id); #endif /* ME */ free(this->nat_detection_dest.ptr); - + DESTROY_IF(this->my_host); DESTROY_IF(this->other_host); DESTROY_IF(this->my_id); DESTROY_IF(this->other_id); DESTROY_IF(this->local_host); DESTROY_IF(this->remote_host); - DESTROY_IF(this->eap_identity); - + DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); DESTROY_IF(this->proposal); this->my_auth->destroy(this->my_auth); this->other_auth->destroy(this->other_auth); - + this->my_auths->destroy_offset(this->my_auths, + offsetof(auth_cfg_t, destroy)); + this->other_auths->destroy_offset(this->other_auths, + offsetof(auth_cfg_t, destroy)); + this->ike_sa_id->destroy(this->ike_sa_id); free(this); } @@ -2024,7 +2074,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) { private_ike_sa_t *this = malloc_thing(private_ike_sa_t); static u_int32_t unique_id = 0; - + /* Public functions */ this->public.get_state = (ike_sa_state_t (*)(ike_sa_t*)) get_state; this->public.set_state = (void (*)(ike_sa_t*,ike_sa_state_t)) set_state; @@ -2037,6 +2087,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.get_peer_cfg = (peer_cfg_t* (*)(ike_sa_t*))get_peer_cfg; this->public.set_peer_cfg = (void (*)(ike_sa_t*,peer_cfg_t*))set_peer_cfg; this->public.get_auth_cfg = (auth_cfg_t*(*)(ike_sa_t*, bool local))get_auth_cfg; + this->public.create_auth_cfg_enumerator = (enumerator_t*(*)(ike_sa_t*, bool local))create_auth_cfg_enumerator; + this->public.add_auth_cfg = (void(*)(ike_sa_t*, bool local, auth_cfg_t *cfg))add_auth_cfg; this->public.get_proposal = (proposal_t*(*)(ike_sa_t*))get_proposal; this->public.set_proposal = (void(*)(ike_sa_t*, proposal_t *proposal))set_proposal; this->public.get_id = (ike_sa_id_t* (*)(ike_sa_t*)) get_id; @@ -2050,8 +2102,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_my_id = (void (*)(ike_sa_t*,identification_t*)) set_my_id; this->public.get_other_id = (identification_t* (*)(ike_sa_t*)) get_other_id; this->public.set_other_id = (void (*)(ike_sa_t*,identification_t*)) set_other_id; - this->public.get_eap_identity = (identification_t* (*)(ike_sa_t*)) get_eap_identity; - this->public.set_eap_identity = (void (*)(ike_sa_t*,identification_t*)) set_eap_identity; this->public.enable_extension = (void(*)(ike_sa_t*, ike_extension_t extension))enable_extension; this->public.supports_extension = (bool(*)(ike_sa_t*, ike_extension_t extension))supports_extension; this->public.set_condition = (void (*)(ike_sa_t*, ike_condition_t,bool)) set_condition; @@ -2084,7 +2134,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.get_unique_id = (u_int32_t (*)(ike_sa_t*))get_unique_id; this->public.set_virtual_ip = (void (*)(ike_sa_t*,bool,host_t*))set_virtual_ip; this->public.get_virtual_ip = (host_t* (*)(ike_sa_t*,bool))get_virtual_ip; - this->public.add_configuration_attribute = (void(*)(ike_sa_t*, configuration_attribute_type_t type, chunk_t data))add_configuration_attribute; + this->public.add_configuration_attribute = (void(*)(ike_sa_t*, attribute_handler_t *handler,configuration_attribute_type_t type, chunk_t data))add_configuration_attribute; this->public.set_kmaddress = (void (*)(ike_sa_t*,host_t*,host_t*))set_kmaddress; #ifdef ME this->public.act_as_mediation_server = (void (*)(ike_sa_t*)) act_as_mediation_server; @@ -2097,7 +2147,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.callback = (status_t (*)(ike_sa_t*,identification_t*)) callback; this->public.respond = (status_t (*)(ike_sa_t*,identification_t*,chunk_t)) respond; #endif /* ME */ - + /* initialize private fields */ this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->child_sas = linked_list_create(); @@ -2106,7 +2156,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->other_host = host_create_any(AF_INET); this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); - this->eap_identity = NULL; this->extensions = 0; this->conditions = 0; this->keymat = keymat_create(ike_sa_id->is_initiator(ike_sa_id)); @@ -2114,11 +2163,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->keepalive_interval = lib->settings->get_time(lib->settings, "charon.keep_alive", KEEPALIVE_INTERVAL); memset(this->stats, 0, sizeof(this->stats)); - this->stats[STAT_INBOUND] = this->stats[STAT_OUTBOUND] = time(NULL); + this->stats[STAT_INBOUND] = this->stats[STAT_OUTBOUND] = time_monotonic(NULL); this->ike_cfg = NULL; this->peer_cfg = NULL; this->my_auth = auth_cfg_create(); this->other_auth = auth_cfg_create(); + this->my_auths = linked_list_create(); + this->other_auths = linked_list_create(); this->proposal = NULL; this->task_manager = task_manager_create(&this->public); this->unique_id = ++unique_id; @@ -2136,6 +2187,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->server_reflexive_host = NULL; this->connect_id = chunk_empty; #endif /* ME */ - + return &this->public; } diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 41d7a7976..4dce1937c 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -66,7 +66,7 @@ typedef struct ike_sa_t ike_sa_t; * Extensions (or optional features) the peer supports */ enum ike_extension_t { - + /** * peer supports NAT traversal as specified in RFC4306 */ @@ -76,58 +76,68 @@ enum ike_extension_t { * peer supports MOBIKE (RFC4555) */ EXT_MOBIKE = (1<<1), - + /** * peer supports HTTP cert lookups as specified in RFC4306 */ EXT_HASH_AND_URL = (1<<2), - + /** * peer supports multiple authentication exchanges, RFC4739 */ EXT_MULTIPLE_AUTH = (1<<3), + + /** + * peer uses strongSwan, accept private use extensions + */ + EXT_STRONGSWAN = (1<<4), + + /** + * peer supports EAP-only authentication, draft-eronen-ipsec-ikev2-eap-auth + */ + EXT_EAP_ONLY_AUTHENTICATION = (1<<5), }; /** * Conditions of an IKE_SA, change during its lifetime */ enum ike_condition_t { - + /** * Connection is natted (or faked) somewhere */ COND_NAT_ANY = (1<<0), - + /** * we are behind NAT */ COND_NAT_HERE = (1<<1), - + /** * other is behind NAT */ COND_NAT_THERE = (1<<2), - + /** * Faking NAT to enforce UDP encapsulation */ COND_NAT_FAKE = (1<<3), - + /** * peer has been authenticated using EAP at least once */ COND_EAP_AUTHENTICATED = (1<<4), - + /** * received a certificate request from the peer */ COND_CERTREQ_SEEN = (1<<5), - + /** * Local peer is the "original" IKE initiator. Unaffected from rekeying. */ COND_ORIGINAL_INITIATOR = (1<<6), - + /** * IKE_SA is stale, the peer is currently unreachable (MOBIKE) */ @@ -150,7 +160,7 @@ enum statistic_t { STAT_INBOUND, /** Timestamp of last outbound IKE packet */ STAT_OUTBOUND, - + STAT_MAX }; @@ -164,7 +174,7 @@ enum statistic_t { ¦ SA_CREATED ¦ +----------------+ ¦ - on initiate()---> ¦ <----- on IKE_SA_INIT received + on initiate()---> ¦ <----- on IKE_SA_INIT received V +----------------+ ¦ SA_CONNECTING ¦ @@ -192,37 +202,37 @@ enum statistic_t { @endverbatim */ enum ike_sa_state_t { - + /** * IKE_SA just got created, but is not initiating nor responding yet. */ IKE_CREATED, - + /** * IKE_SA gets initiated actively or passively */ IKE_CONNECTING, - + /** * IKE_SA is fully established */ IKE_ESTABLISHED, - + /** * IKE_SA is managed externally and does not process messages */ IKE_PASSIVE, - + /** * IKE_SA rekeying in progress */ IKE_REKEYING, - + /** * IKE_SA is in progress of deletion */ IKE_DELETING, - + /** * IKE_SA object gets destroyed */ @@ -246,41 +256,41 @@ struct ike_sa_t { /** * Get the id of the SA. - * + * * Returned ike_sa_id_t object is not getting cloned! * * @return ike_sa's ike_sa_id_t */ ike_sa_id_t* (*get_id) (ike_sa_t *this); - + /** * Get the numerical ID uniquely defining this IKE_SA. * * @return unique ID */ u_int32_t (*get_unique_id) (ike_sa_t *this); - + /** * Get the state of the IKE_SA. * * @return state of the IKE_SA */ ike_sa_state_t (*get_state) (ike_sa_t *this); - + /** * Set the state of the IKE_SA. * * @param state state to set for the IKE_SA */ void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa); - + /** * Get the name of the connection this IKE_SA uses. * * @return name */ char* (*get_name) (ike_sa_t *this); - + /** * Get statistic values from the IKE_SA. * @@ -288,35 +298,35 @@ struct ike_sa_t { * @return value as integer */ u_int32_t (*get_statistic)(ike_sa_t *this, statistic_t kind); - + /** * Get the own host address. - * + * * @return host address */ host_t* (*get_my_host) (ike_sa_t *this); - + /** * Set the own host address. - * + * * @param me host address */ void (*set_my_host) (ike_sa_t *this, host_t *me); - + /** * Get the other peers host address. - * + * * @return host address */ host_t* (*get_other_host) (ike_sa_t *this); - + /** * Set the others host address. - * + * * @param other host address */ void (*set_other_host) (ike_sa_t *this, host_t *other); - + /** * Update the IKE_SAs host. * @@ -326,79 +336,63 @@ struct ike_sa_t { * @param other new remote host address, or NULL */ void (*update_hosts)(ike_sa_t *this, host_t *me, host_t *other); - + /** * Get the own identification. - * + * * @return identification */ identification_t* (*get_my_id) (ike_sa_t *this); - + /** * Set the own identification. - * + * * @param me identification */ void (*set_my_id) (ike_sa_t *this, identification_t *me); - + /** * Get the other peer's identification. - * + * * @return identification */ identification_t* (*get_other_id) (ike_sa_t *this); - + /** * Set the other peer's identification. - * + * * @param other identification */ void (*set_other_id) (ike_sa_t *this, identification_t *other); - - /** - * Get the peers EAP identity. - * - * The EAP identity is exchanged in a EAP-Identity exchange. - * - * @return identification, NULL if none set - */ - identification_t* (*get_eap_identity) (ike_sa_t *this); - - /** - * Set the peer's EAP identity. - * - * @param id identification - */ - void (*set_eap_identity) (ike_sa_t *this, identification_t *id); - + /** * Get the config used to setup this IKE_SA. - * + * * @return ike_config */ ike_cfg_t* (*get_ike_cfg) (ike_sa_t *this); - + /** * Set the config to setup this IKE_SA. - * + * * @param config ike_config to use */ void (*set_ike_cfg) (ike_sa_t *this, ike_cfg_t* config); /** * Get the peer config used by this IKE_SA. - * + * * @return peer_config */ peer_cfg_t* (*get_peer_cfg) (ike_sa_t *this); - + /** * Set the peer config to use with this IKE_SA. - * + * * @param config peer_config to use */ void (*set_peer_cfg) (ike_sa_t *this, peer_cfg_t *config); - + /** * Get the authentication config with rules of the current auth round. * @@ -406,21 +400,37 @@ struct ike_sa_t { * @return current cfg */ auth_cfg_t* (*get_auth_cfg)(ike_sa_t *this, bool local); - + + /** + * Insert a completed authentication round. + * + * @param local TRUE for own rules, FALSE for others constraints + * @param cfg auth config to append + */ + void (*add_auth_cfg)(ike_sa_t *this, bool local, auth_cfg_t *cfg); + + /** + * Create an enumerator over added authentication rounds. + * + * @param local TRUE for own rules, FALSE for others constraints + * @return enumerator over auth_cfg_t + */ + enumerator_t* (*create_auth_cfg_enumerator)(ike_sa_t *this, bool local); + /** * Get the selected proposal of this IKE_SA. * * @return selected proposal */ proposal_t* (*get_proposal)(ike_sa_t *this); - + /** * Set the proposal selected for this IKE_SA. * * @param selected proposal */ void (*set_proposal)(ike_sa_t *this, proposal_t *proposal); - + /** * Set the message id of the IKE_SA. * @@ -431,7 +441,7 @@ struct ike_sa_t { * @param mid message id to set */ void (*set_message_id)(ike_sa_t *this, bool initiate, u_int32_t mid); - + /** * Add an additional address for the peer. * @@ -443,14 +453,14 @@ struct ike_sa_t { * @param host host to add to list */ void (*add_additional_address)(ike_sa_t *this, host_t *host); - + /** * Create an iterator over all additional addresses of the peer. * * @return iterator over addresses */ iterator_t* (*create_additional_address_iterator)(ike_sa_t *this); - + /** * Check if mappings have changed on a NAT for our source address. * @@ -458,7 +468,7 @@ struct ike_sa_t { * @return TRUE if mappings have changed */ bool (*has_mapping_changed)(ike_sa_t *this, chunk_t hash); - + /** * Enable an extension the peer supports. * @@ -468,7 +478,7 @@ struct ike_sa_t { * @param extension extension to enable */ void (*enable_extension)(ike_sa_t *this, ike_extension_t extension); - + /** * Check if the peer supports an extension. * @@ -476,7 +486,7 @@ struct ike_sa_t { * @return TRUE if peer supports it, FALSE otherwise */ bool (*supports_extension)(ike_sa_t *this, ike_extension_t extension); - + /** * Enable/disable a condition flag for this IKE_SA. * @@ -492,150 +502,152 @@ struct ike_sa_t { * @return TRUE if condition flag set, FALSE otherwise */ bool (*has_condition) (ike_sa_t *this, ike_condition_t condition); - + /** * Get the number of queued MOBIKE address updates. * * @return number of pending updates */ u_int32_t (*get_pending_updates)(ike_sa_t *this); - + /** * Set the number of queued MOBIKE address updates. * * @param updates number of pending updates */ void (*set_pending_updates)(ike_sa_t *this, u_int32_t updates); - + #ifdef ME /** * Activate mediation server functionality for this IKE_SA. */ void (*act_as_mediation_server) (ike_sa_t *this); - + /** * Get the server reflexive host. - * + * * @return server reflexive host */ host_t* (*get_server_reflexive_host) (ike_sa_t *this); - + /** * Set the server reflexive host. - * + * * @param host server reflexive host */ void (*set_server_reflexive_host) (ike_sa_t *this, host_t *host); - + /** * Get the connect ID. - * + * * @return connect ID */ chunk_t (*get_connect_id) (ike_sa_t *this); - + /** * Initiate the mediation of a mediated connection (i.e. initiate a - * ME_CONNECT exchange). - * - * @param mediated_cfg peer_cfg of the mediated connection - * @return - * - SUCCESS if initialization started - * - DESTROY_ME if initialization failed + * ME_CONNECT exchange to a mediation server). + * + * @param mediated_cfg peer_cfg of the mediated connection + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed */ status_t (*initiate_mediation) (ike_sa_t *this, peer_cfg_t *mediated_cfg); - + /** * Initiate the mediated connection - * - * @param me local endpoint (gets cloned) - * @param other remote endpoint (gets cloned) - * @param connect_id connect ID (gets cloned) - * @return - * - SUCCESS if initialization started - * - DESTROY_ME if initialization failed + * + * @param me local endpoint (gets cloned) + * @param other remote endpoint (gets cloned) + * @param connect_id connect ID (gets cloned) + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed */ status_t (*initiate_mediated) (ike_sa_t *this, host_t *me, host_t *other, - chunk_t connect_id); - + chunk_t connect_id); + /** - * Relay data from one peer to another (i.e. initiate a - * ME_CONNECT exchange). + * Relay data from one peer to another (i.e. initiate a ME_CONNECT exchange + * to a peer). * * Data is cloned. - * - * @param requester ID of the requesting peer - * @param connect_id data of the ME_CONNECTID payload - * @param connect_key data of the ME_CONNECTKEY payload - * @param endpoints endpoints - * @param response TRUE if this is a response - * @return - * - SUCCESS if relay started - * - DESTROY_ME if relay failed - */ - status_t (*relay) (ike_sa_t *this, identification_t *requester, chunk_t connect_id, - chunk_t connect_key, linked_list_t *endpoints, bool response); - + * + * @param requester ID of the requesting peer + * @param connect_id data of the ME_CONNECTID payload + * @param connect_key data of the ME_CONNECTKEY payload + * @param endpoints endpoints + * @param response TRUE if this is a response + * @return + * - SUCCESS if relay started + * - DESTROY_ME if relay failed + */ + status_t (*relay) (ike_sa_t *this, identification_t *requester, + chunk_t connect_id, chunk_t connect_key, + linked_list_t *endpoints, bool response); + /** * Send a callback to a peer. - * + * * Data is cloned. - * - * @param peer_id ID of the other peer + * + * @param peer_id ID of the other peer * @return - * - SUCCESS if response started - * - DESTROY_ME if response failed + * - SUCCESS if response started + * - DESTROY_ME if response failed */ status_t (*callback) (ike_sa_t *this, identification_t *peer_id); - + /** * Respond to a ME_CONNECT request. - * + * * Data is cloned. - * - * @param peer_id ID of the other peer - * @param connect_id the connect ID supplied by the initiator + * + * @param peer_id ID of the other peer + * @param connect_id the connect ID supplied by the initiator * @return - * - SUCCESS if response started - * - DESTROY_ME if response failed + * - SUCCESS if response started + * - DESTROY_ME if response failed */ - status_t (*respond) (ike_sa_t *this, identification_t *peer_id, chunk_t connect_id); + status_t (*respond) (ike_sa_t *this, identification_t *peer_id, + chunk_t connect_id); #endif /* ME */ - + /** * Initiate a new connection. * * The configs are owned by the IKE_SA after the call. If the initiate * is triggered by a packet, traffic selectors of the packet can be added * to the CHILD_SA. - * + * * @param child_cfg child config to create CHILD from * @param reqid reqid to use for CHILD_SA, 0 assigne uniquely * @param tsi source of triggering packet * @param tsr destination of triggering packet. - * @return + * @return * - SUCCESS if initialization started * - DESTROY_ME if initialization failed */ status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg, u_int32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr); - + /** * Initiates the deletion of an IKE_SA. - * + * * Sends a delete message to the remote peer and waits for * its response. If the response comes in, or a timeout occurs, * the IKE SA gets deleted. - * + * * @return * - SUCCESS if deletion is initialized - * - DESTROY_ME, if the IKE_SA is not in + * - DESTROY_ME, if the IKE_SA is not in * an established state and can not be * deleted (but destroyed). */ status_t (*delete) (ike_sa_t *this); - + /** * Update IKE_SAs after network interfaces have changed. * @@ -649,61 +661,61 @@ struct ike_sa_t { * @return SUCCESS, FAILED, DESTROY_ME */ status_t (*roam)(ike_sa_t *this, bool address); - + /** * Processes a incoming IKEv2-Message. * - * Message processing may fail. If a critical failure occurs, - * process_message() return DESTROY_ME. Then the caller must + * Message processing may fail. If a critical failure occurs, + * process_message() return DESTROY_ME. Then the caller must * destroy the IKE_SA immediatly, as it is unusable. - * + * * @param message message to process - * @return + * @return * - SUCCESS * - FAILED * - DESTROY_ME if this IKE_SA MUST be deleted */ status_t (*process_message) (ike_sa_t *this, message_t *message); - + /** * Generate a IKE message to send it to the peer. - * + * * This method generates all payloads in the message and encrypts/signs * the packet. - * + * * @param message message to generate * @param packet generated output packet - * @return + * @return * - SUCCESS * - FAILED * - DESTROY_ME if this IKE_SA MUST be deleted */ status_t (*generate_message) (ike_sa_t *this, message_t *message, packet_t **packet); - + /** * Retransmits a request. - * + * * @param message_id ID of the request to retransmit * @return * - SUCCESS * - NOT_FOUND if request doesn't have to be retransmited */ status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); - + /** * Sends a DPD request to the peer. * * To check if a peer is still alive, periodic * empty INFORMATIONAL messages are sent if no * other traffic was received. - * + * * @return * - SUCCESS * - DESTROY_ME, if peer did not respond */ status_t (*send_dpd) (ike_sa_t *this); - + /** * Sends a keep alive packet. * @@ -713,39 +725,39 @@ struct ike_sa_t { * was sent. */ void (*send_keepalive) (ike_sa_t *this); - + /** * Get the keying material of this IKE_SA. * * @return per IKE_SA keymat instance */ keymat_t* (*get_keymat)(ike_sa_t *this); - + /** * Associates a child SA to this IKE SA - * + * * @param child_sa child_sa to add */ void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); - + /** * Get a CHILD_SA identified by protocol and SPI. - * + * * @param protocol protocol of the SA * @param spi SPI of the CHILD_SA * @param inbound TRUE if SPI is inbound, FALSE if outbound * @return child_sa, or NULL if none found */ - child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, + child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi, bool inbound); - + /** * Create an iterator over all CHILD_SAs. - * + * * @return iterator */ iterator_t* (*create_child_sa_iterator) (ike_sa_t *this); - + /** * Rekey the CHILD SA with the specified reqid. * @@ -814,14 +826,14 @@ struct ike_sa_t { * @return DESTROY_ME to destroy the IKE_SA */ status_t (*reestablish) (ike_sa_t *this); - + /** * Set the lifetime limit received from a AUTH_LIFETIME notify. * * @param lifetime lifetime in seconds */ void (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); - + /** * Set the virtual IP to use for this IKE_SA and its children. * @@ -832,7 +844,7 @@ struct ike_sa_t { * @param ip IP to set as virtual IP */ void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); - + /** * Get the virtual IP configured. * @@ -840,7 +852,7 @@ struct ike_sa_t { * @return host_t *virtual IP */ host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); - + /** * Register a configuration attribute to the IKE_SA. * @@ -853,8 +865,9 @@ struct ike_sa_t { * @param data associated attribute data */ void (*add_configuration_attribute)(ike_sa_t *this, + attribute_handler_t *handler, configuration_attribute_type_t type, chunk_t data); - + /** * Set local and remote host addresses to be used for IKE. * @@ -865,7 +878,7 @@ struct ike_sa_t { * @param remote remote kmaddress */ void (*set_kmaddress) (ike_sa_t *this, host_t *local, host_t *remote); - + /** * Inherit all attributes of other to this after rekeying. * @@ -877,12 +890,12 @@ struct ike_sa_t { * @return DESTROY_ME if initiation of inherited task failed */ status_t (*inherit) (ike_sa_t *this, ike_sa_t *other); - + /** * Reset the IKE_SA, useable when initiating fails */ void (*reset) (ike_sa_t *this); - + /** * Destroys a ike_sa_t object. */ diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h index 377e64e8a..a833aa9d6 100644 --- a/src/charon/sa/ike_sa_id.h +++ b/src/charon/sa/ike_sa_id.h @@ -67,21 +67,21 @@ struct ike_sa_id_t { /** * 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 other ike_sa_id_t object to check if equal - * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise + * @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); /** * 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 other ike_sa_id_t object from which values will be taken + * @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); @@ -94,7 +94,7 @@ struct ike_sa_id_t { /** * Switche the original initiator flag. - * + * * @return TRUE if we are the original initator after switch, FALSE otherwise */ bool (*switch_initiator) (ike_sa_id_t *this); diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index ec1a7f741..3ef0f3bb0 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -22,7 +22,9 @@ #include <daemon.h> #include <sa/ike_sa_id.h> #include <bus/bus.h> -#include <utils/mutex.h> +#include <threading/condvar.h> +#include <threading/mutex.h> +#include <threading/rwlock.h> #include <utils/linked_list.h> #include <crypto/hashers/hasher.h> @@ -41,67 +43,67 @@ typedef struct entry_t entry_t; * An entry in the linked list, contains IKE_SA, locking and lookup data. */ struct entry_t { - + /** * 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. */ condvar_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; - + /** * Identification of an IKE_SA (SPIs). */ ike_sa_id_t *ike_sa_id; - + /** * The contained ike_sa_t object. */ ike_sa_t *ike_sa; - + /** * hash of the IKE_SA_INIT message, used to detect retransmissions */ chunk_t init_hash; - + /** * remote host address, required for DoS detection */ host_t *other; - + /** * As responder: Is this SA half-open? */ bool half_open; - + /** * own identity, required for duplicate checking */ identification_t *my_id; - + /** * remote identity, required for duplicate checking */ identification_t *other_id; - + /** * message ID currently processing, if any */ @@ -131,10 +133,10 @@ static status_t entry_destroy(entry_t *this) static entry_t *entry_create() { entry_t *this = malloc_thing(entry_t); - + this->waiting_threads = 0; this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); - + /* we set checkout flag when we really give it out */ this->checked_out = FALSE; this->driveout_new_threads = FALSE; @@ -147,7 +149,7 @@ static entry_t *entry_create() this->other_id = NULL; this->ike_sa_id = NULL; this->ike_sa = NULL; - + return this; } @@ -171,7 +173,7 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id) if (id->equals(id, entry->ike_sa_id)) { return TRUE; - } + } if ((id->get_responder_spi(id) == 0 || entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) && id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) && @@ -208,7 +210,7 @@ typedef struct half_open_t half_open_t; struct half_open_t { /** chunk of remote host address */ chunk_t other; - + /** the number of half-open IKE_SAs with that host */ u_int count; }; @@ -235,10 +237,10 @@ typedef struct connected_peers_t connected_peers_t; struct connected_peers_t { /** own identity */ identification_t *my_id; - + /** remote identity */ identification_t *other_id; - + /** list of ike_sa_id_t objects of IKE_SAs between the two identities */ linked_list_t *sas; }; @@ -269,7 +271,7 @@ typedef struct segment_t segment_t; struct segment_t { /** mutex to access a segment exclusively */ mutex_t *mutex; - + /** the number of entries in this segment */ u_int count; }; @@ -282,7 +284,7 @@ typedef struct shareable_segment_t shareable_segment_t; struct shareable_segment_t { /** rwlock to access a segment non-/exclusively */ rwlock_t *lock; - + /** the number of entries in this segment - in case of the "half-open table" * it's the sum of all half_open_t.count in a segment. */ u_int count; @@ -298,67 +300,67 @@ struct private_ike_sa_manager_t { * Public interface of ike_sa_manager_t. */ ike_sa_manager_t public; - + /** * Hash table with entries for the ike_sa_t objects. */ linked_list_t **ike_sa_table; - + /** * The size of the hash table. */ u_int table_size; - + /** * Mask to map the hashes to table rows. */ u_int table_mask; - + /** * Segments of the hash table. */ segment_t *segments; - + /** * The number of segments. */ u_int segment_count; - + /** * Mask to map a table row to a segment. */ u_int segment_mask; - + /** * Hash table with half_open_t objects. */ linked_list_t **half_open_table; - + /** * Segments of the "half-open" hash table. */ shareable_segment_t *half_open_segments; - + /** * Hash table with connected_peers_t objects. */ linked_list_t **connected_peers_table; - + /** * Segments of the "connected peers" hash table. */ shareable_segment_t *connected_peers_segments; - + /** * RNG to get random SPIs for our side */ rng_t *rng; - + /** * SHA1 hasher for IKE_SA_INIT retransmit detection */ hasher_t *hasher; - + /** * reuse existing IKE_SAs in checkout_by_config */ @@ -372,7 +374,7 @@ struct private_ike_sa_manager_t { static void lock_single_segment(private_ike_sa_manager_t *this, u_int index) { mutex_t *lock = this->segments[index & this->segment_mask].mutex; - + lock->lock(lock); } @@ -383,7 +385,7 @@ static void lock_single_segment(private_ike_sa_manager_t *this, u_int index) static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index) { mutex_t *lock = this->segments[index & this->segment_mask].mutex; - + lock->unlock(lock); } @@ -393,7 +395,7 @@ static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index) static void lock_all_segments(private_ike_sa_manager_t *this) { u_int i; - + for (i = 0; i < this->segment_count; ++i) { this->segments[i].mutex->lock(this->segments[i].mutex); @@ -406,7 +408,7 @@ static void lock_all_segments(private_ike_sa_manager_t *this) static void unlock_all_segments(private_ike_sa_manager_t *this) { u_int i; - + for (i = 0; i < this->segment_count; ++i) { this->segments[i].mutex->unlock(this->segments[i].mutex); @@ -424,27 +426,27 @@ struct private_enumerator_t { * implements enumerator interface */ enumerator_t enumerator; - + /** * associated ike_sa_manager_t */ private_ike_sa_manager_t *manager; - + /** * current segment index */ u_int segment; - + /** * currently enumerating entry */ entry_t *entry; - + /** * current table row index */ u_int row; - + /** * enumerator for the current table row */ @@ -468,7 +470,7 @@ static bool enumerate(private_enumerator_t *this, entry_t **entry, u_int *segmen if (this->current) { entry_t *item; - + if (this->current->enumerate(this->current, &item)) { *entry = this->entry = item; @@ -482,7 +484,7 @@ static bool enumerate(private_enumerator_t *this, entry_t **entry, u_int *segmen else { linked_list_t *list; - + lock_single_segment(this->manager, this->segment); if ((list = this->manager->ike_sa_table[this->row]) != NULL && list->get_count(list)) @@ -523,7 +525,7 @@ static void enumerator_destroy(private_enumerator_t *this) static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this) { private_enumerator_t *enumerator = malloc_thing(private_enumerator_t); - + enumerator->enumerator.enumerate = (void*)enumerate; enumerator->enumerator.destroy = (void*)enumerator_destroy; enumerator->manager = this; @@ -531,7 +533,7 @@ static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this) enumerator->entry = NULL; enumerator->row = 0; enumerator->current = NULL; - + return &enumerator->enumerator; } @@ -544,7 +546,7 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry) linked_list_t *list; u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask; u_int segment = row & this->segment_mask; - + lock_single_segment(this, segment); if ((list = this->ike_sa_table[row]) == NULL) { @@ -564,7 +566,7 @@ static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry) linked_list_t *list; u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask; u_int segment = row & this->segment_mask; - + if ((list = this->ike_sa_table[row]) != NULL) { entry_t *current; @@ -609,7 +611,7 @@ static status_t get_entry_by_match_function(private_ike_sa_manager_t *this, linked_list_t *list; u_int row = ike_sa_id_hash(ike_sa_id) & this->table_mask; u_int seg = row & this->segment_mask; - + lock_single_segment(this, seg); if ((list = this->ike_sa_table[row]) != NULL) { @@ -632,7 +634,7 @@ static status_t get_entry_by_match_function(private_ike_sa_manager_t *this, static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment) { - return get_entry_by_match_function(this, ike_sa_id, entry, segment, + return get_entry_by_match_function(this, ike_sa_id, entry, segment, (linked_list_match_t)entry_match_by_id, ike_sa_id, NULL); } @@ -670,7 +672,7 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry, /* we are not allowed to get this */ return FALSE; } - while (entry->checked_out && !entry->driveout_waiting_threads) + while (entry->checked_out && !entry->driveout_waiting_threads) { /* so wait until we can get it for us. * we register us as waiting. */ @@ -698,7 +700,7 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) chunk_t addr = entry->other->get_address(entry->other); u_int row = chunk_hash(addr) & this->table_mask; u_int segment = row & this->segment_mask; - + rwlock_t *lock = this->half_open_segments[segment].lock; lock->write_lock(lock); if ((list = this->half_open_table[row]) == NULL) @@ -716,7 +718,7 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) this->half_open_segments[segment].count++; } } - + if (!half_open) { half_open = malloc_thing(half_open_t); @@ -737,7 +739,7 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) chunk_t addr = entry->other->get_address(entry->other); u_int row = chunk_hash(addr) & this->table_mask; u_int segment = row & this->segment_mask; - + rwlock_t *lock = this->half_open_segments[segment].lock; lock->write_lock(lock); if ((list = this->half_open_table[row]) != NULL) @@ -773,7 +775,7 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry) other_id = entry->other_id->get_encoding(entry->other_id); u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask; u_int segment = row & this->segment_mask; - + rwlock_t *lock = this->connected_peers_segments[segment].lock; lock->write_lock(lock); if ((list = this->connected_peers_table[row]) == NULL) @@ -796,7 +798,7 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry) } } } - + if (!connected_peers) { connected_peers = malloc_thing(connected_peers_t); @@ -821,7 +823,7 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr other_id = entry->other_id->get_encoding(entry->other_id); u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask; u_int segment = row & this->segment_mask; - + rwlock_t *lock = this->connected_peers_segments[segment].lock; lock->write_lock(lock); if ((list = this->connected_peers_table[row]) != NULL) @@ -864,7 +866,7 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr static u_int64_t get_next_spi(private_ike_sa_manager_t *this) { u_int64_t spi; - + this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi); return spi; } @@ -877,9 +879,9 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id ike_sa_t *ike_sa = NULL; entry_t *entry; u_int segment; - + DBG2(DBG_MGR, "checkout IKE_SA"); - + if (get_entry_by_id(this, ike_sa_id, &entry, &segment) == SUCCESS) { if (wait_for_entry(this, entry, segment)) @@ -903,7 +905,7 @@ static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) ike_sa_t *ike_sa; entry_t *entry; u_int segment; - + if (initiator) { ike_sa_id = ike_sa_id_create(get_next_spi(this), 0, TRUE); @@ -913,15 +915,15 @@ static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) ike_sa_id = ike_sa_id_create(0, get_next_spi(this), FALSE); } ike_sa = ike_sa_create(ike_sa_id); - + DBG2(DBG_MGR, "created IKE_SA"); - + if (!initiator) { ike_sa_id->destroy(ike_sa_id); return ike_sa; } - + entry = entry_create(); entry->ike_sa_id = ike_sa_id; entry->ike_sa = ike_sa; @@ -944,19 +946,19 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, id = id->clone(id); id->switch_initiator(id); - + DBG2(DBG_MGR, "checkout IKE_SA by message"); - + if (message->get_request(message) && message->get_exchange_type(message) == IKE_SA_INIT) { /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ chunk_t data, hash; - + data = message->get_packet_data(message); this->hasher->allocate_hash(this->hasher, data, &hash); chunk_free(&data); - + if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS) { if (entry->message_id == 0) @@ -976,7 +978,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, } unlock_single_segment(this, segment); } - + if (ike_sa == NULL) { if (id->get_responder_spi(id) == 0 && @@ -987,15 +989,15 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, entry = entry_create(); entry->ike_sa = ike_sa_create(id); entry->ike_sa_id = id->clone(id); - + segment = put_entry(this, entry); entry->checked_out = TRUE; unlock_single_segment(this, segment); - - entry->message_id = message->get_message_id(message); + + entry->message_id = message->get_message_id(message); entry->init_hash = hash; ike_sa = entry->ike_sa; - + DBG2(DBG_MGR, "created IKE_SA"); } else @@ -1012,7 +1014,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } - + if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS) { /* only check out if we are not processing this request */ @@ -1053,14 +1055,14 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, peer_cfg_t *current_peer; ike_cfg_t *current_ike; u_int segment; - + if (!this->reuse_ikesa) { /* IKE_SA reuse disable by config */ - ike_sa = checkout_new(this, TRUE); + ike_sa = checkout_new(this, TRUE); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } - + enumerator = create_table_enumerator(this); while (enumerator->enumerate(enumerator, &entry, &segment)) { @@ -1072,7 +1074,7 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, { /* skip IKE_SAs which are not usable */ continue; } - + current_peer = entry->ike_sa->get_peer_cfg(entry->ike_sa); if (current_peer && current_peer->equals(current_peer, peer_cfg)) { @@ -1088,10 +1090,10 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, } } enumerator->destroy(enumerator); - + if (!ike_sa) { /* no IKE_SA using such a config, hand out a new */ - ike_sa = checkout_new(this, TRUE); + ike_sa = checkout_new(this, TRUE); } charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; @@ -1109,7 +1111,7 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; u_int segment; - + enumerator = create_table_enumerator(this); while (enumerator->enumerate(enumerator, &entry, &segment)) { @@ -1125,7 +1127,7 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, { ike_sa = entry->ike_sa; break; - } + } } children->destroy(children); } @@ -1145,7 +1147,7 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, } } enumerator->destroy(enumerator); - + charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } @@ -1162,7 +1164,7 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; u_int segment; - + enumerator = create_table_enumerator(this); while (enumerator->enumerate(enumerator, &entry, &segment)) { @@ -1178,7 +1180,7 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, { ike_sa = entry->ike_sa; break; - } + } } children->destroy(children); } @@ -1198,13 +1200,13 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, } } enumerator->destroy(enumerator); - + charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } /** - * enumerator filter function + * enumerator filter function */ static bool enumerator_filter(private_ike_sa_manager_t *this, entry_t **in, ike_sa_t **out, u_int *segment) @@ -1243,14 +1245,14 @@ static void checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) host_t *other; identification_t *my_id, *other_id; u_int segment; - + ike_sa_id = ike_sa->get_id(ike_sa); my_id = ike_sa->get_my_id(ike_sa); other_id = ike_sa->get_other_id(ike_sa); other = ike_sa->get_other_host(ike_sa); - + DBG2(DBG_MGR, "checkin IKE_SA"); - + /* look for the entry */ if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS) { @@ -1293,7 +1295,7 @@ static void checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) entry->ike_sa = ike_sa; segment = put_entry(this, entry); } - + /* apply identities for duplicate test (only as responder) */ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && @@ -1303,9 +1305,9 @@ static void checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) entry->other_id = other_id->clone(other_id); put_connected_peers(this, entry); } - + unlock_single_segment(this, segment); - + charon->bus->set_sa(charon->bus, NULL); } @@ -1322,11 +1324,11 @@ static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa entry_t *entry; ike_sa_id_t *ike_sa_id; u_int segment; - + ike_sa_id = ike_sa->get_id(ike_sa); - + DBG2(DBG_MGR, "checkin and destroy IKE_SA"); - + if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS) { /* drive out waiting threads, as we are in hurry */ @@ -1343,7 +1345,7 @@ static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa } remove_entry(this, entry); unlock_single_segment(this, segment); - + if (entry->half_open) { remove_half_open(this, entry); @@ -1353,9 +1355,9 @@ static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa { remove_connected_peers(this, entry); } - + entry_destroy(entry); - + DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); } else @@ -1366,7 +1368,7 @@ static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa charon->bus->set_sa(charon->bus, NULL); } - + /** * Implementation of ike_sa_manager_t.check_uniqueness. */ @@ -1381,27 +1383,27 @@ static bool check_uniqueness(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) identification_t *me, *other; u_int row, segment; rwlock_t *lock; - + peer_cfg = ike_sa->get_peer_cfg(ike_sa); policy = peer_cfg->get_unique_policy(peer_cfg); if (policy == UNIQUE_NO) { return FALSE; } - + me = ike_sa->get_my_id(ike_sa); other = ike_sa->get_other_id(ike_sa); - + row = chunk_hash_inc(other->get_encoding(other), chunk_hash(me->get_encoding(me))) & this->table_mask; segment = row & this->segment_mask; - + lock = this->connected_peers_segments[segment & this->segment_mask].lock; lock->read_lock(lock); if ((list = this->connected_peers_table[row]) != NULL) { connected_peers_t *current; - + if (list->find_first(list, (linked_list_match_t)connected_peers_match, (void**)¤t, me, other) == SUCCESS) { @@ -1411,18 +1413,18 @@ static bool check_uniqueness(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) } } lock->unlock(lock); - + if (!duplicate_ids) { return FALSE; } - + enumerator = duplicate_ids->create_enumerator(duplicate_ids); while (enumerator->enumerate(enumerator, &duplicate_id)) { status_t status = SUCCESS; ike_sa_t *duplicate; - + duplicate = checkout(this, duplicate_id); if (!duplicate) { @@ -1485,13 +1487,13 @@ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) chunk_t addr = ip->get_address(ip); u_int row = chunk_hash(addr) & this->table_mask; u_int segment = row & this->segment_mask; - + rwlock_t *lock = this->half_open_segments[segment & this->segment_mask].lock; lock->read_lock(lock); if ((list = this->half_open_table[row]) != NULL) { half_open_t *current; - + if (list->find_first(list, (linked_list_match_t)half_open_match, (void**)¤t, &addr) == SUCCESS) { @@ -1503,7 +1505,7 @@ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) else { u_int segment; - + for (segment = 0; segment < this->segment_count; ++segment) { rwlock_t *lock; @@ -1513,7 +1515,7 @@ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) lock->unlock(lock); } } - + return count; } @@ -1526,7 +1528,7 @@ static void flush(private_ike_sa_manager_t *this) enumerator_t *enumerator; entry_t *entry; u_int segment; - + lock_all_segments(this); DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); /* Step 1: drive out all waiting threads */ @@ -1536,7 +1538,7 @@ static void flush(private_ike_sa_manager_t *this) { /* do not accept new threads, drive out waiting threads */ entry->driveout_new_threads = TRUE; - entry->driveout_waiting_threads = TRUE; + entry->driveout_waiting_threads = TRUE; } enumerator->destroy(enumerator); DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); @@ -1573,7 +1575,7 @@ static void flush(private_ike_sa_manager_t *this) entry->ike_sa->delete(entry->ike_sa); } enumerator->destroy(enumerator); - + DBG2(DBG_MGR, "destroy all entries"); /* Step 4: destroy all entries */ enumerator = create_table_enumerator(this); @@ -1633,7 +1635,7 @@ static void destroy(private_ike_sa_manager_t *this) free(this->segments); free(this->half_open_segments); free(this->connected_peers_segments); - + this->rng->destroy(this->rng); this->hasher->destroy(this->hasher); free(this); @@ -1648,7 +1650,7 @@ static void destroy(private_ike_sa_manager_t *this) static u_int get_nearest_powerof2(u_int n) { u_int i; - + --n; for (i = 1; i < sizeof(u_int) * 8; i <<= 1) { @@ -1679,7 +1681,7 @@ ike_sa_manager_t *ike_sa_manager_create() this->public.checkin = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin; this->public.checkin_and_destroy = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; - + /* initialize private variables */ this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_PREFERRED); if (this->hasher == NULL) @@ -1700,21 +1702,21 @@ ike_sa_manager_t *ike_sa_manager_create() "charon.ikesa_table_size", DEFAULT_HASHTABLE_SIZE)); this->table_size = max(1, min(this->table_size, MAX_HASHTABLE_SIZE)); this->table_mask = this->table_size - 1; - + this->segment_count = get_nearest_powerof2(lib->settings->get_int(lib->settings, "charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT)); this->segment_count = max(1, min(this->segment_count, this->table_size)); this->segment_mask = this->segment_count - 1; - + this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*)); - + this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t)); for (i = 0; i < this->segment_count; ++i) { this->segments[i].mutex = mutex_create(MUTEX_TYPE_RECURSIVE); this->segments[i].count = 0; } - + /* we use the same table parameters for the table to track half-open SAs */ this->half_open_table = calloc(this->table_size, sizeof(linked_list_t*)); this->half_open_segments = calloc(this->segment_count, sizeof(shareable_segment_t)); @@ -1723,7 +1725,7 @@ ike_sa_manager_t *ike_sa_manager_create() this->half_open_segments[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT); this->half_open_segments[i].count = 0; } - + /* also for the hash table used for duplicate tests */ this->connected_peers_table = calloc(this->table_size, sizeof(linked_list_t*)); this->connected_peers_segments = calloc(this->segment_count, sizeof(shareable_segment_t)); @@ -1732,7 +1734,7 @@ ike_sa_manager_t *ike_sa_manager_create() this->connected_peers_segments[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT); this->connected_peers_segments[i].count = 0; } - + this->reuse_ikesa = lib->settings->get_bool(lib->settings, "charon.reuse_ikesa", TRUE); return &this->public; diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index 6da768080..38f5454e1 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -38,20 +38,20 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; * by the owning thread. */ struct ike_sa_manager_t { - + /** * Checkout an existing IKE_SA. - * + * * @param ike_sa_id the SA identifier, will be updated - * @returns + * @returns * - checked out IKE_SA if found * - NULL, if specified IKE_SA is not found. */ ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id); - + /** * Create and check out a new IKE_SA. - * + * * @note If initiator equals FALSE, the returned IKE_SA is not registered * in the manager. * @@ -59,30 +59,30 @@ struct ike_sa_manager_t { * @returns created and checked out IKE_SA */ ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator); - + /** * Checkout an IKE_SA by a message. - * + * * In some situations, it is necessary that the manager knows the * message to use for the checkout. This has the following reasons: - * + * * 1. If the targeted IKE_SA is already processing a message, we do not * check it out if the message ID is the same. - * 2. If it is an IKE_SA_INIT request, we have to check if it is a + * 2. If it is an IKE_SA_INIT request, we have to check if it is a * retransmission. If so, we have to drop the message, we would * create another unneeded IKE_SA for each retransmitted packet. * * A call to checkout_by_message() returns a (maybe new created) IKE_SA. * If processing the message does not make sense (for the reasons above), * NULL is returned. - * + * * @param ike_sa_id the SA identifier, will be updated - * @returns + * @returns * - checked out/created IKE_SA * - NULL to not process message further */ ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message); - + /** * Checkout an IKE_SA for initiation by a peer_config. * @@ -97,27 +97,27 @@ struct ike_sa_manager_t { * @return checked out/created IKE_SA */ ike_sa_t* (*checkout_by_config) (ike_sa_manager_t* this, - peer_cfg_t *peer_cfg); - + peer_cfg_t *peer_cfg); + /** * Check for duplicates of the given IKE_SA. - * + * * Measures are taken according to the uniqueness policy of the IKE_SA. * The return value indicates whether duplicates have been found and if * further measures should be taken (e.g. cancelling an IKE_AUTH exchange). * check_uniqueness() must be called before the IKE_SA is complete, * deadlocks occur otherwise. - * + * * @param ike_sa ike_sa to check * @return TRUE, if the given IKE_SA has duplicates and * should be deleted */ bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa); - + /** * Check out an IKE_SA a unique ID. * - * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. + * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. * These checkout function uses, depending * on the child parameter, the unique ID of the IKE_SA or the reqid * of one of a IKE_SAs CHILD_SA. @@ -130,7 +130,7 @@ struct ike_sa_manager_t { */ ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id, bool child); - + /** * Check out an IKE_SA by the policy/connection name. * @@ -145,7 +145,7 @@ struct ike_sa_manager_t { */ ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name, bool child); - + /** * Create an enumerator over all stored IKE_SAs. * @@ -155,7 +155,7 @@ struct ike_sa_manager_t { * @return enumerator over all IKE_SAs. */ enumerator_t *(*create_enumerator) (ike_sa_manager_t* this); - + /** * Checkin the SA after usage. * @@ -165,7 +165,7 @@ struct ike_sa_manager_t { * @param ike_sa checked out SA */ void (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); - + /** * Destroy a checked out SA. * @@ -179,7 +179,7 @@ struct ike_sa_manager_t { * @param ike_sa SA to delete */ void (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); - + /** * Get the number of IKE_SAs which are in the connecting state. * @@ -189,19 +189,19 @@ struct ike_sa_manager_t { * If a host is supplied, only the number of half open IKE_SAs initiated * from this IP are counted. * Only SAs for which we are the responder are counted. - * + * * @param ip NULL for all, IP for half open IKE_SAs with IP * @return number of half open IKE_SAs */ int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); - + /** * Delete all existing IKE_SAs and destroy them immediately. - * + * * Threads will be driven out, so all SAs can be deleted cleanly. */ void (*flush)(ike_sa_manager_t *this); - + /** * Destroys the manager with all associated SAs. * @@ -212,7 +212,7 @@ struct ike_sa_manager_t { /** * Create the IKE_SA manager. - * + * * @returns ike_sa_manager_t object, NULL if initialization fails */ ike_sa_manager_t *ike_sa_manager_create(void); diff --git a/src/charon/sa/keymat.c b/src/charon/sa/keymat.c index 46fb79587..e49626354 100644 --- a/src/charon/sa/keymat.c +++ b/src/charon/sa/keymat.c @@ -24,52 +24,52 @@ typedef struct private_keymat_t private_keymat_t; * Private data of an keymat_t object. */ struct private_keymat_t { - + /** * Public keymat_t interface. */ keymat_t public; - + /** - * IKE_SA Role, initiator or responder - */ - bool initiator; - + * IKE_SA Role, initiator or responder + */ + bool initiator; + /** * inbound signer (verify) */ signer_t *signer_in; - + /** * outbound signer (sign) */ signer_t *signer_out; - + /** * inbound crypter (decrypt) */ crypter_t *crypter_in; - + /** * outbound crypter (encrypt) */ crypter_t *crypter_out; - + /** * General purpose PRF */ prf_t *prf; - + /** * Negotiated PRF algorithm */ pseudo_random_function_t prf_alg; - + /** * Key to derive key material from for CHILD_SAs, rekeying */ chunk_t skd; - + /** * Key to build outging authentication data (SKp) */ @@ -110,6 +110,7 @@ keylen_entry_t keylen_enc[] = { keylen_entry_t keylen_int[] = { {AUTH_HMAC_MD5_96, 128}, {AUTH_HMAC_SHA1_96, 160}, + {AUTH_HMAC_SHA2_256_96, 256}, {AUTH_HMAC_SHA2_256_128, 256}, {AUTH_HMAC_SHA2_384_192, 384}, {AUTH_HMAC_SHA2_512_256, 512}, @@ -158,15 +159,15 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, prf_plus_t *prf_plus; u_int16_t alg, key_size; prf_t *rekey_prf = NULL; - + spi_i = chunk_alloca(sizeof(u_int64_t)); spi_r = chunk_alloca(sizeof(u_int64_t)); - + if (dh->get_shared_secret(dh, &secret) != SUCCESS) { return FALSE; } - + /* Create SAs general purpose PRF first, we may use it here */ if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) { @@ -206,8 +207,8 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id); *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id); prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); - - /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) + + /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) * * if we are rekeying, SKEYSEED is built on another way */ @@ -221,7 +222,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, } else { - /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) + /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) * use OLD SAs PRF functions for both prf_plus and prf */ rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function); if (!rekey_prf) @@ -240,20 +241,20 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, prf_plus = prf_plus_create(rekey_prf, prf_plus_seed); } DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); - + chunk_clear(&skeyseed); chunk_clear(&secret); chunk_free(&full_nonce); chunk_free(&fixed_nonce); chunk_clear(&prf_plus_seed); - + /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ - + /* SK_d is used for generating CHILD_SA key mat => store for later use */ key_size = this->prf->get_key_size(this->prf); prf_plus->allocate_bytes(prf_plus, key_size, &this->skd); DBG4(DBG_IKE, "Sk_d secret %B", &this->skd); - + /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) { @@ -275,17 +276,17 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, return FALSE; } key_size = signer_i->get_key_size(signer_i); - + prf_plus->allocate_bytes(prf_plus, key_size, &key); DBG4(DBG_IKE, "Sk_ai secret %B", &key); signer_i->set_key(signer_i, key); chunk_clear(&key); - + prf_plus->allocate_bytes(prf_plus, key_size, &key); DBG4(DBG_IKE, "Sk_ar secret %B", &key); signer_r->set_key(signer_r, key); chunk_clear(&key); - + if (this->initiator) { this->signer_in = signer_r; @@ -296,7 +297,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, this->signer_in = signer_i; this->signer_out = signer_r; } - + /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) { @@ -318,17 +319,17 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, return FALSE; } key_size = crypter_i->get_key_size(crypter_i); - + prf_plus->allocate_bytes(prf_plus, key_size, &key); DBG4(DBG_IKE, "Sk_ei secret %B", &key); crypter_i->set_key(crypter_i, key); chunk_clear(&key); - + prf_plus->allocate_bytes(prf_plus, key_size, &key); DBG4(DBG_IKE, "Sk_er secret %B", &key); crypter_r->set_key(crypter_r, key); chunk_clear(&key); - + if (this->initiator) { this->crypter_in = crypter_r; @@ -339,8 +340,8 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, this->crypter_in = crypter_i; this->crypter_out = crypter_r; } - - /* SK_pi/SK_pr used for authentication => stored for later */ + + /* SK_pi/SK_pr used for authentication => stored for later */ key_size = this->prf->get_key_size(this->prf); prf_plus->allocate_bytes(prf_plus, key_size, &key); DBG4(DBG_IKE, "Sk_pi secret %B", &key); @@ -362,11 +363,11 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, { this->skp_build = key; } - + /* all done, prf_plus not needed anymore */ prf_plus->destroy(prf_plus); DESTROY_IF(rekey_prf); - + return TRUE; } @@ -382,7 +383,7 @@ static bool derive_child_keys(private_keymat_t *this, u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0; chunk_t seed, secret = chunk_empty; prf_plus_t *prf_plus; - + if (dh) { if (dh->get_shared_secret(dh, &secret) != SUCCESS) @@ -393,13 +394,13 @@ static bool derive_child_keys(private_keymat_t *this, } seed = chunk_cata("mcc", secret, nonce_i, nonce_r); DBG4(DBG_CHD, "seed %B", &seed); - + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, &enc_size)) { - DBG2(DBG_CHD, " using %N for encryption", + DBG2(DBG_CHD, " using %N for encryption", encryption_algorithm_names, enc_alg); - + if (!enc_size) { enc_size = lookup_keylen(keylen_enc, enc_alg); @@ -412,7 +413,7 @@ static bool derive_child_keys(private_keymat_t *this, } /* to bytes */ enc_size /= 8; - + /* CCM/GCM/CTR needs additional bytes */ switch (enc_alg) { @@ -434,13 +435,13 @@ static bool derive_child_keys(private_keymat_t *this, break; } } - + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, &int_size)) { DBG2(DBG_CHD, " using %N for integrity", integrity_algorithm_names, int_alg); - + if (!int_size) { int_size = lookup_keylen(keylen_int, int_alg); @@ -454,17 +455,17 @@ static bool derive_child_keys(private_keymat_t *this, /* to bytes */ int_size /= 8; } - + this->prf->set_key(this->prf, this->skd); prf_plus = prf_plus_create(this->prf, seed); - + prf_plus->allocate_bytes(prf_plus, enc_size, encr_i); prf_plus->allocate_bytes(prf_plus, int_size, integ_i); prf_plus->allocate_bytes(prf_plus, enc_size, encr_r); prf_plus->allocate_bytes(prf_plus, int_size, integ_r); - + prf_plus->destroy(prf_plus); - + if (enc_size) { DBG4(DBG_CHD, "encryption initiator key %B", encr_i); @@ -512,19 +513,19 @@ static chunk_t get_auth_octets(private_keymat_t *this, bool verify, { chunk_t chunk, idx, octets; chunk_t skp; - + skp = verify ? this->skp_verify : this->skp_build; - + chunk = chunk_alloca(4); memset(chunk.ptr, 0, chunk.len); chunk.ptr[0] = id->get_type(id); idx = chunk_cata("cc", chunk, id->get_encoding(id)); - + DBG3(DBG_IKE, "IDx' %B", &idx); DBG3(DBG_IKE, "SK_p %B", &skp); this->prf->set_key(this->prf, skp); this->prf->allocate_bytes(this->prf, idx, &chunk); - + octets = chunk_cat("ccm", ike_sa_init, nonce, chunk); DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets); return octets; @@ -539,12 +540,12 @@ static chunk_t get_auth_octets(private_keymat_t *this, bool verify, /** * Implementation of keymat_t.get_psk_sig */ -static chunk_t get_psk_sig(private_keymat_t *this, bool verify, +static chunk_t get_psk_sig(private_keymat_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, chunk_t secret, identification_t *id) { chunk_t key_pad, key, sig, octets; - + if (!secret.len) { /* EAP uses SK_p if no MSK has been established */ secret = verify ? this->skp_verify : this->skp_build; @@ -561,7 +562,7 @@ static chunk_t get_psk_sig(private_keymat_t *this, bool verify, DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &sig); chunk_free(&octets); chunk_free(&key); - + return sig; } @@ -587,7 +588,7 @@ static void destroy(private_keymat_t *this) keymat_t *keymat_create(bool initiator) { private_keymat_t *this = malloc_thing(private_keymat_t); - + this->public.create_dh = (diffie_hellman_t*(*)(keymat_t*, diffie_hellman_group_t group))create_dh; this->public.derive_ike_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t,chunk_t))derive_ike_keys; this->public.derive_child_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r))derive_child_keys; @@ -597,9 +598,9 @@ keymat_t *keymat_create(bool initiator) this->public.get_auth_octets = (chunk_t(*)(keymat_t *, bool verify, chunk_t ike_sa_init, chunk_t nonce, identification_t *id))get_auth_octets; this->public.get_psk_sig = (chunk_t(*)(keymat_t*, bool verify, chunk_t ike_sa_init, chunk_t nonce, chunk_t secret, identification_t *id))get_psk_sig; this->public.destroy = (void(*)(keymat_t*))destroy; - + this->initiator = initiator; - + this->signer_in = NULL; this->signer_out = NULL; this->crypter_in = NULL; @@ -609,7 +610,7 @@ keymat_t *keymat_create(bool initiator) this->skd = chunk_empty; this->skp_verify = chunk_empty; this->skp_build = chunk_empty; - + return &this->public; } diff --git a/src/charon/sa/keymat.h b/src/charon/sa/keymat.h index 43b9dd113..e51709e8d 100644 --- a/src/charon/sa/keymat.h +++ b/src/charon/sa/keymat.h @@ -35,7 +35,7 @@ typedef struct keymat_t keymat_t; * Derivation an management of sensitive keying material. */ struct keymat_t { - + /** * Create a diffie hellman object for key agreement. * @@ -47,7 +47,7 @@ struct keymat_t { * @return DH object, NULL if group not supported */ diffie_hellman_t* (*create_dh)(keymat_t *this, diffie_hellman_group_t group); - + /** * Derive keys for the IKE_SA. * @@ -86,7 +86,7 @@ struct keymat_t { * @param integ_r chunk to write responders integrity key to * @return TRUE on success */ - bool (*derive_child_keys)(keymat_t *this, + bool (*derive_child_keys)(keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, @@ -98,7 +98,7 @@ struct keymat_t { * @return PRF function to derive keymat */ pseudo_random_function_t (*get_skd)(keymat_t *this, chunk_t *skd); - + /** * Get a signer to sign/verify IKE messages. * @@ -106,7 +106,7 @@ struct keymat_t { * @return signer */ signer_t* (*get_signer)(keymat_t *this, bool in); - + /* * Get a crypter to en-/decrypt IKE messages. * @@ -114,7 +114,7 @@ struct keymat_t { * @return crypter */ crypter_t* (*get_crypter)(keymat_t *this, bool in); - + /** * Generate octets to use for authentication procedure (RFC4306 2.15). * @@ -160,4 +160,4 @@ struct keymat_t { */ keymat_t *keymat_create(bool initiator); -#endif /** KEYMAT_ @}*/ +#endif /** KEYMAT_H_ @}*/ diff --git a/src/charon/sa/mediation_manager.c b/src/charon/sa/mediation_manager.c index a69c00173..035f49053 100644 --- a/src/charon/sa/mediation_manager.c +++ b/src/charon/sa/mediation_manager.c @@ -16,11 +16,10 @@ #include "mediation_manager.h" #include <daemon.h> -#include <utils/mutex.h> +#include <threading/mutex.h> #include <utils/linked_list.h> #include <processing/jobs/mediation_job.h> - typedef struct peer_t peer_t; /** @@ -28,13 +27,13 @@ typedef struct peer_t peer_t; */ struct peer_t { /** id of the peer */ - identification_t *id; + identification_t *id; /** sa id of the peer, NULL if offline */ - ike_sa_id_t *ike_sa_id; - - /** list of peer ids that reuested this peer */ - linked_list_t *requested_by; + ike_sa_id_t *ike_sa_id; + + /** list of peer ids that reuested this peer */ + linked_list_t *requested_by; }; /** @@ -43,8 +42,9 @@ struct peer_t { static void peer_destroy(peer_t *this) { DESTROY_IF(this->id); - DESTROY_IF(this->ike_sa_id); - this->requested_by->destroy_offset(this->requested_by, offsetof(identification_t, destroy)); + DESTROY_IF(this->ike_sa_id); + this->requested_by->destroy_offset(this->requested_by, + offsetof(identification_t, destroy)); free(this); } @@ -54,16 +54,15 @@ static void peer_destroy(peer_t *this) static peer_t *peer_create(identification_t *id, ike_sa_id_t* ike_sa_id) { peer_t *this = malloc_thing(peer_t); - + /* clone everything */ this->id = id->clone(id); - this->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL; - this->requested_by = linked_list_create(); - + this->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL; + this->requested_by = linked_list_create(); + return this; } - typedef struct private_mediation_manager_t private_mediation_manager_t; /** @@ -74,7 +73,7 @@ struct private_mediation_manager_t { * Public interface of mediation_manager_t. */ mediation_manager_t public; - + /** * Lock for exclusivly accessing the manager. */ @@ -93,7 +92,7 @@ static void register_peer(peer_t *peer, identification_t *peer_id) { iterator_t *iterator; identification_t *current; - + iterator = peer->requested_by->create_iterator(peer->requested_by, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -104,20 +103,21 @@ static void register_peer(peer_t *peer, identification_t *peer_id) } } iterator->destroy(iterator); - - peer->requested_by->insert_last(peer->requested_by, peer_id->clone(peer_id)); + + peer->requested_by->insert_last(peer->requested_by, + peer_id->clone(peer_id)); } /** * Get a peer_t object by a peer's id */ static status_t get_peer_by_id(private_mediation_manager_t *this, - identification_t *id, peer_t **peer) + identification_t *id, peer_t **peer) { iterator_t *iterator; peer_t *current; status_t status = NOT_FOUND; - + iterator = this->peers->create_iterator(this->peers, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -141,7 +141,8 @@ static status_t get_peer_by_id(private_mediation_manager_t *this, * and then remove peers completely that are not online and have no registered * peers. */ -static void unregister_peer(private_mediation_manager_t *this, identification_t *peer_id) +static void unregister_peer(private_mediation_manager_t *this, + identification_t *peer_id) { iterator_t *iterator, *iterator_r; peer_t *peer; @@ -150,7 +151,8 @@ static void unregister_peer(private_mediation_manager_t *this, identification_t iterator = this->peers->create_iterator(this->peers, TRUE); while (iterator->iterate(iterator, (void**)&peer)) { - iterator_r = peer->requested_by->create_iterator(peer->requested_by, TRUE); + iterator_r = peer->requested_by->create_iterator(peer->requested_by, + TRUE); while (iterator_r->iterate(iterator_r, (void**)®istered)) { if (peer_id->equals(peer_id, registered)) @@ -161,7 +163,7 @@ static void unregister_peer(private_mediation_manager_t *this, identification_t } } iterator_r->destroy(iterator_r); - + if (!peer->ike_sa_id && !peer->requested_by->get_count(peer->requested_by)) { iterator->remove(iterator); @@ -181,16 +183,16 @@ static void remove_sa(private_mediation_manager_t *this, ike_sa_id_t *ike_sa_id) peer_t *peer; this->mutex->lock(this->mutex); - + iterator = this->peers->create_iterator(this->peers, TRUE); while (iterator->iterate(iterator, (void**)&peer)) { if (ike_sa_id->equals(ike_sa_id, peer->ike_sa_id)) { iterator->remove(iterator); - + unregister_peer(this, peer->id); - + peer_destroy(peer); break; } @@ -222,7 +224,7 @@ static void update_sa_id(private_mediation_manager_t *this, identification_t *pe } } iterator->destroy(iterator); - + if (!found) { DBG2(DBG_IKE, "adding peer '%Y'", peer_id); @@ -230,18 +232,19 @@ static void update_sa_id(private_mediation_manager_t *this, identification_t *pe this->peers->insert_last(this->peers, peer); } - DBG2(DBG_IKE, "changing registered IKE_SA ID of peer '%Y'", peer_id); + DBG2(DBG_IKE, "changing registered IKE_SA ID of peer '%Y'", peer_id); peer->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL; - + /* send callbacks to registered peers */ identification_t *requester; - while(peer->requested_by->remove_last(peer->requested_by, (void**)&requester) == SUCCESS) + while(peer->requested_by->remove_last(peer->requested_by, + (void**)&requester) == SUCCESS) { job_t *job = (job_t*)mediation_callback_job_create(requester, peer_id); charon->processor->queue_job(charon->processor, job); requester->destroy(requester); } - + this->mutex->unlock(this->mutex); } @@ -286,11 +289,12 @@ static ike_sa_id_t *check_and_register(private_mediation_manager_t *this, peer = peer_create(peer_id, NULL); this->peers->insert_last(this->peers, peer); } - + if (!peer->ike_sa_id) { /* the peer is not online */ - DBG2(DBG_IKE, "requested peer '%Y' is offline, registering peer '%Y'", peer_id, requester); + DBG2(DBG_IKE, "requested peer '%Y' is offline, registering peer '%Y'", + peer_id, requester); register_peer(peer, requester); this->mutex->unlock(this->mutex); return NULL; @@ -309,9 +313,9 @@ static ike_sa_id_t *check_and_register(private_mediation_manager_t *this, static void destroy(private_mediation_manager_t *this) { this->mutex->lock(this->mutex); - + this->peers->destroy_function(this->peers, (void*)peer_destroy); - + this->mutex->unlock(this->mutex); this->mutex->destroy(this->mutex); free(this); @@ -329,9 +333,9 @@ mediation_manager_t *mediation_manager_create() this->public.update_sa_id = (void(*)(mediation_manager_t*,identification_t*,ike_sa_id_t*))update_sa_id; this->public.check = (ike_sa_id_t*(*)(mediation_manager_t*,identification_t*))check; this->public.check_and_register = (ike_sa_id_t*(*)(mediation_manager_t*,identification_t*,identification_t*))check_and_register; - + this->peers = linked_list_create(); this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); - + return (mediation_manager_t*)this; } diff --git a/src/charon/sa/mediation_manager.h b/src/charon/sa/mediation_manager.h index 29e16d84f..31a16f69c 100644 --- a/src/charon/sa/mediation_manager.h +++ b/src/charon/sa/mediation_manager.h @@ -31,48 +31,49 @@ typedef struct mediation_manager_t mediation_manager_t; * peers and registered requests for offline peers on the mediation server. */ struct mediation_manager_t { - + /** * Remove the IKE_SA of a peer. - * + * * @param ike_sa_id the IKE_SA ID of the peer's SA */ void (*remove) (mediation_manager_t* this, ike_sa_id_t *ike_sa_id); - + /** * Update the ike_sa_id that is assigned to a peer's ID. If the peer - * is new, it gets a new record assigned. - * + * is new, it gets a new record assigned. + * * @param peer_id the peer's ID * @param ike_sa_id the IKE_SA ID of the peer's SA */ void (*update_sa_id) (mediation_manager_t* this, identification_t *peer_id, - ike_sa_id_t *ike_sa_id); - + ike_sa_id_t *ike_sa_id); + /** * Checks if a specific peer is online. - * + * * @param peer_id the peer's ID - * @returns - * - IKE_SA ID of the peer's SA. - * - NULL, if the peer is not online. + * @returns + * - IKE_SA ID of the peer's SA. + * - NULL, if the peer is not online. */ ike_sa_id_t* (*check) (mediation_manager_t* this, - identification_t *peer_id); - + identification_t *peer_id); + /** * Checks if a specific peer is online and registers the requesting * peer if it is not. - * + * * @param peer_id the peer's ID * @param requester the requesters ID - * @returns - * - IKE_SA ID of the peer's SA. - * - NULL, if the peer is not online. + * @returns + * - IKE_SA ID of the peer's SA. + * - NULL, if the peer is not online. */ ike_sa_id_t* (*check_and_register) (mediation_manager_t* this, - identification_t *peer_id, identification_t *requester); - + identification_t *peer_id, + identification_t *requester); + /** * Destroys the manager with all data. */ @@ -81,8 +82,8 @@ struct mediation_manager_t { /** * Create a manager. - * - * @returns mediation_manager_t object + * + * @returns mediation_manager_t object */ mediation_manager_t *mediation_manager_create(void); diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index f33fcd6d4..1de0c06f0 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -30,6 +30,7 @@ #include <sa/tasks/ike_delete.h> #include <sa/tasks/ike_config.h> #include <sa/tasks/ike_dpd.h> +#include <sa/tasks/ike_vendor.h> #include <sa/tasks/child_create.h> #include <sa/tasks/child_rekey.h> #include <sa/tasks/child_delete.h> @@ -46,12 +47,12 @@ typedef struct exchange_t exchange_t; * An exchange in the air, used do detect and handle retransmission */ struct exchange_t { - + /** * Message ID used for this transaction */ u_int32_t mid; - + /** * generated packet for retransmission */ @@ -64,17 +65,17 @@ typedef struct private_task_manager_t private_task_manager_t; * private data of the task manager */ struct private_task_manager_t { - + /** * public functions */ task_manager_t public; - + /** * associated IKE_SA we are serving */ ike_sa_t *ike_sa; - + /** * Exchange we are currently handling as responder */ @@ -83,14 +84,14 @@ struct private_task_manager_t { * Message ID of the exchange */ u_int32_t mid; - + /** * packet for retransmission */ packet_t *packet; - + } responding; - + /** * Exchange we are currently handling as initiator */ @@ -99,7 +100,7 @@ struct private_task_manager_t { * Message ID of the exchange */ u_int32_t mid; - + /** * how many times we have retransmitted so far */ @@ -109,33 +110,48 @@ struct private_task_manager_t { * packet for retransmission */ packet_t *packet; - + /** * type of the initated exchange */ exchange_type_t type; - + } initiating; - + /** * List of queued tasks not yet in action */ linked_list_t *queued_tasks; - + /** * List of active tasks, initiated by ourselve */ linked_list_t *active_tasks; - + /** * List of tasks initiated by peer */ linked_list_t *passive_tasks; - + /** - * the task manager has been reset + * the task manager has been reset */ bool reset; + + /** + * Number of times we retransmit messages before giving up + */ + u_int retransmit_tries; + + /** + * Retransmission timeout + */ + double retransmit_timeout; + + /** + * Base to calculate retransmission timeout + */ + double retransmit_base; }; /** @@ -143,7 +159,7 @@ struct private_task_manager_t { */ static void flush(private_task_manager_t *this) { - this->queued_tasks->destroy_offset(this->queued_tasks, + this->queued_tasks->destroy_offset(this->queued_tasks, offsetof(task_t, destroy)); this->passive_tasks->destroy_offset(this->passive_tasks, offsetof(task_t, destroy)); @@ -162,7 +178,7 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) iterator_t *iterator; task_t *task; bool found = FALSE; - + iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE); while (iterator->iterate(iterator, (void**)&task)) { @@ -192,7 +208,7 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) packet_t *packet; task_t *task; ike_mobike_t *mobike = NULL; - + /* check if we are retransmitting a MOBIKE routability check */ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) @@ -211,10 +227,10 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) if (mobike == NULL) { - if (this->initiating.retransmitted <= RETRANSMIT_TRIES) + if (this->initiating.retransmitted <= this->retransmit_tries) { - timeout = (u_int32_t)(RETRANSMIT_TIMEOUT * - pow(RETRANSMIT_BASE, this->initiating.retransmitted)); + timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 * + pow(this->retransmit_base, this->initiating.retransmitted)); } else { @@ -226,13 +242,14 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) } return DESTROY_ME; } - + if (this->initiating.retransmitted) { DBG1(DBG_IKE, "retransmit %d of request with message ID %d", this->initiating.retransmitted, message_id); } packet = this->initiating.packet->clone(this->initiating.packet); + charon->sender->send(charon->sender, packet); } else { /* for routeability checks, we use a more aggressive behavior */ @@ -247,18 +264,15 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } - + if (this->initiating.retransmitted) { DBG1(DBG_IKE, "path probing attempt %d", this->initiating.retransmitted); } - packet = this->initiating.packet->clone(this->initiating.packet); - mobike->transmit(mobike, packet); + mobike->transmit(mobike, this->initiating.packet); } - - charon->sender->send(charon->sender, packet); - + this->initiating.retransmitted++; job = (job_t*)retransmit_job_create(this->initiating.mid, this->ike_sa->get_id(this->ike_sa)); @@ -279,14 +293,14 @@ static status_t build_request(private_task_manager_t *this) host_t *me, *other; status_t status; exchange_type_t exchange = 0; - + if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) { DBG2(DBG_IKE, "delaying task initiation, exchange in progress"); /* do not initiate if we already have a message in the air */ return SUCCESS; } - + if (this->active_tasks->get_count(this->active_tasks) == 0) { DBG2(DBG_IKE, "activating new tasks"); @@ -297,6 +311,7 @@ static status_t build_request(private_task_manager_t *this) { this->initiating.mid = 0; exchange = IKE_SA_INIT; + activate_task(this, IKE_VENDOR); activate_task(this, IKE_NATD); activate_task(this, IKE_CERT_PRE); #ifdef ME @@ -402,17 +417,17 @@ static status_t build_request(private_task_manager_t *this) } iterator->destroy(iterator); } - + if (exchange == 0) { DBG2(DBG_IKE, "nothing to initiate"); /* nothing to do yet... */ return SUCCESS; } - + me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); - + message = message_create(); message->set_message_id(message, this->initiating.mid); message->set_source(message, me->clone(me)); @@ -420,7 +435,7 @@ static status_t build_request(private_task_manager_t *this) message->set_exchange_type(message, exchange); this->initiating.type = exchange; this->initiating.retransmitted = 0; - + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { @@ -450,10 +465,11 @@ static status_t build_request(private_task_manager_t *this) } } iterator->destroy(iterator); - + /* update exchange type if a task changed it */ this->initiating.type = message->get_exchange_type(message); - + + charon->bus->message(charon->bus, message, FALSE); status = this->ike_sa->generate_message(this->ike_sa, message, &this->initiating.packet); if (status != SUCCESS) @@ -465,10 +481,8 @@ static status_t build_request(private_task_manager_t *this) charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } - - charon->bus->message(charon->bus, message, FALSE); message->destroy(message); - + return retransmit(this, this->initiating.mid); } @@ -480,7 +494,7 @@ static status_t process_response(private_task_manager_t *this, { iterator_t *iterator; task_t *task; - + if (message->get_exchange_type(message) != this->initiating.type) { DBG1(DBG_IKE, "received %N response, but expected %N", @@ -489,7 +503,7 @@ static status_t process_response(private_task_manager_t *this, charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } - + /* catch if we get resetted while processing */ this->reset = FALSE; iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); @@ -521,15 +535,15 @@ static status_t process_response(private_task_manager_t *this, this->reset = FALSE; iterator->destroy(iterator); return build_request(this); - } + } } iterator->destroy(iterator); - + this->initiating.mid++; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; this->initiating.packet->destroy(this->initiating.packet); this->initiating.packet = NULL; - + return build_request(this); } @@ -541,9 +555,9 @@ static void handle_collisions(private_task_manager_t *this, task_t *task) iterator_t *iterator; task_t *active; task_type_t type; - + type = task->get_type(task); - + /* do we have to check */ if (type == IKE_REKEY || type == CHILD_REKEY || type == CHILD_DELETE || type == IKE_DELETE || type == IKE_REAUTH) @@ -594,10 +608,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) host_t *me, *other; bool delete = FALSE; status_t status; - + me = request->get_destination(request); other = request->get_source(request); - + message = message_create(); message->set_exchange_type(message, request->get_exchange_type(request)); /* send response along the path the request came in */ @@ -605,7 +619,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) message->set_destination(message, other->clone(other)); message->set_message_id(message, this->responding.mid); message->set_request(message, FALSE); - + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { @@ -633,27 +647,27 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } } iterator->destroy(iterator); - + /* remove resonder SPI if IKE_SA_INIT failed */ if (delete && request->get_exchange_type(request) == IKE_SA_INIT) { ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); id->set_responder_spi(id, 0); } - + /* message complete, send it */ DESTROY_IF(this->responding.packet); this->responding.packet = NULL; + charon->bus->message(charon->bus, message, FALSE); status = this->ike_sa->generate_message(this->ike_sa, message, &this->responding.packet); - charon->bus->message(charon->bus, message, FALSE); message->destroy(message); if (status != SUCCESS) { charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } - + charon->sender->send(charon->sender, this->responding.packet->clone(this->responding.packet)); if (delete) @@ -675,7 +689,7 @@ static status_t process_request(private_task_manager_t *this, payload_t *payload; notify_payload_t *notify; delete_payload_t *delete; - + if (this->passive_tasks->get_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ switch (message->get_exchange_type(message)) @@ -684,11 +698,13 @@ static status_t process_request(private_task_manager_t *this, { task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_vendor_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t*)ike_natd_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); -#ifdef ME +#ifdef ME task = (task_t*)ike_me_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); #endif /* ME */ @@ -737,7 +753,7 @@ static status_t process_request(private_task_manager_t *this, } } enumerator->destroy(enumerator); - + if (ts_found) { if (notify_found) @@ -816,7 +832,7 @@ static status_t process_request(private_task_manager_t *this, } } enumerator->destroy(enumerator); - + if (task == NULL) { task = (task_t*)ike_dpd_create(FALSE); @@ -835,7 +851,7 @@ static status_t process_request(private_task_manager_t *this, break; } } - + /* let the tasks process the message */ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) @@ -863,7 +879,7 @@ static status_t process_request(private_task_manager_t *this, } } iterator->destroy(iterator); - + return build_response(this, message); } @@ -873,7 +889,7 @@ static status_t process_request(private_task_manager_t *this, static status_t process_message(private_task_manager_t *this, message_t *msg) { u_int32_t mid = msg->get_message_id(msg); - + if (msg->get_request(msg)) { if (mid == this->responding.mid) @@ -890,9 +906,9 @@ static status_t process_message(private_task_manager_t *this, message_t *msg) { packet_t *clone; host_t *me, *other; - + DBG1(DBG_IKE, "received retransmit of request with ID %d, " - "retransmitting response", mid); + "retransmitting response", mid); clone = this->responding.packet->clone(this->responding.packet); me = msg->get_destination(msg); other = msg->get_source(msg); @@ -935,7 +951,7 @@ static void queue_task(private_task_manager_t *this, task_t *task) { /* there is no need to queue more than one mobike task */ iterator_t *iterator; task_t *current; - + iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE); while (iterator->iterate(iterator, (void**)¤t)) { @@ -958,7 +974,7 @@ static void queue_task(private_task_manager_t *this, task_t *task) static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other) { task_t *task; - + /* move queued tasks from other to this */ while (other->queued_tasks->remove_last(other->queued_tasks, (void**)&task) == SUCCESS) @@ -984,7 +1000,7 @@ static void reset(private_task_manager_t *this, u_int32_t initiate, u_int32_t respond) { task_t *task; - + /* reset message counters and retransmit packets */ DESTROY_IF(this->responding.packet); DESTROY_IF(this->initiating.packet); @@ -999,7 +1015,7 @@ static void reset(private_task_manager_t *this, this->responding.mid = respond; } this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - + /* reset active tasks */ while (this->active_tasks->remove_last(this->active_tasks, (void**)&task) == SUCCESS) @@ -1007,7 +1023,7 @@ static void reset(private_task_manager_t *this, task->migrate(task, this->ike_sa); this->queued_tasks->insert_first(this->queued_tasks, task); } - + this->reset = TRUE; } @@ -1017,11 +1033,11 @@ static void reset(private_task_manager_t *this, static void destroy(private_task_manager_t *this) { flush(this); - + this->active_tasks->destroy(this->active_tasks); this->queued_tasks->destroy(this->queued_tasks); this->passive_tasks->destroy(this->passive_tasks); - + DESTROY_IF(this->responding.packet); DESTROY_IF(this->initiating.packet); free(this); @@ -1033,7 +1049,7 @@ static void destroy(private_task_manager_t *this) task_manager_t *task_manager_create(ike_sa_t *ike_sa) { private_task_manager_t *this = malloc_thing(private_task_manager_t); - + this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message; this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task; this->public.initiate = (status_t(*)(task_manager_t*))build_request; @@ -1042,7 +1058,7 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa) this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks; this->public.busy = (bool(*)(task_manager_t*))busy; this->public.destroy = (void(*)(task_manager_t*))destroy; - + this->ike_sa = ike_sa; this->responding.packet = NULL; this->initiating.packet = NULL; @@ -1053,6 +1069,14 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa) this->active_tasks = linked_list_create(); this->passive_tasks = linked_list_create(); this->reset = FALSE; - + + this->retransmit_tries = lib->settings->get_int(lib->settings, + "charon.retransmit_tries", RETRANSMIT_TRIES); + this->retransmit_timeout = lib->settings->get_double(lib->settings, + "charon.retransmit_timeout", RETRANSMIT_TIMEOUT); + this->retransmit_base = lib->settings->get_double(lib->settings, + "charon.retransmit_base", RETRANSMIT_BASE); + return &this->public; } + diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h index 9c3b2cc87..731ed4898 100644 --- a/src/charon/sa/task_manager.h +++ b/src/charon/sa/task_manager.h @@ -31,9 +31,9 @@ typedef struct task_manager_t task_manager_t; #include <sa/tasks/task.h> /** - * First retransmit timeout in milliseconds. + * First retransmit timeout in seconds. */ -#define RETRANSMIT_TIMEOUT 4000 +#define RETRANSMIT_TIMEOUT 4.0 /** * Base which is raised to the power of the retransmission try. @@ -69,7 +69,7 @@ typedef struct task_manager_t task_manager_t; * For the initial IKE_SA setup, several tasks are queued: One for the * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup * and maybe one for virtual IP assignement. - * The task manager is also responsible for retransmission. It uses a backoff + * The task manager is also responsible for retransmission. It uses a backoff * algorithm. The timeout is calculated using * RETRANSMIT_TIMEOUT * (RETRANSMIT_BASE ** try). * When try reaches RETRANSMIT_TRIES, retransmission is given up. @@ -84,7 +84,7 @@ typedef struct task_manager_t task_manager_t; 4s * (1.8 ** 3) = 23s 47s 4s * (1.8 ** 4) = 42s 89s 4s * (1.8 ** 5) = 76s 165s - + @endverbatim * The peer is considered dead after 2min 45s when no reply comes in. */ @@ -92,10 +92,10 @@ struct task_manager_t { /** * Process an incoming message. - * + * * @param message message to add payloads to * @return - * - DESTROY_ME if IKE_SA must be closed + * - DESTROY_ME if IKE_SA must be closed * - SUCCESS otherwise */ status_t (*process_message) (task_manager_t *this, message_t *message); @@ -118,24 +118,24 @@ struct task_manager_t { * A return value of INVALID_STATE means that the message was already * acknowledged and has not to be retransmitted. A return value of SUCCESS * means retransmission was required and the message has been resent. - * + * * @param message_id ID of the message to retransmit * @return - * - INVALID_STATE if retransmission not required + * - INVALID_STATE if retransmission not required * - SUCCESS if retransmission sent */ status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); - + /** * Migrate all tasks from other to this. * * To rekey or reestablish an IKE_SA completely, all queued or active * tasks should get migrated to the new IKE_SA. - * + * * @param other manager which gives away its tasks */ void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); - + /** * Reset message ID counters of the task manager. * @@ -149,14 +149,14 @@ struct task_manager_t { * @param respond message ID to respond to exchanges (expect) */ void (*reset) (task_manager_t *this, u_int32_t initiate, u_int32_t respond); - + /** * Check if we are currently waiting for a reply. * * @return TRUE if we are waiting, FALSE otherwise */ bool (*busy) (task_manager_t *this); - + /** * Destroy the task_manager_t. */ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c index 558938f2e..3f002f263 100644 --- a/src/charon/sa/tasks/child_create.c +++ b/src/charon/sa/tasks/child_create.c @@ -19,12 +19,14 @@ #include <daemon.h> #include <crypto/diffie_hellman.h> +#include <credentials/certificates/x509.h> #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/ke_payload.h> #include <encoding/payloads/ts_payload.h> #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/notify_payload.h> #include <processing/jobs/delete_ike_sa_job.h> +#include <processing/jobs/inactivity_job.h> typedef struct private_child_create_t private_child_create_t; @@ -33,132 +35,132 @@ typedef struct private_child_create_t private_child_create_t; * Private members of a child_create_t task. */ struct private_child_create_t { - + /** * Public methods and task_t interface. */ child_create_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * nonce chosen by us */ chunk_t my_nonce; - + /** * nonce chosen by peer */ chunk_t other_nonce; - + /** * config to create the CHILD_SA from */ child_cfg_t *config; - + /** * list of proposal candidates */ linked_list_t *proposals; - + /** * selected proposal to use for CHILD_SA */ proposal_t *proposal; - + /** * traffic selectors for initiators side */ linked_list_t *tsi; - + /** * traffic selectors for responders side */ linked_list_t *tsr; - + /** * source of triggering packet */ traffic_selector_t *packet_tsi; - + /** * destination of triggering packet */ traffic_selector_t *packet_tsr; - + /** * optional diffie hellman exchange */ diffie_hellman_t *dh; - + /** * group used for DH exchange */ diffie_hellman_group_t dh_group; - + /** * IKE_SAs keymat */ keymat_t *keymat; - + /** * mode the new CHILD_SA uses (transport/tunnel/beet) */ ipsec_mode_t mode; - + /** * IPComp transform to use */ ipcomp_transform_t ipcomp; - + /** * IPComp transform proposed or accepted by the other peer */ ipcomp_transform_t ipcomp_received; - + /** * Own allocated SPI */ u_int32_t my_spi; - + /** * SPI received in proposal */ u_int32_t other_spi; - + /** * Own allocated Compression Parameter Index (CPI) */ u_int16_t my_cpi; - + /** * Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED */ u_int16_t other_cpi; - + /** * reqid to use if we are rekeying */ u_int32_t reqid; - + /** * CHILD_SA which gets established */ child_sa_t *child_sa; - + /** * successfully established the CHILD? */ bool established; - + /** * whether the CHILD_SA rekeys an existing one */ @@ -171,7 +173,7 @@ struct private_child_create_t { static status_t get_nonce(message_t *message, chunk_t *nonce) { nonce_payload_t *payload; - + payload = (nonce_payload_t*)message->get_payload(message, NONCE); if (payload == NULL) { @@ -187,7 +189,7 @@ static status_t get_nonce(message_t *message, chunk_t *nonce) static status_t generate_nonce(chunk_t *nonce) { rng_t *rng; - + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { @@ -207,7 +209,7 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host) traffic_selector_t *ts; bool is_host = TRUE; iterator_t *iterator = list->create_iterator(list, TRUE); - + while (is_host && iterator->iterate(iterator, (void**)&ts)) { is_host = is_host && ts->is_host(ts, host); @@ -223,8 +225,8 @@ static bool allocate_spi(private_child_create_t *this) { enumerator_t *enumerator; proposal_t *proposal; - - /* TODO: allocate additional SPI for AH if we have such proposals */ + + /* TODO: allocate additional SPI for AH if we have such proposals */ this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); if (this->my_spi) { @@ -247,6 +249,25 @@ static bool allocate_spi(private_child_create_t *this) } /** + * Schedule inactivity timeout for CHILD_SA with reqid, if enabled + */ +static void schedule_inactivity_timeout(private_child_create_t *this) +{ + u_int32_t timeout; + bool close_ike; + + timeout = this->config->get_inactivity(this->config); + if (timeout) + { + close_ike = lib->settings->get_bool(lib->settings, + "charon.inactivity_close_ike", FALSE); + charon->scheduler->schedule_job(charon->scheduler, (job_t*) + inactivity_job_create(this->child_sa->get_reqid(this->child_sa), + timeout, close_ike), timeout); + } +} + +/** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal * - INVALID_ARG: diffie hellman group inacceptable @@ -260,7 +281,8 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; host_t *me, *other, *other_vip, *my_vip; - + bool private; + if (this->proposals == NULL) { DBG1(DBG_IKE, "SA payload missing in message"); @@ -271,32 +293,33 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) DBG1(DBG_IKE, "TS payloads missing in message"); return NOT_FOUND; } - + me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - - this->proposal = this->config->select_proposal(this->config, this->proposals, - no_dh); + + private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); + this->proposal = this->config->select_proposal(this->config, + this->proposals, no_dh, private); if (this->proposal == NULL) { DBG1(DBG_IKE, "no acceptable proposal found"); return FAILED; } this->other_spi = this->proposal->get_spi(this->proposal); - + if (!this->initiator && !allocate_spi(this)) { /* responder has no SPI allocated yet */ DBG1(DBG_IKE, "allocating SPI failed"); return FAILED; } this->child_sa->set_proposal(this->child_sa, this->proposal); - + if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) { u_int16_t group; - + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) { @@ -312,7 +335,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) return FAILED; } } - + if (my_vip == NULL) { my_vip = me; @@ -321,7 +344,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) { other_vip = other; } - + if (this->initiator) { nonce_i = this->my_nonce; @@ -338,9 +361,9 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) } my_ts = this->config->get_traffic_selectors(this->config, TRUE, my_ts, my_vip); - other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts, + other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts, other_vip); - + if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) { my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); @@ -348,7 +371,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) DBG1(DBG_IKE, "no acceptable traffic selectors found"); return NOT_FOUND; } - + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); if (this->initiator) @@ -361,7 +384,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) this->tsr = my_ts; this->tsi = other_ts; } - + if (!this->initiator) { /* check if requested mode is acceptable, downgrade if required */ @@ -394,13 +417,73 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) break; } } - + + /* check for any certificate-based IP address block constraints */ + if (this->mode == MODE_BEET || this->mode == MODE_TUNNEL) + { + auth_cfg_t *auth; + enumerator_t *auth_enum; + certificate_t *cert = NULL; + + auth_enum = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE); + while (auth_enum->enumerate(auth_enum, &auth)) + { + cert = auth->get(auth, AUTH_HELPER_SUBJECT_CERT); + if (cert) + { + break; + } + } + auth_enum->destroy(auth_enum); + + if (cert && cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + + if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS) + { + enumerator_t *enumerator, *block_enum; + traffic_selector_t *ts, *block_ts; + + DBG1(DBG_IKE, "checking certificate-based traffic selector " + "constraints [RFC 3779]"); + enumerator = other_ts->create_enumerator(other_ts); + while (enumerator->enumerate(enumerator, &ts)) + { + bool contained = FALSE; + + block_enum = x509->create_ipAddrBlock_enumerator(x509); + while (block_enum->enumerate(block_enum, &block_ts)) + { + if (ts->is_contained_in(ts, block_ts)) + { + DBG1(DBG_IKE, " TS %R is contained in address block" + " constraint %R", ts, block_ts); + contained = TRUE; + break; + } + } + block_enum->destroy(block_enum); + + if (!contained) + { + DBG1(DBG_IKE, " TS %R is not contained in any" + " address block constraint", ts); + enumerator->destroy(enumerator); + return FAILED; + } + } + enumerator->destroy(enumerator); + } + } + } + this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); this->child_sa->set_ipcomp(this->child_sa, this->ipcomp); this->child_sa->set_mode(this->child_sa, this->mode); this->child_sa->set_protocol(this->child_sa, this->proposal->get_protocol(this->proposal)); - + if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE) { this->my_cpi = this->other_cpi = 0; @@ -408,28 +491,28 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) } status_i = status_o = FAILED; if (this->keymat->derive_child_keys(this->keymat, this->proposal, - this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) + this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) { if (this->initiator) { status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->my_spi, this->my_cpi, TRUE); + this->my_spi, this->my_cpi, TRUE, my_ts, other_ts); status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->other_spi, this->other_cpi, FALSE); + this->other_spi, this->other_cpi, FALSE, my_ts, other_ts); } else { status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->my_spi, this->my_cpi, TRUE); + this->my_spi, this->my_cpi, TRUE, my_ts, other_ts); status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->other_spi, this->other_cpi, FALSE); + this->other_spi, this->other_cpi, FALSE, my_ts, other_ts); } } chunk_clear(&integ_i); chunk_clear(&integ_r); chunk_clear(&encr_i); chunk_clear(&encr_r); - + if (status_i != SUCCESS || status_o != SUCCESS) { DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel", @@ -438,21 +521,26 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) (status_o != SUCCESS) ? "outbound " : ""); return FAILED; } - + status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts); if (status != SUCCESS) - { + { DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); return NOT_FOUND; } - + charon->bus->child_keys(charon->bus, this->child_sa, this->dh, nonce_i, nonce_r); - + /* add to IKE_SA, and remove from task */ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); this->established = TRUE; + + if (!this->rekey) + { /* a rekeyed SA uses the same reqid, no need for a new job */ + schedule_inactivity_timeout(this); + } return SUCCESS; } @@ -476,7 +564,7 @@ static void build_payloads(private_child_create_t *this, message_t *message) sa_payload = sa_payload_create_from_proposal(this->proposal); } message->add_payload(message, (payload_t*)sa_payload); - + /* add nonce payload if not in IKE_AUTH */ if (message->get_exchange_type(message) == CREATE_CHILD_SA) { @@ -484,14 +572,14 @@ static void build_payloads(private_child_create_t *this, message_t *message) nonce_payload->set_nonce(nonce_payload, this->my_nonce); message->add_payload(message, (payload_t*)nonce_payload); } - + /* diffie hellman exchange, if PFS enabled */ if (this->dh) { ke_payload = ke_payload_create_from_diffie_hellman(this->dh); message->add_payload(message, (payload_t*)ke_payload); } - + /* add TSi/TSr payloads */ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); message->add_payload(message, (payload_t*)ts_payload); @@ -524,12 +612,12 @@ static void add_ipcomp_notify(private_child_create_t *this, "IPComp disabled"); return; } - + this->my_cpi = this->child_sa->alloc_cpi(this->child_sa); if (this->my_cpi) { this->ipcomp = ipcomp; - message->add_notify(message, FALSE, IPCOMP_SUPPORTED, + message->add_notify(message, FALSE, IPCOMP_SUPPORTED, chunk_cata("cc", chunk_from_thing(this->my_cpi), chunk_from_thing(ipcomp))); } @@ -550,14 +638,22 @@ static void handle_notify(private_child_create_t *this, notify_payload_t *notify this->mode = MODE_TRANSPORT; break; case USE_BEET_MODE: - this->mode = MODE_BEET; + if (this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)) + { /* handle private use notify only if we know its meaning */ + this->mode = MODE_BEET; + } + else + { + DBG1(DBG_IKE, "received a notify strongSwan uses for BEET " + "mode, but peer implementation unknown, skipped"); + } break; case IPCOMP_SUPPORTED: { ipcomp_transform_t ipcomp; u_int16_t cpi; chunk_t data; - + data = notify->get_notification_data(notify); cpi = *(u_int16_t*)data.ptr; ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); @@ -591,7 +687,7 @@ static void process_payloads(private_child_create_t *this, message_t *message) sa_payload_t *sa_payload; ke_payload_t *ke_payload; ts_payload_t *ts_payload; - + /* defaults to TUNNEL mode */ this->mode = MODE_TUNNEL; @@ -620,7 +716,7 @@ static void process_payloads(private_child_create_t *this, message_t *message) case TRAFFIC_SELECTOR_INITIATOR: ts_payload = (ts_payload_t*)payload; this->tsi = ts_payload->get_traffic_selectors(ts_payload); - break; + break; case TRAFFIC_SELECTOR_RESPONDER: ts_payload = (ts_payload_t*)payload; this->tsr = ts_payload->get_traffic_selectors(ts_payload); @@ -642,7 +738,7 @@ static status_t build_i(private_child_create_t *this, message_t *message) { host_t *me, *other, *vip; peer_cfg_t *peer_cfg; - + switch (message->get_exchange_type(message)) { case IKE_SA_INIT: @@ -668,7 +764,7 @@ static status_t build_i(private_child_create_t *this, message_t *message) default: break; } - + if (this->reqid) { DBG0(DBG_IKE, "establishing CHILD_SA %s{%d}", @@ -679,7 +775,7 @@ static status_t build_i(private_child_create_t *this, message_t *message) DBG0(DBG_IKE, "establishing CHILD_SA %s", this->config->get_name(this->config)); } - + /* reuse virtual IP if we already have one */ me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); if (me == NULL) @@ -691,7 +787,7 @@ static status_t build_i(private_child_create_t *this, message_t *message) { other = this->ike_sa->get_other_host(this->ike_sa); } - + /* check if we want a virtual IP, but don't have one */ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); vip = peer_cfg->get_virtual_ip(peer_cfg); @@ -708,9 +804,9 @@ static status_t build_i(private_child_create_t *this, message_t *message) this->tsi = this->config->get_traffic_selectors(this->config, TRUE, NULL, me); } - this->tsr = this->config->get_traffic_selectors(this->config, FALSE, + this->tsr = this->config->get_traffic_selectors(this->config, FALSE, NULL, other); - + if (this->packet_tsi) { this->tsi->insert_first(this->tsi, @@ -724,37 +820,43 @@ static status_t build_i(private_child_create_t *this, message_t *message) this->proposals = this->config->get_proposals(this->config, this->dh_group == MODP_NONE); this->mode = this->config->get_mode(this->config); - + if (this->mode == MODE_TRANSPORT && + this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using transport mode, connection NATed"); + } + this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - + if (!allocate_spi(this)) { DBG1(DBG_IKE, "unable to allocate SPIs from kernel"); return FAILED; } - + if (this->dh_group != MODP_NONE) { this->dh = this->keymat->create_dh(this->keymat, this->dh_group); } - + if (this->config->use_ipcomp(this->config)) { /* IPCOMP_DEFLATE is the only transform we support at the moment */ add_ipcomp_notify(this, message, IPCOMP_DEFLATE); } - + build_payloads(this, message); - + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); this->tsi = NULL; this->tsr = NULL; this->proposals = NULL; - + return NEED_MORE; } @@ -779,9 +881,9 @@ static status_t process_r(private_child_create_t *this, message_t *message) default: break; } - + process_payloads(this, message); - + return NEED_MORE; } @@ -813,7 +915,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) payload_t *payload; enumerator_t *enumerator; bool no_dh = TRUE; - + switch (message->get_exchange_type(message)) { case IKE_SA_INIT: @@ -835,19 +937,19 @@ static status_t build_r(private_child_create_t *this, message_t *message) default: break; } - + if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) { DBG1(DBG_IKE, "unable to create CHILD_SA while rekeying IKE_SA"); message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); return SUCCESS; } - + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (peer_cfg && this->tsi && this->tsr) { host_t *me, *other; - + me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); if (me == NULL) { @@ -861,7 +963,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) this->config = peer_cfg->select_child_cfg(peer_cfg, this->tsr, this->tsi, me, other); } - + if (this->config == NULL) { DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable", @@ -870,7 +972,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) handle_child_sa_failure(this, message); return SUCCESS; } - + /* check if ike_config_t included non-critical error notifies */ enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) @@ -878,7 +980,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) if (payload->get_type(payload) == NOTIFY) { notify_payload_t *notify = (notify_payload_t*)payload; - + switch (notify->get_notify_type(notify)) { case INTERNAL_ADDRESS_FAILURE: @@ -896,11 +998,11 @@ static status_t build_r(private_child_create_t *this, message_t *message) } } enumerator->destroy(enumerator); - + this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - + if (this->ipcomp_received != IPCOMP_NONE) { if (this->config->use_ipcomp(this->config)) @@ -913,7 +1015,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) notify_type_names, IPCOMP_SUPPORTED); } } - + switch (select_and_install(this, no_dh)) { case SUCCESS: @@ -936,9 +1038,9 @@ static status_t build_r(private_child_create_t *this, message_t *message) handle_child_sa_failure(this, message); return SUCCESS; } - + build_payloads(this, message); - + DBG0(DBG_IKE, "CHILD_SA %s{%d} established " "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", this->child_sa->get_name(this->child_sa), @@ -947,7 +1049,7 @@ static status_t build_r(private_child_create_t *this, message_t *message) ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), this->child_sa->get_traffic_selectors(this->child_sa, TRUE), this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - + if (!this->rekey) { /* invoke the child_up() hook if we are not rekeying */ charon->bus->child_updown(charon->bus, this->child_sa, TRUE); @@ -989,7 +1091,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) { notify_payload_t *notify = (notify_payload_t*)payload; notify_type_t type = notify->get_notify_type(notify); - + switch (type) { /* handle notify errors related to CHILD_SA only */ @@ -1011,15 +1113,18 @@ static status_t process_i(private_child_create_t *this, message_t *message) case INVALID_KE_PAYLOAD: { chunk_t data; - diffie_hellman_group_t bad_group; - - bad_group = this->dh_group; + u_int16_t group = MODP_NONE; + data = notify->get_notification_data(notify); - this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + if (data.len == sizeof(group)) + { + memcpy(&group, data.ptr, data.len); + group = ntohs(group); + } DBG1(DBG_IKE, "peer didn't accept DH group %N, " "it requested %N", diffie_hellman_group_names, - bad_group, diffie_hellman_group_names, this->dh_group); - + this->dh_group, diffie_hellman_group_names, group); + this->dh_group = group; this->public.task.migrate(&this->public.task, this->ike_sa); enumerator->destroy(enumerator); return NEED_MORE; @@ -1030,9 +1135,9 @@ static status_t process_i(private_child_create_t *this, message_t *message) } } enumerator->destroy(enumerator); - + process_payloads(this, message); - + if (this->ipcomp == IPCOMP_NONE && this->ipcomp_received != IPCOMP_NONE) { DBG1(DBG_IKE, "received an IPCOMP_SUPPORTED notify without requesting" @@ -1053,7 +1158,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) handle_child_sa_failure(this, message); return SUCCESS; } - + if (select_and_install(this, no_dh) == SUCCESS) { DBG0(DBG_IKE, "CHILD_SA %s{%d} established " @@ -1064,7 +1169,7 @@ static status_t process_i(private_child_create_t *this, message_t *message) ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), this->child_sa->get_traffic_selectors(this->child_sa, TRUE), this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - + if (!this->rekey) { /* invoke the child_up() hook if we are not rekeying */ charon->bus->child_updown(charon->bus, this->child_sa, TRUE); @@ -1105,7 +1210,7 @@ static child_sa_t* get_child(private_child_create_t *this) * Implementation of child_create_t.get_lower_nonce */ static chunk_t get_lower_nonce(private_child_create_t *this) -{ +{ if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, min(this->my_nonce.len, this->other_nonce.len)) < 0) { @@ -1139,7 +1244,7 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) { this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); } - + this->ike_sa = ike_sa; this->keymat = ike_sa->get_keymat(ike_sa); this->proposal = NULL; @@ -1183,7 +1288,7 @@ static void destroy(private_child_create_t *this) { this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); } - + DESTROY_IF(this->config); free(this); } @@ -1216,7 +1321,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; this->initiator = FALSE; } - + this->ike_sa = ike_sa; this->config = config; this->my_nonce = chunk_empty; @@ -1241,6 +1346,6 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, this->reqid = 0; this->established = FALSE; this->rekey = rekey; - + return &this->public; } diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h index 41f4fe2c8..5dedeb8b1 100644 --- a/src/charon/sa/tasks/child_create.h +++ b/src/charon/sa/tasks/child_create.h @@ -31,7 +31,7 @@ typedef struct child_create_t child_create_t; /** * Task of type CHILD_CREATE, established a new CHILD_SA. * - * This task may be included in the IKE_AUTH message or in a separate + * This task may be included in the IKE_AUTH message or in a separate * CREATE_CHILD_SA exchange. */ struct child_create_t { @@ -40,24 +40,24 @@ struct child_create_t { * Implements the task_t interface */ task_t task; - + /** * Use a specific reqid for the CHILD_SA. * * When this task is used for rekeying, the same reqid is used - * for the new CHILD_SA. + * for the new CHILD_SA. * * @param reqid reqid to use */ void (*use_reqid) (child_create_t *this, u_int32_t reqid); - + /** * Get the lower of the two nonces, used for rekey collisions. * * @return lower nonce */ chunk_t (*get_lower_nonce) (child_create_t *this); - + /** * Get the CHILD_SA established/establishing by this task. * diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c index 7abb07a84..d7c6b0541 100644 --- a/src/charon/sa/tasks/child_delete.c +++ b/src/charon/sa/tasks/child_delete.c @@ -25,42 +25,42 @@ typedef struct private_child_delete_t private_child_delete_t; * Private members of a child_delete_t task. */ struct private_child_delete_t { - + /** * Public methods and task_t interface. */ child_delete_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Protocol of CHILD_SA to delete */ protocol_id_t protocol; - + /** * Inbound SPI of CHILD_SA to delete */ u_int32_t spi; - + /** * whether to enforce delete action policy */ bool check_delete_action; - + /** * is this delete exchange following a rekey? */ bool rekeyed; - + /** * CHILD_SAs which get deleted */ @@ -75,10 +75,10 @@ static void build_payloads(private_child_delete_t *this, message_t *message) delete_payload_t *ah = NULL, *esp = NULL; iterator_t *iterator; child_sa_t *child_sa; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) - { + { protocol_id_t protocol = child_sa->get_protocol(child_sa); u_int32_t spi = child_sa->get_spi(child_sa, TRUE); @@ -91,7 +91,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message) message->add_payload(message, (payload_t*)esp); } esp->add_spi(esp, spi); - DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", + DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, protocol, ntohl(spi)); break; case PROTO_AH: @@ -101,7 +101,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message) message->add_payload(message, (payload_t*)ah); } ah->add_spi(ah, spi); - DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", + DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, protocol, ntohl(spi)); break; default: @@ -124,7 +124,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message) u_int32_t *spi; protocol_id_t protocol; child_sa_t *child_sa; - + payloads = message->create_payload_enumerator(message); while (payloads->enumerate(payloads, &payload)) { @@ -147,9 +147,9 @@ static void process_payloads(private_child_delete_t *this, message_t *message) "but no such SA", protocol_id_names, protocol, ntohl(*spi)); continue; } - DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x", + DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, protocol, ntohl(*spi)); - + switch (child_sa->get_state(child_sa)) { case CHILD_REKEYING: @@ -172,7 +172,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message) default: break; } - + this->child_sas->insert_last(this->child_sas, child_sa); } spis->destroy(spis); @@ -192,7 +192,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) protocol_id_t protocol; u_int32_t spi; status_t status = SUCCESS; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { @@ -215,7 +215,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) status = this->ike_sa->initiate(this->ike_sa, child_cfg, 0, NULL, NULL); break; - case ACTION_ROUTE: + case ACTION_ROUTE: charon->traps->install(charon->traps, this->ike_sa->get_peer_cfg(this->ike_sa), child_cfg); break; @@ -241,13 +241,13 @@ static void log_children(private_child_delete_t *this) iterator_t *iterator; child_sa_t *child_sa; u_int64_t bytes_in, bytes_out; - + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in); child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out); - + DBG0(DBG_IKE, "closing CHILD_SA %s{%d} " "with SPIs %.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), @@ -265,12 +265,19 @@ static void log_children(private_child_delete_t *this) static status_t build_i(private_child_delete_t *this, message_t *message) { child_sa_t *child_sa; - + child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, TRUE); if (!child_sa) - { /* child does not exist anymore */ - return SUCCESS; + { /* check if it is an outbound sa */ + child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, + this->spi, FALSE); + if (!child_sa) + { /* child does not exist anymore */ + return SUCCESS; + } + /* we work only with the inbound SPI */ + this->spi = child_sa->get_spi(child_sa, TRUE); } this->child_sas->insert_last(this->child_sas, child_sa); if (child_sa->get_state(child_sa) == CHILD_REKEYING) @@ -290,7 +297,7 @@ static status_t process_i(private_child_delete_t *this, message_t *message) /* flush the list before adding new SAs */ this->child_sas->destroy(this->child_sas); this->child_sas = linked_list_create(); - + process_payloads(this, message); DBG1(DBG_IKE, "CHILD_SA closed"); return destroy_and_reestablish(this); @@ -314,7 +321,7 @@ static status_t build_r(private_child_delete_t *this, message_t *message) /* if we are rekeying, we send an empty informational */ if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING) { - build_payloads(this, message); + build_payloads(this, message); } DBG1(DBG_IKE, "CHILD_SA closed"); return destroy_and_reestablish(this); @@ -345,7 +352,7 @@ static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa) { this->check_delete_action = FALSE; this->ike_sa = ike_sa; - + this->child_sas->destroy(this->child_sas); this->child_sas = linked_list_create(); } @@ -371,14 +378,14 @@ child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + this->ike_sa = ike_sa; this->check_delete_action = FALSE; this->child_sas = linked_list_create(); this->protocol = protocol; this->spi = spi; this->rekeyed = FALSE; - + if (protocol != PROTO_NONE) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h index 27d847035..365807c68 100644 --- a/src/charon/sa/tasks/child_delete.h +++ b/src/charon/sa/tasks/child_delete.h @@ -37,7 +37,7 @@ struct child_delete_t { * Implements the task_t interface */ task_t task; - + /** * Get the CHILD_SA to delete by this task. * diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c index 601e054ea..b5e4e84b4 100644 --- a/src/charon/sa/tasks/child_rekey.c +++ b/src/charon/sa/tasks/child_rekey.c @@ -30,47 +30,47 @@ typedef struct private_child_rekey_t private_child_rekey_t; * Private members of a child_rekey_t task. */ struct private_child_rekey_t { - + /** * Public methods and task_t interface. */ child_rekey_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Protocol of CHILD_SA to rekey */ protocol_id_t protocol; - + /** * Inbound SPI of CHILD_SA to rekey */ u_int32_t spi; - + /** * the CHILD_CREATE task which is reused to simplify rekeying */ child_create_t *child_create; - + /** * the CHILD_DELETE task to delete rekeyed CHILD_SA */ child_delete_t *child_delete; - + /** * CHILD_SA which gets rekeyed */ child_sa_t *child_sa; - + /** * colliding task, may be delete or rekey */ @@ -84,7 +84,7 @@ static status_t build_i_delete(private_child_rekey_t *this, message_t *message) { /* update exchange type to INFORMATIONAL for the delete */ message->set_exchange_type(message, INFORMATIONAL); - + return this->child_delete->task.build(&this->child_delete->task, message); } @@ -101,35 +101,22 @@ static status_t process_i_delete(private_child_rekey_t *this, message_t *message */ static void find_child(private_child_rekey_t *this, message_t *message) { - enumerator_t *enumerator; - payload_t *payload; - - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) + notify_payload_t *notify; + protocol_id_t protocol; + u_int32_t spi; + + notify = message->get_notify(message, REKEY_SA); + if (notify) { - notify_payload_t *notify; - u_int32_t spi; - protocol_id_t protocol; - - if (payload->get_type(payload) != NOTIFY) - { - continue; - } - - notify = (notify_payload_t*)payload; protocol = notify->get_protocol_id(notify); spi = notify->get_spi(notify); - - if (protocol != PROTO_ESP && protocol != PROTO_AH) + + if (protocol == PROTO_ESP || protocol == PROTO_AH) { - continue; + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); } - this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, - spi, FALSE); - break; - } - enumerator->destroy(enumerator); } /** @@ -140,30 +127,42 @@ static status_t build_i(private_child_rekey_t *this, message_t *message) notify_payload_t *notify; u_int32_t reqid; child_cfg_t *config; - + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, TRUE); if (!this->child_sa) - { /* CHILD_SA is gone, unable to rekey */ - return SUCCESS; + { /* check if it is an outbound CHILD_SA */ + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, + this->spi, FALSE); + if (!this->child_sa) + { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA + * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/ + message->set_exchange_type(message, INFORMATIONAL); + return SUCCESS; + } + /* we work only with the inbound SPI */ + this->spi = this->child_sa->get_spi(this->child_sa, TRUE); } config = this->child_sa->get_config(this->child_sa); - + /* we just need the rekey notify ... */ notify = notify_payload_create_from_protocol_and_type(this->protocol, REKEY_SA); notify->set_spi(notify, this->spi); message->add_payload(message, (payload_t*)notify); - + /* ... our CHILD_CREATE task does the hard work for us. */ + if (!this->child_create) + { + this->child_create = child_create_create(this->ike_sa, config, TRUE, + NULL, NULL); + } reqid = this->child_sa->get_reqid(this->child_sa); - this->child_create = child_create_create(this->ike_sa, config, TRUE, - NULL, NULL); this->child_create->use_reqid(this->child_create, reqid); this->child_create->task.build(&this->child_create->task, message); - + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); - + return NEED_MORE; } @@ -174,9 +173,9 @@ static status_t process_r(private_child_rekey_t *this, message_t *message) { /* let the CHILD_CREATE task process the message */ this->child_create->task.process(&this->child_create->task, message); - + find_child(this, message); - + return NEED_MORE; } @@ -194,21 +193,21 @@ static status_t build_r(private_child_rekey_t *this, message_t *message) message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } - + /* let the CHILD_CREATE task build the response */ reqid = this->child_sa->get_reqid(this->child_sa); this->child_create->use_reqid(this->child_create, reqid); this->child_create->task.build(&this->child_create->task, message); - + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) { /* rekeying failed, reuse old child */ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); return SUCCESS; } - + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); - + /* invoke rekey hook */ charon->bus->child_rekey(charon->bus, this->child_sa, this->child_create->get_child(this->child_create)); @@ -223,33 +222,20 @@ static status_t process_i(private_child_rekey_t *this, message_t *message) protocol_id_t protocol; u_int32_t spi; child_sa_t *to_delete; - enumerator_t *enumerator; - payload_t *payload; - - /* handle NO_ADDITIONAL_SAS notify */ - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) + + if (message->get_notify(message, NO_ADDITIONAL_SAS)) { - if (payload->get_type(payload) == NOTIFY) - { - notify_payload_t *notify = (notify_payload_t*)payload; - - if (notify->get_notify_type(notify) == NO_ADDITIONAL_SAS) - { - DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, " - "starting reauthentication"); - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - charon->processor->queue_job(charon->processor, - (job_t*)rekey_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), TRUE)); - enumerator->destroy(enumerator); - return SUCCESS; - } - } + DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, " + "starting reauthentication"); + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + charon->processor->queue_job(charon->processor, + (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), TRUE)); + return SUCCESS; } - enumerator->destroy(enumerator); - - if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) + + if (this->child_create->task.process(&this->child_create->task, + message) == NEED_MORE) { /* bad DH group while rekeying, try again */ this->child_create->task.migrate(&this->child_create->task, this->ike_sa); @@ -259,39 +245,39 @@ static status_t process_i(private_child_rekey_t *this, message_t *message) { /* establishing new child failed, reuse old. but not when we * recieved a delete in the meantime */ - if (!(this->collision && + if (!(this->collision && this->collision->get_type(this->collision) == CHILD_DELETE)) { job_t *job; u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER); - + job = (job_t*)rekey_child_sa_job_create( this->child_sa->get_reqid(this->child_sa), this->child_sa->get_protocol(this->child_sa), this->child_sa->get_spi(this->child_sa, TRUE)); DBG1(DBG_IKE, "CHILD_SA rekeying failed, " - "trying again in %d seconds", retry); + "trying again in %d seconds", retry); this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); charon->scheduler->schedule_job(charon->scheduler, job, retry); } return SUCCESS; } - + to_delete = this->child_sa; - + /* check for rekey collisions */ if (this->collision && this->collision->get_type(this->collision) == CHILD_REKEY) { chunk_t this_nonce, other_nonce; private_child_rekey_t *other = (private_child_rekey_t*)this->collision; - + this_nonce = this->child_create->get_lower_nonce(this->child_create); other_nonce = other->child_create->get_lower_nonce(other->child_create); - + /* if we have the lower nonce, delete rekeyed SA. If not, delete * the redundant. */ - if (memcmp(this_nonce.ptr, other_nonce.ptr, + if (memcmp(this_nonce.ptr, other_nonce.ptr, min(this_nonce.len, other_nonce.len)) < 0) { DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child"); @@ -307,21 +293,21 @@ static status_t process_i(private_child_rekey_t *this, message_t *message) } } } - + if (to_delete != this->child_create->get_child(this->child_create)) { /* invoke rekey hook if rekeying successful */ charon->bus->child_rekey(charon->bus, this->child_sa, this->child_create->get_child(this->child_create)); } - + spi = to_delete->get_spi(to_delete, TRUE); protocol = to_delete->get_protocol(to_delete); - + /* rekeying done, delete the obsolete CHILD_SA using a subtask */ this->child_delete = child_delete_create(this->ike_sa, protocol, spi); this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete; this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete; - + return NEED_MORE; } @@ -338,7 +324,7 @@ static task_type_t get_type(private_child_rekey_t *this) */ static void collide(private_child_rekey_t *this, task_t *other) { - /* the task manager only detects exchange collision, but not if + /* the task manager only detects exchange collision, but not if * the collision is for the same child. we check it here. */ if (other->get_type(other) == CHILD_REKEY) { @@ -346,6 +332,7 @@ static void collide(private_child_rekey_t *this, task_t *other) if (rekey == NULL || rekey->child_sa != this->child_sa) { /* not the same child => no collision */ + other->destroy(other); return; } } @@ -354,13 +341,15 @@ static void collide(private_child_rekey_t *this, task_t *other) child_delete_t *del = (child_delete_t*)other; if (del == NULL || del->get_child(del) != this->child_sa) { - /* not the same child => no collision */ + /* not the same child => no collision */ + other->destroy(other); return; } } else { /* any other task is not critical for collisisions, ignore */ + other->destroy(other); return; } DESTROY_IF(this->collision); @@ -371,7 +360,7 @@ static void collide(private_child_rekey_t *this, task_t *other) * Implementation of task_t.migrate */ static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) -{ +{ if (this->child_create) { this->child_create->task.migrate(&this->child_create->task, ike_sa); @@ -381,7 +370,7 @@ static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) this->child_delete->task.migrate(&this->child_delete->task, ike_sa); } DESTROY_IF(this->collision); - + this->ike_sa = ike_sa; this->collision = NULL; } @@ -410,7 +399,7 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, u_int32_t spi) { private_child_rekey_t *this = malloc_thing(private_child_rekey_t); - + this->public.collide = (void (*)(child_rekey_t*,task_t*))collide; this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; @@ -429,13 +418,13 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, this->initiator = FALSE; this->child_create = child_create_create(ike_sa, NULL, TRUE, NULL, NULL); } - + this->ike_sa = ike_sa; this->child_sa = NULL; this->protocol = protocol; this->spi = spi; this->collision = NULL; this->child_delete = NULL; - + return &this->public; } diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h index 5aae2fb39..9b1aea5fa 100644 --- a/src/charon/sa/tasks/child_rekey.h +++ b/src/charon/sa/tasks/child_rekey.h @@ -37,7 +37,7 @@ struct child_rekey_t { * Implements the task_t interface */ task_t task; - + /** * Register a rekeying task which collides with this one * @@ -56,7 +56,7 @@ struct child_rekey_t { * @param ike_sa IKE_SA this task works for * @param protocol protocol of CHILD_SA to rekey, PROTO_NONE as responder * @param spi inbound SPI of CHILD_SA to rekey - * @return child_rekey task to handle by the task_manager + * @return child_rekey task to handle by the task_manager */ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, u_int32_t spi); diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c index d0b2a7e91..a07f96767 100644 --- a/src/charon/sa/tasks/ike_auth.c +++ b/src/charon/sa/tasks/ike_auth.c @@ -31,82 +31,72 @@ typedef struct private_ike_auth_t private_ike_auth_t; * Private members of a ike_auth_t task. */ struct private_ike_auth_t { - + /** * Public methods and task_t interface. */ ike_auth_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Nonce chosen by us in ike_init */ chunk_t my_nonce; - + /** * Nonce chosen by peer in ike_init */ chunk_t other_nonce; - + /** * IKE_SA_INIT message sent by us */ packet_t *my_packet; - + /** * IKE_SA_INIT message sent by peer */ packet_t *other_packet; - - /** - * completed authentication configs initiated by us (auth_cfg_t) - */ - linked_list_t *my_cfgs; - - /** - * completed authentication configs initiated by other (auth_cfg_t) - */ - linked_list_t *other_cfgs;; - + /** * currently active authenticator, to authenticate us */ authenticator_t *my_auth; - + /** * currently active authenticator, to authenticate peer */ authenticator_t *other_auth; - + /** * peer_cfg candidates, ordered by priority */ linked_list_t *candidates; - + /** * selected peer config (might change when using multiple authentications) */ peer_cfg_t *peer_cfg; - + /** * have we planned an(other) authentication exchange? */ bool do_another_auth; - + /** * has the peer announced another authentication exchange? */ bool expect_another_auth; - + /** * should we send a AUTHENTICATION_FAILED notify? */ @@ -129,7 +119,7 @@ static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message) { nonce_payload_t *nonce; - + /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, NONCE); if (nonce == NULL) @@ -137,14 +127,14 @@ static status_t collect_my_init_data(private_ike_auth_t *this, return FAILED; } this->my_nonce = nonce->get_nonce(nonce); - + /* pre-generate the message, keep a copy */ if (this->ike_sa->generate_message(this->ike_sa, message, &this->my_packet) != SUCCESS) { return FAILED; } - return NEED_MORE; + return NEED_MORE; } /** @@ -155,7 +145,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this, { /* we collect the needed information in the IKE_SA_INIT exchange */ nonce_payload_t *nonce; - + /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, NONCE); if (nonce == NULL) @@ -163,10 +153,10 @@ static status_t collect_other_init_data(private_ike_auth_t *this, return FAILED; } this->other_nonce = nonce->get_nonce(nonce); - + /* keep a copy of the received packet */ this->other_packet = message->get_packet(message); - return NEED_MORE; + return NEED_MORE; } /** @@ -176,21 +166,14 @@ static auth_cfg_t *get_auth_cfg(private_ike_auth_t *this, bool local) { enumerator_t *e1, *e2; auth_cfg_t *c1, *c2, *next = NULL; - + /* find an available config not already done */ e1 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, local); while (e1->enumerate(e1, &c1)) { bool found = FALSE; - - if (local) - { - e2 = this->my_cfgs->create_enumerator(this->my_cfgs); - } - else - { - e2 = this->other_cfgs->create_enumerator(this->other_cfgs); - } + + e2 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, local); while (e2->enumerate(e2, &c2)) { if (c2->complies(c2, c1, FALSE)) @@ -218,13 +201,13 @@ static bool do_another_auth(private_ike_auth_t *this) bool do_another = FALSE; enumerator_t *done, *todo; auth_cfg_t *done_cfg, *todo_cfg; - + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH)) { return FALSE; } - - done = this->my_cfgs->create_enumerator(this->my_cfgs); + + done = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, TRUE); todo = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, TRUE); while (todo->enumerate(todo, &todo_cfg)) { @@ -252,12 +235,12 @@ static bool load_cfg_candidates(private_ike_auth_t *this) peer_cfg_t *peer_cfg; host_t *me, *other; identification_t *my_id, *other_id; - + me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); - + enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends, me, other, my_id, other_id); while (enumerator->enumerate(enumerator, &peer_cfg)) @@ -296,10 +279,10 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) bool complies = TRUE; enumerator_t *e1, *e2, *tmp; auth_cfg_t *c1, *c2; - - e1 = this->other_cfgs->create_enumerator(this->other_cfgs); + + e1 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE); e2 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, FALSE); - + if (strict) { /* swap lists in strict mode: all configured rounds must be * fulfilled. If !strict, we check only the rounds done so far. */ @@ -342,7 +325,7 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) } } while (this->peer_cfg); - + return this->peer_cfg != NULL; } @@ -352,39 +335,45 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) static status_t build_i(private_ike_auth_t *this, message_t *message) { auth_cfg_t *cfg; - + if (message->get_exchange_type(message) == IKE_SA_INIT) { return collect_my_init_data(this, message); } - + if (this->peer_cfg == NULL) { this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); this->peer_cfg->get_ref(this->peer_cfg); } - - if (message->get_message_id(message) == 1 && - this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH)) - { /* in the first IKE_AUTH, indicate support for multiple authentication */ - message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED, chunk_empty); + + if (message->get_message_id(message) == 1) + { /* in the first IKE_AUTH ... */ + if (this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH)) + { /* indicate support for multiple authentication */ + message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED, + chunk_empty); + } + /* indicate support for EAP-only authentication */ + message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION, + chunk_empty); } - + if (!this->do_another_auth && !this->my_auth) { /* we have done our rounds */ return NEED_MORE; } - + /* check if an authenticator is in progress */ if (this->my_auth == NULL) { identification_t *id; id_payload_t *id_payload; - + /* clean up authentication config from a previous round */ cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); cfg->purge(cfg, TRUE); - + /* add (optional) IDr */ cfg = get_auth_cfg(this, FALSE); if (cfg) @@ -410,7 +399,7 @@ static status_t build_i(private_ike_auth_t *this, message_t *message) this->ike_sa->set_my_id(this->ike_sa, id->clone(id)); id_payload = id_payload_create_from_identification(ID_INITIATOR, id); message->add_payload(message, (payload_t*)id_payload); - + /* build authentication data */ this->my_auth = authenticator_create_builder(this->ike_sa, cfg, this->other_nonce, this->my_nonce, @@ -427,7 +416,7 @@ static status_t build_i(private_ike_auth_t *this, message_t *message) /* authentication step complete, reset authenticator */ cfg = auth_cfg_create(); cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), TRUE); - this->my_cfgs->insert_last(this->my_cfgs, cfg); + this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg); this->my_auth->destroy(this->my_auth); this->my_auth = NULL; break; @@ -436,7 +425,7 @@ static status_t build_i(private_ike_auth_t *this, message_t *message) default: return FAILED; } - + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -460,12 +449,12 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) auth_cfg_t *cfg, *cand; id_payload_t *id_payload; identification_t *id; - + if (message->get_exchange_type(message) == IKE_SA_INIT) { return collect_other_init_data(this, message); } - + if (this->my_auth == NULL && this->do_another_auth) { /* handle (optional) IDr payload, apply proposed identity */ @@ -480,16 +469,26 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) } this->ike_sa->set_my_id(this->ike_sa, id); } - + if (!this->expect_another_auth) { return NEED_MORE; } - if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED)) - { - this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH); + + if (message->get_message_id(message) == 1) + { /* check for extensions in the first IKE_AUTH */ + if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED)) + { + this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH); + } + if (this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN) && + message->get_notify(message, EAP_ONLY_AUTHENTICATION)) + { /* EAP-only has no official notify, accept only from strongSwan */ + this->ike_sa->enable_extension(this->ike_sa, + EXT_EAP_ONLY_AUTHENTICATION); + } } - + if (this->other_auth == NULL) { /* handle IDi payload */ @@ -503,7 +502,7 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) this->ike_sa->set_other_id(this->ike_sa, id); cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id)); - + if (this->peer_cfg == NULL) { if (!load_cfg_candidates(this)) @@ -530,7 +529,7 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) } cfg->merge(cfg, cand, TRUE); } - + /* verify authentication data */ this->other_auth = authenticator_create_verifier(this->ike_sa, message, this->other_nonce, this->my_nonce, @@ -558,27 +557,26 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) this->authentication_failed = TRUE; return NEED_MORE; } - + /* store authentication information */ cfg = auth_cfg_create(); cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE); - this->other_cfgs->insert_last(this->other_cfgs, cfg); - + this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg); + /* another auth round done, invoke authorize hook */ - if (!charon->bus->authorize(charon->bus, this->other_cfgs, FALSE)) + if (!charon->bus->authorize(charon->bus, FALSE)) { - DBG1(DBG_IKE, "round %d authorization hook forbids IKE_SA, cancelling", - this->other_cfgs->get_count(this->other_cfgs)); + DBG1(DBG_IKE, "authorization hook forbids IKE_SA, cancelling"); this->authentication_failed = TRUE; return NEED_MORE; } - + if (!update_cfg_candidates(this, FALSE)) { this->authentication_failed = TRUE; return NEED_MORE; } - + if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) { this->expect_another_auth = FALSE; @@ -597,7 +595,7 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) static status_t build_r(private_ike_auth_t *this, message_t *message) { auth_cfg_t *cfg; - + if (message->get_exchange_type(message) == IKE_SA_INIT) { if (multiple_auth_enabled()) @@ -607,23 +605,23 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) } return collect_my_init_data(this, message); } - + if (this->authentication_failed || this->peer_cfg == NULL) { message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); return FAILED; } - + if (this->my_auth == NULL && this->do_another_auth) { identification_t *id, *id_cfg; id_payload_t *id_payload; - + /* add IDr */ cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); cfg->purge(cfg, TRUE); cfg->merge(cfg, get_auth_cfg(this, TRUE), TRUE); - + id_cfg = cfg->get(cfg, AUTH_RULE_IDENTITY); id = this->ike_sa->get_my_id(this->ike_sa); if (id->get_type(id) == ID_ANY) @@ -648,22 +646,38 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) return FAILED; } } - + id_payload = id_payload_create_from_identification(ID_RESPONDER, id); message->add_payload(message, (payload_t*)id_payload); - - /* build authentication data */ - this->my_auth = authenticator_create_builder(this->ike_sa, cfg, - this->other_nonce, this->my_nonce, - this->other_packet->get_data(this->other_packet), - this->my_packet->get_data(this->my_packet)); - if (!this->my_auth) + + if ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS) == AUTH_CLASS_EAP) + { /* EAP-only authentication */ + if (!this->ike_sa->supports_extension(this->ike_sa, + EXT_EAP_ONLY_AUTHENTICATION)) + { + DBG1(DBG_IKE, "configured EAP-only authentication, but peer " + "does not support it"); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, + chunk_empty); + return FAILED; + } + } + else { - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); - return FAILED; + /* build authentication data */ + this->my_auth = authenticator_create_builder(this->ike_sa, cfg, + this->other_nonce, this->my_nonce, + this->other_packet->get_data(this->other_packet), + this->my_packet->get_data(this->my_packet)); + if (!this->my_auth) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, + chunk_empty); + return FAILED; + } } } - + if (this->other_auth) { switch (this->other_auth->build(this->other_auth, message)) @@ -691,7 +705,7 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) cfg = auth_cfg_create(); cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), TRUE); - this->my_cfgs->insert_last(this->my_cfgs, cfg); + this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg); this->my_auth->destroy(this->my_auth); this->my_auth = NULL; break; @@ -703,7 +717,7 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) return FAILED; } } - + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -723,21 +737,21 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) chunk_empty); return FAILED; } - if (!charon->bus->authorize(charon->bus, this->other_cfgs, TRUE)) + if (!charon->bus->authorize(charon->bus, TRUE)) { DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); return FAILED; } - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", this->ike_sa->get_name(this->ike_sa), this->ike_sa->get_unique_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); return SUCCESS; } @@ -752,7 +766,8 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) enumerator_t *enumerator; payload_t *payload; auth_cfg_t *cfg; - + bool mutual_eap = FALSE; + if (message->get_exchange_type(message) == IKE_SA_INIT) { if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED) && @@ -762,7 +777,7 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) } return collect_other_init_data(this, message); } - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -770,7 +785,7 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) { notify_payload_t *notify = (notify_payload_t*)payload; notify_type_t type = notify->get_notify_type(notify); - + switch (type) { case NO_PROPOSAL_CHOSEN: @@ -801,7 +816,7 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) DBG1(DBG_IKE, "received %N notify error", notify_type_names, type); enumerator->destroy(enumerator); - return FAILED; + return FAILED; } DBG2(DBG_IKE, "received %N notify", notify_type_names, type); @@ -811,41 +826,14 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) } } enumerator->destroy(enumerator); - - if (this->my_auth) - { - switch (this->my_auth->process(this->my_auth, message)) - { - case SUCCESS: - cfg = auth_cfg_create(); - cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), - TRUE); - this->my_cfgs->insert_last(this->my_cfgs, cfg); - this->my_auth->destroy(this->my_auth); - this->my_auth = NULL; - this->do_another_auth = do_another_auth(this); - break; - case NEED_MORE: - break; - default: - return FAILED; - } - } - + if (this->expect_another_auth) { if (this->other_auth == NULL) { id_payload_t *id_payload; identification_t *id; - - /* responder is not allowed to do EAP */ - if (!message->get_payload(message, AUTHENTICATION)) - { - DBG1(DBG_IKE, "AUTH payload missing"); - return FAILED; - } - + /* handle IDr payload */ id_payload = (id_payload_t*)message->get_payload(message, ID_RESPONDER); @@ -858,42 +846,81 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) this->ike_sa->set_other_id(this->ike_sa, id); cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id)); - - /* verify authentication data */ - this->other_auth = authenticator_create_verifier(this->ike_sa, - message, this->other_nonce, this->my_nonce, - this->other_packet->get_data(this->other_packet), - this->my_packet->get_data(this->my_packet)); - if (!this->other_auth) + + if (message->get_payload(message, AUTHENTICATION)) { - return FAILED; + /* verify authentication data */ + this->other_auth = authenticator_create_verifier(this->ike_sa, + message, this->other_nonce, this->my_nonce, + this->other_packet->get_data(this->other_packet), + this->my_packet->get_data(this->my_packet)); + if (!this->other_auth) + { + return FAILED; + } + } + else + { + /* responder omitted AUTH payload, indicating EAP-only */ + mutual_eap = TRUE; } } - switch (this->other_auth->process(this->other_auth, message)) + if (this->other_auth) + { + switch (this->other_auth->process(this->other_auth, message)) + { + case SUCCESS: + break; + case NEED_MORE: + return NEED_MORE; + default: + return FAILED; + } + this->other_auth->destroy(this->other_auth); + this->other_auth = NULL; + } + /* store authentication information, reset authenticator */ + cfg = auth_cfg_create(); + cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE); + this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg); + + /* another auth round done, invoke authorize hook */ + if (!charon->bus->authorize(charon->bus, FALSE)) + { + DBG1(DBG_IKE, "authorization forbids IKE_SA, cancelling"); + return FAILED; + } + } + + if (this->my_auth) + { + switch (this->my_auth->process(this->my_auth, message)) { case SUCCESS: + cfg = auth_cfg_create(); + cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), + TRUE); + this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg); + this->my_auth->destroy(this->my_auth); + this->my_auth = NULL; + this->do_another_auth = do_another_auth(this); break; case NEED_MORE: - return NEED_MORE; + break; default: return FAILED; } - /* store authentication information, reset authenticator */ - cfg = auth_cfg_create(); - cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE); - this->other_cfgs->insert_last(this->other_cfgs, cfg); - this->other_auth->destroy(this->other_auth); - this->other_auth = NULL; - - /* another auth round done, invoke authorize hook */ - if (!charon->bus->authorize(charon->bus, this->other_cfgs, FALSE)) + } + if (mutual_eap) + { + if (!this->my_auth || !this->my_auth->is_mutual(this->my_auth)) { - DBG1(DBG_IKE, "round %d authorization forbids IKE_SA, cancelling", - this->other_cfgs->get_count(this->other_cfgs)); + DBG1(DBG_IKE, "do not allow non-mutual EAP-only authentication"); return FAILED; } + DBG1(DBG_IKE, "allow mutual EAP-only authentication"); } - + if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) { this->expect_another_auth = FALSE; @@ -904,19 +931,19 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) { return FAILED; } - if (!charon->bus->authorize(charon->bus, this->other_cfgs, TRUE)) + if (!charon->bus->authorize(charon->bus, TRUE)) { DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); return FAILED; } - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", this->ike_sa->get_name(this->ike_sa), this->ike_sa->get_unique_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); return SUCCESS; } @@ -943,10 +970,8 @@ static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa) DESTROY_IF(this->peer_cfg); DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); - this->my_cfgs->destroy_offset(this->my_cfgs, offsetof(auth_cfg_t, destroy)); - this->other_cfgs->destroy_offset(this->other_cfgs, offsetof(auth_cfg_t, destroy)); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); - + this->my_packet = NULL; this->other_packet = NULL; this->ike_sa = ike_sa; @@ -956,8 +981,6 @@ static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa) this->do_another_auth = TRUE; this->expect_another_auth = TRUE; this->authentication_failed = FALSE; - this->my_cfgs = linked_list_create(); - this->other_cfgs = linked_list_create(); this->candidates = linked_list_create(); } @@ -973,8 +996,6 @@ static void destroy(private_ike_auth_t *this) DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); DESTROY_IF(this->peer_cfg); - this->my_cfgs->destroy_offset(this->my_cfgs, offsetof(auth_cfg_t, destroy)); - this->other_cfgs->destroy_offset(this->other_cfgs, offsetof(auth_cfg_t, destroy)); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); free(this); } @@ -985,11 +1006,11 @@ static void destroy(private_ike_auth_t *this) ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) { private_ike_auth_t *this = malloc_thing(private_ike_auth_t); - + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -1000,7 +1021,7 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; this->my_nonce = chunk_empty; @@ -1008,15 +1029,13 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) this->my_packet = NULL; this->other_packet = NULL; this->peer_cfg = NULL; - this->my_cfgs = linked_list_create(); - this->other_cfgs = linked_list_create(); this->candidates = linked_list_create(); this->my_auth = NULL; this->other_auth = NULL; this->do_another_auth = TRUE; this->expect_another_auth = TRUE; this->authentication_failed = FALSE; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_auth_lifetime.c b/src/charon/sa/tasks/ike_auth_lifetime.c index a047e6b81..75ff35168 100644 --- a/src/charon/sa/tasks/ike_auth_lifetime.c +++ b/src/charon/sa/tasks/ike_auth_lifetime.c @@ -27,12 +27,12 @@ typedef struct private_ike_auth_lifetime_t private_ike_auth_lifetime_t; * Private members of a ike_auth_lifetime_t task. */ struct private_ike_auth_lifetime_t { - + /** * Public methods and task_t interface. */ ike_auth_lifetime_t public; - + /** * Assigned IKE_SA. */ @@ -46,11 +46,11 @@ static void add_auth_lifetime(private_ike_auth_lifetime_t *this, message_t *mess { chunk_t chunk; u_int32_t lifetime; - + lifetime = this->ike_sa->get_statistic(this->ike_sa, STAT_REAUTH); if (lifetime) { - lifetime -= time(NULL); + lifetime -= time_monotonic(NULL); chunk = chunk_from_thing(lifetime); *(u_int32_t*)chunk.ptr = htonl(lifetime); message->add_notify(message, FALSE, AUTH_LIFETIME, chunk); @@ -62,31 +62,17 @@ static void add_auth_lifetime(private_ike_auth_lifetime_t *this, message_t *mess */ static void process_payloads(private_ike_auth_lifetime_t *this, message_t *message) { - enumerator_t *enumerator; - payload_t *payload; notify_payload_t *notify; - - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) + chunk_t data; + u_int32_t lifetime; + + notify = message->get_notify(message, AUTH_LIFETIME); + if (notify) { - if (payload->get_type(payload) == NOTIFY) - { - notify = (notify_payload_t*)payload; - switch (notify->get_notify_type(notify)) - { - case AUTH_LIFETIME: - { - chunk_t data = notify->get_notification_data(notify); - u_int32_t lifetime = ntohl(*(u_int32_t*)data.ptr); - this->ike_sa->set_auth_lifetime(this->ike_sa, lifetime); - break; - } - default: - break; - } - } + data = notify->get_notification_data(notify); + lifetime = ntohl(*(u_int32_t*)data.ptr); + this->ike_sa->set_auth_lifetime(this->ike_sa, lifetime); } - enumerator->destroy(enumerator); } /** @@ -177,7 +163,7 @@ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -188,9 +174,9 @@ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_auth_lifetime.h b/src/charon/sa/tasks/ike_auth_lifetime.h index 812caaf43..3b129b9e3 100644 --- a/src/charon/sa/tasks/ike_auth_lifetime.h +++ b/src/charon/sa/tasks/ike_auth_lifetime.h @@ -30,7 +30,7 @@ typedef struct ike_auth_lifetime_t ike_auth_lifetime_t; /** * Task of type IKE_AUTH_LIFETIME, implements RFC4478. * - * This task exchanges lifetimes for IKE_AUTH to force a client to + * This task exchanges lifetimes for IKE_AUTH to force a client to * reauthenticate before the responders lifetime reaches the limit. */ struct ike_auth_lifetime_t { @@ -46,7 +46,7 @@ struct ike_auth_lifetime_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if taks is initiated by us - * @return ike_auth_lifetime task to handle by the task_manager + * @return ike_auth_lifetime task to handle by the task_manager */ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_cert_post.c b/src/charon/sa/tasks/ike_cert_post.c index 70e87c2e7..c831df975 100644 --- a/src/charon/sa/tasks/ike_cert_post.c +++ b/src/charon/sa/tasks/ike_cert_post.c @@ -30,17 +30,17 @@ typedef struct private_ike_cert_post_t private_ike_cert_post_t; * Private members of a ike_cert_post_t task. */ struct private_ike_cert_post_t { - + /** * Public methods and task_t interface. */ ike_cert_post_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ @@ -50,49 +50,47 @@ struct private_ike_cert_post_t { /** * Generates the cert payload, if possible with "Hash and URL" */ -static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this, certificate_t *cert) +static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this, + certificate_t *cert) { + hasher_t *hasher; + identification_t *id; + chunk_t hash, encoded ; + enumerator_t *enumerator; + char *url; cert_payload_t *payload = NULL; - - if (this->ike_sa->supports_extension(this->ike_sa, EXT_HASH_AND_URL)) + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_HASH_AND_URL)) { - /* ok, our peer sent us a HTTP_CERT_LOOKUP_SUPPORTED Notify */ - hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); - if (hasher != NULL) - { - chunk_t hash, encoded = cert->get_encoding(cert); - enumerator_t *enumerator; - char *url; - - hasher->allocate_hash(hasher, encoded, &hash); - identification_t *id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash); - - enumerator = charon->credentials->create_cdp_enumerator(charon->credentials, CERT_X509, id); - if (enumerator->enumerate(enumerator, &url)) - { - /* if we have an URL available we send that to our peer */ - payload = cert_payload_create_from_hash_and_url(hash, url); - } - enumerator->destroy(enumerator); - - id->destroy(id); - chunk_free(&hash); - chunk_free(&encoded); - hasher->destroy(hasher); - } - else - { - DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported"); - } + return cert_payload_create_from_cert(cert); + } + + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher) + { + DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported"); + return cert_payload_create_from_cert(cert); } - - if (!payload) + + encoded = cert->get_encoding(cert); + hasher->allocate_hash(hasher, encoded, &hash); + chunk_free(&encoded); + hasher->destroy(hasher); + id = identification_create_from_encoding(ID_KEY_ID, hash); + + enumerator = charon->credentials->create_cdp_enumerator(charon->credentials, + CERT_X509, id); + if (enumerator->enumerate(enumerator, &url)) + { + payload = cert_payload_create_from_hash_and_url(hash, url); + } + else { - /* our peer does not support "Hash and URL" or we do not have an URL - * to send to our peer, just create a normal cert payload */ payload = cert_payload_create_from_cert(cert); } - + enumerator->destroy(enumerator); + chunk_free(&hash); + id->destroy(id); return payload; } @@ -103,14 +101,14 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message) { peer_cfg_t *peer_cfg; auth_payload_t *payload; - + payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (!peer_cfg || !payload || payload->get_auth_method(payload) == AUTH_PSK) { /* no CERT payload for EAP/PSK */ return; } - + switch (peer_cfg->get_cert_policy(peer_cfg)) { case CERT_NEVER_SEND: @@ -128,9 +126,9 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message) certificate_t *cert; auth_rule_t type; auth_cfg_t *auth; - + auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); - + /* get subject cert first, then issuing certificates */ cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); if (!cert) @@ -145,7 +143,7 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message) DBG1(DBG_IKE, "sending end entity cert \"%Y\"", cert->get_subject(cert)); message->add_payload(message, (payload_t*)payload); - + enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &type, &cert)) { @@ -161,7 +159,7 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message) } } enumerator->destroy(enumerator); - } + } } } @@ -171,7 +169,7 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message) static status_t build_i(private_ike_cert_post_t *this, message_t *message) { build_certs(this, message); - + return NEED_MORE; } @@ -179,7 +177,7 @@ static status_t build_i(private_ike_cert_post_t *this, message_t *message) * Implementation of task_t.process for responder */ static status_t process_r(private_ike_cert_post_t *this, message_t *message) -{ +{ return NEED_MORE; } @@ -189,7 +187,7 @@ static status_t process_r(private_ike_cert_post_t *this, message_t *message) static status_t build_r(private_ike_cert_post_t *this, message_t *message) { build_certs(this, message); - + if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) { /* stay alive, we might have additional rounds with certs */ return NEED_MORE; @@ -243,7 +241,7 @@ ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -254,10 +252,10 @@ ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_cert_post.h b/src/charon/sa/tasks/ike_cert_post.h index fa555eac7..a21f45927 100644 --- a/src/charon/sa/tasks/ike_cert_post.h +++ b/src/charon/sa/tasks/ike_cert_post.h @@ -46,7 +46,7 @@ struct ike_cert_post_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if thask is the original initator - * @return ike_cert_post task to handle by the task_manager + * @return ike_cert_post task to handle by the task_manager */ ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_cert_pre.c b/src/charon/sa/tasks/ike_cert_pre.c index 1c72f289f..0805d0290 100644 --- a/src/charon/sa/tasks/ike_cert_pre.c +++ b/src/charon/sa/tasks/ike_cert_pre.c @@ -29,27 +29,27 @@ typedef struct private_ike_cert_pre_t private_ike_cert_pre_t; * Private members of a ike_cert_pre_t task. */ struct private_ike_cert_pre_t { - + /** * Public methods and task_t interface. */ ike_cert_pre_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Do we accept HTTP certificate lookup requests */ bool do_http_lookup; - + /** * wheter this is the final authentication round */ @@ -57,29 +57,29 @@ struct private_ike_cert_pre_t { }; /** - * read certificate requests + * read certificate requests */ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) { enumerator_t *enumerator; payload_t *payload; auth_cfg_t *auth; - + auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { - switch(payload->get_type(payload)) + switch (payload->get_type(payload)) { case CERTIFICATE_REQUEST: { certreq_payload_t *certreq = (certreq_payload_t*)payload; enumerator_t *enumerator; chunk_t keyid; - + this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); - + if (certreq->get_cert_type(certreq) != CERT_X509) { DBG1(DBG_IKE, "cert payload %N not supported - ignored", @@ -91,10 +91,9 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) { identification_t *id; certificate_t *cert; - - id = identification_create_from_encoding( - ID_PUBKEY_INFO_SHA1, keyid); - cert = charon->credentials->get_cert(charon->credentials, + + id = identification_create_from_encoding(ID_KEY_ID, keyid); + cert = charon->credentials->get_cert(charon->credentials, CERT_X509, KEY_ANY, id, TRUE); if (cert) { @@ -115,7 +114,7 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) case NOTIFY: { notify_payload_t *notify = (notify_payload_t*)payload; - + /* we only handle one type of notify here */ if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED) { @@ -135,11 +134,11 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) * tries to extract a certificate from the cert payload or the credential * manager (based on the hash of a "Hash and URL" encoded cert). * Note: the returned certificate (if any) has to be destroyed - */ + */ static certificate_t *try_get_cert(cert_payload_t *cert_payload) { certificate_t *cert = NULL; - + switch (cert_payload->get_cert_encoding(cert_payload)) { case ENC_X509_SIGNATURE: @@ -156,8 +155,8 @@ static certificate_t *try_get_cert(cert_payload_t *cert_payload) /* invalid "Hash and URL" data (logged elsewhere) */ break; } - id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash); - cert = charon->credentials->get_cert(charon->credentials, + id = identification_create_from_encoding(ID_KEY_ID, hash); + cert = charon->credentials->get_cert(charon->credentials, CERT_X509, KEY_ANY, id, FALSE); id->destroy(id); break; @@ -179,9 +178,9 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) payload_t *payload; auth_cfg_t *auth; bool first = TRUE; - + auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -191,10 +190,10 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message) cert_encoding_t encoding; certificate_t *cert; char *url; - + cert_payload = (cert_payload_t*)payload; encoding = cert_payload->get_cert_encoding(cert_payload); - + switch (encoding) { case ENC_X509_HASH_AND_URL: @@ -284,9 +283,9 @@ static void add_certreq(certreq_payload_t **req, certificate_t *cert) case CERT_X509: { public_key_t *public; - identification_t *keyid; + chunk_t keyid; x509_t *x509 = (x509_t*)cert; - + if (!(x509->get_flags(x509) & X509_CA)) { /* no CA cert, skip */ break; @@ -300,11 +299,13 @@ static void add_certreq(certreq_payload_t **req, certificate_t *cert) { *req = certreq_payload_create_type(CERT_X509); } - keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1); - (*req)->add_keyid(*req, keyid->get_encoding(keyid)); + if (public->get_fingerprint(public, KEY_ID_PUBKEY_INFO_SHA1, &keyid)) + { + (*req)->add_keyid(*req, keyid); + DBG1(DBG_IKE, "sending cert request for \"%Y\"", + cert->get_subject(cert)); + } public->destroy(public); - DBG1(DBG_IKE, "sending cert request for \"%Y\"", - cert->get_subject(cert)); break; } default: @@ -320,7 +321,7 @@ static void add_certreqs(certreq_payload_t **req, auth_cfg_t *auth) enumerator_t *enumerator; auth_rule_t type; void *value; - + enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &type, &value)) { @@ -347,13 +348,13 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message) certificate_t *cert; auth_cfg_t *auth; certreq_payload_t *req = NULL; - + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); if (!ike_cfg->send_certreq(ike_cfg)) { return; } - + /* check if we require a specific CA for that peer */ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (peer_cfg) @@ -365,7 +366,7 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message) } enumerator->destroy(enumerator); } - + if (!req) { /* otherwise add all trusted CA certificates */ @@ -377,11 +378,11 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message) } enumerator->destroy(enumerator); } - + if (req) { message->add_payload(message, (payload_t*)req); - + if (lib->settings->get_bool(lib->settings, "charon.hash_and_url", FALSE)) { message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED, @@ -396,29 +397,15 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message) */ static bool final_auth(message_t *message) { - enumerator_t *enumerator; - payload_t *payload; - notify_payload_t *notify; - /* we check for an AUTH payload without a ANOTHER_AUTH_FOLLOWS notify */ if (message->get_payload(message, AUTHENTICATION) == NULL) { return FALSE; } - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) + if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS)) { - if (payload->get_type(payload) == NOTIFY) - { - notify = (notify_payload_t*)payload; - if (notify->get_notify_type(notify) == ANOTHER_AUTH_FOLLOWS) - { - enumerator->destroy(enumerator); - return FALSE; - } - } + return FALSE; } - enumerator->destroy(enumerator); return TRUE; } @@ -426,7 +413,7 @@ static bool final_auth(message_t *message) * Implementation of task_t.process for initiator */ static status_t build_i(private_ike_cert_pre_t *this, message_t *message) -{ +{ if (message->get_message_id(message) == 1) { /* initiator sends CERTREQs in first IKE_AUTH */ build_certreqs(this, message); @@ -474,7 +461,7 @@ static status_t process_i(private_ike_cert_pre_t *this, message_t *message) process_certreqs(this, message); } process_certs(this, message); - + if (final_auth(message)) { return SUCCESS; @@ -516,7 +503,7 @@ ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -527,11 +514,11 @@ ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; this->do_http_lookup = FALSE; this->final = FALSE; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_cert_pre.h b/src/charon/sa/tasks/ike_cert_pre.h index d49005e68..1541b80e5 100644 --- a/src/charon/sa/tasks/ike_cert_pre.h +++ b/src/charon/sa/tasks/ike_cert_pre.h @@ -46,7 +46,7 @@ struct ike_cert_pre_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if thask is the original initator - * @return ike_cert_pre task to handle by the task_manager + * @return ike_cert_pre task to handle by the task_manager */ ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c index 1f75521b6..f010439fe 100644 --- a/src/charon/sa/tasks/ike_config.c +++ b/src/charon/sa/tasks/ike_config.c @@ -19,50 +19,60 @@ #include <daemon.h> #include <encoding/payloads/cp_payload.h> -#define DNS_SERVER_MAX 2 -#define NBNS_SERVER_MAX 2 - typedef struct private_ike_config_t private_ike_config_t; /** * Private members of a ike_config_t task. */ struct private_ike_config_t { - + /** * Public methods and task_t interface. */ ike_config_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * virtual ip */ host_t *virtual_ip; + + /** + * list of attributes requested and its handler, entry_t + */ + linked_list_t *requested; }; /** - * build INTERNAL_IPV4/6_ADDRESS from virtual ip + * Entry for a requested attribute and the requesting handler + */ +typedef struct { + /** attribute requested */ + configuration_attribute_type_t type; + /** handler requesting this attribute */ + attribute_handler_t *handler; +} entry_t; + +/** + * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip */ -static void build_vip(private_ike_config_t *this, host_t *vip, cp_payload_t *cp) +static configuration_attribute_t *build_vip(host_t *vip) { - configuration_attribute_t *ca; + configuration_attribute_type_t type; chunk_t chunk, prefix; - - ca = configuration_attribute_create(); - + if (vip->get_family(vip) == AF_INET) { - ca->set_type(ca, INTERNAL_IP4_ADDRESS); + type = INTERNAL_IP4_ADDRESS; if (vip->is_anyaddr(vip)) { chunk = chunk_empty; @@ -74,7 +84,7 @@ static void build_vip(private_ike_config_t *this, host_t *vip, cp_payload_t *cp) } else { - ca->set_type(ca, INTERNAL_IP6_ADDRESS); + type = INTERNAL_IP6_ADDRESS; if (vip->is_anyaddr(vip)) { chunk = chunk_empty; @@ -87,8 +97,42 @@ static void build_vip(private_ike_config_t *this, host_t *vip, cp_payload_t *cp) chunk = chunk_cata("cc", chunk, prefix); } } - ca->set_value(ca, chunk); - cp->add_configuration_attribute(cp, ca); + return configuration_attribute_create_value(type, chunk); +} + +/** + * Handle a received attribute as initiator + */ +static void handle_attribute(private_ike_config_t *this, + configuration_attribute_t *ca) +{ + attribute_handler_t *handler = NULL; + enumerator_t *enumerator; + entry_t *entry; + + /* find the handler which requested this attribute */ + enumerator = this->requested->create_enumerator(this->requested); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->type == ca->get_type(ca)) + { + handler = entry->handler; + this->requested->remove_at(this->requested, enumerator); + free(entry); + break; + } + } + enumerator->destroy(enumerator); + + /* and pass it to the handle function */ + handler = lib->attributes->handle(lib->attributes, + this->ike_sa->get_other_id(this->ike_sa), handler, + ca->get_type(ca), ca->get_value(ca)); + if (handler) + { + this->ike_sa->add_configuration_attribute(this->ike_sa, + handler, ca->get_type(ca), ca->get_value(ca)); + } } /** @@ -100,7 +144,7 @@ static void process_attribute(private_ike_config_t *this, host_t *ip; chunk_t addr; int family = AF_INET6; - + switch (ca->get_type(ca)) { case INTERNAL_IP4_ADDRESS: @@ -118,7 +162,7 @@ static void process_attribute(private_ike_config_t *this, /* skip prefix byte in IPv6 payload*/ if (family == AF_INET6) { - addr.len--; + addr.len--; } ip = host_create_from_chunk(family, addr, 0); } @@ -130,15 +174,12 @@ static void process_attribute(private_ike_config_t *this, break; } default: + { if (this->initiator) { - this->ike_sa->add_configuration_attribute(this->ike_sa, - ca->get_type(ca), ca->get_value(ca)); - } - else - { - /* we do not handle attribute requests other than for VIPs */ + handle_attribute(this, ca); } + } } } @@ -147,10 +188,9 @@ static void process_attribute(private_ike_config_t *this, */ static void process_payloads(private_ike_config_t *this, message_t *message) { - enumerator_t *enumerator; - iterator_t *attributes; + enumerator_t *enumerator, *attributes; payload_t *payload; - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -158,22 +198,25 @@ static void process_payloads(private_ike_config_t *this, message_t *message) { cp_payload_t *cp = (cp_payload_t*)payload; configuration_attribute_t *ca; - switch (cp->get_config_type(cp)) + + switch (cp->get_type(cp)) { case CFG_REQUEST: case CFG_REPLY: { - attributes = cp->create_attribute_iterator(cp); - while (attributes->iterate(attributes, (void**)&ca)) + attributes = cp->create_attribute_enumerator(cp); + while (attributes->enumerate(attributes, &ca)) { + DBG2(DBG_IKE, "processing %N attribute", + configuration_attribute_type_names, ca->get_type(ca)); process_attribute(this, ca); } attributes->destroy(attributes); break; } default: - DBG1(DBG_IKE, "ignoring %N config payload", - config_type_names, cp->get_config_type(cp)); + DBG1(DBG_IKE, "ignoring %N config payload", + config_type_names, cp->get_type(cp)); break; } } @@ -188,9 +231,14 @@ static status_t build_i(private_ike_config_t *this, message_t *message) { if (message->get_message_id(message) == 1) { /* in first IKE_AUTH only */ + cp_payload_t *cp = NULL; + enumerator_t *enumerator; + attribute_handler_t *handler; peer_cfg_t *config; + configuration_attribute_type_t type; + chunk_t data; host_t *vip; - + /* reuse virtual IP if we already have one */ vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); if (!vip) @@ -200,25 +248,38 @@ static status_t build_i(private_ike_config_t *this, message_t *message) } if (vip) { + cp = cp_payload_create_type(CFG_REQUEST); + cp->add_attribute(cp, build_vip(vip)); + } + + enumerator = lib->attributes->create_initiator_enumerator(lib->attributes, + this->ike_sa->get_other_id(this->ike_sa), vip); + while (enumerator->enumerate(enumerator, &handler, &type, &data)) + { configuration_attribute_t *ca; - cp_payload_t *cp; - - cp = cp_payload_create(); - cp->set_config_type(cp, CFG_REQUEST); - - build_vip(this, vip, cp); - - /* we currently always add a DNS request if we request an IP */ - ca = configuration_attribute_create(); - if (vip->get_family(vip) == AF_INET) - { - ca->set_type(ca, INTERNAL_IP4_DNS); - } - else + entry_t *entry; + + /* create configuration attribute */ + DBG2(DBG_IKE, "building %N attribute", + configuration_attribute_type_names, type); + ca = configuration_attribute_create_value(type, data); + if (!cp) { - ca->set_type(ca, INTERNAL_IP6_DNS); + cp = cp_payload_create_type(CFG_REQUEST); } - cp->add_configuration_attribute(cp, ca); + cp->add_attribute(cp, ca); + + /* save handler along with requested type */ + entry = malloc_thing(entry_t); + entry->type = type; + entry->handler = handler; + + this->requested->insert_last(this->requested, entry); + } + enumerator->destroy(enumerator); + + if (cp) + { message->add_payload(message, (payload_t*)cp); } } @@ -238,30 +299,62 @@ static status_t process_r(private_ike_config_t *this, message_t *message) } /** + * Find a peer (EAP) identity to query provider for attributes + */ +static identification_t *get_peer_identity(private_ike_config_t *this) +{ + identification_t *id = NULL, *current; + enumerator_t *enumerator; + auth_cfg_t *cfg; + + enumerator = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE); + while (enumerator->enumerate(enumerator, &cfg)) + { + /* prefer EAP-Identity of last round */ + current = cfg->get(cfg, AUTH_RULE_EAP_IDENTITY); + if (!current || current->get_type(current) == ID_ANY) + { + current = cfg->get(cfg, AUTH_RULE_IDENTITY); + } + if (current && current->get_type(current) != ID_ANY) + { + id = current; + continue; + } + } + enumerator->destroy(enumerator); + if (!id) + { /* fallback, should not happen */ + id = this->ike_sa->get_other_id(this->ike_sa); + } + return id; +} + +/** * Implementation of task_t.build for responder */ static status_t build_r(private_ike_config_t *this, message_t *message) { if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { /* in last IKE_AUTH exchange */ - peer_cfg_t *config = this->ike_sa->get_peer_cfg(this->ike_sa); - + enumerator_t *enumerator; + configuration_attribute_type_t type; + chunk_t value; + host_t *vip = NULL; + cp_payload_t *cp = NULL; + peer_cfg_t *config; + identification_t *id; + + id = get_peer_identity(this); + + config = this->ike_sa->get_peer_cfg(this->ike_sa); if (config && this->virtual_ip) { - enumerator_t *enumerator; - configuration_attribute_type_t type; - configuration_attribute_t *ca; - chunk_t value; - cp_payload_t *cp; - host_t *vip = NULL; - DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); if (config->get_pool(config)) { - vip = charon->attributes->acquire_address(charon->attributes, - config->get_pool(config), - this->ike_sa->get_other_id(this->ike_sa), - this->virtual_ip); + vip = lib->attributes->acquire_address(lib->attributes, + config->get_pool(config), id, this->virtual_ip); } if (vip == NULL) { @@ -273,27 +366,32 @@ static status_t build_r(private_ike_config_t *this, message_t *message) } DBG1(DBG_IKE, "assigning virtual IP %H to peer", vip); this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); - - cp = cp_payload_create(); - cp->set_config_type(cp, CFG_REPLY); - - build_vip(this, vip, cp); - vip->destroy(vip); - - /* if we add an IP, we also look for other attributes */ - enumerator = charon->attributes->create_attribute_enumerator( - charon->attributes, this->ike_sa->get_other_id(this->ike_sa)); - while (enumerator->enumerate(enumerator, &type, &value)) + + cp = cp_payload_create_type(CFG_REPLY); + cp->add_attribute(cp, build_vip(vip)); + } + + /* query registered providers for additional attributes to include */ + enumerator = lib->attributes->create_responder_enumerator( + lib->attributes, id, vip); + while (enumerator->enumerate(enumerator, &type, &value)) + { + if (!cp) { - ca = configuration_attribute_create(); - ca->set_type(ca, type); - ca->set_value(ca, value); - cp->add_configuration_attribute(cp, ca); + cp = cp_payload_create_type(CFG_REPLY); } - enumerator->destroy(enumerator); - + DBG2(DBG_IKE, "building %N attribute", + configuration_attribute_type_names, type); + cp->add_attribute(cp, + configuration_attribute_create_value(type, value)); + } + enumerator->destroy(enumerator); + + if (cp) + { message->add_payload(message, (payload_t*)cp); } + DESTROY_IF(vip); return SUCCESS; } return NEED_MORE; @@ -306,9 +404,9 @@ static status_t process_i(private_ike_config_t *this, message_t *message) { if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { /* in last IKE_AUTH exchange */ - + process_payloads(this, message); - + if (this->virtual_ip) { this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); @@ -332,9 +430,11 @@ static task_type_t get_type(private_ike_config_t *this) static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) { DESTROY_IF(this->virtual_ip); - + this->ike_sa = ike_sa; this->virtual_ip = NULL; + this->requested->destroy_function(this->requested, free); + this->requested = linked_list_create(); } /** @@ -343,6 +443,7 @@ static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) static void destroy(private_ike_config_t *this) { DESTROY_IF(this->virtual_ip); + this->requested->destroy_function(this->requested, free); free(this); } @@ -352,15 +453,16 @@ static void destroy(private_ike_config_t *this) ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) { private_ike_config_t *this = malloc_thing(private_ike_config_t); - + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + this->initiator = initiator; this->ike_sa = ike_sa; this->virtual_ip = NULL; - + this->requested = linked_list_create(); + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -371,7 +473,7 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h index 32635e85e..8cef08697 100644 --- a/src/charon/sa/tasks/ike_config.h +++ b/src/charon/sa/tasks/ike_config.h @@ -44,7 +44,7 @@ struct ike_config_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator - * @return ike_config task to handle by the task_manager + * @return ike_config task to handle by the task_manager */ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c index cde117934..130948836 100644 --- a/src/charon/sa/tasks/ike_delete.c +++ b/src/charon/sa/tasks/ike_delete.c @@ -25,27 +25,27 @@ typedef struct private_ike_delete_t private_ike_delete_t; * Private members of a ike_delete_t task. */ struct private_ike_delete_t { - + /** * Public methods and task_t interface. */ ike_delete_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * are we deleting a rekeyed SA? */ bool rekeyed; - + /** * are we responding to a delete, but have initated our own? */ @@ -69,7 +69,7 @@ static status_t build_i(private_ike_delete_t *this, message_t *message) delete_payload = delete_payload_create(PROTO_IKE); message->add_payload(message, (payload_t*)delete_payload); - + if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) { this->rekeyed = TRUE; @@ -189,7 +189,7 @@ ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -200,11 +200,11 @@ ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; this->rekeyed = FALSE; this->simultaneous = FALSE; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c index 3aa714049..4c6ba7662 100644 --- a/src/charon/sa/tasks/ike_dpd.c +++ b/src/charon/sa/tasks/ike_dpd.c @@ -24,7 +24,7 @@ typedef struct private_ike_dpd_t private_ike_dpd_t; * Private members of a ike_dpd_t task. */ struct private_ike_dpd_t { - + /** * Public methods and task_t interface. */ @@ -83,7 +83,7 @@ ike_dpd_t *ike_dpd_create(bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more; @@ -94,6 +94,6 @@ ike_dpd_t *ike_dpd_create(bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))return_success; this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more; } - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c index 2705f5886..5eb33b540 100644 --- a/src/charon/sa/tasks/ike_init.c +++ b/src/charon/sa/tasks/ike_init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2009 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -24,7 +24,6 @@ #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/ke_payload.h> #include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/vendor_id_payload.h> /** maximum retries to do with cookies/other dh groups */ #define MAX_RETRIES 5 @@ -35,67 +34,67 @@ typedef struct private_ike_init_t private_ike_init_t; * Private members of a ike_init_t task. */ struct private_ike_init_t { - + /** * Public methods and task_t interface. */ ike_init_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * IKE config to establish */ ike_cfg_t *config; - + /** * diffie hellman group to use */ diffie_hellman_group_t dh_group; - + /** * diffie hellman key exchange */ diffie_hellman_t *dh; - + /** * Keymat derivation (from IKE_SA) */ keymat_t *keymat; - + /** * nonce chosen by us */ chunk_t my_nonce; - + /** * nonce chosen by peer */ chunk_t other_nonce; - + /** * Negotiated proposal used for IKE_SA */ proposal_t *proposal; - + /** * Old IKE_SA which gets rekeyed */ ike_sa_t *old_sa; - + /** * cookie received from responder */ chunk_t cookie; - + /** * retries done so far after failure (cookie or bad dh group) */ @@ -114,16 +113,16 @@ static void build_payloads(private_ike_init_t *this, message_t *message) ike_sa_id_t *id; proposal_t *proposal; iterator_t *iterator; - + id = this->ike_sa->get_id(this->ike_sa); - + this->config = this->ike_sa->get_ike_cfg(this->ike_sa); if (this->initiator) { proposal_list = this->config->get_proposals(this->config); if (this->old_sa) - { + { /* include SPI of new IKE_SA when we are rekeying */ iterator = proposal_list->create_iterator(proposal_list, TRUE); while (iterator->iterate(iterator, (void**)&proposal)) @@ -132,7 +131,7 @@ static void build_payloads(private_ike_init_t *this, message_t *message) } iterator->destroy(iterator); } - + sa_payload = sa_payload_create_from_proposal_list(proposal_list); proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); } @@ -146,11 +145,11 @@ static void build_payloads(private_ike_init_t *this, message_t *message) sa_payload = sa_payload_create_from_proposal(this->proposal); } message->add_payload(message, (payload_t*)sa_payload); - + nonce_payload = nonce_payload_create(); nonce_payload->set_nonce(nonce_payload, this->my_nonce); ke_payload = ke_payload_create_from_diffie_hellman(this->dh); - + if (this->old_sa) { /* payload order differs if we are rekeying */ message->add_payload(message, (payload_t*)nonce_payload); @@ -170,7 +169,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { enumerator_t *enumerator; payload_t *payload; - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -180,18 +179,21 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { sa_payload_t *sa_payload = (sa_payload_t*)payload; linked_list_t *proposal_list; - + bool private; + proposal_list = sa_payload->get_proposals(sa_payload); + private = this->ike_sa->supports_extension(this->ike_sa, + EXT_STRONGSWAN); this->proposal = this->config->select_proposal(this->config, - proposal_list); - proposal_list->destroy_offset(proposal_list, + proposal_list, private); + proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); break; } case KEY_EXCHANGE: { ke_payload_t *ke_payload = (ke_payload_t*)payload; - + this->dh_group = ke_payload->get_dh_group_number(ke_payload); if (!this->initiator) { @@ -212,13 +214,6 @@ static void process_payloads(private_ike_init_t *this, message_t *message) this->other_nonce = nonce_payload->get_nonce(nonce_payload); break; } - case VENDOR_ID: - { - vendor_id_payload_t *vendor_id = (vendor_id_payload_t*)payload; - chunk_t vid = vendor_id->get_data(vendor_id); - - DBG1(DBG_ENC, "received vendor id: %#B", &vid); - } default: break; } @@ -232,20 +227,20 @@ static void process_payloads(private_ike_init_t *this, message_t *message) static status_t build_i(private_ike_init_t *this, message_t *message) { rng_t *rng; - + this->config = this->ike_sa->get_ike_cfg(this->ike_sa); DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H", this->ike_sa->get_name(this->ike_sa), this->ike_sa->get_unique_id(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa)); this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); - - if (this->retry++ >= MAX_RETRIES) + + if (this->retry >= MAX_RETRIES) { DBG1(DBG_IKE, "giving up after %d retries", MAX_RETRIES); return FAILED; } - + /* if the DH group is set via use_dh_group(), we already have a DH object */ if (!this->dh) { @@ -258,7 +253,7 @@ static status_t build_i(private_ike_init_t *this, message_t *message) return FAILED; } } - + /* generate nonce only when we are trying the first time */ if (this->my_nonce.ptr == NULL) { @@ -271,12 +266,12 @@ static status_t build_i(private_ike_init_t *this, message_t *message) rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce); rng->destroy(rng); } - + if (this->cookie.ptr) { message->add_notify(message, FALSE, COOKIE, this->cookie); } - + build_payloads(this, message); #ifdef ME @@ -288,7 +283,7 @@ static status_t build_i(private_ike_init_t *this, message_t *message) } } #endif /* ME */ - + return NEED_MORE; } @@ -296,9 +291,9 @@ static status_t build_i(private_ike_init_t *this, message_t *message) * Implementation of task_t.process for responder */ static status_t process_r(private_ike_init_t *this, message_t *message) -{ +{ rng_t *rng; - + this->config = this->ike_sa->get_ike_cfg(this->ike_sa); DBG0(DBG_IKE, "%H is initiating an IKE_SA", message->get_source(message)); this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); @@ -311,59 +306,22 @@ static status_t process_r(private_ike_init_t *this, message_t *message) } rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce); rng->destroy(rng); - + #ifdef ME { - chunk_t connect_id = chunk_empty; - enumerator_t *enumerator; - payload_t *payload; - - /* check for a ME_CONNECTID notify */ - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) - { - if (payload->get_type(payload) == NOTIFY) - { - notify_payload_t *notify = (notify_payload_t*)payload; - notify_type_t type = notify->get_notify_type(notify); - - switch (type) - { - case ME_CONNECTID: - { - chunk_free(&connect_id); - connect_id = chunk_clone(notify->get_notification_data(notify)); - DBG2(DBG_IKE, "received ME_CONNECTID %#B", &connect_id); - break; - } - default: - { - if (type < 16383) - { - DBG1(DBG_IKE, "received %N notify error", - notify_type_names, type); - break; - } - DBG2(DBG_IKE, "received %N notify", - notify_type_names, type); - break; - } - } - } - } - enumerator->destroy(enumerator); - - if (connect_id.ptr) + notify_payload_t *notify = message->get_notify(message, ME_CONNECTID); + if (notify) { + chunk_t connect_id = notify->get_notification_data(notify); + DBG2(DBG_IKE, "received ME_CONNECTID %#B", &connect_id); charon->connect_manager->stop_checks(charon->connect_manager, - connect_id); - chunk_free(&connect_id); + connect_id); } } #endif /* ME */ - + process_payloads(this, message); - + return NEED_MORE; } @@ -377,7 +335,7 @@ static bool derive_keys(private_ike_init_t *this, pseudo_random_function_t prf_alg = PRF_UNDEFINED; chunk_t skd = chunk_empty; ike_sa_id_t *id; - + id = this->ike_sa->get_id(this->ike_sa); if (this->old_sa) { @@ -417,12 +375,12 @@ static status_t build_r(private_ike_init_t *this, message_t *message) return FAILED; } this->ike_sa->set_proposal(this->ike_sa, this->proposal); - + if (this->dh == NULL || !this->proposal->has_dh_group(this->proposal, this->dh_group)) { u_int16_t group; - + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) { @@ -440,7 +398,7 @@ static status_t build_r(private_ike_init_t *this, message_t *message) } return FAILED; } - + if (!derive_keys(this, this->other_nonce, this->my_nonce)) { DBG1(DBG_IKE, "key derivation failed"); @@ -458,7 +416,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) { enumerator_t *enumerator; payload_t *payload; - + /* check for erronous notifies */ enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) @@ -467,27 +425,28 @@ static status_t process_i(private_ike_init_t *this, message_t *message) { notify_payload_t *notify = (notify_payload_t*)payload; notify_type_t type = notify->get_notify_type(notify); - + switch (type) { case INVALID_KE_PAYLOAD: { chunk_t data; diffie_hellman_group_t bad_group; - + bad_group = this->dh_group; data = notify->get_notification_data(notify); this->dh_group = ntohs(*((u_int16_t*)data.ptr)); DBG1(DBG_IKE, "peer didn't accept DH group %N, " "it requested %N", diffie_hellman_group_names, bad_group, diffie_hellman_group_names, this->dh_group); - + if (this->old_sa == NULL) { /* reset the IKE_SA if we are not rekeying */ this->ike_sa->reset(this->ike_sa); } - + enumerator->destroy(enumerator); + this->retry++; return NEED_MORE; } case NAT_DETECTION_SOURCE_IP: @@ -504,6 +463,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) this->ike_sa->reset(this->ike_sa); enumerator->destroy(enumerator); DBG2(DBG_IKE, "received %N notify", notify_type_names, type); + this->retry++; return NEED_MORE; } default: @@ -513,7 +473,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) DBG1(DBG_IKE, "received %N notify error", notify_type_names, type); enumerator->destroy(enumerator); - return FAILED; + return FAILED; } DBG2(DBG_IKE, "received %N notify", notify_type_names, type); @@ -523,7 +483,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) } } enumerator->destroy(enumerator); - + process_payloads(this, message); /* check if we have everything */ @@ -534,14 +494,14 @@ static status_t process_i(private_ike_init_t *this, message_t *message) return FAILED; } this->ike_sa->set_proposal(this->ike_sa, this->proposal); - + if (this->dh == NULL || !this->proposal->has_dh_group(this->proposal, this->dh_group)) { DBG1(DBG_IKE, "peer DH group selection invalid"); return FAILED; } - + if (!derive_keys(this, this->my_nonce, this->other_nonce)) { DBG1(DBG_IKE, "key derivation failed"); @@ -581,7 +541,7 @@ static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) { DESTROY_IF(this->proposal); chunk_free(&this->other_nonce); - + this->ike_sa = ike_sa; this->proposal = NULL; DESTROY_IF(this->dh); @@ -622,7 +582,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; this->dh_group = MODP_NONE; @@ -635,6 +595,6 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) this->config = NULL; this->old_sa = old_sa; this->retry = 0; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h index 8d3810ef2..7bd784cff 100644 --- a/src/charon/sa/tasks/ike_init.h +++ b/src/charon/sa/tasks/ike_init.h @@ -38,7 +38,7 @@ struct ike_init_t { * Implements the task_t interface */ task_t task; - + /** * Get the lower of the two nonces, used for rekey collisions. * diff --git a/src/charon/sa/tasks/ike_me.c b/src/charon/sa/tasks/ike_me.c index d359aa339..2d2847ae0 100644 --- a/src/charon/sa/tasks/ike_me.c +++ b/src/charon/sa/tasks/ike_me.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ - + #include "ike_me.h" #include <string.h> @@ -33,71 +33,71 @@ typedef struct private_ike_me_t private_ike_me_t; * Private members of a ike_me_t task. */ struct private_ike_me_t { - + /** * Public methods and task_t interface. */ ike_me_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Is this a mediation connection? */ bool mediation; - + /** * Is this the response from another peer? */ bool response; - + /** * Gathered endpoints */ linked_list_t *local_endpoints; - + /** * Parsed endpoints */ linked_list_t *remote_endpoints; - + /** * Did the peer request a callback? */ bool callback; - + /** * Did the connect fail? */ bool failed; - + /** * Was there anything wrong with the payloads? */ bool invalid_syntax; - + /** * The requested peer */ - identification_t *peer_id; + identification_t *peer_id; /** * Received ID used for connectivity checks */ chunk_t connect_id; - + /** * Received key used for connectivity checks */ chunk_t connect_key; - + /** * Peer config of the mediated connection */ @@ -112,7 +112,7 @@ static void add_endpoints_to_message(message_t *message, linked_list_t *endpoint { iterator_t *iterator; endpoint_notify_t *endpoint; - + iterator = endpoints->create_iterator(endpoints, TRUE); while (iterator->iterate(iterator, (void**)&endpoint)) { @@ -129,25 +129,25 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message) enumerator_t *enumerator; host_t *addr, *host; u_int16_t port; - + /* get the port that is used to communicate with the ms */ host = this->ike_sa->get_my_host(this->ike_sa); port = host->get_port(host); - + enumerator = charon->kernel_interface->create_address_enumerator( charon->kernel_interface, FALSE, FALSE); while (enumerator->enumerate(enumerator, (void**)&addr)) { host = addr->clone(addr); host->set_port(host, port); - + this->local_endpoints->insert_last(this->local_endpoints, endpoint_notify_create_from_host(HOST, host, NULL)); - + host->destroy(host); } enumerator->destroy(enumerator); - + host = this->ike_sa->get_server_reflexive_host(this->ike_sa); if (host) { @@ -155,7 +155,7 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message) endpoint_notify_create_from_host(SERVER_REFLEXIVE, host, this->ike_sa->get_my_host(this->ike_sa))); } - + add_endpoints_to_message(message, this->local_endpoints); } @@ -166,7 +166,7 @@ static void process_payloads(private_ike_me_t *this, message_t *message) { enumerator_t *enumerator; payload_t *payload; - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -174,9 +174,9 @@ static void process_payloads(private_ike_me_t *this, message_t *message) { continue; } - + notify_payload_t *notify = (notify_payload_t*)payload; - + switch (notify->get_notify_type(notify)) { case ME_CONNECT_FAILED: @@ -193,16 +193,19 @@ static void process_payloads(private_ike_me_t *this, message_t *message) } case ME_ENDPOINT: { - endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify); + endpoint_notify_t *endpoint; + endpoint = endpoint_notify_create_from_payload(notify); if (!endpoint) { DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify"); break; } - DBG1(DBG_IKE, "received %N ME_ENDPOINT %#H", me_endpoint_type_names, - endpoint->get_type(endpoint), endpoint->get_host(endpoint)); - - this->remote_endpoints->insert_last(this->remote_endpoints, endpoint); + DBG1(DBG_IKE, "received %N ME_ENDPOINT %#H", + me_endpoint_type_names, endpoint->get_type(endpoint), + endpoint->get_host(endpoint)); + + this->remote_endpoints->insert_last(this->remote_endpoints, + endpoint); break; } case ME_CALLBACK: @@ -263,7 +266,9 @@ static status_t build_i(private_ike_me_t *this, message_t *message) { if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) { - endpoint_notify_t *endpoint = endpoint_notify_create_from_host(SERVER_REFLEXIVE, NULL, NULL); + endpoint_notify_t *endpoint; + endpoint = endpoint_notify_create_from_host(SERVER_REFLEXIVE, + NULL, NULL); message->add_payload(message, (payload_t*)endpoint->build_notify(endpoint)); endpoint->destroy(endpoint); } @@ -271,42 +276,42 @@ static status_t build_i(private_ike_me_t *this, message_t *message) } case ME_CONNECT: { - id_payload_t *id_payload; rng_t *rng; - - id_payload = id_payload_create_from_identification(ID_PEER, this->peer_id); + id_payload_t *id_payload; + id_payload = id_payload_create_from_identification(ID_PEER, + this->peer_id); message->add_payload(message, (payload_t*)id_payload); - + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); if (!rng) { - DBG1(DBG_IKE, "unable to generate connect ID for ME_CONNECT"); + DBG1(DBG_IKE, "unable to generate connect ID for ME_CONNECT"); return FAILED; } if (!this->response) { - /* only the initiator creates a connect ID. the responder returns - * the connect ID that it received from the initiator */ + /* only the initiator creates a connect ID. the responder + * returns the connect ID that it received from the initiator */ rng->allocate_bytes(rng, ME_CONNECTID_LEN, &this->connect_id); } rng->allocate_bytes(rng, ME_CONNECTKEY_LEN, &this->connect_key); rng->destroy(rng); - + message->add_notify(message, FALSE, ME_CONNECTID, this->connect_id); message->add_notify(message, FALSE, ME_CONNECTKEY, this->connect_key); - + if (this->response) { message->add_notify(message, FALSE, ME_RESPONSE, chunk_empty); } else { - /* FIXME: should we make that configurable? */ + /* FIXME: should we make this configurable? */ message->add_notify(message, FALSE, ME_CALLBACK, chunk_empty); } - + gather_and_add_endpoints(this, message); - + break; } default: @@ -328,40 +333,44 @@ static status_t process_r(private_ike_me_t *this, message_t *message) id_payload = (id_payload_t*)message->get_payload(message, ID_PEER); if (!id_payload) { - DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload" + ", aborting"); break; } this->peer_id = id_payload->get_identification(id_payload); - + process_payloads(this, message); - + if (this->callback) { DBG1(DBG_IKE, "received ME_CALLBACK for '%Y'", this->peer_id); break; - } - + } + if (!this->connect_id.ptr) { - DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify" + ", aborting"); this->invalid_syntax = TRUE; break; } - + if (!this->connect_key.ptr) { - DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY " + "notify, aborting"); this->invalid_syntax = TRUE; break; } - + if (!this->remote_endpoints->get_count(this->remote_endpoints)) { - DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT payloads, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT " + "payloads, aborting"); this->invalid_syntax = TRUE; break; } - + DBG1(DBG_IKE, "received ME_CONNECT"); break; } @@ -385,33 +394,39 @@ static status_t build_r(private_ike_me_t *this, message_t *message) message->add_notify(message, TRUE, INVALID_SYNTAX, chunk_empty); break; } - + if (this->callback) { - charon->connect_manager->check_and_initiate(charon->connect_manager, + /* we got a callback from the mediation server, initiate the + * queued mediated connecction */ + charon->connect_manager->check_and_initiate( + charon->connect_manager, this->ike_sa->get_id(this->ike_sa), this->ike_sa->get_my_id(this->ike_sa), this->peer_id); return SUCCESS; } - + if (this->response) { /* FIXME: handle result of set_responder_data * as initiator, upon receiving a response from another peer, * update the checklist and start sending checks */ - charon->connect_manager->set_responder_data(charon->connect_manager, - this->connect_id, this->connect_key, this->remote_endpoints); + charon->connect_manager->set_responder_data( + charon->connect_manager, + this->connect_id, this->connect_key, + this->remote_endpoints); } else { /* FIXME: handle result of set_initiator_data * as responder, create a checklist with the initiator's data */ - charon->connect_manager->set_initiator_data(charon->connect_manager, + charon->connect_manager->set_initiator_data( + charon->connect_manager, this->peer_id, this->ike_sa->get_my_id(this->ike_sa), - this->connect_id, this->connect_key, this->remote_endpoints, - FALSE); + this->connect_id, this->connect_key, + this->remote_endpoints, FALSE); if (this->ike_sa->respond(this->ike_sa, this->peer_id, - this->connect_id) != SUCCESS) + this->connect_id) != SUCCESS) { return FAILED; } @@ -434,13 +449,11 @@ static status_t process_i(private_ike_me_t *this, message_t *message) case IKE_SA_INIT: { process_payloads(this, message); - if (!this->mediation) { DBG1(DBG_IKE, "server did not return a ME_MEDIATION, aborting"); return FAILED; } - return NEED_MORE; } case IKE_AUTH: @@ -449,24 +462,21 @@ static status_t process_i(private_ike_me_t *this, message_t *message) /* FIXME: we should update the server reflexive endpoint somehow, * if mobike notices a change */ endpoint_notify_t *reflexive; - if (this->remote_endpoints->get_first(this->remote_endpoints, + if (this->remote_endpoints->get_first(this->remote_endpoints, (void**)&reflexive) == SUCCESS && reflexive->get_type(reflexive) == SERVER_REFLEXIVE) - { /* FIXME: should we accept this endpoint even if we did not send + { /* FIXME: should we accept this endpoint even if we did not send * a request? */ host_t *endpoint = reflexive->get_host(reflexive); - - this->ike_sa->set_server_reflexive_host(this->ike_sa, endpoint->clone(endpoint)); + endpoint = endpoint->clone(endpoint); + this->ike_sa->set_server_reflexive_host(this->ike_sa, endpoint); } - /* FIXME: what if it failed? e.g. AUTH failure */ - DBG1(DBG_IKE, "established mediation connection successfully"); - break; } case ME_CONNECT: { process_payloads(this, message); - + if (this->failed) { DBG1(DBG_IKE, "peer '%Y' is not online", this->peer_id); @@ -476,21 +486,25 @@ static status_t process_i(private_ike_me_t *this, message_t *message) { if (this->response) { - /* FIXME: handle result of set_responder_data. - * as responder, we update the checklist and start sending checks */ - charon->connect_manager->set_responder_data(charon->connect_manager, - this->connect_id, this->connect_key, this->local_endpoints); + /* FIXME: handle result of set_responder_data. */ + /* as responder, we update the checklist and start sending + * checks */ + charon->connect_manager->set_responder_data( + charon->connect_manager, this->connect_id, + this->connect_key, this->local_endpoints); } else { - /* FIXME: handle result of set_initiator_data - * as initiator, we create a checklist and set the initiator's data */ - charon->connect_manager->set_initiator_data(charon->connect_manager, - this->ike_sa->get_my_id(this->ike_sa), this->peer_id, - this->connect_id, this->connect_key, this->local_endpoints, - TRUE); - /* FIXME: also start a timer for the whole transaction (maybe - * within the connect_manager?) */ + /* FIXME: handle result of set_initiator_data */ + /* as initiator, we create a checklist and set the + * initiator's data */ + charon->connect_manager->set_initiator_data( + charon->connect_manager, + this->ike_sa->get_my_id(this->ike_sa), + this->peer_id, this->connect_id, this->connect_key, + this->local_endpoints, TRUE); + /* FIXME: also start a timer for the whole transaction + * (maybe within the connect_manager?) */ } } break; @@ -510,9 +524,11 @@ static status_t build_i_ms(private_ike_me_t *this, message_t *message) { case ME_CONNECT: { - id_payload_t *id_payload = id_payload_create_from_identification(ID_PEER, this->peer_id); + id_payload_t *id_payload; + id_payload = id_payload_create_from_identification(ID_PEER, + this->peer_id); message->add_payload(message, (payload_t*)id_payload); - + if (this->callback) { message->add_notify(message, FALSE, ME_CALLBACK, chunk_empty); @@ -521,11 +537,13 @@ static status_t build_i_ms(private_ike_me_t *this, message_t *message) { if (this->response) { - message->add_notify(message, FALSE, ME_RESPONSE, chunk_empty); - } - message->add_notify(message, FALSE, ME_CONNECTID, this->connect_id); - message->add_notify(message, FALSE, ME_CONNECTKEY, this->connect_key); - + message->add_notify(message, FALSE, ME_RESPONSE, + chunk_empty); + } + message->add_notify(message, FALSE, ME_CONNECTID, + this->connect_id); + message->add_notify(message, FALSE, ME_CONNECTKEY, + this->connect_key); add_endpoints_to_message(message, this->remote_endpoints); } break; @@ -533,7 +551,6 @@ static status_t build_i_ms(private_ike_me_t *this, message_t *message) default: break; } - return NEED_MORE; } @@ -546,15 +563,15 @@ static status_t process_r_ms(private_ike_me_t *this, message_t *message) { case IKE_SA_INIT: { - /* FIXME: we should check for SA* and TS* payloads - * if any are there send NO_ADDITIONAL_SAS back and delete this SA */ + /* FIXME: we should check for SA* and TS* payloads. if there are + * any, send NO_ADDITIONAL_SAS back and delete this SA */ process_payloads(this, message); return this->mediation ? NEED_MORE : SUCCESS; } case IKE_AUTH: { - /* FIXME: we should check whether the current peer_config is configured - * as mediation connection */ + /* FIXME: we should check whether the current peer_config is + * configured as mediation connection */ process_payloads(this, message); break; } @@ -570,32 +587,35 @@ static status_t process_r_ms(private_ike_me_t *this, message_t *message) id_payload = (id_payload_t*)message->get_payload(message, ID_PEER); if (!id_payload) { - DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload" + ", aborting"); this->invalid_syntax = TRUE; break; } - this->peer_id = id_payload->get_identification(id_payload); - + process_payloads(this, message); - + if (!this->connect_id.ptr) { - DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify" + ", aborting"); this->invalid_syntax = TRUE; break; } - + if (!this->connect_key.ptr) { - DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY notify" + ", aborting"); this->invalid_syntax = TRUE; break; } - + if (!this->remote_endpoints->get_count(this->remote_endpoints)) { - DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT payloads, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT " + "payloads, aborting"); this->invalid_syntax = TRUE; break; } @@ -604,7 +624,6 @@ static status_t process_r_ms(private_ike_me_t *this, message_t *message) default: break; } - return NEED_MORE; } @@ -623,58 +642,54 @@ static status_t build_r_ms(private_ike_me_t *this, message_t *message) case IKE_AUTH: { endpoint_notify_t *endpoint; - if (this->remote_endpoints->get_first(this->remote_endpoints, (void**)&endpoint) == SUCCESS && - endpoint->get_type(endpoint) == SERVER_REFLEXIVE) + if (this->remote_endpoints->get_first(this->remote_endpoints, + (void**)&endpoint) == SUCCESS && + endpoint->get_type(endpoint) == SERVER_REFLEXIVE) { host_t *host = this->ike_sa->get_other_host(this->ike_sa); - - DBG2(DBG_IKE, "received request for a server reflexive endpoint " - "sending: %#H", host); - - endpoint = endpoint_notify_create_from_host(SERVER_REFLEXIVE, host, NULL); + DBG2(DBG_IKE, "received request for a server reflexive " + "endpoint sending: %#H", host); + endpoint = endpoint_notify_create_from_host(SERVER_REFLEXIVE, + host, NULL); message->add_payload(message, (payload_t*)endpoint->build_notify(endpoint)); endpoint->destroy(endpoint); } - - /* FIXME: we actually must delete any existing IKE_SAs with the same remote id */ this->ike_sa->act_as_mediation_server(this->ike_sa); - - DBG1(DBG_IKE, "established mediation connection successfully"); - break; } case ME_CONNECT: - { + { if (this->invalid_syntax) { message->add_notify(message, TRUE, INVALID_SYNTAX, chunk_empty); break; } - + ike_sa_id_t *peer_sa; if (this->callback) { - peer_sa = charon->mediation_manager->check_and_register(charon->mediation_manager, - this->peer_id, this->ike_sa->get_other_id(this->ike_sa)); + peer_sa = charon->mediation_manager->check_and_register( + charon->mediation_manager, this->peer_id, + this->ike_sa->get_other_id(this->ike_sa)); } else { - peer_sa = charon->mediation_manager->check(charon->mediation_manager, - this->peer_id); + peer_sa = charon->mediation_manager->check( + charon->mediation_manager, this->peer_id); } - + if (!peer_sa) { /* the peer is not online */ - message->add_notify(message, TRUE, ME_CONNECT_FAILED, chunk_empty); + message->add_notify(message, TRUE, ME_CONNECT_FAILED, + chunk_empty); break; } - + job_t *job = (job_t*)mediation_job_create(this->peer_id, this->ike_sa->get_other_id(this->ike_sa), this->connect_id, this->connect_key, this->remote_endpoints, this->response); charon->processor->queue_job(charon->processor, job); - break; } default: @@ -706,8 +721,8 @@ static void me_connect(private_ike_me_t *this, identification_t *peer_id) /** * Implementation of ike_me.respond */ -static void me_respond(private_ike_me_t *this, identification_t *peer_id, - chunk_t connect_id) +static void me_respond(private_ike_me_t *this, identification_t *peer_id, + chunk_t connect_id) { this->peer_id = peer_id->clone(peer_id); this->connect_id = chunk_clone(connect_id); @@ -726,16 +741,19 @@ static void me_callback(private_ike_me_t *this, identification_t *peer_id) /** * Implementation of ike_me.relay */ -static void relay(private_ike_me_t *this, identification_t *requester, chunk_t connect_id, - chunk_t connect_key, linked_list_t *endpoints, bool response) +static void relay(private_ike_me_t *this, identification_t *requester, + chunk_t connect_id, chunk_t connect_key, + linked_list_t *endpoints, bool response) { this->peer_id = requester->clone(requester); this->connect_id = chunk_clone(connect_id); this->connect_key = chunk_clone(connect_key); - - this->remote_endpoints->destroy_offset(this->remote_endpoints, offsetof(endpoint_notify_t, destroy)); - this->remote_endpoints = endpoints->clone_offset(endpoints, offsetof(endpoint_notify_t, clone)); - + + this->remote_endpoints->destroy_offset(this->remote_endpoints, + offsetof(endpoint_notify_t, destroy)); + this->remote_endpoints = endpoints->clone_offset(endpoints, + offsetof(endpoint_notify_t, clone)); + this->response = response; } @@ -761,13 +779,15 @@ static void migrate(private_ike_me_t *this, ike_sa_t *ike_sa) static void destroy(private_ike_me_t *this) { DESTROY_IF(this->peer_id); - + chunk_free(&this->connect_id); chunk_free(&this->connect_key); - - this->local_endpoints->destroy_offset(this->local_endpoints, offsetof(endpoint_notify_t, destroy)); - this->remote_endpoints->destroy_offset(this->remote_endpoints, offsetof(endpoint_notify_t, destroy)); - + + this->local_endpoints->destroy_offset(this->local_endpoints, + offsetof(endpoint_notify_t, destroy)); + this->remote_endpoints->destroy_offset(this->remote_endpoints, + offsetof(endpoint_notify_t, destroy)); + DESTROY_IF(this->mediated_cfg); free(this); } @@ -782,7 +802,7 @@ ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (ike_sa->has_condition(ike_sa, COND_ORIGINAL_INITIATOR)) { if (initiator) @@ -810,15 +830,15 @@ ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator) this->public.task.process = (status_t(*)(task_t*,message_t*))process_r_ms; } } - + this->public.connect = (void(*)(ike_me_t*,identification_t*))me_connect; this->public.respond = (void(*)(ike_me_t*,identification_t*,chunk_t))me_respond; this->public.callback = (void(*)(ike_me_t*,identification_t*))me_callback; this->public.relay = (void(*)(ike_me_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))relay; - + this->ike_sa = ike_sa; this->initiator = initiator; - + this->peer_id = NULL; this->connect_id = chunk_empty; this->connect_key = chunk_empty; @@ -829,8 +849,8 @@ ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator) this->callback = FALSE; this->failed = FALSE; this->invalid_syntax = FALSE; - + this->mediated_cfg = NULL; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_me.h b/src/charon/sa/tasks/ike_me.h index 4b35c313c..31285a426 100644 --- a/src/charon/sa/tasks/ike_me.h +++ b/src/charon/sa/tasks/ike_me.h @@ -34,18 +34,17 @@ typedef struct ike_me_t ike_me_t; * connection, allows to initiate mediated connections using ME_CONNECT * exchanges and to request reflexive addresses from the mediation server using * ME_ENDPOINT notifies. - * + * * @note This task has to be activated before the IKE_AUTH task, because that * task generates the IKE_SA_INIT message so that no more payloads can be added * to it afterwards. */ struct ike_me_t { - /** * Implements the task_t interface */ task_t task; - + /** * Initiates a connection with another peer (i.e. sends a ME_CONNECT * to the mediation server) @@ -53,45 +52,48 @@ struct ike_me_t { * @param peer_id ID of the other peer (gets cloned) */ void (*connect)(ike_me_t *this, identification_t *peer_id); - + /** * Responds to a ME_CONNECT from another peer (i.e. sends a ME_CONNECT * to the mediation server) - * - * @param peer_id ID of the other peer (gets cloned) - * @param connect_id the connect ID as provided by the initiator (gets cloned) + * + * Data gets cloned. + * + * @param peer_id ID of the other peer + * @param connect_id the connect ID as provided by the initiator */ - void (*respond)(ike_me_t *this, identification_t *peer_id, chunk_t connect_id); - + void (*respond)(ike_me_t *this, identification_t *peer_id, + chunk_t connect_id); + /** - * Sends a ME_CALLBACK to a peer that previously requested another peer. - * + * Sends a ME_CALLBACK to a peer that previously requested some other peer. + * * @param peer_id ID of the other peer (gets cloned) */ void (*callback)(ike_me_t *this, identification_t *peer_id); - + /** * Relays data to another peer (i.e. sends a ME_CONNECT to the peer) - * + * * Data gets cloned. - * + * * @param requester ID of the requesting peer * @param connect_id content of the ME_CONNECTID notify * @param connect_key content of the ME_CONNECTKEY notify * @param endpoints endpoints * @param response TRUE if this is a response */ - void (*relay)(ike_me_t *this, identification_t *requester, chunk_t connect_id, - chunk_t connect_key, linked_list_t *endpoints, bool response); - + void (*relay)(ike_me_t *this, identification_t *requester, + chunk_t connect_id, chunk_t connect_key, + linked_list_t *endpoints, bool response); }; /** * Create a new ike_me task. * * @param ike_sa IKE_SA this task works for - * @param initiator TRUE if taks is initiated by us - * @return ike_me task to handle by the task_manager + * @param initiator TRUE if task is initiated by us + * @return ike_me task to be handled by the task_manager */ ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c index 9a1afe744..d76ba8d2b 100644 --- a/src/charon/sa/tasks/ike_mobike.c +++ b/src/charon/sa/tasks/ike_mobike.c @@ -30,42 +30,42 @@ typedef struct private_ike_mobike_t private_ike_mobike_t; * Private members of a ike_mobike_t task. */ struct private_ike_mobike_t { - + /** * Public methods and task_t interface. */ ike_mobike_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * cookie2 value to verify new addresses */ chunk_t cookie2; - + /** * NAT discovery reusing the IKE_NATD task */ ike_natd_t *natd; - + /** * use task to update addresses */ bool update; - + /** * do routability check */ bool check; - + /** * include address list update */ @@ -79,7 +79,7 @@ static void flush_additional_addresses(private_ike_mobike_t *this) { iterator_t *iterator; host_t *host; - + iterator = this->ike_sa->create_additional_address_iterator(this->ike_sa); while (iterator->iterate(iterator, (void**)&host)) { @@ -98,7 +98,7 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) enumerator_t *enumerator; payload_t *payload; bool first = TRUE; - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -106,7 +106,7 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) notify_payload_t *notify; chunk_t data; host_t *host; - + if (payload->get_type(payload) != NOTIFY) { continue; @@ -117,9 +117,9 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) case MOBIKE_SUPPORTED: { peer_cfg_t *peer_cfg; - + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (!this->initiator && + if (!this->initiator && peer_cfg && !peer_cfg->use_mobike(peer_cfg)) { DBG1(DBG_IKE, "peer supports MOBIKE, but disabled in config"); @@ -191,7 +191,7 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) host_t *host, *me; notify_type_t type; int added = 0; - + me = this->ike_sa->get_my_host(this->ike_sa); enumerator = charon->kernel_interface->create_address_enumerator( charon->kernel_interface, FALSE, FALSE); @@ -227,7 +227,7 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) } /** - * build a cookie and add it to the message + * build a cookie and add it to the message */ static void build_cookie(private_ike_mobike_t *this, message_t *message) { @@ -250,12 +250,12 @@ static void update_children(private_ike_mobike_t *this) { iterator_t *iterator; child_sa_t *child_sa; - + iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); while (iterator->iterate(iterator, (void**)&child_sa)) { if (child_sa->update(child_sa, - this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_virtual_ip(this->ike_sa, TRUE), this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED) @@ -276,7 +276,7 @@ static void transmit(private_ike_mobike_t *this, packet_t *packet) host_t *me, *other, *me_old, *other_old; iterator_t *iterator; packet_t *copy; - + if (!this->check) { return; @@ -284,16 +284,19 @@ static void transmit(private_ike_mobike_t *this, packet_t *packet) me_old = this->ike_sa->get_my_host(this->ike_sa); other_old = this->ike_sa->get_other_host(this->ike_sa); - + me = charon->kernel_interface->get_source_addr( charon->kernel_interface, other_old, NULL); if (me) { me->set_port(me, me->ip_equals(me, me_old) ? me_old->get_port(me_old) : IKEV2_NATT_PORT); - packet->set_source(packet, me); + DBG1(DBG_IKE, "checking original path %#H - %#H", me, other_old); + copy = packet->clone(packet); + copy->set_source(copy, me); + charon->sender->send(charon->sender, copy); } - + iterator = this->ike_sa->create_additional_address_iterator(this->ike_sa); while (iterator->iterate(iterator, (void**)&other)) { @@ -320,9 +323,6 @@ static void transmit(private_ike_mobike_t *this, packet_t *packet) } } iterator->destroy(iterator); - me = packet->get_source(packet); - other = packet->get_destination(packet); - DBG1(DBG_IKE, "checking path %#H - %#H", me, other); } /** @@ -338,8 +338,8 @@ static status_t build_i(private_ike_mobike_t *this, message_t *message) else if (message->get_exchange_type(message) == INFORMATIONAL) { host_t *old, *new; - - /* we check if the existing address is still valid */ + + /* we check if the existing address is still valid */ old = message->get_source(message); new = charon->kernel_interface->get_source_addr(charon->kernel_interface, message->get_destination(message), old); @@ -388,13 +388,13 @@ static status_t process_r(private_ike_mobike_t *this, message_t *message) if (this->update) { host_t *me, *other; - + me = message->get_destination(message); other = message->get_source(message); this->ike_sa->set_my_host(this->ike_sa, me->clone(me)); this->ike_sa->set_other_host(this->ike_sa, other->clone(other)); } - + if (this->natd) { this->natd->task.process(&this->natd->task, message); @@ -461,7 +461,7 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) if (this->cookie2.ptr) { /* check cookie if we included one */ chunk_t cookie2; - + cookie2 = this->cookie2; this->cookie2 = chunk_empty; process_payloads(this, message); @@ -496,17 +496,17 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) if (this->check) { host_t *me_new, *me_old, *other_new, *other_old; - + me_new = message->get_destination(message); other_new = message->get_source(message); me_old = this->ike_sa->get_my_host(this->ike_sa); other_old = this->ike_sa->get_other_host(this->ike_sa); - + if (!me_new->equals(me_new, me_old)) { this->update = TRUE; this->ike_sa->set_my_host(this->ike_sa, me_new->clone(me_new)); - } + } if (!other_new->equals(other_new, other_old)) { this->update = TRUE; @@ -538,7 +538,7 @@ static void roam(private_ike_mobike_t *this, bool address) { this->check = TRUE; this->address = address; - this->ike_sa->set_pending_updates(this->ike_sa, + this->ike_sa->set_pending_updates(this->ike_sa, this->ike_sa->get_pending_updates(this->ike_sa) + 1); } @@ -552,7 +552,7 @@ static void dpd(private_ike_mobike_t *this) this->natd = ike_natd_create(this->ike_sa, this->initiator); } this->address = FALSE; - this->ike_sa->set_pending_updates(this->ike_sa, + this->ike_sa->set_pending_updates(this->ike_sa, this->ike_sa->get_pending_updates(this->ike_sa) + 1); } @@ -612,7 +612,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -623,7 +623,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->initiator = initiator; this->update = FALSE; @@ -631,7 +631,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator) this->address = TRUE; this->cookie2 = chunk_empty; this->natd = NULL; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_mobike.h b/src/charon/sa/tasks/ike_mobike.h index 919b5ddd3..05b2224d1 100644 --- a/src/charon/sa/tasks/ike_mobike.h +++ b/src/charon/sa/tasks/ike_mobike.h @@ -35,7 +35,7 @@ typedef struct ike_mobike_t ike_mobike_t; * and IPsec tunnel addresses. * This tasks handles the MOBIKE_SUPPORTED notify exchange to detect MOBIKE * support, allows the exchange of ADDITIONAL_*_ADDRESS to exchange additional - * endpoints and handles the UPDATE_SA_ADDRESS notify to finally update + * endpoints and handles the UPDATE_SA_ADDRESS notify to finally update * endpoints. */ struct ike_mobike_t { @@ -44,36 +44,36 @@ struct ike_mobike_t { * Implements the task_t interface */ task_t task; - + /** * Use the task to roam to other addresses. * * @param address TRUE to include address list update */ void (*roam)(ike_mobike_t *this, bool address); - + /** * Use the task for a DPD check which detects changes in NAT mappings. */ void (*dpd)(ike_mobike_t *this); - + /** * Transmision hook, called by task manager. * - * The task manager calls this hook whenever it transmits a packet. It + * The task manager calls this hook whenever it transmits a packet. It * allows the mobike task to send the packet on multiple paths to do path * probing. * * @param packet the packet to transmit */ void (*transmit)(ike_mobike_t *this, packet_t *packet); - + /** * Check if this task is probing for routability. * * @return TRUE if task is probing */ - bool (*is_probing)(ike_mobike_t *this); + bool (*is_probing)(ike_mobike_t *this); }; /** @@ -81,7 +81,7 @@ struct ike_mobike_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if taks is initiated by us - * @return ike_mobike task to handle by the task_manager + * @return ike_mobike task to handle by the task_manager */ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c index bb18e7bda..9121fe2ea 100644 --- a/src/charon/sa/tasks/ike_natd.c +++ b/src/charon/sa/tasks/ike_natd.c @@ -30,47 +30,47 @@ typedef struct private_ike_natd_t private_ike_natd_t; * Private members of a ike_natd_t task. */ struct private_ike_natd_t { - + /** * Public methods and task_t interface. */ ike_natd_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * Hasher used to build NAT detection hashes */ hasher_t *hasher; - + /** * Did we process any NAT detection notifys for a source address? */ bool src_seen; - + /** * Did we process any NAT detection notifys for a destination address? */ bool dst_seen; - + /** * Have we found a matching source address NAT hash? */ bool src_matched; - + /** * Have we found a matching destination address NAT hash? */ bool dst_matched; - + /** * whether NAT mappings for our NATed address has changed */ @@ -88,7 +88,7 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this, chunk_t natd_hash; u_int64_t spi_i, spi_r; u_int16_t port; - + /* prepare all required chunks */ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); spi_r = ike_sa_id->get_responder_spi(ike_sa_id); @@ -100,13 +100,13 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this, port_chunk.ptr = (void*)&port; port_chunk.len = sizeof(port); addr_chunk = host->get_address(host); - + /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash); DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); DBG3(DBG_IKE, "natd_hash %B", &natd_hash); - + chunk_free(&natd_chunk); return natd_hash; } @@ -118,7 +118,7 @@ static chunk_t generate_natd_hash_faked(private_ike_natd_t *this) { rng_t *rng; chunk_t chunk; - + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { @@ -137,10 +137,10 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, notify_type_t type, host_t *host) { chunk_t hash; - notify_payload_t *notify; + notify_payload_t *notify; ike_sa_id_t *ike_sa_id; ike_cfg_t *config; - + ike_sa_id = this->ike_sa->get_id(this->ike_sa); config = this->ike_sa->get_ike_cfg(this->ike_sa); if (config->force_encap(config) && type == NAT_DETECTION_SOURCE_IP) @@ -155,7 +155,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, notify->set_notify_type(notify, type); notify->set_notification_data(notify, hash); chunk_free(&hash); - + return notify; } @@ -171,17 +171,17 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) ike_sa_id_t *ike_sa_id; host_t *me, *other; ike_cfg_t *config; - + /* Precompute NAT-D hashes for incoming NAT notify comparison */ ike_sa_id = message->get_ike_sa_id(message); me = message->get_destination(message); other = message->get_source(message); dst_hash = generate_natd_hash(this, ike_sa_id, me); src_hash = generate_natd_hash(this, ike_sa_id, other); - + DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); - + enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { @@ -234,10 +234,10 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) } } enumerator->destroy(enumerator); - + chunk_free(&src_hash); chunk_free(&dst_hash); - + if (this->src_seen && this->dst_seen) { this->ike_sa->enable_extension(this->ike_sa, EXT_NATT); @@ -245,12 +245,12 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, !this->dst_matched); this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, - !this->src_matched); + !this->src_matched); config = this->ike_sa->get_ike_cfg(this->ike_sa); if (this->dst_matched && this->src_matched && config->force_encap(config)) { - this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); + this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); } } } @@ -261,7 +261,7 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) static status_t process_i(private_ike_natd_t *this, message_t *message) { process_payloads(this, message); - + if (message->get_exchange_type(message) == IKE_SA_INIT) { peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); @@ -275,10 +275,10 @@ static status_t process_i(private_ike_natd_t *this, message_t *message) return SUCCESS; } #endif /* ME */ - + if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY) || #ifdef ME - /* if we are on a mediation connection we swith to port 4500 even + /* if we are on a mediation connection we switch to port 4500 even * if no NAT is detected. */ peer_cfg->is_mediation(peer_cfg) || #endif /* ME */ @@ -288,7 +288,7 @@ static status_t process_i(private_ike_natd_t *this, message_t *message) this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))) { host_t *me, *other; - + /* do not switch if we have a custom port from mobike/NAT */ me = this->ike_sa->get_my_host(this->ike_sa); if (me->get_port(me) == IKEV2_UDP_PORT) @@ -302,7 +302,7 @@ static status_t process_i(private_ike_natd_t *this, message_t *message) } } } - + return SUCCESS; } @@ -314,18 +314,18 @@ static status_t build_i(private_ike_natd_t *this, message_t *message) notify_payload_t *notify; enumerator_t *enumerator; host_t *host; - + if (this->hasher == NULL) { DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); return NEED_MORE; } - + /* destination is always set */ host = message->get_destination(message); notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); message->add_payload(message, (payload_t*)notify); - + /* source may be any, we have 3 possibilities to get our source address: * 1. It is defined in the config => use the one of the IKE_SA * 2. We do a routing lookup in the kernel interface @@ -374,7 +374,7 @@ static status_t build_r(private_ike_natd_t *this, message_t *message) { notify_payload_t *notify; host_t *me, *other; - + /* only add notifies on successfull responses. */ if (message->get_exchange_type(message) == IKE_SA_INIT && message->get_payload(message, SECURITY_ASSOCIATION) == NULL) @@ -389,12 +389,12 @@ static status_t build_r(private_ike_natd_t *this, message_t *message) DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); return SUCCESS; } - + /* initiator seems to support NAT detection, add response */ me = message->get_source(message); notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); message->add_payload(message, (payload_t*)notify); - + other = message->get_destination(message); notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); message->add_payload(message, (payload_t*)notify); @@ -406,9 +406,9 @@ static status_t build_r(private_ike_natd_t *this, message_t *message) * Implementation of task_t.process for responder */ static status_t process_r(private_ike_natd_t *this, message_t *message) -{ +{ process_payloads(this, message); - + return NEED_MORE; } @@ -460,7 +460,7 @@ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -471,9 +471,9 @@ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->public.has_mapping_changed = (bool(*)(ike_natd_t*))has_mapping_changed; - + this->ike_sa = ike_sa; this->initiator = initiator; this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); @@ -482,6 +482,6 @@ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) this->src_matched = FALSE; this->dst_matched = FALSE; this->mapping_changed = FALSE; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h index 698394842..97b652ead 100644 --- a/src/charon/sa/tasks/ike_natd.h +++ b/src/charon/sa/tasks/ike_natd.h @@ -36,7 +36,7 @@ struct ike_natd_t { * Implements the task_t interface */ task_t task; - + /** * Check if the NAT mapping has changed for our address. * diff --git a/src/charon/sa/tasks/ike_reauth.c b/src/charon/sa/tasks/ike_reauth.c index 80f1b7b8c..ac89c358b 100644 --- a/src/charon/sa/tasks/ike_reauth.c +++ b/src/charon/sa/tasks/ike_reauth.c @@ -25,17 +25,17 @@ typedef struct private_ike_reauth_t private_ike_reauth_t; * Private members of a ike_reauth_t task. */ struct private_ike_reauth_t { - + /** * Public methods and task_t interface. */ ike_reauth_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * reused ike_delete task */ @@ -60,17 +60,17 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) iterator_t *iterator; child_sa_t *child_sa; peer_cfg_t *peer_cfg; - + /* process delete response first */ this->ike_delete->task.process(&this->ike_delete->task, message); peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - + /* reauthenticate only if we have children */ iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); if (iterator->get_count(iterator) == 0 #ifdef ME - /* we allow a peer to reauth a mediation connection (without CHILD_SA) */ + /* we allow peers to reauth mediation connections (without children) */ && !peer_cfg->is_mediation(peer_cfg) #endif /* ME */ ) @@ -79,9 +79,9 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) iterator->destroy(iterator); return FAILED; } - + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE); - + new->set_peer_cfg(new, peer_cfg); host = this->ike_sa->get_other_host(this->ike_sa); new->set_other_host(new, host->clone(host)); @@ -93,7 +93,7 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) { new->set_virtual_ip(new, TRUE, host); } - + #ifdef ME /* we initiate the new IKE_SA of the mediation connection without CHILD_SA */ if (peer_cfg->is_mediation(peer_cfg)) @@ -109,7 +109,7 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) } } #endif /* ME */ - + while (iterator->iterate(iterator, (void**)&child_sa)) { switch (child_sa->get_state(child_sa)) @@ -144,7 +144,7 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); - + /* we always return failed to delete the obsolete IKE_SA */ return FAILED; } @@ -187,10 +187,10 @@ ike_reauth_t *ike_reauth_create(ike_sa_t *ike_sa) this->public.task.destroy = (void(*)(task_t*))destroy; this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; - + this->ike_sa = ike_sa; this->ike_delete = ike_delete_create(ike_sa, TRUE); - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c index 3a049b566..a2275e796 100644 --- a/src/charon/sa/tasks/ike_rekey.c +++ b/src/charon/sa/tasks/ike_rekey.c @@ -30,37 +30,37 @@ typedef struct private_ike_rekey_t private_ike_rekey_t; * Private members of a ike_rekey_t task. */ struct private_ike_rekey_t { - + /** * Public methods and task_t interface. */ ike_rekey_t public; - + /** * Assigned IKE_SA. */ ike_sa_t *ike_sa; - + /** * New IKE_SA which replaces the current one */ ike_sa_t *new_sa; - + /** * Are we the initiator? */ bool initiator; - + /** * the IKE_INIT task which is reused to simplify rekeying */ ike_init_t *ike_init; - + /** * IKE_DELETE task to delete the old IKE_SA after rekeying was successful */ ike_delete_t *ike_delete; - + /** * colliding task detected by the task manager */ @@ -74,7 +74,7 @@ static status_t build_i_delete(private_ike_rekey_t *this, message_t *message) { /* update exchange type to INFORMATIONAL for the delete */ message->set_exchange_type(message, INFORMATIONAL); - + return this->ike_delete->task.build(&this->ike_delete->task, message); } @@ -93,13 +93,13 @@ static status_t build_i(private_ike_rekey_t *this, message_t *message) { peer_cfg_t *peer_cfg; host_t *other_host; - + /* create new SA only on first try */ if (this->new_sa == NULL) { this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE); - + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); other_host = this->ike_sa->get_other_host(this->ike_sa); this->new_sa->set_peer_cfg(this->new_sa, peer_cfg); @@ -120,7 +120,7 @@ static status_t process_r(private_ike_rekey_t *this, message_t *message) peer_cfg_t *peer_cfg; iterator_t *iterator; child_sa_t *child_sa; - + if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) { DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting"); @@ -144,15 +144,15 @@ static status_t process_r(private_ike_rekey_t *this, message_t *message) } } iterator->destroy(iterator); - + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, FALSE); - + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); this->new_sa->set_peer_cfg(this->new_sa, peer_cfg); this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa); this->ike_init->task.process(&this->ike_init->task, message); - + return NEED_MORE; } @@ -167,12 +167,12 @@ static status_t build_r(private_ike_rekey_t *this, message_t *message) message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } - + if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) { return SUCCESS; } - + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", @@ -182,7 +182,7 @@ static status_t build_r(private_ike_rekey_t *this, message_t *message) this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); - + return SUCCESS; } @@ -191,32 +191,17 @@ static status_t build_r(private_ike_rekey_t *this, message_t *message) */ static status_t process_i(private_ike_rekey_t *this, message_t *message) { - enumerator_t *enumerator; - payload_t *payload; - - /* handle NO_ADDITIONAL_SAS notify */ - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) + if (message->get_notify(message, NO_ADDITIONAL_SAS)) { - if (payload->get_type(payload) == NOTIFY) - { - notify_payload_t *notify = (notify_payload_t*)payload; - - if (notify->get_notify_type(notify) == NO_ADDITIONAL_SAS) - { - DBG1(DBG_IKE, "peer seems to not support IKE rekeying, " - "starting reauthentication"); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->processor->queue_job(charon->processor, - (job_t*)rekey_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), TRUE)); - enumerator->destroy(enumerator); - return SUCCESS; - } - } + DBG1(DBG_IKE, "peer seems to not support IKE rekeying, " + "starting reauthentication"); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->processor->queue_job(charon->processor, + (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), TRUE)); + return SUCCESS; } - enumerator->destroy(enumerator); - + switch (this->ike_init->task.process(&this->ike_init->task, message)) { case FAILED: @@ -230,7 +215,7 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) job = (job_t*)rekey_ike_sa_job_create( this->ike_sa->get_id(this->ike_sa), FALSE); DBG1(DBG_IKE, "IKE_SA rekeying failed, " - "trying again in %d seconds", retry); + "trying again in %d seconds", retry); this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); charon->scheduler->schedule_job(charon->scheduler, job, retry); } @@ -242,7 +227,7 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) default: break; } - + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", this->new_sa->get_name(this->new_sa), @@ -251,7 +236,7 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); - + /* check for collisions */ if (this->collision && this->collision->get_type(this->collision) == IKE_REKEY) @@ -259,13 +244,13 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) chunk_t this_nonce, other_nonce; host_t *host; private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision; - + this_nonce = this->ike_init->get_lower_nonce(this->ike_init); other_nonce = other->ike_init->get_lower_nonce(other->ike_init); - + /* if we have the lower nonce, delete rekeyed SA. If not, delete * the redundant. */ - if (memcmp(this_nonce.ptr, other_nonce.ptr, + if (memcmp(this_nonce.ptr, other_nonce.ptr, min(this_nonce.len, other_nonce.len)) < 0) { /* peer should delete this SA. Add a timeout just in case. */ @@ -305,12 +290,12 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); } - + /* rekeying successful, delete the IKE_SA using a subtask */ this->ike_delete = ike_delete_create(this->ike_sa, TRUE); this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete; this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete; - + return NEED_MORE; } @@ -349,7 +334,7 @@ static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) charon->bus->set_sa(charon->bus, this->ike_sa); } DESTROY_IF(this->collision); - + this->collision = NULL; this->ike_sa = ike_sa; this->new_sa = NULL; @@ -412,13 +397,13 @@ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - + this->ike_sa = ike_sa; this->new_sa = NULL; this->ike_init = NULL; this->ike_delete = NULL; this->initiator = initiator; this->collision = NULL; - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h index 6748279ab..1c9550768 100644 --- a/src/charon/sa/tasks/ike_rekey.h +++ b/src/charon/sa/tasks/ike_rekey.h @@ -36,7 +36,7 @@ struct ike_rekey_t { * Implements the task_t interface */ task_t task; - + /** * Register a rekeying task which collides with this one. * @@ -54,7 +54,7 @@ struct ike_rekey_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator, FALSE for responder - * @return IKE_REKEY task to handle by the task_manager + * @return IKE_REKEY task to handle by the task_manager */ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/charon/sa/tasks/ike_vendor.c b/src/charon/sa/tasks/ike_vendor.c new file mode 100644 index 000000000..7c435b6d1 --- /dev/null +++ b/src/charon/sa/tasks/ike_vendor.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 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_vendor.h" + +#include <daemon.h> +#include <encoding/payloads/vendor_id_payload.h> + +typedef struct private_ike_vendor_t private_ike_vendor_t; + +/** + * Private data of an ike_vendor_t object. + */ +struct private_ike_vendor_t { + + /** + * Public ike_vendor_t interface. + */ + ike_vendor_t public; + + /** + * Associated IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Are we the inititator of this task + */ + bool initiator; +}; + +/** + * strongSwan specific vendor ID without version, MD5("strongSwan") + */ +static chunk_t strongswan_vid = chunk_from_chars( + 0x88,0x2f,0xe5,0x6d,0x6f,0xd2,0x0d,0xbc, + 0x22,0x51,0x61,0x3b,0x2e,0xbe,0x5b,0xeb +); + +METHOD(task_t, build, status_t, + private_ike_vendor_t *this, message_t *message) +{ + if (lib->settings->get_bool(lib->settings, + "charon.send_vendor_id", FALSE)) + { + vendor_id_payload_t *vid; + + vid = vendor_id_payload_create_data(chunk_clone(strongswan_vid)); + message->add_payload(message, &vid->payload_interface); + } + + return this->initiator ? NEED_MORE : SUCCESS; +} + +METHOD(task_t, process, status_t, + private_ike_vendor_t *this, message_t *message) +{ + enumerator_t *enumerator; + payload_t *payload; + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == VENDOR_ID) + { + vendor_id_payload_t *vid; + chunk_t data; + + vid = (vendor_id_payload_t*)payload; + data = vid->get_data(vid); + + if (chunk_equals(data, strongswan_vid)) + { + DBG1(DBG_IKE, "received strongSwan vendor id"); + this->ike_sa->enable_extension(this->ike_sa, EXT_STRONGSWAN); + } + else + { + DBG1(DBG_ENC, "received unknown vendor id: %#B", &data); + } + } + } + enumerator->destroy(enumerator); + + return this->initiator ? SUCCESS : NEED_MORE; +} + +METHOD(task_t, migrate, void, + private_ike_vendor_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_vendor_t *this) +{ + return IKE_VENDOR; +} + +METHOD(task_t, destroy, void, + private_ike_vendor_t *this) +{ + free(this); +} + +/** + * See header + */ +ike_vendor_t *ike_vendor_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_vendor_t *this; + + INIT(this, + .public.task = { + .build = _build, + .process = _process, + .migrate = _migrate, + .get_type = _get_type, + .destroy = _destroy, + }, + .initiator = initiator, + .ike_sa = ike_sa, + ); + + return &this->public; +} + diff --git a/src/charon/sa/tasks/ike_vendor.h b/src/charon/sa/tasks/ike_vendor.h new file mode 100644 index 000000000..dcdd37424 --- /dev/null +++ b/src/charon/sa/tasks/ike_vendor.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009 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. + */ + +/** + * @defgroup ike_vendor ike_vendor + * @{ @ingroup tasks + */ + +#ifndef IKE_VENDOR_H_ +#define IKE_VENDOR_H_ + +typedef struct ike_vendor_t ike_vendor_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * Vendor ID processing task. + */ +struct ike_vendor_t { + + /** + * Implements task interface. + */ + task_t task; +}; + +/** + * Create a ike_vendor instance. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + */ +ike_vendor_t *ike_vendor_create(ike_sa_t *ike_sa, bool initiator); + +#endif /** IKE_VENDOR_H_ @}*/ diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c index 9e35b62a5..0d7383141 100644 --- a/src/charon/sa/tasks/task.c +++ b/src/charon/sa/tasks/task.c @@ -30,6 +30,7 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY, "IKE_REAUTH", "IKE_DELETE", "IKE_DPD", + "IKE_VENDOR", "IKE_ME", "CHILD_CREATE", "CHILD_DELETE", @@ -49,6 +50,7 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY, "IKE_REAUTH", "IKE_DELETE", "IKE_DPD", + "IKE_VENDOR", "CHILD_CREATE", "CHILD_DELETE", "CHILD_REKEY", diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h index 3d2014599..4468f2ebe 100644 --- a/src/charon/sa/tasks/task.h +++ b/src/charon/sa/tasks/task.h @@ -57,6 +57,8 @@ enum task_type_t { IKE_DELETE, /** liveness check */ IKE_DPD, + /** Vendor ID processing */ + IKE_VENDOR, #ifdef ME /** handle ME stuff */ IKE_ME, @@ -79,7 +81,7 @@ extern enum_name_t *task_type_names; * * A task is an elemantary operation. It may be handled by a single or by * multiple exchanges. An exchange may even complete multiple tasks. - * A task has a build() and an process() operation. The build() operation + * A task has a build() and an process() operation. The build() operation * creates payloads and adds it to the message. The process() operation * inspects a message and handles its payloads. An initiator of an exchange * first calls build() to build the request, and processes the response message @@ -97,7 +99,7 @@ struct task_t { /** * Build a request or response message for this task. - * + * * @param message message to add payloads to * @return * - FAILED if a critical error occured @@ -109,7 +111,7 @@ struct task_t { /** * Process a request or response message for this task. - * + * * @param message message to read payloads from * @return * - FAILED if a critical error occured @@ -123,7 +125,7 @@ struct task_t { * Get the type of the task implementation. */ task_type_t (*get_type) (task_t *this); - + /** * Migrate a task to a new IKE_SA. * @@ -138,7 +140,7 @@ struct task_t { * @param ike_sa new IKE_SA this task works for */ void (*migrate) (task_t *this, ike_sa_t *ike_sa); - + /** * Destroys a task_t object. */ diff --git a/src/charon/sa/trap_manager.c b/src/charon/sa/trap_manager.c index 570335eb4..ed758995a 100644 --- a/src/charon/sa/trap_manager.c +++ b/src/charon/sa/trap_manager.c @@ -16,7 +16,7 @@ #include "trap_manager.h" #include <daemon.h> -#include <utils/mutex.h> +#include <threading/rwlock.h> #include <utils/linked_list.h> @@ -27,12 +27,12 @@ typedef struct trap_listener_t trap_listener_t; * listener to track acquires */ struct trap_listener_t { - + /** * Implements listener interface */ listener_t listener; - + /** * points to trap_manager */ @@ -43,22 +43,22 @@ struct trap_listener_t { * Private data of an trap_manager_t object. */ struct private_trap_manager_t { - + /** * Public trap_manager_t interface. */ trap_manager_t public; - + /** * Installed traps, as entry_t */ linked_list_t *traps; - + /** * read write lock for traps list */ rwlock_t *lock; - + /** * listener to track acquiring IKE_SAs */ @@ -102,7 +102,7 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, bool found = FALSE; status_t status; u_int32_t reqid; - + /* check if not already done */ this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); @@ -123,10 +123,10 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, child->get_name(child)); return 0; } - + /* try to resolve addresses */ ike_cfg = peer->get_ike_cfg(peer); - other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), + other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), 0, IKEV2_UDP_PORT); if (!other) { @@ -148,14 +148,14 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, } me->set_port(me, IKEV2_UDP_PORT); } - + /* create and route CHILD_SA */ child_sa = child_sa_create(me, other, child, 0, FALSE); my_ts = child->get_traffic_selectors(child, TRUE, NULL, me); other_ts = child->get_traffic_selectors(child, FALSE, NULL, other); me->destroy(me); other->destroy(other); - + /* while we don't know the finally negotiated protocol (ESP|AH), we * could iterate all proposals for a best guest (TODO). But as we * support ESP only for now, we set here. */ @@ -170,17 +170,17 @@ static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, DBG1(DBG_CFG, "installing trap failed"); return 0; } - + reqid = child_sa->get_reqid(child_sa); entry = malloc_thing(entry_t); entry->child_sa = child_sa; entry->peer_cfg = peer->get_ref(peer); entry->pending = NULL; - + this->lock->write_lock(this->lock); this->traps->insert_last(this->traps, entry); this->lock->unlock(this->lock); - + return reqid; } @@ -191,7 +191,7 @@ static bool uninstall(private_trap_manager_t *this, u_int32_t reqid) { enumerator_t *enumerator; entry_t *entry, *found = NULL; - + this->lock->write_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) @@ -205,13 +205,13 @@ static bool uninstall(private_trap_manager_t *this, u_int32_t reqid) } enumerator->destroy(enumerator); this->lock->unlock(this->lock); - + if (!found) { DBG1(DBG_CFG, "trap %d not found to uninstall", reqid); return FALSE; } - + destroy_entry(found); return TRUE; } @@ -255,7 +255,7 @@ static void acquire(private_trap_manager_t *this, u_int32_t reqid, peer_cfg_t *peer; child_cfg_t *child; ike_sa_t *ike_sa; - + this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) @@ -267,7 +267,7 @@ static void acquire(private_trap_manager_t *this, u_int32_t reqid, } } enumerator->destroy(enumerator); - + if (!found) { DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); @@ -303,37 +303,64 @@ static void acquire(private_trap_manager_t *this, u_int32_t reqid, } /** - * Implementation of listener_t.ike_state_change + * Complete the acquire, if successful or failed */ -static bool ike_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, - ike_sa_state_t state) +static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa) { - private_trap_manager_t *this; enumerator_t *enumerator; entry_t *entry; - - switch (state) - { - case IKE_ESTABLISHED: - case IKE_DESTROYING: - break; - default: - return TRUE; - } - - this = listener->traps; + this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->pending == ike_sa) + if (entry->pending != ike_sa) + { + continue; + } + if (child_sa && child_sa->get_reqid(child_sa) != + entry->child_sa->get_reqid(entry->child_sa)) { - entry->pending = NULL; + continue; } + entry->pending = NULL; } enumerator->destroy(enumerator); this->lock->unlock(this->lock); - return TRUE; +} + +/** + * Implementation of listener_t.ike_state_change + */ +static bool ike_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, + ike_sa_state_t state) +{ + switch (state) + { + case IKE_DESTROYING: + complete(listener->traps, ike_sa, NULL); + return TRUE; + default: + return TRUE; + } +} + +/** + * Implementation of listener_t.child_state_change + */ +static bool child_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *child_sa, child_sa_state_t state) +{ + switch (state) + { + case CHILD_INSTALLED: + case CHILD_DESTROYING: + complete(listener->traps, ike_sa, child_sa); + return TRUE; + default: + return TRUE; + } } /** @@ -354,22 +381,23 @@ static void destroy(private_trap_manager_t *this) trap_manager_t *trap_manager_create() { private_trap_manager_t *this = malloc_thing(private_trap_manager_t); - + this->public.install = (u_int(*)(trap_manager_t*, peer_cfg_t *peer, child_cfg_t *child))install; this->public.uninstall = (bool(*)(trap_manager_t*, u_int32_t id))uninstall; this->public.create_enumerator = (enumerator_t*(*)(trap_manager_t*))create_enumerator; this->public.acquire = (void(*)(trap_manager_t*, u_int32_t reqid, traffic_selector_t *src, traffic_selector_t *dst))acquire; this->public.destroy = (void(*)(trap_manager_t*))destroy; - + this->traps = linked_list_create(); this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - + /* register listener for IKE state changes */ this->listener.traps = this; memset(&this->listener.listener, 0, sizeof(listener_t)); this->listener.listener.ike_state_change = (void*)ike_state_change; + this->listener.listener.child_state_change = (void*)child_state_change; charon->bus->add_listener(charon->bus, &this->listener.listener); - + return &this->public; } diff --git a/src/charon/sa/trap_manager.h b/src/charon/sa/trap_manager.h index cb6907cdc..37b42e2b0 100644 --- a/src/charon/sa/trap_manager.h +++ b/src/charon/sa/trap_manager.h @@ -31,7 +31,7 @@ typedef struct trap_manager_t trap_manager_t; * Manage policies to create SAs from traffic. */ struct trap_manager_t { - + /** * Install a policy as a trap. * @@ -41,7 +41,7 @@ struct trap_manager_t { */ u_int32_t (*install)(trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child); - + /** * Uninstall a trap policy. * @@ -49,14 +49,14 @@ struct trap_manager_t { * @return TRUE if uninstalled successfully */ bool (*uninstall)(trap_manager_t *this, u_int32_t reqid); - + /** * Create an enumerator over all installed traps. * * @return enumerator over (peer_cfg_t, child_sa_t) */ enumerator_t* (*create_enumerator)(trap_manager_t *this); - + /** * Acquire an SA triggered by an installed trap. * @@ -66,7 +66,7 @@ struct trap_manager_t { */ void (*acquire)(trap_manager_t *this, u_int32_t reqid, traffic_selector_t *src, traffic_selector_t *dst); - + /** * Destroy a trap_manager_t. */ @@ -78,4 +78,4 @@ struct trap_manager_t { */ trap_manager_t *trap_manager_create(); -#endif /* TRAP_MANAGER_ @}*/ +#endif /** TRAP_MANAGER_H_ @}*/ |