From: Tobias Brunner Date: Thu, 5 Dec 2024 10:55:52 +0000 (+0100) Subject: ha: Add support to sync IKE and Child SAs with multiple key exchanges X-Git-Tag: 6.0.1rc1~11^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e7848e36fa6ce0f6a19a233173fd302e7e86a5b2;p=thirdparty%2Fstrongswan.git 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 --- diff --git a/src/libcharon/plugins/ha/ha_cache.c b/src/libcharon/plugins/ha/ha_cache.c index 53c6f84e6b..12f38a3c63 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); + } + messages->destroy(messages); + messages = entry->updates->create_enumerator(entry->updates); + while (messages->enumerate(messages, &message)) { this->socket->push(this->socket, message); } - updates->destroy(updates); + 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 364fe1d5f5..9d8fca9eb4 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 b0ec3d9cf0..5de26a65a2 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); - } - if (encr) - { - proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); - } - if (prf) - { - proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 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 (dh_grp) + else { - 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 e6dab84579..f6983ff466 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 1a528d46cc..8fdb3782e7 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 618245cb0f..04a77618c3 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 05e743c869..ee76433f73 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 6e6594fc21..0103878ed4 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. */