Commit 1f9abe01d7 for strongswan.org

commit 1f9abe01d7737aec64aeed378e4975c94d8448c2
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Fri Jun 5 13:50:55 2026 +0200

    ikev2: Respond with STATE_NOT_FOUND if IKE_FOLLOWUP_KE arrives outside of a rekeying

diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index 5a19ce85b3..b2aab6f8d6 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1121,6 +1121,44 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
 	return SUCCESS;
 }

+/**
+ * Send a notify back to the sender
+ */
+static void send_notify_response(private_task_manager_t *this,
+								 message_t *request, notify_type_t type,
+								 chunk_t data)
+{
+	message_t *response;
+	packet_t *packet;
+	host_t *me, *other;
+
+	response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+	response->set_exchange_type(response, request->get_exchange_type(request));
+	response->set_request(response, FALSE);
+	response->set_message_id(response, request->get_message_id(request));
+	response->add_notify(response, FALSE, type, data);
+	me = this->ike_sa->get_my_host(this->ike_sa);
+	if (me->is_anyaddr(me))
+	{
+		me = request->get_destination(request);
+		this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+	}
+	other = this->ike_sa->get_other_host(this->ike_sa);
+	if (other->is_anyaddr(other))
+	{
+		other = request->get_source(request);
+		this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+	}
+	response->set_source(response, me->clone(me));
+	response->set_destination(response, other->clone(other));
+	if (this->ike_sa->generate_message(this->ike_sa, response,
+									   &packet) == SUCCESS)
+	{
+		charon->sender->send(charon->sender, packet);
+	}
+	response->destroy(response);
+}
+
 /**
  * handle an incoming request message
  */
@@ -1233,6 +1271,21 @@ static status_t process_request(private_task_manager_t *this,
 				array_insert(this->passive_tasks, ARRAY_TAIL, task);
 				break;
 			}
+			case IKE_FOLLOWUP_KE:
+			{
+				if (state == IKE_CREATED ||
+					state == IKE_CONNECTING)
+				{
+					DBG1(DBG_IKE, "received IKE_FOLLOWUP_KE request for "
+						 "unestablished IKE_SA, rejected");
+					return FAILED;
+				}
+				/* receiving this when we don't have an active rekey task is
+				 * an error, we send back a notify accordingly */
+				send_notify_response(this, message, STATE_NOT_FOUND,
+									 chunk_empty);
+				return SUCCESS;
+			}
 			case INFORMATIONAL:
 			{
 				enumerator = message->create_payload_enumerator(message);
@@ -1524,44 +1577,6 @@ static status_t handle_fragment(private_task_manager_t *this,
 	return status;
 }

-/**
- * Send a notify back to the sender
- */
-static void send_notify_response(private_task_manager_t *this,
-								 message_t *request, notify_type_t type,
-								 chunk_t data)
-{
-	message_t *response;
-	packet_t *packet;
-	host_t *me, *other;
-
-	response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
-	response->set_exchange_type(response, request->get_exchange_type(request));
-	response->set_request(response, FALSE);
-	response->set_message_id(response, request->get_message_id(request));
-	response->add_notify(response, FALSE, type, data);
-	me = this->ike_sa->get_my_host(this->ike_sa);
-	if (me->is_anyaddr(me))
-	{
-		me = request->get_destination(request);
-		this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
-	}
-	other = this->ike_sa->get_other_host(this->ike_sa);
-	if (other->is_anyaddr(other))
-	{
-		other = request->get_source(request);
-		this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
-	}
-	response->set_source(response, me->clone(me));
-	response->set_destination(response, other->clone(other));
-	if (this->ike_sa->generate_message(this->ike_sa, response,
-									   &packet) == SUCCESS)
-	{
-		charon->sender->send(charon->sender, packet);
-	}
-	response->destroy(response);
-}
-
 /**
  * Send an INVALID_SYNTAX notify and destroy the IKE_SA for authenticated
  * messages.
diff --git a/src/libcharon/tests/suites/test_child_rekey.c b/src/libcharon/tests/suites/test_child_rekey.c
index 4ef081a249..7305782538 100644
--- a/src/libcharon/tests/suites/test_child_rekey.c
+++ b/src/libcharon/tests/suites/test_child_rekey.c
@@ -2669,6 +2669,7 @@ START_TEST(test_collision_delayed_response_multi_ke_failure)
 	/* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
 	assert_payload(IN, PLV2_KEY_EXCHANGE);
 	assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+	assert_single_notify(OUT, STATE_NOT_FOUND);
 	exchange_test_helper->process_message(exchange_test_helper, b, NULL);

 	/* child_rekey/child_updown */
diff --git a/src/libcharon/tests/suites/test_ike_rekey.c b/src/libcharon/tests/suites/test_ike_rekey.c
index 2c4102201d..0ce3dd0662 100644
--- a/src/libcharon/tests/suites/test_ike_rekey.c
+++ b/src/libcharon/tests/suites/test_ike_rekey.c
@@ -1918,6 +1918,7 @@ START_TEST(test_collision_delayed_response_multi_ke_failure)
 	/* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
 	assert_payload(IN, PLV2_KEY_EXCHANGE);
 	assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+	assert_single_notify(OUT, STATE_NOT_FOUND);
 	exchange_test_helper->process_message(exchange_test_helper, b, NULL);

 	/* ike_updown/rekey/child_updown */