Commit e059077d3f for strongswan.org

commit e059077d3f3e307e78be7f91e5648aa5f94916a8
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Jul 27 15:05:45 2026 +0200

    eap-ttls: Merge returned auth-cfg with inner EAP method and propagate EAP-Identity

    This fixes several issues with binding identities to the IKE SA.

    If the client is authenticated with a certificate, the previous code still
    used the client's proclaimed inner EAP-Identity when starting the EAP-TNC
    method.  So that method would potentially operate on an unverified
    identity.

    Second, if the inner EAP method overrides the client identity (the only
    one is currently EAP-MSCHAPV2), the missing merge meant that the outer
    IKE/EAP identity could potentially be unconfirmed.

    For inner methods that don't override the identity (e.g. EAP-MD5), not
    propagating the inner EAP-Identity could potentially have the same
    effect.

    While the implementation returned the auth-cfg of the TLS exchange
    since the first referenced commit, this was mainly intended to enforce
    public key constraints.  So it didn't cover the phase 2 EAP methods.
    For some reason EAP-PEAP did not get that method at all in that
    changeset, so we'll add that in the next commit.

    Fixes: 0864a31d13ff ("eap-ttls: Support EAP auth information getter in EAP-TTLS")
    Fixes: 79f2102cb442 ("implemented server side support for EAP-TTLS")
    Fixes: CVE-2026-78134

diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls.c b/src/libcharon/plugins/eap_ttls/eap_ttls.c
index d8ad781f09..3df78bba4b 100644
--- a/src/libcharon/plugins/eap_ttls/eap_ttls.c
+++ b/src/libcharon/plugins/eap_ttls/eap_ttls.c
@@ -40,6 +40,25 @@ struct private_eap_ttls_t {
 	 * TLS stack, wrapped by EAP helper
 	 */
 	tls_eap_t *tls_eap;
+
+	/**
+	 * Role
+	 */
+	bool is_server;
+
+	/**
+	 * Actual server/client implementation
+	 */
+	union {
+		tls_application_t *application;
+		eap_ttls_server_t *server;
+		eap_ttls_peer_t *client;
+	} impl;
+
+	/**
+	 * Cached auth data for TLS and inner EAP methods
+	 */
+	auth_cfg_t *auth;
 };

 /** Maximum number of EAP-TTLS messages/fragments allowed */
@@ -116,13 +135,31 @@ METHOD(eap_method_t, is_mutual, bool,
 METHOD(eap_method_t, get_auth, auth_cfg_t*,
 	private_eap_ttls_t *this)
 {
-	return this->tls_eap->get_auth(this->tls_eap);
+	if (!this->auth)
+	{
+		auth_cfg_t *inner;
+
+		this->auth = auth_cfg_create();
+		this->auth->merge(this->auth,
+						  this->tls_eap->get_auth(this->tls_eap), FALSE);
+		if (this->is_server)
+		{
+			inner = this->impl.server->get_auth(this->impl.server);
+		}
+		else
+		{
+			inner = this->impl.client->get_auth(this->impl.client);
+		}
+		this->auth->merge(this->auth, inner, FALSE);
+	}
+	return this->auth;
 }

 METHOD(eap_method_t, destroy, void,
 	private_eap_ttls_t *this)
 {
 	this->tls_eap->destroy(this->tls_eap);
+	DESTROY_IF(this->auth);
 	free(this);
 }

@@ -153,6 +190,10 @@ static eap_ttls_t *eap_ttls_create(identification_t *server,
 				.destroy = _destroy,
 			},
 		},
+		.is_server = is_server,
+		.impl = {
+			.application = application,
+		},
 	);
 	if (is_server && !lib->settings->get_bool(lib->settings,
 								"%s.plugins.eap-ttls.request_peer_auth", FALSE,
diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
index 63126a5c3a..f8229f5e0f 100644
--- a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
+++ b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
@@ -54,6 +54,11 @@ struct private_eap_ttls_peer_t {
 	 */
 	eap_method_t *method;

+	/**
+	 * Auth data for phase 2 method
+	 */
+	auth_cfg_t *auth;
+
 	/**
      * Pending outbound EAP message
 	 */
@@ -215,6 +220,11 @@ METHOD(tls_application_t, process, status_t,
 	switch (status)
 	{
 		case SUCCESS:
+			if (this->method->get_auth)
+			{
+				this->auth->merge(this->auth,
+								  this->method->get_auth(this->method), FALSE);
+			}
 			this->method->destroy(this->method);
 			this->method = NULL;
 			/* fall through to NEED_MORE */
@@ -275,11 +285,18 @@ METHOD(tls_application_t, build, status_t,
 	return INVALID_STATE;
 }

+METHOD(eap_ttls_peer_t, get_auth, auth_cfg_t*,
+	private_eap_ttls_peer_t *this)
+{
+	return this->auth;
+}
+
 METHOD(tls_application_t, destroy, void,
 	private_eap_ttls_peer_t *this)
 {
 	this->server->destroy(this->server);
 	this->peer->destroy(this->peer);
+	this->auth->destroy(this->auth);
 	DESTROY_IF(this->method);
 	DESTROY_IF(this->out);
 	this->avp->destroy(this->avp);
@@ -301,10 +318,12 @@ eap_ttls_peer_t *eap_ttls_peer_create(identification_t *server,
 				.build = _build,
 				.destroy = _destroy,
 			},
+			.get_auth = _get_auth,
 		},
 		.server = server->clone(server),
 		.peer = peer->clone(peer),
 		.start_phase2 = TRUE,
+		.auth = auth_cfg_create(),
 		.avp = eap_ttls_avp_create(),
 	);

diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.h b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.h
index 0c3d90a45b..69a8435aef 100644
--- a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.h
+++ b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.h
@@ -37,6 +37,13 @@ struct eap_ttls_peer_t {
 	 * Implements the TLS application data handler.
 	 */
 	tls_application_t application;
+
+	/**
+	 * Get authentication details of this EAP method and its inner method(s).
+	 *
+	 * @return				auth method, internal data
+	 */
+	auth_cfg_t *(*get_auth)(eap_ttls_peer_t *this);
 };

 /**
diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_server.c b/src/libcharon/plugins/eap_ttls/eap_ttls_server.c
index 86f5037651..d07731009e 100644
--- a/src/libcharon/plugins/eap_ttls/eap_ttls_server.c
+++ b/src/libcharon/plugins/eap_ttls/eap_ttls_server.c
@@ -60,6 +60,11 @@ struct private_eap_ttls_server_t {
 	 */
 	eap_method_t *method;

+	/**
+	 * Auth data for phase 2 method
+	 */
+	auth_cfg_t *auth;
+
 	/**
      * Pending outbound EAP message
 	 */
@@ -220,6 +225,10 @@ METHOD(tls_application_t, process, status_t,
 	if (!received_vendor && received_type == EAP_IDENTITY)
 	{
 		chunk_t eap_id;
+		bool peer_auth;
+
+		peer_auth = lib->settings->get_bool(lib->settings,
+					"%s.plugins.eap-ttls.request_peer_auth", FALSE, lib->ns);

 		if (this->method == NULL)
 		{
@@ -245,9 +254,22 @@ METHOD(tls_application_t, process, status_t,

 		if (this->method->get_msk(this->method, &eap_id) == SUCCESS)
 		{
-			this->peer->destroy(this->peer);
-			this->peer = identification_create_from_data(eap_id);
-			DBG1(DBG_IKE, "received EAP identity '%Y'", this->peer);
+			identification_t *id;
+
+			id = identification_create_from_data(eap_id);
+			if (peer_auth && !id->equals(id, this->peer))
+			{
+				DBG1(DBG_IKE, "received tunneled EAP identity '%Y', keeping "
+					 "certificate-authenticated identity '%Y'", id, this->peer);
+				id->destroy(id);
+			}
+			else
+			{
+				DBG1(DBG_IKE, "received EAP identity '%Y'", id);
+				this->auth->add(this->auth, AUTH_RULE_EAP_IDENTITY, id);
+				this->peer->destroy(this->peer);
+				this->peer = id->clone(id);
+			}
 		}

 		in->destroy(in);
@@ -255,8 +277,7 @@ METHOD(tls_application_t, process, status_t,
 		this->method = NULL;

 		/* Start Phase 2 of EAP-TTLS authentication */
-		if (lib->settings->get_bool(lib->settings,
-					"%s.plugins.eap-ttls.request_peer_auth", FALSE, lib->ns))
+		if (peer_auth)
 		{
 			return start_phase2_tnc(this, EAP_TLS);
 		}
@@ -279,6 +300,20 @@ METHOD(tls_application_t, process, status_t,
 	switch (status)
 	{
 		case SUCCESS:
+			if (this->method->get_auth)
+			{
+				identification_t *id;
+				auth_cfg_t *auth;
+
+				auth = this->method->get_auth(this->method);
+				id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
+				if (id)
+				{
+					this->peer->destroy(this->peer);
+					this->peer = id->clone(id);
+				}
+				this->auth->merge(this->auth, auth, FALSE);
+			}
 			DBG1(DBG_IKE, "%N phase2 authentication of '%Y' with %N successful",
 							eap_type_names, EAP_TTLS, this->peer,
 							eap_type_names, type);
@@ -349,11 +384,18 @@ METHOD(tls_application_t, build, status_t,
 	return INVALID_STATE;
 }

+METHOD(eap_ttls_server_t, get_auth, auth_cfg_t*,
+	private_eap_ttls_server_t *this)
+{
+	return this->auth;
+}
+
 METHOD(tls_application_t, destroy, void,
 	private_eap_ttls_server_t *this)
 {
 	this->server->destroy(this->server);
 	this->peer->destroy(this->peer);
+	this->auth->destroy(this->auth);
 	DESTROY_IF(this->method);
 	DESTROY_IF(this->out);
 	this->avp->destroy(this->avp);
@@ -375,11 +417,13 @@ eap_ttls_server_t *eap_ttls_server_create(identification_t *server,
 				.build = _build,
 				.destroy = _destroy,
 			},
+			.get_auth = _get_auth,
 		},
 		.server = server->clone(server),
-		.peer = peer->clone(peer),
+		.auth = auth_cfg_create(),
 		.start_phase2 = TRUE,
 		.start_phase2_tnc = TRUE,
+		.peer = peer->clone(peer),
 		.avp = eap_ttls_avp_create(),
 	);

diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_server.h b/src/libcharon/plugins/eap_ttls/eap_ttls_server.h
index 1e13f55c4c..3348706cf2 100644
--- a/src/libcharon/plugins/eap_ttls/eap_ttls_server.h
+++ b/src/libcharon/plugins/eap_ttls/eap_ttls_server.h
@@ -37,6 +37,13 @@ struct eap_ttls_server_t {
 	 * Implements the TLS application data handler.
 	 */
 	tls_application_t application;
+
+	/**
+	 * Get authentication details of this EAP method and its inner method(s).
+	 *
+	 * @return				auth method, internal data
+	 */
+	auth_cfg_t *(*get_auth)(eap_ttls_server_t *this);
 };

 /**