Commit d64176ce90 for strongswan.org

commit d64176ce90a6c077de5d5405cccefce6b2ae88b7
Author: Jarno Mäkelä <jarno.makela@iki.fi>
Date:   Mon Oct 12 15:38:19 2026 +0300

    nm: Add cert-eap method to GNOME editor and auth dialog

    The editor gains a fifth method entry.  It uses the existing certificate-
    and EAP-related fields.  Every certificate source (file, ssh-agent,
    smartcard) can be selected.  The key password row does not fall back
    to the EAP password for this method.

    The auth dialog treats "cert-eap" as a certificate method so the
    certificate source is resolved (agent socket, or pin / userkey-password
    secret).  It then calls get_secrets() with the EAP password as the main
    entry and the certificate secret as a secondary entry, so both are
    requested in a single dialog or keyfile exchange.  The no-dialog
    shortcut is skipped when a secondary secret is still needed.  In
    non-external mode both secrets are printed before the single
    wait_for_quit().

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

diff --git a/src/frontends/gnome/auth-dialog/main.c b/src/frontends/gnome/auth-dialog/main.c
index 39e0e1e3a0..e78c2fac19 100644
--- a/src/frontends/gnome/auth-dialog/main.c
+++ b/src/frontends/gnome/auth-dialog/main.c
@@ -106,13 +106,14 @@ static void keyfile_print_stdout (GKeyFile *keyfile)
 }

 static gboolean get_secrets(const char *type, const char *cert_source, const char *uuid, const char *name,
-							const char *secret_name,
+							const char *secret_name, const char *secondary_name,
 							gboolean retry, gboolean allow_interaction, gboolean external_ui_mode,
-							const char *in_pw, char **out_pw, NMSettingSecretFlags flags)
+							const char *in_pw, char **out_pw, const char *in_pw2, char **out_pw2,
+							NMSettingSecretFlags flags)
 {
 	NMAVpnPasswordDialog *dialog;
 	char *prompt, *pw = NULL;
-	const char *new_pw = NULL;
+	const char *new_pw = NULL, *secondary_prompt = NULL;
 	guint32 minlen = 0;

 	if (!(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) &&
@@ -132,7 +133,8 @@ static gboolean get_secrets(const char *type, const char *cert_source, const cha
 		g_free (pw);
 		return TRUE;
 	}
-	if (!strcmp(type, "eap"))
+	if (!strcmp(type, "eap") ||
+		!strcmp(type, "cert-eap"))
 	{
 		prompt = g_strdup_printf (_("EAP password required to establish VPN connection '%s'."),
 								  name);
@@ -156,39 +158,60 @@ static gboolean get_secrets(const char *type, const char *cert_source, const cha
 									  name);
 		}
 	}
+	if (secondary_name)
+	{
+		secondary_prompt = !strcmp(cert_source, "smartcard") ? _("PIN:")
+												: _("Private key password:");
+	}
 	if (external_ui_mode)
 	{
 		GKeyFile *keyfile;

 		keyfile = g_key_file_new ();

-		g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
-		g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
-		g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
+		if (strcmp(type, "cert-eap") || strcmp(cert_source, "agent"))
+		{
+			g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
+			g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
+			g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
+		}

 		keyfile_add_entry_info (keyfile, secret_name, pw ?: "", _("Password:"), TRUE, allow_interaction);
-
+		if (secondary_name)
+		{
+			keyfile_add_entry_info (keyfile, secondary_name, in_pw2 ?: "",
+									secondary_prompt, TRUE, allow_interaction);
+		}
 		keyfile_print_stdout (keyfile);
 		g_key_file_unref (keyfile);
 		goto out;
 	}
 	else if (!allow_interaction ||
-			(!retry && pw && !(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)))
+			(!retry && pw && !(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) &&
+			(!secondary_name || in_pw2)))
 	{
 		/* If we can't prompt the user, just return the existing password. Do the same
 		 * if we don't nee a new password (!retry) and have an existing saved one */
 		*out_pw = pw;
+		if (secondary_name)
+			*out_pw2 = g_strdup (in_pw2);
 		g_free (prompt);
 		return TRUE;
 	}

 	dialog = (NMAVpnPasswordDialog*)nma_vpn_password_dialog_new(_("Authenticate VPN"), prompt, NULL);
-	nma_vpn_password_dialog_set_show_password_secondary(dialog, FALSE);
+	nma_vpn_password_dialog_set_show_password_secondary(dialog, secondary_name != NULL);

 	if (pw && !(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
 	{
 		nma_vpn_password_dialog_set_password(dialog, pw);
 	}
+	if (secondary_name)
+	{
+		nma_vpn_password_dialog_set_password_secondary_label(dialog, secondary_prompt);
+		if (in_pw2)
+			nma_vpn_password_dialog_set_password_secondary(dialog, in_pw2);
+	}
 	gtk_widget_show (GTK_WIDGET (dialog));

 too_short_retry:
@@ -203,6 +226,8 @@ too_short_retry:
 		{
 			*out_pw = g_strdup (new_pw);
 		}
+		if (secondary_name)
+			*out_pw2 = g_strdup (nma_vpn_password_dialog_get_password_secondary (dialog));
 	}
 	gtk_widget_hide (GTK_WIDGET (dialog));
 	gtk_widget_destroy (GTK_WIDGET (dialog));
@@ -213,13 +238,11 @@ out:

 static void print_secret (const char *secret_name, gchar *secret)
 {
-	if (secret)
+	if (secret && *secret)
 	{
 		printf("%s\n%s\n", secret_name, secret);
-		g_free(secret);
 	}
-	printf("\n\n");
-	fflush(stdout);
+	g_free(secret);
 }

 static void wait_for_quit (void)
@@ -229,6 +252,9 @@ static void wait_for_quit (void)
 	ssize_t n;
 	time_t start;

+	printf("\n\n");
+	fflush(stdout);
+
 	str = g_string_sized_new (10);
 	start = time (NULL);
 	do {
@@ -250,12 +276,13 @@ int main (int argc, char *argv[])
 {
 	gboolean retry = FALSE, allow_interaction = FALSE, external_ui_mode = FALSE;
 	gboolean need_secret = FALSE;
-	gchar *name = NULL, *uuid = NULL, *service = NULL, *pass = NULL;
+	gchar *name = NULL, *uuid = NULL, *service = NULL, *pass = NULL, *pass2 = NULL;
 	GHashTable *data = NULL, *secrets = NULL;
 	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	NMSettingSecretFlags flags2 = NM_SETTING_SECRET_FLAG_NONE;
 	GOptionContext *context;
 	char *agent, *type, *cert_source;
-	const char *secret_name = "password";
+	const char *secret_name = "password", *secondary_name = NULL;
 	int status = 0;
 	GOptionEntry entries[] = {
 		{ "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL},
@@ -311,7 +338,8 @@ int main (int argc, char *argv[])
 		!strcmp(type, "eap-tls") ||
 		!strcmp(type, "key") ||
 		!strcmp(type, "agent") ||
-		!strcmp(type, "smartcard"))
+		!strcmp(type, "smartcard") ||
+		!strcmp(type, "cert-eap"))
 	{
 		if (!strcmp(cert_source, "agent"))
 		{
@@ -321,12 +349,20 @@ int main (int argc, char *argv[])
 				if (external_ui_mode)
 				{
 					GKeyFile *keyfile;
+					char *prompt;

 					keyfile = g_key_file_new ();

+					if (!strcmp(type, "cert-eap"))
+						prompt = g_strdup_printf (_("EAP password required to establish VPN connection '%s'."),
+												  name);
+					else
+						prompt = g_strdup ("SSH agent");
+
 					g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
-					g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", "SSH agent");
+					g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
 					g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
+					g_free (prompt);

 					keyfile_add_entry_info (keyfile, "agent", agent, "SSH agent socket", TRUE, FALSE);

@@ -336,18 +372,23 @@ int main (int argc, char *argv[])
 				else
 				{
 					print_secret("agent", g_strdup (agent));
-					wait_for_quit ();
+					if (strcmp(type, "cert-eap") != 0)
+						wait_for_quit ();
 				}
 			}
-			else if (allow_interaction)
+			else
 			{
-				GtkWidget *dialog;
-				dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
-							  GTK_BUTTONS_OK,
-							  _("Configuration uses ssh-agent for authentication, "
-							  "but ssh-agent is not running!"));
-				gtk_dialog_run (GTK_DIALOG (dialog));
-				gtk_widget_destroy (dialog);
+				if (allow_interaction)
+				{
+					GtkWidget *dialog;
+					dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
+								  GTK_BUTTONS_OK,
+								  _("Configuration uses ssh-agent for authentication, "
+								  "but ssh-agent is not running!"));
+					gtk_dialog_run (GTK_DIALOG (dialog));
+					gtk_widget_destroy (dialog);
+				}
+				goto out;
 			}
 		}
 		else
@@ -355,30 +396,49 @@ int main (int argc, char *argv[])
 			NMSettingSecretFlags new_flags = NM_SETTING_SECRET_FLAG_NONE;

 			need_secret = TRUE;
-			secret_name = !strcmp(cert_source, "smartcard") ? "pin" : "userkey-password";
-			/* fallback to the legacy field for connections that were saved
-			 * before the rename, otherwise stored passwords/flags won't be used
-			 * and the returned flagless password is stored in the connection */
-			if (!nm_vpn_service_plugin_get_secret_flags (data, secret_name, &new_flags))
+			if (!strcmp(type, "cert-eap"))
 			{
-				secret_name = "password";
+				secondary_name = !strcmp(cert_source, "smartcard") ? "pin" : "userkey-password";
+			}
+			else
+			{
+				secret_name = !strcmp(cert_source, "smartcard") ? "pin" : "userkey-password";
+				/* fallback to the legacy field for connections that were saved
+				 * before the rename, otherwise stored passwords/flags won't be used
+				 * and the returned flagless password is stored in the connection */
+				if (!nm_vpn_service_plugin_get_secret_flags (data, secret_name, &new_flags))
+				{
+					secret_name = "password";
+				}
 			}
 		}
 	}

 	if (need_secret ||
 		!strcmp(type, "eap") ||
-		!strcmp(type, "psk"))
+		!strcmp(type, "psk") ||
+		!strcmp(type, "cert-eap"))
 	{
 		nm_vpn_service_plugin_get_secret_flags (data, secret_name, &flags);
-		if (!get_secrets(type, cert_source, uuid, name, secret_name, retry, allow_interaction,
-						 external_ui_mode, g_hash_table_lookup (secrets, secret_name), &pass, flags))
+		if (secondary_name &&
+			nm_vpn_service_plugin_get_secret_flags (data, secondary_name, &flags2) &&
+			(flags2 & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+		{
+			secondary_name = NULL;
+		}
+		if (!get_secrets(type, cert_source, uuid, name, secret_name, secondary_name,
+						 retry, allow_interaction, external_ui_mode,
+						 g_hash_table_lookup (secrets, secret_name), &pass,
+						 secondary_name ? g_hash_table_lookup (secrets, secondary_name) : NULL,
+						 secondary_name ? &pass2 : NULL, flags))
 		{
 			status = 1;
 		}
 		else if (!external_ui_mode)
 		{
 			print_secret(secret_name, pass);
+			if (secondary_name)
+				print_secret(secondary_name, pass2);
 			wait_for_quit ();
 		}
 	}
diff --git a/src/frontends/gnome/properties/nm-strongswan.c b/src/frontends/gnome/properties/nm-strongswan.c
index f74eb9be96..415ccaed00 100644
--- a/src/frontends/gnome/properties/nm-strongswan.c
+++ b/src/frontends/gnome/properties/nm-strongswan.c
@@ -212,6 +212,11 @@ static void update_sensitive (StrongswanPluginUiWidgetPrivate *priv)
 			update_pass_field (priv, TRUE);
 			update_cert_fields (priv, FALSE);
 			break;
+		case 4:
+			update_user_field (priv, TRUE);
+			update_pass_field (priv, TRUE);
+			update_cert_fields (priv, TRUE);
+			break;
 	}

 }
@@ -438,7 +443,8 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
 		(nm_setting_vpn_get_secret (settings, "password") ||
 		 (nm_setting_get_secret_flags (NM_SETTING (settings), "password",
 									   &flags, NULL) && flags)) &&
-		method && g_strcmp0 (method, "eap") != 0 && g_strcmp0 (method, "psk") != 0)
+		method && g_strcmp0 (method, "eap") != 0 && g_strcmp0 (method, "psk") != 0 &&
+		g_strcmp0 (method, "cert-eap") != 0)
 	{
 		secret = "password";
 	}
@@ -455,8 +461,11 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
 	widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "passwd-entry"));
 	value = nm_setting_vpn_get_secret (settings, "password");
 	/* ignore legacy password for certificate configs */
-	if (value && method && (g_strcmp0 (method, "eap") == 0 || g_strcmp0 (method, "psk") == 0))
+	if (value && method && (g_strcmp0 (method, "eap") == 0 ||
+		g_strcmp0 (method, "psk") == 0 || g_strcmp0 (method, "cert-eap") == 0))
+	{
 		gtk_editable_set_text (GTK_EDITABLE (widget), value);
+	}
 	g_signal_connect (G_OBJECT (widget), "changed", G_CALLBACK (settings_changed_cb), self);
 	init_password_icon (self, settings, "password", "passwd-entry",
 						NM_SETTING_SECRET_FLAG_NOT_SAVED);
@@ -466,6 +475,7 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Certificate"));
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("EAP-TLS"));
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Pre-shared key"));
+	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Certificate + EAP (Username/Password)"));
 	if (method) {
 		if (g_strcmp0 (method, "eap") == 0) {
 			gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
@@ -483,6 +493,9 @@ init_plugin_ui (StrongswanPluginUiWidget *self, NMConnection *connection, GError
 		if (g_strcmp0 (method, "psk") == 0) {
 			gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 3);
 		}
+		if (g_strcmp0 (method, "cert-eap") == 0) {
+			gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 4);
+		}
 	}
 	if (gtk_combo_box_get_active (GTK_COMBO_BOX (widget)) == -1)
 	{
@@ -744,6 +757,12 @@ update_connection (NMVpnEditor *iface,
 			save_password_and_flags (settings, priv->builder, "passwd-entry", "password");
 			str = "psk";
 			break;
+		case 4:
+			save_entry (settings, priv->builder, "user-entry", "user");
+			save_password_and_flags (settings, priv->builder, "passwd-entry", "password");
+			save_cert (settings, priv->builder);
+			str = "cert-eap";
+			break;
 	}
 	nm_setting_vpn_add_data_item (settings, "method", str);