]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
ha: Add support to sync IKE and Child SAs with multiple key exchanges
authorTobias Brunner <tobias@strongswan.org>
Thu, 5 Dec 2024 10:55:52 +0000 (11:55 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 28 Feb 2025 15:02:41 +0000 (16:02 +0100)
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 <thomas.egerer@secunet.com>
src/libcharon/plugins/ha/ha_cache.c
src/libcharon/plugins/ha/ha_child.c
src/libcharon/plugins/ha/ha_dispatcher.c
src/libcharon/plugins/ha/ha_ike.c
src/libcharon/plugins/ha/ha_message.c
src/libcharon/plugins/ha/ha_message.h
src/libcharon/sa/ikev2/tasks/child_create.c
src/libstrongswan/crypto/transform.h

index 53c6f84e6be0e29b4b1d0a4f3808e479b4bec87a..12f38a3c63aae3a43c827db05de2b81577f7c5f5 100644 (file)
@@ -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);
index 364fe1d5f5f12f9e61eeb16f7bea259dd8efeab5..9d8fca9eb464c571b57296835251b34d4b05230f 100644 (file)
@@ -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();
index b0ec3d9cf0276fd45a0b287530f2157eecc1298c..5de26a65a2709643214d125c3fc9841a69dda3f4 100644 (file)
@@ -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 <processing/jobs/adopt_children_job.h>
 
 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");
index e6dab84579ce39993ef317e3cb3c3fdeeaaa8e3a..f6983ff46663dc1d7d1cf94dd91cf306456cff5b 100644 (file)
@@ -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;
 }
-
index 1a528d46cce4fa43035a33543069fc7da0e7ba70..8fdb3782e7b91138179b675c20b62a04a3ddfd64 100644 (file)
@@ -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;
 }
-
index 618245cb0facfeed6886471450ae104f599b3cad..04a77618c3b96fd224d119ad6c2e735422ebc23d 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2024 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
 #include <networking/host.h>
 #include <utils/identification.h>
 #include <sa/ike_sa_id.h>
+#include <crypto/proposal/proposal.h>
 #include <selectors/traffic_selector.h>
 
 /**
  * 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.
         *
index 05e743c86944755c5aaab1a5ac9d6b181ce583a5..ee76433f73456d237674fc8d8d1fff2ec684f56e 100644 (file)
@@ -33,8 +33,7 @@
 #include <processing/jobs/initiate_tasks_job.h>
 
 /** 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;
 
index 6e6594fc21d584d4d969c03144442e94d651c568..0103878ed46aecfc3686024c5290f8c1e8b34d68 100644 (file)
@@ -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.
  */