]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
ha: Destroy incomplete IKE_SAs after de-/activating a segment
authorTobias Brunner <tobias@strongswan.org>
Fri, 6 Dec 2024 14:02:13 +0000 (15:02 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 28 Feb 2025 15:02:41 +0000 (16:02 +0100)
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

index 996b2483e9e504513c324b93ad44714ca19ed628..afb76b39ea27592432cee8659710fb5b1186ca56 100644 (file)
@@ -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)