From e5ed391f288d7e2a653aa19d58ae0dedbc9a67f7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 4 Sep 2009 09:07:00 -0500 Subject: libnm-util: allow certificate/key paths Overload the certificate and key properties to allow paths to the certificates and keys using a special prefix for the property data. Add API to libnm-util for easy certificate path handling, and documentation for NMSetting8021x. --- libnm-util/crypto.c | 4 +- libnm-util/crypto.h | 2 +- libnm-util/libnm-util.ver | 40 +- libnm-util/nm-setting-8021x.c | 1621 +++++++++++++++++--- libnm-util/nm-setting-8021x.h | 155 +- libnm-util/tests/test-crypto.c | 2 +- src/supplicant-manager/nm-supplicant-config.c | 174 ++- system-settings/plugins/ifcfg-rh/common.h | 18 - system-settings/plugins/ifcfg-rh/reader.c | 145 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 204 ++- system-settings/plugins/ifcfg-rh/utils.c | 11 - system-settings/plugins/ifcfg-rh/utils.h | 2 - system-settings/plugins/ifcfg-rh/writer.c | 209 ++- 13 files changed, 2008 insertions(+), 579 deletions(-) diff --git a/libnm-util/crypto.c b/libnm-util/crypto.c index 9f4ccd48a5..cce7144c28 100644 --- a/libnm-util/crypto.c +++ b/libnm-util/crypto.c @@ -619,14 +619,14 @@ crypto_is_pkcs12_data (const GByteArray *data) } gboolean -crypto_is_pkcs12_file (const char *file) +crypto_is_pkcs12_file (const char *file, GError **error) { GByteArray *contents; gboolean success = FALSE; g_return_val_if_fail (file != NULL, FALSE); - contents = file_to_g_byte_array (file, TRUE, NULL); + contents = file_to_g_byte_array (file, TRUE, error); if (contents) { success = crypto_is_pkcs12_data (contents); g_byte_array_free (contents, TRUE); diff --git a/libnm-util/crypto.h b/libnm-util/crypto.h index bb66c7757e..f8c3ffd68c 100644 --- a/libnm-util/crypto.h +++ b/libnm-util/crypto.h @@ -83,7 +83,7 @@ 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_file (const char *file, GError **error); gboolean crypto_is_pkcs12_data (const GByteArray *data); diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 6f2976152d..1471e54be9 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -31,11 +31,15 @@ global: nm_setting_802_1x_error_get_type; nm_setting_802_1x_error_quark; nm_setting_802_1x_get_anonymous_identity; - nm_setting_802_1x_get_ca_cert; - nm_setting_802_1x_set_ca_cert_from_file; + nm_setting_802_1x_get_ca_cert_scheme; + nm_setting_802_1x_get_ca_cert_blob; + nm_setting_802_1x_get_ca_cert_path; + nm_setting_802_1x_set_ca_cert; nm_setting_802_1x_get_ca_path; - nm_setting_802_1x_get_client_cert; - nm_setting_802_1x_set_client_cert_from_file; + nm_setting_802_1x_get_client_cert_scheme; + nm_setting_802_1x_get_client_cert_blob; + nm_setting_802_1x_get_client_cert_path; + nm_setting_802_1x_set_client_cert; nm_setting_802_1x_get_eap_method; nm_setting_802_1x_get_identity; nm_setting_802_1x_get_num_eap_methods; @@ -45,20 +49,28 @@ global: nm_setting_802_1x_get_phase1_peapver; nm_setting_802_1x_get_phase2_auth; nm_setting_802_1x_get_phase2_autheap; - nm_setting_802_1x_get_phase2_ca_cert; - nm_setting_802_1x_set_phase2_ca_cert_from_file; + nm_setting_802_1x_get_phase2_ca_cert_scheme; + nm_setting_802_1x_get_phase2_ca_cert_blob; + nm_setting_802_1x_get_phase2_ca_cert_path; + nm_setting_802_1x_set_phase2_ca_cert; nm_setting_802_1x_get_phase2_ca_path; - nm_setting_802_1x_get_phase2_client_cert; - 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_client_cert_scheme; + nm_setting_802_1x_get_phase2_client_cert_blob; + nm_setting_802_1x_get_phase2_client_cert_path; + nm_setting_802_1x_set_phase2_client_cert; + nm_setting_802_1x_get_phase2_private_key_blob; + nm_setting_802_1x_get_phase2_private_key_format; nm_setting_802_1x_get_phase2_private_key_password; - nm_setting_802_1x_get_phase2_private_key_type; + nm_setting_802_1x_get_phase2_private_key_path; + nm_setting_802_1x_get_phase2_private_key_scheme; + nm_setting_802_1x_set_phase2_private_key; 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_blob; + nm_setting_802_1x_get_private_key_format; nm_setting_802_1x_get_private_key_password; - nm_setting_802_1x_get_private_key_type; + nm_setting_802_1x_get_private_key_path; + nm_setting_802_1x_get_private_key_scheme; + nm_setting_802_1x_set_private_key; nm_setting_802_1x_get_psk; nm_setting_802_1x_get_system_ca_certs; nm_setting_802_1x_get_type; diff --git a/libnm-util/nm-setting-8021x.c b/libnm-util/nm-setting-8021x.c index 68efff391f..15c0608040 100644 --- a/libnm-util/nm-setting-8021x.c +++ b/libnm-util/nm-setting-8021x.c @@ -33,6 +33,44 @@ #include "crypto.h" #include "nm-utils-private.h" +/** + * SECTION:nm-setting-802-1x + * @short_description: Describes 802.1x-authenticated connection properties + * @include: nm-setting-8021x.h + * + * The #NMSetting8021x object is a #NMSetting subclass that describes + * properties necessary for connection to 802.1x-authenticated networks, such as + * WPA and WPA2 Enterprise WiFi networks and wired 802.1x networks. 802.1x + * connections typically use certificates and/or EAP authentication methods to + * securely verify, identify, and authenticate the client to the network itself, + * instead of simply relying on a widely shared static key. + * + * It's a good idea to read up on wpa_supplicant configuration before using this + * setting extensively, since most of the options here correspond closely with + * the relevant wpa_supplicant configuration options. + * + * Furthermore, to get a good idea of 802.1x, EAP, TLS, TTLS, etc and their + * applications to WiFi and wired networks, you'll want to get copies of the + * following books. + * + * 802.11 Wireless Networks: The Definitive Guide, Second Edition + * Author: Matthew Gast + * ISBN: 978-0596100520 + * + * Cisco Wireless LAN Security + * Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky + * ISBN: 978-1587051548 + **/ + +#define SCHEME_PATH "file://" + +/** + * nm_setting_802_1x_error_quark: + * + * Registers an error quark for #NMSetting8021x if necessary. + * + * Returns: the error quark used for #NMSetting8021x errors. + **/ GQuark nm_setting_802_1x_error_quark (void) { @@ -124,12 +162,31 @@ enum { LAST_PROP }; +/** + * nm_setting_802_1x_new: + * + * Creates a new #NMSetting8021x object with default values. + * + * Returns: the new empty #NMSetting8021x object + **/ NMSetting * nm_setting_802_1x_new (void) { return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL); } +/** + * nm_setting_802_1x_get_num_eap_methods: + * @setting: the #NMSetting8021x + * + * Returns the number of eap methods allowed for use when connecting to the + * network. Generally only one EAP method is used. Use the functions + * nm_setting_802_1x_get_eap_method(), nm_setting_802_1x_add_eap_method(), + * and nm_setting_802_1x_remove_eap_method() for adding, removing, and retrieving + * allowed EAP methods. + * + * Returns: the number of allowed EAP methods + **/ guint32 nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting) { @@ -138,6 +195,15 @@ nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting) return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->eap); } +/** + * nm_setting_802_1x_get_eap_method: + * @setting: the #NMSetting8021x + * @i: the index of the EAP method name to return + * + * Returns the name of the allowed EAP method at index @i. + * + * Returns: the name of the allowed EAP method at index @i + **/ const char * nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i) { @@ -151,6 +217,18 @@ nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i) return (const char *) g_slist_nth_data (priv->eap, i); } +/** + * nm_setting_802_1x_add_eap_method: + * @setting: the #NMSetting8021x + * @eap: the name of the EAP method to allow for this connection + * + * Adds an allowed EAP method. The setting is not valid until at least one + * EAP method has been added. See #NMSetting8021x:eap property for a list of + * allowed EAP methods. + * + * Returns: TRUE if the EAP method was successfully added, FALSE if it was + * not a valid method or if it was already allowed. + **/ gboolean nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap) { @@ -170,6 +248,13 @@ nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap) return TRUE; } +/** + * nm_setting_802_1x_remove_eap_method: + * @setting: the #NMSetting8021x + * @i: the index of the EAP method to remove + * + * Removes the allowed EAP method at the specified index. + **/ void nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i) { @@ -186,6 +271,12 @@ nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i) priv->eap = g_slist_delete_link (priv->eap, elt); } +/** + * nm_setting_802_1x_clear_eap_methods: + * @setting: the #NMSetting8021x + * + * Clears all allowed EAP methods. + **/ void nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting) { @@ -198,6 +289,15 @@ nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting) priv->eap = NULL; } +/** + * nm_setting_802_1x_get_identity: + * @setting: the #NMSetting8021x + * + * Returns the identifier used by some EAP methods (like TLS) to + * authenticate the user. Often this is a username or login name. + * + * Returns: the user identifier + **/ const char * nm_setting_802_1x_get_identity (NMSetting8021x *setting) { @@ -206,6 +306,17 @@ nm_setting_802_1x_get_identity (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->identity; } +/** + * nm_setting_802_1x_get_anonymous_identity: + * @setting: the #NMSetting8021x + * + * Returns the anonymous identifier used by some EAP methods (like TTLS) to + * authenticate the user in the outer unencrypted "phase 1" authentication. The + * inner "phase 2" authentication will use the #NMSetting8021x:identity in + * a secure form, if applicable for that EAP method. + * + * Returns: the anonymous identifier + **/ const char * nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting) { @@ -214,121 +325,382 @@ nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->anonymous_identity; } +/** + * nm_setting_802_1x_get_ca_path: + * @setting: the #NMSetting8021x + * + * Returns the path of the CA certificate directory if previously set. Systems + * will often have a directory that contains multiple individual CA certificates + * which the supplicant can then add to the verification chain. This may be + * used in addition to the #NMSetting8021x:ca-cert property to add more CA + * certificates for verifying the network to client. + * + * Returns: the CA certificate directory path + **/ +const char * +nm_setting_802_1x_get_ca_path (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_path; +} + +/** + * nm_setting_802_1x_get_system_ca_certs: + * @setting: the #NMSetting8021x + * + * Sets the #NMSetting8021x:system-ca-certs property. The + * #NMSetting8021x:ca-path and #NMSetting8021x:phase2-ca-path + * properties are ignored if the #NMSetting8021x:system-ca-certs property is + * TRUE, in which case a system-wide CA certificate directory specified at + * compile time (using the --system-ca-path configure option) is used in place + * of these properties. + * + * Returns: TRUE if a system CA certificate path should be used, FALSE if not + **/ +gboolean +nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs; +} + +static NMSetting8021xCKScheme +get_cert_scheme (GByteArray *array) +{ + if (!array || !array->len) + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + + if ( (array->len > strlen (SCHEME_PATH)) + && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH))) + return NM_SETTING_802_1X_CK_SCHEME_PATH; + + return NM_SETTING_802_1X_CK_SCHEME_BLOB; +} + +/** + * nm_setting_802_1x_get_ca_cert_scheme: + * @setting: the #NMSetting8021x + * + * 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(). + * + * Returns: scheme used to store the CA certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert); +} + +/** + * nm_setting_802_1x_get_ca_cert_blob: + * @setting: the #NMSetting8021x + * + * Returns the CA certificate blob if the CA certificate is stored using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB 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: the CA certificate data + **/ const GByteArray * -nm_setting_802_1x_get_ca_cert (NMSetting8021x *setting) +nm_setting_802_1x_get_ca_cert_blob (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_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert; } +/** + * nm_setting_802_1x_get_ca_cert_path: + * @setting: the #NMSetting8021x + * + * Returns the CA certificate path if the CA certificate is stored using the + * %NM_SETTING_802_1X_CK_SCHEME_PATH 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: path to the CA certificate file + **/ const char * -nm_setting_802_1x_get_ca_path (NMSetting8021x *setting) +nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_path; + scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data + strlen (SCHEME_PATH)); } +/** + * nm_setting_802_1x_set_ca_cert: + * @setting: the #NMSetting8021x + * @filename: path of the CA certificate file (PEM or DER format). Filename + * must be UTF-8 encoded; use g_filename_to_utf8() to convert if needed. Pass + * NULL to clear the CA certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property + * with the raw certificate data if using the %NM_SETTING_802_1X_CK_SCHEME_BLOB + * scheme, or with the path to the certificate file if using the + * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ gboolean -nm_setting_802_1x_set_ca_cert_from_file (NMSetting8021x *self, - const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err) +nm_setting_802_1x_set_ca_cert (NMSetting8021x *self, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_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, &format, err); + /* Clear out any previous ca_cert blob */ if (priv->ca_cert) { + g_byte_array_free (priv->ca_cert, TRUE); + priv->ca_cert = NULL; + } + + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { /* 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; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; break; default: - g_byte_array_free (priv->ca_cert, TRUE); - priv->ca_cert = NULL; - g_set_error (err, + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, NM_SETTING_802_1X_ERROR, NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, NM_SETTING_802_1X_CA_CERT); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->ca_cert = data; + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->ca_cert = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->ca_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->ca_cert, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->ca_cert, (const guint8 *) "\0", 1); + } else + g_assert_not_reached (); + } } return priv->ca_cert != NULL; } -gboolean -nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting) +/** + * nm_setting_802_1x_get_client_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the client certificate. If the returned scheme + * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_client_cert_blob(); + * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the client certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs; + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert); } +/** + * nm_setting_802_1x_get_client_cert_blob: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the client certificate data + **/ const GByteArray * -nm_setting_802_1x_get_client_cert (NMSetting8021x *setting) +nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + scheme = nm_setting_802_1x_get_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert; } +/** + * nm_setting_802_1x_get_client_cert_path: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the client certificate file + **/ +const char * +nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_client_cert: + * @setting: the #NMSetting8021x + * @filename: path of the client certificate file (PEM, DER, or PKCS#12 format). + * Filename must be UTF-8 encoded; use g_filename_to_utf8() to convert if + * needed. Pass NULL to clear the client certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:client-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ gboolean -nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *self, - const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err) +nm_setting_802_1x_set_client_cert (NMSetting8021x *self, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_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, &format, err); + /* Clear out any previous ca_cert blob */ if (priv->client_cert) { + g_byte_array_free (priv->client_cert, TRUE); + priv->client_cert = NULL; + } + + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { + /* 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; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; break; case NM_CRYPTO_FILE_FORMAT_PKCS12: - if (out_ck_type) - *out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; break; default: - g_byte_array_free (priv->client_cert, TRUE); - priv->client_cert = NULL; - g_set_error (err, + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, NM_SETTING_802_1X_ERROR, NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, NM_SETTING_802_1X_CLIENT_CERT); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->client_cert = data; + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->client_cert = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->client_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->client_cert, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->client_cert, (const guint8 *) "\0", 1); + } else + g_assert_not_reached (); + } } return priv->client_cert != NULL; } +/** + * nm_setting_802_1x_get_phase1_peapver: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 1" PEAP version to be used when authenticating with + * EAP-PEAP as contained in the #NMSetting8021x:phase1-peapver property. Valid + * values are NULL (unset), "0" (PEAP version 0), and "1" (PEAP version 1). + **/ const char * nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting) { @@ -337,6 +709,16 @@ nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peapver; } +/** + * nm_setting_802_1x_get_phase1_peaplabel: + * @setting: the #NMSetting8021x + * + * Returns: whether the "phase 1" PEAP label is new-style or old-style, to be + * used when authenticating with EAP-PEAP, as contained in the + * #NMSetting8021x:phase1-peaplabel property. Valid values are NULL (unset), + * "0" (use old-style label), and "1" (use new-style label). See the + * wpa_supplicant documentation for more details. + **/ const char * nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting) { @@ -345,6 +727,14 @@ nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peaplabel; } +/** + * nm_setting_802_1x_get_phase1_fast_provisioning: + * @setting: the #NMSetting8021x + * + * Returns: whether "phase 1" PEAP fast provisioning should be used, as specified + * by the #NMSetting8021x:phase1-fast-provisioning property. See the + * wpa_supplicant documentation for more details. + **/ const char * nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting) { @@ -353,6 +743,13 @@ nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_fast_provisioning; } +/** + * nm_setting_802_1x_get_phase2_auth: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 2" non-EAP (ex MD5) allowed authentication method as + * specified by the #NMSetting8021x:phase2-auth property. + **/ const char * nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting) { @@ -361,6 +758,13 @@ nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_auth; } +/** + * nm_setting_802_1x_get_phase2_autheap: + * @setting: the #NMSetting8021x + * + * Returns: the "phase 2" EAP-based (ex TLS) allowed authentication method as + * specified by the #NMSetting8021x:phase2-autheap property. + **/ const char * nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting) { @@ -369,114 +773,350 @@ nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_autheap; } +/** + * nm_setting_802_1x_get_phase2_ca_path: + * @setting: the #NMSetting8021x + * + * Returns the path of the "phase 2" CA certificate directory if previously set. + * Systems will often have a directory that contains multiple individual CA + * certificates which the supplicant can then add to the verification chain. + * This may be used in addition to the #NMSetting8021x:phase2-ca-cert property + * to add more CA certificates for verifying the network to client. + * + * Returns: the "phase 2" CA certificate directory path + **/ +const char * +nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_path; +} + +/** + * nm_setting_802_1x_get_phase2_ca_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" 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(). + * + * Returns: scheme used to store the "phase 2" CA certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert); +} + +/** + * nm_setting_802_1x_get_phase2_ca_cert_blob: + * @setting: the #NMSetting8021x + * + * Returns the "phase 2" CA certificate blob if the CA certificate is stored + * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB 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: the "phase 2" CA certificate data + **/ const GByteArray * -nm_setting_802_1x_get_phase2_ca_cert (NMSetting8021x *setting) +nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert; } +/** + * nm_setting_802_1x_get_phase2_ca_cert_path: + * @setting: the #NMSetting8021x + * + * Returns the "phase 2" CA certificate path if the CA certificate is stored + * using the %NM_SETTING_802_1X_CK_SCHEME_PATH 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: path to the "phase 2" CA certificate file + **/ const char * -nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting) +nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_path; + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert->data + strlen (SCHEME_PATH)); } +/** + * nm_setting_802_1x_set_phase2_ca_cert: + * @setting: the #NMSetting8021x + * @filename: path of the "phase 2" CA certificate file (PEM or DER format). + * Filename must be UTF-8 encoded; use g_filename_to_utf8() to convert if + * needed. Pass NULL to clear the "phase 2" CA certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ gboolean -nm_setting_802_1x_set_phase2_ca_cert_from_file (NMSetting8021x *self, - const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err) +nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *self, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_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, &format, err); + /* Clear out any previous ca_cert blob */ if (priv->phase2_ca_cert) { - /* wpa_supplicant can only use X509 CA certs */ + g_byte_array_free (priv->phase2_ca_cert, TRUE); + priv->phase2_ca_cert = NULL; + } + + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { + /* 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; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; break; default: - g_byte_array_free (priv->phase2_ca_cert, TRUE); - priv->phase2_ca_cert = NULL; - g_set_error (err, + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, NM_SETTING_802_1X_ERROR, NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, NM_SETTING_802_1X_PHASE2_CA_CERT); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_ca_cert = data; + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->phase2_ca_cert = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) "\0", 1); + } else + g_assert_not_reached (); + } } return priv->phase2_ca_cert != NULL; } +/** + * nm_setting_802_1x_get_phase2_client_cert_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" client certificate. If the + * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the "phase 2" client certificate (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert); +} + +/** + * nm_setting_802_1x_get_phase2_client_cert_blob: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the "phase 2" client certificate data + **/ const GByteArray * -nm_setting_802_1x_get_phase2_client_cert (NMSetting8021x *setting) +nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert; } +/** + * nm_setting_802_1x_get_phase2_client_cert_path: + * @setting: the #NMSetting8021x + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the "phase 2" client certificate file + **/ +const char * +nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting) +{ + NMSetting8021xCKScheme scheme; + + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert->data + strlen (SCHEME_PATH)); +} + +/** + * nm_setting_802_1x_set_phase2_client_cert: + * @setting: the #NMSetting8021x + * @filename: path of the client certificate file (PEM, DER, or PKCS#12 format). + * Filename must be UTF-8 encoded; use g_filename_to_utf8() to convert if + * needed. Pass NULL to clear the client certificate. + * @scheme: desired storage scheme for the certificate + * @out_format: on successful return, the type of the certificate added + * @error: on unsuccessful return, an error + * + * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert + * property with the raw certificate data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Client certificates are used to identify the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ gboolean -nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *self, - const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err) +nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *self, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_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, &format, err); + /* Clear out any previous ca_cert blob */ if (priv->phase2_client_cert) { - /* Only X509 client certs should be used; not pkcs#12 */ + g_byte_array_free (priv->phase2_client_cert, TRUE); + priv->phase2_client_cert = NULL; + } + + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { + /* 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; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; break; case NM_CRYPTO_FILE_FORMAT_PKCS12: - if (out_ck_type) - *out_ck_type = NM_SETTING_802_1X_CK_TYPE_PKCS12; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; break; default: - g_byte_array_free (priv->phase2_client_cert, TRUE); - priv->phase2_client_cert = NULL; - g_set_error (err, + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, NM_SETTING_802_1X_ERROR, NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, - NM_SETTING_802_1X_CLIENT_CERT); + NM_SETTING_802_1X_PHASE2_CLIENT_CERT); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_client_cert = data; + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->phase2_client_cert = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->phase2_client_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->phase2_client_cert, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->phase2_client_cert, (const guint8 *) "\0", 1); + } else + g_assert_not_reached (); + } } return priv->phase2_client_cert != NULL; } +/** + * nm_setting_802_1x_get_password: + * @setting: the #NMSetting8021x + * + * Returns: the password used by the authentication method, if any, as specified + * by the #NMSetting8021x:password property + **/ const char * nm_setting_802_1x_get_password (NMSetting8021x *setting) { @@ -485,6 +1125,13 @@ nm_setting_802_1x_get_password (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->password; } +/** + * nm_setting_802_1x_get_pin: + * @setting: the #NMSetting8021x + * + * Returns: the PIN used by the authentication method, if any, as specified + * by the #NMSetting8021x:pin property + **/ const char * nm_setting_802_1x_get_pin (NMSetting8021x *setting) { @@ -493,6 +1140,13 @@ nm_setting_802_1x_get_pin (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin; } +/** + * nm_setting_802_1x_get_psk: + * @setting: the #NMSetting8021x + * + * Returns: the Pre-Shared-Key used by the authentication method, if any, as + * specified by the #NMSetting8021x:psk property + **/ const char * nm_setting_802_1x_get_psk (NMSetting8021x *setting) { @@ -501,192 +1155,518 @@ nm_setting_802_1x_get_psk (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->psk; } +/** + * nm_setting_802_1x_get_private_key_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the private key. If the returned scheme is + * %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the private key (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key); +} + +/** + * nm_setting_802_1x_get_private_key_blob: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the private key data + **/ const GByteArray * -nm_setting_802_1x_get_private_key (NMSetting8021x *setting) +nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key; } +/** + * nm_setting_802_1x_get_private_key_path: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the private key file + **/ const char * -nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting) +nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password; + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key->data + strlen (SCHEME_PATH)); } +/** + * nm_setting_802_1x_set_private_key: + * @setting: the #NMSetting8021x + * @filename: path of the private key file (PEM, DER, or PKCS#12 format). + * Filename must be UTF-8 encoded; use g_filename_to_utf8() to convert if + * needed. Pass NULL to clear the private key. + * @scheme: desired storage scheme for the private key + * @out_format: on successful return, the type of the private key added + * @error: on unsuccessful return, an error + * + * Reads a private key from disk and sets the #NMSetting8021x:private-key + * property with the raw private key data if using the + * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the private key + * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ gboolean -nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *self, - const char *filename, - const char *password, - NMSetting8021xCKType *out_ck_type, - GError **err) +nm_setting_802_1x_set_private_key (NMSetting8021x *self, + const char *filename, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; - NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + /* Clear out any previous private key blob */ if (priv->private_key) { - /* Try not to leave the decrypted private key around in memory */ + /* Try not to leave the private key around in memory */ memset (priv->private_key, 0, priv->private_key->len); g_byte_array_free (priv->private_key, TRUE); + priv->private_key = NULL; } 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) { + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { + /* wpa_supplicant can only use raw x509 CA certs */ switch (format) { case NM_CRYPTO_FILE_FORMAT_RAW_KEY: - if (out_ck_type) - *out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + break; + case NM_CRYPTO_FILE_FORMAT_X509: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; 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; - - /* 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); + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; break; default: - g_assert_not_reached (); + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + NM_SETTING_802_1X_PRIVATE_KEY); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->private_key = data; + + /* Always update the private key for blob + pkcs12 since the + * pkcs12 files are encrypted + */ + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) + priv->private_key_password = g_strdup (password); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->private_key = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->private_key, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->private_key, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->private_key, (const guint8 *) "\0", 1); + + /* Always update the private key with paths since the key the + * cert refers to is encrypted. + */ + priv->private_key_password = g_strdup (password); + } else + g_assert_not_reached (); + + /* As required by NM and wpa_supplicant, set the client-cert + * property to the same PKCS#12 data. + */ + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { + 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; + if (out_format && crypto_is_pkcs12_file (filename, NULL)) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; } return priv->private_key != NULL; } -NMSetting8021xCKType -nm_setting_802_1x_get_private_key_type (NMSetting8021x *setting) +/** + * nm_setting_802_1x_get_private_key_password: + * @setting: the #NMSetting8021x + * + * Returns: the private key password used to decrypt the private key if + * previously set with nm_setting_802_1x_set_private_key_from_file(), + * nm_setting_802_1x_set_private_key_path(), or the + * #NMSetting8021x:private-key-password property. + **/ +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; +} + +/** + * nm_setting_802_1x_get_private_key_format: + * @setting: the #NMSetting8021x + * + * Returns: the data format of the private key data stored in the + * #NMSetting8021x:private-key property + **/ +NMSetting8021xCKFormat +nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting) { NMSetting8021xPrivate *priv; + const char *path; + GError *error = NULL; - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN); + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); priv = NM_SETTING_802_1X_GET_PRIVATE (setting); if (!priv->private_key) - return NM_SETTING_802_1X_CK_TYPE_UNKNOWN; + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + switch (nm_setting_802_1x_get_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + if (crypto_is_pkcs12_data (priv->private_key)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + return NM_SETTING_802_1X_CK_FORMAT_X509; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_private_key_path (setting); + if (crypto_is_pkcs12_file (path, &error)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + if (error) { + /* Couldn't read the file or something */ + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + } + return NM_SETTING_802_1X_CK_FORMAT_X509; + default: + break; + } + + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; +} + +/** + * nm_setting_802_1x_get_phase2_private_key_password: + * @setting: the #NMSetting8021x + * + * Returns: the private key password used to decrypt the private key if + * previously set with nm_setting_802_1x_set_phase2_private_key_from_file(), + * nm_setting_802_1x_set_phase2_private_key_path(), or the + * #NMSetting8021x:phase2-private-key-password property. + **/ +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; +} - if (crypto_is_pkcs12_data (priv->private_key)) - return NM_SETTING_802_1X_CK_TYPE_PKCS12; +/** + * nm_setting_802_1x_get_phase2_private_key_scheme: + * @setting: the #NMSetting8021x + * + * Returns the scheme used to store the "phase 2" private key. If the returned + * scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use + * nm_setting_802_1x_get_client_cert_blob(); if + * %NM_SETTING_802_1X_CK_SCHEME_PATH, use + * nm_setting_802_1x_get_client_cert_path(). + * + * Returns: scheme used to store the "phase 2" private key (blob or path) + **/ +NMSetting8021xCKScheme +nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - return NM_SETTING_802_1X_CK_TYPE_X509; + return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key); } +/** + * nm_setting_802_1x_get_phase2_private_key_blob: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: the "phase 2" private key data + **/ const GByteArray * -nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting) +nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); + return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key; } +/** + * nm_setting_802_1x_get_phase2_private_key_path: + * @setting: the #NMSetting8021x + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: path to the "phase 2" private key file + **/ const char * -nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting) +nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting) { + NMSetting8021xCKScheme scheme; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password; + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); + g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); + + return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key->data + strlen (SCHEME_PATH)); } +/** + * nm_setting_802_1x_set_phase2_private_key: + * @setting: the #NMSetting8021x + * @filename: path of the private key file (PEM, DER, or PKCS#12 format). + * Filename must be UTF-8 encoded; use g_filename_to_utf8() to convert if + * needed. Pass NULL to clear the private key. + * @scheme: desired storage scheme for the private key + * @out_format: on successful return, the type of the private key added + * @error: on unsuccessful return, an error + * + * Reads a "phase 2" private key from disk and sets the + * #NMSetting8021x:phase2-private-key property with the raw private key data if + * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the + * private key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. + * + * Private keys are used to authenticate the connecting client to the network + * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x + * authentication method. + * + * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful + **/ 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) +nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *self, + const char *filename, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { NMSetting8021xPrivate *priv; - NMCryptoKeyType ignore = NM_CRYPTO_KEY_TYPE_UNKNOWN; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + GByteArray *data; 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); + + if (filename) { + g_return_val_if_fail (g_utf8_validate (filename, -1, NULL), FALSE); + g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB + || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + FALSE); + } + + if (out_format) + g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + /* Clear out any previous private key blob */ if (priv->phase2_private_key) { - /* Try not to leave the decrypted private key around in memory */ + /* Try not to leave the private key around in memory */ memset (priv->phase2_private_key, 0, priv->phase2_private_key->len); g_byte_array_free (priv->phase2_private_key, TRUE); + priv->phase2_private_key = NULL; } 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) { + if (!filename) + return TRUE; + + data = crypto_load_and_verify_certificate (filename, &format, error); + if (data) { + /* wpa_supplicant can only use raw x509 CA certs */ switch (format) { case NM_CRYPTO_FILE_FORMAT_RAW_KEY: - if (out_ck_type) - *out_ck_type = NM_SETTING_802_1X_CK_TYPE_RAW_KEY; + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + break; + case NM_CRYPTO_FILE_FORMAT_X509: + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; 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; - - /* 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); + if (out_format) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; break; default: - g_assert_not_reached (); + g_byte_array_free (data, TRUE); + data = NULL; + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); break; - } + } + + if (data) { + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_private_key = data; + + /* Always update the private key for blob + pkcs12 since the + * pkcs12 files are encrypted + */ + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) + priv->phase2_private_key_password = g_strdup (password); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + /* Add the path scheme tag to the front, then the fielname */ + priv->phase2_private_key = g_byte_array_sized_new (strlen (filename) + strlen (SCHEME_PATH) + 1); + g_byte_array_append (priv->phase2_private_key, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); + g_byte_array_append (priv->phase2_private_key, (const guint8 *) filename, strlen (filename)); + g_byte_array_append (priv->phase2_private_key, (const guint8 *) "\0", 1); + + /* Always update the private key with paths since the key the + * cert refers to is encrypted. + */ + priv->phase2_private_key_password = g_strdup (password); + } else + g_assert_not_reached (); + + /* As required by NM and wpa_supplicant, set the client-cert + * property to the same PKCS#12 data. + */ + if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { + 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; + if (out_format && crypto_is_pkcs12_file (filename, NULL)) + *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; } return priv->phase2_private_key != NULL; } -NMSetting8021xCKType -nm_setting_802_1x_get_phase2_private_key_type (NMSetting8021x *setting) +/** + * nm_setting_802_1x_get_phase2_private_key_format: + * @setting: the #NMSetting8021x + * + * Returns: the data format of the "phase 2" private key data stored in the + * #NMSetting8021x:phase2-private-key property + **/ +NMSetting8021xCKFormat +nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting) { NMSetting8021xPrivate *priv; + const char *path; + GError *error = NULL; - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_TYPE_UNKNOWN); + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_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_FORMAT_UNKNOWN; + + switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + if (crypto_is_pkcs12_data (priv->phase2_private_key)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + return NM_SETTING_802_1X_CK_FORMAT_X509; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_private_key_path (setting); + if (crypto_is_pkcs12_file (path, &error)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + if (error) { + /* Couldn't read the file or something */ + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + } + return NM_SETTING_802_1X_CK_FORMAT_X509; + default: + break; + } - return NM_SETTING_802_1X_CK_TYPE_X509; + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; } static void @@ -1026,6 +2006,36 @@ need_secrets (NMSetting *setting) return secrets; } +static gboolean +verify_cert (GByteArray *array, const char *prop_name, GError **error) +{ + if (!array) + return TRUE; + + switch (get_cert_scheme (array)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + return TRUE; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + /* For path-based schemes, verify that the path is zero-terminated */ + if (array->data[array->len - 1] == '\0') { + /* And ensure it's UTF-8 valid too so we can pass it through + * D-Bus and stuff like that. + */ + if (g_utf8_validate ((const char *) (array->data + strlen (SCHEME_PATH)), -1, NULL)) + return TRUE; + } + break; + default: + break; + } + + g_set_error (error, + NM_SETTING_802_1X_ERROR, + NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, + prop_name); + return FALSE; +} + static gboolean verify (NMSetting *setting, GSList *all_settings, GError **error) { @@ -1034,6 +2044,7 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", NULL }; const char *valid_phase1_peapver[] = { "0", "1", NULL }; const char *valid_phase1_peaplabel[] = { "0", "1", NULL }; + const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL }; const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL }; const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL }; GSList *iter; @@ -1089,7 +2100,7 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return FALSE; } - if (priv->phase1_fast_provisioning && strcmp (priv->phase1_fast_provisioning, "1")) { + if (priv->phase1_fast_provisioning && !_nm_utils_string_in_list (priv->phase1_fast_provisioning, valid_phase1_fast_pac)) { g_set_error (error, NM_SETTING_802_1X_ERROR, NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, @@ -1113,6 +2124,21 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return FALSE; } + if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT, error)) + return FALSE; + if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT, error)) + return FALSE; + + if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT, error)) + return FALSE; + if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, error)) + return FALSE; + + if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, error)) + return FALSE; + if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, error)) + return FALSE; + /* FIXME: finish */ return TRUE; @@ -1163,12 +2189,31 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); } +static GByteArray * +set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error) +{ + gboolean valid; + GByteArray *data = NULL; + + data = g_value_dup_boxed (value); + /* Verify the new data */ + if (data) { + valid = verify_cert (data, prop_name, error); + if (!valid) { + g_byte_array_free (data, TRUE); + data = NULL; + } + } + return data; +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMSetting8021x *setting = NM_SETTING_802_1X (object); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + GError *error = NULL; switch (prop_id) { case PROP_EAP: @@ -1184,18 +2229,32 @@ set_property (GObject *object, guint prop_id, priv->anonymous_identity = g_value_dup_string (value); break; case PROP_CA_CERT: - if (priv->ca_cert) + if (priv->ca_cert) { g_byte_array_free (priv->ca_cert, TRUE); - priv->ca_cert = g_value_dup_boxed (value); + priv->ca_cert = NULL; + } + priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } break; case PROP_CA_PATH: g_free (priv->ca_path); priv->ca_path = g_value_dup_string (value); break; case PROP_CLIENT_CERT: - if (priv->client_cert) + if (priv->client_cert) { g_byte_array_free (priv->client_cert, TRUE); - priv->client_cert = g_value_dup_boxed (value); + priv->client_cert = NULL; + } + priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } break; case PROP_PHASE1_PEAPVER: g_free (priv->phase1_peapver); @@ -1218,36 +2277,64 @@ set_property (GObject *object, guint prop_id, priv->phase2_autheap = g_value_dup_string (value); break; case PROP_PHASE2_CA_CERT: - if (priv->phase2_ca_cert) + if (priv->phase2_ca_cert) { g_byte_array_free (priv->phase2_ca_cert, TRUE); - priv->phase2_ca_cert = g_value_dup_boxed (value); + priv->phase2_ca_cert = NULL; + } + priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } break; case PROP_PHASE2_CA_PATH: g_free (priv->phase2_ca_path); priv->phase2_ca_path = g_value_dup_string (value); break; case PROP_PHASE2_CLIENT_CERT: - if (priv->phase2_client_cert) + if (priv->phase2_client_cert) { g_byte_array_free (priv->phase2_client_cert, TRUE); - priv->phase2_client_cert = g_value_dup_boxed (value); + priv->phase2_client_cert = NULL; + } + priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error); + if (error) { + g_warning ("Error setting certificate (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } break; case PROP_PASSWORD: g_free (priv->password); priv->password = g_value_dup_string (value); break; case PROP_PRIVATE_KEY: - if (priv->private_key) + if (priv->private_key) { g_byte_array_free (priv->private_key, TRUE); - priv->private_key = g_value_dup_boxed (value); + priv->private_key = NULL; + } + priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error); + if (error) { + g_warning ("Error setting private key (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } 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) + if (priv->phase2_private_key) { g_byte_array_free (priv->phase2_private_key, TRUE); - priv->phase2_private_key = g_value_dup_boxed (value); + priv->phase2_private_key = NULL; + } + priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error); + if (error) { + g_warning ("Error setting private key (invalid data): (%d) %s", + error->code, error->message); + g_error_free (error); + } break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD: g_free (priv->phase2_private_key_password); @@ -1354,6 +2441,16 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) parent_class->need_secrets = need_secrets; /* Properties */ + + /** + * NMSetting8021x:eap: + * + * The allowed EAP method to be used when authenticating to the network with + * 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", and + * "fast". Each method requires different configuration using the + * properties of this object; refer to wpa_supplicant documentation for the + * allowed combinations. + **/ g_object_class_install_property (object_class, PROP_EAP, _nm_param_spec_specialized (NM_SETTING_802_1X_EAP, @@ -1362,6 +2459,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_LIST_OF_STRING, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:identity: + * + * Identity string for EAP authentication methods. Often the user's + * user or login name. + **/ g_object_class_install_property (object_class, PROP_IDENTITY, g_param_spec_string (NM_SETTING_802_1X_IDENTITY, @@ -1370,6 +2473,13 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:anonymous-identity: + * + * Anonymous identity string for EAP authentication methods. Used as the + * unencrypted identity with EAP types that support different tunelled + * identity like EAP-TTLS. + **/ g_object_class_install_property (object_class, PROP_ANONYMOUS_IDENTITY, g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, @@ -1378,6 +2488,16 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:ca-cert: + * + * Raw X.509 CA certificate data if used by the EAP method specified in the + * #NMSetting8021x:eap property. Can be unset even if the EAP method + * supports CA certificates, but this allows man-in-the-middle attacks and + * is NOT recommended. This property is cleared when the + * #NMSetting8021x:ca-cert-path property is set since the two are mutually + * exclusive. + **/ g_object_class_install_property (object_class, PROP_CA_CERT, _nm_param_spec_specialized (NM_SETTING_802_1X_CA_CERT, @@ -1386,6 +2506,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:ca-path: + * + * Path to a directory containing PEM or DER formatted certificates to be + * added to the verification chain in addition to the certificate specified + * in either of the #NMSetting8021x:ca-cert or #NMSetting8021x:ca-cert-path + * properties. + **/ g_object_class_install_property (object_class, PROP_CA_PATH, g_param_spec_string (NM_SETTING_802_1X_CA_PATH, @@ -1394,6 +2522,15 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:client-cert: + * + * Raw X.509 client certificate data if used by the EAP method specified in + * the #NMSetting8021x:eap property. Currently only EAP-TLS supports client + * certificates. This property is cleared when the + * #NMSetting8021x:client-cert-path property is set since the two are mutually + * exclusive. + **/ g_object_class_install_property (object_class, PROP_CLIENT_CERT, _nm_param_spec_specialized (NM_SETTING_802_1X_CLIENT_CERT, @@ -1402,6 +2539,16 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase1-peapver: + * + * Forces which PEAP version is used when PEAP is set as the EAP method in + * the #NMSetting8021x:eap property. When unset, the version reported by + * the server will be used. Sometimes when using older RADIUS servers, it + * is necessary to force the client to use a particular PEAP version. To do + * so, this property may be set to "0" or "1" to force that specific PEAP + * version. + **/ g_object_class_install_property (object_class, PROP_PHASE1_PEAPVER, g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, @@ -1410,6 +2557,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase1-peaplabel: + * + * Forces use of the new PEAP label during key derivation. Some RADIUS + * servers may require forcing the new PEAP label to interoperate with + * PEAPv1. Set to "1" to force use of the new PEAP label. See the + * wpa_supplicant documentation for more details. + **/ g_object_class_install_property (object_class, PROP_PHASE1_PEAPLABEL, g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, @@ -1418,6 +2573,15 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase1-fast-provisioning: + * + * Enables or disables in-line provisioning of EAP-FAST credentials when + * FAST is specified as the EAP method in the #NMSetting8021x:eap property. + * Recognized values are "0" (disabled), "1" (allow unauthenticated + * provisioning), "2" (allow authenticated provisioning), and "3" (allow + * both authenticated and unauthenticated provisioning). + **/ g_object_class_install_property (object_class, PROP_PHASE1_FAST_PROVISIONING, g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, @@ -1426,6 +2590,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase2-auth: + * + * Specifies the allowed "phase 2" inner non-EAP authentication methods when + * an EAP method that uses an inner TLS tunnel is specified in the + * #NMSetting8021x:eap property. Recognized non-EAP phase2 methods are + * "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", and "tls". + **/ g_object_class_install_property (object_class, PROP_PHASE2_AUTH, g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, @@ -1434,6 +2606,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase2-autheap: + * + * Specifies the allowed "phase 2" inner EAP-based authentication methods + * when an EAP method that uses an inner TLS tunnel is specified in the + * #NMSetting8021x:eap property. Recognized EAP-based phase2 methods are + * "md5", "mschapv2", "otp", "gtc", and "tls". + **/ g_object_class_install_property (object_class, PROP_PHASE2_AUTHEAP, g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, @@ -1442,6 +2622,17 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase2-ca-cert: + * + * Raw X.509 CA certificate data if used by the authentication method + * specified in the #NMSetting8021x:phase2-auth or + * #NMSetting8021x:phase2-autheap properties. Can be unset even if the + * authentication method supports CA certificates, but this allows + * man-in-the-middle attacks and is NOT recommended. This property is + * cleared when the #NMSetting8021x:phase2-ca-cert-path property is set + * since the two are mutually exclusive. + **/ g_object_class_install_property (object_class, PROP_PHASE2_CA_CERT, _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CA_CERT, @@ -1450,6 +2641,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase2-ca-path: + * + * Path to a directory containing PEM or DER formatted certificates to be + * added to the verification chain in addition to the certificate specified + * in either of the #NMSetting8021x:phase2-ca-cert or + * #NMSetting8021x:phase2-ca-cert-path properties. + **/ g_object_class_install_property (object_class, PROP_PHASE2_CA_PATH, g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, @@ -1458,6 +2657,16 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:phase2-client-cert: + * + * Raw X.509 client certificate data if used by the authentication method + * specified in the #NMSetting8021x:phase2-auth or + * #NMSetting8021x:phase2-autheap properties. Currently only TLS supports + * client certificates. This property is cleared when the + * #NMSetting8021x:phase2-client-cert-path property is set since the two are + * mutually exclusive. + **/ g_object_class_install_property (object_class, PROP_PHASE2_CLIENT_CERT, _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, @@ -1466,6 +2675,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + /** + * NMSetting8021x:password: + * + * Password used for EAP authentication methods. + **/ g_object_class_install_property (object_class, PROP_PASSWORD, g_param_spec_string (NM_SETTING_802_1X_PASSWORD, @@ -1474,6 +2688,21 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET)); + /** + * NMSetting8021x:private-key: + * + * When X.509 private keys are used by the EAP method specified in the + * #NMSetting8021x:eap property, contains the raw decrypted X.509 key + * data. When PKCS#12 format private keys are used, contains the PCKS#12 + * data (which is encrypted) and the #NMSetting8021x:private-key-password + * property must also be set to the password used to decrypt the PKCS#12 + * certificate and key. Currently only TLS supports private keys. When + * PKCS#12 format private keys are used, the #NMSetting8021x:client-cert + * must also be set to the same value. + * + * This property is cleared when the #NMSetting8021x:private-key-path + * property is set since the two are mutually exclusive. + **/ g_object_class_install_property (object_class, PROP_PRIVATE_KEY, _nm_param_spec_specialized (NM_SETTING_802_1X_PRIVATE_KEY, @@ -1482,6 +2711,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET)); + /** + * NMSetting8021x:private-key-password: + * + * The password used to decrypt the private key specified by + * #NMSetting8021x:private-key or #NMSetting8021x:private-key-path + **/ g_object_class_install_property (object_class, PROP_PRIVATE_KEY_PASSWORD, g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, @@ -1490,6 +2725,24 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET)); + /** + * NMSetting8021x:phase2-private-key: + * + * Private key data used by "phase 2" inner authentication methods. + * + * When X.509 private keys are used by the authentication method specified + * by the #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap + * properties, contains the raw decrypted X.509 key data. When PKCS#12 + * format private keys are used, contains the PCKS#12 data (which is + * encrypted) and the #NMSetting8021x:private-key-password property must + * also be set to the password used to decrypt the PKCS#12 certificate and + * key. Currently only TLS supports private keys. When PKCS#12 format + * private keys are used, the #NMSetting8021x:phase2-client-cert must also + * be set to the same value. + * + * This property is cleared when the #NMSetting8021x:phase2-private-key-path + * property is set since the two are mutually exclusive. + **/ g_object_class_install_property (object_class, PROP_PHASE2_PRIVATE_KEY, _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, @@ -1498,6 +2751,13 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) DBUS_TYPE_G_UCHAR_ARRAY, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET)); + /** + * NMSetting8021x:phase2-private-key-password: + * + * The password used to decrypt the private key specified by + * #NMSetting8021x:phase2-private-key or + * #NMSetting8021x:phase2-private-key-path + **/ g_object_class_install_property (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD, g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, @@ -1506,6 +2766,17 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET)); + /** + * NMSetting8021x:system-ca-certs: + * + * When TRUE, overrides #NMSetting8021x:ca-path and + * #NMSetting8021x:phase2-ca-path properties using the system CA directory + * specified and configure time with the --system-ca-path switch. The + * certificates in this directory are added to the verification chain in + * addition to any certificates specified by the #NMSetting8021x:ca-cert, + * #NMSetting8021x:ca-cert-path, #NMSetting8021x:phase2-ca-cert and + * #NMSetting8021x:phase2-ca-cert-path properties. + **/ g_object_class_install_property (object_class, PROP_SYSTEM_CA_CERTS, g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, diff --git a/libnm-util/nm-setting-8021x.h b/libnm-util/nm-setting-8021x.h index e956e685ae..a8ad941b39 100644 --- a/libnm-util/nm-setting-8021x.h +++ b/libnm-util/nm-setting-8021x.h @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * (C) Copyright 2007 - 2008 Red Hat, Inc. + * (C) Copyright 2007 - 2009 Red Hat, Inc. * (C) Copyright 2007 - 2008 Novell, Inc. */ @@ -31,11 +31,17 @@ 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; + NM_SETTING_802_1X_CK_FORMAT_UNKNOWN = 0, + NM_SETTING_802_1X_CK_FORMAT_X509, + NM_SETTING_802_1X_CK_FORMAT_RAW_KEY, + NM_SETTING_802_1X_CK_FORMAT_PKCS12 +} NMSetting8021xCKFormat; + +typedef enum { + NM_SETTING_802_1X_CK_SCHEME_UNKNOWN = 0, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NM_SETTING_802_1X_CK_SCHEME_PATH +} NMSetting8021xCKScheme; #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)) @@ -83,6 +89,30 @@ GQuark nm_setting_802_1x_error_quark (void); #define NM_SETTING_802_1X_PSK "psk" #define NM_SETTING_802_1X_SYSTEM_CA_CERTS "system-ca-certs" +/* PRIVATE KEY NOTE: when setting PKCS#12 private keys directly via properties + * using the "blob" scheme, the data must be passed in PKCS#12 format. 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. This is because the + * PKCS#12 file contains both the private key and client certificate, so both + * properties need to be set to the same thing. When using the "path" scheme, + * just set both the private-key and client-cert properties to the same path, + * and set the private-key password correctly. + * + * When setting OpenSSL-derived "traditional" format (ie S/MIME style, not + * PKCS#8) RSA and DSA keys directly via properties with the "blob" scheme, 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 (because the data is + * already decrypted by the client), and the appropriate "client-cert" (or + * "phase2-client-cert") property of the NMSetting8021x object must be a valid + * client certificate. When using the "path" scheme, just set the private-key + * and client-cert properties to the paths to their respective objects, and + * set the private-key password correctly. + */ + typedef struct { NMSetting parent; } NMSetting8021x; @@ -105,18 +135,27 @@ const char * nm_setting_802_1x_get_identity (NMSetting8 const char * nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting); -const GByteArray *nm_setting_802_1x_get_ca_cert (NMSetting8021x *setting); +gboolean nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting); 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 char * nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting); -const GByteArray *nm_setting_802_1x_get_client_cert (NMSetting8021x *setting); -gboolean nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *setting, +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); +gboolean nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +NMSetting8021xCKScheme nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err); + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); const char * nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting); @@ -128,19 +167,23 @@ const char * nm_setting_802_1x_get_phase2_auth (NMSetting8 const char * nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting); -const GByteArray *nm_setting_802_1x_get_phase2_ca_cert (NMSetting8021x *setting); -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); -gboolean nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting); - -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, +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, const char *filename, - NMSetting8021xCKType *out_ck_type, - GError **err); + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); + +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, + const char *filename, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); const char * nm_setting_802_1x_get_password (NMSetting8021x *setting); @@ -148,39 +191,31 @@ const char * nm_setting_802_1x_get_pin (NMSetting8 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); +NMSetting8021xCKScheme nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_private_key (NMSetting8021x *setting, + const char *filename, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); +const char * nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting); + +NMSetting8021xCKFormat nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting); + +NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting); +const GByteArray * nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting); +const char * nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting); +gboolean nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, + const char *filename, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error); +const char * nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting); + +NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting); G_END_DECLS diff --git a/libnm-util/tests/test-crypto.c b/libnm-util/tests/test-crypto.c index 75e34d3ad7..18920342d4 100644 --- a/libnm-util/tests/test-crypto.c +++ b/libnm-util/tests/test-crypto.c @@ -205,7 +205,7 @@ test_is_pkcs12 (const char *path, gboolean expect_fail, const char *desc) { gboolean is_pkcs12; - is_pkcs12 = crypto_is_pkcs12_file (path); + is_pkcs12 = crypto_is_pkcs12_file (path, NULL); if (expect_fail) { ASSERT (is_pkcs12 == FALSE, desc, "unexpected success reading non-PKCS#12 file '%s'", diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index 64a4a06265..3bf92794d2 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -702,8 +702,8 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, { NMSupplicantConfigPrivate *priv; char *tmp; - const char *peapver, *value; - gboolean success; + const char *peapver, *value, *path; + gboolean success, added; GString *phase1, *phase2; const GByteArray *array; @@ -780,53 +780,163 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, } 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)) + /* CA path */ + path = nm_setting_802_1x_get_ca_path (setting); + if (nm_setting_802_1x_get_system_ca_certs (setting)) + path = SYSTEM_CA_PATH; + if (path) { + if (!add_string_val (self, 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) { + /* Phase2 CA path */ + path = nm_setting_802_1x_get_phase2_ca_path (setting); + if (nm_setting_802_1x_get_system_ca_certs (setting)) + path = SYSTEM_CA_PATH; + if (path) { + if (!add_string_val (self, path, "ca_path2", FALSE, FALSE)) + return FALSE; + } + + /* CA certificate */ + switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_ca_cert_blob (setting); + ADD_BLOB_VAL (array, "ca_cert", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + + /* Phase 2 CA certificate */ + switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_ca_cert_blob (setting); + ADD_BLOB_VAL (array, "ca_cert2", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } + + /* Private key */ + added = FALSE; + switch (nm_setting_802_1x_get_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_private_key_blob (setting); ADD_BLOB_VAL (array, "private_key", connection_uid); + added = TRUE; + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_private_key_path (setting); + if (!add_string_val (self, path, "private_key", FALSE, FALSE)) + return FALSE; + added = TRUE; + break; + default: + break; + } + + if (added) { + NMSetting8021xCKFormat format; + NMSetting8021xCKScheme scheme; - 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 */ + format = nm_setting_802_1x_get_private_key_format (setting); + scheme = nm_setting_802_1x_get_private_key_scheme (setting); + + if ( scheme == NM_SETTING_802_1X_CK_SCHEME_PATH + || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the private key password for PKCS#12 blobs and + * all path schemes, since in both of these cases the private key + * isn't decrypted at all. + */ 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 (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the client cert if the private key is not PKCS#12, as + * wpa_supplicant configuration directs us to do. + */ + switch (nm_setting_802_1x_get_client_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_client_cert_blob (setting); + ADD_BLOB_VAL (array, "client_cert", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_client_cert_path (setting); + if (!add_string_val (self, path, "client_cert", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } } } - if (nm_setting_802_1x_get_system_ca_certs (setting)) { - if (!add_string_val (self, SYSTEM_CA_PATH, "ca_path2", FALSE, FALSE)) + /* Phase 2 private key */ + added = FALSE; + switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_private_key_blob (setting); + ADD_BLOB_VAL (array, "private_key2", connection_uid); + added = TRUE; + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_private_key_path (setting); + if (!add_string_val (self, path, "private_key2", FALSE, FALSE)) return FALSE; - } else { - ADD_BLOB_VAL (nm_setting_802_1x_get_phase2_ca_cert (setting), "ca_cert2", connection_uid); + added = TRUE; + break; + default: + break; } - array = nm_setting_802_1x_get_phase2_private_key (setting); - if (array) { - ADD_BLOB_VAL (array, "private_key2", connection_uid); + if (added) { + NMSetting8021xCKFormat format; + NMSetting8021xCKScheme scheme; + + format = nm_setting_802_1x_get_phase2_private_key_format (setting); + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); - 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 */ + if ( scheme == NM_SETTING_802_1X_CK_SCHEME_PATH + || format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the private key password for PKCS#12 blobs and + * all path schemes, since in both of these cases the private key + * isn't decrypted at all. + */ value = nm_setting_802_1x_get_phase2_private_key_password (setting); - if (!add_string_val (self, value, "private_key2_passwd", FALSE, TRUE)) + if (!add_string_val (self, value, "private_key_passwd2", 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; + } + + if (format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + /* Only add the client cert if the private key is not PKCS#12, as + * wpa_supplicant configuration directs us to do. + */ + switch (nm_setting_802_1x_get_phase2_client_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + array = nm_setting_802_1x_get_phase2_client_cert_blob (setting); + ADD_BLOB_VAL (array, "client_cert2", connection_uid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_client_cert_path (setting); + if (!add_string_val (self, path, "client_cert2", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } } } diff --git a/system-settings/plugins/ifcfg-rh/common.h b/system-settings/plugins/ifcfg-rh/common.h index 1569132a93..c2dca77a6d 100644 --- a/system-settings/plugins/ifcfg-rh/common.h +++ b/system-settings/plugins/ifcfg-rh/common.h @@ -38,24 +38,6 @@ #define TYPE_ETHERNET "Ethernet" #define TYPE_WIRELESS "Wireless" -#define TAG_CA_CERT_PATH "ca-cert-path" -#define TAG_CA_CERT_HASH "ca-cert-hash" - -#define TAG_CLIENT_CERT_PATH "client-cert-path" -#define TAG_CLIENT_CERT_HASH "client-cert-hash" - -#define TAG_PRIVATE_KEY_PATH "private-key-path" -#define TAG_PRIVATE_KEY_HASH "private-key-hash" - -#define TAG_PHASE2_CA_CERT_PATH "phase2-ca-cert-path" -#define TAG_PHASE2_CA_CERT_HASH "phase2-ca-cert-hash" - -#define TAG_PHASE2_CLIENT_CERT_PATH "phase2-client-cert-path" -#define TAG_PHASE2_CLIENT_CERT_HASH "phase2-client-cert-hash" - -#define TAG_PHASE2_PRIVATE_KEY_PATH "phase2-private-key-path" -#define TAG_PHASE2_PRIVATE_KEY_HASH "phase2-private-key-hash" - GQuark ifcfg_plugin_error_quark (void); diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index 8d6b311d4c..0ac6e9f975 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -1201,21 +1201,6 @@ get_cert_file (const char *ifcfg_path, const char *cert_path) return ret; } -static void -set_file_path (NMSetting8021x *s_8021x, - const char *path_tag, - const char *hash_tag, - const char *path, - const char *setting_key) -{ - GByteArray *data = NULL; - - g_object_set_data_full (G_OBJECT (s_8021x), path_tag, g_strdup (path), g_free); - g_object_get (G_OBJECT (s_8021x), setting_key, &data, NULL); - if (data) - g_object_set_data_full (G_OBJECT (s_8021x), hash_tag, utils_hash_byte_array (data), g_free); -} - static gboolean eap_tls_reader (const char *eap_method, shvarFile *ifcfg, @@ -1231,7 +1216,7 @@ eap_tls_reader (const char *eap_method, char *privkey = NULL; char *privkey_password = NULL; gboolean success = FALSE; - NMSetting8021xCKType privkey_type = NM_SETTING_802_1X_CK_TYPE_UNKNOWN; + NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; value = svGetValue (ifcfg, "IEEE_8021X_IDENTITY", FALSE); if (!value) { @@ -1249,21 +1234,19 @@ eap_tls_reader (const char *eap_method, if (ca_cert) { real_path = get_cert_file (ifcfg->fileName, ca_cert); if (phase2) { - if (!nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, real_path, NULL, error)) + if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, + real_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_PHASE2_CA_CERT_PATH, - TAG_PHASE2_CA_CERT_HASH, - real_path, - NM_SETTING_802_1X_PHASE2_CA_CERT); } else { - if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_path, NULL, error)) + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + real_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_CA_CERT_PATH, - TAG_CA_CERT_HASH, - real_path, - NM_SETTING_802_1X_CA_CERT); } } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing %s for EAP" @@ -1306,56 +1289,30 @@ eap_tls_reader (const char *eap_method, g_free (real_path); real_path = get_cert_file (ifcfg->fileName, privkey); if (phase2) { - if (!nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, real_path, privkey_password, &privkey_type, error)) + if (!nm_setting_802_1x_set_phase2_private_key (s_8021x, + real_path, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, + error)) goto done; - set_file_path (s_8021x, - TAG_PHASE2_PRIVATE_KEY_PATH, - TAG_PHASE2_PRIVATE_KEY_HASH, - real_path, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); } else { - if (!nm_setting_802_1x_set_private_key_from_file (s_8021x, real_path, privkey_password, &privkey_type, error)) + if (!nm_setting_802_1x_set_private_key (s_8021x, + real_path, + privkey_password, + NM_SETTING_802_1X_CK_SCHEME_PATH, + &privkey_format, + error)) goto done; - set_file_path (s_8021x, - TAG_PRIVATE_KEY_PATH, - TAG_PRIVATE_KEY_HASH, - real_path, - NM_SETTING_802_1X_PRIVATE_KEY); } - /* Per NM requirements, if the private key is pkcs12, set the client cert to the - * same data as the private key, since pkcs12 files contain both. + /* Only set the client certificate if the private key is not PKCS#12 format, + * as NM (due to supplicant restrictions) requires. If the key was PKCS#12, + * then nm_setting_802_1x_set_private_key() already set the client certificate + * to the same value as the private key. */ - if (privkey_type == NM_SETTING_802_1X_CK_TYPE_PKCS12) { - /* Set the private key password if PKCS#12, because PKCS#12 doesn't get - * decrypted when being stored in the Setting. - */ - if (phase2) - g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, privkey_password, NULL); - else - g_object_set (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, privkey_password, NULL); - - if (phase2) { - if (!nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, real_path, NULL, error)) - goto done; - set_file_path (s_8021x, - TAG_PHASE2_CLIENT_CERT_PATH, - TAG_PHASE2_CLIENT_CERT_HASH, - real_path, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - } else { - if (!nm_setting_802_1x_set_client_cert_from_file (s_8021x, real_path, NULL, error)) - goto done; - set_file_path (s_8021x, - TAG_CLIENT_CERT_PATH, - TAG_CLIENT_CERT_HASH, - real_path, - NM_SETTING_802_1X_CLIENT_CERT); - } - } else { - /* Otherwise, private key is "traditional" OpenSSL format, so - * client certificate will be a separate file. - */ + if ( privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY + || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) { client_cert = svGetValue (ifcfg, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", FALSE); @@ -1370,21 +1327,19 @@ eap_tls_reader (const char *eap_method, g_free (real_path); real_path = get_cert_file (ifcfg->fileName, client_cert); if (phase2) { - if (!nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, real_path, NULL, error)) + if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, + real_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_PHASE2_CLIENT_CERT_PATH, - TAG_PHASE2_CLIENT_CERT_HASH, - real_path, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); } else { - if (!nm_setting_802_1x_set_client_cert_from_file (s_8021x, real_path, NULL, error)) + if (!nm_setting_802_1x_set_client_cert (s_8021x, + real_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_CLIENT_CERT_PATH, - TAG_CLIENT_CERT_HASH, - real_path, - NM_SETTING_802_1X_CLIENT_CERT); } } @@ -1418,13 +1373,12 @@ eap_peap_reader (const char *eap_method, ca_cert = svGetValue (ifcfg, "IEEE_8021X_CA_CERT", FALSE); if (ca_cert) { real_cert_path = get_cert_file (ifcfg->fileName, ca_cert); - if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_cert_path, NULL, error)) + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + real_cert_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_CA_CERT_PATH, - TAG_CA_CERT_HASH, - real_cert_path, - NM_SETTING_802_1X_CA_CERT); } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing " "IEEE_8021X_CA_CERT for EAP method '%s'; this is" @@ -1520,13 +1474,12 @@ eap_ttls_reader (const char *eap_method, ca_cert = svGetValue (ifcfg, "IEEE_8021X_CA_CERT", FALSE); if (ca_cert) { real_cert_path = get_cert_file (ifcfg->fileName, ca_cert); - if (!nm_setting_802_1x_set_ca_cert_from_file (s_8021x, real_cert_path, NULL, error)) + if (!nm_setting_802_1x_set_ca_cert (s_8021x, + real_cert_path, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + error)) goto done; - set_file_path (s_8021x, - TAG_CA_CERT_PATH, - TAG_CA_CERT_HASH, - real_cert_path, - NM_SETTING_802_1X_CA_CERT); } else { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: missing " "IEEE_8021X_CA_CERT for EAP method '%s'; this is" diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 2afb147b9b..12e8008596 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -69,8 +69,9 @@ verify_cert_or_key (CertKeyType ck_type, NMSetting8021x *s_8021x; GError *error = NULL; gboolean success = FALSE; - const GByteArray *expected = NULL, *setting = NULL; + const char *expected = NULL, *setting = NULL; gboolean phase2 = FALSE; + NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; if (strstr (setting_key, "phase2")) phase2 = TRUE; @@ -83,19 +84,19 @@ verify_cert_or_key (CertKeyType ck_type, if (ck_type == CK_CA_CERT) { if (phase2) - success = nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, file, NULL, &error); + success = nm_setting_802_1x_set_phase2_ca_cert (s_8021x, file, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); else - success = nm_setting_802_1x_set_ca_cert_from_file (s_8021x, file, NULL, &error); + success = nm_setting_802_1x_set_ca_cert (s_8021x, file, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); } else if (ck_type == CK_CLIENT_CERT) { if (phase2) - success = nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, file, NULL, &error); + success = nm_setting_802_1x_set_phase2_client_cert (s_8021x, file, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); else - success = nm_setting_802_1x_set_client_cert_from_file (s_8021x, file, NULL, &error); + success = nm_setting_802_1x_set_client_cert (s_8021x, file, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); } else if (ck_type == CK_PRIV_KEY) { if (phase2) - success = nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, file, privkey_password, NULL, &error); + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, file, privkey_password, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); else - success = nm_setting_802_1x_set_private_key_from_file (s_8021x, file, privkey_password, NULL, &error); + success = nm_setting_802_1x_set_private_key (s_8021x, file, privkey_password, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, &error); } ASSERT (success == TRUE, test_name, "failed to verify %s: could not load item for %s / %s: %s", @@ -103,19 +104,39 @@ verify_cert_or_key (CertKeyType ck_type, if (ck_type == CK_CA_CERT) { if (phase2) - expected = nm_setting_802_1x_get_phase2_ca_cert (s_8021x); + scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (s_8021x); else - expected = nm_setting_802_1x_get_ca_cert (s_8021x); + scheme = nm_setting_802_1x_get_ca_cert_scheme (s_8021x); } else if (ck_type == CK_CLIENT_CERT) { if (phase2) - expected = nm_setting_802_1x_get_phase2_client_cert (s_8021x); + scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (s_8021x); else - expected = nm_setting_802_1x_get_client_cert (s_8021x); + scheme = nm_setting_802_1x_get_client_cert_scheme (s_8021x); } else if (ck_type == CK_PRIV_KEY) { if (phase2) - expected = nm_setting_802_1x_get_phase2_private_key (s_8021x); + scheme = nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x); else - expected = nm_setting_802_1x_get_private_key (s_8021x); + scheme = nm_setting_802_1x_get_private_key_scheme (s_8021x); + } + ASSERT (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, + test_name, "failed to verify %s: unexpected cert/key scheme for %s / %s", + ifcfg, NM_SETTING_802_1X_SETTING_NAME, setting_key); + + if (ck_type == CK_CA_CERT) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_ca_cert_path (s_8021x); + else + expected = nm_setting_802_1x_get_ca_cert_path (s_8021x); + } else if (ck_type == CK_CLIENT_CERT) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_client_cert_path (s_8021x); + else + expected = nm_setting_802_1x_get_client_cert_path (s_8021x); + } else if (ck_type == CK_PRIV_KEY) { + if (phase2) + expected = nm_setting_802_1x_get_phase2_private_key_path (s_8021x); + else + expected = nm_setting_802_1x_get_private_key_path (s_8021x); } ASSERT (expected != NULL, test_name, "failed to verify %s: failed to get read item for %s / %s", @@ -123,29 +144,29 @@ verify_cert_or_key (CertKeyType ck_type, if (ck_type == CK_CA_CERT) { if (phase2) - setting = nm_setting_802_1x_get_phase2_ca_cert (s_compare); + setting = nm_setting_802_1x_get_phase2_ca_cert_path (s_compare); else - setting = nm_setting_802_1x_get_ca_cert (s_compare); + setting = nm_setting_802_1x_get_ca_cert_path (s_compare); } else if (ck_type == CK_CLIENT_CERT) { if (phase2) - setting = nm_setting_802_1x_get_phase2_client_cert (s_compare); + setting = nm_setting_802_1x_get_phase2_client_cert_path (s_compare); else - setting = nm_setting_802_1x_get_client_cert (s_compare); + setting = nm_setting_802_1x_get_client_cert_path (s_compare); } else if (ck_type == CK_PRIV_KEY) { if (phase2) - setting = nm_setting_802_1x_get_phase2_private_key (s_compare); + setting = nm_setting_802_1x_get_phase2_private_key_path (s_compare); else - setting = nm_setting_802_1x_get_private_key (s_compare); + setting = nm_setting_802_1x_get_private_key_path (s_compare); } ASSERT (setting != NULL, test_name, "failed to verify %s: missing %s / %s key", ifcfg, NM_SETTING_802_1X_SETTING_NAME, setting_key); - ASSERT (setting->len == expected->len, + ASSERT (strlen (setting) == strlen (expected), test_name, "failed to verify %s: unexpected %s / %s certificate length", test_name, NM_SETTING_802_1X_SETTING_NAME, setting_key); - ASSERT (memcmp (setting->data, expected->data, setting->len) == 0, + ASSERT (strcmp (setting, expected) == 0, test_name, "failed to verify %s: %s / %s key certificate mismatch", ifcfg, NM_SETTING_802_1X_SETTING_NAME, setting_key); @@ -1040,8 +1061,8 @@ test_read_wired_8021x_peap_mschapv2 (void) const char *expected_identity = "David Smith"; const char *expected_password = "foobar baz"; gboolean success = FALSE; - const GByteArray *expected_ca_cert; - const GByteArray *read_ca_cert; + const char *expected_ca_cert_path; + const char *read_ca_cert_path; connection = connection_from_file (TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, NULL, @@ -1166,40 +1187,34 @@ test_read_wired_8021x_peap_mschapv2 (void) ASSERT (tmp_8021x != NULL, "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: could not create temp 802.1x setting", TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CA_CERT); + NM_SETTING_802_1X_SETTING_NAME); - success = nm_setting_802_1x_set_ca_cert_from_file (tmp_8021x, - TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_ca_cert (tmp_8021x, + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: could not load CA certificate", TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - expected_ca_cert = nm_setting_802_1x_get_ca_cert (tmp_8021x); - ASSERT (expected_ca_cert != NULL, + expected_ca_cert_path = nm_setting_802_1x_get_ca_cert_path (tmp_8021x); + ASSERT (expected_ca_cert_path != NULL, "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: failed to get CA certificate", TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - read_ca_cert = nm_setting_802_1x_get_ca_cert (s_8021x); - ASSERT (read_ca_cert != NULL, + read_ca_cert_path = nm_setting_802_1x_get_ca_cert_path (s_8021x); + ASSERT (read_ca_cert_path != NULL, "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: missing %s / %s key", TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - ASSERT (read_ca_cert->len == expected_ca_cert->len, - "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: unexpected %s / %s certificate length", - TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CA_CERT); - - ASSERT (memcmp (read_ca_cert->data, expected_ca_cert->data, read_ca_cert->len) == 0, - "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: %s / %s key certificate mismatch", + ASSERT (strcmp (read_ca_cert_path, expected_ca_cert_path) == 0, + "wired-8021x-peap-mschapv2-verify-8021x", "failed to verify %s: unexpected %s / %s certificate path", TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); @@ -2878,7 +2893,7 @@ test_read_wifi_wpa_eap_tls (void) char *keyfile = NULL; gboolean ignore_error = FALSE; GError *error = NULL; - const char *tmp; + const char *tmp, *password; const char *expected_identity = "Bill Smith"; const char *expected_privkey_password = "test1"; @@ -2980,7 +2995,14 @@ test_read_wifi_wpa_eap_tls (void) NM_SETTING_802_1X_CLIENT_CERT); /* Private Key Password */ - ASSERT (nm_setting_802_1x_get_private_key_password (s_8021x) == NULL, + password = nm_setting_802_1x_get_private_key_password (s_8021x); + ASSERT (password != NULL, + "wifi-wpa-eap-tls-verify-8021x", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WPA_EAP_TLS, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + + ASSERT (strcmp (password, expected_privkey_password) == 0, "wifi-wpa-eap-tls-verify-8021x", "failed to verify %s: unexpected %s / %s key", TEST_IFCFG_WIFI_WPA_EAP_TLS, NM_SETTING_802_1X_SETTING_NAME, @@ -3013,7 +3035,7 @@ test_read_wifi_wpa_eap_ttls_tls (void) char *keyfile = NULL; gboolean ignore_error = FALSE; GError *error = NULL; - const char *tmp; + const char *tmp, *password; const char *expected_identity = "Chuck Shumer"; const char *expected_privkey_password = "test1"; @@ -3124,7 +3146,14 @@ test_read_wifi_wpa_eap_ttls_tls (void) NM_SETTING_802_1X_PHASE2_CLIENT_CERT); /* Inner Private Key Password */ - ASSERT (nm_setting_802_1x_get_phase2_private_key_password (s_8021x) == NULL, + password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + ASSERT (password != NULL, + "wifi-wpa-eap-ttls-tls-verify-8021x", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIFI_WPA_EAP_TTLS_TLS, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + + ASSERT (strcmp (password, expected_privkey_password) == 0, "wifi-wpa-eap-ttls-tls-verify-8021x", "failed to verify %s: unexpected %s / %s key", TEST_IFCFG_WIFI_WPA_EAP_TTLS_TLS, NM_SETTING_802_1X_SETTING_NAME, @@ -3619,10 +3648,11 @@ test_write_wired_dhcp_8021x_peap_mschapv2 (void) nm_setting_802_1x_add_eap_method (s_8021x, "peap"); - success = nm_setting_802_1x_set_ca_cert_from_file (s_8021x, - TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_IFCFG_WIRED_8021x_PEAP_MSCHAPV2_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wired-dhcp-8021x-peap-mschapv2write", "failed to verify connection: %s", (error && error->message) ? error->message : "(unknown)"); @@ -4548,27 +4578,30 @@ test_write_wifi_wpa_eap_tls (void) nm_setting_802_1x_add_eap_method (s_8021x, "tls"); - success = nm_setting_802_1x_set_ca_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-tls-write", "failed to set CA certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, error->message); - success = nm_setting_802_1x_set_client_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_client_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-tls-write", "failed to set client certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, error->message); - success = nm_setting_802_1x_set_private_key_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, - "test1", - NULL, - &error); + success = nm_setting_802_1x_set_private_key (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + "test1", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-tls-write", "failed to set private key '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, error->message); @@ -4712,10 +4745,11 @@ test_write_wifi_wpa_eap_ttls_tls (void) NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL); - success = nm_setting_802_1x_set_ca_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-ttls-tls-write", "failed to set CA certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, error->message); @@ -4723,29 +4757,32 @@ test_write_wifi_wpa_eap_ttls_tls (void) /* Phase 2 TLS stuff */ /* phase2 CA cert */ - success = nm_setting_802_1x_set_phase2_ca_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_phase2_ca_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-ttls-tls-write", "failed to set inner CA certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, error->message); /* phase2 client cert */ - success = nm_setting_802_1x_set_phase2_client_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_phase2_client_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-ttls-tls-write", "failed to set inner client certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CLIENT_CERT, error->message); /* phase2 private key */ - success = nm_setting_802_1x_set_phase2_private_key_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, - "test1", - NULL, - &error); + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, + "test1", + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-ttls-tls-write", "failed to set private key '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_PRIVATE_KEY, error->message); @@ -4893,10 +4930,11 @@ test_write_wifi_wpa_eap_ttls_mschapv2 (void) NM_SETTING_802_1X_PHASE2_AUTHEAP, "mschapv2", NULL); - success = nm_setting_802_1x_set_ca_cert_from_file (s_8021x, - TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, - NULL, - &error); + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error); ASSERT (success == TRUE, "wifi-wpa-eap-ttls-mschapv2-write", "failed to set CA certificate '%s': %s", TEST_IFCFG_WIFI_WPA_EAP_TLS_CA_CERT, error->message); diff --git a/system-settings/plugins/ifcfg-rh/utils.c b/system-settings/plugins/ifcfg-rh/utils.c index 015769ac0a..956c53e26a 100644 --- a/system-settings/plugins/ifcfg-rh/utils.c +++ b/system-settings/plugins/ifcfg-rh/utils.c @@ -116,17 +116,6 @@ utils_hexstr2bin (const char *hex, size_t len) /* End from hostap */ -char * -utils_hash_byte_array (const GByteArray *data) -{ - unsigned char buf[SHA1_MAC_LEN]; - static const char *key = "0123456789abcdefghijklmnopqrstuvwxyz"; - - memset (buf, 0, sizeof (buf)); - sha1_mac ((const unsigned char *) key, strlen (key), (const u_int8_t *) data->data, data->len, &buf[0]); - return utils_bin2hexstr ((const char *) &buf[0], SHA1_MAC_LEN, SHA1_MAC_LEN * 2); -} - char * utils_cert_path (const char *parent, const char *suffix) { diff --git a/system-settings/plugins/ifcfg-rh/utils.h b/system-settings/plugins/ifcfg-rh/utils.h index 1dd7e7dcee..3424c6078d 100644 --- a/system-settings/plugins/ifcfg-rh/utils.h +++ b/system-settings/plugins/ifcfg-rh/utils.h @@ -29,8 +29,6 @@ char *utils_bin2hexstr (const char *bytes, int len, int final_len); char *utils_hexstr2bin (const char *hex, size_t len); -char *utils_hash_byte_array (const GByteArray *data); - char *utils_cert_path (const char *parent, const char *suffix); char *utils_get_ifcfg_name (const char *file); diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index 02e4fa1146..763b94ed73 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -144,151 +144,187 @@ out: return success; } +typedef NMSetting8021xCKScheme (*SchemeFunc)(NMSetting8021x *setting); +typedef const char * (*PathFunc) (NMSetting8021x *setting); +typedef const GByteArray * (*BlobFunc) (NMSetting8021x *setting); + typedef struct ObjectType { const char *setting_key; + SchemeFunc scheme_func; + PathFunc path_func; + BlobFunc blob_func; const char *ifcfg_key; - const char *path_tag; - const char *hash_tag; const char *suffix; } ObjectType; 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_blob, "IEEE_8021X_CA_CERT", - TAG_CA_CERT_PATH, - TAG_CA_CERT_HASH, "ca-cert.der" }; 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, + nm_setting_802_1x_get_phase2_ca_cert_blob, "IEEE_8021X_INNER_CA_CERT", - TAG_PHASE2_CA_CERT_PATH, - TAG_PHASE2_CA_CERT_HASH, "inner-ca-cert.der" }; 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, + nm_setting_802_1x_get_client_cert_blob, "IEEE_8021X_CLIENT_CERT", - TAG_CLIENT_CERT_PATH, - TAG_CLIENT_CERT_HASH, "client-cert.der" }; 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, + nm_setting_802_1x_get_phase2_client_cert_blob, "IEEE_8021X_INNER_CLIENT_CERT", - TAG_PHASE2_CLIENT_CERT_PATH, - TAG_PHASE2_CLIENT_CERT_HASH, "inner-client-cert.der" }; 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, + nm_setting_802_1x_get_private_key_blob, "IEEE_8021X_PRIVATE_KEY", - TAG_PRIVATE_KEY_PATH, - TAG_PRIVATE_KEY_HASH, "private-key.pem" }; 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, + nm_setting_802_1x_get_phase2_private_key_blob, "IEEE_8021X_INNER_PRIVATE_KEY", - TAG_PHASE2_PRIVATE_KEY_PATH, - TAG_PHASE2_PRIVATE_KEY_HASH, "inner-private-key.pem" }; 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, + nm_setting_802_1x_get_private_key_blob, "IEEE_8021X_PRIVATE_KEY", - TAG_PRIVATE_KEY_PATH, - TAG_PRIVATE_KEY_HASH, "private-key.p12" }; 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, + nm_setting_802_1x_get_phase2_private_key_blob, "IEEE_8021X_INNER_PRIVATE_KEY", - TAG_PHASE2_PRIVATE_KEY_PATH, - TAG_PHASE2_PRIVATE_KEY_HASH, "inner-private-key.p12" }; static gboolean write_object (NMSetting8021x *s_8021x, shvarFile *ifcfg, - const GByteArray *object, + const GByteArray *override_data, const ObjectType *objtype, - gboolean *wrote, GError **error) { - const char *orig_hash, *orig_file; - char *new_hash = NULL, *new_file = NULL; - gboolean success = FALSE; - GError *write_error = NULL; + NMSetting8021xCKScheme scheme; + const char *path = NULL; + const GByteArray *blob = NULL; - g_return_val_if_fail (objtype != NULL, FALSE); g_return_val_if_fail (ifcfg != NULL, FALSE); - g_return_val_if_fail (wrote != NULL, FALSE); + g_return_val_if_fail (objtype != NULL, FALSE); + + if (override_data) { + /* if given explicit data to save, always use that instead of asking + * the setting what to do. + */ + blob = override_data; + } else { + scheme = (*(objtype->scheme_func))(s_8021x); + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + blob = (*(objtype->blob_func))(s_8021x); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = (*(objtype->path_func))(s_8021x); + break; + default: + break; + } + } - *wrote = FALSE; + /* If certificate/private key was sent, the connection may no longer be + * 802.1x and thus we clear out the paths and certs. + */ + if (!path && !blob) { + char *standard_file; + int ignored; + + /* Since no cert/private key is now being used, delete any standard file + * that was created for this connection, but leave other files alone. + * Thus, for example, + * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der + * will be deleted, but /etc/pki/tls/cert.pem will not. + */ + standard_file = utils_cert_path (ifcfg->fileName, objtype->suffix); + if (g_file_test (standard_file, G_FILE_TEST_EXISTS)) + ignored = unlink (standard_file); + g_free (standard_file); - if (!object) { svSetValue (ifcfg, objtype->ifcfg_key, NULL, FALSE); return TRUE; } - new_hash = utils_hash_byte_array (object); - if (!new_hash) { - g_set_error (error, ifcfg_plugin_error_quark (), 0, - "Could not hash certificate/key data for %s / %s", - NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key); - return FALSE; + /* If the object path was specified, prefer that over any raw cert data that + * may have been sent. + */ + if (path) { + svSetValue (ifcfg, objtype->ifcfg_key, path, FALSE); + return TRUE; } - orig_hash = g_object_get_data (G_OBJECT (s_8021x), objtype->hash_tag); - orig_file = g_object_get_data (G_OBJECT (s_8021x), objtype->path_tag); + /* If it's raw certificate data, write the cert data out to the standard file */ + if (blob) { + gboolean success; + char *new_file; + GError *write_error = NULL; - if (!orig_hash || !orig_file || strcmp (new_hash, orig_hash)) { - /* if the cert data has changed, or there wasn't a cert - * originally, write data out to the standard file. - */ new_file = utils_cert_path (ifcfg->fileName, objtype->suffix); if (!new_file) { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not create file path for %s / %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key); - goto out; + return FALSE; } - if (!write_secret_file (new_file, (const char *) object->data, object->len, &write_error)) { + /* Write the raw certificate data out to the standard file so that we + * can use paths from now on instead of pushing around the certificate + * data itself. + */ + success = write_secret_file (new_file, (const char *) blob->data, blob->len, &write_error); + if (success) { + svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE); + return TRUE; + } else { g_set_error (error, ifcfg_plugin_error_quark (), 0, "Could not write certificate/key for %s / %s: %s", NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key, (write_error && write_error->message) ? write_error->message : "(unknown)"); g_clear_error (&write_error); - goto out; } - *wrote = TRUE; - - svSetValue (ifcfg, objtype->ifcfg_key, new_file, FALSE); - g_object_set_data_full (G_OBJECT (s_8021x), objtype->path_tag, new_file, g_free); - new_file = NULL; /* g_object_set_data_full() took ownership */ - - g_object_set_data_full (G_OBJECT (s_8021x), objtype->hash_tag, new_hash, g_free); - new_hash = NULL; /* g_object_set_data_full() took ownership */ - } else { - /* cert data hasn't changed */ - svSetValue (ifcfg, objtype->ifcfg_key, orig_file, FALSE); + g_free (new_file); } - success = TRUE; -out: - g_free (new_hash); - g_free (new_file); - return success; + return FALSE; } static gboolean @@ -297,16 +333,15 @@ write_8021x_certs (NMSetting8021x *s_8021x, shvarFile *ifcfg, GError **error) { - const GByteArray *data; GByteArray *enc_key = NULL; const char *password = NULL; char *generated_pw = NULL; - gboolean success = FALSE, is_pkcs12 = FALSE, wrote; + gboolean success = FALSE, is_pkcs12 = FALSE; const ObjectType *otype = NULL; const char *prop; + const GByteArray *blob = NULL; /* CA certificate */ - data = NULL; if (phase2) { prop = NM_SETTING_802_1X_PHASE2_CA_CERT; otype = &phase2_ca_type; @@ -314,24 +349,22 @@ write_8021x_certs (NMSetting8021x *s_8021x, prop = NM_SETTING_802_1X_CA_CERT; otype = &ca_type; } - g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); - if (!write_object (s_8021x, ifcfg, data, otype, &wrote, error)) + + if (!write_object (s_8021x, ifcfg, NULL, otype, error)) return FALSE; /* Private key */ if (phase2) { - if (nm_setting_802_1x_get_phase2_private_key (s_8021x)) { - if (nm_setting_802_1x_get_phase2_private_key_type (s_8021x) == NM_SETTING_802_1X_CK_TYPE_PKCS12) + if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_phase2_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) is_pkcs12 = TRUE; } - prop = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY; password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); } else { - if (nm_setting_802_1x_get_private_key (s_8021x)) { - if (nm_setting_802_1x_get_private_key_type (s_8021x) == NM_SETTING_802_1X_CK_TYPE_PKCS12) + if (nm_setting_802_1x_get_private_key_scheme (s_8021x) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + if (nm_setting_802_1x_get_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) is_pkcs12 = TRUE; } - prop = NM_SETTING_802_1X_PRIVATE_KEY; password = nm_setting_802_1x_get_private_key_password (s_8021x); } @@ -340,11 +373,19 @@ write_8021x_certs (NMSetting8021x *s_8021x, else otype = phase2 ? &phase2_pk_type : &pk_type; - data = NULL; - g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); - if (data && !is_pkcs12) { + if ((*(otype->scheme_func))(s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB) + blob = (*(otype->blob_func))(s_8021x); + + /* Only do the private key re-encrypt dance if we got the raw key data, which + * by definition will be unencrypted. If we're given a direct path to the + * private key file, it'll be encrypted, so we don't need to re-encrypt. + */ + if (blob && !is_pkcs12) { GByteArray *array; + /* If the private key is an unencrypted blob, re-encrypt it with a + * random password since we don't store unencrypted private keys on disk. + */ if (!password) { /* Create a random private key */ array = crypto_random (32, error); @@ -356,13 +397,14 @@ write_8021x_certs (NMSetting8021x *s_8021x, g_byte_array_free (array, TRUE); } - /* Re-encrypt the private key if it's not PKCS#12 (which never decrypted by NM) */ - enc_key = crypto_key_to_pem (data, password, error); + /* Encrypt the unencrypted private key with the fake password */ + enc_key = crypto_key_to_pem (blob, password, error); if (!enc_key) goto out; } - if (!write_object (s_8021x, ifcfg, enc_key ? enc_key : data, otype, &wrote, error)) + /* Save the private key */ + if (!write_object (s_8021x, ifcfg, enc_key, otype, error)) goto out; /* Private key password */ @@ -371,11 +413,6 @@ write_8021x_certs (NMSetting8021x *s_8021x, else set_secret (ifcfg, "IEEE_8021X_PRIVATE_KEY_PASSWORD", password); - if (enc_key) { - memset (enc_key->data, 0, enc_key->len); - g_byte_array_free (enc_key, TRUE); - } - /* Client certificate */ if (is_pkcs12) { svSetValue (ifcfg, @@ -389,9 +426,9 @@ write_8021x_certs (NMSetting8021x *s_8021x, prop = NM_SETTING_802_1X_CLIENT_CERT; otype = &client_type; } - data = NULL; - g_object_get (G_OBJECT (s_8021x), prop, &data, NULL); - if (!write_object (s_8021x, ifcfg, data, otype, &wrote, error)) + + /* Save the client certificate */ + if (!write_object (s_8021x, ifcfg, NULL, otype, error)) goto out; } @@ -402,6 +439,10 @@ out: memset (generated_pw, 0, strlen (generated_pw)); g_free (generated_pw); } + if (enc_key) { + memset (enc_key->data, 0, enc_key->len); + g_byte_array_free (enc_key, TRUE); + } return success; } -- cgit v1.2.3