Commit 4dcb132266 for strongswan.org

commit 4dcb132266a954202509a3b4b3be99378f3e3b4d
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Jul 27 08:53:50 2026 +0200

    ikev2: Properly reject CREATE_CHILD_SA requests on unestablished IKE_SAs

    The previous check was not actually enforced as long as there were still
    tasks in the passive queue (it was originally added to fix an issue on
    initiators, so the passive queue was expected to be empty).  This allowed
    an unauthenticated attacker to potentially establish a usable Child SA
    if certain preconditions were met.

    First, it required that the initiator is authenticated with EAP so the
    authentication and the creation of the first Child SA is deferred.
    Second, the responder must either not configure an IP address pool or
    an explicit remote TS, otherwise, traffic selector negotiation fails.

    Note that the half-open IKE SA and the installed IPsec SA will be removed
    after the default timeout of 30 seconds.

    Fixes: 28b33d7cacd2 ("ike-auth: Support exchange between IKE_SA_INIT and IKE_AUTH")
    Fixes: 8503077175cd ("ikev2: Reject CREATE_CHILD_SA exchange on unestablished IKE_SAs")
    Fixes: c60c7694d2d8 ("merged tasking branch into trunk")
    Fixes: CVE-2026-78135

diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index b2aab6f8d6..ffadc2d80a 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1172,9 +1172,18 @@ static status_t process_request(private_task_manager_t *this,
 	delete_payload_t *delete;
 	ike_sa_state_t state;

+	state = this->ike_sa->get_state(this->ike_sa);
+	if (message->get_exchange_type(message) == CREATE_CHILD_SA &&
+		(state == IKE_CREATED || state == IKE_CONNECTING))
+	{
+		DBG1(DBG_IKE, "received CREATE_CHILD_SA request for "
+			 "unestablished IKE_SA, rejected");
+		return FAILED;
+	}
+
+	/* create tasks depending on request type, if not already some queued */
 	if (array_count(this->passive_tasks) == 0)
-	{	/* create tasks depending on request type, if not already some queued */
-		state = this->ike_sa->get_state(this->ike_sa);
+	{
 		switch (message->get_exchange_type(message))
 		{
 			case IKE_SA_INIT:
@@ -1215,14 +1224,6 @@ static status_t process_request(private_task_manager_t *this,
 			{	/* FIXME: we should prevent this on mediation connections */
 				bool notify_found = FALSE, ts_found = FALSE;

-				if (state == IKE_CREATED ||
-					state == IKE_CONNECTING)
-				{
-					DBG1(DBG_IKE, "received CREATE_CHILD_SA request for "
-						 "unestablished IKE_SA, rejected");
-					return FAILED;
-				}
-
 				enumerator = message->create_payload_enumerator(message);
 				while (enumerator->enumerate(enumerator, &payload))
 				{