network-manager-applet r1018 - in trunk: . src/connection-editor src/gconf-helpers src/utils src/wireless-security



Author: dcbw
Date: Thu Nov 13 21:30:30 2008
New Revision: 1018
URL: http://svn.gnome.org/viewvc/network-manager-applet?rev=1018&view=rev

Log:
2008-11-13  Dan Williams  <dcbw redhat com>

	Add support for PKCS#12 private keys (bgo #558982)

	* src/utils/utils.c
		- (utils_fill_connection_certs): report errors

	* src/connection-editor/nm-connection-editor.c
		- (nm_connection_editor_set_connection): run initial validation from
			and idle handler to allow file choosers time to asynchronously
			find their files

	* src/gconf-helpers/gconf-helpers.c
		- (get_one_private_key): add private key passwords to the secrets hash
			if the private key is a pkcs#12 private key
		- (nm_gconf_get_keyring_items): move force-included private key passwords
			functionality into get_one_private_key()

	* src/wireless-security/eap-method.c
	  src/wireless-security/eap-method.h
		- (eap_method_default_file_chooser_filter_new): use differnet filters
			for private keys versus certificates, since private keys can be
			pkcs#12 and certificates cannot
		- (default_filter): split up into file_has_extension(),
			file_is_der_or_pem(), default_filter_cert(), and
			default_filter_privkey(); fix a bug where only the first 1K of a 
			candidate file would be read, missing some certificates with long
			text descriptions
		- (eap_method_validate_filepicker): take the private key password for
			validation purposes; return the certificate/key type

	* src/wireless-security/eap-method-peap.c
	  src/wireless-security/eap-method-ttls.c
		- Update for eap_method_validate_filepicker() changes

	* src/wireless-security/eap-method-tls.c
		- (eap_method_tls_new): handle phase2 secrets too; and do initial
			validation from and idle handler to allow file choosers time to
			asynchronously find their file
		- (setup_filepicker): connect a special handler to the private key
			chooser so that the client certificate chooser can be disabled when
			the user picks a pkcs#12 private key; additionally, work around a
			GTK+ issue where GTK would clear the choosers filter
		- (private_key_picker_helper): disable the client certificate chooser
			button when the private key is pkcs#12
		- (fill_connection): if the private key is pkcs#12, set the client
			certificate to the the same file as the private key, as NM requires
		- (validate): ignore the client certificate if the private key is
			pkcs#12



Modified:
   trunk/ChangeLog
   trunk/src/connection-editor/nm-connection-editor.c
   trunk/src/gconf-helpers/gconf-helpers.c
   trunk/src/utils/utils.c
   trunk/src/wireless-security/eap-method-peap.c
   trunk/src/wireless-security/eap-method-tls.c
   trunk/src/wireless-security/eap-method-ttls.c
   trunk/src/wireless-security/eap-method.c
   trunk/src/wireless-security/eap-method.h

Modified: trunk/src/connection-editor/nm-connection-editor.c
==============================================================================
--- trunk/src/connection-editor/nm-connection-editor.c	(original)
+++ trunk/src/connection-editor/nm-connection-editor.c	Thu Nov 13 21:30:30 2008
@@ -482,6 +482,13 @@
 	editor->pages = g_slist_append (editor->pages, page);
 }
 
+static gboolean
+idle_validate (gpointer user_data)
+{
+	connection_editor_validate (NM_CONNECTION_EDITOR (user_data));
+	return FALSE;
+}
+
 static void
 nm_connection_editor_set_connection (NMConnectionEditor *editor, NMConnection *connection)
 {
@@ -529,7 +536,10 @@
 	/* set the UI */
 	populate_connection_ui (editor);
 
-	connection_editor_validate (editor);
+	/* Validate the connection from an idle handler to ensure that stuff like
+	 * GtkFileChoosers have had a chance to asynchronously find their files.
+	 */
+	g_idle_add (idle_validate, editor);
 }
 
 void

Modified: trunk/src/gconf-helpers/gconf-helpers.c
==============================================================================
--- trunk/src/gconf-helpers/gconf-helpers.c	(original)
+++ trunk/src/gconf-helpers/gconf-helpers.c	Thu Nov 13 21:30:30 2008
@@ -1616,6 +1616,7 @@
                      const char *setting_name,
                      const char *tag,
                      const char *password,
+                     gboolean include_password,
                      GHashTable *secrets,
                      GError **error)
 {
@@ -1623,7 +1624,9 @@
 	GByteArray *array = NULL;
 	const char *filename = NULL;
 	const char *secret_name;
+	const char *real_password_secret_name = NULL;
 	gboolean success = FALSE;
+	gboolean add_password = FALSE;
 
 	g_return_val_if_fail (connection != NULL, FALSE);
 	g_return_val_if_fail (tag != NULL, FALSE);
@@ -1636,9 +1639,11 @@
 	if (!strcmp (tag, NMA_PRIVATE_KEY_PASSWORD_TAG)) {
 		filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_PRIVATE_KEY_TAG);
 		secret_name = NM_SETTING_802_1X_PRIVATE_KEY;
+		real_password_secret_name = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
 	} else if (!strcmp (tag, NMA_PHASE2_PRIVATE_KEY_PASSWORD_TAG)) {
 		filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_PHASE2_PRIVATE_KEY_TAG);
 		secret_name = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY;
+		real_password_secret_name = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD;
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_SECRETS_UNAVAILABLE,
 		             "%s.%d - %s/%s Unknown private key password type '%s'.",
@@ -1648,15 +1653,23 @@
 
 	if (filename) {
 		NMSetting8021x *setting;
-		const GByteArray *tmp;
+		const GByteArray *tmp = NULL;
+		NMSetting8021xCKType ck_type = NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
 
 		setting = (NMSetting8021x *) nm_setting_802_1x_new ();
-		nm_setting_802_1x_set_private_key_from_file (setting, filename, password, error);
+		if (nm_setting_802_1x_set_private_key_from_file (setting, filename, password, &ck_type, error)) {
+			/* For PKCS#12 files, which don't get decrypted, add the private key
+			 * password to the secrets hash.
+			 */
+			if (ck_type == NM_SETTING_802_1X_CK_TYPE_PKCS12)
+				add_password = TRUE;
 
-		/* Steal the private key */
-		tmp = nm_setting_802_1x_get_private_key (setting);
-		array = g_byte_array_sized_new (tmp->len);
-		g_byte_array_append (array, tmp->data, tmp->len);
+			/* Steal the private key */
+			tmp = nm_setting_802_1x_get_private_key (setting);
+			g_assert (tmp);
+			array = g_byte_array_sized_new (tmp->len);
+			g_byte_array_append (array, tmp->data, tmp->len);
+		}
 		g_object_unref (setting);
 	}
 
@@ -1669,9 +1682,11 @@
 		goto out;
 	}
 
-	g_hash_table_insert (secrets,
-	                     g_strdup (secret_name),
-	                     byte_array_to_gvalue (array));
+	g_hash_table_insert (secrets, g_strdup (secret_name), byte_array_to_gvalue (array));
+
+	if (include_password || add_password)
+		g_hash_table_insert (secrets, g_strdup (real_password_secret_name), string_to_gvalue (password));
+
 	success = TRUE;
 
 out:
@@ -1750,25 +1765,17 @@
 		if (   !strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)
 		    && (   !strcmp (key_name, NMA_PRIVATE_KEY_PASSWORD_TAG)
 		        || !strcmp (key_name, NMA_PHASE2_PRIVATE_KEY_PASSWORD_TAG))) {
-			/* Private key passwords aren't passed to NM, they are used
-			 * to decrypt the private key and send _that_ to NM.
+			/* Private key passwords aren't passed to NM for "traditional"
+			 * OpenSSL private keys, but are passed to NM for PKCS#12 keys.
 			 */
 			if (!get_one_private_key (connection, setting_name, key_name,
-			                          found->secret, secrets, error)) {
+			                          found->secret, include_private_passwords, secrets, error)) {
 				if (!*error) {
 					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_SECRETS_UNAVAILABLE,
 					             "%s.%d - %s/%s unknown error from get_one_private_key().",
 					             __FILE__, __LINE__, connection_name, setting_name);
 				}
 				break;
-			} else if (include_private_passwords) {
-				/* If asked, include the actual private key passwords and such
-				 * too.  NOT to be used in response to a GetSecrets call, but
-				 * for displaying the passwords in the UI if required.
-				 */
-				g_hash_table_insert (secrets,
-				                     g_strdup (key_name),
-				                     string_to_gvalue (found->secret));
 			}
 		} else {
 			/* Ignore older obsolete keyring keys that we don't want to leak

Modified: trunk/src/utils/utils.c
==============================================================================
--- trunk/src/utils/utils.c	(original)
+++ trunk/src/utils/utils.c	Thu Nov 13 21:30:30 2008
@@ -219,6 +219,7 @@
 {
 	NMSetting8021x *s_8021x;
 	const char *filename;
+	GError *error = NULL;
 
 	g_return_if_fail (connection != NULL);
 
@@ -227,20 +228,32 @@
 		return;
 
 	filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_CA_CERT_TAG);
-	if (filename)
-		nm_setting_802_1x_set_ca_cert_from_file (s_8021x, filename, NULL);
+	if (filename) {
+		if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, filename, NULL, &error))
+			g_warning ("%s: couldn't read CA certificate: %d %s", __func__, error->code, error->message);
+		g_clear_error (&error);
+	}
 
 	filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_CLIENT_CERT_TAG);
-	if (filename)
-		nm_setting_802_1x_set_client_cert_from_file (s_8021x, filename, NULL);
+	if (filename) {
+		if (!nm_setting_802_1x_set_client_cert_from_file (s_8021x, filename, NULL, &error))
+			g_warning ("%s: couldn't read client certificate: %d %s", __func__, error->code, error->message);
+		g_clear_error (&error);
+	}
 
 	filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_PHASE2_CA_CERT_TAG);
-	if (filename)
-		nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, filename, NULL);
+	if (filename) {
+		if (!nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, filename, NULL, &error))
+			g_warning ("%s: couldn't read phase2 CA certificate: %d %s", __func__, error->code, error->message);
+		g_clear_error (&error);
+	}
 
 	filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_PHASE2_CLIENT_CERT_TAG);
-	if (filename)
-		nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, filename, NULL);
+	if (filename) {
+		if (!nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, filename, NULL, &error))
+			g_warning ("%s: couldn't read phase2 client certificate: %d %s", __func__, error->code, error->message);
+		g_clear_error (&error);
+	}
 }
 
 void

Modified: trunk/src/wireless-security/eap-method-peap.c
==============================================================================
--- trunk/src/wireless-security/eap-method-peap.c	(original)
+++ trunk/src/wireless-security/eap-method-peap.c	Thu Nov 13 21:30:30 2008
@@ -52,7 +52,7 @@
 	EAPMethod *eap = NULL;
 	gboolean valid = FALSE;
 
-	if (!eap_method_validate_filepicker (parent->xml, "eap_peap_ca_cert_button", TRUE, FALSE, NULL))
+	if (!eap_method_validate_filepicker (parent->xml, "eap_peap_ca_cert_button", TYPE_CA_CERT, NULL, NULL))
 		return FALSE;
 
 	widget = glade_xml_get_widget (parent->xml, "eap_peap_inner_auth_combo");
@@ -392,10 +392,10 @@
 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
 	gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
 	                                   _("Choose a Certificate Authority certificate..."));
-	g_signal_connect (G_OBJECT (widget), "selection-changed",
+	g_signal_connect (G_OBJECT (widget), "file-set",
 	                  (GCallback) wireless_security_changed_cb,
 	                  parent);
-	filter = eap_method_default_file_chooser_filter_new ();
+	filter = eap_method_default_file_chooser_filter_new (FALSE);
 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
 	if (connection) {
 		filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_CA_CERT_TAG);

Modified: trunk/src/wireless-security/eap-method-tls.c
==============================================================================
--- trunk/src/wireless-security/eap-method-tls.c	(original)
+++ trunk/src/wireless-security/eap-method-tls.c	Thu Nov 13 21:30:30 2008
@@ -57,34 +57,36 @@
 static gboolean
 validate (EAPMethod *parent)
 {
+	NMSetting8021xCKType ck_type = NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
 	GtkWidget *widget;
-	const char *text;
+	const char *password, *identity;
 
 	widget = glade_xml_get_widget (parent->xml, "eap_tls_identity_entry");
 	g_assert (widget);
-	text = gtk_entry_get_text (GTK_ENTRY (widget));
-	if (!text || !strlen (text))
+	identity = gtk_entry_get_text (GTK_ENTRY (widget));
+	if (!identity || !strlen (identity))
 		return FALSE;
 
-	if (!eap_method_validate_filepicker (parent->xml, "eap_tls_user_cert_button", FALSE, FALSE, NULL))
+	if (!eap_method_validate_filepicker (parent->xml, "eap_tls_ca_cert_button", TYPE_CA_CERT, NULL, NULL))
 		return FALSE;
 
-	if (!eap_method_validate_filepicker (parent->xml, "eap_tls_ca_cert_button", TRUE, FALSE, NULL))
+	widget = glade_xml_get_widget (parent->xml, "eap_tls_private_key_password_entry");
+	g_assert (widget);
+	password = gtk_entry_get_text (GTK_ENTRY (widget));
+	if (!password || !strlen (password))
 		return FALSE;
 
 	if (!eap_method_validate_filepicker (parent->xml,
 	                                     "eap_tls_private_key_button",
-	                                     FALSE,
-	                                     TRUE,
-	                                     "eap_tls_private_key_password_entry"))
+	                                     TYPE_PRIVATE_KEY,
+	                                     password,
+	                                     &ck_type))
 		return FALSE;
 
-	widget = glade_xml_get_widget (parent->xml, "eap_tls_private_key_password_entry");
-	g_assert (widget);
-	// FIXME: require encrypted private keys for now
-	text = gtk_entry_get_text (GTK_ENTRY (widget));
-	if (!text || !strlen (text))
-		return FALSE;
+	if (ck_type != NM_SETTING_802_1X_CK_TYPE_PKCS12) {
+		if (!eap_method_validate_filepicker (parent->xml, "eap_tls_user_cert_button", TYPE_CLIENT_CERT, NULL, NULL))
+			return FALSE;
+	}
 
 	return TRUE;
 }
@@ -129,9 +131,10 @@
 fill_connection (EAPMethod *parent, NMConnection *connection)
 {
 	EAPMethodTLS *method = (EAPMethodTLS *) parent;
+	NMSetting8021xCKType key_type = NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
 	NMSetting8021x *s_8021x;
 	GtkWidget *widget;
-	char *filename;
+	char *filename, *pk_filename, *cc_filename;
 	char *password = NULL;
 	GError *error = NULL;
 
@@ -147,31 +150,6 @@
 	g_assert (widget);
 	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, gtk_entry_get_text (GTK_ENTRY (widget)), NULL);
 
-	widget = glade_xml_get_widget (parent->xml, "eap_tls_user_cert_button");
-	g_assert (widget);
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
-	g_assert (filename);
-	g_object_set_data_full (G_OBJECT (connection),
-	                        method->phase2 ? NMA_PATH_PHASE2_CLIENT_CERT_TAG : NMA_PATH_CLIENT_CERT_TAG,
-	                        g_strdup (filename),
-	                        (GDestroyNotify) g_free);
-	g_free (filename);
-
-	widget = glade_xml_get_widget (parent->xml, "eap_tls_ca_cert_button");
-	g_assert (widget);
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
-	if (filename) {
-		g_object_set_data_full (G_OBJECT (connection),
-		                        method->phase2 ? NMA_PATH_PHASE2_CA_CERT_TAG : NMA_PATH_CA_CERT_TAG,
-		                        g_strdup (filename),
-		                        (GDestroyNotify) g_free);
-		g_free (filename);
-	} else {
-		g_object_set_data (G_OBJECT (connection),
-		                   method->phase2 ? NMA_PATH_PHASE2_CA_CERT_TAG : NMA_PATH_CA_CERT_TAG,
-		                   NULL);
-	}
-
 	widget = glade_xml_get_widget (parent->xml, "eap_tls_private_key_password_entry");
 	g_assert (widget);
 	password = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
@@ -187,29 +165,60 @@
 		                        (GDestroyNotify) free_password);
 	}
 
+	/* TLS private key */
 	widget = glade_xml_get_widget (parent->xml, "eap_tls_private_key_button");
 	g_assert (widget);
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
-	g_assert (filename);
+	pk_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+	g_assert (pk_filename);
 	g_object_set_data_full (G_OBJECT (connection),
 	                        method->phase2 ? NMA_PATH_PHASE2_PRIVATE_KEY_TAG : NMA_PATH_PRIVATE_KEY_TAG,
-	                        g_strdup (filename),
+	                        g_strdup (pk_filename),
 	                        (GDestroyNotify) g_free);
 	if (method->phase2) {
-		nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, filename, password, &error);
-		if (error) {
-			g_warning ("Couldn't read phase2 private key: %s", error->message);
+		if (!nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, pk_filename, password, &key_type, &error)) {
+			g_warning ("Couldn't read phase2 private key '%s': %s", pk_filename, error ? error->message : "(unknown)");
 			g_clear_error (&error);
 		}
 	} else {
-		nm_setting_802_1x_set_private_key_from_file (s_8021x, filename, password, &error);
-		if (error) {
-			g_warning ("Couldn't read private key: %s", error->message);
+		if (!nm_setting_802_1x_set_private_key_from_file (s_8021x, pk_filename, password, &key_type, &error)) {
+			g_warning ("Couldn't read private key '%s': %s", pk_filename, error ? error->message : "(unknown)");
 			g_clear_error (&error);
 		}
 	}
 
-	g_free (filename);
+	/* TLS client certificate */
+	if (key_type == NM_SETTING_802_1X_CK_TYPE_PKCS12) {
+		/* if the key is pkcs#12, the cert is filled with the same data */
+		cc_filename = g_strdup (pk_filename);
+	} else {
+		widget = glade_xml_get_widget (parent->xml, "eap_tls_user_cert_button");
+		g_assert (widget);
+		cc_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+	}
+
+	g_assert (cc_filename);
+	g_object_set_data_full (G_OBJECT (connection),
+	                        method->phase2 ? NMA_PATH_PHASE2_CLIENT_CERT_TAG : NMA_PATH_CLIENT_CERT_TAG,
+	                        g_strdup (cc_filename),
+	                        (GDestroyNotify) g_free);
+	g_free (cc_filename);
+	g_free (pk_filename);
+
+	/* TLS CA certificate */
+	widget = glade_xml_get_widget (parent->xml, "eap_tls_ca_cert_button");
+	g_assert (widget);
+	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+	if (filename) {
+		g_object_set_data_full (G_OBJECT (connection),
+		                        method->phase2 ? NMA_PATH_PHASE2_CA_CERT_TAG : NMA_PATH_CA_CERT_TAG,
+		                        g_strdup (filename),
+		                        (GDestroyNotify) g_free);
+		g_free (filename);
+	} else {
+		g_object_set_data (G_OBJECT (connection),
+		                   method->phase2 ? NMA_PATH_PHASE2_CA_CERT_TAG : NMA_PATH_CA_CERT_TAG,
+		                   NULL);
+	}
 
 	if (method->ignore_ca_cert) {
 		g_object_set_data (G_OBJECT (connection),
@@ -301,32 +310,113 @@
 }
 
 static void
+private_key_picker_helper (EAPMethod *parent, const char *filename, gboolean changed)
+{
+	NMSetting8021x *setting;
+	NMSetting8021xCKType cert_type = NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
+	const char *password;
+	GtkWidget *widget;
+
+	widget = glade_xml_get_widget (parent->xml, "eap_tls_private_key_password_entry");
+	g_assert (widget);
+	password = gtk_entry_get_text (GTK_ENTRY (widget));
+
+	setting = (NMSetting8021x *) nm_setting_802_1x_new ();
+	nm_setting_802_1x_set_private_key_from_file (setting, filename, password, &cert_type, NULL);
+	g_object_unref (setting);
+
+	/* With PKCS#12, the client cert must be the same as the private key */
+	widget = glade_xml_get_widget (parent->xml, "eap_tls_user_cert_button");
+	if (cert_type == NM_SETTING_802_1X_CK_TYPE_PKCS12) {
+		gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (widget));
+		gtk_widget_set_sensitive (widget, FALSE);
+	} else if (changed)
+		gtk_widget_set_sensitive (widget, TRUE);
+}
+
+static void
+private_key_picker_file_set_cb (GtkWidget *chooser, gpointer user_data)
+{
+	EAPMethod *parent = (EAPMethod *) user_data;
+	char *filename;
+
+	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+	if (filename)
+		private_key_picker_helper (parent, filename, TRUE);
+	g_free (filename);
+}
+
+static void reset_filter (GtkWidget *widget, GParamSpec *spec, gpointer user_data)
+{
+	if (!gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (widget))) {
+		g_signal_handlers_block_by_func (widget, reset_filter, user_data);
+		gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (widget), GTK_FILE_FILTER (user_data));
+		g_signal_handlers_unblock_by_func (widget, reset_filter, user_data);
+	}
+}
+
+static void
 setup_filepicker (GladeXML *xml,
                   const char *name,
                   const char *title,
                   WirelessSecurity *parent,
+                  EAPMethodTLS *method,
                   NMConnection *connection,
                   const char *tag)
 {
 	GtkWidget *widget;
 	GtkFileFilter *filter;
-	const char *filename;
+	const char *filename = NULL;
+	gboolean privkey = FALSE, client_cert = FALSE;
+
+	if (!strcmp (tag, NMA_PATH_PHASE2_PRIVATE_KEY_TAG) || !strcmp (tag, NMA_PATH_PRIVATE_KEY_TAG))
+		privkey = TRUE;
+	if (!strcmp (tag, NMA_PATH_PHASE2_CLIENT_CERT_TAG) || !strcmp (tag, NMA_PATH_CLIENT_CERT_TAG))
+		client_cert = TRUE;
 
 	widget = glade_xml_get_widget (xml, name);
 	g_assert (widget);
 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
 	gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget), title);
-	g_signal_connect (G_OBJECT (widget), "selection-changed",
-	                  (GCallback) wireless_security_changed_cb,
-	                  parent);
+
 	if (connection && tag) {
 		filename = g_object_get_data (G_OBJECT (connection), tag);
 		if (filename)
 			gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), filename);
 	}
 
-	filter = eap_method_default_file_chooser_filter_new ();
+	/* Connect a special handler for private keys to intercept PKCS#12 key types
+	 * and desensitize the user cert button.
+	 */
+	if (privkey) {
+		g_signal_connect (G_OBJECT (widget), "file-set",
+		                  (GCallback) private_key_picker_file_set_cb,
+		                  method);
+		if (filename)
+			private_key_picker_helper ((EAPMethod *) method, filename, FALSE);
+	}
+
+	g_signal_connect (G_OBJECT (widget), "file-set",
+	                  (GCallback) wireless_security_changed_cb,
+	                  parent);
+
+	filter = eap_method_default_file_chooser_filter_new (privkey);
 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
+
+	/* For some reason, GTK+ calls set_current_filter (..., NULL) from 
+	 * gtkfilechooserdefault.c::show_and_select_files_finished_loading() on our
+	 * dialog; so force-reset the filter to what we want it to be whenever
+	 * it gets cleared.
+	 */
+	if (client_cert)
+		g_signal_connect (G_OBJECT (widget), "notify::filter", (GCallback) reset_filter, filter);
+}
+
+static gboolean
+revalidate (gpointer user_data)
+{
+	wireless_security_changed_cb (NULL, (WirelessSecurity *) user_data);
+	return FALSE;
 }
 
 EAPMethodTLS *
@@ -395,41 +485,43 @@
 		gtk_entry_set_text (GTK_ENTRY (widget), nm_setting_802_1x_get_identity (s_8021x));
 
 	widget = glade_xml_get_widget (xml, "eap_tls_private_key_password_entry");
-	g_assert (widget);
-	g_signal_connect (G_OBJECT (widget), "changed",
-	                  (GCallback) wireless_security_changed_cb,
-	                  parent);
 	/* Fill secrets, if any */
 	if (connection) {
 		GHashTable *secrets;
 		GError *error = NULL;
 		GValue *value;
+		const char *pw_secret_name = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD :
+		                                      NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
 
 		secrets = nm_gconf_get_keyring_items (connection,
 		                                      NM_SETTING_802_1X_SETTING_NAME,
 		                                      TRUE,
 		                                      &error);
 		if (secrets) {
-			value = g_hash_table_lookup (secrets, NMA_PRIVATE_KEY_PASSWORD_TAG);
+			value = g_hash_table_lookup (secrets, pw_secret_name);
 			if (value)
 				gtk_entry_set_text (GTK_ENTRY (widget), g_value_get_string (value));
 			g_hash_table_destroy (secrets);
-		} else if (error)
-			g_error_free (error);
+		}
+		g_clear_error (&error);
 	}
+	g_assert (widget);
+	g_signal_connect (G_OBJECT (widget), "changed",
+	                  (GCallback) wireless_security_changed_cb,
+	                  parent);
 
 	setup_filepicker (xml, "eap_tls_user_cert_button",
 	                  _("Choose your personal certificate..."),
-	                  parent, connection,
+	                  parent, method, connection,
 	                  phase2 ? NMA_PATH_PHASE2_CLIENT_CERT_TAG : NMA_PATH_CLIENT_CERT_TAG);
 	setup_filepicker (xml, "eap_tls_ca_cert_button",
 	                  _("Choose a Certificate Authority certificate..."),
-	                  parent, connection,
+	                  parent, method, connection,
 	                  phase2 ? NMA_PATH_PHASE2_CA_CERT_TAG : NMA_PATH_CA_CERT_TAG);
 	setup_filepicker (xml,
 	                  "eap_tls_private_key_button",
 	                  _("Choose your private key..."),
-	                  parent, connection,
+	                  parent, method, connection,
 	                  phase2 ? NMA_PATH_PHASE2_PRIVATE_KEY_TAG : NMA_PATH_PRIVATE_KEY_TAG);
 
 	widget = glade_xml_get_widget (xml, "show_checkbutton");
@@ -438,6 +530,11 @@
 	                  (GCallback) show_toggled_cb,
 	                  method);
 
+	/* Re-validate from an idle-handler becuase file chooser widgets set their
+	 * file asynchronously, not when gtk_file_chooser_set_filename() is called.
+	 */
+	g_idle_add (revalidate, parent);
+
 	return method;
 }
 

Modified: trunk/src/wireless-security/eap-method-ttls.c
==============================================================================
--- trunk/src/wireless-security/eap-method-ttls.c	(original)
+++ trunk/src/wireless-security/eap-method-ttls.c	Thu Nov 13 21:30:30 2008
@@ -52,7 +52,7 @@
 	EAPMethod *eap = NULL;
 	gboolean valid = FALSE;
 
-	if (!eap_method_validate_filepicker (parent->xml, "eap_ttls_ca_cert_button", TRUE, FALSE, NULL))
+	if (!eap_method_validate_filepicker (parent->xml, "eap_ttls_ca_cert_button", TYPE_CA_CERT, NULL, NULL))
 		return FALSE;
 
 	widget = glade_xml_get_widget (parent->xml, "eap_ttls_inner_auth_combo");
@@ -429,10 +429,10 @@
 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE);
 	gtk_file_chooser_button_set_title (GTK_FILE_CHOOSER_BUTTON (widget),
 	                                   _("Choose a Certificate Authority certificate..."));
-	g_signal_connect (G_OBJECT (widget), "selection-changed",
+	g_signal_connect (G_OBJECT (widget), "file-set",
 	                  (GCallback) wireless_security_changed_cb,
 	                  parent);
-	filter = eap_method_default_file_chooser_filter_new ();
+	filter = eap_method_default_file_chooser_filter_new (FALSE);
 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget), filter);
 	if (connection) {
 		filename = g_object_get_data (G_OBJECT (connection), NMA_PATH_CA_CERT_TAG);

Modified: trunk/src/wireless-security/eap-method.c
==============================================================================
--- trunk/src/wireless-security/eap-method.c	(original)
+++ trunk/src/wireless-security/eap-method.c	Thu Nov 13 21:30:30 2008
@@ -146,9 +146,9 @@
 gboolean
 eap_method_validate_filepicker (GladeXML *xml,
                                 const char *name,
-                                gboolean ignore_blank,
-                                gboolean is_private_key,
-                                const char *pw_entry_name)
+                                guint32 item_type,
+                                const char *password,
+                                NMSetting8021xCKType *out_ck_type)
 {
 	GtkWidget *widget;
 	char *filename;
@@ -156,42 +156,45 @@
 	gboolean success = FALSE;
 	GError *error = NULL;
 
-	if (is_private_key)
-		g_return_val_if_fail (pw_entry_name != NULL, FALSE);
+	if (item_type == TYPE_PRIVATE_KEY) {
+		g_return_val_if_fail (password != NULL, NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
+		g_return_val_if_fail (strlen (password), NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
+	}
 
 	widget = glade_xml_get_widget (xml, name);
 	g_assert (widget);
 	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
 	if (!filename)
-		return ignore_blank ? TRUE : FALSE;
+		return (item_type == TYPE_CA_CERT) ? NM_SETTING_802_1X_CK_TYPE_X509 : NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
 
 	if (!g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
 		goto out;
 
 	setting = (NMSetting8021x *) nm_setting_802_1x_new ();
 
-	if (is_private_key) {
-		const char *pw;
-
-		if (!pw_entry_name)
-			goto out;
-
-		/* Need the private key password to decrypt the private key */
-		widget = glade_xml_get_widget (xml, pw_entry_name);
-		g_assert (widget);
-		pw = gtk_entry_get_text (GTK_ENTRY (widget));
-		if (!pw || !strlen (pw))
-			goto out;
-
-		success = nm_setting_802_1x_set_private_key_from_file (setting, filename, pw, NULL);
-	} else {
-		success = nm_setting_802_1x_set_ca_cert_from_file (setting, filename, &error);
-		if (error) {
-			g_warning ("Error: couldn't verify certificate: %d %s",
-			           error->code, error->message);
+	if (item_type == TYPE_PRIVATE_KEY) {
+		if (!nm_setting_802_1x_set_private_key_from_file (setting, filename, password, out_ck_type, &error)) {
+			g_warning ("Error: couldn't verify private key: %d %s",
+			           error ? error->code : -1, error ? error->message : "(none)");
 			g_clear_error (&error);
-		}
-	}
+		} else
+			success = TRUE;
+	} else if (item_type == TYPE_CLIENT_CERT) {
+		if (!nm_setting_802_1x_set_client_cert_from_file (setting, filename, out_ck_type, &error)) {
+			g_warning ("Error: couldn't verify client certificate: %d %s",
+			           error ? error->code : -1, error ? error->message : "(none)");
+			g_clear_error (&error);
+		} else
+			success = TRUE;
+	} else if (item_type == TYPE_CA_CERT) {
+		if (!nm_setting_802_1x_set_ca_cert_from_file (setting, filename, out_ck_type, &error)) {
+			g_warning ("Error: couldn't verify CA certificate: %d %s",
+			           error ? error->code : -1, error ? error->message : "(none)");
+			g_clear_error (&error);
+		} else
+			success = TRUE;
+	} else
+		g_warning ("%s: invalid item type %d.", __func__, item_type);
 
 	g_object_unref (setting);
 
@@ -221,33 +224,40 @@
 static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
 
 static gboolean
-default_filter (const GtkFileFilterInfo *filter_info, gpointer data)
+file_has_extension (const char *filename, const char *extensions[])
 {
-	int fd;
-	unsigned char buffer[1024];
-	ssize_t bytes_read;
-	gboolean show = FALSE;
-	guint16 der_tag = 0x8230;
-	char *p;
-	char *ext;
+	char *p, *ext;
+	int i = 0;
+	gboolean found = FALSE;
 
-	if (!filter_info->filename)
-		return FALSE;
-
-	p = strrchr (filter_info->filename, '.');
+	p = strrchr (filename, '.');
 	if (!p)
 		return FALSE;
 
 	ext = g_ascii_strdown (p, -1);
-	if (!ext)
-		return FALSE;
-	if (strcmp (ext, ".der") && strcmp (ext, ".pem") && strcmp (ext, ".crt") && strcmp (ext, ".cer")) {
-		g_free (ext);
-		return FALSE;
+	if (ext) {
+		while (extensions[i]) {
+			if (!strcmp (ext, extensions[i++])) {
+				found = TRUE;
+				break;
+			}
+		}
 	}
 	g_free (ext);
 
-	fd = open (filter_info->filename, O_RDONLY);
+	return found;
+}
+
+static gboolean
+file_is_der_or_pem (const char *filename, gboolean privkey)
+{
+	int fd;
+	unsigned char buffer[8192];
+	ssize_t bytes_read;
+	guint16 der_tag = 0x8230;
+	gboolean success = FALSE;
+
+	fd = open (filename, O_RDONLY);
 	if (fd < 0)
 		return FALSE;
 
@@ -258,39 +268,80 @@
 
 	/* Check for DER signature */
 	if (!memcmp (buffer, &der_tag, 2)) {
-		show = TRUE;
+		success = TRUE;
 		goto out;
 	}
 
 	/* Check for PEM signatures */
-	if (find_tag (pem_rsa_key_begin, (const char *) buffer, bytes_read)) {
-		show = TRUE;
-		goto out;
-	}
-
-	if (find_tag (pem_dsa_key_begin, (const char *) buffer, bytes_read)) {
-		show = TRUE;
-		goto out;
-	}
+	if (privkey) {
+		if (find_tag (pem_rsa_key_begin, (const char *) buffer, bytes_read)) {
+			success = TRUE;
+			goto out;
+		}
 
-	if (find_tag (pem_cert_begin, (const char *) buffer, bytes_read)) {
-		show = TRUE;
-		goto out;
+		if (find_tag (pem_dsa_key_begin, (const char *) buffer, bytes_read)) {
+			success = TRUE;
+			goto out;
+		}
+	} else {
+		if (find_tag (pem_cert_begin, (const char *) buffer, bytes_read)) {
+			success = TRUE;
+			goto out;
+		}
 	}
 
 out:
 	close (fd);
-	return show;
+	return success;
+}
+
+static gboolean
+default_filter_privkey (const GtkFileFilterInfo *filter_info, gpointer user_data)
+{
+	const char *extensions[] = { ".der", ".pem", ".p12", NULL };
+
+	if (!filter_info->filename)
+		return FALSE;
+
+	if (!file_has_extension (filter_info->filename, extensions))
+		return FALSE;
+
+	if (!file_is_der_or_pem (filter_info->filename, TRUE))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+default_filter_cert (const GtkFileFilterInfo *filter_info, gpointer user_data)
+{
+	const char *extensions[] = { ".der", ".pem", ".crt", ".cer", NULL };
+
+	if (!filter_info->filename)
+		return FALSE;
+
+	if (!file_has_extension (filter_info->filename, extensions))
+		return FALSE;
+
+	if (!file_is_der_or_pem (filter_info->filename, FALSE))
+		return FALSE;
+
+	return TRUE;
 }
 
 GtkFileFilter *
-eap_method_default_file_chooser_filter_new (void)
+eap_method_default_file_chooser_filter_new (gboolean privkey)
 {
 	GtkFileFilter *filter;
 
 	filter = gtk_file_filter_new ();
-	gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, default_filter, NULL, NULL);
-	gtk_file_filter_set_name (filter, _("DER or PEM certificates (*.der, *.pem, *.crt, *.cer)"));
+	if (privkey) {
+		gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, default_filter_privkey, NULL, NULL);
+		gtk_file_filter_set_name (filter, _("DER, PEM, or PKCS#12 private keys (*.der, *.pem, *.p12)"));
+	} else {
+		gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, default_filter_cert, NULL, NULL);
+		gtk_file_filter_set_name (filter, _("DER or PEM certificates (*.der, *.pem, *.crt, *.cer)"));
+	}
 	return filter;
 }
 

Modified: trunk/src/wireless-security/eap-method.h
==============================================================================
--- trunk/src/wireless-security/eap-method.h	(original)
+++ trunk/src/wireless-security/eap-method.h	Thu Nov 13 21:30:30 2008
@@ -29,6 +29,7 @@
 #include <gtk/gtkwidget.h>
 
 #include <nm-connection.h>
+#include <nm-setting-8021x.h>
 
 typedef struct _EAPMethod EAPMethod;
 
@@ -85,13 +86,17 @@
                       GladeXML *xml,
                       GtkWidget *ui_widget);
 
-GtkFileFilter * eap_method_default_file_chooser_filter_new (void);
+GtkFileFilter * eap_method_default_file_chooser_filter_new (gboolean privkey);
+
+#define TYPE_CLIENT_CERT 0
+#define TYPE_CA_CERT     1
+#define TYPE_PRIVATE_KEY 2
 
 gboolean eap_method_validate_filepicker (GladeXML *xml,
                                          const char *name,
-                                         gboolean ignore_blank,
-                                         gboolean is_private_key,
-                                         const char *pw_entry_name);
+                                         guint32 item_type,
+                                         const char *password,
+                                         NMSetting8021xCKType *out_ck_type);
 
 #endif /* EAP_METHOD_H */
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]