diff options
Diffstat (limited to 'system-settings/plugins/ifcfg-rh/writer.c')
-rw-r--r-- | system-settings/plugins/ifcfg-rh/writer.c | 209 |
1 files changed, 125 insertions, 84 deletions
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; } |