From e7848e36fa6ce0f6a19a233173fd302e7e86a5b2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 5 Dec 2024 11:55:52 +0100 Subject: [PATCH 1/3] ha: Add support to sync IKE and Child SAs with multiple key exchanges Synchronization for the additional transforms in the IKE and Child SA proposals is added. Details of the IKE_SA synchronization are changed to support IKE_INTERMEDIATE exchanges that cause multiple HA_IKE_ADD messages and key derivations. The cache has been extended to handle multiple such messages. Co-authored-by: Thomas Egerer --- src/libcharon/plugins/ha/ha_cache.c | 43 +++-- src/libcharon/plugins/ha/ha_child.c | 13 +- src/libcharon/plugins/ha/ha_dispatcher.c | 203 ++++++++++++++------ src/libcharon/plugins/ha/ha_ike.c | 48 ++--- src/libcharon/plugins/ha/ha_message.c | 43 ++++- src/libcharon/plugins/ha/ha_message.h | 25 ++- src/libcharon/sa/ikev2/tasks/child_create.c | 3 +- src/libstrongswan/crypto/transform.h | 6 + 8 files changed, 263 insertions(+), 121 deletions(-) diff --git a/src/libcharon/plugins/ha/ha_cache.c b/src/libcharon/plugins/ha/ha_cache.c index 53c6f84e6..12f38a3c6 100644 --- a/src/libcharon/plugins/ha/ha_cache.c +++ b/src/libcharon/plugins/ha/ha_cache.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015-2024 Tobias Brunner * Copyright (C) 2010 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -70,9 +71,9 @@ struct private_ha_cache_t { typedef struct { /* segment this entry is associate to */ u_int segment; - /* ADD message */ - ha_message_t *add; - /* list of updates UPDATE message */ + /* list of ADD messages */ + linked_list_t *add; + /* list of UPDATE messages */ linked_list_t *updates; /* last initiator mid */ ha_message_t *midi; @@ -83,27 +84,27 @@ typedef struct { } entry_t; /** - * Create a entry with an add message + * Create a entry */ -static entry_t *entry_create(ha_message_t *add) +static entry_t *entry_create() { entry_t *entry; INIT(entry, - .add = add, + .add = linked_list_create(), .updates = linked_list_create(), ); return entry; } /** - * clean up a entry + * Clean up a entry */ static void entry_destroy(entry_t *entry) { entry->updates->destroy_offset(entry->updates, - offsetof(ha_message_t, destroy)); - entry->add->destroy(entry->add); + offsetof(ha_message_t, destroy)); + entry->add->destroy_offset(entry->add, offsetof(ha_message_t, destroy)); DESTROY_IF(entry->midi); DESTROY_IF(entry->midr); DESTROY_IF(entry->iv); @@ -119,12 +120,13 @@ METHOD(ha_cache_t, cache, void, switch (message->get_type(message)) { case HA_IKE_ADD: - entry = entry_create(message); - entry = this->cache->put(this->cache, ike_sa, entry); - if (entry) + entry = this->cache->get(this->cache, ike_sa); + if (!entry) { - entry_destroy(entry); + entry = entry_create(); + this->cache->put(this->cache, ike_sa, entry); } + entry->add->insert_last(entry->add, message); break; case HA_IKE_UPDATE: entry = this->cache->get(this->cache, ike_sa); @@ -305,7 +307,7 @@ static void rekey_segment(private_ha_cache_t *this, u_int segment) METHOD(ha_cache_t, resync, void, private_ha_cache_t *this, u_int segment) { - enumerator_t *enumerator, *updates; + enumerator_t *enumerator, *messages; ike_sa_t *ike_sa; entry_t *entry; ha_message_t *message; @@ -318,13 +320,18 @@ METHOD(ha_cache_t, resync, void, { if (entry->segment == segment) { - this->socket->push(this->socket, entry->add); - updates = entry->updates->create_enumerator(entry->updates); - while (updates->enumerate(updates, &message)) + messages = entry->add->create_enumerator(entry->add); + while (messages->enumerate(messages, &message)) { this->socket->push(this->socket, message); } - updates->destroy(updates); + messages->destroy(messages); + messages = entry->updates->create_enumerator(entry->updates); + while (messages->enumerate(messages, &message)) + { + this->socket->push(this->socket, message); + } + messages->destroy(messages); if (entry->midi) { this->socket->push(this->socket, entry->midi); diff --git a/src/libcharon/plugins/ha/ha_child.c b/src/libcharon/plugins/ha/ha_child.c index 364fe1d5f..9d8fca9eb 100644 --- a/src/libcharon/plugins/ha/ha_child.c +++ b/src/libcharon/plugins/ha/ha_child.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -91,21 +92,23 @@ METHOD(listener_t, child_keys, bool, { m->add_attribute(m, HA_ALG_INTEG, alg); } - if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD, &alg, NULL)) - { - m->add_attribute(m, HA_ALG_DH, alg); - } + m->add_key_exchange_methods(m, proposal); if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, &alg, NULL)) { m->add_attribute(m, HA_ESN, alg); } + m->add_attribute(m, HA_NONCE_I, nonce_i); m->add_attribute(m, HA_NONCE_R, nonce_r); if (kes && key_exchange_concat_secrets(kes, &secret, &add_secret)) { m->add_attribute(m, HA_SECRET, secret); chunk_clear(&secret); - chunk_clear(&add_secret); + if (add_secret.len) + { + m->add_attribute(m, HA_ADD_SECRET, add_secret); + chunk_clear(&add_secret); + } } local_ts = linked_list_create(); diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index b0ec3d9cf..5de26a65a 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016-2024 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -23,7 +24,7 @@ #include typedef struct private_ha_dispatcher_t private_ha_dispatcher_t; -typedef struct ha_diffie_hellman_t ha_diffie_hellman_t; +typedef struct ha_key_exchange_t ha_key_exchange_t; /** * Private data of an ha_dispatcher_t object. @@ -62,14 +63,14 @@ struct private_ha_dispatcher_t { }; /** - * DH implementation for HA synced DH values + * KE implementation for HA synced KE shared secrets */ -struct ha_diffie_hellman_t { +struct ha_key_exchange_t { /** - * Implements key_exchange_t + * Public interface */ - key_exchange_t dh; + key_exchange_t ke; /** * Shared secret @@ -77,49 +78,73 @@ struct ha_diffie_hellman_t { chunk_t secret; /** - * Own public value + * Own public value (IKEv1 only) */ chunk_t pub; }; -METHOD(key_exchange_t, dh_get_shared_secret, bool, - ha_diffie_hellman_t *this, chunk_t *secret) +METHOD(key_exchange_t, ke_get_shared_secret, bool, + ha_key_exchange_t *this, chunk_t *secret) { *secret = chunk_clone(this->secret); return TRUE; } -METHOD(key_exchange_t, dh_get_public_key, bool, - ha_diffie_hellman_t *this, chunk_t *value) +METHOD(key_exchange_t, ke_get_public_key, bool, + ha_key_exchange_t *this, chunk_t *value) { *value = chunk_clone(this->pub); return TRUE; } -METHOD(key_exchange_t, dh_destroy, void, - ha_diffie_hellman_t *this) +METHOD(key_exchange_t, ke_destroy, void, + ha_key_exchange_t *this) { free(this); } /** - * Create a HA synced DH implementation + * Create a HA synced KE implementation */ -static key_exchange_t *ha_diffie_hellman_create(chunk_t secret, chunk_t pub) +static key_exchange_t *ha_key_exchange_create(chunk_t secret, chunk_t pub) { - ha_diffie_hellman_t *this; + ha_key_exchange_t *this; INIT(this, - .dh = { - .get_shared_secret = _dh_get_shared_secret, - .get_public_key = _dh_get_public_key, - .destroy = _dh_destroy, + .ke = { + .get_shared_secret = _ke_get_shared_secret, + .get_public_key = _ke_get_public_key, + .destroy = _ke_destroy, }, .secret = secret, .pub = pub, ); - return &this->dh; + return &this->ke; +} + +/** + * Add the given KE methods to a proposal + */ +static void add_ke_methods_to_proposal(proposal_t *proposal, uint16_t ke_alg, + chunk_t add_kes) +{ + int i, count; + + if (ke_alg) + { + proposal->add_algorithm(proposal, KEY_EXCHANGE_METHOD, ke_alg, 0); + } + count = min(add_kes.len / sizeof(uint16_t), MAX_ADDITIONAL_KEY_EXCHANGES); + for (i = 0; i < count; i++) + { + ke_alg = ntohs(((uint16_t*)add_kes.ptr)[i]); + if (ke_alg) + { + proposal->add_algorithm(proposal, i + ADDITIONAL_KEY_EXCHANGE_1, + ke_alg, 0); + } + } } /** @@ -133,9 +158,10 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message ike_sa_t *ike_sa = NULL, *old_sa = NULL; ike_version_t version = IKEV2; uint16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED; - uint16_t dh_grp = 0; + uint16_t ke_alg = 0; chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty; chunk_t secret = chunk_empty, old_skd = chunk_empty; + chunk_t add_secret = chunk_empty, add_kes = chunk_empty; chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty; host_t *other = NULL; bool ok = FALSE; @@ -147,8 +173,13 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message switch (attribute) { case HA_IKE_ID: - ike_sa = ike_sa_create(value.ike_sa_id, + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + value.ike_sa_id); + if (!ike_sa) + { + ike_sa = ike_sa_create(value.ike_sa_id, value.ike_sa_id->is_initiator(value.ike_sa_id), version); + } break; case HA_IKE_REKEY_ID: old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, @@ -169,6 +200,9 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message case HA_SECRET: secret = value.chunk; break; + case HA_ADD_SECRET: + add_secret = value.chunk; + break; case HA_LOCAL_DH: dh_local = value.chunk; break; @@ -196,11 +230,15 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message case HA_ALG_OLD_PRF: old_prf = value.u16; break; - case HA_ALG_DH: - dh_grp = value.u16; + case HA_ALG_KE: + ke_alg = value.u16; + break; + case HA_ALG_ADD_KES: + add_kes = value.chunk; break; case HA_AUTH_METHOD: method = value.u16; + break; default: break; } @@ -210,38 +248,52 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message if (ike_sa) { proposal_t *proposal; - key_exchange_t *dh; + key_exchange_t *ke; + array_t *kes = NULL; + bool key_update = FALSE; - proposal = proposal_create(PROTO_IKE, 0); - if (integ) + proposal = ike_sa->get_proposal(ike_sa); + if (!proposal) { - proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); + proposal = proposal_create(PROTO_IKE, 0); + if (integ) + { + proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); + } + if (encr) + { + proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); + } + if (prf) + { + proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0); + } + add_ke_methods_to_proposal(proposal, ke_alg, add_kes); } - if (encr) + else { - proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); - } - if (prf) - { - proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0); - } - if (dh_grp) - { - proposal->add_algorithm(proposal, KEY_EXCHANGE_METHOD, dh_grp, 0); + key_update = TRUE; } charon->bus->set_sa(charon->bus, ike_sa); - dh = ha_diffie_hellman_create(secret, dh_local); + ke = ha_key_exchange_create(secret, dh_local); + array_insert_create(&kes, ARRAY_HEAD, ke); if (ike_sa->get_version(ike_sa) == IKEV2) { keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); - array_t *kes = NULL; - array_insert_create(&kes, ARRAY_HEAD, dh); + if (add_secret.len) + { + ke = ha_key_exchange_create(add_secret, chunk_empty); + array_insert_create(&kes, ARRAY_TAIL, ke); + } + if (key_update) + { + old_prf = keymat_v2->get_skd(keymat_v2, &old_skd); + } ok = keymat_v2->derive_ike_keys(keymat_v2, proposal, kes, nonce_i, nonce_r, ike_sa->get_id(ike_sa), old_prf, old_skd); - array_destroy(kes); } - if (ike_sa->get_version(ike_sa) == IKEV1) + else if (ike_sa->get_version(ike_sa) == IKEV1) { keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); shared_key_t *shared = NULL; @@ -254,12 +306,12 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message if (keymat_v1->create_hasher(keymat_v1, proposal)) { ok = keymat_v1->derive_ike_keys(keymat_v1, proposal, - dh, dh_remote, nonce_i, nonce_r, + ke, dh_remote, nonce_i, nonce_r, ike_sa->get_id(ike_sa), method, shared); } DESTROY_IF(shared); } - dh->destroy(dh); + array_destroy_offset(kes, offsetof(key_exchange_t, destroy)); if (ok) { if (old_sa) @@ -278,8 +330,11 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message ike_sa->set_other_host(ike_sa, other); other = NULL; } - ike_sa->set_state(ike_sa, IKE_CONNECTING); - ike_sa->set_proposal(ike_sa, proposal); + if (!key_update) + { + ike_sa->set_state(ike_sa, IKE_CONNECTING); + ike_sa->set_proposal(ike_sa, proposal); + } this->cache->cache(this->cache, ike_sa, message); message = NULL; charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); @@ -287,10 +342,21 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message else { DBG1(DBG_IKE, "HA keymat derivation failed"); - ike_sa->destroy(ike_sa); + if (key_update) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + ike_sa->destroy(ike_sa); + charon->bus->set_sa(charon->bus, NULL); + } + } + if (!key_update) + { + proposal->destroy(proposal); } - charon->bus->set_sa(charon->bus, NULL); - proposal->destroy(proposal); } if (old_sa) { @@ -659,12 +725,13 @@ static void process_child_add(private_ha_dispatcher_t *this, uint32_t inbound_spi = 0, outbound_spi = 0; uint16_t inbound_cpi = 0, outbound_cpi = 0; uint8_t mode = MODE_TUNNEL, ipcomp = 0; - uint16_t encr = 0, integ = 0, len = 0, dh_grp = 0; + uint16_t encr = 0, integ = 0, len = 0, ke_alg = 0; uint16_t esn = NO_EXT_SEQ_NUMBERS; chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty; + chunk_t add_secret = chunk_empty, add_kes = chunk_empty; chunk_t encr_i, integ_i, encr_r, integ_r; linked_list_t *local_ts, *remote_ts; - key_exchange_t *dh = NULL; + key_exchange_t *ke = NULL; array_t *kes = NULL; enumerator = message->create_attribute_enumerator(message); @@ -709,8 +776,11 @@ static void process_child_add(private_ha_dispatcher_t *this, case HA_ALG_INTEG: integ = value.u16; break; - case HA_ALG_DH: - dh_grp = value.u16; + case HA_ALG_KE: + ke_alg = value.u16; + break; + case HA_ALG_ADD_KES: + add_kes = value.chunk; break; case HA_ESN: esn = value.u16; @@ -724,6 +794,9 @@ static void process_child_add(private_ha_dispatcher_t *this, case HA_SECRET: secret = value.chunk; break; + case HA_ADD_SECRET: + add_secret = value.chunk; + break; default: break; } @@ -763,15 +836,18 @@ static void process_child_add(private_ha_dispatcher_t *this, { proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); } - if (dh_grp) - { - proposal->add_algorithm(proposal, KEY_EXCHANGE_METHOD, dh_grp, 0); - } + add_ke_methods_to_proposal(proposal, ke_alg, add_kes); proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0); + if (secret.len) { - dh = ha_diffie_hellman_create(secret, chunk_empty); - array_insert_create(&kes, ARRAY_HEAD, dh); + ke = ha_key_exchange_create(secret, chunk_empty); + array_insert_create(&kes, ARRAY_HEAD, ke); + } + if (add_secret.len) + { + ke = ha_key_exchange_create(add_secret, chunk_empty); + array_insert_create(&kes, ARRAY_TAIL, ke); } if (ike_sa->get_version(ike_sa) == IKEV2) { @@ -780,7 +856,7 @@ static void process_child_add(private_ha_dispatcher_t *this, ok = keymat_v2->derive_child_keys(keymat_v2, proposal, kes, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); } - if (ike_sa->get_version(ike_sa) == IKEV1) + else if (ike_sa->get_version(ike_sa) == IKEV1) { keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); uint32_t spi_i, spi_r; @@ -788,11 +864,10 @@ static void process_child_add(private_ha_dispatcher_t *this, spi_i = initiator ? inbound_spi : outbound_spi; spi_r = initiator ? outbound_spi : inbound_spi; - ok = keymat_v1->derive_child_keys(keymat_v1, proposal, dh, spi_i, spi_r, + ok = keymat_v1->derive_child_keys(keymat_v1, proposal, ke, spi_i, spi_r, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r); } - array_destroy(kes); - DESTROY_IF(dh); + array_destroy_offset(kes, offsetof(key_exchange_t, destroy)); if (!ok) { DBG1(DBG_CHD, "HA CHILD_SA key derivation failed"); diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index e6dab8457..f6983ff46 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -97,8 +98,7 @@ METHOD(listener_t, ike_keys, bool, return TRUE; } if (!key_exchange_concat_secrets(kes, &secret, &add_secret) || - !array_get(kes, ARRAY_HEAD, &ke) || - add_secret.len > 0) + !array_get(kes, ARRAY_HEAD, &ke)) { chunk_clear(&secret); chunk_clear(&add_secret); @@ -109,7 +109,7 @@ METHOD(listener_t, ike_keys, bool, m->add_attribute(m, HA_IKE_VERSION, ike_sa->get_version(ike_sa)); m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - if (rekey && rekey->get_version(rekey) == IKEV2) + if (rekey && rekey != ike_sa && rekey->get_version(rekey) == IKEV2) { chunk_t skd; keymat_v2_t *keymat; @@ -119,32 +119,37 @@ METHOD(listener_t, ike_keys, bool, m->add_attribute(m, HA_ALG_OLD_PRF, keymat->get_skd(keymat, &skd)); m->add_attribute(m, HA_OLD_SKD, skd); } - - proposal = ike_sa->get_proposal(ike_sa); - if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len)) + if (rekey != ike_sa) { - m->add_attribute(m, HA_ALG_ENCR, alg); - if (len) + /* only sync the proposal for initial key derivation and rekeyings */ + proposal = ike_sa->get_proposal(ike_sa); + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len)) { - m->add_attribute(m, HA_ALG_ENCR_LEN, len); + m->add_attribute(m, HA_ALG_ENCR, alg); + if (len) + { + m->add_attribute(m, HA_ALG_ENCR_LEN, len); + } } - } - if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) - { - m->add_attribute(m, HA_ALG_INTEG, alg); - } - if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) - { - m->add_attribute(m, HA_ALG_PRF, alg); - } - if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD, &alg, NULL)) - { - m->add_attribute(m, HA_ALG_DH, alg); + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) + { + m->add_attribute(m, HA_ALG_INTEG, alg); + } + if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) + { + m->add_attribute(m, HA_ALG_PRF, alg); + } + m->add_key_exchange_methods(m, proposal); } m->add_attribute(m, HA_NONCE_I, nonce_i); m->add_attribute(m, HA_NONCE_R, nonce_r); m->add_attribute(m, HA_SECRET, secret); chunk_clear(&secret); + if (add_secret.len) + { + m->add_attribute(m, HA_ADD_SECRET, add_secret); + chunk_clear(&add_secret); + } if (ike_sa->get_version(ike_sa) == IKEV1) { if (ke->get_public_key(ke, &secret)) @@ -421,4 +426,3 @@ ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel, return &this->public; } - diff --git a/src/libcharon/plugins/ha/ha_message.c b/src/libcharon/plugins/ha/ha_message.c index 1a528d46c..8fdb3782e 100644 --- a/src/libcharon/plugins/ha/ha_message.c +++ b/src/libcharon/plugins/ha/ha_message.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -231,7 +232,7 @@ METHOD(ha_message_t, add_attribute, void, break; } /* uint16_t */ - case HA_ALG_DH: + case HA_ALG_KE: case HA_ALG_PRF: case HA_ALG_OLD_PRF: case HA_ALG_ENCR: @@ -270,11 +271,13 @@ METHOD(ha_message_t, add_attribute, void, case HA_NONCE_I: case HA_NONCE_R: case HA_SECRET: + case HA_ADD_SECRET: case HA_LOCAL_DH: case HA_REMOTE_DH: case HA_PSK: case HA_IV: case HA_OLD_SKD: + case HA_ALG_ADD_KES: { chunk_t chunk; @@ -318,6 +321,36 @@ METHOD(ha_message_t, add_attribute, void, va_end(args); } +METHOD(ha_message_t, add_key_exchange_methods, void, + private_ha_message_t *this, proposal_t *proposal) +{ + uint16_t algs[MAX_ADDITIONAL_KEY_EXCHANGES] = {}; + int i, count = 0; + + if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD, &algs[0], NULL)) + { + add_attribute(this, HA_ALG_KE, algs[0]); + algs[0] = 0; + } + for (i = 0; i < countof(algs); i++) + { + if (proposal->get_algorithm(proposal, i + ADDITIONAL_KEY_EXCHANGE_1, + &algs[i], NULL)) + { + count = i+1; + } + } + if (count) + { + for (i = 0; i < count; i++) + { + algs[i] = htons(algs[i]); + } + add_attribute(this, HA_ALG_ADD_KES, + chunk_create((u_char*)algs, count * sizeof(uint16_t))); + } +} + /** * Attribute enumerator implementation */ @@ -455,7 +488,7 @@ METHOD(enumerator_t, attribute_enumerate, bool, return TRUE; } /** uint16_t */ - case HA_ALG_DH: + case HA_ALG_KE: case HA_ALG_PRF: case HA_ALG_OLD_PRF: case HA_ALG_ENCR: @@ -496,11 +529,13 @@ METHOD(enumerator_t, attribute_enumerate, bool, case HA_NONCE_I: case HA_NONCE_R: case HA_SECRET: + case HA_ADD_SECRET: case HA_LOCAL_DH: case HA_REMOTE_DH: case HA_PSK: case HA_IV: case HA_OLD_SKD: + case HA_ALG_ADD_KES: { size_t len; @@ -626,7 +661,7 @@ METHOD(ha_message_t, get_encoding, chunk_t, METHOD(ha_message_t, destroy, void, private_ha_message_t *this) { - free(this->buf.ptr); + chunk_clear(&this->buf); free(this); } @@ -639,6 +674,7 @@ static private_ha_message_t *ha_message_create_generic() .public = { .get_type = _get_type, .add_attribute = _add_attribute, + .add_key_exchange_methods = _add_key_exchange_methods, .create_attribute_enumerator = _create_attribute_enumerator, .get_encoding = _get_encoding, .destroy = _destroy, @@ -688,4 +724,3 @@ ha_message_t *ha_message_parse(chunk_t data) return &this->public; } - diff --git a/src/libcharon/plugins/ha/ha_message.h b/src/libcharon/plugins/ha/ha_message.h index 618245cb0..04a77618c 100644 --- a/src/libcharon/plugins/ha/ha_message.h +++ b/src/libcharon/plugins/ha/ha_message.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 Tobias Brunner * Copyright (C) 2008 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -26,12 +27,13 @@ #include #include #include +#include #include /** * Protocol version of this implementation */ -#define HA_MESSAGE_VERSION 3 +#define HA_MESSAGE_VERSION 4 typedef struct ha_message_t ha_message_t; typedef enum ha_message_type_t ha_message_type_t; @@ -109,8 +111,10 @@ enum ha_message_attribute_t { HA_NONCE_I, /** chunk_t, responders nonce */ HA_NONCE_R, - /** chunk_t, diffie hellman shared secret */ + /** chunk_t, KE shared secret */ HA_SECRET, + /** chunk_t, optional additional KE shared secret(s) */ + HA_ADD_SECRET, /** chunk_t, SKd of old SA if rekeying */ HA_OLD_SKD, /** uint16_t, pseudo random function */ @@ -123,8 +127,10 @@ enum ha_message_attribute_t { HA_ALG_ENCR_LEN, /** uint16_t, integrity protection algorithm */ HA_ALG_INTEG, - /** uint16_t, DH group */ - HA_ALG_DH, + /** uint16_t, KE method */ + HA_ALG_KE, + /** chunk_t of uint16_t[], optional additional KE methods (IKEv2 only) */ + HA_ALG_ADD_KES, /** uint8_t, IPsec mode, TUNNEL|TRANSPORT|... */ HA_IPSEC_MODE, /** uint8_t, IPComp protocol */ @@ -149,9 +155,9 @@ enum ha_message_attribute_t { HA_ESN, /** uint8_t, IKE version */ HA_IKE_VERSION, - /** chunk_t, own DH public value */ + /** chunk_t, own DH public value (IKEv1 only) */ HA_LOCAL_DH, - /** chunk_t, remote DH public value */ + /** chunk_t, remote DH public value (IKEv1 only) */ HA_REMOTE_DH, /** chunk_t, shared secret for IKEv1 key derivation */ HA_PSK, @@ -197,6 +203,13 @@ struct ha_message_t { void (*add_attribute)(ha_message_t *this, ha_message_attribute_t attribute, ...); + /** + * Add attributes for key exchange methods in the given proposal. + * + * @param proposal proposal from which to get key exchange methods + */ + void (*add_key_exchange_methods)(ha_message_t *this, proposal_t *proposal); + /** * Create an enumerator over all attributes in a message. * diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 05e743c86..ee76433f7 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -33,8 +33,7 @@ #include /** Maximum number of key exchanges (including the initial one, if any) */ -#define MAX_KEY_EXCHANGES (ADDITIONAL_KEY_EXCHANGE_7 - \ - ADDITIONAL_KEY_EXCHANGE_1 + 2) +#define MAX_KEY_EXCHANGES (MAX_ADDITIONAL_KEY_EXCHANGES + 1) typedef struct private_child_create_t private_child_create_t; diff --git a/src/libstrongswan/crypto/transform.h b/src/libstrongswan/crypto/transform.h index 6e6594fc2..0103878ed 100644 --- a/src/libstrongswan/crypto/transform.h +++ b/src/libstrongswan/crypto/transform.h @@ -52,6 +52,12 @@ enum transform_type_t { KEY_DERIVATION_FUNCTION = 262, }; +/** + * Maximum number of additional key exchanges. + */ +#define MAX_ADDITIONAL_KEY_EXCHANGES (ADDITIONAL_KEY_EXCHANGE_7 - \ + ADDITIONAL_KEY_EXCHANGE_1 + 1) + /** * enum names for transform_type_t. */ From fd6ac87fc3b54bb55cb43e0b38b2784ffe66a2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Hren?= Date: Tue, 29 Oct 2024 11:27:38 +0100 Subject: [PATCH 2/3] testing: Add ha/active-passive-multi-ke scenario --- testing/scripts/build-certs-chroot | 4 +- .../active-passive-multi-ke/description.txt | 8 +++ .../ha/active-passive-multi-ke/evaltest.dat | 34 +++++++++++ .../hosts/alice/etc/iptables.rules | 57 +++++++++++++++++++ .../hosts/alice/etc/strongswan.conf | 17 ++++++ .../hosts/alice/etc/swanctl/swanctl.conf | 25 ++++++++ .../hosts/carol/etc/strongswan.conf | 5 ++ .../hosts/carol/etc/swanctl/swanctl.conf | 27 +++++++++ .../hosts/dave/etc/strongswan.conf | 6 ++ .../hosts/dave/etc/swanctl/swanctl.conf | 27 +++++++++ .../hosts/moon/etc/iptables.rules | 57 +++++++++++++++++++ .../hosts/moon/etc/strongswan.conf | 16 ++++++ .../hosts/moon/etc/swanctl/swanctl.conf | 25 ++++++++ .../ha/active-passive-multi-ke/posttest.dat | 22 +++++++ .../ha/active-passive-multi-ke/pretest.dat | 27 +++++++++ .../ha/active-passive-multi-ke/test.conf | 25 ++++++++ testing/tests/ha/active-passive/evaltest.dat | 2 +- 17 files changed, 381 insertions(+), 3 deletions(-) create mode 100644 testing/tests/ha/active-passive-multi-ke/description.txt create mode 100644 testing/tests/ha/active-passive-multi-ke/evaltest.dat create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/iptables.rules create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/strongswan.conf create mode 100755 testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/swanctl/swanctl.conf create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/strongswan.conf create mode 100755 testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/swanctl/swanctl.conf create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/swanctl/swanctl.conf create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/iptables.rules create mode 100644 testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/ha/active-passive-multi-ke/posttest.dat create mode 100644 testing/tests/ha/active-passive-multi-ke/pretest.dat create mode 100644 testing/tests/ha/active-passive-multi-ke/test.conf diff --git a/testing/scripts/build-certs-chroot b/testing/scripts/build-certs-chroot index 161139f04..0cafb99ce 100755 --- a/testing/scripts/build-certs-chroot +++ b/testing/scripts/build-certs-chroot @@ -733,8 +733,8 @@ mkdir -p ${TEST}/hosts/alice/${SWANCTL_DIR}/x509 cp ${TEST_KEY} ${TEST}/hosts/alice/${SWANCTL_DIR}/rsa cp ${TEST_CERT} ${TEST}/hosts/alice/${SWANCTL_DIR}/x509 -# Put a copy into the ha/active-passive and swanctl/redirect-active scenarios -for t in ha/active-passive ikev2/redirect-active +# Put a copy into the ha/active-passive, ha/active-passive-multi-ke and swanctl/redirect-active scenarios +for t in ha/active-passive ha/active-passive-multi-ke ikev2/redirect-active do TEST="${TEST_DIR}/${t}" for h in alice moon diff --git a/testing/tests/ha/active-passive-multi-ke/description.txt b/testing/tests/ha/active-passive-multi-ke/description.txt new file mode 100644 index 000000000..750048073 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/description.txt @@ -0,0 +1,8 @@ +The roadwarriors carol and dave set up a connection each using +multiple key exchanges to the virtual gateway mars implemented by the +two real gateways alice and moon in a High Availability +(HA) setup based on ClusterIP. The HA synchronization link between the +two gateways is secured by an IPsec transport connection. At the outset +alice is the active and moon is the passive gateway. +After alice gets killed moon automatically takes over +all existing IKE_SAs and CHILD_SAs. diff --git a/testing/tests/ha/active-passive-multi-ke/evaltest.dat b/testing/tests/ha/active-passive-multi-ke/evaltest.dat new file mode 100644 index 000000000..fb872f94c --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/evaltest.dat @@ -0,0 +1,34 @@ +alice::cat /var/log/daemon.log::HA segment 1 was not handled, taking::YES +moon:: cat /var/log/daemon.log::remote node takes segment 1::YES +alice::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.10 local-port=500 local-id=10.1.0.10 remote-host=10.1.0.1 remote-port=500 remote-id=10.1.0.1.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]] remote-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]]::YES +alice::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.10 local-port=500 local-id=10.1.0.10 remote-host=10.1.0.1 remote-port=500 remote-id=10.1.0.1.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]] remote-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]]::YES +alice::swanctl --list-sas --ike-id 3 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096 ake3=ML_KEM_768.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +alice::swanctl --list-sas --ike-id 4 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*net.*reqid=3 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.1 local-port=500 local-id=10.1.0.1 remote-host=10.1.0.10 remote-port=500 remote-id=10.1.0.10.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]] remote-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]]::YES +moon::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.1 local-port=500 local-id=10.1.0.1 remote-host=10.1.0.10 remote-port=500 remote-id=10.1.0.10.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]] remote-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]]::YES +moon ::swanctl --list-sas --ike-id 3 --raw 2> /dev/null::rw.*version=2 state=PASSIVE local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096 ake3=ML_KEM_768.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon ::swanctl --list-sas --ike-id 4 --raw 2> /dev/null::rw.*version=2 state=PASSIVE local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*net.*reqid=3 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.5 remote-port=4500 remote-id=mars.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.5 remote-port=4500 remote-id=mars.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES +alice::cat /var/log/daemon.log::HA segment 1 activated::YES +alice::cat /var/log/daemon.log::handling HA CHILD_SA::YES +moon:: cat /var/log/daemon.log::installed HA CHILD_SA::YES +carol::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES +dave:: ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES +alice::ip xfrm policy flush::no output expected::NO +alice::ip xfrm state flush::no output expected::NO +alice::systemctl kill -s SIGKILL strongswan::no output expected::NO +carol::sleep 2::no output expected::NO +moon:: cat /var/log/daemon.log::no heartbeat received, taking all segments::YES +moon ::swanctl --list-sas --ike-id 3 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon ::swanctl --list-sas --ike-id 4 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=MODP_4096.*child-sas.*net.*reqid=3 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +carol::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES +dave:: ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES +carol::tcpdump::IP carol.strongswan.org > mars.strongswan.org: ESP::YES +carol::tcpdump::IP mars.strongswan.org > carol.strongswan.org: ESP::YES +dave::tcpdump::IP dave.strongswan.org > mars.strongswan.org: ESP::YES +dave::tcpdump::IP mars.strongswan.org > dave.strongswan.org: ESP::YES +venus::tcpdump::IP carol.strongswan.org > venus.strongswan.org: ICMP echo request::YES +venus::tcpdump::IP venus.strongswan.org > carol.strongswan.org: ICMP echo reply::YES +venus::tcpdump::IP dave.strongswan.org > venus.strongswan.org: ICMP echo request::YES +venus::tcpdump::IP venus.strongswan.org > dave.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/iptables.rules b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/iptables.rules new file mode 100644 index 000000000..873578632 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/iptables.rules @@ -0,0 +1,57 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# forward ESP-tunneled traffic +-A FORWARD -i eth1 -m policy --dir in --pol ipsec --proto esp -s PH_IP_CAROL -j ACCEPT +-A FORWARD -i eth1 -m policy --dir in --pol ipsec --proto esp -s PH_IP_DAVE -j ACCEPT +-A FORWARD -o eth1 -m policy --dir out --pol ipsec --proto esp -j ACCEPT + +# clusterip rules +-A INPUT -i eth1 -d 192.168.0.5 -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:c0:a8:00:05 --total-nodes 1 --local-node 0 +-A INPUT -i eth0 -d 10.1.0.5 -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:0a:01:00:05 --total-nodes 1 --local-node 0 + +# allow esp +-A INPUT -p 50 -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_CAROL -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_DAVE -j ACCEPT + +# allow esp on internal interface +-A OUTPUT -o eth0 -s PH_IP_ALICE -d PH_IP_MOON1 -p 50 -j ACCEPT + +# allow IKE on internal interface +-A INPUT -i eth0 -d PH_IP_ALICE -s PH_IP_MOON1 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -s PH_IP_ALICE -d PH_IP_MOON1 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow IKE +-A INPUT -i eth1 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth1 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow crl fetch from winnetou +-A INPUT -i eth1 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth1 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT + +# allow heartbeat +-A INPUT -i eth0 -d PH_IP_ALICE -s PH_IP_MOON1 -p udp --dport 4510 --sport 4510 -j ACCEPT +-A OUTPUT -o eth0 -s PH_IP_ALICE -d PH_IP_MOON1 -p udp --dport 4510 --sport 4510 -j ACCEPT + +# allow ICMP type 3 +-A INPUT -i eth0 -d PH_IP_ALICE -s PH_IP_MOON1 -p icmp --icmp-type 3 -j ACCEPT +-A OUTPUT -o eth0 -s PH_IP_ALICE -d PH_IP_MOON1 -p icmp --icmp-type 3 -j ACCEPT + +# allow IGMP multicasts +-A INPUT -d 224.0.0.1 -p igmp -j ACCEPT +-A OUTPUT -s 224.0.0.1 -p igmp -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +COMMIT diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/strongswan.conf b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/strongswan.conf new file mode 100644 index 000000000..8f57ee9d3 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/strongswan.conf @@ -0,0 +1,17 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon-systemd { + load = random nonce openssl pem pkcs1 curl revocation vici kernel-netlink socket-default ha ml + + plugins { + ha { + local = PH_IP_ALICE + remote = PH_IP_MOON1 + secret = PliyxREnfoPaSXDJx1NrlH0kkKXT/LWZ + segment_count = 1 + fifo_interface = yes + monitor = yes + } + } +} + diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/swanctl/swanctl.conf b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..e80ee4289 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/alice/etc/swanctl/swanctl.conf @@ -0,0 +1,25 @@ +connections { + + rw { + local_addrs = 192.168.0.5 + + local { + auth = pubkey + certs = marsCert.pem + id = mars.strongswan.org + } + remote { + auth = pubkey + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519-ke1_modp4096-ke3_mlkem768-ke3_none + } + } + version = 2 + proposals = aes128-sha256-x25519-ke1_modp4096-ke3_mlkem768-ke3_none + } +} diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/strongswan.conf b/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/strongswan.conf new file mode 100644 index 000000000..53ec85794 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon-systemd { + load = random nonce openssl pem pkcs1 curl revocation vici kernel-netlink socket-default updown ml +} diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..5b18917cf --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,27 @@ +connections { + + home { + local_addrs = 192.168.0.100 + remote_addrs = 192.168.0.5 + + local { + auth = pubkey + certs = carolCert.pem + id = carol@strongswan.org + } + remote { + auth = pubkey + id = mars.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519-ke1_modp4096-ke3_mlkem768 + } + } + version = 2 + proposals = aes128-sha256-x25519-ke1_modp4096-ke3_mlkem768 + } +} diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/strongswan.conf b/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/strongswan.conf new file mode 100644 index 000000000..e785a90cd --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon-systemd { + load = random nonce openssl pem pkcs1 curl revocation vici kernel-netlink socket-default updown +} + diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..dc3ccf0fc --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,27 @@ +connections { + + home { + local_addrs = 192.168.0.200 + remote_addrs = 192.168.0.5 + + local { + auth = pubkey + certs = daveCert.pem + id = dave@strongswan.org + } + remote { + auth = pubkey + id = mars.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519-ke1_modp4096 + } + } + version = 2 + proposals = aes128-sha256-x25519-ke1_modp4096 + } +} diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/iptables.rules b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/iptables.rules new file mode 100644 index 000000000..09df2225c --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/iptables.rules @@ -0,0 +1,57 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# forward ESP-tunneled traffic +-A FORWARD -m policy -i eth0 --dir in --pol ipsec --proto esp -s PH_IP_CAROL -j ACCEPT +-A FORWARD -m policy -i eth0 --dir in --pol ipsec --proto esp -s PH_IP_DAVE -j ACCEPT +-A FORWARD -m policy -o eth0 --dir out --pol ipsec --proto esp -j ACCEPT + +# clusterip rules +-A INPUT -i eth0 -d 192.168.0.5 -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:c0:a8:00:05 --total-nodes 1 --local-node 0 +-A INPUT -i eth1 -d 10.1.0.5 -j CLUSTERIP --new --hashmode sourceip --clustermac 01:00:0a:01:00:05 --total-nodes 1 --local-node 0 + +# allow esp +-A INPUT -p 50 -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_CAROL -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_DAVE -j ACCEPT + +# allow esp on internal interface +-A OUTPUT -o eth1 -s PH_IP_MOON1 -d PH_IP_ALICE -p 50 -j ACCEPT + +# allow IKE on internal interface +-A INPUT -i eth1 -d PH_IP_MOON1 -s PH_IP_ALICE -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth1 -s PH_IP_MOON1 -d PH_IP_ALICE -p udp --dport 500 --sport 500 -j ACCEPT + +# allow IKE +-A INPUT -i eth0 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth0 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow crl fetch from winnetou +-A INPUT -i eth0 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth0 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT + +# allow heartbeat +-A INPUT -i eth1 -d PH_IP_MOON1 -s PH_IP_ALICE -p udp --dport 4510 --sport 4510 -j ACCEPT +-A OUTPUT -o eth1 -s PH_IP_MOON1 -d PH_IP_ALICE -p udp --dport 4510 --sport 4510 -j ACCEPT + +# allow ICMP type 3 +-A INPUT -i eth1 -d PH_IP_MOON1 -s PH_IP_ALICE -p icmp --icmp-type 3 -j ACCEPT +-A OUTPUT -o eth1 -s PH_IP_MOON1 -d PH_IP_ALICE -p icmp --icmp-type 3 -j ACCEPT + +# allow IGMP multicasts +-A INPUT -d 224.0.0.1 -p igmp -j ACCEPT +-A OUTPUT -s 224.0.0.1 -p igmp -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +COMMIT diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/strongswan.conf b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..72bc21da7 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/strongswan.conf @@ -0,0 +1,16 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon-systemd { + load = random nonce openssl pem pkcs1 curl revocation vici kernel-netlink socket-default ha ml + + plugins { + ha { + local = PH_IP_MOON1 + remote = PH_IP_ALICE + secret = PliyxREnfoPaSXDJx1NrlH0kkKXT/LWZ + segment_count = 1 + fifo_interface = yes + monitor = yes + } + } +} diff --git a/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..e80ee4289 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,25 @@ +connections { + + rw { + local_addrs = 192.168.0.5 + + local { + auth = pubkey + certs = marsCert.pem + id = mars.strongswan.org + } + remote { + auth = pubkey + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519-ke1_modp4096-ke3_mlkem768-ke3_none + } + } + version = 2 + proposals = aes128-sha256-x25519-ke1_modp4096-ke3_mlkem768-ke3_none + } +} diff --git a/testing/tests/ha/active-passive-multi-ke/posttest.dat b/testing/tests/ha/active-passive-multi-ke/posttest.dat new file mode 100644 index 000000000..e62d23ef5 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/posttest.dat @@ -0,0 +1,22 @@ +carol::systemctl stop strongswan +dave::systemctl stop strongswan +moon::ip xfrm policy flush::no output expected::NO +moon::ip xfrm state flush::no output expected::NO +moon::systemctl kill -s SIGKILL strongswan::no output expected::NO +moon::cd /etc/swanctl; rm rsa/marsKey.pem x509/marsCert.pem +alice::cd /etc/swanctl; rm rsa/marsKey.pem x509/marsCert.pem +moon::iptables-restore < /etc/iptables.flush +alice::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush +moon::ip addr del 192.168.0.5/24 dev eth0 +moon::ip addr del 10.1.0.5/16 dev eth1 +alice::ip addr del 192.168.0.5/24 dev eth1 +alice::ip addr del 10.1.0.5/16 dev eth0 +alice::ifdown eth1 +venus::ip route del default via 10.1.0.5 dev eth0 +venus::ip route add default via 10.1.0.1 dev eth0 +alice::sed -i s/Restart=no/Restart=on-abnormal/ /lib/systemd/system/strongswan.service +alice::systemctl daemon-reload +moon::sed -i s/Restart=no/Restart=on-abnormal/ /lib/systemd/system/strongswan.service +moon::systemctl daemon-reload diff --git a/testing/tests/ha/active-passive-multi-ke/pretest.dat b/testing/tests/ha/active-passive-multi-ke/pretest.dat new file mode 100644 index 000000000..bf5eb8329 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/pretest.dat @@ -0,0 +1,27 @@ +alice::sed -i s/Restart=on-abnormal/Restart=no/ /lib/systemd/system/strongswan.service +alice::systemctl daemon-reload +moon::sed -i s/Restart=on-abnormal/Restart=no/ /lib/systemd/system/strongswan.service +moon::systemctl daemon-reload +moon::ip addr add 192.168.0.5/24 dev eth0 +moon::ip addr add 10.1.0.5/16 dev eth1 +alice::ifup eth1 +alice::ip addr add 192.168.0.5/24 dev eth1 +alice::ip addr add 10.1.0.5/16 dev eth0 +venus::ip route del default via 10.1.0.1 dev eth0 +venus::ip route add default via 10.1.0.5 dev eth0 +moon::iptables-restore < /etc/iptables.rules +alice::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::cd /etc/swanctl; rm rsa/moonKey.pem x509/moonCert.pem +alice::cd /etc/swanctl; rm rsa/aliceKey.pem x509/aliceCert.pem +moon::systemctl start strongswan +alice::systemctl start strongswan +moon::sleep 2 +alice::echo "+1" > /var/run/charon.ha +carol::systemctl start strongswan +dave::systemctl start strongswan +carol::expect-connection home +dave::expect-connection home +carol::swanctl --initiate --child home +dave::swanctl --initiate --child home diff --git a/testing/tests/ha/active-passive-multi-ke/test.conf b/testing/tests/ha/active-passive-multi-ke/test.conf new file mode 100644 index 000000000..43f8bbcc3 --- /dev/null +++ b/testing/tests/ha/active-passive-multi-ke/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice venus moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-v-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="venus carol dave" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="alice moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/ha/active-passive/evaltest.dat b/testing/tests/ha/active-passive/evaltest.dat index aa3576793..418cadf85 100644 --- a/testing/tests/ha/active-passive/evaltest.dat +++ b/testing/tests/ha/active-passive/evaltest.dat @@ -5,7 +5,7 @@ alice::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::ha.*version=2 state=EST alice::swanctl --list-sas --ike-id 3 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES alice::swanctl --list-sas --ike-id 4 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=3 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.1 local-port=500 local-id=10.1.0.1 remote-host=10.1.0.10 remote-port=500 remote-id=10.1.0.10.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]] remote-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]]::YES -moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.1 local-port=500 local-id=10.1.0.1 remote-host=10.1.0.10 remote-port=500 remote-id=10.1.0.10.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]] remote-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]]::YES +moon::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::ha.*version=2 state=ESTABLISHED local-host=10.1.0.1 local-port=500 local-id=10.1.0.1 remote-host=10.1.0.10 remote-port=500 remote-id=10.1.0.10.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_256.*child-sas.*ha.*reqid=1 state=INSTALLED mode=TRANSPORT.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.1/32\[icmp] 10.1.0.1/32\[udp/4510]] remote-ts=\[10.1.0.10/32\[icmp] 10.1.0.10/32\[udp/4510]]::YES moon ::swanctl --list-sas --ike-id 3 --raw 2> /dev/null::rw.*version=2 state=PASSIVE local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES moon ::swanctl --list-sas --ike-id 4 --raw 2> /dev/null::rw.*version=2 state=PASSIVE local-host=192.168.0.5 local-port=4500 local-id=mars.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net.*reqid=3 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.5 remote-port=4500 remote-id=mars.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES From 97bd0e2297ed1df69337188168b338ed404b64e0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 6 Dec 2024 15:02:13 +0100 Subject: [PATCH 3/3] ha: Destroy incomplete IKE_SAs after de-/activating a segment The node that gets activated usually won't be able to complete the IKE_SA mainly because the IKE keys are now derived delayed, so the key material required to process a message often won't be available (only later IKE_AUTH messages and retransmits of earlier messages that the active node already received and synced the keys for may be decrypted). A second issue affects IKE_SAs with multiple key exchanges. Because the IntAuth value(s) are currently not synced, which are necessary to verify/create the AUTH payloads, the IKE_AUTH exchange couldn't be completed. --- src/libcharon/plugins/ha/ha_segments.c | 33 +++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/ha/ha_segments.c b/src/libcharon/plugins/ha/ha_segments.c index 996b2483e..afb76b39e 100644 --- a/src/libcharon/plugins/ha/ha_segments.c +++ b/src/libcharon/plugins/ha/ha_segments.c @@ -135,9 +135,11 @@ static void enable_disable(private_ha_segments_t *this, u_int segment, { ike_sa_t *ike_sa; enumerator_t *enumerator; - ike_sa_state_t old, new; + ike_sa_state_t old, new, cur; ha_message_t *message = NULL; ha_message_type_t type; + array_t *to_destroy; + uint32_t unique_id; bool changes = FALSE; if (segment > this->count) @@ -172,11 +174,13 @@ static void enable_disable(private_ha_segments_t *this, u_int segment, if (changes) { + to_destroy = array_create(sizeof(u_int), 0); enumerator = charon->ike_sa_manager->create_enumerator( charon->ike_sa_manager, TRUE); while (enumerator->enumerate(enumerator, &ike_sa)) { - if (ike_sa->get_state(ike_sa) != old) + cur = ike_sa->get_state(ike_sa); + if (cur != old && cur != IKE_CONNECTING) { continue; } @@ -187,11 +191,34 @@ static void enable_disable(private_ha_segments_t *this, u_int segment, if (this->kernel->get_segment(this->kernel, ike_sa->get_other_host(ike_sa)) == segment) { - ike_sa->set_state(ike_sa, new); + if (cur == IKE_CONNECTING) + { + unique_id = ike_sa->get_unique_id(ike_sa); + array_insert(to_destroy, ARRAY_TAIL, &unique_id); + } + else + { + ike_sa->set_state(ike_sa, new); + } } } enumerator->destroy(enumerator); log_segments(this, enable, segment); + + while (array_remove(to_destroy, ARRAY_HEAD, &unique_id)) + { + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + unique_id); + if (ike_sa) + { + DBG1(DBG_IKE, "destroying incomplete IKE_SA %s[%d] after " + "%sactivating HA segment %d", ike_sa->get_name(ike_sa), + unique_id, segment, enable ? "" : "de"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + } + array_destroy(to_destroy); } if (notify)