Commit c92a599d80 for strongswan.org

commit c92a599d80ca8bdc750d2daf5899896637a93e50
Author: Jarno Mäkelä <jarno.makela@iki.fi>
Date:   Mon Oct 12 15:09:28 2026 +0300

    charon-nm: Add combined certificate/EAP VPN method

    Add the "cert-eap" method that performs two IKEv2 authentication rounds
    within one IKE_SA as described in RFC 4739.  The first round uses the
    client X.509 certificate and private key, the second uses EAP with a
    username and password.  This follows the Android client's "ikev2-cert-eap"
    type.  Every certificate source (file, ssh-agent, smartcard) works.

    Signed-off-by: Jarno Mäkelä <jarno.makela@iki.fi>

diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c
index c60bd76649..5a3b83b737 100644
--- a/src/charon-nm/nm/nm_service.c
+++ b/src/charon-nm/nm/nm_service.c
@@ -589,18 +589,23 @@ static bool add_auth_cfg_cert(NMStrongswanPluginPrivate *priv,
 	identification_t *id = NULL;
 	certificate_t *cert = NULL;
 	auth_cfg_t *auth;
-	const char *str, *method, *cert_source;
+	const char *str, *method, *cert_source, *pw = NULL;
 	chunk_t safe_file;

 	method = nm_setting_vpn_get_data_item(vpn, "method");
 	cert_source = nm_setting_vpn_get_data_item(vpn, "cert-source") ?: method;

+	/* legacy fallback, except when actually used as EAP password */
+	if (!streq(method, "cert-eap"))
+	{
+		pw = nm_setting_vpn_get_secret(vpn, "password");
+	}
+
 	if (streq(cert_source, "smartcard"))
 	{
 		char *pin;

-		pin = (char*)(nm_setting_vpn_get_secret(vpn, "pin") ?:
-					  nm_setting_vpn_get_secret(vpn, "password"));
+		pin = (char*)(nm_setting_vpn_get_secret(vpn, "pin") ?: pw);
 		if (pin)
 		{
 			id = find_smartcard_key(priv, pin);
@@ -665,7 +670,7 @@ static bool add_auth_cfg_cert(NMStrongswanPluginPrivate *priv,
 			char *secret;

 			secret = (char*)(nm_setting_vpn_get_secret(vpn, "userkey-password")
-							 ?: nm_setting_vpn_get_secret(vpn, "password"));
+							 ?: pw);
 			if (secret)
 			{
 				priv->creds->set_key_password(priv->creds, secret);
@@ -1062,6 +1067,21 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
 			return FALSE;
 		}
 	}
+	else if (streq(method, "cert-eap"))
+	{
+		if (!add_auth_cfg_cert(priv, vpn, peer_cfg, user, err))
+		{
+			peer_cfg->destroy(peer_cfg);
+			gateway->destroy(gateway);
+			return FALSE;
+		}
+		if (!add_auth_cfg_pw(priv, vpn, peer_cfg, err))
+		{
+			peer_cfg->destroy(peer_cfg);
+			gateway->destroy(gateway);
+			return FALSE;
+		}
+	}
 	else
 	{
 		g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
@@ -1190,7 +1210,7 @@ static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connectio
 {
 	NMStrongswanPluginPrivate *priv;
 	NMSettingVpn *settings;
-	const char *method, *cert_source, *path;
+	const char *method, *cert_source, *path, *pw = NULL;
 	bool need_secret = FALSE;

 	priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE((NMStrongswanPlugin*)plugin);
@@ -1203,13 +1223,19 @@ static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connectio
 			streq(method, "eap-tls") ||
 			streq(method, "key") ||
 			streq(method, "agent") ||
-			streq(method, "smartcard"))
+			streq(method, "smartcard") ||
+			streq(method, "cert-eap"))
 		{
 			cert_source = nm_setting_vpn_get_data_item(settings, "cert-source");
 			if (!cert_source)
 			{
 				cert_source = method;
 			}
+			/* legacy fallback, unless used as actual EAP password */
+			if (!streq(method, "cert-eap"))
+			{
+				pw = nm_setting_vpn_get_secret(settings, "password");
+			}
 			if (streq(cert_source, "agent"))
 			{
 				/* always request the socket/username from the current user */
@@ -1223,7 +1249,7 @@ static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connectio
 			else if (streq(cert_source, "smartcard"))
 			{
 				need_secret = !nm_setting_vpn_get_secret(settings, "pin") &&
-							  !nm_setting_vpn_get_secret(settings, "password");
+							  !pw;
 			}
 			else
 			{
@@ -1264,15 +1290,18 @@ static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connectio
 							need_secret = FALSE;
 						}
 						else if (nm_setting_vpn_get_secret(settings,
-														"userkey-password") ||
-								 nm_setting_vpn_get_secret(settings,
-														"password"))
+													"userkey-password") || pw)
 						{
 							need_secret = FALSE;
 						}
 					}
 				}
 			}
+			if (streq(method, "cert-eap") &&
+				!nm_setting_vpn_get_secret(settings, "password"))
+			{
+				need_secret = TRUE;
+			}
 		}
 		else if (streq(method, "eap") ||
 				 streq(method, "psk"))