[PATCH 2/2] settings: support SHA-256 hash as CA certificate



Besides CA certificate file, wpa_supplicant also supports SHA-256
hash of the server certificate. The server hash is especially useful
when the CA certificate is not available in the system, and the
system can use the hash to verify the RADIUS server without the
CA certificate file.
---
 libnm-util/libnm-util.ver                      |    1 +
 libnm-util/nm-setting-8021x.c                  |   54 +++++++++++++++++++++-
 libnm-util/nm-setting-8021x.h                  |    6 ++-
 src/settings/plugins/ifnet/connection_parser.c |   59 ++++++++++++++++++++----
 src/settings/plugins/keyfile/reader.c          |    7 +++
 src/settings/plugins/keyfile/writer.c          |   14 +++++-
 6 files changed, 127 insertions(+), 14 deletions(-)

diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver
index 53c2482..54a70e0 100644
--- a/libnm-util/libnm-util.ver
+++ b/libnm-util/libnm-util.ver
@@ -108,6 +108,7 @@ global:
 	nm_setting_802_1x_get_anonymous_identity;
 	nm_setting_802_1x_get_ca_cert_blob;
 	nm_setting_802_1x_get_ca_cert_path;
+	nm_setting_802_1x_get_ca_cert_hash;
 	nm_setting_802_1x_get_ca_cert_scheme;
 	nm_setting_802_1x_get_ca_path;
 	nm_setting_802_1x_get_client_cert_blob;
diff --git a/libnm-util/nm-setting-8021x.c b/libnm-util/nm-setting-8021x.c
index 07fdcc2..70c668d 100644
--- a/libnm-util/nm-setting-8021x.c
+++ b/libnm-util/nm-setting-8021x.c
@@ -64,6 +64,7 @@
  **/
 
 #define SCHEME_PATH "file://"
+#define SCHEME_HASH "hash://server/sha256/"
 
 /**
  * nm_setting_802_1x_error_quark:
@@ -390,6 +391,9 @@ get_cert_scheme (GByteArray *array)
 	if (   (array->len > strlen (SCHEME_PATH))
 	    && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH)))
 		return NM_SETTING_802_1X_CK_SCHEME_PATH;
+	else if (   (array->len > strlen (SCHEME_HASH))
+	         && !memcmp (array->data, SCHEME_HASH, strlen (SCHEME_HASH)))
+		return NM_SETTING_802_1X_CK_SCHEME_HASH;
 
 	return NM_SETTING_802_1X_CK_SCHEME_BLOB;
 }
@@ -400,7 +404,8 @@ get_cert_scheme (GByteArray *array)
  *
  * Returns the scheme used to store the CA certificate.  If the returned scheme
  * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_ca_cert_blob();
- * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path().
+ * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path();
+ * if %NM_SETTING_802_1X_CK_SCHEME_HASH, use nm_setting_802_1x_get_ca_cert_hash().
  *
  * Returns: scheme used to store the CA certificate (blob or path)
  **/
@@ -464,6 +469,32 @@ nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
 	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data + strlen (SCHEME_PATH));
 }
 
+/**
+ * nm_setting_802_1x_get_ca_cert_hash:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate path if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_HASH scheme.  Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: hash of the RADIUS server
+ **/
+const char *
+nm_setting_802_1x_get_ca_cert_hash (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data);
+}
+
 static GByteArray *
 path_to_scheme_value (const char *path)
 {
@@ -515,7 +546,8 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *self,
 	if (cert_path) {
 		g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE);
 		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
-		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_HASH,
 		                      FALSE);
 	}
 
@@ -533,6 +565,17 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *self,
 	if (!cert_path)
 		return TRUE;
 
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH) {
+		int length = strlen (cert_path);
+		if (   length == (strlen (SCHEME_HASH) + 64)
+		    && !g_str_has_prefix (cert_path, SCHEME_HASH))
+			return FALSE;
+		data = g_byte_array_sized_new (length + 1);
+		g_byte_array_append (data, (guint8 *) cert_path, length + 1);
+		priv->ca_cert = data;
+		return TRUE;
+	}
+
 	data = crypto_load_and_verify_certificate (cert_path, &format, error);
 	if (data) {
 		/* wpa_supplicant can only use raw x509 CA certs */
@@ -2397,6 +2440,13 @@ verify_cert (GByteArray *array, const char *prop_name, GError **error)
 				return TRUE;
 		}
 		break;
+	case NM_SETTING_802_1X_CK_SCHEME_HASH:
+		/* For hash-based schemes, verify that the has is zero-terminated */
+		if (array->data[array->len - 1] == '\0') {
+			if (g_str_has_prefix ((char *)array->data, SCHEME_HASH))
+				return TRUE;
+		}
+		break;
 	default:
 		break;
 	}
diff --git a/libnm-util/nm-setting-8021x.h b/libnm-util/nm-setting-8021x.h
index a6016ae..443822c 100644
--- a/libnm-util/nm-setting-8021x.h
+++ b/libnm-util/nm-setting-8021x.h
@@ -57,6 +57,8 @@ typedef enum {
  * item data
  * @NM_SETTING_802_1X_CK_SCHEME_PATH: certificate or key is stored as a path
  * to a file containing the certificate or key data
+ * @NM_SETTING_802_1X_CK_SCHEME_HASH: certificate or key is stored as a path
+ * of the CA server hash
  *
  * #NMSetting8021xCKScheme values indicate how a certificate or private key is
  * stored in the setting properties, either as a blob of the item's data, or as
@@ -65,7 +67,8 @@ typedef enum {
 typedef enum {
 	NM_SETTING_802_1X_CK_SCHEME_UNKNOWN = 0,
 	NM_SETTING_802_1X_CK_SCHEME_BLOB,
-	NM_SETTING_802_1X_CK_SCHEME_PATH
+	NM_SETTING_802_1X_CK_SCHEME_PATH,
+	NM_SETTING_802_1X_CK_SCHEME_HASH
 } NMSetting8021xCKScheme;
 
 
@@ -183,6 +186,7 @@ const char *      nm_setting_802_1x_get_phase2_ca_path               (NMSetting8
 NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme          (NMSetting8021x *setting);
 const GByteArray *     nm_setting_802_1x_get_ca_cert_blob            (NMSetting8021x *setting);
 const char *           nm_setting_802_1x_get_ca_cert_path            (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_ca_cert_hash            (NMSetting8021x *setting);
 gboolean               nm_setting_802_1x_set_ca_cert                 (NMSetting8021x *setting,
                                                                       const char *cert_path,
                                                                       NMSetting8021xCKScheme scheme,
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index b4aaa8d..9a52baa 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -292,6 +292,8 @@ done:
 	return success;
 }
 
+#define SCHEME_HASH "hash://server/sha256/"
+
 static gboolean
 eap_peap_reader (const char *eap_method,
                  const char *ssid,
@@ -307,11 +309,18 @@ eap_peap_reader (const char *eap_method,
 
 	ca_cert = wpa_get_value (ssid, "ca_cert");
 	if (ca_cert) {
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-						    ca_cert,
-						    NM_SETTING_802_1X_CK_SCHEME_PATH,
-						    NULL, error))
-			goto done;
+		if (g_str_has_prefix (ca_cert, SCHEME_HASH))
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+							    ca_cert,
+							    NM_SETTING_802_1X_CK_SCHEME_HASH,
+							    NULL, error))
+				goto done;
+		else
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+							    ca_cert,
+							    NM_SETTING_802_1X_CK_SCHEME_PATH,
+							    NULL, error))
+				goto done;
 	} else {
 		PLUGIN_WARN (IFNET_PLUGIN_NAME, "    warning: missing "
 			     "IEEE_8021X_CA_CERT for EAP method '%s'; this is"
@@ -409,11 +418,18 @@ eap_ttls_reader (const char *eap_method,
 	/* ca cert */
 	ca_cert = wpa_get_value (ssid, "ca_cert");
 	if (ca_cert) {
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-						    ca_cert,
-						    NM_SETTING_802_1X_CK_SCHEME_PATH,
-						    NULL, error))
-			goto done;
+		if (g_str_has_prefix (ca_cert, SCHEME_HASH))
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+							    ca_cert,
+							    NM_SETTING_802_1X_CK_SCHEME_HASH,
+							    NULL, error))
+				goto done;
+		else
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+							    ca_cert,
+							    NM_SETTING_802_1X_CK_SCHEME_PATH,
+							    NULL, error))
+				goto done;
 	} else {
 		PLUGIN_WARN (IFNET_PLUGIN_NAME, "    warning: missing "
 			     "IEEE_8021X_CA_CERT for EAP method '%s'; this is"
@@ -1769,12 +1785,14 @@ error:
 
 typedef NMSetting8021xCKScheme (*SchemeFunc) (NMSetting8021x * setting);
 typedef const char *(*PathFunc) (NMSetting8021x * setting);
+typedef const char *(*HashFunc) (NMSetting8021x * setting);
 typedef const GByteArray *(*BlobFunc) (NMSetting8021x * setting);
 
 typedef struct ObjectType {
 	const char *setting_key;
 	SchemeFunc scheme_func;
 	PathFunc path_func;
+	HashFunc hash_func;
 	BlobFunc blob_func;
 	const char *conn_name_key;
 	const char *suffix;
@@ -1784,6 +1802,7 @@ static const ObjectType ca_type = {
 	NM_SETTING_802_1X_CA_CERT,
 	nm_setting_802_1x_get_ca_cert_scheme,
 	nm_setting_802_1x_get_ca_cert_path,
+	nm_setting_802_1x_get_ca_cert_hash,
 	nm_setting_802_1x_get_ca_cert_blob,
 	"ca_cert",
 	"ca-cert.der"
@@ -1793,6 +1812,7 @@ static const ObjectType phase2_ca_type = {
 	NM_SETTING_802_1X_PHASE2_CA_CERT,
 	nm_setting_802_1x_get_phase2_ca_cert_scheme,
 	nm_setting_802_1x_get_phase2_ca_cert_path,
+	NULL,
 	nm_setting_802_1x_get_phase2_ca_cert_blob,
 	"ca_cert2",
 	"inner-ca-cert.der"
@@ -1802,6 +1822,7 @@ static const ObjectType client_type = {
 	NM_SETTING_802_1X_CLIENT_CERT,
 	nm_setting_802_1x_get_client_cert_scheme,
 	nm_setting_802_1x_get_client_cert_path,
+	NULL,
 	nm_setting_802_1x_get_client_cert_blob,
 	"client_cert",
 	"client-cert.der"
@@ -1811,6 +1832,7 @@ static const ObjectType phase2_client_type = {
 	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
 	nm_setting_802_1x_get_phase2_client_cert_scheme,
 	nm_setting_802_1x_get_phase2_client_cert_path,
+	NULL,
 	nm_setting_802_1x_get_phase2_client_cert_blob,
 	"client_cert2",
 	"inner-client-cert.der"
@@ -1820,6 +1842,7 @@ static const ObjectType pk_type = {
 	NM_SETTING_802_1X_PRIVATE_KEY,
 	nm_setting_802_1x_get_private_key_scheme,
 	nm_setting_802_1x_get_private_key_path,
+	NULL,
 	nm_setting_802_1x_get_private_key_blob,
 	"private_key",
 	"private-key.pem"
@@ -1829,6 +1852,7 @@ static const ObjectType phase2_pk_type = {
 	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
 	nm_setting_802_1x_get_phase2_private_key_scheme,
 	nm_setting_802_1x_get_phase2_private_key_path,
+	NULL,
 	nm_setting_802_1x_get_phase2_private_key_blob,
 	"private_key2",
 	"inner-private-key.pem"
@@ -1838,6 +1862,7 @@ static const ObjectType p12_type = {
 	NM_SETTING_802_1X_PRIVATE_KEY,
 	nm_setting_802_1x_get_private_key_scheme,
 	nm_setting_802_1x_get_private_key_path,
+	NULL,
 	nm_setting_802_1x_get_private_key_blob,
 	"private_key",
 	"private-key.p12"
@@ -1847,6 +1872,7 @@ static const ObjectType phase2_p12_type = {
 	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
 	nm_setting_802_1x_get_phase2_private_key_scheme,
 	nm_setting_802_1x_get_phase2_private_key_path,
+	NULL,
 	nm_setting_802_1x_get_phase2_private_key_blob,
 	"private_key2",
 	"inner-private-key.p12"
@@ -1861,6 +1887,7 @@ write_object (NMSetting8021x *s_8021x,
 {
 	NMSetting8021xCKScheme scheme;
 	const char *path = NULL;
+	const char *hash = NULL;
 	const GByteArray *blob = NULL;
 
 	g_return_val_if_fail (conn_name != NULL, FALSE);
@@ -1879,6 +1906,9 @@ write_object (NMSetting8021x *s_8021x,
 		case NM_SETTING_802_1X_CK_SCHEME_PATH:
 			path = (*(objtype->path_func)) (s_8021x);
 			break;
+		case NM_SETTING_802_1X_CK_SCHEME_HASH:
+			hash = (*(objtype->hash_func)) (s_8021x);
+			break;
 		default:
 			break;
 		}
@@ -1893,6 +1923,15 @@ write_object (NMSetting8021x *s_8021x,
 		return TRUE;
 	}
 
+	/* If the object hash was specified, prefer that over any raw cert data that
+	 * may have been sent.
+	 */
+	if (hash) {
+		wpa_set_data (conn_name, (gchar *) objtype->conn_name_key,
+			      (gchar *) hash);
+		return TRUE;
+	}
+
 	/* does not support writing encryption data now */
 	if (blob) {
 		PLUGIN_WARN (IFNET_PLUGIN_NAME,
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index 4128b9f..7cc7a32 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -852,6 +852,7 @@ get_cert_path (const char *keyfile_path, GByteArray *cert_path)
 }
 
 #define SCHEME_PATH "file://"
+#define SCHEME_HASH "hash://server/sha256/"
 
 static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
 
@@ -876,6 +877,12 @@ handle_as_scheme (GByteArray *array, NMSetting *setting, const char *key)
 	    && (array->data[array->len - 1] == '\0')) {
 		g_object_set (setting, key, array, NULL);
 		return TRUE;
+	} else if (   (array->len > strlen (SCHEME_HASH))
+	           && g_str_has_prefix ((const char *) array->data, SCHEME_HASH)
+	           && (array->data[array->len - 1] == '\0')) {
+		/* It's the HASH scheme, can just set plain data */
+		g_object_set (setting, key, array, NULL);
+		return TRUE;
 	}
 	return FALSE;
 }
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index db43b23..6da0876 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/keyfile/writer.c
@@ -539,6 +539,7 @@ typedef struct ObjectType {
 	NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
 	NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
 	const char *           (*path_func)   (NMSetting8021x *setting);
+	const char *           (*hash_func)   (NMSetting8021x *setting);
 	const GByteArray *     (*blob_func)   (NMSetting8021x *setting);
 } ObjectType;
 
@@ -549,6 +550,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_ca_cert_scheme,
 	  NULL,
 	  nm_setting_802_1x_get_ca_cert_path,
+	  nm_setting_802_1x_get_ca_cert_hash,
 	  nm_setting_802_1x_get_ca_cert_blob },
 
 	{ NM_SETTING_802_1X_PHASE2_CA_CERT,
@@ -557,6 +559,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_phase2_ca_cert_scheme,
 	  NULL,
 	  nm_setting_802_1x_get_phase2_ca_cert_path,
+	  NULL,
 	  nm_setting_802_1x_get_phase2_ca_cert_blob },
 
 	{ NM_SETTING_802_1X_CLIENT_CERT,
@@ -565,6 +568,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_client_cert_scheme,
 	  NULL,
 	  nm_setting_802_1x_get_client_cert_path,
+	  NULL,
 	  nm_setting_802_1x_get_client_cert_blob },
 
 	{ NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
@@ -573,6 +577,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_phase2_client_cert_scheme,
 	  NULL,
 	  nm_setting_802_1x_get_phase2_client_cert_path,
+	  NULL,
 	  nm_setting_802_1x_get_phase2_client_cert_blob },
 
 	{ NM_SETTING_802_1X_PRIVATE_KEY,
@@ -581,6 +586,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_private_key_scheme,
 	  nm_setting_802_1x_get_private_key_format,
 	  nm_setting_802_1x_get_private_key_path,
+	  NULL,
 	  nm_setting_802_1x_get_private_key_blob },
 
 	{ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
@@ -589,6 +595,7 @@ static const ObjectType objtypes[10] = {
 	  nm_setting_802_1x_get_phase2_private_key_scheme,
 	  nm_setting_802_1x_get_phase2_private_key_format,
 	  nm_setting_802_1x_get_phase2_private_key_path,
+	  NULL,
 	  nm_setting_802_1x_get_phase2_private_key_blob },
 
 	{ NULL },
@@ -667,7 +674,7 @@ cert_writer (GKeyFile *file,
 	const char *setting_name = nm_setting_get_name (setting);
 	NMSetting8021xCKScheme scheme;
 	NMSetting8021xCKFormat format;
-	const char *path = NULL, *ext = "pem";
+	const char *path = NULL, *hash = NULL, *ext = "pem";
 	const ObjectType *objtype = NULL;
 	int i;
 
@@ -729,6 +736,11 @@ cert_writer (GKeyFile *file,
 			g_error_free (error);
 		}
 		g_free (new_path);
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH) {
+		hash = objtype->hash_func (NM_SETTING_802_1X (setting));
+		g_assert (hash);
+
+		g_key_file_set_string (file, setting_name, key, hash);
 	} else
 		g_assert_not_reached ();
 }
-- 
1.7.3.4



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