NetworkManager r4280 - in trunk: . libnm-util src/supplicant-manager



Author: dcbw
Date: Thu Nov 13 21:19:08 2008
New Revision: 4280
URL: http://svn.gnome.org/viewvc/NetworkManager?rev=4280&view=rev

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

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

	* libnm-util/crypto.c
	  libnm-util/crypto.h
		- (parse_old_openssl_key_file): rename from parse_key_file(); adapt to
			take a GByteArray instead of a filename
		- (file_to_g_byte_array): handle private key files too
		- (decrypt_key): take a GByteArray rather than data + len
		- (crypto_get_private_key_data): refactor crypto_get_private_key() into
			one function that takes a filename, and one that takes raw data;
			detect pkcs#12 files as well
		- (crypto_load_and_verify_certificate): detect file type
		- (crypto_is_pkcs12_data, crypto_is_pkcs12_file): add pkcs#12 detection
			functions

	* libnm-util/crypto_gnutls.c
		- (crypto_decrypt): take GByteArray rather than data + len; fix a bug
			whereby tail padding was incorrectly handled, leading to erroneous
			successes when trying to decrypt the data
		- (crypto_verify_cert): rework somewhat
		- (crypto_verify_pkcs12): validate pkcs#12 keys

	* libnm-util/crypto_nss.c
		- (crypto_init): enable various pkcs#12 ciphers
		- (crypto_decrypt): take a GByteArray rather than data + len
		- (crypto_verify_cert): clean up
		- (crypto_verify_pkcs12): validate pkcs#12 keys

	* libnm-util/test-crypto.c
		- Handle pkcs#12 keys

	* libnm-util/nm-setting-8021x.c
	  libnm-util/nm-setting-8021x.h
	  libnm-util/libnm-util.ver
		- Add two new properties, 'private-key-password' and
			'phase2-private-key-password', to be used in conjunction with
			pkcs#12 keys
		- (nm_setting_802_1x_set_ca_cert_from_file,
		   nm_setting_802_1x_set_client_cert_from_file,
		   nm_setting_802_1x_set_phase2_ca_cert_from_file,
		   nm_setting_802_1x_set_phase2_client_from_file): return certificate
			type
		- (nm_setting_802_1x_get_private_key_password,
		   nm_setting_802_1x_get_phase2_private_key_password): return private
			key passwords
		- (nm_setting_802_1x_set_private_key_from_file,
		   nm_setting_802_1x_set_phase2_private_key_from_file): set the private
			key from a file, and update the private key password at the same time
		- (nm_setting_802_1x_get_private_key_type,
		   nm_setting_802_1x_get_phase2_private_key_type): return the private
			key type

	* src/supplicant-manager/nm-supplicant-settings-verify.c
		- Whitelist private key passwords

	* src/supplicant-manager/nm-supplicant-config.c
		- (nm_supplicant_config_add_setting_8021x): for pkcs#12 private keys,
			add the private key password to the supplicant config, but do not
			add the client certificate (as required by wpa_supplicant)



Modified:
   trunk/ChangeLog
   trunk/libnm-util/crypto.c
   trunk/libnm-util/crypto.h
   trunk/libnm-util/crypto_gnutls.c
   trunk/libnm-util/crypto_nss.c
   trunk/libnm-util/libnm-util.ver
   trunk/libnm-util/nm-setting-8021x.c
   trunk/libnm-util/nm-setting-8021x.h
   trunk/libnm-util/test-crypto.c
   trunk/src/supplicant-manager/nm-supplicant-config.c
   trunk/src/supplicant-manager/nm-supplicant-settings-verify.c

Modified: trunk/libnm-util/crypto.c
==============================================================================
--- trunk/libnm-util/crypto.c	(original)
+++ trunk/libnm-util/crypto.c	Thu Nov 13 21:19:08 2008
@@ -68,26 +68,24 @@
 #define DEK_INFO_TAG "DEK-Info: "
 #define PROC_TYPE_TAG "Proc-Type: "
 
-static char *
-parse_key_file (const char *filename,
-                int key_type,
-                gsize *out_length,
-                char **out_cipher,
-                char **out_iv,
-                GError **error)
+static GByteArray *
+parse_old_openssl_key_file (GByteArray *contents,
+                            int key_type,
+                            char **out_cipher,
+                            char **out_iv,
+                            GError **error)
 {
-	char *contents = NULL;
+	GByteArray *bindata = NULL;
 	char **lines = NULL;
 	char **ln = NULL;
-	gsize length = 0;
 	const char *pos;
 	const char *end;
 	GString *str = NULL;
 	int enc_tags = 0;
 	char *iv = NULL;
 	char *cipher = NULL;
-	char *bindata = NULL;
-	gsize bindata_len = 0;
+	unsigned char *tmp = NULL;
+	gsize tmp_len = 0;
 	const char *start_tag;
 	const char *end_tag;
 
@@ -109,19 +107,16 @@
 		return NULL;
 	}
 
-	if (!g_file_get_contents (filename, &contents, &length, error))
-		return NULL;
-
-	pos = find_tag (start_tag, contents, length);
+	pos = find_tag (start_tag, (const char *) contents->data, contents->len);
 	if (!pos)
 		goto parse_error;
 
 	pos += strlen (start_tag);
 
-	end = find_tag (end_tag, pos, contents + length - pos);
+	end = find_tag (end_tag, pos, (const char *) contents->data + contents->len - pos);
 	if (end == NULL) {
 		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 		             _("PEM key file had no end tag '%s'."),
 		             end_tag);
 		goto parse_error;
@@ -131,7 +126,7 @@
 	lines = g_strsplit (pos, "\n", 0);
 	if (!lines || g_strv_length (lines) <= 1) {
 		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 		             _("Doesn't look like a PEM private key file."));
 		goto parse_error;
 	}
@@ -155,7 +150,7 @@
 		if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
 			if (enc_tags++ != 0) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             _("Malformed PEM file: Proc-Type was not first tag."));
 				goto parse_error;
 			}
@@ -163,7 +158,7 @@
 			p += strlen (PROC_TYPE_TAG);
 			if (strcmp (p, "4,ENCRYPTED")) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             _("Malformed PEM file: unknown Proc-Type tag '%s'."),
 				             p);
 				goto parse_error;
@@ -173,7 +168,7 @@
 
 			if (enc_tags++ != 1) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             _("Malformed PEM file: DEK-Info was not the second tag."));
 				goto parse_error;
 			}
@@ -184,14 +179,14 @@
 			comma = strchr (p, ',');
 			if (!comma || (*(comma + 1) == '\0')) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             _("Malformed PEM file: no IV found in DEK-Info tag."));
 				goto parse_error;
 			}
 			*comma++ = '\0';
 			if (!g_ascii_isxdigit (*comma)) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
 				goto parse_error;
 			}
@@ -212,7 +207,7 @@
 		} else {
 			if ((enc_tags != 0) && (enc_tags != 2)) {
 				g_set_error (error, NM_CRYPTO_ERROR,
-				             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 				             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
 				goto parse_error;
 			}
@@ -220,8 +215,8 @@
 		}
 	}
 
-	bindata = (char *) g_base64_decode (str->str, &bindata_len);
-	if (bindata == NULL || !bindata_len) {
+	tmp = g_base64_decode (str->str, &tmp_len);
+	if (tmp == NULL || !tmp_len) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_DECODE_FAILED,
 		             _("Could not decode private key."));
@@ -230,36 +225,37 @@
 
 	if (lines)
 		g_strfreev (lines);
-	g_free (contents);
 
+	bindata = g_byte_array_sized_new (tmp_len);
+	g_byte_array_append (bindata, tmp, tmp_len);
 	*out_iv = iv;
 	*out_cipher = cipher;
-	*out_length = bindata_len;
 	return bindata;
 
 parse_error:
-	g_free (bindata);
 	g_free (cipher);
 	g_free (iv);
 	if (lines)
 		g_strfreev (lines);
-	g_free (contents);
 	return NULL;
 }
 
 static GByteArray *
 file_to_g_byte_array (const char *filename,
+                      gboolean privkey,
                       GError **error)
 {
 	char *contents, *der = NULL;
 	GByteArray *array = NULL;
 	gsize length = 0;
-	const char *pos;
+	const char *pos = NULL;
 
 	if (!g_file_get_contents (filename, &contents, &length, error))
 		return NULL;
 
-	pos = find_tag (pem_cert_begin, contents, length);
+	if (!privkey)
+		pos = find_tag (pem_cert_begin, contents, length);
+
 	if (pos) {
 		const char *end;
 
@@ -267,7 +263,7 @@
 		end = find_tag (pem_cert_end, pos, contents + length - pos);
 		if (end == NULL) {
 			g_set_error (error, NM_CRYPTO_ERROR,
-			             NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 			             _("PEM certificate '%s' had no end tag '%s'."),
 			             filename, pem_cert_end);
 			goto done;
@@ -295,7 +291,7 @@
 	if (array->len != length) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
-		             _("Not enough memory to store certificate data."));
+		             _("Not enough memory to store file data."));
 		g_byte_array_free (array, TRUE);
 		array = NULL;
 	}
@@ -420,8 +416,7 @@
 static char *
 decrypt_key (const char *cipher,
              int key_type,
-             const char *data,
-             gsize data_len,
+             GByteArray *data,
              const char *iv,
              const char *password,
              gsize *out_len,
@@ -443,7 +438,7 @@
 		goto out;
 
 	output = crypto_decrypt (cipher, key_type,
-	                         data, data_len,
+	                         data,
 	                         bin_iv, bin_iv_len,
 	                         key, key_len,
 	                         out_len,
@@ -467,38 +462,60 @@
 	return output;
 }
 
-
 GByteArray *
-crypto_get_private_key (const char *file,
-                        const char *password,
-                        guint32 *out_key_type,
-                        GError **error)
+crypto_get_private_key_data (GByteArray *contents,
+                             const char *password,
+                             NMCryptoKeyType *out_key_type,
+                             NMCryptoFileFormat *out_file_type,
+                             GError **error)
 {
 	GByteArray *array = NULL;
-	guint32 key_type = NM_CRYPTO_KEY_TYPE_RSA;
-	char *data = NULL;
-	gsize data_len = 0;
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA;
+	GByteArray *data;
 	char *iv = NULL;
 	char *cipher = NULL;
 	char *decrypted = NULL;
 	gsize decrypted_len = 0;
 
-	/* Try RSA first */
-	data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
+	g_return_val_if_fail (contents != NULL, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_key_type != NULL, NULL);
+	g_return_val_if_fail (out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
+	g_return_val_if_fail (out_file_type != NULL, NULL);
+	g_return_val_if_fail (out_file_type == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
+
+	/* Try PKCS#12 first */
+	if (crypto_verify_pkcs12 (contents, password, NULL)) {
+		*out_key_type = NM_CRYPTO_KEY_TYPE_ENCRYPTED;
+		*out_file_type = NM_CRYPTO_FILE_FORMAT_PKCS12;
+
+		array = g_byte_array_sized_new (contents->len);
+		g_byte_array_append (array, contents->data, contents->len);
+		return array;
+	}
+
+	/* OpenSSL non-standard legacy PEM files */
+
+	/* Try RSA keys first */
+	data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
 	if (!data) {
 		g_clear_error (error);
 
 		/* DSA next */
 		key_type = NM_CRYPTO_KEY_TYPE_DSA;
-		data = parse_key_file (file, key_type, &data_len, &cipher, &iv, error);
-		if (!data)
+		data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
+		if (!data) {
+			g_clear_error (error);
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+			             _("Unable to determine private key type."));
 			goto out;
+		}
 	}
 
 	decrypted = decrypt_key (cipher,
 	                         key_type,
 	                         data,
-	                         data_len,
 	                         iv,
 	                         password,
 	                         &decrypted_len,
@@ -516,6 +533,7 @@
 
 	g_byte_array_append (array, (const guint8 *) decrypted, decrypted_len);
 	*out_key_type = key_type;
+	*out_file_type = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
 
 out:
 	if (decrypted) {
@@ -523,27 +541,96 @@
 		memset (decrypted, 0, decrypted_len);
 		g_free (decrypted);
 	}
-	g_free (data);
+	if (data)
+		g_byte_array_free (data, TRUE);
 	g_free (cipher);
 	g_free (iv);
 	return array;
 }
 
 GByteArray *
+crypto_get_private_key (const char *file,
+                        const char *password,
+                        NMCryptoKeyType *out_key_type,
+                        NMCryptoFileFormat *out_file_type,
+                        GError **error)
+{
+	GByteArray *contents;
+	GByteArray *key = NULL;
+
+	contents = file_to_g_byte_array (file, TRUE, error);
+	if (contents) {
+		key = crypto_get_private_key_data (contents, password, out_key_type, out_file_type, error);
+		g_byte_array_free (contents, TRUE);
+	}
+	return key;
+}
+
+GByteArray *
 crypto_load_and_verify_certificate (const char *file,
+                                    NMCryptoFileFormat *out_file_format,
                                     GError **error)
 {
 	GByteArray *array;
 
-	array = file_to_g_byte_array (file, error);
+	g_return_val_if_fail (file != NULL, NULL);
+	g_return_val_if_fail (out_file_format != NULL, NULL);
+	g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
+
+	array = file_to_g_byte_array (file, FALSE, error);
 	if (!array)
 		return NULL;
 
-	if (!crypto_verify_cert (array->data, array->len, error)) {
-		g_byte_array_free (array, TRUE);
-		array = NULL;
+	*out_file_format = crypto_verify_cert (array->data, array->len, error);
+	if (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
+		/* Try PKCS#12 */
+		if (crypto_is_pkcs12_data (array)) {
+			*out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
+			g_clear_error (error);
+		} else {
+			g_byte_array_free (array, TRUE);
+			array = NULL;
+		}
 	}
 
 	return array;
 }
 
+gboolean
+crypto_is_pkcs12_data (const GByteArray *data)
+{
+	GError *error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	success = crypto_verify_pkcs12 (data, NULL, &error);
+	if (success)
+		return TRUE;
+
+	/* If the error was just a decryption error, then it's pkcs#12 */
+	if (error) {
+		if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
+			success = TRUE;
+		g_error_free (error);		
+	}
+
+	return success;
+}
+
+gboolean
+crypto_is_pkcs12_file (const char *file)
+{
+	GByteArray *contents;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (file != NULL, FALSE);
+
+	contents = file_to_g_byte_array (file, TRUE, NULL);
+	if (contents) {
+		success = crypto_is_pkcs12_data (contents);
+		g_byte_array_free (contents, TRUE);
+	}
+	return success;
+}
+

Modified: trunk/libnm-util/crypto.h
==============================================================================
--- trunk/libnm-util/crypto.h	(original)
+++ trunk/libnm-util/crypto.h	Thu Nov 13 21:19:08 2008
@@ -31,7 +31,7 @@
 	NM_CRYPTO_ERR_NONE = 0,
 	NM_CRYPTO_ERR_INIT_FAILED,
 	NM_CRYPTO_ERR_CANT_READ_FILE,
-	NM_CRYPTO_ERR_PEM_FORMAT_INVALID,
+	NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
 	NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
 	NM_CRYPTO_ERR_DECODE_FAILED,
 	NM_CRYPTO_ERR_OUT_OF_MEMORY,
@@ -43,14 +43,22 @@
 	NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
 	NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
 	NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+	NM_CRYPTO_ERR_INVALID_PASSWORD,
 };
 
-enum {
+typedef enum {
 	NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
 	NM_CRYPTO_KEY_TYPE_RSA,
 	NM_CRYPTO_KEY_TYPE_DSA,
-};
+	NM_CRYPTO_KEY_TYPE_ENCRYPTED
+} NMCryptoKeyType;
 
+typedef enum {
+	NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
+	NM_CRYPTO_FILE_FORMAT_X509,
+	NM_CRYPTO_FILE_FORMAT_RAW_KEY,
+	NM_CRYPTO_FILE_FORMAT_PKCS12
+} NMCryptoFileFormat;
 
 #define NM_CRYPTO_ERROR _nm_crypto_error_quark ()
 GQuark _nm_crypto_error_quark (void);
@@ -59,14 +67,27 @@
 
 void crypto_deinit (void);
 
+GByteArray * crypto_get_private_key_data (GByteArray *contents,
+                                          const char *password,
+                                          NMCryptoKeyType *out_key_type,
+                                          NMCryptoFileFormat *out_file_format,
+                                          GError **error);
+
 GByteArray * crypto_get_private_key (const char *file,
                                      const char *password,
-                                     guint32 *out_key_type,
+                                     NMCryptoKeyType *out_key_type,
+                                     NMCryptoFileFormat *out_file_format,
                                      GError **error);
 
 GByteArray * crypto_load_and_verify_certificate (const char *file,
+                                                 NMCryptoFileFormat *out_file_format,
                                                  GError **error);
 
+gboolean crypto_is_pkcs12_file (const char *file);
+
+gboolean crypto_is_pkcs12_data (const GByteArray *data);
+
+
 /* Internal utils API bits for crypto providers */
 
 gboolean crypto_md5_hash (const char *salt,
@@ -79,8 +100,7 @@
 
 char * crypto_decrypt (const char *cipher,
                        int key_type,
-                       const char *data,
-                       gsize data_len,
+                       GByteArray *data,
                        const char *iv,
                        const gsize iv_len,
                        const char *key,
@@ -88,8 +108,11 @@
                        gsize *out_len,
                        GError **error);
 
-gboolean crypto_verify_cert (const unsigned char *data,
-                             gsize len,
-                             GError **error);
-
+NMCryptoFileFormat crypto_verify_cert (const unsigned char *data,
+                                       gsize len,
+                                       GError **error);
+
+gboolean crypto_verify_pkcs12 (const GByteArray *data,
+                               const char *password,
+                               GError **error);
 

Modified: trunk/libnm-util/crypto_gnutls.c
==============================================================================
--- trunk/libnm-util/crypto_gnutls.c	(original)
+++ trunk/libnm-util/crypto_gnutls.c	Thu Nov 13 21:19:08 2008
@@ -26,6 +26,7 @@
 #include <gcrypt.h>
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
+#include <gnutls/pkcs12.h>
 
 #include "crypto.h"
 
@@ -117,8 +118,7 @@
 char *
 crypto_decrypt (const char *cipher,
                 int key_type,
-                const char *data,
-                gsize data_len,
+                GByteArray *data,
                 const char *iv,
                 const gsize iv_len,
                 const char *key,
@@ -128,10 +128,10 @@
 {
 	gcry_cipher_hd_t ctx;
 	gcry_error_t err;
-	int cipher_mech;
+	int cipher_mech, i;
 	char *output = NULL;
 	gboolean success = FALSE;
-	gsize len;
+	gsize pad_len;
 
 	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
 		cipher_mech = GCRY_CIPHER_3DES;
@@ -145,7 +145,7 @@
 		return NULL;
 	}
 
-	output = g_malloc0 (data_len + 1);
+	output = g_malloc0 (data->len + 1);
 	if (!output) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
@@ -180,7 +180,7 @@
 		goto out;
 	}
 
-	err = gcry_cipher_decrypt (ctx, output, data_len, data, data_len);
+	err = gcry_cipher_decrypt (ctx, output, data->len, data->data, data->len);
 	if (err) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
@@ -188,11 +188,21 @@
 		             gcry_strsource (err), gcry_strerror (err));
 		goto out;
 	}
-	len = data_len - output[data_len - 1];
-	if (len > data_len)
-		goto out;
+	pad_len = output[data->len - 1];
+
+	/* Validate tail padding; last byte is the padding size, and all pad bytes
+	 * should contain the padding size.
+	 */
+	for (i = 1; i <= pad_len; ++i) {
+		if (output[data->len - i] != pad_len) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+			             _("Failed to decrypt the private key."));
+			goto out;
+		}
+	}
 
-	*out_len = len;
+	*out_len = data->len - pad_len;
 	output[*out_len] = '\0';
 	success = TRUE;
 
@@ -200,7 +210,7 @@
 	if (!success) {
 		if (output) {
 			/* Don't expose key material */
-			memset (output, 0, data_len);
+			memset (output, 0, data->len);
 			g_free (output);
 			output = NULL;
 		}
@@ -209,37 +219,96 @@
 	return output;
 }
 
-gboolean
+NMCryptoFileFormat
 crypto_verify_cert (const unsigned char *data,
                     gsize len,
                     GError **error)
 {
-	gnutls_x509_crt_t crt;
+	gnutls_x509_crt_t der;
 	gnutls_datum dt;
 	int err;
 
-	err = gnutls_x509_crt_init (&crt);
+	err = gnutls_x509_crt_init (&der);
 	if (err < 0) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
 		             _("Error initializing certificate data: %s"),
 		             gnutls_strerror (err));
-		return FALSE;
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 	}
 
+	/* Try DER first */
 	dt.data = (unsigned char *) data;
 	dt.size = len;
+	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
+	if (err == GNUTLS_E_SUCCESS) {
+		gnutls_x509_crt_deinit (der);
+		return NM_CRYPTO_FILE_FORMAT_X509;
+	}
+
+	/* And PEM next */
+	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
+	gnutls_x509_crt_deinit (der);
+	if (err == GNUTLS_E_SUCCESS)
+		return NM_CRYPTO_FILE_FORMAT_X509;
+
+	g_set_error (error, NM_CRYPTO_ERROR,
+	             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+	             _("Couldn't decode certificate: %s"),
+	             gnutls_strerror (err));
+	return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+                      const char *password,
+                      GError **error)
+{
+	gnutls_pkcs12_t p12;
+	gnutls_datum dt;
+	gboolean success = FALSE;
+	int err;
+
+	g_return_val_if_fail (data != NULL, FALSE);
 
-	err = gnutls_x509_crt_import (crt, &dt, GNUTLS_X509_FMT_DER);
+	dt.data = (unsigned char *) data->data;
+	dt.size = data->len;
+
+	err = gnutls_pkcs12_init (&p12);
 	if (err < 0) {
 		g_set_error (error, NM_CRYPTO_ERROR,
-		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
-		             _("Couldn't decode certificate: %s"),
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Couldn't initialize PKCS#12 decoder: %s"),
 		             gnutls_strerror (err));
 		return FALSE;
 	}
 
-	gnutls_x509_crt_deinit (crt);
-	return TRUE;
+	/* DER first */
+	err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0);
+	if (err < 0) {
+		/* PEM next */
+		err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0);
+		if (err < 0) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+			             _("Couldn't decode PKCS#12 file: %s"),
+			             gnutls_strerror (err));
+			goto out;
+		}
+	}
+
+	err = gnutls_pkcs12_verify_mac (p12, password);
+	if (err == GNUTLS_E_SUCCESS)
+		success = TRUE;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Couldn't verify PKCS#12 file: %s"),
+		             gnutls_strerror (err));
+	}
+
+out:
+	gnutls_pkcs12_deinit (p12);
+	return success;
 }
 

Modified: trunk/libnm-util/crypto_nss.c
==============================================================================
--- trunk/libnm-util/crypto_nss.c	(original)
+++ trunk/libnm-util/crypto_nss.c	Thu Nov 13 21:19:08 2008
@@ -21,6 +21,8 @@
  * (C) Copyright 2007 - 2008 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 
@@ -30,6 +32,9 @@
 #include <pkcs11t.h>
 #include <cert.h>
 #include <prerror.h>
+#include <p12.h>
+#include <ciferfam.h>
+#include <p12plcy.h>
 
 #include "crypto.h"
 
@@ -54,6 +59,14 @@
 		return FALSE;
 	}
 
+	SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
+	SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
+	SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
+	SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
+
 	initialized = TRUE;
 	return TRUE;
 }
@@ -125,8 +138,7 @@
 char *
 crypto_decrypt (const char *cipher,
                 int key_type,
-                const char *data,
-                gsize data_len,
+                GByteArray *data,
                 const char *iv,
                 const gsize iv_len,
                 const char *key,
@@ -159,7 +171,7 @@
 		return NULL;
 	}
 
-	output = g_malloc0 (data_len + 1);
+	output = g_malloc0 (data->len + 1);
 	if (!output) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
@@ -206,9 +218,9 @@
 	s = PK11_CipherOp (ctx,
 	                   (unsigned char *) output,
 	                   &tmp1_len,
-	                   data_len,
-	                   (unsigned char *) data,
-	                   data_len);
+	                   data->len,
+	                   data->data,
+	                   data->len);
 	if (s != SECSuccess) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
@@ -220,7 +232,7 @@
 	s = PK11_DigestFinal (ctx,
 	                      (unsigned char *) (output + tmp1_len),
 	                      &tmp2_len,
-	                      data_len - tmp1_len);
+	                      data->len - tmp1_len);
 	if (s != SECSuccess) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
@@ -229,7 +241,7 @@
 		goto out;
 	}
 	len = tmp1_len + tmp2_len;
-	if (len > data_len)
+	if (len > data->len)
 		goto out;
 
 	*out_len = len;
@@ -249,7 +261,7 @@
 	if (!success) {
 		if (output) {
 			/* Don't expose key material */
-			memset (output, 0, data_len);
+			memset (output, 0, data->len);
 			g_free (output);
 			output = NULL;
 		}
@@ -257,23 +269,114 @@
 	return output;
 }
 
-gboolean
+NMCryptoFileFormat
 crypto_verify_cert (const unsigned char *data,
                     gsize len,
                     GError **error)
 {
 	CERTCertificate *cert;
 
+	/* Try DER/PEM first */
 	cert = CERT_DecodeCertFromPackage ((char *) data, len);
 	if (!cert) {
 		g_set_error (error, NM_CRYPTO_ERROR,
 		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
 		             _("Couldn't decode certificate: %d"),
 		             PORT_GetError());
-		return FALSE;
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	}
+
+	CERT_DestroyCertificate (cert);
+	return NM_CRYPTO_FILE_FORMAT_X509;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+                      const char *password,
+                      GError **error)
+{
+	SEC_PKCS12DecoderContext *p12ctx = NULL;
+	SECItem pw = { 0 };
+	PK11SlotInfo *slot = NULL;
+	SECStatus s;
+	char *ucs2_password;
+	glong ucs2_chars = 0;
+	guint16 *p;
+
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	/* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
+	 * any conversions for us.
+	 */
+	if (password && strlen (password)) {
+		ucs2_password = (char *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
+		if (!ucs2_password || !ucs2_chars) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_INVALID_PASSWORD,
+			             _("Couldn't convert password to UCS2: %d"),
+			             PORT_GetError());
+			return FALSE;
+		}
+
+		ucs2_chars *= 2;  /* convert # UCS2 characters -> bytes */
+		pw.data = PORT_ZAlloc(ucs2_chars + 2);
+		memcpy (pw.data, ucs2_password, ucs2_chars);
+		pw.len = ucs2_chars + 2;  /* include terminating NULL */
+
+		memset (ucs2_password, 0, ucs2_chars);
+		g_free (ucs2_password);
+
+#ifndef WORDS_BIGENDIAN
+		for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
+			*p = GUINT16_SWAP_LE_BE (*p);
+#endif
+	} else {
+		/* NULL password */
+		pw.data = NULL;
+		pw.len = 0;
+	}
+
+	slot = PK11_GetInternalKeySlot();
+	p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
+	if (!p12ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Couldn't initialize PKCS#12 decoder: %d"),
+		             PORT_GetError());
+		goto error;
 	}
 
-    CERT_DestroyCertificate (cert);
+	s = SEC_PKCS12DecoderUpdate (p12ctx, data->data, data->len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+		             _("Couldn't decode PKCS#12 file: %d"),
+		             PORT_GetError());
+		goto error;
+	}
+
+	s = SEC_PKCS12DecoderVerify (p12ctx);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Couldn't verify PKCS#12 file: %d"),
+		             PORT_GetError());
+		goto error;
+	}
+
+	SEC_PKCS12DecoderFinish (p12ctx);
+	SECITEM_ZfreeItem (&pw, PR_FALSE);
 	return TRUE;
+
+error:
+	if (p12ctx)
+		SEC_PKCS12DecoderFinish (p12ctx);
+
+	if (slot)
+		PK11_FreeSlot(slot);
+
+	SECITEM_ZfreeItem (&pw, PR_FALSE);
+	return FALSE;
 }
 

Modified: trunk/libnm-util/libnm-util.ver
==============================================================================
--- trunk/libnm-util/libnm-util.ver	(original)
+++ trunk/libnm-util/libnm-util.ver	Thu Nov 13 21:19:08 2008
@@ -52,9 +52,13 @@
 	nm_setting_802_1x_set_phase2_client_cert_from_file;
 	nm_setting_802_1x_get_phase2_private_key;
 	nm_setting_802_1x_set_phase2_private_key_from_file;
+	nm_setting_802_1x_get_phase2_private_key_password;
+	nm_setting_802_1x_get_phase2_private_key_type;
 	nm_setting_802_1x_get_pin;
 	nm_setting_802_1x_get_private_key;
 	nm_setting_802_1x_set_private_key_from_file;
+	nm_setting_802_1x_get_private_key_password;
+	nm_setting_802_1x_get_private_key_type;
 	nm_setting_802_1x_get_psk;
 	nm_setting_802_1x_get_type;
 	nm_setting_802_1x_new;

Modified: trunk/libnm-util/nm-setting-8021x.c
==============================================================================
--- trunk/libnm-util/nm-setting-8021x.c	(original)
+++ trunk/libnm-util/nm-setting-8021x.c	Thu Nov 13 21:19:08 2008
@@ -89,7 +89,9 @@
 	char *pin;
 	char *psk;
 	GByteArray *private_key;
+	char *private_key_password;
 	GByteArray *phase2_private_key;
+	char *phase2_private_key_password;
 } NMSetting8021xPrivate;
 
 enum {
@@ -110,7 +112,9 @@
 	PROP_PHASE2_CLIENT_CERT,
 	PROP_PASSWORD,
 	PROP_PRIVATE_KEY,
+	PROP_PRIVATE_KEY_PASSWORD,
 	PROP_PHASE2_PRIVATE_KEY,
+	PROP_PHASE2_PRIVATE_KEY_PASSWORD,
 	PROP_PIN,
 	PROP_PSK,
 
@@ -226,18 +230,39 @@
 gboolean
 nm_setting_802_1x_set_ca_cert_from_file (NMSetting8021x *self,
                                          const char *filename,
+                                         NMSetting8021xCKType *out_ck_type,
                                          GError **err)
 {
 	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->ca_cert)
 		g_byte_array_free (priv->ca_cert, TRUE);
 
-	priv->ca_cert = crypto_load_and_verify_certificate (filename, err);
+	priv->ca_cert = crypto_load_and_verify_certificate (filename, &format, err);
+	if (priv->ca_cert) {
+		/* wpa_supplicant can only use raw x509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
+			break;
+		default:
+			g_byte_array_free (priv->ca_cert, TRUE);
+			priv->ca_cert = NULL;
+			g_set_error (err,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CA_CERT);
+			break;
+		} 
+	}
 
 	return priv->ca_cert != NULL;
 }
@@ -253,18 +278,42 @@
 gboolean
 nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *self,
                                              const char *filename,
+                                             NMSetting8021xCKType *out_ck_type,
                                              GError **err)
 {
 	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->client_cert)
 		g_byte_array_free (priv->client_cert, TRUE);
 
-	priv->client_cert = crypto_load_and_verify_certificate (filename, err);
+	priv->client_cert = crypto_load_and_verify_certificate (filename, &format, err);
+	if (priv->client_cert) {
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+			break;
+		default:
+			g_byte_array_free (priv->client_cert, TRUE);
+			priv->client_cert = NULL;
+			g_set_error (err,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CLIENT_CERT);
+			break;
+		} 
+	}
 
 	return priv->client_cert != NULL;
 }
@@ -328,21 +377,41 @@
 gboolean
 nm_setting_802_1x_set_phase2_ca_cert_from_file (NMSetting8021x *self,
                                                 const char *filename,
+                                                NMSetting8021xCKType *out_ck_type,
                                                 GError **err)
 {
 	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->phase2_ca_cert)
 		g_byte_array_free (priv->phase2_ca_cert, TRUE);
 
-	priv->phase2_ca_cert = crypto_load_and_verify_certificate (filename, err);
+	priv->phase2_ca_cert = crypto_load_and_verify_certificate (filename, &format, err);
+	if (priv->phase2_ca_cert) {
+		/* wpa_supplicant can only use X509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
+			break;
+		default:
+			g_byte_array_free (priv->phase2_ca_cert, TRUE);
+			priv->phase2_ca_cert = NULL;
+			g_set_error (err,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_CA_CERT);
+			break;
+		} 
+	}
 
 	return priv->phase2_ca_cert != NULL;
-
 }
 
 const GByteArray *
@@ -356,18 +425,43 @@
 gboolean
 nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *self,
                                                     const char *filename,
+                                                    NMSetting8021xCKType *out_ck_type,
                                                     GError **err)
 {
 	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->phase2_client_cert)
 		g_byte_array_free (priv->phase2_client_cert, TRUE);
 
-	priv->phase2_client_cert = crypto_load_and_verify_certificate (filename, err);
+	priv->phase2_client_cert = crypto_load_and_verify_certificate (filename, &format, err);
+	if (priv->phase2_client_cert) {
+		/* Only X509 client certs should be used; not pkcs#12 */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_X509;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+			break;
+		default:
+			g_byte_array_free (priv->phase2_client_cert, TRUE);
+			priv->phase2_client_cert = NULL;
+			g_set_error (err,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CLIENT_CERT);
+			break;
+		} 
+	}
 
 	return priv->phase2_client_cert != NULL;
 }
@@ -404,18 +498,29 @@
 	return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key;
 }
 
+const char *
+nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password;
+}
+
 gboolean
 nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *self,
                                              const char *filename,
                                              const char *password,
+                                             NMSetting8021xCKType *out_ck_type,
                                              GError **err)
 {
 	NMSetting8021xPrivate *priv;
-	guint32 ignore;
+	NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (password != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->private_key) {
@@ -424,11 +529,61 @@
 		g_byte_array_free (priv->private_key, TRUE);
 	}
 
-	priv->private_key = crypto_get_private_key (filename, password, &ignore, err);
+	g_free (priv->private_key_password);
+	priv->private_key_password = NULL;
+
+	priv->private_key = crypto_get_private_key (filename, password, &ignore, &format, err);
+	if (priv->private_key) {
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			// FIXME: use secure memory
+			priv->private_key_password = g_strdup (password);
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		} 
+
+		/* As required by NM, set the client-cert property to the same PKCS#12 data */
+		if (priv->client_cert)
+			g_byte_array_free (priv->client_cert, TRUE);
+
+		priv->client_cert = g_byte_array_sized_new (priv->private_key->len);
+		g_byte_array_append (priv->client_cert, priv->private_key->data, priv->private_key->len);
+	} else {
+		/* As a special case for private keys, even if the decrypt fails,
+		 * return the key's file type.
+		 */
+		if (out_ck_type && crypto_is_pkcs12_file (filename))
+			*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+	}
 
 	return priv->private_key != NULL;
 }
 
+NMSetting8021xCKType
+nm_setting_802_1x_get_private_key_type (NMSetting8021x *setting)
+{
+	NMSetting8021xPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	if (!priv->private_key)
+		return NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
+
+	if (crypto_is_pkcs12_data (priv->private_key))
+		return NM_SETTING_802_1X_CK_TYPE_PKCS12;
+
+	return NM_SETTING_802_1X_CK_TYPE_X509;
+}
+
 const GByteArray *
 nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting)
 {
@@ -437,18 +592,29 @@
 	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key;
 }
 
+const char *
+nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password;
+}
+
 gboolean
 nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *self,
                                                     const char *filename,
                                                     const char *password,
+                                                    NMSetting8021xCKType *out_ck_type,
                                                     GError **err)
 {
 	NMSetting8021xPrivate *priv;
-	guint32 ignore;
+	NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
 	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (password != NULL, FALSE);
+	if (out_ck_type)
+		g_return_val_if_fail (*out_ck_type == NM_SETTING_802_1X_CK_TYPE_UNKNOWN, FALSE);
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 	if (priv->phase2_private_key) {
@@ -457,11 +623,61 @@
 		g_byte_array_free (priv->phase2_private_key, TRUE);
 	}
 
-	priv->phase2_private_key = crypto_get_private_key (filename, password, &ignore, err);
+	g_free (priv->phase2_private_key_password);
+	priv->phase2_private_key_password = NULL;
+
+	priv->phase2_private_key = crypto_get_private_key (filename, password, &ignore, &format, err);
+	if (priv->phase2_private_key) {
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			// FIXME: use secure memory
+			priv->phase2_private_key_password = g_strdup (password);
+			if (out_ck_type)
+				*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		} 
+
+		/* As required by NM, set the client-cert property to the same PKCS#12 data */
+		if (priv->phase2_client_cert)
+			g_byte_array_free (priv->phase2_client_cert, TRUE);
+
+		priv->phase2_client_cert = g_byte_array_sized_new (priv->phase2_private_key->len);
+		g_byte_array_append (priv->phase2_client_cert, priv->phase2_private_key->data, priv->phase2_private_key->len);
+	} else {
+		/* As a special case for private keys, even if the decrypt fails,
+		 * return the key's file type.
+		 */
+		if (out_ck_type && crypto_is_pkcs12_file (filename))
+			*out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12;
+	}
 
 	return priv->phase2_private_key != NULL;
 }
 
+NMSetting8021xCKType
+nm_setting_802_1x_get_phase2_private_key_type (NMSetting8021x *setting)
+{
+	NMSetting8021xPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN);
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	if (!priv->phase2_private_key)
+		return NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
+
+	if (crypto_is_pkcs12_data (priv->phase2_private_key))
+		return NM_SETTING_802_1X_CK_TYPE_PKCS12;
+
+	return NM_SETTING_802_1X_CK_TYPE_X509;
+}
+
 static void
 need_secrets_password (NMSetting8021x *self,
                        GPtrArray *secrets,
@@ -484,6 +700,30 @@
 		g_ptr_array_add (secrets, NM_SETTING_802_1X_PIN);
 }
 
+static gboolean
+need_private_key_password (GByteArray *key, const char *password)
+{
+	GError *error = NULL;
+	gboolean needed = TRUE;
+
+	/* See if a private key password is needed, which basically is whether
+	 * or not the private key is a PKCS#12 file or not, since PKCS#1 files
+	 * are decrypted by the settings service.
+	 */
+	if (!crypto_is_pkcs12_data (key))
+		return FALSE;
+
+	if (crypto_verify_pkcs12 (key, password, &error))
+		return FALSE;  /* pkcs#12 validation successful */
+
+	/* If the error was a decryption error then a password is needed */
+	if (!error || g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
+		needed = TRUE;
+
+	g_clear_error (&error);
+	return needed;
+}
+
 static void
 need_secrets_tls (NMSetting8021x *self,
                   GPtrArray *secrets,
@@ -492,13 +732,15 @@
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 
 	if (phase2) {
-		if (   priv->phase2_client_cert
-		    && (!priv->phase2_private_key || !priv->phase2_private_key->len))
+		if (!priv->phase2_private_key || !priv->phase2_private_key->len)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+		else if (need_private_key_password (priv->phase2_private_key, priv->phase2_private_key_password))
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
 	} else {
-		if (priv->client_cert
-		    && (!priv->private_key || !priv->private_key->len))
+		if (!priv->private_key || !priv->private_key->len)
 			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY);
+		else if (need_private_key_password (priv->private_key, priv->private_key_password))
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 	}
 }
 
@@ -521,6 +763,27 @@
 			             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
 			return FALSE;
 		}
+
+		/* If the private key is PKCS#12, check that it matches the client cert */
+		if (priv->phase2_private_key && crypto_is_pkcs12_data (priv->phase2_private_key)) {
+			if (priv->phase2_private_key->len != priv->phase2_client_cert->len) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+				return FALSE;
+			}
+
+			if (memcmp (priv->phase2_private_key->data,
+			            priv->phase2_client_cert->data,
+			            priv->phase2_private_key->len)) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+				return FALSE;
+			}
+		}
 	} else {
 		if (!priv->client_cert) {
 			g_set_error (error,
@@ -535,6 +798,27 @@
 			             NM_SETTING_802_1X_CLIENT_CERT);
 			return FALSE;
 		}
+
+		/* If the private key is PKCS#12, check that it matches the client cert */
+		if (priv->private_key && crypto_is_pkcs12_data (priv->private_key)) {
+			if (priv->private_key->len != priv->client_cert->len) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_CLIENT_CERT);
+				return FALSE;
+			}
+
+			if (memcmp (priv->private_key->data,
+			            priv->client_cert->data,
+			            priv->private_key->len)) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_CLIENT_CERT);
+				return FALSE;
+			}
+		}
 	}
 
 	return TRUE;
@@ -856,12 +1140,14 @@
 		g_byte_array_free (priv->client_cert, TRUE);
 	if (priv->private_key)
 		g_byte_array_free (priv->private_key, TRUE);
+	g_free (priv->private_key_password);
 	if (priv->phase2_ca_cert)
 		g_byte_array_free (priv->phase2_ca_cert, TRUE);
 	if (priv->phase2_client_cert)
 		g_byte_array_free (priv->phase2_client_cert, TRUE);
 	if (priv->phase2_private_key)
 		g_byte_array_free (priv->phase2_private_key, TRUE);
+	g_free (priv->phase2_private_key_password);
 
 	G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
 }
@@ -943,11 +1229,19 @@
 			g_byte_array_free (priv->private_key, TRUE);
 		priv->private_key = g_value_dup_boxed (value);
 		break;
+	case PROP_PRIVATE_KEY_PASSWORD:
+		g_free (priv->private_key_password);
+		priv->private_key_password = g_value_dup_string (value);
+		break;
 	case PROP_PHASE2_PRIVATE_KEY:
 		if (priv->phase2_private_key)
 			g_byte_array_free (priv->phase2_private_key, TRUE);
 		priv->phase2_private_key = g_value_dup_boxed (value);
 		break;
+	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+		g_free (priv->phase2_private_key_password);
+		priv->phase2_private_key_password = g_value_dup_string (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1010,9 +1304,15 @@
 	case PROP_PRIVATE_KEY:
 		g_value_set_boxed (value, priv->private_key);
 		break;
+	case PROP_PRIVATE_KEY_PASSWORD:
+		g_value_set_string (value, priv->private_key_password);
+		break;
 	case PROP_PHASE2_PRIVATE_KEY:
 		g_value_set_boxed (value, priv->phase2_private_key);
 		break;
+	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+		g_value_set_string (value, priv->phase2_private_key_password);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1166,6 +1466,14 @@
 							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
 
 	g_object_class_install_property
+		(object_class, PROP_PRIVATE_KEY_PASSWORD,
+		 g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+						  "Private key password",
+						  "Private key password",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	g_object_class_install_property
 		(object_class, PROP_PHASE2_PRIVATE_KEY,
 		 _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
 							   "Phase2 private key",
@@ -1173,6 +1481,14 @@
 							   DBUS_TYPE_G_UCHAR_ARRAY,
 							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
 
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
+						  "Phase2 private key password",
+						  "Phase2 private key password",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
 	/* Initialize crypto lbrary. */
 	if (!nm_utils_init (&error)) {
 		g_warning ("Couldn't initilize nm-utils/crypto system: %d %s",

Modified: trunk/libnm-util/nm-setting-8021x.h
==============================================================================
--- trunk/libnm-util/nm-setting-8021x.h	(original)
+++ trunk/libnm-util/nm-setting-8021x.h	Thu Nov 13 21:19:08 2008
@@ -30,6 +30,13 @@
 
 G_BEGIN_DECLS
 
+typedef enum {
+	NM_SETTING_802_1X_CK_TYPE_UNKNOWN = 0,
+	NM_SETTING_802_1X_CK_TYPE_X509,
+	NM_SETTING_802_1X_CK_TYPE_RAW_KEY,
+	NM_SETTING_802_1X_CK_TYPE_PKCS12
+} NMSetting8021xCKType;
+
 #define NM_TYPE_SETTING_802_1X            (nm_setting_802_1x_get_type ())
 #define NM_SETTING_802_1X(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021x))
 #define NM_SETTING_802_1X_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
@@ -69,7 +76,9 @@
 #define NM_SETTING_802_1X_PHASE2_CLIENT_CERT "phase2-client-cert"
 #define NM_SETTING_802_1X_PASSWORD "password"
 #define NM_SETTING_802_1X_PRIVATE_KEY "private-key"
+#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD "private-key-password"
 #define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY "phase2-private-key"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD "phase2-private-key-password"
 #define NM_SETTING_802_1X_PIN "pin"
 #define NM_SETTING_802_1X_PSK "psk"
 
@@ -99,11 +108,13 @@
 const char *      nm_setting_802_1x_get_ca_path                      (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_ca_cert_from_file            (NMSetting8021x *setting,
                                                                       const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
 
 const GByteArray *nm_setting_802_1x_get_client_cert                  (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_client_cert_from_file        (NMSetting8021x *setting,
                                                                       const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
 
 const char *      nm_setting_802_1x_get_phase1_peapver               (NMSetting8021x *setting);
@@ -120,11 +131,13 @@
 const char *      nm_setting_802_1x_get_phase2_ca_path               (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_phase2_ca_cert_from_file     (NMSetting8021x *setting,
                                                                       const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
 
 const GByteArray *nm_setting_802_1x_get_phase2_client_cert           (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *setting,
                                                                       const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
 
 const char *      nm_setting_802_1x_get_password                     (NMSetting8021x *setting);
@@ -133,17 +146,39 @@
 
 const char *      nm_setting_802_1x_get_psk                          (NMSetting8021x *setting);
 
+/* PRIVATE KEY NOTE: when PKCS#12 private keys are used, the PKCS#12 data must
+ * be passed to NetworkManager as PKCS#12 (ie, shrouded).  In this case, the
+ * private key password must also be passed to NetworkManager, and the
+ * appropriate "client-cert" (or "phase2-client-cert") property of the
+ * NMSetting8021x object must also contain the exact same PKCS#12 data that the
+ * private key will when NetworkManager requests secrets.
+ *
+ * When OpenSSL-derived "traditional" format (ie S/MIME style, not PKCS#8) RSA
+ * and DSA keys are used, they must passed to NetworkManager completely
+ * decrypted because the OpenSSL "traditional" format is non-standard and is not
+ * complete enough for all crypto libraries to use.  Thus, for OpenSSL
+ * "traditional" format keys, the private key password is not passed to
+ * NetworkManager, and the appropriate "client-cert" (or "phase2-client-cert")
+ * property of the NMSetting8021x object must be a valid client certificate.
+ */
+
 const GByteArray *nm_setting_802_1x_get_private_key                  (NMSetting8021x *setting);
+const char *      nm_setting_802_1x_get_private_key_password         (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_private_key_from_file        (NMSetting8021x *setting,
                                                                       const char *filename,
                                                                       const char *password,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
+NMSetting8021xCKType nm_setting_802_1x_get_private_key_type          (NMSetting8021x *setting);
 
 const GByteArray *nm_setting_802_1x_get_phase2_private_key           (NMSetting8021x *setting);
+const char *      nm_setting_802_1x_get_phase2_private_key_password  (NMSetting8021x *setting);
 gboolean          nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *setting,
                                                                       const char *filename,
                                                                       const char *password,
+                                                                      NMSetting8021xCKType *out_ck_type,
                                                                       GError **err);
+NMSetting8021xCKType nm_setting_802_1x_get_phase2_private_key_type   (NMSetting8021x *setting);
 
 G_END_DECLS
 

Modified: trunk/libnm-util/test-crypto.c
==============================================================================
--- trunk/libnm-util/test-crypto.c	(original)
+++ trunk/libnm-util/test-crypto.c	Thu Nov 13 21:19:08 2008
@@ -103,7 +103,6 @@
 
 int main (int argc, char **argv)
 {
-	guint32 key_type = 0;
 	int mode = 0;
 	const char *file;
 	GError *error = NULL;
@@ -140,26 +139,54 @@
 
 	if (mode == MODE_CERT) {
 		GByteArray *array;
+		NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 
-		array = crypto_load_and_verify_certificate (file, &error);
+		array = crypto_load_and_verify_certificate (file, &format, &error);
 		if (!array) {
 			g_warning ("Couldn't read certificate file '%s': %d %s",
 			           file, error->code, error->message);
 			goto out;
 		}
+
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			g_message ("Format: pkcs#1");
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			g_message ("Format: pkcs#12");
+			break;
+		default:
+			g_message ("Format: unknown");
+			break;
+		}
+
 		g_byte_array_free (array, TRUE);
 	} else if (mode == MODE_KEY) {
+		NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+		NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
 		const char *password = argv[3];
 		GByteArray *array;
 
-		array = crypto_get_private_key (file, password, &key_type, &error);
+		array = crypto_get_private_key (file, password, &key_type, &format, &error);
 		if (!array) {
 			g_warning ("Couldn't read key file '%s': %d %s",
 			           file, error->code, error->message);
 			goto out;
 		}
 
-		dump_key_to_pem ((const char *) array->data, array->len, key_type);
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
+			g_message ("Original format: pkcs#1\n");
+			dump_key_to_pem ((const char *) array->data, array->len, key_type);
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			g_message ("Original format: pkcs#12");
+			break;
+		default:
+			g_message ("Original format: unknown");
+			break;
+		}
+
 		g_byte_array_free (array, TRUE);
 	} else {
 		g_assert_not_reached ();

Modified: trunk/src/supplicant-manager/nm-supplicant-config.c
==============================================================================
--- trunk/src/supplicant-manager/nm-supplicant-config.c	(original)
+++ trunk/src/supplicant-manager/nm-supplicant-config.c	Thu Nov 13 21:19:08 2008
@@ -561,10 +561,10 @@
                                         gboolean wired)
 {
 	NMSupplicantConfigPrivate *priv;
-	char * value;
+	char *value, *tmp;
 	gboolean success;
 	GString *phase1, *phase2;
-	char *tmp;
+	const GByteArray *array;
 
 	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
 	g_return_val_if_fail (setting != NULL, FALSE);
@@ -620,18 +620,41 @@
 		ADD_STRING_VAL (phase2->str, "phase2", FALSE, FALSE, FALSE);
 	g_string_free (phase2, TRUE);
 
-	/* Private key passwords are never passed to wpa_supplicant because the
-	 * user agent is responsible for decoding and decrypting the private key,
-	 * and file paths are never passed to wpa_supplicant to ensure that
-	 * the supplicant can be locked down and doesn't try to read stuff from
-	 * all over the drive.
-	 */
 	ADD_BLOB_VAL (nm_setting_802_1x_get_ca_cert (setting), "ca_cert", connection_uid);
-	ADD_BLOB_VAL (nm_setting_802_1x_get_client_cert (setting), "client_cert", connection_uid);
-	ADD_BLOB_VAL (nm_setting_802_1x_get_private_key (setting), "private_key", connection_uid);
+
+	array = nm_setting_802_1x_get_private_key (setting);
+	if (array) {
+		ADD_BLOB_VAL (array, "private_key", connection_uid);
+
+		switch (nm_setting_802_1x_get_private_key_type (setting)) {
+		case NM_SETTING_802_1X_CK_TYPE_PKCS12:
+			/* Only add the private key password for PKCS#12 keys */
+			ADD_STRING_VAL (nm_setting_802_1x_get_private_key_password (setting), "private_key_passwd", FALSE, FALSE, TRUE);
+			break;
+		default:
+			/* Only add the client cert if the private key is not PKCS#12 */
+			ADD_BLOB_VAL (nm_setting_802_1x_get_client_cert (setting), "client_cert", connection_uid);
+			break;
+		}
+	}
+
 	ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_ca_cert (setting), "ca_cert2", connection_uid);
-	ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_client_cert (setting), "client_cert2", connection_uid);
-	ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_private_key (setting), "private_key2", connection_uid);
+
+	array = nm_setting_802_1x_get_phase2_private_key (setting);
+	if (array) {
+		ADD_BLOB_VAL (array, "private_key2", connection_uid);
+
+		switch (nm_setting_802_1x_get_phase2_private_key_type (setting)) {
+		case NM_SETTING_802_1X_CK_TYPE_PKCS12:
+			/* Only add the private key password for PKCS#12 keys */
+			ADD_STRING_VAL (nm_setting_802_1x_get_phase2_private_key_password (setting), "private_key2_passwd", FALSE, FALSE, TRUE);
+			break;
+		default:
+			/* Only add the client cert if the private key is not PKCS#12 */
+			ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_client_cert (setting), "client_cert2", connection_uid);
+			break;
+		}
+	}
 
 	ADD_STRING_VAL (nm_setting_802_1x_get_identity (setting), "identity", FALSE, FALSE, FALSE);
 	ADD_STRING_VAL (nm_setting_802_1x_get_anonymous_identity (setting), "anonymous_identity", FALSE, FALSE, FALSE);

Modified: trunk/src/supplicant-manager/nm-supplicant-settings-verify.c
==============================================================================
--- trunk/src/supplicant-manager/nm-supplicant-settings-verify.c	(original)
+++ trunk/src/supplicant-manager/nm-supplicant-settings-verify.c	Thu Nov 13 21:19:08 2008
@@ -104,12 +104,14 @@
 	{ "ca_cert",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key_passwd", TYPE_BYTES,   0, 1024, FALSE,  NULL },
 	{ "phase1",             TYPE_KEYWORD, 0, 0, TRUE, phase1_allowed },
 	{ "phase2",             TYPE_KEYWORD, 0, 0, TRUE, phase2_allowed },
 	{ "anonymous_identity", TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_cert2",           TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "private_key2_passwd",TYPE_BYTES,   0, 1024, FALSE,  NULL },
 	{ "pin",                TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "pcsc",               TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "nai",                TYPE_BYTES,   0, 0, FALSE,  NULL },



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