diff options
Diffstat (limited to 'src/libcharon/sa/ikev1')
-rw-r--r-- | src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/iv_manager.c | 355 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/iv_manager.h | 120 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/keymat_v1.c | 259 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/keymat_v1.h | 33 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/phase1.c | 70 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 29 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 21 |
9 files changed, 573 insertions, 322 deletions
diff --git a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c index 5debeeb37..ddb8c650b 100644 --- a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c +++ b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c @@ -81,7 +81,7 @@ METHOD(authenticator_t, build, status_t, keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa); if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value, this->ike_sa->get_id(this->ike_sa), this->sa_payload, - this->id_payload, &hash)) + this->id_payload, &hash, NULL)) { free(dh.ptr); return FAILED; @@ -118,7 +118,7 @@ METHOD(authenticator_t, process, status_t, keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa); if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh, this->ike_sa->get_id(this->ike_sa), this->sa_payload, - this->id_payload, &hash)) + this->id_payload, &hash, NULL)) { free(dh.ptr); return FAILED; diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c index eee7dd10b..344c1bf5d 100644 --- a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c +++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c @@ -102,7 +102,7 @@ METHOD(authenticator_t, build, status_t, keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa); if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value, this->ike_sa->get_id(this->ike_sa), this->sa_payload, - this->id_payload, &hash)) + this->id_payload, &hash, &scheme)) { private->destroy(private); free(dh.ptr); @@ -163,7 +163,7 @@ METHOD(authenticator_t, process, status_t, keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa); if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh, this->ike_sa->get_id(this->ike_sa), this->sa_payload, - this->id_payload, &hash)) + this->id_payload, &hash, &scheme)) { free(dh.ptr); return FAILED; diff --git a/src/libcharon/sa/ikev1/iv_manager.c b/src/libcharon/sa/ikev1/iv_manager.c new file mode 100644 index 000000000..c9f737ccd --- /dev/null +++ b/src/libcharon/sa/ikev1/iv_manager.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2011-2016 Tobias Brunner + * 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 "iv_manager.h" + +#include <collections/linked_list.h> + +/** + * Max. number of IVs/QMs to track. + */ +#define MAX_EXCHANGES_DEFAULT 3 + +typedef struct private_iv_manager_t private_iv_manager_t; +typedef struct iv_data_t iv_data_t; +typedef struct qm_data_t qm_data_t; + +/** + * Data stored for IVs. + */ +struct iv_data_t { + /** + * message ID + */ + uint32_t mid; + + /** + * current IV + */ + chunk_t iv; + + /** + * last block of encrypted message + */ + chunk_t last_block; +}; + +/** + * Private data of a iv_manager_t object. + */ +struct private_iv_manager_t { + /** + * Implement public interface. + */ + iv_manager_t public; + + /** + * Phase 1 IV. + */ + iv_data_t phase1_iv; + + /** + * Keep track of IVs for exchanges after phase 1. We store only a limited + * number of IVs in an MRU sort of way. Stores iv_data_t objects. + */ + linked_list_t *ivs; + + /** + * Keep track of Nonces during Quick Mode exchanges. Only a limited number + * of QMs are tracked at the same time. Stores qm_data_t objects. + */ + linked_list_t *qms; + + /** + * Max. number of IVs/Quick Modes to track. + */ + int max_exchanges; + + /** + * Hasher used for IV generation. + */ + hasher_t *hasher; + + /* + * Encryption algorithm the block size. + */ + size_t block_size; +}; + +/** + * Data stored for Quick Mode exchanges. + */ +struct qm_data_t { + /** + * Message ID. + */ + uint32_t mid; + + /** + * Ni_b (Nonce from first message). + */ + chunk_t n_i; + + /** + * Nr_b (Nonce from second message). + */ + chunk_t n_r; +}; + +/** + * Destroy an iv_data_t object. + */ +static void iv_data_destroy(iv_data_t *this) +{ + chunk_free(&this->last_block); + chunk_free(&this->iv); + free(this); +} + +/** + * Destroy a qm_data_t object. + */ +static void qm_data_destroy(qm_data_t *this) +{ + chunk_free(&this->n_i); + chunk_free(&this->n_r); + free(this); +} + +/** + * Generate an IV. + */ +static bool generate_iv(private_iv_manager_t *this, iv_data_t *iv) +{ + if (iv->mid == 0 || iv->iv.ptr) + { /* use last block of previous encrypted message */ + chunk_free(&iv->iv); + iv->iv = iv->last_block; + iv->last_block = chunk_empty; + } + else + { + /* initial phase 2 IV = hash(last_phase1_block | mid) */ + uint32_t net;; + chunk_t data; + + net = htonl(iv->mid); + data = chunk_cata("cc", this->phase1_iv.iv, chunk_from_thing(net)); + if (!this->hasher->allocate_hash(this->hasher, data, &iv->iv)) + { + return FALSE; + } + if (iv->iv.len > this->block_size) + { + iv->iv.len = this->block_size; + } + } + DBG4(DBG_IKE, "next IV for MID %u %B", iv->mid, &iv->iv); + return TRUE; +} + +/** + * Try to find an IV for the given message ID, if not found, generate it. + */ +static iv_data_t *lookup_iv(private_iv_manager_t *this, uint32_t mid) +{ + enumerator_t *enumerator; + iv_data_t *iv, *found = NULL; + + if (mid == 0) + { + return &this->phase1_iv; + } + + enumerator = this->ivs->create_enumerator(this->ivs); + while (enumerator->enumerate(enumerator, &iv)) + { + if (iv->mid == mid) + { /* IV gets moved to the front of the list */ + this->ivs->remove_at(this->ivs, enumerator); + found = iv; + break; + } + } + enumerator->destroy(enumerator); + if (!found) + { + INIT(found, + .mid = mid, + ); + if (!generate_iv(this, found)) + { + iv_data_destroy(found); + return NULL; + } + } + this->ivs->insert_first(this->ivs, found); + /* remove least recently used IV if maximum reached */ + if (this->ivs->get_count(this->ivs) > this->max_exchanges && + this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS) + { + iv_data_destroy(iv); + } + return found; +} + +METHOD(iv_manager_t, init_iv_chain, bool, + private_iv_manager_t *this, chunk_t data, hasher_t *hasher, + size_t block_size) +{ + this->hasher = hasher; + this->block_size = block_size; + + if (!this->hasher->allocate_hash(this->hasher, data, &this->phase1_iv.iv)) + { + return FALSE; + } + if (this->phase1_iv.iv.len > this->block_size) + { + this->phase1_iv.iv.len = this->block_size; + } + DBG4(DBG_IKE, "initial IV %B", &this->phase1_iv.iv); + return TRUE; +} + +METHOD(iv_manager_t, get_iv, bool, + private_iv_manager_t *this, uint32_t mid, chunk_t *out) +{ + iv_data_t *iv; + + iv = lookup_iv(this, mid); + if (iv) + { + *out = iv->iv; + return TRUE; + } + return FALSE; +} + +METHOD(iv_manager_t, update_iv, bool, + private_iv_manager_t *this, uint32_t mid, chunk_t last_block) +{ + iv_data_t *iv = lookup_iv(this, mid); + if (iv) + { /* update last block */ + chunk_free(&iv->last_block); + iv->last_block = chunk_clone(last_block); + return TRUE; + } + return FALSE; +} + +METHOD(iv_manager_t, confirm_iv, bool, + private_iv_manager_t *this, uint32_t mid) +{ + iv_data_t *iv = lookup_iv(this, mid); + if (iv) + { + return generate_iv(this, iv); + } + return FALSE; +} + +METHOD(iv_manager_t, lookup_quick_mode, void, + private_iv_manager_t *this, uint32_t mid, chunk_t **n_i, chunk_t **n_r) +{ + enumerator_t *enumerator; + qm_data_t *qm, *found = NULL; + + enumerator = this->qms->create_enumerator(this->qms); + while (enumerator->enumerate(enumerator, &qm)) + { + if (qm->mid == mid) + { /* state gets moved to the front of the list */ + this->qms->remove_at(this->qms, enumerator); + found = qm; + break; + } + } + enumerator->destroy(enumerator); + if (!found) + { + INIT(found, + .mid = mid, + ); + } + + *n_i = &found->n_i; + *n_r = &found->n_r; + + this->qms->insert_first(this->qms, found); + /* remove least recently used state if maximum reached */ + if (this->qms->get_count(this->qms) > this->max_exchanges && + this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS) + { + qm_data_destroy(qm); + } +} + +METHOD(iv_manager_t, remove_quick_mode, void, + private_iv_manager_t *this, uint32_t mid) +{ + enumerator_t *enumerator; + qm_data_t *qm; + + enumerator = this->qms->create_enumerator(this->qms); + while (enumerator->enumerate(enumerator, &qm)) + { + if (qm->mid == mid) + { + this->qms->remove_at(this->qms, enumerator); + qm_data_destroy(qm); + break; + } + } + enumerator->destroy(enumerator); +} + +METHOD(iv_manager_t, destroy, void, + private_iv_manager_t *this) +{ + chunk_free(&this->phase1_iv.iv); + chunk_free(&this->phase1_iv.last_block); + this->ivs->destroy_function(this->ivs, (void*)iv_data_destroy); + this->qms->destroy_function(this->qms, (void*)qm_data_destroy); + free(this); +} + +iv_manager_t *iv_manager_create(int max_exchanges) +{ + private_iv_manager_t *this; + + INIT(this, + .public = { + .init_iv_chain = _init_iv_chain, + .get_iv = _get_iv, + .update_iv = _update_iv, + .confirm_iv = _confirm_iv, + .lookup_quick_mode = _lookup_quick_mode, + .remove_quick_mode = _remove_quick_mode, + .destroy = _destroy, + }, + .ivs = linked_list_create(), + .qms = linked_list_create(), + .max_exchanges = max_exchanges, + ); + + if (!this->max_exchanges) + { + this->max_exchanges = lib->settings->get_int(lib->settings, + "%s.max_ikev1_exchanges", MAX_EXCHANGES_DEFAULT, lib->ns); + } + return &this->public; +} diff --git a/src/libcharon/sa/ikev1/iv_manager.h b/src/libcharon/sa/ikev1/iv_manager.h new file mode 100644 index 000000000..c5273fed9 --- /dev/null +++ b/src/libcharon/sa/ikev1/iv_manager.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011-2016 Tobias Brunner + * 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 iv_manager iv_manager + * @{ @ingroup ikev1 + */ + +#ifndef IV_MANAGER_H_ +#define IV_MANAGER_H_ + +#include <utils/chunk.h> +#include <crypto/hashers/hasher.h> + +typedef struct iv_manager_t iv_manager_t; + +/** + * IV and QM managing instance for IKEv1. Keeps track of phase 2 exchanges + * and IV, as well as the phase 1 IV. + */ +struct iv_manager_t { + + /** + * Set the value of the first phase1 IV. + * + * @param data input to calc initial IV from (g^xi | g^xr) + * @param hasher hasher to be used for IV calculation + * (shared with keymat, must not be destroyed here) + * @param block_size cipher block size of aead + * @return TRUE for success, FALSE otherwise + */ + bool (*init_iv_chain)(iv_manager_t *this, chunk_t data, hasher_t *hasher, + size_t block_size); + + /** + * Returns the IV for a message with the given message ID. + * + * The return chunk contains internal data and is valid until the next + * get_iv/udpate_iv/confirm_iv() call. + * + * @param mid message ID + * @param iv chunk receiving IV, internal data + * @return TRUE if IV allocated successfully + */ + bool (*get_iv)(iv_manager_t *this, uint32_t mid, chunk_t *iv); + + /** + * Updates the IV for the next message with the given message ID. + * + * A call of confirm_iv() is required in order to actually make the IV + * available. This is needed for the inbound case where we store the last + * block of the encrypted message but want to update the IV only after + * verification of the decrypted message. + * + * @param mid message ID + * @param last_block last block of encrypted message (gets cloned) + * @return TRUE if IV updated successfully + */ + bool (*update_iv)(iv_manager_t *this, uint32_t mid, chunk_t last_block); + + /** + * Confirms the updated IV for the given message ID. + * + * To actually make the new IV available via get_iv() this method has to + * be called after update_iv(). + * + * @param mid message ID + * @return TRUE if IV confirmed successfully + */ + bool (*confirm_iv)(iv_manager_t *this, uint32_t mid); + + /** + * Try to find a QM for the given message ID, if not found, generate it. + * The nonces shall be assigned by the caller if they are not set yet. + * + * @param mid message ID + * @param n_i chunk pointer to contain Ni_b (Nonce from first + * message) + * @param n_r chunk pointer to contain Nr_b (Nonce from second + * message) + */ + void (*lookup_quick_mode)(iv_manager_t *this, uint32_t mid, chunk_t **n_i, + chunk_t **n_r); + + /** + * Remove the QM for the given message ID. + * + * @param mid message ID + */ + void (*remove_quick_mode)(iv_manager_t *this, uint32_t mid); + + /* + * Destroy a iv_manager_t. + */ + void (*destroy)(iv_manager_t *this); +}; + +/** + * Create an IV and QM manager which is able to store up to max_exchanges + * initialization vectors and quick modes. + * + * @param max_exchanges maximum number of IVs and QMs to be stored, set + * to 0 to use default (3, or as configured) + * @return IV and QM manager instance + */ +iv_manager_t *iv_manager_create(int max_exchanges); + +#endif /** IV_MANAGER_H_ @}*/ diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index d1d4cbd9b..673a7a131 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -16,30 +16,13 @@ #include "keymat_v1.h" #include <daemon.h> +#include <sa/ikev1/iv_manager.h> #include <encoding/generator.h> #include <encoding/payloads/nonce_payload.h> -#include <collections/linked_list.h> typedef struct private_keymat_v1_t private_keymat_v1_t; /** - * Max. number of IVs/QMs to track. - */ -#define MAX_EXCHANGES_DEFAULT 3 - -/** - * Data stored for IVs - */ -typedef struct { - /** message ID */ - uint32_t mid; - /** current IV */ - chunk_t iv; - /** last block of encrypted message */ - chunk_t last_block; -} iv_data_t; - -/** * Private data of an keymat_t object. */ struct private_keymat_v1_t { @@ -85,61 +68,11 @@ struct private_keymat_v1_t { chunk_t skeyid_a; /** - * Phase 1 IV - */ - iv_data_t phase1_iv; - - /** - * Keep track of IVs for exchanges after phase 1. We store only a limited - * number of IVs in an MRU sort of way. Stores iv_data_t objects. - */ - linked_list_t *ivs; - - /** - * Keep track of Nonces during Quick Mode exchanges. Only a limited number - * of QMs are tracked at the same time. Stores qm_data_t objects. + * IV and QM manager */ - linked_list_t *qms; - - /** - * Max. number of IVs/Quick Modes to track. - */ - int max_exchanges; + iv_manager_t *iv_manager; }; - -/** - * Destroy an iv_data_t object. - */ -static void iv_data_destroy(iv_data_t *this) -{ - chunk_free(&this->last_block); - chunk_free(&this->iv); - free(this); -} - -/** - * Data stored for Quick Mode exchanges - */ -typedef struct { - /** message ID */ - uint32_t mid; - /** Ni_b (Nonce from first message) */ - chunk_t n_i; - /** Nr_b (Nonce from second message) */ - chunk_t n_r; -} qm_data_t; - -/** - * Destroy a qm_data_t object. - */ -static void qm_data_destroy(qm_data_t *this) -{ - chunk_free(&this->n_i); - chunk_free(&this->n_r); - free(this); -} - /** * Constants used in key derivation. */ @@ -567,17 +500,8 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, /* initial IV = hash(g^xi | g^xr) */ data = chunk_cata("cc", g_xi, g_xr); chunk_free(&dh_me); - if (!this->hasher->allocate_hash(this->hasher, data, &this->phase1_iv.iv)) - { - return FALSE; - } - if (this->phase1_iv.iv.len > this->aead->get_block_size(this->aead)) - { - this->phase1_iv.iv.len = this->aead->get_block_size(this->aead); - } - DBG4(DBG_IKE, "initial IV %B", &this->phase1_iv.iv); - - return TRUE; + return this->iv_manager->init_iv_chain(this->iv_manager, data, this->hasher, + this->aead->get_block_size(this->aead)); } METHOD(keymat_v1_t, derive_child_keys, bool, @@ -748,7 +672,8 @@ METHOD(keymat_v1_t, get_hasher, hasher_t*, METHOD(keymat_v1_t, get_hash, bool, private_keymat_v1_t *this, bool initiator, chunk_t dh, chunk_t dh_other, - ike_sa_id_t *ike_sa_id, chunk_t sa_i, chunk_t id, chunk_t *hash) + ike_sa_id_t *ike_sa_id, chunk_t sa_i, chunk_t id, chunk_t *hash, + signature_scheme_t *scheme) { chunk_t data; uint64_t spi, spi_other; @@ -843,47 +768,11 @@ static chunk_t get_message_data(message_t *message, generator_t *generator) return generator->get_chunk(generator, &lenpos); } -/** - * Try to find data about a Quick Mode with the given message ID, - * if none is found, state is generated. - */ -static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, uint32_t mid) -{ - enumerator_t *enumerator; - qm_data_t *qm, *found = NULL; - - enumerator = this->qms->create_enumerator(this->qms); - while (enumerator->enumerate(enumerator, &qm)) - { - if (qm->mid == mid) - { /* state gets moved to the front of the list */ - this->qms->remove_at(this->qms, enumerator); - found = qm; - break; - } - } - enumerator->destroy(enumerator); - if (!found) - { - INIT(found, - .mid = mid, - ); - } - this->qms->insert_first(this->qms, found); - /* remove least recently used state if maximum reached */ - if (this->qms->get_count(this->qms) > this->max_exchanges && - this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS) - { - qm_data_destroy(qm); - } - return found; -} - METHOD(keymat_v1_t, get_hash_phase2, bool, private_keymat_v1_t *this, message_t *message, chunk_t *hash) { uint32_t mid, mid_n; - chunk_t data = chunk_empty; + chunk_t data = chunk_empty, *n_i, *n_r; bool add_message = TRUE; char *name = "Hash"; @@ -907,34 +796,34 @@ METHOD(keymat_v1_t, get_hash_phase2, bool, { case QUICK_MODE: { - qm_data_t *qm = lookup_quick_mode(this, mid); - if (!qm->n_i.ptr) + this->iv_manager->lookup_quick_mode(this->iv_manager, mid, &n_i, + &n_r); + if (!n_i->ptr) { /* Hash(1) = prf(SKEYID_a, M-ID | Message after HASH payload) */ name = "Hash(1)"; - if (!get_nonce(message, &qm->n_i)) + if (!get_nonce(message, n_i)) { return FALSE; } data = chunk_from_thing(mid_n); } - else if (!qm->n_r.ptr) + else if (!n_r->ptr) { /* Hash(2) = prf(SKEYID_a, M-ID | Ni_b | Message after HASH) */ name = "Hash(2)"; - if (!get_nonce(message, &qm->n_r)) + if (!get_nonce(message, n_r)) { return FALSE; } - data = chunk_cata("cc", chunk_from_thing(mid_n), qm->n_i); + data = chunk_cata("cc", chunk_from_thing(mid_n), *n_i); } else { /* Hash(3) = prf(SKEYID_a, 0 | M-ID | Ni_b | Nr_b) */ name = "Hash(3)"; data = chunk_cata("cccc", octet_0, chunk_from_thing(mid_n), - qm->n_i, qm->n_r); + *n_i, *n_r); add_message = FALSE; /* we don't need the state anymore */ - this->qms->remove(this->qms, qm, NULL); - qm_data_destroy(qm); + this->iv_manager->remove_quick_mode(this->iv_manager, mid); } break; } @@ -976,119 +865,22 @@ METHOD(keymat_v1_t, get_hash_phase2, bool, return TRUE; } -/** - * Generate an IV - */ -static bool generate_iv(private_keymat_v1_t *this, iv_data_t *iv) -{ - if (iv->mid == 0 || iv->iv.ptr) - { /* use last block of previous encrypted message */ - chunk_free(&iv->iv); - iv->iv = iv->last_block; - iv->last_block = chunk_empty; - } - else - { - /* initial phase 2 IV = hash(last_phase1_block | mid) */ - uint32_t net;; - chunk_t data; - - net = htonl(iv->mid); - data = chunk_cata("cc", this->phase1_iv.iv, chunk_from_thing(net)); - if (!this->hasher->allocate_hash(this->hasher, data, &iv->iv)) - { - return FALSE; - } - if (iv->iv.len > this->aead->get_block_size(this->aead)) - { - iv->iv.len = this->aead->get_block_size(this->aead); - } - } - DBG4(DBG_IKE, "next IV for MID %u %B", iv->mid, &iv->iv); - return TRUE; -} - -/** - * Try to find an IV for the given message ID, if not found, generate it. - */ -static iv_data_t *lookup_iv(private_keymat_v1_t *this, uint32_t mid) -{ - enumerator_t *enumerator; - iv_data_t *iv, *found = NULL; - - if (mid == 0) - { - return &this->phase1_iv; - } - - enumerator = this->ivs->create_enumerator(this->ivs); - while (enumerator->enumerate(enumerator, &iv)) - { - if (iv->mid == mid) - { /* IV gets moved to the front of the list */ - this->ivs->remove_at(this->ivs, enumerator); - found = iv; - break; - } - } - enumerator->destroy(enumerator); - if (!found) - { - INIT(found, - .mid = mid, - ); - if (!generate_iv(this, found)) - { - iv_data_destroy(found); - return NULL; - } - } - this->ivs->insert_first(this->ivs, found); - /* remove least recently used IV if maximum reached */ - if (this->ivs->get_count(this->ivs) > this->max_exchanges && - this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS) - { - iv_data_destroy(iv); - } - return found; -} - METHOD(keymat_v1_t, get_iv, bool, private_keymat_v1_t *this, uint32_t mid, chunk_t *out) { - iv_data_t *iv; - - iv = lookup_iv(this, mid); - if (iv) - { - *out = iv->iv; - return TRUE; - } - return FALSE; + return this->iv_manager->get_iv(this->iv_manager, mid, out); } METHOD(keymat_v1_t, update_iv, bool, private_keymat_v1_t *this, uint32_t mid, chunk_t last_block) { - iv_data_t *iv = lookup_iv(this, mid); - if (iv) - { /* update last block */ - chunk_free(&iv->last_block); - iv->last_block = chunk_clone(last_block); - return TRUE; - } - return FALSE; + return this->iv_manager->update_iv(this->iv_manager, mid, last_block); } METHOD(keymat_v1_t, confirm_iv, bool, private_keymat_v1_t *this, uint32_t mid) { - iv_data_t *iv = lookup_iv(this, mid); - if (iv) - { - return generate_iv(this, iv); - } - return FALSE; + return this->iv_manager->confirm_iv(this->iv_manager, mid); } METHOD(keymat_t, get_version, ike_version_t, @@ -1124,10 +916,7 @@ METHOD(keymat_t, destroy, void, DESTROY_IF(this->hasher); chunk_clear(&this->skeyid_d); chunk_clear(&this->skeyid_a); - chunk_free(&this->phase1_iv.iv); - chunk_free(&this->phase1_iv.last_block); - this->ivs->destroy_function(this->ivs, (void*)iv_data_destroy); - this->qms->destroy_function(this->qms, (void*)qm_data_destroy); + this->iv_manager->destroy(this->iv_manager); free(this); } @@ -1157,12 +946,8 @@ keymat_v1_t *keymat_v1_create(bool initiator) .update_iv = _update_iv, .confirm_iv = _confirm_iv, }, - .ivs = linked_list_create(), - .qms = linked_list_create(), .initiator = initiator, - .max_exchanges = lib->settings->get_int(lib->settings, - "%s.max_ikev1_exchanges", MAX_EXCHANGES_DEFAULT, lib->ns), + .iv_manager = iv_manager_create(0), ); - return &this->public; } diff --git a/src/libcharon/sa/ikev1/keymat_v1.h b/src/libcharon/sa/ikev1/keymat_v1.h index 46eeea8b6..ada5bdb04 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.h +++ b/src/libcharon/sa/ikev1/keymat_v1.h @@ -102,11 +102,14 @@ struct keymat_v1_t { * @param sa_i encoded SA payload of initiator * @param id encoded IDii payload for HASH_I (IDir for HASH_R) * @param hash chunk receiving allocated HASH data + * @param scheme pointer to signature scheme in case it needs to be + * modified by the keymat implementation * @return TRUE if hash allocated successfully */ bool (*get_hash)(keymat_v1_t *this, bool initiator, chunk_t dh, chunk_t dh_other, ike_sa_id_t *ike_sa_id, - chunk_t sa_i, chunk_t id, chunk_t *hash); + chunk_t sa_i, chunk_t id, chunk_t *hash, + signature_scheme_t *scheme); /** * Get HASH data for integrity/authentication in Phase 2 exchanges. @@ -118,39 +121,17 @@ struct keymat_v1_t { bool (*get_hash_phase2)(keymat_v1_t *this, message_t *message, chunk_t *hash); /** - * Returns the IV for a message with the given message ID. - * - * The return chunk contains internal data and is valid until the next - * get_iv/udpate_iv/confirm_iv call. - * - * @param mid message ID - * @param iv chunk receiving IV, internal data - * @return TRUE if IV allocated successfully + * @see iv_manager_t.get_iv */ bool (*get_iv)(keymat_v1_t *this, uint32_t mid, chunk_t *iv); /** - * Updates the IV for the next message with the given message ID. - * - * A call of confirm_iv() is required in order to actually make the IV - * available. This is needed for the inbound case where we store the last - * block of the encrypted message but want to update the IV only after - * verification of the decrypted message. - * - * @param mid message ID - * @param last_block last block of encrypted message (gets cloned) - * @return TRUE if IV updated successfully + * @see iv_manager_t.update_iv */ bool (*update_iv)(keymat_v1_t *this, uint32_t mid, chunk_t last_block); /** - * Confirms the updated IV for the given message ID. - * - * To actually make the new IV available via get_iv this method has to - * be called after update_iv. - * - * @param mid message ID - * @return TRUE if IV confirmed successfully + * @see iv_manager_t.confirm_iv */ bool (*confirm_iv)(keymat_v1_t *this, uint32_t mid); }; diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index c968b2a9c..adce59f7e 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -113,22 +113,8 @@ static shared_key_t *lookup_shared_key(private_phase1_t *this, auth_cfg_t *my_auth, *other_auth; enumerator_t *enumerator; - /* try to get a PSK for IP addresses */ me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); - my_id = identification_create_from_sockaddr(me->get_sockaddr(me)); - other_id = identification_create_from_sockaddr(other->get_sockaddr(other)); - if (my_id && other_id) - { - shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, - my_id, other_id); - } - DESTROY_IF(my_id); - DESTROY_IF(other_id); - if (shared_key) - { - return shared_key; - } if (peer_cfg) { /* as initiator or aggressive responder, use identities */ @@ -156,39 +142,51 @@ static shared_key_t *lookup_shared_key(private_phase1_t *this, } } } - return shared_key; } - /* as responder, we try to find a config by IP */ - enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends, - me, other, NULL, NULL, IKEV1); - while (enumerator->enumerate(enumerator, &peer_cfg)) - { - my_auth = get_auth_cfg(peer_cfg, TRUE); - other_auth = get_auth_cfg(peer_cfg, FALSE); - if (my_auth && other_auth) + else + { /* as responder, we try to find a config by IP addresses and use the + * configured identities to find the PSK */ + enumerator = charon->backends->create_peer_cfg_enumerator( + charon->backends, me, other, NULL, NULL, IKEV1); + while (enumerator->enumerate(enumerator, &peer_cfg)) { - my_id = my_auth->get(my_auth, AUTH_RULE_IDENTITY); - other_id = other_auth->get(other_auth, AUTH_RULE_IDENTITY); - if (my_id) + my_auth = get_auth_cfg(peer_cfg, TRUE); + other_auth = get_auth_cfg(peer_cfg, FALSE); + if (my_auth && other_auth) { - shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, - my_id, other_id); - if (shared_key) - { - break; - } - else + my_id = my_auth->get(my_auth, AUTH_RULE_IDENTITY); + other_id = other_auth->get(other_auth, AUTH_RULE_IDENTITY); + if (my_id) { + shared_key = lib->credmgr->get_shared(lib->credmgr, + SHARED_IKE, my_id, other_id); + if (shared_key) + { + break; + } DBG1(DBG_IKE, "no shared key found for '%Y'[%H] - '%Y'[%H]", my_id, me, other_id, other); } } } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); if (!shared_key) - { - DBG1(DBG_IKE, "no shared key found for %H - %H", me, other); + { /* try to get a PSK for IP addresses */ + my_id = identification_create_from_sockaddr(me->get_sockaddr(me)); + other_id = identification_create_from_sockaddr( + other->get_sockaddr(other)); + if (my_id && other_id) + { + shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, + my_id, other_id); + } + DESTROY_IF(my_id); + DESTROY_IF(other_id); + if (!shared_key) + { + DBG1(DBG_IKE, "no shared key found for %H - %H", me, other); + } } return shared_key; } diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 3b0c1cfd1..1da17ee50 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -367,7 +367,7 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr, send_packets(this, packets); lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*) retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t); - return NEED_MORE; + return SUCCESS; } METHOD(task_manager_t, retransmit, status_t, @@ -380,10 +380,9 @@ METHOD(task_manager_t, retransmit, status_t, { status = retransmit_packet(this, seqnr, this->initiating.mid, this->initiating.retransmitted, this->initiating.packets); - if (status == NEED_MORE) + if (status == SUCCESS) { this->initiating.retransmitted++; - status = SUCCESS; } } if (seqnr == this->responding.seqnr && @@ -391,10 +390,9 @@ METHOD(task_manager_t, retransmit, status_t, { status = retransmit_packet(this, seqnr, this->responding.mid, this->responding.retransmitted, this->responding.packets); - if (status == NEED_MORE) + if (status == SUCCESS) { this->responding.retransmitted++; - status = SUCCESS; } } return status; @@ -554,6 +552,12 @@ METHOD(task_manager_t, initiate, status_t, new_mid = TRUE; break; } + if (activate_task(this, TASK_ISAKMP_DPD)) + { + exchange = INFORMATIONAL_V1; + new_mid = TRUE; + break; + } break; default: break; @@ -685,13 +689,9 @@ METHOD(task_manager_t, initiate, status_t, message->destroy(message); return retransmit(this, this->initiating.seqnr); } - if (keep) - { /* keep the packet for retransmission, the responder might request it */ - send_packets(this, this->initiating.packets); - } - else + send_packets(this, this->initiating.packets); + if (!keep) { - send_packets(this, this->initiating.packets); clear_packets(this->initiating.packets); } message->destroy(message); @@ -1902,6 +1902,12 @@ METHOD(task_manager_t, incr_mid, void, { } +METHOD(task_manager_t, get_mid, uint32_t, + private_task_manager_t *this, bool initiate) +{ + return initiate ? this->initiating.mid : this->responding.mid; +} + METHOD(task_manager_t, reset, void, private_task_manager_t *this, uint32_t initiate, uint32_t respond) { @@ -2005,6 +2011,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .initiate = _initiate, .retransmit = _retransmit, .incr_mid = _incr_mid, + .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, .adopt_child_tasks = _adopt_child_tasks, diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 6b896416a..bbb885850 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -703,25 +703,30 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message) { identification_t *id; id_payload_t *nat_oa; - host_t *src, *dst; + host_t *init, *resp; payload_type_t nat_oa_payload_type; - src = message->get_source(message); - dst = message->get_destination(message); - - src = this->initiator ? src : dst; - dst = this->initiator ? dst : src; + if (this->initiator) + { + init = message->get_source(message); + resp = message->get_destination(message); + } + else + { + init = message->get_destination(message); + resp = message->get_source(message); + } nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa); /* first NAT-OA is the initiator's address */ - id = identification_create_from_sockaddr(src->get_sockaddr(src)); + id = identification_create_from_sockaddr(init->get_sockaddr(init)); nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); message->add_payload(message, (payload_t*)nat_oa); id->destroy(id); /* second NAT-OA is that of the responder */ - id = identification_create_from_sockaddr(dst->get_sockaddr(dst)); + id = identification_create_from_sockaddr(resp->get_sockaddr(resp)); nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id); message->add_payload(message, (payload_t*)nat_oa); id->destroy(id); |