From a34fb034926ccfebe556eb66aae5572a3b0b7e14 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 27 Jul 2026 08:53:50 +0200 Subject: [PATCH] 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: 8503077175cd ("ikev2: Reject CREATE_CHILD_SA exchange on unestablished IKE_SAs") Fixes: c60c7694d2d8 ("merged tasking branch into trunk") Fixes: CVE-2026-78135 --- src/libcharon/sa/ikev2/task_manager_v2.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 5a19ce85b331..f9e9ab933a69 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1134,9 +1134,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: @@ -1177,14 +1186,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)) { -- 2.43.0