diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2017-04-01 16:26:44 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2017-04-01 16:26:44 +0200 |
commit | 05ddd767992d68bb38c7f16ece142e8c2e9ae016 (patch) | |
tree | 302c618be306d4ed3c7f9fc58a1f6aaad4dd252f /src/libcharon/sa | |
parent | 25663e04c3ab01ef8dc9f906608282319cfea2db (diff) | |
download | vyos-strongswan-05ddd767992d68bb38c7f16ece142e8c2e9ae016.tar.gz vyos-strongswan-05ddd767992d68bb38c7f16ece142e8c2e9ae016.zip |
New upstream version 5.5.2
Diffstat (limited to 'src/libcharon/sa')
26 files changed, 1238 insertions, 384 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index e4364de12..b9dd59b07 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Coypright (C) 2016 Andreas Steffen + * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2006-2016 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger @@ -479,7 +479,6 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) .dst = this->my_addr, .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), - .mark = this->mark_in, }; kernel_ipsec_query_sa_t query = {}; @@ -495,9 +494,11 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { this->my_usetime = time; } - return SUCCESS; } - return FAILED; + else + { + status = FAILED; + } } } } @@ -526,9 +527,11 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { this->other_usetime = time; } - return SUCCESS; } - return FAILED; + else + { + status = FAILED; + } } } } @@ -797,7 +800,7 @@ METHOD(child_sa_t, install, status_t, .dst = dst, .spi = spi, .proto = proto_ike2ip(this->protocol), - .mark = inbound ? this->mark_in : this->mark_out, + .mark = inbound ? (mark_t){} : this->mark_out, }; sa = (kernel_ipsec_add_sa_t){ .reqid = this->reqid, @@ -1144,7 +1147,6 @@ METHOD(child_sa_t, update, status_t, .dst = this->my_addr, .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), - .mark = this->mark_in, }; kernel_ipsec_update_sa_t sa = { .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, @@ -1319,7 +1321,6 @@ METHOD(child_sa_t, destroy, void, .dst = this->my_addr, .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), - .mark = this->mark_in, }; kernel_ipsec_del_sa_t sa = { .cpi = this->my_cpi, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 7b87918d3..76e10691f 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -617,6 +617,12 @@ METHOD(ike_sa_t, set_message_id, void, } } +METHOD(ike_sa_t, get_message_id, uint32_t, + private_ike_sa_t *this, bool initiate) +{ + return this->task_manager->get_mid(this->task_manager, initiate); +} + METHOD(ike_sa_t, send_keepalive, void, private_ike_sa_t *this, bool scheduled) { @@ -756,6 +762,10 @@ METHOD(ike_sa_t, send_dpd, status_t, { return INVALID_STATE; } + if (this->version == IKEV1 && this->state == IKE_REKEYING) + { /* don't send DPDs for rekeyed IKEv1 SAs */ + return SUCCESS; + } delay = this->peer_cfg->get_dpd(this->peer_cfg); if (this->task_manager->busy(this->task_manager)) { @@ -2436,6 +2446,25 @@ static bool is_current_path_valid(private_ike_sa_t *this) { bool valid = FALSE; host_t *src; + + if (supports_extension(this, EXT_MOBIKE) && + lib->settings->get_bool(lib->settings, + "%s.prefer_best_path", FALSE, lib->ns)) + { + /* check if the current path is the best path; migrate otherwise */ + src = charon->kernel->get_source_addr(charon->kernel, this->other_host, + NULL); + if (src) + { + valid = src->ip_equals(src, this->my_host); + src->destroy(src); + } + if (!valid) + { + DBG1(DBG_IKE, "old path is not preferred anymore"); + } + return valid; + } src = charon->kernel->get_source_addr(charon->kernel, this->other_host, this->my_host); if (src) @@ -2446,6 +2475,10 @@ static bool is_current_path_valid(private_ike_sa_t *this) } src->destroy(src); } + if (!valid) + { + DBG1(DBG_IKE, "old path is not available anymore, try to find another"); + } return valid; } @@ -2472,7 +2505,6 @@ static bool is_any_path_valid(private_ike_sa_t *this) break; } - DBG1(DBG_IKE, "old path is not available anymore, try to find another"); enumerator = create_peer_address_enumerator(this); while (enumerator->enumerate(enumerator, &addr)) { @@ -2511,6 +2543,16 @@ METHOD(ike_sa_t, roam, status_t, break; } + /* ignore roam events if MOBIKE is not supported/enabled and the local + * address is statically configured */ + if (this->version == IKEV2 && !supports_extension(this, EXT_MOBIKE) && + ike_cfg_has_address(this->ike_cfg, this->my_host, TRUE)) + { + DBG2(DBG_IKE, "keeping statically configured path %H - %H", + this->my_host, this->other_host); + return SUCCESS; + } + /* keep existing path if possible */ if (is_current_path_valid(this)) { @@ -2885,6 +2927,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .get_other_host = _get_other_host, .set_other_host = _set_other_host, .set_message_id = _set_message_id, + .get_message_id = _get_message_id, .float_ports = _float_ports, .update_hosts = _update_hosts, .get_my_id = _get_my_id, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 6f5040d7c..c8ba2fd2a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -151,6 +151,11 @@ enum ike_extension_t { * IKEv2 Redirect Mechanism, RFC 5685 */ EXT_IKE_REDIRECTION = (1<<13), + + /** + * IKEv2 Message ID sync, RFC 6311 + */ + EXT_IKE_MESSAGE_ID_SYNC = (1<<14), }; /** @@ -554,7 +559,7 @@ struct ike_sa_t { void (*set_proposal)(ike_sa_t *this, proposal_t *proposal); /** - * Set the message id of the IKE_SA. + * Set the message ID of the IKE_SA. * * The IKE_SA stores two message IDs, one for initiating exchanges (send) * and one to respond to exchanges (expect). @@ -565,6 +570,17 @@ struct ike_sa_t { void (*set_message_id)(ike_sa_t *this, bool initiate, uint32_t mid); /** + * Get the message ID of the IKE_SA. + * + * The IKE_SA stores two message IDs, one for initiating exchanges (send) + * and one to respond to exchanges (expect). + * + * @param initiate TRUE to get message ID for initiating + * @return current message + */ + uint32_t (*get_message_id)(ike_sa_t *this, bool initiate); + + /** * Add an additional address for the peer. * * In MOBIKE, a peer may transmit additional addresses where it is diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index ce44207c4..6bd49a086 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -2303,7 +2303,6 @@ ike_sa_manager_t *ike_sa_manager_create() 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 */ @@ -2312,7 +2311,6 @@ ike_sa_manager_t *ike_sa_manager_create() for (i = 0; i < this->segment_count; i++) { 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 */ @@ -2321,7 +2319,6 @@ ike_sa_manager_t *ike_sa_manager_create() for (i = 0; i < this->segment_count; i++) { this->connected_peers_segments[i].lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - this->connected_peers_segments[i].count = 0; } /* and again for the table of hashes of seen initial IKE messages */ @@ -2330,7 +2327,6 @@ ike_sa_manager_t *ike_sa_manager_create() for (i = 0; i < this->segment_count; i++) { this->init_hashes_segments[i].mutex = mutex_create(MUTEX_TYPE_RECURSIVE); - this->init_hashes_segments[i].count = 0; } this->reuse_ikesa = lib->settings->get_bool(lib->settings, 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); diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 592f49770..19ea72d0b 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -217,7 +217,8 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, } if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, - this->nonce, id, this->reserved, &octets)) + this->nonce, id, this->reserved, &octets, + schemes)) { enumerator = array_create_enumerator(schemes); while (enumerator->enumerate(enumerator, &schemep)) @@ -247,6 +248,32 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, } /** + * Get the auth octets and the signature scheme (in case it is changed by the + * keymat). + */ +static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, + bool verify, identification_t *id, + chunk_t *octets, signature_scheme_t *scheme) +{ + keymat_v2_t *keymat; + array_t *schemes; + bool success = FALSE; + + schemes = array_create(sizeof(signature_scheme_t), 0); + array_insert(schemes, ARRAY_TAIL, scheme); + + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); + if (keymat->get_auth_octets(keymat, verify, this->ike_sa_init, this->nonce, + id, this->reserved, octets, schemes) && + array_get(schemes, 0, &scheme)) + { + success = TRUE; + } + array_destroy(schemes); + return success; +} + +/** * Create a classic IKEv2 signature */ static status_t sign_classic(private_pubkey_authenticator_t *this, @@ -255,7 +282,6 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, chunk_t *auth_data) { signature_scheme_t scheme; - keymat_v2_t *keymat; chunk_t octets = chunk_empty; status_t status = FAILED; @@ -293,9 +319,7 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, return FAILED; } - keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); - if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, - this->nonce, id, this->reserved, &octets) && + if (get_auth_octets_scheme(this, FALSE, id, &octets, &scheme) && private->sign(private, scheme, octets, auth_data)) { status = SUCCESS; @@ -363,7 +387,6 @@ METHOD(authenticator_t, process, status_t, key_type_t key_type = KEY_ECDSA; signature_scheme_t scheme; status_t status = NOT_FOUND; - keymat_v2_t *keymat; const char *reason = "unsupported"; bool online; @@ -402,9 +425,7 @@ METHOD(authenticator_t, process, status_t, return INVALID_ARG; } id = this->ike_sa->get_other_id(this->ike_sa); - keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); - if (!keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init, - this->nonce, id, this->reserved, &octets)) + if (!get_auth_octets_scheme(this, TRUE, id, &octets, &scheme)) { return FAILED; } diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 58efdbabe..70dacd1dc 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -629,7 +629,8 @@ METHOD(keymat_t, get_aead, aead_t*, METHOD(keymat_v2_t, get_auth_octets, bool, private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets) + chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets, + array_t *schemes) { chunk_t chunk, idx; chunk_t skp; @@ -669,7 +670,8 @@ METHOD(keymat_v2_t, get_psk_sig, bool, { /* EAP uses SK_p if no MSK has been established */ secret = verify ? this->skp_verify : this->skp_build; } - if (!get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved, &octets)) + if (!get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved, + &octets, NULL)) { return FALSE; } diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h index 927b62b03..36bf149fe 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.h +++ b/src/libcharon/sa/ikev2/keymat_v2.h @@ -22,6 +22,7 @@ #define KEYMAT_V2_H_ #include <sa/keymat.h> +#include <collections/array.h> typedef struct keymat_v2_t keymat_v2_t; @@ -100,11 +101,14 @@ struct keymat_v2_t { * @param id identity * @param reserved reserved bytes of id_payload * @param octests chunk receiving allocated auth octets + * @param schemes array containing signature schemes in case they + * need to be modified by the keymat implementation * @return TRUE if octets created successfully */ bool (*get_auth_octets)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, identification_t *id, - char reserved[3], chunk_t *octets); + char reserved[3], chunk_t *octets, + array_t *schemes); /** * Build the shared secret signature used for PSK and EAP authentication. * diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 60a262ffc..e4a16faf0 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -34,6 +34,7 @@ #include <sa/ikev2/tasks/ike_delete.h> #include <sa/ikev2/tasks/ike_config.h> #include <sa/ikev2/tasks/ike_dpd.h> +#include <sa/ikev2/tasks/ike_mid_sync.h> #include <sa/ikev2/tasks/ike_vendor.h> #include <sa/ikev2/tasks/ike_verify_peer_cert.h> #include <sa/ikev2/tasks/child_create.h> @@ -817,7 +818,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) task_t *task; message_t *message; host_t *me, *other; - bool delete = FALSE, hook = FALSE; + bool delete = FALSE, hook = FALSE, mid_sync = FALSE; ike_sa_id_t *id = NULL; uint64_t responder_spi = 0; bool result; @@ -836,6 +837,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { + if (task->get_type(task) == TASK_IKE_MID_SYNC) + { + mid_sync = TRUE; + } switch (task->build(task, message)) { case SUCCESS: @@ -908,6 +913,15 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } return DESTROY_ME; } + else if (mid_sync) + { + /* we don't want to resend messages to sync MIDs if requests with the + * previous MID arrive */ + clear_packets(this->responding.packets); + /* avoid increasing the expected message ID after handling a message + * to sync MIDs with MID 0 */ + return NEED_MORE; + } array_compress(this->passive_tasks); @@ -1069,6 +1083,10 @@ static status_t process_request(private_task_manager_t *this, task = (task_t*)ike_redirect_create( this->ike_sa, NULL); break; + case IKEV2_MESSAGE_ID_SYNC: + task = (task_t*)ike_mid_sync_create( + this->ike_sa); + break; default: break; } @@ -1200,6 +1218,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; +} + /** * Handle the given IKE fragment, if it is one. * @@ -1373,6 +1397,64 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return status; } +/** + * Check if a message with message ID 0 looks like it is used to synchronize + * the message IDs. + */ +static bool looks_like_mid_sync(private_task_manager_t *this, message_t *msg, + bool strict) +{ + enumerator_t *enumerator; + notify_payload_t *notify; + payload_t *payload; + bool found = FALSE, other = FALSE; + + if (msg->get_exchange_type(msg) == INFORMATIONAL) + { + enumerator = msg->create_payload_enumerator(msg); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == PLV2_NOTIFY) + { + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case IKEV2_MESSAGE_ID_SYNC: + case IPSEC_REPLAY_COUNTER_SYNC: + found = TRUE; + continue; + default: + break; + } + } + if (strict) + { + other = TRUE; + break; + } + } + enumerator->destroy(enumerator); + } + return found && !other; +} + +/** + * Check if a message with message ID 0 looks like it is used to synchronize + * the message IDs and we are prepared to process it. + * + * Note: This is not called if the responder never sent a message before (i.e. + * we expect MID 0). + */ +static bool is_mid_sync(private_task_manager_t *this, message_t *msg) +{ + if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED && + this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC)) + { + return looks_like_mid_sync(this, msg, TRUE); + } + return FALSE; +} METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) @@ -1421,7 +1503,7 @@ METHOD(task_manager_t, process_message, status_t, mid = msg->get_message_id(msg); if (msg->get_request(msg)) { - if (mid == this->responding.mid) + if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg))) { /* reject initial messages if not received in specific states, * after rekeying we only expect a DELETE in an INFORMATIONAL */ @@ -1462,7 +1544,8 @@ METHOD(task_manager_t, process_message, status_t, } } else if ((mid == this->responding.mid - 1) && - array_count(this->responding.packets)) + array_count(this->responding.packets) && + !(mid == 0 && looks_like_mid_sync(this, msg, FALSE))) { status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) @@ -1477,7 +1560,7 @@ METHOD(task_manager_t, process_message, status_t, } else { - DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + DBG1(DBG_IKE, "received message ID %d, expected %d, ignored", mid, this->responding.mid); } } @@ -1515,7 +1598,7 @@ METHOD(task_manager_t, process_message, status_t, } else { - DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + DBG1(DBG_IKE, "received message ID %d, expected %d, ignored", mid, this->initiating.mid); return SUCCESS; } @@ -2046,6 +2129,7 @@ task_manager_v2_t *task_manager_v2_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/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 036910d0e..53daaf2ad 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -417,6 +417,9 @@ METHOD(task_t, build_i, status_t, /* indicate support for EAP-only authentication */ message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION, chunk_empty); + /* indicate support for RFC 6311 Message ID synchronization */ + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED, + chunk_empty); } if (!this->do_another_auth && !this->my_auth) @@ -466,7 +469,8 @@ METHOD(task_t, build_i, status_t, get_reserved_id_bytes(this, id_payload); message->add_payload(message, (payload_t*)id_payload); - if (idr && message->get_message_id(message) == 1 && + if (idr && !idr->contains_wildcards(idr) && + message->get_message_id(message) == 1 && this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NO && this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NEVER) { @@ -991,6 +995,10 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received invalid REDIRECT notify"); } break; + case IKEV2_MESSAGE_ID_SYNC_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC); + break; default: { if (type <= 16383) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index d82e206b8..58b710616 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -159,6 +159,10 @@ static void send_supported_hash_algorithms(private_ike_init_t *this, auth_cfg_t *auth; auth_rule_t rule; uintptr_t config; + int written; + size_t len = BUF_LEN; + char buf[len]; + char *pos = buf; char *plugin_name; algos = hash_algorithm_set_create(); @@ -205,11 +209,23 @@ static void send_supported_hash_algorithms(private_ike_init_t *this, while (enumerator->enumerate(enumerator, &hash)) { writer->write_uint16(writer, hash); + + /* generate debug output */ + written = snprintf(pos, len, " %N", hash_algorithm_short_names, + hash); + if (written > 0 && written < len) + { + pos += written; + len -= written; + } } enumerator->destroy(enumerator); message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS, writer->get_buf(writer)); writer->destroy(writer); + + *pos = '\0'; + DBG2(DBG_CFG, "sending supported signature hash algorithms:%s", buf); } algos->destroy(algos); } @@ -222,6 +238,10 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this, { bio_reader_t *reader; uint16_t algo; + int written; + size_t len = BUF_LEN; + char buf[len]; + char *pos = buf; bool added = FALSE; reader = bio_reader_create(notify->get_notification_data(notify)); @@ -231,10 +251,22 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this, { this->keymat->add_hash_algorithm(this->keymat, algo); added = TRUE; + + /* generate debug output */ + written = snprintf(pos, len, " %N", hash_algorithm_short_names, + algo); + if (written > 0 && written < len) + { + pos += written; + len -= written; + } } } reader->destroy(reader); + *pos = '\0'; + DBG2(DBG_CFG, "received supported signature hash algorithms:%s", buf); + if (added) { this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c new file mode 100644 index 000000000..24cf276f4 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR 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. + */ +/* + * Copyright (C) 2016 Stephen J. Bevan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ike_mid_sync.h" + +#include <daemon.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> +#include <encoding/payloads/notify_payload.h> + +typedef struct private_ike_mid_sync_t private_ike_mid_sync_t; + +/** + * Private members + */ +struct private_ike_mid_sync_t { + + /** + * Public methods and task_t interface. + */ + ike_mid_sync_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Nonce sent by the peer and expected to be returned + */ + chunk_t nonce; + + /** + * Expected next sender message ID + */ + uint32_t send; + + /** + * Expected received message ID + */ + uint32_t recv; +}; + +/* + * Encoding of IKEV2_MESSAGE_SYNC_ID notify, RFC 6311 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Next Payload |C| RESERVED | Payload Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Protocol ID(=0)| SPI Size (=0) | Notify Message Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Nonce Data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EXPECTED_SEND_REQ_MESSAGE_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EXPECTED_RECV_REQ_MESSAGE_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * RFC 6311 section 5.1 + * + * o The peer MUST silently drop any received synchronization message + * if M1 is lower than or equal to the highest value it has seen from + * the cluster. This includes any previous received synchronization + * messages. + */ +METHOD(task_t, pre_process, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + notify_payload_t *notify; + bio_reader_t *reader; + chunk_t nonce; + uint32_t resp; + + if (message->get_message_id(message) != 0) + { /* ignore the notify if it was contained in an INFORMATIONAL with + * unexpected message ID */ + return SUCCESS; + } + if (!this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC)) + { + DBG1(DBG_ENC, "unexpected %N notify, ignored", notify_type_names, + IKEV2_MESSAGE_ID_SYNC); + return FAILED; + } + notify = message->get_notify(message, IKEV2_MESSAGE_ID_SYNC); + + reader = bio_reader_create(notify->get_notification_data(notify)); + if (!reader->read_data(reader, 4, &nonce) || + !reader->read_uint32(reader, &this->send) || + !reader->read_uint32(reader, &this->recv)) + { + reader->destroy(reader); + DBG1(DBG_ENC, "received invalid %N notify", + notify_type_names, IKEV2_MESSAGE_ID_SYNC); + return FAILED; + } + reader->destroy(reader); + resp = this->ike_sa->get_message_id(this->ike_sa, FALSE); + if (this->send < resp) + { + DBG1(DBG_ENC, "ignore %N notify with lower (%d) than expected (%d) " + "sender MID", notify_type_names, IKEV2_MESSAGE_ID_SYNC, this->send, + resp); + return FAILED; + } + this->nonce = chunk_clone(nonce); + return SUCCESS; +} + +/** + * Check if there are any active tasks, indicating that we already + * used the currents message ID and are waiting for a response. + */ +static bool has_active_tasks(private_ike_mid_sync_t *this) +{ + enumerator_t *enumerator; + task_t *task; + bool active; + + enumerator = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_ACTIVE); + active = enumerator->enumerate(enumerator, &task); + enumerator->destroy(enumerator); + return active; +} + +/* + * RFC 6311 section 5.1 + * + * o M2 MUST be at least the higher of the received M1, and one more + * than the highest sender value received from the cluster. This + * includes any previous received synchronization messages. + * + * o P2 MUST be the higher of the received P1 value, and one more than + * the highest sender value used by the peer. + * + * M1 is this->send, P1 is this->recv + */ +METHOD(task_t, process, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + uint32_t resp, init, m2, p2; + + if (message->get_message_id(message) != 0) + { /* ignore the notify if it was contained in an INFORMATIONAL with + * unexpected message id */ + return SUCCESS; + } + resp = this->ike_sa->get_message_id(this->ike_sa, FALSE); + m2 = max(this->send, resp); + if (resp != m2) + { + this->ike_sa->set_message_id(this->ike_sa, FALSE, m2); + } + init = this->ike_sa->get_message_id(this->ike_sa, TRUE); + p2 = max(this->recv, has_active_tasks(this) ? init + 1 : init); + if (init != p2) + { + this->ike_sa->set_message_id(this->ike_sa, TRUE, p2); + } + DBG1(DBG_IKE, "responder requested MID sync: initiating %d[%d], " + "responding %d[%d]", p2, init, m2, resp); + this->send = p2; + this->recv = m2; + return NEED_MORE; +} + +METHOD(task_t, build, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + bio_writer_t *writer; + + writer = bio_writer_create(12); + writer->write_data(writer, this->nonce); + writer->write_uint32(writer, this->send); + writer->write_uint32(writer, this->recv); + + message->set_message_id(message, 0); + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC, + writer->get_buf(writer)); + + writer->destroy(writer); + return SUCCESS; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_mid_sync_t *this) +{ + return TASK_IKE_MID_SYNC; +} + +METHOD(task_t, migrate, void, + private_ike_mid_sync_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + chunk_free(&this->nonce); +} + +METHOD(task_t, destroy, void, + private_ike_mid_sync_t *this) +{ + chunk_free(&this->nonce); + free(this); +} + +/* + * Described in header. + */ +ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa) +{ + private_ike_mid_sync_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .build = _build, + .pre_process = _pre_process, + .process = _process, + .migrate = _migrate, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h new file mode 100644 index 000000000..9dd46f925 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR 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. + */ +/* + * Copyright (C) 2016 Stephen J. Bevan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup ike_mid_sync ike_mid_sync + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_MID_SYNC_H_ +#define IKE_MID_SYNC_H_ + +typedef struct ike_mid_sync_t ike_mid_sync_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/task.h> + +/** + * Task of type TASK_IKE_MID_SYNC, implements RFC 6311 responder. + * + * This task handles an IKEV2_MESSAGE_ID_SYNC notify sent by a peer + * and if acceptable updates the SA MIDs and replies with the updated + * MID values. + */ +struct ike_mid_sync_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new TASK_IKE_MID_SYNC task. + * + * @param ike_sa IKE_SA this task works for + * @return task to handle by the task_manager + */ +ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa); + +#endif /** IKE_MID_SYNC_H_ @}*/ diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 40e291be5..b0162751d 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -36,7 +36,7 @@ struct private_shunt_manager_t { shunt_manager_t public; /** - * Installed shunts, as child_cfg_t + * Installed shunts, as entry_t */ linked_list_t *shunts; @@ -57,6 +57,32 @@ struct private_shunt_manager_t { }; /** + * Config entry for a shunt + */ +typedef struct { + /** + * Configured namespace + */ + char *ns; + + /** + * Child config + */ + child_cfg_t *cfg; + +} entry_t; + +/** + * Destroy a config entry + */ +static void entry_destroy(entry_t *this) +{ + this->cfg->destroy(this->cfg); + free(this->ns); + free(this); +} + +/** * Install in and out shunt policies in the kernel */ static bool install_shunt_policy(child_cfg_t *child) @@ -162,10 +188,10 @@ static bool install_shunt_policy(child_cfg_t *child) } METHOD(shunt_manager_t, install, bool, - private_shunt_manager_t *this, child_cfg_t *child) + private_shunt_manager_t *this, char *ns, child_cfg_t *cfg) { enumerator_t *enumerator; - child_cfg_t *child_cfg; + entry_t *entry; bool found = FALSE, success; /* check if not already installed */ @@ -176,9 +202,10 @@ METHOD(shunt_manager_t, install, bool, return FALSE; } enumerator = this->shunts->create_enumerator(this->shunts); - while (enumerator->enumerate(enumerator, &child_cfg)) + while (enumerator->enumerate(enumerator, &entry)) { - if (streq(child_cfg->get_name(child_cfg), child->get_name(child))) + if (streq(ns, entry->ns) && + streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg))) { found = TRUE; break; @@ -188,21 +215,25 @@ METHOD(shunt_manager_t, install, bool, if (found) { DBG1(DBG_CFG, "shunt %N policy '%s' already installed", - ipsec_mode_names, child->get_mode(child), child->get_name(child)); + ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg)); this->lock->unlock(this->lock); return TRUE; } - this->shunts->insert_last(this->shunts, child->get_ref(child)); + INIT(entry, + .ns = strdupnull(ns), + .cfg = cfg->get_ref(cfg), + ); + this->shunts->insert_last(this->shunts, entry); this->installing++; this->lock->unlock(this->lock); - success = install_shunt_policy(child); + success = install_shunt_policy(cfg); this->lock->write_lock(this->lock); if (!success) { - this->shunts->remove(this->shunts, child, NULL); - child->destroy(child); + this->shunts->remove(this->shunts, entry, NULL); + entry_destroy(entry); } this->installing--; this->condvar->signal(this->condvar); @@ -320,19 +351,20 @@ static void uninstall_shunt_policy(child_cfg_t *child) } METHOD(shunt_manager_t, uninstall, bool, - private_shunt_manager_t *this, char *name) + private_shunt_manager_t *this, char *ns, char *name) { enumerator_t *enumerator; - child_cfg_t *child, *found = NULL; + entry_t *entry, *found = NULL; this->lock->write_lock(this->lock); enumerator = this->shunts->create_enumerator(this->shunts); - while (enumerator->enumerate(enumerator, &child)) + while (enumerator->enumerate(enumerator, &entry)) { - if (streq(name, child->get_name(child))) + if (streq(ns, entry->ns) && + streq(name, entry->cfg->get_name(entry->cfg))) { this->shunts->remove_at(this->shunts, enumerator); - found = child; + found = entry; break; } } @@ -343,8 +375,19 @@ METHOD(shunt_manager_t, uninstall, bool, { return FALSE; } - uninstall_shunt_policy(child); - child->destroy(child); + uninstall_shunt_policy(found->cfg); + entry_destroy(found); + return TRUE; +} + +CALLBACK(filter_entries, bool, + void *unused, entry_t **entry, char **ns, void **in, child_cfg_t **cfg) +{ + if (ns) + { + *ns = (*entry)->ns; + } + *cfg = (*entry)->cfg; return TRUE; } @@ -352,25 +395,26 @@ METHOD(shunt_manager_t, create_enumerator, enumerator_t*, private_shunt_manager_t *this) { this->lock->read_lock(this->lock); - return enumerator_create_cleaner( + return enumerator_create_filter( this->shunts->create_enumerator(this->shunts), - (void*)this->lock->unlock, this->lock); + (void*)filter_entries, this->lock, + (void*)this->lock->unlock); } METHOD(shunt_manager_t, flush, void, private_shunt_manager_t *this) { - child_cfg_t *child; + entry_t *entry; this->lock->write_lock(this->lock); while (this->installing) { this->condvar->wait(this->condvar, this->lock); } - while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS) + while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS) { - uninstall_shunt_policy(child); - child->destroy(child); + uninstall_shunt_policy(entry->cfg); + entry_destroy(entry); } this->installing = INSTALL_DISABLED; this->lock->unlock(this->lock); diff --git a/src/libcharon/sa/shunt_manager.h b/src/libcharon/sa/shunt_manager.h index c43f5db3d..f2b721032 100644 --- a/src/libcharon/sa/shunt_manager.h +++ b/src/libcharon/sa/shunt_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Tobias Brunner + * Copyright (C) 2015-2016 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -36,23 +36,26 @@ struct shunt_manager_t { /** * Install a policy as a shunt. * - * @param child child configuration to install as a shunt + * @param ns optional namespace (e.g. name of a connection or + * plugin), cloned + * @param child child configuration to install as a shunt * @return TRUE if installed successfully */ - bool (*install)(shunt_manager_t *this, child_cfg_t *child); + bool (*install)(shunt_manager_t *this, char *ns, child_cfg_t *child); /** * Uninstall a shunt policy. * + * @param ns namespace (same as given during installation) * @param name name of child configuration to uninstall as a shunt * @return TRUE if uninstalled successfully */ - bool (*uninstall)(shunt_manager_t *this, char *name); + bool (*uninstall)(shunt_manager_t *this, char *ns, char *name); /** * Create an enumerator over all installed shunts. * - * @return enumerator over (child_sa_t) + * @return enumerator over (char*, child_cfg_t*) */ enumerator_t* (*create_enumerator)(shunt_manager_t *this); diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index 405eda66b..30de08c9b 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -30,6 +30,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REAUTH_COMPLETE", "IKE_REDIRECT", "IKE_VERIFY_PEER_CERT", + "IKE_MID_SYNC", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 31d70fb3b..5f77149ba 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -61,6 +61,8 @@ enum task_type_t { TASK_IKE_REDIRECT, /** verify a peer's certificate */ TASK_IKE_VERIFY_PEER_CERT, + /** synchronize message IDs, RFC6311 */ + TASK_IKE_MID_SYNC, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 86077d373..7e9262291 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -240,6 +240,14 @@ struct task_manager_t { void (*incr_mid)(task_manager_t *this, bool initiate); /** + * Get the current message ID counter, in- or outbound. + * + * @param initiate TRUE to get the initiating ID + * @return current message ID + */ + uint32_t (*get_mid)(task_manager_t *this, bool initiate); + + /** * Reset message ID counters of the task manager. * * The IKEv2 protocol requires to restart exchanges with message IDs @@ -253,7 +261,7 @@ struct task_manager_t { * @param initiate message ID / DPD seq to initiate exchanges (send) * @param respond message ID / DPD seq to respond to exchanges (expect) */ - void (*reset) (task_manager_t *this, uint32_t initiate, uint32_t respond); + void (*reset)(task_manager_t *this, uint32_t initiate, uint32_t respond); /** * Check if we are currently waiting for a reply. |