From 1e5cdbba7d816b06ce163710d2d6bdc87fe44b51 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 18 Jul 2012 12:14:56 +0800 Subject: [PATCH 1/4] libnm-util: allow the server hash to be the CA certificate --- libnm-core/nm-keyfile-internal.h | 2 + libnm-core/nm-keyfile-reader.c | 9 +++ libnm-core/nm-keyfile-writer.c | 14 +++++ libnm-core/nm-setting-8021x.c | 67 ++++++++++++++++++++++- libnm-core/nm-setting-8021x.h | 5 +- libnm-util/libnm-util.ver | 1 + libnm-util/nm-setting-8021x.c | 51 ++++++++++++++++- libnm-util/nm-setting-8021x.h | 6 +- src/settings/plugins/ifnet/connection_parser.c | 76 +++++++++++++++++++++----- src/settings/plugins/keyfile/writer.c | 7 ++- 10 files changed, 216 insertions(+), 22 deletions(-) diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h index f4bb079..9b28ea6 100644 --- a/libnm-core/nm-keyfile-internal.h +++ b/libnm-core/nm-keyfile-internal.h @@ -33,6 +33,7 @@ #define NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB "data:;base64," #define NM_KEYFILE_CERT_SCHEME_PREFIX_PATH "file://" +#define NM_KEYFILE_CERT_SCHEME_PREFIX_HASH "hash://server/sha256/" char *nm_keyfile_detect_unqualified_path_scheme (const char *base_dir, gconstpointer pdata, @@ -148,6 +149,7 @@ typedef struct { NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting); NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting); const char * (*path_func) (NMSetting8021x *setting); + const char * (*hash_func) (NMSetting8021x *setting); GBytes * (*blob_func) (NMSetting8021x *setting); } NMKeyfileWriteTypeDataCert; diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 353804f..94492cd 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -881,6 +881,15 @@ handle_as_scheme (KeyfileReaderInfo *info, GBytes *bytes, NMSetting *setting, co } return TRUE; } + + if ( data_len >= STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_HASH) + && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_HASH)) { + if (nm_setting_802_1x_check_cert_scheme (data, data_len + 1, NULL) == NM_SETTING_802_1X_CK_SCHEME_HASH) { + g_object_set (setting, key, bytes, NULL); + return TRUE; + } + } + if ( data_len > STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB) && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)) { const char *cdata = data + STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB); diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index ff433b2..c9ce926 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -362,6 +362,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); GBytes * (*blob_func) (NMSetting8021x *setting); } ObjectType; @@ -371,6 +372,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, @@ -378,6 +380,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, @@ -385,6 +388,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, @@ -392,6 +396,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, @@ -399,6 +404,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, @@ -406,6 +412,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 }, @@ -454,6 +461,12 @@ cert_writer_default (NMConnection *connection, nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, path); g_free (tmp); g_free (path_free); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH) { + const char *hash; + hash = cert_data->hash_func (cert_data->setting); + g_assert (hash); + + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, hash); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { GBytes *blob; const guint8 *blob_data; @@ -506,6 +519,7 @@ cert_writer (KeyfileWriterInfo *info, type_data.scheme_func = objtype->scheme_func; type_data.format_func = objtype->format_func; type_data.path_func = objtype->path_func; + type_data.hash_func = objtype->hash_func; type_data.blob_func = objtype->blob_func; if (info->handler) { diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index d53ae43..b68e119 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -480,6 +480,28 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError * } return NM_SETTING_802_1X_CK_SCHEME_PATH; + + } else if ( length >= STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH) + && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH, STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH))) { + gsize hash_length = STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH) + 64; + + if (data[length - 1] != '\0') { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Cert Hash is not NUL terminated")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + + if (length != (hash_length + 1)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Cert Hash is not a SHA256 hash")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + + return NM_SETTING_802_1X_CK_SCHEME_HASH; } return NM_SETTING_802_1X_CK_SCHEME_BLOB; @@ -520,7 +542,8 @@ load_and_verify_certificate (const char *cert_path, * * 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) **/ @@ -586,6 +609,34 @@ nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting) return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_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; + gconstpointer data; + + 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); + + data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL); + return (const char *)data; +} + static GBytes * path_to_scheme_value (const char *path) { @@ -640,7 +691,8 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, 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); } @@ -656,6 +708,17 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, return TRUE; } + if (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH) { + int length = strlen (cert_path); + if ( length == (STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH) + 64) + && !g_str_has_prefix (cert_path, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH)) + return FALSE; + data = g_byte_array_sized_new (length + 1); + g_byte_array_append (data, (guint8 *) cert_path, length + 1); + priv->ca_cert = g_byte_array_free_to_bytes (data); + return TRUE; + } + data = load_and_verify_certificate (cert_path, scheme, &format, error); if (data) { /* wpa_supplicant can only use raw x509 CA certs */ diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h index 3099fbd..82be606 100644 --- a/libnm-core/nm-setting-8021x.h +++ b/libnm-core/nm-setting-8021x.h @@ -32,6 +32,7 @@ G_BEGIN_DECLS #define NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH "file://" +#define NM_SETTING_802_1X_CERT_SCHEME_PREFIX_HASH "hash://server/sha256/" /** * NMSetting8021xCKFormat: @@ -68,7 +69,8 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_format >*/ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ 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; @@ -169,6 +171,7 @@ const char * nm_setting_802_1x_get_phase2_ca_path (NMSetting8 NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting); GBytes * 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/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 9d78e50..6c66e22 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -127,6 +127,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 678d70a..87cf948 100644 --- a/libnm-util/nm-setting-8021x.c +++ b/libnm-util/nm-setting-8021x.c @@ -65,6 +65,7 @@ **/ #define SCHEME_PATH "file://" +#define SCHEME_HASH "hash://server/sha256/" /** * nm_setting_802_1x_error_quark: @@ -444,6 +445,13 @@ get_cert_scheme (GByteArray *array) && g_utf8_validate ((const char *) &array->data[STRLEN (SCHEME_PATH)], array->len - (STRLEN (SCHEME_PATH) + 1), NULL)) return NM_SETTING_802_1X_CK_SCHEME_PATH; return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } else if ( array->len >= STRLEN (SCHEME_HASH) + && !memcmp (array->data, SCHEME_HASH, STRLEN (SCHEME_HASH))) { + if ( array->len > STRLEN (SCHEME_HASH) + 1 + && array->data[array->len - 1] == '\0' + && array->len == (STRLEN (SCHEME_HASH) + 64 + 1)) + return NM_SETTING_802_1X_CK_SCHEME_HASH; + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; } return NM_SETTING_802_1X_CK_SCHEME_BLOB; @@ -483,7 +491,8 @@ load_and_verify_certificate (const char *cert_path, * * 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) **/ @@ -547,6 +556,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) { @@ -600,7 +635,8 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, 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); } @@ -620,6 +656,17 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, 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 = load_and_verify_certificate (cert_path, scheme, &format, error); if (data) { /* wpa_supplicant can only use raw x509 CA certs */ diff --git a/libnm-util/nm-setting-8021x.h b/libnm-util/nm-setting-8021x.h index 62b4390..42c7035 100644 --- a/libnm-util/nm-setting-8021x.h +++ b/libnm-util/nm-setting-8021x.h @@ -54,6 +54,8 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_format >*/ * 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 @@ -62,7 +64,8 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_format >*/ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/ 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; @@ -184,6 +187,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 888ee58..5481963 100644 --- a/src/settings/plugins/ifnet/connection_parser.c +++ b/src/settings/plugins/ifnet/connection_parser.c @@ -37,6 +37,8 @@ #include "connection_parser.h" #include "nm-ifnet-connection.h" +#define SCHEME_HASH "hash://server/sha256/" + static char * connection_id_from_ifnet_name (const char *conn_name) { @@ -193,11 +195,18 @@ eap_tls_reader (const char *eap_method, 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; + 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 { nm_log_warn (LOGD_SETTINGS, " missing %s for EAP method '%s'; this is insecure!", @@ -302,11 +311,18 @@ eap_peap_reader (const char *eap_method, ca_cert = get_cert (ssid, "ca_cert", basepath); 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 { nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); @@ -404,11 +420,18 @@ eap_ttls_reader (const char *eap_method, /* ca cert */ ca_cert = get_cert (ssid, "ca_cert", basepath); 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 { nm_log_warn (LOGD_SETTINGS, " missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", eap_method); @@ -1719,12 +1742,14 @@ error: typedef NMSetting8021xCKScheme (*SchemeFunc) (NMSetting8021x * setting); typedef const char *(*PathFunc) (NMSetting8021x * setting); +typedef const char *(*HashFunc) (NMSetting8021x * setting); typedef GBytes *(*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; @@ -1734,6 +1759,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" @@ -1743,6 +1769,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" @@ -1752,6 +1779,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" @@ -1761,6 +1789,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" @@ -1770,6 +1799,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" @@ -1779,6 +1809,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" @@ -1788,6 +1819,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" @@ -1797,6 +1829,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" @@ -1812,6 +1845,7 @@ write_object (NMSetting8021x *s_8021x, NMSetting8021xCKScheme scheme; const char *path = NULL; GBytes *blob = NULL; + const char *hash = NULL; g_return_val_if_fail (conn_name != NULL, FALSE); g_return_val_if_fail (objtype != NULL, FALSE); @@ -1829,6 +1863,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; } @@ -1843,6 +1880,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) nm_log_warn (LOGD_SETTINGS, " Currently we do not support cert writing."); diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c index b6a8786..1d7f3ea 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -114,7 +114,7 @@ cert_writer (NMConnection *connection, const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting)); NMSetting8021xCKScheme scheme; NMSetting8021xCKFormat format; - const char *path = NULL, *ext = "pem"; + const char *path = NULL, *hash = NULL, *ext = "pem"; scheme = cert_data->scheme_func (cert_data->setting); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { @@ -159,6 +159,11 @@ cert_writer (NMConnection *connection, accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL); nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path); g_free (tmp); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_HASH) { + hash = cert_data->hash_func (cert_data->setting); + g_assert (hash); + + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, hash); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { GBytes *blob; const guint8 *blob_data; -- 2.1.4 From 23b16038d354685923630e862dd3e260dd514983 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Fri, 8 Feb 2013 11:23:15 +0800 Subject: [PATCH 2/4] wifi: add the dbus method to probe the certificate --- introspection/nm-device-wifi.xml | 23 +++++ src/devices/wifi/nm-device-wifi.c | 126 +++++++++++++++++++++++ src/devices/wifi/nm-device-wifi.h | 1 + src/supplicant-manager/nm-supplicant-config.c | 25 +++++ src/supplicant-manager/nm-supplicant-config.h | 2 + src/supplicant-manager/nm-supplicant-interface.c | 11 ++ src/supplicant-manager/nm-supplicant-interface.h | 5 + 7 files changed, 193 insertions(+) diff --git a/introspection/nm-device-wifi.xml b/introspection/nm-device-wifi.xml index d1c5313..01b2ac0 100644 --- a/introspection/nm-device-wifi.xml +++ b/introspection/nm-device-wifi.xml @@ -45,6 +45,18 @@ + + + + + The SSID of the AP to be probed + + + + Probe the certificate of the RADIUS server. + + + The active hardware address of the device. @@ -120,6 +132,17 @@ + + + + The certificate of the RADIUS server + + + + Emitted when wpa_supplicant replies the certificate of the RADIUS server. + + + Flags describing the capabilities of a wireless device. diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 2e116f8..45945ed 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -60,6 +60,9 @@ static gboolean impl_device_get_access_points (NMDeviceWifi *device, GPtrArray **aps, GError **err); +static gboolean impl_device_probe_cert (NMDeviceWifi *device, + GByteArray *ssid, + GError **err); static gboolean impl_device_get_all_access_points (NMDeviceWifi *device, GPtrArray **aps, @@ -103,6 +106,7 @@ enum { ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED, SCANNING_ALLOWED, + CERT_RECEIVED, LAST_SIGNAL }; @@ -128,6 +132,7 @@ struct _NMDeviceWifiPrivate { NMSupplicantManager *sup_mgr; NMSupplicantInterface *sup_iface; guint sup_timeout_id; /* supplicant association timeout */ + guint sup_cert_id; gboolean ssid_found; NM80211Mode mode; @@ -1658,6 +1663,118 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap) } } +static void +supplicant_iface_certification_cb (NMSupplicantInterface * iface, + GVariant *cert, + NMDeviceWifi * self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GVariantIter *iter; + GVariant *variant; + gchar *str, *subject = NULL, *hash = NULL; + guint depth; + gboolean got_depth = FALSE; + GHashTable *cert_table; + + g_variant_get (cert, "(a{sv})", &iter); + while (g_variant_iter_loop (iter, "{sv}", &str, &variant)) { + if (g_strcmp0 (str, "depth") == 0) { + depth = g_variant_get_uint32 (variant); + got_depth = TRUE; + } + + if (g_strcmp0 (str, "subject") == 0) + g_variant_get (variant, "s", &subject); + + if (g_strcmp0 (str, "cert_hash") == 0) + g_variant_get (variant, "s", &hash); + + } + g_variant_iter_free (iter); + + if (!got_depth) { + nm_log_dbg (LOGD_WIFI_SCAN, "Depth was not set"); + return; + } + + if (!subject) { + nm_log_dbg (LOGD_WIFI_SCAN, "no subject"); + return; + } + + if (!hash) { + nm_log_dbg (LOGD_WIFI_SCAN, "no cert hash"); + return; + } + + nm_log_info (LOGD_WIFI_SCAN, "Got Server Certificate %u, subject %s, hash %s", + depth, subject, hash); + + if (depth != 0) + return; + + cert_table = value_hash_create (); + if (!cert_table) { + nm_log_warn (LOGD_WIFI_SCAN, "Failed to new a cert table"); + return; + } + value_hash_add_uint (cert_table, "depth", depth); + value_hash_add_str (cert_table, "subject", subject); + value_hash_add_str (cert_table, "cert_hash", hash); + + g_signal_emit (self, signals[CERT_RECEIVED], 0, cert_table); + + g_hash_table_unref (cert_table); + + if (priv->sup_cert_id > 0) { + g_signal_handler_disconnect (priv->sup_iface, priv->sup_cert_id); + priv->sup_cert_id = 0; + } + + nm_supplicant_interface_disconnect (iface); +} + +static gboolean +impl_device_probe_cert (NMDeviceWifi *self, + GByteArray *ssid, + GError **err) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSupplicantConfig *config = NULL; + guint id; + gboolean ret = FALSE; + + config = nm_supplicant_config_new_probe (ssid); + if (!config) + goto error; + + /* Hook up signal handler to capture certification signal */ + id = g_signal_connect (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_CERTIFICATION, + G_CALLBACK (supplicant_iface_certification_cb), + self); + priv->sup_cert_id = id; + + if (!nm_supplicant_interface_set_config (priv->sup_iface, config)) + goto error; + + ret = TRUE; + +error: + if (!ret) { + g_set_error_literal (err, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + _("Couldn't probe RADIUS server certificate")); + if (priv->sup_cert_id) { + g_signal_handler_disconnect (priv->sup_iface, priv->sup_cert_id); + priv->sup_cert_id = 0; + } + } + + return ret; +} + /* * merge_scanned_ap * @@ -3359,6 +3476,15 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) scanning_allowed_accumulator, NULL, NULL, G_TYPE_BOOLEAN, 0); + signals[CERT_RECEIVED] = + g_signal_new ("cert-received", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, cert_received), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_wifi_object_info); diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index bcba91d..fcf91c9 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -70,6 +70,7 @@ struct _NMDeviceWifiClass void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); gboolean (*scanning_allowed) (NMDeviceWifi *device); + void (*cert_received) (NMDeviceWifi *device, GHashTable *cert); }; diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index b78a24d..8ab7b7d 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -160,6 +160,25 @@ nm_supplicant_config_add_option (NMSupplicantConfig *self, return nm_supplicant_config_add_option_with_type (self, key, value, len, TYPE_INVALID, secret); } +NMSupplicantConfig * +nm_supplicant_config_new_probe (const GByteArray *ssid) +{ + NMSupplicantConfig *probe_config; + + if (!ssid) + return NULL; + + probe_config = (NMSupplicantConfig *)g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL); + + nm_supplicant_config_add_option (probe_config, "ssid", (char *)ssid->data, ssid->len, FALSE); + nm_supplicant_config_add_option (probe_config, "key_mgmt", "WPA-EAP", -1, FALSE); + nm_supplicant_config_add_option (probe_config, "eap", "TTLS PEAP TLS", -1, FALSE); + nm_supplicant_config_add_option (probe_config, "identity", " ", -1, FALSE); + nm_supplicant_config_add_option (probe_config, "ca_cert", "probe://", -1, FALSE); + + return probe_config; +} + static gboolean nm_supplicant_config_add_blob (NMSupplicantConfig *self, const char *key, @@ -911,6 +930,12 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) return FALSE; break; + case NM_SETTING_802_1X_CK_SCHEME_HASH: + path = nm_setting_802_1x_get_ca_cert_hash (setting); + if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) + return FALSE; + break; + default: break; } diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h index 3324f63..3491fca 100644 --- a/src/supplicant-manager/nm-supplicant-config.h +++ b/src/supplicant-manager/nm-supplicant-config.h @@ -52,6 +52,8 @@ GType nm_supplicant_config_get_type (void); NMSupplicantConfig *nm_supplicant_config_new (void); +NMSupplicantConfig *nm_supplicant_config_new_probe (const GByteArray *ssid); + guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); void nm_supplicant_config_set_ap_scan (NMSupplicantConfig *self, diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index e9775a1..dab1047 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -55,6 +55,7 @@ enum { SCAN_DONE, /* wifi scan is complete */ CONNECTION_ERROR, /* an error occurred during a connection request */ CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + CERTIFICATION, /* a RADIUS server certificate was received */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -527,6 +528,8 @@ signal_cb (GDBusProxy *proxy, g_variant_get (args, "(&o&s&s)", &path, &field, &message); if (priv->has_credreq && priv->net_path && !g_strcmp0 (path, priv->net_path)) g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message); + } else if (MATCH_SIGNAL (signal, "Certification", args, G_VARIANT_TYPE ("(a{sv})"))) { + g_signal_emit (self, signals[CERTIFICATION], 0, g_variant_ref_sink (args)); } } @@ -1393,5 +1396,13 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_STRUCT_OFFSET (NMSupplicantInterfaceClass, credentials_request), NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + signals[CERTIFICATION] = + g_signal_new (NM_SUPPLICANT_INTERFACE_CERTIFICATION, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMSupplicantInterfaceClass, certification), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, G_TYPE_VARIANT); } diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h index 1b1139d..5328df4 100644 --- a/src/supplicant-manager/nm-supplicant-interface.h +++ b/src/supplicant-manager/nm-supplicant-interface.h @@ -62,6 +62,7 @@ enum { #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" +#define NM_SUPPLICANT_INTERFACE_CERTIFICATION "certification" typedef enum { AP_SUPPORT_UNKNOWN = 0, /* Can't detect whether supported or not */ @@ -114,6 +115,10 @@ typedef struct { void (*credentials_request) (NMSupplicantInterface *iface, const char *field, const char *message); + + /* a RADIUS server certificate was received */ + void (*certification) (NMSupplicantInterface * iface, + const GHashTable * ca_cert); } NMSupplicantInterfaceClass; -- 2.1.4 From 2efdcb519b6b5e490193afe980593cdd228f4b55 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Fri, 8 Feb 2013 11:24:10 +0800 Subject: [PATCH 3/4] libnm-glib: add the function to probe the certificate --- libnm-glib/libnm-glib.ver | 1 + libnm-glib/nm-device-wifi.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ libnm-glib/nm-device-wifi.h | 5 +++- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index 9219c4a..12ca784 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -200,6 +200,7 @@ global: nm_device_wifi_get_type; nm_device_wifi_new; nm_device_wifi_request_scan_simple; + nm_device_wifi_probe_cert; nm_device_wimax_error_get_type; nm_device_wimax_error_quark; nm_device_wimax_get_active_nsp; diff --git a/libnm-glib/nm-device-wifi.c b/libnm-glib/nm-device-wifi.c index 28609c7..977539e 100644 --- a/libnm-glib/nm-device-wifi.c +++ b/libnm-glib/nm-device-wifi.c @@ -80,6 +80,7 @@ enum { enum { ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED, + CERT_RECEIVED, LAST_SIGNAL }; @@ -381,6 +382,49 @@ nm_device_wifi_request_scan_simple (NMDeviceWifi *device, g_hash_table_unref (options); } +/** + * nm_device_wifi_probe_cert: + * @device: a #NMDeviceWifi + * @ssid: the ssid of the AP to probe + * + * Probe the certificate of the RADIUS server + * + * Returns: if the probe is sent or not + **/ +gboolean +nm_device_wifi_probe_cert (NMDeviceWifi *device, + const GByteArray *ssid) +{ + NMDeviceWifiPrivate *priv; + GError *error = NULL; + gboolean ret; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), FALSE); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + + ret = dbus_g_proxy_call (priv->proxy, "ProbeCert", &error, + DBUS_TYPE_G_UCHAR_ARRAY, ssid, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!ret) { + g_warning ("%s: error probe certificate: %s", __func__, error->message); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static void +cert_received_proxy (DBusGProxy *proxy, GHashTable *cert, gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + + g_signal_emit (self, signals[CERT_RECEIVED], 0, cert); +} + static void clean_up_aps (NMDeviceWifi *self, gboolean notify) { @@ -649,6 +693,14 @@ constructed (GObject *object) G_OBJECT_CLASS (nm_device_wifi_parent_class)->constructed (object); priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIRELESS); + + dbus_g_proxy_add_signal (priv->proxy, "CertReceived", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "CertReceived", + G_CALLBACK (cert_received_proxy), + object, NULL); + register_properties (NM_DEVICE_WIFI (object)); g_signal_connect (NM_DEVICE (object), @@ -836,4 +888,22 @@ nm_device_wifi_class_init (NMDeviceWifiClass *wifi_class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); + + /** + * NMDeviceWifi::cert-received: + * @device: the wifi device that received the signal + * @subject: the subject of the RADIUS server + * @hash: the hash of the RADIUS server + * + * Notifies that a certificate of a RADIUS server is received. + **/ + signals[CERT_RECEIVED] = + g_signal_new ("cert-received", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, cert_received), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + G_TYPE_HASH_TABLE); } diff --git a/libnm-glib/nm-device-wifi.h b/libnm-glib/nm-device-wifi.h index 2bb432a..ac9f8dd 100644 --- a/libnm-glib/nm-device-wifi.h +++ b/libnm-glib/nm-device-wifi.h @@ -77,6 +77,7 @@ typedef struct { /* Signals */ void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*cert_received) (NMDeviceWifi *device, GHashTable *cert); /* Padding for future expansion */ void (*_reserved1) (void); @@ -84,7 +85,6 @@ typedef struct { void (*_reserved3) (void); void (*_reserved4) (void); void (*_reserved5) (void); - void (*_reserved6) (void); } NMDeviceWifiClass; GType nm_device_wifi_get_type (void); @@ -110,6 +110,9 @@ void nm_device_wifi_request_scan_simple (NMDeviceWifi * NMDeviceWifiRequestScanFn callback, gpointer user_data); +gboolean nm_device_wifi_probe_cert (NMDeviceWifi *device, + const GByteArray *ssid); + G_END_DECLS #endif /* NM_DEVICE_WIFI_H */ -- 2.1.4 From 77adeaeafe978934280c50543ecf3ed8dce800d1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Thu, 25 Apr 2013 11:13:52 +0800 Subject: [PATCH 4/4] wifi: Probe the Radius server with the user credential Some servers do not accept anonymous probe. --- introspection/nm-device-wifi.xml | 4 +-- libnm-glib/nm-device-wifi.c | 14 +++++++--- libnm-glib/nm-device-wifi.h | 2 +- src/devices/wifi/nm-device-wifi.c | 39 ++++++++++++++++++++++++--- src/supplicant-manager/nm-supplicant-config.c | 37 +++++++++++++++++++++---- src/supplicant-manager/nm-supplicant-config.h | 3 ++- 6 files changed, 83 insertions(+), 16 deletions(-) diff --git a/introspection/nm-device-wifi.xml b/introspection/nm-device-wifi.xml index 01b2ac0..2adb7d4 100644 --- a/introspection/nm-device-wifi.xml +++ b/introspection/nm-device-wifi.xml @@ -47,9 +47,9 @@ - + - The SSID of the AP to be probed + Connection settings and properties diff --git a/libnm-glib/nm-device-wifi.c b/libnm-glib/nm-device-wifi.c index 977539e..ff816eb 100644 --- a/libnm-glib/nm-device-wifi.c +++ b/libnm-glib/nm-device-wifi.c @@ -385,7 +385,7 @@ nm_device_wifi_request_scan_simple (NMDeviceWifi *device, /** * nm_device_wifi_probe_cert: * @device: a #NMDeviceWifi - * @ssid: the ssid of the AP to probe + * @partial: the connection settings and properties * * Probe the certificate of the RADIUS server * @@ -393,21 +393,29 @@ nm_device_wifi_request_scan_simple (NMDeviceWifi *device, **/ gboolean nm_device_wifi_probe_cert (NMDeviceWifi *device, - const GByteArray *ssid) + NMConnection *partial) { NMDeviceWifiPrivate *priv; + GHashTable *hash = NULL; GError *error = NULL; gboolean ret; g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), FALSE); + g_return_val_if_fail (partial, FALSE); priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + hash = nm_connection_to_hash (partial, NM_SETTING_HASH_FLAG_ALL); + if (hash == NULL) + return FALSE; + ret = dbus_g_proxy_call (priv->proxy, "ProbeCert", &error, - DBUS_TYPE_G_UCHAR_ARRAY, ssid, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, G_TYPE_INVALID, G_TYPE_INVALID); + g_hash_table_unref (hash); + if (!ret) { g_warning ("%s: error probe certificate: %s", __func__, error->message); g_error_free (error); diff --git a/libnm-glib/nm-device-wifi.h b/libnm-glib/nm-device-wifi.h index ac9f8dd..c69e64c 100644 --- a/libnm-glib/nm-device-wifi.h +++ b/libnm-glib/nm-device-wifi.h @@ -111,7 +111,7 @@ void nm_device_wifi_request_scan_simple (NMDeviceWifi * gpointer user_data); gboolean nm_device_wifi_probe_cert (NMDeviceWifi *device, - const GByteArray *ssid); + NMConnection *partial); G_END_DECLS diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 45945ed..a767ffb 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -50,6 +50,7 @@ #include "nm-setting-ip6-config.h" #include "nm-platform.h" #include "nm-auth-utils.h" +#include "nm-core-internal.h" #include "nm-settings-connection.h" #include "nm-enum-types.h" #include "nm-dbus-glib-types.h" @@ -61,7 +62,7 @@ static gboolean impl_device_get_access_points (NMDeviceWifi *device, GPtrArray **aps, GError **err); static gboolean impl_device_probe_cert (NMDeviceWifi *device, - GByteArray *ssid, + GHashTable *settings, GError **err); static gboolean impl_device_get_all_access_points (NMDeviceWifi *device, @@ -1736,16 +1737,43 @@ supplicant_iface_certification_cb (NMSupplicantInterface * iface, static gboolean impl_device_probe_cert (NMDeviceWifi *self, - GByteArray *ssid, + GHashTable *settings, GError **err) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMConnection *connection = NULL; + NMSettingWireless *setting_wifi; + GBytes *ssid; + NMSetting8021x *setting_8021x; NMSupplicantConfig *config = NULL; guint id; gboolean ret = FALSE; - config = nm_supplicant_config_new_probe (ssid); - if (!config) + if (!settings) + goto error; + + connection = nm_simple_connection_new (); + if (settings && g_hash_table_size (settings)) { + GVariant *settings_dict = nm_utils_connection_hash_to_dict (settings); + + nm_connection_replace_settings (connection, settings_dict, NULL); + g_variant_unref (settings_dict); + } + + setting_wifi = nm_connection_get_setting_wireless (connection); + if (setting_wifi == NULL) + goto error; + + ssid = nm_setting_wireless_get_ssid (setting_wifi); + if (ssid == NULL) + goto error; + + setting_8021x = nm_connection_get_setting_802_1x (connection); + if (setting_8021x == NULL) + goto error; + + config = nm_supplicant_config_new_probe (ssid, setting_8021x); + if (config == NULL) goto error; /* Hook up signal handler to capture certification signal */ @@ -1761,6 +1789,9 @@ impl_device_probe_cert (NMDeviceWifi *self, ret = TRUE; error: + if (connection) + g_object_unref (connection); + if (!ret) { g_set_error_literal (err, NM_DEVICE_ERROR, diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index 8ab7b7d..9cffbe1 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -161,21 +161,48 @@ nm_supplicant_config_add_option (NMSupplicantConfig *self, } NMSupplicantConfig * -nm_supplicant_config_new_probe (const GByteArray *ssid) +nm_supplicant_config_new_probe (GBytes *ssid, NMSetting8021x *setting) { NMSupplicantConfig *probe_config; + const char *identity, *password; + gboolean use_pw = FALSE; + guint32 i, num_eap; + gconstpointer data; + gsize length; + + g_return_val_if_fail (ssid != NULL, NULL); + g_return_val_if_fail (setting != NULL, NULL); + + data = g_bytes_get_data (ssid, &length); - if (!ssid) - return NULL; + num_eap = nm_setting_802_1x_get_num_eap_methods (setting); + for (i = 0; i < num_eap; i++) { + const char *method = nm_setting_802_1x_get_eap_method (setting, i); + + if (method && (strcasecmp (method, "ttls") == 0 || + strcasecmp (method, "peap") == 0)) + use_pw = TRUE; + } + + identity = nm_setting_802_1x_get_identity (setting); + if (!identity) + identity = " "; probe_config = (NMSupplicantConfig *)g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL); - nm_supplicant_config_add_option (probe_config, "ssid", (char *)ssid->data, ssid->len, FALSE); + nm_supplicant_config_add_option (probe_config, "ssid", (const char *)data, length, FALSE); nm_supplicant_config_add_option (probe_config, "key_mgmt", "WPA-EAP", -1, FALSE); nm_supplicant_config_add_option (probe_config, "eap", "TTLS PEAP TLS", -1, FALSE); - nm_supplicant_config_add_option (probe_config, "identity", " ", -1, FALSE); + nm_supplicant_config_add_option (probe_config, "identity", identity, -1, FALSE); nm_supplicant_config_add_option (probe_config, "ca_cert", "probe://", -1, FALSE); + if (use_pw) { + password = nm_setting_802_1x_get_password(setting); + if (!password) + return NULL; + nm_supplicant_config_add_option (probe_config, "password", password, -1, TRUE); + } + return probe_config; } diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h index 3491fca..e6149d8 100644 --- a/src/supplicant-manager/nm-supplicant-config.h +++ b/src/supplicant-manager/nm-supplicant-config.h @@ -52,7 +52,8 @@ GType nm_supplicant_config_get_type (void); NMSupplicantConfig *nm_supplicant_config_new (void); -NMSupplicantConfig *nm_supplicant_config_new_probe (const GByteArray *ssid); +NMSupplicantConfig *nm_supplicant_config_new_probe (GBytes *ssid, + NMSetting8021x *setting); guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); -- 2.1.4