From 01b4539b4b9905f8f00ab6f16b8a1b10a1f2eda1 Mon Sep 17 00:00:00 2001
From: Tambet Ingo <tambet@gmail.com>
Date: Tue, 13 Jan 2009 12:31:34 +0200
Subject: [PATCH] Certificate formats.


Index: b/libnm-util/nm-setting-8021x.c
===================================================================
--- a/libnm-util/nm-setting-8021x.c
+++ b/libnm-util/nm-setting-8021x.c
@@ -717,6 +717,9 @@ need_private_key_password (GByteArray *k
 	GError *error = NULL;
 	gboolean needed = TRUE;
 
+	if (key && key->data && g_str_has_prefix ((char *) key->data, NM_SETTING_802_1X_CK_FORMAT_FILE))
+		return FALSE;
+
 	/* 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.
@@ -1384,7 +1387,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "CA certificate",
 							   "CA certificate",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_CA_PATH,
@@ -1400,7 +1403,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "Client certificate",
 							   "Client certificate",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_PHASE1_PEAPVER,
@@ -1448,7 +1451,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "Phase2 CA certificate",
 							   "Phase2 CA certificate",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_PHASE2_CA_PATH,
@@ -1464,7 +1467,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "Phase2 client certificate",
 							   "Phase2 client certificate",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_PASSWORD,
@@ -1480,7 +1483,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "Private key",
 							   "Private key",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_PRIVATE_KEY_PASSWORD,
@@ -1496,7 +1499,7 @@ nm_setting_802_1x_class_init (NMSetting8
 							   "Phase2 private key",
 							   "Phase2 private key",
 							   DBUS_TYPE_G_UCHAR_ARRAY,
-							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_CERTIFICATE));
 
 	g_object_class_install_property
 		(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
Index: b/libnm-util/nm-setting-8021x.h
===================================================================
--- a/libnm-util/nm-setting-8021x.h
+++ b/libnm-util/nm-setting-8021x.h
@@ -83,6 +83,9 @@ GQuark nm_setting_802_1x_error_quark (vo
 #define NM_SETTING_802_1X_PSK "psk"
 #define NM_SETTING_802_1X_SYSTEM_CA_CERTS "system-ca-certs"
 
+#define NM_SETTING_802_1X_CK_FORMAT_ID   "id:"
+#define NM_SETTING_802_1X_CK_FORMAT_FILE "file:"
+
 typedef struct {
 	NMSetting parent;
 } NMSetting8021x;
Index: b/libnm-util/nm-setting.h
===================================================================
--- a/libnm-util/nm-setting.h
+++ b/libnm-util/nm-setting.h
@@ -81,6 +81,8 @@ GQuark nm_setting_error_quark (void);
  */
 #define NM_SETTING_PARAM_FUZZY_IGNORE (1 << (3 + G_PARAM_USER_SHIFT))
 
+#define NM_SETTING_PARAM_CERTIFICATE  (1 << (4 + G_PARAM_USER_SHIFT))
+
 #define NM_SETTING_NAME "name"
 
 /**
Index: b/src/supplicant-manager/nm-supplicant-config.c
===================================================================
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -694,6 +694,174 @@ nm_supplicant_config_add_setting_wireles
 	return TRUE;
 }
 
+static gboolean
+add_certificates (NMSupplicantConfig *self, NMSetting8021x *setting, const char *connection_uid)
+{
+	const GByteArray *array;
+	const char *str;
+	char *value;
+	gboolean use_system_ca;
+	gboolean send_private_key_passwd;
+	gboolean send_client_cert;
+	gboolean success;
+
+	use_system_ca = nm_setting_802_1x_get_system_ca_certs (setting) || nm_setting_802_1x_get_ca_cert (setting) == NULL;
+
+	if (use_system_ca) {
+		add_string_val (self, SYSTEM_CA_PATH, "ca_path", FALSE, FALSE);
+	} else {
+		array = nm_setting_802_1x_get_ca_cert (setting);
+		if (array && array->data) {
+			str = (char *) array->data;
+
+			if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID))
+				nm_supplicant_config_add_option (self, "ca_cert_id",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+												 -1, FALSE);
+			else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE))
+				nm_supplicant_config_add_option (self, "ca_cert",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+												 -1, FALSE);
+			else {
+				ADD_BLOB_VAL (array, "ca_cert", connection_uid);
+			}
+		}
+	}
+
+	array = nm_setting_802_1x_get_private_key (setting);
+	if (array && array->data) {
+		str = (char *) array->data;
+
+		if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID)) {
+			nm_supplicant_config_add_option (self, "key_id",
+											 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+											 -1, FALSE);
+
+			send_private_key_passwd = FALSE;
+			send_client_cert = TRUE;
+		} else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE)) {
+			nm_supplicant_config_add_option (self, "private_key",
+											 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+											 -1, FALSE);
+
+			send_private_key_passwd = TRUE;
+			send_client_cert = TRUE;
+		} else {
+			ADD_BLOB_VAL (array, "private_key", connection_uid);
+
+			if (nm_setting_802_1x_get_private_key_type (setting) == NM_SETTING_802_1X_CK_TYPE_PKCS12) {
+				send_private_key_passwd = TRUE;
+				send_client_cert = FALSE;
+			} else {
+				send_private_key_passwd = FALSE;
+				send_client_cert = TRUE;
+			}
+		}
+	}
+
+	if (send_private_key_passwd) {
+		add_string_val (self, nm_setting_802_1x_get_private_key_password (setting),
+						"private_key_passwd", FALSE, TRUE);
+	} 
+
+	if (send_client_cert) {
+		array = nm_setting_802_1x_get_client_cert (setting);
+		if (array && array->data) {
+			str = (char *) array->data;
+
+			if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID))
+				nm_supplicant_config_add_option (self, "cert_id", 
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+												 -1, FALSE);
+			else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE))
+				nm_supplicant_config_add_option (self, "client_cert",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+												 -1, FALSE);
+			else {
+				ADD_BLOB_VAL (array, "client_cert", connection_uid);
+			}
+		}
+	}
+
+	/* phase 2 */
+
+	if (!use_system_ca) {
+		array = nm_setting_802_1x_get_phase2_ca_cert (setting);
+		if (array && array->data) {
+			str = (char *) array->data;
+
+			if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID)) {
+				nm_supplicant_config_add_option (self, "ca_cert2_id",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+												 -1, FALSE);
+			} else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE)) {
+				nm_supplicant_config_add_option (self, "ca_cert2",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+												 -1, FALSE);
+			} else {
+				ADD_BLOB_VAL (array, "ca_cert", connection_uid);
+			}
+		}
+	}
+
+	array = nm_setting_802_1x_get_phase2_private_key (setting);
+	if (array && array->data) {
+		str = (char *) array->data;
+
+		if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID)) {
+			nm_supplicant_config_add_option (self, "key2_id",
+											 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+											 -1, FALSE);
+
+			send_private_key_passwd = FALSE;
+			send_client_cert = TRUE;
+		} else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE)) {
+			nm_supplicant_config_add_option (self, "private_key2",
+											 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+											 -1, FALSE);
+
+			send_private_key_passwd = TRUE;
+			send_client_cert = TRUE;
+		} else {
+			ADD_BLOB_VAL (array, "private_key2", connection_uid);
+
+			if (nm_setting_802_1x_get_phase2_private_key_type (setting) == NM_SETTING_802_1X_CK_TYPE_PKCS12) {
+				send_private_key_passwd = TRUE;
+				send_client_cert = FALSE;
+			} else {
+				send_private_key_passwd = FALSE;
+				send_client_cert = TRUE;
+			}
+		}
+	}
+
+	if (send_private_key_passwd) {
+		add_string_val (self, nm_setting_802_1x_get_phase2_private_key_password (setting),
+						"private_key2_passwd", FALSE, TRUE);
+	} 
+
+	if (send_client_cert) {
+		array = nm_setting_802_1x_get_phase2_client_cert (setting);
+		if (array && array->data) {
+			str = (char *) array->data;
+
+			if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_ID)) {
+				nm_supplicant_config_add_option (self, "cert2_id", 
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_ID),
+												 -1, FALSE);
+			} else if (g_str_has_prefix (str, NM_SETTING_802_1X_CK_FORMAT_FILE)) {
+				nm_supplicant_config_add_option (self, "client_cert2",
+												 str + strlen (NM_SETTING_802_1X_CK_FORMAT_FILE),
+												 -1, FALSE);
+			} else {
+				ADD_BLOB_VAL (array, "client_cert2", connection_uid);
+			}
+		}
+	}
+
+	return TRUE;
+}
+
 gboolean
 nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
                                         NMSetting8021x *setting,
@@ -705,7 +873,6 @@ nm_supplicant_config_add_setting_8021x (
 	const char *peapver, *value;
 	gboolean success;
 	GString *phase1, *phase2;
-	const GByteArray *array;
 
 	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
 	g_return_val_if_fail (setting != NULL, FALSE);
@@ -780,56 +947,6 @@ nm_supplicant_config_add_setting_8021x (
 	}
 	g_string_free (phase2, TRUE);
 
-	if (nm_setting_802_1x_get_system_ca_certs (setting)) {
-		if (!add_string_val (self, SYSTEM_CA_PATH, "ca_path", FALSE, FALSE))
-			return FALSE;
-	} else {
-		ADD_BLOB_VAL (nm_setting_802_1x_get_ca_cert (setting), "ca_cert", 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 */
-			value = nm_setting_802_1x_get_private_key_password (setting);
-			if (!add_string_val (self, value, "private_key_passwd", FALSE, TRUE))
-				return FALSE;
-			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;
-		}
-	}
-
-	if (nm_setting_802_1x_get_system_ca_certs (setting)) {
-		if (!add_string_val (self, SYSTEM_CA_PATH, "ca_path2", FALSE, FALSE))
-			return FALSE;
-	} else {
-		ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_ca_cert (setting), "ca_cert2", 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 */
-			value = nm_setting_802_1x_get_phase2_private_key_password (setting);
-			if (!add_string_val (self, value, "private_key2_passwd", FALSE, TRUE))
-				return FALSE;
-			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;
-		}
-	}
-
 	value = nm_setting_802_1x_get_identity (setting);
 	if (!add_string_val (self, value, "identity", FALSE, FALSE))
 		return FALSE;
@@ -837,6 +954,8 @@ nm_supplicant_config_add_setting_8021x (
 	if (!add_string_val (self, value, "anonymous_identity", FALSE, FALSE))
 		return FALSE;
 
+	add_certificates (self, setting, connection_uid);
+
 	return TRUE;
 }
 
Index: b/src/supplicant-manager/nm-supplicant-settings-verify.c
===================================================================
--- a/src/supplicant-manager/nm-supplicant-settings-verify.c
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.c
@@ -103,16 +103,22 @@ static const struct Opt opt_table[] = {
 	{ "password",           TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_path",            TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_cert",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "ca_cert_id",         TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "cert_id",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key_passwd", TYPE_BYTES,   0, 1024, FALSE,  NULL },
+	{ "key_id",             TYPE_BYTES,   0, 65536, 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 },
+	{ "ca_cert2_id",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
+	{ "cert2_id",           TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key2_passwd",TYPE_BYTES,   0, 1024, FALSE,  NULL },
+	{ "key2_id",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "pin",                TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "pcsc",               TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "nai",                TYPE_BYTES,   0, 0, FALSE,  NULL },
Index: b/system-settings/plugins/keyfile/io/reader.c
===================================================================
--- a/system-settings/plugins/keyfile/io/reader.c
+++ b/system-settings/plugins/keyfile/io/reader.c
@@ -31,6 +31,7 @@
 #include <nm-setting-connection.h>
 #include <nm-setting-wired.h>
 #include <nm-setting-wireless.h>
+#include <nm-setting-8021x.h>
 #include <arpa/inet.h>
 #include <netinet/ether.h>
 #include <string.h>
@@ -397,6 +398,55 @@ read_hash_of_string (GKeyFile *file, NMS
 	g_strfreev (keys);
 }
 
+static void
+read_guchar_array (GKeyFile *file, NMSetting *setting, const char *key)
+{
+	gint *tmp;
+	GByteArray *array;
+	gsize length;
+	int i;
+
+	tmp = g_key_file_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
+	array = g_byte_array_sized_new (length);
+	for (i = 0; i < length; i++) {
+		int val = tmp[i];
+		unsigned char v = (unsigned char) (val & 0xFF);
+
+		if (val < 0 || val > 255)
+			g_warning ("Value out of range for a byte value");
+		else
+			g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
+	}
+
+	g_object_set (setting, key, array, NULL);
+	g_byte_array_free (array, TRUE);
+	g_free (tmp);
+}
+
+static void
+read_certificate (NMSetting *setting, GKeyFile *file, const char *key)
+{
+	char *value;
+
+	value = g_key_file_get_value (file, nm_setting_get_name (setting), key, NULL);
+	if (!value)
+		return;
+
+	if (g_str_has_prefix (value, NM_SETTING_802_1X_CK_FORMAT_ID) || 
+		g_str_has_prefix (value, NM_SETTING_802_1X_CK_FORMAT_FILE)) {
+		GByteArray *array;
+		gsize len;
+
+		len = strlen (value);
+		array = g_byte_array_sized_new (len);
+		g_byte_array_append (array, (guint8 *) value, len);
+		g_object_set (setting, key, array, NULL);
+		g_byte_array_free (array, TRUE);
+	} else
+		read_guchar_array (file, setting, key);
+
+	g_free (value);
+}
 
 typedef struct {
 	const char *setting_name;
@@ -502,6 +552,12 @@ read_one_setting_value (NMSetting *setti
 		return;
 	}
 
+	/* Certificates are handled differently */
+	if (flags & NM_SETTING_PARAM_CERTIFICATE) {
+		read_certificate (setting, file, key);
+		return;
+	}
+
 	type = G_VALUE_TYPE (value);
 
 	if (type == G_TYPE_STRING) {
@@ -544,29 +600,7 @@ read_one_setting_value (NMSetting *setti
 		g_free (tmp_str);
 		g_object_set (setting, key, uint_val, NULL);
  	} else if (type == DBUS_TYPE_G_UCHAR_ARRAY) {
-		gint *tmp;
-		GByteArray *array;
-		gsize length;
-		int i;
-
-		tmp = g_key_file_get_integer_list (file, setting_name, key, &length, NULL);
-
-		array = g_byte_array_sized_new (length);
-		for (i = 0; i < length; i++) {
-			int val = tmp[i];
-			unsigned char v = (unsigned char) (val & 0xFF);
-
-			if (val < 0 || val > 255) {
-				g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
-				           " between 0 and 255 inclusive)", __func__, setting_name,
-				           key, val);
-			} else
-				g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
-		}
-
-		g_object_set (setting, key, array, NULL);
-		g_byte_array_free (array, TRUE);
-		g_free (tmp);
+		read_guchar_array (file, setting, key);
  	} else if (type == DBUS_TYPE_G_LIST_OF_STRING) {
 		gchar **sa;
 		gsize length;
